Plugin basics

Contents...

Plugins offer a posibility to extend the standard functionality of an invididual 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.

A plugin can be developed for only one application (not for several applications and for the entire framework). 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 terms of the terminology the difference between a hook and an event is similar to that between the syntax and the semantics. A hook is a place in the source code, and an event is the specific way to add new functionality.

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. Method wa()->event($event_name, $params); returns an array with the following structure:

array(
      '[plugin_id]-plugin' => result returned by the plugin's event handler
);

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 sould 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:

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 contruct 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 files install.php and uninstall.php , because the tables are automatically created and deleted based on the contents of another configuration file db.php. See "Console command for generating file db.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.

View list of events available in various apps →