Files
wp-multisite-waas/inc/objects/class-limitations.php
2024-11-30 18:24:12 -07:00

577 lines
12 KiB
PHP

<?php
/**
* Limitation manager.
*
* This class centralizes the limitation modules.
*
* @package WP_Ultimo
* @subpackage Limitations
* @since 2.0.0
*/
namespace WP_Ultimo\Objects;
use WP_Ultimo\Objects\Limitations;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Limitation manager.
*
* This class centralizes the limitation modules.
*
* @since 2.0.0
*/
class Limitations {
/**
* Caches early limitation queries to prevent
* to many database hits.
*
* @since 2.0.0
* @var array
*/
static $limitations_cache = array();
/**
* Version of the limitation schema.
*
* @since 2.0.0
* @var integer
*/
protected $version = 2;
/**
* Limitation modules.
*
* @since 2.0.0
* @var array
*/
protected $modules = array();
/**
* The current limitation being merged in merge_recursive.
*
* @since 2.1.0
* @var string
*/
protected $current_merge_id = '';
/**
* Constructs the limitation class with module data.
*
* @since 2.0.0
*
* @param array $modules_data Array of modules data.
*/
public function __construct($modules_data = array()) {
$this->build_modules($modules_data);
} // end __construct;
/**
* Returns the module via magic getter.
*
* @since 2.0.0
*
* @param string $name The module name.
* @return \WP_Ultimo\Limitations\Limit
*/
public function __get($name) {
$module = wu_get_isset($this->modules, $name, false);
if ($module === false) {
$repo = self::repository();
$class_name = wu_get_isset($repo, $name, false);
if (class_exists($class_name)) {
$module = new $class_name(array());
$this->modules[$name] = $module;
return $module;
} // end if;
} // end if;
return $module;
} // end __get;
/**
* Prepare to serialization.
*
* @see requires php 7.3
* @since 2.0.0
* @return array
*/
public function __serialize() { // phpcs:ignore
return $this->to_array();
} // end __serialize;
/**
* Handles un-serialization.
*
* @since 2.0.0
*
* @see requires php 7.3
* @param array $modules_data Array of modules data.
* @return void
*/
public function __unserialize($modules_data) { // phpcs:ignore
$this->build_modules($modules_data);
} // end __unserialize;
/**
* Builds the module list based on the module data.
*
* @since 2.0.0
*
* @param array $modules_data Array of modules data.
* @return self
*/
public function build_modules($modules_data) {
foreach ($modules_data as $type => $data) {
$module = self::build($data, $type);
if ($module) {
$this->modules[$type] = $module;
} // end if;
} // end foreach;
return $this;
} // end build_modules;
/**
* Build a module, based on the data.
*
* @since 2.0.0
*
* @param string|array $data The module data.
* @param string $module_name The module_name.
* @return false|\WP_Ultimo\Limitations\Limit
*/
static public function build($data, $module_name) {
$class = wu_get_isset(self::repository(), $module_name);
if (class_exists($class)) {
if (is_string($data)) {
$data = json_decode($data, true);
}
return new $class($data);
} // end if;
return false;
} // end build;
/**
* Checks if a limitation model exists in this limitations.
*
* @since 2.0.0
*
* @param string $module The module name.
* @return bool
*/
public function exists($module) {
return wu_get_isset($this->modules, $module, false);
} // end exists;
/**
* Checks if we have any limitation modules setup at all.
*
* @since 2.0.0
* @return boolean
*/
public function has_limitations() {
$has_limitations = false;
foreach ($this->modules as $module) {
if ($module->is_enabled()) {
return true;
} // end if;
} // end foreach;
return $has_limitations;
} // end has_limitations;
/**
* Checks if a particular module is enabled.
*
* @since 2.0.0
*
* @param string $module_name Module name.
* @return boolean
*/
public function is_module_enabled($module_name) {
$module = $this->{$module_name};
return $module ? $module->is_enabled() : false;
} // end is_module_enabled;
/**
* Merges limitations from other entities.
*
* This is what we use to combine different limitations from
* different sources. For example: we override the limitations
* of site with restrictions coming from the membership,
* products, etc.
*
* @since 2.0.0
*
* @param array|bool $override A limitation array or a boolean to override the values from first to last limitation.
* @param array ...$limitations Limitation arrays.
* @return self
*/
public function merge($override = false, ...$limitations) {
if (!is_bool($override)) {
$limitations[] = $override;
$override = false;
} // end if;
$results = $this->to_array();
foreach ($limitations as $limitation) {
if (is_a($limitation, \WP_Ultimo\Objects\Limitations::class)) { // @phpstan-ignore-line
$limitation = $limitation->to_array();
} // end if;
if (!is_array($limitation)) {
continue;
} // end if;
$this->merge_recursive($results, $limitation, !$override);
} // end foreach;
return new self($results);
} // end merge;
/**
* Merges a limitation array
*
* @since 2.0.20
*
* @param array $array1 The arrays original.
* @param array $array2 The array to be merged in.
* @param bool $should_sum If we should add up numeric values instead of replacing the original.
* @return void
*/
protected function merge_recursive(array &$array1, array &$array2, $should_sum = true) {
$current_id = $this->current_merge_id;
$force_enabled_list = array(
'plugins',
'themes',
);
$force_enabled = in_array($current_id, $force_enabled_list, true);
if ($force_enabled && (!wu_get_isset($array1, 'enabled', true) || !wu_get_isset($array2, 'enabled', true))) {
$array1['enabled'] = true;
$array2['enabled'] = true;
} // end if;
if (!wu_get_isset($array1, 'enabled', true)) {
$array1 = array(
'enabled' => false,
);
} // end if;
if (!wu_get_isset($array2, 'enabled', true) && $should_sum) {
return;
} // end if;
foreach ($array2 as $key => &$value) {
/**
* Here we need to work with arrays and some limits
* as themes and plugins have stdClass values.
*/
$value = is_object($value) ? get_object_vars($value) : $value;
if (isset($array1[$key])) {
$array1[$key] = is_object($array1[$key]) ? get_object_vars($array1[$key]) : $array1[$key];
} // end if;
if (is_array($value) && isset($array1[$key]) && is_array($array1[$key])) {
$array1_id = wu_get_isset($array1[$key], 'id', $current_id);
$this->current_merge_id = wu_get_isset($value, 'id', $array1_id);
$this->merge_recursive($array1[$key], $value, $should_sum);
$this->current_merge_id = $current_id;
} else {
$original_value = wu_get_isset($array1, $key);
// If the value is 0 or '' it can be a unlimited value
$is_unlimited = (is_numeric($value) || $value === '') && (int) $value === 0;
if ($should_sum && ($original_value === '' || $original_value === 0)) {
/**
* We use values 0 or '' as unlimited in our limits
*/
continue;
} elseif (isset($array1[$key]) && is_numeric($array1[$key]) && is_numeric($value) && $should_sum && !$is_unlimited) {
$array1[$key] = ((int) $array1[$key]) + $value;
} elseif ($key === 'visibility' && isset($array1[$key]) && $should_sum) {
$key_priority = array(
'hidden' => 0,
'visible' => 1,
);
$array1[$key] = $key_priority[$value] > $key_priority[$array1[$key]] ? $value : $array1[$key];
} elseif ($key === 'behavior' && isset($array1[$key]) && $should_sum) {
$key_priority_list = array(
'plugins' => array(
'default' => 10,
'force_inactive_locked' => 20,
'force_inactive' => 30,
'force_active_locked' => 40,
'force_active' => 50,
),
'site' => array(
'not_available' => 10,
'available' => 20,
'pre_selected' => 30,
),
'themes' => array(
'not_available' => 10,
'available' => 20,
'force_active' => 30,
),
);
$key_priority = apply_filters("wu_limitation_{$current_id}_priority", $key_priority_list[$current_id]);
$array1[$key] = $key_priority[$value] > $key_priority[$array1[$key]] ? $value : $array1[$key];
} else {
// Avoid change true values
$array1[$key] = $original_value !== true || !$should_sum ? $value : true;
$array1[$key] = $original_value !== true || !$should_sum ? $value : true;
} // end if;
} // end if;
} // end foreach;
} // end merge_recursive;
/**
* Converts the limitations list to an array.
*
* @since 2.0.0
*/
public function to_array(): array {
return array_map(fn($module) => method_exists($module, 'to_array') ? $module->to_array() : (array) $module, $this->modules);
} // end to_array;
/**
* Static method to return limitations in very early stages of the WordPress lifecycle.
*
* @since 2.0.0
*
* @param string $slug Slug of the model.
* @param int $id ID of the model.
* @return \WP_Ultimo\Objects\Limitations
*/
public static function early_get_limitations($slug, $id) {
$wu_prefix = 'wu_';
/*
* Reset the slug and prefixes
* for the native tables of blogs.
*/
if ($slug === 'site') {
$slug = 'blog';
$wu_prefix = '';
} // end if;
$cache = static::$limitations_cache;
$key = sprintf('%s-%s', $slug, $id);
if (isset($cache[$key])) {
return $cache[$key];
} // end if;
global $wpdb;
$limitations = array();
$table_name = "{$wpdb->base_prefix}{$wu_prefix}{$slug}meta";
$sql = $wpdb->prepare("SELECT meta_value FROM {$table_name} WHERE meta_key = 'wu_limitations' AND {$wu_prefix}{$slug}_id = %d LIMIT 1", $id); // phpcs:ignore
$results = $wpdb->get_var($sql); // phpcs:ignore
if (!empty($results)) {
$limitations = unserialize($results);
} // end if;
/*
* Caches the results.
*/
static::$limitations_cache[$key] = $limitations;
return $limitations;
} // end early_get_limitations;
/**
* Delete limitations.
*
* @since 2.0.0
*
* @param string $slug The slug of the model.
* @param int $id The id of the meta id.
* @return void
*/
public static function remove_limitations($slug, $id) {
global $wpdb;
$wu_prefix = 'wu_';
/*
* Site apis are already available,
* so no need to use low-level sql calls.
*/
if ($slug === 'site') {
$wu_prefix = '';
$slug = 'blog';
} // end if;
$table_name = "{$wpdb->base_prefix}{$wu_prefix}{$slug}meta";
$sql = $wpdb->prepare("DELETE FROM {$table_name} WHERE meta_key = 'wu_limitations' AND {$wu_prefix}{$slug}_id = %d LIMIT 1", $id); // phpcs:ignore
$wpdb->get_var($sql); // phpcs:ignore
} // end remove_limitations;
/**
* Returns an empty permission set, with modules.
*
* @since 2.0.0
* @return self
*/
static public function get_empty() {
$limitations = new self();
foreach (array_keys(self::repository()) as $module_name) {
$limitations->{$module_name};
} // end foreach;
return $limitations;
} // end get_empty;
/**
* Repository of the limitation modules.
*
* @see wu_register_limit_module()
*
* @since 2.0.0
* @return array
*/
static public function repository() {
$classes = array(
'post_types' => \WP_Ultimo\Limitations\Limit_Post_Types::class,
'plugins' => \WP_Ultimo\Limitations\Limit_Plugins::class,
'sites' => \WP_Ultimo\Limitations\Limit_Sites::class,
'themes' => \WP_Ultimo\Limitations\Limit_Themes::class,
'visits' => \WP_Ultimo\Limitations\Limit_Visits::class,
'disk_space' => \WP_Ultimo\Limitations\Limit_Disk_Space::class,
'users' => \WP_Ultimo\Limitations\Limit_Users::class,
'site_templates' => \WP_Ultimo\Limitations\Limit_Site_Templates::class,
'domain_mapping' => \WP_Ultimo\Limitations\Limit_Domain_Mapping::class,
'customer_user_role' => \WP_Ultimo\Limitations\Limit_Customer_User_Role::class,
);
return apply_filters('wu_limit_classes', $classes);
} // end repository;
} // end class Limitations;