Plugins¶
Plugins are one of the key component of Tuleap. The code is structured around:
- Core (everything under
src/
) - Plugins (located under
plugins/pluginname
)
Plugins can provide a new service (like AgileDashboard or Git) or underlying plumbing with almost no dedicated UI (like LDAP). A plugin can depend on another (Cardwall depends on Tracker).
Plugins rely on events to change behaviour of a given part of the code (either Core or another Plugin).
Unless a very good reason, all new significant development MUST be done within a plugin. It’s always true for new services.
Structure¶
A plugin folder is structured like
db # Plugin tables and start data creation, uninstall & upgrade buckets etc # Configuration include # Plugin code site-content # Localisation strings template # mustache files tests # Unit & rest tests www # Entry point for web pages
As a good start, you can copy plugins/template
and rename all template
stuff
to your plugin name.
There are a handful of files already there, the most important are:
db/install.sql
definition of tables and initial datadb/uninstall.sql
clean-up the base when the plugin is removed (purge)include/templatePlugin.class.php
the entry point that define the plugin behaviourinclude/Template/Plugin/PluginDescription.php
describe the plugininclude/Template/Plugin/PluginInfo.php
plugin’s metadata (optionally access to the configuration values)
Note: if you copy the default plugin for “mercurial” plugin for instance, you will end up with:
mercurial/include/mercurialPlugin.class.php
mercurial/include/Mercurial/Plugin/PluginDescription.php
mercurial/include/Mercurial/Plugin/PluginInfo.php
pluginnamePlugin.class.php¶
This is the central place for plugin interaction with the rest of the application.
Let’s take a look at what our basic Mercurial plugin would look like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 require_once 'autoload.php'; require_once 'constants.php'; class mercurialPlugin extends Plugin { public function __construct($id) { parent::__construct($id); $this->setScope(self::SCOPE_PROJECT); } /** * @return Tuleap\Mercurial\Plugin\PluginInfo */ public function getPluginInfo() { if (!$this->pluginInfo) { $this->pluginInfo = new Tuleap\Mercurial\Plugin\PluginInfo($this); } return $this->pluginInfo; } public function getServiceShortname() { return 'plugin_mercurial'; } public function process() { $renderer = TemplateRendererFactory::build()->getRenderer(MERCURIAL_BASE_DIR.'/template'); $renderer->renderToPage('index', array('world' => 'World')); } }
Line by line:
- L1: code must not explicitly require PHP class definitions, everything should pass through the autoloader (automatically generated by a
make autoload-docker
at the root of the sources) - L2: generic class constants must be defined there (eg. L26, usage of
MERCURIAL_BASE_DIR
) - L8: define the scope to either
SCOPE_SYSTEM
(for system wide features like ldap, openidconnect, etc) orSCOPE_PROJECT
for plugins that is relevant in the context of a project (here mercurial would be a service of the project) - L14 & L21: boilerplate to manage plugin description
- L25: example of a basic controller (should only be done with very basic plugins).
Why process
in the plugin class?
It aims to encapsulate the controller with plugin information to only execute when
plugin is activated. Example with the corresponding www/index.php
:
$plugin_manager = PluginManager::instance(); $plugin = $plugin_manager->getPluginByName('mercurial'); if ($plugin && $plugin_manager->isPluginAvailable($plugin)) { $plugin->process(); } else { $GLOBALS['Response']->redirect('/'); }
Bring a new service to life¶
At this stage the plugin doesn’t do anything useful but it will display “Hello World” when the plugin is activated and someone reach the URL https://tuleap.example.com/plugins/mercurial
TBC…