WordPress 插件 TOC+(Table of Contents Plus) 不支持多路径生成目录的 bug 修复

TOC+ 插件

Table of Contents Plus (TOC+) 是一款非常流行且功能强大的 WordPress 目录生成插件。它能自动扫描文章中的标题标签(如 H1, H2, H3),并根据标题层级生成一个结构清晰、带有锚点链接的目录。
 
插件的后台可设置选项也很丰富,除了基础的插入位置,插入条件,是否收缩显示,主题背景字体颜色等简单设置之外,高级设置部分提供了更多方便的可定制化选项,包括:标题级别 (Heading levels),排除标题 (Exclude headings),路径限制 (Restrict path),平滑滚动 (Smooth scroll),锚点链接格式 (Lowercase / Hyphenate),CSS控制 (Exclude CSS) 等。
 
然而最近发现,在一些我的生活类文章中,插件仍然会生成标题目录,这并不是我所希望的。在后台的路径限制设置处按照官方规定配置我希望生成目录的路径时,发现并不能成功进行限制,故决定自己修复这个bug。
修复完毕后想向作者提交更改时发现,作者在 1 October 2024 便决定不再维护这个项目了,代码也进行了锁定,有点遗憾。便 fork 了一份在自己的仓库,保存了修复 bug 之后的代码。
也希望对生成页面路径进行限制的可以去我的仓库直接复制新的文件到自己的wordpress就好了。
仓库地址:lichenrobo/table-of-contents-plus
 
如何在自己的 wordpress 更改插件代码可以参考这个笔记:LaTeX 错误渲染了 PHP 代码,wordpress插件解决方案

Bug 记录

在插件后台的高级设置区,官方提供了一个功能:
路径限制(Restrict Path)

  • Restrict automatic generation of the table of contents to pages that match the required path. This path is from the root of your site and always begins with a forward slash.
    例:/wiki/,/corporate/annual-reports/

从官方的说明来看,这个功能可以实现

  • 将目录的自动生成功能,限制在与指定路径匹配的页面上。此路径为从您的网站根目录开始的绝对路径,且必须以正斜杠(/)开头。
    示例: /wiki/,/corporate/annual-reports/

所以当我在设置框中像图中这样填写并应用设置的时候,理应在这三个路径下生成目录,其余路径跳过。

 
然而如此之后所有路径都被跳过了,没有一个页面生成的目录;有趣的是如果在此处只填写一个路径,例如 /slam/,则可以只在此路径下生成目录。
结合错误情况来看,应该是插件并不能识别多路径的设置,可能将这个字符串当做一个路径拿去做匹配了,这才导致了多路径配置会完全匹配不到,而单路径设置则可以正常匹配生成。多路径的配置规则说明形同虚设。
 
经过查阅插件源代码,很快把 bug 定位在文件 includes/class-toc-plus.php 里的函数 public function is_eligible()
在函数里有一段相关代码如下:

### includes/class-toc-plus.php ###

public function is_eligible(){
...
if ( $this->options['restrict_path'] ) {
        if ( strpos( $_SERVER['REQUEST_URI'], $this->options['restrict_path'] ) === 0 ) {
                return true;
        } else {
                return false;
        }
} else {
        return true;
...
}

可见插件根本就是把设置字符串当做单一路径处理了,和我们的预想一致。
下面只需将 if ( $this->options['restrict_path'] ) 判断的逻辑修改为支持多路径字符串即可。

代码修复

文件 includes/class-toc-plus.php 其余代码不变,只需将 if ( $this->options['restrict_path'] ) 判断部分的代码修改为:

### includes/class-toc-plus.php ###

public function is_eligible(){
...
if ( $this->options['restrict_path'] ) {

        // Split the 'restrict_path' setting and try to match the current url. 
        $restricted_paths_string = $this->options['restrict_path'];
        $current_uri = $_SERVER['REQUEST_URI'];
        $is_path_matched = false;

        // 1. Split the 'restrict_path' to support multi-path-setting, and ignore the space.
        // preg_split is a good tool to split string.
                    // support ',' ',' '|' to be the split symbol
        $paths_to_check = preg_split( '/[,,|]/', $restricted_paths_string, -1, PREG_SPLIT_NO_EMPTY );

        if ( ! empty( $paths_to_check ) ) {
            // 2. search all restrict paths with current url
            foreach ( $paths_to_check as $path ) {
                $trimmed_path = trim( $path );
                if ( ! empty( $trimmed_path ) && strpos( $current_uri, $trimmed_path ) === 0 ) {
                    // 3. once matched, means need to build TOC on this page and break the loop
                    $is_path_matched = true;
                    break; 
                }
            }
        } else {
            $is_path_matched = false;
        }

        return $is_path_matched;

    } else {
        return true;
    }
} else {
    return false;
}
...
}

代码修改涉及多层的判断逻辑,需要小心分别修改哪一部分,也可以直接去仓库拉取整个 includes/class-toc-plus.php 文件覆盖 wordpress 的插件文件。
仓库地址:lichenrobo/table-of-contents-plus
 
修复后,插件原设置位置将支持多路径的限制生成,并且支持 , | 三种分割符,甚至按照下面这种配置方式都可以成功进行 Multi-Path-Restrict 了~