| Version 9 (modified by corbosman, 4 years ago) (diff) |
|---|
Plugin Resources
Plugins are tools to extend the functionality of RoundCube. They are not part of the application core but can be installed and activated individually.
How to write a plugin
Names, Files and Locations
Each plugin defines a folder in the local plugins directory [roundcuberoot]/plugins. Make sure you choose a unique, unix-style name for your plugin folder. A plugin can be built with any number of files inside this folder but there needs to be one file named the same as the folder itself (with .php added) which holds the plugin class definition. This class extends the rcube_plugin class and has again the same name as the plugin folder itself.
For example, if your plugin is called Fancy Emoticons the unix-name will probably be fancy_emoticons. This will lead to the following file structure:
plugins/
fancy_emoticons/
fancy_emoticons.php
localization/
media/
Inside fancy_emoticons.php you create the plugin class with the name fancy_emoticons which will be explained in the next section.
The Plugin class
RoundCube defines an abstract class named rcube_plugin which provides basic functions and connections to the internal API. Each plugin consists of a class which extends rcube_plugin. The plugin API instantiates a plugin object and then calls the method init() which has to be implemented by the derived plugin class. This startup method is the place where a plugin registers hooks, actions and scripts that will be passed to the client.
To continue our sample plugin Fancy Emoticons, the plugin class could look like this:
/**
* Fancy Emoticons
*
* Sample plugin to replace emoticons in plain text message body with real icons
*
* @version 1.0
* @author Thomas Bruederli
* @url http://roundcube.net/plugins/fancy_emoticons
*/
class fancy_emoticons extends rcube_plugin
{
public $task = 'mail';
private $map;
function init()
{
$this->add_hook('message_part_after', array($this, 'replace'));
$this->map = array(
':)' => html::img(array('src' => $this->urlbase.'media/smile.gif', 'alt' => ':)')),
':-)' => html::img(array('src' => $this->urlbase.'media/smile.gif', 'alt' => ':-)')),
':(' => html::img(array('src' => $this->urlbase.'media/cry.gif', 'alt' => ':(')),
':-(' => html::img(array('src' => $this->urlbase.'media/cry.gif', 'alt' => ':-(')),
);
}
function replace($args)
{
if ($args['type'] == 'plain')
return array('body' => strtr($args['body'], $this->map));
return null;
}
}
Let's have a deeper look at the above example:
- The plugin class should be preceded by a comment block that describes the plugin.
- If your plugin is meant to only run in a certain task (e.g. mail,addressbook,settings) you should specify a public property $task. If this property is set the plugin will only be activated within that specific task in order to save memory and performance in all the other tasks.
- The method init() is mandatory and first links the hook message_part_after to the object method replace.
- The callback method replace receives one argument containing context-specific data. In this example we first check if the message type is plain and then the body field is altered and returned. The plugin API will replace the returned fields in it's original context.
Plugin hooks
The way plugin hooks work is that at various times while RoundCube is procesing, it checks to see if any plugins have registered functions to run at that time, and if so, the functions are run (by executing a "hook"). These functions may modify or extend the default behavior of RoundCube.
Registration of hooks is done by calling
$this->add_hook('hook-name', $callback);
where the second argument is a PHP Callback which can link to a simple function or an object method.
The registered function receives one hash array as argument which contains specific data of the current context depending on the hook. See Plugin_Hooks for a complete description of all hooks and their argument fields. The argument var may be altered by the callback function and can (even partially) be returned to the application.
Client scripts and UI elements
Of course there is more to do with plugins than just catching events on the server side. The plugin API also enables you to extend the UI and the client functionality.
First step is to add JavaScript code to a specific page/action. Create a script file in your plugin folder and then include it in the init() method of your plugin class with
$this->include_script('client.js');
The client script can actually make use of all the methods of the RoundCube application object. This application object - which can be accessed by window.rcmail - also provides hooks but in a slightly different way: similar to the browser DOM one can register event listeners using the following methods:
rcmail.addEventListener('event', callback);
rcmail.removeEventListener('event', callback);
For a detailed list of events supported by the app see Plugin_Events. The most important event of course is init. This is where your plugin can add buttons and register its own commands to the main application script. Here's a sample showing what a plugin script could look like:
rcmail.addEventListener('init', function(evt) {
// create custom button
var button = $('<A>').attr('id', 'rcmSampleButton').html(rcmail.gettext('buttontitle', 'sampleplugin'));
button.bind('click', function(e){ return rcmail.command('plugin.samplecmd', this); });
// add and register
rcmail.add_element(button, 'toolbar');
rcmail.register_button('plugin.samplecmd', 'rcmSampleButton', 'link');
rcmail.register_command('plugin.samplecmd', sample_handler, true);
});
With rcmail.add_element(button,'toolbar') the button is appended to the toolbar container. Then rcmail.register_button() tells the application where the button is and which command it is responsible for. And finally rcmail.register_command() links the custom command (triggered by the onclick handler of the button) with the callback function of the plugin client script. For a detailed documentation of the client API, see Doc_Javascript.
jQuery is a fixed part of RoundCube which allows you to make use of the very comfortable jQuery functions when writing plugin scripts.
Custom actions
Now you still need to combine the client functionality of your plugin with server-side actions. The client script can send GET or POST ajax-requests to the server by using rcmail.http_get('plugin.someaction', ...) and rcmail.http_post('plugin.someaction', ...).
In order to direct these requests to the right function of your plugin, a custom action (e.g. 'plugin.someaction') is registered in the init() method of the plugin class:
$this->register_action('plugin.someaction', array($this, 'request_handler'));
Now HTTP requests of the form ./?_task=mail&_action=plugin.someaction will trigger the registered callback function. This function is responsible to process the request and send a valid response back to the client. Calling rcmail::get_instance()->output->send('plugin') at the end of the function will do the job.
Custom actions are not only meant to serve ajax-requests but can also extend the application with custom screens and steps.
Templates
In order to render HTML pages from a plugin there exists a blank skin template named plugin which can be used. This template defines one object container where the HTML content is put into. Your plugin therefore needs to define a handler function which is called once the template engine processes the placeholder tag:
$this->register_handler('plugin.body', array($this, 'generate_html'));
function generate_html()
{
...
return($the_content);
}
If the blank plugin template does not fulfill your needs, you can create your own templates. You can put these inside a sub directory of your plugin:
plugins/
my_plugin/
my_plugin.php
localization/
media/
skins/myskin/templates/mytemplate.html
mytemplate.html can have one or more object containers that can be filled by your plugin. You can use plugin.html from the default skin as an example to start with.
<roundcube:object name="plugin.my_content" />
In your plugin code, you need to link this container with a function using a handler:
$this->register_handler('plugin.my_content', array($this, 'my_function'));
function my_function()
{
$content = "Foo";
return($content);
}
Finally, to instruct roundcube to show your template:
$rcmail = rcmail::get_instance();
$rcmail->output->set_pagetitle('my_title');
$rcmail->output->send('mytemplate');
When roundcube renders the template it encounters the container objects, and for each object will call the specific handler function to generate the HTML.
$this->include_script(...) , $this->include_stylesheet(...) and others can be used to include Javascript and CSS. Have a look at the rcube_template class to find methods for adding stuff to the HTML output of your handler function.
Plugin Configuration
Each plugin can have its own configuration file:
plugins/
fancy_emoticons/
fancy_emoticons.php
config.inc.php
localization/
media/
The file can be loaded through an API call:
$this->load_config();
Even though config.inc.php is the default name, you can use your own filename as well. This allows you to do things like:
$this->load_config('config.inc.php.dist');
$this->load_config('config.inc.php');
This will load a distribution config file, and then merge a local configuration file overriding any settings.
Internationalization
Plugins that add UI elements usually have their own texts to name and describe things. It is easy to use the integrated localization system of RoundCube to display texts in the user's language.
First, create php-style localization files that contain the custom texts in an array named $labels, just as in program/localization/*/labels.inc.
Then put these files in a subfolder of your plugin folder and name them like [langcode].inc. The langcode is a combination of language and country code (see trunk/roundcubemail/program/localization/index.inc for a list of language codes). Having a localization file named en_US.inc is mandatory!
Finally, tell the plugin API where to search for texts by calling
$this->add_texts('localization/', true);
in the init() sequence of your plugin. The first argument is the folder name (relative to the plugin folder) which contains all the localization files. The seconds argument makes the texts available on the client if set to true.
Now you can access the localized texts with $this->gettext('somelabel') in PHP and rcmail.gettext('somelabel', 'plugin-name') in JavaScript.
Development suggestions
TBD.
Installing and activating Plugins
After downloading and unpacking a plugin in RoundCube's plugin directory you have to activate it by adding its unix-name to the array of the config option plugins in your local config/main.inc.php file. Only plugins enabled by config will be loaded and executed.
Public Plugin Repository
Here's a temporary list of available plugins: Plugin_Repository
All plugins are currently available for the 0.3-beta version of RoundCube.
