Files
kate522andClaude Opus 5 35bb96e82f refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.

Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
  cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
  window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)

Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
  so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
  (dropped 3 stale entries, added the 17 missing ones)

Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
  get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
  when it is empty

Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.

Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-01 20:05:04 +08:00

387 lines
19 KiB
PHP

<?php
/**
* Exercise the native ban queue, detail templates, and post handlers with WordPress fixtures.
*
* @package Jio
*/
namespace Jio\Core {
final class DBMigrator {
public static function table(string $name): string { return 'wp_jio_' . $name; }
}
}
namespace Jio\User {
final class Restriction {
public const TYPE_WARN = 'warn';
public const TYPE_MUTE = 'mute';
public const TYPE_SUSPEND = 'suspend';
public const TYPE_BAN = 'ban';
public const SCOPE_COMMENT = 'comment';
public static int $lifted = 0;
public static function get_instance(): self { return new self(); }
public function lift_user_restriction(int $user_id): int { self::$lifted++; return $user_id > 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 = '_jio_ban_appeal_status';
public const META_PAYLOAD = '_jio_ban_appeal_payload';
public const META_HISTORY = '_jio_ban_appeal_history';
public static function get_status(int $user_id): string {
return (string) ($GLOBALS['jio_test_user_meta'][$user_id][self::META_STATUS] ?? '');
}
public static function get_payload(int $user_id): array {
$value = $GLOBALS['jio_test_user_meta'][$user_id][self::META_PAYLOAD] ?? [];
return is_array($value) ? $value : [];
}
}
}
namespace Jio\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['jio_test_reports'][$id]['status'] = $status;
$GLOBALS['jio_test_reports'][$id]['updated_at'] = '2026-10-30 13:00:00';
$GLOBALS['jio_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['jio_test_messages'][] = [$user_id, $type, $message, $args];
}
}
}
namespace Jio\Admin\Native {
final class AdminListPagination {
public static function render(string $slug, int $total, int $page, int $per_page, array $filters = [], string $parent = ''): void {
$GLOBALS['jio_test_pagination'][] = [$slug, $total, $page, $per_page, $filters, $parent];
echo '<nav data-test-pagination="' . esc_attr((string) $total) . ':' . esc_attr((string) $page) . '"></nav>';
}
}
}
namespace {
define('ABSPATH', dirname(__DIR__, 2) . DIRECTORY_SEPARATOR);
define('JIO_DIR', dirname(__DIR__, 2) . DIRECTORY_SEPARATOR);
define('JIO_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['jio_test_reports'][$id]['notes_json'] ?? '[]',
], $this->args);
}
if (str_contains($query, 'jio_user_restriction')) {
return $GLOBALS['jio_test_restrictions'];
}
$reports = array_values($GLOBALS['jio_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['jio_test_reports'][$id] ?? null;
}
}
$GLOBALS['wpdb'] = new TestWpdb();
$GLOBALS['jio_test_reports'] = [];
$GLOBALS['jio_test_user_meta'] = [];
$GLOBALS['jio_test_restrictions'] = [[
'user_id' => 7,
'type' => 'ban',
'scope' => 'site',
'reason' => 'fixture restriction',
'created_at' => '2026-10-01 08:00:00',
'expires_at' => null,
]];
$GLOBALS['jio_test_assets'] = ['styles' => [], 'scripts' => []];
$GLOBALS['jio_test_pagination'] = [];
$GLOBALS['jio_test_report_actions'] = [];
$GLOBALS['jio_test_messages'] = [];
$GLOBALS['jio_test_nonce_checks'] = [];
$GLOBALS['jio_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['jio_test_capability']; }
function wp_die($message): void { throw new \RuntimeException((string) $message); }
function wp_nonce_field(string $action): void { echo '<input type="hidden" name="_wpnonce" value="' . esc_attr($action . '-nonce') . '">'; }
function check_admin_referer(string $action): void {
$GLOBALS['jio_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['jio_test_assets']['styles'][] = [$handle, $src, $deps, $version];
}
function wp_enqueue_script(string $handle, string $src, array $deps = [], string $version = '', bool $footer = false): void {
$GLOBALS['jio_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 <script>alert(1)</script>' : '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['jio_test_user_meta'][$user_id][$key] ?? ''; }
function update_user_meta(int $user_id, string $key, $value): bool { $GLOBALS['jio_test_user_meta'][$user_id][$key] = $value; return true; }
function delete_user_meta(int $user_id, string $key): bool { unset($GLOBALS['jio_test_user_meta'][$user_id][$key]); return true; }
function get_users(array $args = []): array {
$ids = array_keys($GLOBALS['jio_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 => \Jio\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 ? '<img src=x onerror=alert(1)>' : '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 jio_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 <script>alert(1)</script> 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['jio_test_reports'][$id] = fixture_report($id, $id === 1 ? 7 : 20 + $id, $status, $time, $reason);
}
$GLOBALS['jio_test_reports'][1]['category'] = '版权投诉';
$GLOBALS['jio_test_reports'][2]['category'] = '人身攻击';
$GLOBALS['jio_test_reports'][2]['reason'] = '';
$GLOBALS['jio_test_reports'][9001] = fixture_report(9001, 7, 'pending', '2026-09-01 09:00:00', 'needle action report');
$GLOBALS['jio_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['jio_test_user_meta'][$user_id][\Jio\User\BanAppeal::META_STATUS] = $status;
$GLOBALS['jio_test_user_meta'][$user_id][\Jio\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 <svg onload=alert(1)>',
'reason' => 'Appeal note <script>alert(2)</script> https://appeal.example.test/file.pdf javascript:alert(3)',
'data' => [
'Name <img src=x onerror=alert(4)>' => 'Applicant <b>value</b>',
'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 JIO_DIR . 'src/Admin/Native/NativeUi.php';
require_once JIO_DIR . 'src/Admin/Native/UsersMenusBanQuery.php';
require_once JIO_DIR . 'src/Admin/Native/UsersMenusBanDialogs.php';
require_once JIO_DIR . 'src/Admin/Native/UsersMenusBanActions.php';
require_once JIO_DIR . 'src/Admin/Native/UsersMenusBan.php';
final class BanFixtureMenu {
use \Jio\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' => 'jio_user_ban', 'status' => '', 'orderby' => 'time', 'order' => 'DESC']);
$filteredHtml = render_ban_page([
'page' => 'jio_user_ban', 'type' => 'report', 'status' => 'pending', 's' => 'needle',
'send_user' => '7', 'orderby' => 'updated', 'order' => 'ASC', 'paged' => '1',
]);
$GLOBALS['jio_test_assets'] = ['styles' => [], 'scripts' => []];
$assetMenu = new BanFixtureMenu();
$assetMenu->enqueue_ban_assets('users_page_jio_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', 'jio_users_update_report_9001', array_merge($reportBase, ['new_status' => 'dismissed', 'note' => ' ']));
$staleReport = call_action($menu, 'handle_update_report', 'jio_users_update_report_9002', array_merge($reportBase, ['report_id' => '9002', 'record_version' => 'stale-version']));
$validReport = call_action($menu, 'handle_update_report', 'jio_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', 'jio_users_process_appeal_7001', array_merge($appealBase, ['note' => ' ']));
$validAppeal = call_action($menu, 'handle_process_appeal', 'jio_users_process_appeal_7001', $appealBase);
$approveRow = $menu->query_record('appeal', 7002);
$approve = call_action($menu, 'handle_process_appeal', 'jio_users_process_appeal_7002', [
'user_id' => '7002', 'decision' => 'approve', 'note' => '', 'record_version' => $menu->version($approveRow),
]);
$filtersHtml = render_ban_page(['page' => 'jio_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['jio_test_assets'],
'actions' => [
'blank_report' => $blankReport, 'stale_report' => $staleReport, 'valid_report' => $validReport,
'blank_appeal' => $blankAppeal, 'valid_appeal' => $validAppeal, 'approve_appeal' => $approve,
'report_calls' => $GLOBALS['jio_test_report_actions'], 'messages' => $GLOBALS['jio_test_messages'],
'appeal_status' => \Jio\User\BanAppeal::get_status(7001),
'appeal_payload' => \Jio\User\BanAppeal::get_payload(7001),
'approved_status' => \Jio\User\BanAppeal::get_status(7002),
'restrictions_lifted' => \Jio\User\Restriction::$lifted,
],
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
}