- Rebuild users.php?page=aksrt_messages: overview stats, collapsible compose card (users list / role recipients), read-state views, type/recipient/keyword filters, bulk mark-read/delete, shared AdminListPagination - Extract NotificationAdminQuery / AdminBroadcast / MessageTypes under src/Messages; page logic lives in UsersMenusMessages trait - Page-scoped assets/css/admin/messages.css - Remove dead aksrt_notification_type_label() and Message::get_all_notifications() Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
56 lines
2.8 KiB
JavaScript
56 lines
2.8 KiB
JavaScript
'use strict';
|
||
|
||
/**
|
||
* 消息管理后台(users.php → aksrt_messages)契约:
|
||
* 1) 页面由独立 trait 承载,UsersMenus 只做组合与路由注册;
|
||
* 2) 写操作走 admin_post + nonce + 能力校验;
|
||
* 3) SQL 全部在 NotificationAdminQuery 内且经 prepare;
|
||
* 4) 分页用共享 AdminListPagination;
|
||
* 5) 旧实现与死代码已删除。
|
||
*/
|
||
|
||
const assert = require('node:assert/strict');
|
||
const fs = require('node:fs');
|
||
const path = require('node:path');
|
||
const test = require('node:test');
|
||
|
||
const root = path.resolve(__dirname, '../..');
|
||
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
||
|
||
test('messages page is composed from its own trait', () => {
|
||
const menus = read('src/Admin/Native/UsersMenus.php');
|
||
assert.ok(menus.includes('use UsersMenusMessages;'));
|
||
assert.ok(menus.includes("'aksrt_messages'"));
|
||
assert.ok(menus.includes("add_action('admin_post_aksrt_users_send_message', [$this, 'handle_send_message'])"));
|
||
assert.ok(menus.includes("add_action('admin_post_aksrt_users_bulk_messages', [$this, 'handle_bulk_messages'])"));
|
||
assert.ok(menus.includes("add_action('admin_enqueue_scripts', [$this, 'enqueue_message_assets'])"));
|
||
// 旧实现不得残留在壳类中。
|
||
assert.doesNotMatch(menus, /function render_messages_page|function handle_send_message|message_sent|message_failed/);
|
||
assert.doesNotMatch(menus, /aksrt_notification/);
|
||
});
|
||
|
||
test('write handlers check capability and nonce', () => {
|
||
const page = read('src/Admin/Native/UsersMenusMessages.php');
|
||
assert.match(page, /function handle_send_message\(\): void \{\s*\$this->ensure_cap\(\);\s*check_admin_referer\('aksrt_users_send_message'\);/);
|
||
assert.match(page, /function handle_bulk_messages\(\): void \{\s*\$this->ensure_cap\(\);\s*check_admin_referer\('aksrt_users_bulk_messages'\);/);
|
||
assert.ok(page.includes("wp_nonce_field('aksrt_users_send_message')"));
|
||
assert.ok(page.includes("wp_nonce_field('aksrt_users_bulk_messages')"));
|
||
// 页面层不直接碰数据库,分页走共享组件。
|
||
assert.doesNotMatch(page, /\$wpdb/);
|
||
assert.ok(page.includes('AdminListPagination::render('));
|
||
});
|
||
|
||
test('admin query keeps SQL prepared and bulk size capped', () => {
|
||
const query = read('src/Messages/NotificationAdminQuery.php');
|
||
assert.match(query, /DELETE FROM \{\$table\} WHERE id IN \(\{\$in\}\)", \$ids\)/);
|
||
assert.ok(query.includes("array_fill(0, count($ids), '%d')"));
|
||
assert.ok(query.includes('array_slice($ids, 0, self::MAX_BULK)'));
|
||
assert.ok(query.includes('$wpdb->esc_like($s)'));
|
||
});
|
||
|
||
test('dead notification helpers are removed', () => {
|
||
assert.doesNotMatch(read('inc/modules/user-notifications.php'), /aksrt_notification_type_label/);
|
||
assert.doesNotMatch(read('src/Messages/Message.php'), /get_all_notifications/);
|
||
assert.ok(fs.existsSync(path.join(root, 'assets/css/admin/messages.css')));
|
||
});
|