summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Action/CommentCreation.php2
-rw-r--r--app/Action/TaskAssignCategoryLabel.php2
-rw-r--r--app/Action/TaskAssignUser.php2
-rw-r--r--app/Action/TaskClose.php11
-rw-r--r--app/Action/TaskCreation.php4
-rw-r--r--app/Action/TaskOpen.php2
-rw-r--r--app/Controller/Project.php17
-rw-r--r--app/Controller/Webhook.php22
-rw-r--r--app/Integration/Base.php49
-rw-r--r--app/Integration/GithubWebhook.php (renamed from app/Model/GithubWebhook.php)9
-rw-r--r--app/Integration/GitlabWebhook.php213
-rw-r--r--app/Locale/da_DK/translations.php11
-rw-r--r--app/Locale/de_DE/translations.php11
-rw-r--r--app/Locale/es_ES/translations.php11
-rw-r--r--app/Locale/fi_FI/translations.php11
-rw-r--r--app/Locale/fr_FR/translations.php11
-rw-r--r--app/Locale/hu_HU/translations.php11
-rw-r--r--app/Locale/it_IT/translations.php11
-rw-r--r--app/Locale/ja_JP/translations.php11
-rw-r--r--app/Locale/pl_PL/translations.php11
-rw-r--r--app/Locale/pt_BR/translations.php11
-rw-r--r--app/Locale/ru_RU/translations.php11
-rw-r--r--app/Locale/sv_SE/translations.php11
-rw-r--r--app/Locale/th_TH/translations.php11
-rw-r--r--app/Locale/zh_CN/translations.php11
-rw-r--r--app/Model/Acl.php2
-rw-r--r--app/Model/Action.php5
-rw-r--r--app/Model/Base.php1
-rw-r--r--app/ServiceProvider/ClassProvider.php5
-rw-r--r--app/Subscriber/ProjectModificationDateSubscriber.php2
-rw-r--r--app/Template/project/integrations.php15
-rw-r--r--app/Template/project/show.php11
-rw-r--r--app/Template/project/sidebar.php3
-rw-r--r--tests/units/ActionTaskCloseTest.php2
-rw-r--r--tests/units/ActionTest.php6
-rw-r--r--tests/units/GitlabWebhookTest.php116
36 files changed, 597 insertions, 58 deletions
diff --git a/app/Action/CommentCreation.php b/app/Action/CommentCreation.php
index 5dbe32f1..27ed011b 100644
--- a/app/Action/CommentCreation.php
+++ b/app/Action/CommentCreation.php
@@ -2,7 +2,7 @@
namespace Action;
-use Model\GithubWebhook;
+use Integration\GithubWebhook;
/**
* Create automatically a comment from a webhook
diff --git a/app/Action/TaskAssignCategoryLabel.php b/app/Action/TaskAssignCategoryLabel.php
index 19064526..1383d491 100644
--- a/app/Action/TaskAssignCategoryLabel.php
+++ b/app/Action/TaskAssignCategoryLabel.php
@@ -2,7 +2,7 @@
namespace Action;
-use Model\GithubWebhook;
+use Integration\GithubWebhook;
/**
* Set a category automatically according to a label
diff --git a/app/Action/TaskAssignUser.php b/app/Action/TaskAssignUser.php
index f24ff415..cf2a9a4b 100644
--- a/app/Action/TaskAssignUser.php
+++ b/app/Action/TaskAssignUser.php
@@ -2,7 +2,7 @@
namespace Action;
-use Model\GithubWebhook;
+use Integration\GithubWebhook;
/**
* Assign a task to someone
diff --git a/app/Action/TaskClose.php b/app/Action/TaskClose.php
index 6cf9be05..760dfd84 100644
--- a/app/Action/TaskClose.php
+++ b/app/Action/TaskClose.php
@@ -2,7 +2,8 @@
namespace Action;
-use Model\GithubWebhook;
+use Integration\GitlabWebhook;
+use Integration\GithubWebhook;
use Model\Task;
/**
@@ -25,6 +26,8 @@ class TaskClose extends Base
Task::EVENT_MOVE_COLUMN,
GithubWebhook::EVENT_COMMIT,
GithubWebhook::EVENT_ISSUE_CLOSED,
+ GitlabWebhook::EVENT_COMMIT,
+ GitlabWebhook::EVENT_ISSUE_CLOSED,
);
}
@@ -39,6 +42,8 @@ class TaskClose extends Base
switch ($this->event_name) {
case GithubWebhook::EVENT_COMMIT:
case GithubWebhook::EVENT_ISSUE_CLOSED:
+ case GitlabWebhook::EVENT_COMMIT:
+ case GitlabWebhook::EVENT_ISSUE_CLOSED:
return array();
default:
return array('column_id' => t('Column'));
@@ -56,6 +61,8 @@ class TaskClose extends Base
switch ($this->event_name) {
case GithubWebhook::EVENT_COMMIT:
case GithubWebhook::EVENT_ISSUE_CLOSED:
+ case GitlabWebhook::EVENT_COMMIT:
+ case GitlabWebhook::EVENT_ISSUE_CLOSED:
return array('task_id');
default:
return array('task_id', 'column_id');
@@ -86,6 +93,8 @@ class TaskClose extends Base
switch ($this->event_name) {
case GithubWebhook::EVENT_COMMIT:
case GithubWebhook::EVENT_ISSUE_CLOSED:
+ case GitlabWebhook::EVENT_COMMIT:
+ case GitlabWebhook::EVENT_ISSUE_CLOSED:
return true;
default:
return $data['column_id'] == $this->getParam('column_id');
diff --git a/app/Action/TaskCreation.php b/app/Action/TaskCreation.php
index 9b7a0b13..1c093eee 100644
--- a/app/Action/TaskCreation.php
+++ b/app/Action/TaskCreation.php
@@ -2,7 +2,8 @@
namespace Action;
-use Model\GithubWebhook;
+use Integration\GithubWebhook;
+use Integration\GitlabWebhook;
/**
* Create automatically a task from a webhook
@@ -22,6 +23,7 @@ class TaskCreation extends Base
{
return array(
GithubWebhook::EVENT_ISSUE_OPENED,
+ GitlabWebhook::EVENT_ISSUE_OPENED,
);
}
diff --git a/app/Action/TaskOpen.php b/app/Action/TaskOpen.php
index fc29e9eb..73f1fad3 100644
--- a/app/Action/TaskOpen.php
+++ b/app/Action/TaskOpen.php
@@ -2,7 +2,7 @@
namespace Action;
-use Model\GithubWebhook;
+use Integration\GithubWebhook;
/**
* Open automatically a task
diff --git a/app/Controller/Project.php b/app/Controller/Project.php
index 9037a91a..a7e8a39b 100644
--- a/app/Controller/Project.php
+++ b/app/Controller/Project.php
@@ -52,7 +52,6 @@ class Project extends Base
$this->response->html($this->projectLayout('project/show', array(
'project' => $project,
'stats' => $this->project->getStats($project['id']),
- 'webhook_token' => $this->config->get('webhook_token'),
'title' => $project['name'],
)));
}
@@ -153,6 +152,22 @@ class Project extends Base
}
/**
+ * Integrations page
+ *
+ * @access public
+ */
+ public function integration()
+ {
+ $project = $this->getProjectManagement();
+
+ $this->response->html($this->projectLayout('project/integrations', array(
+ 'project' => $project,
+ 'title' => t('Integrations'),
+ 'webhook_token' => $this->config->get('webhook_token'),
+ )));
+ }
+
+ /**
* Display a form to edit a project
*
* @access public
diff --git a/app/Controller/Webhook.php b/app/Controller/Webhook.php
index dcd66a1a..1ae3b0a4 100644
--- a/app/Controller/Webhook.php
+++ b/app/Controller/Webhook.php
@@ -57,7 +57,27 @@ class Webhook extends Base
$result = $this->githubWebhook->parsePayload(
$this->request->getHeader('X-Github-Event'),
- $this->request->getJson()
+ $this->request->getJson() ?: array()
+ );
+
+ echo $result ? 'PARSED' : 'IGNORED';
+ }
+
+ /**
+ * Handle Gitlab webhooks
+ *
+ * @access public
+ */
+ public function gitlab()
+ {
+ if ($this->config->get('webhook_token') !== $this->request->getStringParam('token')) {
+ $this->response->text('Not Authorized', 401);
+ }
+
+ $this->gitlabWebhook->setProjectId($this->request->getIntegerParam('project_id'));
+
+ $result = $this->gitlabWebhook->parsePayload(
+ $this->request->getJson() ?: array()
);
echo $result ? 'PARSED' : 'IGNORED';
diff --git a/app/Integration/Base.php b/app/Integration/Base.php
new file mode 100644
index 00000000..babf8c8f
--- /dev/null
+++ b/app/Integration/Base.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Integration;
+
+use Pimple\Container;
+
+/**
+ * Base class
+ *
+ * @package integration
+ * @author Frederic Guillot
+ *
+ * @property \Model\Task $task
+ * @property \Model\TaskFinder $taskFinder
+ * @property \Model\User $user
+ */
+abstract class Base
+{
+ /**
+ * Container instance
+ *
+ * @access protected
+ * @var \Pimple\Container
+ */
+ protected $container;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param \Pimple\Container $container
+ */
+ public function __construct(Container $container)
+ {
+ $this->container = $container;
+ }
+
+ /**
+ * Load automatically class from the container
+ *
+ * @access public
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return $this->container[$name];
+ }
+}
diff --git a/app/Model/GithubWebhook.php b/app/Integration/GithubWebhook.php
index f66358eb..fd0b49f6 100644
--- a/app/Model/GithubWebhook.php
+++ b/app/Integration/GithubWebhook.php
@@ -1,13 +1,14 @@
<?php
-namespace Model;
+namespace Integration;
use Event\GenericEvent;
+use Model\Task;
/**
- * Github Webhook model
+ * Github Webhook
*
- * @package model
+ * @package integration
* @author Frederic Guillot
*/
class GithubWebhook extends Base
@@ -89,7 +90,7 @@ class GithubWebhook extends Base
continue;
}
- if ($task['is_active'] == Task::STATUS_OPEN) {
+ if ($task['is_active'] == Task::STATUS_OPEN && $task['project_id'] == $this->project_id) {
$this->container['dispatcher']->dispatch(
self::EVENT_COMMIT,
new GenericEvent(array('task_id' => $task_id) + $task)
diff --git a/app/Integration/GitlabWebhook.php b/app/Integration/GitlabWebhook.php
new file mode 100644
index 00000000..f5df32a6
--- /dev/null
+++ b/app/Integration/GitlabWebhook.php
@@ -0,0 +1,213 @@
+<?php
+
+namespace Integration;
+
+use Event\GenericEvent;
+use Event\TaskEvent;
+use Model\Task;
+
+/**
+ * Gitlab Webhook
+ *
+ * @package integration
+ * @author Frederic Guillot
+ */
+class GitlabWebhook extends Base
+{
+ /**
+ * Events
+ *
+ * @var string
+ */
+ const EVENT_ISSUE_OPENED = 'gitlab.webhook.issue.opened';
+ const EVENT_ISSUE_CLOSED = 'gitlab.webhook.issue.closed';
+ const EVENT_COMMIT = 'gitlab.webhook.commit';
+
+ /**
+ * Supported webhook events
+ *
+ * @var string
+ */
+ const TYPE_PUSH = 'push';
+ const TYPE_ISSUE = 'issue';
+
+ /**
+ * Project id
+ *
+ * @access private
+ * @var integer
+ */
+ private $project_id = 0;
+
+ /**
+ * Set the project id
+ *
+ * @access public
+ * @param integer $project_id Project id
+ */
+ public function setProjectId($project_id)
+ {
+ $this->project_id = $project_id;
+ }
+
+ /**
+ * Parse events
+ *
+ * @access public
+ * @param array $payload Gitlab event
+ * @return boolean
+ */
+ public function parsePayload(array $payload)
+ {
+ switch ($this->getType($payload)) {
+ case self::TYPE_PUSH:
+ return $this->handlePushEvent($payload);
+ case self::TYPE_ISSUE;
+ return $this->handleIssueEvent($payload);
+ }
+
+ return false;
+ }
+
+ /**
+ * Get event type
+ *
+ * @access public
+ * @param array $payload Gitlab event
+ * @return string
+ */
+ public function getType(array $payload)
+ {
+ if (isset($payload['object_kind']) && $payload['object_kind'] === 'issue') {
+ return self::TYPE_ISSUE;
+ }
+
+ if (isset($payload['commits'])) {
+ return self::TYPE_PUSH;
+ }
+
+ return '';
+ }
+
+ /**
+ * Parse push event
+ *
+ * @access public
+ * @param array $payload Gitlab event
+ * @return boolean
+ */
+ public function handlePushEvent(array $payload)
+ {
+ foreach ($payload['commits'] as $commit) {
+ $this->handleCommit($commit);
+ }
+
+ return true;
+ }
+
+ /**
+ * Parse commit
+ *
+ * @access public
+ * @param array $commit Gitlab commit
+ * @return boolean
+ */
+ public function handleCommit(array $commit)
+ {
+ $task_id = $this->task->getTaskIdFromText($commit['message']);
+
+ if (! $task_id) {
+ return false;
+ }
+
+ $task = $this->taskFinder->getById($task_id);
+
+ if (! $task) {
+ return false;
+ }
+
+ if ($task['is_active'] == Task::STATUS_OPEN && $task['project_id'] == $this->project_id) {
+
+ $this->container['dispatcher']->dispatch(
+ self::EVENT_COMMIT,
+ new TaskEvent(array('task_id' => $task_id) + $task)
+ );
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse issue event
+ *
+ * @access public
+ * @param array $payload Gitlab event
+ * @return boolean
+ */
+ public function handleIssueEvent(array $payload)
+ {
+ switch ($payload['object_attributes']['state']) {
+ case 'opened':
+ return $this->handleIssueOpened($payload['object_attributes']);
+ case 'closed':
+ return $this->handleIssueClosed($payload['object_attributes']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Handle new issues
+ *
+ * @access public
+ * @param array $issue Issue data
+ * @return boolean
+ */
+ public function handleIssueOpened(array $issue)
+ {
+ $event = array(
+ 'project_id' => $this->project_id,
+ 'reference' => $issue['id'],
+ 'title' => $issue['title'],
+ 'description' => $issue['description']."\n\n[".t('Gitlab Issue').']('.$issue['url'].')',
+ );
+
+ $this->container['dispatcher']->dispatch(
+ self::EVENT_ISSUE_OPENED,
+ new GenericEvent($event)
+ );
+
+ return true;
+ }
+
+ /**
+ * Handle issue closing
+ *
+ * @access public
+ * @param array $issue Issue data
+ * @return boolean
+ */
+ public function handleIssueClosed(array $issue)
+ {
+ $task = $this->taskFinder->getByReference($issue['id']);
+
+ if ($task) {
+ $event = array(
+ 'project_id' => $this->project_id,
+ 'task_id' => $task['id'],
+ 'reference' => $issue['id'],
+ );
+
+ $this->container['dispatcher']->dispatch(
+ self::EVENT_ISSUE_CLOSED,
+ new GenericEvent($event)
+ );
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php
index b6227771..a0c04610 100644
--- a/app/Locale/da_DK/translations.php
+++ b/app/Locale/da_DK/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php
index f9d8e714..b03ef801 100644
--- a/app/Locale/de_DE/translations.php
+++ b/app/Locale/de_DE/translations.php
@@ -556,8 +556,8 @@ return array(
'Webhooks' => 'Webhooks',
'API' => 'API',
'Integration' => 'Integration',
- 'Github webhook' => 'Github Webhook',
- 'Help on Github webhook' => 'Hilfe bei einem Github Webhook',
+ 'Github webhooks' => 'Github Webhook',
+ 'Help on Github webhooks' => 'Hilfe bei einem Github Webhook',
'Create a comment from an external provider' => 'Kommentar eines externen Providers hinzufügen',
'Github issue comment created' => 'Github Fehler Kommentar hinzugefügt',
'Configure' => 'konfigurieren',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php
index 1f4a3d87..527e3b95 100644
--- a/app/Locale/es_ES/translations.php
+++ b/app/Locale/es_ES/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php
index 25cf67c3..98fcfdc8 100644
--- a/app/Locale/fi_FI/translations.php
+++ b/app/Locale/fi_FI/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php
index fb281f13..ef52d333 100644
--- a/app/Locale/fr_FR/translations.php
+++ b/app/Locale/fr_FR/translations.php
@@ -556,8 +556,8 @@ return array(
'Webhooks' => 'Webhooks',
'API' => 'API',
'Integration' => 'Intégration',
- 'Github webhook' => 'Webhook Github',
- 'Help on Github webhook' => 'Aide sur les webhooks Github',
+ 'Github webhooks' => 'Webhook Github',
+ 'Help on Github webhooks' => 'Aide sur les webhooks Github',
'Create a comment from an external provider' => 'Créer un commentaire depuis un fournisseur externe',
'Github issue comment created' => 'Commentaire créé sur un ticket Github',
'Configure' => 'Configurer',
@@ -626,4 +626,11 @@ return array(
'Your swimlane have been created successfully.' => 'Votre swimlane a été créée avec succès.',
'Example: "Bug, Feature Request, Improvement"' => 'Exemple: « Incident, Demande de fonctionnalité, Amélioration »',
'Default categories for new projects (Comma-separated)' => 'Catégories par défaut pour les nouveaux projets (séparé par des virgules)',
+ 'Gitlab commit received' => '« Commit » reçu via Gitlab',
+ 'Gitlab issue opened' => 'Ouverture d\'un ticket sur Gitlab',
+ 'Gitlab issue closed' => 'Fermeture d\'un ticket sur Gitlab',
+ 'Gitlab webhooks' => 'Webhook Gitlab',
+ 'Help on Gitlab webhooks' => 'Aide sur les webhooks Gitlab',
+ 'Integrations' => 'Intégrations',
+ 'Integration with third-party services' => 'Intégration avec des services externes',
);
diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php
index 7188d197..5dbbe988 100644
--- a/app/Locale/hu_HU/translations.php
+++ b/app/Locale/hu_HU/translations.php
@@ -556,8 +556,8 @@ return array(
'Webhooks' => 'Webhook',
'API' => 'API',
'Integration' => 'Integráció',
- 'Github webhook' => 'Github webhook',
- 'Help on Github webhook' => 'Github Webhook súgó',
+ 'Github webhooks' => 'Github webhooks',
+ 'Help on Github webhooks' => 'Github Webhook súgó',
'Create a comment from an external provider' => 'Megjegyzés létrehozása külső felhasználótól',
'Github issue comment created' => 'Github issue megjegyzés létrehozva',
'Configure' => 'Konfigurál',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php
index 3117a068..7bd48a2b 100644
--- a/app/Locale/it_IT/translations.php
+++ b/app/Locale/it_IT/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php
index 60ded536..b519d68c 100644
--- a/app/Locale/ja_JP/translations.php
+++ b/app/Locale/ja_JP/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php
index 1c0492ed..bcab4874 100644
--- a/app/Locale/pl_PL/translations.php
+++ b/app/Locale/pl_PL/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php
index 28858ec5..48c15f61 100644
--- a/app/Locale/pt_BR/translations.php
+++ b/app/Locale/pt_BR/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
'Integration' => 'Integração',
- // 'Github webhook' => '',
- 'Help on Github webhook' => 'Ajuda para o Github webhook',
+ // 'Github webhooks' => '',
+ 'Help on Github webhooks' => 'Ajuda para o Github webhooks',
'Create a comment from an external provider' => 'Criar um comentário de um provedor externo',
// 'Github issue comment created' => '',
'Configure' => 'Configurar',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php
index 5d58ca25..08fc35de 100644
--- a/app/Locale/ru_RU/translations.php
+++ b/app/Locale/ru_RU/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php
index cd0bb2bf..b33983ca 100644
--- a/app/Locale/sv_SE/translations.php
+++ b/app/Locale/sv_SE/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php
index 7331d877..0dbe486c 100644
--- a/app/Locale/th_TH/translations.php
+++ b/app/Locale/th_TH/translations.php
@@ -556,8 +556,8 @@ return array(
// 'Webhooks' => '',
// 'API' => '',
// 'Integration' => '',
- // 'Github webhook' => '',
- // 'Help on Github webhook' => '',
+ // 'Github webhooks' => '',
+ // 'Help on Github webhooks' => '',
// 'Create a comment from an external provider' => '',
// 'Github issue comment created' => '',
// 'Configure' => '',
@@ -626,4 +626,11 @@ return array(
// 'Your swimlane have been created successfully.' => '',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php
index 455e37ed..b77a21d0 100644
--- a/app/Locale/zh_CN/translations.php
+++ b/app/Locale/zh_CN/translations.php
@@ -556,8 +556,8 @@ return array(
'Webhooks' => '网络钩子',
'API' => '应用程序接口',
'Integration' => '整合',
- 'Github webhook' => 'Github 网络钩子',
- 'Help on Github webhook' => 'Github 网络钩子帮助',
+ 'Github webhooks' => 'Github 网络钩子',
+ 'Help on Github webhooks' => 'Github 网络钩子帮助',
'Create a comment from an external provider' => '从外部创建一个评论',
'Github issue comment created' => '已经创建了Github问题评论',
'Configure' => '配置',
@@ -626,4 +626,11 @@ return array(
'Your swimlane have been created successfully.' => '已经成功创建泳道。',
// 'Example: "Bug, Feature Request, Improvement"' => '',
// 'Default categories for new projects (Comma-separated)' => '',
+ // 'Gitlab commit received' => '',
+ // 'Gitlab issue opened' => '',
+ // 'Gitlab issue closed' => '',
+ // 'Gitlab webhooks' => '',
+ // 'Help on Gitlab webhooks' => '',
+ // 'Integrations' => '',
+ // 'Integration with third-party services' => '',
);
diff --git a/app/Model/Acl.php b/app/Model/Acl.php
index 3f454885..d294197a 100644
--- a/app/Model/Acl.php
+++ b/app/Model/Acl.php
@@ -21,7 +21,7 @@ class Acl extends Base
'task' => array('readonly'),
'board' => array('readonly'),
'project' => array('feed'),
- 'webhook' => array('task', 'github'),
+ 'webhook' => array('task', 'github', 'gitlab'),
);
/**
diff --git a/app/Model/Action.php b/app/Model/Action.php
index 36e0aa62..905b8914 100644
--- a/app/Model/Action.php
+++ b/app/Model/Action.php
@@ -2,6 +2,8 @@
namespace Model;
+use Integration\GitlabWebhook;
+use Integration\GithubWebhook;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
@@ -79,6 +81,9 @@ class Action extends Base
GithubWebhook::EVENT_ISSUE_ASSIGNEE_CHANGE => t('Github issue assignee change'),
GithubWebhook::EVENT_ISSUE_LABEL_CHANGE => t('Github issue label change'),
GithubWebhook::EVENT_ISSUE_COMMENT => t('Github issue comment created'),
+ GitlabWebhook::EVENT_COMMIT => t('Gitlab commit received'),
+ GitlabWebhook::EVENT_ISSUE_OPENED => t('Gitlab issue opened'),
+ GitlabWebhook::EVENT_ISSUE_CLOSED => t('Gitlab issue closed'),
);
asort($values);
diff --git a/app/Model/Base.php b/app/Model/Base.php
index dfac12ae..db670969 100644
--- a/app/Model/Base.php
+++ b/app/Model/Base.php
@@ -29,6 +29,7 @@ use Pimple\Container;
* @property \Model\ProjectPermission $projectPermission
* @property \Model\SubTask $subTask
* @property \Model\SubtaskHistory $subtaskHistory
+ * @property \Model\Swimlane $swimlane
* @property \Model\Task $task
* @property \Model\TaskCreation $taskCreation
* @property \Model\TaskExport $taskExport
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index acba26d4..645a85ef 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -22,7 +22,6 @@ class ClassProvider implements ServiceProviderInterface
'Config',
'DateParser',
'File',
- 'GithubWebhook',
'LastLogin',
'Notification',
'Project',
@@ -53,6 +52,10 @@ class ClassProvider implements ServiceProviderInterface
'Template',
'Session',
),
+ 'Integration' => array(
+ 'GitlabWebhook',
+ 'GithubWebhook',
+ )
);
public function register(Container $container)
diff --git a/app/Subscriber/ProjectModificationDateSubscriber.php b/app/Subscriber/ProjectModificationDateSubscriber.php
index 3d5484f7..cd83f370 100644
--- a/app/Subscriber/ProjectModificationDateSubscriber.php
+++ b/app/Subscriber/ProjectModificationDateSubscriber.php
@@ -4,7 +4,7 @@ namespace Subscriber;
use Event\GenericEvent;
use Model\Task;
-use Model\GithubWebhook;
+use Integration\GithubWebhook;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ProjectModificationDateSubscriber extends Base implements EventSubscriberInterface
diff --git a/app/Template/project/integrations.php b/app/Template/project/integrations.php
new file mode 100644
index 00000000..8ec43f90
--- /dev/null
+++ b/app/Template/project/integrations.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Integration with third-party services') ?></h2>
+</div>
+
+<h3><i class="fa fa-github fa-fw"></i>&nbsp;<?= t('Github webhooks') ?></h3>
+<div class="listing">
+<input type="text" class="auto-select" readonly="readonly" value="<?= $this->getCurrentBaseUrl().$this->u('webhook', 'github', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/>
+<p class="form-help"><a href="http://kanboard.net/documentation/github-webhooks" target="_blank"><?= t('Help on Github webhooks') ?></a></p>
+</div>
+
+<h3><i class="fa fa-git fa-fw"></i>&nbsp;<?= t('Gitlab webhooks') ?></h3>
+<div class="listing">
+<input type="text" class="auto-select" readonly="readonly" value="<?= $this->getCurrentBaseUrl().$this->u('webhook', 'gitlab', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/>
+<p class="form-help"><a href="http://kanboard.net/documentation/gitlab-webhooks" target="_blank"><?= t('Help on Gitlab webhooks') ?></a></p>
+</div> \ No newline at end of file
diff --git a/app/Template/project/show.php b/app/Template/project/show.php
index 22d710e6..bc622d0d 100644
--- a/app/Template/project/show.php
+++ b/app/Template/project/show.php
@@ -53,14 +53,3 @@
</tr>
<?php endforeach ?>
</table>
-
-<?php if ($this->acl->isAdminUser()): ?>
-<div class="page-header">
- <h2><?= t('Integration') ?></h2>
-</div>
-
-<h3><i class="fa fa-github fa-fw"></i><?= t('Github webhook') ?></h3>
-<input type="text" class="auto-select" readonly="readonly" value="<?= $this->getCurrentBaseUrl().$this->u('webhook', 'github', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/>
-<p class="form-help"><a href="http://kanboard.net/documentation/github-webhooks" target="_blank"><?= t('Help on Github webhook') ?></a></p>
-
-<?php endif ?>
diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php
index 35019fdb..52a971d6 100644
--- a/app/Template/project/sidebar.php
+++ b/app/Template/project/sidebar.php
@@ -10,6 +10,9 @@
<?= $this->a(t('Public access'), 'project', 'share', array('project_id' => $project['id'])) ?>
</li>
<li>
+ <?= $this->a(t('Integrations'), 'project', 'integration', array('project_id' => $project['id'])) ?>
+ </li>
+ <li>
<?= $this->a(t('Edit project'), 'project', 'edit', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
diff --git a/tests/units/ActionTaskCloseTest.php b/tests/units/ActionTaskCloseTest.php
index a5087af0..7f2c42de 100644
--- a/tests/units/ActionTaskCloseTest.php
+++ b/tests/units/ActionTaskCloseTest.php
@@ -7,7 +7,7 @@ use Model\Task;
use Model\TaskCreation;
use Model\TaskFinder;
use Model\Project;
-use Model\GithubWebhook;
+use Integration\GithubWebhook;
class ActionTaskCloseTest extends Base
{
diff --git a/tests/units/ActionTest.php b/tests/units/ActionTest.php
index 77a939e0..429a181a 100644
--- a/tests/units/ActionTest.php
+++ b/tests/units/ActionTest.php
@@ -10,10 +10,10 @@ use Model\TaskPosition;
use Model\TaskCreation;
use Model\TaskFinder;
use Model\Category;
-use Model\GithubWebhook;
+use Integration\GithubWebhook;
class ActionTest extends Base
-{/*
+{
public function testSingleAction()
{
$tp = new TaskPosition($this->container);
@@ -61,7 +61,7 @@ class ActionTest extends Base
$this->assertEquals(4, $t1['column_id']);
$this->assertEquals(0, $t1['is_active']);
}
-*/
+
public function testMultipleActions()
{
$tp = new TaskPosition($this->container);
diff --git a/tests/units/GitlabWebhookTest.php b/tests/units/GitlabWebhookTest.php
new file mode 100644
index 00000000..0f2a5c12
--- /dev/null
+++ b/tests/units/GitlabWebhookTest.php
@@ -0,0 +1,116 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+use Integration\GitlabWebhook;
+use Model\TaskCreation;
+use Model\TaskFinder;
+use Model\Project;
+
+class GitlabWebhookTest extends Base
+{
+ private $push_payload = '{"before":"9187f41ba34a2b40d41c50ed4b624ce374c5e583","after":"b3caaee62ad27dc31497946065ac18299784aee4","ref":"refs/heads/master","user_id":74067,"user_name":"Fred","project_id":124474,"repository":{"name":"kanboard","url":"git@gitlab.com:minicoders/kanboard.git","description":"Test repo","homepage":"https://gitlab.com/minicoders/kanboard"},"commits":[{"id":"b3caaee62ad27dc31497946065ac18299784aee4","message":"Fix bug #2\n","timestamp":"2014-12-28T20:31:48-05:00","url":"https://gitlab.com/minicoders/kanboard/commit/b3caaee62ad27dc31497946065ac18299784aee4","author":{"name":"Frédéric Guillot","email":"git@localhost"}}],"total_commits_count":1}';
+ private $issue_open_payload = '{"object_kind":"issue","user":{"name":"Fred","username":"minicoders","avatar_url":"https://secure.gravatar.com/avatar/3c44936e5a56f80711bff14987d2733f?s=40\u0026d=identicon"},"object_attributes":{"id":103356,"title":"Test Webhook","assignee_id":null,"author_id":74067,"project_id":124474,"created_at":"2014-12-29 01:24:24 UTC","updated_at":"2014-12-29 01:24:24 UTC","position":0,"branch_name":null,"description":"- test1\r\n- test2","milestone_id":null,"state":"opened","iid":1,"url":"https://gitlab.com/minicoders/kanboard/issues/1","action":"open"}}';
+ private $issue_closed_payload = '{"object_kind":"issue","user":{"name":"Fred","username":"minicoders","avatar_url":"https://secure.gravatar.com/avatar/3c44936e5a56f80711bff14987d2733f?s=40\u0026d=identicon"},"object_attributes":{"id":103361,"title":"uu","assignee_id":null,"author_id":74067,"project_id":124474,"created_at":"2014-12-29 01:28:44 UTC","updated_at":"2014-12-29 01:34:47 UTC","position":0,"branch_name":null,"description":"","milestone_id":null,"state":"closed","iid":4,"url":"https://gitlab.com/minicoders/kanboard/issues/4","action":"update"}}';
+
+ public function testGetEventType()
+ {
+ $g = new GitlabWebhook($this->container);
+
+ $this->assertEquals(GitlabWebhook::TYPE_PUSH, $g->getType(json_decode($this->push_payload, true)));
+ $this->assertEquals(GitlabWebhook::TYPE_ISSUE, $g->getType(json_decode($this->issue_open_payload, true)));
+ $this->assertEquals(GitlabWebhook::TYPE_ISSUE, $g->getType(json_decode($this->issue_closed_payload, true)));
+ $this->assertEquals('', $g->getType(array()));
+ }
+
+ public function testHandleCommit()
+ {
+ $g = new GitlabWebhook($this->container);
+ $p = new Project($this->container);
+ $tc = new TaskCreation($this->container);
+ $tf = new TaskFinder($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'test')));
+ $g->setProjectId(1);
+
+ $this->container['dispatcher']->addListener(GitlabWebhook::EVENT_COMMIT, function() {});
+
+ $event = json_decode($this->push_payload, true);
+
+ // No task
+ $this->assertFalse($g->handleCommit($event['commits'][0]));
+
+ // Create task with the wrong id
+ $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
+ $this->assertFalse($g->handleCommit($event['commits'][0]));
+
+ // Create task with the right id
+ $this->assertEquals(2, $tc->create(array('title' => 'test', 'project_id' => 1)));
+ $this->assertTrue($g->handleCommit($event['commits'][0]));
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertArrayHasKey(GitlabWebhook::EVENT_COMMIT.'.closure', $called);
+ }
+
+ public function testHandleIssueOpened()
+ {
+ $g = new GitlabWebhook($this->container);
+ $g->setProjectId(1);
+
+ $this->container['dispatcher']->addListener(GitlabWebhook::EVENT_ISSUE_OPENED, array($this, 'onOpen'));
+
+ $event = json_decode($this->issue_open_payload, true);
+ $this->assertTrue($g->handleIssueOpened($event['object_attributes']));
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertArrayHasKey(GitlabWebhook::EVENT_ISSUE_OPENED.'.GitlabWebhookTest::onOpen', $called);
+ }
+
+ public function testHandleIssueClosed()
+ {
+ $g = new GitlabWebhook($this->container);
+ $p = new Project($this->container);
+ $tc = new TaskCreation($this->container);
+ $tf = new TaskFinder($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'test')));
+ $g->setProjectId(1);
+
+ $this->container['dispatcher']->addListener(GitlabWebhook::EVENT_ISSUE_CLOSED, array($this, 'onClose'));
+
+ $event = json_decode($this->issue_closed_payload, true);
+
+ // Issue not there
+ $this->assertFalse($g->handleIssueClosed($event['object_attributes']));
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertEmpty($called);
+
+ // Create a task with the issue reference
+ $this->assertEquals(1, $tc->create(array('title' => 'A', 'project_id' => 1, 'reference' => 103361)));
+ $task = $tf->getByReference(103361);
+ $this->assertNotEmpty($task);
+
+ $this->assertTrue($g->handleIssueClosed($event['object_attributes']));
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertArrayHasKey(GitlabWebhook::EVENT_ISSUE_CLOSED.'.GitlabWebhookTest::onClose', $called);
+ }
+
+ public function onOpen($event)
+ {
+ $data = $event->getAll();
+ $this->assertEquals(1, $data['project_id']);
+ $this->assertEquals(103356, $data['reference']);
+ $this->assertEquals('Test Webhook', $data['title']);
+ $this->assertEquals("- test1\r\n- test2\n\n[Gitlab Issue](https://gitlab.com/minicoders/kanboard/issues/1)", $data['description']);
+ }
+
+ public function onClose($event)
+ {
+ $data = $event->getAll();
+ $this->assertEquals(1, $data['project_id']);
+ $this->assertEquals(1, $data['task_id']);
+ $this->assertEquals(103361, $data['reference']);
+ }
+}