summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorGerardo Zamudio <gerardozamudio@users.noreply.github.com>2016-02-24 23:48:50 -0600
committerGerardo Zamudio <gerardozamudio@users.noreply.github.com>2016-02-24 23:48:50 -0600
commite4de6b3898b64b26d29aff31f21df5fda8055686 (patch)
tree575f8a65440f291d70a070d168eafca8c82a6459 /doc
parentd9ffbea174ea6524d0a22f8375ca8b3aa04a3c96 (diff)
parenta6540bc604c837d92c9368540c145606723e97f7 (diff)
Merge pull request #1 from fguillot/master
Update from upstream
Diffstat (limited to 'doc')
-rw-r--r--doc/2fa.markdown12
-rw-r--r--doc/analytics-tasks.markdown6
-rw-r--r--doc/analytics.markdown31
-rw-r--r--doc/api-action-procedures.markdown245
-rw-r--r--doc/api-application-procedures.markdown291
-rw-r--r--doc/api-authentication.markdown66
-rw-r--r--doc/api-board-procedures.markdown158
-rw-r--r--doc/api-category-procedures.markdown172
-rw-r--r--doc/api-column-procedures.markdown229
-rw-r--r--doc/api-comment-procedures.markdown182
-rw-r--r--doc/api-examples.markdown152
-rw-r--r--doc/api-file-procedures.markdown217
-rw-r--r--doc/api-group-member-procedures.markdown152
-rw-r--r--doc/api-group-procedures.markdown174
-rw-r--r--doc/api-json-rpc.markdown4530
-rw-r--r--doc/api-link-procedures.markdown470
-rw-r--r--doc/api-me-procedures.markdown385
-rw-r--r--doc/api-project-permission-procedures.markdown274
-rw-r--r--doc/api-project-procedures.markdown411
-rw-r--r--doc/api-subtask-procedures.markdown194
-rw-r--r--doc/api-swimlane-procedures.markdown438
-rw-r--r--doc/api-task-procedures.markdown636
-rw-r--r--doc/api-user-procedures.markdown357
-rw-r--r--doc/application-configuration.markdown12
-rw-r--r--doc/automatic-actions.markdown100
-rw-r--r--doc/bitbucket-webhooks.markdown55
-rw-r--r--doc/board-collapsed-expanded.markdown2
-rw-r--r--doc/board-configuration.markdown6
-rw-r--r--doc/board-horizontal-scrolling-and-compact-view.markdown4
-rw-r--r--doc/board-show-hide-columns.markdown2
-rw-r--r--doc/bruteforce-protection.markdown10
-rw-r--r--doc/calendar-configuration.markdown12
-rw-r--r--doc/calendar.markdown8
-rw-r--r--doc/centos-installation.markdown4
-rw-r--r--doc/cli.markdown29
-rw-r--r--doc/closing-tasks.markdown10
-rw-r--r--doc/coding-standards.markdown2
-rw-r--r--doc/config.markdown107
-rw-r--r--doc/contributing.markdown15
-rw-r--r--doc/create-tasks-by-email.markdown23
-rw-r--r--doc/creating-projects.markdown6
-rw-r--r--doc/creating-tasks.markdown10
-rw-r--r--doc/cronjob.markdown32
-rw-r--r--doc/currency-rate.markdown2
-rw-r--r--doc/custom-filters.markdown8
-rw-r--r--doc/debian-installation.markdown2
-rw-r--r--doc/docker.markdown45
-rw-r--r--doc/duplicate-move-tasks.markdown4
-rw-r--r--doc/editing-projects.markdown6
-rw-r--r--doc/email-configuration.markdown10
-rw-r--r--doc/faq.markdown52
-rw-r--r--doc/fr/analytics-tasks.markdown4
-rw-r--r--doc/fr/duplicate-move-tasks.markdown2
-rw-r--r--doc/fr/notifications.markdown8
-rw-r--r--doc/fr/recurring-tasks.markdown2
-rw-r--r--doc/fr/time-tracking.markdown2
-rw-r--r--doc/fr/transitions.markdown2
-rw-r--r--doc/freebsd-installation.markdown7
-rw-r--r--doc/gantt-chart-projects.markdown6
-rw-r--r--doc/gantt-chart-tasks.markdown4
-rw-r--r--doc/github-authentication.markdown80
-rw-r--r--doc/github-webhooks.markdown99
-rw-r--r--doc/gitlab-authentication.markdown83
-rw-r--r--doc/gitlab-webhooks.markdown65
-rw-r--r--doc/google-authentication.markdown64
-rw-r--r--doc/groups.markdown17
-rw-r--r--doc/heroku.markdown5
-rw-r--r--doc/ical.markdown22
-rw-r--r--doc/index.markdown23
-rw-r--r--doc/installation.markdown15
-rw-r--r--doc/kanban-vs-todo-and-scrum.markdown4
-rw-r--r--doc/keyboard-shortcuts.markdown3
-rw-r--r--doc/ldap-authentication.markdown144
-rw-r--r--doc/ldap-group-sync.markdown48
-rw-r--r--doc/ldap-parameters.markdown33
-rw-r--r--doc/link-labels.markdown2
-rw-r--r--doc/mysql-configuration.markdown4
-rw-r--r--doc/nginx-ssl-php-fpm.markdown238
-rw-r--r--doc/nice-urls.markdown47
-rw-r--r--doc/notifications.markdown6
-rw-r--r--doc/plugin-authentication-architecture.markdown99
-rw-r--r--doc/plugin-authentication.markdown40
-rw-r--r--doc/plugin-authorization-architecture.markdown39
-rw-r--r--doc/plugin-automatic-actions.markdown60
-rw-r--r--doc/plugin-events.markdown27
-rw-r--r--doc/plugin-external-link.markdown78
-rw-r--r--doc/plugin-group-provider.markdown55
-rw-r--r--doc/plugin-hooks.markdown82
-rw-r--r--doc/plugin-ldap-client.markdown99
-rw-r--r--doc/plugin-mail-transports.markdown6
-rw-r--r--doc/plugin-metadata.markdown9
-rw-r--r--doc/plugin-notifications.markdown4
-rw-r--r--doc/plugin-overrides.markdown2
-rw-r--r--doc/plugin-registration.markdown94
-rw-r--r--doc/plugin-routes.markdown85
-rw-r--r--doc/plugin-schema-migrations.markdown5
-rw-r--r--doc/plugins.markdown15
-rw-r--r--doc/postgresql-configuration.markdown8
-rw-r--r--doc/project-configuration.markdown4
-rw-r--r--doc/project-permissions.markdown54
-rw-r--r--doc/project-types.markdown14
-rw-r--r--doc/project-views.markdown22
-rw-r--r--doc/recommended-configuration.markdown36
-rw-r--r--doc/recurring-tasks.markdown4
-rw-r--r--doc/requirements.markdown103
-rw-r--r--doc/reverse-proxy-authentication.markdown12
-rw-r--r--doc/roles.markdown25
-rw-r--r--doc/rss.markdown4
-rw-r--r--doc/screenshots.markdown6
-rw-r--r--doc/screenshots/groups-management.pngbin0 -> 17922 bytes
-rw-r--r--doc/screenshots/mention-autocomplete.pngbin0 -> 3066 bytes
-rw-r--r--doc/screenshots/project-permissions.pngbin0 -> 64960 bytes
-rw-r--r--doc/search.markdown44
-rw-r--r--doc/sharing-projects.markdown2
-rw-r--r--doc/subtasks.markdown4
-rw-r--r--doc/suse-installation.markdown14
-rw-r--r--doc/swimlanes.markdown6
-rw-r--r--doc/task-links.markdown2
-rw-r--r--doc/tests.markdown130
-rw-r--r--doc/time-tracking.markdown4
-rw-r--r--doc/transitions.markdown4
-rw-r--r--doc/translations.markdown23
-rw-r--r--doc/ubuntu-installation.markdown10
-rw-r--r--doc/update.markdown35
-rw-r--r--doc/usage-examples.markdown2
-rw-r--r--doc/user-management.markdown54
-rw-r--r--doc/user-mentions.markdown17
-rw-r--r--doc/user-types.markdown14
-rw-r--r--doc/vagrant.markdown4
-rw-r--r--doc/webhooks.markdown26
-rw-r--r--doc/what-is-kanban.markdown10
-rw-r--r--doc/windows-apache-installation.markdown14
-rw-r--r--doc/windows-iis-installation.markdown13
133 files changed, 6830 insertions, 6092 deletions
diff --git a/doc/2fa.markdown b/doc/2fa.markdown
index 627c636a..1085f621 100644
--- a/doc/2fa.markdown
+++ b/doc/2fa.markdown
@@ -1,21 +1,21 @@
-Two factor authentication
+Two-Factor Authentication
=========================
-Each user can enable the [two factor authentication](http://en.wikipedia.org/wiki/Two_factor_authentication).
-After a successful login, a one-time code (6 characters) is asked to the user to allow the access to Kanboard.
+Each user can enable the [two-factor authentication](http://en.wikipedia.org/wiki/Two_factor_authentication).
+After a successful login, a one-time code (6 characters) is asked to the user to allow access to Kanboard.
-This code have to be provided by a compatible software generally installed on your smartphone.
+This code has to be provided by a compatible software generally installed on your smartphone.
Kanboard use the [Time-based One-time Password Algorithm](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) defined in the [RFC 6238](http://tools.ietf.org/html/rfc6238).
There are many software compatible with the standard TOTP system.
-By example, you can use these free and open source applications:
+For example, you can use these free and open source applications:
- [Google Authenticator](https://github.com/google/google-authenticator/) (Android, iOS, Blackberry)
- [FreeOTP](https://fedorahosted.org/freeotp/) (Android, iOS)
- [OATH Toolkit](http://www.nongnu.org/oath-toolkit/) (Command line utility on Unix/Linux)
-This system can work offline and you don't necessary need to have a mobile phone.
+This system can work offline and you don't necessarily need to have a mobile phone.
Setup
-----
diff --git a/doc/analytics-tasks.markdown b/doc/analytics-tasks.markdown
index c5acea5f..4a49c154 100644
--- a/doc/analytics-tasks.markdown
+++ b/doc/analytics-tasks.markdown
@@ -1,7 +1,7 @@
Analytics for tasks
===================
-Each task have an analytics section available from the left menu in the task view.
+Each task has an analytics section available from the left menu in the task view.
Lead and cycle time
-------------------
@@ -13,12 +13,12 @@ Lead and cycle time
- If the task is not closed the current time is used instead of the date of completion.
- If the start date is not specified, the cycle time is not calculated.
-Note: You can configure an automatic action to define automatically the start date when you move a task to the column of your choice.
+Note: You can configure an automatic action to define the start date automatically when you move a task to the column of your choice.
Time spent into each column
---------------------------
![Time spent into each column](http://kanboard.net/screenshots/documentation/time-into-each-column.png)
-- This chart show the total time spent into each column for the task.
+- This chart shows the total time spent into each column for the task.
- The time spent is calculated until the task is closed.
diff --git a/doc/analytics.markdown b/doc/analytics.markdown
index 4ae9572f..d72fc383 100644
--- a/doc/analytics.markdown
+++ b/doc/analytics.markdown
@@ -1,7 +1,7 @@
Analytics
=========
-Each project have an analytics section. Depending how you are using Kanboard, you can see those reports:
+Each project have an analytics section. Depending on how you are using Kanboard, you can see those reports:
User repartition
----------------
@@ -22,21 +22,21 @@ Cumulative flow diagram
![Cumulative flow diagram](http://kanboard.net/screenshots/documentation/cfd.png)
-- This chart show the number of tasks cumulatively for each column over the time.
-- Everyday, the total number of tasks is recorded for each column.
+- This chart shows the number of tasks cumulatively for each column over the time.
+- Every day, the total number of tasks is recorded for each column.
- If you would like to exclude closed tasks, change the [global project settings](project-configuration.markdown).
-Note: You need to have at least 2 days of data to see the graph.
+Note: You need to have at least two days of data to see the graph.
-Burndown chart
---------------
+Burn down chart
+---------------
![Burndown chart](http://kanboard.net/screenshots/documentation/burndown-chart.png)
The [burn down chart](http://en.wikipedia.org/wiki/Burn_down_chart) is available for each project.
- This chart is a graphical representation of work left to do versus time.
-- Kanboard use the complexity or story point to generate this diagram.
+- Kanboard use the complexity or story point to generate this diagram.
- Everyday, the sum of the story points for each column is calculated.
Average time spent into each column
@@ -44,9 +44,9 @@ Average time spent into each column
![Average time spent into each column](http://kanboard.net/screenshots/documentation/average-time-spent-into-each-column.png)
-This chart show the average time spent into each column for the last 1000 tasks.
+This chart shows the average time spent into each column for the last 1000 tasks.
-- Kanboard use the task transitions to calculate the data.
+- Kanboard uses the task transitions to calculate the data.
- The time spent is calculated until the task is closed.
Average Lead and Cycle time
@@ -54,17 +54,12 @@ Average Lead and Cycle time
![Average time spent into each column](http://kanboard.net/screenshots/documentation/average-lead-cycle-time.png)
-This chart show the average lead and cycle time for the last 1000 tasks over the time.
+This chart show the average lead and cycle time for the last 1000 tasks over time.
- The lead time is the time between the task creation and the date of completion.
-- The cycle time is time between the specified start date of the task to completion date.
+- The cycle time is time between the specified start date of the task to the completion date.
- If the task is not closed, the current time is used instead of the date of completion.
-Those metrics are calculated and recorded everyday for the whole project.
+Those metrics are calculated and recorded every day for the whole project.
-Don't forget to run the daily job for stats calculation
--------------------------------------------------------
-
-To generate accurate analytics data, you should run the daily cronjob **project daily statistics**.
-
-[Read the documentation about Kanboard CLI](cli.markdown)
+Note: Don't forget to run the [daily cronjob](cronjob.markdown) to have accurate statistics.
diff --git a/doc/api-action-procedures.markdown b/doc/api-action-procedures.markdown
new file mode 100644
index 00000000..377ca56a
--- /dev/null
+++ b/doc/api-action-procedures.markdown
@@ -0,0 +1,245 @@
+API Automatic Actions Procedures
+================================
+
+## getAvailableActions
+
+- Purpose: **Get list of available automatic actions**
+- Parameters: none
+- Result on success: **list of actions**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAvailableActions",
+ "id": 1217735483
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1217735483,
+ "result": {
+ "\Kanboard\Action\TaskLogMoveAnotherColumn": "Add a comment logging moving the task between columns",
+ "\Kanboard\Action\TaskAssignColorUser": "Assign a color to a specific user",
+ "\Kanboard\Action\TaskAssignColorColumn": "Assign a color when the task is moved to a specific column",
+ "\Kanboard\Action\TaskAssignCategoryColor": "Assign automatically a category based on a color",
+ "\Kanboard\Action\TaskAssignColorCategory": "Assign automatically a color based on a category",
+ "\Kanboard\Action\TaskAssignSpecificUser": "Assign the task to a specific user",
+ "\Kanboard\Action\TaskAssignCurrentUser": "Assign the task to the person who does the action",
+ "\Kanboard\Action\TaskUpdateStartDate": "Automatically update the start date",
+ "\Kanboard\Action\TaskAssignUser": "Change the assignee based on an external username",
+ "\Kanboard\Action\TaskAssignCategoryLabel": "Change the category based on an external label",
+ "\Kanboard\Action\TaskClose": "Close a task",
+ "\Kanboard\Action\CommentCreation": "Create a comment from an external provider",
+ "\Kanboard\Action\TaskCreation": "Create a task from an external provider",
+ "\Kanboard\Action\TaskDuplicateAnotherProject": "Duplicate the task to another project",
+ "\Kanboard\Action\TaskMoveColumnAssigned": "Move the task to another column when assigned to a user",
+ "\Kanboard\Action\TaskMoveColumnUnAssigned": "Move the task to another column when assignee is cleared",
+ "\Kanboard\Action\TaskMoveAnotherProject": "Move the task to another project",
+ "\Kanboard\Action\TaskOpen": "Open a task"
+ }
+}
+```
+
+## getAvailableActionEvents
+
+- Purpose: **Get list of available events for actions**
+- Parameters: none
+- Result on success: **list of events**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAvailableActionEvents",
+ "id": 2116665643
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2116665643,
+ "result": {
+ "bitbucket.webhook.commit": "Bitbucket commit received",
+ "task.close": "Closing a task",
+ "github.webhook.commit": "Github commit received",
+ "github.webhook.issue.assignee": "Github issue assignee change",
+ "github.webhook.issue.closed": "Github issue closed",
+ "github.webhook.issue.commented": "Github issue comment created",
+ "github.webhook.issue.label": "Github issue label change",
+ "github.webhook.issue.opened": "Github issue opened",
+ "github.webhook.issue.reopened": "Github issue reopened",
+ "gitlab.webhook.commit": "Gitlab commit received",
+ "gitlab.webhook.issue.closed": "Gitlab issue closed",
+ "gitlab.webhook.issue.opened": "Gitlab issue opened",
+ "task.move.column": "Move a task to another column",
+ "task.open": "Open a closed task",
+ "task.assignee_change": "Task assignee change",
+ "task.create": "Task creation",
+ "task.create_update": "Task creation or modification",
+ "task.update": "Task modification"
+ }
+}
+```
+
+## getCompatibleActionEvents
+
+- Purpose: **Get list of events compatible with an action**
+- Parameters:
+ - **action_name** (string, required)
+- Result on success: **list of events**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getCompatibleActionEvents",
+ "id": 899370297,
+ "params": [
+ "\Kanboard\Action\TaskClose"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 899370297,
+ "result": {
+ "bitbucket.webhook.commit": "Bitbucket commit received",
+ "github.webhook.commit": "Github commit received",
+ "github.webhook.issue.closed": "Github issue closed",
+ "gitlab.webhook.commit": "Gitlab commit received",
+ "gitlab.webhook.issue.closed": "Gitlab issue closed",
+ "task.move.column": "Move a task to another column"
+ }
+}
+```
+
+## getActions
+
+- Purpose: **Get list of actions for a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **list of actions properties**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getActions",
+ "id": 1433237746,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1433237746,
+ "result": [
+ {
+ "id" : "13",
+ "project_id" : "2",
+ "event_name" : "task.move.column",
+ "action_name" : "\Kanboard\Action\TaskAssignSpecificUser",
+ "params" : {
+ "column_id" : "5",
+ "user_id" : "1"
+ }
+ }
+ ]
+}
+```
+
+## createAction
+
+- Purpose: **Create an action**
+- Parameters:
+ - **project_id** (integer, required)
+ - **event_name** (string, required)
+ - **action_name** (string, required)
+ - **params** (key/value parameters, required)
+- Result on success: **action_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createAction",
+ "id": 1433237746,
+ "params": {
+ "project_id" : "2",
+ "event_name" : "task.move.column",
+ "action_name" : "\Kanboard\Action\TaskAssignSpecificUser",
+ "params" : {
+ "column_id" : "3",
+ "user_id" : "2"
+ }
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1433237746,
+ "result": 14
+}
+```
+
+## removeAction
+
+- Purpose: **Remove an action**
+- Parameters:
+ - **action_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeAction",
+ "id": 1510741671,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1510741671,
+ "result": true
+}
+```
diff --git a/doc/api-application-procedures.markdown b/doc/api-application-procedures.markdown
new file mode 100644
index 00000000..08474559
--- /dev/null
+++ b/doc/api-application-procedures.markdown
@@ -0,0 +1,291 @@
+API Application Procedures
+==========================
+
+## getVersion
+
+- Purpose: **Get the application version**
+- Parameters: none
+- Result: **version** (Example: 1.0.12, master)
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getVersion",
+ "id": 1661138292
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1661138292,
+ "result": "1.0.13"
+}
+```
+
+## getTimezone
+
+- Purpose: **Get the application timezone**
+- Parameters: none
+- Result on success: **Timezone** (Example: UTC, Europe/Paris)
+- Result on failure: **Default timezone** (UTC)
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getTimezone",
+ "id": 1661138292
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1661138292,
+ "result": "Europe\/Paris"
+}
+```
+
+## getDefaultTaskColors
+
+- Purpose: **Get all default task colors**
+- Parameters: None
+- Result on success: **Color properties**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getDefaultTaskColors",
+ "id": 2108929212
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2108929212,
+ "result": {
+ "yellow": {
+ "name": "Yellow",
+ "background": "rgb(245, 247, 196)",
+ "border": "rgb(223, 227, 45)"
+ },
+ "blue": {
+ "name": "Blue",
+ "background": "rgb(219, 235, 255)",
+ "border": "rgb(168, 207, 255)"
+ },
+ "green": {
+ "name": "Green",
+ "background": "rgb(189, 244, 203)",
+ "border": "rgb(74, 227, 113)"
+ },
+ "purple": {
+ "name": "Purple",
+ "background": "rgb(223, 176, 255)",
+ "border": "rgb(205, 133, 254)"
+ },
+ "red": {
+ "name": "Red",
+ "background": "rgb(255, 187, 187)",
+ "border": "rgb(255, 151, 151)"
+ },
+ "orange": {
+ "name": "Orange",
+ "background": "rgb(255, 215, 179)",
+ "border": "rgb(255, 172, 98)"
+ },
+ "grey": {
+ "name": "Grey",
+ "background": "rgb(238, 238, 238)",
+ "border": "rgb(204, 204, 204)"
+ },
+ "brown": {
+ "name": "Brown",
+ "background": "#d7ccc8",
+ "border": "#4e342e"
+ },
+ "deep_orange": {
+ "name": "Deep Orange",
+ "background": "#ffab91",
+ "border": "#e64a19"
+ },
+ "dark_grey": {
+ "name": "Dark Grey",
+ "background": "#cfd8dc",
+ "border": "#455a64"
+ },
+ "pink": {
+ "name": "Pink",
+ "background": "#f48fb1",
+ "border": "#d81b60"
+ },
+ "teal": {
+ "name": "Teal",
+ "background": "#80cbc4",
+ "border": "#00695c"
+ },
+ "cyan": {
+ "name": "Cyan",
+ "background": "#b2ebf2",
+ "border": "#00bcd4"
+ },
+ "lime": {
+ "name": "Lime",
+ "background": "#e6ee9c",
+ "border": "#afb42b"
+ },
+ "light_green": {
+ "name": "Light Green",
+ "background": "#dcedc8",
+ "border": "#689f38"
+ },
+ "amber": {
+ "name": "Amber",
+ "background": "#ffe082",
+ "border": "#ffa000"
+ }
+ }
+}
+```
+
+## getDefaultTaskColor
+
+- Purpose: **Get default task color**
+- Parameters: None
+- Result on success: **color_id**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getDefaultTaskColor",
+ "id": 1144775215
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1144775215,
+ "result": "yellow"
+}
+```
+
+## getColorList
+
+- Purpose: **Get the list of task colors**
+- Parameters: none
+- Result on success: **Dictionary of color_id => color_name**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getColorList",
+ "id": 1677051386
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1677051386,
+ "result": {
+ "yellow": "Yellow",
+ "blue": "Blue",
+ "green": "Green",
+ "purple": "Purple",
+ "red": "Red",
+ "orange": "Orange",
+ "grey": "Grey",
+ "brown": "Brown",
+ "deep_orange": "Deep Orange",
+ "dark_grey": "Dark Grey",
+ "pink": "Pink",
+ "teal": "Teal",
+ "cyan": "Cyan",
+ "lime": "Lime",
+ "light_green": "Light Green",
+ "amber": "Amber"
+ }
+}
+```
+
+## getApplicationRoles
+
+- Purpose: **Get the application roles**
+- Parameters: none
+- Result: **Dictionary of role => role_name**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getApplicationRoles",
+ "id": 317154243
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 317154243,
+ "result": {
+ "app-admin": "Administrator",
+ "app-manager": "Manager",
+ "app-user": "User"
+ }
+}
+```
+
+## getProjectRoles
+
+- Purpose: **Get the project roles**
+- Parameters: none
+- Result: **Dictionary of role => role_name**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectRoles",
+ "id": 8981960
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 8981960,
+ "result": {
+ "project-manager": "Project Manager",
+ "project-member": "Project Member",
+ "project-viewer": "Project Viewer"
+ }
+}
+``` \ No newline at end of file
diff --git a/doc/api-authentication.markdown b/doc/api-authentication.markdown
new file mode 100644
index 00000000..962e5b1b
--- /dev/null
+++ b/doc/api-authentication.markdown
@@ -0,0 +1,66 @@
+API Authentication
+==================
+
+Default method (HTTP Basic)
+---------------------------
+
+The API credentials are available on the settings page.
+
+- API end-point: `https://YOUR_SERVER/jsonrpc.php`
+
+If you want to use the "application api":
+
+- Username: `jsonrpc`
+- Password: API token on the settings page
+
+Otherwise for the "user api", just use the real username/passsword.
+
+The API use the [HTTP Basic Authentication Scheme described in the RFC2617](http://www.ietf.org/rfc/rfc2617.txt).
+If there is an authentication error, you will receive the HTTP status code `401 Not Authorized`.
+
+### Authorized User API procedures
+
+- getMe
+- getMyDashboard
+- getMyActivityStream
+- createMyPrivateProject
+- getMyProjectsList
+- getMyProjects
+- getTimezone
+- getVersion
+- getDefaultTaskColor
+- getDefaultTaskColors
+- getColorList
+- getProjectById
+- getTask
+- getTaskByReference
+- getAllTasks
+- openTask
+- closeTask
+- moveTaskPosition
+- createTask
+- updateTask
+- getBoard
+- getProjectActivity
+- getMyOverdueTasks
+
+Custom HTTP header
+------------------
+
+You can use an alternative HTTP header for the authentication if your server have a very specific configuration.
+
+- The header name can be anything you want, by example `X-API-Auth`.
+- The header value is the `username:password` encoded in Base64.
+
+Configuration:
+
+1. Define your custom header in your `config.php`: `define('API_AUTHENTICATION_HEADER', 'X-API-Auth');`
+2. Encode the credentials in Base64, example with PHP `base64_encode('jsonrpc:19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929');`
+3. Test with curl:
+
+```bash
+curl \
+-H 'X-API-Auth: anNvbnJwYzoxOWZmZDk3MDlkMDNjZTUwNjc1YzNhNDNkMWM0OWMxYWMyMDdmNGJjNDVmMDZjNWIyNzAxZmJkZjg5Mjk=' \
+-d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \
+http://localhost/kanboard/jsonrpc.php
+```
diff --git a/doc/api-board-procedures.markdown b/doc/api-board-procedures.markdown
new file mode 100644
index 00000000..6f8a878e
--- /dev/null
+++ b/doc/api-board-procedures.markdown
@@ -0,0 +1,158 @@
+API Board Procedures
+====================
+
+## getBoard
+
+- Purpose: **Get all necessary information to display a board**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **board properties**
+- Result on failure: **empty list**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getBoard",
+ "id": 827046470,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 827046470,
+ "result": [
+ {
+ "id": 0,
+ "name": "Default swimlane",
+ "columns": [
+ {
+ "id": "1",
+ "title": "Backlog",
+ "position": "1",
+ "project_id": "1",
+ "task_limit": "0",
+ "description": "",
+ "tasks": [],
+ "nb_tasks": 0,
+ "score": 0
+ },
+ {
+ "id": "2",
+ "title": "Ready",
+ "position": "2",
+ "project_id": "1",
+ "task_limit": "0",
+ "description": "",
+ "tasks": [
+ {
+ "nb_comments":"0",
+ "nb_files":"0",
+ "nb_subtasks":"0",
+ "nb_completed_subtasks":"0",
+ "nb_links":"0",
+ "id":"2",
+ "reference":"",
+ "title":"Test",
+ "description":"",
+ "date_creation":"1430870507",
+ "date_modification":"1430870507",
+ "date_completed":null,
+ "date_due":"0",
+ "color_id":"yellow",
+ "project_id":"1",
+ "column_id":"2",
+ "swimlane_id":"0",
+ "owner_id":"0",
+ "creator_id":"1",
+ "position":"1",
+ "is_active":"1",
+ "score":"0",
+ "category_id":"0",
+ "date_moved":"1430870507",
+ "recurrence_status":"0",
+ "recurrence_trigger":"0",
+ "recurrence_factor":"0",
+ "recurrence_timeframe":"0",
+ "recurrence_basedate":"0",
+ "recurrence_parent":null,
+ "recurrence_child":null,
+ "assignee_username":null,
+ "assignee_name":null
+ }
+ ],
+ "nb_tasks": 1,
+ "score": 0
+ },
+ {
+ "id": "3",
+ "title": "Work in progress",
+ "position": "3",
+ "project_id": "1",
+ "task_limit": "0",
+ "description": "",
+ "tasks": [
+ {
+ "nb_comments":"0",
+ "nb_files":"0",
+ "nb_subtasks":"1",
+ "nb_completed_subtasks":"0",
+ "nb_links":"0",
+ "id":"1",
+ "reference":"",
+ "title":"Task with comment",
+ "description":"",
+ "date_creation":"1430783188",
+ "date_modification":"1430783188",
+ "date_completed":null,
+ "date_due":"0",
+ "color_id":"red",
+ "project_id":"1",
+ "column_id":"3",
+ "swimlane_id":"0",
+ "owner_id":"1",
+ "creator_id":"0",
+ "position":"1",
+ "is_active":"1",
+ "score":"0",
+ "category_id":"0",
+ "date_moved":"1430783191",
+ "recurrence_status":"0",
+ "recurrence_trigger":"0",
+ "recurrence_factor":"0",
+ "recurrence_timeframe":"0",
+ "recurrence_basedate":"0",
+ "recurrence_parent":null,
+ "recurrence_child":null,
+ "assignee_username":"admin",
+ "assignee_name":null
+ }
+ ],
+ "nb_tasks": 1,
+ "score": 0
+ },
+ {
+ "id": "4",
+ "title": "Done",
+ "position": "4",
+ "project_id": "1",
+ "task_limit": "0",
+ "description": "",
+ "tasks": [],
+ "nb_tasks": 0,
+ "score": 0
+ }
+ ],
+ "nb_columns": 4,
+ "nb_tasks": 2
+ }
+ ]
+}
+```
diff --git a/doc/api-category-procedures.markdown b/doc/api-category-procedures.markdown
new file mode 100644
index 00000000..644c09c6
--- /dev/null
+++ b/doc/api-category-procedures.markdown
@@ -0,0 +1,172 @@
+API Category Procedures
+=======================
+
+## createCategory
+
+- Purpose: **Create a new category**
+- Parameters:
+- **project_id** (integer, required)
+ - **name** (string, required, must be unique for the given project)
+- Result on success: **category_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createCategory",
+ "id": 541909890,
+ "params": {
+ "name": "Super category",
+ "project_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 541909890,
+ "result": 4
+}
+```
+
+## getCategory
+
+- Purpose: **Get category information**
+- Parameters:
+ - **category_id** (integer, required)
+- Result on success: **category properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getCategory",
+ "id": 203539163,
+ "params": {
+ "category_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+
+ "jsonrpc": "2.0",
+ "id": 203539163,
+ "result": {
+ "id": "1",
+ "name": "Super category",
+ "project_id": "1"
+ }
+}
+```
+
+## getAllCategories
+
+- Purpose: **Get all available categories**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **List of categories**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllCategories",
+ "id": 1261777968,
+ "params": {
+ "project_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1261777968,
+ "result": [
+ {
+ "id": "1",
+ "name": "Super category",
+ "project_id": "1"
+ }
+ ]
+}
+```
+
+## updateCategory
+
+- Purpose: **Update a category**
+- Parameters:
+ - **id** (integer, required)
+ - **name** (string, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateCategory",
+ "id": 570195391,
+ "params": {
+ "id": 1,
+ "name": "Renamed category"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 570195391,
+ "result": true
+}
+```
+
+## removeCategory
+
+- Purpose: **Remove a category**
+- Parameters:
+ - **category_id** (integer)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeCategory",
+ "id": 88225706,
+ "params": {
+ "category_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 88225706,
+ "result": true
+}
+```
diff --git a/doc/api-column-procedures.markdown b/doc/api-column-procedures.markdown
new file mode 100644
index 00000000..c5d2793b
--- /dev/null
+++ b/doc/api-column-procedures.markdown
@@ -0,0 +1,229 @@
+API Column Procedures
+=====================
+
+## getColumns
+
+- Purpose: **Get all columns information for a given project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **columns properties**
+- Result on failure: **empty list**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getColumns",
+ "id": 887036325,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 887036325,
+ "result": [
+ {
+ "id": "1",
+ "title": "Backlog",
+ "position": "1",
+ "project_id": "1",
+ "task_limit": "0"
+ },
+ {
+ "id": "2",
+ "title": "Ready",
+ "position": "2",
+ "project_id": "1",
+ "task_limit": "0"
+ },
+ {
+ "id": "3",
+ "title": "Work in progress",
+ "position": "3",
+ "project_id": "1",
+ "task_limit": "0"
+ }
+ ]
+}
+```
+
+## getColumn
+
+- Purpose: **Get a single column**
+- Parameters:
+ - **column_id** (integer, required)
+- Result on success: **column properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getColumn",
+ "id": 1242049935,
+ "params": [
+ 2
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1242049935,
+ "result": {
+ "id": "2",
+ "title": "Youpi",
+ "position": "2",
+ "project_id": "1",
+ "task_limit": "5"
+ }
+}
+```
+
+## changeColumnPosition
+
+- Purpose: **Change the column position**
+- Parameters:
+ - **project_id** (integer, required)
+ - **column_id** (integer, required)
+ - **position** (integer, required, must be >= 1)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "changeColumnPosition",
+ "id": 99275573,
+ "params": [
+ 1,
+ 2,
+ 3
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 99275573,
+ "result": true
+}
+```
+
+## updateColumn
+
+- Purpose: **Update column properties**
+- Parameters:
+ - **column_id** (integer, required)
+ - **title** (string, required)
+ - **task_limit** (integer, optional)
+ - **description** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateColumn",
+ "id": 480740641,
+ "params": [
+ 2,
+ "Boo",
+ 5
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 480740641,
+ "result": true
+}
+```
+
+## addColumn
+
+- Purpose: **Add a new column**
+- Parameters:
+ - **project_id** (integer, required)
+ - **title** (string, required)
+ - **task_limit** (integer, optional)
+ - **description** (string, optional)
+- Result on success: **column_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "addColumn",
+ "id": 638544704,
+ "params": [
+ 1,
+ "Boo"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 638544704,
+ "result": 5
+}
+```
+
+## removeColumn
+
+- Purpose: **Remove a column**
+- Parameters:
+ - **column_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeColumn",
+ "id": 1433237746,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1433237746,
+ "result": true
+}
+```
diff --git a/doc/api-comment-procedures.markdown b/doc/api-comment-procedures.markdown
new file mode 100644
index 00000000..5ac25b13
--- /dev/null
+++ b/doc/api-comment-procedures.markdown
@@ -0,0 +1,182 @@
+API Comment Procedures
+======================
+
+## createComment
+
+- Purpose: **Create a new comment**
+- Parameters:
+ - **task_id** (integer, required)
+ - **user_id** (integer, required)
+ - **content** Markdown content (string, required)
+- Result on success: **comment_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createComment",
+ "id": 1580417921,
+ "params": {
+ "task_id": 1,
+ "user_id": 1,
+ "content": "Comment #1"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1580417921,
+ "result": 11
+}
+```
+
+## getComment
+
+- Purpose: **Get comment information**
+- Parameters:
+ - **comment_id** (integer, required)
+- Result on success: **comment properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getComment",
+ "id": 867839500,
+ "params": {
+ "comment_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 867839500,
+ "result": {
+ "id": "1",
+ "task_id": "1",
+ "user_id": "1",
+ "date_creation": "1410881970",
+ "comment": "Comment #1",
+ "username": "admin",
+ "name": null
+ }
+}
+```
+
+## getAllComments
+
+- Purpose: **Get all available comments**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **List of comments**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllComments",
+ "id": 148484683,
+ "params": {
+ "task_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 148484683,
+ "result": [
+ {
+ "id": "1",
+ "date_creation": "1410882272",
+ "task_id": "1",
+ "user_id": "1",
+ "comment": "Comment #1",
+ "username": "admin",
+ "name": null
+ },
+ ...
+ ]
+}
+```
+
+## updateComment
+
+- Purpose: **Update a comment**
+- Parameters:
+ - **id** (integer, required)
+ - **content** Markdown content (string, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateComment",
+ "id": 496470023,
+ "params": {
+ "id": 1,
+ "content": "Comment #1 updated"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1493368950,
+ "result": true
+}
+```
+
+## removeComment
+
+- Purpose: **Remove a comment**
+- Parameters:
+ - **comment_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeComment",
+ "id": 328836871,
+ "params": {
+ "comment_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 328836871,
+ "result": true
+}
+```
diff --git a/doc/api-examples.markdown b/doc/api-examples.markdown
new file mode 100644
index 00000000..14d5db98
--- /dev/null
+++ b/doc/api-examples.markdown
@@ -0,0 +1,152 @@
+API Examples
+============
+
+Example with cURL
+-----------------
+
+From the command line:
+
+```bash
+curl \
+-u "jsonrpc:19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" \
+-d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \
+http://localhost/kanboard/jsonrpc.php
+```
+
+Response from the server:
+
+```json
+{
+ "jsonrpc":"2.0",
+ "id":1,
+ "result":[
+ {
+ "id":"1",
+ "name":"API test",
+ "is_active":"1",
+ "token":"6bd0932fe7f4b5e6e4bc3c72800bfdef36a2c5de2f38f756dfb5bd632ebf",
+ "last_modified":"1403392631"
+ }
+ ]
+}
+```
+
+Example with Python
+-------------------
+
+You can use the [official Python client for Kanboard](https://github.com/kanboard/kanboard-api-python):
+
+```bash
+pip install kanboard
+```
+
+Here an example to create a project and a task:
+
+```python
+from kanboard import Kanboard
+
+kb = Kanboard("http://localhost/jsonrpc.php", "jsonrpc", "your_api_token")
+
+project_id = kb.create_project(name="My project")
+
+task_id = kb.create_task(project_id=project_id, title="My task title")
+```
+
+There are more examples on the [official website](https://github.com/kanboard/kanboard-api-python).
+
+Example with a PHP client
+-------------------------
+
+You can use this [Json-RPC Client/Server library for PHP](https://github.com/fguillot/JsonRPC), here an example:
+
+```php
+<?php
+
+$client = new JsonRPC\Client('http://localhost:8000/jsonrpc.php');
+$client->authentication('jsonrpc', '19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929');
+
+print_r($client->getAllProjects());
+
+```
+
+The response:
+
+```
+Array
+(
+ [0] => Array
+ (
+ [id] => 1
+ [name] => API test
+ [is_active] => 1
+ [token] => 6bd0932fe7f4b5e6e4bc3c72800bfdef36a2c5de2f38f756dfb5bd632ebf
+ [last_modified] => 1403392631
+ )
+
+)
+```
+
+Example with Ruby
+-----------------
+
+This example can be used with Kanboard configured with Reverse-Proxy authentication and the API configured with a custom authentication header:
+
+```ruby
+require 'faraday'
+
+conn = Faraday.new(:url => 'https://kanboard.example.com') do |faraday|
+ faraday.response :logger
+ faraday.headers['X-API-Auth'] = 'XXX' # base64_encode('jsonrpc:API_KEY')
+ faraday.basic_auth(ENV['user'], ENV['pw']) # user/pass to get through basic auth
+ faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
+end
+
+response = conn.post do |req|
+ req.url '/jsonrpc.php'
+ req.headers['Content-Type'] = 'application/json'
+ req.body = '{ "jsonrpc": "2.0", "id": 1, "method": "getAllProjects" }'
+end
+
+puts response.body
+```
+
+
+Example with Java
+-----------------
+
+This is a basic example using Spring. For proper usage see [this link](http://spring.io/guides/gs/consuming-rest).
+
+```java
+import java.io.UnsupportedEncodingException;
+import java.util.Base64;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.client.RestTemplate;
+
+public class ProjectService {
+
+ public void getAllProjects() throws UnsupportedEncodingException {
+
+ RestTemplate restTemplate = new RestTemplate();
+
+ String url = "http://localhost/kanboard/jsonrpc.php";
+ String requestJson = "{\"jsonrpc\": \"2.0\", \"method\": \"getAllProjects\", \"id\": 1}";
+ String user = "jsonrpc";
+ String apiToken = "19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929";
+
+ // encode api token
+ byte[] xApiAuthTokenBytes = String.join(":", user, apiToken).getBytes("utf-8");
+ String xApiAuthToken = Base64.getEncoder().encodeToString(xApiAuthTokenBytes);
+
+ // consume request
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-API-Auth", xApiAuthToken);
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ HttpEntity<String> entity = new HttpEntity<String>(requestJson, headers);
+ String answer = restTemplate.postForObject(url, entity, String.class);
+ System.out.println(answer);
+ }
+}
+```
diff --git a/doc/api-file-procedures.markdown b/doc/api-file-procedures.markdown
new file mode 100644
index 00000000..930be733
--- /dev/null
+++ b/doc/api-file-procedures.markdown
@@ -0,0 +1,217 @@
+API File Procedures
+===================
+
+## createTaskFile
+
+- Purpose: **Create and upload a new task attachment**
+- Parameters:
+ - **project_id** (integer, required)
+ - **task_id** (integer, required)
+ - **filename** (integer, required)
+ - **blob** File content encoded in base64 (string, required)
+- Result on success: **file_id**
+- Result on failure: **false**
+- Note: **The maximum file size depends of your PHP configuration, this method should not be used to upload large files**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createTaskFile",
+ "id": 94500810,
+ "params": [
+ 1,
+ 1,
+ "My file",
+ "cGxhaW4gdGV4dCBmaWxl"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 94500810,
+ "result": 1
+}
+```
+
+## getAllTaskFiles
+
+- Purpose: **Get all files attached to task**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **list of files**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllTaskFiles",
+ "id": 1880662820,
+ "params": {
+ "task_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1880662820,
+ "result": [
+ {
+ "id": "1",
+ "name": "My file",
+ "path": "1\/1\/0db4d0a897a4c852f6e12f0239d4805f7b4ab596",
+ "is_image": "0",
+ "task_id": "1",
+ "date": "1432509941",
+ "user_id": "0",
+ "size": "15",
+ "username": null,
+ "user_name": null
+ }
+ ]
+}
+```
+
+## getTaskFile
+
+- Purpose: **Get file information**
+- Parameters:
+ - **file_id** (integer, required)
+- Result on success: **file properties**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getTaskFile",
+ "id": 318676852,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 318676852,
+ "result": {
+ "id": "1",
+ "name": "My file",
+ "path": "1\/1\/0db4d0a897a4c852f6e12f0239d4805f7b4ab596",
+ "is_image": "0",
+ "task_id": "1",
+ "date": "1432509941",
+ "user_id": "0",
+ "size": "15"
+ }
+}
+```
+
+## downloadTaskFile
+
+- Purpose: **Download file contents (encoded in base64)**
+- Parameters:
+ - **file_id** (integer, required)
+- Result on success: **base64 encoded string**
+- Result on failure: **empty string**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "downloadTaskFile",
+ "id": 235943344,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 235943344,
+ "result": "cGxhaW4gdGV4dCBmaWxl"
+}
+```
+
+## removeTaskFile
+
+- Purpose: **Remove file**
+- Parameters:
+ - **file_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeTaskFile",
+ "id": 447036524,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 447036524,
+ "result": true
+}
+```
+
+## removeAllTaskFiles
+
+- Purpose: **Remove all files associated to a task**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeAllTaskFiles",
+ "id": 593312993,
+ "params": {
+ "task_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 593312993,
+ "result": true
+}
+```
diff --git a/doc/api-group-member-procedures.markdown b/doc/api-group-member-procedures.markdown
new file mode 100644
index 00000000..c4889265
--- /dev/null
+++ b/doc/api-group-member-procedures.markdown
@@ -0,0 +1,152 @@
+Group Member API Procedures
+===========================
+
+## getGroupMembers
+
+- Purpose: **Get all members of a group**
+- Parameters:
+ - **group_id** (integer, required)
+- Result on success: **List of users**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getGroupMembers",
+ "id": 1987176726,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1987176726,
+ "result": [
+ {
+ "group_id": "1",
+ "user_id": "1",
+ "id": "1",
+ "username": "admin",
+ "is_ldap_user": "0",
+ "name": null,
+ "email": null,
+ "notifications_enabled": "0",
+ "timezone": null,
+ "language": null,
+ "disable_login_form": "0",
+ "notifications_filter": "4",
+ "nb_failed_login": "0",
+ "lock_expiration_date": "0",
+ "is_project_admin": "0",
+ "gitlab_id": null,
+ "role": "app-admin"
+ }
+ ]
+}
+```
+
+## addGroupMember
+
+- Purpose: **Add a user to a group**
+- Parameters:
+ - **group_id** (integer, required)
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "addGroupMember",
+ "id": 1589058273,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1589058273,
+ "result": true
+}
+```
+
+## removeGroupMember
+
+- Purpose: **Remove a user from a group**
+- Parameters:
+ - **group_id** (integer, required)
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeGroupMember",
+ "id": 1730416406,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1730416406,
+ "result": true
+}
+```
+
+## isGroupMember
+
+- Purpose: **Check if a user is member of a group**
+- Parameters:
+ - **group_id** (integer, required)
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "isGroupMember",
+ "id": 1052800865,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1052800865,
+ "result": false
+}
+```
diff --git a/doc/api-group-procedures.markdown b/doc/api-group-procedures.markdown
new file mode 100644
index 00000000..cb11fb96
--- /dev/null
+++ b/doc/api-group-procedures.markdown
@@ -0,0 +1,174 @@
+Group API Procedures
+====================
+
+## createGroup
+
+- Purpose: **Create a new group**
+- Parameters:
+ - **name** (string, required)
+ - **external_id** (string, optional)
+- Result on success: **link_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createGroup",
+ "id": 1416806551,
+ "params": [
+ "My Group B",
+ "1234"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1416806551,
+ "result": 2
+}
+```
+
+## updateGroup
+
+- Purpose: **Update a group**
+- Parameters:
+ - **group_id** (integer, required)
+ - **name** (string, optional)
+ - **external_id** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateGroup",
+ "id": 866078030,
+ "params": {
+ "group_id": "1",
+ "name": "ABC",
+ "external_id": "something"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 866078030,
+ "result": true
+}
+```
+
+## removeGroup
+
+- Purpose: **Remove a group**
+- Parameters:
+ - **group_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeGroup",
+ "id": 566000661,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 566000661,
+ "result": true
+}
+```
+
+## getGroup
+
+- Purpose: **Get one group**
+- Parameters:
+ - **group_id** (integer, required)
+- Result on success: **Group dictionary**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getGroup",
+ "id": 1968647622,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1968647622,
+ "result": {
+ "id": "1",
+ "external_id": "",
+ "name": "My Group A"
+ }
+}
+```
+
+## getAllGroups
+
+- Purpose: **Get all groups**
+- Parameters: none
+- Result on success: **list of groups**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllGroups",
+ "id": 546070742
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 546070742,
+ "result": [
+ {
+ "id": "1",
+ "external_id": "",
+ "name": "My Group A"
+ },
+ {
+ "id": "2",
+ "external_id": "1234",
+ "name": "My Group B"
+ }
+ ]
+}
+```
diff --git a/doc/api-json-rpc.markdown b/doc/api-json-rpc.markdown
index 359f8b05..bb14b008 100644
--- a/doc/api-json-rpc.markdown
+++ b/doc/api-json-rpc.markdown
@@ -43,4511 +43,25 @@ You must call the API with a `POST` HTTP request.
Kanboard support batch requests, so you can make multiple API calls in a single HTTP request. It's particularly useful for mobile clients with higher network latency.
-Authentication
---------------
-
-### Default method (HTTP Basic)
-
-The API credentials are available on the settings page.
-
-- API end-point: `https://YOUR_SERVER/jsonrpc.php`
-
-If you want to use the "application api":
-
-- Username: `jsonrpc`
-- Password: API token on the settings page
-
-Otherwise for the "user api", just use the real username/passsword.
-
-The API use the [HTTP Basic Authentication Scheme described in the RFC2617](http://www.ietf.org/rfc/rfc2617.txt).
-If there is an authentication error, you will receive the HTTP status code `401 Not Authorized`.
-
-### Authorized User API procedures
-
-- getMe
-- getMyDashboard
-- getMyActivityStream
-- createMyPrivateProject
-- getMyProjectsList
-- getMyProjects
-- getTimezone
-- getVersion
-- getDefaultTaskColor
-- getDefaultTaskColors
-- getColorList
-- getProjectById
-- getTask
-- getTaskByReference
-- getAllTasks
-- openTask
-- closeTask
-- moveTaskPosition
-- createTask
-- updateTask
-- getBoard
-- getProjectActivity
-- getMyOverdueTasks
-
-### Custom HTTP header
-
-You can use an alternative HTTP header for the authentication if your server have a very specific configuration.
-
-- The header name can be anything you want, by example `X-API-Auth`.
-- The header value is the `username:password` encoded in Base64.
-
-Configuration:
-
-1. Define your custom header in your `config.php`: `define('API_AUTHENTICATION_HEADER', 'X-API-Auth');`
-2. Encode the credentials in Base64, example with PHP `base64_encode('jsonrpc:19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929');`
-3. Test with curl:
-
-```bash
-curl \
--H 'X-API-Auth: anNvbnJwYzoxOWZmZDk3MDlkMDNjZTUwNjc1YzNhNDNkMWM0OWMxYWMyMDdmNGJjNDVmMDZjNWIyNzAxZmJkZjg5Mjk=' \
--d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \
-http://localhost/kanboard/jsonrpc.php
-```
-
-Examples
---------
-
-### Example with cURL
-
-From the command line:
-
-```bash
-curl \
--u "jsonrpc:19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" \
--d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \
-http://localhost/kanboard/jsonrpc.php
-```
-
-Response from the server:
-
-```json
-{
- "jsonrpc":"2.0",
- "id":1,
- "result":[
- {
- "id":"1",
- "name":"API test",
- "is_active":"1",
- "token":"6bd0932fe7f4b5e6e4bc3c72800bfdef36a2c5de2f38f756dfb5bd632ebf",
- "last_modified":"1403392631"
- }
- ]
-}
-```
-
-### Example with Python
-
-Here a basic example written in Python to create a task:
-
-```python
-#!/usr/bin/env python
-
-import requests
-import json
-
-def main():
- url = "http://demo.kanboard.net/jsonrpc.php"
- api_key = "be4271664ca8169d32af49d8e1ec854edb0290bc3588a2e356275eab9505"
- headers = {"content-type": "application/json"}
-
- payload = {
- "method": "createTask",
- "params": {
- "title": "Python API test",
- "project_id": 1
- },
- "jsonrpc": "2.0",
- "id": 1,
- }
-
- response = requests.post(
- url,
- data=json.dumps(payload),
- headers=headers,
- auth=("jsonrpc", api_key)
- )
-
- if response.status_code == 401:
- print "Authentication failed"
- else:
- result = response.json()
-
- assert result["result"] == True
- assert result["jsonrpc"]
- assert result["id"] == 1
-
- print "Task created successfully!"
-
-if __name__ == "__main__":
- main()
-```
-
-Run this script from your terminal:
-
-```bash
-python jsonrpc.py
-Task created successfully!
-```
-
-### Example with a PHP client:
-
-I wrote a simple [Json-RPC Client/Server library in PHP](https://github.com/fguillot/JsonRPC), here an example:
-
-```php
-<?php
-
-$client = new JsonRPC\Client('http://localhost:8000/jsonrpc.php');
-$client->authentication('jsonrpc', '19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929');
-
-print_r($client->getAllProjects());
-
-```
-
-The response:
-
-```
-Array
-(
- [0] => Array
- (
- [id] => 1
- [name] => API test
- [is_active] => 1
- [token] => 6bd0932fe7f4b5e6e4bc3c72800bfdef36a2c5de2f38f756dfb5bd632ebf
- [last_modified] => 1403392631
- )
-
-)
-```
-
-### Example with Ruby
-
-This example can be used with Kanboard configured with Reverse-Proxy authentication and the API configured with a custom authentication header:
-
-```ruby
-require 'faraday'
-
-conn = Faraday.new(:url => 'https://kanboard.example.com') do |faraday|
- faraday.response :logger
- faraday.headers['X-API-Auth'] = 'XXX' # base64_encode('jsonrpc:API_KEY')
- faraday.basic_auth(ENV['user'], ENV['pw']) # user/pass to get through basic auth
- faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
-end
-
-response = conn.post do |req|
- req.url '/jsonrpc.php'
- req.headers['Content-Type'] = 'application/json'
- req.body = '{ "jsonrpc": "2.0", "id": 1, "method": "getAllProjects" }'
-end
-
-puts response.body
-```
-
-
-### Example with Java
-
-This is a basic example using Spring. For proper usage see [this link](http://spring.io/guides/gs/consuming-rest).
-
-```java
-import java.io.UnsupportedEncodingException;
-import java.util.Base64;
-
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
-import org.springframework.web.client.RestTemplate;
-
-public class ProjectService {
-
- public void getAllProjects() throws UnsupportedEncodingException {
-
- RestTemplate restTemplate = new RestTemplate();
-
- String url = "http://localhost/kanboard/jsonrpc.php";
- String requestJson = "{\"jsonrpc\": \"2.0\", \"method\": \"getAllProjects\", \"id\": 1}";
- String user = "jsonrpc";
- String apiToken = "19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929";
-
- // encode api token
- byte[] xApiAuthTokenBytes = String.join(":", user, apiToken).getBytes("utf-8");
- String xApiAuthToken = Base64.getEncoder().encodeToString(xApiAuthTokenBytes);
-
- // consume request
- HttpHeaders headers = new HttpHeaders();
- headers.add("X-API-Auth", xApiAuthToken);
- headers.setContentType(MediaType.APPLICATION_JSON);
- HttpEntity<String> entity = new HttpEntity<String>(requestJson, headers);
- String answer = restTemplate.postForObject(url, entity, String.class);
- System.out.println(answer);
- }
-}
-```
-
-Procedures
-----------
-
-### getVersion
-
-- Purpose: **Get the application version**
-- Parameters: none
-- Result: **version** (Example: 1.0.12, master)
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getVersion",
- "id": 1661138292
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1661138292,
- "result": "1.0.13"
-}
-```
-
-### getTimezone
-
-- Purpose: **Get the application timezone**
-- Parameters: none
-- Result on success: **Timezone** (Example: UTC, Europe/Paris)
-- Result on failure: **Default timezone** (UTC)
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getTimezone",
- "id": 1661138292
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1661138292,
- "result": "Europe\/Paris"
-}
-```
-
-### getDefaultTaskColors
-
-- Purpose: **Get all default task colors**
-- Parameters: None
-- Result on success: **Color properties**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getDefaultTaskColors",
- "id": 2108929212
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2108929212,
- "result": {
- "yellow": {
- "name": "Yellow",
- "background": "rgb(245, 247, 196)",
- "border": "rgb(223, 227, 45)"
- },
- "blue": {
- "name": "Blue",
- "background": "rgb(219, 235, 255)",
- "border": "rgb(168, 207, 255)"
- },
- "green": {
- "name": "Green",
- "background": "rgb(189, 244, 203)",
- "border": "rgb(74, 227, 113)"
- },
- "purple": {
- "name": "Purple",
- "background": "rgb(223, 176, 255)",
- "border": "rgb(205, 133, 254)"
- },
- "red": {
- "name": "Red",
- "background": "rgb(255, 187, 187)",
- "border": "rgb(255, 151, 151)"
- },
- "orange": {
- "name": "Orange",
- "background": "rgb(255, 215, 179)",
- "border": "rgb(255, 172, 98)"
- },
- "grey": {
- "name": "Grey",
- "background": "rgb(238, 238, 238)",
- "border": "rgb(204, 204, 204)"
- },
- "brown": {
- "name": "Brown",
- "background": "#d7ccc8",
- "border": "#4e342e"
- },
- "deep_orange": {
- "name": "Deep Orange",
- "background": "#ffab91",
- "border": "#e64a19"
- },
- "dark_grey": {
- "name": "Dark Grey",
- "background": "#cfd8dc",
- "border": "#455a64"
- },
- "pink": {
- "name": "Pink",
- "background": "#f48fb1",
- "border": "#d81b60"
- },
- "teal": {
- "name": "Teal",
- "background": "#80cbc4",
- "border": "#00695c"
- },
- "cyan": {
- "name": "Cyan",
- "background": "#b2ebf2",
- "border": "#00bcd4"
- },
- "lime": {
- "name": "Lime",
- "background": "#e6ee9c",
- "border": "#afb42b"
- },
- "light_green": {
- "name": "Light Green",
- "background": "#dcedc8",
- "border": "#689f38"
- },
- "amber": {
- "name": "Amber",
- "background": "#ffe082",
- "border": "#ffa000"
- }
- }
-}
-```
-
-### getDefaultTaskColor
-
-- Purpose: **Get default task color**
-- Parameters: None
-- Result on success: **color_id**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getDefaultTaskColor",
- "id": 1144775215
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1144775215,
- "result": "yellow"
-}
-```
-
-### getColorList
-
-- Purpose: **Get the list of task colors**
-- Parameters: none
-- Result on success: **Dictionary of color_id => color_name**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getColorList",
- "id": 1677051386
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1677051386,
- "result": {
- "yellow": "Yellow",
- "blue": "Blue",
- "green": "Green",
- "purple": "Purple",
- "red": "Red",
- "orange": "Orange",
- "grey": "Grey",
- "brown": "Brown",
- "deep_orange": "Deep Orange",
- "dark_grey": "Dark Grey",
- "pink": "Pink",
- "teal": "Teal",
- "cyan": "Cyan",
- "lime": "Lime",
- "light_green": "Light Green",
- "amber": "Amber"
- }
-}
-```
-
-### createProject
-
-- Purpose: **Create a new project**
-- Parameters:
- - **name** (string, required)
- - **description** (string, optional)
-- Result on success: **project_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createProject",
- "id": 1797076613,
- "params": {
- "name": "PHP client"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1797076613,
- "result": 2
-}
-```
-
-### getProjectById
-
-- Purpose: **Get project information**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **project properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getProjectById",
- "id": 226760253,
- "params": {
- "project_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 226760253,
- "result": {
- "id": "1",
- "name": "API test",
- "is_active": "1",
- "token": "",
- "last_modified": "1436119135",
- "is_public": "0",
- "is_private": "0",
- "is_everybody_allowed": "0",
- "default_swimlane": "Default swimlane",
- "show_default_swimlane": "1",
- "description": "test",
- "identifier": "",
- "url": {
- "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1",
- "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1",
- "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1"
- }
- }
-}
-```
-
-### getProjectByName
-
-- Purpose: **Get project information**
-- Parameters:
- - **name** (string, required)
-- Result on success: **project properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getProjectByName",
- "id": 1620253806,
- "params": {
- "name": "Test"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1620253806,
- "result": {
- "id": "1",
- "name": "Test",
- "is_active": "1",
- "token": "",
- "last_modified": "1436119135",
- "is_public": "0",
- "is_private": "0",
- "is_everybody_allowed": "0",
- "default_swimlane": "Default swimlane",
- "show_default_swimlane": "1",
- "description": "test",
- "identifier": "",
- "url": {
- "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1",
- "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1",
- "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1"
- }
- }
-}
-```
-
-### getAllProjects
-
-- Purpose: **Get all available projects**
-- Parameters:
- - **none**
-- Result on success: **List of projects**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllProjects",
- "id": 2134420212
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2134420212,
- "result": [
- {
- "id": "1",
- "name": "API test",
- "is_active": "1",
- "token": "",
- "last_modified": "1436119570",
- "is_public": "0",
- "is_private": "0",
- "is_everybody_allowed": "0",
- "default_swimlane": "Default swimlane",
- "show_default_swimlane": "1",
- "description": null,
- "identifier": "",
- "url": {
- "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1",
- "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1",
- "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1"
- }
- }
- ]
-}
-```
-
-### updateProject
-
-- Purpose: **Update a project**
-- Parameters:
- - **id** (integer, required)
- - **name** (string, required)
- - **description** (string, optional)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateProject",
- "id": 1853996288,
- "params": {
- "id": 1,
- "name": "PHP client update"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1853996288,
- "result": true
-}
-```
-
-### removeProject
-
-- Purpose: **Remove a project**
-- Parameters:
- **project_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeProject",
- "id": 46285125,
- "params": {
- "project_id": "2"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 46285125,
- "result": true
-}
-```
-
-### enableProject
-
-- Purpose: **Enable a project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "enableProject",
- "id": 1775494839,
- "params": [
- "1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1775494839,
- "result": true
-}
-```
-
-### disableProject
-
-- Purpose: **Disable a project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "disableProject",
- "id": 1734202312,
- "params": [
- "1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1734202312,
- "result": true
-}
-```
-
-### enableProjectPublicAccess
-
-- Purpose: **Enable public access for a given project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "enableProjectPublicAccess",
- "id": 103792571,
- "params": [
- "1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 103792571,
- "result": true
-}
-```
-
-### disableProjectPublicAccess
-
-- Purpose: **Disable public access for a given project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "disableProjectPublicAccess",
- "id": 942472945,
- "params": [
- "1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 942472945,
- "result": true
-}
-```
-
-### getProjectActivity
-
-- Purpose: **Get activity stream for a project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **List of events**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getProjectActivity",
- "id": 942472945,
- "params": [
- "project_id": 1
- ]
-}
-```
-
-### getProjectActivities
-
-- Purpose: **Get Activityfeed for Project(s)**
-- Parameters:
- - **project_ids** (integer array, required)
-- Result on success: **List of events**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getProjectActivities",
- "id": 942472945,
- "params": [
- "project_ids": [1,2]
- ]
-}
-```
-
-### getMembers
-
-- Purpose: **Get members of a project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: Key/value pair of user_id and username
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getMembers",
- "id": 1944388643,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1944388643,
- "result": {
- "1": "user1",
- "2": "user2",
- "3": "user3"
- }
-}
-```
-
-### revokeUser
-
-- Purpose: **Revoke user access for a given project**
-- Parameters:
- - **project_id** (integer, required)
- - **user_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "revokeUser",
- "id": 251218350,
- "params": [
- 1,
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 251218350,
- "result": true
-}
-```
-
-### allowUser
-
-- Purpose: **Grant user access for a given project**
-- Parameters:
- - **project_id** (integer, required)
- - **user_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "allowUser",
- "id": 2111451404,
- "params": [
- 1,
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2111451404,
- "result": true
-}
-```
-
-
-### getBoard
-
-- Purpose: **Get all necessary information to display a board**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **board properties**
-- Result on failure: **empty list**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getBoard",
- "id": 827046470,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 827046470,
- "result": [
- {
- "id": 0,
- "name": "Default swimlane",
- "columns": [
- {
- "id": "1",
- "title": "Backlog",
- "position": "1",
- "project_id": "1",
- "task_limit": "0",
- "description": "",
- "tasks": [],
- "nb_tasks": 0,
- "score": 0
- },
- {
- "id": "2",
- "title": "Ready",
- "position": "2",
- "project_id": "1",
- "task_limit": "0",
- "description": "",
- "tasks": [
- {
- "nb_comments":"0",
- "nb_files":"0",
- "nb_subtasks":"0",
- "nb_completed_subtasks":"0",
- "nb_links":"0",
- "id":"2",
- "reference":"",
- "title":"Test",
- "description":"",
- "date_creation":"1430870507",
- "date_modification":"1430870507",
- "date_completed":null,
- "date_due":"0",
- "color_id":"yellow",
- "project_id":"1",
- "column_id":"2",
- "swimlane_id":"0",
- "owner_id":"0",
- "creator_id":"1",
- "position":"1",
- "is_active":"1",
- "score":"0",
- "category_id":"0",
- "date_moved":"1430870507",
- "recurrence_status":"0",
- "recurrence_trigger":"0",
- "recurrence_factor":"0",
- "recurrence_timeframe":"0",
- "recurrence_basedate":"0",
- "recurrence_parent":null,
- "recurrence_child":null,
- "assignee_username":null,
- "assignee_name":null
- }
- ],
- "nb_tasks": 1,
- "score": 0
- },
- {
- "id": "3",
- "title": "Work in progress",
- "position": "3",
- "project_id": "1",
- "task_limit": "0",
- "description": "",
- "tasks": [
- {
- "nb_comments":"0",
- "nb_files":"0",
- "nb_subtasks":"1",
- "nb_completed_subtasks":"0",
- "nb_links":"0",
- "id":"1",
- "reference":"",
- "title":"Task with comment",
- "description":"",
- "date_creation":"1430783188",
- "date_modification":"1430783188",
- "date_completed":null,
- "date_due":"0",
- "color_id":"red",
- "project_id":"1",
- "column_id":"3",
- "swimlane_id":"0",
- "owner_id":"1",
- "creator_id":"0",
- "position":"1",
- "is_active":"1",
- "score":"0",
- "category_id":"0",
- "date_moved":"1430783191",
- "recurrence_status":"0",
- "recurrence_trigger":"0",
- "recurrence_factor":"0",
- "recurrence_timeframe":"0",
- "recurrence_basedate":"0",
- "recurrence_parent":null,
- "recurrence_child":null,
- "assignee_username":"admin",
- "assignee_name":null
- }
- ],
- "nb_tasks": 1,
- "score": 0
- },
- {
- "id": "4",
- "title": "Done",
- "position": "4",
- "project_id": "1",
- "task_limit": "0",
- "description": "",
- "tasks": [],
- "nb_tasks": 0,
- "score": 0
- }
- ],
- "nb_columns": 4,
- "nb_tasks": 2
- }
- ]
-}
-```
-
-### getColumns
-
-- Purpose: **Get all columns information for a given project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **columns properties**
-- Result on failure: **empty list**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getColumns",
- "id": 887036325,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 887036325,
- "result": [
- {
- "id": "1",
- "title": "Backlog",
- "position": "1",
- "project_id": "1",
- "task_limit": "0"
- },
- {
- "id": "2",
- "title": "Ready",
- "position": "2",
- "project_id": "1",
- "task_limit": "0"
- },
- {
- "id": "3",
- "title": "Work in progress",
- "position": "3",
- "project_id": "1",
- "task_limit": "0"
- }
- ]
-}
-```
-
-### getColumn
-
-- Purpose: **Get a single column**
-- Parameters:
- - **column_id** (integer, required)
-- Result on success: **column properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getColumn",
- "id": 1242049935,
- "params": [
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1242049935,
- "result": {
- "id": "2",
- "title": "Youpi",
- "position": "2",
- "project_id": "1",
- "task_limit": "5"
- }
-}
-```
-
-### moveColumnUp
-
-- Purpose: **Move up the column position**
-- Parameters:
- - **project_id** (integer, required)
- - **column_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "moveColumnUp",
- "id": 99275573,
- "params": [
- 1,
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 99275573,
- "result": true
-}
-```
-
-### moveColumnDown
-
-- Purpose: **Move down the column position**
-- Parameters:
- - **project_id** (integer, required)
- - **column_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "moveColumnDown",
- "id": 957090649,
- "params": {
- "project_id": 1,
- "column_id": 2
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 957090649,
- "result": true
-}
-```
-
-### updateColumn
-
-- Purpose: **Update column properties**
-- Parameters:
- - **column_id** (integer, required)
- - **title** (string, required)
- - **task_limit** (integer, optional)
- - **description** (string, optional)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateColumn",
- "id": 480740641,
- "params": [
- 2,
- "Boo",
- 5
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 480740641,
- "result": true
-}
-```
-
-### addColumn
-
-- Purpose: **Add a new column**
-- Parameters:
- - **project_id** (integer, required)
- - **title** (string, required)
- - **task_limit** (integer, optional)
- - **description** (string, optional)
-- Result on success: **column_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "addColumn",
- "id": 638544704,
- "params": [
- 1,
- "Boo"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 638544704,
- "result": 5
-}
-```
-
-### removeColumn
-
-- Purpose: **Remove a column**
-- Parameters:
- - **column_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeColumn",
- "id": 1433237746,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1433237746,
- "result": true
-}
-```
-
-### getDefaultSwimlane
-
-- Purpose: **Get the default swimlane for a project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getDefaultSwimlane",
- "id": 898774713,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 898774713,
- "result": {
- "id": "1",
- "default_swimlane": "Default swimlane",
- "show_default_swimlane": "1"
- }
-}
-```
-
-### getActiveSwimlanes
-
-- Purpose: **Get the list of enabled swimlanes of a project (include default swimlane if enabled)**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **List of swimlanes**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getActiveSwimlanes",
- "id": 934789422,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 934789422,
- "result": [
- {
- "id": 0,
- "name": "Default swimlane"
- },
- {
- "id": "2",
- "name": "Swimlane A"
- }
- ]
-}
-```
-
-### getAllSwimlanes
-
-- Purpose: **Get the list of all swimlanes of a project (enabled or disabled) and sorted by position**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **List of swimlanes**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllSwimlanes",
- "id": 509791576,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 509791576,
- "result": [
- {
- "id": "1",
- "name": "Another swimlane",
- "position": "1",
- "is_active": "1",
- "project_id": "1"
- },
- {
- "id": "2",
- "name": "Swimlane A",
- "position": "2",
- "is_active": "1",
- "project_id": "1"
- }
- ]
-}
-```
-
-### getSwimlane
-
-- Purpose: **Get the a swimlane by id**
-- Parameters:
- - **swimlane_id** (integer, required)
-- Result on success: **swimlane properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getSwimlane",
- "id": 131071870,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 131071870,
- "result": {
- "id": "1",
- "name": "Swimlane 1",
- "position": "1",
- "is_active": "1",
- "project_id": "1"
- }
-}
-```
-
-### getSwimlaneById
-
-- Purpose: **Get the a swimlane by id**
-- Parameters:
- - **swimlane_id** (integer, required)
-- Result on success: **swimlane properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getSwimlaneById",
- "id": 131071870,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 131071870,
- "result": {
- "id": "1",
- "name": "Swimlane 1",
- "position": "1",
- "is_active": "1",
- "project_id": "1"
- }
-}
-```
-
-### getSwimlaneByName
-
-- Purpose: **Get the a swimlane by name**
-- Parameters:
- - **project_id** (integer, required)
- - **name** (string, required)
-- Result on success: **swimlane properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getSwimlaneByName",
- "id": 824623567,
- "params": [
- 1,
- "Swimlane 1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 824623567,
- "result": {
- "id": "1",
- "name": "Swimlane 1",
- "position": "1",
- "is_active": "1",
- "project_id": "1"
- }
-}
-```
-
-### moveSwimlaneUp
-
-- Purpose: **Move up the swimlane position**
-- Parameters:
- - **project_id** (integer, required)
- - **swimlane_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "moveSwimlaneUp",
- "id": 99275573,
- "params": [
- 1,
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 99275573,
- "result": true
-}
-```
-
-### moveSwimlaneDown
-
-- Purpose: **Move down the swimlane position**
-- Parameters:
- - **project_id** (integer, required)
- - **swimlane_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "moveSwimlaneDown",
- "id": 957090649,
- "params": {
- "project_id": 1,
- "swimlane_id": 2
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 957090649,
- "result": true
-}
-```
-
-### updateSwimlane
-
-- Purpose: **Update swimlane properties**
-- Parameters:
- - **swimlane_id** (integer, required)
- - **name** (string, required)
- - **description** (string, optional)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateSwimlane",
- "id": 87102426,
- "params": [
- "1",
- "Another swimlane"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 87102426,
- "result": true
-}
-```
-
-### addSwimlane
-
-- Purpose: **Add a new swimlane**
-- Parameters:
- - **project_id** (integer, required)
- - **name** (string, required)
- - **description** (string, optional)
-- Result on success: **swimlane_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "addSwimlane",
- "id": 849940086,
- "params": [
- 1,
- "Swimlane 1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 849940086,
- "result": 1
-}
-```
-
-### removeSwimlane
-
-- Purpose: **Remove a swimlane**
-- Parameters:
- - **project_id** (integer, required)
- - **swimlane_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeSwimlane",
- "id": 1433237746,
- "params": [
- 2,
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1433237746,
- "result": true
-}
-```
-
-### disableSwimlane
-
-- Purpose: **Enable a swimlane**
-- Parameters:
- - **project_id** (integer, required)
- - **swimlane_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "disableSwimlane",
- "id": 1433237746,
- "params": [
- 2,
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1433237746,
- "result": true
-}
-```
-
-### enableSwimlane
-
-- Purpose: **Enable a swimlane**
-- Parameters:
- - **project_id** (integer, required)
- - **swimlane_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "enableSwimlane",
- "id": 1433237746,
- "params": [
- 2,
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1433237746,
- "result": true
-}
-```
-
-### getAvailableActions
-
-- Purpose: **Get list of available automatic actions**
-- Parameters: none
-- Result on success: **list of actions**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAvailableActions",
- "id": 1217735483
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1217735483,
- "result": {
- "TaskLogMoveAnotherColumn": "Add a comment logging moving the task between columns",
- "TaskAssignColorUser": "Assign a color to a specific user",
- "TaskAssignColorColumn": "Assign a color when the task is moved to a specific column",
- "TaskAssignCategoryColor": "Assign automatically a category based on a color",
- "TaskAssignColorCategory": "Assign automatically a color based on a category",
- "TaskAssignSpecificUser": "Assign the task to a specific user",
- "TaskAssignCurrentUser": "Assign the task to the person who does the action",
- "TaskUpdateStartDate": "Automatically update the start date",
- "TaskAssignUser": "Change the assignee based on an external username",
- "TaskAssignCategoryLabel": "Change the category based on an external label",
- "TaskClose": "Close a task",
- "CommentCreation": "Create a comment from an external provider",
- "TaskCreation": "Create a task from an external provider",
- "TaskDuplicateAnotherProject": "Duplicate the task to another project",
- "TaskMoveColumnAssigned": "Move the task to another column when assigned to a user",
- "TaskMoveColumnUnAssigned": "Move the task to another column when assignee is cleared",
- "TaskMoveAnotherProject": "Move the task to another project",
- "TaskOpen": "Open a task"
- }
-}
-```
-
-### getAvailableActionEvents
-
-- Purpose: **Get list of available events for actions**
-- Parameters: none
-- Result on success: **list of events**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAvailableActionEvents",
- "id": 2116665643
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2116665643,
- "result": {
- "bitbucket.webhook.commit": "Bitbucket commit received",
- "task.close": "Closing a task",
- "github.webhook.commit": "Github commit received",
- "github.webhook.issue.assignee": "Github issue assignee change",
- "github.webhook.issue.closed": "Github issue closed",
- "github.webhook.issue.commented": "Github issue comment created",
- "github.webhook.issue.label": "Github issue label change",
- "github.webhook.issue.opened": "Github issue opened",
- "github.webhook.issue.reopened": "Github issue reopened",
- "gitlab.webhook.commit": "Gitlab commit received",
- "gitlab.webhook.issue.closed": "Gitlab issue closed",
- "gitlab.webhook.issue.opened": "Gitlab issue opened",
- "task.move.column": "Move a task to another column",
- "task.open": "Open a closed task",
- "task.assignee_change": "Task assignee change",
- "task.create": "Task creation",
- "task.create_update": "Task creation or modification",
- "task.update": "Task modification"
- }
-}
-```
-
-### getCompatibleActionEvents
-
-- Purpose: **Get list of events compatible with an action**
-- Parameters:
- - **action_name** (string, required)
-- Result on success: **list of events**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getCompatibleActionEvents",
- "id": 899370297,
- "params": [
- "TaskClose"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 899370297,
- "result": {
- "bitbucket.webhook.commit": "Bitbucket commit received",
- "github.webhook.commit": "Github commit received",
- "github.webhook.issue.closed": "Github issue closed",
- "gitlab.webhook.commit": "Gitlab commit received",
- "gitlab.webhook.issue.closed": "Gitlab issue closed",
- "task.move.column": "Move a task to another column"
- }
-}
-```
-
-### getActions
-
-- Purpose: **Get list of actions for a project**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **list of actions properties**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getActions",
- "id": 1433237746,
- "params": [
- "1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1433237746,
- "result": [
- {
- "id" : "13",
- "project_id" : "2",
- "event_name" : "task.move.column",
- "action_name" : "TaskAssignSpecificUser",
- "params" : {
- "column_id" : "5",
- "user_id" : "1"
- }
- }
- ]
-}
-```
-
-### createAction
-
-- Purpose: **Create an action**
-- Parameters:
- - **project_id** (integer, required)
- - **event_name** (string, required)
- - **action_name** (string, required)
- - **params** (key/value parameters, required)
-- Result on success: **action_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createAction",
- "id": 1433237746,
- "params": {
- "project_id" : "2",
- "event_name" : "task.move.column",
- "action_name" : "TaskAssignSpecificUser",
- "params" : {
- "column_id" : "3",
- "user_id" : "2"
- }
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1433237746,
- "result": 14
-}
-```
-
-### removeAction
-
-- Purpose: **Remove an action**
-- Parameters:
- - **action_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeAction",
- "id": 1510741671,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1510741671,
- "result": true
-}
-```
-
-### createTask
-
-- Purpose: **Create a new task**
-- Parameters:
- - **title** (string, required)
- - **project_id** (integer, required)
- - **color_id** (string, optional)
- - **column_id** (integer, optional)
- - **owner_id** (integer, optional)
- - **creator_id** (integer, optional)
- - **date_due**: ISO8601 format (string, optional)
- - **description** Markdown content (string, optional)
- - **category_id** (integer, optional)
- - **score** (integer, optional)
- - **swimlane_id** (integer, optional)
- - **recurrence_status** (integer, optional)
- - **recurrence_trigger** (integer, optional)
- - **recurrence_factor** (integer, optional)
- - **recurrence_timeframe** (integer, optional)
- - **recurrence_basedate** (integer, optional)
-- Result on success: **task_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createTask",
- "id": 1176509098,
- "params": {
- "owner_id": 1,
- "creator_id": 0,
- "date_due": "",
- "description": "",
- "category_id": 0,
- "score": 0,
- "title": "Test",
- "project_id": 1,
- "color_id": "green",
- "column_id": 2,
- "recurrence_status": 0,
- "recurrence_trigger": 0,
- "recurrence_factor": 0,
- "recurrence_timeframe": 0,
- "recurrence_basedate": 0
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1176509098,
- "result": 3
-}
-```
-
-### getTask
-
-- Purpose: **Get task by the unique id**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **task properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getTask",
- "id": 700738119,
- "params": {
- "task_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 700738119,
- "result": {
- "id": "1",
- "title": "Task #1",
- "description": "",
- "date_creation": "1409963206",
- "color_id": "blue",
- "project_id": "1",
- "column_id": "2",
- "owner_id": "1",
- "position": "1",
- "is_active": "1",
- "date_completed": null,
- "score": "0",
- "date_due": "0",
- "category_id": "0",
- "creator_id": "0",
- "date_modification": "1409963206",
- "reference": "",
- "date_started": null,
- "time_spent": "0",
- "time_estimated": "0",
- "swimlane_id": "0",
- "date_moved": "1430875287",
- "recurrence_status": "0",
- "recurrence_trigger": "0",
- "recurrence_factor": "0",
- "recurrence_timeframe": "0",
- "recurrence_basedate": "0",
- "recurrence_parent": null,
- "recurrence_child": null,
- "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=1",
- "color": {
- "name": "Yellow",
- "background": "rgb(245, 247, 196)",
- "border": "rgb(223, 227, 45)"
- }
- }
-}
-```
-
-### getTaskByReference
-
-- Purpose: **Get task by the external reference**
-- Parameters:
- - **project_id** (integer, required)
- - **reference** (string, required)
-- Result on success: **task properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getTaskByReference",
- "id": 1992081213,
- "params": {
- "project_id": 1,
- "reference": "TICKET-1234"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1992081213,
- "result": {
- "id": "5",
- "title": "Task with external ticket number",
- "description": "[Link to my ticket](http:\/\/my-ticketing-system\/1234)",
- "date_creation": "1434227446",
- "color_id": "yellow",
- "project_id": "1",
- "column_id": "1",
- "owner_id": "0",
- "position": "4",
- "is_active": "1",
- "date_completed": null,
- "score": "0",
- "date_due": "0",
- "category_id": "0",
- "creator_id": "0",
- "date_modification": "1434227446",
- "reference": "TICKET-1234",
- "date_started": null,
- "time_spent": "0",
- "time_estimated": "0",
- "swimlane_id": "0",
- "date_moved": "1434227446",
- "recurrence_status": "0",
- "recurrence_trigger": "0",
- "recurrence_factor": "0",
- "recurrence_timeframe": "0",
- "recurrence_basedate": "0",
- "recurrence_parent": null,
- "recurrence_child": null,
- "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=5&project_id=1"
- }
-}
-```
-
-### getAllTasks
-
-- Purpose: **Get all available tasks**
-- Parameters:
- - **project_id** (integer, required)
- - **status_id**: The value 1 for active tasks and 0 for inactive (integer, required)
-- Result on success: **List of tasks**
-- Result on failure: **false**
-
-Request example to fetch all tasks on the board:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllTasks",
- "id": 133280317,
- "params": {
- "project_id": 1,
- "status_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 133280317,
- "result": [
- {
- "id": "1",
- "title": "Task #1",
- "description": "",
- "date_creation": "1409961789",
- "color_id": "blue",
- "project_id": "1",
- "column_id": "2",
- "owner_id": "1",
- "position": "1",
- "is_active": "1",
- "date_completed": null,
- "score": "0",
- "date_due": "0",
- "category_id": "0",
- "creator_id": "0",
- "date_modification": "1409961789",
- "reference": "",
- "date_started": null,
- "time_spent": "0",
- "time_estimated": "0",
- "swimlane_id": "0",
- "date_moved": "1430783191",
- "recurrence_status": "0",
- "recurrence_trigger": "0",
- "recurrence_factor": "0",
- "recurrence_timeframe": "0",
- "recurrence_basedate": "0",
- "recurrence_parent": null,
- "recurrence_child": null,
- "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=1"
- },
- {
- "id": "2",
- "title": "Test",
- "description": "",
- "date_creation": "1409962115",
- "color_id": "green",
- "project_id": "1",
- "column_id": "2",
- "owner_id": "1",
- "position": "2",
- "is_active": "1",
- "date_completed": null,
- "score": "0",
- "date_due": "0",
- "category_id": "0",
- "creator_id": "0",
- "date_modification": "1409962115",
- "reference": "",
- "date_started": null,
- "time_spent": "0",
- "time_estimated": "0",
- "swimlane_id": "0",
- "date_moved": "1430783191",
- "recurrence_status": "0",
- "recurrence_trigger": "0",
- "recurrence_factor": "0",
- "recurrence_timeframe": "0",
- "recurrence_basedate": "0",
- "recurrence_parent": null,
- "recurrence_child": null,
- "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=2&project_id=1"
- },
- ...
- ]
-}
-```
-
-### getOverdueTasks
-
-- Purpose: **Get all overdue tasks**
-- Result on success: **List of tasks**
-- Result on failure: **false**
-
-Request example to fetch all tasks on the board:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getOverdueTasks",
- "id": 133280317
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 133280317,
- "result": [
- {
- "id": "1",
- "title": "Task #1",
- "date_due": "1409961789",
- "project_id": "1",
- "project_name": "Test",
- "assignee_username":"admin",
- "assignee_name": null
- },
- {
- "id": "2",
- "title": "Test",
- "date_due": "1409962115",
- "project_id": "1",
- "project_name": "Test",
- "assignee_username":"admin",
- "assignee_name": null
- },
- ...
- ]
-}
-```
-
-### getOverdueTasksByProject
-
-- Purpose: **Get all overdue tasks for a special project**
-- Result on success: **List of tasks**
-- Result on failure: **false**
-
-Request example to fetch all tasks on the board:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getOverdueTasksByProject",
- "id": 133280317,
- "params": {
- "project_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 133280317,
- "result": [
- {
- "id": "1",
- "title": "Task #1",
- "date_due": "1409961789",
- "project_id": "1",
- "project_name": "Test",
- "assignee_username":"admin",
- "assignee_name": null
- },
- {
- "id": "2",
- "title": "Test",
- "date_due": "1409962115",
- "project_id": "1",
- "project_name": "Test",
- "assignee_username":"admin",
- "assignee_name": null
- },
- ...
- ]
-}
-```
-
-### updateTask
-
-- Purpose: **Update a task**
-- Parameters:
- - **id** (integer, required)
- - **title** (string, optional)
- - **project_id** (integer, optional)
- - **color_id** (string, optional)
- - **owner_id** (integer, optional)
- - **creator_id** (integer, optional)
- - **date_due**: ISO8601 format (string, optional)
- - **description** Markdown content (string, optional)
- - **category_id** (integer, optional)
- - **score** (integer, optional)
- - **recurrence_status** (integer, optional)
- - **recurrence_trigger** (integer, optional)
- - **recurrence_factor** (integer, optional)
- - **recurrence_timeframe** (integer, optional)
- - **recurrence_basedate** (integer, optional)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example to change the task color:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateTask",
- "id": 1406803059,
- "params": {
- "id": 1,
- "color_id": "blue"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1406803059,
- "result": true
-}
-```
-
-### openTask
-
-- Purpose: **Set a task to the status open**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "openTask",
- "id": 1888531925,
- "params": {
- "task_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1888531925,
- "result": true
-}
-```
-
-### closeTask
-
-- Purpose: **Set a task to the status close**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "closeTask",
- "id": 1654396960,
- "params": {
- "task_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1654396960,
- "result": true
-}
-```
-
-### removeTask
-
-- Purpose: **Remove a task**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeTask",
- "id": 1423501287,
- "params": {
- "task_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1423501287,
- "result": true
-}
-```
-
-### moveTaskPosition
-
-- Purpose: **Move a task to another column or another position**
-- Parameters:
- - **project_id** (integer, required)
- - **task_id** (integer, required)
- - **column_id** (integer, required)
- - **position** (integer, required)
- - **swimlane_id** (integer, optional, default=0)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "moveTaskPosition",
- "id": 117211800,
- "params": {
- "project_id": 1,
- "task_id": 1,
- "column_id": 2,
- "position": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 117211800,
- "result": true
-}
-```
-
-### createUser
-
-- Purpose: **Create a new user**
-- Parameters:
- - **username** Must be unique (string, required)
- - **password** Must have at least 6 characters (string, required)
- - **name** (string, optional)
- - **email** (string, optional)
- - **is_admin** Set the value 1 for admins or 0 for regular users (integer, optional)
- - **is_project_admin** Set the value 1 for project admins or 0 for regular users (integer, optional)
-- Result on success: **user_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createUser",
- "id": 1518863034,
- "params": {
- "username": "biloute",
- "password": "123456"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1518863034,
- "result": 22
-}
-```
-
-### createLdapUser
-
-- Purpose: **Create a new user authentified by LDAP**
-- Parameters:
- - **username** (string, optional if email is set)
- - **email** (string, optional if username is set)
- - **is_admin** Set the value 1 for admins or 0 for regular users (integer, optional)
- - **is_project_admin** Set the value 1 for project admins or 0 for regular users (integer, optional)
-- Result on success: **user_id**
-- Result on failure: **false**
-
-The user will only be created if a matching is found on the LDAP server.
-Username or email (or both) must be provided.
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createLdapUser",
- "id": 1518863034,
- "params": {
- "username": "biloute",
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1518863034,
- "result": 22
-}
-```
-
-### getUser
-
-- Purpose: **Get user information**
-- Parameters:
- - **user_id** (integer, required)
-- Result on success: **user properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getUser",
- "id": 1769674781,
- "params": {
- "user_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1769674781,
- "result": {
- "id": "1",
- "username": "biloute",
- "password": "$2y$10$dRs6pPoBu935RpmsrhmbjevJH5MgZ7Kr9QrnVINwwyZ3.MOwqg.0m",
- "is_admin": "0",
- "is_ldap_user": "0",
- "name": "",
- "email": "",
- "google_id": null,
- "github_id": null,
- "notifications_enabled": "0"
- }
-}
-```
-
-### getAllUsers
-
-- Purpose: **Get all available users**
-- Parameters:
- - **none**
-- Result on success: **List of users**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllUsers",
- "id": 1438712131
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1438712131,
- "result": [
- {
- "id": "1",
- "username": "biloute",
- "name": "",
- "email": "",
- "is_admin": "0",
- "is_ldap_user": "0",
- "notifications_enabled": "0",
- "google_id": null,
- "github_id": null
- },
- ...
- ]
-}
-```
-
-### updateUser
-
-- Purpose: **Update a user**
-- Parameters:
- - **id** (integer)
- - **username** (string, optional)
- - **name** (string, optional)
- - **email** (string, optional)
- - **is_admin** (integer, optional)
- - **is_project_admin** Set the value 1 for project admins or 0 for regular users (integer, optional)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateUser",
- "id": 322123657,
- "params": {
- "id": 1,
- "is_admin": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 322123657,
- "result": true
-}
-```
-
-### removeUser
-
-- Purpose: **Remove a user**
-- Parameters:
- - **user_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeUser",
- "id": 2094191872,
- "params": {
- "user_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2094191872,
- "result": true
-}
-```
-
-
-### createCategory
-
-- Purpose: **Create a new category**
-- Parameters:
-- **project_id** (integer, required)
- - **name** (string, required, must be unique for the given project)
-- Result on success: **category_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createCategory",
- "id": 541909890,
- "params": {
- "name": "Super category",
- "project_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 541909890,
- "result": 4
-}
-```
-
-### getCategory
-
-- Purpose: **Get category information**
-- Parameters:
- - **category_id** (integer, required)
-- Result on success: **category properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getCategory",
- "id": 203539163,
- "params": {
- "category_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
-
- "jsonrpc": "2.0",
- "id": 203539163,
- "result": {
- "id": "1",
- "name": "Super category",
- "project_id": "1"
- }
-}
-```
-
-### getAllCategories
-
-- Purpose: **Get all available categories**
-- Parameters:
- - **project_id** (integer, required)
-- Result on success: **List of categories**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllCategories",
- "id": 1261777968,
- "params": {
- "project_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1261777968,
- "result": [
- {
- "id": "1",
- "name": "Super category",
- "project_id": "1"
- }
- ]
-}
-```
-
-### updateCategory
-
-- Purpose: **Update a category**
-- Parameters:
- - **id** (integer, required)
- - **name** (string, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateCategory",
- "id": 570195391,
- "params": {
- "id": 1,
- "name": "Renamed category"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 570195391,
- "result": true
-}
-```
-
-### removeCategory
-
-- Purpose: **Remove a category**
-- Parameters:
- - **category_id** (integer)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeCategory",
- "id": 88225706,
- "params": {
- "category_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 88225706,
- "result": true
-}
-```
-
-
-### createComment
-
-- Purpose: **Create a new comment**
-- Parameters:
- - **task_id** (integer, required)
- - **user_id** (integer, required)
- - **content** Markdown content (string, required)
-- Result on success: **comment_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createComment",
- "id": 1580417921,
- "params": {
- "task_id": 1,
- "user_id": 1,
- "content": "Comment #1"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1580417921,
- "result": 11
-}
-```
-
-### getComment
-
-- Purpose: **Get comment information**
-- Parameters:
- - **comment_id** (integer, required)
-- Result on success: **comment properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getComment",
- "id": 867839500,
- "params": {
- "comment_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 867839500,
- "result": {
- "id": "1",
- "task_id": "1",
- "user_id": "1",
- "date_creation": "1410881970",
- "comment": "Comment #1",
- "username": "admin",
- "name": null
- }
-}
-```
-
-### getAllComments
-
-- Purpose: **Get all available comments**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **List of comments**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllComments",
- "id": 148484683,
- "params": {
- "task_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 148484683,
- "result": [
- {
- "id": "1",
- "date_creation": "1410882272",
- "task_id": "1",
- "user_id": "1",
- "comment": "Comment #1",
- "username": "admin",
- "name": null
- },
- ...
- ]
-}
-```
-
-### updateComment
-
-- Purpose: **Update a comment**
-- Parameters:
- - **id** (integer, required)
- - **content** Markdown content (string, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateComment",
- "id": 496470023,
- "params": {
- "id": 1,
- "content": "Comment #1 updated"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1493368950,
- "result": true
-}
-```
-
-### removeComment
-
-- Purpose: **Remove a comment**
-- Parameters:
- - **comment_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeComment",
- "id": 328836871,
- "params": {
- "comment_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 328836871,
- "result": true
-}
-```
-
-### createSubtask
-
-- Purpose: **Create a new subtask**
-- Parameters:
- - **task_id** (integer, required)
- - **title** (integer, required)
- - **user_id** (int, optional)
- - **time_estimated** (int, optional)
- - **time_spent** (int, optional)
- - **status** (int, optional)
-- Result on success: **subtask_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createSubtask",
- "id": 2041554661,
- "params": {
- "task_id": 1,
- "title": "Subtask #1"
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2041554661,
- "result": 45
-}
-```
-
-### getSubtask
-
-- Purpose: **Get subtask information**
-- Parameters:
- - **subtask_id** (integer)
-- Result on success: **subtask properties**
-- Result on failure: **null**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getSubtask",
- "id": 133184525,
- "params": {
- "subtask_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 133184525,
- "result": {
- "id": "1",
- "title": "Subtask #1",
- "status": "0",
- "time_estimated": "0",
- "time_spent": "0",
- "task_id": "1",
- "user_id": "0"
- }
-}
-```
-
-### getAllSubtasks
-
-- Purpose: **Get all available subtasks**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **List of subtasks**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllSubtasks",
- "id": 2087700490,
- "params": {
- "task_id": 1
- }
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2087700490,
- "result": [
- {
- "id": "1",
- "title": "Subtask #1",
- "status": "0",
- "time_estimated": "0",
- "time_spent": "0",
- "task_id": "1",
- "user_id": "0",
- "username": null,
- "name": null,
- "status_name": "Todo"
- },
- ...
- ]
-}
-```
-
-### updateSubtask
-
-- Purpose: **Update a subtask**
-- Parameters:
- - **id** (integer, required)
- - **task_id** (integer, required)
- - **title** (integer, optional)
- - **user_id** (integer, optional)
- - **time_estimated** (integer, optional)
- - **time_spent** (integer, optional)
- - **status** (integer, optional)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateSubtask",
- "id": 191749979,
- "params": {
- "id": 1,
- "task_id": 1,
- "status": 1,
- "time_spent": 5,
- "user_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 191749979,
- "result": true
-}
-```
-
-### removeSubtask
-
-- Purpose: **Remove a subtask**
-- Parameters:
- - **subtask_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeSubtask",
- "id": 1382487306,
- "params": {
- "subtask_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1382487306,
- "result": true
-}
-```
-
-### getAllLinks
-
-- Purpose: **Get the list of possible relations between tasks**
-- Parameters: none
-- Result on success: **List of links**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllLinks",
- "id": 113057196
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 113057196,
- "result": [
- {
- "id": "1",
- "label": "relates to",
- "opposite_id": "0"
- },
- {
- "id": "2",
- "label": "blocks",
- "opposite_id": "3"
- },
- {
- "id": "3",
- "label": "is blocked by",
- "opposite_id": "2"
- },
- {
- "id": "4",
- "label": "duplicates",
- "opposite_id": "5"
- },
- {
- "id": "5",
- "label": "is duplicated by",
- "opposite_id": "4"
- },
- {
- "id": "6",
- "label": "is a child of",
- "opposite_id": "7"
- },
- {
- "id": "7",
- "label": "is a parent of",
- "opposite_id": "6"
- },
- {
- "id": "8",
- "label": "targets milestone",
- "opposite_id": "9"
- },
- {
- "id": "9",
- "label": "is a milestone of",
- "opposite_id": "8"
- },
- {
- "id": "10",
- "label": "fixes",
- "opposite_id": "11"
- },
- {
- "id": "11",
- "label": "is fixed by",
- "opposite_id": "10"
- }
- ]
-}
-```
-
-### getOppositeLinkId
-
-- Purpose: **Get the opposite link id of a task link**
-- Parameters:
- - **link_id** (integer, required)
-- Result on success: **link_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getOppositeLinkId",
- "id": 407062448,
- "params": [
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 407062448,
- "result": "3"
-}
-```
-
-### getLinkByLabel
-
-- Purpose: **Get a link by label**
-- Parameters:
- - **label** (integer, required)
-- Result on success: **link properties**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getLinkByLabel",
- "id": 1796123316,
- "params": [
- "blocks"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1796123316,
- "result": {
- "id": "2",
- "label": "blocks",
- "opposite_id": "3"
- }
-}
-```
-
-### getLinkById
-
-- Purpose: **Get a link by id**
-- Parameters:
- - **link_id** (integer, required)
-- Result on success: **link properties**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getLinkById",
- "id": 1190238402,
- "params": [
- 4
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1190238402,
- "result": {
- "id": "4",
- "label": "duplicates",
- "opposite_id": "5"
- }
-}
-```
-
-### createLink
-
-- Purpose: **Create a new task relation**
-- Parameters:
- - **label** (integer, required)
- - **opposite_label** (integer, optional)
-- Result on success: **link_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createLink",
- "id": 1040237496,
- "params": [
- "foo",
- "bar"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1040237496,
- "result": 13
-}
-```
-
-### updateLink
-
-- Purpose: **Update a link**
-- Parameters:
- - **link_id** (integer, required)
- - **opposite_link_id** (integer, required)
- - **label** (string, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateLink",
- "id": 2110446926,
- "params": [
- "14",
- "12",
- "boo"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2110446926,
- "result": true
-}
-```
-
-### removeLink
-
-- Purpose: **Remove a link**
-- Parameters:
- - **link_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeLink",
- "id": 2136522739,
- "params": [
- "14"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2136522739,
- "result": true
-}
-```
-
-### createTaskLink
-
-- Purpose: **Create a link between two tasks**
-- Parameters:
- - **task_id** (integer, required)
- - **opposite_task_id** (integer, required)
- - **link_id** (integer, required)
-- Result on success: **task_link_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createTaskLink",
- "id": 509742912,
- "params": [
- 2,
- 3,
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 509742912,
- "result": 1
-}
-```
-
-### updateTaskLink
-
-- Purpose: **Update task link**
-- Parameters:
- - **task_link_id** (integer, required)
- - **task_id** (integer, required)
- - **opposite_task_id** (integer, required)
- - **link_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "updateTaskLink",
- "id": 669037109,
- "params": [
- 1,
- 2,
- 4,
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 669037109,
- "result": true
-}
-```
-
-### getTaskLinkById
-
-- Purpose: **Get a task link**
-- Parameters:
- - **task_link_id** (integer, required)
-- Result on success: **task link properties**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getTaskLinkById",
- "id": 809885202,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 809885202,
- "result": {
- "id": "1",
- "link_id": "1",
- "task_id": "2",
- "opposite_task_id": "3"
- }
-}
-```
-
-### getAllTaskLinks
-
-- Purpose: **Get all links related to a task**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **list of task link**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllTaskLinks",
- "id": 810848359,
- "params": [
- 2
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 810848359,
- "result": [
- {
- "id": "1",
- "task_id": "3",
- "label": "relates to",
- "title": "B",
- "is_active": "1",
- "project_id": "1",
- "task_time_spent": "0",
- "task_time_estimated": "0",
- "task_assignee_id": "0",
- "task_assignee_username": null,
- "task_assignee_name": null,
- "column_title": "Backlog"
- }
- ]
-}
-```
-
-### removeTaskLink
-
-- Purpose: **Remove a link between two tasks**
-- Parameters:
- - **task_link_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeTaskLink",
- "id": 473028226,
- "params": [
- 1
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 473028226,
- "result": true
-}
-```
-
-### createFile
-
-- Purpose: **Create and upload a new task attachment**
-- Parameters:
- - **project_id** (integer, required)
- - **task_id** (integer, required)
- - **filename** (integer, required)
- - **blob** File content encoded in base64 (string, required)
-- Result on success: **file_id**
-- Result on failure: **false**
-- Note: **The maximum file size depends of your PHP configuration, this method should not be used to upload large files**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createFile",
- "id": 94500810,
- "params": [
- 1,
- 1,
- "My file",
- "cGxhaW4gdGV4dCBmaWxl"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 94500810,
- "result": 1
-}
-```
-
-### getAllFiles
-
-- Purpose: **Get all files attached to task**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **list of files**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getAllFiles",
- "id": 1880662820,
- "params": {
- "task_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1880662820,
- "result": [
- {
- "id": "1",
- "name": "My file",
- "path": "1\/1\/0db4d0a897a4c852f6e12f0239d4805f7b4ab596",
- "is_image": "0",
- "task_id": "1",
- "date": "1432509941",
- "user_id": "0",
- "size": "15",
- "username": null,
- "user_name": null
- }
- ]
-}
-```
-
-### getFile
-
-- Purpose: **Get file information**
-- Parameters:
- - **file_id** (integer, required)
-- Result on success: **file properties**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getFile",
- "id": 318676852,
- "params": [
- "1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 318676852,
- "result": {
- "id": "1",
- "name": "My file",
- "path": "1\/1\/0db4d0a897a4c852f6e12f0239d4805f7b4ab596",
- "is_image": "0",
- "task_id": "1",
- "date": "1432509941",
- "user_id": "0",
- "size": "15"
- }
-}
-```
-
-### downloadFile
-
-- Purpose: **Download file contents (encoded in base64)**
-- Parameters:
- - **file_id** (integer, required)
-- Result on success: **base64 encoded string**
-- Result on failure: **empty string**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "downloadFile",
- "id": 235943344,
- "params": [
- "1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 235943344,
- "result": "cGxhaW4gdGV4dCBmaWxl"
-}
-```
-
-### removeFile
-
-- Purpose: **Remove file**
-- Parameters:
- - **file_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeFile",
- "id": 447036524,
- "params": [
- "1"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 447036524,
- "result": true
-}
-```
-
-### removeAllFiles
-
-- Purpose: **Remove all files associated to a task**
-- Parameters:
- - **task_id** (integer, required)
-- Result on success: **true**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "removeAllFiles",
- "id": 593312993,
- "params": {
- "task_id": 1
- }
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 593312993,
- "result": true
-}
-```
-
-### getMe
-
-- Purpose: **Get logged user session**
-- Parameters: None
-- Result on success: **user session data**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getMe",
- "id": 1718627783
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1718627783,
- "result": {
- "id": 2,
- "username": "user",
- "is_admin": false,
- "is_ldap_user": false,
- "name": "",
- "email": "",
- "google_id": null,
- "github_id": null,
- "notifications_enabled": "0",
- "timezone": null,
- "language": null,
- "disable_login_form": "0",
- "twofactor_activated": false,
- "twofactor_secret": null,
- "token": "",
- "notifications_filter": "4"
- }
-}
-```
-
-### getMyDashboard
-
-- Purpose: **Get the dashboard of the logged user without pagination**
-- Parameters: None
-- Result on success: **Dashboard information**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getMyDashboard",
- "id": 447898718
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1563664593,
- "result": {
- "projects": [
- {
- "id": "2",
- "name": "my project",
- "is_active": "1",
- "token": "",
- "last_modified": "1438205337",
- "is_public": "0",
- "is_private": "1",
- "is_everybody_allowed": "0",
- "default_swimlane": "Default swimlane",
- "show_default_swimlane": "1",
- "description": null,
- "identifier": "",
- "columns": [
- {
- "id": "5",
- "title": "Backlog",
- "position": "1",
- "project_id": "2",
- "task_limit": "0",
- "description": "",
- "nb_tasks": 0
- },
- {
- "id": "6",
- "title": "Ready",
- "position": "2",
- "project_id": "2",
- "task_limit": "0",
- "description": "",
- "nb_tasks": 0
- },
- {
- "id": "7",
- "title": "Work in progress",
- "position": "3",
- "project_id": "2",
- "task_limit": "0",
- "description": "",
- "nb_tasks": 0
- },
- {
- "id": "8",
- "title": "Done",
- "position": "4",
- "project_id": "2",
- "task_limit": "0",
- "description": "",
- "nb_tasks": 0
- }
- ],
- "url": {
- "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=2",
- "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=2",
- "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=2"
- }
- }
- ],
- "tasks": [
- {
- "id": "1",
- "title": "new title",
- "date_due": "0",
- "date_creation": "1438205336",
- "project_id": "2",
- "color_id": "yellow",
- "time_spent": "0",
- "time_estimated": "0",
- "project_name": "my project",
- "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=2"
- }
- ],
- "subtasks": []
- }
-}
-```
-
-### getMyActivityStream
-
-- Purpose: **Get the last 100 events for the logged user**
-- Parameters: None
-- Result on success: **List of events**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getMyActivityStream",
- "id": 1132562181
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1132562181,
- "result": [
- {
- "id": "1",
- "date_creation": "1438205054",
- "event_name": "task.create",
- "creator_id": "2",
- "project_id": "2",
- "task_id": "1",
- "author_username": "user",
- "author_name": "",
- "email": "",
- "task": {
- "id": "1",
- "reference": "",
- "title": "my user title",
- "description": "",
- "date_creation": "1438205054",
- "date_completed": null,
- "date_modification": "1438205054",
- "date_due": "0",
- "date_started": null,
- "time_estimated": "0",
- "time_spent": "0",
- "color_id": "yellow",
- "project_id": "2",
- "column_id": "5",
- "owner_id": "0",
- "creator_id": "2",
- "position": "1",
- "is_active": "1",
- "score": "0",
- "category_id": "0",
- "swimlane_id": "0",
- "date_moved": "1438205054",
- "recurrence_status": "0",
- "recurrence_trigger": "0",
- "recurrence_factor": "0",
- "recurrence_timeframe": "0",
- "recurrence_basedate": "0",
- "recurrence_parent": null,
- "recurrence_child": null,
- "category_name": null,
- "swimlane_name": null,
- "project_name": "my project",
- "default_swimlane": "Default swimlane",
- "column_title": "Backlog",
- "assignee_username": null,
- "assignee_name": null,
- "creator_username": "user",
- "creator_name": ""
- },
- "changes": [],
- "author": "user",
- "event_title": "user created the task #1",
- "event_content": "\n<p class=\"activity-title\">\n user created the task <a href=\"\/?controller=task&amp;action=show&amp;task_id=1&amp;project_id=2\" class=\"\" title=\"\" >#1<\/a><\/p>\n<p class=\"activity-description\">\n <em>my user title<\/em>\n<\/p>"
- }
- ]
-}
-```
-
-### createMyPrivateProject
-
-- Purpose: **Create a private project for the logged user**
-- Parameters:
- - **name** (string, required)
- - **description** (string, optional)
-- Result on success: **project_id**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "createMyPrivateProject",
- "id": 1271580569,
- "params": [
- "my project"
- ]
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1271580569,
- "result": 2
-}
-```
-
-### getMyProjectsList
-
-- Purpose: **Get projects of the connected user**
-- Parameters: None
-- Result on success: **dictionary of project_id => project_name**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getMyProjectsList",
- "id": 987834805
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 987834805,
- "result": {
- "2": "my project"
- }
-}
-```
-### getMyOverdueTasks
-
-- Purpose: **Get my overdue tasks**
-- Result on success: **List of tasks**
-- Result on failure: **false**
-
-Request example to fetch all tasks on the board:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getMyOverdueTasks",
- "id": 133280317
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 133280317,
- "result": [
- {
- "id": "1",
- "title": "Task #1",
- "date_due": "1409961789",
- "project_id": "1",
- "project_name": "Test",
- "assignee_username":"admin",
- "assignee_name": null
- },
- {
- "id": "2",
- "title": "Test",
- "date_due": "1409962115",
- "project_id": "1",
- "project_name": "Test",
- "assignee_username":"admin",
- "assignee_name": null
- },
- ...
- ]
-}
-```
-
-### getMyProjects
-
-- Purpose: **Get projects of connected user with full details**
-- Parameters:
- - **none**
-- Result on success: **List of projects with details**
-- Result on failure: **false**
-
-Request example:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "getmyProjects",
- "id": 2134420212
-}
-```
-
-Response example:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 2134420212,
- "result": [
- {
- "id": "1",
- "name": "API test",
- "is_active": "1",
- "token": "",
- "last_modified": "1436119570",
- "is_public": "0",
- "is_private": "0",
- "is_everybody_allowed": "0",
- "default_swimlane": "Default swimlane",
- "show_default_swimlane": "1",
- "description": null,
- "identifier": "",
- "url": {
- "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1",
- "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1",
- "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1"
- }
- }
- ]
-}
-```
+Usage
+-----
+
+- [Authentication](api-authentication.markdown)
+- [Examples](api-examples.markdown)
+- [Application](api-application-procedures.markdown)
+- [Projects](api-project-procedures.markdown)
+- [Project Permissions](api-project-permission-procedures.markdown)
+- [Boards](api-board-procedures.markdown)
+- [Columns](api-column-procedures.markdown)
+- [Swimlanes](api-swimlane-procedures.markdown)
+- [Categories](api-category-procedures.markdown)
+- [Automatic Actions](api-action-procedures.markdown)
+- [Tasks](api-task-procedures.markdown)
+- [Subtasks](api-subtask-procedures.markdown)
+- [Files](api-file-procedures.markdown)
+- [Links](api-link-procedures.markdown)
+- [Comments](api-comment-procedures.markdown)
+- [Users](api-user-procedures.markdown)
+- [Groups](api-group-procedures.markdown)
+- [Group Members](api-group-member-procedures.markdown)
+- [Me](api-me-procedures.markdown)
diff --git a/doc/api-link-procedures.markdown b/doc/api-link-procedures.markdown
new file mode 100644
index 00000000..6113316f
--- /dev/null
+++ b/doc/api-link-procedures.markdown
@@ -0,0 +1,470 @@
+API Link Procedures
+===================
+
+## getAllLinks
+
+- Purpose: **Get the list of possible relations between tasks**
+- Parameters: none
+- Result on success: **List of links**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllLinks",
+ "id": 113057196
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 113057196,
+ "result": [
+ {
+ "id": "1",
+ "label": "relates to",
+ "opposite_id": "0"
+ },
+ {
+ "id": "2",
+ "label": "blocks",
+ "opposite_id": "3"
+ },
+ {
+ "id": "3",
+ "label": "is blocked by",
+ "opposite_id": "2"
+ },
+ {
+ "id": "4",
+ "label": "duplicates",
+ "opposite_id": "5"
+ },
+ {
+ "id": "5",
+ "label": "is duplicated by",
+ "opposite_id": "4"
+ },
+ {
+ "id": "6",
+ "label": "is a child of",
+ "opposite_id": "7"
+ },
+ {
+ "id": "7",
+ "label": "is a parent of",
+ "opposite_id": "6"
+ },
+ {
+ "id": "8",
+ "label": "targets milestone",
+ "opposite_id": "9"
+ },
+ {
+ "id": "9",
+ "label": "is a milestone of",
+ "opposite_id": "8"
+ },
+ {
+ "id": "10",
+ "label": "fixes",
+ "opposite_id": "11"
+ },
+ {
+ "id": "11",
+ "label": "is fixed by",
+ "opposite_id": "10"
+ }
+ ]
+}
+```
+
+## getOppositeLinkId
+
+- Purpose: **Get the opposite link id of a task link**
+- Parameters:
+ - **link_id** (integer, required)
+- Result on success: **link_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getOppositeLinkId",
+ "id": 407062448,
+ "params": [
+ 2
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 407062448,
+ "result": "3"
+}
+```
+
+## getLinkByLabel
+
+- Purpose: **Get a link by label**
+- Parameters:
+ - **label** (integer, required)
+- Result on success: **link properties**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getLinkByLabel",
+ "id": 1796123316,
+ "params": [
+ "blocks"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1796123316,
+ "result": {
+ "id": "2",
+ "label": "blocks",
+ "opposite_id": "3"
+ }
+}
+```
+
+## getLinkById
+
+- Purpose: **Get a link by id**
+- Parameters:
+ - **link_id** (integer, required)
+- Result on success: **link properties**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getLinkById",
+ "id": 1190238402,
+ "params": [
+ 4
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1190238402,
+ "result": {
+ "id": "4",
+ "label": "duplicates",
+ "opposite_id": "5"
+ }
+}
+```
+
+## createLink
+
+- Purpose: **Create a new task relation**
+- Parameters:
+ - **label** (integer, required)
+ - **opposite_label** (integer, optional)
+- Result on success: **link_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createLink",
+ "id": 1040237496,
+ "params": [
+ "foo",
+ "bar"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1040237496,
+ "result": 13
+}
+```
+
+## updateLink
+
+- Purpose: **Update a link**
+- Parameters:
+ - **link_id** (integer, required)
+ - **opposite_link_id** (integer, required)
+ - **label** (string, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateLink",
+ "id": 2110446926,
+ "params": [
+ "14",
+ "12",
+ "boo"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2110446926,
+ "result": true
+}
+```
+
+## removeLink
+
+- Purpose: **Remove a link**
+- Parameters:
+ - **link_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeLink",
+ "id": 2136522739,
+ "params": [
+ "14"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2136522739,
+ "result": true
+}
+```
+
+## createTaskLink
+
+- Purpose: **Create a link between two tasks**
+- Parameters:
+ - **task_id** (integer, required)
+ - **opposite_task_id** (integer, required)
+ - **link_id** (integer, required)
+- Result on success: **task_link_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createTaskLink",
+ "id": 509742912,
+ "params": [
+ 2,
+ 3,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 509742912,
+ "result": 1
+}
+```
+
+## updateTaskLink
+
+- Purpose: **Update task link**
+- Parameters:
+ - **task_link_id** (integer, required)
+ - **task_id** (integer, required)
+ - **opposite_task_id** (integer, required)
+ - **link_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateTaskLink",
+ "id": 669037109,
+ "params": [
+ 1,
+ 2,
+ 4,
+ 2
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 669037109,
+ "result": true
+}
+```
+
+## getTaskLinkById
+
+- Purpose: **Get a task link**
+- Parameters:
+ - **task_link_id** (integer, required)
+- Result on success: **task link properties**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getTaskLinkById",
+ "id": 809885202,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 809885202,
+ "result": {
+ "id": "1",
+ "link_id": "1",
+ "task_id": "2",
+ "opposite_task_id": "3"
+ }
+}
+```
+
+## getAllTaskLinks
+
+- Purpose: **Get all links related to a task**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **list of task link**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllTaskLinks",
+ "id": 810848359,
+ "params": [
+ 2
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 810848359,
+ "result": [
+ {
+ "id": "1",
+ "task_id": "3",
+ "label": "relates to",
+ "title": "B",
+ "is_active": "1",
+ "project_id": "1",
+ "task_time_spent": "0",
+ "task_time_estimated": "0",
+ "task_assignee_id": "0",
+ "task_assignee_username": null,
+ "task_assignee_name": null,
+ "column_title": "Backlog"
+ }
+ ]
+}
+```
+
+## removeTaskLink
+
+- Purpose: **Remove a link between two tasks**
+- Parameters:
+ - **task_link_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeTaskLink",
+ "id": 473028226,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 473028226,
+ "result": true
+}
+```
diff --git a/doc/api-me-procedures.markdown b/doc/api-me-procedures.markdown
new file mode 100644
index 00000000..e90bee61
--- /dev/null
+++ b/doc/api-me-procedures.markdown
@@ -0,0 +1,385 @@
+User API Specific Procedures
+============================
+
+## getMe
+
+- Purpose: **Get logged user session**
+- Parameters: None
+- Result on success: **user session data**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getMe",
+ "id": 1718627783
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1718627783,
+ "result": {
+ "id": 2,
+ "username": "user",
+ "role": "app-user",
+ "is_ldap_user": false,
+ "name": "",
+ "email": "",
+ "google_id": null,
+ "github_id": null,
+ "notifications_enabled": "0",
+ "timezone": null,
+ "language": null,
+ "disable_login_form": "0",
+ "twofactor_activated": false,
+ "twofactor_secret": null,
+ "token": "",
+ "notifications_filter": "4"
+ }
+}
+```
+
+## getMyDashboard
+
+- Purpose: **Get the dashboard of the logged user without pagination**
+- Parameters: None
+- Result on success: **Dashboard information**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getMyDashboard",
+ "id": 447898718
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1563664593,
+ "result": {
+ "projects": [
+ {
+ "id": "2",
+ "name": "my project",
+ "is_active": "1",
+ "token": "",
+ "last_modified": "1438205337",
+ "is_public": "0",
+ "is_private": "1",
+ "is_everybody_allowed": "0",
+ "default_swimlane": "Default swimlane",
+ "show_default_swimlane": "1",
+ "description": null,
+ "identifier": "",
+ "columns": [
+ {
+ "id": "5",
+ "title": "Backlog",
+ "position": "1",
+ "project_id": "2",
+ "task_limit": "0",
+ "description": "",
+ "nb_tasks": 0
+ },
+ {
+ "id": "6",
+ "title": "Ready",
+ "position": "2",
+ "project_id": "2",
+ "task_limit": "0",
+ "description": "",
+ "nb_tasks": 0
+ },
+ {
+ "id": "7",
+ "title": "Work in progress",
+ "position": "3",
+ "project_id": "2",
+ "task_limit": "0",
+ "description": "",
+ "nb_tasks": 0
+ },
+ {
+ "id": "8",
+ "title": "Done",
+ "position": "4",
+ "project_id": "2",
+ "task_limit": "0",
+ "description": "",
+ "nb_tasks": 0
+ }
+ ],
+ "url": {
+ "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=2",
+ "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=2",
+ "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=2"
+ }
+ }
+ ],
+ "tasks": [
+ {
+ "id": "1",
+ "title": "new title",
+ "date_due": "0",
+ "date_creation": "1438205336",
+ "project_id": "2",
+ "color_id": "yellow",
+ "time_spent": "0",
+ "time_estimated": "0",
+ "project_name": "my project",
+ "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=2"
+ }
+ ],
+ "subtasks": []
+ }
+}
+```
+
+## getMyActivityStream
+
+- Purpose: **Get the last 100 events for the logged user**
+- Parameters: None
+- Result on success: **List of events**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getMyActivityStream",
+ "id": 1132562181
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1132562181,
+ "result": [
+ {
+ "id": "1",
+ "date_creation": "1438205054",
+ "event_name": "task.create",
+ "creator_id": "2",
+ "project_id": "2",
+ "task_id": "1",
+ "author_username": "user",
+ "author_name": "",
+ "email": "",
+ "task": {
+ "id": "1",
+ "reference": "",
+ "title": "my user title",
+ "description": "",
+ "date_creation": "1438205054",
+ "date_completed": null,
+ "date_modification": "1438205054",
+ "date_due": "0",
+ "date_started": null,
+ "time_estimated": "0",
+ "time_spent": "0",
+ "color_id": "yellow",
+ "project_id": "2",
+ "column_id": "5",
+ "owner_id": "0",
+ "creator_id": "2",
+ "position": "1",
+ "is_active": "1",
+ "score": "0",
+ "category_id": "0",
+ "swimlane_id": "0",
+ "date_moved": "1438205054",
+ "recurrence_status": "0",
+ "recurrence_trigger": "0",
+ "recurrence_factor": "0",
+ "recurrence_timeframe": "0",
+ "recurrence_basedate": "0",
+ "recurrence_parent": null,
+ "recurrence_child": null,
+ "category_name": null,
+ "swimlane_name": null,
+ "project_name": "my project",
+ "default_swimlane": "Default swimlane",
+ "column_title": "Backlog",
+ "assignee_username": null,
+ "assignee_name": null,
+ "creator_username": "user",
+ "creator_name": ""
+ },
+ "changes": [],
+ "author": "user",
+ "event_title": "user created the task #1",
+ "event_content": "\n<p class=\"activity-title\">\n user created the task <a href=\"\/?controller=task&amp;action=show&amp;task_id=1&amp;project_id=2\" class=\"\" title=\"\" >#1<\/a><\/p>\n<p class=\"activity-description\">\n <em>my user title<\/em>\n<\/p>"
+ }
+ ]
+}
+```
+
+## createMyPrivateProject
+
+- Purpose: **Create a private project for the logged user**
+- Parameters:
+ - **name** (string, required)
+ - **description** (string, optional)
+- Result on success: **project_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createMyPrivateProject",
+ "id": 1271580569,
+ "params": [
+ "my project"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1271580569,
+ "result": 2
+}
+```
+
+## getMyProjectsList
+
+- Purpose: **Get projects of the connected user**
+- Parameters: None
+- Result on success: **dictionary of project_id => project_name**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getMyProjectsList",
+ "id": 987834805
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 987834805,
+ "result": {
+ "2": "my project"
+ }
+}
+```
+## getMyOverdueTasks
+
+- Purpose: **Get my overdue tasks**
+- Result on success: **List of tasks**
+- Result on failure: **false**
+
+Request example to fetch all tasks on the board:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getMyOverdueTasks",
+ "id": 133280317
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 133280317,
+ "result": [
+ {
+ "id": "1",
+ "title": "Task #1",
+ "date_due": "1409961789",
+ "project_id": "1",
+ "project_name": "Test",
+ "assignee_username":"admin",
+ "assignee_name": null
+ },
+ {
+ "id": "2",
+ "title": "Test",
+ "date_due": "1409962115",
+ "project_id": "1",
+ "project_name": "Test",
+ "assignee_username":"admin",
+ "assignee_name": null
+ },
+ ...
+ ]
+}
+```
+
+## getMyProjects
+
+- Purpose: **Get projects of connected user with full details**
+- Parameters:
+ - **none**
+- Result on success: **List of projects with details**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getmyProjects",
+ "id": 2134420212
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2134420212,
+ "result": [
+ {
+ "id": "1",
+ "name": "API test",
+ "is_active": "1",
+ "token": "",
+ "last_modified": "1436119570",
+ "is_public": "0",
+ "is_private": "0",
+ "is_everybody_allowed": "0",
+ "default_swimlane": "Default swimlane",
+ "show_default_swimlane": "1",
+ "description": null,
+ "identifier": "",
+ "url": {
+ "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1",
+ "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1",
+ "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1"
+ }
+ }
+ ]
+}
+```
diff --git a/doc/api-project-permission-procedures.markdown b/doc/api-project-permission-procedures.markdown
new file mode 100644
index 00000000..2844ae3c
--- /dev/null
+++ b/doc/api-project-permission-procedures.markdown
@@ -0,0 +1,274 @@
+Project Permission API Procedures
+=================================
+
+## getProjectUsers
+
+- Purpose: **Get all members of a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **Dictionary of user_id => user name**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectUsers",
+ "id": 1601016721,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1601016721,
+ "result": {
+ "1": "admin"
+ }
+}
+```
+
+## getAssignableUsers
+
+- Purpose: **Get users that can be assigned to a task for a project** (all members except viewers)
+- Parameters:
+ - **project_id** (integer, required)
+ - **prepend_unassigned** (boolean, optional, default is false)
+- Result on success: **Dictionary of user_id => user name**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAssignableUsers",
+ "id": 658294870,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 658294870,
+ "result": {
+ "1": "admin"
+ }
+}
+```
+
+## addProjectUser
+
+- Purpose: **Grant access to a project for a user**
+- Parameters:
+ - **project_id** (integer, required)
+ - **user_id** (integer, required)
+ - **role** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "addProjectUser",
+ "id": 1294688355,
+ "params": [
+ "1",
+ "1",
+ "project-viewer"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1294688355,
+ "result": true
+}
+```
+
+## addProjectGroup
+
+- Purpose: **Grant access to a project for a group**
+- Parameters:
+ - **project_id** (integer, required)
+ - **group_id** (integer, required)
+ - **role** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "addProjectGroup",
+ "id": 1694959089,
+ "params": [
+ "1",
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1694959089,
+ "result": true
+}
+```
+
+## removeProjectUser
+
+- Purpose: **Revoke user access to a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeProjectUser",
+ "id": 645233805,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 645233805,
+ "result": true
+}
+```
+
+## removeProjectGroup
+
+- Purpose: **Revoke group access to a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **group_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeProjectGroup",
+ "id": 557146966,
+ "params": [
+ 1,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 557146966,
+ "result": true
+}
+```
+
+## changeProjectUserRole
+
+- Purpose: **Change role of a user for a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **user_id** (integer, required)
+ - **role** (string, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "changeProjectUserRole",
+ "id": 193473170,
+ "params": [
+ "1",
+ "1",
+ "project-viewer"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 193473170,
+ "result": true
+}
+```
+
+## changeProjectGroupRole
+
+- Purpose: **Change role of a group for a project**
+- Parameters:
+ - **project_id** (integer, required)
+ - **group_id** (integer, required)
+ - **role** (string, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "changeProjectGroupRole",
+ "id": 2114673298,
+ "params": [
+ "1",
+ "1",
+ "project-viewer"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2114673298,
+ "result": true
+}
+```
diff --git a/doc/api-project-procedures.markdown b/doc/api-project-procedures.markdown
new file mode 100644
index 00000000..6cc1b15b
--- /dev/null
+++ b/doc/api-project-procedures.markdown
@@ -0,0 +1,411 @@
+API Project Procedures
+======================
+
+## createProject
+
+- Purpose: **Create a new project**
+- Parameters:
+ - **name** (string, required)
+ - **description** (string, optional)
+- Result on success: **project_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createProject",
+ "id": 1797076613,
+ "params": {
+ "name": "PHP client"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1797076613,
+ "result": 2
+}
+```
+
+## getProjectById
+
+- Purpose: **Get project information**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **project properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectById",
+ "id": 226760253,
+ "params": {
+ "project_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 226760253,
+ "result": {
+ "id": "1",
+ "name": "API test",
+ "is_active": "1",
+ "token": "",
+ "last_modified": "1436119135",
+ "is_public": "0",
+ "is_private": "0",
+ "is_everybody_allowed": "0",
+ "default_swimlane": "Default swimlane",
+ "show_default_swimlane": "1",
+ "description": "test",
+ "identifier": "",
+ "url": {
+ "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1",
+ "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1",
+ "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1"
+ }
+ }
+}
+```
+
+## getProjectByName
+
+- Purpose: **Get project information**
+- Parameters:
+ - **name** (string, required)
+- Result on success: **project properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectByName",
+ "id": 1620253806,
+ "params": {
+ "name": "Test"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1620253806,
+ "result": {
+ "id": "1",
+ "name": "Test",
+ "is_active": "1",
+ "token": "",
+ "last_modified": "1436119135",
+ "is_public": "0",
+ "is_private": "0",
+ "is_everybody_allowed": "0",
+ "default_swimlane": "Default swimlane",
+ "show_default_swimlane": "1",
+ "description": "test",
+ "identifier": "",
+ "url": {
+ "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1",
+ "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1",
+ "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1"
+ }
+ }
+}
+```
+
+## getAllProjects
+
+- Purpose: **Get all available projects**
+- Parameters:
+ - **none**
+- Result on success: **List of projects**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllProjects",
+ "id": 2134420212
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2134420212,
+ "result": [
+ {
+ "id": "1",
+ "name": "API test",
+ "is_active": "1",
+ "token": "",
+ "last_modified": "1436119570",
+ "is_public": "0",
+ "is_private": "0",
+ "is_everybody_allowed": "0",
+ "default_swimlane": "Default swimlane",
+ "show_default_swimlane": "1",
+ "description": null,
+ "identifier": "",
+ "url": {
+ "board": "http:\/\/127.0.0.1:8000\/?controller=board&action=show&project_id=1",
+ "calendar": "http:\/\/127.0.0.1:8000\/?controller=calendar&action=show&project_id=1",
+ "list": "http:\/\/127.0.0.1:8000\/?controller=listing&action=show&project_id=1"
+ }
+ }
+ ]
+}
+```
+
+## updateProject
+
+- Purpose: **Update a project**
+- Parameters:
+ - **id** (integer, required)
+ - **name** (string, required)
+ - **description** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateProject",
+ "id": 1853996288,
+ "params": {
+ "id": 1,
+ "name": "PHP client update"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1853996288,
+ "result": true
+}
+```
+
+## removeProject
+
+- Purpose: **Remove a project**
+- Parameters:
+ **project_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeProject",
+ "id": 46285125,
+ "params": {
+ "project_id": "2"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 46285125,
+ "result": true
+}
+```
+
+## enableProject
+
+- Purpose: **Enable a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "enableProject",
+ "id": 1775494839,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1775494839,
+ "result": true
+}
+```
+
+## disableProject
+
+- Purpose: **Disable a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "disableProject",
+ "id": 1734202312,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1734202312,
+ "result": true
+}
+```
+
+## enableProjectPublicAccess
+
+- Purpose: **Enable public access for a given project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "enableProjectPublicAccess",
+ "id": 103792571,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 103792571,
+ "result": true
+}
+```
+
+## disableProjectPublicAccess
+
+- Purpose: **Disable public access for a given project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "disableProjectPublicAccess",
+ "id": 942472945,
+ "params": [
+ "1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 942472945,
+ "result": true
+}
+```
+
+## getProjectActivity
+
+- Purpose: **Get activity stream for a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **List of events**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectActivity",
+ "id": 942472945,
+ "params": [
+ "project_id": 1
+ ]
+}
+```
+
+## getProjectActivities
+
+- Purpose: **Get Activityfeed for Project(s)**
+- Parameters:
+ - **project_ids** (integer array, required)
+- Result on success: **List of events**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getProjectActivities",
+ "id": 942472945,
+ "params": [
+ "project_ids": [1,2]
+ ]
+}
+```
diff --git a/doc/api-subtask-procedures.markdown b/doc/api-subtask-procedures.markdown
new file mode 100644
index 00000000..c1dbae37
--- /dev/null
+++ b/doc/api-subtask-procedures.markdown
@@ -0,0 +1,194 @@
+API Subtask procedures
+======================
+
+## createSubtask
+
+- Purpose: **Create a new subtask**
+- Parameters:
+ - **task_id** (integer, required)
+ - **title** (integer, required)
+ - **user_id** (int, optional)
+ - **time_estimated** (int, optional)
+ - **time_spent** (int, optional)
+ - **status** (int, optional)
+- Result on success: **subtask_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createSubtask",
+ "id": 2041554661,
+ "params": {
+ "task_id": 1,
+ "title": "Subtask #1"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2041554661,
+ "result": 45
+}
+```
+
+## getSubtask
+
+- Purpose: **Get subtask information**
+- Parameters:
+ - **subtask_id** (integer)
+- Result on success: **subtask properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getSubtask",
+ "id": 133184525,
+ "params": {
+ "subtask_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 133184525,
+ "result": {
+ "id": "1",
+ "title": "Subtask #1",
+ "status": "0",
+ "time_estimated": "0",
+ "time_spent": "0",
+ "task_id": "1",
+ "user_id": "0"
+ }
+}
+```
+
+## getAllSubtasks
+
+- Purpose: **Get all available subtasks**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **List of subtasks**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllSubtasks",
+ "id": 2087700490,
+ "params": {
+ "task_id": 1
+ }
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2087700490,
+ "result": [
+ {
+ "id": "1",
+ "title": "Subtask #1",
+ "status": "0",
+ "time_estimated": "0",
+ "time_spent": "0",
+ "task_id": "1",
+ "user_id": "0",
+ "username": null,
+ "name": null,
+ "status_name": "Todo"
+ },
+ ...
+ ]
+}
+```
+
+## updateSubtask
+
+- Purpose: **Update a subtask**
+- Parameters:
+ - **id** (integer, required)
+ - **task_id** (integer, required)
+ - **title** (integer, optional)
+ - **user_id** (integer, optional)
+ - **time_estimated** (integer, optional)
+ - **time_spent** (integer, optional)
+ - **status** (integer, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateSubtask",
+ "id": 191749979,
+ "params": {
+ "id": 1,
+ "task_id": 1,
+ "status": 1,
+ "time_spent": 5,
+ "user_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 191749979,
+ "result": true
+}
+```
+
+## removeSubtask
+
+- Purpose: **Remove a subtask**
+- Parameters:
+ - **subtask_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeSubtask",
+ "id": 1382487306,
+ "params": {
+ "subtask_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1382487306,
+ "result": true
+}
+```
diff --git a/doc/api-swimlane-procedures.markdown b/doc/api-swimlane-procedures.markdown
new file mode 100644
index 00000000..c58e56c9
--- /dev/null
+++ b/doc/api-swimlane-procedures.markdown
@@ -0,0 +1,438 @@
+API Swimlane Procedures
+=======================
+
+## getDefaultSwimlane
+
+- Purpose: **Get the default swimlane for a project**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getDefaultSwimlane",
+ "id": 898774713,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 898774713,
+ "result": {
+ "id": "1",
+ "default_swimlane": "Default swimlane",
+ "show_default_swimlane": "1"
+ }
+}
+```
+
+## getActiveSwimlanes
+
+- Purpose: **Get the list of enabled swimlanes of a project (include default swimlane if enabled)**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **List of swimlanes**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getActiveSwimlanes",
+ "id": 934789422,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 934789422,
+ "result": [
+ {
+ "id": 0,
+ "name": "Default swimlane"
+ },
+ {
+ "id": "2",
+ "name": "Swimlane A"
+ }
+ ]
+}
+```
+
+## getAllSwimlanes
+
+- Purpose: **Get the list of all swimlanes of a project (enabled or disabled) and sorted by position**
+- Parameters:
+ - **project_id** (integer, required)
+- Result on success: **List of swimlanes**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllSwimlanes",
+ "id": 509791576,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 509791576,
+ "result": [
+ {
+ "id": "1",
+ "name": "Another swimlane",
+ "position": "1",
+ "is_active": "1",
+ "project_id": "1"
+ },
+ {
+ "id": "2",
+ "name": "Swimlane A",
+ "position": "2",
+ "is_active": "1",
+ "project_id": "1"
+ }
+ ]
+}
+```
+
+## getSwimlane
+
+- Purpose: **Get the a swimlane by id**
+- Parameters:
+ - **swimlane_id** (integer, required)
+- Result on success: **swimlane properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getSwimlane",
+ "id": 131071870,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 131071870,
+ "result": {
+ "id": "1",
+ "name": "Swimlane 1",
+ "position": "1",
+ "is_active": "1",
+ "project_id": "1"
+ }
+}
+```
+
+## getSwimlaneById
+
+- Purpose: **Get the a swimlane by id**
+- Parameters:
+ - **swimlane_id** (integer, required)
+- Result on success: **swimlane properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getSwimlaneById",
+ "id": 131071870,
+ "params": [
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 131071870,
+ "result": {
+ "id": "1",
+ "name": "Swimlane 1",
+ "position": "1",
+ "is_active": "1",
+ "project_id": "1"
+ }
+}
+```
+
+## getSwimlaneByName
+
+- Purpose: **Get the a swimlane by name**
+- Parameters:
+ - **project_id** (integer, required)
+ - **name** (string, required)
+- Result on success: **swimlane properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getSwimlaneByName",
+ "id": 824623567,
+ "params": [
+ 1,
+ "Swimlane 1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 824623567,
+ "result": {
+ "id": "1",
+ "name": "Swimlane 1",
+ "position": "1",
+ "is_active": "1",
+ "project_id": "1"
+ }
+}
+```
+
+## changeSwimlanePosition
+
+- Purpose: **Move up the swimlane position** (only for active swimlanes)
+- Parameters:
+ - **project_id** (integer, required)
+ - **swimlane_id** (integer, required)
+ - **position** (integer, required, must be >= 1)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "changeSwimlanePosition",
+ "id": 99275573,
+ "params": [
+ 1,
+ 2,
+ 3
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 99275573,
+ "result": true
+}
+```
+
+## updateSwimlane
+
+- Purpose: **Update swimlane properties**
+- Parameters:
+ - **swimlane_id** (integer, required)
+ - **name** (string, required)
+ - **description** (string, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateSwimlane",
+ "id": 87102426,
+ "params": [
+ "1",
+ "Another swimlane"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 87102426,
+ "result": true
+}
+```
+
+## addSwimlane
+
+- Purpose: **Add a new swimlane**
+- Parameters:
+ - **project_id** (integer, required)
+ - **name** (string, required)
+ - **description** (string, optional)
+- Result on success: **swimlane_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "addSwimlane",
+ "id": 849940086,
+ "params": [
+ 1,
+ "Swimlane 1"
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 849940086,
+ "result": 1
+}
+```
+
+## removeSwimlane
+
+- Purpose: **Remove a swimlane**
+- Parameters:
+ - **project_id** (integer, required)
+ - **swimlane_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeSwimlane",
+ "id": 1433237746,
+ "params": [
+ 2,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1433237746,
+ "result": true
+}
+```
+
+## disableSwimlane
+
+- Purpose: **Enable a swimlane**
+- Parameters:
+ - **project_id** (integer, required)
+ - **swimlane_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "disableSwimlane",
+ "id": 1433237746,
+ "params": [
+ 2,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1433237746,
+ "result": true
+}
+```
+
+## enableSwimlane
+
+- Purpose: **Enable a swimlane**
+- Parameters:
+ - **project_id** (integer, required)
+ - **swimlane_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "enableSwimlane",
+ "id": 1433237746,
+ "params": [
+ 2,
+ 1
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1433237746,
+ "result": true
+}
+```
diff --git a/doc/api-task-procedures.markdown b/doc/api-task-procedures.markdown
new file mode 100644
index 00000000..486c0a09
--- /dev/null
+++ b/doc/api-task-procedures.markdown
@@ -0,0 +1,636 @@
+API Task Procedures
+===================
+
+## createTask
+
+- Purpose: **Create a new task**
+- Parameters:
+ - **title** (string, required)
+ - **project_id** (integer, required)
+ - **color_id** (string, optional)
+ - **column_id** (integer, optional)
+ - **owner_id** (integer, optional)
+ - **creator_id** (integer, optional)
+ - **date_due**: ISO8601 format (string, optional)
+ - **description** Markdown content (string, optional)
+ - **category_id** (integer, optional)
+ - **score** (integer, optional)
+ - **swimlane_id** (integer, optional)
+ - **recurrence_status** (integer, optional)
+ - **recurrence_trigger** (integer, optional)
+ - **recurrence_factor** (integer, optional)
+ - **recurrence_timeframe** (integer, optional)
+ - **recurrence_basedate** (integer, optional)
+- Result on success: **task_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createTask",
+ "id": 1176509098,
+ "params": {
+ "owner_id": 1,
+ "creator_id": 0,
+ "date_due": "",
+ "description": "",
+ "category_id": 0,
+ "score": 0,
+ "title": "Test",
+ "project_id": 1,
+ "color_id": "green",
+ "column_id": 2,
+ "recurrence_status": 0,
+ "recurrence_trigger": 0,
+ "recurrence_factor": 0,
+ "recurrence_timeframe": 0,
+ "recurrence_basedate": 0
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1176509098,
+ "result": 3
+}
+```
+
+## getTask
+
+- Purpose: **Get task by the unique id**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **task properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getTask",
+ "id": 700738119,
+ "params": {
+ "task_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 700738119,
+ "result": {
+ "id": "1",
+ "title": "Task #1",
+ "description": "",
+ "date_creation": "1409963206",
+ "color_id": "blue",
+ "project_id": "1",
+ "column_id": "2",
+ "owner_id": "1",
+ "position": "1",
+ "is_active": "1",
+ "date_completed": null,
+ "score": "0",
+ "date_due": "0",
+ "category_id": "0",
+ "creator_id": "0",
+ "date_modification": "1409963206",
+ "reference": "",
+ "date_started": null,
+ "time_spent": "0",
+ "time_estimated": "0",
+ "swimlane_id": "0",
+ "date_moved": "1430875287",
+ "recurrence_status": "0",
+ "recurrence_trigger": "0",
+ "recurrence_factor": "0",
+ "recurrence_timeframe": "0",
+ "recurrence_basedate": "0",
+ "recurrence_parent": null,
+ "recurrence_child": null,
+ "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=1",
+ "color": {
+ "name": "Yellow",
+ "background": "rgb(245, 247, 196)",
+ "border": "rgb(223, 227, 45)"
+ }
+ }
+}
+```
+
+## getTaskByReference
+
+- Purpose: **Get task by the external reference**
+- Parameters:
+ - **project_id** (integer, required)
+ - **reference** (string, required)
+- Result on success: **task properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getTaskByReference",
+ "id": 1992081213,
+ "params": {
+ "project_id": 1,
+ "reference": "TICKET-1234"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1992081213,
+ "result": {
+ "id": "5",
+ "title": "Task with external ticket number",
+ "description": "[Link to my ticket](http:\/\/my-ticketing-system\/1234)",
+ "date_creation": "1434227446",
+ "color_id": "yellow",
+ "project_id": "1",
+ "column_id": "1",
+ "owner_id": "0",
+ "position": "4",
+ "is_active": "1",
+ "date_completed": null,
+ "score": "0",
+ "date_due": "0",
+ "category_id": "0",
+ "creator_id": "0",
+ "date_modification": "1434227446",
+ "reference": "TICKET-1234",
+ "date_started": null,
+ "time_spent": "0",
+ "time_estimated": "0",
+ "swimlane_id": "0",
+ "date_moved": "1434227446",
+ "recurrence_status": "0",
+ "recurrence_trigger": "0",
+ "recurrence_factor": "0",
+ "recurrence_timeframe": "0",
+ "recurrence_basedate": "0",
+ "recurrence_parent": null,
+ "recurrence_child": null,
+ "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=5&project_id=1"
+ }
+}
+```
+
+## getAllTasks
+
+- Purpose: **Get all available tasks**
+- Parameters:
+ - **project_id** (integer, required)
+ - **status_id**: The value 1 for active tasks and 0 for inactive (integer, required)
+- Result on success: **List of tasks**
+- Result on failure: **false**
+
+Request example to fetch all tasks on the board:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllTasks",
+ "id": 133280317,
+ "params": {
+ "project_id": 1,
+ "status_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 133280317,
+ "result": [
+ {
+ "id": "1",
+ "title": "Task #1",
+ "description": "",
+ "date_creation": "1409961789",
+ "color_id": "blue",
+ "project_id": "1",
+ "column_id": "2",
+ "owner_id": "1",
+ "position": "1",
+ "is_active": "1",
+ "date_completed": null,
+ "score": "0",
+ "date_due": "0",
+ "category_id": "0",
+ "creator_id": "0",
+ "date_modification": "1409961789",
+ "reference": "",
+ "date_started": null,
+ "time_spent": "0",
+ "time_estimated": "0",
+ "swimlane_id": "0",
+ "date_moved": "1430783191",
+ "recurrence_status": "0",
+ "recurrence_trigger": "0",
+ "recurrence_factor": "0",
+ "recurrence_timeframe": "0",
+ "recurrence_basedate": "0",
+ "recurrence_parent": null,
+ "recurrence_child": null,
+ "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=1&project_id=1"
+ },
+ {
+ "id": "2",
+ "title": "Test",
+ "description": "",
+ "date_creation": "1409962115",
+ "color_id": "green",
+ "project_id": "1",
+ "column_id": "2",
+ "owner_id": "1",
+ "position": "2",
+ "is_active": "1",
+ "date_completed": null,
+ "score": "0",
+ "date_due": "0",
+ "category_id": "0",
+ "creator_id": "0",
+ "date_modification": "1409962115",
+ "reference": "",
+ "date_started": null,
+ "time_spent": "0",
+ "time_estimated": "0",
+ "swimlane_id": "0",
+ "date_moved": "1430783191",
+ "recurrence_status": "0",
+ "recurrence_trigger": "0",
+ "recurrence_factor": "0",
+ "recurrence_timeframe": "0",
+ "recurrence_basedate": "0",
+ "recurrence_parent": null,
+ "recurrence_child": null,
+ "url": "http:\/\/127.0.0.1:8000\/?controller=task&action=show&task_id=2&project_id=1"
+ },
+ ...
+ ]
+}
+```
+
+## getOverdueTasks
+
+- Purpose: **Get all overdue tasks**
+- Result on success: **List of tasks**
+- Result on failure: **false**
+
+Request example to fetch all tasks on the board:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getOverdueTasks",
+ "id": 133280317
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 133280317,
+ "result": [
+ {
+ "id": "1",
+ "title": "Task #1",
+ "date_due": "1409961789",
+ "project_id": "1",
+ "project_name": "Test",
+ "assignee_username":"admin",
+ "assignee_name": null
+ },
+ {
+ "id": "2",
+ "title": "Test",
+ "date_due": "1409962115",
+ "project_id": "1",
+ "project_name": "Test",
+ "assignee_username":"admin",
+ "assignee_name": null
+ },
+ ...
+ ]
+}
+```
+
+## getOverdueTasksByProject
+
+- Purpose: **Get all overdue tasks for a special project**
+- Result on success: **List of tasks**
+- Result on failure: **false**
+
+Request example to fetch all tasks on the board:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getOverdueTasksByProject",
+ "id": 133280317,
+ "params": {
+ "project_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 133280317,
+ "result": [
+ {
+ "id": "1",
+ "title": "Task #1",
+ "date_due": "1409961789",
+ "project_id": "1",
+ "project_name": "Test",
+ "assignee_username":"admin",
+ "assignee_name": null
+ },
+ {
+ "id": "2",
+ "title": "Test",
+ "date_due": "1409962115",
+ "project_id": "1",
+ "project_name": "Test",
+ "assignee_username":"admin",
+ "assignee_name": null
+ },
+ ...
+ ]
+}
+```
+
+## updateTask
+
+- Purpose: **Update a task**
+- Parameters:
+ - **id** (integer, required)
+ - **title** (string, optional)
+ - **color_id** (string, optional)
+ - **owner_id** (integer, optional)
+ - **date_due**: ISO8601 format (string, optional)
+ - **description** Markdown content (string, optional)
+ - **category_id** (integer, optional)
+ - **score** (integer, optional)
+ - **recurrence_status** (integer, optional)
+ - **recurrence_trigger** (integer, optional)
+ - **recurrence_factor** (integer, optional)
+ - **recurrence_timeframe** (integer, optional)
+ - **recurrence_basedate** (integer, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example to change the task color:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateTask",
+ "id": 1406803059,
+ "params": {
+ "id": 1,
+ "color_id": "blue"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1406803059,
+ "result": true
+}
+```
+
+## openTask
+
+- Purpose: **Set a task to the status open**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "openTask",
+ "id": 1888531925,
+ "params": {
+ "task_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1888531925,
+ "result": true
+}
+```
+
+## closeTask
+
+- Purpose: **Set a task to the status close**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "closeTask",
+ "id": 1654396960,
+ "params": {
+ "task_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1654396960,
+ "result": true
+}
+```
+
+## removeTask
+
+- Purpose: **Remove a task**
+- Parameters:
+ - **task_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeTask",
+ "id": 1423501287,
+ "params": {
+ "task_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1423501287,
+ "result": true
+}
+```
+
+## moveTaskPosition
+
+- Purpose: **Move a task to another column, position or swimlane inside the same board**
+- Parameters:
+ - **project_id** (integer, required)
+ - **task_id** (integer, required)
+ - **column_id** (integer, required)
+ - **position** (integer, required)
+ - **swimlane_id** (integer, optional, default=0)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "moveTaskPosition",
+ "id": 117211800,
+ "params": {
+ "project_id": 1,
+ "task_id": 1,
+ "column_id": 2,
+ "position": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 117211800,
+ "result": true
+}
+```
+
+## moveTaskToProject
+
+- Purpose: **Move a task to another project**
+- Parameters:
+ - **task_id** (integer, required)
+ - **project_id** (integer, required)
+ - **swimlane_id** (integer, optional)
+ - **column_id** (integer, optional)
+ - **category_id** (integer, optional)
+ - **owner_id** (integer, optional)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "moveTaskToProject",
+ "id": 15775829,
+ "params": [
+ 4,
+ 5
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 15775829,
+ "result": true
+}
+```
+
+## duplicateTaskToProject
+
+- Purpose: **Move a task to another column or another position**
+- Parameters:
+ - **task_id** (integer, required)
+ - **project_id** (integer, required)
+ - **swimlane_id** (integer, optional)
+ - **column_id** (integer, optional)
+ - **category_id** (integer, optional)
+ - **owner_id** (integer, optional)
+- Result on success: **task_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "duplicateTaskToProject",
+ "id": 1662458687,
+ "params": [
+ 5,
+ 7
+ ]
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1662458687,
+ "result": 6
+}
+```
diff --git a/doc/api-user-procedures.markdown b/doc/api-user-procedures.markdown
new file mode 100644
index 00000000..6c09355d
--- /dev/null
+++ b/doc/api-user-procedures.markdown
@@ -0,0 +1,357 @@
+API User Procedures
+===================
+
+## createUser
+
+- Purpose: **Create a new user**
+- Parameters:
+ - **username** Must be unique (string, required)
+ - **password** Must have at least 6 characters (string, required)
+ - **name** (string, optional)
+ - **email** (string, optional)
+ - **role** (string, optional, example: app-admin, app-manager, app-user)
+- Result on success: **user_id**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createUser",
+ "id": 1518863034,
+ "params": {
+ "username": "biloute",
+ "password": "123456"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1518863034,
+ "result": 22
+}
+```
+
+## createLdapUser
+
+- Purpose: **Create a new user authentified by LDAP**
+- Parameters:
+ - **username** (string, required)
+- Result on success: **user_id**
+- Result on failure: **false**
+
+The user will only be created if he is found on the LDAP server.
+This method works only with LDAP authentication configured in proxy or anonymous mode.
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "createLdapUser",
+ "id": 1518863034,
+ "params": {
+ "username": "my_ldap_user",
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1518863034,
+ "result": 22
+}
+```
+
+## getUser
+
+- Purpose: **Get user information**
+- Parameters:
+ - **user_id** (integer, required)
+- Result on success: **user properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getUser",
+ "id": 1769674781,
+ "params": {
+ "user_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1769674781,
+ "result": {
+ "id": "1",
+ "username": "biloute",
+ "password": "$2y$10$dRs6pPoBu935RpmsrhmbjevJH5MgZ7Kr9QrnVINwwyZ3.MOwqg.0m",
+ "role": "app-user",
+ "is_ldap_user": "0",
+ "name": "",
+ "email": "",
+ "google_id": null,
+ "github_id": null,
+ "notifications_enabled": "0"
+ }
+}
+```
+
+## getUserByName
+
+- Purpose: **Get user information**
+- Parameters:
+ - **username** (string, required)
+- Result on success: **user properties**
+- Result on failure: **null**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getUserByName",
+ "id": 1769674782,
+ "params": {
+ "username": "biloute"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1769674782,
+ "result": {
+ "id": "1",
+ "username": "biloute",
+ "password": "$2y$10$dRs6pPoBu935RpmsrhmbjevJH5MgZ7Kr9QrnVINwwyZ3.MOwqg.0m",
+ "role": "app-user",
+ "is_ldap_user": "0",
+ "name": "",
+ "email": "",
+ "google_id": null,
+ "github_id": null,
+ "notifications_enabled": "0"
+ }
+}
+```
+
+## getAllUsers
+
+- Purpose: **Get all available users**
+- Parameters:
+ - **none**
+- Result on success: **List of users**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "getAllUsers",
+ "id": 1438712131
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1438712131,
+ "result": [
+ {
+ "id": "1",
+ "username": "biloute",
+ "name": "",
+ "email": "",
+ "role": "app-user",
+ "is_ldap_user": "0",
+ "notifications_enabled": "0",
+ "google_id": null,
+ "github_id": null
+ },
+ ...
+ ]
+}
+```
+
+## updateUser
+
+- Purpose: **Update a user**
+- Parameters:
+ - **id** (integer)
+ - **username** (string, optional)
+ - **name** (string, optional)
+ - **email** (string, optional)
+ - **role** (string, optional, example: app-admin, app-manager, app-user)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "updateUser",
+ "id": 322123657,
+ "params": {
+ "id": 1,
+ "role": "app-manager"
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 322123657,
+ "result": true
+}
+```
+
+## removeUser
+
+- Purpose: **Remove a user**
+- Parameters:
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "removeUser",
+ "id": 2094191872,
+ "params": {
+ "user_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2094191872,
+ "result": true
+}
+```
+
+## disableUser
+
+- Purpose: **Disable a user**
+- Parameters:
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "disableUser",
+ "id": 2094191872,
+ "params": {
+ "user_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2094191872,
+ "result": true
+}
+```
+
+## enableUser
+
+- Purpose: **Enable a user**
+- Parameters:
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "enableUser",
+ "id": 2094191872,
+ "params": {
+ "user_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2094191872,
+ "result": true
+}
+```
+
+## isActiveUser
+
+- Purpose: **Check if a user is active**
+- Parameters:
+ - **user_id** (integer, required)
+- Result on success: **true**
+- Result on failure: **false**
+
+Request example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "isActiveUser",
+ "id": 2094191872,
+ "params": {
+ "user_id": 1
+ }
+}
+```
+
+Response example:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2094191872,
+ "result": true
+}
+```
diff --git a/doc/application-configuration.markdown b/doc/application-configuration.markdown
index 87929732..e3ddf6e9 100644
--- a/doc/application-configuration.markdown
+++ b/doc/application-configuration.markdown
@@ -11,23 +11,23 @@ Go to the menu **Settings**, then choose **Application settings** on the left.
### Application URL
This parameter is used for email notifications.
-The email footer will contains a link to the Kanboard task.
+The email footer will contain a link to the Kanboard task.
### Language
The application language can be changed at anytime.
The language will be set for all users.
-### Timezone
+### Time zone
-By default, Kanboard use UTC as timezone, but you can define your own timezone.
-The list contains all supported timezones by your web server.
+By default, Kanboard use UTC as time zone, but you can define your own time zone.
+The list contains all supported time zones by your web server.
### Date format
-Input format used for date fields, by example the due date for tasks.
+Input format used for date fields, for examples the due date for tasks.
-Kanboard offer 4 different formats:
+Kanboard offers 4 different formats:
- DD/MM/YYYY
- MM/DD/YYYY (default)
diff --git a/doc/automatic-actions.markdown b/doc/automatic-actions.markdown
index 71dfa90a..db56ccc0 100644
--- a/doc/automatic-actions.markdown
+++ b/doc/automatic-actions.markdown
@@ -6,17 +6,17 @@ To minimize the user interaction, Kanboard support automated actions.
Each automatic action is defined like that:
- An event to listen
-- An action linked to this event
+- Action linked to this event
- Eventually there is some parameters to define
-Each project have a different set of automatic actions, the configuration panel is located on the project listing page, just click on the link **Automatic actions**.
+Each project has a different set of automatic actions, the configuration panel is located on the project listing page, just click on the link **Automatic actions**.
Add a new action
----------------
### Choose an action
-![Choose an action](http://kanboard.net/screenshots/documentation/project-automatic-action-step1.png)
+![Choose action](http://kanboard.net/screenshots/documentation/project-automatic-action-step1.png)
### Choose an event
@@ -26,70 +26,44 @@ Add a new action
![Define parameters](http://kanboard.net/screenshots/documentation/project-automatic-action-step3.png)
-List of available events
-------------------------
-
-- Move a task to another column
-- Move a task to another position in the same column
-- Task modification
-- Task creation
-- Reopen a task
-- Closing a task
-- Task creation or modification
-- Task assignee change
-- Task link created or updated
-- Github commit received
-- Github issue opened
-- Github issue closed
-- Github issue reopened
-- Github issue assignee change
-- Github issue label change
-- Github issue comment created
-- Gitlab issue opened
-- Gitlab issue closed
-- Gitlab commit received
-- Bitbucket commit received
-- Bitbucket issue opened
-- Bitbucket issue closed
-- Bitbucket issue reopened
-- Bitbucket issue assignee change
-- Bitbucket issue comment created
-
List of available actions
-------------------------
-- Close the task
-- Open a task
-- Assign the task to a specific user
+- Create a comment from an external provider
+- Add a comment log when moving the task between columns
+- Assign automatically a category based on a color
+- Change the category based on an external label
+- Assign automatically a category based on a link
+- Assign automatically a color based on a category
+- Assign a color when the task is moved to a specific column
+- Change task color when using a specific task link
+- Assign a color to a specific user
- Assign the task to the person who does the action
+- Assign the task to the person who does the action when the column is changed
+- Assign the task to a specific user
+- Change the assignee based on an external username
+- Close the task
+- Close a task in a specific column
+- Create a task from an external provider
- Duplicate the task to another project
+- Send a task by email to someone
- Move the task to another project
- Move the task to another column when assigned to a user
+- Move the task to another column when the category is changed
- Move the task to another column when assignee is cleared
-- Assign a color when the task is moved to a specific column
-- Assign a color to a specific user
-- Assign automatically a color based on a category
-- Assign automatically a category based on a color
-- Create a comment from an external provider
-- Create a task from an external provider
-- Add a comment log when moving the task between columns
-- Change the assignee based on an external username
-- Change the category based on an external label
+- Open a task
- Automatically update the start date
-- Move the task to another column when the category is changed
-- Send a task by email to someone
-- Change task color when using a specific task link
Examples
--------
-Here are some examples used in the real life:
+Here are some examples used in real life:
### When I move a task to the column "Done", automatically close this task
-- Choose the action: **Close the task**
+- Choose action: **Close a task in a specific column**
- Choose the event: **Move a task to another column**
-- Define the action parameter: **Column = Done** (this is the destination column)
+- Define action parameter: **Column = Done** (this is the destination column)
### When I move a task to the column "To be validated", assign this task to a specific user
@@ -99,40 +73,40 @@ Here are some examples used in the real life:
### When I move a task to the column "Work in progress", assign this task to the current user
-- Choose the action: **Assign the task to the person who does the action**
+- Choose action: **Assign the task to the person who does the action when the column is changed**
- Choose the event: **Move a task to another column**
-- Define the action parameter: **Column = Work in progress**
+- Define action parameter: **Column = Work in progress**
### When a task is completed, duplicate this task to another project
Let's say we have two projects "Customer orders" and "Production", once the order is validated, swap it to the "Production" project.
-- Choose the action: **Duplicate the task to another project**
+- Choose action: **Duplicate the task to another project**
- Choose the event: **Closing a task**
-- Define the action parameters: **Column = Validated** and **Project = Production**
+- Define action parameters: **Column = Validated** and **Project = Production**
### When a task is moved to the last column, move the exact same task to another project
Let's say we have two projects "Ideas" and "Development", once the idea is validated, swap it to the "Development" project.
-- Choose the action: **Move the task to another project**
+- Choose action: **Move the task to another project**
- Choose the event: **Move a task to another column**
-- Define the action parameters: **Column = Validated** and **Project = Development**
+- Define action parameters: **Column = Validated** and **Project = Development**
### I want to assign automatically a color to the user Bob
-- Choose the action: **Assign a color to a specific user**
+- Choose action: **Assign a color to a specific user**
- Choose the event: **Task assignee change**
-- Define the action parameters: **Color = Green** and **Assignee = Bob**
+- Define action parameters: **Color = Green** and **Assignee = Bob**
-### I want to assign automatically a color to the defined category "Feature Request"
+### I want to assign a color automatically to the defined category "Feature Request"
-- Choose the action: **Assign automatically a color based on a category**
+- Choose action: **Assign automatically a color based on a category**
- Choose the event: **Task creation or modification**
-- Define the action parameters: **Color = Blue** and **Category = Feature Request**
+- Define action parameters: **Color = Blue** and **Category = Feature Request**
### I want to set the start date automatically when the task is moved to the column "Work in progress"
-- Choose the action: **Automatically update the start date**
+- Choose action: **Automatically update the start date**
- Choose the event: **Move a task to another column**
-- Define the action parameters: **Column = Work in progress**
+- Define action parameters: **Column = Work in progress**
diff --git a/doc/bitbucket-webhooks.markdown b/doc/bitbucket-webhooks.markdown
deleted file mode 100644
index 7f59aa11..00000000
--- a/doc/bitbucket-webhooks.markdown
+++ /dev/null
@@ -1,55 +0,0 @@
-Bitbucket webhooks
-==================
-
-Bitbucket events can be connected to Kanboard automatic actions.
-
-List of supported events
-------------------------
-
-- Bitbucket commit received
-- Bitbucket issue opened
-- Bitbucket issue closed
-- Bitbucket issue reopened
-- Bitbucket issue assignee change
-- Bitbucket issue comment created
-
-List of supported actions
--------------------------
-
-- Create a task from an external provider
-- Change the assignee based on an external username
-- Create a comment from an external provider
-- Close a task
-- Open a task
-
-Configuration
--------------
-
-![Bitbucket configuration](http://kanboard.net/screenshots/documentation/bitbucket-webhooks.png)
-
-1. On Kanboard, go to the project settings and choose the section **Integrations**
-2. Copy the Bitbucket webhook url
-3. On Bitbucket, go to the project settings and go to the section **Webhooks**
-4. Choose a title for your webhook and paste the Kanboard url
-
-Examples
---------
-
-### Close a Kanboard task when a commit pushed to Bitbucket
-
-- Choose the event: **Bitbucket commit received**
-- Choose the action: **Close the task**
-
-When one or more commits are sent to Bitbucket, Kanboard will receive the information, each commit message with a task number included will be closed.
-
-Example:
-
-- Commit message: "Fix bug #1234"
-- That will close the Kanboard task #1234
-
-### Add comment when a commit received
-
-- Choose the event: **Bitbucket commit received**
-- Choose the action: **Create a comment from an external provider**
-
-The comment will contains the commit message and the url to the commit.
diff --git a/doc/board-collapsed-expanded.markdown b/doc/board-collapsed-expanded.markdown
index e094e817..594676bc 100644
--- a/doc/board-collapsed-expanded.markdown
+++ b/doc/board-collapsed-expanded.markdown
@@ -2,7 +2,7 @@ Collapsed and Expanded mode
===========================
Tasks on the board can be displayed in collapsed or in expanded mode.
-Switching from one view to another can be done with the keyboard shortcut **"s"** or by using the dropdown menu on the left.
+Switching from one view to another can be done with the keyboard shortcut **"s"** or by using the drop-down menu on the left.
Collapsed mode
--------------
diff --git a/doc/board-configuration.markdown b/doc/board-configuration.markdown
index 1c5ff51a..bdcc2463 100644
--- a/doc/board-configuration.markdown
+++ b/doc/board-configuration.markdown
@@ -7,7 +7,7 @@ Go to the menu **Settings**, then choose **Board settings** on the left.
### Task highlighting
-This feature display a shadow around the task when a task is moved recently.
+This feature displays a shadow around the task when a task is moved recently.
Set the value 0 to disable this feature, 2 days by default (172800 seconds).
@@ -15,10 +15,10 @@ Everything moved since 2 days will have shadow around the task.
### Refresh interval for public board
-When you share a board, the page will refresh automatically every 60 seconds by default.
+When you share a board, the page will refresh every 60 seconds automatically by default.
### Refresh interval for private board
-When your web browser is open on a board, Kanboard check every 10 seconds if something have been changed by someone else.
+When your web browser is open on a board, Kanboard checks every 10 seconds if something has been changed by someone else.
Technically this process is done by Ajax polling.
diff --git a/doc/board-horizontal-scrolling-and-compact-view.markdown b/doc/board-horizontal-scrolling-and-compact-view.markdown
index 5dc5dcc0..323d0de5 100644
--- a/doc/board-horizontal-scrolling-and-compact-view.markdown
+++ b/doc/board-horizontal-scrolling-and-compact-view.markdown
@@ -1,12 +1,12 @@
Horizontal scrolling and compact mode
=====================================
-When the board cannot fit on your screen, an horizontal scroll bar will appear at the bottom.
+When the board cannot fit on your screen, a horizontal scroll bar will appear at the bottom.
However, it's possible to switch to the compact the view to display all columns in your screen.
![Board in compact mode](http://kanboard.net/screenshots/documentation/board-compact-mode.png)
-Switching between horizontal scrolling and compact view can be done with the keyboard shortcut **"c"** or by using the dropdown menu on the top left.
+Switching between horizontal scrolling and compact view can be done with the keyboard shortcut **"c"** or by using the drop-down menu on the top left.
Note: It's possible that text overlaps in compact mode, that will be improved over the next releases. \ No newline at end of file
diff --git a/doc/board-show-hide-columns.markdown b/doc/board-show-hide-columns.markdown
index e459f555..40edc5d8 100644
--- a/doc/board-show-hide-columns.markdown
+++ b/doc/board-show-hide-columns.markdown
@@ -8,4 +8,4 @@ You can hide or display columns very easily on the board:
- To hide a column, just click on the column title
- To show a hidden column, click on the vertical title
-When a column is hidden the number of tasks is displayed at the top.
+When a column is hidden, the number of tasks is displayed at the top.
diff --git a/doc/bruteforce-protection.markdown b/doc/bruteforce-protection.markdown
index 633cfe87..a7bef45e 100644
--- a/doc/bruteforce-protection.markdown
+++ b/doc/bruteforce-protection.markdown
@@ -1,14 +1,14 @@
-Bruteforce Protection
-=====================
+Brute Force Protection
+======================
The brute force protection of Kanboard works at the user account level:
-- After 3 authentication failure for the same username, the login form show a captcha image to prevent automated bot tentatives.
+- After 3 authentication failure for the same username, the login form shows a captcha image to prevent automated bot tentatives.
- After 6 authentication failure, the user account is locked down for a period of 15 minutes.
This feature works only for authentication methods that use the login form.
-However, **after 3 authentication failure through the user API**, the account have to be unlocked by using the login form.
+However, **after three authentication failure through the user API**, the account has to be unlocked by using the login form.
Kanboard doesn't block any IP addresses since bots can use several anonymous proxies. However, you can use external tools like [fail2ban](http://www.fail2ban.org) to avoid massive scans.
@@ -21,6 +21,6 @@ define('BRUTEFORCE_CAPTCHA', 3);
// Lock the account after 6 authentication failure
define('BRUTEFORCE_LOCKDOWN', 6);
-// Lock account duration in minute
+// Lock account duration in minutes
define('BRUTEFORCE_LOCKDOWN_DURATION', 15);
```
diff --git a/doc/calendar-configuration.markdown b/doc/calendar-configuration.markdown
index 95e13e52..438ba37a 100644
--- a/doc/calendar-configuration.markdown
+++ b/doc/calendar-configuration.markdown
@@ -13,7 +13,7 @@ There are two different calendars in Kanboard:
Project calendar
----------------
-This calendar show tasks with defined due date and tasks based on the creation date or the start date.
+This calendar shows tasks with defined due date and tasks based on the creation date or the start date.
### Show tasks based on the creation date
@@ -30,13 +30,13 @@ This calendar show tasks with defined due date and tasks based on the creation d
User calendar
-------------
-This calendar show only tasks assigned to the user and optionally subtasks information.
+This calendar shows only tasks assigned to the user and optionally sub-tasks information.
-### Show subtasks based on the time tracking
+### Show sub-tasks based on the time tracking
-- Display subtasks in the calendar from the information recorded in the time tracking table.
+- Display sub-tasks in the calendar from the information recorded in the time tracking table.
- The intersection with the user timetable is also calculated.
-### Show subtask estimates (forecast of future work)
+### Show sub-task estimates (forecast of future work)
-- Display the estimate of future work for subtasks in status "todo" and with a defined "estimate" value.
+- Display the estimate of future work for sub-tasks in status "todo" and with a defined "estimate" value.
diff --git a/doc/calendar.markdown b/doc/calendar.markdown
index 7b95baa2..6a790b1e 100644
--- a/doc/calendar.markdown
+++ b/doc/calendar.markdown
@@ -6,15 +6,15 @@ There are two different views for the calendar:
- The project view with filters (available from the board)
- The user view (available from the dashboard and from the user section)
-At this time the calendar is able to display these information:
+At this time the calendar is able to display this information:
- Tasks with a due date, displayed at the top. **The due date can be changed by moving the task to another day**.
- Tasks based on the creation date or the start date. **These events cannot be modified with the calendar**.
-- Subtask time tracking, all recorded time slot will be shown in the calendar.
-- Subtask estimates, forecast of work left
+- Sub-task time tracking, all recorded time slot will be shown in the calendar.
+- Sub-task estimates, forecasts of work left
![Calendar](http://kanboard.net/screenshots/documentation/calendar.png)
The calendar configuration can be changed in the settings page.
-Note: The due date doesn't contains time information.
+Note: The due date doesn't contain time information.
diff --git a/doc/centos-installation.markdown b/doc/centos-installation.markdown
index 6cfc31ff..576119b4 100644
--- a/doc/centos-installation.markdown
+++ b/doc/centos-installation.markdown
@@ -1,6 +1,8 @@
Centos Installation
===================
+Note: Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
+
Centos 7
--------
@@ -40,7 +42,7 @@ Be sure to configure your server to allow Kanboard to send emails and make exter
setsebool -P httpd_can_network_connect=1
```
-Allowing external connections is necessary if you use LDAP, SMTP, Webhooks or any third-party integrations.
+Allowing external connections is necessary if you use LDAP, SMTP, Web hooks or any third-party integration.
Centos 6.x
----------
diff --git a/doc/cli.markdown b/doc/cli.markdown
index 38cba496..9334d84b 100644
--- a/doc/cli.markdown
+++ b/doc/cli.markdown
@@ -1,10 +1,10 @@
Command Line Interface
======================
-Kanboard provide a simple command line interface that can be used from any Unix terminal.
+Kanboard provides a simple command line interface that can be used from any Unix terminal.
This tool can be used only on the local machine.
-This feature is useful to run commands outside the web server process by example running a huge report.
+This feature is useful to run commands outside of the web server processes.
Usage
-----
@@ -28,6 +28,7 @@ Options:
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
+ cronjob Execute daily cronjob
help Displays help for a command
list Lists commands
export
@@ -42,6 +43,8 @@ Available commands:
notification:overdue-tasks Send notifications for overdue tasks
projects
projects:daily-stats Calculate daily statistics for all projects
+ trigger
+ trigger:tasks Trigger scheduler event for all tasks
```
Available commands
@@ -116,7 +119,7 @@ Emails will be sent to all users with notifications enabled.
You can also display the overdue tasks with the flag `--show`:
```bash
-$ ./kanboard notification:overdue-tasks --show
+./kanboard notification:overdue-tasks --show
+-----+---------+------------+------------+--------------+----------+
| Id | Title | Due date | Project Id | Project name | Assignee |
+-----+---------+------------+------------+--------------+----------+
@@ -125,20 +128,22 @@ $ ./kanboard notification:overdue-tasks --show
+-----+---------+------------+------------+--------------+----------+
```
-Cronjob example:
-
-```bash
-# Everyday at 8am we check for due tasks
-0 8 * * * cd /path/to/kanboard && ./kanboard notification:overdue-tasks >/dev/null 2>&1
-```
-
### Run daily project stats calculation
-You can add a background task to calculate the project statistics everyday:
+This command calculate the statistics of each project:
```bash
-$ ./kanboard projects:daily-stats
+./kanboard projects:daily-stats
Run calculation for Project #0
Run calculation for Project #1
Run calculation for Project #10
```
+
+### Trigger for tasks
+
+This command send a "daily cronjob event" to all open tasks of each project.
+
+```bash
+./kanboard trigger:tasks
+Trigger task event: project_id=2, nb_tasks=1
+```
diff --git a/doc/closing-tasks.markdown b/doc/closing-tasks.markdown
index 018acace..dc3ef134 100644
--- a/doc/closing-tasks.markdown
+++ b/doc/closing-tasks.markdown
@@ -3,14 +3,14 @@ Closing tasks
When a task is closed, it is hidden from the board.
-However, you can always access to the list of closed tasks by using the query **status:closed** in any search form or simply choose **Closed tasks** from the filter dropdown.
+However, you can always access to the list of closed tasks by using the query **status:closed** in any search form or simply choose **Closed tasks** from the filter drop-down.
-There are two different ways to close a task, from the task dropdown menu on the board:
+There are two different ways to close a task, from the task drop-down menu on the board:
-![Close a task from dropdown menu](http://kanboard.net/screenshots/documentation/menu-close-task.png)
+![Close a task from drop-down menu](http://kanboard.net/screenshots/documentation/menu-close-task.png)
Or from the task sidebar menu in the task detail view:
-![Close a task](http://kanboard.net/screenshots/documentation/closing-tasks.png)
+![Close task](http://kanboard.net/screenshots/documentation/closing-tasks.png)
-Note: When you close a task, all subtasks not completed will be changed to the status "Done".
+Note: When you close a task, all sub-tasks not completed will be changed to the status "Done".
diff --git a/doc/coding-standards.markdown b/doc/coding-standards.markdown
index 4d3db814..0ee3ecd6 100644
--- a/doc/coding-standards.markdown
+++ b/doc/coding-standards.markdown
@@ -11,7 +11,7 @@ PHP code
- Always write PHPdoc comments for methods and class properties
- Coding style: [PSR-1](http://www.php-fig.org/psr/psr-1/) and [PSR-2](http://www.php-fig.org/psr/psr-2/)
-Javascript code
+JavaScript code
---------------
- Indentation: 4 spaces
diff --git a/doc/config.markdown b/doc/config.markdown
index f375b2fc..150cb6dc 100644
--- a/doc/config.markdown
+++ b/doc/config.markdown
@@ -1,8 +1,8 @@
Config file
===========
-You can customize the default settings of Kanboard by adding a file `config.php` at the project root.
-You can also rename the `config.default.php` and change the desired values.
+You can customize the default settings of Kanboard by adding a file `config.php` at the project root or in the `data` folder.
+You can also rename the file `config.default.php` to `config.php` and change the desired values.
Enable/Disable debug mode
-------------------------
@@ -102,87 +102,76 @@ define('LDAP_SERVER', '');
// LDAP server port (389 by default)
define('LDAP_PORT', 389);
-// By default, require certificate to be verified for ldaps:// style URL. Set to false to skip the verification.
+// By default, require certificate to be verified for ldaps:// style URL. Set to false to skip the verification
define('LDAP_SSL_VERIFY', true);
// Enable LDAP START_TLS
define('LDAP_START_TLS', false);
-// LDAP bind type: "anonymous", "user" (use the given user/password from the form) and "proxy" (a specific user to browse the LDAP directory)
+// By default Kanboard lowercase the ldap username to avoid duplicate users (the database is case sensitive)
+// Set to true if you want to preserve the case
+define('LDAP_USERNAME_CASE_SENSITIVE', false);
+
+// LDAP bind type: "anonymous", "user" or "proxy"
define('LDAP_BIND_TYPE', 'anonymous');
-// LDAP username to connect with. null for anonymous bind (by default).
-// Or for user bind type, you can use a pattern: %s@kanboard.local
+// LDAP username to use with proxy mode
+// LDAP username pattern to use with user mode
define('LDAP_USERNAME', null);
-// LDAP password to connect with. null for anonymous bind (by default).
+// LDAP password to use for proxy mode
define('LDAP_PASSWORD', null);
-// LDAP account base, i.e. root of all user account
-// Example: ou=People,dc=example,dc=com
-define('LDAP_ACCOUNT_BASE', '');
+// LDAP DN for users
+// Example for ActiveDirectory: CN=Users,DC=kanboard,DC=local
+// Example for OpenLDAP: ou=People,dc=example,dc=com
+define('LDAP_USER_BASE_DN', '');
-// LDAP query pattern to use when searching for a user account
+// LDAP pattern to use when searching for a user account
// Example for ActiveDirectory: '(&(objectClass=user)(sAMAccountName=%s))'
// Example for OpenLDAP: 'uid=%s'
-define('LDAP_USER_PATTERN', '');
-
-// Name of an attribute of the user account object which should be used as the full name of the user.
-define('LDAP_ACCOUNT_FULLNAME', 'displayname');
+define('LDAP_USER_FILTER', '');
-// Name of an attribute of the user account object which should be used as the email of the user.
-define('LDAP_ACCOUNT_EMAIL', 'mail');
-
-// Name of an attribute of the user account object which should be used as the id of the user.
+// LDAP attribute for username
// Example for ActiveDirectory: 'samaccountname'
// Example for OpenLDAP: 'uid'
-define('LDAP_ACCOUNT_ID', 'samaccountname');
-
-// LDAP Attribute for group membership
-define('LDAP_ACCOUNT_MEMBEROF', 'memberof');
-
-// DN for administrators
-// Example: CN=Kanboard Admins,CN=Users,DC=kanboard,DC=local
-define('LDAP_GROUP_ADMIN_DN', '');
-
-// DN for project administrators
-// Example: CN=Kanboard Project Admins,CN=Users,DC=kanboard,DC=local
-define('LDAP_GROUP_PROJECT_ADMIN_DN', '');
+define('LDAP_USER_ATTRIBUTE_USERNAME', 'uid');
-// By default Kanboard lowercase the ldap username to avoid duplicate users (the database is case sensitive)
-// Set to true if you want to preserve the case
-define('LDAP_USERNAME_CASE_SENSITIVE', false);
+// LDAP attribute for user full name
+// Example for ActiveDirectory: 'displayname'
+// Example for OpenLDAP: 'cn'
+define('LDAP_USER_ATTRIBUTE_FULLNAME', 'cn');
-// Automatically create user account
-define('LDAP_ACCOUNT_CREATION', true);
-```
+// LDAP attribute for user email
+define('LDAP_USER_ATTRIBUTE_EMAIL', 'mail');
-Google Authentication settings
-------------------------------
+// LDAP attribute to find groups in user profile
+define('LDAP_USER_ATTRIBUTE_GROUPS', 'memberof');
-```php
-// Enable/disable Google authentication
-define('GOOGLE_AUTH', false);
+// Allow automatic LDAP user creation
+define('LDAP_USER_CREATION', true);
-// Google client id (Get this value from the Google developer console)
-define('GOOGLE_CLIENT_ID', '');
+// LDAP DN for administrators
+// Example: CN=Kanboard-Admins,CN=Users,DC=kanboard,DC=local
+define('LDAP_GROUP_ADMIN_DN', '');
-// Google client secret key (Get this value from the Google developer console)
-define('GOOGLE_CLIENT_SECRET', '');
-```
+// LDAP DN for managers
+// Example: CN=Kanboard Managers,CN=Users,DC=kanboard,DC=local
+define('LDAP_GROUP_MANAGER_DN', '');
-Github Authentication settings
-------------------------------
+// Enable LDAP group provider for project permissions
+// The end-user will be able to browse LDAP groups from the user interface and allow access to specified projects
+define('LDAP_GROUP_PROVIDER', false);
-```php
-// Enable/disable GitHub authentication
-define('GITHUB_AUTH', false);
+// LDAP Base DN for groups
+define('LDAP_GROUP_BASE_DN', '');
-// GitHub client id (Copy it from your settings -> Applications -> Developer applications)
-define('GITHUB_CLIENT_ID', '');
+// LDAP group filter
+// Example for ActiveDirectory: (&(objectClass=group)(sAMAccountName=%s*))
+define('LDAP_GROUP_FILTER', '');
-// GitHub client secret key (Copy it from your settings -> Applications -> Developer applications)
-define('GITHUB_CLIENT_SECRET', '');
+// LDAP attribute for the group name
+define('LDAP_GROUP_ATTRIBUTE_NAME', 'cn');
```
Reverse-Proxy Authentication settings
@@ -277,4 +266,10 @@ define('API_AUTHENTICATION_HEADER', '');
// Hide login form, useful if all your users use Google/Github/ReverseProxy authentication
define('HIDE_LOGIN_FORM', false);
+
+// Disabling logout (for external SSO authentication)
+define('DISABLE_LOGOUT', false);
+
+// Override API token stored in the database, useful for automated tests
+define('API_AUTHENTICATION_TOKEN', 'My unique API Token');
```
diff --git a/doc/contributing.markdown b/doc/contributing.markdown
index 63c56782..088d2cce 100644
--- a/doc/contributing.markdown
+++ b/doc/contributing.markdown
@@ -4,7 +4,7 @@ Contributor Guidelines
How can I help?
---------------
-Kanboard is not perfect but there is many ways to help:
+Kanboard is not perfect but there are many ways to help:
- Give feedback
- Report bugs
@@ -18,11 +18,11 @@ Before doing any large undertaking, open a new issue and explain your proposal.
I want to give feedback
-----------------------
-- You think something should be improved (user interface, feature request)
+- You think something should be improved (user interface, feature requests)
- Check if your idea is not already proposed
- Open a new issue
- Describe your idea
-- You can also up vote with +1 on existing proposals
+- You can also up-vote with +1 on existing proposals
I want to report a bug
----------------------
@@ -31,7 +31,7 @@ I want to report a bug
- Open a new ticket
- Explain what is broken
- Describe how to reproduce the bug
-- Describe your environment (Kanboard version, OS, web server, PHP version, database version, hosting type)
+- Describe your environment (Kanboard version, OS, web server, PHP version, database version, hosting provider)
I want to translate Kanboard
----------------------------
@@ -44,18 +44,19 @@ I want to improve the documentation
- You think something is not clear, there is grammatical errors, typo errors, anything.
- The documentation is written in Markdown and stored in the folder `docs`.
-- Edit the file and send a pull-request.
+- Edit the files and send a pull-request.
- The documentation on the official website is synchronized with the repository.
I want to contribute to the code
--------------------------------
-Pull-requests are always welcome, however to be accepted you have to follow those directives:
+Pull-requests are always welcome however, to be accepted you have to follow those directives:
- **Before doing any large change or design proposal, open a new ticket to start a discussion.**
- If you want to add a new feature, respect the philosophy behind Kanboard. **We focus on simplicity**, we don't want to have a bloated software.
- The same apply for the user interface, **simplicity and efficiency**.
-- Send only one pull-request per feature or bug fix, your patch will be merged into one single commit in the master branch.
+- Send only one pull-request per feature or bug fix.
+- A smaller pull-request is easier to review and faster it will be merged.
- Make sure the [unit tests pass](tests.markdown).
- Respect the [coding standards](coding-standards.markdown).
- Write maintainable code, avoid code duplication, use PHP good practices.
diff --git a/doc/create-tasks-by-email.markdown b/doc/create-tasks-by-email.markdown
index 46dae480..b46e5797 100644
--- a/doc/create-tasks-by-email.markdown
+++ b/doc/create-tasks-by-email.markdown
@@ -2,12 +2,13 @@ Create tasks by email
=====================
You can create tasks directly by sending an email.
+This feature is available by using plugins.
At the moment, Kanboard is integrated with 3 external services:
-- [Mailgun](http://kanboard.net/documentation/mailgun)
-- [Sendgrid](http://kanboard.net/documentation/sendgrid)
-- [Postmark](http://kanboard.net/documentation/postmark)
+- [Mailgun](https://github.com/kanboard/plugin-mailgun)
+- [Sendgrid](https://github.com/kanboard/plugin-sendgrid)
+- [Postmark](https://github.com/kanboard/plugin-postmark)
These services handle incoming emails without having to configure any SMTP server.
@@ -17,18 +18,18 @@ All complicated works are already handled by those services.
Incoming emails workflow
------------------------
-1. You send an email to a specific address, by example **something+myproject@inbound.mydomain.tld**
+1. You send an email to a specific address, for example **something+myproject@inbound.mydomain.tld**
2. Your email is forwarded to the third-party SMTP servers
-3. The SMTP provider call the Kanboard webhook with the email in JSON or multipart/form-data formats
-4. Kanboard parse the received email and create the task to the right project
+3. The SMTP provider call the Kanboard web hook with the email in JSON or multipart/form-data formats
+4. Kanboard parses the received email and create the task to the right project
Note: New tasks are automatically created in the first column.
Email format
------------
-- The local part of the email address must use the plus separator, by example **kanboard+project123**
-- The string defined after the plus sign must match a project identifier, by example **project123** is the identifier of the project **Project 123**
+- The local part of the email address must use the plus separator, for example **kanboard+project123**
+- The string defined after the plus sign must match a project identifier, for example **project123** is the identifier of the project **Project 123**
- The email subject becomes the task title
- The email body becomes the task description (Markdown format)
@@ -38,7 +39,7 @@ Incoming emails can be written in text or HTML formats.
Security and requirements
-------------------------
-- The Kanboard webhook is protected by a random token
+- The Kanboard web hook is protected by a random token
- The sender email address must match a Kanboard user
-- The Kanboard project must have a unique identifier, by example **MYPROJECT**
-- The Kanboard user must be member of the project
+- The Kanboard project must have a unique identifier, for example **MYPROJECT**
+- The Kanboard user must be a member of the project
diff --git a/doc/creating-projects.markdown b/doc/creating-projects.markdown
index 6177d161..a87dbca0 100644
--- a/doc/creating-projects.markdown
+++ b/doc/creating-projects.markdown
@@ -1,9 +1,9 @@
-Creating projects
+Creating Projects
=================
-Kanboard can handle multiple projects. There are two kinds of project:
+Kanboard can handle multiple projects. There are two kinds of projects:
-- Project with mutliple users (you work in team)
+- Team projects
- Private project for a single user
Creating projects for multiple users
diff --git a/doc/creating-tasks.markdown b/doc/creating-tasks.markdown
index afcc5ecb..a4eca794 100644
--- a/doc/creating-tasks.markdown
+++ b/doc/creating-tasks.markdown
@@ -1,4 +1,4 @@
-Creating tasks
+Creating Tasks
==============
From the board, click on the plus sign next to the column name:
@@ -13,14 +13,14 @@ The only mandatory field is the title.
Field description:
-- **Title**: The title of your task, that will be displayed on the board.
+- **Title**: The title of your task, which will be displayed on the board.
- **Description**: Allow you to add more information about the task, the content can be written in [Markdown](http://kanboard.net/documentation/syntax-guide).
-- **Create another task**: Check this box if you want to create a similar task (fields will be prefilled).
+- **Create another task**: Check this box if you want to create a similar task (some fields will be pre-filled).
- **Assignee**: The person that will work on the task.
-- **Category**: Only one category can be assign to a task.
+- **Category**: Only one category can be assigned to a task.
- **Column**: The column where the task will be created, your task will be positioned at the bottom.
- **Color**: Choose the color of the card.
-- **Complexity**: Used in agile project management (Scrum), the complexity or story points is a number that tells the team how hard the story is. Often, people use the fibonacci series.
+- **Complexity**: Used in agile project management (Scrum), the complexity or story points is a number that tells the team how hard the story is. Often, people use the Fibonacci series.
- **Original Estimate**: Estimation in hours to complete the tasks.
- **Due Date**: Overdue tasks will have a red due date and upcoming due dates will be black on the board. Several date format are accepted in addition to the date picker.
diff --git a/doc/cronjob.markdown b/doc/cronjob.markdown
new file mode 100644
index 00000000..32f12888
--- /dev/null
+++ b/doc/cronjob.markdown
@@ -0,0 +1,32 @@
+Background Job Scheduling
+=========================
+
+To work properly, Kanboard requires that a background job run on a daily basis.
+Usually on Unix platforms, this process is done by `cron`.
+
+This background job is necessary for these features:
+
+- Reports and analytics (calculate daily stats of each projects)
+- Send overdue task notifications
+- Execute automatic actions connected to the event "Daily background job for tasks"
+
+Configuration on Unix and Linux platforms
+-----------------------------------------
+
+There are multiple ways to define a cronjob on Unix/Linux operating systems, this example is for Ubuntu 14.04.
+The procedure is similar to other systems.
+
+Edit the crontab of your web server user:
+
+```bash
+sudo crontab -u www-data -e
+```
+
+Example to execute the daily cronjob at 8am:
+
+```bash
+0 8 * * * cd /path/to/kanboard && ./kanboard cronjob >/dev/null 2>&1
+```
+
+Note: the cronjob process must have write access to the database in case you are using Sqlite.
+Usually, running the cronjob under the web server user is enough.
diff --git a/doc/currency-rate.markdown b/doc/currency-rate.markdown
index b959e4d1..c61c8977 100644
--- a/doc/currency-rate.markdown
+++ b/doc/currency-rate.markdown
@@ -1,7 +1,7 @@
Currency Rate
==============
-Since each user can have a predefined hourly rate in different currency.
+Since each user can have a pre-defined hourly rate in different currencies.
If you have to handle multiple currencies, you define here the rate according to the reference currency.
This feature is used for project budget calculation.
diff --git a/doc/custom-filters.markdown b/doc/custom-filters.markdown
index 18bf8584..cb55ab42 100644
--- a/doc/custom-filters.markdown
+++ b/doc/custom-filters.markdown
@@ -1,8 +1,8 @@
Custom Filters
==============
-Custom filters allow you to save any search query.
-In this way, you can extends easily the default filters and save most used search queries.
+Custom filters allow you to save any search query.
+In this way, you can extend the default filters easily and save most used search queries.
- Custom filters are stored by project and associated to the creator.
- If the creator is project manager, he can choose to share the filter with other project members.
@@ -10,10 +10,10 @@ In this way, you can extends easily the default filters and save most used searc
Filter creation
---------------
-Go to the action dropdown or in the project settings and choose **custom filters**:
+Go to the action drop-down or in the project settings and choose **custom filters**:
![Custom Filter Creation](http://kanboard.net/screenshots/documentation/custom-filter-creation.png)
-After creating your filter, it will appears on the board next to the default filters:
+After creating your filter, it will appear on the board next to the default filters:
![Custom Filter Dropdown](http://kanboard.net/screenshots/documentation/custom-filter-dropdown.png)
diff --git a/doc/debian-installation.markdown b/doc/debian-installation.markdown
index 147fe452..ec956049 100644
--- a/doc/debian-installation.markdown
+++ b/doc/debian-installation.markdown
@@ -1,6 +1,8 @@
How to install Kanboard on Debian?
==================================
+Note: Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
+
Debian 8 (Jessie)
-----------------
diff --git a/doc/docker.markdown b/doc/docker.markdown
index fe72a6c8..6bd966d3 100644
--- a/doc/docker.markdown
+++ b/doc/docker.markdown
@@ -2,7 +2,18 @@ How to run Kanboard with Docker?
================================
Kanboard can run easily with [Docker](https://www.docker.com).
-There is a `Dockerfile` in the repository to build your own container.
+
+The image size is approximately **50MB** and contains:
+
+- [Alpine Linux](http://alpinelinux.org/)
+- The [process manager S6](http://skarnet.org/software/s6/)
+- Nginx
+- PHP-FPM
+
+The Kanboard cronjob is also running everyday at midnight.
+URL rewriting is enabled in the included config file.
+
+When the container is running, the memory utilization is around **20MB**.
Use the stable version
----------------------
@@ -17,7 +28,7 @@ docker run -d --name kanboard -p 80:80 -t kanboard/kanboard:stable
Use the development version (automated build)
---------------------------------------------
-Every new commit on the repository trigger a new build on [Docker Hub](https://registry.hub.docker.com/u/kanboard/kanboard/).
+Every new commit on the repository trigger a new build on the [Docker Hub](https://registry.hub.docker.com/u/kanboard/kanboard/).
```bash
docker pull kanboard/kanboard
@@ -29,31 +40,45 @@ The tag **latest** is the **development version** of Kanboard, use at your own r
Build your own Docker image
---------------------------
+There is a `Dockerfile` in the Kanboard repository to build your own image.
Clone the Kanboard repository and run the following command:
```bash
docker build -t youruser/kanboard:master .
```
-To run your image in background on the port 80:
+or
```bash
-docker run -d --name kanboard -p 80:80 -t youruser/kanboard:master
+make docker-image
```
-Store your data on a volume
----------------------------
-
-By default Kanboard will store attachments and the Sqlite database in the directory data. Run this command to use a custom volume path:
+To run your container in background on the port 80:
```bash
-docker run -d --name kanboard -v /your/local/data/folder:/var/www/html/data -p 80:80 -t kanboard/kanboard:master
+docker run -d --name kanboard -p 80:80 -t youruser/kanboard:master
```
+Volumes
+-------
+
+You can attach 2 volumes to your container:
+
+- Data folder: `/var/www/kanboard/data`
+- Plugins folder: `/var/www/kanboard/plugins`
+
+Use the flag `-v` to mount a volume on the host machine like described in [official Docker documentation](https://docs.docker.com/engine/userguide/containers/dockervolumes/).
+
+Config files
+------------
+
+- The container already include a custom config file located at `/var/www/kanboard/config.php`.
+- You can store your own config file on the data volume: `/var/www/kanboard/data/config.php`.
+
References
----------
- [Official Kanboard images](https://registry.hub.docker.com/u/kanboard/kanboard/)
- [Docker documentation](https://docs.docker.com/)
-- [Dockerfile stable version](https://github.com/kanboard/docker/blob/master/Dockerfile)
+- [Dockerfile stable version](https://github.com/kanboard/docker)
- [Dockerfile dev version](https://github.com/fguillot/kanboard/blob/master/Dockerfile)
diff --git a/doc/duplicate-move-tasks.markdown b/doc/duplicate-move-tasks.markdown
index dcb01df5..4451f1df 100644
--- a/doc/duplicate-move-tasks.markdown
+++ b/doc/duplicate-move-tasks.markdown
@@ -17,13 +17,13 @@ Go to the task view and choose **Duplicate to another project**.
![Task Duplication Another Project](http://kanboard.net/screenshots/documentation/task-duplication-another-project.png)
-Only projects where you are member will be shown in the dropdown.
+Only projects where you are members will be shown in the drop-down.
Before to copy the tasks, Kanboard will ask you the destination properties that are not common between the source and destination project.
Basically, you need to define:
-- The destination swimlane
+- The destination swim lane
- The column
- The category
- The assignee
diff --git a/doc/editing-projects.markdown b/doc/editing-projects.markdown
index 7e0fc8ed..781a7cc6 100644
--- a/doc/editing-projects.markdown
+++ b/doc/editing-projects.markdown
@@ -1,4 +1,4 @@
-Editing projects
+Editing Projects
================
Projects can be renamed and disabled at any time.
@@ -9,7 +9,7 @@ To rename a project, just click on the link "Edit project" on the left.
- The start date and end date are used to generate the project Gantt chart
- The description is visible as tooltip on the board and on the projects listing page
-- Administrators and project administrators can convert a private project to a multiple users project by changing the checkbox "Private project".
-- You can also convert a multiple users project to a private project.
+- Administrators and project administrators can convert a private project to multiple users project by changing the checkbox "Private project".
+- You can also convert multiple users project to a private project.
Note: When you make a project private, all existing users will still have access to the project. Adjust the list of users according to your needs.
diff --git a/doc/email-configuration.markdown b/doc/email-configuration.markdown
index 40736c6a..d643fa72 100644
--- a/doc/email-configuration.markdown
+++ b/doc/email-configuration.markdown
@@ -8,7 +8,7 @@ To receive email notifications, users of Kanboard must have:
- Activated notifications in their profile
- Have a valid email address in their profile
-- Be member of the project that will trigger notifications
+- Be a member of the project that will trigger notifications
Note: The logged user who performs the action doesn't receive any notifications, only other project members.
@@ -26,7 +26,7 @@ Server settings
---------------
By default, Kanboard will use the bundled PHP mail function to send emails.
-Usually that require no configuration if your server can already send emails.
+Usually that requires no configuration if your server can already send emails.
However, it's possible to use other methods, the SMTP protocol and Sendmail.
@@ -101,14 +101,14 @@ Examples:
Don't forget the ending slash `/`.
-You need to define that manually because Kanboard cannot guess the URL from a command line script and some people have very specific configuration.
+You need to define that manually because Kanboard cannot guess the URL from a command line script and some people have a very specific configuration.
Troubleshooting
---------------
-If no emails are send and you are sure that everything is configured correctly:
+If no emails are sent and you are sure that everything is configured correctly:
- Check your spam folder
- Enable the debug mode and check the debug file `data/debug.log`, you should see the exact error
-- Be sure that your server or your hosting provider allow you to send emails
+- Be sure that your server or your hosting provider allows you to send emails
- If you use SeLinux, allow PHP to send emails
diff --git a/doc/faq.markdown b/doc/faq.markdown
index 3d542a30..953f98e1 100644
--- a/doc/faq.markdown
+++ b/doc/faq.markdown
@@ -10,25 +10,25 @@ Kanboard works well with any great VPS hosting provider such as [Digital Ocean](
To have the best performances, choose a provider with fast disk I/O because Kanboard use Sqlite by default.
Avoid hosting providers that use a shared NFS mount point.
+
I get a blank page after installing or upgrading Kanboard
---------------------------------------------------------
- Check if you have installed all requirements on your server
-- Check if the files have the correct permissions
-- If you use php-fpm and opcode caching, reload the process to be sure to clear the cache
-- Enable PHP error logging in your php.ini
-- Check the PHP and Apache error logs you should see the exact error
+- Check the PHP and Apache error logs
+- Check if the files have the correct permission
+- If you use an aggressive OPcode caching, reload your web-server or php-fpm
-Page not found and the url seems wrong (&amp;amp;)
-----------------------------------------------
+Page not found and the URL seems wrong (&amp;amp;)
+--------------------------------------------------
-- The url looks like `/?controller=auth&amp;action=login&amp;redirect_query=` instead of `?controller=auth&action=login&redirect_query=`
+- The URL looks like `/?controller=auth&amp;action=login&amp;redirect_query=` instead of `?controller=auth&action=login&redirect_query=`
- Kanboard returns a "Page not found" error
-This issue come from your PHP configuration, the value of `arg_separator.output` is not the PHP's default, there is different ways to fix that:
+This issue comes from your PHP configuration, the value of `arg_separator.output` is not the PHP's default, there is different ways to fix that:
-Change the value directly in your `php.ini` if you have the permission:
+Change the value directly in your `php.ini` if you can:
```
arg_separator.output = "&"
@@ -62,12 +62,12 @@ We recommend to switch to the last version of PHP because it's bundled with [OPc
Why the minimum requirement is PHP 5.3.3?
-----------------------------------------
-Kanboard use the function `password_hash()` to crypt passwords but it's available only for PHP >= 5.5.
+Kanboard uses the function `password_hash()` to crypt passwords but it's available only for PHP >= 5.5.
-However, there is a backport for [older versions of PHP](https://github.com/ircmaxell/password_compat#requirements).
-This library require at least PHP 5.3.7 to work correctly.
+However, there is a back-port for [older versions of PHP](https://github.com/ircmaxell/password_compat#requirements).
+This library requires at least PHP 5.3.7 to work correctly.
-Apparently, Centos and Debian backports security patches so PHP 5.3.3 should be ok.
+Apparently, Centos and Debian back-ports security patches so PHP 5.3.3 should be ok.
Kanboard v1.0.10 and v1.0.11 requires at least PHP 5.3.7 but this change has been reverted to be compatible with PHP 5.3.3 with Kanboard >= v1.0.12
@@ -85,20 +85,6 @@ open http://localhost:8000/
```
-How to migrate my tasks from Wunderlist?
-----------------------------------------
-
-You can use an external tool to import automatically your tasks and lists from Wunderlist to Kanboard.
-
-This is a command line script made by a contributor of Kanboard.
-It's simple, quick and dirty but it works :)
-
-More information here:
-
-- [Wunderlist](http://www.wunderlist.com/)
-- <https://github.com/EpocDotFr/WunderlistToKanboard>
-
-
How to install Kanboard on Yunohost?
------------------------------------
@@ -107,6 +93,18 @@ How to install Kanboard on Yunohost?
There is a [package to install Kanboard on Yunohost easily](https://github.com/mbugeia/kanboard_ynh).
+Where can I find a list of related projects?
+--------------------------------------------
+
+- [Kanboard API python client by @freekoder](https://github.com/freekoder/kanboard-py)
+- [Kanboard Presenter by David Eberlein](https://github.com/davideberlein/kanboard-presenter)
+- [CSV2Kanboard by @ashbike](https://github.com/ashbike/csv2kanboard)
+- [Kanboard for Yunohost by @mbugeia](https://github.com/mbugeia/kanboard_ynh)
+- [Trello import script by @matueranet](https://github.com/matueranet/kanboard-import-trello)
+- [Chrome extension by Timo](https://chrome.google.com/webstore/detail/kanboard-quickmenu/akjbeplnnihghabpgcfmfhfmifjljneh?utm_source=chrome-ntp-icon), [Source code](https://github.com/BlueTeck/kanboard_chrome_extension)
+- [Python client script by @dzudek](https://gist.github.com/fguillot/84c70d4928eb1e0cb374)
+
+
Are there some tutorials about Kanboard in other languages?
-----------------------------------------------------------
diff --git a/doc/fr/analytics-tasks.markdown b/doc/fr/analytics-tasks.markdown
index bef7377b..9fef5709 100644
--- a/doc/fr/analytics-tasks.markdown
+++ b/doc/fr/analytics-tasks.markdown
@@ -11,9 +11,9 @@ Lead et cycle time
- Le lead time est la durée entre la création de la tâche et son achèvement (tâche fermée).
- Le cycle time est la durée entre la date de début et l'achèvement.
- Si la tâche n’est pas fermée, l’heure courante est utilisée à la place de la date d'achèvement.
-- Si la date de départ n\'est pas spécifiée, le cycle time n'est pas calculé.
+- Si la date de départ n'est pas spécifiée, le cycle time n'est pas calculé.
-Remarque : vous pouvez configurer une action pour définir automatiquement que la date de départ sera le moment ou vous déplacez une tâche vers une colonne de votre choix
+Remarque : vous pouvez configurer une action pour définir automatiquement que la date de départ sera le moment où vous déplacez une tâche vers une colonne de votre choix
Temps passé dans chaque colonne
---------------------------
diff --git a/doc/fr/duplicate-move-tasks.markdown b/doc/fr/duplicate-move-tasks.markdown
index 6d8e269e..19c86847 100644
--- a/doc/fr/duplicate-move-tasks.markdown
+++ b/doc/fr/duplicate-move-tasks.markdown
@@ -4,7 +4,7 @@ Dupliquer et déplacer des tâches
Dupliquer une tâche dans le même projet
--------------------------------------
-Allez à la vue des par tâche et choisissez **Dupliquer** sur la gauche.
+Allez à la vue par tâche et choisissez **Dupliquer** sur la gauche.
![Duplication de tâche](http://kanboard.net/screenshots/documentation/task-duplication.png)
diff --git a/doc/fr/notifications.markdown b/doc/fr/notifications.markdown
index 2190f317..f49b6495 100644
--- a/doc/fr/notifications.markdown
+++ b/doc/fr/notifications.markdown
@@ -6,7 +6,7 @@ Kanboard est capable d'envoyer des notifications via différents canaux :
- Email
- Web (Liste de message non lus)
-Vous pouvez ajouter d'autres cannaux an ajoutant des extensions comme par exemple Hipchat, Slack ou encore Jabber.
+Vous pouvez ajouter d'autres canaux en ajoutant des extensions comme par exemple Hipchat, Slack ou encore Jabber.
Configuration
--------------
@@ -34,12 +34,12 @@ Vous pouvez aussi sélectionner certain projets, par défaut tous les projets do
Notifications web
-----------------
-Les notifications web sont accéssibles depuis le tableau de bord ou depuis l'icône en haut de la page :
+Les notifications web sont accessibles depuis le tableau de bord ou depuis l'icône en haut de la page :
![Icône des notifications web](http://kanboard.net/screenshots/documentation/web-notifications-icon.png)
-Les notifications sont affichés sous forme de liste. Vous pouvez marquer comme lu chacune d'entre-elle ou toutes en même temps.
+Les notifications sont affichées sous forme de liste. Vous pouvez marquer comme lu chacune d'entre-elle ou toutes en même temps.
![Notifications web](http://kanboard.net/screenshots/documentation/web-notifications.png)
-Avec cette méthode vous pouvez quand même rester avertis de ce que se passe sans pour autant être innonder d'emails.
+Avec cette méthode vous pouvez quand même rester avertis de ce que se passe sans pour autant être inondé d'emails.
diff --git a/doc/fr/recurring-tasks.markdown b/doc/fr/recurring-tasks.markdown
index 98759a98..623f12bf 100644
--- a/doc/fr/recurring-tasks.markdown
+++ b/doc/fr/recurring-tasks.markdown
@@ -3,7 +3,7 @@ Tâches récurrentes
Pour convenir à ma méthodologie de Kanban, les tâches récurrentes ne sont pas basées sur une date mais sur les évènements du tableau.
-- Les tâches récurrentes sont dupliquée dans la première colonne du tableau quand les évènements sélectionnés se produisent
+- Les tâches récurrentes sont dupliquées dans la première colonne du tableau quand les évènements sélectionnés se produisent
- La date d'échéance peut être automatiquement recalculée
- Chaque tâche enregistre l'identifiant de tâche de la tâche parente qui l'a créée et la tâche enfant qui a été créée.
diff --git a/doc/fr/time-tracking.markdown b/doc/fr/time-tracking.markdown
index 0b722c63..be94053b 100644
--- a/doc/fr/time-tracking.markdown
+++ b/doc/fr/time-tracking.markdown
@@ -1,7 +1,7 @@
Suivi du temps
=============
-Les information de la feuille de suivi du temps peuvent être définies au niveau des tâches ou des sous-tâches
+Les informations de la feuille de suivi du temps peuvent être définies au niveau des tâches ou des sous-tâches
Suivi de temps des tâches
------------------
diff --git a/doc/fr/transitions.markdown b/doc/fr/transitions.markdown
index 72106a64..253d090e 100644
--- a/doc/fr/transitions.markdown
+++ b/doc/fr/transitions.markdown
@@ -10,7 +10,7 @@ Depuis la page des tâches, vous pouvez accéder à ces informations:
- Date de l'action
- Colonne d'origine
- Colonne de destination
-- Exécuter (Pour l'utilisateur qui a déplacé la tâche)
+- Exécutant (Pour l'utilisateur qui a déplacé la tâche)
- Temps passé sur la colonne d’origine
Les données de transition entre les tâches peuvent aussi être exportées depuis la page des paramètres du projet
diff --git a/doc/freebsd-installation.markdown b/doc/freebsd-installation.markdown
index 84b35ad8..7b36dff1 100644
--- a/doc/freebsd-installation.markdown
+++ b/doc/freebsd-installation.markdown
@@ -55,7 +55,7 @@ Generally 3 elements have to be installed:
Fetch and extract ports...
```bash
-$ portsnap fetch
+$ portsnap fetch
$ portsnap extract
```
@@ -122,6 +122,7 @@ there is no need to install it manually.
Please note
-----------
-Port is being hosted on [bitbucket](https://bitbucket.org/if0/freebsd-kanboard/). Feel free to comment,
+- Port is being hosted on [bitbucket](https://bitbucket.org/if0/freebsd-kanboard/). Feel free to comment,
fork and suggest updates!
- \ No newline at end of file
+- Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
+
diff --git a/doc/gantt-chart-projects.markdown b/doc/gantt-chart-projects.markdown
index 4dfe95fb..536443e6 100644
--- a/doc/gantt-chart-projects.markdown
+++ b/doc/gantt-chart-projects.markdown
@@ -1,10 +1,10 @@
-Gantt chart for all projects
+Gantt Chart for all projects
============================
The goal of this Gantt chart is to display an overview of all projects based on the start and end dates.
- This Gantt chart is available in the project management section
-- Only project administrators and administrators can access to this section
+- Only project administrators and administrators can access this section
- Project administrators will see only projects where they are members
- Private projects are not shown on this chart
@@ -14,4 +14,4 @@ The goal of this Gantt chart is to display an overview of all projects based on
- Horizontal bars can be resized and moved horizontally with your mouse
- There is no vertical drag and drop
- Project bars are displayed in black when there is no start or end date defined
-- The information tooltip show the list of project managers and standard members
+- The information tooltip shows the list of project managers and standard members
diff --git a/doc/gantt-chart-tasks.markdown b/doc/gantt-chart-tasks.markdown
index ca6521ad..9943f573 100644
--- a/doc/gantt-chart-tasks.markdown
+++ b/doc/gantt-chart-tasks.markdown
@@ -4,7 +4,7 @@ Gantt chart for tasks
The goal of this Gantt chart is to display a time based overview of the tasks for a given project.
- The Gantt chart is available from the "view switcher"
-- Only project managers can access to this section
+- Only project managers can access this section
![Gantt Chart](http://kanboard.net/screenshots/documentation/gantt-chart-project.png)
@@ -13,7 +13,7 @@ The goal of this Gantt chart is to display a time based overview of the tasks fo
- There is no vertical drag and drop
- The bar is the same color as the task
- Each bar display a progression status in percentage, this percentage is calculated by using the column position on the board
-- To fit with the Kanban model, tasks can be ordered by the board positions or by start date
+- To fit with the Kanban model, tasks can be ordered by the board positions or by the start date
- New tasks created from this view will be displayed on the board at the position 1 in the first column
- Tasks are displayed in black when there is no start or due date defined
diff --git a/doc/github-authentication.markdown b/doc/github-authentication.markdown
deleted file mode 100644
index ba0f371f..00000000
--- a/doc/github-authentication.markdown
+++ /dev/null
@@ -1,80 +0,0 @@
-Github Authentication
-=====================
-
-Requirements
-------------
-
-OAuth Github API credentials (available in your [Settings > Applications > Developer applications](https://github.com/settings/applications))
-
-How does this work?
--------------------
-
-The Github authentication in Kanboard uses the [OAuth 2.0](http://oauth.net/2/) protocol, so any user of Kanboard can be linked to a Github account.
-
-That means you can use your Github account to login on Kanboard.
-
-How to link a Github account
-----------------------------
-
-1. Go to your user profile
-2. Click on **External accounts**
-3. Click on the link **Link my Github Account**
-4. You are redirected to the **Github Authorize application form**
-5. Authorize Kanboard by clicking on the button **Accept**
-6. Your account is now linked
-
-Now, on the login page you can be authenticated in one click with the link **Login with my Github Account**.
-
-Your name and email are automatically updated from your Github Account if defined.
-
-Installation instructions
--------------------------
-
-### Setting up OAuth 2.0
-
-- On Github, go to the page [Register a new OAuth application](https://github.com/settings/applications/new)
-- Just follow the [official Github documentation](https://developer.github.com/guides/basics-of-authentication/#registering-your-app)
-- In Kanboard, you can get the **callback url** in **Settings > Integrations > Github Authentication**
-
-### Setting up Kanboard
-
-Either create a new `config.php` file or rename the `config.default.php` file and set the following values:
-
-```php
-// Enable/disable Github authentication
-define('GITHUB_AUTH', true);
-
-// Github client id (Copy it from your settings -> Applications -> Developer applications)
-define('GITHUB_CLIENT_ID', 'YOUR_GITHUB_CLIENT_ID');
-
-// Github client secret key (Copy it from your settings -> Applications -> Developer applications)
-define('GITHUB_CLIENT_SECRET', 'YOUR_GITHUB_CLIENT_SECRET');
-```
-
-### Github Entreprise
-
-To use this authentication method with Github Enterprise you have to change the default urls.
-
-Replace these values by your self-hosted instance of Github:
-
-```php
-// Github oauth2 authorize url
-define('GITHUB_OAUTH_AUTHORIZE_URL', 'https://github.com/login/oauth/authorize');
-
-// Github oauth2 token url
-define('GITHUB_OAUTH_TOKEN_URL', 'https://github.com/login/oauth/access_token');
-
-// Github API url (don't forget the slash at the end)
-define('GITHUB_API_URL', 'https://api.github.com/');
-```
-
-Notes
------
-
-Kanboard uses these information from your public Github profile:
-
-- Full name
-- Public email address
-- Github unique id
-
-The Github unique id is used to link the local user account and the Github account.
diff --git a/doc/github-webhooks.markdown b/doc/github-webhooks.markdown
deleted file mode 100644
index a20b5a18..00000000
--- a/doc/github-webhooks.markdown
+++ /dev/null
@@ -1,99 +0,0 @@
-Github webhooks integration
-===========================
-
-Kanboard can be synchronized with Github.
-Currently, it's only a one-way synchronization: Github to Kanboard.
-
-Github webhooks are plugged to Kanboard automatic actions.
-When an event occurs on Github, an action can be performed on Kanboard.
-
-List of available events
-------------------------
-
-- Github commit received
-- Github issue opened
-- Github issue closed
-- Github issue reopened
-- Github issue assignee change
-- Github issue label change
-- Github issue comment created
-
-List of available actions
--------------------------
-
-- Create a task from an external provider
-- Change the assignee based on an external username
-- Change the category based on an external label
-- Create a comment from an external provider
-- Close a task
-- Open a task
-
-Configuration on Github
------------------------
-
-Go to your project settings page, on the left choose "Webhooks & Services", then click on the button "Add webhook".
-
-![Github configuration](http://kanboard.net/screenshots/documentation/github-webhooks.png)
-
-- **Payload url**: Copy and paste the link from the Kanboard project settings (section **Integrations > Github**).
-- Select **"Send me everything"**
-
-![Github webhook](http://kanboard.net/screenshots/documentation/kanboard-github-webhooks.png)
-
-Each time an event happens, Github will send an event to Kanboard now.
-The Kanboard webhook url is protected by a random token.
-
-Everything else is handled by automatic actions in your Kanboard project settings.
-
-Examples
---------
-
-### Close a Kanboard task when a commit pushed to Github
-
-- Choose the event: **Github commit received**
-- Choose the action: **Close the task**
-
-When one or more commits are sent to Github, Kanboard will receive the information, each commit message with a task number included will be closed.
-
-Example:
-
-- Commit message: "Fix bug #1234"
-- That will close the Kanboard task #1234
-
-### Create a Kanboard task when a new issue is opened on Github
-
-- Choose the event: **Github issue opened**
-- Choose the action: **Create a task from an external provider**
-
-When a task is created from a Github issue, the link to the issue is added to the description and the task have a new field named "Reference" (this is the Github ticket number).
-
-### Close a Kanboard task when an issue is closed on Github
-
-- Choose the event: **Github issue closed**
-- Choose the action: **Close the task**
-
-### Reopen a Kanboard task when an issue is reopened on Github
-
-- Choose the event: **Github issue reopened**
-- Choose the action: **Open the task**
-
-### Assign a task to a Kanboard user when an issue is assigned on Github
-
-- Choose the event: **Github issue assignee change**
-- Choose the action: **Change the assignee based on an external username**
-
-Note: The username must be the same between Github and Kanboard and the user must be member of the project.
-
-### Assign a category when an issue is tagged on Github
-
-- Choose the event: **Github issue label change**
-- Choose the action: **Change the category based on an external label**
-- Define the label and the category
-
-### Create a comment on Kanboard when an issue is commented on Github
-
-- Choose the event: **Github issue comment created**
-- Choose the action: **Create a comment from an external provider**
-
-If the username is the same between Github and Kanboard the comment author will be assigned, otherwise there is no author.
-The user also have to be member of the project in Kanboard.
diff --git a/doc/gitlab-authentication.markdown b/doc/gitlab-authentication.markdown
deleted file mode 100644
index 8d2f0000..00000000
--- a/doc/gitlab-authentication.markdown
+++ /dev/null
@@ -1,83 +0,0 @@
-Gitlab Authentication
-=====================
-
-Requirements
-------------
-
-- Account on [Gitlab.com](https://gitlab.com) or you own self-hosted Gitlab instance
-- Have Kanboard registered as application in Gitlab
-
-How does this work?
--------------------
-
-The Gitlab authentication in Kanboard uses the [OAuth 2.0](http://oauth.net/2/) protocol, so any user of Kanboard can be linked to a Gitlab account.
-
-That means you can use your Gitlab account to login on Kanboard.
-
-How to link a Gitlab account
-----------------------------
-
-1. Go to your user profile
-2. Click on **External accounts**
-3. Click on the link **Link my Gitlab Account**
-4. You are redirected to the **Gitlab authorization form**
-5. Authorize Kanboard by clicking on the button **Accept**
-6. Your account is now linked
-
-Now, on the login page you can be authenticated in one click with the link **Login with my Gitlab Account**.
-
-Your name and email are automatically updated from your Gitlab Account if defined.
-
-Installation instructions
--------------------------
-
-### Setting up OAuth 2.0
-
-- On Gitlab, register a new application by following the [official documentation](http://doc.gitlab.com/ce/integration/oauth_provider.html)
-- In Kanboard, you can get the **callback url** in **Settings > Integrations > Gitlab Authentication**, just copy and paste the url
-
-### Setting up Kanboard
-
-Either create a new `config.php` file or rename the `config.default.php` file and set the following values:
-
-```php
-// Enable/disable Gitlab authentication
-define('GITLAB_AUTH', true);
-
-// Gitlab application id
-define('GITLAB_CLIENT_ID', 'YOUR_APPLICATION_ID');
-
-// Gitlab application secret
-define('GITLAB_CLIENT_SECRET', 'YOUR_APPLICATION_SECRET');
-```
-
-### Custom endpoints for self-hosted Gitlab
-
-Change these default values if you use a self-hosted instance of Gitlab:
-
-```php
-// Gitlab oauth2 authorize url
-define('GITLAB_OAUTH_AUTHORIZE_URL', 'https://gitlab.com/oauth/authorize');
-
-// Gitlab oauth2 token url
-define('GITLAB_OAUTH_TOKEN_URL', 'https://gitlab.com/oauth/token');
-
-// Gitlab API url endpoint (don't forget the slash at the end)
-define('GITLAB_API_URL', 'https://gitlab.com/api/v3/');
-```
-
-Notes
------
-
-Kanboard uses these information from your Gitlab profile:
-
-- Full name
-- Email address
-- Gitlab unique id
-
-The Gitlab unique id is used to link the local user account and the Gitlab account.
-
-Known issues
-------------
-
-Gitlab OAuth will work only with url rewrite enabled. At the moment, Gitlab doesn't support callback url with query string parameters. See [Gitlab issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/2443)
diff --git a/doc/gitlab-webhooks.markdown b/doc/gitlab-webhooks.markdown
deleted file mode 100644
index 9d9ecaf5..00000000
--- a/doc/gitlab-webhooks.markdown
+++ /dev/null
@@ -1,65 +0,0 @@
-Gitlab webhooks
-===============
-
-Gitlab events can be connected to Kanboard automatic actions.
-
-List of supported events
-------------------------
-
-- Gitlab commit received
-- Gitlab issue opened
-- Gitlab issue closed
-- Gitlab issue comment created
-
-List of supported actions
--------------------------
-
-- Create a task from an external provider
-- Close a task
-- Create a comment from an external provider
-
-Configuration
--------------
-
-![Gitlab configuration](http://kanboard.net/screenshots/documentation/gitlab-webhooks.png)
-
-1. On Kanboard, go to the project settings and choose the section **Integrations**
-2. Copy the Gitlab webhook url
-3. On Gitlab, go to the project settings and go to the section **Webhooks**
-4. Check the boxes **Push Events**, **Comments** and **Issues Events**
-5. Paste the url and save
-
-Examples
---------
-
-### Close a Kanboard task when a commit pushed to Gitlab
-
-- Choose the event: **Gitlab commit received**
-- Choose the action: **Close the task**
-
-When one or more commits are sent to Gitlab, Kanboard will receive the information, each commit message with a task number included will be closed.
-
-Example:
-
-- Commit message: "Fix bug #1234"
-- That will close the Kanboard task #1234
-
-### Create a Kanboard task when a new issue is opened on Gitlab
-
-- Choose the event: **Gitlab issue opened**
-- Choose the action: **Create a task from an external provider**
-
-When a task is created from a Gitlab issue, the link to the issue is added to the description and the task have a new field named "Reference" (this is the Gitlab ticket number).
-
-### Close a Kanboard task when an issue is closed on Gitlab
-
-- Choose the event: **Gitlab issue closed**
-- Choose the action: **Close the task**
-
-### Create a comment on Kanboard when an issue is commented on Gitlab
-
-- Choose the event: **Gitlab issue comment created**
-- Choose the action: **Create a comment from an external provider**
-
-If the username is the same between Gitlab and Kanboard the comment author will be assigned, otherwise there is no author.
-The user also have to be member of the project in Kanboard. \ No newline at end of file
diff --git a/doc/google-authentication.markdown b/doc/google-authentication.markdown
deleted file mode 100644
index 0f4f3ec1..00000000
--- a/doc/google-authentication.markdown
+++ /dev/null
@@ -1,64 +0,0 @@
-Google Authentication
-=====================
-
-Requirements
-------------
-
-OAuth Google API credentials (available in the Google Developer Console)
-
-How does this work?
--------------------
-
-- The Google authentication in Kanboard use the OAuth 2.0 protocol
-- Any user account in Kanboard can be linked to a Google Account
-- When a Kanboard user account is linked to Google, you can login with one click
-
-Procedure to link a Google Account
-----------------------------------
-
-1. Go to your user profile
-2. Click on **External accounts**
-3. Click on the link **Link my Google Account**
-4. You are redirected to the **Google Consent screen**
-5. Authorize Kanboard by clicking on the button **Accept**
-6. Your account is now linked
-
-Now, on the login page you can be authenticated in one click with the link **Login with my Google Account**.
-
-Your name and email are automatically updated from your Google Account.
-
-Installation instructions
--------------------------
-
-### Setting up OAuth 2.0 in Google Developer Console
-
-- Follow the [official Google documentation](https://developers.google.com/accounts/docs/OAuth2Login#appsetup) to create a new application
-- In Kanboard, you can get the **redirect url** in **Settings > Integrations > Google Authentication**
-
-### Setting up Kanboad
-
-Create a custom `config.php` file or copy the `config.default.php` file:
-
-```php
-<?php
-
-// Enable/disable Google authentication
-define('GOOGLE_AUTH', true); // Set this value to true
-
-// Google client id (Get this value from the Google developer console)
-define('GOOGLE_CLIENT_ID', 'YOUR_CLIENT_ID');
-
-// Google client secret key (Get this value from the Google developer console)
-define('GOOGLE_CLIENT_SECRET', 'YOUR_CLIENT_SECRET');
-```
-
-Notes
------
-
-Kanboard use these information from your Google profile:
-
-- Full name
-- Email address
-- Google unique id
-
-The Google unique id is used to link together the local user account and the Google account.
diff --git a/doc/groups.markdown b/doc/groups.markdown
new file mode 100644
index 00000000..16f7ed0b
--- /dev/null
+++ b/doc/groups.markdown
@@ -0,0 +1,17 @@
+Groups Management
+=================
+
+In Kanboard, each user can be a member of one or many groups.
+A group is like a team or an organization.
+
+Only administrators can create new groups and assign users.
+
+Groups can be managed from **User management > View All Groups**.
+From there, you can create groups and assign users.
+
+![Group Management](screenshots/groups-management.png)
+
+Each project manager can authorize the access to a set of groups from the [project permissions page](project-permissions.markdown).
+
+The external id is mainly used for external group providers.
+Kanboard provides a LDAP group provider to [sync automatically groups from LDAP servers](ldap-group-sync.markdown).
diff --git a/doc/heroku.markdown b/doc/heroku.markdown
index ea3c19f9..f145f70e 100644
--- a/doc/heroku.markdown
+++ b/doc/heroku.markdown
@@ -10,7 +10,7 @@ Requirements
------------
- Heroku account, you can use a free account
-- Heroku command line tool installed
+- Heroku command line tools installed
Manual instructions
-------------------
@@ -35,4 +35,5 @@ heroku open
Limitations
-----------
-The storage on Heroku is ephemeral, that means uploaded files through Kanboard are not persistent after a reboot.
+- The storage of Heroku is ephemeral, that means uploaded files through Kanboard are not persistent after a reboot. You may want to install a plugin to store your files in a cloud storage provider like [Amazon S3](https://github.com/kanboard/plugin-s3).
+- Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
diff --git a/doc/ical.markdown b/doc/ical.markdown
index 5c52bf82..fc2318cf 100644
--- a/doc/ical.markdown
+++ b/doc/ical.markdown
@@ -2,24 +2,24 @@ Syncing your calendars
======================
Kanboard supports iCal feeds for projects and users.
-This feature allow you to import Kanboard tasks in almost any calendar program (by example Microsoft Outlook, Apple Calendar, Mozilla Thunderbird and Google Calendar).
+This feature allows you to import Kanboard tasks in almost any calendar program (by example Microsoft Outlook, Apple Calendar, Mozilla Thunderbird and Google Calendar).
-Calendar subscriptions are **read-only** access, you cannot create tasks from an external calendar software.
-The Calendar feed export follow the iCal standard.
+Calendar subscriptions are **read-only** access, you cannot create tasks from external calendar software.
+The Calendar feed export follows the iCal standard.
Note: Only tasks within the date range of -2 months to +6 months are exported to the iCalendar feed.
Project calendars
-----------------
-- Each project have its own calendar.
+- Each project has its own calendar.
- The subscription link is unique per project, the link is activated when you enable the public access of your project: **Project settings > Public access**.
-- This calendar show only tasks for the selected project.
+- This calendar shows only tasks for the selected project.
User calendars
--------------
-- Each user have its own calendar.
+- Each user has its own calendar.
- The subscription link is unique per user, the link is activated when you enable the public access of your user: **User profile > Public access**.
- This calendar show tasks assigned to the user for all projects.
@@ -28,7 +28,7 @@ Adding your Kanboard calendar to Apple Calendar
- Open Calendar
- Select **File > New Calendar Subscription**
-- Copy and paste the iCal feed url from Kanboard
+- Copy and paste the iCal feed URL from Kanboard
![Add iCal subscription](http://kanboard.net/screenshots/documentation/apple-calendar-add-subscription.png)
@@ -44,21 +44,21 @@ Adding your Kanboard calendar to Microsoft Outlook
- Open Outlook
- Select **Open Calendar > From Internet**
-- Copy and paste the iCal feed url from Kanboard
+- Copy and paste the iCal feed URL from Kanboard
![Outlook Edit Internet Calendar](http://kanboard.net/screenshots/documentation/outlook-edit-subscription.png)
Adding your Kanboard calendar to Mozilla Thunderbird
----------------------------------------------------
-- Install the Add-on **Lightning** to add the calendar support to Thunderbird
+- Install the Add-on **Lightning** to add calendar support to Thunderbird
- Click on **File > New Calendar**
- In the dialog box, choose **On the Network**
![Thunderbird Step 1](http://kanboard.net/screenshots/documentation/thunderbird-new-calendar-step1.png)
- Choose the format iCalendar
-- Copy and paste the iCal feed url from Kanboard
+- Copy and paste the iCal feed URL from Kanboard
![Thunderbird Step 2](http://kanboard.net/screenshots/documentation/thunderbird-new-calendar-step2.png)
@@ -69,7 +69,7 @@ Adding your Kanboard calendar to Google Calendar
- Click the down-arrow next to **Other calendars**.
- Select **Add by URL** from the menu.
-- Copy and paste the iCal feed url from Kanboard
+- Copy and paste the iCal feed URL from Kanboard
![Google Calendar](http://kanboard.net/screenshots/documentation/google-calendar-add-subscription.png)
diff --git a/doc/index.markdown b/doc/index.markdown
index 3c0cae97..99ca01f6 100644
--- a/doc/index.markdown
+++ b/doc/index.markdown
@@ -19,6 +19,7 @@ Using Kanboard
### Working with projects
+- [Project Types](project-types.markdown)
- [Creating projects](creating-projects.markdown)
- [Editing projects](editing-projects.markdown)
- [Sharing boards and tasks](sharing-projects.markdown)
@@ -44,9 +45,13 @@ Using Kanboard
- [Create tasks by email](create-tasks-by-email.markdown)
- [Subtasks](subtasks.markdown)
- [Analytics for tasks](analytics-tasks.markdown)
+- [User mentions](user-mentions.markdown)
-### Working with users
+### Working with users and groups
+- [Roles](roles.markdown)
+- [User Types](user-types.markdown)
+- [Group management](groups.markdown)
- [User management](user-management.markdown)
- [Notifications](notifications.markdown)
- [Two factor authentication](2fa.markdown)
@@ -63,9 +68,6 @@ Using Kanboard
### Integrations
-- [Bitbucket webhooks](bitbucket-webhooks.markdown)
-- [Github webhooks](github-webhooks.markdown)
-- [Gitlab webhooks](gitlab-webhooks.markdown)
- [iCalendar subscriptions](ical.markdown)
- [RSS/Atom subscriptions](rss.markdown)
- [Json-RPC API](api-json-rpc.markdown)
@@ -77,7 +79,7 @@ Using Kanboard
- [Advanced Search Syntax](search.markdown)
- [Command line interface](cli.markdown)
- [Syntax guide](syntax-guide.markdown)
-- [Bruteforce protection](bruteforce-protection.markdown)
+- [Brute force protection](bruteforce-protection.markdown)
- [Frequently asked questions](faq.markdown)
Technical details
@@ -85,22 +87,23 @@ Technical details
### Installation
-- [Recommended configuration](recommended-configuration.markdown)
+- [Requirements](requirements.markdown)
- [Installation instructions](installation.markdown)
- [Upgrade Kanboard to a new version](update.markdown)
- [Installation on Ubuntu](ubuntu-installation.markdown)
- [Installation on Debian](debian-installation.markdown)
- [Installation on Centos](centos-installation.markdown)
+- [Installation on OpenSuse](suse-installation.markdown)
- [Installation on FreeBSD](freebsd-installation.markdown)
- [Installation on Windows Server with IIS](windows-iis-installation.markdown)
- [Installation on Windows Server with Apache](windows-apache-installation.markdown)
- [Installation on Heroku](heroku.markdown)
-- [Example with Nginx + HTTPS + SPDY + PHP-FPM](nginx-ssl-php-fpm.markdown)
- [Run Kanboard with Docker](docker.markdown)
- [Run Kanboard with Vagrant](vagrant.markdown)
### Configuration
+- [Daily background job](cronjob.markdown)
- [Config file](config.markdown)
- [Email configuration](email-configuration.markdown)
- [URL rewriting](nice-urls.markdown)
@@ -114,10 +117,8 @@ Technical details
### Authentication
- [LDAP authentication](ldap-authentication.markdown)
-- [LDAP group sync](ldap-group-sync.markdown)
-- [Google authentication](google-authentication.markdown)
-- [Github authentication](github-authentication.markdown)
-- [Gitlab authentication](gitlab-authentication.markdown)
+- [LDAP group synchronization](ldap-group-sync.markdown)
+- [LDAP parameters](ldap-parameters.markdown)
- [Reverse proxy authentication](reverse-proxy-authentication.markdown)
### Contributors
diff --git a/doc/installation.markdown b/doc/installation.markdown
index 30a2916c..dd4283f8 100644
--- a/doc/installation.markdown
+++ b/doc/installation.markdown
@@ -1,13 +1,7 @@
Installation
============
-Requirements
-------------
-
-- Apache or Nginx
-- PHP >= 5.3.3 (Kanboard is compatible with PHP 5.3, 5.4, 5.5, 5.6 and 7.0)
-- PHP extensions required: mbstring, gd and pdo_sqlite
-- A modern web browser
+Firstly, check the [requirements](requirements.markdown) before to go further.
From the archive (stable version)
---------------------------------
@@ -27,7 +21,7 @@ The data folder is used to store:
- Uploaded files: `files/*`
- Image thumbnails: `files/thumbnails/*`
-People who are using a remote database (Mysql/Postgresql) and a remote file storage (Aws S3 or similar) don't necessary needs to have a persistent local data folder or to change the permissions.
+People who are using a remote database (Mysql/Postgresql) and a remote file storage (Aws S3 or similar) don't necessarily need to have a persistent local data folder or to change the permission.
From the repository (development version)
-----------------------------------------
@@ -45,3 +39,8 @@ Security
- Don't forget to change the default user/password
- Don't allow everybody to access to the directory `data` from the URL. There is already a `.htaccess` for Apache but nothing for Nginx.
+
+Notes
+-----
+
+- Some features of Kanboard require that you run [a daily background job](cronjob.markdown)
diff --git a/doc/kanban-vs-todo-and-scrum.markdown b/doc/kanban-vs-todo-and-scrum.markdown
index 3d53023a..4e083ff8 100644
--- a/doc/kanban-vs-todo-and-scrum.markdown
+++ b/doc/kanban-vs-todo-and-scrum.markdown
@@ -24,8 +24,8 @@ Kanban vs Scrum
- Estimation is required
- Uses velocity as default metric
- Scrum board is cleared between each sprint
-- Scrum has predefined roles like scrum master, product owner and the team
-- A lot of meetings: planning, backlog grooming, daily stand-up, retrospective
+- Scrum has pre-defined roles like scrum master, product owners and the team
+- A lot of meetings: planning, backlogs grooming, daily stand-up, retrospective
### Kanban:
diff --git a/doc/keyboard-shortcuts.markdown b/doc/keyboard-shortcuts.markdown
index 065d20c2..beb15d3d 100644
--- a/doc/keyboard-shortcuts.markdown
+++ b/doc/keyboard-shortcuts.markdown
@@ -6,6 +6,7 @@ Keyboard shortcuts availability depends of the page you are presently.
Project views (Board, Calendar, List, Gantt)
--------------------------------------------
+- Switch to the project overview = **v o**
- Switch to the board view = **v b** (press on **v** then **b**)
- Switch to the calendar view = **v c**
- Switch to the list view = **v l**
@@ -25,4 +26,4 @@ Application
- Go to the search box = **f**
- Reset the search box = **r**
- Close dialog box = **ESC**
-- Submit a form = **CTRL+ENTER** or **⌘+ENTER** \ No newline at end of file
+- Submit form = **CTRL+ENTER** or **⌘+ENTER**
diff --git a/doc/ldap-authentication.markdown b/doc/ldap-authentication.markdown
index f2e4869a..cacfb523 100644
--- a/doc/ldap-authentication.markdown
+++ b/doc/ldap-authentication.markdown
@@ -1,4 +1,4 @@
-LDAP authentication
+LDAP Authentication
===================
Requirements
@@ -13,28 +13,24 @@ Requirements
Workflow
--------
-When the LDAP authentication is activated, the login process work like that:
+When the LDAP authentication is activated, the login process works like that:
1. Try first to authenticate the user by using the database
2. If the user is not found inside the database, a LDAP authentication is performed
-3. If the LDAP authentication is successful, by default a local user is created automatically with no password and marked as LDAP user.
-
-### Differences between a local user and a LDAP user are the following:
-
-- LDAP users have no local passwords
-- LDAP users can't modify their password with the user interface
+3. If the LDAP authentication is successful, by default a local user is created automatically with no password and marked as LDAP users.
The full name and the email address are automatically fetched from the LDAP server.
-Configuration
--------------
-
-You have to create a custom config file named `config.php` (you can also use the template `config.default.php`).
-This file must be stored in the root directory of Kanboard.
+Authentication Types
+--------------------
-### LDAP bind type
+| Type | Description |
+|------------|-----------------------------------------------------------------|
+| Proxy User | A specific user is used to browse LDAP directory |
+| User | The end-user credentials are used for browsing LDAP directory |
+| Anonymous | No authentication is performed for LDAP browsing |
-There are 3 possible ways to browse the LDAP directory:
+**The recommended authentication method is "Proxy"**.
#### Anonymous mode
@@ -44,7 +40,7 @@ define('LDAP_USERNAME', null);
define('LDAP_PASSWORD', null);
```
-This is the default value but some LDAP servers don't allow that.
+This is the default value but some LDAP servers don't allow anonymous browsing for security reasons.
#### Proxy mode
@@ -60,7 +56,7 @@ define('LDAP_PASSWORD', 'my proxy password');
This method uses the credentials provided by the end-user.
-By example, Microsoft Active Directory doesn't allow anonymous browsing by default and if you don't want to use a proxy user you can use this method.
+For example, Microsoft Active Directory doesn't allow anonymous browsing by default and if you don't want to use a proxy user you can use this method.
```php
define('LDAP_BIND_TYPE', 'user');
@@ -73,7 +69,26 @@ In this case, the constant `LDAP_USERNAME` is used as a pattern to the ldap user
- `%s@kanboard.local` will be replaced by `my_user@kanboard.local`
- `KANBOARD\\%s` will be replaced by `KANBOARD\my_user`
-### Example for Microsoft Active Directory
+User LDAP filter
+----------------
+
+The configuration parameter `LDAP_USER_FILTER` is used to find users in LDAP directory.
+
+Examples:
+
+- `(&(objectClass=user)(sAMAccountName=%s))` is replaced by `(&(objectClass=user)(sAMAccountName=my_username))`
+- `uid=%s` is replaced by `uid=my_username`
+
+Other examples of [filters for Active Directory](http://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx)
+
+Example to filter access to Kanboard:
+
+`(&(objectClass=user)(sAMAccountName=%s)(memberOf=CN=Kanboard Users,CN=Users,DC=kanboard,DC=local))`
+
+This example allows only people members of the group "Kanboard Users" to connect to Kanboard.
+
+Example for Microsoft Active Directory
+--------------------------------------
Let's say we have a domain `KANBOARD` (kanboard.local) and the primary controller is `myserver.kanboard.local`.
@@ -93,8 +108,8 @@ define('LDAP_PASSWORD', 'my super secret password');
define('LDAP_SERVER', 'myserver.kanboard.local');
// LDAP properties
-define('LDAP_ACCOUNT_BASE', 'CN=Users,DC=kanboard,DC=local');
-define('LDAP_USER_PATTERN', '(&(objectClass=user)(sAMAccountName=%s))');
+define('LDAP_USER_BASE_DN', 'CN=Users,DC=kanboard,DC=local');
+define('LDAP_USER_FILTER', '(&(objectClass=user)(sAMAccountName=%s))');
```
Second example with user mode:
@@ -113,11 +128,12 @@ define('LDAP_PASSWORD', null);
define('LDAP_SERVER', 'myserver.kanboard.local');
// LDAP properties
-define('LDAP_ACCOUNT_BASE', 'CN=Users,DC=kanboard,DC=local');
-define('LDAP_USER_PATTERN', '(&(objectClass=user)(sAMAccountName=%s))');
+define('LDAP_USER_BASE_DN', 'CN=Users,DC=kanboard,DC=local');
+define('LDAP_USER_FILTER', '(&(objectClass=user)(sAMAccountName=%s))');
```
-### Example for OpenLDAP
+Example for OpenLDAP
+--------------------
Our LDAP server is `myserver.example.com` and all users are stored under `ou=People,dc=example,dc=com`.
@@ -133,15 +149,14 @@ define('LDAP_AUTH', true);
define('LDAP_SERVER', 'myserver.example.com');
// LDAP properties
-define('LDAP_ACCOUNT_BASE', 'ou=People,dc=example,dc=com');
-define('LDAP_USER_PATTERN', 'uid=%s');
+define('LDAP_USER_BASE_DN', 'ou=People,dc=example,dc=com');
+define('LDAP_USER_FILTER', 'uid=%s');
```
-The `%s` is replaced by the username for the parameter `LDAP_USER_PATTERN`, so you can define a custom Distinguished Name: ` (&(objectClass=user)(uid=%s)(!(ou:dn::=trainees)))`.
-
-### Disable automatic account creation
+Disable automatic account creation
+-----------------------------------
-By default, Kanboard will create automatically a user account if nothing is found.
+By default, Kanboard will create a user account automatically if nothing is found.
You can disable this behavior if you prefer to create user accounts manually to restrict Kanboard to only some people.
@@ -152,77 +167,18 @@ Just change the value of `LDAP_ACCOUNT_CREATION` to `false`:
define('LDAP_ACCOUNT_CREATION', false);
```
+Troubleshootings
+----------------
+
### SELinux restrictions
If SELinux is enabled, you have to allow Apache to reach out your LDAP server.
-- You can switch SELinux to the permissive mode or disable it (not recomemnded)
+- You can switch SELinux to the permissive mode or disable it (not recommended)
- You can allow all network connections, by example `setsebool -P httpd_can_network_connect=1` or have a more restrictive rule
In any case, refer to the official Redhat/Centos documentation.
-### Available configuration parameters
-
-```php
-// Enable LDAP authentication (false by default)
-define('LDAP_AUTH', false);
-
-// LDAP server hostname
-define('LDAP_SERVER', '');
-
-// LDAP server port (389 by default)
-define('LDAP_PORT', 389);
+### Enable debug mode
-// By default, require certificate to be verified for ldaps:// style URL. Set to false to skip the verification
-define('LDAP_SSL_VERIFY', true);
-
-// Enable LDAP START_TLS
-define('LDAP_START_TLS', false);
-
-// LDAP bind type: "anonymous", "user" or "proxy"
-define('LDAP_BIND_TYPE', 'anonymous');
-
-// LDAP username to connect with. null for anonymous bind (default).
-define('LDAP_USERNAME', null);
-
-// LDAP password to connect with. null for anonymous bind (default).
-define('LDAP_PASSWORD', null);
-
-// LDAP account base, i.e. root of all user account
-// Example: ou=People,dc=example,dc=com
-define('LDAP_ACCOUNT_BASE', '');
-
-// LDAP query pattern to use when searching for a user account
-// Example for ActiveDirectory: '(&(objectClass=user)(sAMAccountName=%s))'
-// Example for OpenLDAP: 'uid=%s'
-define('LDAP_USER_PATTERN', '');
-
-// Name of an attribute of the user account object which should be used as the full name of the user.
-define('LDAP_ACCOUNT_FULLNAME', 'displayname');
-
-// Name of an attribute of the user account object which should be used as the email of the user.
-define('LDAP_ACCOUNT_EMAIL', 'mail');
-
-// Name of an attribute of the user account object which should be used as the id of the user.
-// Example for ActiveDirectory: 'samaccountname'
-// Example for OpenLDAP: 'uid'
-define('LDAP_ACCOUNT_ID', '');
-
-// LDAP Attribute for group membership
-define('LDAP_ACCOUNT_MEMBEROF', 'memberof');
-
-// DN for administrators
-// Example: CN=Kanboard Admins,CN=Users,DC=kanboard,DC=local
-define('LDAP_GROUP_ADMIN_DN', '');
-
-// DN for project administrators
-// Example: CN=Kanboard Project Admins,CN=Users,DC=kanboard,DC=local
-define('LDAP_GROUP_PROJECT_ADMIN_DN', '');
-
-// By default Kanboard lowercase the ldap username to avoid duplicate users (the database is case sensitive)
-// Set to true if you want to preserve the case
-define('LDAP_USERNAME_CASE_SENSITIVE', false);
-
-// Automatically create user account
-define('LDAP_ACCOUNT_CREATION', true);
-```
+If you are not able to setup correctly the LDAP authentication, you can [enable the debug mode](config.markdown) and watch log files.
diff --git a/doc/ldap-group-sync.markdown b/doc/ldap-group-sync.markdown
index 355a1cde..69678a8a 100644
--- a/doc/ldap-group-sync.markdown
+++ b/doc/ldap-group-sync.markdown
@@ -7,30 +7,52 @@ Requirements
- Have LDAP authentication properly configured
- Use a LDAP server that supports `memberOf`
-Automatically define Kanboard groups based on LDAP groups
----------------------------------------------------------
+Define automatically user roles based on LDAP groups
+----------------------------------------------------
-In your config file, define the constants `LDAP_GROUP_ADMIN_DN` and `LDAP_GROUP_PROJECT_ADMIN_DN`. Here an example, replace the values according to your own LDAP configuration:
+Use these constants in your config file:
+
+- `LDAP_GROUP_ADMIN_DN`: Distinguished names for application administrators
+- `LDAP_GROUP_MANAGER_DN`: Distinguished names for application managers
+
+Example:
```php
define('LDAP_GROUP_ADMIN_DN', 'CN=Kanboard Admins,CN=Users,DC=kanboard,DC=local');
-define('LDAP_GROUP_PROJECT_ADMIN_DN', 'CN=Kanboard Project Admins,CN=Users,DC=kanboard,DC=local');
+define('LDAP_GROUP_MANAGER_DN', 'CN=Kanboard Managers,CN=Users,DC=kanboard,DC=local');
```
-- People member of "Kanboard Admins" will be "Kanboard Administrators"
-- People member of "Kanboard Project Admins" will be "Kanboard Project Administrators"
-- Everybody else will be Kanboard Standard Users
+- People member of "Kanboard Admins" will have the role "Administrator"
+- People member of "Kanboard Managers" will have the role "Managers"
+- Everybody else will have the role "User"
+
+Automatically load LDAP groups for project permissions
+------------------------------------------------------
-Note: At the moment, that works only at account creation.
+This feature allows you to sync automatically LDAP groups with Kanboard groups.
+Each group can have a different project role assigned.
-Filter Kanboard access based on the LDAP group
-----------------------------------------------
+On the project permissions page, people can enter groups in the auto-complete field and Kanboard can search for groups with any provider enabled.
-To allow only some users to use Kanboard, use the existing `LDAP_USER_PATTERN` constant:
+If the group doesn't exist in the local database, it will be automatically synced.
+
+- `LDAP_GROUP_PROVIDER`: Enable the LDAP group provider
+- `LDAP_GROUP_BASE_DN`: Distinguished names to find groups in LDAP directory
+- `LDAP_GROUP_FILTER`: LDAP filter used to perform the query
+- `LDAP_GROUP_ATTRIBUTE_NAME`: LDAP attribute used to fetch the group name
+
+Example:
```php
-define('LDAP_USER_PATTERN', '(&(objectClass=user)(sAMAccountName=%s)(memberOf=CN=Kanboard Users,CN=Users,DC=kanboard,DC=local))');
+define('LDAP_GROUP_PROVIDER', true);
+define('LDAP_GROUP_BASE_DN', 'CN=Groups,DC=kanboard,DC=local');
+define('LDAP_GROUP_FILTER', '(&(objectClass=group)(sAMAccountName=%s*))');
```
-This example allow only people member of the group "Kanboard Users" to connect to Kanboard.
+With the filter given as example above, Kanboard will search for groups that match the query.
+If the end-user enter the text "My group" in the auto-complete box, Kanboard will return all groups that match the pattern: `(&(objectClass=group)(sAMAccountName=My group*))`.
+
+- Note 1: The special characters `*` is important here, otherwise an exact match will be done.
+- Note 2: This feature is only compatible with LDAP authentication configured in "proxy" or "anonymous" mode
+[More examples of LDAP filters for Active Directory](http://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx)
diff --git a/doc/ldap-parameters.markdown b/doc/ldap-parameters.markdown
new file mode 100644
index 00000000..bd02baf2
--- /dev/null
+++ b/doc/ldap-parameters.markdown
@@ -0,0 +1,33 @@
+LDAP Configuration Parameters
+=============================
+
+Here is the list of available LDAP parameters:
+
+| Parameter | Default value | Description |
+|---------------------------------|----------------|------------------------------------------------|
+| `LDAP_AUTH` | false | Enable LDAP authentication |
+| `LDAP_SERVER` | Empty | LDAP server hostname |
+| `LDAP_PORT` | 389 | LDAP server port |
+| `LDAP_SSL_VERIFY` | true | Validate certificate for `ldaps://` style URL |
+| `LDAP_START_TLS` | false | Enable LDAP start TLS |
+| `LDAP_USERNAME_CASE_SENSITIVE` | false | Kanboard lowercase the ldap username to avoid duplicate users (the database is case sensitive) |
+| `LDAP_BIND_TYPE` | anonymous | Bind type: "anonymous", "user" or "proxy" |
+| `LDAP_USERNAME` | null | LDAP username to use with proxy mode or username pattern to use with user mode |
+| `LDAP_PASSWORD` | null | LDAP password to use for proxy mode |
+| `LDAP_USER_BASE_DN` | Empty | LDAP DN for users (Example: "CN=Users,DC=kanboard,DC=local") |
+| `LDAP_USER_FILTER` | Empty | LDAP pattern to use when searching for a user account (Example: "(&(objectClass=user)(sAMAccountName=%s))") |
+| `LDAP_USER_ATTRIBUTE_USERNAME` | uid | LDAP attribute for username (Example: "samaccountname") |
+| `LDAP_USER_ATTRIBUTE_FULLNAME` | cn | LDAP attribute for user full name (Example: "displayname") |
+| `LDAP_USER_ATTRIBUTE_EMAIL` | mail | LDAP attribute for user email |
+| `LDAP_USER_ATTRIBUTE_GROUPS` | memberof | LDAP attribute to find groups in user profile |
+| `LDAP_USER_CREATION` | true | Enable automatic LDAP user creation |
+| `LDAP_GROUP_ADMIN_DN` | Empty | LDAP DN for administrators (Example: "CN=Kanboard-Admins,CN=Users,DC=kanboard,DC=local") |
+| `LDAP_GROUP_MANAGER_DN` | Empty | LDAP DN for managers (Example: "CN=Kanboard Managers,CN=Users,DC=kanboard,DC=local") |
+| `LDAP_GROUP_PROVIDER` | false | Enable LDAP group provider for project permissions |
+| `LDAP_GROUP_BASE_DN` | Empty | LDAP Base DN for groups |
+| `LDAP_GROUP_FILTER` | Empty | LDAP group filter (Example: "(&(objectClass=group)(sAMAccountName=%s*))") |
+| `LDAP_GROUP_ATTRIBUTE_NAME` | cn | LDAP attribute for the group name |
+
+Notes:
+
+- LDAP attributes must be in lowercase
diff --git a/doc/link-labels.markdown b/doc/link-labels.markdown
index e6423566..1a53ecfa 100644
--- a/doc/link-labels.markdown
+++ b/doc/link-labels.markdown
@@ -6,6 +6,6 @@ Task relations can be changed from the application settings (**Settings > Link s
![Link Labels](http://kanboard.net/screenshots/documentation/link-labels.png)
Each label may have an opposite label defined.
-If there is no opposite, the label is considered bi-directionnal.
+If there is no opposite, the label is considered bidirectionnal.
![Link Label Creation](http://kanboard.net/screenshots/documentation/link-label-creation.png)
diff --git a/doc/mysql-configuration.markdown b/doc/mysql-configuration.markdown
index 554bec3b..6bfb209d 100644
--- a/doc/mysql-configuration.markdown
+++ b/doc/mysql-configuration.markdown
@@ -45,7 +45,7 @@ Note: You can also rename the template file `config.default.php` to `config.php`
### Importing SQL dump (alternative method)
-The first time, Kanboard will run one by one each database migration and this process can take some time according to your configuration.
+For the first time, Kanboard will run one by one each database migration and this process can take some time according to your configuration.
To avoid any issues or potential timeouts you can initialize the database directly by importing the SQL schema:
@@ -53,5 +53,5 @@ To avoid any issues or potential timeouts you can initialize the database direct
mysql -u root -p my_database < app/Schema/Sql/mysql.sql
```
-The file `app/Schema/Sql/mysql.sql` is a sql dump that represent the last version of the database.
+The file `app/Schema/Sql/mysql.sql` is a sql dump that represents the last version of the database.
diff --git a/doc/nginx-ssl-php-fpm.markdown b/doc/nginx-ssl-php-fpm.markdown
deleted file mode 100644
index 61afd2b1..00000000
--- a/doc/nginx-ssl-php-fpm.markdown
+++ /dev/null
@@ -1,238 +0,0 @@
-Kanboard with Nginx, HTTPS, SPDY and PHP-FPM
-============================================
-
-This installation example will help you to have the following features:
-
-- Latest stable nginx version
-- HTTPS only with a valid certificate
-- [SPDY protocol](http://en.wikipedia.org/wiki/SPDY) activated
-- PHP 5.5 with php-fpm
-- Recommended security parameters
-- File uploads with a 10MB file size limit
-
-This procedure is written for **Ubuntu 14.04 LTS** but it should be similar for any Linux distribution.
-
-For this setup, we suppose that only Kanboard is installed on the server.
-It can be a small virtual machine by example.
-
-Kanboard detect automatically the utilization of HTTPS and enable some extra features:
-
-- [HTTP Strict Transport Security](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
-- [Secure Cookie Flag](http://en.wikipedia.org/wiki/HTTP_cookie#Secure_and_HttpOnly)
-
-PHP 5.5 Installation
---------------------
-
-```bash
-sudo apt-get install php5-fpm php5-cli php5-sqlite
-```
-
-You can also install `php5-mysql` if you prefer to use Kanboard with Mysql or MariaDB.
-
-Customize your `/etc/php5/fpm/php.ini`:
-
-```ini
-; Security settings
-expose_php = Off
-cgi.fix_pathinfo=0
-
-; Log errors
-error_reporting = E_ALL
-display_errors = Off
-log_errors = On
-html_errors = Off
-error_log = syslog
-
-; File uploads
-upload_max_filesize = 10M
-post_max_size = 10M
-```
-
-Restart PHP background processes:
-
-```bash
-sudo service php5-fpm restart
-```
-
-Nginx Installation
-------------------
-
-We want the latest stable version of nginx to be able to use the SPDY protocol.
-Hopefully, there is PPA for Ubuntu (unofficial):
-
-```bash
-sudo add-apt-repository ppa:nginx/stable
-sudo apt-get install nginx
-```
-
-Generate a SSL certificate
---------------------------
-
-We want a SSL certificate that work everywhere, not a self-signed certificate.
-You can buy a cheap one at [Namecheap](http://www.namecheap.com/?aff=73824) or anywhere else.
-
-Here the different steps to configure your certificate:
-
-```bash
-# Generate a private key
-openssl genrsa -des3 -out kanboard.key 2048
-
-# Create a key with no password for Nginx
-openssl rsa -in kanboard.key -out kanboard.key.nopass
-
-# Generate the Certificate Signing Request, enter your domain name for the field 'Common Name'
-openssl req -new -key kanboard.key.nopass -out kanboard.csr
-
-# Copy and paste the content of the CSR to the Namecheap control panel and finalize the procedure
-cat kanboard.csr
-
-# After that, you receive by email your certificate, then concat everything into a single file
-cat kanboard.crt COMODORSAAddTrustCA.crt COMODORSADomainValidationSecureServerCA.crt AddTrustExternalCARoot.crt > kanboard.pem
-```
-
-Copy the certificates in a new directory:
-
-```bash
-mkdir /etc/nginx/ssl
-cp kanboard.pem /etc/nginx/ssl
-cp kanboard.key.nopass /etc/nginx/ssl
-chmod 400 /etc/nginx/ssl/*
-```
-
-Configure Nginx
----------------
-
-Now, we can customize our installation, start to modify the main configuration file `/etc/nginx/nginx.conf`:
-
-```nginx
-user www-data;
-worker_processes auto;
-pid /run/nginx.pid;
-
-events {
- worker_connections 1024;
-}
-
-http {
- sendfile on;
- tcp_nopush on;
- tcp_nodelay on;
- keepalive_timeout 65;
- types_hash_max_size 2048;
- server_tokens off;
-
- # SSL shared cache between workers
- ssl_session_cache shared:SSL:10m;
- ssl_session_timeout 10m;
-
- # We disable weak protocols and ciphers
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
- ssl_prefer_server_ciphers on;
- ssl_ciphers HIGH:!SSLv2:!MEDIUM:!LOW:!EXP:!RC4:!DSS:!aNULL:@STRENGTH;
-
- include /etc/nginx/mime.types;
- default_type application/octet-stream;
-
- access_log /var/log/nginx/access.log;
- error_log /var/log/nginx/error.log;
-
- # We enable the Gzip compression for some mime types
- gzip on;
- gzip_disable "msie6";
- gzip_vary on;
- gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
-
- include /etc/nginx/conf.d/*.conf;
- include /etc/nginx/sites-enabled/*;
-}
-```
-
-Create a new virtual host for Kanboard `/etc/nginx/sites-available/kanboard`
-
-
-```nginx
-server {
- # We also enable the SPDY protocol
- listen 443 ssl spdy;
-
- # Our SSL certificate
- ssl on;
- ssl_certificate /etc/nginx/ssl/kanboard.pem;
- ssl_certificate_key /etc/nginx/ssl/kanboard.key.nopass;
-
- # You can change the default root directory here
- root /usr/share/nginx/html;
-
- index index.php;
-
- # Your domain name
- server_name localhost;
-
- # The maximum body size, useful for file uploads
- client_max_body_size 10M;
-
- location / {
- try_files $uri $uri/ =404;
- }
-
- error_page 404 /404.html;
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root /usr/share/nginx/html;
- }
-
- # PHP-FPM configuration
- location ~ \.php$ {
- try_files $uri =404;
- fastcgi_split_path_info ^(.+\.php)(/.+)$;
- fastcgi_pass unix:/var/run/php5-fpm.sock;
- fastcgi_index index.php;
- include fastcgi.conf;
- }
-
- # Deny access to the directory data
- location ~* /data {
- deny all;
- return 404;
- }
-
- # Deny access to .htaccess
- location ~ /\.ht {
- deny all;
- return 404;
- }
-}
-```
-
-Now it's time to test our setup
-
-```bash
-# Disable the default virtual host
-sudo unlink /etc/nginx/sites-enabled/default
-
-# Add our default virtual host
-sudo ln -s /etc/nginx/sites-available/kanboard /etc/nginx/sites-enabled/kanboard
-
-# Check the config file
-sudo nginx -t
-nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
-nginx: configuration file /etc/nginx/nginx.conf test is successful
-
-# Restart nginx
-sudo service nginx restart
-```
-
-Kanboard Installation
----------------------
-
-You can install Kanboard in a subdirectory or not, it's up to you.
-
-```bash
-cd /usr/share/nginx/html
-sudo wget http://kanboard.net/kanboard-latest.zip
-sudo unzip kanboard-latest.zip
-sudo chown -R www-data:www-data kanboard/data
-sudo rm kanboard-latest.zip
-```
-
-Now, you should be able to use Kanboard with your web browser.
diff --git a/doc/nice-urls.markdown b/doc/nice-urls.markdown
index 82292889..9fbb3510 100644
--- a/doc/nice-urls.markdown
+++ b/doc/nice-urls.markdown
@@ -1,12 +1,12 @@
URL rewriting
=============
-Kanboard is able to work indifferently with url rewriting enabled or not.
+Kanboard is able to work indifferently with URL rewriting enabled or not.
- Example of URL rewritten: `/board/123`
- Otherwise: `?controller=board&action=show&project_id=123`
-If you use Kanboard with Apache and with the mode rewrite enabled, nice urls will be used automatically.
+If you use Kanboard with Apache and with the mode rewrite enabled, nice URLs will be used automatically.
In case you get a "404 Not Found", you might need to set at least the following overrides for your DocumentRoot to get the .htaccess files working:
```sh
@@ -29,7 +29,7 @@ Configuration
By default, Kanboard will check if the Apache mode rewrite is enabled.
-To avoid the automatic detection of url rewriting from the web server, you can enable this feature in your config file:
+To avoid the automatic detection of URL rewriting from the web server, you can enable this feature in your config file:
```php
define('ENABLE_URL_REWRITE', true);
@@ -38,9 +38,9 @@ define('ENABLE_URL_REWRITE', true);
When this constant is at `true`:
- URLs generated from command line tools will be also converted
-- If you use another web server than Apache, by example Nginx or Microsoft IIS, you have to configure yourself the url rewriting
+- If you use another web server than Apache, by example Nginx or Microsoft IIS, you have to configure yourself the URL rewriting
-Note: Kanboard always fallback to old school urls when it's not configured, this configuration is optional.
+Note: Kanboard always fallback to old school URLs when it's not configured, this configuration is optional.
Nginx configuration example
---------------------------
@@ -51,7 +51,7 @@ In the section `server` of your Nginx config file you can use this example:
index index.php;
location / {
- try_files $uri $uri/ /index.php;
+ try_files $uri $uri/ /index.php$is_args$args;
# If Kanboard is under a subfolder
# try_files $uri $uri/ /kanboard/index.php;
@@ -61,6 +61,7 @@ location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
}
@@ -85,3 +86,37 @@ define('ENABLE_URL_REWRITE', true);
```
Adapt the example above according to your own configuration.
+
+IIS configuration example
+---------------------------
+
+Create a web.config in you installation folder:
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+ <system.webServer>
+ <rewrite>
+ <rules>
+ <rule name="Imported Rule 1" stopProcessing="true">
+ <match url="^" ignoreCase="false" />
+ <conditions logicalGrouping="MatchAll">
+ <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
+ </conditions>
+ <action type="Rewrite" url="index.php" appendQueryString="true" />
+ </rule>
+ </rules>
+ </rewrite>
+ </system.webServer>
+</configuration>
+
+```
+
+In your Kanboard `config.php`:
+
+```php
+define('ENABLE_URL_REWRITE', true);
+```
+
+Adapt the example above according to your own configuration.
+
diff --git a/doc/notifications.markdown b/doc/notifications.markdown
index 55ba94bb..b69696fa 100644
--- a/doc/notifications.markdown
+++ b/doc/notifications.markdown
@@ -13,7 +13,7 @@ Configuration
Each user must enable the notifications in their profile: **User Profile > Notifications**. It's disabled by default.
-To receive email notifications you need a valid email address in you profile and the application must be configured to send emails.
+To receive email notifications you need a valid email address in your profile and the application must be configured to send emails.
![Notifications](http://kanboard.net/screenshots/documentation/notifications.png)
@@ -22,14 +22,14 @@ You can choose your favorite notification method:
- Emails
- Web (see below)
-For each project your are member, you can choose to receive notifications for:
+For each project you are a member, you can choose to receive notifications for:
- All tasks
- Only for tasks assigned to you
- Only for tasks created by you
- Only for tasks created by you and assigned to you
-You can also select only some projects, by default it's all projects where you are member.
+You can also select only some projects, by default it's all projects where you are a member.
Web notifications
-----------------
diff --git a/doc/plugin-authentication-architecture.markdown b/doc/plugin-authentication-architecture.markdown
new file mode 100644
index 00000000..d357c933
--- /dev/null
+++ b/doc/plugin-authentication-architecture.markdown
@@ -0,0 +1,99 @@
+Authentication Architecture
+===========================
+
+Kanboard provides a flexible and pluggable authentication architecture.
+
+By default, user authentication can be done with multiple methods:
+
+- Username and password authentication (Local database and LDAP)
+- OAuth2 authentication
+- Reverse-Proxy authentication
+- Cookie based authentication (Remember Me)
+
+More over, after a successful authentication, a Two-Factor post authentication can be done.
+Kanboard supports natively the TOTP standard.
+
+Authentication Interfaces
+-------------------------
+
+To have a pluggable system, authentication drivers must implement a set of interfaces:
+
+| Interface | Role |
+|------------------------------------------|------------------------------------------------------------------|
+| AuthenticationProviderInterface | Base interface for other authentication interfaces |
+| PreAuthenticationProviderInterface | The user is already authenticated when reaching the application, web servers usually define some environment variables |
+| PasswordAuthenticationProviderInterface | Authentication methods that uses the username and password provided in the login form |
+| OAuthAuthenticationProviderInterface | OAuth2 providers |
+| PostAuthenticationProviderInterface | Two-Factor auhentication drivers, ask for confirmation code |
+| SessionCheckProviderInterface | Providers that are able to check if the user session is valid |
+
+### Examples of authentication providers:
+
+- The default Database method implements `PasswordAuthenticationProviderInterface` and `SessionCheckProviderInterface`
+- The Reverse-Proxy method implements `PreAuthenticationProviderInterface` and `SessionCheckProviderInterface`
+- The Google method implements `OAuthAuthenticationProviderInterface`
+- The LDAP method implements `PasswordAuthenticationProviderInterface`
+- The RememberMe cookie method implements `PreAuthenticationProviderInterface`
+- The Two-Factor TOTP method implements `PostAuthenticationProviderInterface`
+
+Authentication Workflow
+-----------------------
+
+For each HTTP request:
+
+1. If the user session is already open, execute registered providers that implements `SessionCheckProviderInterface`
+2. Execute all providers that implements `PreAuthenticationProviderInterface`
+3. If the end-user submit the login form, providers that implements `PasswordAuthenticationProviderInterface` are executed
+4. If the end-user wants to use OAuth2, the selected provider will be executed
+5. After a successful authentication, the last registered `PostAuthenticationProviderInterface` will be used
+6. Synchronize user information if necessary
+
+This workflow is managed by the class `Kanboard\Core\Security\AuthenticationManager`.
+
+Events triggered:
+
+- `AuthenticationManager::EVENT_SUCCESS`: Successful authentication
+- `AuthenticationManager::EVENT_FAILURE`: Failed authentication
+
+Each time a failure event occurs, the counter of failed logins is incremented.
+
+The user account can be locked down for the configured period of time and a captcha can be shown to avoid brute force attacks.
+
+User Provider Interface
+-----------------------
+
+When the authentication is successful, the `AuthenticationManager` will ask the user information to your driver by calling the method `getUser()`.
+This method must return an object that implements the interface `Kanboard\Core\User\UserProviderInterface`.
+
+This class abstract the information gathered from another system.
+
+Examples:
+
+- `DatabaseUserProvider` provides information for an internal user
+- `LdapUserProvider` for a LDAP user
+- `ReverseProxyUserProvider` for a Reverse-Proxy user
+- `GoogleUserProvider` represents a Google user
+
+Methods for User Provider Interface:
+
+- `isUserCreationAllowed()`: Return true to allow automatic user creation
+- `getExternalIdColumn()`: Get external id column name (google_id, github_id, gitlab_id...)
+- `getInternalId()`: Get internal database id
+- `getExternalId()`: Get external id (Unique id)
+- `getRole()`: Get user role
+- `getUsername()`: Get username
+- `getName()`: Get user full name
+- `getEmail()`: Get user email address
+- `getExternalGroupIds()`: Get external group ids, automatically sync group membership if present
+- `getExtraAttributes()`: Get extra attributes to set for the user during the local sync
+
+It's not mandatory to return a value for each method.
+
+User Local Synchronization
+--------------------------
+
+User information can be automatically synced with the local database.
+
+- If the method `getInternalId()` return a value no synchronization is performed
+- The methods `getExternalIdColumn()` and `getExternalId()` must return a value to sync the user
+- Properties that returns an empty string won't be synced
diff --git a/doc/plugin-authentication.markdown b/doc/plugin-authentication.markdown
new file mode 100644
index 00000000..06fdfd8d
--- /dev/null
+++ b/doc/plugin-authentication.markdown
@@ -0,0 +1,40 @@
+Authentication Plugin
+=====================
+
+New authentication backends can be written with very few lines of code.
+
+Provider Registration
+---------------------
+
+In the method `initialize()` of your plugin, call the method `register()` of the class `AuthenticationManager`:
+
+```php
+public function initialize()
+{
+ $this->authenticationManager->register(new ReverseProxyLdapAuth($this->container));
+}
+```
+
+The object provided to the method `register()` must implement one of the pre-defined authentication interfaces.
+
+Those interfaces are defined in the namepsace `Kanboard\Core\Security`:
+
+- `Kanboard\Core\Security\PreAuthenticationProviderInterface`
+- `Kanboard\Core\Security\PostAuthenticationProviderInterface`
+- `Kanboard\Core\Security\PasswordAuthenticationProviderInterface`
+- `Kanboard\Core\Security\OAuthAuthenticationProviderInterface`
+
+The only requirement is to implement the interfaces, you class can be written the way you want and located anywhere on the disk.
+
+User Provider
+-------------
+
+When the authentication is successful, your driver must return an object that represents the user.
+This object must implement the interface `Kanboard\Core\User\UserProviderInterface`.
+
+Example of authentication plugins
+---------------------------------
+
+- [Authentication providers included in Kanboard](https://github.com/fguillot/kanboard/tree/master/app/Auth)
+- [Reverse-Proxy Authentication with LDAP support](https://github.com/kanboard/plugin-reverse-proxy-ldap)
+- [SMS Two-Factor Authentication](https://github.com/kanboard/plugin-sms-2fa)
diff --git a/doc/plugin-authorization-architecture.markdown b/doc/plugin-authorization-architecture.markdown
new file mode 100644
index 00000000..24acee17
--- /dev/null
+++ b/doc/plugin-authorization-architecture.markdown
@@ -0,0 +1,39 @@
+Authorization Architecture
+==========================
+
+Kanboard [supports multiple roles](roles.markdown) at the application level and at the project level.
+
+Authorization Workflow
+----------------------
+
+For each HTTP request:
+
+1. Authorize or not access to the resource based on the application access list
+2. If the resource is for a project (board, task...):
+ 1. Fetch user role for this project
+ 2. Grant/Denied access based on the project access map
+
+Extending Access Map
+--------------------
+
+The Access List (ACL) is based on the controller class name and the method name.
+The list of access is handled by the class `Kanboard\Core\Security\AccessMap`.
+
+There are two access map: one for the application and another one for projects.
+
+- Application access map: `$this->applicationAccessMap`
+- Project access map: `$this->projectAccessMap`
+
+Examples to define a new policy from your plugin:
+
+```php
+// All methods of the class MyController:
+$this->projectAccessMap->add('MyController', '*', Role::PROJECT_MANAGER);
+
+// All some methods:
+$this->projectAccessMap->add('MyOtherController', array('create', 'save'), Role::PROJECT_MEMBER);
+```
+
+Roles are defined in the class `Kanboard\Core\Security\Role`.
+
+The Authorization class (`Kanboard\Core\Security\Authorization`) will check the access for each page.
diff --git a/doc/plugin-automatic-actions.markdown b/doc/plugin-automatic-actions.markdown
new file mode 100644
index 00000000..b309fac9
--- /dev/null
+++ b/doc/plugin-automatic-actions.markdown
@@ -0,0 +1,60 @@
+Adding Automatic Actions
+========================
+
+Adding a new automatic action is pretty simple.
+
+Creating a new action
+---------------------
+
+Your automatic action must inherit of the class `Kanboard\Action\Base`.
+Several abstract methods must be implemented by yourself:
+
+| Method | Description |
+|-------------------------------------|------------------------------------------------------------------|
+| `getDescription()` | Description visible in the user interface |
+| `getCompatibleEvents()` | Get the list of compatible events |
+| `getActionRequiredParameters()` | Get the required parameter for the action (defined by the user) |
+| `getEventRequiredParameters()` | Get the required parameter for the event |
+| `doAction(array $data)` | Execute the action, must return true on success |
+| `hasRequiredCondition(array $data)` | Check if the event data meet the action condition |
+
+Your automatic action is identified in Kanboard by using the absolute class name with the name space included.
+
+Adding new events
+-----------------
+
+The list of application events is available in the class `Kanboard\Core\Event\EventManager::getAll()`.
+However, if your plugin fires new events, you can register these events like that:
+
+```php
+$this->actionManager->getAction('\Kanboard\Plugin\MyPlugin\MyActionName')->addEvent('my.event', 'My event description');
+```
+
+You can extend the list of compatible events of existing actions by using the same method.
+
+Registering the action
+----------------------
+
+You have to call the method `register()` from the class `Kanboard\Core\Action\ActionManager`:
+
+```php
+<?php
+
+namespace Kanboard\Plugin\AutomaticAction;
+
+use Kanboard\Core\Plugin\Base;
+use Kanboard\Plugin\AutomaticAction\Action\TaskRename;
+
+class Plugin extends Base
+{
+ public function initialize()
+ {
+ $this->actionManager->register(new TaskRename($this->container));
+ }
+}
+```
+
+Example
+-------
+
+- [Automatic Action example](https://github.com/kanboard/plugin-example-automatic-action)
diff --git a/doc/plugin-events.markdown b/doc/plugin-events.markdown
new file mode 100644
index 00000000..f4db8ff3
--- /dev/null
+++ b/doc/plugin-events.markdown
@@ -0,0 +1,27 @@
+Using Events
+============
+
+Kanboard use internally the [Symfony EventDispatcher component](https://symfony.com/doc/2.3/components/event_dispatcher/index.html) to manage internal events.
+
+Event Listening
+---------------
+
+```php
+$this->on('app.bootstrap', function($container) {
+ // Do something
+});
+```
+
+- The first argument is the event name (string)
+- The second argument is a PHP callable function (closure or class method)
+
+Adding a new event
+------------------
+
+To add a new event, you have to call the method `register()` of the class `Kanboard\Core\Event\EventManager`:
+
+```php
+$this->eventManager->register('my.event.name', 'My new event description');
+```
+
+These events can be used by other components of Kanboard like automatic actions.
diff --git a/doc/plugin-external-link.markdown b/doc/plugin-external-link.markdown
new file mode 100644
index 00000000..36252aff
--- /dev/null
+++ b/doc/plugin-external-link.markdown
@@ -0,0 +1,78 @@
+External Link Providers
+=======================
+
+This functionality allows you to link a task to additional items stored on another system.
+
+For example, you can link a task to:
+
+- Traditional web page
+- Attachment (PDF documents stored on the web, archive...)
+- Any ticketing system (bug tracker, customer support ticket...)
+
+Each item has a type, a URL, a dependency type and a title.
+
+By default, Kanboard includes two kinds of providers:
+
+- Web Link: You copy and paste a link and Kanboard will fetch the page title automatically
+- Attachment: Link to anything that is not a web page
+
+Workflow
+--------
+
+1. The end-user copy and paste the URL to the form and submit
+2. If the link type is "auto", Kanboard will loop through all providers registered until there is a match
+3. Then, the link provider returns a object that implements the interface `ExternalLinkInterface`
+4. A form is shown to the user with all pre-filled data before to save the link
+
+Interfaces
+----------
+
+To implement a new link provider from a plugin, you need to create 2 classes that implement those interfaces:
+
+- `Kanboard\Core\ExternalLink\ExternalLinkProviderInterface`
+- `Kanboard\Core\ExternalLink\ExternalLinkInterface`
+
+### ExternalLinkProviderInterface
+
+| Method | Usage |
+|----------------------------|-----------------------------------------------------------------|
+| `getName()` | Get provider name (label) |
+| `getType()` | Get link type (will be saved in the database) |
+| `getDependencies()` | Get a dictionary of supported dependency types by the provider |
+| `setUserTextInput($input)` | Set text entered by the user |
+| `match()` | Return true if the provider can parse correctly the user input |
+| `getLink()` | Get the link found with the properties |
+
+### ExternalLinkInterface
+
+| Method | Usage |
+|-------------------|------------------|
+| `getTitle()` | Get link title |
+| `getUrl()` | Get link URL |
+| `setUrl($url)` | Set link URL |
+
+Register a new link provider
+----------------------------
+
+In your `Plugin.php`, just call the method `register()` from the object `ExternalLinkManager`:
+
+```php
+<?php
+
+namespace Kanboard\Plugin\MyExternalLink;
+
+use Kanboard\Core\Plugin\Base;
+
+class Plugin extends Base
+{
+ public function initialize()
+ {
+ $this->externalLinkManager->register(new MyLinkProvider());
+ }
+}
+```
+
+Examples
+--------
+
+- Kanboard includes the default providers "WebLink" and "Attachment"
diff --git a/doc/plugin-group-provider.markdown b/doc/plugin-group-provider.markdown
new file mode 100644
index 00000000..4d73b740
--- /dev/null
+++ b/doc/plugin-group-provider.markdown
@@ -0,0 +1,55 @@
+Custom Group Providers
+======================
+
+Kanboard is able to load groups from an external system.
+This feature is mainly used for project permissions.
+
+Project managers can allow access to a project for a group.
+The end-user will use an auto-complete box and search for a group.
+
+Each time a group query is executed, all registered group providers are executed.
+
+Group Provider Workflow
+-----------------------
+
+1. The end-user start to type the group name in the auto-complete field
+2. The `GroupManager` class will execute the query across all registered group providers
+3. Results are merged and returned to the user interface
+4. After selecting a group, the information of the group are synced to the local database if necessary
+
+Group Provider Interface
+------------------------
+
+Interface to implement: `Kanboard\Core\Group\GroupProviderInterface`.
+
+Classes that implements this interface abstract the group information, there are only 3 methods:
+
+- `getInternalId()`: Get internal database id, return 0 otherwise
+- `getExternalId()`: Get external unique id
+- `getName()`: Get group name
+
+Kanboard will use the external id to sync with the local database.
+
+Group Backend Provider Interface
+--------------------------------
+
+Interface to implement: `Kanboard\Core\Group\GroupBackendProviderInterface`.
+
+This interface requires only one method: `find($input)`.
+The argument `$input` is the text entered from the user interface.
+
+This method must return a list of `GroupProviderInterface`, this is the result of the search.
+
+Backend Registration from Plugins
+---------------------------------
+
+In the method `initialize()` of your plugin register your custom backend like that:
+
+```php
+$groupManager->register(new MyCustomLdapBackendGroupProvider($this->container));
+```
+
+Examples
+--------
+
+- [Group providers included in Kanboard (LDAP and Database)](https://github.com/fguillot/kanboard/tree/master/app/Group)
diff --git a/doc/plugin-hooks.markdown b/doc/plugin-hooks.markdown
index eab2fa15..3dcb5b1f 100644
--- a/doc/plugin-hooks.markdown
+++ b/doc/plugin-hooks.markdown
@@ -16,13 +16,13 @@ $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
+### Hooks executed only once
Some hooks can have only one listener:
#### model:subtask-time-tracking:calculate:time-spent
-- Override time spent calculation when subtask timer is stopped
+- Override time spent calculation when sub-task timer is stopped
- Arguments:
- `$user_id` (integer)
- `$start` (DateTime)
@@ -58,7 +58,27 @@ class Plugin extends Base
}
```
-List of merge hooks:
+Example to override default values for task forms:
+
+```php
+class Plugin extends Base
+{
+ public function initialize()
+ {
+ $this->hook->on('controller:task:form:default', function (array $default_values) {
+ return empty($default_values['score']) ? array('score' => 4) : array();
+ });
+ }
+}
+```
+
+List of merging hooks:
+
+#### controller:task:form:default
+
+- Override default values for task forms
+- Arguments:
+ - `$default_values`: actual default values (array)
#### controller:calendar:project:events
@@ -79,7 +99,7 @@ List of merge hooks:
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.
+Asset hooks can be used to add a new stylesheet easily 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:
@@ -127,26 +147,40 @@ Example with `myplugin:dashboard/sidebar`:
- 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.
+Template names without prefix are core templates.
List of template hooks:
-- `template:auth:login-form:before`
-- `template:auth:login-form:after`
-- `template:dashboard:sidebar`
-- `template:config:sidebar`
-- `template:config:integrations`
-- `template:project:integrations`
-- `template:user: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.
+| Hook | Description |
+|--------------------------------------|----------------------------------------------------|
+| `template:analytic:sidebar` | Sidebar on analytic pages |
+| `template:app:filters-helper:before` | Filter helper dropdown (top) |
+| `template:app:filters-helper:after` | Filter helper dropdown (bottom) |
+| `template:auth:login-form:before` | Login page (top) |
+| `template:auth:login-form:after` | Login page (bottom) |
+| `template:config:sidebar` | Sidebar on settings page |
+| `template:config:integrations` | Integration page in global settings |
+| `template:dashboard:sidebar` | Sidebar on dashboard page |
+| `template:export:sidebar` | Sidebar on export pages |
+| `template:layout:head` | Page layout `<head/>` tag |
+| `template:layout:top` | Page layout top header |
+| `template:layout:bottom` | Page layout footer |
+| `template:project:dropdown` | "Actions" menu on left in different project views |
+| `template:project:header:before` | Project filters (before) |
+| `template:project:header:after` | Project filters (after) |
+| `template:project:integrations` | Integration page in projects settings |
+| `template:project:sidebar` | Sidebar in project settings |
+| `template:project-user:sidebar` | Sidebar on project user overview page |
+| `template:task:menu` | "Actions" menu on left in different task views |
+| `template:task:dropdown` | Task dropdown menu in listing pages |
+| `template:task:sidebar` | Sidebar on task page |
+| `template:task:form:right-column` | Right column in task form |
+| `template:user:authentication:form` | "Edit authentication" form in user profile |
+| `template:user:create-remote:form` | "Create remote user" form |
+| `template:user:external` | "External authentication" page in user profile |
+| `template:user:integrations` | Integration page in user profile |
+| `template:user:sidebar:actions` | Sidebar in user profile (section actions) |
+| `template:user:sidebar:information` | Sidebar in user profile (section information) |
+
+
+Another template hooks can be added if necessary, just ask on the issue tracker.
diff --git a/doc/plugin-ldap-client.markdown b/doc/plugin-ldap-client.markdown
new file mode 100644
index 00000000..312eea71
--- /dev/null
+++ b/doc/plugin-ldap-client.markdown
@@ -0,0 +1,99 @@
+LDAP Library
+============
+
+To facilitate LDAP integration, Kanboard has its own LDAP library.
+This library can perform common operations.
+
+Client
+------
+
+Class: `Kanboard\Core\Ldap\Client`
+
+To connect to your LDAP server easily, use this method:
+
+```php
+use Kanboard\Core\Ldap\Client as LdapClient;
+use Kanboard\Core\Ldap\ClientException as LdapException;
+
+try {
+ $client = LdapClient::connect();
+
+ // Get native LDAP resource
+ $resource = $client->getConnection();
+
+ // ...
+
+} catch (LdapException $e) {
+ // ...
+}
+```
+
+LDAP Queries
+------------
+
+Classes:
+
+- `Kanboard\Core\Ldap\Query`
+- `Kanboard\Core\Ldap\Entries`
+- `Kanboard\Core\Ldap\Entry`
+
+Example to query the LDAP directory:
+
+```php
+
+$query = new Query($client)
+$query->execute('ou=People,dc=kanboard,dc=local', 'uid=my_user', array('cn', 'mail'));
+
+if ($query->hasResult()) {
+ $entries = $query->getEntries(); // Return an instance of Entries
+}
+```
+
+Read one entry:
+
+```php
+$firstEntry = $query->getEntries()->getFirstEntry();
+$email = $firstEntry->getFirstValue('mail');
+$name = $firstEntry->getFirstValue('cn', 'Default Name');
+```
+
+Read multiple entries:
+
+```php
+foreach ($query->getEntries()->getAll() as $entry) {
+ $emails = $entry->getAll('mail'); // Fetch all emails
+ $dn = $entry->getDn(); // Get LDAP DN of this user
+
+ // Check if a value is present for an attribute
+ if ($entry->hasValue('mail', 'user2@localhost')) {
+ // ...
+ }
+}
+```
+
+User Helper
+-----------
+
+Class: `Kanboard\Core\Ldap\User`
+
+Fetch a single user in one line:
+
+```php
+// Return an instance of LdapUserProvider
+$user = User::getUser($client, 'my_username');
+```
+
+Group Helper
+------------
+
+Class: `Kanboard\Core\Ldap\Group`
+
+Fetch groups in one line:
+
+```php
+// Define LDAP filter
+$filter = '(&(objectClass=group)(sAMAccountName=My group*))';
+
+// Return a list of LdapGroupProvider
+$groups = Group::getGroups($client, $filter);
+```
diff --git a/doc/plugin-mail-transports.markdown b/doc/plugin-mail-transports.markdown
index cb7dd6ce..33ce5e3b 100644
--- a/doc/plugin-mail-transports.markdown
+++ b/doc/plugin-mail-transports.markdown
@@ -8,12 +8,12 @@ By default Kanboard supports 3 standards mail transports:
- Sendmail command
With the plugin API you can add a driver for any email provider.
-By example, your plugin can add a mail transport for a provider that uses an HTTP API.
+For example, your plugin can add a mail transport for a provider that uses an HTTP API.
Implementation
--------------
-Your plugin must implements the interface `Kanboard\Core\Mail\ClientInterface` and extends from `Kanboard\Core\Base`.
+Your plugin must implement the interface `Kanboard\Core\Mail\ClientInterface` and extends from `Kanboard\Core\Base`.
The only method you need to implement is `sendEmail()`:
@@ -40,7 +40,7 @@ To register your new mail transport, use the method `setTransport($transport, $c
$this->emailClient->setTransport('myprovider', '\Kanboard\Plugin\MyProvider\MyEmailHandler');
```
-The second argument contains the absolute namespace of your concrete class.
+The second argument contains the absolute name space of your concrete class.
Examples of mail transport plugins
----------------------------------
diff --git a/doc/plugin-metadata.markdown b/doc/plugin-metadata.markdown
index a01b0ddc..1f4bfa2b 100644
--- a/doc/plugin-metadata.markdown
+++ b/doc/plugin-metadata.markdown
@@ -4,10 +4,10 @@ Metadata
You can attach metadata for each project, task and user.
Metadata are custom fields, it's a key/value table.
-By example your plugin can store external information for a task or new settings for a project.
-Basically that allow you to exend the default fields without having to create new tables.
+For example your plugin can store external information for a task or new settings for a project.
+Basically that allow you to extend the default fields without having to create new tables.
-Attach metadata to tasks
+Attach metadata to tasks and remove them
------------------------
```php
@@ -23,6 +23,9 @@ $this->taskMetadata->exists($task_id, 'my_plugin_variable');
// Create or update metadata for the task
$this->taskMetadata->save($task_id, ['my_plugin_variable' => 'something']);
+
+// Remove a metadata from a project
+$this->projectMetadata->remove($project_id, my_plugin_variable);
```
Metadata types
diff --git a/doc/plugin-notifications.markdown b/doc/plugin-notifications.markdown
index 83fdb5e3..15cf4b9b 100644
--- a/doc/plugin-notifications.markdown
+++ b/doc/plugin-notifications.markdown
@@ -17,14 +17,14 @@ $this->userNotificationType->setType('irc', t('IRC'), '\Kanboard\Plugin\IRC\Noti
$this->projectNotificationType->setType('irc', t('IRC'), '\Kanboard\Plugin\IRC\Notification\IrcHandler');
```
-Your handler can be registered for user or project notification. You don't necessary need to support both.
+Your handler can be registered for user or project notification. You don't necessarily need to support both.
When your handler is registered, the end-user can choose to receive the new notification type or not.
Notification Handler
--------------------
-Your notification handler must implements the interface `Kanboard\Notification\NotificationInterface`:
+Your notification handler must implement the interface `Kanboard\Notification\NotificationInterface`:
```php
interface NotificationInterface
diff --git a/doc/plugin-overrides.markdown b/doc/plugin-overrides.markdown
index 905808d5..722b4126 100644
--- a/doc/plugin-overrides.markdown
+++ b/doc/plugin-overrides.markdown
@@ -25,7 +25,7 @@ class Plugin extends Base
Template Overrides
------------------
-Any templates defined in the core can be overrided. By example, you can redefine the default layout or change email notifications.
+Any templates defined in the core can be overridden. For example, you can redefine the default layout or change email notifications.
Example of template override:
diff --git a/doc/plugin-registration.markdown b/doc/plugin-registration.markdown
index 312f61b9..f212fc2e 100644
--- a/doc/plugin-registration.markdown
+++ b/doc/plugin-registration.markdown
@@ -25,7 +25,7 @@ plugins
└── Test <= Unit tests
```
-Only the registration file `Plugin.php` is required. Other folders are optionals.
+Only the registration file `Plugin.php` is required. Other folders are optional.
The first letter of the plugin name must be capitalized.
@@ -43,7 +43,7 @@ namespace Kanboard\Plugin\Foobar;
use Kanboard\Core\Plugin\Base;
-class Plugin extends Plugin\Base
+class Plugin extends Base
{
public function initialize()
{
@@ -52,7 +52,7 @@ class Plugin extends Plugin\Base
}
```
-This file should contains a class `Plugin` defined under the namespace `Kanboard\Plugin\Yourplugin` and extends `Kanboard\Core\Plugin\Base`.
+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.
@@ -71,7 +71,7 @@ Available methods from `Kanboard\Core\Plugin\Base`:
- `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.
+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:
@@ -82,20 +82,22 @@ $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:
+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
-$this->on('session.bootstrap', function($container) {
+$this->on('app.bootstrap', function($container) {
Translator::load($container['config']->getCurrentLanguage(), __DIR__.'/Locale');
});
```
-The translations must be stored in `plugins/Myplugin/Locale/xx_XX/translations.php`.
+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 use Pimple, a simple PHP Dependency Injection Container. However, Kanboard can register any class in the container easily.
+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.
@@ -123,78 +125,4 @@ $this->budget->getDailyBudgetBreakdown(456);
$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\DoSomething', // Use absolute namespace
- t('Do something 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
+Keys of the containers are unique across the application. If you override an existing class, you will change the default behavior.
diff --git a/doc/plugin-routes.markdown b/doc/plugin-routes.markdown
new file mode 100644
index 00000000..b943bb19
--- /dev/null
+++ b/doc/plugin-routes.markdown
@@ -0,0 +1,85 @@
+Custom Routes
+=============
+
+When URL rewriting is enabled, you can define custom routes from your plugins.
+
+Define new routes
+-----------------
+
+Routes are handled by the class `Kanboard\Core\Http\Route`.
+
+New routes can be added by using the method `addRoute($path, $controller, $action, $plugin)`, here an example:
+
+```php
+$this->route->addRoute('/my/custom/route', 'myController', 'myAction', 'myplugin');
+```
+
+When the end-user go to the URL `/my/custom/route`, the method `Kanboard\Plugin\Myplugin\Controller\MyController::myAction()` will be executed.
+
+The first character of the controller and the plugin name will converted in uppercase with the function `ucfirst()`.
+
+You can also define routes with variables:
+
+```php
+$this->route->addRoute('/my/route/:my_variable', 'myController', 'myAction', 'myplugin');
+```
+
+The colon prefix `:`, define a variable.
+For example `:my_variable` declare a new variable named `my_variable`.
+
+To fetch the value of the variable you can use the method `getStringParam()` or `getIntegerParam()` from the class `Kanboard\Core\Http\Request`:
+
+If we have the URL `/my/route/foobar`, the value of `my_variable` is `foobar`:
+
+```php
+$this->request->getStringParam('my_variable'); // Return foobar
+```
+
+Generate links based on the routing table
+-----------------------------------------
+
+From templates, you have to use the helper `Kanboard\Helper\Url`.
+
+### Generate a HTML link
+
+```php
+<?= $this->url->link('My link', 'mycontroller', 'myaction', array('plugin' => 'myplugin')) ?>
+```
+
+Will generate this HTML:
+
+```html
+<a href="/my/custom/route">My link</a>
+```
+
+### Generate only the attribute `href`:
+
+```php
+<?= $this->url->href('My link', 'mycontroller', 'myaction', array('plugin' => 'myplugin')) ?>
+```
+
+HTML output:
+
+```html
+/my/custom/route
+```
+
+HTML output when URL rewriting is not enabled:
+
+```html
+?controller=mycontroller&amp;action=myaction&amp;plugin=myplugin
+```
+
+### Generate redirect link:
+
+From a controller, if you need to perform a redirection:
+
+```php
+$this->url->to('mycontroller', 'myaction', array('plugin' => 'myplugin'));
+```
+
+Generate:
+
+```
+?controller=mycontroller&action=myaction&plugin=myplugin
+```
diff --git a/doc/plugin-schema-migrations.markdown b/doc/plugin-schema-migrations.markdown
index d595605f..36a57f37 100644
--- a/doc/plugin-schema-migrations.markdown
+++ b/doc/plugin-schema-migrations.markdown
@@ -1,7 +1,8 @@
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:
+Kanboard executes 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
@@ -31,7 +32,7 @@ function version_1($pdo)
```
- The constant `VERSION` is the last version of your schema
-- Each function is a migration `version_1()`, `version_2()`, etc...
+- 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
diff --git a/doc/plugins.markdown b/doc/plugins.markdown
index 0a08fa8e..55575612 100644
--- a/doc/plugins.markdown
+++ b/doc/plugins.markdown
@@ -5,19 +5,30 @@ 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.
+Plugin creators should specify explicitly the compatible versions of Kanboard. Internal code of Kanboard may change over time and your plugin must be tested with new versions. Always check the [ChangeLog](https://github.com/fguillot/kanboard/blob/master/ChangeLog) for breaking changes.
- [Creating your plugin](plugin-registration.markdown)
- [Using plugin hooks](plugin-hooks.markdown)
+- [Events](plugin-events.markdown)
- [Override default application behaviors](plugin-overrides.markdown)
- [Add schema migrations for plugins](plugin-schema-migrations.markdown)
+- [Custom routes](plugin-routes.markdown)
- [Add mail transports](plugin-mail-transports.markdown)
- [Add notification types](plugin-notifications.markdown)
+- [Add automatic actions](plugin-automatic-actions.markdown)
- [Attach metadata to users, tasks and projects](plugin-metadata.markdown)
+- [Authentication architecture](plugin-authentication-architecture.markdown)
+- [Authentication plugin registration](plugin-authentication.markdown)
+- [Authorization Architecture](plugin-authorization-architecture.markdown)
+- [Custom Group Providers](plugin-group-provider.markdown)
+- [External Link Providers](plugin-external-link.markdown)
+- [LDAP client](plugin-ldap-client.markdown)
Examples of plugins
-------------------
+- [SMS Two-Factor Authentication](https://github.com/kanboard/plugin-sms-2fa)
+- [Reverse-Proxy Authentication with LDAP support](https://github.com/kanboard/plugin-reverse-proxy-ldap)
- [Slack](https://github.com/kanboard/plugin-slack)
- [Hipchat](https://github.com/kanboard/plugin-hipchat)
- [Jabber](https://github.com/kanboard/plugin-jabber)
@@ -26,7 +37,7 @@ Examples of plugins
- [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)
+- [User timetables](https://github.com/kanboard/plugin-timetable)
- [Subtask Forecast](https://github.com/kanboard/plugin-subtask-forecast)
- [Automatic Action example](https://github.com/kanboard/plugin-example-automatic-action)
- [Theme plugin example](https://github.com/kanboard/plugin-example-theme)
diff --git a/doc/postgresql-configuration.markdown b/doc/postgresql-configuration.markdown
index 904dfc0f..627e2ded 100644
--- a/doc/postgresql-configuration.markdown
+++ b/doc/postgresql-configuration.markdown
@@ -6,7 +6,7 @@ By default, Kanboard use Sqlite to store its data but it's also possible to use
Requirements
------------
-- A Postgresql server already installed and configured
+- Postgresql server already installed and configured
- The PHP extension `pdo_pgsql` installed (Debian/Ubuntu: `apt-get install php5-pgsql`)
Note: Kanboard is tested with **Postgresql 9.3 and 9.4**
@@ -22,7 +22,7 @@ CREATE DATABASE kanboard;
### Create a config file
-The file `config.php` should contains those values:
+The file `config.php` should contain those values:
```php
<?php
@@ -41,7 +41,7 @@ Note: You can also rename the template file `config.default.php` to `config.php`
### Importing SQL dump (alternative method)
-The first time, Kanboard will run one by one each database migration and this process can take some time according to your configuration.
+For the first time, Kanboard will run one by one each database migration and this process can take some time according to your configuration.
To avoid any issues or potential timeouts you can initialize the database directly by importing the SQL schema:
@@ -49,4 +49,4 @@ To avoid any issues or potential timeouts you can initialize the database direct
psql -U postgres my_database < app/Schema/Sql/postgres.sql
```
-The file `app/Schema/Sql/postgres.sql` is a sql dump that represent the last version of the database.
+The file `app/Schema/Sql/postgres.sql` is a sql dump that represents the last version of the database.
diff --git a/doc/project-configuration.markdown b/doc/project-configuration.markdown
index 252ace67..e458ebe3 100644
--- a/doc/project-configuration.markdown
+++ b/doc/project-configuration.markdown
@@ -1,4 +1,4 @@
-Project settings
+Project Settings
================
Go to the menu **Settings**, then choose **Project settings** on the left.
@@ -38,5 +38,5 @@ If another subtask have the status "in progress", the user will see this dialog
- If enabled, closed tasks will be included in the cumulative flow diagram.
- If disabled, only open tasks will be included.
-- This option affect the column "total" of the table "project_daily_column_stats"
+- This option affects the column "total" of the table "project_daily_column_stats"
diff --git a/doc/project-permissions.markdown b/doc/project-permissions.markdown
index 762ab307..16586bc6 100644
--- a/doc/project-permissions.markdown
+++ b/doc/project-permissions.markdown
@@ -1,49 +1,23 @@
-Project permissions
+Project Permissions
===================
-A project can have two kinds of people: **project managers** and **project members**.
+Each project is isolated and compartmented from each other.
+The project access must be allowed by the project owner.
-- Project managers can manage the configuration of the project and access to the reports.
-- Project members can only do basic operations (create or move tasks).
+Each user and each group can have a different role assigned.
+There are 3 types of [roles for projects](roles.markdown):
-When you create a new project, you are automatically assigned as a project manager.
+- Project Manager
+- Project Member
+- Project Viewer
-Kanboard administrators can access to everything but they are not necessary project members or managers. **Those permissions are defined at the project level**.
+Only administrators have access to everything.
-Permissions for each role
--------------------------
+Roles assignation is available from **Project Settings > Permissions**:
-### Project members
+![Project Permissions](screenshots/project-permissions.png)
-- Use the board (create, move and edit tasks)
-- Remove only tasks created by themselves
+If you choose to allow everybody, all Kanboard users will be considered Project Member.
+That also means there is no role management anymore. Permission per user or per group cannot be applied.
-### Project managers
-
-- Use the board
-- Configure the project
- - Share, rename, duplicate and disable the project
- - Manage swimlanes, categories, columns and users
- - Edit automatic actions
-- CSV Exports
-- Remove tasks of any project members
-- Access to the analytics section
-
-They **cannot remove the project**.
-
-Manage users and permissions
-----------------------------
-
-To define project roles, go to the **project configuration page** then click on **User management**.
-
-### User management
-
-![Project permissions](http://kanboard.net/screenshots/documentation/project-permissions.png)
-
-From there, you can choose to add new members, change the role or revoke user access.
-
-### Allow everybody
-
-If you choose to allow everybody (all Kanboard users), the project is considered public.
-
-That means there is no role management anymore. Permissions per user cannot be applied.
+Private projects cannot define permission.
diff --git a/doc/project-types.markdown b/doc/project-types.markdown
new file mode 100644
index 00000000..ae18b9ef
--- /dev/null
+++ b/doc/project-types.markdown
@@ -0,0 +1,14 @@
+Project Types
+=============
+
+There are two kinds of projects:
+
+| Type | Description |
+|-------------------|-----------------------------------------------------------------------|
+| Team Project | Project with user and group management |
+| Private Project | Project that belongs to only one person, there is no user management |
+
+- Only Administrators and Application Managers can create team projects.
+- Private projects can be created by anyone.
+
+[Read the documentation about roles in Kanboard](roles.markdown)
diff --git a/doc/project-views.markdown b/doc/project-views.markdown
index e1fb4c1e..b45f8178 100644
--- a/doc/project-views.markdown
+++ b/doc/project-views.markdown
@@ -1,9 +1,11 @@
-Board, Calendar and List views
+Board, Calendar and List Views
==============================
-For each project, tasks can be visualized with several views: **Board, Calendar, List and Gantt**. Each view show the result of the filter box at the top. The search engine use the [advanced syntax](search.markdown).
+For each project, tasks can be visualized with several views: **Board, Calendar, List and Gantt**.
+Each view shows the result of the filter box at the top.
+The search engine uses the [advanced syntax](search.markdown).
-Board view
+Board View
----------
![Board view](http://kanboard.net/screenshots/documentation/board-view.png)
@@ -17,8 +19,8 @@ Board view
When the task limit is reached for a column, the background becomes red. That means there are too many tasks in progress at the same time.
[Learn more about board configuration](board-configuration.markdown)
-
-Calendar view
+
+Calendar View
--------------
![Calendar view](http://kanboard.net/screenshots/documentation/calendar-view.png)
@@ -28,19 +30,19 @@ Calendar view
- You can also use the keyboard shortcut **"v c"** to switch to the calendar view.
- [Learn more about calendar configuration](calendar-configuration.markdown)
-List view
-----------
+List View
+---------
![List view](http://kanboard.net/screenshots/documentation/list-view.png)
- With this view all results of your search are displayed in a table.
- You can also use the keyboard shortcut **"v l"** to switch to the list view.
-Gantt view
+Gantt View
----------
![Gantt view](http://kanboard.net/screenshots/documentation/gantt-view.png)
-- The Gantt view display tasks on a horizontal timeline
+- The Gantt view displays tasks on a horizontal timeline
- The start date and the due date are used to display the chart
-- For a quick access, use the keyboard shortcut: **v g**
+- For quick access, use the keyboard shortcut: **v g**
diff --git a/doc/recommended-configuration.markdown b/doc/recommended-configuration.markdown
deleted file mode 100644
index 35ed652d..00000000
--- a/doc/recommended-configuration.markdown
+++ /dev/null
@@ -1,36 +0,0 @@
-Recommended Configuration
-=========================
-
-Server side
------------
-
-- Modern Linux/Unix operating system: **Ubuntu/Debian or FreeBSD**
-- Most recent version of PHP and Apache (Kanboard is compatible with PHP 5.3, 5.4, 5.5, 5.6 and 7.0)
-- Use the Sqlite database only when you have a disk with fast I/O (SSD disks) otherwise use Mysql or Postgresql
-
-Client side
------------
-
-- Use a modern browser: **Mozilla Firefox or Google Chrome or Safari**
-
-Tested configurations
----------------------
-
-The following configurations are tested with Kanboard but that doesn't mean all features are available:
-
-### Server
-
-- Ubuntu 14.04 LTS
-- Debian 6, 7 and 8
-- Centos 6.x and 7.0
-- Windows 2012 Server
-- Windows 2008 Server
-
-### Desktops
-
-- Last version of Mozilla Firefox, Safari and Google Chrome
-- Microsoft Internet Explorer 11
-
-### Tablets
-
-- iPad mini 3
diff --git a/doc/recurring-tasks.markdown b/doc/recurring-tasks.markdown
index b1de8d05..d7283a54 100644
--- a/doc/recurring-tasks.markdown
+++ b/doc/recurring-tasks.markdown
@@ -3,14 +3,14 @@ Recurring tasks
To fit with the Kanban methodology, the recurring tasks are not based on a date but on board events.
-- Recurring tasks are duplicated to the first column of the board when the selected events occurs
+- Recurring tasks are duplicated to the first column of the board when the selected events occur
- The due date can be recalculated automatically
- Each task records the task id of the parent task that created it and the child task created
Configuration
-------------
-Go to the task view page or use the dropdown menu on the board, then select **Edit recurrence**.
+Go to the task view page or use the drop-down menu on the board, then select **Edit recurrence**.
![Recurring task](http://kanboard.net/screenshots/documentation/recurring-tasks.png)
diff --git a/doc/requirements.markdown b/doc/requirements.markdown
new file mode 100644
index 00000000..cf2c7c8a
--- /dev/null
+++ b/doc/requirements.markdown
@@ -0,0 +1,103 @@
+Requirements
+============
+
+Server side
+-----------
+
+### Compatible Operating Systems
+
+| Operating System |
+|--------------------------------|
+| Linux Ubuntu Trusty 14.04 LTS |
+| Linux Centos 6.x |
+| Linux Centos 7.x |
+| Linux Redhat 6.x |
+| Linux Redhat 7.x |
+| Linux Debian 8 |
+| FreeBSD 10.x |
+| Microsoft Windows 2012 R2 |
+| Microsoft Windows 2008 |
+
+### Compatible Databases
+
+| Database |
+|--------------------|
+| Sqlite 3.x |
+| Mysql >= 5.5 |
+| MariaDB >= 10 |
+| Postgresql >= 9.3 |
+
+Which database to choose?
+
+| Type | Usage |
+|-----------------|-----------------------------------------------------|
+| Sqlite | Single user or small team (almost no concurrency) |
+| Mysql/Postgres | Larger team, high-availability configuration |
+
+Do not use Sqlite on NFS mounts, use Sqlite only when you have a disk with fast I/O
+
+### Compatible Web Servers
+
+| Web Server |
+|--------------------|
+| Apache HTTP Server |
+| Nginx |
+| Microsoft IIS |
+
+Kanboard is pre-configured to work with Apache (URL rewriting).
+
+### PHP Versions
+
+| PHP Version |
+|----------------|
+| PHP >= 5.3.3 |
+| PHP 5.4 |
+| PHP 5.5 |
+| PHP 5.6 |
+| PHP 7.x |
+
+### PHP Extensions Required
+
+| PHP Extensions required | Note |
+|----------------------------|-------------------------------|
+| pdo_sqlite | Only if you use Sqlite |
+| pdo_mysql | Only if you use Mysql/MariaDB |
+| pdo_pgsql | Only if you use Postgres |
+| gd | |
+| mbstring | |
+| openssl | |
+| json | |
+| hash | |
+| ctype | |
+| session | |
+| ldap | Only for LDAP authentication |
+| Zend OPcache | Recommended |
+
+### Recommendations
+
+- Modern Linux or Unix operating system.
+- Best performances are obtained with the latest version of PHP with OPcode caching activated.
+
+Client side
+-----------
+
+### Browsers
+
+Always use a modern browser with the latest version if possible:
+
+| Browser |
+|---------------------------------------|
+| Safari |
+| Google Chrome |
+| Mozilla Firefox |
+| Microsoft Internet Explorer >= 11 |
+| Microsoft Edge |
+
+### Devices
+
+| Device | Screen resolution |
+|-------------------|--------------------|
+| Laptop or desktop | >= 1366 x 768 |
+| Tablet | >= 1024 x 768 |
+
+Kanboard is not yet optimized for smartphones. It's working but the user interface is not really convenient to use.
diff --git a/doc/reverse-proxy-authentication.markdown b/doc/reverse-proxy-authentication.markdown
index 7c001f3d..609bad7a 100644
--- a/doc/reverse-proxy-authentication.markdown
+++ b/doc/reverse-proxy-authentication.markdown
@@ -8,18 +8,18 @@ The authentication is done by another system, Kanboard doesn't know your passwor
Requirements
------------
-- A well configured reverse proxy
+- A well-configured reverse proxy
or
-- Apache auth on the same server
+- Apache Auth on the same server
How does this work?
-------------------
1. Your reverse proxy authenticates the user and send the username through a HTTP header.
-2. Kanboard retreive the username from the request
+2. Kanboard retrieve the username from the request
- The user is created automatically if necessary
- Open a new Kanboard session without any prompt assuming it's valid
@@ -29,7 +29,7 @@ Installation instructions
### Setting up your reverse proxy
This is not in the scope of this documentation.
-You should check the user login is sent by the reverse proxy using a HTTP header, and find which one.
+You should check the user login is sent by the reverse proxy using a HTTP header, and find out which one.
### Setting up Kanboard
@@ -59,6 +59,6 @@ Notes:
- If the proxy is the same web server that runs Kanboard, according the [CGI protocol](http://www.ietf.org/rfc/rfc3875) the header name will be `REMOTE_USER`. By example, Apache add `REMOTE_USER` by default if `Require valid-user` is set.
-- If Apache is a reverse proxy to another Apache running Kanboard, the header `REMOTE_USER` is not set (same behaviour with IIS and Nginx).
+- If Apache is a reverse proxy to another Apache running Kanboard, the header `REMOTE_USER` is not set (same behavior with IIS and Nginx).
-- If you have a real reverse proxy, the [HTTP ICAP draft](http://tools.ietf.org/html/draft-stecher-icap-subid-00#section-3.4) proposes the header to be `X-Authenticated-User`. This de-facto standart has been adopted by a number of tools.
+- If you have a real reverse proxy, the [HTTP ICAP draft](http://tools.ietf.org/html/draft-stecher-icap-subid-00#section-3.4) proposes the header to be `X-Authenticated-User`. This de facto standard has been adopted by a number of tools.
diff --git a/doc/roles.markdown b/doc/roles.markdown
new file mode 100644
index 00000000..1b65a8c4
--- /dev/null
+++ b/doc/roles.markdown
@@ -0,0 +1,25 @@
+User Roles
+==========
+
+Application Roles
+-----------------
+
+Each Kanboard user has one of these roles:
+
+| Role | Description |
+|---------------|-----------------------------------------------------------------|
+| Administrator | Access to everything |
+| Manager | Can create team projects but cannot change application settings |
+| User | Can create private projects only |
+
+Project Roles
+-------------
+
+Each individual team project can assign a different role to each user and group:
+
+| Role | Description |
+|-----------------|----------------------------------------------------------------------|
+| Project Manager | Can change project settings, access to the Gantt chart and reports |
+| Project Member | Can create tasks and use the board |
+| Project Viewer | Read-only access to the board and tasks |
+
diff --git a/doc/rss.markdown b/doc/rss.markdown
index 57c81020..7930ec40 100644
--- a/doc/rss.markdown
+++ b/doc/rss.markdown
@@ -4,7 +4,7 @@ RSS/Atom subscriptions
Kanboard supports RSS feeds for projects and users.
- Project feeds contains only the activity the project
-- User feeds contains the activity stream of all projects the user is member
+- User feeds contains the activity stream of all projects the user is a member
Those subscriptions are only activated when the public access is enabled in the user profile or in the project settings.
@@ -20,4 +20,4 @@ Enable/disable user RSS feeds
Go to **User profile > Public access**.
-The RSS link is protected by a random token, only people who knows the url can access to the feed. \ No newline at end of file
+The RSS link is protected by a random token, only people who know the URL can access to the feed. \ No newline at end of file
diff --git a/doc/screenshots.markdown b/doc/screenshots.markdown
index 95972405..30a3c532 100644
--- a/doc/screenshots.markdown
+++ b/doc/screenshots.markdown
@@ -4,11 +4,11 @@ Adding screenshots
You can copy and paste images directly in Kanboard to save time.
These images are uploaded as attachments to the task.
-This is especially useful for taking screenshots to describe an issue by example.
+This is especially useful for taking screenshots to describe an issue for example.
You can add screenshots directly from the board by clicking on the dropdown menu or in the task view page.
-![Dropdown screenshot menu](http://kanboard.net/screenshots/documentation/dropdown-screenshot.png)
+![Drop-down screenshot menu](http://kanboard.net/screenshots/documentation/dropdown-screenshot.png)
To add a new image, take your screenshot and paste with CTRL+V or Command+V:
@@ -17,7 +17,7 @@ To add a new image, take your screenshot and paste with CTRL+V or Command+V:
On Mac OS X, you can use those shortcuts to take screenshots:
- Command-Control-Shift-3: Take a screenshot of the screen, and save it to the clipboard
-- Command-Control-Shift-4, then select an area: Take a screenshot of an area and save it to the clipboard
+- Command-Control-Shift-4, then select an area: Take a screenshot of the area and save it to the clipboard
- Command-Control-Shift-4, then space, then click a window: Take a screenshot of a window and save it to the clipboard
There are also several third-party applications that can be used to take screenshots with annotations and shapes.
diff --git a/doc/screenshots/groups-management.png b/doc/screenshots/groups-management.png
new file mode 100644
index 00000000..475b6827
--- /dev/null
+++ b/doc/screenshots/groups-management.png
Binary files differ
diff --git a/doc/screenshots/mention-autocomplete.png b/doc/screenshots/mention-autocomplete.png
new file mode 100644
index 00000000..f23fb6d1
--- /dev/null
+++ b/doc/screenshots/mention-autocomplete.png
Binary files differ
diff --git a/doc/screenshots/project-permissions.png b/doc/screenshots/project-permissions.png
new file mode 100644
index 00000000..c4450755
--- /dev/null
+++ b/doc/screenshots/project-permissions.png
Binary files differ
diff --git a/doc/search.markdown b/doc/search.markdown
index 34a20bc6..1a97a7fc 100644
--- a/doc/search.markdown
+++ b/doc/search.markdown
@@ -1,12 +1,12 @@
Advanced Search Syntax
======================
-Kanboard use a simple query language for advanced search.
+Kanboard uses a simple query language for advanced search.
Example of query
----------------
-This example will returns all tasks assigned to me with a due date for tomorrow and a title that contains "my title":
+This example will return all tasks assigned to me with a due date for tomorrow and a title that contains "my title":
```
assigne:me due:tomorrow my title
@@ -17,7 +17,7 @@ Search by task id or title
- Search by task id: `#123`
- Search by task id and task title: `123`
-- Search by task title: anything that don't match any search attributes
+- Search by task title: anything that doesn't match any search attributes
Search by status
----------------
@@ -27,8 +27,8 @@ Attribute: **status**
- Query to find open tasks: `status:open`
- Query to find closed tasks: `status:closed`
-Search by assignee
-------------------
+Search by assignees
+-------------------
Attribute: **assignee**
@@ -48,8 +48,8 @@ Attribute: **color**
- Query to search by color id: `color:blue`
- Query to search by color name: `color:"Deep Orange"`
-Search by due date
-------------------
+Search by the due date
+----------------------
Attribute: **due**
@@ -58,9 +58,9 @@ Attribute: **due**
- Search tasks due yesterday: `due:yesterday`
- Search tasks due with the exact date: `due:2015-06-29`
-The date must use the ISO8601 format: **YYYY-MM-DD**.
+The date must use the ISO 8601 format: **YYYY-MM-DD**.
-All string formats supported by the `strtotime()` function are supported, by example `next Thursday`, `-2 days`, `+2 months`, `tomorrow`, etc...
+All string formats supported by the `strtotime()` function are supported, for example `next Thursday`, `-2 days`, `+2 months`, `tomorrow`, etc.
Operators supported with a date:
@@ -74,11 +74,11 @@ Search by modification date
Attribute: **modified** or **updated**
-The date formats are the same as the due date.
+The date formats are the same as the due date.
There is also a filter by recently modified tasks: `modified:recently`.
-This query will use the same value as the board highlight period configured in settings.
+This query will use the same value as the board highlight period configured in settings.
Search by creation date
-----------------------
@@ -119,20 +119,28 @@ Attribute: **project**
- Find tasks by project id: `project:23`
- Find tasks for several projects: `project:"My project A" project:"My project B"`
-Search by column
-----------------
+Search by columns
+-----------------
Attribute: **column**
- Find tasks by column name: `column:"Work in progress"`
- Find tasks for several columns: `column:"Backlog" column:ready`
-Search by swimlane
-------------------
+Search by swim lane
+-------------------
Attribute: **swimlane**
-- Find tasks by swimlane: `swimlane:"Version 42"`
-- Find tasks in the default swimlane: `swimlane:default`
-- Find tasks into several swimlanes: `swimlane:"Version 1.2" swimlane:"Version 1.3"`
+- Find tasks by swim lane: `swimlane:"Version 42"`
+- Find tasks in the default swim lane: `swimlane:default`
+- Find tasks into several swim lanes: `swimlane:"Version 1.2" swimlane:"Version 1.3"`
+
+Search by task link
+------------------
+
+Attribute: **link**
+
+- Find tasks by link name: `link:"is a milestone of"`
+- Find tasks into several links: `link:"is a milestone of" link:"relates to"`
diff --git a/doc/sharing-projects.markdown b/doc/sharing-projects.markdown
index 17a552ce..c10b0f39 100644
--- a/doc/sharing-projects.markdown
+++ b/doc/sharing-projects.markdown
@@ -4,7 +4,7 @@ Sharing boards and tasks
By default, boards are private but it's possible to make a board public.
A public board **cannot be modified, it's a read-only access**.
-This access is protected by a random token, only people who have the right url can see the board.
+This access is protected by a random token, only people who have the right URL can see the board.
Public boards are automatically refreshed every 60 seconds.
Task details are also available in read-only.
diff --git a/doc/subtasks.markdown b/doc/subtasks.markdown
index e1acd98c..2819aaf4 100644
--- a/doc/subtasks.markdown
+++ b/doc/subtasks.markdown
@@ -17,7 +17,7 @@ From the task view, on left sidebar click on **Add a subtask**:
![Add a subtask](http://kanboard.net/screenshots/documentation/add-subtask.png)
-You can also add quickly a subtask by entering only the title:
+You can also add a subtask quickly by entering only the title:
![Add a subtask from the task view](http://kanboard.net/screenshots/documentation/add-subtask-shortcut.png)
@@ -40,5 +40,5 @@ Subtask timer
- Each time a subtask is in progress, the timer is also started. The timer can be started and stopped at any time.
- The timer records the time spent on the subtask automatically. You can also change manually the value of the time spent field when you edit a subtask.
- The time calculated is rounded to the nearest quarter. This information is recorded in a separate table.
-- The task time spent and time estimated are updated automatically according to the sum of all subtasks.
+- The task time spent and time estimated is updated automatically according to the sum of all subtasks.
diff --git a/doc/suse-installation.markdown b/doc/suse-installation.markdown
new file mode 100644
index 00000000..ce36c8f7
--- /dev/null
+++ b/doc/suse-installation.markdown
@@ -0,0 +1,14 @@
+Installation on OpenSuse
+========================
+
+OpenSuse Leap 42.1
+------------------
+
+```bash
+sudo zypper install php5 php5-sqlite php5-gd php5-json php5-mcrypt php5-mbstring php5-openssl
+cd /srv/www/htdocs
+sudo wget http://kanboard.net/kanboard-latest.zip
+sudo unzip kanboard-latest.zip
+sudo chmod -R 777 kanboard
+sudo rm kanboard-latest.zip
+```
diff --git a/doc/swimlanes.markdown b/doc/swimlanes.markdown
index 25e8b6b9..3a67fb89 100644
--- a/doc/swimlanes.markdown
+++ b/doc/swimlanes.markdown
@@ -1,8 +1,8 @@
-Swimlanes
-=========
+Swim lanes
+==========
Swimlanes are horizontal separations in your board.
-By example, it's useful to separate software releases, divide your tasks in different products, teams or what ever you want.
+By example, it's useful to separate software releases, divide your tasks in different products, teams or whatever you want.
Board with swimlanes
--------------------
diff --git a/doc/task-links.markdown b/doc/task-links.markdown
index 1eab51fd..e9ffb014 100644
--- a/doc/task-links.markdown
+++ b/doc/task-links.markdown
@@ -1,7 +1,7 @@
Task Links
==========
-Tasks can be linked together with predefined relationships:
+Tasks can be linked together with pre-defined relationships:
![Task Links](http://kanboard.net/screenshots/documentation/task-links.png)
diff --git a/doc/tests.markdown b/doc/tests.markdown
index 31937f33..b2d95491 100644
--- a/doc/tests.markdown
+++ b/doc/tests.markdown
@@ -1,7 +1,7 @@
-How to run units and functionals tests?
-=======================================
+Automated tests
+===============
-[PHPUnit](https://phpunit.de/) is used to run automatic tests on Kanboard.
+[PHPUnit](https://phpunit.de/) is used to run automated tests on Kanboard.
You can run tests across different databases (Sqlite, Mysql and Postgresql) to be sure that the result is the same everywhere.
@@ -9,31 +9,18 @@ Requirements
------------
- Linux/Unix machine
-- PHP command line
+- PHP cli
- PHPUnit installed
- Mysql and Postgresql (optional)
-Install the latest version of PHPUnit
--------------------------------------
-
-Simply download the PHPUnit PHAR et copy the file somewhere in your `$PATH`:
-
-```bash
-wget https://phar.phpunit.de/phpunit.phar
-chmod +x phpunit.phar
-sudo mv phpunit.phar /usr/local/bin/phpunit
-phpunit --version
-PHPUnit 4.2.6 by Sebastian Bergmann.
-```
-
-Running unit tests
-------------------
+Unit Tests
+----------
-### Testing with Sqlite
+### Test with Sqlite
-Sqlite tests use a in-memory database, nothing is written on the filesystem.
+Sqlite tests use a in-memory database, nothing is written on the file system.
-The config file is `tests/units.sqlite.xml`.
+The PHPUnit config file is `tests/units.sqlite.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.sqlite.xml`.
Example:
@@ -41,21 +28,26 @@ Example:
```bash
phpunit -c tests/units.sqlite.xml
-PHPUnit 4.2.6 by Sebastian Bergmann.
+PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
-Configuration read from /Volumes/Devel/apps/kanboard/tests/units.sqlite.xml
+............................................................... 63 / 649 ( 9%)
+............................................................... 126 / 649 ( 19%)
+............................................................... 189 / 649 ( 29%)
+............................................................... 252 / 649 ( 38%)
+............................................................... 315 / 649 ( 48%)
+............................................................... 378 / 649 ( 58%)
+............................................................... 441 / 649 ( 67%)
+............................................................... 504 / 649 ( 77%)
+............................................................... 567 / 649 ( 87%)
+............................................................... 630 / 649 ( 97%)
+................... 649 / 649 (100%)
-................................................................. 65 / 74 ( 87%)
-.........
+Time: 1.22 minutes, Memory: 151.25Mb
-Time: 9.05 seconds, Memory: 17.75Mb
-
-OK (74 tests, 6145 assertions)
+OK (649 tests, 43595 assertions)
```
-**NOTE:** PHPUnit is already included in the Vagrant environment
-
-### Testing with Mysql
+### Test with Mysql
You must have Mysql or MariaDb installed on localhost.
@@ -68,27 +60,10 @@ By default, those credentials are used:
For each execution the database is dropped and created again.
-The config file is `tests/units.mysql.xml`.
+The PHPUnit config file is `tests/units.mysql.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.mysql.xml`.
-Example:
-
-```bash
-phpunit -c tests/units.mysql.xml
-
-PHPUnit 4.2.6 by Sebastian Bergmann.
-
-Configuration read from /Volumes/Devel/apps/kanboard/tests/units.mysql.xml
-
-................................................................. 65 / 74 ( 87%)
-.........
-
-Time: 49.77 seconds, Memory: 17.50Mb
-
-OK (74 tests, 6145 assertions)
-```
-
-### Testing with Postgresql
+### Test with Postgresql
You must have Postgresql installed on localhost.
@@ -100,37 +75,20 @@ By default, those credentials are used:
- Database: **kanboard_unit_test**
Be sure to allow the user `postgres` to create and drop databases.
-For each execution the database is dropped and created again.
+The database is recreated for each execution.
-The config file is `tests/units.postgres.xml`.
+The PHPUnit config file is `tests/units.postgres.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.postgres.xml`.
-Example:
-
-```bash
-phpunit -c tests/units.postgres.xml
-
-PHPUnit 4.2.6 by Sebastian Bergmann.
-
-Configuration read from /Volumes/Devel/apps/kanboard/tests/units.postgres.xml
-
-................................................................. 65 / 74 ( 87%)
-.........
-
-Time: 52.66 seconds, Memory: 17.50Mb
-
-OK (74 tests, 6145 assertions)
-```
-
-Running functionals tests
--------------------------
+Integration Tests
+-----------------
Actually only the API calls are tested.
Real HTTP calls are made with those tests.
-So a local instance of Kanboard is necessary and must listen on `http://localhost:8000`.
+So a local instance of Kanboard is necessary and must listen on `http://localhost:8000/`.
-Don't forget that all data will be removed/altered by the test suite.
+All data will be removed/altered by the test suite.
Moreover the script will reset and set a new API key.
1. Start a local instance of Kanboard `php -S 127.0.0.1:8000`
@@ -138,27 +96,27 @@ Moreover the script will reset and set a new API key.
The same method as above is used to run tests across different databases:
-- Sqlite: `phpunit -c tests/functionals.sqlite.xml`
-- Mysql: `phpunit -c tests/functionals.mysql.xml`
-- Postgresql: `phpunit -c tests/functionals.postgres.xml`
+- Sqlite: `phpunit -c tests/integration.sqlite.xml`
+- Mysql: `phpunit -c tests/integration.mysql.xml`
+- Postgresql: `phpunit -c tests/integration.postgres.xml`
Example:
```bash
-phpunit -c tests/functionals.sqlite.xml
-
-PHPUnit 4.2.6 by Sebastian Bergmann.
+phpunit -c tests/integration.sqlite.xml
-Configuration read from /Volumes/Devel/apps/kanboard/tests/functionals.sqlite.xml
+PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
-..........................................
+............................................................... 63 / 135 ( 46%)
+............................................................... 126 / 135 ( 93%)
+......... 135 / 135 (100%)
-Time: 1.72 seconds, Memory: 4.25Mb
+Time: 1.18 minutes, Memory: 14.75Mb
-OK (42 tests, 160 assertions)
+OK (135 tests, 526 assertions)
```
-Continuous Integration with Travis-ci
+Continuous Integration with Travis-CI
-------------------------------------
After each commit pushed on the main repository, unit tests are executed across 5 different versions of PHP:
@@ -171,6 +129,4 @@ After each commit pushed on the main repository, unit tests are executed across
Each version of PHP is tested against the 3 supported database: Sqlite, Mysql and Postgresql.
-That mean we run 15 jobs each time the repository is updated. The execution time is around 25 minutes.
-
The Travis config file `.travis.yml` is located on the root directory of Kanboard.
diff --git a/doc/time-tracking.markdown b/doc/time-tracking.markdown
index 8c65b16a..528f91b3 100644
--- a/doc/time-tracking.markdown
+++ b/doc/time-tracking.markdown
@@ -13,7 +13,7 @@ Tasks have two fields:
- Time estimated
- Time spent
-These values represents hours of work and have to be set manually.
+These values represent hours of work and have to be set manually.
Subtask time tracking
---------------------
@@ -37,7 +37,7 @@ For each subtask, the timer can be stopped/started at any time:
![Subtask timer](http://kanboard.net/screenshots/documentation/subtask-timer.png)
-- The timer doesn't depends of the subtask status
+- The timer doesn't depend of the subtask status
- Each time you start the timer a new record is created in the time tracking table
- Each time you stop the clock the end date is recorded in the time tracking table
- The calculated time spent is rounded to the nearest quarter
diff --git a/doc/transitions.markdown b/doc/transitions.markdown
index c32f696a..b52ccff0 100644
--- a/doc/transitions.markdown
+++ b/doc/transitions.markdown
@@ -5,12 +5,12 @@ Transitions record each movement of the tasks between columns.
![Transitions](http://kanboard.net/screenshots/documentation/transitions.png)
-Available from the task view, you can see those information:
+Available from the task view, you can see that information:
- Date of the action
- Source column
- Destination column
-- Executer (user that move the task)
+- Executor (users that moves the task)
- Time spent in the origin column
Task transition data can also be exported from the project settings page.
diff --git a/doc/translations.markdown b/doc/translations.markdown
index 7a4e325b..00707e1c 100644
--- a/doc/translations.markdown
+++ b/doc/translations.markdown
@@ -5,19 +5,19 @@ How to translate Kanboard to a new language?
--------------------------------------------
- Translations are stored inside the directory `app/Locale`
-- There is sub-directory for each language, by example for the French we have `fr_FR`, Italian `it_IT` etc...
+- There is a subdirectory for each language, for example in French we have `fr_FR`, Italian `it_IT` etc.
- A translation is a PHP file that returns an Array with a key-value pairs
-- The key is the original text in english and the value is the translation for the corresponding language
+- The key is the original text in English and the value is the translation of the corresponding language
- **French translations are always up to date**
- Always use the last version (branch master)
### Create a new translation:
-1. Make a new directory: `app/Locale/xx_XX` by example `app/Locale/fr_CA` for French Canadian
+1. Make a new directory: `app/Locale/xx_XX` for example `app/Locale/fr_CA` for French Canadian
2. Create a new file for the translation: `app/Locale/xx_XX/translations.php`
3. Use the content of the French locales and replace the values
4. Inside the file `app/Model/Config.php`, add a new entry for your translation inside the function `getLanguages()`
-5. Check with your local installation of Kanboard if everything is ok
+5. Check with your local installation of Kanboard if everything is OK
6. Send a [pull-request with Github](https://help.github.com/articles/using-pull-requests/)
How to update an existing translation?
@@ -32,22 +32,11 @@ How to add new translated text in the application?
Translations are displayed with the following functions in the source code:
-- `t()`: dispaly text with HTML escaping
+- `t()`: display text with HTML escaping
- `e()`: display text without HTML escaping
-- `dt()`: display date and time using the `strftime()` function formats
Always use the english version in the source code.
-### Date and time translation
-
-Date strings use the function `strftime()` to format the date.
-
-By example, the original English version can be defined like that `Created on %B %e, %Y at %k:%M %p` and that will output something like that `Created on January 11, 2015 at 15:19 PM`. The French version can be modified to display a different format, `Créé le %d/%m/%Y à %H:%M` and the result will be `Créé le 11/01/2015 à 15:19`.
-
-All formats are available in the [PHP documentation](http://php.net/strftime).
-
-### Placeholders
-
Text strings use the function `sprintf()` to replace elements:
- `%s` is used to replace a string
@@ -76,4 +65,4 @@ From a Unix shell run this command:
./kanboard locale:sync
```
-The French translation is used a reference for other locales.
+The French translation is used a reference to other locales.
diff --git a/doc/ubuntu-installation.markdown b/doc/ubuntu-installation.markdown
index 5f9ee65b..ab4dfe7c 100644
--- a/doc/ubuntu-installation.markdown
+++ b/doc/ubuntu-installation.markdown
@@ -1,17 +1,17 @@
How to install Kanboard on Ubuntu?
==================================
-Ubuntu 14.04 LTS
-----------------
+Ubuntu Trusty 14.04 LTS
+-----------------------
Install Apache and PHP:
```bash
sudo apt-get update
-sudo apt-get install -y php5 php5-sqlite unzip
+sudo apt-get install -y php5 php5-sqlite php5-gd php5-json php5-mcrypt unzip
```
-In case your webserver was running restart to make sure the php modules are reloaded
+In case your web server was running restart to make sure the php modules are reloaded:
```bash
service apache2 restart
@@ -26,3 +26,5 @@ sudo unzip kanboard-latest.zip
sudo chown -R www-data:www-data kanboard/data
sudo rm kanboard-latest.zip
```
+
+Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
diff --git a/doc/update.markdown b/doc/update.markdown
index 0c59a85e..7be8a65a 100644
--- a/doc/update.markdown
+++ b/doc/update.markdown
@@ -1,26 +1,33 @@
-Update
-======
+Upgrade Kanboard to a new version
+=================================
-**Always make a backup of your database before upgrading!**
+Upgrading Kanboard to a newer version is seamless.
+The process can be summarized to simply copy your data folder to the new Kanboard folder.
+Kanboard will run database migrations automatically for you.
+
+Important things to do before updating
+--------------------------------------
+
+- Always make a backup of your data before upgrading
+- Always read the [change log](https://github.com/fguillot/kanboard/blob/master/ChangeLog) to check for breaking changes
+- Always close all user sessions (flush all sessions on the server)
From the archive (stable version)
---------------------------------
-1. Close all sessions (logout)
-2. Rename your actual Kanboard directory (to keep a backup)
-3. Uncompress the new archive and copy your `data` directory to the newly uncompressed directory.
-4. Copy your custom `config.php` (if you created one) to the root of the newly uncompressed directory.
-5. Make the directory `data` writeable by the web server user
-6. Login and check if everything is ok
-7. Remove the old Kanboard directory
-
+1. Decompress the new archive
+2. Copy the content of your data folder into the newly uncompressed directory
+3. Copy your custom `config.php` if you have one
+4. Copy your plugins if necessary
+5. Make sure the directory `data` is writeable by your web server user
+6. Test
+7. Remove your old Kanboard directory
From the repository (development version)
-----------------------------------------
-1. Close all sessions
-2. `git pull`
-3. `composer install`
+1. `git pull`
+2. `composer install`
3. Login and check if everything is ok
Note: This method will install the **current development version**, use at your own risk.
diff --git a/doc/usage-examples.markdown b/doc/usage-examples.markdown
index f2d457a6..5efc6f9a 100644
--- a/doc/usage-examples.markdown
+++ b/doc/usage-examples.markdown
@@ -1,4 +1,4 @@
-Usage examples
+Usage Examples
==============
You can customize your boards according to your business activities:
diff --git a/doc/user-management.markdown b/doc/user-management.markdown
index 942eea09..38033eb3 100644
--- a/doc/user-management.markdown
+++ b/doc/user-management.markdown
@@ -1,60 +1,14 @@
User management
===============
-Roles at the application level
-------------------------------
-
-Kanboard use a basic permission system, there are 3 type of users:
-
-### Administrators
-
-- Access to everything
-
-### Project Administrators
-
-- Can create multi-users and private projects
-- Can convert multi-users and private projects
-- Can see only their own projects
-- Cannot change application settings
-- Cannot manage users
-
-### Standard Users
-
-- Can create only private projects
-- Can see only their own projects
-- Cannot remove projects
-
-Roles at the project level
---------------------------
-
-These role are related to the project permission.
-
-### Project Managers
-
-- Can manage only their own projects
-- Can access to reports and budget section
-
-### Project Members
-
-- Can do any daily operations in their projects (create and move tasks...)
-- Cannot configure projects
-
-Note: Any "Standard User" can be promoted "Project Manager" for a given project, they don't necessary need to be "Project Administrator".
-
-Local and remote users
-----------------------
-
-- A local user is an account that use the database to store credentials. Local users use the login form for the authentication.
-- A remote user is an account that use an external system to store credentials. By example, it can be LDAP, Github or Google accounts. Authentication of these users can be done through the login form or not.
-
Add a new user
--------------
-To add a new user, you must be administrator.
+To add a new user, you must be an administrator.
1. From the dashboard, go to the menu **User Management**
2. On the top, you have a link **New local user** or **New remote user**
-3. Fill the form and save
+3. Fill out the form and save
![New user](http://kanboard.net/screenshots/documentation/new-user.png)
@@ -71,11 +25,11 @@ Edit users
When you go to the **users** menu, you have the list of users, to modify a user click on the **edit link**.
- If you are a regular user, you can change only your own profile
-- You have to be administrator to be able to edit any users
+- You have to be an administrator to be able to edit any users
Remove users
------------
-From the **users** menu, click on the link **remove**. This link is visible only if you are administrator.
+From the **users** menu, click on the link **remove**. This link is visible only if you are administrators.
If you remove a specific user, **tasks assigned to this person will be unassigned** after the operation.
diff --git a/doc/user-mentions.markdown b/doc/user-mentions.markdown
new file mode 100644
index 00000000..156456d6
--- /dev/null
+++ b/doc/user-mentions.markdown
@@ -0,0 +1,17 @@
+User Mentions
+=============
+
+Kanboard offers the possibility to send notifications when someone is mentioned.
+
+If you need to get the attention of someone in a comment or in a task, use the @ symbol followed by their username.
+Kanboard will automatically suggest a list of users:
+
+![User Mention](screenshots/mention-autocomplete.png)
+
+- At the moment, only the task description and the comment text area have this feature enabled.
+- The user mentions works only during tasks and comments creation.
+- To be notified, mentioned users need to be a member of the project.
+- When someone is mentioned, this user will receive a notification.
+- The @username mention is linked to the public user profile.
+
+The notification is sent according to the user settings, it can be an email, a web notification or even a message on Slack/Hipchat/Jabber if you have installed the right plugins.
diff --git a/doc/user-types.markdown b/doc/user-types.markdown
new file mode 100644
index 00000000..8c88a7dd
--- /dev/null
+++ b/doc/user-types.markdown
@@ -0,0 +1,14 @@
+User Types
+==========
+
+In Kanboard there are two types of users:
+
+| Type | Description |
+|---------------|-----------------------------------------------------------------------|
+| Local User | User that stores his password in Kanboard's database |
+| Remote User | User credentials are managed by another system (Example: LDAP server) |
+
+Examples of remote users:
+
+- LDAP user
+- Users authenticated by a reverse-proxy
diff --git a/doc/vagrant.markdown b/doc/vagrant.markdown
index 8c41e141..d11bca84 100644
--- a/doc/vagrant.markdown
+++ b/doc/vagrant.markdown
@@ -41,7 +41,7 @@ The current directory is synced to the Apache document root.
Composer dependencies have to be there, so if you didn't run `composer install` on your host machine you can also do it on the guest machine.
-Each box have its own TCP port:
+Each box has its own TCP port:
- ubuntu: http://localhost:8001/
- debian8: http://localhost:8002/
@@ -61,4 +61,4 @@ Available boxes are:
- `vagrant up centos6`
- `vagrant up freebsd10`
-Any specific configuration have to done manually (Postgres or Mysql).
+Any specific configuration has to be done manually (Postgres or Mysql).
diff --git a/doc/webhooks.markdown b/doc/webhooks.markdown
index f7925350..d6d22400 100644
--- a/doc/webhooks.markdown
+++ b/doc/webhooks.markdown
@@ -1,20 +1,20 @@
-Webhooks
-========
+Web Hooks
+=========
Webhooks are useful to perform actions with external applications.
- Webhooks can be used to create a task by calling a simple URL (You can also do that with the API)
- An external URL can be called automatically when an event occurs in Kanboard (task creation, comment updated, etc)
-How to write a webhook receiver?
---------------------------------
+How to write a web hook receiver?
+---------------------------------
All internal events of Kanboard can be sent to an external URL.
-- The webhook url have to be defined in **Settings > Webhooks > Webhook URL**.
-- When an event is triggered Kanboard call automatically the predefined URL
+- The web hook URL has to be defined in **Settings > Webhooks > Webhook URL**.
+- When an event is triggered Kanboard calls the pre-defined URL automatically
- The data are encoded in JSON format and sent with a POST HTTP request
-- The webhook token is also sent as a query string parameter, so you can check if the request really come from Kanboard.
+- The web hook token is also sent as a query string parameter, so you can check if the request really comes from Kanboard.
- **Your custom URL must answer in less than 1 second**, those requests are synchronous (PHP limitation) and that can slow down the user interface if your script is too slow!
### List of supported events
@@ -270,10 +270,8 @@ Screenshot created:
}
```
-Note: Webhooks configuration and payload have changed since Kanboard >= 1.0.15
-
-How to create a task with a webhook?
-------------------------------------
+How to create a task with a web hook?
+-------------------------------------
Firstly, you have to get the token from the settings page. After that, just call this url from anywhere:
@@ -288,7 +286,7 @@ curl "http://myserver/?controller=webhook&action=task&token=superSecretToken&tit
### Available responses
- When a task is created successfully, Kanboard return the message "OK" in plain text.
-- However if the task creation fail, you will got a "FAILED" message.
+- However if the task creation fail, you will get a "FAILED" message.
- If the token is wrong, you got a "Not Authorized" message and a HTTP status code 401.
### Available parameters
@@ -298,9 +296,9 @@ Base URL: `http://YOUR_SERVER_HOSTNAME/?controller=webhook&action=task`
- `token`: Token displayed on the settings page (required)
- `title`: Task title (required)
- `description`: Task description
-- `color_id`: Supported colors are yellow, blue, green, purple, red, orange and grey
+- `color_id`: Supported colors are yellow, blue, green, purple, red, orange and gray
- `project_id`: Project id (Get the id from the project page)
-- `owner_id`: Assignee (Get the user id from the users page)
+- `owner_id`: Assignee (Get the user id from the user's page)
- `column_id`: Column on the board (Get the column id from the projects page, mouse over on the column name)
Only the token and the title parameters are mandatory. The different id can also be found in the database.
diff --git a/doc/what-is-kanban.markdown b/doc/what-is-kanban.markdown
index 3a37bb7a..5d2eed02 100644
--- a/doc/what-is-kanban.markdown
+++ b/doc/what-is-kanban.markdown
@@ -3,7 +3,7 @@ What is Kanban?
Kanban is a methodology originally developed by Toyota to be more efficient.
-There is only two constraints imposed by Kanban:
+There are only two constraints imposed by Kanban:
- Visualize your workflow
- Limit your work in progress
@@ -12,14 +12,14 @@ Visualize your workflow
-----------------------
- Your work is displayed on a board, you have a clear overview of your project
-- Each column represent a step in your workflow
+- Each column represents a step in your workflow
Bring focus and avoid multitasking
----------------------------------
-- Each phase can have a work in progress limit
+- Each phase can have a work in progress limits
- Limits are great to identify bottlenecks
-- Limits avoid working on too many tasks in the same time
+- Limits avoid working on too many tasks at the same time
Measure performance and improvement
-----------------------------------
@@ -29,4 +29,4 @@ Kanban uses lead and cycle times to measure performance:
- **Lead time**: Time between the task is created and completed
- **Cycle time**: Time between the task is started and completed
-By example, you may have a lead time of 100 days and only have to work 1 hour to complete the task.
+For example, you may have a lead time of 100 days and only have to work 1 hour to complete the task.
diff --git a/doc/windows-apache-installation.markdown b/doc/windows-apache-installation.markdown
index 25d39373..27b6812e 100644
--- a/doc/windows-apache-installation.markdown
+++ b/doc/windows-apache-installation.markdown
@@ -3,7 +3,7 @@ Installation on Windows Server and Apache
This guide will help you to setup step by step Kanboard on a Windows Server with Apache and PHP.
-Note: If you have a 64 bits platform choose "x64" otherwise choose "x86" for 32 bits systems.
+Note: If you have a 64 bits platform choose "x64" otherwise choose "x86" for 32-bit systems.
Visual C++ Redistributable Installation
---------------------------------------
@@ -65,7 +65,6 @@ extension_dir = "C:/php/ext"
Uncomment these PHP modules:
```ini
-extension=php_curl.dll
extension=php_gd2.dll
extension=php_ldap.dll
extension=php_mbstring.dll
@@ -73,13 +72,13 @@ extension=php_openssl.dll
extension=php_pdo_sqlite.dll
```
-Set the timezone:
+Set the time zone:
```ini
date.timezone = America/Montreal
```
-The list of supported timezones can be found in the [PHP documentation](http://php.net/manual/en/timezones.america.php).
+The list of supported time zones can be found in the [PHP documentation](http://php.net/manual/en/timezones.america.php).
Load the PHP module for Apache:
@@ -116,7 +115,7 @@ Kanboard installation
---------------------
- Download the zip file
-- Uncompress the archive in `C:\Apache24\htdocs\kanboard` by example
+- Decompress the archive in `C:\Apache24\htdocs\kanboard` by example
- Open your web browser to use Kanboard http://localhost/kanboard/
- The default credentials are **admin/admin**
@@ -124,3 +123,8 @@ Tested configuration
--------------------
- Windows 2008 R2 / Apache 2.4.12 / PHP 5.6.8
+
+Notes
+-----
+
+- Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
diff --git a/doc/windows-iis-installation.markdown b/doc/windows-iis-installation.markdown
index 84a2e53c..bd4607de 100644
--- a/doc/windows-iis-installation.markdown
+++ b/doc/windows-iis-installation.markdown
@@ -15,7 +15,6 @@ PHP installation
Edit the `php.ini`, uncomment these PHP modules:
```ini
-extension=php_curl.dll
extension=php_gd2.dll
extension=php_ldap.dll
extension=php_mbstring.dll
@@ -23,13 +22,13 @@ extension=php_openssl.dll
extension=php_pdo_sqlite.dll
```
-Set the timezone:
+Set the time zone:
```ini
date.timezone = America/Montreal
```
-The list of supported timezones can be found in the [PHP documentation](http://php.net/manual/en/timezones.america.php).
+The list of supported time zones can be found in the [PHP documentation](http://php.net/manual/en/timezones.america.php).
Check if PHP runs correctly:
@@ -56,7 +55,7 @@ Kanboard installation
---------------------
- Download the zip file
-- Uncompress the archive in `C:\inetpub\wwwroot\kanboard` by example
+- Decompress the archive in `C:\inetpub\wwwroot\kanboard` by example
- Make sure the directory `data` is writable by the IIS user
- Open your web browser to use Kanboard http://localhost/kanboard/
- The default credentials are **admin/admin**
@@ -66,3 +65,9 @@ Tested configurations
- Windows 2008 R2 Standard Edition / IIS 7.5 / PHP 5.5.16
- Windows 2012 Standard Edition / IIS 8.5 / PHP 5.3.29
+
+Notes
+-----
+
+- Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
+