插件设计


您在开始进行社区插件的设计之前,有必要了解一下我们所推荐的插件设计方式,更好的规范性和兼容性,将使得您设计的插件受到更多使用者的欢迎,对于程序员
而言,也有助于形成良好的编码习惯,实现自身能力的提升。如果您有意编写 Discuz! 社区插件,请按照先后顺序仔细阅读本文档。
准备工作

插件实现流程

开始编写社区插件,您应当首先对插件实现的流程有一个大致的了解,以下是我们推荐的插件编写流程:

  • 熟练使用 Discuz! 社区系统后,对希望完善或补充的个性化功能进行评估,进而提出插件的功能需求。
  • 对插件做一个概括性的设计,例如:需要使用什么菜单、什么参数,配置哪些选项、数据结构如何设计、前后台实现哪些功能等等。
  • 阅读本文档并在系统设置中实际体验 Discuz! 插件接口所实现的功用,例如:您的插件应当如何设计才能良好的挂接到社区系统中来。插件接口能够实现哪些功能、不能实现哪些功能,插件为此而需要做的优 化、改造和取舍。
  • 编写相应程序代码和模板语句,实现所需的功能并进行代码测试、兼容性测试和代码改进。
  • 如果需要公开您的插件,可以用插件导出的方式,将插件配置信息导出到一个 XML
    文件中,连同相应的程序和模板文件一同打包。同时,编写一个适合新手的插件的说明书也是必不可少的,其中包括:插件适用的 Discuz!
    版本、功能概述、兼容性声明、安装方法、使用方法、卸载方法等等。
  • 将插件提供给他人,或自己使用,根据使用者反馈,对插件进行完善。插件实现流程至此结束。

文件命名规范

Discuz! 按照如下的规范对程序和模板进行命名,请在设计插件时尽量遵循此命名规范:

  • 可以直接通过浏览器访问的普通程序文件,以 .php 后缀命名。
  • 被普通程序文件引用的程序文件,以 .inc.php 后缀命名。
  • 被普通程序文件,或引用程序文件引用的函数库或类库,以 .func.php(函数库) 或 .class.php(类库) 后缀命名。
  • 模板文件,以 .htm 后缀命名,插件模板文件存在于 source/plugin/
    identifier
    /template/ 目录中,手机版插件模板存在于 source/plugin/
    identifier
    /template/mobile/目录中
  • 模板语言包文件,以 .lang.php 后缀命名,插件语言包文件开发时存放于 data/plugindata/ 目录中,文件名为
    identifier
    .lang.php。
  • 动态缓存文件,存放于 ./data/cache 目录中,依据不同的功用进行独立的命名。
  • 使用后台数据备份功能生成的备份文件,通常以 .sql 为后缀,存放于 data/ 目录中。
  • 有些目录中存在内容为空白的 index.htm 文件,此类文件是为了避免 Web 服务器打开 Directory Index 时可能产生的安全问题。

class_core.php 模块功能白皮书

source/class/class_core.php 是 Discuz! 的通用初始化模块程序,其几乎被所有的外部代码所引用,在您开始插件设计之前,可以先对该模块的大致功能做一定的了解。class_core.php 主要完成了以下任务:

  • 对不同 PHP 及操作系统环境做了判断和兼容性处理,使得 Discuz! 可以运行于各种不同配置的服务器环境下。
  • 初始化常量 IN_DISCUZ 为 TRUE,用于 include 或 require 后续程序的判断,避免其他程序被非法引用。
  • 读取社区程序所在绝对路径,存放于常量 DISCUZ_ROOT 中。
  • 加载所需的基本函数库 source/function/function_core.php。
  • 通过 config/config_global.php 中提供的数据库账号信息,建立数据库连接。Discuz! 支持数据表的前缀,如需获得表的全名,可使用“DB::table(\’tablename\’)”方式。
  • 判断用户是否登录,如登录标记 $_G[\’uid\’] 为非 0,同时将 $_G[\’user\’](加了 addslashes
    的用户名,可用于不加修改的插入数据库)、
    $_G[\’member\’][\’username\’](原始的用户名,可用于页面显示)、$_G[\’member\’][\’password\’](用户密码
    的MD5串)等相应用户信息赋值,其他用户信息存放于 $_G[\’member\’],更多信息可通过“getuserprofile()”获取。
  • 判断用户管理权限,将管理权限标记 $_G[\’adminid\’] 为 1~3 中间的值。0 代表普通用户;1 代表论坛管理员;2
    代表超级版主;3 代表论坛版主。 将用户权限按照其所在的主用户组 ID 标记为 $_G[\’groupid\’],相关权限从该
    $_G[\’groupid\’] 所对应的系统缓存中读出,存放于 $_G[\’group\’]。
  • 预置读入了每个模块的各种设置变量。
插件接口概述

使用管理员账号登录 Discuz!
管理中心,在顶部菜单将可以看到“插件”菜单。“插件列表”列出了所有已安装的插件,是控制插件打开与否、设计插件模块、菜单、参数和使用权限的地方,插
件开发者可以依照设计意图,在此进行插件的初步设置,这里同时也提供插件导入和插件开关的功能,用于导入他人设计的插件和对插件的可用状态进行变更。

开始编写一个新插件,请首先打开 config/config_global.php 文件,在文件结尾添加以下代码开启插件设计者模式。

