discuz插件制作开发教程

网上开发discuz插件的教程太少了,好在官方有提供,而且有非常详细的手册说明,只是对于新手不太友好,但只要懂php、mysql、css,入门会很快,在某些视频平台也有视频教程,不过那都是2019年的了,比较老了,有兴趣也可以去看看。

下文有一个插件开发的实例,感兴趣可以先看看,官方有介绍插件的实现流程。

discuz插件制作开发教程-第1张-吾帮找网

插件开发实例

在开始插件开发前, 请您务必详细阅读插件开发手册。
马甲插件是一款能够让用户在论坛中以多个帐号身份进行使用、交流的插件。论坛开启马甲插件后, 用户无需进行退出操作即可迅速切换到其他帐号。下面以马甲插件为例进行插件开发说明。

前台实现帐号切换、马甲设置等功能图:

discuz插件制作开发教程-第2张-吾帮找网

后台实现管理、锁定、查找等管理功能, 如图:

discuz插件制作开发教程-第3张-吾帮找网

插件开发流程介绍说明:

1.首先在 Discuz! 配置文件 config/config_global.php 底部加入代码:

$_config['plugindeveloper'] = 1;

开启开发者模式。( 论坛后台插件设置项会开启开发者模式。 新增加的插件也会有设计模式的设置选项。)

discuz插件制作开发教程-第4张-吾帮找网

2.管理员进入后台应用 -> 插件 -> 插件设计, 开始进行新插件的设计, 添加初始化插件的基本信息。

discuz插件制作开发教程-第5张-吾帮找网

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

discuz插件制作开发教程-第6张-吾帮找网

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

<?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! 插件模块中的以下几项:

扩展项目 个人面板:可在个人面板上部增加一个菜单项。(实现个人设置面板部分)

discuz插件制作开发教程-第7张-吾帮找网

程序脚本 页面嵌入:设置一个包含页面嵌入脚本的模块,模块文件名指派为 source/plugin/插件目录/插件模块名.class.php”。(通过嵌入点来实现头部用户信息中, 快捷切换马甲的效果。)

discuz插件制作开发教程-第8张-吾帮找网

扩展项目 管理中心:可在后台 -> 插件栏目中为此插件增添一个管理模块。(在后台为我们的马甲插件添加一个管理项目。)

discuz插件制作开发教程-第9张-吾帮找网

在刚才新建好的插件中添加以上三个插件模块, 如图:

discuz插件制作开发教程-第10张-吾帮找网

以上指定好模块文件后, 我们需要在对应的插件文件夹 source/plugin/myrepeats/ 里新建我们刚才添加的模块脚本文件。

接下来添加插件中需要用到的变量。(马甲插件只需要一个用户组是否开启的状态。)如图:

discuz插件制作开发教程-第11张-吾帮找网

根据需要开发的插件功能, 设计数据表结构。在自己的开发环境下建好数据表, 以便在后面的开发过程中使用。当你新建完数据库之后, 需要根据你构建的数据库, 在 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']['usergroups']);
		if(in_array('', $myrepeatsusergroups)) {
			$myrepeatsusergroups = array();
		}
		$userlist = array();

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

				/* 这里需要注意一下你所建的数据表对象的构建, 即 source/plugin/myrepeats/table/下的 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\',\'ajaxwaitid\');}'.
		'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" onmouseover="delayShow(this, showmyrepeats)">'.lang('plugin/myrepeats', 'switch').'</a>'."\n";
	}
	/* 这里使用了嵌入点函数 global_usernav_extra1() 返回到它对应输的显示位置, 所有嵌入点函数及对应位置见手册。 */
	function global_usernav_extra1() {
		return $this->value['global_usernav_extra1'];
	}

}
?>

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

discuz插件制作开发教程-第12张-吾帮找网

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

discuz插件制作开发教程-第13张-吾帮找网

上图所示的模板显示是直接调用的 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['username']);
	$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.'&identifier=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 onchange="location.href=\''.ADMINSCRIPT.'?action=plugins&operation=config&do='.$pluginid.'&identifier=myrepeats&pmod=admincp'.$extra.'&status=\' + this.value">';
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='.$pluginid.'&identifier=myrepeats&pmod=admincp&srchuid='.$myrepeat['uid'].'">'.$users[$myrepeat['uid']]['username'].'</a></td>'.'<td>'.$_G['cache']['usergroups'][$users[$myrepeat['uid']]['groupid']]['grouptitle'].'</td>'.'<td><a href="'.ADMINSCRIPT.'?action=plugins&operation=config&do='.$pluginid.'&identifier=myrepeats&pmod=admincp&srchrepeat='.rawurlencode($myrepeat['username']).'" title="'.htmlspecialchars($myrepeat['comment']).'">'.$myrepeat['username'].'</a>'.'</td>'.'<td>'.($myrepeat['lastswitch'] ? $myrepeat['lastswitch'] : '').'</td>'.'<td><a id="d'.$i.'" onclick="ajaxget(this.href, this.id, \'\');return false" 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.'" onclick="ajaxget(this.href, this.id, \'\');return false" href="'.ADMINSCRIPT.'?action=plugins&operation=config&do='.$pluginid.'&identifier=myrepeats&pmod=admincp&uid='.$myrepeat['uid'].'&username='.$myrepeat['usernameenc'].'&op=delete">['.$lang['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>

网站地址

开发插件需要一定的php、myql、css基础,不然看不懂代码,推荐在本站搜索相关的教程网站。

吾帮找网
吾帮找网

致力于免费分享全网最干货的内容。

文章: 1068

留下评论