fix(admin): 修复快捷编辑组描述被 esc_html 转义成可见标签

「付费参数」组的说明文字 `<div class="c-yellow">…</div>` 整段被显示
成了纯文本标签。原因:fields_html() 对 $desc 做了 esc_html(),而该
参数传入的是一段自带 <div> 的 HTML,转义后标签就变成了可见字符。

改为按参考主题的做法原样输出 $desc,责任前移到调用方:
- pay 组:esc_html__() 转义文字,外层 div 由模板给出;
- filter 组:同样自带 <div class="px12 aksrt-bulk-edit-desc"> 包裹,
  以保住原有样式(此前样式靠 fields_html 里的外层 div 提供)。
字段标题仍保持 esc_html(),因为 filter 那组的标题来自设置项。

新增契约断言锁住这个契约:不得再 esc_html($desc),两个调用点必须
各自转义/包裹;同时保留「字段标题仍转义」的断言。

验证:node --test tests/widget-system/*.test.cjs 445 项全部通过
(原有 444 项 + 新增 1 项);php -l 无语法错误。
This commit is contained in:
2026-10-06 19:35:56 +08:00
parent 27868b6131
commit c8fdd81d6a
2 changed files with 17 additions and 2 deletions
+6 -2
View File
@@ -347,12 +347,15 @@ class PostListEnhance {
return '';
}
return $this->fields_html($fields, 'filter', __('高级筛选', 'aksrt'), __('让文章实现更加精细化的分类,同时方便用户进行文章筛选', 'aksrt'));
return $this->fields_html($fields, 'filter', __('高级筛选', 'aksrt'), '<div class="px12 aksrt-bulk-edit-desc">' . esc_html__('让文章实现更加精细化的分类,同时方便用户进行文章筛选', 'aksrt') . '</div>');
}
/**
* Render one field group: the shared builder for every quick-edit box.
*
* $desc 是调用方预先转义好的 HTML 片段,这里原样输出(付费参数那组
* 自带 <div class="c-yellow">,转义会把标签显示成文字)。
*
* @param array<int,array<string,mixed>> $fields
*/
private function fields_html(array $fields, string $id, string $title = '', string $desc = ''): string {
@@ -430,8 +433,9 @@ class PostListEnhance {
return '';
}
// $desc 由调用方传入并保证安全(均为代码内常量),故原样输出:
// 付费参数那组自带 <div class="c-yellow">,转义会让标签直接显示成文字。
$legend = $title !== '' ? '<legend class="inline-edit-legend">' . esc_html($title) . '</legend>' : '';
$desc = $desc !== '' ? '<div class="px12 aksrt-bulk-edit-desc">' . esc_html($desc) . '</div>' : '';
return '<fieldset style="margin-top: 20px">' . $legend . '<div class="flex hh">' . $item_html . '</div>' . $desc . '</fieldset>';
}
@@ -56,6 +56,17 @@ test('quick-edit markup is emitted without kses stripping the controls', () => {
assert.match(list, /<fieldset/)
assert.match(list, /edit_boxes_html\(\)/)
})
test('field-group descriptions are emitted as pre-escaped HTML', () => {
// 组描述由调用方预先转义后整体传入(付费参数那组自带 <div class="c-yellow">),
// 渲染处若再 esc_html,标签会直接显示成文字。
assert.doesNotMatch(list, /esc_html\(\$desc\)/)
// 两个调用点都必须自带转义/HTML 包裹,不能传裸翻译串。
assert.match(list, /'<div class="c-yellow">' \. esc_html__\('批量设置付费参数/)
assert.match(list, /'<div class="px12 aksrt-bulk-edit-desc">' \. esc_html__\('让文章实现更加精细化/)
// 仍来自设置项的字段标题必须保持转义。
assert.match(list, /esc_html\(\$title_html\)/)
})
test('number fields support the full operation set', () => {
for (const op of ['ignore', 'set', 'plus', 'subtract', 'multiply', 'division']) {
assert.match(list, new RegExp(`value="${op}"`), `${op} operation exists`)