插件
From LimeSurvey Manual
概览
LimeSurvey 能够通过插件添加功能。LimeSurvey 已内置大量核心插件。此外,请查看我们的第三方插件列表,将来也许您可以将自己的插件添加到其中!
插件允许用户自定义其安装的功能,同时仍可从定期的软件更新中获益。
本文档面向为自己或客户扩展 LimeSurvey 的开发人员;最终用户无法从本文档中获得帮助。
插件必须实现 iPlugin 接口。我们建议让您的插件类继承自 PluginBase 类。
插件是围绕事件机制开发的。
插件设置
通过继承,您可以受益于我们已为您实现的插件所需的通用功能。其中之一是 getPluginSettings 函数的实现。该函数必须返回一个描述用户配置选项的数组。
示例插件仅公开了 1 个可配置的设置,即它将显示的消息。
protected $settings = array(
'logo' => array(
'type' => 'logo',
'path' => 'assets/logo.png'
),
'message' => array(
'type' => 'string',
'label' => 'Message'
)
);
该数组以每个设置的名称作为键,其值是包含所需元数据的数组。
支持的类型有:
- logo
- int(整数)
- string(字母数字)
- text
- html
- relevance
- info
- password
- date
- select
除 type 外,还有许多其他可用的键:
- label,定义标签
- default,定义未指定值时显示的值(仅适用于全局设置,不适用于调查设置)
- current,定义当前值。
- readOnly:将设置显示为只读
- htmlOptions,输入部分的 htmlOptions(参见 Yii 手册 [[1]])
- pluginOptions,用于某些设置(html 或 select):设置小部件选项
- labelOptions:标签的 htmlOptions
- controlOptions:标签和输入包装器的 htmlOptions
- localized:如果为 true,则按语言存储该值。输入以
name[language]的形式提交,并且current必须是以语言编码为索引的数组。 - language:与 localized 配合使用。可以是单个语言编码,或者 (New in X.Y.Z ) 语言编码数组,以便为每种语言显示一个输入(参见本地化插件设置)
您可以在 exampleSettings 找到使用所有现有设置的插件示例
读取和写入插件设置
您可以直接在插件代码中读取和写入插件设置。
示例:
$mySetting = $this->get('mySetting');
$this->set('mySetting', $mySetting + 1);
如果设置恰好为 null,您可以获取一个默认值:
$mySetting = $this->get('mySetting', null, null, 10); // 10 is default
特定于调查的插件设置
使用两个事件来创建特定于调查的插件设置:
- newSurveySettings
- beforeSurveySettings
为特定调查禁用插件的示例:
public function init()
{
$this->subscribe('beforeSurveySettings');
$this->subscribe('newSurveySettings');
// Other events...
}
public function beforeSurveySettings()
{
$event = $this->event;
$surveyId = intval($event->get('survey'));
$event->set(
"surveysettings.{$this->id}",
[
'name' => get_class($this),
'settings' => [
'isActive' => [
'type' => 'boolean',
'label' => 'isActive',
'current' => $this->getIsActive($surveyId),
'help' => 'Activate plugin for this survey'
],
]
]
);
}
public function newSurveySettings()
{
$event = $this->event;
foreach ($event->get('settings') as $name => $value)
{
$this->set($name, $value, 'Survey', $event->get('survey'), false);
}
}
private function getIsActive(int $sid): bool
{
return (bool) $this->get('isActive', 'Survey', $sid, false);
}
<span id="Localized_plugin_settings_ (New in 7.4 )">
本地化插件设置 (New in 7.4 )
若要为同一设置按调查语言分别显示一个输入框,请将 localized 设置为 true,并在 language 中传入语言列表。语言名称会添加到每个输入框的标签中。
public function beforeSurveySettings()
{
$event = $this->event;
$surveyId = intval($event->get('survey'));
$survey = Survey::model()->findByPk($surveyId);
$event->set(
"surveysettings.{$this->id}",
[
'name' => get_class($this),
'settings' => [
'errorText' => [
'type' => 'text',
'label' => $this->gT('Error text'),
'localized' => true,
'language' => $survey->getAllLanguages(),
// 以语言编码为索引的数组,例如 ['en' => '...', 'de' => '...']
'current' => $this->get('errorText', 'Survey', $surveyId, []),
],
]
]
);
}
提交的值是以语言编码为索引的数组,因此上述示例中的 newSurveySettings 函数无需任何修改即可保存它。要读取当前语言的文本:
$errorTexts = $this->get('errorText', 'Survey', $surveyId, []);
$errorText = $errorTexts[App()->getLanguage()] ?? '';
事件
插件订阅事件,并可在事件触发时与 LimeSurvey 交互。有关当前可用事件的列表,请查看插件事件。
API
插件应仅通过 LimeSurvey 的“公共”API 来扩展 LimeSurvey。这意味着直接使用源代码中的类是不好的做法。虽然我们无法强制您不这样做,但每次我们发布小版本更新时,您的插件都可能会失效。
请尽可能仅通过此处描述的方法与 LimeSurvey 交互。事件也是如此。
从 PluginBase 扩展时,可通过 $this->api 使用 API 对象;否则,您可以从传递给插件构造函数的 PluginManager 实例中获取它。
可根据需求向 API 对象添加新函数。
<span id="Form_extension (New in 6 )">
表单扩展 (New in 6 )
简介
表单扩展系统是一种更通用的方式,可用于扩展 LimeSurvey 核心中的表单,而无需为每个表单添加新事件。
它由以下组件构成:
- 名为 FormExtensionService 的全局模块
- 一个 输入类 库,插件可将其添加到上述模块的初始化中
- 一个 小部件,以及自定义渲染器,用于 LimeSurvey 的视图文件中
每个表单由一个 位置字符串 标识,格式如 <form name><dot><tab name>。示例:globalsettings.general 或 globalsettings.security。
采用不含 HTML 的基于类的系统,其目的是让插件作者无需在核心 HTML 更改时去更新 HTML。不过,如有需要,作者仍可使用 RawHtmlInput 类型。
在该系统中无法做到的一点是添加新的表单选项卡。
示例
若要通过插件向表单添加新的输入框,请在 init() 函数中使用以下代码:
TODO: 保存到插件设置中,而不是全局设置
// At top of file
use LimeSurvey\Libraries\FormExtension\Inputs\TextInput;
use LimeSurvey\Libraries\FormExtension\SaveFailedException;
// Inside init()
Yii::app()->formExtensionService->add(
'globalsettings.general',
new TextInput([
'name' => 'myinput',
'label' => 'Label',
'disabled' => true,
'tooltip' => 'Moo moo moo',
'help' => 'Some help text',
'save' => function($request, $connection) {
$value = $request->getPost('myinput');
if ($value === 'some invalid value') {
throw new SaveFailedException("Could not save custom input 'myinput'");
} else {
SettingGlobal::setSetting('myinput', $value);
}
},
'load' => function () {
return getGlobalSetting('myinput');
}
])
);
验证
输入的验证在 save 函数中完成(见上例)。如果提交的值无效,请抛出 SaveFailedException,系统将向用户显示警告闪现消息。
支持的表单
以下表单可以扩展:
- globalsettings.general (New in 6.0.0 )
如果您想为其他核心表单添加支持,需要在拉取请求(pull request)中应用以下更改:
在视图文件中添加:
<?php
use LimeSurvey\Libraries\FormExtension\FormExtensionWidget;
use LimeSurvey\Libraries\FormExtension\Inputs\DefaultBaseRenderer;
?>
... more HTML
<?= FormExtensionWidget::render(
App()->formExtensionService->getAll('globalsettings.security'),
new DefaultBaseRenderer()
); ?>
如果该表单的 HTML 与其他表单不同,您可能需要基于 DefaultBaseRenderer 创建一个新的渲染器类。您可能还需要为默认渲染器类扩展尚未添加的输入类型。
第二项更改是在保存该表单的控制器操作中,添加对表单扩展服务类的调用:
$request = App()->request;
Yii::app()->formExtensionService->applySave('globalsettings', $request);
完成!
<span id="Localization_ (New in 3 )">
本地化 (New in 3 )
插件可以添加自己的语言区域文件。所用文件格式为 .mo,与核心翻译相同。这些文件必须存放在:
<plugin root folder>/locale/<language>/<language>.mo
其中 "<language>" 是由两个字母组成的语言代码,例如 "de" 或 "fr"。
要使用特定的语言区域文件,请使用插件函数 gT:
$this->gT("A plugin text that needs to be translated");
如果在插件专用的语言区域文件中找不到给定的字符串,该函数会在核心语言区域文件中查找。因此可以放心使用诸如 "Cancel"(取消)之类的字符串:
$this->gT("Cancel"); // Will be translated even if "Cancel" is not in the plugin locale file
如果您的插件同时使用了视图,则应使用:
$plugin->gT("Translate me");
以便在视图中进行插件专用的翻译。
您可以将 limesurvey.pot 文件作为 pot 文件的示例。该文件会被导入到您的翻译工具中。
工具
Poedit 是一款用于编辑 po 和 mo 文件的开源工具。
<span id="Logging_ (New in 3 )">
日志记录 (New in 3 )
如果您想从插件中记录日志,只需编写:
$this->log("Your message");
默认的日志级别为 trace,但您可以通过可选的第二个参数指定其他日志级别:
$this->log("Something went wrong!", CLogger::LEVEL_ERROR);
日志文件位于以下文件夹中:
<limesurvey root folder>/tmp/runtime/plugin.log
您的插件名称会自动用作类别。若只想查看来自您插件的错误,可以使用 grep(在 Linux 上):
$ tail -f tmp/runtime/plugin.log | grep <your plugin name>
<span id="Logging_plugin_deactivation_due_to_error_ (New in 6.13.3 )">
记录因错误而停用插件的日志 (New in 6.13.3 )
自 LimeSurvey 5 版起:发生 PHP 错误时,插件可以被自动停用。
系统会自动向 LimeSurvey 管理员发送一封电子邮件。您可以利用日志系统向其他服务器管理员生成警报。
例如:发送警报消息
// Plugin error happen, plugin was disable : send email using CLogRoute
'pluginError' => array(
'class' => 'CEmailLogRoute', // https://www.yiiframework.com/doc/api/1.1/CEmailLogRoute
'categories' => 'application.model.plugin.setLoadError', // A plugin was disable
'subject' => '[ERROR] Plugin deactivated',
'emails' => ['webmaster@example.org'],
'sentFrom' => 'noreply@example.org',
'enabled' => 1, // enabled here
)
有关在 Yii 1 中配置日志记录的更多信息:可选设置。
<span id="Extension_updates_ (New in 4 )">
扩展更新 (New in 4 )
自 LimeSurvey 4.0.0 版起,系统提供了处理插件及其他扩展更新的机制。要使用此机制,您的扩展 config.xml 文件需要包含更新程序配置。
<updaters>
<updater>
<stable>1</stable>
<type>rest</type>
<source>https://comfortupdate.limesurvey.org/index.php?r=limestorerest</source>
<manualUpdateUrl>https://somedownloadlink.com/maybegithub</manualUpdateUrl>
</updater>
</updaters>
(上面的 source 标签指向 LimeStore REST API,LimeStore 中提供的所有扩展都将使用该接口。)
| 标签 | 描述 |
|---|---|
| stable | 如果此来源只提供稳定版本号,则为 "1";如果此来源还会提供不稳定版本(例如 0.3.3-beta),则为 "0"。
|
| type | 目前仅支持 rest 类型。添加新的更新程序类型(版本检查器)很容易,例如 git、wget 等。
|
| source | 用于获取新版本的 URL。 |
| manualUpdateUrl | 用户可以访问该 URL 来更新扩展的最新版本。 |
| automaticUpdateUrl | TODO |
如果您不想提供更新程序,应在配置 XML 文件中加入以下文本:
<updaters disabled="disabled">
</updaters>
这样,您就是在告诉系统,您有意禁用了更新系统,而不是忘记添加它。
新的插件 UpdateCheck(默认已安装并启用)会在超级管理员登录时异步检查所有已安装扩展的新更新,每 24 小时最多检查一次。如果发现任何新版本,系统会推送一条通知。
如果发现新的安全更新,通知将自动打开,并以 "danger" 样式显示。
您可以进入插件管理器查看页面并点击 "Check updates",手动检查更新。请注意,只有在 UpdateCheck 插件已启用时,此按钮才可见。
内部实现
本节简要概述扩展更新程序的实现。
扩展更新程序是 ExtensionInstaller 库的一部分。下面是与更新过程相关的类的 UML 图。
Yii 启动时的程序流程:
Yii init
VersionFetcherServiceLocator->init()
Add REST version fetcher
ExtensionUpdaterServiceLocator->init()
Add PluginUpdater
TODO: Add an updater for each extension type (theme, question template, ...)
运行 UpdaterCheck 插件时的程序流程:
Get all updaters from ExtensionUpdaterServiceLocator
Loop each updater
For each updater, loop through version fetchers configured by <updater> XML
For each version fetcher, contact remote source and get version information
Compose all versions into a notification
UpdateCheck 插件中的 checkAll 方法提供了如何查询所有扩展的新版本的示例。
添加新的版本获取器
要添加新的自定义版本获取器,请在 Yii 初始化期间运行以下代码:
$service = \Yii::app()->versionFetcherServiceLocator
$service->addVersionFetcherType(
'myNewVersionFetcherType',
function (\SimpleXMLElement $updaterXml) {
return new MyNewVersionFetcher($updaterXml);
}
);
当然,类 MyNewVersionFetcher 必须是 VersionFetcher 的子类。
要使用您的新版本获取器,请在更新程序 XML 中将 type 标签配置为使用
myNewVersionFetcherType(而不是例如 rest)。
添加新的扩展更新程序
要添加新的自定义扩展更新程序,请在 Yii 初始化期间运行以下代码:
$service = \Yii::app()->extensionUpdaterServiceLocator;
$service->addUpdaterType(
'myNewExtensionUpdater',
function () {
return MyNewExtensionUpdater::createUpdaters();
}
);
类 MyNewExtensionUpdater 必须是 ExtensionUpdater 的子类。
config.xml 中顶层的 type 标签('plugin'、'theme' 等)将控制该扩展使用哪种扩展更新程序。该系统目前还不能完全自定义,因为您还需要添加自定义的 ExtensionInstaller、菜单项等。但理论上,也许在未来,可以通过这种方式添加新的扩展类型。
扩展安装程序
扩展安装程序库由两个抽象类组成:
- ExtensionInstaller
- FileFetcher
ExtensionInstaller 会为每种扩展类型创建子类,例如 PluginInstaller、QuestionThemeInstaller 等。
FileFetcher 会为每种不同的文件获取方式创建子类。目前仅支持上传的 zip 文件,但将来也可能会有 Github 或 LimeStore 获取器。
教程
这篇分步教程演示了如何创建一个插件,在每次提交调查反馈时发送 POST 请求。该教程介绍了如何创建并保存全局设置和各调查的设置、如何注册事件等内容。




