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 Backlog 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 there is a very good reason, all new significant work MUST be done within a plugin. It's always true for new services.
See the corresponding ADR: ADR-0030 - PHP folder structure for plugins.
A plugin folder is structured like
/plugins/template/
├── db/ # Plugin tables and start data creation, uninstall & upgrade buckets
│ ├── mysql/
│ │ └── updates/
│ │ └── 2024/
│ │ └── 202411041134_some_db_update.php
│ ├── install.sql
│ └── uninstal.sql
├── etc/ # Configuration
├── include/ # Plugin code
│ └── templatePlugin.php
├── scripts/ # Front-end applications
│ └── my_script/
│ └── package.json
├── site-content/ # Localization strings
│ ├── fr_FR/LC_MESSAGES/tuleap-template.po
│ └── pt_BR/LC_MESSAGES/tuleap-template.po
├── tests/ # Testing
│ ├── e2e/
│ ├── integration/
│ ├── rest/
│ └── unit/
├── composer.json
└── composer.lock
As a good start, you can copy plugins/template
and rename all
template
stuff to your plugin name.
There is a handful of files already there, the most important ones are:
db/install.sql
definition of tables and initial datadb/uninstall.sql
clean-up the database when the plugin is removed (purge)include/templatePlugin.class.php
the entry point that defines the plugin behaviour
Note: if you copy the default plugin for "mercurial" plugin for instance, you will end up with:
mercurial/include/mercurialPlugin.class.php
This directory contains all the PHP plugin code and also Mustache template files. The main file is pluginnamePlugin.php
, it
declares the plugin, its events, its router, etc. This directory must be under namespace Tuleap\PluginName
.
pluginnamePlugin.php
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:
<?php
declare(strict_types=1);
// code must not explicitly require PHP class definitions,
// everything should pass through the autoloader (automatically
// generated by Composer)
require_once __DIR__ . '/../vendor/autoload.php';
// phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace,Squiz.Classes.ValidClassName.NotCamelCaps
final class mercurialPlugin extends Plugin
{
public function __construct($id)
{
parent::__construct($id);
// 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)
$this->setScope(self::SCOPE_PROJECT);
bindtextdomain('tuleap-mercurial', __DIR__ . '/../site-content');
}
// boilerplate to manage plugin description
public function getPluginInfo() : PluginInfo
{
if (! $this->pluginInfo) {
$plugin_info = new PluginInfo($this);
$plugin_info->setPluginDescriptor(new PluginDescriptor(
dgettext('tuleap-mercurial', 'Mercurial plugin'),
dgettext('tuleap-mercurial', 'Description of mercurial plugin'),
));
$this->pluginInfo = $plugin_info;
}
return $this->pluginInfo;
}
public function getServiceShortname() : string
{
return 'plugin_mercurial';
}
}
For more, see this documentation:
Plugins tests are all here (except typescript unit tests). There is mainly 4 test environments:
- e2e ⟶ Cypress tests
- integration ⟶ testing the database and queries on it
- rest ⟶ testing rest API
- unit ⟶ good old PHP unit testing
For the last three environments, all those tests are written in PHP under the Tuleap\PluginName
namespace. If some
stubs or builders are needed, add directories Tests\Stubs
and Tests\Builders
inside.
This directory is a collection of front-end applications. Each application must be under its own directory. See the front-end documentation.
It contains all localization strings for the plugin. See internationalization.
It must contain at least install.sql
and uninstall.sql
. Those SQL scripts are executed when the plugin is
respectively installed and uninstalled.
install.sql
create needed tables and initialize plugin data. uninstall.db
drop previously created table.
When database schema is modified, a new update bucket can be created inside mysql/updates
. This directory contains one
subdir per year. Each one containing upgrades. More details
in database section 'Database structure change with ForgeUpgrade'.
If the plugin needs some configuration variables hosted on the server (not a ConfigKey) (e.g. gitolite config for git plugin)