fix(reports): separate readable categories from report content
Store readable category snapshots separately from details and evidence, validate report categories, and preserve context in admin displays and moderation actions. Widen report categories to TEXT and bump the schema to 3.2.0 without legacy data conversion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b2811cc248
commit
b45fd161f0
16 files changed
+797
-75
No files matched your search
@@ -54,6 +54,14 @@ test('report REST surface is canonical: submit + users/reports only', () => {
|
||||
assert.ok(!reportOps.includes('public function get_report_counts'), 'dead get_report_counts method')
|
||||
})
|
||||
|
||||
test('report submit validates required category text and optional content before sanitizing', () => {
|
||||
const start = userController.indexOf("'/report/submit'")
|
||||
const route = userController.slice(start, userController.indexOf(']);', start) + 3)
|
||||
assert.match(route, /'category'\s*=>\s*\[\s*'type' => 'string', 'required' => true/)
|
||||
assert.match(route, /'reason'\s*=>\s*\[\s*'type' => 'string', 'default' => ''/)
|
||||
assert.equal((route.match(/'validate_callback' => 'rest_validate_request_arg'/g) || []).length, 2)
|
||||
})
|
||||
|
||||
test('report center has no legacy ajax handlers or option migration', () => {
|
||||
assert.ok(!reportCenter.includes('ajax_update_status'))
|
||||
assert.ok(!reportCenter.includes('ajax_list'))
|
||||
@@ -124,7 +132,6 @@ test('ban/report settings page mirrors the reference theme schema', () => {
|
||||
assert.match(schemaJs, /添加原因/)
|
||||
assert.match(schemaJs, /添加申诉资料/)
|
||||
|
||||
// 运行时读取兼容 group 数组 [{t}] 并保留旧格式回退
|
||||
const runtime = read('src/Settings/UserRuntime.php')
|
||||
assert.match(runtime, /ban_settings\(\): array/)
|
||||
assert.match(runtime, /report_settings\(\): array/)
|
||||
@@ -132,10 +139,8 @@ test('ban/report settings page mirrors the reference theme schema', () => {
|
||||
// 缺键回退 schema 默认值(参考主题 _pz 语义):运行时经 read_domain 读取,
|
||||
// 裸 get_all() 会让未保存过分区的站点拿不到 ban_preset_reason 默认值。
|
||||
assert.match(runtime, /read_domain\('users', 'report'\)/)
|
||||
// 预置原因 slug 不得由 sanitize_key(标签) 直接派生:中文标签会被清洗成
|
||||
// 空串,前端按 slug 非空过滤后整张原因列表会被丢弃。ASCII 键优先,
|
||||
// 否则用标签内容的稳定 md5 短哈希。
|
||||
assert.match(runtime, /md5\(\$text\)/)
|
||||
assert.match(runtime, /\$reasons\[\] = \['name' => \$text\]/)
|
||||
assert.doesNotMatch(runtime, /reason_slug|md5\(\$text\)/)
|
||||
// 死链清理:user_report_categories 从不是真实存储键,解析器必须删除。
|
||||
assert.ok(!runtime.includes('parse_report_categories'), 'dead parse_report_categories must stay removed')
|
||||
assert.ok(!runtime.includes('user_report_categories'), 'dead user_report_categories key must stay removed')
|
||||
@@ -404,7 +409,11 @@ test('report modal uses accessible reasons, automatic target association and opt
|
||||
}
|
||||
assert.doesNotMatch(reportJs, /report_url|违规链接|data-aksrt-report-reason=|bindAutoHeight/)
|
||||
for (const source of [reportJs, commentItem, authorHero]) assert.doesNotMatch(source, /target_avatar/)
|
||||
assert.match(reportJs, /description\) reason \+= '\\n详细描述:\\n' \+ description/)
|
||||
assert.match(reportJs, /description\) content\.push\(description\)/)
|
||||
assert.doesNotMatch(reportJs, /category\.slug|reason = category/)
|
||||
assert.doesNotMatch(reportOps, /sanitize_key\(\(string\) (?:\$request->get_param\('category'\)|\$row\['category'\])/)
|
||||
assert.match(reportCenter, /'category'\s*=>\s*\$category/)
|
||||
assert.doesNotMatch(reportCenter, /sanitize_key\(\$category\)/)
|
||||
assert.match(reportJs, /report\/upload-image/)
|
||||
assert.match(reportJs, /window\.AksrtModalRuntime\.getActive\(\) === shell/)
|
||||
assert.match(userController, /'\/report\/upload-image'/)
|
||||
|
||||
Reference in new issue
Block a user