diff --git a/config/zentaopms.php b/config/zentaopms.php
index 8933ef5f0f..3e192df98c 100644
--- a/config/zentaopms.php
+++ b/config/zentaopms.php
@@ -104,6 +104,8 @@ define('TABLE_TESTREPORT', '`' . $config->db->prefix . 'testreport`');
define('TABLE_ENTRY', '`' . $config->db->prefix . 'entry`');
define('TABLE_WEBHOOK', '`' . $config->db->prefix . 'webhook`');
+define('TABLE_WEBHOOKDATAS', '`' . $config->db->prefix . 'webhookdatas`');
+define('TABLE_LOG', '`' . $config->db->prefix . 'log`');
if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`');
$config->objectTables['product'] = TABLE_PRODUCT;
diff --git a/db/update9.5.1.sql b/db/update9.5.1.sql
index 88c7cd1fdb..6368a5b6fe 100644
--- a/db/update9.5.1.sql
+++ b/db/update9.5.1.sql
@@ -18,12 +18,43 @@ CREATE TABLE IF NOT EXISTS `zt_webhook` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`url` varchar(255) NOT NULL,
- `requestType` enum('post', 'get') NOT NULL DEFAULT 'get',
- `params` text NOT NULL,
+ `contentType` enum('json','form') NOT NULL DEFAULT 'json',
+ `sendType` enum('sync','async') NOT NULL DEFAULT 'sync',
+ `params` varchar(100) NOT NULL,
+ `actions` text NOT NULL,
`desc` text NOT NULL,
`createdBy` varchar(30) NOT NULL,
`createdDate` datetime NOT NULL,
`editedBy` varchar(30) NOT NULL,
`editedDate` datetime NOT NULL,
- PRIMARY KEY `id` (`id`)
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- DROP TABLE IF EXISTS `zt_webhookdatas`;
+CREATE TABLE IF NOT EXISTS `zt_webhookdatas` (
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
+ `webhook` mediumint(8) unsigned NOT NULL,
+ `action` mediumint(8) unsigned NOT NULL,
+ `data` text NOT NULL,
+ `status` enum('wait', 'sended') NOT NULL DEFAULT 'wait',
+ `createdBy` varchar(30) NOT NULL,
+ `createdDate` datetime NOT NULL,
+ PRIMARY KEY `id` (`id`),
+ UNIQUE KEY `uniq` (`webhook`, `action`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- DROP TABLE IF EXISTS `zt_log`;
+CREATE TABLE IF NOT EXISTS `zt_log` (
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
+ `objectType` varchar(30) NOT NULL,
+ `objectID` mediumint(8) unsigned NOT NULL,
+ `action` mediumint(8) unsigned NOT NULL,
+ `date` datetime NOT NULL,
+ `url` varchar(255) NOT NULL,
+ `contentType` varchar(40) NOT NULL,
+ `data` text NOT NULL,
+ `status` smallint(5) NOT NULL,
+ PRIMARY KEY `id` (`id`),
+ KEY `objectType` (`objectType`),
+ KEY `obejctID` (`objectID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php
index 78f3a12eb0..8e0bce22e6 100755
--- a/module/action/lang/zh-cn.php
+++ b/module/action/lang/zh-cn.php
@@ -139,7 +139,7 @@ $lang->action->label->blocked = '阻塞了';
$lang->action->label->resolved = '解决了';
$lang->action->label->reviewed = '评审了';
$lang->action->label->moved = '移动了';
-$lang->action->label->confirmed = '确认了需求,';
+$lang->action->label->confirmed = '确认了需求';
$lang->action->label->bugconfirmed = '确认了';
$lang->action->label->tostory = '转需求';
$lang->action->label->frombug = '转需求';
diff --git a/module/action/model.php b/module/action/model.php
index 5f1a54b01d..5784e55b4a 100755
--- a/module/action/model.php
+++ b/module/action/model.php
@@ -60,6 +60,8 @@ class actionModel extends model
$this->file->updateObjectID($this->post->uid, $objectID, $objectType);
+ $this->loadModel('webhook')->send($objectType, $objectID, $actionType, $actionID);
+
return $actionID;
}
diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php
index b960c80214..b55ab98d6b 100644
--- a/module/common/lang/zh-cn.php
+++ b/module/common/lang/zh-cn.php
@@ -418,6 +418,7 @@ $lang->error->int = array("『%s』应当是数字。", "『%s』应
$lang->error->float = "『%s』应当是数字,可以是小数。";
$lang->error->email = "『%s』应当为合法的EMAIL。";
$lang->error->date = "『%s』应当为合法的日期。";
+$lang->error->code = "『%s』应当为字母或数字的组合。";
$lang->error->account = "『%s』应当为合法的用户名。";
$lang->error->passwordsame = "两次密码应当相等。";
$lang->error->passwordrule = "密码应该符合规则,长度至少为六位。";
diff --git a/module/entry/js/create.js b/module/entry/js/create.js
index ccb04adea0..be33903126 100644
--- a/module/entry/js/create.js
+++ b/module/entry/js/create.js
@@ -16,16 +16,19 @@ function createKey()
return false;
}
-$('#allIP').change(function()
+$(function()
{
- if($(this).prop('checked'))
+ $('#allIP').change(function()
{
- $('#ip').attr('disabled', 'disabled');
- }
- else
- {
- $('#ip').removeAttr('disabled');
- }
-})
-
-$('#name').focus();
+ if($(this).prop('checked'))
+ {
+ $('#ip').attr('disabled', 'disabled');
+ }
+ else
+ {
+ $('#ip').removeAttr('disabled');
+ }
+ })
+
+ $('#name').focus();
+});
diff --git a/module/entry/js/edit.js b/module/entry/js/edit.js
index ccb04adea0..be33903126 100644
--- a/module/entry/js/edit.js
+++ b/module/entry/js/edit.js
@@ -16,16 +16,19 @@ function createKey()
return false;
}
-$('#allIP').change(function()
+$(function()
{
- if($(this).prop('checked'))
+ $('#allIP').change(function()
{
- $('#ip').attr('disabled', 'disabled');
- }
- else
- {
- $('#ip').removeAttr('disabled');
- }
-})
-
-$('#name').focus();
+ if($(this).prop('checked'))
+ {
+ $('#ip').attr('disabled', 'disabled');
+ }
+ else
+ {
+ $('#ip').removeAttr('disabled');
+ }
+ })
+
+ $('#name').focus();
+});
diff --git a/module/entry/lang/zh-cn.php b/module/entry/lang/zh-cn.php
index 8d1b8b6460..0cdf3ee54d 100644
--- a/module/entry/lang/zh-cn.php
+++ b/module/entry/lang/zh-cn.php
@@ -22,11 +22,13 @@ $lang->entry->createdDate = '创建时间';
$lang->entry->editedby = '最后编辑';
$lang->entry->editedDate = '编辑时间';
+$lang->entry->apiIndex = '应用入口';
$lang->entry->saveSuccess = '保存成功';
$lang->entry->confirmDelete = '您确认要删除该应用吗?';
$lang->entry->note = new stdClass();
$lang->entry->note->name = '授权应用名称';
-$lang->entry->note->code = '授权应用代号,必须为英文、数字或下划线的组合';
+$lang->entry->note->code = '授权应用代号,必须为字母或数字的组合';
$lang->entry->note->ip = "允许访问应用的ip,多个ip用逗号隔开。支持IP段,如192.168.1.*";
$lang->entry->note->allIP = '无限制';
+$lang->entry->note->api = 'moduleName、methodName以及参数列表替换成实际的值';
diff --git a/module/entry/model.php b/module/entry/model.php
index a495dfbd04..35693b499b 100644
--- a/module/entry/model.php
+++ b/module/entry/model.php
@@ -27,7 +27,7 @@ class entryModel extends model
* Get entry list.
*
* @param string $orderBy
- * @param obejct $pager
+ * @param object $pager
* @access public
* @return array
*/
@@ -54,6 +54,7 @@ class entryModel extends model
$this->dao->insert(TABLE_ENTRY)->data($entry)
->batchCheck($this->config->entry->create->requiredFields, 'notempty')
+ ->check('code', 'code')
->check('code', 'unique')
->autoCheck()
->exec();
@@ -83,6 +84,7 @@ class entryModel extends model
$this->dao->update(TABLE_ENTRY)->data($entry)
->batchCheck($this->config->entry->edit->requiredFields, 'notempty')
+ ->check('code', 'code')
->check('code', 'unique', "id!=$entryID")
->autoCheck()
->where('id')->eq($entryID)
diff --git a/module/entry/view/create.html.php b/module/entry/view/create.html.php
index 020593d8d1..097f9bdf07 100644
--- a/module/entry/view/create.html.php
+++ b/module/entry/view/create.html.php
@@ -53,6 +53,11 @@
|
|
+
+ | entry->apiIndex;?> |
+ moduleVar}=moduleName&{$config->methodVar}=methodName¶ms=params";?> |
+ |
+
|
|
diff --git a/module/entry/view/edit.html.php b/module/entry/view/edit.html.php
index 8282446994..7a997621b0 100644
--- a/module/entry/view/edit.html.php
+++ b/module/entry/view/edit.html.php
@@ -53,6 +53,11 @@
desc, "rows='3' class='form-control'");?> |
|
+
+ | entry->apiIndex;?> |
+ moduleVar}=moduleName&{$config->methodVar}=methodName¶ms=params";?> |
+ |
+
|
|
diff --git a/module/entry/view/log.html.php b/module/entry/view/log.html.php
index 82d5fb3f7a..d005085aa2 100644
--- a/module/entry/view/log.html.php
+++ b/module/entry/view/log.html.php
@@ -15,8 +15,18 @@
| action->date;?> |
- |
+ action->actor;?> |
+ entry->desc;?> |
+
+
+
+ | date;?> |
+ actor);?> |
+ extra;?> |
+
+
+
diff --git a/module/webhook/config.php b/module/webhook/config.php
index 09291c3ead..a237812206 100644
--- a/module/webhook/config.php
+++ b/module/webhook/config.php
@@ -5,5 +5,23 @@ $config->webhook->create->requiredFields = 'name, url';
$config->webhook->edit = new stdclass();
$config->webhook->edit->requiredFields = 'name, url';
-$config->webhook->requestType['post'] = 'post';
-$config->webhook->requestType['get'] = 'get';
+$config->webhook->contentTypes['json'] = 'application/json';
+$config->webhook->contentTypes['form'] = 'application/x-www-form-urlencoded';
+
+$config->webhook->objectTypes['product'] = array('opened', 'edited', 'closed', 'undeleted');
+$config->webhook->objectTypes['story'] = array('opened', 'edited', 'commented', 'frombug', 'changed', 'reviewed', 'closed', 'activated');
+$config->webhook->objectTypes['productplan'] = array('opened', 'edited');
+//$config->webhook->objectTypes['release'] = array('opened', 'edited', 'changestatus');
+$config->webhook->objectTypes['project'] = array('opened', 'edited', 'started', 'delayed', 'suspended', 'closed', 'activated', 'undeleted');
+$config->webhook->objectTypes['task'] = array('opened', 'edited', 'commented', 'assigned', 'confirmed', 'started', 'finished', 'editestimate', 'deleteestimate', 'paused', 'canceled', 'restarted', 'closed', 'activated');
+//$config->webhook->objectTypes['build'] = array('opened', 'edited');
+$config->webhook->objectTypes['bug'] = array('opened', 'edited', 'commented', 'assigned', 'confirmed', 'bugconfirmed', 'resolved', 'closed', 'activated');
+$config->webhook->objectTypes['case'] = array('opened', 'edited', 'commented', 'reviewed', 'confirmed');
+$config->webhook->objectTypes['testtask'] = array('opened', 'edited', 'started', 'blocked', 'closed', 'activated');
+$config->webhook->objectTypes['todo'] = array('opened', 'edited', 'erased');
+//$config->webhook->objectTypes['testsuite'] = array('opened', 'edited');
+//$config->webhook->objectTypes['caselib'] = array('opened', 'edited');
+//$config->webhook->objectTypes['testreport'] = array('opened', 'edited');
+//$config->webhook->objectTypes['doc'] = array('created', 'edited', 'commented');
+//$config->webhook->objectTypes['doclib'] = array('created', 'edited');
+//$config->webhook->objectTypes['user'] = array('login', 'logout');
diff --git a/module/webhook/control.php b/module/webhook/control.php
index 1a955316ca..14a9e00149 100644
--- a/module/webhook/control.php
+++ b/module/webhook/control.php
@@ -12,29 +12,25 @@
class webhook extends control
{
/**
- * Browse entries.
+ * Browse webhooks.
*
- * @param string $orderBy
- * @param int $recTotal
- * @param int $recPerPage
- * @param int $pageID
* @access public
* @return void
*/
- public function browse($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 10, $pageID = 1)
+ public function browse($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
- $pager = $this->app->loadClass('pager', $static = true);
+ $this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
- $this->view->title = $this->lang->webhook->api . $this->lang->colon . $this->lang->webhook->list;
- $this->view->entries = $this->webhook->getList($orderBy, $pager);
- $this->view->orderBy = $orderBy;
- $this->view->pager = $pager;
+ $this->view->title = $this->lang->webhook->api . $this->lang->colon . $this->lang->webhook->list;
+ $this->view->webhooks = $this->webhook->getList($orderBy, $pager);
+ $this->view->orderBy = $orderBy;
+ $this->view->pager = $pager;
$this->display();
}
/**
- * Create an webhook.
+ * Create a webhook.
*
* @access public
* @return void
@@ -43,85 +39,112 @@ class webhook extends control
{
if($_POST)
{
- $webhookID = $this->webhook->create();
+ $this->webhook->create();
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
-
- $this->loadModel('action')->create('webhook', $webhookID, 'created');
$this->send(array('result' => 'success', 'message' => $this->lang->webhook->saveSuccess, 'locate' => inlink('browse')));
}
- $this->view->title = $this->lang->webhook->api . $this->lang->colon . $this->lang->webhook->create;
+ $this->app->loadLang('action');
+ $this->view->title = $this->lang->webhook->api . $this->lang->colon . $this->lang->webhook->create;
+ $this->view->objectTypes = $this->webhook->getObjectTypes();
+ $this->view->objectActions = $this->webhook->getObjectActions();
$this->display();
}
/**
- * Edit an webhook.
+ * Edit a webhook.
*
- * @param int $webhookID
+ * @param int $id
* @access public
* @return void
*/
- public function edit($webhookID)
+ public function edit($id)
{
if($_POST)
{
- $changes = $this->webhook->update($webhookID);
+ $this->webhook->update($id);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
-
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('webhook', $webhookID, 'edited');
- $this->action->logHistory($actionID, $changes);
- }
$this->send(array('result' => 'success', 'message' => $this->lang->webhook->saveSuccess, 'locate' => inlink('browse')));
}
- $webhook = $this->webhook->getById($webhookID);
- $this->view->title = $this->lang->webhook->edit . $this->lang->colon . $webhook->name;
- $this->view->webhook = $webhook;
+ $this->app->loadLang('action');
+ $webhook = $this->webhook->getByID($id);
+ $this->view->title = $this->lang->webhook->edit . $this->lang->colon . $webhook->name;
+ $this->view->objectTypes = $this->webhook->getObjectTypes();
+ $this->view->objectActions = $this->webhook->getObjectActions();
+ $this->view->webhook = $webhook;
$this->display();
}
/**
- * Delete an webhook.
+ * Delete a webhook.
*
- * @param int $webhookID
+ * @param int $id
* @access public
* @return void
*/
- public function delete($webhookID)
+ public function delete($id)
{
- $this->webhook->delete($webhookID);
+ $this->webhook->delete($id);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->send(array('result' => 'success'));
}
- public function log($webhookID)
+ /**
+ * Browse logs of a webhook.
+ *
+ * @param int $id
+ * @param string $orderBy
+ * @param int $recTotal
+ * @param int $recPerPage
+ * @param int $pageID
+ * @access public
+ * @return void
+ */
+ public function log($id, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
- $webhook = $this->webhook->getById($webhookID);
+ $this->app->loadClass('pager', $static = true);
+ $pager = new pager($recTotal, $recPerPage, $pageID);
+
+ $webhook = $this->webhook->getByID($id);
$this->view->title = $this->lang->webhook->log . $this->lang->colon . $webhook->name;
- $this->view->actions = $this->loadModel('action')->getList('webhook', $webhookID);
+ $this->view->logs = $this->webhook->getLogList($id, $orderBy, $pager);
+ $this->view->webhook = $webhook;
+ $this->view->orderBy = $orderBy;
+ $this->view->pager = $pager;
$this->display();
}
- public function actions($webhookID)
+ /**
+ * Send data by async.
+ *
+ * @access public
+ * @return void
+ */
+ public function asyncSend()
{
- if($_POST)
- {
- $this->webhook->saveActions($webhookID);
- if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
+ $webhooks = $this->webhook->getList();
+ if(empty($hooks)) return false;
+ $dataList = $this->webhook->getDataList();
+ if(empty($dataList)) return true;
- $this->send(array('result' => 'success', 'message' => $this->lang->webhook->saveSuccess, 'locate' => inlink('browse')));
+ $snoopy = $this->app->loadClass('snoopy');
+ foreach($dataList as $data)
+ {
+ $webhook = zget($webhooks, $data->webhook, '');
+ if($webhook)
+ {
+ $snoopy->_submit_type = zget($this->config->webhook->contentTypes, $webhook->contentType, 'application/json');
+ $snoopy->submit($webhook->url, $data->data);
+
+ $this->saveLog($data->webhook, $data->action, $webhook->url, $snoopy->_submit_type, $postData, $snoopy->status, $snoopy->error);
+ }
+
+ if($snoopy->status == 200) $this->dao->update(TABLE_WEBHOOKDATA)->set('status')->eq('sended')->where('id')->eq($data->id)->exec();
}
- $webhook = $this->webhook->getById($webhookID);
- $this->view->title = $this->lang->webhook->actions . $this->lang->colon . $webhook->name;
- $this->view->actions = $this->webhook->getActions($webhookID);
- $this->display();
- }
-
- public function ajaxGetActions($module)
- {
+ $this->dao->delete()->from(TABLE_WEBHOOKDATA)->where('status')->eq('sended')->exec();
+ return true;
}
}
diff --git a/module/webhook/css/browse.css b/module/webhook/css/browse.css
new file mode 100644
index 0000000000..1d88a4811d
--- /dev/null
+++ b/module/webhook/css/browse.css
@@ -0,0 +1 @@
+.text {overflow: hidden; text-overflow: ellipsis;}
diff --git a/module/webhook/css/create.css b/module/webhook/css/create.css
new file mode 100644
index 0000000000..19916caa95
--- /dev/null
+++ b/module/webhook/css/create.css
@@ -0,0 +1,3 @@
+.labelWidth label.checkbox-inline {width: 20%;}
+.checkbox-inline + .checkbox-inline {margin-left: 0px;}
+.objectType {margin-right: 5px !important;}
diff --git a/module/webhook/css/edit.css b/module/webhook/css/edit.css
new file mode 100644
index 0000000000..19916caa95
--- /dev/null
+++ b/module/webhook/css/edit.css
@@ -0,0 +1,3 @@
+.labelWidth label.checkbox-inline {width: 20%;}
+.checkbox-inline + .checkbox-inline {margin-left: 0px;}
+.objectType {margin-right: 5px !important;}
diff --git a/module/webhook/js/create.js b/module/webhook/js/create.js
index ccb04adea0..3acb71ac46 100644
--- a/module/webhook/js/create.js
+++ b/module/webhook/js/create.js
@@ -1,31 +1,17 @@
-/**
- * create key for an entry.
- *
- * @access public
- * @return void
- */
-function createKey()
+$(function()
{
- var chars = '0123456789abcdefghiklmnopqrstuvwxyz'.split('');
- var key = '';
- for(var i = 0; i < 32; i ++)
- {
- key += chars[Math.floor(Math.random() * chars.length)];
- }
- $('#key').val(key);
- return false;
-}
-
-$('#allIP').change(function()
-{
- if($(this).prop('checked'))
+ $('.objectType').click(function()
{
- $('#ip').attr('disabled', 'disabled');
- }
- else
- {
- $('#ip').removeAttr('disabled');
- }
-})
+ if($(this).prop('checked'))
+ {
+ $(this).parent().parent().next().find('input[type=checkbox]').attr('checked', 'checked');
+ }
+ else
+ {
+ $(this).parent().parent().next().find('input[type=checkbox]').removeAttr('checked');
+ }
+ });
-$('#name').focus();
+ $('#name').focus();
+ $('#paramsmessage').attr('disabled', 'disabled');
+});
diff --git a/module/webhook/js/edit.js b/module/webhook/js/edit.js
index ccb04adea0..3acb71ac46 100644
--- a/module/webhook/js/edit.js
+++ b/module/webhook/js/edit.js
@@ -1,31 +1,17 @@
-/**
- * create key for an entry.
- *
- * @access public
- * @return void
- */
-function createKey()
+$(function()
{
- var chars = '0123456789abcdefghiklmnopqrstuvwxyz'.split('');
- var key = '';
- for(var i = 0; i < 32; i ++)
- {
- key += chars[Math.floor(Math.random() * chars.length)];
- }
- $('#key').val(key);
- return false;
-}
-
-$('#allIP').change(function()
-{
- if($(this).prop('checked'))
+ $('.objectType').click(function()
{
- $('#ip').attr('disabled', 'disabled');
- }
- else
- {
- $('#ip').removeAttr('disabled');
- }
-})
+ if($(this).prop('checked'))
+ {
+ $(this).parent().parent().next().find('input[type=checkbox]').attr('checked', 'checked');
+ }
+ else
+ {
+ $(this).parent().parent().next().find('input[type=checkbox]').removeAttr('checked');
+ }
+ });
-$('#name').focus();
+ $('#name').focus();
+ $('#paramsmessage').attr('disabled', 'disabled');
+});
diff --git a/module/webhook/lang/zh-cn.php b/module/webhook/lang/zh-cn.php
index eec399e63c..aa35893694 100644
--- a/module/webhook/lang/zh-cn.php
+++ b/module/webhook/lang/zh-cn.php
@@ -1,31 +1,43 @@
webhook->common = 'webhook';
-$lang->webhook->list = 'webhook列表';
+$lang->webhook->list = '钩子列表';
$lang->webhook->api = '接口';
$lang->webhook->entry = '应用';
$lang->webhook->log = '日志';
-$lang->webhook->action = '触发操作';
-$lang->webhook->browse = '浏览webhook';
-$lang->webhook->create = '添加webhook';
-$lang->webhook->edit = '编辑webhook';
-$lang->webhook->delete = '删除webhook';
+$lang->webhook->browse = '浏览钩子';
+$lang->webhook->create = '添加钩子';
+$lang->webhook->edit = '编辑钩子';
+$lang->webhook->delete = '删除钩子';
$lang->webhook->id = 'ID';
$lang->webhook->name = '名称';
-$lang->webhook->url = '请求地址';
-$lang->webhook->requestType = '请求类型';
+$lang->webhook->url = 'Hook地址';
+$lang->webhook->contentType = '内容类型';
+$lang->webhook->sendType = '发送方式';
$lang->webhook->params = '参数';
+$lang->webhook->action = '触发动作';
$lang->webhook->desc = '描述';
-$lang->webhook->createdBy = '由谁创建';
-$lang->webhook->createdDate = '创建时间';
-$lang->webhook->editedby = '最后编辑';
-$lang->webhook->editedDate = '编辑时间';
+$lang->webhook->data = '数据';
+$lang->webhook->status = '状态';
+
+$lang->webhook->sendTypeList['sync'] = '同步';
+$lang->webhook->sendTypeList['async'] = '异步';
+
+$lang->webhook->paramsList['objectType'] = '对象类型';
+$lang->webhook->paramsList['objectID'] = '对象ID';
+$lang->webhook->paramsList['product'] = '所属产品';
+$lang->webhook->paramsList['project'] = '所属项目';
+$lang->webhook->paramsList['action'] = '动作';
+$lang->webhook->paramsList['actor'] = '操作者';
+$lang->webhook->paramsList['date'] = '操作日期';
+$lang->webhook->paramsList['comment'] = '备注';
+$lang->webhook->paramsList['message'] = '操作内容';
$lang->webhook->saveSuccess = '保存成功';
$lang->webhook->confirmDelete = '您确认要删除该webhook吗?';
+$lang->webhook->trimWords = '了';
+
$lang->webhook->note = new stdClass();
-$lang->webhook->note->name = '授权webhook名称';
-$lang->webhook->note->url = '授权webhook地址';
-$lang->webhook->note->params = '参数列表,以 & 隔开';
+$lang->webhook->note->async = '异步需要打开计划任务';
diff --git a/module/webhook/model.php b/module/webhook/model.php
index a2887d9d9e..8171198d2e 100644
--- a/module/webhook/model.php
+++ b/module/webhook/model.php
@@ -12,89 +12,342 @@
class webhookModel extends model
{
/**
- * Get an webhook by id.
+ * Get a webhook by id.
*
- * @param int $webhookID
+ * @param int $id
* @access public
* @return array
*/
- public function getById($webhookID)
+ public function getByID($id)
{
- return $this->dao->select('*')->from(TABLE_WEBHOOK)->where('id')->eq($webhookID)->fetch();
+ $webhook = $this->dao->select('*')->from(TABLE_WEBHOOK)->where('id')->eq($id)->fetch();
+ $webhook->actions = json_decode($webhook->actions);
+ return $webhook;
}
/**
* Get webhook list.
*
* @param string $orderBy
- * @param obejct $pager
+ * @param object $pager
* @access public
* @return array
*/
public function getList($orderBy = 'id_desc', $pager = null)
{
- return $this->dao->select('*')->from(TABLE_WEBHOOK)->orderBy($orderBy)->page($pager)->fetchAll('id');
+ $webhooks = $this->dao->select('*')->from(TABLE_WEBHOOK)->orderBy($orderBy)->page($pager)->fetchAll('id');
+ foreach($webhooks as $webhook) $webhook->actions = json_decode($webhook->actions);
+ return $webhooks;
}
/**
- * Create an webhook.
+ * Get log list of a webhook.
+ *
+ * @param int $id
+ * @param string $orderBy
+ * @param object $pager
+ * @access public
+ * @return array
+ */
+ public function getLogList($id, $orderBy = 'date_desc', $pager = null)
+ {
+ $logs = $this->dao->select('*')->from(TABLE_LOG)
+ ->where('objectType')->eq('webhook')
+ ->andWhere('objectID')->eq($id)
+ ->orderBy($orderBy)
+ ->page($pager)
+ ->fetchAll('id');
+ foreach($logs as $log) $log->data = json_decode($log->data);
+ return $logs;
+ }
+
+ /**
+ * Get saved data list.
*
* @access public
- * @return bool | int
+ * @return array
+ */
+ public function getDataList()
+ {
+ return $this->dao->select('*')->from(TABLE_WEBHOOKDATA)->where('status')->eq('wait')->orderBy('id')->fetchAll('id');
+ }
+
+ /**
+ * Get object types.
+ *
+ * @access public
+ * @return array
+ */
+ public function getObjectTypes()
+ {
+ $objectTypes = array();
+ foreach($this->config->webhook->objectTypes as $objectType => $actions)
+ {
+ $objectTypes[$objectType] = $this->lang->action->objectTypes[$objectType];
+ }
+ return $objectTypes;
+ }
+
+ /**
+ * Get object actions.
+ *
+ * @access public
+ * @return array
+ */
+ public function getObjectActions()
+ {
+ $objectActions = array();
+ foreach($this->config->webhook->objectTypes as $objectType => $actions)
+ {
+ foreach($actions as $action)
+ {
+ $objectActions[$objectType][$action] = str_replace($this->lang->webhook->trimWords, '', $this->lang->action->label->$action);
+ }
+ }
+ return $objectActions;
+ }
+
+ /**
+ * Create a webhook.
+ *
+ * @access public
+ * @return bool
*/
public function create()
{
$webhook = fixer::input('post')
->add('createdBy', $this->app->user->account)
->add('createdDate', helper::now())
+ ->setForce('params', implode(',', $this->post->params) . ',message')
+ ->setForce('actions', helper::jsonEncode($this->post->actions))
+ ->skipSpecial('url,actions')
->get();
-
+
$this->dao->insert(TABLE_WEBHOOK)->data($webhook)
->batchCheck($this->config->webhook->create->requiredFields, 'notempty')
->autoCheck()
->exec();
- if(dao::isError()) return false;
-
- return $this->dao->lastInsertId();
+ return !dao::isError();
}
/**
- * Update an webhook.
+ * Update a webhook.
*
- * @param int $webhookID
+ * @param int $id
* @access public
- * @return bool | array
+ * @return bool
*/
- public function update($webhookID)
+ public function update($id)
{
- $oldEntry = $this->getById($webhookID);
-
$webhook = fixer::input('post')
->add('editedBy', $this->app->user->account)
->add('editedDate', helper::now())
+ ->setForce('params', implode(',', $this->post->params) . ',message')
+ ->setForce('actions', helper::jsonEncode($this->post->actions))
+ ->skipSpecial('url,actions')
->get();
$this->dao->update(TABLE_WEBHOOK)->data($webhook)
->batchCheck($this->config->webhook->edit->requiredFields, 'notempty')
->autoCheck()
- ->where('id')->eq($webhookID)
+ ->where('id')->eq($id)
->exec();
- if(dao::isError()) return false;
-
- return common::createChanges($oldEntry, $webhook);
+ return !dao::isError();
}
/**
- * Delete an webhook.
+ * Delete a webhook.
*
- * @param int $webhookID
- * @param int $null
+ * @param int $id
+ * @param object $null
* @access public
* @return bool
*/
- public function delete($webhookID, $null = null)
+ public function delete($id, $null = null)
{
- $this->dao->delete()->from(TABLE_WEBHOOK)->where('id')->eq($webhookID)->exec();
+ $this->dao->delete()->from(TABLE_WEBHOOK)->where('id')->eq($id)->exec();
+ $this->dao->delete()->from(TABLE_LOG)->where('objectType')->eq('webhook')->andWhere('objectID')->eq($id)->exec();
+ return !dao::isError();
+ }
+
+ /**
+ * Send data.
+ *
+ * @param string $objectType
+ * @param int $objectID
+ * @param string $actionType
+ * @param int $actionID
+ * @access public
+ * @return bool
+ */
+ public function send($objectType, $objectID, $actionType, $actionID)
+ {
+ static $webhooks = array();
+ if(!$webhooks) $webhooks = $this->getList();
+ if(!$webhooks) return true;
+
+ $snoopy = $this->app->loadClass('snoopy');
+ foreach($webhooks as $id => $webhook)
+ {
+ if(!in_array($actionType, $webhook->actions->$objectType)) continue;
+ $postData = $this->buildData($objectType, $objectID, $actionType, $actionID, explode(',', $webhook->params));
+ if($webhook->sendType == 'async')
+ {
+ $this->saveData($id, $actionID, $postData);
+ continue;
+ }
+
+ $contentType = zget($this->config->webhook->contentTypes, $webhook->contentType, 'application/json');
+ $result = $this->fetchHook($contentType, $webhook->url, $postData);
+
+ $this->saveLog($id, $actionID, $webhook->url, $contentType, $postData, $result);
+ }
+ return !dao::isError();
+ }
+
+ /**
+ * Build data.
+ *
+ * @param string $objectType
+ * @param int $objectID
+ * @param string $actionType
+ * @param int $actionID
+ * @param array $params
+ * @access public
+ * @return string
+ */
+ public function buildData($objectType, $objectID, $actionType, $actionID, $params)
+ {
+ static $users = array();
+ if(empty($users)) $users = $this->loadModel('user')->getPairs('noletter');
+ if(!isset($this->lang->action->label)) $this->loadModel('action');
+ if(!isset($this->lang->action->label->$actionType)) return false;
+ if(empty($this->config->objectTables[$objectType])) return false;
+ $action = $this->dao->select('*')->from(TABLE_ACTION)->where('id')->eq($actionID)->fetch();
+ $object = $this->dao->select('*')->from($this->config->objectTables[$objectType])->where('id')->eq($objectID)->fetch();
+ $field = $this->config->action->objectNameFields[$objectType];
+ $title = $object->$field;
+
+ $oldOnlyBody = '';
+ if(isset($_GET['onlybody']) and $_GET['onlybody'] == 'yes')
+ {
+ $oldOnlyBody = 'yes';
+ unset($_GET['onlybody']);
+ }
+ $viewLink = helper::createLink($objectType, 'view', "id=$objectID");
+ if($oldOnlyBody) $_GET['onlybody'] = $oldOnlyBody;
+
+ $host = common::getSysURL();
+ $data = new stdclass();
+ foreach($params as $param)
+ {
+ if($param == 'message')
+ {
+ $data->text = $this->app->user->realname . $this->lang->action->label->$actionType . $this->lang->action->objectTypes[$objectType] . ' ' . "[#{$objectID}::{$title}](" . $host . $viewLink . ")";
+ if($actionType == 'assigned') $data->text .= ' ' . $this->lang->webhooks->assigned . ' ' . zget($users, $object->assignedTo);
+ }
+ else
+ {
+ $data->$param = $action->$param;
+ }
+ }
+
+ if(!empty($_FILES['files']['name'][0]))
+ {
+ $this->loadModel('file');
+ $files = $this->dao->select('*')->from(TABLE_FILE)->where('objectType')->eq($objectType)->andWhere('objectID')->eq($objectID)->orderBy('addedDate_desc,id_desc')->limit(count($_FILES['files']['name']))->fetchAll();
+ if($files)
+ {
+ foreach($files as $file)
+ {
+ $attachment = array();
+ $attachment['title'] = $file->title;
+ $attachment['images'][]['url'] = $data->host . $this->file->webPath . $file->pathname;
+ $data->attachments[] = $attachment;
+ }
+ }
+ }
+
+ return helper::jsonEncode($data);
+ }
+
+ /**
+ * Post hook data.
+ *
+ * @param string $contentType
+ * @param string $url
+ * @param string $sendData
+ * @access public
+ * @return int
+ */
+ public function fetchHook($contentType, $url, $sendData)
+ {
+ $header[] = "Content-Type: $contentType";
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $sendData);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_exec($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ curl_close($ch);
+
+ return $httpCode;
+ }
+
+ /**
+ * Save datas.
+ *
+ * @param int $webhookID
+ * @param int $actionID
+ * @param string $data
+ * @access public
+ * @return bool
+ */
+ public function saveData($webhookID, $actionID, $data)
+ {
+ $webhookData = new stdclass();
+ $webhookData->webhook = $webhookID;
+ $webhookData->action = $actionID;
+ $webhookData->data = $data;
+ $webhookData->createdBy = $this->app->user->account;
+ $webhookData->createdDate = helper::now();
+
+ $this->dao->insert(TABLE_WEBHOOKDATA)->data($webhookData)->exec();
+ return !dao::isError();
+ }
+
+ /**
+ * Save log.
+ *
+ * @param int $webhookID
+ * @param int $actionID
+ * @param string $url
+ * @param string $contentType
+ * @param string $data
+ * @param int $status
+ * @param string $error
+ * @access public
+ * @return bool
+ */
+ public function saveLog($webhookID, $actionID, $url, $contentType, $data, $status, $error = '')
+ {
+ $log = new stdclass();
+ $log->objectType = 'webhook';
+ $log->objectID = $webhookID;
+ $log->action = $actionID;
+ $log->date = helper::now();
+ $log->url = $url;
+ $log->contentType = $contentType;
+ $log->data = $data;
+ $log->status = $status;
+ $log->error = $error;
+
+ $this->dao->insert(TABLE_LOG)->data($log)->exec();
return !dao::isError();
}
}
diff --git a/module/webhook/view/browse.html.php b/module/webhook/view/browse.html.php
index 1f6a7cfafb..2ec836e1f4 100644
--- a/module/webhook/view/browse.html.php
+++ b/module/webhook/view/browse.html.php
@@ -13,36 +13,29 @@
webhook->confirmDelete);?>