把钩子收成插件:从 plugin.xml 到点启用
上一篇结尾我说要把那三处代码收成插件。真动手时,第一版我是直接粘进主题的 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 那一行。大多数「启用了但没反应」,就是这三处有一处不一样。