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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
Plugin Registration
===================
Directory structure
-------------------
Plugins are stored in the `plugins` subdirectory. An example of a plugin directory structure:
```bash
plugins
└── Budget <= Plugin name
├── Asset <= Javascript/CSS files
├── Controller
├── LICENSE <= Plugin license
├── Locale
│ ├── fr_FR
│ ├── it_IT
│ ├── ja_JP
│ └── zh_CN
├── Model
├── Plugin.php <= Plugin registration file
├── README.md
├── Schema <= Database migrations
├── Template
└── Test <= Unit tests
```
Only the registration file `Plugin.php` is required. Other folders are optional.
The first letter of the plugin name must be capitalized.
Plugin Registration File
------------------------
Kanboard will scan the directory `plugins` and load automatically everything under this directory. The file `Plugin.php` is used to load and register the plugin.
Example of `Plugin.php` file (`plugins/Foobar/Plugin.php`):
```php
<?php
namespace Kanboard\Plugin\Foobar;
use Kanboard\Core\Plugin\Base;
class Plugin extends Base
{
public function initialize()
{
$this->template->hook->attach('template:layout:head', 'theme:layout/head');
}
}
```
This file should contain a class `Plugin` defined under the namespace `Kanboard\Plugin\Yourplugin` and extends `Kanboard\Core\Plugin\Base`.
The only required method is `initialize()`. This method is called for each request when the plugin is loaded.
Plugin Methods
--------------
Available methods from `Kanboard\Core\Plugin\Base`:
- `initialize()`: Executed when the plugin is loaded
- `getClasses()`: Return all classes that should be stored in the dependency injection container
- `on($event, $callback)`: Listen on internal events
- `getPluginName()`: Should return plugin name
- `getPluginAuthor()`: Should return plugin author
- `getPluginVersion()`: Should return plugin version
- `getPluginDescription()`: Should return plugin description
- `getPluginHomepage()`: Should return plugin Homepage (link)
- `setContentSecurityPolicy(array $rules)`: Override default HTTP CSP rules
- `onStartup()`: If present, this method is executed automatically when the event "app.bootstrap" is triggered
Your plugin registration class can also inherit from Kanboard\Core\Base, that way you can access all classes and methods of Kanboard easily.
This example will fetch the user #123:
```php
$this->user->getById(123);
```
Plugin Translations
-------------------
Plugin can be translated in the same way as the rest of the application. You must load the translations yourself when the session is created:
```php
public function onStartup()
{
Translator::load($this->languageModel->getCurrentLanguage(), __DIR__.'/Locale');
}
```
The translations must be stored in the file `plugins/Myplugin/Locale/xx_XX/translations.php` (replace xx_XX by the language code fr_FR, en_US...).
Translations are stored in a dictionary, if you would like to override an existing string, you just need to use the same key in your translation file.
Dependency Injection Container
------------------------------
Kanboard uses Pimple, a simple PHP Dependency Injection Container. However, Kanboard can register any class in the container easily.
Those classes are available everywhere in the application and only one instance is created.
Here an example to register your own models in the container:
```php
public function getClasses()
{
return array(
'Plugin\Budget\Model' => array(
'HourlyRateModel',
'BudgetModel',
)
);
}
```
Now, if you use a class that extends from `Core\Base`, you can access directly to those class instance:
```php
$this->hourlyRateModel->remove(123);
$this->budgetModel->getDailyBudgetBreakdown(456);
// It's the same thing as using the container:
$this->container['hourlyRateModel']->getAll();
```
Keys of the containers are unique across the application. If you override an existing class, you will change the default behavior.
Add new API methods
-------------------
Kanboard use this library [JSON-RPC](https://github.com/fguillot/JsonRPC) to handle API calls.
To add a new method you can do something like that from your plugin:
```php
$this->api->getProcedureHandler()->withCallback('my_method', function() {
return 'foobar';
});
```
`$this->container['api']` or `$this->api` expose an instance of the object `JsonRPC\Server`.
Read the library documentation for more information.
Add new console commands
------------------------
Kanboard use the library [Symfony Console](http://symfony.com/doc/current/components/console/introduction.html) to handle local command lines.
Kanboard expose an instance of the object `Symfony\Component\Console\Application` via `$this->cli`.
You can add new commands from your plugin:
```php
$this->cli->add(new MyCommand());
```
Read the library documentation for more information.
Add new task filters
--------------------
Since the task lexer is a factory that returns a new instance each time,
you have to extend the `taskLexer` container with the method `extend()` of Pimple.
Here is a example:
```php
public function initialize()
{
$this->container->extend('taskLexer', function($taskLexer, $c) {
$taskLexer->withFilter(TaskBoardDateFilter::getInstance($c)->setDateParser($c['dateParser']));
return $taskLexer;
});
}
```
For the filter class implementation, there are several examples in the source code under the namespace `Kanboard\Filter`.
|