The following code listings illustrate a basic sample plugin for the
generic plugin category. This plugin can be installed by placing
all of its files in a directory called
plugins/generic/example.
This plugin will add an entry to the Journal Manager's list of functions, available by following the “Journal Manager" link from User Home.
The plugin is loaded by OJS by loading a file in the plugin directory called
index.php. This is a loader stub responsible for
instantiating and returning the plugin object:
Example 5.1. Plugin Loader Stub
<?php
require_once('ExamplePlugin.inc.php');
return new ExamplePlugin();
?>
The plugin object encapsulates the plugin and generally will do most of the work. In this case, since this plugin will be in the generic category, the object must extend the GenericPlugin class:
Example 5.2. Plugin Object
<?php
import('classes.plugins.GenericPlugin');
class ExamplePlugin extends GenericPlugin {
function register($category, $path) {
if (parent::register($category, $path)) {
HookRegistry::register(
'Templates::Manager::Index::ManagementPages',
array(&$this, 'callback')
);
return true;
}
return false;
}
function getName() {
return 'ExamplePlugin';
}
function getDisplayName() {
return 'Example Plugin';
}
function getDescription() {
return 'A description of this plugin';
}
function callback($hookName, $args) {
$params =& $args[0];
$smarty =& $args[1];
$output =& $args[2];
$output = '<li>» <a href=”http://pkp.sfu.ca”>My New Link</a></li>';
return false;
}
}
?>
The above code illustrates a few of the most important parts of plugins: the
register function, hook registration and callback, and plugin
management.
Whenever OJS loads and registers a plugin, the plugin's
register(...) function will be called. This is an opportunity
for the plugin to register against any hooks it needs, load configuration,
initialize data structures, etc. In the above example, all the plugin needs to
do (aside from calling the parent class's register function) is
register against the Templates::Manager::Index::ManagementPages
hook.
Another common task to perform in the registration function is loading locale
data. Locale data should be included in subdirectories of the plugin's directory
called locale/[localeName]/locale.xml, where
[localeName] is the standard symbolic name of the
locale, such as en_US for US English. In order for these data files
to be loaded, plugins should call $this->addLocaleData(); in the
registration function after calling the parent registration function.
The above example serves as a clear illustration of hook registration and callback; along with the list of hooks below, this should provide all the required information for extending OJS using hooks. However, there are a few important details that need further examination.
The process by which a plugin registers against a hook is as follows:
Example 5.3. Hook Registration Process
HookRegistry::register(
'Templates::Manager::Index::ManagementPages',
array(&$this, 'callback')
);
In the example above, the parameters to HookRegistry::register
are:
The name of the hook. See the complete list of hooks below.
The callback function to which control should be passed when the hook
is encountered. This is the same callback format used by PHP's
call_user_func function; see the documentation at http://php.net for more
information. It is important that $this be included in the
array by reference, or you may encounter problems with multiple
instances of the plugin object.
The definition of the callback function (named and located in the above registration call) is:
Example 5.4. Hook Registration Definition
function callback($hookName, $args) {
$params =& $args[0];
$smarty =& $args[1];
$output =& $args[2];
...
}
The parameter list for the callback function is always the same:
The name of the hook that resulted in the callback receiving control (which can be useful when several hook registrations are made with the same callback function), and
An array of additional parameters passed to the callback. The contents of this array depend on the hook being registered against. Since this is a template hook, the callback can expect the three parameters named above.
The array-based passing of parameters is slightly cumbersome, but it allows
hook calls to compatibly pass references to parameters if desired. Otherwise,
for example, the above code would receive a duplicated Smarty object rather than
the actual Smarty object and any changes to attributes of the
$smarty object would disappear upon returning.
Finally, the return value from a hook callback is very important. If a hook
callback returns true, the hook registry considers this callback to
have definitively “handled" the hook and will not call further registered
callbacks on the same hook. If the callback returns false, other
callbacks registered on the same hook after the current one will have a chance
to handle the hook call.
The above example adds a link to the Journal Manager's list of management
functions. If another plugin (or even the same plugin) was registered to add
another link to the same list, and this plugin returned true, the
other plugin's hook registration would not be called.
In the example plugin, there are three functions that provide metadata about
the plugin: getName(), getDisplayName(), and
getDescription(). These are part of a management interface that
is available to the Journal Manager under “System Plugins".
The result of the getName() call is used to refer to the plugin
symbolically and need not be human-readable; however, the
getDisplayName() and getDescription() functions
should return localized values. This was not done in the above example for
brevity.
The management interface allows plugins to specify various management
functions the Journal Manager can perform on the plugin using the
getManagementVerbs() and manage($verb, $args)
functions. getManagementVerbs() should return an array of
two-element arrays as follows:
Example 5.5. Specifying Management Verbs
$verbs = parent::getManagementVerbs();
$verbs[] = array('func1', Locale::translate('my.localization.key.for.func1'));
$verbs[] = array('func2', Locale::translate('my.localization.key.for.func2'));
Note that the parent call should be respected as above, as some plugin categories provide management verbs automatically.
Using the above sample code, the plugin should be ready to receive the
management verbs func1 and func2 as follows (once
again respecting any management verbs provided by the parent class):
Example 5.6. Receiving Management Verbs
function manage($verb, $args) {
if (!parent::manage($verb, $args)) switch ($verb) {
case 'func1':
// Handle func1 here.
break;
case 'func2':
// Handle func2 here.
break;
default:
return false;
}
return true;
}