分享

[插件] 实例讲解

[复制链接]

实例讲解

发表于 2020-1-5 08:55:45 来自 插件 阅读模式 倒序浏览
1224 0 查看全部
马甲插件是一款能够让用户在论坛中以多个帐号身份进行使用、交流的插件。论坛开启马甲插件后, 用户无需进行退出操作即可迅速切换到其他帐号。下面以马甲插件为例进行插件开发说明。
前台实现帐号切换、马甲设置等功能, 如图:

后台实现管理、锁定、查找等管理功能, 如图:
插件开发流程介绍说明:
  • 1.首先在 Discuz! 配置文件 config/config_global.php 底部加入代码:$_config['plugindeveloper'] = 1;
    开启开发者模式。( 论坛后台插件设置项会开启开发者模式。 新增加的插件也会有设计模式的设置选项。)
  • 2.管理员进入后台应用 -> 插件 -> 插件设计, 开始进行新插件的设计, 添加初始化插件的基本信息。

    添加完基本信息后, 在插件文件夹目录 source/plugin/ 下新建对应的文件夹(这里我们新建 myrepeats/ 文件夹)。 如果插件开发过程中需要语言包, 则后台开启设置后在 data/plugindata/ 下添加语言包文件, myrepeats.lang.php (myrepeats为插件初始化中添加的唯一标识符)来存储我们插件开发过程中用到的语言。

    添加的语言包文件,初始化状态如下:
<?php

$scriptlang['myrepeats'] = array(
        'login_strike' => "密码错误次数过多,请重新设置马甲账号信息并在 15 分钟后再尝试切换。",

        /* 含有变量值的语言包一般用在脚本文件中调用, 其中变量可以在showmessage(), lang()等函数中某个参数以数组        键值对的形式指定替换值。*/
        例如:showmessage('myrepeats:adduser_succeed', 'home.php?mod=spacecp&ac=plugin&id=myrepeats:        memcp', array('usernamenew' => stripslashes($usernamenew))); */
        'adduser_succeed' => "马甲账号 {usernamenew} 已成功添加。",
);


$templatelang['myrepeats'] = array(
        'myrepeats' => "我的马甲",
        'adduser' => "添加马甲账号",
);

$installlang['myrepeats'] = array(
       
);

?>
  • $scriptlang数组中存储脚本文件的语言包,$templatelang 数组中存储模版文件的语言包,$installlang 数组中存储安装、升级、卸载脚本用的语言包。
  • 3.接下来我们需要添加我们开发过程中需要用到的程序模块文件。明确需要使用 Discuz! 插件模块中的以下几项:
    • 扩展项目 个人面板:可在个人面板上部增加一个菜单项。(实现个人设置面板部分)
    • 程序脚本 页面嵌入:设置一个包含页面嵌入脚本的模块,模块文件名指派为 source/plugin/插件目录/插件模块名.class.php”。(通过嵌入点来实现头部用户信息中, 快捷切换马甲的效果。)
    • 扩展项目 管理中心:可在后台 -> 插件栏目中为此插件增添一个管理模块。(在后台为我们的马甲插件添加一个管理项目。)


    在刚才新建好的插件中添加以上三个插件模块, 如图:
  • 以上指定好模块文件后, 我们需要在对应的插件文件夹 source/plugin/myrepeats/ 里新建我们刚才添加的模块脚本文件。
  • 接下来添加插件中需要用到的变量。(马甲插件只需要一个用户组是否开启的状态。)如图:
  • 根据需要开发的插件功能, 设计数据表结构。在自己的开发环境下建好数据表, 以便在后面的开发过程中使用。当你新建完数据库之后, 需要根据你构建的数据库, 在 source/plugin/插件目录/table/ 下构建你的数据库类对象, 以便在后续的开发中使用。(每一个数据表, 都需要构建一个单独的数据库类对象。)
  • 现在前期工作基本完成了, 接下来开始编写脚本文件,开发需要的功能了。以马甲插件为例, 现在开始在页面头部用户资料栏添加一个马甲切换的功能。此功能需要用页面嵌入的模块来开发。 前期准备工作中已经新建了这个模块文件, 即 myrepeats.class.php 脚本文件。下面我们来看一看这个脚本文件的代码实现:
