如何开发Discuz插件(三):上下篇日志链接
美中网的博客有个很好的功能:在每篇博文内容的后面有该博主前一篇博文和后一篇博文的链接,方便读者在读完该篇后继续阅读该博主的其它博文。这里我们来考虑下如何用插件来实现这个功能。
在前文中我们介绍了开发和设置插件的基本步骤和规范(链接),更完整和权威的介绍见Discuz!插件开发手册。 我们这里要做的是一个包含页面嵌入模块的插件。首先我们看一下日志页上Discuz提供的嵌入点(又名钩子)。
一个适合安放上下篇链接的地方是嵌入点space_blog_title。我们要按前文所述的步骤在管理中心设置和启用新插件,这里不再赘述。假定在那里我们指定新插件的唯一标识符叫imyoona_pre_next而且它包括了一个名叫pre_next的页面嵌入模块,则我们需要建立一个新的文件夹source/plugin/imyoona_pre_next,其中加一个页面嵌入模块文件pre_next.class.php:
fetch($id);
if ($blog && $blog['uid']==$uid) {
$pre = DB::fetch_first("SELECT b.* FROM %t b WHERE b.uid=%d AND b.friend=0 AND b.status=0 AND b.dateline>%d ORDER BY b.dateline", array(DB::table('home_blog'), $blog['uid'], $blog['dateline']));
if(!empty($pre)) {
$spre = lang('plugin/imyoona_pre_next', 'pre').': < a href="home.php?mod=space&uid=$uid&do=blog&id='.$pre['blogid'].'" >'.$pre['subject'].'';
}
$next = DB::fetch_first("SELECT b.* FROM %t b WHERE b.uid=%d AND b.friend=0 AND b.status=0 AND b.dateline<%d ORDER BY b.dateline DESC", array(DB::table('home_blog'), $blog['uid'], $blog['dateline']));
if(!empty($next)) {
$snext = lang('plugin/imyoona_pre_next', 'next').': < a href="home.php?mod=space&uid=$uid&do=blog&id='.$next['blogid'].'" >'.$next['subject'].'';
}
}
}
return $spre."
".$snext;
}
}
?>
注意在上面的SQL语句中,friend=0表示这个日志是全站用户可见,而status=0表示这个日志不在审核之中。还有我们要检查$blog是否为空,只有当它非空时才执行后面的语句。我们需要这个条件是因为当日志被删除可能还能在界面上看到,点击它就会执行$blog = C::t('home_blog')->fetch($id); 并返回空值,再执行后面的语句就出错了。
注意上面的SQL语句带有形式参数的格式化处理,它的安全性比下面旧式的SQL语句强,是Discuz官方推荐的办法,见Discuz技术文库里的介绍(
链接)
$pre = DB::fetch_first("SELECT b.* FROM ".DB::table('home_blog')." b WHERE b.uid=".$blog['uid']." AND b.friend=0 AND b.status=0 AND b.dateline>".$blog['dateline']." ORDER BY b.dateline");
我们还需要一个语言包文件 data/plugindata/imyoona_pre_next.lang.php (
此文件仅供插件设计者模式时使用):
'上一篇',
'next' => '下一篇',
);
?>
这样我们就完成了上一篇下一篇链接这个功能。下面是效果图:
下面我们讨论三点改进,一是模板,二是数据层,三是自定义嵌入点。
象pre_next.class.php那样通过PHP代码来输出HTML代码并不是个好的做法,当输出的HTML更复杂时这种PHP代码里混杂HTML代码的做法会使得PHP代码很凌乱。有趣的是Discuz官方给出的插件开发实例:马甲插件 http://faq.comsenz.com/library/plug/plugin/plugin_example.htm 在这方面是个坏的榜样。更好的办法是将HTML代码写在一个单独的模板文件里。添加模板文件source/plugin/imyoona_pre_next/template/pn.htm:
{lang imyoona_pre_next:pre}: < a href="home.php?mod=space&uid=$uid&do=blog&id=$pre['blogid']"> {$pre['subject']}
{lang imyoona_pre_next:next}: < a href="home.php?mod=space&uid=$uid&do=blog&id=$next['blogid']"> {$next['subject']}
这样我们的页面嵌入模块文件里的嵌入点函数就可以简化成:
function space_blog_title() {
$id = intval($_GET['id']);
$uid = intval($_GET['uid']);
if($id && $uid){
$blog = C::t('home_blog')->fetch($id);
if ($blog && $blog['uid']==$uid) {
$pre = DB::fetch_first("SELECT b.* FROM %t b WHERE b.uid=%d AND b.friend=0 AND b.status=0 AND b.dateline>%d ORDER BY b.dateline", array(DB::table('home_blog'), $blog['uid'], $blog['dateline']));
$next = DB::fetch_first("SELECT b.* FROM %t b WHERE b.uid=%d AND b.friend=0 AND b.status=0 AND b.dateline<%d ORDER BY b.dateline DESC", array(DB::table('home_blog'), $blog['uid'], $blog['dateline']));
}
}
include template('imyoona_pre_next:pn');
return $spn;
}
同时我们还要修改语言包文件如下:
'上一篇',
'next' => '下一篇',
);
?>
我们的应用层模块里含有SQL语句,这不是个好的做法。应该将它们放入数据层。 添加数据表文件source/plugin/imyoona_pre_next/table/table_home_blog_pn.php:
fetch($blogid);
if (!$blog) return null;
return DB::fetch_first("SELECT b.* FROM %t b WHERE b.uid=%d AND b.friend=0 AND b.status=0 AND b.dateline>%d ORDER BY b.dateline", array($this->_table, $blog['uid'], $blog['dateline']));
}
public function fetch_next($blogid) {
if (!$blogid) return null;
$blog = $this->fetch($blogid);
if (!$blog) return null;
return DB::fetch_first("SELECT b.* FROM %t b WHERE b.uid=%d AND b.friend=0 AND b.status=0 AND b.dateline<%d ORDER BY b.dateline DESC", array($this->_table, $blog['uid'], $blog['dateline']));
}
}
?>
这样我们的嵌入点函数就能进一步简化成:
function space_blog_title() {
$id = intval($_GET['id']);
$uid = intval($_GET['uid']);
if($id && $uid){
$blog = C::t('home_blog')->fetch($id);
if ($blog && $blog['uid']==$uid) {
$pre = C::t('#imyoona_pre_next#home_blog_pn')->fetch_pre($id);
$next = C::t('#imyoona_pre_next#home_blog_pn')->fetch_next($id);
}
}
include template('imyoona_pre_next:pn');
return $ss;
}
如果我们要象美中网那样将这个上下篇链接加在博文内容的结尾处,我们需要在那里添加一个自定义嵌入点,因为Discuz在那个位置没有提供嵌入点。 通过页面分析不难看出我们要添加的嵌入点的位置是标识符为blog_article的div的后面,我们在日志页模板文件template/default/home/space_blog_view.htm里加入了一个名叫space_blog_pn的嵌入点:
$blog[message]
然后把前面我们定义的嵌入点函数space_blog_title改名为space_blog_pn就可以了。
注意这里为简单起见我们只给出全站用户可见的博文链接,美中网的功能更细致一点,同一篇博文的上下篇链接会因当前用户的不同而改变。对上面的代码略做修改后这不难实现。
最后谈一下如何将插件供他人用。在管理中心的插件设计页里有个导出的按钮,可用来产生一个描述插件的XML文件:discuz_plugin_imyoona_pre_next.xml。注意这个文件里已经包括了语言包文件imyoona_pre_next.lang.php的内容,这也是我们不同将这个文件交给用户的原因。将这个XML文件放入文件夹source/plugin/imyoona_pre_next。然后将整个文件夹交给用户,让用户放在他的网站的同样位置。然后他就能在网站的管理中心的插件页看到这个新插件,先安装:
然后再启用:
注:在本文中的代码中,在<符号和a字符相连的地方在两者之间加了一个不应该有的空格,以避免Discuz在保存日志时自动改变日志内容。
插件下载:
http://www.bian-wang.com/discuz/data/userupload/10005/txgz_pre_next.zip