Files
wordpress-theme-aksrt/tests/widget-system/message-admin-harness.php
T
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

168 lines
10 KiB
PHP

<?php
/** WordPress-free message admin render / handler / SQL harness. No actual notifications are sent. */
namespace Jio\Core {
final class DBMigrator {
public static function table($key) { return 'wp_jio_' . $key; }
}
}
namespace Jio\Messages {
final class Message {
public static $sent = [];
public static function get_instance() { return new self(); }
public function send($id, $type, $content, $args) {
self::$sent[] = compact('id', 'type', 'content', 'args');
return true;
}
}
}
namespace {
define('ABSPATH', __DIR__);
define('ARRAY_A', 'ARRAY_A');
$root = dirname(__DIR__, 2);
define('JIO_DIR', $root);
define('JIO_URI', 'https://example.test/theme');
class WP_Error {
public function __construct(public $code, public $message) {}
public function get_error_message() { return $this->message; }
}
class WP_User {
public $ID = 7;
public $display_name = 'Test <user>';
}
class Redirect extends \RuntimeException {}
function __($text, $domain = '') { return $text; }
function esc_html($text) { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); }
function esc_attr($text) { return esc_html($text); }
function esc_html__($text, $domain) { return esc_html($text); }
function esc_attr__($text, $domain) { return esc_attr($text); }
function esc_url($url) { return preg_match('/^javascript:/i', $url) ? '' : esc_attr($url); }
function esc_url_raw($url) { return preg_match('/^javascript:/i', $url) ? '' : $url; }
function sanitize_key($text) { return preg_replace('/[^a-z0-9_\-]/', '', strtolower($text)); }
function sanitize_text_field($text) { return trim(strip_tags($text)); }
function sanitize_textarea_field($text) { return trim(strip_tags($text)); }
function sanitize_user($text) { return $text; }
function wp_unslash($text) { return $text; }
function absint($value) { return abs((int) $value); }
function is_wp_error($value) { return $value instanceof WP_Error; }
function is_email($value) { return filter_var($value, FILTER_VALIDATE_EMAIL); }
function admin_url($path) { return 'https://example.test/wp-admin/' . $path; }
function get_edit_user_link($id) { return admin_url('user-edit.php?user_id=' . $id); }
function get_avatar_url($id, $args) { return 'https://example.test/avatar.png'; }
function get_userdata($id) { return $id > 0 ? new WP_User() : false; }
function get_user_by($key, $value) { return new WP_User(); }
function cache_users($ids) { $GLOBALS['cache_calls'][] = $ids; }
function number_format_i18n($count) { return number_format($count); }
function human_time_diff($time) { return '1 分钟'; }
function checked($actual, $expected, $echo = true) { return $actual === $expected ? ' checked' : ''; }
function selected($actual, $expected, $echo = true) { return $actual === $expected ? ' selected' : ''; }
function wp_nonce_field($action) { echo '<input type="hidden" name="_wpnonce" value="' . esc_attr($action) . '" />'; }
function check_admin_referer($action) { $GLOBALS['nonce_checked'][] = $action; }
function wp_roles() {
return new class {
public $roles = ['subscriber' => [], 'editor' => [], 'author' => [], 'contributor' => []];
public function get_names() { return ['subscriber' => '订阅者', 'editor' => '编辑', 'author' => '作者', 'contributor' => '贡献者']; }
};
}
function translate_user_role($role) { return $role; }
function count_users($strategy) { return ['avail_roles' => ['subscriber' => 3, 'editor' => 501, 'author' => 0, 'contributor' => 500]]; }
function get_users($args) { return array_slice(range(1, $GLOBALS['role_count'] ?? 3), 0, $args['number']); }
function wp_safe_redirect($url) { throw new Redirect($url); }
function add_query_arg($args, $url) { return $url . '?' . http_build_query($args); }
function submit_button($text, $type, $name, $wrap) { echo '<button type="submit">' . esc_html($text) . '</button>'; }
function current_datetime() { return new \DateTimeImmutable($GLOBALS['now'] ?? '2026-10-30 12:00:00', new \DateTimeZone($GLOBALS['zone'] ?? 'Asia/Shanghai')); }
function get_gmt_from_date($local) { return (new \DateTimeImmutable($local, new \DateTimeZone($GLOBALS['zone'] ?? 'Asia/Shanghai')))->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s'); }
function get_date_from_gmt($gmt, $format) { return (new \DateTimeImmutable($gmt, new \DateTimeZone('UTC')))->setTimezone(new \DateTimeZone('Asia/Shanghai'))->format($format); }
function current_time($format, $gmt = false) { return '2026-10-30 04:00:00'; }
class Database {
public $prepared = [];
public $queries = [];
public function prepare($sql, ...$params) {
$params = count($params) === 1 && is_array($params[0]) ? $params[0] : $params;
$this->prepared[] = ['sql' => $sql, 'params' => $params];
return $sql;
}
public function get_var($sql) { return 2; }
public function get_results($sql, $format) { return []; }
public function get_row($sql, $format) { return ['total' => 2, 'unread' => 1, 'today' => 1, 'recipients' => 2]; }
public function get_col($sql) { return ['system', 'asset_balance_increase', 'asset_balance_decrease', 'asset_points_increase', 'asset_points_decrease']; }
public function query($sql) { $this->queries[] = $sql; return 1; }
public function esc_like($text) { return addcslashes($text, '_%\\'); }
}
$wpdb = new Database();
require $root . '/src/Messages/MessageTypes.php';
require $root . '/src/Messages/AdminBroadcast.php';
require $root . '/src/Messages/NotificationAdminQuery.php';
require $root . '/src/Admin/Native/NativeUi.php';
require $root . '/src/Admin/Native/UsersMenusMessagesList.php';
require $root . '/src/Admin/Native/UsersMenusMessagesCompose.php';
require $root . '/src/Admin/Native/UsersMenusMessages.php';
class Probe {
use \Jio\Admin\Native\UsersMenusMessages;
public $cap_checked = 0;
private function ensure_cap(): void { $this->cap_checked++; }
public function invoke($method, ...$args) { return $this->$method(...$args); }
public function html($method, ...$args) {
ob_start(); $this->$method(...$args); return ob_get_clean();
}
}
$probe = new Probe();
$out = [];
$filters = $probe->invoke('message_filters', ['period' => 'today', 'status' => 'unread', 's' => 'a%_']);
$out['filters'] = $filters;
$out['bad_period'] = $probe->invoke('message_filters', ['period' => 'evil']);
$out['query_filters'] = $probe->invoke('message_query_filters', $filters);
$out['compose'] = $probe->html('render_message_compose');
$out['dialogs'] = $probe->html('render_message_dialogs');
$out['overview'] = $probe->html('render_message_overview');
$out['filter_form'] = $probe->html('render_message_filter_form', $filters);
$out['views'] = $probe->invoke('message_views_html', $filters, ['all' => 2, 'unread' => 1, 'read' => 1]);
$row = ['id' => 8, 'user_id' => 7, 'type' => 'system', 'title' => '<script>alert(1)</script>', 'description' => str_repeat('完整内容', 100) . "\n<script>x</script>", 'target_url' => 'javascript:alert(1)', 'is_read' => 0, 'created_at' => '2026-10-30 03:00:00', 'read_at' => null];
$out['table'] = $probe->html('render_message_table', [$row, array_merge($row, ['id' => 9, 'is_read' => 1, 'read_at' => '2026-10-30 03:30:00'])], $filters, 3, false);
$out['empty'] = $probe->html('render_message_table', [], $filters, 1, false);
$out['cache_calls'] = $GLOBALS['cache_calls'];
$out['asset_table'] = $probe->html('render_message_table', [array_merge($row, ['type' => 'asset_balance_increase'])], $filters, 1, false);
foreach (['asset_balance_increase', 'asset_balance_decrease', 'asset_points_increase', 'asset_points_decrease'] as $type) {
$out['asset_types'][$type] = [\Jio\Messages\MessageTypes::label($type), \Jio\Messages\MessageTypes::tone($type)];
}
$wpdb->prepared = [];
\Jio\Messages\NotificationAdminQuery::paginate($out['query_filters'], 1);
\Jio\Messages\NotificationAdminQuery::status_counts($out['query_filters']);
\Jio\Messages\NotificationAdminQuery::overview();
$out['sql'] = $wpdb->prepared;
$GLOBALS['zone'] = 'America/New_York';
$GLOBALS['now'] = '2026-11-01 12:00:00';
$wpdb->prepared = [];
\Jio\Messages\NotificationAdminQuery::overview();
$out['dst_sql'] = $wpdb->prepared;
$base = ['recipient_mode' => 'role', 'role' => 'subscriber', 'content' => 'Hello', 'title' => 'Title'];
$cases = [
'unconfirmed' => $base,
'changed_count' => $base + ['confirmed_role' => 'subscriber', 'confirmed_count' => 2],
'changed_role' => $base + ['confirmed_role' => 'editor', 'confirmed_count' => 3],
'confirmed' => $base + ['confirmed_role' => 'subscriber', 'confirmed_count' => 3],
'too_large' => $base + ['confirmed_role' => 'subscriber', 'confirmed_count' => 501],
'users' => ['recipient_mode' => 'users', 'users' => '7', 'content' => 'Hello'],
'long_title' => array_merge($base, ['title' => str_repeat('字', 192)]),
];
foreach ($cases as $name => $post) {
$_POST = $post;
$GLOBALS['role_count'] = $name === 'too_large' ? 501 : 3;
\Jio\Messages\Message::$sent = [];
try { $probe->handle_send_message(); } catch (Redirect $redirect) {
parse_str(parse_url($redirect->getMessage(), PHP_URL_QUERY), $query);
$out['send'][$name] = ['redirect' => $query, 'sent' => count(\Jio\Messages\Message::$sent)];
}
}
$_POST = ['bulk_action' => 'delete', 'message_id' => 8, 'ids' => [9, 10], 'period' => 'today', 'status' => 'unread', 'paged' => 3];
$wpdb->prepared = [];
try { $probe->handle_bulk_messages(); } catch (Redirect $redirect) {
parse_str(parse_url($redirect->getMessage(), PHP_URL_QUERY), $query);
$out['single_delete'] = ['redirect' => $query, 'sql' => $wpdb->prepared];
}
$out['cap_checked'] = $probe->cap_checked;
$out['nonce_checked'] = $GLOBALS['nonce_checked'];
echo json_encode($out, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
}