<?php
/* 所有与插件有关的程序,包括全部的前后台程序,因全部使用外壳调用, 请务必在第一行加入以下三行代码, 以免其被 URL 直接请求调用,产生安全问题。 */
if(!defined('IN_DISCUZ')) {
        exit('Access Denied');
}

/* 全局嵌入点类(必须存在)*/
class plugin_myrepeats {
        var $value = array(); //初始化返回值变量。

        /* 嵌入点对象初始化函数, 属于php面向对象机制特性。这里的函数名和类名是一致的, 在初始化类的时候以便执行这        个函数,对$value进行赋值,以便下面的global_usernav_extra1()函数调用。*/
        function plugin_myrepeats() {
                global $_G;
                if(!$_G['uid']) {
                        return;
                }

                /* 读取可以使用马甲的用户组 usergroups 变量值。需要注意参数的读取方式,详情见插件手册-参数读取                。 */
                $myrepeatsusergroups = (array)dunserialize($_G['cache']['plugin']['myrepeats']['use                rgroups']);
                if(in_array('', $myrepeatsusergroups)) {
                        $myrepeatsusergroups = array();
                }
                $userlist = array();

                /* 对当前登录用户进行马甲验证, 即当前用户组不再权限许可范围内, 但其他帐号所在用户组有权限, 则当                前用户也有使用权限。*/
                if(!in_array($_G['groupid'], $myrepeatsusergroups)) {
                        if(!isset($_G['cookie']['myrepeat_rr'])) {

                                /* 这里需要注意一下你所建的数据表对象的构建, 即 source/plugin/myrepeats/t                                able/下的 table_新建表名.php */
                                $users = count(C::t('#myrepeats#myrepeats')->fetch_all_by_username(                                $_G['username']));
                                dsetcookie('myrepeat_rr', 'R'.$users, 86400);
                        } else {
                                $users = substr($_G['cookie']['myrepeat_rr'], 1);
                        }
                        if(!$users) {
                                return '';
                        }
                }

                /* 前台显示代码 */
                $this->value['global_usernav_extra1'] = '<script>'.
                'function showmyrepeats() {if(!$(\'myrepeats_menu\')) {'.
                'menu=document.createElement(\'div\');menu.id=\'myrepeats_menu\';menu.style                        .display=\'none\';menu.className=\'p_pop\';'.
                '$(\'append_parent\').appendChild(menu);'.
                'ajaxget(\'plugin.php?id=myrepeats:switch&list=yes\',\'myrepeats_menu\',\'a                        jaxwaitid\');}'.
                'showMenu({\'ctrlid\':\'myrepeats\',\'duration\':2});}'.
                '</script>'.
                /* 此处是对个人前台设置管理马甲程序模块的连接,需要注意下格式是固定的。 */
                '<span class="pipe">|</span><a id="myrepeats" href="home.php?mod=spacecp&ac=plugin&                id=myrepeats:memcp" class="showmenu cur1">'.lang('plugin/myrepeats', 'switch').'</a>'."\n";
        }
        /* 这里使用了嵌入点函数 global_usernav_extra1() 返回到它对应输的显示位置, 所有嵌入点函数及对应位置见        手册。 */
        function global_usernav_extra1() {
                return $this->value['global_usernav_extra1'];
        }

}
?>



  • 上面的脚本在前台界面头部增加了马甲入口,如图:

    它的连接地址指向前台个人设置页面。程序由 home.php 进入, 然后进入默认的个人设置流程里面。

    上图所示的模板显示是直接调用的 source/plugin/myrepeats/template/ 文件夹下的 memcp.htm 模版文件, memcp.inc.php 是与之对应的脚本处理代码。入口处的马甲切换列表, 是ajax调用插件中的 switch.inc.php 扩展脚本处理返回的。以上个人设置和帐号切换流程都是正常的 php 逻辑代码处理流程,这里就不复述了。
  • 以上前台的功能我们基本已经开发完成, 现在需要开始开发后台管理的功能, 即 admincp.inc.php, 此文件在前面已经添加。
<?php

