summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/email-configuration.markdown61
-rw-r--r--doc/index.markdown3
-rw-r--r--doc/mailgun.markdown28
-rw-r--r--doc/plugin-hooks.markdown148
-rw-r--r--doc/plugin-overrides.markdown36
-rw-r--r--doc/plugin-registration.markdown200
-rw-r--r--doc/plugin-schema-migrations.markdown38
-rw-r--r--doc/plugins.markdown424
-rw-r--r--doc/postmark.markdown29
-rw-r--r--doc/sendgrid.markdown24
10 files changed, 432 insertions, 559 deletions
diff --git a/doc/email-configuration.markdown b/doc/email-configuration.markdown
index c66996c6..40736c6a 100644
--- a/doc/email-configuration.markdown
+++ b/doc/email-configuration.markdown
@@ -20,9 +20,7 @@ There are several email transports available:
- SMTP
- Sendmail
- PHP native mail function
-- Mailgun
-- Postmark
-- Sendgrid
+- Other methods can be provided by external plugins: Postmark, Sendgrid and Mailgun
Server settings
---------------
@@ -77,63 +75,6 @@ This is the default configuration:
define('MAIL_TRANSPORT', 'mail');
```
-### Mailgun HTTP API
-
-You can use the HTTP API of Mailgun to send emails.
-
-Configuration:
-
-```php
-// We choose "mailgun" as mail transport
-define('MAIL_TRANSPORT', 'mailgun');
-
-// Mailgun API key
-define('MAILGUN_API_TOKEN', 'YOUR_API_KEY');
-
-// Mailgun domain name
-define('MAILGUN_DOMAIN', 'YOUR_DOMAIN_CONFIGURED_IN_MAILGUN');
-
-// Be sure to use the sender email address configured in Mailgun
-define('MAIL_FROM', 'sender-address-configured-in-mailgun@example.org');
-```
-
-### Postmark HTTP API
-
-Postmark is a third-party email service.
-If you already use the Postmark integration to receive emails in Kanboard you can use the same provider to send email too.
-
-This system use their HTTP API instead of the SMTP protocol.
-
-Here are the required settings for this configuration:
-
-```php
-// We choose "postmark" as mail transport
-define('MAIL_TRANSPORT', 'postmark');
-
-// Copy and paste your Postmark API token
-define('POSTMARK_API_TOKEN', 'COPY HERE YOUR POSTMARK API TOKEN');
-
-// Be sure to use the Postmark configured sender email address
-define('MAIL_FROM', 'sender-address-configured-in-postmark@example.org');
-```
-
-### Sendgrid HTTP API
-
-You can use the HTTP API of Sendgrid to send emails.
-
-Configuration:
-
-```php
-// We choose "sendgrid" as mail transport
-define('MAIL_TRANSPORT', 'sendgrid');
-
-// Sendgrid username
-define('SENDGRID_API_USER', 'YOUR_SENDGRID_USERNAME');
-
-// Sendgrid password
-define('SENDGRID_API_KEY', 'YOUR_SENDGRID_PASSWORD');
-```
-
### The sender email address
By default, emails will use the sender address `notifications@kanboard.local`.
diff --git a/doc/index.markdown b/doc/index.markdown
index 797c8fae..86b5b7fc 100644
--- a/doc/index.markdown
+++ b/doc/index.markdown
@@ -68,10 +68,7 @@ Using Kanboard
- [Gitlab webhooks](gitlab-webhooks.markdown)
- [Hipchat](hipchat.markdown)
- [Jabber](jabber.markdown)
-- [Mailgun](mailgun.markdown)
-- [Sendgrid](sendgrid.markdown)
- [Slack](slack.markdown)
-- [Postmark](postmark.markdown)
- [iCalendar subscriptions](ical.markdown)
- [RSS/Atom subscriptions](rss.markdown)
- [Json-RPC API](api-json-rpc.markdown)
diff --git a/doc/mailgun.markdown b/doc/mailgun.markdown
deleted file mode 100644
index 6465903a..00000000
--- a/doc/mailgun.markdown
+++ /dev/null
@@ -1,28 +0,0 @@
-Mailgun
-=======
-
-You can use the service [Mailgun](http://www.mailgun.com/) to create tasks directly by email.
-
-This integration works with the inbound email service of Mailgun (routes).
-Kanboard use a webhook to handle incoming emails.
-
-The [incoming email workflow is described here](create-tasks-by-email.markdown).
-
-Mailgun configuration
----------------------
-
-Create a new route in the web interface or via the API ([official documentation](https://documentation.mailgun.com/user_manual.html#routes)), here an example:
-
-```
-match_recipient("^kanboard\+(.*)@mydomain.tld$")
-forward("https://mykanboard/?controller=webhook&action=mailgun&token=mytoken")
-```
-
-The Kanboard webhook url is displayed in **Settings > Integrations > Mailgun**
-
-Kanboard configuration
-----------------------
-
-1. Be sure that your users have an email address in their profiles
-2. Assign a project identifier to the desired projects: **Project settings > Edit**
-3. Try to send an email to your project
diff --git a/doc/plugin-hooks.markdown b/doc/plugin-hooks.markdown
new file mode 100644
index 00000000..eca8c14a
--- /dev/null
+++ b/doc/plugin-hooks.markdown
@@ -0,0 +1,148 @@
+Plugin Hooks
+============
+
+Application Hooks
+-----------------
+
+Hooks can extend, replace, filter data or change the default behavior. Each hook is identified with a unique name, example: `controller:calendar:user:events`
+
+### Listen on hook events
+
+In your `initialize()` method you need to call the method `on()` of the class `Kanboard\Core\Plugin\Hook`:
+
+```php
+$this->hook->on('hook_name', $callable);
+```
+
+The first argument is the name of the hook and the second is a PHP callable.
+
+### Hooks executed only one time
+
+Some hooks can have only one listener:
+
+#### model:subtask-time-tracking:calculate:time-spent
+
+- Override time spent calculation when subtask timer is stopped
+- Arguments:
+ - `$user_id` (integer)
+ - `$start` (DateTime)
+ - `$end` (DateTime)
+
+#### model:subtask-time-tracking:calendar:events
+
+- Override subtask time tracking events to display the calendar
+- Arguments:
+ - `$user_id` (integer)
+ - `$events` (array)
+ - `$start` (string, ISO-8601 format)
+ - `$end` (string, ISO-8601 format)
+
+### Merge hooks
+
+"Merge hooks" act in the same way as the function `array_merge`. The hook callback must return an array. This array will be merged with the default one.
+
+Example to add events in the user calendar:
+
+```php
+class Plugin extends Base
+{
+ public function initialize()
+ {
+ $container = $this->container;
+
+ $this->hook->on('controller:calendar:user:events', function($user_id, $start, $end) use ($container) {
+ $model = new SubtaskForecast($container);
+ return $model->getCalendarEvents($user_id, $end); // Return new events
+ });
+ }
+}
+```
+
+List of merge hooks:
+
+#### controller:calendar:project:events
+
+- Add more events to the project calendar
+- Arguments:
+ - `$project_id` (integer)
+ - `$start` Calendar start date (string, ISO-8601 format)
+ - `$end` Calendar` end date (string, ISO-8601 format)
+
+#### controller:calendar:user:events
+
+- Add more events to the user calendar
+- Arguments:
+ - `$user_id` (integer)
+ - `$start` Calendar start date (string, ISO-8601 format)
+ - `$end` Calendar end date (string, ISO-8601 format)
+
+Asset Hooks
+-----------
+
+Asset hooks can be used to add easily a new stylesheet or a new javascript file in the layout. You can use this feature to create a theme and override all Kanboard default styles.
+
+Example to add a new stylesheet:
+
+```php
+<?php
+
+namespace Kanboard\Plugin\Css;
+
+use Kanboard\Core\Plugin\Base;
+
+class Plugin extends Base
+{
+ public function initialize()
+ {
+ $this->hook->on('template:layout:css', 'plugins/Css/skin.css');
+ }
+}
+```
+
+List of asset Hooks:
+
+- `template:layout:css`
+- `template:layout:js`
+
+Template Hooks
+--------------
+
+Template hooks allow to add new content in existing templates.
+
+Example to add new content in the dashboard sidebar:
+
+```php
+$this->template->hook->attach('template:dashboard:sidebar', 'myplugin:dashboard/sidebar');
+```
+
+This call is usually defined in the `initialize()` method.
+The first argument is name of the hook and the second argument is the template name.
+
+Template names prefixed with the plugin name and colon indicate the location of the template.
+
+Example with `myplugin:dashboard/sidebar`:
+
+- `myplugin` is the name of your plugin (lowercase)
+- `dashboard/sidebar` is the template name
+- On the filesystem, the plugin will be located here: `plugins\Myplugin\Template\dashboard\sidebar.php`
+- Templates are written in pure PHP (don't forget to escape data)
+
+Template name without prefix are core templates.
+
+List of template hooks:
+
+- `template:dashboard:sidebar`
+- `template:config:sidebar`
+- `template:config:integrations`
+- `template:export:sidebar`
+- `template:layout:head`
+- `template:layout:top`
+- `template:layout:bottom`
+- `template:project:dropdown`
+- `template:project-user:sidebar`
+- `template:task:sidebar:information`
+- `template:task:sidebar:actions`
+- `template:user:sidebar:information`
+- `template:user:sidebar:actions`
+
+Other template hooks can be added if necessary, just ask on the issue tracker.
diff --git a/doc/plugin-overrides.markdown b/doc/plugin-overrides.markdown
new file mode 100644
index 00000000..905808d5
--- /dev/null
+++ b/doc/plugin-overrides.markdown
@@ -0,0 +1,36 @@
+Plugin Overrides
+================
+
+Override HTTP Content Security Policy
+-------------------------------------
+
+If you would like to replace the default HTTP Content Security Policy header, you can use the method `setContentSecurityPolicy()`:
+
+```php
+<?php
+
+namespace Kanboard\Plugin\Csp;
+
+use Kanboard\Core\Plugin\Base;
+
+class Plugin extends Base
+{
+ public function initialize()
+ {
+ $this->setContentSecurityPolicy(array('script-src' => 'something'));
+ }
+}
+```
+
+Template Overrides
+------------------
+
+Any templates defined in the core can be overrided. By example, you can redefine the default layout or change email notifications.
+
+Example of template override:
+
+```php
+$this->template->setTemplateOverride('header', 'theme:layout/header');
+```
+
+The first argument is the original template name and the second argument the template to use as replacement.
diff --git a/doc/plugin-registration.markdown b/doc/plugin-registration.markdown
new file mode 100644
index 00000000..50bfaff2
--- /dev/null
+++ b/doc/plugin-registration.markdown
@@ -0,0 +1,200 @@
+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 optionals.
+
+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 Plugin\Base
+{
+ public function initialize()
+ {
+ $this->template->hook->attach('template:layout:head', 'theme:layout/head');
+ }
+}
+```
+
+This file should contains 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
+
+Your plugin registration class also inherit from `Kanboard\Core\Base`, that means you can access to 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 the rest of the application. You must load the translations yourself when the session is created:
+
+```php
+$this->on('session.bootstrap', function($container) {
+ Translator::load($container['config']->getCurrentLanguage(), __DIR__.'/Locale');
+});
+```
+
+The translations must be stored in `plugins/Myplugin/Locale/xx_XX/translations.php`.
+
+Dependency Injection Container
+------------------------------
+
+Kanboard use 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(
+ 'HourlyRate',
+ 'Budget',
+ )
+ );
+}
+```
+
+Now, if you use a class that extends from `Core\Base`, you can access directly to those class instance:
+
+```php
+$this->hourlyRate->remove(123);
+$this->budget->getDailyBudgetBreakdown(456);
+
+// It's the same thing as using the container:
+$this->container['hourlyRate']->getAll();
+```
+
+Keys of the containers are unique across the application. If you override an existing class you will change the default behavior.
+
+Event Listening
+----------------
+
+Kanboard use internal events and your plugin can listen and perform actions on these events.
+
+```php
+$this->on('session.bootstrap', function($container) {
+ // Do something
+});
+```
+
+- The first argument is the event name
+- The second argument is a PHP callable function (closure or class method)
+
+Extend Automatic Actions
+------------------------
+
+To define a new automatic action with a plugin, you just need to call the method `extendActions()` from the class `Kanboard\Model\Action`, here an example:
+
+```php
+<?php
+
+namespace Kanboard\Plugin\AutomaticAction;
+
+use Kanboard\Core\Plugin\Base;
+
+class Plugin extends Base
+{
+ public function initialize()
+ {
+ $this->action->extendActions(
+ '\Kanboard\Plugin\AutomaticAction\Action\SendSlackMessage', // Use absolute namespace
+ t('Send a message to Slack when the task color change')
+ );
+ }
+}
+```
+
+- The first argument of the method `extendActions()` is the action class with the complete namespace path. **The namespace path must starts with a backslash** otherwise Kanboard will not be able to load your class.
+- The second argument is the description of your automatic action.
+
+The automatic action class must inherits from the class `Kanboard\Action\Base` and implements all abstract methods:
+
+- `getCompatibleEvents()`
+- `getActionRequiredParameters()`
+- `getEventRequiredParameters()`
+- `doAction(array $data)`
+- `hasRequiredCondition(array $data)`
+
+For more details you should take a look to existing automatic actions or this [plugin example](https://github.com/kanboard/plugin-example-automatic-action).
+
+Extend ACL
+----------
+
+Kanboard use an access list for privilege separations. Your extension can add new rules:
+
+```php
+$this->acl->extend('project_manager_acl', array('mycontroller' => '*'));
+```
+
+- The first argument is the ACL name
+- The second argument are the new rules
+ + Syntax to include only some actions: `array('controller' => array('action1', 'action2'))`
+ + Syntax to include all actions of a controller: `array('controller' => '*')`
+ + Everything is lowercase
+
+List of ACL:
+
+- `public_acl`: Public access without authentication
+- `project_member_acl`: Project member access
+- `project_manager_acl`: Project manager access
+- `project_admin_acl`: Project Admins
+- `admin_acl`: Administrators
diff --git a/doc/plugin-schema-migrations.markdown b/doc/plugin-schema-migrations.markdown
new file mode 100644
index 00000000..d595605f
--- /dev/null
+++ b/doc/plugin-schema-migrations.markdown
@@ -0,0 +1,38 @@
+Plugin Schema Migrations
+========================
+
+Kanboard execute database migrations automatically for you. Migrations must be stored in a folder **Schema** and the filename must be the same as the database driver:
+
+```bash
+Schema
+├── Mysql.php
+├── Postgres.php
+└── Sqlite.php
+```
+
+Each file contains all migrations, here an example for Sqlite:
+
+```php
+<?php
+
+namespace Kanboard\Plugin\Something\Schema;
+
+const VERSION = 1;
+
+function version_1($pdo)
+{
+ $pdo->exec('CREATE TABLE IF NOT EXISTS something (
+ "id" INTEGER PRIMARY KEY,
+ "project_id" INTEGER NOT NULL,
+ "something" TEXT,
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
+ )');
+}
+```
+
+- The constant `VERSION` is the last version of your schema
+- Each function is a migration `version_1()`, `version_2()`, etc...
+- A `PDO` instance is passed as first argument
+- Everything is executed inside a transaction, if something doesn't work a rollback is performed and the error is displayed to the user
+
+Kanboard will compare the version defined in your schema and the version stored in the database. If the versions are different, Kanboard will execute one by one each migration until to reach the last version.
diff --git a/doc/plugins.markdown b/doc/plugins.markdown
index 0a0577f9..a0f389e0 100644
--- a/doc/plugins.markdown
+++ b/doc/plugins.markdown
@@ -1,430 +1,24 @@
Plugin Development
==================
-Note: The plugin API is considered alpha at the moment.
+Note: The plugin API is **considered alpha** at the moment.
Plugins are useful to extend the core functionalities of Kanboard, adding features, creating themes or changing the default behavior.
Plugin creators should specify explicitly the compatible versions of Kanboard. Internal code of Kanboard may change over the time and your plugin must be tested with new versions.
-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 optionals.
-
-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 Plugin\Base
-{
- public function initialize()
- {
- $this->template->hook->attach('template:layout:head', 'theme:layout/head');
- }
-}
-```
-
-This file should contains 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)
-
-Your plugin registration class also inherit from `Kanboard\Core\Base`, that means you can access to all classes and methods of Kanboard easily.
-
-This example will fetch the user #123:
-
-```php
-$this->user->getById(123);
-```
-
-Application Hooks
------------------
-
-Hooks can extend, replace, filter data or change the default behavior. Each hook is identified with a unique name, example: `controller:calendar:user:events`
-
-### Listen on hook events
-
-In your `initialize()` method you need to call the method `on()` of the class `Kanboard\Core\Plugin\Hook`:
-
-```php
-$this->hook->on('hook_name', $callable);
-```
-
-The first argument is the name of the hook and the second is a PHP callable.
-
-### Hooks executed only one time
-
-Some hooks can have only one listener:
-
-#### model:subtask-time-tracking:calculate:time-spent
-
-- Override time spent calculation when subtask timer is stopped
-- Arguments:
- - `$user_id` (integer)
- - `$start` (DateTime)
- - `$end` (DateTime)
-
-#### model:subtask-time-tracking:calendar:events
-
-- Override subtask time tracking events to display the calendar
-- Arguments:
- - `$user_id` (integer)
- - `$events` (array)
- - `$start` (string, ISO-8601 format)
- - `$end` (string, ISO-8601 format)
-
-### Merge hooks
-
-"Merge hooks" act in the same way as the function `array_merge`. The hook callback must return an array. This array will be merged with the default one.
-
-Example to add events in the user calendar:
-
-```php
-class Plugin extends Base
-{
- public function initialize()
- {
- $container = $this->container;
-
- $this->hook->on('controller:calendar:user:events', function($user_id, $start, $end) use ($container) {
- $model = new SubtaskForecast($container);
- return $model->getCalendarEvents($user_id, $end); // Return new events
- });
- }
-}
-```
-
-List of merge hooks:
-
-#### controller:calendar:project:events
-
-- Add more events to the project calendar
-- Arguments:
- - `$project_id` (integer)
- - `$start` Calendar start date (string, ISO-8601 format)
- - `$end` Calendar` end date (string, ISO-8601 format)
-
-#### controller:calendar:user:events
-
-- Add more events to the user calendar
-- Arguments:
- - `$user_id` (integer)
- - `$start` Calendar start date (string, ISO-8601 format)
- - `$end` Calendar end date (string, ISO-8601 format)
-
-Asset Hooks
------------
-
-Asset hooks can be used to add easily a new stylesheet or a new javascript file in the layout. You can use this feature to create a theme and override all Kanboard default styles.
-
-Example to add a new stylesheet:
-
-```php
-<?php
-
-namespace Kanboard\Plugin\Css;
-
-use Kanboard\Core\Plugin\Base;
-
-class Plugin extends Base
-{
- public function initialize()
- {
- $this->hook->on('template:layout:css', 'plugins/Css/skin.css');
- }
-}
-```
-
-List of asset Hooks:
-
-- `template:layout:css`
-- `template:layout:js`
-
-Template hooks
---------------
-
-Template hooks allow to add new content in existing templates.
-
-Example to add new content in the dashboard sidebar:
-
-```php
-$this->template->hook->attach('template:dashboard:sidebar', 'myplugin:dashboard/sidebar');
-```
-
-This call is usually defined in the `initialize()` method.
-The first argument is name of the hook and the second argument is the template name.
-
-Template names prefixed with the plugin name and colon indicate the location of the template.
-
-Example with `myplugin:dashboard/sidebar`:
-
-- `myplugin` is the name of your plugin (lowercase)
-- `dashboard/sidebar` is the template name
-- On the filesystem, the plugin will be located here: `plugins\Myplugin\Template\dashboard\sidebar.php`
-- Templates are written in pure PHP (don't forget to escape data)
-
-Template name without prefix are core templates.
-
-List of template hooks:
-
-- `template:dashboard:sidebar`
-- `template:config:sidebar`
-- `template:export:sidebar`
-- `template:layout:head`
-- `template:layout:top`
-- `template:layout:bottom`
-- `template:project:dropdown`
-- `template:project-user:sidebar`
-- `template:task:sidebar:information`
-- `template:task:sidebar:actions`
-- `template:user:sidebar:information`
-- `template:user:sidebar:actions`
-
-Other template hooks can be added if necessary, just ask on the issue tracker.
-
-Template overrides
-------------------
-
-Any templates defined in the core can be overrided. By example, you can redefine the default layout or change email notifications.
-
-Example of template override:
-
-```php
-$this->template->setTemplateOverride('header', 'theme:layout/header');
-```
-
-The first argument is the original template name and the second argument the template to use as replacement.
-
-Listen on events
-----------------
-
-Kanboard use internal events and your plugin can listen and perform actions on these events.
-
-```php
-$this->on('session.bootstrap', function($container) {
- // Do something
-});
-```
-
-- The first argument is the event name
-- The second argument is a PHP callable function (closure or class method)
-
-Extend Automatic Actions
-------------------------
-
-To define a new automatic action with a plugin, you just need to call the method `extendActions()` from the class `Model\Action`, here an example:
-
-```php
-<?php
-
-namespace Kanboard\Plugin\AutomaticAction;
-
-use Kanboard\Core\Plugin\Base;
-
-class Plugin extends Base
-{
- public function initialize()
- {
- $this->action->extendActions(
- '\Kanboard\Plugin\AutomaticAction\Action\SendSlackMessage', // Use absolute namespace
- t('Send a message to Slack when the task color change')
- );
- }
-}
-```
-
-- The first argument of the method `extendActions()` is the action class with the complete namespace path. **The namespace path must starts with a backslash** otherwise Kanboard will not be able to load your class.
-- The second argument is the description of your automatic action.
-
-The automatic action class must inherits from the class `Action\Base` and implements all abstract methods:
-
-- `getCompatibleEvents()`
-- `getActionRequiredParameters()`
-- `getEventRequiredParameters()`
-- `doAction(array $data)`
-- `hasRequiredCondition(array $data)`
-
-For more details you should take a look to existing automatic actions or this [plugin example](https://github.com/kanboard/plugin-example-automatic-action).
-
-Extend ACL
-----------
-
-Kanboard use an access list for privilege separations. Your extension can add new rules:
-
-```php
-$this->acl->extend('project_manager_acl', array('mycontroller' => '*'));
-```
-
-- The first argument is the ACL name
-- The second argument are the new rules
- + Syntax to include only some actions: `array('controller' => array('action1', 'action2'))`
- + Syntax to include all actions of a controller: `array('controller' => '*')`
- + Everything is lowercase
-
-List of ACL:
-
-- `public_acl`: Public access without authentication
-- `project_member_acl`: Project member access
-- `project_manager_acl`: Project manager access
-- `project_admin_acl`: Project Admins
-- `admin_acl`: Administrators
-
-Plugin Translations
--------------------
-
-Plugin can be translated in the same way the rest of the application. You must load the translations yourself when the session is created:
-
-```php
-$this->on('session.bootstrap', function($container) {
- Translator::load($container['config']->getCurrentLanguage(), __DIR__.'/Locale');
-});
-```
-
-The translations must be stored in `plugins/Myplugin/Locale/xx_XX/translations.php`.
-
-Override HTTP Content Security Policy
--------------------------------------
-
-If you would like to replace the default HTTP Content Security Policy header, you can use the method `setContentSecurityPolicy()`:
-
-```php
-<?php
-
-namespace Kanboard\Plugin\Csp;
-
-use Kanboard\Core\Plugin\Base;
-
-class Plugin extends Base
-{
- public function initialize()
- {
- $this->setContentSecurityPolicy(array('script-src' => 'something'));
- }
-}
-```
-
-Dependency Injection Container
-------------------------------
-
-Kanboard use 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(
- 'HourlyRate',
- 'Budget',
- )
- );
-}
-```
-
-Now, if you use a class that extends from `Core\Base`, you can access directly to those class instance:
-
-```php
-$this->hourlyRate->remove(123);
-$this->budget->getDailyBudgetBreakdown(456);
-
-// It's the same thing as using the container:
-$this->container['hourlyRate']->getAll();
-```
-
-Keys of the containers are unique across the application. If you override an existing class you will change the default behavior.
-
-Schema migrations
------------------
-
-Kanboard execute database migrations automatically for you. Migrations must be stored in a folder **Schema** and the filename must be the same as the database driver:
-
-```bash
-Schema
-├── Mysql.php
-├── Postgres.php
-└── Sqlite.php
-```
-
-Each file contains all migrations, here an example for Sqlite:
-
-```php
-<?php
-
-namespace Kanboard\Plugin\Something\Schema;
-
-const VERSION = 1;
-
-function version_1($pdo)
-{
- $pdo->exec('CREATE TABLE IF NOT EXISTS something (
- "id" INTEGER PRIMARY KEY,
- "project_id" INTEGER NOT NULL,
- "something" TEXT,
- FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
- )');
-}
-```
-
-- The constant `VERSION` is the last version of your schema
-- Each function is a migration `version_1()`, `version_2()`, etc...
-- A `PDO` instance is passed as first argument
-- Everything is executed inside a transaction, if something doesn't work a rollback is performed and the error is displayed to the user
-
-Kanboard will compare the version defined in your schema and the version stored in the database. If the versions are different, Kanboard will execute one by one each migration until to reach the last version.
+- [Plugin Registration](plugin-registration.markdown)
+- [Plugin Hooks](plugin-hooks.markdown)
+- [Plugin Overrides](plugin-overrides.markdown)
+- [Plugin Schema Migrations](plugin-schema-migrations.markdown)
Examples of plugins
-------------------
+- [Sendgrid](https://github.com/kanboard/plugin-sendgrid)
+- [Mailgun](https://github.com/kanboard/plugin-mailgun)
+- [Postmark](https://github.com/kanboard/plugin-postmark)
+- [Amazon S3](https://github.com/kanboard/plugin-s3)
- [Budget planning](https://github.com/kanboard/plugin-budget)
- [User timetable](https://github.com/kanboard/plugin-timetable)
- [Subtask Forecast](https://github.com/kanboard/plugin-subtask-forecast)
diff --git a/doc/postmark.markdown b/doc/postmark.markdown
deleted file mode 100644
index 7c33ee63..00000000
--- a/doc/postmark.markdown
+++ /dev/null
@@ -1,29 +0,0 @@
-Postmark
-========
-
-You can use the service [Postmark](https://postmarkapp.com/) to create tasks directly by email.
-
-This integration works with the inbound email service of Postmark.
-Kanboard use a webhook to handle incoming emails.
-
-The [incoming email workflow is described here](create-tasks-by-email.markdown).
-
-Postmark configuration
-----------------------
-
-Just follow the [official documentation about inbound email processing](http://developer.postmarkapp.com/developer-process-configure.html).
-Basically, you have to forward your own domain or subdomain to a specific Postmark email address.
-
-The Kanboard webhook url is displayed in **Settings > Integrations > Postmark**
-
-Kanboard configuration
-----------------------
-
-1. Be sure that your users have an email address in their profiles
-2. Assign a project identifier to the desired projects: **Project settings > Edit**
-3. Try to send an email to your project
-
-Troubleshootings
-----------------
-
-- Test the webhook url from the Postmark console, you should have a status code `200 OK`
diff --git a/doc/sendgrid.markdown b/doc/sendgrid.markdown
deleted file mode 100644
index 0f307dac..00000000
--- a/doc/sendgrid.markdown
+++ /dev/null
@@ -1,24 +0,0 @@
-Sendgrid
-========
-
-You can use the service [Sendgrid](https://sendgrid.com/) to create tasks directly by email.
-
-This integration works with the [Parse API of Sendgrid](https://sendgrid.com/docs/API_Reference/Webhooks/parse.html).
-Kanboard use a webhook to handle incoming emails.
-
-The [incoming email workflow is described here](create-tasks-by-email.markdown).
-
-Sendgrid configuration
-----------------------
-
-1. Create a new domain or subdomain (by example **inbound.mydomain.tld**) with a MX record that point to **mx.sendgrid.net**
-2. Add your domain and the Kanboard webhook url to [the configuration page in Sendgrid](https://sendgrid.com/developer/reply)
-
-The Kanboard webhook url is displayed in **Settings > Integrations > Sendgrid**
-
-Kanboard configuration
-----------------------
-
-1. Be sure that your users have an email address in their profiles
-2. Assign a project identifier to the desired projects: **Project settings > Edit**
-3. Try to send an email to your project