0 ? 1 : 0; } public function get_user_restrictions(int $user_id, bool $active = false): array { unset($user_id, $active); return []; } } final class BanAppeal { public const META_STATUS = '_aksrt_ban_appeal_status'; public const META_PAYLOAD = '_aksrt_ban_appeal_payload'; public const META_HISTORY = '_aksrt_ban_appeal_history'; public static function get_status(int $user_id): string { return (string) ($GLOBALS['aksrt_test_user_meta'][$user_id][self::META_STATUS] ?? ''); } public static function get_payload(int $user_id): array { $value = $GLOBALS['aksrt_test_user_meta'][$user_id][self::META_PAYLOAD] ?? []; return is_array($value) ? $value : []; } } } namespace AKSRT\Messages { final class ReportCenter { private static ?self $instance = null; public static function get_instance(): self { return self::$instance ??= new self(); } public function update_status(int $id, string $status, string $note): bool { $GLOBALS['aksrt_test_reports'][$id]['status'] = $status; $GLOBALS['aksrt_test_reports'][$id]['updated_at'] = '2026-10-30 13:00:00'; $GLOBALS['aksrt_test_report_actions'][] = [$id, $status, $note]; return true; } } final class Message { private static ?self $instance = null; public static function get_instance(): self { return self::$instance ??= new self(); } public function send(int $user_id, string $type, string $message, array $args = []): void { $GLOBALS['aksrt_test_messages'][] = [$user_id, $type, $message, $args]; } } } namespace AKSRT\Admin\Native { final class AdminListPagination { public static function render(string $slug, int $total, int $page, int $per_page, array $filters = [], string $parent = ''): void { $GLOBALS['aksrt_test_pagination'][] = [$slug, $total, $page, $per_page, $filters, $parent]; echo ''; } } } namespace { define('ABSPATH', dirname(__DIR__, 2) . DIRECTORY_SEPARATOR); define('AKSRT_DIR', dirname(__DIR__, 2) . DIRECTORY_SEPARATOR); define('AKSRT_URI', 'https://theme.example.test'); define('ARRAY_A', 'ARRAY_A'); class WP_User { public function __construct(public int $ID, public string $display_name) {} } final class TestRedirect extends \RuntimeException { public function __construct(public string $page, public string $redirectMessage, public array $args) { parent::__construct($redirectMessage); } } final class TestWpdb { public string $prefix = 'wp_'; public string $users = 'wp_users'; public string $last_error = ''; private array $args = []; public function prepare(string $query, $args = []): string { $this->args = is_array($args) ? $args : [$args]; return $query; } public function esc_like(string $text): string { return addcslashes($text, '_%\\'); } public function get_results(string $query, $output = null): array { if (str_contains($query, 'notes_json')) { return array_map(static fn (int $id): array => [ 'id' => $id, 'notes_json' => $GLOBALS['aksrt_test_reports'][$id]['notes_json'] ?? '[]', ], $this->args); } if (str_contains($query, 'aksrt_user_restriction')) { return $GLOBALS['aksrt_test_restrictions']; } $reports = array_values($GLOBALS['aksrt_test_reports']); $search = ''; $send_user = 0; foreach ($this->args as $arg) { if (is_string($arg) && str_starts_with($arg, '%') && str_ends_with($arg, '%')) $search = trim($arg, '%'); if (is_int($arg) && $arg > 0) $send_user = $arg; } return array_values(array_filter($reports, static function (array $row) use ($search, $send_user): bool { if ($send_user > 0 && (int) $row['reporter_id'] !== $send_user) return false; if ($search !== '' && stripos(implode(' ', [$row['reason'], $row['category'], $row['target_type'], $row['target_id']]), $search) === false) return false; return true; })); } public function get_row(string $query, $output = null) { $id = (int) ($this->args[0] ?? 0); return $GLOBALS['aksrt_test_reports'][$id] ?? null; } } $GLOBALS['wpdb'] = new TestWpdb(); $GLOBALS['aksrt_test_reports'] = []; $GLOBALS['aksrt_test_user_meta'] = []; $GLOBALS['aksrt_test_restrictions'] = [[ 'user_id' => 7, 'type' => 'ban', 'scope' => 'site', 'reason' => 'fixture restriction', 'created_at' => '2026-10-01 08:00:00', 'expires_at' => null, ]]; $GLOBALS['aksrt_test_assets'] = ['styles' => [], 'scripts' => []]; $GLOBALS['aksrt_test_pagination'] = []; $GLOBALS['aksrt_test_report_actions'] = []; $GLOBALS['aksrt_test_messages'] = []; $GLOBALS['aksrt_test_nonce_checks'] = []; $GLOBALS['aksrt_test_capability'] = true; function __($text, $domain = null): string { return (string) $text; } function esc_html__($text, $domain = null): string { return esc_html((string) $text); } function esc_attr__($text, $domain = null): string { return esc_attr((string) $text); } function esc_html($value): string { return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } function esc_attr($value): string { return esc_html((string) $value); } function esc_url($url, array $protocols = ['http', 'https']): string { $url = trim((string) $url); if (preg_match('/^([a-z][a-z0-9+.-]*):/i', $url, $match) && !in_array(strtolower($match[1]), $protocols, true)) return ''; return esc_attr($url); } function sanitize_key($value): string { return preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)) ?? ''; } function sanitize_text_field($value): string { return trim(preg_replace('/[\r\n\t]+/', ' ', strip_tags((string) $value)) ?? ''); } function sanitize_textarea_field($value): string { return trim(strip_tags((string) $value)); } function wp_unslash($value) { return $value; } function wp_slash($value) { return $value; } function absint($value): int { return abs((int) $value); } function admin_url(string $path = ''): string { return 'https://example.test/wp-admin/' . ltrim($path, '/'); } function add_query_arg($key, $value = null, $url = ''): string { if (is_array($key)) { $args = $key; $base = (string) $value; } else { $args = [(string) $key => $value]; $base = (string) $url; } $parts = parse_url($base); $query = []; if (!empty($parts['query'])) parse_str($parts['query'], $query); $query = array_merge($query, $args); $prefix = isset($parts['scheme']) ? $parts['scheme'] . '://' . ($parts['host'] ?? '') : ''; $prefix .= $parts['path'] ?? ''; return $prefix . '?' . http_build_query($query); } function current_user_can(string $capability): bool { return $capability === 'manage_options' && $GLOBALS['aksrt_test_capability']; } function wp_die($message): void { throw new \RuntimeException((string) $message); } function wp_nonce_field(string $action): void { echo ''; } function check_admin_referer(string $action): void { $GLOBALS['aksrt_test_nonce_checks'][] = $action; if (($_POST['_wpnonce'] ?? '') !== $action . '-nonce') throw new \RuntimeException('nonce mismatch'); } function wp_enqueue_style(string $handle, string $src, array $deps = [], string $version = ''): void { $GLOBALS['aksrt_test_assets']['styles'][] = [$handle, $src, $deps, $version]; } function wp_enqueue_script(string $handle, string $src, array $deps = [], string $version = '', bool $footer = false): void { $GLOBALS['aksrt_test_assets']['scripts'][] = [$handle, $src, $deps, $version, $footer]; } function get_userdata(int $user_id) { return $user_id > 0 ? new WP_User($user_id, $user_id === 7 ? 'Report ' : 'User ' . $user_id) : false; } function get_current_user_id(): int { return 999; } function get_user_meta(int $user_id, string $key, bool $single = false) { return $GLOBALS['aksrt_test_user_meta'][$user_id][$key] ?? ''; } function update_user_meta(int $user_id, string $key, $value): bool { $GLOBALS['aksrt_test_user_meta'][$user_id][$key] = $value; return true; } function delete_user_meta(int $user_id, string $key): bool { unset($GLOBALS['aksrt_test_user_meta'][$user_id][$key]); return true; } function get_users(array $args = []): array { $ids = array_keys($GLOBALS['aksrt_test_user_meta']); if (isset($args['include'])) $ids = array_values(array_intersect($ids, $args['include'])); $ids = array_values(array_filter($ids, static fn (int $id): bool => \AKSRT\User\BanAppeal::get_status($id) !== '')); return $args['fields'] === 'ID' ? $ids : array_map('get_userdata', $ids); } function update_meta_cache(string $type, array $ids): void { unset($type, $ids); } function cache_users(array $ids): void { unset($ids); } function _prime_post_caches(array $ids, bool $update_term_cache = true, bool $update_meta_cache = true): void { unset($ids, $update_term_cache, $update_meta_cache); } function _prime_comment_caches(array $ids, bool $update_meta_cache = true): void { unset($ids, $update_meta_cache); } function get_date_from_gmt(string $date, string $format): string { return $date; } function current_time(string $type, bool $gmt = false): string { return '2026-10-30 12:00:00'; } function number_format_i18n($number): string { return number_format((float) $number); } function get_avatar_url(int $user_id, array $args = []): string { return 'https://example.test/avatar/' . $user_id . '.png'; } function get_edit_user_link(int $user_id): string { return admin_url('user-edit.php?user_id=' . $user_id); } function get_post(int $id) { return $id === 44 ? (object) ['ID' => $id] : false; } function get_the_title(int $id): string { return $id === 44 ? '' : 'Post ' . $id; } function get_permalink(int $id): string { return 'https://content.example.test/post/' . $id; } function get_comment(int $id) { return false; } function get_comment_link(int $id): string { return 'https://content.example.test/comment/' . $id; } function get_author_posts_url(int $user_id): string { return 'https://content.example.test/author/' . $user_id; } function aksrt_get_user_home_url(int $user_id): string { return 'https://content.example.test/user/' . $user_id; } function do_action(string $hook, ...$args): void { unset($hook, $args); } function fixture_report(int $id, int $reporter, string $status, string $time, string $reason = ''): array { return [ 'id' => $id, 'reporter_id' => $reporter, 'target_type' => 'post', 'target_id' => 44, 'reason' => $reason !== '' ? $reason : 'report reason ' . $id, 'category' => '垃圾信息', 'status' => $status, 'created_at' => $time, 'updated_at' => $time, 'notes_json' => '[]', ]; } for ($id = 1; $id <= 110; $id++) { $status = $id === 2 ? 'resolved' : ($id === 3 ? 'dismissed' : ($id === 4 ? 'reviewing' : 'pending')); $time = gmdate('Y-m-d H:i:s', strtotime('2026-10-01 00:00:00 UTC') + ($id * 60)); $reason = $id === 1 ? 'Evidence https://evidence.example.test/proof.jpg javascript:alert(2)' : 'report reason ' . $id; $time = $id === 1 ? '2026-10-30 12:00:00' : ($id === 2 ? '2026-10-29 11:00:00' : $time); $GLOBALS['aksrt_test_reports'][$id] = fixture_report($id, $id === 1 ? 7 : 20 + $id, $status, $time, $reason); } $GLOBALS['aksrt_test_reports'][1]['category'] = '版权投诉'; $GLOBALS['aksrt_test_reports'][2]['category'] = '人身攻击'; $GLOBALS['aksrt_test_reports'][2]['reason'] = ''; $GLOBALS['aksrt_test_reports'][9001] = fixture_report(9001, 7, 'pending', '2026-09-01 09:00:00', 'needle action report'); $GLOBALS['aksrt_test_reports'][9002] = fixture_report(9002, 7, 'pending', '2026-09-02 09:00:00', 'needle stale report'); function fixture_appeal(int $user_id, string $status, string $time, array $payload = []): void { $GLOBALS['aksrt_test_user_meta'][$user_id][\AKSRT\User\BanAppeal::META_STATUS] = $status; $GLOBALS['aksrt_test_user_meta'][$user_id][\AKSRT\User\BanAppeal::META_PAYLOAD] = array_merge([ 'time' => $time, 'reason_key' => 'ban reason', 'reason' => 'appeal explanation', 'data' => [], ], $payload); } fixture_appeal(501, 'pending', '2026-10-29 12:00:00', [ 'reason_key' => 'reason ', 'reason' => 'Appeal note https://appeal.example.test/file.pdf javascript:alert(3)', 'data' => [ 'Name ' => 'Applicant value', 'Evidence' => 'https://appeal.example.test/document.pdf', 'Ignored array' => ['nested'], ], ]); fixture_appeal(502, 'approved', '2026-10-20 09:00:00', ['handled_at' => '2026-10-21 09:00:00', 'handled_by' => 999, 'decision' => 'approved']); fixture_appeal(503, 'rejected', '2026-10-19 09:00:00', ['handled_at' => '2026-10-20 09:00:00', 'handled_by' => 999, 'decision' => 'rejected']); for ($user_id = 600; $user_id < 615; $user_id++) fixture_appeal($user_id, 'pending', '2026-10-02 ' . sprintf('%02d', $user_id - 600) . ':00:00'); fixture_appeal(7001, 'pending', '2026-09-03 09:00:00', ['reason' => 'pending appeal action']); fixture_appeal(7002, 'pending', '2026-09-04 09:00:00', ['reason' => 'pending appeal approve']); require_once AKSRT_DIR . 'src/Admin/Native/NativeUi.php'; require_once AKSRT_DIR . 'src/Admin/Native/UsersMenusBanQuery.php'; require_once AKSRT_DIR . 'src/Admin/Native/UsersMenusBanDialogs.php'; require_once AKSRT_DIR . 'src/Admin/Native/UsersMenusBanActions.php'; require_once AKSRT_DIR . 'src/Admin/Native/UsersMenusBan.php'; final class BanFixtureMenu { use \AKSRT\Admin\Native\UsersMenusBan; public const CAPABILITY = 'manage_options'; public function ensure_cap(): void {} public function flash(): void {} public function redirect(string $page, string $message, array $args = []): void { throw new TestRedirect($page, $message, $args); } public function record(string $kind, int $id): ?array { return $this->find_ban_request($kind, $id); } public function filters(array $input): array { return $this->get_ban_filters($input); } public function version(array $row): string { return $this->ban_record_version($row); } public function query(array $filters, int $page): array { return $this->query_ban_requests($this->get_ban_filters($filters), $page); } public function query_record(string $kind, int $id): ?array { $filters = $this->get_ban_filters(['type' => $kind, 'status' => '']); $firstPage = $this->query_ban_requests($filters, 1); $pages = max(1, (int) ceil($firstPage['total'] / 20)); for ($page = 1; $page <= $pages; $page++) { $result = $page === 1 ? $firstPage : $this->query_ban_requests($filters, $page); foreach ($result['items'] as $row) if ($row['kind'] === $kind && (int) $row['id'] === $id) return $row; } return null; } } function render_ban_page(array $get): string { $_GET = $get; ob_start(); (new BanFixtureMenu())->render_ban_page(); return (string) ob_get_clean(); } function call_action(BanFixtureMenu $menu, string $method, string $nonce_action, array $post): array { $_POST = array_merge($post, ['_wpnonce' => $nonce_action . '-nonce']); try { $menu->{$method}(); return ['redirected' => false]; } catch (TestRedirect $redirect) { return ['redirected' => true, 'page' => $redirect->page, 'message' => $redirect->redirectMessage, 'args' => $redirect->args]; } } $queueMenu = new BanFixtureMenu(); $all = $queueMenu->query(['status' => ''], 2); $overlargePage = $queueMenu->query(['status' => ''], 99); $filters = $queueMenu->filters([ 'type' => 'report', 'status' => '', 's' => 'needle', 'send_user' => '7', 'orderby' => 'updated', 'order' => 'ASC', ]); $html = render_ban_page(['page' => 'aksrt_user_ban', 'status' => '', 'orderby' => 'time', 'order' => 'DESC']); $filteredHtml = render_ban_page([ 'page' => 'aksrt_user_ban', 'type' => 'report', 'status' => 'pending', 's' => 'needle', 'send_user' => '7', 'orderby' => 'updated', 'order' => 'ASC', 'paged' => '1', ]); $GLOBALS['aksrt_test_assets'] = ['styles' => [], 'scripts' => []]; $assetMenu = new BanFixtureMenu(); $assetMenu->enqueue_ban_assets('users_page_aksrt_user_ban'); $assetMenu->enqueue_ban_assets('users_page_other'); $menu = new BanFixtureMenu(); $reportQueryRow = $menu->query_record('report', 9001); $reportVersion = $menu->version($reportQueryRow); $reportBase = [ 'report_id' => '9001', 'new_status' => 'resolved', 'note' => 'Reviewed', 'record_version' => $reportVersion, 'filter_type' => 'report', 'filter_status' => 'pending', 's' => 'needle', 'send_user' => '7', 'orderby' => 'updated', 'order' => 'ASC', 'paged' => '2', ]; $blankReport = call_action($menu, 'handle_update_report', 'aksrt_users_update_report_9001', array_merge($reportBase, ['new_status' => 'dismissed', 'note' => ' '])); $staleReport = call_action($menu, 'handle_update_report', 'aksrt_users_update_report_9002', array_merge($reportBase, ['report_id' => '9002', 'record_version' => 'stale-version'])); $validReport = call_action($menu, 'handle_update_report', 'aksrt_users_update_report_9001', $reportBase); $appealRow = $menu->query_record('appeal', 7001); $appealVersion = $menu->version($appealRow); $appealBase = [ 'user_id' => '7001', 'decision' => 'reject', 'note' => ' Missing evidence ', 'record_version' => $appealVersion, 'filter_type' => 'appeal', 'filter_status' => 'pending', 's' => 'needle', 'send_user' => '7001', 'orderby' => 'updated', 'order' => 'ASC', 'paged' => '3', ]; $blankAppeal = call_action($menu, 'handle_process_appeal', 'aksrt_users_process_appeal_7001', array_merge($appealBase, ['note' => ' '])); $validAppeal = call_action($menu, 'handle_process_appeal', 'aksrt_users_process_appeal_7001', $appealBase); $approveRow = $menu->query_record('appeal', 7002); $approve = call_action($menu, 'handle_process_appeal', 'aksrt_users_process_appeal_7002', [ 'user_id' => '7002', 'decision' => 'approve', 'note' => '', 'record_version' => $menu->version($approveRow), ]); $filtersHtml = render_ban_page(['page' => 'aksrt_user_ban', 'type' => 'report', 'status' => 'pending', 's' => 'needle', 'send_user' => '7', 'orderby' => 'updated', 'order' => 'ASC']); echo json_encode([ 'queue' => [ 'total' => $all['total'], 'page' => $all['page'], 'counts' => $all['counts'], 'items' => count($all['items']), 'overlarge_page' => $overlargePage['page'], 'overlarge_items' => count($overlargePage['items']), ], 'filters' => $filters, 'html' => $html, 'filtered_html' => $filteredHtml, 'assets' => $GLOBALS['aksrt_test_assets'], 'actions' => [ 'blank_report' => $blankReport, 'stale_report' => $staleReport, 'valid_report' => $validReport, 'blank_appeal' => $blankAppeal, 'valid_appeal' => $validAppeal, 'approve_appeal' => $approve, 'report_calls' => $GLOBALS['aksrt_test_report_actions'], 'messages' => $GLOBALS['aksrt_test_messages'], 'appeal_status' => \AKSRT\User\BanAppeal::get_status(7001), 'appeal_payload' => \AKSRT\User\BanAppeal::get_payload(7001), 'approved_status' => \AKSRT\User\BanAppeal::get_status(7002), 'restrictions_lifted' => \AKSRT\User\Restriction::$lifted, ], ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); }