if(!defined('IN_DISCUZ') || !defined('IN_ADMINCP')) {
        exit('Access Denied');
}

/* 语言包文件已经引入, 这里直接读取语言包,赋值给变量 $Plang。 */
$Plang = $scriptlang['myrepeats'];

/* 锁定、删除处理流程 */
if($_GET['op'] == 'lock') {
        /* 插件数据库表对象方法的调用和使用形式。 */
        $myrepeat = C::t('#myrepeats#myrepeats')->fetch_all_by_uid_username($_GET['uid'], $_GET['us        ername']);
        $lock = $myrepeat['lock'];
        $locknew = $lock ? 0 : 1;
        C::t('#myrepeats#myrepeats')->update_locked_by_uid_username($_GET['uid'], $_GET['username']        , $locknew);
        ajaxshowheader();
        echo $lock ? $Plang['normal'] : $Plang['lock'];
        ajaxshowfooter();
} elseif($_GET['op'] == 'delete') {
        C::t('#myrepeats#myrepeats')->delete_by_uid_usernames($_GET['uid'], $_GET['username']);
        ajaxshowheader();
        echo $Plang['deleted'];
        ajaxshowfooter();
}

$ppp = 100;
$resultempty = FALSE;
$srchadd = $searchtext = $extra = $srchuid = '';
$page = max(1, intval($_GET['page']));
if(!empty($_GET['srchuid'])) {
        $srchuid = intval($_GET['srchuid']);
        $srchadd = "AND uid='$srchuid'";
} elseif(!empty($_GET['srchusername'])) {
        $srchuid = C::t('common_member')->fetch_uid_by_username($_GET['srchusername']);
        if($srchuid) {
                $srchadd = "AND uid='$srchuid'";
        } else {
                $resultempty = TRUE;
        }
} elseif(!empty($_GET['srchrepeat'])) {
        $extra = '&srchrepeat='.rawurlencode($_GET['srchrepeat']);
        $srchadd = "AND username='".addslashes($_GET['srchrepeat'])."'";
        $searchtext = $Plang['search'].' "'.$_GET['srchrepeat'].'" '.$Plang['repeats'].' ';
}

if($srchuid) {
        $extra = '&srchuid='.$srchuid;
        $member = getuserbyuid($srchuid);
        $searchtext = $Plang['search'].' "'.$member['username'].'" '.$Plang['repeatusers'].' ';
}

$statary = array(-1 => $Plang['status'], 0 => $Plang['normal'], 1 => $Plang['lock']);
$status = isset($_GET['status']) ? intval($_GET['status']) : -1;

if(isset($status) && $status >= 0) {
        $srchadd .= " AND locked='$status'";
        $searchtext .= $Plang['search'].$statary[$status].$Plang['statuss'];
}

if($searchtext) {
        $searchtext = '<a href="'.ADMINSCRIPT.'?action=plugins&operation=config&do='.$pluginid.'&id        entifier=myrepeats&pmod=admincp">'.$Plang['viewall'].'</a> '.$searchtext;
}

/* 加载用户组缓存信息。 */
loadcache('usergroups');

/* 这里输出表格头部和表单 html 到当前位置。Discuz! 后台输出 html 界面函数, 可在后台函数库文件source/function/function_admincp.php 中查看具体输出内容。*/
showtableheader();

/* 本页面的地址连接,其中 do = $pluginid 为当前插件标识id, 此id为自动生成的id, 在书写本页面地址时需要注意此参数。*/
showformheader('plugins&operation=config&do='.$pluginid.'&identifier=myrepeats&pmod=admincp', 'repeatsubmit');
showsubmit('repeatsubmit', $Plang['search'], $lang['username'].': <input name="srchusername" value="'.htmlspecialchars($_GET['srchusername']).'" class="txt" />  '.$Plang['repeat'].': <input name="srchrepeat" value="'.htmlspecialchars($_GET['srchrepeat']).'" class="txt" />', $searchtext);
showformfooter();

$statselect = '<select>';
foreach($statary as $k => $v) {
        $statselect .= '<option value="'.$k.'"'.($k == $status ? ' selected' : '').'>'.$v.'</option>';
}
$statselect .= '</select>';

