diff --git a/db/update9.5.1.sql b/db/update9.5.1.sql index 6274fc817b..50c22de4a3 100644 --- a/db/update9.5.1.sql +++ b/db/update9.5.1.sql @@ -58,7 +58,7 @@ CREATE TABLE IF NOT EXISTS `zt_log` ( `url` varchar(255) NOT NULL, `contentType` varchar(40) NOT NULL, `data` text NOT NULL, - `status` smallint(5) NOT NULL, + `result` text NOT NULL, PRIMARY KEY `id` (`id`), KEY `objectType` (`objectType`), KEY `obejctID` (`objectID`) diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 05fd1dd1cb..b82e62f4db 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -63,7 +63,7 @@ $lang->action->objectTypes['bug'] = 'Bug'; $lang->action->objectTypes['case'] = 'Case'; $lang->action->objectTypes['caseresult'] = 'Case Result'; $lang->action->objectTypes['stepresult'] = 'Case Steps'; -$lang->action->objectTypes['testtask'] = 'Test Task'; +$lang->action->objectTypes['testtask'] = 'Test Build'; $lang->action->objectTypes['user'] = 'User'; $lang->action->objectTypes['doc'] = 'Document'; $lang->action->objectTypes['doclib'] = 'Doc Lib'; diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 5a4bd7f0b7..96e6aa1ca1 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -63,7 +63,7 @@ $lang->action->objectTypes['bug'] = 'Bug'; $lang->action->objectTypes['case'] = '用例'; $lang->action->objectTypes['caseresult'] = '用例结果'; $lang->action->objectTypes['stepresult'] = '用例步骤'; -$lang->action->objectTypes['testtask'] = '测试任务'; +$lang->action->objectTypes['testtask'] = '测试版本'; $lang->action->objectTypes['user'] = '用户'; $lang->action->objectTypes['doc'] = '文档'; $lang->action->objectTypes['doclib'] = '文档库'; diff --git a/module/common/lang/en.php b/module/common/lang/en.php index f838adff7e..d85d3bccfa 100644 --- a/module/common/lang/en.php +++ b/module/common/lang/en.php @@ -218,7 +218,7 @@ $lang->project->menu = new stdclass(); $lang->project->menu->list = array('link' => '%s', 'fixed' => true); $lang->project->menu->task = array('link' => 'Task|project|task|projectID=%s', 'subModule' => 'task,tree', 'alias' => 'grouptask,importtask,burn,importbug,kanban,printkanban,tree'); -$lang->project->menu->story = array('link' => 'Story|project|story|projectID=%s', 'subModule' => 'story', 'alias' => 'linkstory'); +$lang->project->menu->story = array('link' => 'Story|project|story|projectID=%s', 'subModule' => 'story', 'alias' => 'linkstory,storykanban'); $lang->project->menu->bug = 'Bug|project|bug|projectID=%s'; $lang->project->menu->dynamic = 'Dynamic|project|dynamic|projectID=%s'; $lang->project->menu->build = array('link' => 'Build|project|build|projectID=%s', 'subModule' => 'build'); diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index 0b8b7aa470..e7777a6a78 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -218,7 +218,7 @@ $lang->project->menu = new stdclass(); $lang->project->menu->list = array('link' => '%s', 'fixed' => true); $lang->project->menu->task = array('link' => '任务|project|task|projectID=%s', 'subModule' => 'task,tree', 'alias' => 'grouptask,importtask,burn,importbug,kanban,printkanban,tree'); -$lang->project->menu->story = array('link' => '需求|project|story|projectID=%s', 'subModule' => 'story', 'alias' => 'linkstory'); +$lang->project->menu->story = array('link' => '需求|project|story|projectID=%s', 'subModule' => 'story', 'alias' => 'linkstory,storykanban'); $lang->project->menu->bug = 'Bug|project|bug|projectID=%s'; $lang->project->menu->dynamic = '动态|project|dynamic|projectID=%s'; $lang->project->menu->build = array('link' => '版本|project|build|projectID=%s', 'subModule' => 'build'); diff --git a/module/common/model.php b/module/common/model.php index 92dd1eb233..898f63c27b 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -1162,6 +1162,8 @@ class commonModel extends model global $app; if(!empty($app->user->admin)) return true; + if($module = 'todo' and ($method == 'create' or $method == 'batchcreate')) return true; + if($module = 'effort' and ($method == 'createforobject' or $method == 'batchcreate')) return true; // limited project $limitedProject = false; diff --git a/module/entry/model.php b/module/entry/model.php index 2fc73aeddc..092c8c0556 100644 --- a/module/entry/model.php +++ b/module/entry/model.php @@ -23,6 +23,18 @@ class entryModel extends model return $this->dao->select('*')->from(TABLE_ENTRY)->where('id')->eq($entryID)->fetch(); } + /** + * Get an entry by code. + * + * @param string $code + * @access public + * @return array + */ + public function getByCode($code) + { + return $this->dao->select('*')->from(TABLE_ENTRY)->where('deleted')->eq('0')->andWhere('code')->eq($code)->fetch(); + } + /** * Get entry list. * @@ -93,4 +105,16 @@ class entryModel extends model return common::createChanges($oldEntry, $entry); } + + public function saveLog($entryID, $url) + { + $log = new stdclass(); + $log->objectType = 'entry'; + $log->objectID = $entryID; + $log->url = $url; + $log->date = helper::now(); + + $this->dao->insert(TABLE_LOG)->data($log)->exec(); + return !dao::isError(); + } } diff --git a/module/entry/view/create.html.php b/module/entry/view/create.html.php index 097f9bdf07..c159ef0a24 100644 --- a/module/entry/view/create.html.php +++ b/module/entry/view/create.html.php @@ -55,8 +55,7 @@ entry->apiIndex;?> - moduleVar}=moduleName&{$config->methodVar}=methodName&params=params";?> - + moduleVar}=moduleName&{$config->methodVar}=methodName&params=params&code=code&token=token";?> diff --git a/module/entry/view/edit.html.php b/module/entry/view/edit.html.php index 7a997621b0..9bdd029539 100644 --- a/module/entry/view/edit.html.php +++ b/module/entry/view/edit.html.php @@ -55,8 +55,7 @@ entry->apiIndex;?> - moduleVar}=moduleName&{$config->methodVar}=methodName&params=params";?> - + moduleVar}=moduleName&{$config->methodVar}=methodName&params=params&token=token";?> diff --git a/module/group/js/managepriv.js b/module/group/js/managepriv.js index eaf8b87770..e09acad56d 100644 --- a/module/group/js/managepriv.js +++ b/module/group/js/managepriv.js @@ -25,3 +25,15 @@ function setNoChecked() }) $('#noChecked').val(noCheckValue); } + +$(function() +{ + $("[name^='allchecker']").change(function() + { + var click = $(this).attr('onclick'); + if(click.indexOf('""') >= 0 || click.indexOf('"my"') >= 0) + { + $('#my-limited').closest('.group-item').find('input').prop("checked", false); + } + }) +}) diff --git a/module/project/control.php b/module/project/control.php index f4eb10402b..47eff3bad5 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -1425,6 +1425,47 @@ class project extends control $this->display(); } + /** + * Story kanban. + * + * @param int $projectID + * @access public + * @return void + */ + public function storyKanban($projectID) + { + /* Save to session. */ + $uri = $this->app->getURI(true); + $this->app->session->set('storyList', $uri); + + /* Compatibility IE8*/ + if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) header("X-UA-Compatible: IE=EmulateIE7"); + + $this->project->setMenu($this->projects, $projectID); + $project = $this->loadModel('project')->getById($projectID); + $stories = $this->loadModel('story')->getProjectStories($projectID); + $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false); + + /* Get project's product. */ + $productID = 0; + $productPairs = $this->loadModel('product')->getProductsByProject($projectID); + if($productPairs) $productID = key($productPairs); + + $this->view->title = $this->lang->project->kanban; + $this->view->position[] = html::a($this->createLink('project', 'story', "projectID=$projectID"), $project->name); + $this->view->position[] = $this->lang->project->kanban; + $this->view->stories = $this->story->getKanbanGroupData($stories); + $this->view->realnames = $this->loadModel('user')->getPairs('noletter'); + $this->view->projectID = $projectID; + $this->view->project = $project; + $this->view->productID = $productID; + + $kanbanSetting = $this->project->getKanbanSetting($projectID); + $this->view->showOption = $kanbanSetting->showOption; + + $this->display(); + } + /** * Delete a project. * diff --git a/module/project/css/storykanban.css b/module/project/css/storykanban.css new file mode 100644 index 0000000000..2a46209874 --- /dev/null +++ b/module/project/css/storykanban.css @@ -0,0 +1,69 @@ +#kanbanHeader th{border: 1px solid #ddd; padding:8px;} +#kanbanHeader .dropdown {display: inline-block;} +#kanbanHeader .dropdown > a {color: #036} +#kanbanHeader th{position: relative} +#kanbanHeader .actions {position: absolute; right: 0; top: 0; padding: 0px; background: #f1f1f1; border-left: 1px solid #ccc} +#kanbanHeader .actions .btn {display: block; border-radius: 0; width: 34px; height: 36px; position: relative; text-align: center; padding: 5px 0; color: #036} +#kanbanHeader .actions .btn-text {display: none} + +#kanbanWrapper {table-layout: fixed; border-top: none} +#kanbanWrapper > tbody > tr:first-child > td {border-top: none} +#kanbanWrapper thead th, #kanbanWrapper thead, #kanbanWrapper thead tr {padding: 0!important; border: none!important; height: 0!important; border-bottom: none!important} +#kanbanWrapper tbody td {vertical-align: top; padding: 6px} +#kanbanWrapper tbody th {background: #f1f1f1} +#kanbanWrapper .board-shadow {height: 0; opacity: 0; transition: opacity 0.2s;} +#kanbanWrapper tbody tr.dragging td.col-droppable {background: #fff4e5} +#kanbanWrapper tbody tr.dragging td.col-droppable.drop-to {background: #ffeacc} +#kanbanWrapper tbody tr.dragging td.col-droppable.drop-to .board-story + .board-shadow {margin-top: 6px;} +#kanbanWrapper tbody tr.dragging td.col-droppable.drop-to .board-shadow {height: 28px; background: #999; background: rgba(0,0,0,0.35); opacity: 1;} +#kanbanWrapper tbody tr.dragging td.col-droppable.drop-to.drag-from .board-shadow {display: none} +#kanbanWrapper .board.drag-from.dragging {opacity: 0.8;} + +.board {background: #333; color: #f1f1f1; color: rgba(255,255,255,0.9); opacity: 1; padding: 3px 6px; transition: all 0.2s; position:relative;} +.board + .board {margin-top: 6px;} +.board:hover {opacity: 1} +.board a {opacity: 0.9; color: inherit} +.board a:hover {opacity: 1} +.board-title, .board-header, .board-footer {padding: 2px 0} +.board-title {padding-right:35px;} +.board-actions {position: absolute; right: 3px; top: 3px} +.board-actions .btn {color: inherit; margin: 0; min-width: 20px} +.board-actions .btn:active, .board-actions .btn:focus, .board-actions .btn:hover {background: #999; color: inherit; background: rgba(255,255,255,.2);} +.ie-8 .board-actions .btn {min-width: 10px} +.board-actions > .dropdown {display: inline-block; margin-left: -5px;} +.board .dropdown-menu {min-width: 80px;} +.board .dropdown .caret {border-top-color: #999} +.board .dropdown-menu > a {display: block; padding: 3px 15px; color: #333; font-size: 12px} +.board .dropdown-menu > a:hover {background: #f1f1f1; color: #1a53ff; text-decoration: none} +.board .dropdown-menu > a.disabled, .board .dropdown-menu > a.disabled:hover, .board .dropdown-menu > a.active {color: #aaa; background: none} +.board .board-footer {display: none; opacity: 0.6} +.board.show-info .board-footer {display: block; margin: 0 -6px -3px -6px; padding: 5px; background: rgba(0,0,0,0.07);} +.board-footer .board-id {display: inline-block; border: 1px solid #aaa; font-weight: normal; padding: 0px 4px; text-align: center; min-width: 20px; line-height: 16px; height: 17px; margin-right: 10px; font-size: 12px;} +.board-footer .story-pri {margin-right: 10px;} + +.pri-0:before, .pri-3:before {content: "3";} +.pri-1:before {content: "1";} +.pri-2:before {content: "2";} +.pri-4:before {content: "4";} +.pri-5:before {content: "5";} +.pri-6:before {content: "6";} + +.story-pri {display: inline-block; width: 16px; height: 16px; text-align: center; border-radius: 8px; line-height: 16px; font-size: 12px} +.story-pri.pri-0 {background-color:#b56a00;} +.story-pri.pri-1 {background-color:#E30413;} +.story-pri.pri-2 {background-color:#E84E0E;} +.story-pri.pri-3 {background-color:#b56a00;} +.story-pri.pri-4 {background-color:#795523;} + +.board-story {color: #dd7503; background-color: #FFF3E0;} +.board-story.stage-projected {color:#777;} +.board-story.stage-developing {color:#D2322D; background-color: #FFEBEE;} +.board-story.stage-developed {color:#229F24; background-color: #E8F5E9;} +.board-story.stage-testing {color:#EF6C00; background-color: #FFF3E0;} +.board-story.stage-tested {color:#827717; background-color: #F9FBE7} +.board-story.stage-released {color:#9C27B0; background-color: #F3E5F5} + +.affix {position:fixed; top:0px; width:95%;z-index:1030;} +#kanbanHeader.affix {box-shadow: 0 3px 10px rgba(0,0,0,.1);} +#kanbanHeader.affix th {border-bottom: 1px solid #bbb; background: #114f8e; color: #ccc; color: rgba(255,255,255,.8)} +#kanbanHeader.affix th {border-color: rgba(255,255,255,.2)} diff --git a/module/project/js/storykanban.js b/module/project/js/storykanban.js new file mode 100644 index 0000000000..e11db3f2c0 --- /dev/null +++ b/module/project/js/storykanban.js @@ -0,0 +1,130 @@ +$(function() +{ + var boardID = ''; + var onlybody = config.requestType == 'GET' ? "&onlybody=yes" : "?onlybody=yes"; + $(".kanbanFrame").modalTrigger({type: 'iframe', width: '80%', afterShow:function(){ $('#ajaxModal').data('cancel-reload', true)}, afterHidden: function(){refresh()}}); + + $.cookie('selfClose', 0, {expires:config.cookieLife, path:config.webRoot}); + + var $kanban = $('#kanban'); + var $kanbanWrapper = $('#kanbanWrapper'); + + initBoards(); + + var stageMap = + { + story: + { + projected : {verified: 'batchChangeStage'}, + developing : {verified: 'batchChangeStage'}, + developed : {verified: 'batchChangeStage'}, + testing : {verified: 'batchChangeStage'}, + tested : {verified: 'batchChangeStage'}, + } + }; + + var lastOperation; + + function dropTo(id, from, to, type) + { + if(stageMap[type][from] && stageMap[type][from][to]) + { + lastOperation = {id: id, from: from, to: to}; + $.post(createLink(type, stageMap[type][from][to], 'stage=' + to), {'storyIDList[]':[id]}); + return true; + } + return false; + } + + function initBoards() + { + $('.col-droppable').append('
'); + + var $boardTasks = $kanban.find('.board-story'); + $boardTasks.droppable( + { + target: '.col-droppable', + flex: true, + before: function(e) + { + if(e.element.find('.dropdown.open').length) return false; + }, + start: function(e) + { + e.element.closest('td').addClass('drag-from').closest('tr').addClass('dragging'); + $kanban.addClass('dragging').find('.board-item-shadow').height(e.element.outerHeight()); + }, + drag: function(e) + { + if(e.isNew) + { + var $dargShadow = $('.drag-shadow.board-story'); + for(var stage in stageMap['story']) + { + $dargShadow.removeClass('board-story-' + stage); + } + $dargShadow.addClass('board-story-' + e.target.data('id')); + } + }, + drop: function(e) + { + if(e.isNew && e.element.closest('tr').data('id') == e.target.closest('tr').data('id')) + { + var result = dropTo(e.element.data('id'), e.element.closest('td').data('id'), e.target.data('id'), 'story'); + if(result !== false) + { + for(var stage in stageMap['story']) + { + e.element.removeClass('board-story-' + stage); + } + e.element.addClass('board-story-' + e.target.data('id')).insertBefore(e.target.find('.board-shadow')); + } + } + }, + finish: function(e) + { + $kanban.removeClass('dragging drop-in'); + $kanbanWrapper.find('tr.dragging').removeClass('dragging').find('.drop-in, .drag-from').removeClass('drop-in drag-from'); + } + }); + } + + function refresh() + { + var selfClose = $.cookie('selfClose'); + $.cookie('selfClose', 0, {expires:config.cookieLife, path:config.webRoot}); + if(selfClose == 1) + { + $('#kanbanWrapper').wrap("
"); + $('#tempDIV').load(location.href + ' #kanbanWrapper', function() + { + $('#kanbanWrapper').unwrap(); + initBoards() + $(".kanbanFrame").modalTrigger({type: 'iframe', width: '80%', afterShow:function(){ $('#ajaxModal').data('cancel-reload', true)}, afterHidden: function(){refresh()}}); + }); + } + } + + var fixH = $("#kanbanHeader").offset().top; + $(window).scroll(function() + { + var scroH = $(this).scrollTop(); + if(scroH>=fixH) + { + $("#kanbanHeader").addClass('affix'); + $("#kanbanHeader").width($('#kanbanWrapper').width()); + } + else if(scroH
id", '', 'button', 'columns', '', '', '', '', $lang->project->kanban); common::printIcon('story', 'export', "productID=$productID&orderBy=id_desc", '', 'button', '', '', 'export'); $this->lang->story->create = $this->lang->project->createStory; diff --git a/module/project/view/storykanban.html.php b/module/project/view/storykanban.html.php new file mode 100644 index 0000000000..21fe7bb2f1 --- /dev/null +++ b/module/project/view/storykanban.html.php @@ -0,0 +1,101 @@ + + +project->confirmUnlinkStory)?> +
+
icons['story']);?> project->story;?>
+
+
+ id", '', 'button', 'list', '', '', '', '', $lang->project->story); + common::printIcon('story', 'export', "productID=$productID&orderBy=id_desc", '', 'button', '', '', 'export'); + + $this->lang->story->create = $this->lang->project->createStory; + if($productID and !$this->loadModel('story')->checkForceReview()) common::printIcon('story', 'create', "productID=$productID&branch=&moduleID=0&story=0&project=$project->id"); + + if(commonModel::isTutorialMode()) + { + $wizardParams = helper::safe64Encode("project=$project->id"); + echo html::a($this->createLink('tutorial', 'wizard', "module=project&method=linkStory¶ms=$wizardParams"), " {$lang->project->linkStory}",'', "class='btn link-story-btn'"); + } + else + { + common::printIcon('project', 'linkStory', "project=$project->id", '', 'button', 'link', '', 'link-story-btn'); + } + ?> +
+
+
+
+app->user->account +?> +
+ + + + + + + + + + +
story->stageList[$col];?>story->stageList[$endCol];?>
+ + + + + + + + + + + + + + +
+ + +
+
+ createLink('story', 'view', "story=$story->id", '', true), $story->title, '', 'class="kanbanFrame" title="' . $story->title . '"');?> +
+ + +
+
+ +
+ + +
+
+ + diff --git a/module/project/view/view.html.php b/module/project/view/view.html.php index 388c7a333e..aa3a862486 100644 --- a/module/project/view/view.html.php +++ b/module/project/view/view.html.php @@ -126,7 +126,8 @@ { if($product->type !== 'normal') { - echo html::a($this->createLink('product', 'browse', "productID=$productID&branch=$product->branch"), $product->name . '/' . $branchGroups[$productID][$product->branch]); + $branchName = isset($branchGroups[$productID][$product->branch]) ? '/' . $branchGroups[$productID][$product->branch] : '' + echo html::a($this->createLink('product', 'browse', "productID=$productID&branch=$product->branch"), $product->name . $branchName); } else { diff --git a/module/story/model.php b/module/story/model.php index 017dde597f..da7345bef5 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -2174,6 +2174,21 @@ class storyModel extends model ->groupBy('version')->orderBy('value')->fetchAll(); } + /** + * Get kanban group data. + * + * @param array $stories + * @access public + * @return array + */ + public function getKanbanGroupData($stories) + { + $storyGroup = array(); + foreach($stories as $story) $storyGroup[$story->stage][$story->id] = $story; + + return $storyGroup; + } + /** * Adjust the action clickable. * diff --git a/module/webhook/config.php b/module/webhook/config.php index f261ff89bb..7f69b69cd9 100644 --- a/module/webhook/config.php +++ b/module/webhook/config.php @@ -13,7 +13,7 @@ $config->webhook->objectTypes['story'] = array('opened', 'edited', 'commen $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', 'paused', 'canceled', 'restarted', 'closed', 'activated'); +$config->webhook->objectTypes['task'] = array('opened', 'edited', 'commented', 'assigned', 'confirmed', 'started', 'finished', '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'); diff --git a/module/webhook/lang/zh-cn.php b/module/webhook/lang/zh-cn.php index 1ebd344a02..be16f050e3 100644 --- a/module/webhook/lang/zh-cn.php +++ b/module/webhook/lang/zh-cn.php @@ -5,7 +5,7 @@ $lang->webhook->api = '接口'; $lang->webhook->entry = '应用'; $lang->webhook->dingding = '钉钉'; $lang->webhook->log = '日志'; -$lang->webhook->assigend = '指派给'; +$lang->webhook->assigned = '指派给'; $lang->webhook->browse = '浏览钩子'; $lang->webhook->create = '添加钩子'; diff --git a/module/webhook/model.php b/module/webhook/model.php index 0fb152b733..211dd1b870 100644 --- a/module/webhook/model.php +++ b/module/webhook/model.php @@ -69,7 +69,7 @@ class webhookModel extends model $this->loadModel('action'); $actions = $this->dao->select('*')->from(TABLE_ACTION)->where('id')->in($actions)->fetchAll('id'); - + $users = $this->loadModel('user')->getPairs('noletter'); foreach($logs as $log) { if(!isset($actions[$log->action])) @@ -100,7 +100,7 @@ class webhookModel extends model */ public function getDataList() { - return $this->dao->select('*')->from(TABLE_WEBHOOKDATA)->where('status')->eq('wait')->orderBy('id')->fetchAll('id'); + return $this->dao->select('*')->from(TABLE_WEBHOOKDATAS)->where('status')->eq('wait')->orderBy('id')->fetchAll('id'); } /** @@ -317,6 +317,7 @@ class webhookModel extends model $oldOnlyBody = 'yes'; unset($_GET['onlybody']); } + if($objectType == 'case') $objectType = 'testcase'; $viewLink = helper::createLink($objectType, 'view', "id=$objectID"); if($oldOnlyBody) $_GET['onlybody'] = $oldOnlyBody; @@ -401,7 +402,7 @@ class webhookModel extends model $webhookData->createdBy = $this->app->user->account; $webhookData->createdDate = helper::now(); - $this->dao->insert(TABLE_WEBHOOKDATA)->data($webhookData)->exec(); + $this->dao->insert(TABLE_WEBHOOKDATAS)->data($webhookData)->exec(); return !dao::isError(); }