From 27868b6131e7bd8d8ca496e55f761bacc1072601 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 6 Oct 2026 19:19:08 +0800 Subject: [PATCH] =?UTF-8?q?fix(admin):=20=E4=BF=AE=E5=A4=8D=E9=94=80?= =?UTF-8?q?=E5=94=AE=E6=95=B0=E6=8D=AE=E6=8E=92=E5=BA=8F=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E9=94=99=E4=BD=8D=E4=B8=8E=E5=BF=AB=E6=8D=B7=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=8E=A7=E4=BB=B6=E8=A2=AB=E5=89=A5=E6=88=90=E6=96=87=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1 排序图标跑到「销售数据」文字前面: 原因是注册了 manage_edit-post_sortable_columns。注册后 WordPress 会给 该列套上自己的排序指示器与 ,而列头 label 里已经自带一个 , 浏览器解析嵌套 时把内层内容抛出,箭头因而显示到文字之前。 参考主题不注册该钩子,靠 label 链接 + pre_get_posts 落地排序,现改为 同一做法,同时删掉因此变成死代码的钩子与方法。 P2 快捷/批量编辑新增的配置项不可用,只显示为纯文本: render_edit_boxes() 用 wp_kses_post() 包裹面板输出。该函数按「文章 内容」白名单过滤,select/option/input/fieldset/legend/label 均不在 白名单内,标签被剥掉只剩文字,于是所有控件退化成纯文本。改为原样 输出,字段值在生成时已逐个 esc_attr/esc_html 转义。 同步更新两份契约测试:sales 列表头排序改为断言「不注册 sortable」, 并为快捷编辑新增一条防回归断言,锁住不得再用 wp_kses_post 包裹面板。 验证:node --test tests/widget-system/*.test.cjs 444 项全部通过 (原有 443 项 + 新增 1 项);php -l 无语法错误。 --- src/Admin/Native/PostListEnhance.php | 18 +++--------------- .../admin-stats-column-contract.test.cjs | 9 ++++++--- .../post-list-quick-edit-contract.test.cjs | 12 ++++++++++++ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/Admin/Native/PostListEnhance.php b/src/Admin/Native/PostListEnhance.php index ca27ec36..9f3fc707 100644 --- a/src/Admin/Native/PostListEnhance.php +++ b/src/Admin/Native/PostListEnhance.php @@ -38,7 +38,6 @@ class PostListEnhance { public function init(): void { add_filter('manage_post_posts_columns', [$this, 'register_columns']); add_action('manage_post_posts_custom_column', [$this, 'render_column'], 10, 2); - add_filter('manage_edit-post_sortable_columns', [$this, 'register_sortable_columns']); add_action('restrict_manage_posts', [$this, 'render_type_filter']); add_action('pre_get_posts', [$this, 'apply_query_args'], 99); @@ -108,19 +107,6 @@ class PostListEnhance { return $new_columns; } - /** - * @param array $columns - * @return array - */ - public function register_sortable_columns($columns) { - if (!is_array($columns)) { - return $columns; - } - $columns[self::COLUMN_STATS] = 'views'; - $columns[self::COLUMN_SALES] = 'sales_volume'; - return $columns; - } - /** * @param string $column * @param int $post_id @@ -305,7 +291,9 @@ class PostListEnhance { return; } - echo wp_kses_post($this->edit_boxes_html()); + // 表单控件(select/input/fieldset)不在 wp_kses_post 的文章白名单里, + // 会被剥成纯文本,因此这里直接输出;字段值均已 esc_attr/esc_html 转义。 + echo $this->edit_boxes_html(); } private function edit_boxes_html(): string { diff --git a/tests/widget-system/admin-stats-column-contract.test.cjs b/tests/widget-system/admin-stats-column-contract.test.cjs index 36c6388a..6c4b4c90 100644 --- a/tests/widget-system/admin-stats-column-contract.test.cjs +++ b/tests/widget-system/admin-stats-column-contract.test.cjs @@ -24,9 +24,12 @@ test('admin post list has the sales data column with a sortable header', () => { assert.match(list, /'aksrt_sales'/) assert.match(list, /orderby.*sales_volume/) - // 排序列必须在表头注册,并把 sales_volume 映射回真实存储键。 - assert.match(list, /manage_edit-post_sortable_columns/) - assert.match(list, /sales_volume/) + // 排序靠表头 label 里的链接 + pre_get_posts 落地,不注册 sortable_columns: + // 注册后 WordPress 会套自己的排序指示器与 ,与 label 里的 嵌套, + // 箭头会被解析到文字前面。 + assert.doesNotMatch(list, /manage_edit-post_sortable_columns/) + assert.match(list, /pre_get_posts/) + assert.match(list, /'sales_volume'/) assert.match(list, /RESOURCE_SALES_VOLUME_KEY/) }) diff --git a/tests/widget-system/post-list-quick-edit-contract.test.cjs b/tests/widget-system/post-list-quick-edit-contract.test.cjs index ac9fcd98..d2fd2545 100644 --- a/tests/widget-system/post-list-quick-edit-contract.test.cjs +++ b/tests/widget-system/post-list-quick-edit-contract.test.cjs @@ -44,6 +44,18 @@ test('quick-edit inputs are namespaced under aksrt_bulk_edit', () => { assert.match(list, /\[operation\]/) assert.match(list, /\[val\]/) }) + +test('quick-edit markup is emitted without kses stripping the controls', () => { + // select/option/input/fieldset/legend 不在 wp_kses_post 的文章白名单里, + // 一旦用它包上面板,所有控件会被剥成纯文本(表现为「新增的操作都不可用」)。 + // 字段值均已在生成时 esc_attr/esc_html 转义,这里必须原样输出。 + assert.doesNotMatch(list, /wp_kses_post\(\$this->edit_boxes_html\(\)\)/) + // 面板必须真的产生表单控件,而不是纯文本。 + assert.match(list, /