插件设计 |
您在开始论坛插件的设计之前,有必要了解一下我们所推荐的插件设计方式,更好的规范性和兼容性,将使得您设计的插件受到更多使用者的欢迎,对于程序员而言,也有助于形成良好的编码习惯,实现自身能力的提升。如果您有意编写 Discuz! 论坛插件,请按照先后顺序仔细阅读本文档。
|
准备工作 |
插件实现流程 文件命名规范 common.inc.php 模块功能白皮书
./include/common.inc.php 是 Discuz! 的通用初始化模块程序,其几乎被所有的外部代码所引用,在您开始插件设计之前,可以先对该模块的大致功能做一定的了解。common.inc.php 主要完成了以下任务:
- 对不同 PHP 及操作系统环境做了判断和兼容性处理,使得 Discuz! 可以运行于各种不同配置的服务器环境下。
- 初始化常量 IN_DISCUZ 为 TRUE,用于 include 或 require 后续程序的判断,避免其他程序被非法引用。
- 读取论坛所在绝对路径,存放于常量 DISCUZ_ROOT 中。
- 加载所需的基本函数库 include/global.func.php。
- 通过 config.inc.php 中提供的数据库账号信息,建立数据库连接。Discuz!支持数据表的前缀,如需获得表的全名,可使用”{$tablepre}tablename”
或 $tablepre.’tablename’的方式。
- 判断用户是否登录,如登录标记 $discuz_uid 为非 0,同时将 $discuz_user(加了 slash 的用户名,可用于不加修改的插入数据库)、
$discuz_userss(原始的用户名,可用于页面显示)、$discuz_pw(用户密码的MD5串)、$discuz_secques(登录提示问题的加密串)等相应用户信息赋值。
- 判断用户管理权限,将管理权限标记 $adminid 为 -1~3 中间的值。-1 代表为特殊用户组用户。0 代表普通用户;1 代表论坛管理员;2 代表超级版主;3 代表论坛版主。
将用户权限按照其所在的主用户组 ID 标记为 $groupid,相关权限从该 $groupid 所对应的系统缓存中读出(./forumdata/cache/usergroup_$groupid.php)。
将用户扩展权限按照其扩展用户组 ID 标记为 $extgroupids,中间以 \t(tab) 分隔,格式为“$groupid1\t$groupid2...”,扩展用户组用于确定用户的扩展浏览权限,例如能否访问某些有特殊权限设定的论坛等。
- 读入系统设置中的各种变量,并根据 Cache 模块的设定,根据当前被调用的程序文件名(如 index.php,forumdisplay.php 等等)读入
相应的缓存代码。缓存代码被存放于 ./forumdata/cache/ 中。除了对应当前程序的缓存,可能还会加载一些通用的缓存数据,例如整个论坛
的设置(./forumdata/cache/cache_settings.php)、界面风格(./forumdata/cache/style_x.php)、当前用户的用户组(./forumdata/cache/usergroup_x.php)、
管理组权限(./forumdata/cache/adminusergroup_x.php)等。
- 缓存数据的格式,大多是存放在 $_DCACHE['cachename'] 数组中,有些常用的参数,如系统设置中的参数、风格界面等,通常还被进行了
展开操作(extract)或使用常量进行赋值。
- 用户如果处在登录状态,会自动读出 members 表相关用户的参数值,用户的个性设置参数:如时差、时间格式、界面风格等等,会根据实际
情况覆盖系统默认值,因此在后续程序通常不用再做判断。
- 如果程序提交的 URL 中包含 tid=x 或 fid=x,common.inc.php 模块会自动读出其所对应的论坛记录及包括 access masks、版主设定等相应
权限,记录在 $forum 变量中。后续程序只要通过URL将tid或fid传递过来,便可通过 $forum 数组的存在性或相关参数来对论坛权限进行判断,
不需要再读 forums 表的资料。
|
插件接口概述 |
使用管理员账号登录 Discuz! 系统设置,在左侧菜单将可以看到“插件设置”和“插件管理”两个选项,使用超级版主或版主账号登录,将只出现“插件设置”一个选项。“插件管理”是控制插件打开与否、设计插件模块、菜单、参数和使用权限的地方,插件开发者可以依照设计意图,在此进行插件的初步设置,这里同时也提供插件导入和插件开关的功能,用于导入他人设计的插件和对插件的可用状态进行变更。“插件设置”是对已经安装的插件进行设置的地方,供使用者对插件参数进行调整以实现不同的插件功能。即前者主要面向开发者,后者主要面向使用者。
开始编写一个新插件,请首先在插件管理中,输入新插件的名称和惟一标识符。名称用于表明此插件的用途,例如设置为“虚拟银行插件”。惟一标识符用于在后续的插件模块中调用本插件,不可与现有插件重复,命名规则限制与 PHP 变量命名相同,虽然初次设置后仍可改动,但强烈建议一次性将此配置设置好,否则可能涉及到很多代码方面的变更,增加编码的麻烦。请注意:惟一标识符请不要设置的过短,或使用有可能与其他插件重复的命名,例如制作此插件的公司叫做 Comsenz Inc.,插件名称是“虚拟银行插件”,惟一标识符可设置为“comsenz_virtual_bank”,后面将以“虚拟银行插件”和“comsenz_virtual_bank”为例进行说明。
在插件管理中添加插件后,仅仅是增加了一条插件记录,后面还需要很多相关的设计和设置。在列表中选择插件的“详情”进入插件的详细设置。插件设置分为三个部分:
|
参数读取与缓存控制 |
编写插件程序时,可能需要读取一些插件的信息,如果插件需要使用者进行配置,还需要读取使用者设置的参数值。Discuz! 允许插件程序使用数据库读取和缓存读取这两种方法获取插件信息和参数。Discuz! 的插件接口已经对插件信息进行了合理的缓存,使用缓存读取的方式,将比数据库读取速度更快,消耗的资源更是几乎可以忽略不计。缓存读取唯一的局限是需要插件使用插件接口提供的通用后台管理程序。如果使用自定义后台模块的方式,需要后台模块将参数存放到 pluginvars 数据表中,才能被系统正常缓存。我们强烈推荐您通过缓存读取插件信息和配置数据。
插件数据结构
插件数据使用两个数据表存放,分别是 plugins 和 pluginvars。前者用于存放插件信息:安装了多少个插件,就有多少条记录;后者用于存放插件的配置参数和配置值:所有已安装的插件总共有多少个配置项目,就有多少条记录。下面的表格列出了这两个表的主要字段及其用途说明。
plugins 表:
pluginid 插件的惟一 ID,自动递增
available 插件是否可用,1=是,0=否
adminid 使用系统设置中插件接口自带的插件参数设置程序所需的最低权限等级要求,1=管理员,2=超级版主,3=版主
name 插件名称
identifier 插件惟一标识符
description 插件简介
datatables 插件数据表,不包含前缀,多个表使用半角逗号“,”分隔
directory 插件所在目录,例如设置为 comsenz_bank,则对应论坛目录的位置为 ./plugins/comsenz_bank/
copyright 插件版权信息
modules 插件模块信息,数组格式,使用 serialize() 序列化后存放
|
pluginvars 表:
pluginvarid 插件配置的惟一 ID,自动地增
pluginid 本项配置所隶属的插件 ID
displayorder 本项配置的显示顺序,数值低的排在前面
title 插件配置的名称
description 插件配置的简介
variable 插件配置的变量名
type 插件配置的类型
value 插件配置的值
extra 当本项配置为“选择(select)”时,可选的取值范围
|
如果您使用自行编写的插件后台管理模块进行插件参数配置,请尽量将配置项目按照 pluginid 的对应关系,将参数存储于 pluginvars 表中,这样系统就可以自动将您增加的配置参数缓存起来,以供插件程序进行调用。
插件参数读取
了解了 Discuz! 插件存储的数据结构后,您可以在插件程序中根据需要选择合适的数据读取方式。由于数据库读取方式可以由数据结构推断而来,因此这里只介绍缓存读取的方式,这种方式是我们强烈推荐的插件数据读取方式。
在管理者配置好插件信息,或用户进行插件的参数设置之后,系统将根据插件设置的惟一标识符,自动生成一个插件数据的缓存文件,例如惟一标识符为 comsenz_virtual_bank,则缓存文件位于 ./forumdata/cache/plugin_comsenz_virtual_bank.php,您可以打开此文件查看其中的数据内容和格式。缓存采用数组的方式进行存储,引用此文件即可将所需的插件参数一次性赋值。
其中,$_DPLUGIN['comsenz_virtual_bank'] 这个数组下标,为插件的惟一标识符,所有插件缓存数据,一经被引用,就会赋值到 $_DPLUGIN 这个多维数组中。modules 描述了这个插件的模块信息;vars 描述了这个插件的配置变量,前面为变量名,后面为使用者赋予这个变量的值。
|
页面嵌入模块开发 |
|
特殊主题模块开发 |
- 特殊主题模块用于创建一个特殊主题,特殊主题类型脚本格式
<?php
class threadplugin_identifier {
var $name = 'XX主题'; //主题类型名称
var $iconfile = 'icon.gif'; //images/icons/ 目录下新增的主题类型图片文件名
var $buttontext = '发布xx主题'; //发帖时按钮文字
function newthread($fid) {
return ...;
}
function newthread_submit($fid) {
}
function newthread_submit_end($fid) {
}
function editpost() {
return ...;
}
function editpost_submit() {
}
function editpost_submit_end() {
}
function newreply_submit_end() {
}
function viewthread() {
return ...;
}
}
?>
|
identifier
插件的唯一标识符,在插件设置中设置。
- 函数名以及含义
函数名 | 含义 |
newthread | 发主题时页面新增的表单项目,通过 return 返回即可输出到发帖页面中 |
newthread_submit | 主题发布后的数据判断 |
newthread_submit_end | 主题发布后的数据处理 |
editpost | 编辑主题时页面新增的表单项目,通过 return 返回即可输出到编辑主题页面中 |
editpost_submit | 主题编辑后的数据判断 |
editpost_submit_end | 主题编辑后的数据处理 |
newreply_submit_end | 回帖后的数据处理 |
viewthread | 查看主题时页面新增的内容,通过 return 返回即可输出到主题首贴页面中 |
|
插件安装、卸载、升级脚本的设计 |
- 安装、卸载
插件作者可以设计 2 个脚本文件用于插件的安装和卸载,文件名任意。脚本中可用 runquery() 函数执行 SQL 语句,表名可以直接写“cdb_”。插件作者只需在导出的 XML 文件结尾加上安装、卸载脚本的文件名即可
<item id="installfile"><![CDATA[install.php]]></item>
<item id="uninstallfile"><![CDATA[uninstall.php]]></item>
</item>
</root>
|
安装、卸载程序中可随意设计页面的跳转,只要在插件安装、卸载结束时候输出添加以下代码即可。
- 升级
插件作者可以设计一个脚本文件用于插件的升级,文件名任意。脚本中可用 runquery() 函数执行 SQL 语句,表名可以直接写“cdb_”。插件作者只需在导出的 XML 文件结尾加上升级脚本的文件名即可
<item id="upgradefile"><![CDATA[upgrade.php]]></item>
</item>
</root>
|
升级程序中可通过 $fromversion 和 $toversion 变量判断升级的具体版本号,并随意设计页面的跳转,只要在插件升级结束时候输出添加以下代码即可。
插件的当前版本号位于 XML 文件的以下分支中,可自行更改。
<item id="plugin">
......
<item id="version"><![CDATA[当前版本]]></item>
......
</item>
|
- 授权协议、插件介绍
插件在安装的时候您可以自定义授权信息文本,文本支持 Discuz 代码,站长同意后才能安装插件。如果插件存在后台管理界面或者变量配置,那么插件介绍文本会显示在插件后台页面中。插件作者只需在导出的 XML 文件结尾加上以下内容即可
<item id="license"><![CDATA[授权协议文本]]></item>
<item id="intro"><![CDATA[插件介绍文本]]></item>
</item>
</root>
|
- 模板、语言包
插件中的语言包可以写到导出的 XML 文件结尾,这样在插件安装的时候会自动把语言包生成文件名为 forumdata/plugins/identifier.lang.php 的文件。插件开发时,可直接在后台开启语言包选项后编辑此语言包文件。
<item id="language">
<item id="scriptlang">
<item id="text"><![CDATA[脚本语言文字]]></item>
</item>
<item id="templatelang">
<item id="text"><![CDATA[模版语言文字]]></item>
</item>
<item id="installlang">
<item id="text"><![CDATA[安装语言文字]]></item>
</item>
</item>
</item>
</root>
|
scriptlang 为脚本文件的语言包,templatelang 为模版文件的语言包,installlang 为安装、升级、卸载脚本用的语言包。
插件的模板文件可以放置在 plugins 目录下,如 plugins/hooktest/templates 子目录下。页面嵌入模块的脚本中可用以下方式调用此目录下的模板。
include template('index_top', 'hooktest', './plugins/hooktest/templates'); |
通过 plugin.php 调用的插件可直接用以下函数。
include plugintemplate('index_top'); |
插件模版文件中的语言包中通过 {lang identifier:langvar} 方式调用,例如:
<!--{block return}-->
{lang hooktest:text}
<!--{/block}-->
|
上例中对应的变量为语言包文件 forumdata/plugins/identifier.lang.php 中 $templatelang['hooktest']['text'] 的值。脚本中引用语言包无需包含语言包文件,可直接使用变量。如插件脚本中可用变量 $scriptlang['hooktest']['text'],安装脚本中可用变量 $installlang['hooktest']['text']。
- 其他论坛数据导入
插件安装时可以直接导入一个或多个论坛数据,这些论坛数据包括数据调用(request)、论坛方案(project)、表情(smilies)、风格(styles)的数据。在导出的 XML 文件结尾加上需要导入数据的类型和数据文件名即可,多个文件名用逗号(",")分隔。
<item id="importfile">
<item id="request"><![CDATA[discuz_request_test.xml,discuz_request_test2.xml]]></item>
<item id="project"><![CDATA[discuz_project_test.xml]]></item>
<item id="smilies"><![CDATA[discuz_smilies_test.xml]]></item>
<item id="styles"><![CDATA[discuz_styles_test.xml]]></item>
</item>
</item>
</root>
|
- 小提示
在新插件内核中,通过 plugin.php 方式访问的插件可直接通过 plugin.php?id=xxx:yyy 方式调用而无需再在后台定义为普通脚本模块,只要 plugins/xxx/yyy.inc.php 文件存在即可。如果 xxx 和 yyy 同名,可直接通过 plugin.php?id=xxx 方式访问。如果导出的 XML 文件名以 SC_GBK、SC_UTF8、TC_BIG5、TC_UTF8 结尾,显示的时候将直接显示为“简体”、“繁体”、“UTF8”等字样。
|
编写插件的原则与注意事项 |
请在您动手编写插件之前,还需要仔细的阅读以下原则,遵循这些原则,将有效的避免可能发生的问题:
- 所有与插件的程序,包括其全部的前后台程序,请全部放入 ./plugins 目录中,同时在插件的安装说明中指出,插件的文件需要复制到哪些目录。为了避免与其他插件冲突,请尽量建立 ./plugins 下的子目录,并将插件程序放置于子目录下,这样您编写的插件将获得更好的兼容性。
- 如果您的插件包含“前台调用(前台菜单)”模块,该模块将统一用 plugin.php?identifier=xxx&module=yyy 的方式调用,请在相应链接、表单中使用此方式。其中 xxx 为插件的惟一标识符,yyy 为模块名称。前台插件外壳程序 plugin.php 已经加载了通用初始化模块(./include/common.inc.php),不需再次引用。
- 如果您的插件包含“后台调用(后台菜单)”模块,该模块将统一用 admincp.php?action=plugins&identifier=xxx&mod=yyy 的方式调用,请在相应链接、表单中使用此方式。其中 xxx 和 yyy 的定义与“前台调用(前台菜单)”模块中的相同。系统还允许用 admincp.php?action=plugins&edit=$edit&mod=$mod 的方式来生成链接和表单地址,$edit 和 $mod 变量已经被插件后台管理接口赋值,因此将这两个变量值带入 URL 中也是被支持的。由于后台模块是被 admincp.php 调用,因此已加载了通用初始化模块(./include/common.inc.php)并进行了后台管理人员权限验证,因此模块程序中可直接写功能代码,不需再进行验证。
- 请勿绕过插件的前后台外壳(plugin.php 和 admincp.php)而以直接调用某程序的方式编写插件,因为这样既导致了用户使用不便,代码冗余和不规范,同时又产生了因验证程序考虑不周到而带来的安全隐患。您可以在任何地方,包括链接、表单等处方便的使用上述 URL 地址对插件模块进行调用。
- 所有与插件有关的程序,包括全部的前后台程序,因全部使用外壳调用,请务必在第一行加入
if(!defined('IN_DISCUZ')) {
exit('Access Denied');
}
|
以免其被 URL 直接请求调用,产生安全问题。
- 一般情况下,您发布插件请使用插件导出的功能,以方便使用者一次性导入插件的配置数据,极特殊的情况下,也可以分步骤告知使用者如何进行插件配置管理和安装此插件。
- 如果功能独立,请尽量使用单独程序的方式编写插件(即外挂型插件),而尽量少的对论坛本身代码进行修改,这将为使用者今后的升级带来很大方便。
- 您可以修改 Discuz! 本身的数据结构,但更推荐在不很影响效率的前提下将插件数据用另外的数据表存储,因为不能排除您增加的字段或索引和今后版本 Discuz! 核心数据字段重名的可能。在任何情况下,请不要删除 Discuz! 标准版本数据结构中已有的字段或索引。
- 请在插件说明书中对插件做以详尽的描述,例如增加了哪些字段、哪些表,修改了或新增了哪些程序,版本兼容性,后续支持的提供方式(例如不提供支持,或以什么样的方式提供)。如果方便,请尽可能提供插件的卸载方法,例如去除哪些字段、删除哪些新增的程序、将哪些被插件修改的程序恢复原状等等,使用者会感激您为此付出的辛勤劳动,甚至愿意支付相应的费用支持您未来的发展。
- 如果插件使用另外的数据表存储,请在插件管理中准确的设置插件所使用的数据表名称(不包含前缀),这样用户在备份数据的时候,能够把插件数据一同备份。
- Discuz! 自 4.0.0 版本起,内置了 8 种自定义积分,存储于 members 表中的 extcredits1 至 extcredits8 字段中,类型为有符号整数,您可以在引用 common.inc.php 后,在 $extcredits 或 $_DCACHE['settings']['extcredits'] 中读取 8 种积分的启用信息(详情请参考 ./forumdata/cache/cache_settings.php)。插件程序中如需更新用户积分,可直接 UPDATE 相应的积分字段,无需其他操作。
|
插件钩子的设计 |
插件钩子的设计,需要您具有一定编程基础,比较了解 Discuz! 论坛程序的结构,并能够使用 PHP 语言撰写代码。对于普通用户,可以略过以下内容。 插件钩子(以下简称“钩子”)属于插件的一部分,因此在设计钩子之前应当首先进入后台——插件管理,新增插件或者编辑一个现有的插件,即可看到相关设置。
钩子的添加
- 钩子名称:在一个插件内,钩子的名称是唯一的,不可重复。名称可以由英文字母、数字和“_”组成,不支持中文,最长255个字符。为了便于理解和记忆钩子的作用, 名称应当尽量简洁清晰,能够表述一定的含义。注意:钩子名称对字母大小敏感,例如:Index_start 和 index_start 将视为两个不同的钩子。
- 钩子描述:对钩子的详细说明,如功能介绍、调用方法、使用方法等。
- PHP代码:这里是钩子的核心内容,也是一段PHP代码,需要您自行设计,完成钩子需要处理的数据或者需要执行的操作。
- 可用:每个插件允许有多个钩子,您可以自由选择关闭或者开启某个钩子。
钩子的删除
钩子的编辑与升级
- 编辑:插件设计阶段,您可能需要随时编辑钩子,每次更改,系统会自动更新缓存文件,您可以立即看到更改的效果。如果是更改钩子名称,那么您可能需要调整钩子放置的程序,修改钩子调用的名称。
- 升级:论坛程序进行升级之前,您应当使用插件的导出功能,导出插件备份。论坛升级并正常运行后,再导入插件备份,修改相关程序,重新安放钩子。
钩子的放置与调用
钩子设计完成以后,您需要在相应的程序中安放钩子,不同钩子由于作用的不同,放置的位置也是不同的。 安放钩子,您仅仅需要将钩子的调用代码放入即可。调用代码格式如下:
eval($hooks['插件唯一标识符(identifier)_钩子名称']);
|
例如:调用 插件demo 的钩子 testhook, 我们需要在程序中适当的地方加入下面的代码
eval($hooks['demo_testhook']);
|
设计范例 Discuz! 插件的钩子技术,为广大的插件开发者提供了一个更加灵活的插件设计机制。当 Discuz! 升级后,用户只需重新将钩子调用代码安放到程序中原来的位置,就几乎可以继续使用原来已安装的插件,降低了对于程序修改的幅度和插件安装的难度,更加有利于插件程序的规范、管理、维护、相互交流。因此我们强烈建议插件开发者能够深入研究并应用这一机制,创作出越来越多的优秀插件。
|
意见反馈 |
插件接口是 Discuz! 开发组为了方便插件设计、安装和使用而专门开发,虽然经过长期的优化和改进,可能仍然会有不够合理或不够完善的地方,欢迎各位插件程序员在使用此接口的过程中,为我们提出意见和建议,感谢您的支持。
|