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 .. code-block:: bash 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 data * ``db/uninstall.sql`` clean-up the base when the plugin is removed (purge) * ``include/templatePlugin.class.php`` the entry point that define the plugin behaviour * ``include/Template/Plugin/PluginDescription.php`` describe the plugin * ``include/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 .. code-block:: php :linenos: setScope(self::SCOPE_PROJECT); } public function getPluginInfo() : Tuleap\Mercurial\Plugin\PluginInfo { if (! $this->pluginInfo) { $this->pluginInfo = new Tuleap\Mercurial\Plugin\PluginInfo($this); } return $this->pluginInfo; } public function getServiceShortname() : string { return 'plugin_mercurial'; } public function process() : void { $renderer = TemplateRendererFactory::build()->getRenderer(MERCURIAL_BASE_DIR.'/template'); $renderer->renderToPage('index', array('world' => 'World')); } } Line by line: * L5: code must not explicitly require PHP class definitions, everything should pass through the autoloader (automatically generated by Composer) * L11: define the scope to either ``SCOPE_SYSTEM`` (for system wide features like ldap, openidconnect, etc) or ``SCOPE_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``: .. code-block:: 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...*