Plugins offer a possibility to extend the standard functionality of an individual app without modifying its source code. The functionality of a plugin remains non-affected, when the corresponding application is being updated (i.e. when its source files are being overwritten), because plugins' logic is contained in separate files and automatically is included in an application. For example, plugin "Tags" for the Blog app, which allows filtering of blog posts by the assigned tags in the backend and the frontend is tightly integrated with the user interface of the Blog; however, it is implemented without direct dependency on the Blog's basic scripts and templates. This allows a developer to update and modify plugins and applications independently.
Webasyst framework contains a plugin development platform, which provides flexibility in the modification of application functionality, standard method of connecting plugins to an application, automated installation via the Installer, and other benefits. This platform is desibed in more details below in this article and is recommended for plugin development. Of course, this platform does not limit a developer in implementing his own plugin management system; however, it is important to remember that such a system will not be integrated with the standard framework functionality nor with the Installer application.
The support for plugins is entirely at the developer's discretion. To allow extending an application's functionality with the help of plugins, the application must be accordingly designed with places provided for plugin developers to include their plugins' logic. Some applications allow extending their functionality with plugins; e.g., Blog; and some do not; e.g., simple applications Guestbook, Checklists.
The developer of an application provides places in the application's logic, where extra functionality can be added. Such places are conventionally called hooksand assume triggering of certain events. For example, in the Blog app there are hooks in the backend's left-hand navigation panel (to allow plugin developers to add their hyperlinks or entire blocks of HTML code there) and hooks assigned to the post saving event (to allow a plugin developer to extend the functionality of the application, which is used to publish a blog post with a function exporting the newly published post to Facebook, for example).
In a specially provided place the application developer can define a hook by adding the following code:
wa()->event('[event_name]', $params);
$params
will contain the parameters passed by reference (!)
Then all plugins are being "scanned" to find those having a handler for the specified event, and all such plugins are called one by one.
Plugins are very similar to applications. Their file structure is identical to the application file structure,
but a plugin resides in subfolder plugins/
inside the application folder:
wa-apps/[app_id]/plugins/[plugin_id]/
The simplest plugin can consist of only two files:
lib/config/plugin.php
— plugin description:
<?php return array( 'name' => 'PLUGIN NAME', 'version' => '1.0', // event => handler relationship (method name in the plugin class) 'handlers' => array( 'EVENT_NAME' => 'method1', //... ), // other optional parameters 'img' => 'img/plugin.png', // 16x16px large icon (to be displayed in the Installer) 'description' => 'PLUGIN DESCRIPTION' );
lib/[app_id][Plugin_id].plugin.php
— class containing event handler description:
<?php class [app_id][Plugin_id]Plugin extends waPlugin // e.g., blogAkismetPlugin { public function method1($params) { // event handler logic // if a template file is required $view = wa()->getView(); $content = $view->fetch($this->path.'/templates/[template file name].html') } // … other event handlers }
Controllers & actions
The functionality of a plugin is implemeneted exactly like in applications. For example, if a plugin must add some block of HTML code to
the a web page or if it is necessary to save some data by means of AJAX, then corresponding controllers and actions should be created in the same fashion
as it is done for applications. The request routing is no different, you only need to add the plugin=PLUGIN
parameter.
For example, requesting URL ?plugin=[plugin_id]&action=edit
will pass over control to action
lib/actions/[app_id][Plugin_id]PluginBackendEdit.action.php
:
<?php class [app_id][Plugin_id]PluginBackendEditAction extends waViewAction { public function execute() { // action logic } }
By default template templates/actions/backend/BackendEdit.html
will be used.
When developing action and controller classes for uploading data to the server using the POST method, consider the necessity to comply with requirements of the built-in anti-CSRF protection if it is enabled for the application your a developing a plugin for. Non-compliance will result in failure to upload data. Read more about the anti-CSRF protection at http://www.webasyst.com/framework/docs/dev/csrf/.
Plugin settings
By default, a plugin has no settings. To add settings options, describe them in file lib/config/settings.php
. The contents of this file will be used for automatic generation of the plugin settings screen.
<?php return array( 'api_key' => array( 'title' => /*_wp*/('Akismet API Key'), 'description' => array( /*_wp*/('Get an API key for your domain at <a target="_blank" href="%s">Akismet website</a>'), 'https://akismet.com/signup/' ), 'value' => '', // default value 'control_type'=> waHtmlControl::INPUT, ), );
If default settings options are not sufficient and you need to add custom JavaScript code or use your own HTML template, add parameter custom_settings
to file lib/config/plugin.php
:
'custom_settings' => true,
Then create settings action in file lib/actions/[app_id][Plugin_id]PluginSettings.action.php
:
<?php class [app_id][Plugin_id]PluginSettingsAction extends waViewAction { public function execute() { $plugin = wa('[app_id]')->getPlugin('[Plugin_id]'); // getting plugin settings to pass them to template $settings = $plugin->getSettings(); $this->view->assign('settings', $settings); } }
Custom action class requires a custom HTML template: templates/actions/settings/Settings.html
; e.g.:
<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>
Note that the value of the name
attribute if settings controls must begin with [app_id]_[plugin_id]
followed by the field ID; e.g., shop_brands[feature_id]
.
You do not need to create a custom JavaScript handler and a PHP controller for saving custom settings fields. You may implement custom logic by overriding plugin's main class method saveSettings
:
public function saveSettings($settings = array()) { // add your custom logic here ... parent::saveSettings($settings); }
Scheduled actions
Webasyst framework offers an infrastructure for creation of scripts, which can be launched from within the command line (cli scripts) and, consequently, can be used to set up a cron job. Plugins can have their own cli scripts, separately from those used by the application. Cli scripts should be created as PHP classes extending base class waCliController
. The main logic must be contained in method execute()
. The path to the PHP file of a cli script must have the following form: wa-apps/[app_id]/plugins/[plugin_id]/lib/cli/[app_id][Plugin_id]Classname.cli.php
and the name of a cli script class must be created according to rule [app_id][Plugin_id]ClassnameCli
.
For example, some cli script named update
for plugin brands
to the shop
application should reside in file wa-apps/shop/plugins/brands/lib/cli/shopBrandsUpdate.cli.php
and have approximately the following contents:
<?php class shopBrandsUpdateCli extends waCliController { public function execute() { // Add code which should be executed when this script is called // You can use models and other app's classes } }
To call such a cli script, enter the following command in your server's console:
php cli.php shop brandsUpdate
Read more about creation and use of cli scripts in article "Scheduled actions".
Localization
Localization of plugins is implemented in exactly the same manner as for applications (view
documentation). You need to place localization files *.po и *.mo in folder
locale
and then you can use localization keys as shown below:
_wp($string)
in PHP code (instead of the _w() method, which is used only for the localization of applications, use method _wp(), which will load the localization of your plugin),[`string`]
in Smarty templates (here is no difference from the applications localization).
The plugin name and description (name
и description
parameters in the configuration file) are localized in accordance with the
default plugin localization; therefore, you do need to use 'name' => _wp('PLUGIN NAME')
; it is enough to write 'name'=>'PLUGIN NAME'
.
Using localization in static methods
When public static methods of plugin classes are used in external environment; e.g., in a design theme, plugin localization is not linked by default; therefore, function _wp()
will not return the localizaed string value as you might expect. To use the plugin loclization mechanism in such methods, be sure to call _wp()
function inside a special construct shown in bold in the example below:
class appMyPlugin extends waPlugin { public static function displayData() { //specify app ID and your plugin ID in both lines waLocale::loadByDomain(array('app_id', 'plugin_id')); waSystem::pushActivePlugin('plugin_id', 'app_id'); $result = _wp('...'); waSystem::popActivePlugin(); return $result; } }
Read more about localization of plugins →.
Database
If your plugin utilizes its own database tables then their names must begin with a prefix of the form [app_id]_[plugin]_
; e.g., shop_ebay_tablename
.
Connecting a plugin
To make a new plugin accessible for use, it must be enabled in the application's configuration file
wa-config/apps/[app_id]/plugins.php
through adding the following line:
'plugin_id' => true
Below is an example of this file for the Blog app (wa-config/apps/blog/plugins.php
):
<?php return array( 'akismet' => true, 'tag' => true, 'category' => true, 'gravatar' => true, 'favorite' => true, 'troll' => true, );
In this file, you can disable unnecessary plugins.
install.php
& uninstall.php
If some additional actions need to be performed when a plugin is being installed; e.g., adding of a custom field to a default app table in the database, then you must describe such actions in configuration file lib/config/install.php
. Below is shown an example of adding a custom database table field during plugin installation:
$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'); }
Actions necessary during plugin uninstallation must be described in configuration file lib/config/uninstall.php
.
Do not describe creation and deletion of plugin's own database tables in filesinstall.php
anduninstall.php
, because the tables are automatically created and deleted based on the contents of another configuration filedb.php
. See "Console command for generating filedb.php
".
Example
The plugin-oriented platform was introduced in the framework with the release of the Blog application; therefore, we suggest installing this application and learning the details of plugin development by the example of its available plugins (plugins are installable via the Installer app). The plugins written for the Blog have very different areas of application: for the frontend, for the backend, for extending the functionality of the user interface, for data import, etc.