Плагины — это способ расширить функциональность приложения без изменения его исходного кода. Функции, реализованные плагином, продолжают работать после установки обновлений приложения, когда перезаписываются его файлы. Исходный код плагина оформляется в независимых файлах, которые лишь подключаются к приложению.
Пример — плагин «Теги» для приложения «Блог», который добавляет возможность фильтровать записи блога по тегам в бекенде и во фронтенде. Плагин встроен в пользовательский интерфейс приложения «Блог», но его исходный код хранится вне файлов приложения «Блог». Поэтому можно обновлять и развивать плагины и приложения независимо друг от друга.
Фреймворк Webasyst предоставляет программную платформу для разработки плагинов, благодаря которой можно гибко измененять функциональность приложений и устанавливать обновления плагинов через «Инсталлер». Эта платформа описана далее в этой статье и является рекомендованнойдля внедрения в приложения. Предложенная платформа не мешает разработчику приложения реализовать свою собственную методику работы с плагинами, однако собственная система может не поддерживаться в полной мере стандартными функциями фреймворка и приложением «Инсталлер».
Должно ли приложение поддерживать плагины, решает разработчик приложения. Чтобы возможности приложения можно было расширять плагинами, нужно соответствующим образом проектировать приложение и заранее предусмотреть места, в которых смогут подключаться плагины. Такие места называютсяхуками(hooks) и подразумевают обработку событий. Например, в приложении «Блог» есть хуки в левом навигационном меню бекенда (они позволяют плагинам добавлять туда свои HTML-блоки) и хуки события сохранения записи (они позволяют добавить новые действия при публикации записей — например, экспорт записи в «Фейсбук»).
В предусмотренном месте приложения разработчик «определяет хук», добавляя следующий код:
wa()->event('[НАЗВАНИЕ_СОБЫТИЯ]', $params);
$params
– параметры, которые передаются по ссылке(!)
Далее «опрашиваются» все плагины, у которых в описании указан обработчик указанного события, и все найденные плагины последовательно вызываются. Метод wa()->event($event_name, $params);
возвращает массив вида:
array( '[PLUGIN_ID]-plugin' => результат, который возвращает обработчик плагина );
Плагины очень похожи на приложения. Структура плагина идентичнаструктуре приложения, плагин хранится в plugins/
внутри директории приложения:
wa-apps/[APP_ID]/plugins/[PLUGIN]/
Замочком помечены подкаталоги, которые рекомендуется закрыть с помощью директивы Deny from all
в файле .htaccess
.
Простейший плагин может состоять всего из двух файлов:
lib/config/plugin.php
— описание плагина:
<?php return array( // обязательные параметры 'name' => 'НАЗВАНИЕ ПЛАГИНА', 'description' => 'ОПИСАНИЕ ПЛАГИНА', 'version' => '1.0', // соответствие событие => обработчик (название метода в классе плагина), // если плагину нужно обрабатывать какие-то события 'handlers' => array( 'НАЗВАНИЕ_СОБЫТИЯ' => 'method1', //... ), // остальные параметры — необязательные 'img' => 'img/plugin.png', // иконка (будет показываться в Инсталлере) размером 16x16 );
lib/[app_id][PLUGIN].plugin.php
— класс с описанием обработчиков (должен быть унаследован от базового класса плагина приложения, например shopPlugin, если же такого класса в приложении нет, то тогда от класса waPlugin):
<?php class [app_id][PLUGIN]Plugin extends waPlugin // например, blogAkismetPlugin extends blogPlugin { public function method1($params) { // реализация обработчика // если нужен шаблон $view = wa()->getView(); $content = $view->fetch($this->path.'/templates/[Название шаблона].html') } // … остальные обработчики }
Контроллеры/экшены
Функциональная часть плагинов реализуется в экшенах — в точности, как в приложениях. Если, например, плагин добавляет какой-то HTML в интерфейс приложения или необходимо сохранить что-либо по AJAX, то следует создать контроллеры и экшены аналогично тому, как это делается в приложении. Маршрутизация ничем не отличается от маршрутизации в приложении. Для бекенда нужно только в URL добавлять параметр plugin=PLUGIN
. Для фронтенда необходимо создать файл routing.php, аналогично тому, как это делается для приложения.
Вызов URL (в бекенде) ?plugin=[PLUGIN]&action=edit
передаст управление экшену lib/actions/[app_id][PLUGIN]PluginBackendEdit.action.php
:
<?php class [app_id][PLUGIN]PluginBackendEditAction extends waViewAction { public function execute() { // реализация экшена } }
И по умолчанию будет использоваться шаблон:templates/actions/backend/BackendEdit.html
При реализации экшенов и контроллеров, обрабатывающих загрузку данных на сервер методом POST, учитывайте необходимость проверки данных для защиты от CSRF-атак, если такая защита включена для приложения, для которого разрабатывается плагин. Несоблюдение этого требования приведёт к неработоспособности функции загрузки данных. Подробнее о защите от CSRF-атак »
Настройки плагина
По умолчанию у плагина нет никаких настроек. Чтобы добавить настройки нужно описать их в файле lib/config/settings.php
. В этом случае на странице плагина в списке плагинов в бекенде приложения будет показана форма настроек.
<?php return array( 'api_key' => array( 'title' => /*_wp*/('Akismet API Key'), 'description' => array( /*_wp*/('Get an API key for your domain at Akismet website'), 'https://akismet.com/signup/' ), 'value' => '', // значение по умолчанию 'control_type'=> waHtmlControl::INPUT, ), );
Иногда простых настроек недостаточно и необходимо, например, добавить дополнительный JavaScript или собственные нестандартные элементы настроек.
В этом случае нужно в файле lib/config/plugin.php
указать:
'custom_settings' => true,
Далее нужно создать экшен для вашей кастомной страницы настроек в файле lib/actions/[app_id][PLUGIN]PluginSettings.action.php
:
<?php class [app_id][PLUGIN]PluginSettingsAction extends waViewAction { public function execute() { $plugin = wa('[app_id]')->getPlugin('[PLUGIN]'); // получаем все настройки плагина, чтобы передать их в шаблон $settings = $plugin->getSettings(); $this->view->assign('settings', $settings); } }
Далее нужно создать шаблон настроек в файле templates/actions/settings/Settings.html
:
<h1>MY PLUGIN NAME</h1> <div class="fields form"> <form action="?module=plugins&id=myplugin&action=save" method="post" id="plugins-settings-form"> {$wa->csrf()} <div class="field"> <div class="name"> [`My setting`] </div> <div class="value"> <input type="text" name="myapp_myplugin[my_setting]" value="{if isset($settings.my_setting)}{$settings.my_setting}{/if}"> </div> </div> ... <div class="field"> <div class="value submit"> <input type="submit" class="button green" value="[s`Save`]"> <span id="plugins-settings-form-status" style="display:none"></span> </div> </div> </form> </div>
Обратите внимание, что name для всех полей должен быть вида [app_id]_[PLUGIN] и дальше ключ с кодом настройки [SETTING], например shop_brands[feature_id].
Никакого javascript для отправки формы, а так же собственого контроллера для сохранения настроек делать нет необходимости.
Если вам нужна какая-то своя логика при сохранении настроек, вы можете переопределить в основном классе плагина метод saveSettings:
public function saveSettings($settings = array()) { // тут ваша логика ... parent::saveSettings($settings); }
Действия по расписанию
Фреймворк Вебасист содержит инфраструктуру для создания cli-скриптов (от английского command-line interface, «интерфейс командной строки»), которые могут исполняться из консоли сервера и, следовательно, могут быть включены в задания планировщика (cron). Плагины могут иметь собственные cli-скрипты отдельно от cli-скриптов приложения. Такие скрипты в плагинах оформляются в виде классов, унаследованных от базового класса waCliController
. Логика выполнения скрипта должна содержаться в методе класса execute()
. PHP-файл класса следует находиться по адресу wa-apps/[app_id]/plugins/[plugin_id]/lib/cli/[app_id][Plugin_id]Classname.cli.php
, а имя класса должно быть сформировано по правилу [app_id][Plugin_id]ClassnameCli
.
Например, PHP-файл некоторого cli-скрипта с именем update
для плагина brands
приложения shop
может располагаться по адресу wa-apps/shop/plugins/brands/lib/cli/shopBrandsUpdate.cli.php
и иметь следующий вид:
<?php class shopBrandsUpdateCli extends waCliController { public function execute() { // Код, который должен запуститься при вызове скрипта // Можно использовать модели и любые другие классы приложения } }
Для вызова такого скрипта нужно использовать команду следующего вида:
php cli.php shop brandsUpdate
Более подробно о создании cli-скриптов читайте в статье «Действия по расписанию».
Локализация
Локализация плагинов реализуется полностью аналогично локализации приложений (документация). В папке locale следует разместить файлы переводов *.po и *.mo и подключать ключи в коде следующим образом:
_wp('string')
в PHP (вместо метода _w(), который работает только с локализацией приложения, следует использовать метод _wp(), подгружающий локализацию плагина),[`string`]
в шаблонах Smarty (здесь нет отличий от локализации приложений).
Название и описание плагина (name и description в конфигурационном файле) переводятся с использованием локализации плагина по умолчанию, таким образом указывать 'name' => _wp('НАЗВАНИЕ ПЛАГИНА')
не надо — достаточно просто указать 'name'=>'НАЗВАНИЕ ПЛАГИНА'
.
Использование локализации в статических методах
В случае вызова публичных статических методов классов плагина во внешнем окружении, например, в коде темы дизайна, локализация плагина автоматически не подключается, и функция _wp()
не возвращает перевод строки, как ожидалось. Для того чтобы использовать локализацию плагина в таких методах, необходимо помещать все вызовы функции _wp()
внутри специальной конструкции, выделенной жирным шрифтом в показанном ниже примере:
class appMyPlugin extends waPlugin { public static function displayData() { //в обеих строках укажите ID приложения и своего плагина waLocale::loadByDomain(array('app_id', 'plugin_id')); waSystem::pushActivePlugin('plugin_id', 'app_id'); $result = _wp('...'); waSystem::popActivePlugin(); return $result; } }
Подробнее о локализации плагинов читайте в основной статье о локализации.
База данных
Если плагин использует собственные таблицы в базе данных, то имена таблиц должны начинаться с фрагмента вида [app_id]_[plugin]_
, например: shop_ebay_tablename
.
Подключение плагина
Для того чтобы написанный плагин заработал, необходимо подключить его в системном конфигурационном файле приложения wa-config/apps/APP_ID/plugins.php
, добавив в него строку:
'plugin_id' => true
Пример этого файла для приложения «Блог» (wa-config/apps/blog/plugins.php
):
<?php return array( 'akismet' => true, 'tag' => true, 'category' => true, 'gravatar' => true, 'favorite' => true, 'troll' => true, );
В этом же файле можно отключить ненужные плагины.
install.php
и uninstall.php
Если при установке плагина требуется выполнить какие-то нестандартные действия (например, добавить новые поля в существующие таблицы приложения), то логику таких действий необходимо описать в конфигурационном файле lib/config/install.php
. Пример добавления дополнительного поля в таблицу при установке плагина:
$model = new waModel(); try { $model->query('SELECT `custom_field` FROM `shop_product` WHERE 0'); } catch (waDbException $e) { $model->exec('ALTER TABLE `shop_product` ADD `custom_field` INT(11) UNSIGNED NULL DEFAULT NULL'); }
Действия, которые нужно выполнить при удалении плагина, аналогичным образом описывайте в файле lib/config/uninstall.php
.
Создание и удаление собственныхтаблиц плагина в файлах
install.php
иuninstall.php
описывать не нужно — таблицы автоматически создаются и удаляются на основании содержимого другого конфигурационного файла:db.php
. См. «Консольная команда для формирования файлаdb.php
».
Пример
Плагинная платформа была внедрена во фреймворк вместе с приложением «Блог», поэтому для дальнейшего изучения рекомендуем установить это приложение и рассмотреть плагины, написанные для него (плагины устанавливаются через «Инсталлер»). Для «Блога» написаны плагины различной направленности и практического применения: для фронтенда, бекенда, расширяющие возможности пользовательского интерфейса, выгрузки данных и пр.