3.2.53 后台小工具页恢复传统拖放面板(#23)

现象:用户在后台「外观 → 小工具」找不到可以拖动挂件的地方,看到的全是区块。

根因:WP 5.8 起后台小工具页默认走区块编辑器
(wp-admin/widgets-form-blocks.php),它没有「从左栏拖到右栏」这个动作,
只能点「+」再搜名字。本主题此前从未关掉它。

改动:
1. 关闭区块小工具编辑器,两道开关互为备份
   - add_filter('use_widgets_block_editor', '__return_false')
   - remove_theme_support('widgets-block-editor'),挂 after_setup_theme:20
     (核心 wp_setup_widgets_block_editor 挂在 priority 1 默认 add,
       所以 remove 必须晚于它,否则静默失败)
2. 4 个挂件(ad/about/tags/posts)构造函数的名字改为显式传第 2 参

关于第 2 点的性质说明(重要,避免误读成"修了缺陷"):
原先写 parent::__construct(false, false, $widget_ops) 时,
$widget->name 确实是 false,但后台列表/搜索读的**不是**它,而是
$wp_registered_widgets[...]['name']。后者经
wp_register_sidebar_widget() 里的 array_merge($widget, $options)
被 $widget_ops['name'] 覆盖成真名字。
实测(老代码):$widget->name = false,
$wp_registered_widgets['ad-1']['name'] = '图片广告'。
所以挂件名字从来没空过,用户找不到挂件与名字无关,纯粹是区块编辑器没有拖放。
第 2 点属于可读性清理(不再依赖隐式覆盖),不是用户可见缺陷的修复。

id_base 全部保持原值(ad/about/tags/posts),已有挂件实例不会丢。

验证:
- 真实 WP 7.1.3 加载待发布包,页面级 28 条 + 元数据 50 条判据全绿
- 老代码跑同一套判据:仅区块开关相关失败,名字类判据全部通过(印证上述判断)
- 9 组反证全部真红(含「只去掉一道开关仍应全绿」的冗余性验证)
- 96 个 PHP 文件全量 lint 通过
This commit is contained in:
2026-10-27 09:21:02 +08:00
parent baa1d16fd1
commit 75848f2ba2
2 changed files with 103 additions and 10 deletions
+102 -9
View File
@@ -35,6 +35,52 @@ function widgets_init()
}
add_action('widgets_init', 'widgets_init');
/* ==========================================================================
* 后台「外观 → 小工具」用传统拖放面板,不用区块编辑器(3.2.53 / 工单 #23)
*
* ── 为什么要关 ──────────────────────────────────────────────────────
* WP 5.8 起把后台小工具页默认换成了区块编辑器(`wp-admin/widgets-form-blocks.php`)。
* 它**没有"从左边拖到右边"这个动作**,只能点「+」再搜索挂件名,操作方式
* 与传统面板差异很大。用户的实际反馈就是「找不到可以拖动的地方」。
*
* 那挂件名字有没有问题?实测没有 —— 4 个挂件虽然写的是
* `parent::__construct(false, false, $widget_ops)`(`$widget->name` 确实是
* false),但名字放在 `$widget_ops['name']` 里,经
* `wp_register_sidebar_widget()` 的 `array_merge($widget, $options)`
* 覆盖后,`$wp_registered_widgets[...]['name']` 一直是正常中文名。
* 实测结论(老代码):`$widget->name` = false,
* `$wp_registered_widgets['ad-1']['name']` = '图片广告'。
* **所以"找不到挂件"与名字无关,只是区块编辑器没有拖放。**
* (这一度被我误判成名字为空,记一次教训:见 tests 里的说明。)
*
* ── 为什么这个钩子名是 `use_widgets_block_editor` ───────────────────
* WP 7.1.3 的 `wp_use_widgets_block_editor()` 只认这一个 filter:
*
* return apply_filters('use_widgets_block_editor',
* get_theme_support('widgets-block-editor'));
*
* 网上常见的 `gutenberg_use_widgets_block_editor` / `widgets_block_editor_disabled`
* 在 7.1.3 里**一处都不存在**(`grep -rn` 查过 wp-admin/ 与 wp-includes/ 全无命中),
* 写了也是白写。这里按实际源码来。
*
* ── 顺带把主题声明也去掉 ────────────────────────────────────────────
* 只挂 filter 也能生效,但 `get_theme_support('widgets-block-editor')` 仍返回 true,
* 语义上"主题声称支持区块小工具"却又不让用,容易误导后来人。
* 所以在 after_setup_theme 里 `remove_theme_support` —— 必须挂在这个钩子上:
* 主题的 `add_theme_support` 与 `functions.php` 的 require 同在 after_setup_theme
* 早期跑,直接写 `remove_theme_support` 会因为"还没 add"而静默失败。
*
* 🔴 如果哪天决定改用区块界面,把这两段一起删掉即可 —— 但**必须先确认
* 4 个挂件的名字都补上了**,否则会退回"找不到挂件"的老问题。
* ====================================================================== */
add_filter('use_widgets_block_editor', '__return_false');
function yang_drop_widgets_block_editor_support()
{
remove_theme_support('widgets-block-editor');
}
add_action('after_setup_theme', 'yang_drop_widgets_block_editor_support', 20);
/* ==========================================================================
* 后台挂件表单的公共样式与脚本(3.2.51)
*
@@ -439,12 +485,47 @@ class widget_ad extends WP_Widget
{
public function __construct()
{
/* 名字与 id_base 都走显式实参(2026-10-27,工单 #23)。
*
* ── 原来写法为什么"看着像 bug 但实际没坏" ────────────────────
* 原来写的是 `parent::__construct(false, false, $widget_ops)`,
* 把名字放在 `$widget_ops['name']` 里。两条链路分开看:
*
* ① `$widget->name`:确实等于 false。
* `WP_Widget::__construct` 只有 `$this->name = $name;`,
* 而 `wp_parse_args` 的默认值只补 classname 与
* customize_selective_refresh,**不补 name**。
*
* ② 但后台列表/搜索用的**不是** `$widget->name`,而是
* `$wp_registered_widgets[<id>]['name']`。它是这么来的:
* `_register_one()` → `wp_register_sidebar_widget($id, $this->name, ..., $this->widget_options)`
* → 里面先 `$widget = array('name' => $name, ...)`
* → 紧接着 `$widget = array_merge($widget, $options)` ← 关键
* `$options` 就是 `$this->widget_options`,其中的 `'name'`
* **把前面的 false 覆盖掉了**。所以注册表里名字一直是好的。
*
* 实测(老代码):`$widget->name` = false,但
* `$wp_registered_widgets['ad-1']['name']` = '图片广告' —— 名字没丢。
* 也就是说「用户找不到挂件」的真实原因是后台走了区块编辑器、
* 没有拖放面板,与本构造函数的实参无关(开关见文件上方)。
*
* ── 那为什么还是改了 ────────────────────────────────────────
* ① 名字配在 `$widget_ops` 里是靠 array_merge **顺带**覆盖生效的,
* 属于隐式行为;显式走第 2 参是 WP 文档写法,读代码的人不用
* 再去追 array_merge 才敢确认名字有没有生效。
* ② `$this->name` 在整个 WP 里有多处直接读(例如区块编辑器的
* 挂件类型接口),值是 false 时行为取决于调用方是否兜底 ——
* 这次侥幸没事,不代表以后没事。
*
* 第 1 参显式写 'ad':$id_base 决定 option 名(widget_ad)。
* 库里已经有用户的实例,**id_base 一旦变了配置就全丢**。
* 原来传 false 时 WP 靠类名推导出 'ad'(不是 widget_ad,它会把
* wp_/widget_ 前缀剥掉),效果一样 —— 写出来是为了不依赖推导。 */
$widget_ops = array(
'name' => __('图片广告', 'kratos'),
'description' => __('显示自定义图片广告的工具', 'kratos'),
);
parent::__construct(false, false, $widget_ops);
parent::__construct('ad', __('图片广告', 'kratos'), $widget_ops);
}
public function widget($args, $instance)
@@ -518,12 +599,16 @@ class widget_about extends WP_Widget
{
public function __construct()
{
/* 名字走第 2 参(工单 #23,完整理由与实测见 widget_ad 的注释):
* 原来把名字塞在 `$widget_ops['name']` 里,靠
* `wp_register_sidebar_widget()` 里 `array_merge($widget, $options)`
* 顺带覆盖才生效 —— 能用,但是隐式的。显式传更清楚。
* 第 1 参显式写 'about',保住 option 名 widget_about —— 线上已有实例。 */
$widget_ops = array(
'name' => __('个人简介', 'kratos'),
'description' => __('可跳转后台的个人简介展示工具', 'kratos'),
);
parent::__construct(false, false, $widget_ops);
parent::__construct('about', __('个人简介', 'kratos'), $widget_ops);
}
/**
@@ -788,12 +873,13 @@ class widget_tags extends WP_Widget
{
public function __construct()
{
/* 名字走第 2 参(工单 #23,完整理由与实测见 widget_ad 的注释)。
* 第 1 参写 'tags',保住 option 名 widget_tags。 */
$widget_ops = array(
'name' => __('标签聚合', 'kratos'),
'description' => __('文章标签的展示工具', 'kratos'),
);
parent::__construct(false, false, $widget_ops);
parent::__construct('tags', __('标签聚合', 'kratos'), $widget_ops);
}
public function widget($args, $instance)
@@ -865,12 +951,13 @@ class widget_posts extends WP_Widget
{
public function __construct()
{
/* 名字走第 2 参(工单 #23,完整理由与实测见 widget_ad 的注释)。
* 第 1 参写 'posts',保住 option 名 widget_posts —— 线上已有实例。 */
$widget_ops = array(
'name' => __('文章聚合', 'kratos'),
'description' => __('展示最热、随机、最新文章的工具', 'kratos'),
);
parent::__construct(false, false, $widget_ops);
parent::__construct('posts', __('文章聚合', 'kratos'), $widget_ops);
}
public function widget($args, $instance)
@@ -1009,8 +1096,14 @@ class widget_recent_comments extends WP_Widget
{
public function __construct()
{
/* 名字只走第 2 参(工单 #23,理由见 widget_ad 的注释)。
*
* 3.2.52 这里曾同时写了两处:`$widget_ops` 里带 'name',第 2 参也传名字。
* 两处一致时不会出问题,但配两处会让"名字到底在哪配"产生歧义。
* 统一成第 2 参:这是 WP 文档写法,也不用依赖
* `wp_register_sidebar_widget()` 里 `array_merge($widget, $options)`
* 那层覆盖 —— 覆盖确实管用,但读代码的人得追进去才知道。 */
$widget_ops = array(
'name' => __('最近评论', 'kratos'),
'description' => __('带头像与摘要的最近评论,替代 Comments Widget Plus 插件', 'kratos'),
);
+1 -1
View File
@@ -1,7 +1,7 @@
/*
Theme Name: Kratos_Yang
Text Domain: kratos_Yang
Version: 3.2.52
Version: 3.2.53
Requires PHP: 7.0
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好