summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2015-02-08 21:13:59 -0500
committerFrederic Guillot <fred@kanboard.net>2015-02-08 21:13:59 -0500
commit6f94ce6af3072543ee62d64016931ed424f800a7 (patch)
tree348d250d786dba40c14828cef3fb347116a0fa1a
parent02f7c8d33d60b41f5c2a5335f9f6ee56f236a7fe (diff)
Add Bitbucket webhook
-rw-r--r--README.markdown1
-rw-r--r--app/Action/TaskClose.php5
-rw-r--r--app/Controller/Webhook.php18
-rw-r--r--app/Integration/BitbucketWebhook.php98
-rw-r--r--app/Locale/da_DK/translations.php3
-rw-r--r--app/Locale/de_DE/translations.php3
-rw-r--r--app/Locale/es_ES/translations.php3
-rw-r--r--app/Locale/fi_FI/translations.php3
-rw-r--r--app/Locale/fr_FR/translations.php3
-rw-r--r--app/Locale/hu_HU/translations.php3
-rw-r--r--app/Locale/it_IT/translations.php3
-rw-r--r--app/Locale/ja_JP/translations.php3
-rw-r--r--app/Locale/pl_PL/translations.php3
-rw-r--r--app/Locale/pt_BR/translations.php3
-rw-r--r--app/Locale/ru_RU/translations.php3
-rw-r--r--app/Locale/sv_SE/translations.php3
-rw-r--r--app/Locale/th_TH/translations.php3
-rw-r--r--app/Locale/zh_CN/translations.php3
-rw-r--r--app/Model/Action.php2
-rw-r--r--app/ServiceProvider/ClassProvider.php1
-rw-r--r--app/Template/project/integrations.php6
-rw-r--r--docs/bitbucket-webhooks.markdown40
-rw-r--r--docs/gitlab-webhooks.markdown2
-rw-r--r--tests/units/BitbucketWebhookTest.php65
24 files changed, 279 insertions, 1 deletions
diff --git a/README.markdown b/README.markdown
index 21f2ebe9..9b012ad0 100644
--- a/README.markdown
+++ b/README.markdown
@@ -86,6 +86,7 @@ Documentation
### Integrations
+- [Bitbucket webhooks](docs/bitbucket-webhooks.markdown)
- [Github webhooks](docs/github-webhooks.markdown)
- [Gitlab webhooks](docs/gitlab-webhooks.markdown)
diff --git a/app/Action/TaskClose.php b/app/Action/TaskClose.php
index 760dfd84..b7cd4dbf 100644
--- a/app/Action/TaskClose.php
+++ b/app/Action/TaskClose.php
@@ -4,6 +4,7 @@ namespace Action;
use Integration\GitlabWebhook;
use Integration\GithubWebhook;
+use Integration\BitbucketWebhook;
use Model\Task;
/**
@@ -28,6 +29,7 @@ class TaskClose extends Base
GithubWebhook::EVENT_ISSUE_CLOSED,
GitlabWebhook::EVENT_COMMIT,
GitlabWebhook::EVENT_ISSUE_CLOSED,
+ BitbucketWebhook::EVENT_COMMIT,
);
}
@@ -44,6 +46,7 @@ class TaskClose extends Base
case GithubWebhook::EVENT_ISSUE_CLOSED:
case GitlabWebhook::EVENT_COMMIT:
case GitlabWebhook::EVENT_ISSUE_CLOSED:
+ case BitbucketWebhook::EVENT_COMMIT:
return array();
default:
return array('column_id' => t('Column'));
@@ -63,6 +66,7 @@ class TaskClose extends Base
case GithubWebhook::EVENT_ISSUE_CLOSED:
case GitlabWebhook::EVENT_COMMIT:
case GitlabWebhook::EVENT_ISSUE_CLOSED:
+ case BitbucketWebhook::EVENT_COMMIT:
return array('task_id');
default:
return array('task_id', 'column_id');
@@ -95,6 +99,7 @@ class TaskClose extends Base
case GithubWebhook::EVENT_ISSUE_CLOSED:
case GitlabWebhook::EVENT_COMMIT:
case GitlabWebhook::EVENT_ISSUE_CLOSED:
+ case BitbucketWebhook::EVENT_COMMIT:
return true;
default:
return $data['column_id'] == $this->getParam('column_id');
diff --git a/app/Controller/Webhook.php b/app/Controller/Webhook.php
index 1ae3b0a4..ef79379f 100644
--- a/app/Controller/Webhook.php
+++ b/app/Controller/Webhook.php
@@ -82,4 +82,22 @@ class Webhook extends Base
echo $result ? 'PARSED' : 'IGNORED';
}
+
+ /**
+ * Handle Bitbucket webhooks
+ *
+ * @access public
+ */
+ public function bitbucket()
+ {
+ if ($this->config->get('webhook_token') !== $this->request->getStringParam('token')) {
+ $this->response->text('Not Authorized', 401);
+ }
+
+ $this->bitbucketWebhook->setProjectId($this->request->getIntegerParam('project_id'));
+
+ $result = $this->bitbucketWebhook->parsePayload(json_decode(@$_POST['payload'], true));
+
+ echo $result ? 'PARSED' : 'IGNORED';
+ }
}
diff --git a/app/Integration/BitbucketWebhook.php b/app/Integration/BitbucketWebhook.php
new file mode 100644
index 00000000..9f82d5c0
--- /dev/null
+++ b/app/Integration/BitbucketWebhook.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Integration;
+
+use Event\GenericEvent;
+use Event\TaskEvent;
+use Model\Task;
+
+/**
+ * Bitbucket Webhook
+ *
+ * @package integration
+ * @author Frederic Guillot
+ */
+class BitbucketWebhook extends Base
+{
+ /**
+ * Events
+ *
+ * @var string
+ */
+ const EVENT_COMMIT = 'bitbucket.webhook.commit';
+
+ /**
+ * 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)
+ {
+ if (! empty($payload['commits'])) {
+
+ foreach ($payload['commits'] as $commit) {
+
+ if ($this->handleCommit($commit)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 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;
+ }
+}
diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php
index 07c05b89..fbd7b3ab 100644
--- a/app/Locale/da_DK/translations.php
+++ b/app/Locale/da_DK/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php
index af15c6b2..b3a9c04b 100644
--- a/app/Locale/de_DE/translations.php
+++ b/app/Locale/de_DE/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php
index b39e9793..8cfa91b8 100644
--- a/app/Locale/es_ES/translations.php
+++ b/app/Locale/es_ES/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php
index 9adc19e4..39676bf1 100644
--- a/app/Locale/fi_FI/translations.php
+++ b/app/Locale/fi_FI/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php
index 2542a6fe..5df39809 100644
--- a/app/Locale/fr_FR/translations.php
+++ b/app/Locale/fr_FR/translations.php
@@ -680,4 +680,7 @@ return array(
'Disable login form' => 'Désactiver le formulaire d\'authentification',
'Show/hide calendar' => 'Afficher/cacher le calendrier',
'User calendar' => 'Calendrier de l\'utilisateur',
+ 'Bitbucket commit received' => '« Commit » reçu via Bitbucket',
+ 'Bitbucket webhooks' => 'Webhook Bitbucket',
+ 'Help on Bitbucket webhooks' => 'Aide sur les webhooks Bitbucket',
);
diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php
index 8cbe6d8d..b7fbd04e 100644
--- a/app/Locale/hu_HU/translations.php
+++ b/app/Locale/hu_HU/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php
index 98ed4a91..2a974629 100644
--- a/app/Locale/it_IT/translations.php
+++ b/app/Locale/it_IT/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php
index 229622c5..2647f9fd 100644
--- a/app/Locale/ja_JP/translations.php
+++ b/app/Locale/ja_JP/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php
index e2cc3ab8..c83e130a 100644
--- a/app/Locale/pl_PL/translations.php
+++ b/app/Locale/pl_PL/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php
index db640901..0cd80f8f 100644
--- a/app/Locale/pt_BR/translations.php
+++ b/app/Locale/pt_BR/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php
index 513436af..ed68a447 100644
--- a/app/Locale/ru_RU/translations.php
+++ b/app/Locale/ru_RU/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php
index 4958189f..1d793b35 100644
--- a/app/Locale/sv_SE/translations.php
+++ b/app/Locale/sv_SE/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php
index 962971f4..cf5302f6 100644
--- a/app/Locale/th_TH/translations.php
+++ b/app/Locale/th_TH/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php
index 4c76d7ab..6dbd1676 100644
--- a/app/Locale/zh_CN/translations.php
+++ b/app/Locale/zh_CN/translations.php
@@ -678,4 +678,7 @@ return array(
// 'Disable login form' => '',
// 'Show/hide calendar' => '',
// 'User calendar' => '',
+ // 'Bitbucket commit received' => '',
+ // 'Bitbucket webhooks' => '',
+ // 'Help on Bitbucket webhooks' => '',
);
diff --git a/app/Model/Action.php b/app/Model/Action.php
index 6aef81a3..6fb2a2f1 100644
--- a/app/Model/Action.php
+++ b/app/Model/Action.php
@@ -4,6 +4,7 @@ namespace Model;
use Integration\GitlabWebhook;
use Integration\GithubWebhook;
+use Integration\BitbucketWebhook;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
@@ -85,6 +86,7 @@ class Action extends Base
GitlabWebhook::EVENT_COMMIT => t('Gitlab commit received'),
GitlabWebhook::EVENT_ISSUE_OPENED => t('Gitlab issue opened'),
GitlabWebhook::EVENT_ISSUE_CLOSED => t('Gitlab issue closed'),
+ BitbucketWebhook::EVENT_COMMIT => t('Bitbucket commit received'),
);
asort($values);
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index 48157991..bee03184 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -62,6 +62,7 @@ class ClassProvider implements ServiceProviderInterface
'Integration' => array(
'GitlabWebhook',
'GithubWebhook',
+ 'BitbucketWebhook',
)
);
diff --git a/app/Template/project/integrations.php b/app/Template/project/integrations.php
index 8ec43f90..194bd672 100644
--- a/app/Template/project/integrations.php
+++ b/app/Template/project/integrations.php
@@ -12,4 +12,10 @@
<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>
+
+<h3><i class="fa fa-bitbucket fa-fw"></i>&nbsp;<?= t('Bitbucket webhooks') ?></h3>
+<div class="listing">
+<input type="text" class="auto-select" readonly="readonly" value="<?= $this->getCurrentBaseUrl().$this->u('webhook', 'bitbucket', array('token' => $webhook_token, 'project_id' => $project['id'])) ?>"/><br/>
+<p class="form-help"><a href="http://kanboard.net/documentation/bitbucket-webhooks" target="_blank"><?= t('Help on Bitbucket webhooks') ?></a></p>
</div> \ No newline at end of file
diff --git a/docs/bitbucket-webhooks.markdown b/docs/bitbucket-webhooks.markdown
new file mode 100644
index 00000000..fbb80d12
--- /dev/null
+++ b/docs/bitbucket-webhooks.markdown
@@ -0,0 +1,40 @@
+Bitbucket webhooks
+==================
+
+Bitbucket events can be connected to Kanboard automatic actions.
+
+List of supported events
+------------------------
+
+- Bitbucket commit received
+
+List of supported actions
+-------------------------
+
+- Close 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 **Hooks**
+4. Select the service **POST**
+5. Paste the url and save
+
+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
diff --git a/docs/gitlab-webhooks.markdown b/docs/gitlab-webhooks.markdown
index 3369b223..9ef73f97 100644
--- a/docs/gitlab-webhooks.markdown
+++ b/docs/gitlab-webhooks.markdown
@@ -23,7 +23,7 @@ Configuration
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 the section **Webhooks**
+3. On Gitlab, go to the project settings and go to the section **Webhooks**
4. Check the boxes **Push Events** and **Issues Events**
5. Paste the url and save
diff --git a/tests/units/BitbucketWebhookTest.php b/tests/units/BitbucketWebhookTest.php
new file mode 100644
index 00000000..cb33b595
--- /dev/null
+++ b/tests/units/BitbucketWebhookTest.php
@@ -0,0 +1,65 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+use Integration\BitbucketWebhook;
+use Model\TaskCreation;
+use Model\TaskFinder;
+use Model\Project;
+
+class BitbucketWebhookTest extends Base
+{
+ private $post_payload = '{"repository": {"website": "", "fork": false, "name": "webhooks", "scm": "git", "owner": "minicoders", "absolute_url": "/minicoders/webhooks/", "slug": "webhooks", "is_private": true}, "truncated": false, "commits": [{"node": "28569937627f", "files": [{"type": "added", "file": "README.md"}], "raw_author": "Frederic Guillot <fred@localhost>", "utctimestamp": "2015-02-09 00:57:45+00:00", "author": "Frederic Guillot", "timestamp": "2015-02-09 01:57:45", "raw_node": "28569937627fb406eeda9376a02b39581a974d4f", "parents": [], "branch": "master", "message": "first commit\\n", "revision": null, "size": -1}, {"node": "285699376274", "files": [{"type": "added", "file": "README.md"}], "raw_author": "Frederic Guillot <fred@localhost>", "utctimestamp": "2015-02-09 00:57:45+00:00", "author": "Frederic Guillot", "timestamp": "2015-02-09 01:57:45", "raw_node": "28569937627fb406eeda9376a02b39581a974d4f", "parents": [], "branch": "master", "message": "Fix #2\\n", "revision": null, "size": -1}], "canon_url": "https://bitbucket.org", "user": "minicoders"}';
+
+ public function testHandleCommit()
+ {
+ $g = new BitbucketWebhook($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(BitbucketWebhook::EVENT_COMMIT, function() {});
+
+ $event = json_decode($this->post_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'][1]));
+
+ // Create task with the right id
+ $this->assertEquals(2, $tc->create(array('title' => 'test', 'project_id' => 1)));
+ $this->assertTrue($g->handleCommit($event['commits'][1]));
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertArrayHasKey(BitbucketWebhook::EVENT_COMMIT.'.closure', $called);
+ }
+
+ public function testParsePayload()
+ {
+ $g = new BitbucketWebhook($this->container);
+ $p = new Project($this->container);
+ $tc = new TaskCreation($this->container);
+ $tf = new TaskFinder($this->container);
+
+ $this->container['dispatcher']->addListener(BitbucketWebhook::EVENT_COMMIT, function() {});
+
+ $this->assertEquals(1, $p->create(array('name' => 'test')));
+
+ $g->setProjectId(1);
+
+ $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
+ $this->assertEquals(2, $tc->create(array('title' => 'test', 'project_id' => 1)));
+
+ $event = json_decode($this->post_payload, true);
+ $this->assertTrue($g->parsePayload($event));
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertArrayHasKey(BitbucketWebhook::EVENT_COMMIT.'.closure', $called);
+ }
+}