$_config[\'plugindeveloper\'] = 1;

在插件管理中选择“设计新插件”,填写插件名称,名称用于表明此插件的用途,例如设置为“虚拟银行插件”。惟一标识符用于在后续的插件模块中调用本 插件, 不可与现有插件重复,命名规则限制与 PHP 变量命名相同,虽然初次设置后仍可改动,但强烈建议一次性将此配置设置好,否则可能涉及到很多代码方面的变更,增加编码的麻烦。请注意:惟一标识符请不要 设置的过短,或使用有可能与其他插件重复的命名,例如制作此插件的公司叫做 Comsenz Inc.,插件名称是“虚拟银行插件”,惟一标识符可设置为“comsenz_virtual_bank”,后面将以“虚拟银行插件”和 “comsenz_virtual_bank”为例进行说明。

在 source/plugin/ 目录中创建与唯一标识符同名的目录名,如 source/plugin/comsenz_virtual_bank/。

在插件管理中添加插件后,仅仅是增加了一条插件记录,后面还需要很多相关的设计和设置。在列表中选择插件的“详情”进入插件的详细设置。插件设置分为三个部分:

  • 插件基本设置:

    设置插件的基本参数,配置项目右边括号中的内容,为此设置对应的参数名称,调用方法将在后面的《参数读取与缓存控制》中详细说明。

  • 插件模块:
    插件模块分为程序链接、扩展项目和程序脚本 3 类:

    • 程序链接 主导航项目:可在主导航栏增加一个菜单项,可自主指派菜单链接的 URL,也可以调用插件的一个模块,模块文件名指派为
      source/plugin/插件目录/插件模块名.inc.php”。注意:由于引用外部程序,因此即便设置了模块的使用等级,您的程序仍需进行判断使
      用等级是否合法。
    • 程序链接 主导航项目 – 插件菜单:可在主导航栏的插件子菜单中增加一个菜单项。
    • 程序链接 顶部导航项目、底部导航项目、快捷导航项目、家园导航项目:可在各个导航中增加一个菜单项。
    • 扩展项目 个人设置:可在个人设置中增加一个菜单项。
    • 扩展项目 个人设置 – 个人资料:可在个人设置的个人资料页上部增加一个菜单项。
    • 扩展项目 个人设置 – 积分和用户组:可在个人设置的积分和用户组页上部增加一个菜单项。
    • 扩展项目 站点帮助:可在站点帮助中增加一个菜单项。
    • 扩展项目 我的帖子:可在我的帖子中增加一个菜单项。
    • 扩展项目 门户管理:可在门户管理面板上部增加一个菜单项。
    • 扩展项目 论坛管理 – 基本:可在前台论坛管理面板侧边上部增加一个菜单项。
    • 扩展项目 论坛管理 – 工具:可在前台论坛管理面板侧边下部增加一个菜单项。
    • 扩展项目 管理中心:可在后台插件栏目中为此插件增添一个管理模块。
    • 程序脚本 页面嵌入 – 普通版:设置一个包含页面嵌入脚本的模块,该模块用于在普通电脑访问的页面显示。模块文件名指派为 source/plugin/插件目录/插件模块名.class.php”。(页面嵌入将在后面的《页面嵌入模块开发》中详细说明)
    • 程序脚本 页面嵌入 – 手机版:设置一个包含页面嵌入脚本的模块,该模块用于在手机访问的页面显示。
    • 程序脚本 特殊主题:设置一个特殊主题脚本的模块,模块文件名指派为 source/plugin/插件目录/插件模块名.class.php”。(特殊主题将在后面的《特殊主题模块开发》中详细说明)

    您可以为每个模块设置不同的使用等级,例如设置为“超级版主”,则超级版主及更高的管理者可以使用此模块。

  • 插件变量配置:
    插件接口中提供了一个通用的插件配置管理程序,在大多数情况下可实现插件的参数配
    置,省却了插件开发者自行编写后台管理模块(即上面提到的“扩展项目
    管理中心”模块)的麻烦。通常情况下,应优先使用通用插件配置管理程序来实现插件的参数配置,只有在通用程序确实无法实现时,才自行编写后台管理模块。输

    入配置名称和配置变量名、选择合适的配置类型后,即可为此插件增加一个配置变量,点“详情”可以编辑此配置变量的更多信息。为了方便插件程序调用使用者配
    置好的参数,配置变量同样被存放在了缓存文件中,读取方法将在后面的《参数读取与缓存控制》中详细说明。

    注意:您只有在插件管理中将插件设置为“可用”,以上设置才能生效。

参数读取与缓存控制
  • 编写插件程序时,可能需要读取一些插件的信息,如果插件需要使用者进行配置,还需要读取使用者设置的参数值。Discuz!
    允许插件程序使用数据库读取和缓存读取这两种方法获取插件信息和参数。Discuz!
    的插件接口已经对插件信息进行了合理的缓存,使用缓存读取的方式,将比数据库读取速度更快,消耗的资源更是几乎可以忽略不计。缓存读取唯一的局限是需要插
    件使用插件接口提供的通用后台管理程序。如果使用自定义后台模块的方式,需要后台模块将参数存放到 pluginvars
    数据表中,才能被系统正常缓存。我们强烈推荐您通过缓存读取插件信息和配置数据。

  • 插件参数读取

    由于调用系统缓存统一通过“loadcache()”函数调用,并存放于 $_G[\’cache\’]
    中,因此“loadcache(\’plugin\’)”后插件的变量缓会存放于 $_G[\’cache\’][\’plugin\’] 中。嵌入点插件和以
    plugin.php 为主脚本调用的插件无需加载此缓存,系统已自动加载了缓存。

    变量配置类型为“版块/*”的变量会保存在 $_G[\’cache\’][\’forums\’][fid][\’plugin\’] 中。变量配置类型为“用户组/*”的变量会保存在 $_G[\’cache\’][\’usergroup_groupid\’][\’plugin\’] 和 $_G[\’group\’][\’plugin\’] 中。

页面嵌入模块开发

 

  • 页面嵌入类型脚本格式
    <?php

    //全局嵌入点类(必须存在)
    class plugin_identifier {

    function HookId_1() {
    ......
    return ...;
    }

    function HookId_2() {
    ......
    return ...;
    }

    ......

    }

    //脚本嵌入点类
    class plugin_identifier_CURSCRIPT extends plugin_identifier {

    function HookId_1() {
    ......
    return ...;
    }

    function HookId_2() {
    ......
    return ...;
    }

    ......

    }

    ?>

    plugin_
    普通版脚本中的类名以 plugin_ 开头。手机版脚本中的类名以 mobileplugin_ 开头。

    identifier

    插件的唯一标识符,在插件设置中设置。

    CURSCRIPT

    嵌入点位于的脚本名,如 forum.php 为 forum。

    HookId

    函数名 调用位置 声明位置 第一个参数含义
    HookId() 所有模块执行前被调用 脚本嵌入点类  
    HookId_output() 模块执行完毕,模板输出前被调用 脚本嵌入点类   array(
        \’template\’ => 当前要输出的模版,
        \’message\’ => showmessage 的信息内容,
        \’values\’ => showmessage 的信息变量,
      );
    global_HookId() 模块执行完毕,模板输出前被调用 全局嵌入点类  
    HookId_message() showmessage() 执行时调用 脚本嵌入点类   array(
        \’param\’ => showmessage() 函数的参数数组,
      );
    ad_adId() 相应的广告位中调用,函数名为广告位脚本 ID。
    如:ad_headerbanner()。
    全局嵌入点类
    脚本嵌入点类
      array(
        \’params\’ => 广告位参数,
        \’content\’ => 当前广告位原本将要显示的内容,
      );
    common() 所有模块执行前被调用 全局嵌入点类  
    discuzcode() discuzcode() 函数执行时调用
    函数中 $_G[\’discuzcodemessage\’] 变量为解析的字串
    全局嵌入点类   array(
        \’param\’ => discuzcode() 函数的参数数组,
      );

    要查看所有的预定义嵌入点,请打开 config/config_global.php 文件,将文件结尾添加的设计者模式值改成“2”,然后更新缓存即可。在页面源码中查找”<hook>”可搜索到嵌入点。(详细内容可参阅后面 的《嵌入点列表》)

    $_config[\'plugindeveloper\'] = 2;

    预定义的嵌入点会在页面预置好的位置输出函数返回的内容。函数返回值类型如果是 array 且是空值的,必须输出一个空数组,如:

    return array();

    函数名并不限于以上列表,您可以自定义,只要符合以下规则,函数就会在适当的地方被调用。

    function CURMODULE_USERDEFINE[_output]()

    CURMODULE 指明了此函数在哪个模块执行,可通过常量 CURMODULE 得到当前页面的 CURMODULE 值。 USERDEFINE 可自定义,如果函数名以“_output”结尾则会在模板输出前调用,否则会在模块执行前调用。
    如:attachment_test() 函数会在论坛的下载附件的时候执行。
    “_output”结尾的函数的第一个参数为数组,含义为 array(\’template\’ => 要输出的模板名, \’message\’ => showmessage 的文字)
    如:以下函数将在登录的时候输出调试文字

    function logging_test_output($a) {
    print_r($a);
    print_r($_POST);
    }

    plugin_ identifier 类中的其它函数为了便于阅读建议以“_”开头,如:

    <?php

    class plugin_sample {

    function _updatecache() {
    ......
    return ...;
    }

    }

    class plugin_sample_forum extends plugin_sample {

    function viewthread_posttop() {
    ......
    return ...;
    }

    ......

    }

    ?>
  • 嵌入点列表
    全局(common/)
      extcredits.htm
          string spacecp_credit_extra
      faq.htm
          string faq_extra
      footer.htm
          string global_footer
          string global_footerlink
      header.htm
          string global_cpnav_extra1
          string global_cpnav_extra2
          string global_usernav_extra1
          string global_usernav_extra2
          string global_usernav_extra3
          string global_header
      userabout.htm
          array global_userabout_top
          string userapp_menu_top
          string userapp_menu_middle
          array global_userabout_bottom

    论坛(forum/)
      discuz.htm
          string index_status_extra
          string index_top
          string index_middle
          string index_bottom
          string index_side_top
          string index_side_bottom
      editor_ajax.htm
          string ajax_editorctrl_right
          string ajax_editorctrl_left
      forumdisplay.htm
          string forumdisplay_leftside_top
          string forumdisplay_leftside_bottom
          string forumdisplay_forumaction
          string forumdisplay_modlink
          string forumdisplay_top
          string forumdisplay_middle
          string forumdisplay_postbutton_top
          string forumdisplay_bottom
          string forumdisplay_side_top
          string forumdisplay_side_bottom
      forumdisplay_fastpost.htm
          string forumdisplay_fastpost_content
          string forumdisplay_fastpost_func_extra
          string forumdisplay_fastpost_ctrl_extra
          string forumdisplay_fastpost_btn_extra
      forumdisplay_list.htm
          string forumdisplay_filter_extra
          array forumdisplay_thread
          array forumdisplay_author
          string forumdisplay_postbutton_bottom
      index_navbar.htm
          string index_navbar
      post.htm
          string post_top
          string post_middle
          string post_btn_extra
          string post_bottom
      post_activity.htm
          string post_activity_extra
      post_debate.htm
          string post_debate_extra
      post_editor_body.htm
          string post_editorctrl_right
          string post_editorctrl_left
          string post_editorctrl_top
          string post_editorctrl_bottom
      post_editor_option.htm
          string post_side_top
          string post_side_bottom
      post_infloat.htm
          string post_infloat_top
          string post_infloat_middle
          string post_infloat_btn_extra
      post_poll.htm
          string post_poll_extra
      post_reward.htm
          string post_reward_extra
      post_trade.htm
          string post_trade_extra
      topicadmin_modlayer.htm
          string forumdisplay_modlayer
          string modcp_modlayer
      trade_info.htm
          string viewthread_tradeinfo_extra
      viewthread.htm
          string viewthread_top
          string viewthread_postbutton_top
          string viewthread_modoption
          string viewthread_title_extra
          string viewthread_title_row
          string viewthread_middle
          string viewthread_bottom
      viewthread_activity.htm
          string viewthread_activity_extra1
          string viewthread_activity_extra2
      viewthread_fastpost.htm
          string viewthread_fastpost_side
          string viewthread_fastpost_content
          string viewthread_fastpost_func_extra
          string viewthread_fastpost_ctrl_extra
      viewthread_from_node.htm
          array viewthread_postheader
          array viewthread_postheader
          array viewthread_postheader
          array viewthread_endline
      viewthread_node.htm
          array viewthread_profileside
          array viewthread_imicons
          array viewthread_magic_user
          array viewthread_avatar
          array viewthread_sidetop
          array viewthread_sidebottom
          array viewthread_postheader
          array viewthread_postheader
          array viewthread_postheader
          string viewthread_useraction_prefix
          string viewthread_useraction
          array viewthread_postfooter
          string viewthread_magic_thread
          array viewthread_magic_post
          array viewthread_endline
      viewthread_node_body.htm
          array viewthread_posttop
          array viewthread_postbottom
      viewthread_poll.htm
          string viewthread_poll_top
          string viewthread_poll_bottom
      viewthread_portal.htm
          string viewthread_useraction_prefix
          string viewthread_useraction
          string viewthread_side_bottom
      viewthread_trade.htm
          array viewthread_trade_extra

    群组(group/)
      group.htm
          string group_navlink
          string forumdisplay_navlink
          string group_navlink
          string forumdisplay_navlink
          string group_top
          string forumdisplay_top
          string group_nav_extra
          string forumdisplay_nav_extra
          string group_bottom
          string forumdisplay_bottom
          string group_side_bottom
          string forumdisplay_side_bottom
      group_list.htm
          string forumdisplay_postbutton_top
          string forumdisplay_filter_extra
          array forumdisplay_thread
          string forumdisplay_postbutton_bottom
      group_my.htm
          string my_header
          string my_bottom
          string my_side_top
          string my_side_bottom
      group_right.htm
          string group_index_side
          string group_side_top
          string forumdisplay_side_top
      index.htm
          string index_header
          string index_top
          string index_bottom
          string index_side_top
          string index_side_bottom
      type.htm
          string index_top
          array index_grouplist
          string index_bottom
          string index_side_top
          string index_side_bottom

    家园(home/)
      spacecp_avatar.htm
          string spacecp_avatar_top
          string spacecp_avatar_bottom
      spacecp_blog.htm
          string spacecp_top
          string spacecp_middle
          string spacecp_bottom
      spacecp_credit_base.htm
          string spacecp_credit_top
          string spacecp_credit_extra
          string spacecp_credit_bottom
      spacecp_credit_log.htm
          string spacecp_credit_top
          string spacecp_credit_bottom
      spacecp_privacy.htm
          string spacecp_privacy_top
          string spacecp_privacy_base_extra
          string spacecp_privacy_feed_extra
          string spacecp_privacy_bottom
      spacecp_profile.htm
          string spacecp_profile_top
          string spacecp_profile_extra
          string spacecp_profile_bottom
      spacecp_promotion.htm
          string spacecp_promotion_top
          string spacecp_promotion_bottom
      spacecp_usergroup.htm
          string spacecp_usergroup_top
          string spacecp_usergroup_bottom
          string spacecp_usergroup_top
          string spacecp_usergroup_bottom
          string spacecp_usergroup_top
          string spacecp_usergroup_bottom
      space_album_pic.htm
          string space_albumpic_top
          string space_albumpic_bottom
          string space_albumpic_op_extra
          string space_albumpic_face_extra
      space_album_view.htm
          string space_album_op_extra
      space_blog_list.htm
          array space_blog_list_status
      space_blog_view.htm
          string space_blog_title
          string space_blog_op_extra
          string space_blog_face_extra
      space_card.htm
          string space_card_top
          string space_card_baseinfo_middle
          string space_card_baseinfo_bottom
          string space_card_option
          string space_card_magic_user
          string space_card_bottom
      space_comment_li.htm
          array space_blog_comment_op
          string space_blog_comment_bottom
      space_doing.htm
          string space_doing_top
          string space_doing_bottom
      space_friend.htm
          string space_interaction_extra
      space_header.htm
          string global_usernav_extra1
          string global_usernav_extra2
      space_home.htm
          string home_home_side_bottom
          string home_home_top
          string home_home_navlink
          string home_home_bottom
      space_menu.htm
          string space_menu_extra
      space_profile.htm
          string space_profile_baseinfo_top
          string space_profile_baseinfo_middle
          string space_profile_baseinfo_bottom
          string space_profile_extrainfo
      space_share_li.htm
          array space_share_comment_op
      space_wall.htm
          string space_wall_face_extra

    注册/登录(member/)
      login.htm
          string logging_top
          string logging_input
          string logging_side_top
          string logging_side
      login_simple.htm
          string global_login_extra
      register.htm
          string register_side_top
          string register_top
          string register_input
          string register_bottom

    门户(portal/)
      portalcp_article.htm
          string portalcp_top
          string portalcp_extend
          string portalcp_middle
          string portalcp_bottom

    排行榜(ranklist/)
      side_left.htm
          string ranklist_nav_extra

    搜索(search/)
      album.htm
          string album_top
          string album_bottom
      blog.htm
          string blog_top
          string blog_bottom
      footer.htm
          string global_footer
      forum.htm
          string forum_top
          string forum_bottom
      forum_adv.htm
          string forum_top
      group.htm
          string group_top
          string group_bottom
      header.htm
          string global_usernav_extra1
          string global_usernav_extra2
      portal.htm
          string portal_top
          string portal_bottom

    应用(userapp/)
      userapp_app.htm
          string userapp_app_top
          string userapp_app_bottom
      userapp_index.htm
          string userapp_index_top
          string userapp_index_bottom
      userapp_menu_list.htm
          string userapp_menu_top
          string userapp_menu_middle
          string userapp_menu_bottom

    手机全局(mobile/common/)
      footer.htm
          string global_footer_mobile
      header.htm
          string global_header_mobile

    手机论坛(mobile/forum/)
      discuz.htm
          string index_top_mobile
          string index_middle_mobile
          string index_bottom_mobile
      forumdisplay.htm
          string forumdisplay_top_mobile
          array forumdisplay_thread_mobile
          string forumdisplay_bottom_mobile
      viewthread.htm
          string viewthread_top_mobile
          array viewthread_posttop_mobile
          array viewthread_postbottom_mobile
          string viewthread_bottom_mobile

特殊主题模块开发

  • 特殊主题模块用于创建一个特殊主题,特殊主题类型脚本格式
    <?php

    class threadplugin_identifier {

    var $name = \'XX主题\'; //主题类型名称
    var $iconfile = \'icon.gif\'; //发布主题链接中的前缀图标
    var $buttontext = \'发布xx主题\'; //发帖时按钮文字

    function newthread($fid) {
    return ...;
    }

    function newthread_submit($fid) {

    }

    function newthread_submit_end($fid, $tid) {

    }

    function editpost($fid, $tid) {
    return ...;
    }

    function editpost_submit($fid, $tid) {

    }

    function editpost_submit_end($fid, $tid) {

    }

    function newreply_submit_end($fid, $tid) {

    }

    function viewthread($tid) {
    return ...;
    }
    }

    ?>

    identifier
    插件的唯一标识符,在插件设置中设置。

  • 函数名以及含义

     

    函数名 含义
    newthread 发主题时页面新增的表单项目,通过 return 返回即可输出到发帖页面中
    newthread_submit 主题发布后的数据判断
    newthread_submit_end 主题发布后的数据处理
    editpost 编辑主题时页面新增的表单项目,通过 return 返回即可输出到编辑主题页面中
    editpost_submit 主题编辑后的数据判断
    editpost_submit_end 主题编辑后的数据处理
    newreply_submit_end 回帖后的数据处理
    viewthread 查看主题时页面新增的内容,通过 return 返回即可输出到主题首贴页面中
扩展项目模块开发

  • 扩展项目模块可以在社区的特定位置扩展出新的功能,通常用于扩展新的设置项目。
    扩展项目的脚本文件以 .inc.php 结尾(如 test.inc.php),模版为固定文件名,位于插件目录的 template/ 子目录中,文件名与脚本名同名(如 test.htm),扩展名为 .htm。
    添加相应的扩展项目模块时,需注明程序模块、菜单名称。例如我们添加个人面板项目,程序模块为 test,菜单名称是“测试”,当插件启用后,个人面板即家园的设置中会出现“测试”拓展项目。
  • 小提示
    在新插件内核中,通过 plugin.php 方式访问的插件可直接通过 plugin.php?id=xxx:yyy 方式调用而无需再在后台定义为普通脚本模块,只要 source/plugin/xxx/yyy.inc.php 文件存在即可。如果 xxxyyy 同名,可直接通过 plugin.php?id=xxx 方式访问。
第三方DIY模块拓展类的开发

  • 内置PHP类扩展方式:
    • 脚本目录:

      source/class/block/[模块大分类目录] ,此目录需要自行创建

    • 必需的脚本:

      1. source/class/block/[模块大分类目录]/blockclass.php, 此文件为该目录中必需存在的文件,其内容为:

      	<?php
      $blockclass = array(
      \'name\' => \'模块大分类名\', //为此目录定义一个名字
      );
      ?>

      2. source/class/block/[模块大分类目录]/block_name.php
      注意:脚本文件名必需以
      block_
      开头,且类名必需和文件名一样。

    • 语言包位置(非必需):

      source/language/block/lang_
      name
      .php

    source/class/block/[模块大分类目录]/block_name.php内容示例:

    <?php

    class block_name {

    /**
    * 必须!
    * 返回本数据调用类的显示名称(显示在创建模块时选择“模块数据”的下拉列表里)
    * @return <type>
    */
    function name() {
    return \'示例数据类\';
    }

    /**
    * 必须!
    * 返回一个数组: 第一个值为本数据类所在的模块分类;第二个值为模块分类显示的名称(显示在 DIY 模块面板)
    * @return <type>
    */
    function blockclass() {
    return array(\'sample\', \'示例分类\');
    }

    /**
    * 必须!
    * 返回数据类中可供“模块样式”使用的字段。
    * 格式见示例:
    * name 为该字段的显示名称
    * formtype 决定编辑单条数据时该字段的显示方式: 类型有: text, textarea, date, title, summary, pic; 详见 portalcp_block.htm 模板(搜 $field[formtype] )
    * datatype 决定该字段的数据展示,类型有: string, int, date, title, summary, pic; 详见 function_block.php 中 block_template 函数
    * @return <type>
    */
    function fields() {
    return array(
    \'field1\' => array(\'name\' => \'示例字段1\', \'formtype\' => \'text\', \'datatype\' => \'string\'),
    \'field2\' => array(\'name\' => \'示例字段2\', \'formtype\' => \'title\', \'datatype\' => \'title\'),
    );
    }

    /**
    * 必须!
    * 返回使用本数据类调用数据时的设置项
    * 格式见示例:
    * title 为显示的名称
    * type 为表单类型, 有: text, password, number, textarea, radio, select, mselect, mradio, mcheckbox, calendar; 详见 function_block.php 中 block_makeform() 函数
    * @return <type>
    */
    function getsetting() {
    return array(
    \'param1\' => array(
    \'title\' => \'数据调用参数1\',
    \'type\' => \'text\',
    \'default\' => \'\'
    ),
    \'param2\' => array(
    \'title\' => \'数据调用参数2\',
    \'type\' => \'mcheckbox\',
    \'value\' => array(
    array(\'1\', \'选项1\'),
    array(\'2\', \'选项2\'),
    ),
    \'default\' => \'1\'
    ),
    );
    }

    /**
    * 必须!
    * 处理设置参数,返回数据
    * 返回数据有两种:
    * 一种是返回 html,放到模块 summary 字段,直接显示; 返回格式为: array(\'html\'=>\'返回内容\', \'data\'=>null)
    * 一种是返回 data,通过模块样式渲染后展示,返回的数据应该包含 fields() 函数中指定的所有字段; 返回格式为: array(\'html\'=>\'\', \'data\'=>array(array(\'title\'=>\'value1\'), array(\'title\'=>\'value2\')))
    * 特别的:
    * parameter 参数包含 getsetting() 提交后的内容; 并附加了字段:
    * items ,为用户指定显示的模块数据条数;
    * bannedids ,为用户选择屏蔽某数据时记录在模块中的该数据 id。 应该在获取数据时屏蔽该数据;
    *
    * 如果返回的数据给 data, 那么应该包含 fields() 函数指定的所有字段。并附加以下字段:
    * id 标志该数据的 id,如果用户屏蔽某数据时,会将该数据的 id 添加到 parameter[bannedids] 里
    * idtype 标志该数据的 idtype
    *
    * @param <type> $style 模块样式(见 common_block_style 表)。 可以根据模块样式中用到的字段来选择性的获取/不获取某些数据
    * @param <type> $parameter 用户对 getsetting() 给出的表单提交后的内容。
    * @return <type>
    */
    function getdata($style, $parameter) {

    // 返回summary
    return array(\'html\' => \'<p>这是一个演示模块数据类</p>\', \'data\' => null);

    // 返回数据
    // 需要注意: 除 id,idtype, title, url, pic, picflag, summary 几个字段外,其它字段需要放到 fields 数组里。 可以参考系统内置模块类 source/class/block/block_thread.php
    return array(\'html\'=>\'\', \'data\' => array(
    array(
    \'id\' => \'1\',
    \'idtype\' => \'sampleid\',
    \'title\' => \'title1\',
    \'url\' => \'#\',
    \'pic\' => \'nophoto.gif\',
    \'picflag\' => \'1\',
    \'summary\' => \'\',
    \'fields\' => array(
    \'field1\' => \'value1\'
    )
    )
    ));
    }

    }

    ?>
  • 第三方 C/S 扩展方式:
    此扩展方式需要第三方提供一个服务端应用程序接口,为使用该服务的客户端提供数据。
    服务端提供的数据必需为 XML 格式的数据,具体的 XML 规范请参考下面的详细说明。
    • XML 规范共分为两类:
      1、配置规范
      说明:
      客户端以 GET 的方式向服务器端提交以下参数请求此 XML 文档,:
      * op=getconfig,此参数表示客户端要请求配置文档;
      * clientid,客户端ID(服务器分配给客户端的ID);
      * charset,客户端的数据编码
      * sign=签名,如果服务器端没有设置通信密钥则此值为空,如果服务器端不使用签名则此值为通信密钥;签名机制
      此规范包括两部分:Title和Data分部,
      其中Title分部是固定的且区分大小写:<item id=”Title”><![CDATA[Discuz! Block]]></item>
      Data分部主要包括5个属性:
      a、version: 版本号(必需)
      b、name: 模块名(必需)
      c、fields: 可显示的字段,在模块样式中使用(必需)
      * name 为该字段的显示名称
      * formtype 决定编辑单条数据时该字段的显示方式: 类型有: text, textarea, date, title, summary, pic;
      * datatype 决定该字段的数据展示,类型有: string, int, date, title, summary, pic;
      d、getsetting: 可设置和接收的参数(必需)
      * title 为显示的名称
      * type 为表单类型, 有: text, password, number, textarea, radio, select, mselect, mradio, mcheckbox, calendar;
      f、style: 内置的显示样式(非必需)
      配置规范 XML 文档示例如下:
      <?xml version="1.0" encoding="ISO-8859-1"?>
      <root>
      <item id="Title"><![CDATA[Discuz! Block]]></item>
      <item id="Data">
      <item id="version"><![CDATA[X1.5]]></item>
      <item id="name"><![CDATA[C/S 数据类]]></item>
      <item id="fields">
      <item id="url">
      <item id="name"><![CDATA[链接地址]]></item>
      <item id="formtype"><![CDATA[text]]></item>
      <item id="datatype"><![CDATA[string]]></item>
      </item>
      <item id="title">
      <item id="name"><![CDATA[标题]]></item>
      <item id="formtype"><![CDATA[title]]></item>
      <item id="datatype"><![CDATA[title]]></item>
      </item>
      <item id="pic">
      <item id="name"><![CDATA[图片]]></item>
      <item id="formtype"><![CDATA[pic]]></item>
      <item id="datatype"><![CDATA[pic]]></item>
      </item>
      <item id="summary">
      <item id="name"><![CDATA[简介]]></item>
      <item id="formtype"><![CDATA[summary]]></item>
      <item id="datatype"><![CDATA[summary]]></item>
      </item>
      <item id="author">
      <item id="name"><![CDATA[作者]]></item>
      <item id="formtype"><![CDATA[text]]></item>
      <item id="datatype"><![CDATA[text]]></item>
      </item>
      <item id="authorid">
      <item id="name"><![CDATA[作者ID]]></item>
      <item id="formtype"><![CDATA[text]]></item>
      <item id="datatype"><![CDATA[int]]></item>
      </item>
      <item id="field1">
      <item id="name"><![CDATA[字段1]]></item>
      <item id="formtype"><![CDATA[text]]></item>
      <item id="datatype"><![CDATA[string]]></item>
      </item>
      <item id="field2">
      <item id="name"><![CDATA[字段2]]></item>
      <item id="formtype"><![CDATA[title]]></item>
      <item id="datatype"><![CDATA[title]]></item>
      </item>
      </item>
      <item id="getsetting">
      <item id="param1">
      <item id="title"><![CDATA[数据调用参数1]]></item>
      <item id="type"><![CDATA[text]]></item>
      <item id="default"><![CDATA[]]></item>
      </item>
      <item id="param2">
      <item id="title"><![CDATA[数据调用参数2]]></item>
      <item id="type"><![CDATA[mcheckbox]]></item>
      <item id="value">
      <item id="0">
      <item id="0"><![CDATA[1]]></item>
      <item id="1"><![CDATA[选项1]]></item>
      </item>
      <item id="1">
      <item id="0"><![CDATA[2]]></item>
      <item id="1"><![CDATA[选项2]]></item>
      </item>
      </item>
      <item id="default"><![CDATA[1]]></item>
      </item>
      <item id="start">
      <item id="title"><![CDATA[起始数据行数]]></item>
      <item id="type"><![CDATA[text]]></item>
      <item id="default"><![CDATA[0]]></item>
      </item>
      </item>
      <item id="style">
      <item id="0">
      <item id="name"><![CDATA[模板名称]]></item>
      <item id="template"><![CDATA[<div class="module cl xl xl1"><ul>[loop]<li><em><a href="home.php?uid={authorid}"><FONT COLOR="RED">{author}</FONT></a></em><a href="{url}">{title}</a></li>[/loop]</ul></div>]]></item>
      </item>
      <item id="1">
      <item id="name"><![CDATA[模板名称红色]]></item>
      <item id="template"><![CDATA[<div class="module cl xl xl1"><ul>[loop]<li><em><font color="red"><a href="home.php?uid={authorid}">{author}</a></font></em><a href="{url}">{title}</a></li>[/loop]</ul></div>]]></item>
      </item>
      </item>
      </item>
      </root>

      2、数据规范
      可以返回两种数据:数据列表和HTML代码
      a、数据列表格式
      说明:
      客户端在请求数据时以 POST 的方式提交客户端设置的参数值,参数值包括在配置规范中可设置和接收的参数 getsetting 指定的所有字段,
      除了设置的参数外,系统会以 POST 的方式追加以下参数:
      * op=getdata ,此参数表示客户端要请求数据;
      * clientid ,客户端ID(服务器分配给客户端的ID);
      * op=getdata ,此参数表示客户端要请求数据;
      * items ,为用户指定显示的模块数据条数;
      * bannedids ,为用户选择屏蔽某数据时记录在模块中的该数据 id,多个 id 以半角分号(,)分隔。 应该在获取数据时屏蔽该数据;
      * charset,客户端的数据编码
      * sign ,数据签名,如果服务器端没有设置通信密钥则此值为空,如果服务器端不使用签名则此值为通信密钥;签名机制
      服务器端返回数据的 data 中应该包含 配置规范中可显示的字段 fields 指定的所有字段。并附加以下字段:
      * id 标志该数据的 id,如果用户屏蔽某数据时,会将该数据的 id 以 POST 的方式变量名为 bannedids,多个id以半角逗号(,)分隔提交到服务器端
      * picflag 如果有图片,则该值标志图片的类型,0 为 url、1 为本地、2 为 ftp 远程;如果图片是 Discuz! X 系统中的图片可以情况设置为 1 或 2,其它情况为 0
      需要注意: 除 id,title, url, pic, picflag, summary 几个字段外,其它字段需要放到 fields 数组里。

      <?xml version="1.0" encoding="ISO-8859-1"?>
      <root>
      <item id="html"><![CDATA[]]></item>
      <item id="data">
      <item id="0">
      <item id="id"><![CDATA[14]]></item>
      <item id="title"><![CDATA[xml_block_title14]]></item>
      <item id="url"><![CDATA[xml_server.php]]></item>
      <item id="pic"><![CDATA[nophoto.gif]]></item>
      <item id="picflag"><![CDATA[1]]></item>
      <item id="summary"><![CDATA[]]></item>
      <item id="fields">
      <item id="author"><![CDATA[xml_user14]]></item>
      <item id="authorid"><![CDATA[14]]></item>
      <item id="field1"><![CDATA[field1value14]]></item>
      <item id="field2"><![CDATA[field2value14]]></item>
      </item>
      </item>
      <item id="1">
      <item id="id"><![CDATA[15]]></item>
      <item id="title"><![CDATA[xml_block_title15]]></item>
      <item id="url"><![CDATA[xml_server.php]]></item>
      <item id="pic"><![CDATA[nophoto.gif]]></item>
      <item id="picflag"><![CDATA[1]]></item>
      <item id="summary"><![CDATA[]]></item>
      <item id="fields">
      <item id="author"><![CDATA[xml_user15]]></item>
      <item id="authorid"><![CDATA[15]]></item>
      <item id="field1"><![CDATA[field1value15]]></item>
      <item id="field2"><![CDATA[field2value15]]></item>
      </item>
      </item>
      </item>
      </root>

      b、HTML 代码格式

      <?xml version="1.0" encoding="ISO-8859-1"?>
      <root>
      <item id="html"><![CDATA[<div style="border:1px solid red;width:100px; height: 100px;">HTML CODE</div>]]></item>
      <item id="data"><![CDATA[]]></item>
      </root>
    • 服务端应用程序接口示例:

      以下提供一个 PHP 版本的程序示例:

      <?php

      define(\'CHARSET\', \'GBK\'); //服务器端数据编码
      require \'./source/class/class_xml.php\'; //XML格式的文档和array的相互转换的类
      error_reporting(7);

      $charset = $_GET[\'charset\'] ? $_GET[\'charset\'] : $_POST[\'charset\']; //客户端数据编码
      //数据转码
      if(strtoupper($charset) != CHARSET) {
      foreach($POST as $key => $value) {
      $POST[$key] = iconv($charset, CHARSET, $value);
      }
      foreach($GET as $key => $value) {
      $GET[$key] = iconv($charset, CHARSET, $value);
      }
      }

      $data = array(\'html\'=>\'\', \'data\'=>\'\'); //初始化要返回数据
      $sign = $_GET[\'sign\'] ? $_GET[\'sign\'] : $_POST[\'sign\']; //获取客户端请求数据的签名
      $clientid = $_GET[\'clientid\'] ? $_GET[\'clientid\'] : $_POST[\'clientid\']; //客户端ID

      $client = get_client_by_clientid($clientid); //得到客户端的相关信息
      if(empty($client)) { //客户端不存在
      exit(\'CLIENT_NOT_EXISTS\'); //直接返回失败
      }

      $datasign = \'\'; //数据签名
      if(!empty($_POST)) {
      unset($_POST[\'sign\']); //删除签名参数,此参数不参加签名计算
      $datasign = get_sign($_POST, $client[\'key\']); //计算数据的签名
      } else {
      unset($_GET[\'sign\']); //删除签名参数,此参数不参加签名计算
      $datasign = get_sign($_GET, $client[\'key\']); //计算数据的签名
      }

      if($datasign != $sign) { //签名不正确
      exit(\'SIGN_ERROR\'); //输入签名错误
      }

      if($_POST[\'op\'] == \'getdata\') { //判断是否为请求数据列表
      $datalist = $data = array();//数据列表
      $wherearr = array(); //SQL 条件数组

      //获取客户端POST参数
      $start = intval($_POST[\'start\']); //起始数据行数
      $limit = intval($_POST[\'items\']); //要显示多少条数
      $bannedids = addslashes($_POST[\'bannedids\']); //客户端屏蔽的IDS
      $param1 = addslashes($_POST[\'param1\']); //数据调用参数1,假设此值要求为string型
      $param2 = intval($_POST[\'param2\']); //数据调用参数2,假设此值要求为int型

      //处理参数1
      if(!empty($param1)){
      $wherearr[] = "fieldsparam1=\'$param1\'";
      }
      //处理参数2
      if(!empty($param2)) {
      $wherearr[] = "fieldsparam2=\'$param2\'";
      }
      //处理客户端屏蔽的IDS
      if(!empty($bannedids)) {
      $banids = explode(\',\', $bannedids);
      $wherearr[] = "csid NOT IN (".implode("\',\'", $banids)."\')";
      }
      $where = !empty($wherearr) ? \'WHERE \'.implode(\' AND \', $wherearr) : \'\'; //构造条件
      /*数据库相关处理
      $query = DB::query(\'SELECT * FROM \'.DB::table(\'tablename\')." $where LIMIT $start, $limit"); //SQL查询
      while($value = DB::fetch($query)) {
      //此处为数据处理逻辑代码
      $data[] = $value;
      }
      */

      //以下为临时测试数据,正式环境请根据自己的业务做相关调整
      $url = \'http://www.xxx.com/\';
      $data = range($start, $start + $limit);//构造临时的假数据
      foreach($data as $value) {
      //需要注意: 除 id, title, url, pic, picflag, summary 几个字段外,其它字段需要放到 fields 数组里。
      $datalist[] = array(
      \'id\' => $value,
      \'title\' => \'xml_block_title\'.$value, //标题
      \'url\' => $url.\'xml_server.php?csid=\'.$value, //链接地址
      \'pic\' => $url.\'/data/attachment/photo.gif\', //图片地址
      \'picflag\' => \'0\', //0为url 1为本地 2 为ftp远程;如果图片是DX系统中的图片可以情况设置为1或2,其它情况为0
      \'summary\' => \'\', //简介
      \'fields\' => array( //配置规范中fields中指定的字段
      \'author\' => \'xml_user\'.$value,
      \'authorid\' => $value,
      \'field1\' => \'field1value\'.$value,
      \'field2\' => \'field2value\'.$value
      )
      );
      }
      $data[\'data\'] = $datalist;

      //如果要返回HTML代码,可直接使用以下代码
      //$data[\'html\'] = \'HTML CODE\';
      $xml = array2xml($data); //转换为XML文档
      } else if($_GET[\'op\'] == \'getconfig\') {
      $xml = file_get_contents(\'block_xml_sample.xml\');//block_xml_sample.xml文件中的内容为 配置规范XML文档示例 的内容
      } else {
      $xml = \'NO_OPERATION\';
      }
      ob_end_clean();
      @header("Expires: -1");
      @header("Cache-Control: no-store, private, post-check=0, pre-check=0, max-age=0", FALSE);
      @header("Pragma: no-cache");
      header("Content-type: text/xml");
      echo $xml;
      exit();

      /**
      * 获得客户端信息
      * @param $clientid
      * @return array 客户端信息数组
      */
      function get_client_by_clientid($clientid){
      $client = array();
      $clientid = intval($clientid);
      if($clientid) {

      /*数据库相关处理
      $client = DB::fetch_first(\'SELECT * FROM \'.DB::table(\'clienttable\')." clientid=\'$clientid\'"); //SQL查询
      */

      //以下为临时测试数据,正式环境请根据自己的业务做相关调整
      //模拟数据库
      $CLIENTSDB = array(
      \'100000\' => array(
      \'clientid\' => \'100000\',
      \'key\' => \'*654%#(asd94\',
      ),
      \'200000\' => array(
      \'clientid\' => \'200000\',
      \'key\' => \'1#9!(@@34#94\',
      ),
      \'300000\' => array(
      \'clientid\' => \'300000\',
      \'key\' => \'7$@^8^$7as89\',
      ),
      \'400000\' => array(
      \'clientid\' => \'400000\',
      \'key\' => \'23@#86^%4&32\',
      ),
      );
      $client = isset($CLIENTSDB[$clientid]) ? $CLIENTSDB[$clientid] : array();
      }
      return $client;
      }


      /**
      * 生成签名
      * @param array $para 参数数组
      * @param string $key 加密密钥
      * @return string 签名
      */
      function get_sign($para, $key = \'\'){
      ksort($para);
      $signarr = array();
      foreach($para as $k => $v) {
      $signarr[] = $k.\'=\'.$v;
      }
      $sign = implode(\'&\', $signarr);
      $sign = md5($sign.$key);
      return $sign;
      }
      ?>
    • 签名机制:

       

      计算方法为:待签名数据 + 通信密钥(服务器端提供给客户端的通信密钥)的MD5值作为签名。
      所有HTTP请求中传递的参数(除sign外)按照参数名称字符升序的顺序串联起来(例如:k1=v1&k2=v2&k3=v3),构成待签名数据。
      例如:请示配置文档需要以下参数:
      op=getconfig
      clientid=10000
      charset=utf-8
      那么待签名数据就是:clientid=10000&op=getconfig&charset=utf-8。
      签名注意事项:
      无论参数是否有值,只要在请示中传递即包含到待签名数据中。
      根据 HTTP 协议要求,传递参数的值中如果存在特殊字符(如:&、@等),那么
      该值需要做 URL Encoding,这样请求接受方才能接受到正确的参数值。这种情况
      下,做签名时使用的应该是原生值而不是 encoding 之后的值。

第三方拓展类的开发

  • 广告类
    脚本位置:source/class/adv/adv_
    name
    .php
    语言包位置:source/language/adv/lang_
    name
    .php

    <?php

    class adv_name {

    var $version = \'1.0\';//脚本版本号
    var $name = \'name\';//广告类型名称 (可填写语言包项目)
    var $description = \'desc\';//广告类型说明 (可填写语言包项目)
    var $copyright = \'Comsenz Inc.\';//版权 (可填写语言包项目)
    var $targets = array(\'portal\', \'home\', \'member\', \'forum\', \'group\', \'userapp\', \'plugin\', \'custom\');//广告类型适用的投放范围
    var $imagesizes = array(\'120x60\', \'120x240\');//图片广告推荐大小

    function getsetting() {//返回设置项目
    $settings = array(
    \'text\' => array(
    \'title\' => \'text_title\',//设置项目名称 (可填写语言项目)
    \'type\' => \'mradio\',//项目类型
    \'value\' => array(),//项目选项
    \'default\' => 0,//项目默认值
    )
    );
    return $settings;
    }

    function setsetting(&$advnew, &$parameters) {//保存设置项目
    }

    function evalcode() {//广告显示时的运行代码
    return array(
    //检测广告是否投放时的代码
    \'check\' => \'
    if(condition) {
    $checked = false;
    }\',
    //广告显示时的代码 (随机调用投放的广告)
    \'create\' => \'$adcode = $codes[$adids[array_rand($adids)]];\',
    );
    }
    }

    ?>
  • 道具类
    脚本位置:source/class/magic/magic_
    name
    .php
    语言包位置:source/language/magic/lang_
    name
    .php

    <?php

    class magic_name {

    var $version = \'1.0\';//脚本版本号
    var $name = \'name\';//道具名称 (可填写语言包项目)
    var $description = \'desc\';//道具说明 (可填写语言包项目)
    var $price = \'10\';//道具默认价格
    var $weight = \'10\';//道具默认重量
    var $copyright = \'Comsenz Inc.\';//版权 (可填写语言包项目)

    function getsetting() {//返回设置项目
    $settings = array(
    \'text\' => array(
    \'title\' => \'text_title\',//设置项目名称 (可填写语言项目)
    \'type\' => \'mradio\',//项目类型
    \'value\' => array(),//项目选项
    \'default\' => 0,//项目默认值
    )
    );
    return $settings;
    }

    function setsetting(&$advnew, &$parameters) {//保存设置项目
    }

    function usesubmit($magic, $parameters) {//道具使用
    }

    function show($magic) {//道具显示
    }

    }

    ?>
  • 任务类
    脚本位置:source/class/task/task_
    name
    .php
    语言包位置:source/language/task/lang_
    name
    .php

    <?php

    class task_name {

    var $version = \'1.0\';//脚本版本号
    var $name = \'name\';//任务名称 (可填写语言包项目)
    var $description = \'desc\';//任务说明 (可填写语言包项目)
    var $copyright = \'Comsenz Inc.\';//版权 (可填写语言包项目)
    var $icon = \'\';//默认图标
    var $period = \'\';//默认任务间隔周期
    var $periodtype = 0;//默认任务间隔周期单位
    var $conditions = array(//任务附加条件
    \'text\' => array(
    \'title\' => \'text_title\',//设置项目名称 (可填写语言项目)
    \'type\' => \'mradio\',//项目类型
    \'value\' => array(),//项目选项
    \'default\' => 0,//项目默认值
    \'sort\' => \'complete\',//条件类型 (apply:申请任务条件 complete:完成任务条件)
    ),
    );

    function preprocess($task) {//申请任务成功后的附加处理
    }

    function csc($task = array()) {//判断任务是否完成 (返回 TRUE:成功 FALSE:失败 0:任务进行中进度未知或尚未开始 大于0的正数:任务进行中返回任务进度)
    }

    function sufprocess($task) {//完成任务后的附加处理
    }

    function view($task, $taskvars) {//任务显示
    }

    function install() {//任务安装的附加处理
    }

    function uninstall() {//任务卸载的附加处理
    }

    function upgrade() {//任务升级的附加处理
    }

    }

    ?>
  • 验证问答类
    脚本位置:source/class/secqaa/secqaa_
    name
    .php
    语言包位置:source/language/secqaa/lang_
    name
    .php

    <?php

    class secqaa_name {

    var $version = \'1.0\';//脚本版本号
    var $name = \'name\';//验证问答名称 (可填写语言包项目)
    var $description = \'desc\';//验证问答说明 (可填写语言包项目)
    var $copyright = \'Comsenz Inc.\';//版权 (可填写语言包项目)

    function make(&$question) {//返回安全问答的答案和问题 ($question 为问题,函数返回值为答案)
    }

    }

    ?>
CSS 继承规范

  • Discuz!X 中 CSS 文件会在缓存时按照以下顺序进行合并:
    1、template/default/*.css 文件
    2、当默认模版是非默认模版时,template/模版目录/extend_*.css 文件 或 template/模版目录/*.css
    3、当某插件启用时,source/plugin/插件目录/extend_*.css 文件
    因此非默认模版目录中的 CSS 属性将继承默认模版中的 CSS 属性,插件目录中的 CSS 文件将继承前二者的 CSS 属性。
插件安装、卸载、升级脚本的设计

  • 安装、卸载
    插件作者可以设计 2 个脚本文件用于插件的安装和卸载,文件名任意。脚本中可用 runquery() 函数执行 SQL 语句,表名可以直接写“cdb_”。插件作者只需在导出的 XML 文件结尾加上安装、卸载脚本的文件名即可
    		<item id="installfile"><![CDATA[install.php]]></item>
    <item id="uninstallfile"><![CDATA[uninstall.php]]></item>

    </item>
    </root>

    安装、卸载程序中可随意设计页面的跳转,只要在插件安装、卸载结束时候输出添加以下代码即可。

    $finish = TRUE;
  • 升级
    插件作者可以设计一个脚本文件用于插件的升级,文件名任意。脚本中可用 runquery() 函数执行 SQL 语句,表名可以直接写“cdb_”。插件作者只需在导出的 XML 文件结尾加上升级脚本的文件名即可
    		<item id="upgradefile"><![CDATA[upgrade.php]]></item>
    </item>
    </root>

    升级程序中可通过 $fromversion 和 $toversion 变量判断升级的具体版本号,并随意设计页面的跳转,只要在插件升级结束时候输出添加以下代码即可。

    $finish = TRUE;

    插件的当前版本号位于 XML 文件的以下分支中,可自行更改。

    	<item id="plugin">
    ......
    <item id="version"><![CDATA[当前版本]]></item>
    ......
    </item>
  • 检测
    插件作者可以设计一个脚本文件用于插件在安装、卸载、升级操作前的检测,文件名任意。插件作者只需在导出的 XML 文件结尾加上检测脚本的文件名即可
    		<item id="checkfile"><![CDATA[check.php]]></item>
    </item>
    </root>
  • 授权协议、插件介绍
    插件在安装的时候您可以自定义授权信息文本,文本支持 Discuz! 代码,站长同意后才能安装插件。如果插件存在后台管理界面或者变量配置,那么插件介绍文本会显示在插件后台页面中。插件作者只需在导出的 XML 文件结尾加上以下内容即可
    		<item id="license"><![CDATA[授权协议文本]]></item>
    <item id="intro"><![CDATA[插件介绍文本]]></item>

    </item>
    </root>
  • 其他论坛数据导入
    插件安装时可以直接导入一个或多个论坛数据,这些论坛数据包括表情(smilies)、风格(styles)的数据。在导出的 XML 文件结尾加上需要导入数据的类型和数据文件名即可,多个文件名用逗号(“,”)分隔。
    		<item id="importfile">
    <item id="smilies"><![CDATA[discuz_smilies_test.xml]]></item>
    <item id="styles"><![CDATA[discuz_styles_test.xml]]></item>
    </item>

    </item>
    </root>
  • 小提示
    如果导出的 XML 文件名以 SC_GBK、SC_UTF8、TC_BIG5、TC_UTF8 结尾,显示的时候将直接显示为“简体”、“繁体”、“UTF8”等字样。
插件模板和语言包的设计

  • 模板、语言包
    插件中的语言包可以写到导出的 XML 文件结尾,这样在插件安装的时候会自动把语言包生成文件名为 data/plugindata/
    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 为安装、升级、卸载脚本用的语言包。
    插件的模板文件可以放置在 plugin 目录下,如 source/plugin/hooktest/template 子目录下。页面嵌入模块的脚本中可用以下方式调用此目录下的模板。
    脚本中调用插件模版:

    include template(\'hooktest:index_top\');

    模版中调用插件模版:

    {template hooktest:index_top}

    插件模版文件中的语言包中通过 {lang identifier : langvar } 方式调用,例如:

    <!--{block return}-->
    {lang hooktest:text}
    <!--{/block}-->

    上例中对应的变量为语言包文件 data/plugin/ identifier .lang.php 中 $templatelang[\’hooktest\’][\’text\’] 的值。脚本中引用语言包无需包含语言包文件,可直接使用变量。如插件脚本中可用变量 $scriptlang[\’hooktest\’][\’text\’],安装脚本中可用变量 $installlang[\’hooktest\’][\’text\’]。

插件注册及插件新版本提示

为了保护插件的合法权益,你可以把设计好的插件到官方的扩展中心(http://addons.discuz.com)
进行注册,注册后你将拥有此插件的唯一所有权,通过插件 ID
可以查询到你自己指定的发布页面地址,同时你还可以享受到插件新版本提示功能。插件新版本提示需要插件作者把自己发布插件的相关文件生成 MD5
校验码,然后把校验码提交到扩展中心。这样站长在后台即可随时检测到你插件是否存在新版本。

插件校验码生成函数:

function createValidator($pluginid, $md5files) {

define(\'IN_DISCUZ\', true);
require_once \'source/class/class_xml.php\';
require_once \'source/discuz_version.php\';

$plugindir = \'source/plugin/\'.$pluginid.\'/\';
$md5 = \'\';
foreach($md5files as $file) {
$md5 .= md5_file($file);
}

echo md5(md5($md5).$pluginid);

$xml = array(
\'Title\' => \'Discuz! Plugin Validator\',
\'Version\' => DISCUZ_VERSION,
\'Data\' => $md5files,
);

if($fp = @fopen($plugindir.\'validator.xml\', \'wb\')) {
fwrite($fp, array2xml($xml));
fclose($fp);
}

}

使用范例:

$md5files = array(
\'source/plugin/myrepeats/switch.inc.php\',
\'source/plugin/myrepeats/admincp.inc.php\',
\'source/plugin/myrepeats/discuz_plugin_myrepeats.xml\',
\'source/plugin/myrepeats/memcp.inc.php\',
);

createValidator(\'myrepeats\', $md5files);
编写插件的原则与注意事项

请在您动手编写插件之前,还需要仔细的阅读以下原则,遵循这些原则,将有效的避免可能发生的问题:

 

  • 所有与插件的程序,包括其全部的前后台程序,请全部放入 source/plugin/ 目录中,同时在插件的安装说明中指出,插件的文件需要复制到哪些目录。为了避免与其他插件冲突,请尽量建立 source/plugin/ 下的子目录,并将插件程序放置于子目录下,这样您编写的插件将获得更好的兼容性。
  • 如果您的插件包含“导航栏”模块,该模块将统一用 plugin.php?identifier=xxx&module=yyy
    的方式调用,请在相应链接、表单中使用此方式。其中 xxx 为插件的惟一标识符,yyy 为模块名称。前台插件外壳程序 plugin.php
    已经加载了通用初始化模块 /source/class/class_core.php,不需再次引用。
  • 如果您的插件包含“管理中心”模块,该模块将统一用
    admin.php?action=plugins&identifier=xxx&pmod=yyy
    的方式调用,请在相应链接、表单中使用此方式。其中 xxx 和 yyy 的定义与“导航栏”模块中的相同。系统还允许用
    admin.php?action=plugins&edit=$edit&pmod=$mod
    的方式来生成链接和表单地址,$edit 和 $mod 变量已经被插件后台管理接口赋值,因此将这两个变量值带入 URL
    中也是被支持的。由于后台模块是被 admin.php 调用,因此已加载了通用初始化模块 /source/class/class_core.php
    并进行了后台管理人员权限验证,因此模块程序中可直接写功能代码,不需再进行验证。
  • 请勿绕过插件的前后台外壳(plugin.php 和
    admin.php)而以直接调用某程序的方式编写插件,因为这样既导致了用户使用不便,代码冗余和不规范,同时又产生了因验证程序考虑不周到而带来的安
    全隐患。您可以在任何地方,包括链接、表单等处方便的使用上述 URL 地址对插件模块进行调用。
  • 所有与插件有关的程序,包括全部的前后台程序,因全部使用外壳调用,请务必在第一行加入
    	if(!defined(\'IN_DISCUZ\')) {
    exit(\'Access Denied\');
    }

    以免其被 URL 直接请求调用,产生安全问题。

  • 一般情况下,您发布插件请使用插件导出的功能,以方便使用者一次性导入插件的配置数据,极特殊的情况下,也可以分步骤告知使用者如何进行插件配置管理和安装此插件。
  • 如果功能独立,请尽量使用单独程序的方式编写插件(即外挂型插件),而尽量少的对论坛本身代码进行修改,这将为使用者今后的升级带来很大方便。
  • 您可以修改 Discuz! 本身的数据结构,但更推荐在不很影响效率的前提下将插件数据用另外的数据表存储,因为不能排除您增加的字段或索引和今后版本 Discuz! 核心数据字段重名的可能。在任何情况下,请不要删除 Discuz! 标准版本数据结构中已有的字段或索引
  • 请在插件说明书中对插件做以详尽的描述,例如增加了哪些字段、哪些表,修改了或新增了哪些程序,版本兼容性,后续支持的提供方式(例如不提供支 持,或以什 么样的方式提供)。如果方便,请尽可能提供插件的卸载方法,例如去除哪些字段、删除哪些新增的程序、将哪些被插件修改的程序恢复原状等等,使用者会感激您 为此付出的辛勤劳动,甚至愿意支付相应的费用支持您未来的发展。
  • 如果插件使用另外的数据表存储,请在插件管理中准确的设置插件所使用的数据表名称(不包含前缀),这样用户在备份数据的时候,能够把插件数据一同备份。
  • Discuz! 内置了 8 种自定义积分,存储于 common_member 表中的 extcredits1 至 extcredits8 字段中,类型为有符号整数。
意见反馈

插件接口是 Discuz! 开发组为了方便插件设计、安装和使用而专门开发,虽然经过长期的优化和改进,可能仍然会有不够合理或不够完善的地方,欢迎各位插件程序员在使用此接口的过程中,为我们提出意见和建议,感谢您的支持。

欢迎加入“Discuz! 开发技术指导”QQ 群一起探讨 Discuz! X 开发中您遇到的问题。

版权声明:本文为yonge原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/yonge/articles/2474477.html