/* 界面具体内容显示输出。*/
echo '<tr class="header"><th>'.$Plang['username'].'</th><th>'.$lang['usergroup'].'</th><th>'.$Plang['repeat'].'</th><th>'.$Plang['lastswitch'].'</th><th>'.$statselect.'</th><th></th></tr>';

if(!$resultempty) {
        $count = C::t('#myrepeats#myrepeats')->count_by_search($srchadd);
        $myrepeats = C::t('#myrepeats#myrepeats')->fetch_all_by_search($srchadd, ($page - 1) * $ppp        , $ppp);
        $uids = array();
        foreach($myrepeats as $myrepeat) {
                $uids[] = $myrepeat['uid'];
        }
        $users = C::t('common_member')->fetch_all($uids);
        $i = 0;
        foreach($myrepeats as $myrepeat) {
                $myrepeat['lastswitch'] = $myrepeat['lastswitch'] ? dgmdate($myrepeat['lastswitch']                ) : '';
                $myrepeat['usernameenc'] = rawurlencode($myrepeat['username']);
                $opstr = !$myrepeat['locked'] ? $Plang['normal'] : $Plang['lock'];
                $i++;
                echo '<tr><td><a href="'.ADMINSCRIPT.'?action=plugins&operation=config&do='.$plugin                id.'&identifier=myrepeats&pmod=admincp&srchuid='.$myrepeat['uid'].'">'.$users[$myre                peat['uid']]['username'].'</a></td>'.'<td>'.$_G['cache']['usergroups'][$users[$myre                peat['uid']]['groupid']]['grouptitle'].'</td>'.'<td><a href="'.ADMINSCRIPT.'?action                =plugins&operation=config&do='.$pluginid.'&identifier=myrepeats&pmod=admincp&srchre                peat='.rawurlencode($myrepeat['username']).'" title="'.htmlspecialchars($myrepeat['                comment']).'">'.$myrepeat['username'].'</a>'.'</td>'.'<td>'.($myrepeat['lastswitch'                ] ? $myrepeat['lastswitch'] : '').'</td>'.'<td><a id="d'.$i.'" href="'.ADMINSCRIPT.'?action=plugins&operation                =config&do='.$pluginid.'&identifier=myrepeats&pmod=admincp&uid='.$myrepeat['uid'].'                &username='.$myrepeat['usernameenc'].'&op=lock">'.$opstr.'</a></td>'.'<td><a id="p'                .$i.'" href="'.ADMINSCRIPT                .'?action=plugins&operation=config&do='.$pluginid.'&identifier=myrepeats&pmod=admin                cp&uid='.$myrepeat['uid'].'&username='.$myrepeat['usernameenc'].'&op=delete">['.$la                ng['delete'].']</a></td></tr>';
        }
}
showtablefooter();

/* 分页输出 */
echo multi($count, $ppp, $page, ADMINSCRIPT."?action=plugins&operation=config&do=$pluginid&identifier=myrepeats&pmod=admincp$extra");

?>



  • 这个文件主要功能是对后台插件数据进行处理。开发者可以根据自己的需求, 设计此文件的代码结构。插件开发时需要注意, 后台提供了很多Discuz! 内置的函数来显示界面, 例如: showtableheader(), showformheader(), showsubmit() 等函数, 方便开发使用。
  • 这样整个插件的功能开发已经完成, 下面我们需要将我们制作好的插件导出即可。此时我们导出的是xml配置文件, 里面主要是插件的一些基本信息配置参数以及语言包内容。另外, 插件作者可以设计 2 个脚本文件用于插件的安装和卸载,文件名任意。脚本中可用 runquery() 函数执行 SQL 语句,表名可以直接写“cdb_”。插件作者只需在导出的 XML 文件结尾加上安装、卸载脚本的文件名即可


  <item id="installfile"><![CDATA[install.php]]></item>
                <item id="uninstallfile"><![CDATA[uninstall.php]]></item>
        </item>
</root>




回复

使用道具 举报

游客~
懒得打字嘛,点击右侧快捷回复
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋| 鲸云论坛

© 2013-2016 Comsenz Inc. Powered by Discuz! X3.4