Discuz:插件设计
Discuz! 6.0|Discuz! 6.0安装|Discuz! 6.0使用|Discuz! 6.0风格模板|Discuz! 6.0插件|Discuz! 6.0升级|Discuz! 6.0开发|Discuz! 6.0 FAQ |
Discuz插件的设计,在开始论坛插件的设计之前,有必要了解一下我们所推荐的插件设计方式,更好的规范性和兼容性,将使得您设计的插件受到更多使用者的欢迎,对于程序员而言,也有助于形成
良好的编码习惯,实现自身能力的提升。如果您有意编写 Discuz! 论坛插件,请按照先后顺序仔细阅读本文档。
准备工作
Discuz插件实现流程
开始编写Discuz论坛插件,您应当首先对插件实现的流程有一个大致的了解,以下是我们推荐的插件编写流程:
1. 熟练使用 Discuz! 论坛系统后,对希望完善或补充的个性化功能进行评估,进而提出插件的功能需求。 2. 对插件做一个概括性的设计,例如:需要使用什么菜单、什么参数,配置哪些选项、数据结构如何设计、前后台实现哪些功能等等。 3. 阅读本文档并在系统设置中实际体验 Discuz! 插件接口所实现的功用,例如:您的插件应当如何设计才能良好的挂接到论坛系统中 来。插件接口能够实现哪些功能、不能实现哪些功能,插件为此而需要做的优化、改造和取舍。 4. 编写相应程序代码和模板语句,实现所需的功能并进行代码测试、兼容性测试和代码改进。 5. 如果需要公开您的Discuz插件,可以用插件导出的方式,将插件配置信息导出到一个文本文件中,连同相应的程序和模板文件一同打包。同 时,编写一个适合新手的插件的说明书也是必不可少的,其中包括:插件适用的 Discuz! 版本、功能概述、兼容性声明、安装方 法、使用方法、卸载方法等等。 6. 将插件提供给他人,或自己使用,根据使用者反馈,对插件进行完善。插件实现流程至此结束。
Discuz文件命名规范
Discuz! 按照如下的规范对程序和模板进行命名,请在设计插件时尽量遵循此命名规范:
1. 可以直接通过浏览器访问的普通程序文件,以 .php 后缀命名。 2. 被普通程序文件引用的程序文件,以 .inc.php 后缀命名。 3. 被普通程序文件,或引用程序文件引用的函数库或类库,以 .func.php(函数库) 或 .class.php(类库) 后缀命名。 4. 模板文件,以 .htm 后缀命名,模板文件只存在于 ./templates 目录中。 5. 模板语言包文件,以 .lang.php 后缀命名,语言包文件只存放于 ./templates 目录中,与模板文件同级目录。 6. 被编译后的模板文件,以 .tpl.php 后缀命名,前面的数字是模板套系的 ID,下划线后面的是模板原名, 编译模板文件只存在于 ./forumdata/templates 目录中。 7. 动态缓存文件,存放于 ./forumdata/cache 目录中,依据不同的功用进行独立的命名。 8. 使用后台数据备份功能生成的备份文件,通常以 .sql 为后缀,存放于 ./forumdata/ 目录中。 9. 有些目录中存在内容为空白的 index.htm 文件,此类文件是为了避免 [[Web]] 服务器打开 Directory Index 时可能产生的安全问题
common.inc.php 模块功能白皮书
./include/common.inc.php 是 Discuz! 的通用初始化模块程序,其几乎被所有的外部代码所引用,在您开始插件设计之前,可以先对该模
块的大致功能做一定的了解。common.inc.php 主要完成了以下任务:
1. 对不同 [[PHP]] 及操作系统环境做了判断和兼容性处理,使得 Discuz! 可以运行于各种不同配置的[[服务器]]环境下。 2. 初始化常量 IN_DISCUZ 为 TRUE,用于 include 或 require 后续程序的判断,避免其他程序被非法引用。 3. 读取论坛所在绝对路径,存放于常量 DISCUZ_ROOT 中。 4. 加载所需的基本函数库 include/global.func.php。 5. 通过 config.inc.php 中提供的数据库账号信息,建立数据库连接。Discuz!支持数据表的前缀,如需获得表的全名,可使 用”{$tablepre}tablename” 或 $tablepre.’tablename’的方式。 6. 判断用户是否登录,如登录标记 $discuz_uid 为非 0,同时将 $discuz_user(加了 slash 的用户名,可用于不加修改的插入数 据库)、 $discuz_userss(原始的用户名,可用于页面显示)、$discuz_pw(用户密码的MD5串)、$discuz_secques(登录提示 问题的加密串)等相应用户信息赋值。 7. 判断用户管理权限,将管理权限标记 $adminid 为 -1~3 中间的值。-1 代表为特殊用户组用户。0 代表普通用户;1 代表论坛管 理员;2 代表超级版主;3 代表论坛版主。将用户权限按照其所在的主用户组 ID 标记为 $groupid,相关权限从该 $groupid 所 对应的系统缓存中读出(./forumdata/cache/usergroup_$groupid.php)。将用户扩展权限按照其扩展用户组 ID 标记为 $ extgroupids,中间以 \t(tab) 分隔,格式为“$groupid1\t$groupid2...”,扩展用户组用于确定用户的扩展浏览权限,例如 能否访问某些有特殊权限设定的论坛等。 8. 读入系统设置中的各种变量,并根据 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)等。 9. 缓存数据的格式,大多是存放在 $_DCACHE['cachename'] 数组中,有些常用的参数,如系统设置中的参数、风格界面等,通常还 被进行了展开操作(extract)或使用常量进行赋值。 10. 用户如果处在登录状态,会自动读出 members 表相关用户的参数值,用户的个性设置参数:如时差、时间格式、界面风格等等,会 根据实际情况覆盖系统默认值,因此在后续程序通常不用再做判断。 11. 如果程序提交的 [[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插件模块和自定义菜单:
Discuz插件接口默认提供四种可选的模块方式:
1. 直接链接(前台菜单):可在前台右上角加入一个菜单项,可自主指派菜单链接的 URL。注意:由于引用外部程序,因此即便设置了 模块的使用等级,您的程序如需权限判断,仍需要引用 common.inc.php 和插件相关的缓存文件(将在后面的《参数读取与缓存控 制》中详细说明),并自行判断使用等级是否合法。 2. 前台调用(前台菜单):与直接链接类似,但其调用的是插件的一个模块,模块文件名指派为“./plugins/插件目录/插件模块名. inc.php”,由 plugin.php 调用此模块,调用 URL 将在后面的《编写插件的原则与注意事项》中详细说明。 3. 后台调用(后台菜单):可在后台插件设置中为此插件增添一个管理模块,模块文件名指派为“./plugins/插件目录/插件模块名. inc.php”,由 admincp.php 调用此模块,调用 URL 将在后面的《编写插件的原则与注意事项》中详细说明。 4. 包含运行(无菜单):可设置一个在论坛所有页面均包含运行的脚本,此脚本在 ./include/common.inc.php 中加载,脚本文件名 指派为“./plugins/插件目录/插件模块名.inc.php”。请注意,为了不导致错误的插件影响论坛运行,在 common.inc.php 加载 此模块时,屏蔽了错误信息,因此请务必仔细检查是否存在语法错误,任何微小的语法错误都将不被提示出来,并且导致此模块不被 正常加载。如果您配置了不正确的包含脚本而导致论坛系统设置无法使用,删除服务器上相应的脚本文件即可解决。
您可以为每个模块设置不同的使用等级,例如设置为“超级版主”,则超级版主及更高的管理者(例如论坛管理员)可以使用此模块。
- Discuz插件钩子设置:
插件钩子能够将插件代码埋藏在程序中的任意位置,从而实现更加灵活的插件功能,最大限度的减少对原有程序代码的修改。 论坛版本升级后,只需将相应钩子重新埋入相应程序中,原有插件即可继续使用。 关于插件钩子的具体设计方法请见下面的《插件钩子的设计》。
- Discuz插件变量配置:
插件接口中提供了一个通用的插件配置管理程序,在大多数情况下可实现插件的参数配置,省却了插件开发者自行编写后台管理模块(即上面提到的“后台调用(后台菜单)”模块)的麻烦。通常情况下,应优先使用通用插件配置管理程序来实现插件的参数配置,只有在通用程序确实无法实现时,才自行编写后台管理模块。输入配置名称和配置变量名、选择合适的配置类型后,即可为此插件增加一个配置变量,点“详情”可以编辑此配置变量的更多信息。为了方便插件程序调用使用者配置好的参数,配置变量同样被存放在了缓存文件中,读取方法将在后面的《参数读取与缓存控制》中详细说明。
注意:您只有在插件管理中将插件设置为“可用”,以上设置才能生效。
参数读取与缓存控制
编写插件程序时,可能需要读取一些插件的信息,如果插件需要使用者进行配置,还需要读取使用者设置的参数值。Discuz! 允许插件程序使用数据库读取和缓存读取这两种方法获取插件信息和参数。Discuz! 的插件接口已经对插件信息进行了合理的缓存,使用缓存读取的方式,将比数据库读取速度更快,消耗的资源更是几乎可以忽略不计。缓存读取唯一的局限是需要插件使用插件接口提供的通用后台管理程序。如果使用自定义后台模块的方式,需要后台模块将参数存放到 pluginvars 数据表中,才能被系统正常缓存。我们强烈推荐您通过缓存读取插件信息和配置数据。
- Discuz插件数据结构
插件数据使用两个数据表存放,分别是 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 描述了这个插件的模块信息,其中 type 为 1~4 的整数值,从小到大依次为“直接链接(前台菜单)”、“前台调用(前台菜单)”、“后台调用(后台菜单)”、“包含运行(无菜单)”;vars 描述了这个插件的配置变量,前面为变量名,后面为使用者赋予这个变量的值。
编写插件的原则与注意事项
请在您动手编写插件之前,还需要仔细的阅读以下原则,遵循这些原则,将有效的避免可能发生的问题:
1. 所有与插件的程序,包括其全部的前后台程序,请全部放入 ./plugins 目录中,同时在插件的安装说明中指出,插件的文件需要复制到哪些目录。为了避免与其他插件冲突,请尽量建立 ./plugins 下的子目录,并将插件程序放置于子目录下,这样您编写的插件将获得更好的兼容性。
2. 如果您的插件包含“前台调用(前台菜单)”模块,该模块将统一用 plugin.php?identifier=xxx&module=yyy 的方式调用,请在相应链接、表单中使用此方式。其中 xxx 为插件的惟一标识符,yyy 为模块名称。前台插件外壳程序 plugin.php 已经加载了通用初始化模块(./include/common.inc.php),不需再次引用。
3. 如果您的插件包含“后台调用(后台菜单)”模块,该模块将统一用 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)并进行了后台管理人员权限验证,因此模块程序中可直接写功能代码,不需再进行验证。
4. 请勿绕过插件的前后台外壳(plugin.php 和 admincp.php)而以直接调用某程序的方式编写插件,因为这样既导致了用户使用不便,代码冗余和不规范,同时又产生了因验证程序考虑不周到而带来的安全隐患。您可以在任何地方,包括链接、表单等处方便的使用上述 URL 地址对插件模块进行调用。
5. 所有与插件有关的程序,包括全部的前后台程序,因全部使用外壳调用,请务必在第一行加入
if(!defined('IN_DISCUZ')) { exit('Access Denied'); }
以免其被 URL 直接请求调用,产生安全问题。
6. 一般情况下,您发布插件请使用插件导出的功能,以方便使用者一次性导入插件的配置数据,极特殊的情况下,也可以分步骤告知使用者如何进行插件配置管理和安装此插件。
7. 如果功能独立,请尽量使用单独程序的方式编写插件(即外挂型插件),而尽量少的对论坛本身代码进行修改,这将为使用者今后的升级带来很大方便。
8. 您可以修改 Discuz! 本身的数据结构,但更推荐在不很影响效率的前提下将插件数据用另外的数据表存储,因为不能排除您增加的字段或索引和今后版本 Discuz! 核心数据字段重名的可能。在任何情况下,请不要删除 Discuz! 标准版本数据结构中已有的字段或索引。
9. 请在插件说明书中对插件做以详尽的描述,例如增加了哪些字段、哪些表,修改了或新增了哪些程序,版本兼容性,后续支持的提供方式(例如不提供支持,或以什么样的方式提供)。如果方便,请尽可能提供插件的卸载方法,例如去除哪些字段、删除哪些新增的程序、将哪些被插件修改的程序恢复原状等等,使用者会感激您为此付出的辛勤劳动,甚至愿意支付相应的费用支持您未来的发展。
10. 如果插件使用另外的数据表存储,请在插件管理中准确的设置插件所使用的数据表名称(不包含前缀),这样用户在备份数据的时候,能够把插件数据一同备份。
11. 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设计范例
此处我们为您提供一个使用钩子技术的插件范例,完成功能十分简单,旨在使您直观的了解钩子的使用。此范例实现了当游客访问论坛首页时,强制用户登录的功能。
1. 进入系统设置中的插件管理,添加一个名称和惟一标识符均为 demo 的插件。
2. 编辑插件 demo 详情,找到“插件钩子设置”,添加一个名为 index_force_loggedin 的钩子,然后提交。
3. 编辑钩子 index_force_loggedin,按如下内容填写设置:
钩子描述:当游客访问论坛首页的时候,程序会弹出提示框,并自动跳转到登陆页面。
PHP代码:
if(!$discuz_uid) { showmessage('请您登陆后访问本站,现在将转入登录页面。', 'logging.php?action=login'); }
4. 提交后,回到此插件的设置页面。将钩子 index_force_loggedin 设置为可用。 5. 点击左侧导航,进入插件管理,将插件 demo 设置为可用。 6. 编辑论坛程序 index.php 安放钩子,方法如下:
查找代码:
$discuz_action = 1;
在上述代码下放添加代码:
eval($hooks['demo_index_alter']);
8. 至此,一个简单的使用钩子实现的插件已经彻底完成。您可以退出登陆并访问论坛首页,测试一下插件的效果。
Discuz! 插件的钩子技术,为广大的插件开发者提供了一个更加灵活的插件设计机制。当 Discuz! 升级后,用户只需重新将钩子调用代码安放到程序中原来的位置,就几乎可以继续使用原来已安装的插件,降低了对于程序修改的幅度和插件安装的难度,更加有利于插件程序的规范、管理、维护、相互交流。因此我们强烈建议插件开发者能够深入研究个应用这一机制,创作出越来越多的优秀插件。
意见反馈
插件接口是 Discuz! 开发组为了方便插件设计、安装和使用而专门开发,虽然经过长期的优化和改进,可能仍然会有不够合理或不够完善的地方,欢迎各位插件程序员在使用此接口的过程中,为我们提出意见和建议,感谢您的支持。