把钩子收成插件:从 plugin.xml 到点启用

· zblog · 阅读 39
摘要

上一篇结尾我说要把那三处代码收成插件。真动手时,第一版我是直接粘进主题的 include.php 里的。第二天换样式试效果,主题文件一覆盖,目录、相关文章、面包屑全没了。代码还在,只是不在插件里,主题一动它就跟着走。 所以这篇不讲新钩子。把

上一篇结尾我说要把那三处代码收成插件。真动手时,第一版我是直接粘进主题的 include.php 里的。第二天换样式试效果,主题文件一覆盖,目录、相关文章、面包屑全没了。代码还在,只是不在插件里,主题一动它就跟着走。

所以这篇不讲新钩子。把上一篇的「文末相关文章」收成一个能装、能关、关了页面就恢复原样的小插件。我给它起名 RelatedBox,目录也必须叫这个。

后台认的不是函数,是三个名字

Z-Blog 扫的是 zb_users/plugin/ 下面的目录。目录名、plugin.xml 里的 id、include.php 里 RegisterPlugin 的第一个参数,这三处必须是同一串字。有一处不一样,表现通常不是报错,而是插件列表里没有它,或者显示已启用、钩子却一次都不进。

zb_users/plugin/RelatedBox/
├── plugin.xml
├── include.php
└── main.php    (这篇没有后台页,可以先不建)

id 写成 relatedbox、目录写成 RelatedBox,在 Windows 上经常能混过去。丢到 Linux 上,插件直接从列表里消失。我就是这么丢过一次,所以现在这三处我都复制同一串,不手打。

plugin.xml 真正要紧的就几行

这个文件看着字段很多。决定能不能被认出来的,是 id、name、include。其余先空着也能装上。

<?xml version="1.0" encoding="utf-8"?>
<plugin version="php">
  <id>RelatedBox</id>
  <name>文末相关文章</name>
  <url></url>
  <note>按同分类取几篇,挂到正文后面</note>
  <include>include.php</include>
  <level>1</level>
  <author><name>me</name></author>
  <version>1.0</version>
  <adapted>173000</adapted>
</plugin>

include.php 只做两件事

第一件是告诉系统:启用我的时候调用哪个函数。第二件是在那个函数里挂钩子。相关文章挂在文章页模板上,这时候文章对象已经在模板里,改的是这一次输出的 Content,不会写回数据库。插件一关,原文还是原文。

<?php
RegisterPlugin('RelatedBox', 'ActivePlugin_RelatedBox');

function ActivePlugin_RelatedBox() {
    Add_Filter_Plugin('Filter_Plugin_ViewPost_Template', 'RelatedBox_ViewPost');
}

function RelatedBox_ViewPost(&$template) {
    global $zbp;
    $article = $template->GetTags('article');
    if (!$article || (int)$article->ID === 0) {
        return;
    }
    $html = RelatedBox_Build($article);
    if ($html !== '') {
        $article->Content .= $html;
    }
}

function RelatedBox_Build($article) {
    global $zbp;
    $cateId = (int)$article->Category->ID;
    $cates = array($cateId);
    foreach ($zbp->categorys as $c) {
        if ((int)$c->ParentID === $cateId) {
            $cates[] = (int)$c->ID;
        }
    }
    $w = array();
    $w[] = array('=', 'log_Status', 0);
    $w[] = array('<>', 'log_ID', (int)$article->ID);
    $w[] = array('IN', 'log_CateID', $cates);
    $list = $zbp->GetArticleList('*', $w, array('log_PostTime' => 'DESC'), 5, null);
    if (!$list) {
        return '';
    }
    $html = '<div class="related-box"><h3>同分类里这几篇也看过</h3><ul>';
    foreach ($list as $p) {
        $html .= '<li><a href="' . $p->Url . '">' . $p->Title . '</a></li>';
    }
    return $html . '</ul></div>';
}

function InstallPlugin_RelatedBox() {}
function UninstallPlugin_RelatedBox() {}

这里用 GetArticleList,不是 GetPostList。后者会把页面也取出来,文末列表里混进「关于」那种页面,我第一版就是这样,盯着标题看了半天,还以为分类没设对。

子分类那几行是故意留下的。首页「技术片段」空过一次,就是父分类查询没把子分类算进去。插件如果再犯同一个错,就白收了。

启用别去改配置文件

后台插件管理里点启用就行。不要手改启用名单那个配置,下次保存设置会盖回去,改坏了后台可能直接进不去。

启用后先打开一篇文章,文末应该多出那个列表。再禁用,刷新,列表消失,正文一个字都不多。能这样来回开关,才算收成插件,不是又焊死在主题里。

我在这一步踩过的三个坑

函数名少了一个字母。目录和 id 都是 RelatedBox,启用函数写成了 ActivePlugin_Relatedbox。插件显示已启用,文章页却没变化,也没有致命错误。对着列表看了十分钟,最后是大小写。所以三个名字我现在只复制,不重打。

函数名太好记。构建函数我一度叫 BuildRelated()。主题里若也有同名函数,启用的瞬间就是致命错误,整站白屏,禁用入口都打不开。插件里的函数全部带前缀,RelatedBox_Build 这种,丑,但不会和别人撞。

少写 &。写成 function RelatedBox_ViewPost($template) 不会报错。你改的是一份拷贝,页面上什么都不变。这个钩子进来时,模板已经装满了,要改的是原来那份。

这篇故意没做的

条数写死成 5,标题写死成「同分类里这几篇也看过」。能跑,但改一个字就得改代码。下一篇给它加后台页,用插件自己的 Config 存条数和标题。关插件时配置还在,不用每次重填。

如果你手上已经有半成品,把这三行原文发我就行:目录名、plugin.xml 里的 id、RegisterPlugin 那一行。大多数「启用了但没反应」,就是这三处有一处不一样。