4.1 广告类、道具类、任务类扩展
三类挂载式扩展的位置约定,以及广告类的完整类骨架。
4.1.1 什么是挂载式扩展
前面几篇的插件模块本质是「我提供一个新页面」。广告类、道具类、任务类不是这样:它们把自己挂到系统已有的功能位上,系统负责页面、后台设置页和调用时机,你只提供一个类。省掉的是整套界面——广告的投放表单是系统画的,道具的使用按钮是系统给的。代价是控制权少,模板怎么排版不由你定。
生活里整理工具箱有条朴素经验:刀、钳子、螺丝刀各有各的抽屉,抽屉上贴标签。文件名和语言包名的对应关系就是那个标签——写错一个字符,等于工具放错抽屉,用户找不着。
4.1.2 三类扩展对照表
从 X3.0 起位置约定就固定了,X5.0 沿用未改:
| 类型 | 脚本位置(X3.0 起) | 语言包 | 附件 |
|---|---|---|---|
| 广告类 | source/plugin/{标识}/adv/adv_{name}.php |
source/language/adv/lang_{name}.php |
同名 .gif 缩略图 |
| 道具类 | source/plugin/{标识}/magic/magic_{name}.php |
source/language/magic/lang_{name}.php |
.small.gif 与 .gif |
| 任务类 | source/plugin/{标识}/task/task_{name}.php |
source/language/task/lang_{name}.php |
.gif |
脚本文件名的 {name} 段,就是语言包文件名里的 {name} 段。写 adv_promo.php,语言包就得叫 lang_promo.php。缩略图跟语言包同级:source/language/adv/promo.gif;道具类要放两个,xxx.small.gif 是小图标、xxx.gif 是大图。
4.1.3 广告类:完整类骨架
广告类结构最完整,先吃透它,另两类就是减法。文件放 source/plugin/{标识}/adv/adv_promo.php:
<?php
// source/plugin/你的标识/adv/adv_promo.php
// 语言包 source/language/adv/lang_promo.php,缩略图同目录 promo.gif
class adv_promo {
var $version = '1.0'; // 这个类自己的版本
var $name = 'lang_promo_name'; // 名称,填语言包 key
var $description = 'lang_promo_desc'; // 说明,填语言包 key
var $copyright = '你的团队';
var $targets = array('portal','home','member','forum','group','userapp','plugin','custom');
var $imagesizes = array('120x60','120x240');
function getsetting() {} // 返回后台设置项目定义
function setsetting(&$advnew, &$parameters) {} // 保存设置,两个都是引用
function evalcode() {} // 返回 check / create 两段代码
}
$targets 决定广告出现在哪些入口的设置页,取值是 portal 门户、home 空间、member 个人中心、forum 论坛、group 群组、userapp 应用、plugin 插件页、custom 自定义位置。想不清楚就全写上,让站长自己勾。$imagesizes 的 x 是小写字母,仅作提示。$version 与插件 XML 清单里的 version 是两回事,后者是兼容性声明,会写成 X3,X3.5,X5.0 这种形式。
4.1.4 getsetting 与 setsetting
getsetting() 返回设置项目定义,键是设置项标识,值描述类型、标题、说明。字段拼法随类型有差别,照抄系统自带广告脚本的结构最保险:
function getsetting() {
return array(
'promoid' => array(
'type' => 'string',
'label' => '推广活动标识',
'description' => '由活动方提供,用于拼接推广链接',
),
);
}
function setsetting(&$advnew, &$parameters) {
$advnew['promoid'] = isset($parameters['promoid']) ? trim($parameters['promoid']) : '';
return $advnew;
}
$parameters 是站长刚提交的内容,$advnew 是引用传进来的当前记录,取参数一律 isset 兜底。
4.1.5 evalcode:两段代码的分工
| 段 | 何时执行 | 作用 |
|---|---|---|
check |
广告位渲染之前 | 判断此刻该不该出现,不成立返回 false 或空串 |
create |
决定投放之后 | 生成实际输出的 HTML,即广告位显示的内容 |
function evalcode() {
return array(
'check' => 'return !empty($parameters[\'promoid\']);',
'create' => 'return \'<a href="\' . $parameters[\'promourl\'] . \'">\' . $parameters[\'promoid\'] . \'</a>\';',
);
}
check 做前置校验:参数没填全、活动过期、投放次数用完,都在这里返回 false,广告位自动留空。create 拿到的参数已通过校验,直接拼 HTML 返回;系统不替你转义,用户可控内容自己动手 htmlspecialchars。
4.1.6 道具类:多两个方法
道具类的类名是 magic_{name},成员与广告类大体相同,额外多两个方法:
| 方法 | 何时调用 | 作用 |
|---|---|---|
usesubmit($magic, $parameters) |
用户点「使用」时 | 执行道具效果,返回处理结果 |
show($magic) |
渲染道具条目时 | 返回这一条的显示内容 |
<?php
// source/plugin/你的标识/magic/magic_card.php
class magic_card {
var $icon = 'card'; // 图标名,对应 .small.gif / .gif
function usesubmit($magic, $parameters) {
global $_G, $discuz;
if (empty($parameters['toid'])) {
return '请选择要使用的对象'; // 返回值直接显示给用户
}
// 真实业务在这里,用 C::t('xxx')->... 读写
return 'ok';
}
function show($magic) {
return '把卡片交给对方';
}
}
道具不可退,usesubmit 开头先校验参数,别让用户白扣道具。
4.1.7 任务类:规则写在类里
任务类(task_{name})的思路不同:任务的完成条件本身就是一类规则,按约定填数组即可。
| 成员 | 作用 |
|---|---|
$icon |
任务图标名,对应同名 .gif |
$period |
周期数值,如每天 1 次填 1 |
$periodtype |
周期类型:0 按天,1 按周,2 按月 |
$conditions |
完成条件数组,任务类的主角 |
class task_sign {
var $icon = 'sign';
var $period = 1;
var $periodtype = 0;
var $conditions = array(
'posts' => array(
'title' => '发表主题数',
'required' => 5, // 达成阈值
'selected' => 1, // 是否计入任务进度
),
);
}
可用的条件维度随版本变化,写之前搜一遍系统自带的任务脚本看字段名。
4.1.8 上线前检查清单
- 脚本、语言包、缩略图三处的
{name}完全一致。 $name、$description填的是语言包 key,不是中文。$targets包含所有要投放的位置。setsetting()取值全部isset兜底,create里自己转义用户内容。
这几类扩展控制权有限,但省掉的界面代码极多。下一节处理插件页面上的文字,尤其是要发到海外的那些。
