diff --git a/db/update15.4.sql b/db/update15.4.sql index 190152b7b2..cf157be1ee 100644 --- a/db/update15.4.sql +++ b/db/update15.4.sql @@ -23,4 +23,3 @@ CREATE TABLE `zt_mr` ( `mergeStatus` char(30) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - diff --git a/framework/api/entry.class.php b/framework/api/entry.class.php index 1905de0985..8f8496fed1 100644 --- a/framework/api/entry.class.php +++ b/framework/api/entry.class.php @@ -264,6 +264,10 @@ class baseEntry global $app; $app->setModuleName($moduleName); $app->setMethodName($methodName); + + /* Check user permission. */ + $this->checkPriv(); + $app->setControlFile(); /* @@ -550,4 +554,20 @@ class baseEntry $entry = new $entryName(); return call_user_func_array(array($entry, $method), $params); } + + /** + * Check the user has permission to access this method, if not, return 403. + * + * @access public + * @return void + */ + public function checkPriv() + { + $module = $this->app->getModuleName(); + $method = $this->app->getMethodName(); + if($module and $method and !commonModel::hasPriv($module, $method)) + { + $this->send(403, array('error' => 'Access not allowed')); + } + } } diff --git a/lib/front/front.class.php b/lib/front/front.class.php index a3a0fbfe2c..b097da7a91 100644 --- a/lib/front/front.class.php +++ b/lib/front/front.class.php @@ -234,11 +234,14 @@ class html extends baseHTML /** * Create user avatar. * - * @param string|object|array $user User object or user avatar address or user account - * @param string|int $size Avatar size, can be a number or preset sizes: "xs", "sm", "", "lg", "xl", default is "" - * @param string $className Avatar element class name, default is "avatar-circle" - * @param string $attrib Extra attributes on avatar element - * @param string $tag Avatar element tag name, default is "div" + * @param string|object|array $user User object or user avatar url or user account + * @param string|int $size Avatar size, can be a number or preset sizes: "xs", "sm", "", "lg", "xl", default is "" + * @param string $className Avatar element class name, default is "avatar-circle" + * @param string $attrib Extra attributes on avatar element + * @param string $tag Avatar element tag name, default is "div" + * @param string $hueDistance Hue distance used as background color for default avatar + * @param string $saturation Saturation used as background color for default avatar + * @param string $lightness Lightness used as background color for default avatar * @static * @access public * @return string @@ -290,7 +293,7 @@ class html extends baseHTML /** * Create a small user avatar. * - * @param string|object $user User object or user avatar address or user account + * @param string|object $user User object or user avatar url or user account * @param string $className Avatar element class name, default is "avatar-circle" * @param string $attrib Extra attributes on avatar element * @param string $tag Avatar element tag name, default is "div" @@ -306,7 +309,7 @@ class html extends baseHTML /** * Create a large user avatar. * - * @param string|object $user User object or user avatar address or user account + * @param string|object $user User object or user avatar url or user account * @param string $className Avatar element class name, default is "avatar-circle" * @param string $attrib Extra attributes on avatar element * @param string $tag Avatar element tag name, default is "div" diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 9d69e0cbc0..3eccd994f4 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -566,3 +566,99 @@ $lang->action->search->label['paused'] = $lang->action->label->pa $lang->action->search->label['verified'] = $lang->action->label->verified; $lang->action->search->label['login'] = $lang->action->label->login; $lang->action->search->label['logout'] = $lang->action->label->logout; + +/* Action language config for API(JIHU) */ +$lang->action->apiTitle = new stdclass(); +$lang->action->apiTitle->opened = 'Opened.'; +$lang->action->apiTitle->created = 'Created.'; +$lang->action->apiTitle->added = 'Added.'; +$lang->action->apiTitle->changed = 'Changed.'; +$lang->action->apiTitle->edited = 'Edited.'; +$lang->action->apiTitle->assigned = 'Assigned to %s.'; +$lang->action->apiTitle->closed = 'Closed.'; +$lang->action->apiTitle->deleted = 'Deleted.'; +$lang->action->apiTitle->deletedfile = 'Deleted: %s.'; +$lang->action->apiTitle->editfile = 'Edited: %s.'; +$lang->action->apiTitle->erased = 'Erased.'; +$lang->action->apiTitle->undeleted = 'Restored.'; +$lang->action->apiTitle->hidden = 'Hidden.'; +$lang->action->apiTitle->commented = 'Commented'; +$lang->action->apiTitle->activated = 'Activated.'; +$lang->action->apiTitle->confirmed = 'Confirmed.'; +$lang->action->apiTitle->caseconfirmed = '$actor confirmed the case change. The latest build is #$extra'; +$lang->action->apiTitle->bugconfirmed = '$actor confirmed Bug.'; +$lang->action->apiTitle->frombug = 'Converted from $actor. Its ID was $extra.'; +$lang->action->apiTitle->started = 'Started by $actor.'; +$lang->action->apiTitle->restarted = 'Continued by $actor.'; +$lang->action->apiTitle->delayed = 'Postponed by $actor.'; +$lang->action->apiTitle->suspended = 'Suspended by $actor.'; +$lang->action->apiTitle->recordestimate = 'Recorded by $actor and it cost $extra hours.'; +$lang->action->apiTitle->editestimate = '$actor edited Hour.'; +$lang->action->apiTitle->deleteestimate = '$actor deleted Hour.'; +$lang->action->apiTitle->canceled = 'Cancelled by $actor.'; +$lang->action->apiTitle->svncommited = '$actor committed and the build is #$extra.'; +$lang->action->apiTitle->gitcommited = '$actor committed and the build is #$extra.'; +$lang->action->apiTitle->finished = 'Finished by $actor.'; +$lang->action->apiTitle->paused = 'Paused by $actor.'; +$lang->action->apiTitle->linked2bug = 'Linked to $extra by $actor'; +$lang->action->apiTitle->linked2testtask = 'Linked to $extra by $actor'; +$lang->action->apiTitle->resolved = 'Resolved by $actor '; +$lang->action->apiTitle->managed = 'By $actor managed.'; +$lang->action->apiTitle->estimated = 'By $actor estimated.'; +$lang->action->apiTitle->run = 'By $actor executed.'; +$lang->action->apiTitle->tostory = "To story: {$lang->SRCommon}"; +$lang->action->apiTitle->createchildren = 'Create children %s.'; +$lang->action->apiTitle->linkchildtask = 'Link a child task %s.'; +$lang->action->apiTitle->unlinkchildrentask = 'Unlink a child task %s.'; +$lang->action->apiTitle->linkparenttask = 'Link a parent task %s.'; +$lang->action->apiTitle->unlinkparenttask = 'Unlink a parent task %s.'; +$lang->action->apiTitle->deletechildrentask = 'Delete a child task%s.'; +$lang->action->apiTitle->createchildrenstory = 'Create a child story %s.'; +$lang->action->apiTitle->linkchildstory = 'Link a child story %s.'; +$lang->action->apiTitle->unlinkchildrenstory = 'Unlink a child story %s.'; +$lang->action->apiTitle->linkparentstory = 'Link parent story %s.'; +$lang->action->apiTitle->unlinkparentstory = 'Unlink parent story from %s.'; +$lang->action->apiTitle->deletechildrenstory = 'Deleted a child story %s.'; +$lang->action->apiTitle->linkrelatedcase = 'Link related case %s.'; +$lang->action->apiTitle->unlinkrelatedcase = 'Unlink related case %s.'; +$lang->action->apiTitle->openedbysystem = 'Opened by system'; +$lang->action->apiTitle->closedbysystem = 'Closed by system'; +$lang->action->apiTitle->communicated = 'Communicated'; +$lang->action->apiTitle->blocked = 'Blocked'; +$lang->action->apiTitle->reviewed = 'Reviewed'; +$lang->action->apiTitle->recalled = 'Recalled'; +$lang->action->apiTitle->moved = 'Moved'; +$lang->action->apiTitle->fromlib = 'Import from lib'; +$lang->action->apiTitle->totask = 'To task'; +$lang->action->apiTitle->linked2plan = "Link to plan"; +$lang->action->apiTitle->unlinkedfromplan = "Unlinked from plan"; +$lang->action->apiTitle->changestatus = 'Changed status'; +$lang->action->apiTitle->marked = 'Marked'; +$lang->action->apiTitle->linked2execution = "Linked to {$lang->executionCommon}"; +$lang->action->apiTitle->unlinkedfromexecution = "Unlinked from {$lang->executionCommon}"; +$lang->action->apiTitle->linked2project = "Linked to project"; +$lang->action->apiTitle->unlinkedfromproject = "Unlinked from project"; +$lang->action->apiTitle->unlinkedfrombuild = "Unlinked from build"; +$lang->action->apiTitle->linked2release = "Linked to release"; +$lang->action->apiTitle->unlinkedfromrelease = "Unlinked from release"; +$lang->action->apiTitle->linkrelatedbug = "Linked related Bug"; +$lang->action->apiTitle->unlinkrelatedbug = "Unlinked related Bug"; +$lang->action->apiTitle->linkrelatedstory = "Link related story {$lang->SRCommon}"; +$lang->action->apiTitle->unlinkrelatedstory = "Unlink related story {$lang->SRCommon}"; +$lang->action->apiTitle->subdividestory = "Subdivided story: {$lang->SRCommon}"; +$lang->action->apiTitle->unlinkchildstory = "Unlink child story: {$lang->SRCommon}"; +$lang->action->apiTitle->verified = 'Verified'; +$lang->action->apiTitle->login = 'Login'; +$lang->action->apiTitle->logout = "Logout"; +$lang->action->apiTitle->linked2build = "Linked to build"; +$lang->action->apiTitle->unlinkedfromtesttask = "Unlinked from test task"; +$lang->action->apiTitle->batchcreate = "Batch create"; +$lang->action->apiTitle->managedteam = "Managed team"; +$lang->action->apiTitle->managedwhitelist = "Managed white list"; +$lang->action->apiTitle->tracked = 'Tracked'; +$lang->action->apiTitle->hangup = 'Hangup'; +$lang->action->apiTitle->reviewpassed = 'Review passed'; +$lang->action->apiTitle->reviewrejected = 'Review rejected'; +$lang->action->apiTitle->reviewclarified = 'Review clarified'; +$lang->action->apiTitle->commitsummary = 'Commit summary'; +$lang->action->apiTitle->updatetrainee = 'Update trainee'; diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 3a36d6bb45..37a184818e 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -566,3 +566,99 @@ $lang->action->search->label['paused'] = $lang->action->label->pa $lang->action->search->label['verified'] = $lang->action->label->verified; $lang->action->search->label['login'] = $lang->action->label->login; $lang->action->search->label['logout'] = $lang->action->label->logout; + +/* Action language config for API(JIHU) */ +$lang->action->apiTitle = new stdclass(); +$lang->action->apiTitle->opened = '首次创建。'; +$lang->action->apiTitle->created = '首次创建。'; +$lang->action->apiTitle->added = '添加成功。'; +$lang->action->apiTitle->changed = '进行了需求变更。'; +$lang->action->apiTitle->edited = '编辑操作。'; +$lang->action->apiTitle->assigned = '指派给 %s。'; +$lang->action->apiTitle->closed = '执行了关闭操作。'; +$lang->action->apiTitle->deleted = '执行删除操作。'; +$lang->action->apiTitle->deletedfile = '删除了附件:%s。'; +$lang->action->apiTitle->editfile = '编辑了附件:%s。'; +$lang->action->apiTitle->erased = '执行了删除操作。'; +$lang->action->apiTitle->undeleted = '执行还原操作。'; +$lang->action->apiTitle->hidden = '进行隐藏操作。'; +$lang->action->apiTitle->commented = '添加了备注'; +$lang->action->apiTitle->activated = '执行激活操作。'; +$lang->action->apiTitle->confirmed = '确认' . $lang->SRCommon . '变动,最新版本为#%s。'; +$lang->action->apiTitle->caseconfirmed = '确认用例变动,最新版本为#%s。'; +$lang->action->apiTitle->bugconfirmed = '确认了Bug。'; +$lang->action->apiTitle->frombug = 'Bug转化而来,Bug编号为 %s。'; +$lang->action->apiTitle->started = '启动。'; +$lang->action->apiTitle->restarted = '继续。'; +$lang->action->apiTitle->delayed = '执行了延期操作。'; +$lang->action->apiTitle->suspended = '挂起。'; +$lang->action->apiTitle->recordestimate = '记录工时,消耗 %s 小时。'; +$lang->action->apiTitle->editestimate = '编辑了工时。'; +$lang->action->apiTitle->deleteestimate = '删除工时。'; +$lang->action->apiTitle->canceled = '执行取消操作。'; +$lang->action->apiTitle->svncommited = '提交代码,版本为#%s。'; +$lang->action->apiTitle->gitcommited = '提交代码,版本为#%s。'; +$lang->action->apiTitle->finished = '完成。'; +$lang->action->apiTitle->paused = '暂停。'; +$lang->action->apiTitle->linked2bug = '关联到版本 %s'; +$lang->action->apiTitle->linked2testtask = '关联到测试单 %s'; +$lang->action->apiTitle->resolved = '解决。'; +$lang->action->apiTitle->managed = '维护。'; +$lang->action->apiTitle->estimated = '估算。'; +$lang->action->apiTitle->run = '执行。'; +$lang->action->apiTitle->tostory = "转{$lang->SRCommon}"; +$lang->action->apiTitle->createchildren = '创建子任务 %s。'; +$lang->action->apiTitle->linkchildtask = '关联子任务 %s。'; +$lang->action->apiTitle->unlinkchildrentask = '移除子任务 %s。'; +$lang->action->apiTitle->linkparenttask = '关联到父任务 %s。'; +$lang->action->apiTitle->unlinkparenttask = '从父任务%s取消关联。'; +$lang->action->apiTitle->deletechildrentask = '删除子任务%s。'; +$lang->action->apiTitle->createchildrenstory = '创建子需求 %s。'; +$lang->action->apiTitle->linkchildstory = '关联子需求 %s。'; +$lang->action->apiTitle->unlinkchildrenstory = '移除子需求 %s。'; +$lang->action->apiTitle->linkparentstory = '关联到父需求 %s。'; +$lang->action->apiTitle->unlinkparentstory = '从父需求%s取消关联。'; +$lang->action->apiTitle->deletechildrenstory = '删除子需求%s。'; +$lang->action->apiTitle->linkrelatedcase = '关联相关用例 %s。'; +$lang->action->apiTitle->unlinkrelatedcase = '移除相关用例 %s。'; +$lang->action->apiTitle->openedbysystem = '系统创建'; +$lang->action->apiTitle->closedbysystem = '系统关闭'; +$lang->action->apiTitle->communicated = '沟通'; +$lang->action->apiTitle->blocked = '阻塞'; +$lang->action->apiTitle->reviewed = '评审'; +$lang->action->apiTitle->recalled = '撤销评审'; +$lang->action->apiTitle->moved = '移动'; +$lang->action->apiTitle->fromlib = '从用例库导入'; +$lang->action->apiTitle->totask = '转任务'; +$lang->action->apiTitle->linked2plan = "关联计划"; +$lang->action->apiTitle->unlinkedfromplan = "移除计划"; +$lang->action->apiTitle->changestatus = '修改状态'; +$lang->action->apiTitle->marked = '编辑'; +$lang->action->apiTitle->linked2execution = "关联{$lang->executionCommon}"; +$lang->action->apiTitle->unlinkedfromexecution = "移除{$lang->executionCommon}"; +$lang->action->apiTitle->linked2project = "关联项目"; +$lang->action->apiTitle->unlinkedfromproject = "移除项目"; +$lang->action->apiTitle->unlinkedfrombuild = "移除版本"; +$lang->action->apiTitle->linked2release = "关联发布"; +$lang->action->apiTitle->unlinkedfromrelease = "移除发布"; +$lang->action->apiTitle->linkrelatedbug = "关联了相关Bug"; +$lang->action->apiTitle->unlinkrelatedbug = "移除了相关Bug"; +$lang->action->apiTitle->linkrelatedstory = "关联了相关{$lang->SRCommon}"; +$lang->action->apiTitle->unlinkrelatedstory = "移除了相关{$lang->SRCommon}"; +$lang->action->apiTitle->subdividestory = "细分了{$lang->SRCommon}"; +$lang->action->apiTitle->unlinkchildstory = "移除了细分{$lang->SRCommon}"; +$lang->action->apiTitle->verified = '验收'; +$lang->action->apiTitle->login = '登录系统'; +$lang->action->apiTitle->logout = "退出登录"; +$lang->action->apiTitle->linked2build = "关联"; +$lang->action->apiTitle->unlinkedfromtesttask = "移除"; +$lang->action->apiTitle->batchcreate = "批量创建任务"; +$lang->action->apiTitle->managedteam = "维护"; +$lang->action->apiTitle->managedwhitelist = "维护"; +$lang->action->apiTitle->tracked = '跟踪'; +$lang->action->apiTitle->hangup = '挂起'; +$lang->action->apiTitle->reviewpassed = '确认通过'; +$lang->action->apiTitle->reviewrejected = '拒绝'; +$lang->action->apiTitle->reviewclarified = '有待明确'; +$lang->action->apiTitle->commitsummary = '提交培训总结'; +$lang->action->apiTitle->updatetrainee = '更新培训人员'; diff --git a/module/action/model.php b/module/action/model.php index 684976023f..eba7679086 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -1513,4 +1513,43 @@ class actionModel extends model $this->search->saveIndex($objectType, $data); } + + /** + * Print actions of an object for API(JIHU). + * + * @param array $action + * @access public + * @return void + */ + public function printActionAPI($action) + { + if(!isset($action->objectType) or !isset($action->action)) return false; + + $objectType = $action->objectType; + $actionType = strtolower($action->action); + + if(isset($this->lang->action->apiTitle->$actionType) and isset($action->extra)) + { + /* If extra column is a username, then assemble link to that. */ + if($action->action == "assigned") + { + $userDetails = $this->loadModel('user')->getUserDetailsForAPI($action->extra); + if(isset($userDetails[$action->extra])) + { + $userDetail = $userDetails[$action->extra]; + $action->extra = "{$action->extra}"; + } + } + + echo sprintf($this->lang->action->apiTitle->$actionType, $action->extra); + } + elseif(isset($this->lang->action->apiTitle->$actionType) and !isset($action->extra)) + { + echo $this->lang->action->apiTitle->$actionType; + } + else + { + echo $actionType; + } + } } diff --git a/module/bug/css/batchcreate.css b/module/bug/css/batchcreate.css index 30412ed704..9cd6305f9e 100644 --- a/module/bug/css/batchcreate.css +++ b/module/bug/css/batchcreate.css @@ -1,6 +1,6 @@ .c-id {width: 50px !important;} .c-branch, .c-module, .c-os {width: 120px;} .c-execution {width: 130px;} -.c-build, .c-browser, .c-keywords, .c-other {width: 100px;} +.c-build, .c-browser, .c-keywords {width: 100px;} .c-steps {width: 150px;} .c-pri, .c-severity {width: 80px;} diff --git a/module/bug/css/batchedit.css b/module/bug/css/batchedit.css index b928b1c4ac..3b9c06964d 100644 --- a/module/bug/css/batchedit.css +++ b/module/bug/css/batchedit.css @@ -4,6 +4,6 @@ .c-id {width: 50px !important;} .c-branch, .c-module, .c-os {width: 120px;} .c-execution {width: 130px;} -.c-build, .c-browser, .c-keywords, .c-other {width: 100px;} +.c-build, .c-browser, .c-keywords {width: 100px;} .c-steps, .c-title, .c-plan, .c-assigned, .c-resolution {width: 150px;} .c-pri, .c-severity {width: 80px;} diff --git a/module/bug/view/batchcreate.html.php b/module/bug/view/batchcreate.html.php index 26e6b3cfda..8df99d947d 100644 --- a/module/bug/view/batchcreate.html.php +++ b/module/bug/view/batchcreate.html.php @@ -62,7 +62,7 @@ '>bug->keywords;?> bug->getFlowExtendFields(); - foreach($extendFields as $extendField) echo "{$extendField->name}"; + foreach($extendFields as $extendField) echo "{$extendField->name}"; ?> diff --git a/module/bug/view/batchedit.html.php b/module/bug/view/batchedit.html.php index 490f027efa..86a92f7867 100644 --- a/module/bug/view/batchedit.html.php +++ b/module/bug/view/batchedit.html.php @@ -66,7 +66,7 @@ '>bug->resolutionAB;?> bug->getFlowExtendFields(); - foreach($extendFields as $extendField) echo "{$extendField->name}"; + foreach($extendFields as $extendField) echo "{$extendField->name}"; ?> diff --git a/module/common/view/kanban.html.php b/module/common/view/kanban.html.php index d8dacfdea2..9a6cf6bd3f 100644 --- a/module/common/view/kanban.html.php +++ b/module/common/view/kanban.html.php @@ -8,9 +8,9 @@ #kanbanList .kanban-item {margin-top: 0; border: 1px solid #ebebeb; border-radius: 2px;} #kanbanList .kanban-item:hover {border: 1px solid #ccc;} #kanbanList .kanban-item + .kanban-item {margin-top: 10px;} -#kanbanList .kanban-lane-items {padding: 10px;} +#kanbanList .kanban-lane-items {padding: 10px; min-height: 38px;} #kanbanList .kanban-header, -#kanbanList .kanban-lane {border-bottom: none; margin-bottom: 0;} +#kanbanList .kanban-lane {border-bottom: none; margin-bottom: 0; min-height: 60px;} #kanbanList .kanban-sub-lane {border-bottom: 0;} #kanbanList .kanban-lane {border-top: 2px solid #fff;} #kanbanList .kanban-lane + .kanban-lane {border-top: 10px solid #fff;} @@ -196,7 +196,7 @@ function renderExecutionItem(item, $item) { $item.addClass('link-block'); $title = $('') - .attr('href', $.createLink('execution', 'task', 'executionID=' + item.id)); + .attr('href', $.createLink('execution', 'task', 'executionID=' + item._id)); } else { diff --git a/module/doc/control.php b/module/doc/control.php index 3c10c9a467..7f72f78c8e 100644 --- a/module/doc/control.php +++ b/module/doc/control.php @@ -334,7 +334,7 @@ class doc extends control $lib = $this->doc->getLibByID($libID); $type = isset($lib->type) ? $lib->type : 'product'; - $libName = isset($lib->name) ? $lib->name : 'product'; + $libName = isset($lib->name) ? $lib->name . $this->lang->colon : ''; $this->view->title = $libName . $this->lang->doc->create; diff --git a/module/doc/js/edit.js b/module/doc/js/edit.js index bae3663c3e..e2e43fde11 100644 --- a/module/doc/js/edit.js +++ b/module/doc/js/edit.js @@ -35,7 +35,7 @@ $(function() $('#subNavbar li[data-id="doc"]').addClass('active'); /* Automatically save document contents. */ - setInterval("saveTempContent()", 60 * 1000); + setInterval("saveDraft()", 60 * 1000); $(document).on("mouseup", 'span[data-name="fullscreen"]', function() { @@ -86,12 +86,12 @@ $(function() }) /** - * Save temporary doc content. + * Save draft doc. * * @access public * @return void */ -function saveTempContent() +function saveDraft() { var content = $('#content').val(); var link = createLink('doc', 'ajaxSaveDraft', 'docID=' + docID); diff --git a/module/doc/lang/en.php b/module/doc/lang/en.php index dd767c530d..fff3f5d5a4 100644 --- a/module/doc/lang/en.php +++ b/module/doc/lang/en.php @@ -117,7 +117,7 @@ $lang->doc->allExecutions = 'All' . $lang->executionCommon . 's'; $lang->doc->libTypeList['product'] = $lang->productCommon . ' Library'; if($config->systemMode == 'new') $lang->doc->libTypeList['project'] = 'Project Library'; -$lang->doc->libTypeList['execution'] = $lang->executionCommon . ' Library'; +$lang->doc->libTypeList['execution'] = $lang->execution->common . ' Library'; $lang->doc->libTypeList['custom'] = 'Custom Library'; $lang->doc->libIconList['product'] = 'icon-product'; diff --git a/module/doc/lang/zh-cn.php b/module/doc/lang/zh-cn.php index e1a00c04e4..7bc3bb78c2 100644 --- a/module/doc/lang/zh-cn.php +++ b/module/doc/lang/zh-cn.php @@ -117,7 +117,7 @@ $lang->doc->allExecutions = '所有' . $lang->executionCommon; $lang->doc->libTypeList['product'] = $lang->productCommon . '文档库'; if($config->systemMode == 'new') $lang->doc->libTypeList['project'] = '项目文档库'; -$lang->doc->libTypeList['execution'] = $lang->executionCommon . '文档库'; +$lang->doc->libTypeList['execution'] = $lang->execution->common . '文档库'; $lang->doc->libTypeList['custom'] = '自定义文档库'; $lang->doc->libIconList['product'] = 'icon-product'; diff --git a/module/execution/js/create.js b/module/execution/js/create.js index 8f82a97143..3904670691 100644 --- a/module/execution/js/create.js +++ b/module/execution/js/create.js @@ -31,11 +31,11 @@ $(function() } /* Assign value to the manage products by the different request type.*/ - var product = $('#products0'); - if(copyExecutionID) productID = product.val(); - product.val(productID); - product.trigger("chosen:updated"); - loadBranches(product); + var $product = $('#products0'); + if(copyExecutionID) productID = $product.val(); + $product.val(productID); + $product.trigger("chosen:updated"); + loadBranches($product); var adjustMainCol = function() { diff --git a/module/execution/model.php b/module/execution/model.php index 132dc5954f..f80cdac3d4 100644 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -271,7 +271,7 @@ class executionModel extends model public function setProjectSession($executionID) { $execution = $this->getByID($executionID); - $this->session->set('project', $execution->project); + if(!empty($execution)) $this->session->set('project', $execution->project); } /** @@ -486,7 +486,6 @@ class executionModel extends model /* Child stage inherits parent stage permissions. */ if(!isset($execution->acl)) $execution->acl = $oldExecution->acl; - if($execution->acl == 'open') $execution->whitelist = ''; $execution = $this->loadModel('file')->processImgURL($execution, $this->config->execution->editor->edit['id'], $this->post->uid); @@ -2477,7 +2476,7 @@ class executionModel extends model { if(empty($projectID) and $this->config->systemMode == 'new') return array(); - $teams = $this->dao->select('id,name,type')->from(TABLE_PROJECT) + $objects = $this->dao->select('id,name,type')->from(TABLE_PROJECT) ->where('deleted')->eq(0) ->andWhere('(project')->eq($projectID) ->orWhere('id')->eq($projectID) @@ -2485,15 +2484,13 @@ class executionModel extends model ->orderBy('project_asc') ->fetchAll('id'); - if(empty($teams)) return array(); + if(empty($objects)) return array(); $teamPairs = array(); - foreach($teams as $id => $team) + foreach($objects as $id => $object) { - if(empty($team->name)) continue; - - $prefix = ($team->type != 'project' and $this->config->systemMode == 'new') ? '   ' : ''; - $teamPairs[$id] = $prefix . $team->name; + $prefix = ($object->type != 'project' and $this->config->systemMode == 'new') ? '   ' : ''; + $teamPairs[$id] = $prefix . $object->name; } return $teamPairs; @@ -3418,7 +3415,7 @@ class executionModel extends model $days = count($dateList) - 1; $rate = $days ? $firstTime / $days : ''; $baselineJSON = '['; - foreach($dateList as $i => $date) $baselineJSON .= round(($days - $i) * (int)$rate, 1) . ','; + foreach($dateList as $i => $date) $baselineJSON .= round(($days - $i) * (float)$rate, 1) . ','; $baselineJSON = rtrim($baselineJSON, ',') . ']'; $chartData['labels'] = $this->report->convertFormat($dateList, DT_DATE5); diff --git a/module/execution/view/ajaxgetdropmenu.html.php b/module/execution/view/ajaxgetdropmenu.html.php index 7ae7f3bf3a..e5ac0900c0 100644 --- a/module/execution/view/ajaxgetdropmenu.html.php +++ b/module/execution/view/ajaxgetdropmenu.html.php @@ -91,7 +91,7 @@ foreach($executions as $projectID => $projectExecutions) } else if($execution->status == 'done' or $execution->status == 'closed') { - $closedExecutionsHtml .= '
  • ' . html::a(sprintf($link, $execution->id), $execution->name, '', "class='$selected' title='$execution->name' data-key='" . zget($executionsPinYin, $execution->name, '') . "' data-app='{$this->app->tab}'") . '
  • '; + $closedExecutionsHtml .= '
  • ' . html::a(sprintf($link, $execution->id), $execution->name, '', "class='$selected executionName' title='$execution->name' data-key='" . zget($executionsPinYin, $execution->name, '') . "' data-app='{$this->app->tab}'") . '
  • '; if($selected == 'selected') $tabActive = 'closed'; } diff --git a/module/execution/view/bug.html.php b/module/execution/view/bug.html.php index 1e091a5887..6acf38e9a7 100644 --- a/module/execution/view/bug.html.php +++ b/module/execution/view/bug.html.php @@ -114,8 +114,8 @@ common::printIcon('bug', 'confirmBug', $params, $bug, 'list', 'ok', '', 'iframe', true); common::printIcon('bug', 'resolve', $params, $bug, 'list', 'checked', '', 'iframe', true); common::printIcon('bug', 'close', $params, $bug, 'list', '', '', 'iframe', true); - common::printIcon('bug', 'create', "product=$bug->product&branch=$bug->branch&extra=$params", $bug, 'list', 'copy'); - common::printIcon('bug', 'edit', $params, $bug, 'list'); + common::printIcon('bug', 'create', "product=$bug->product&branch=$bug->branch&extra=$params,executionID=$bug->execution", $bug, 'list', 'copy'); + common::printIcon('bug', 'edit', $params, $bug, 'list'); } ?> diff --git a/module/execution/view/managemembers.html.php b/module/execution/view/managemembers.html.php index 07543f6c1e..8ec7fd2478 100644 --- a/module/execution/view/managemembers.html.php +++ b/module/execution/view/managemembers.html.php @@ -12,7 +12,7 @@ ?> systemMode);?> -projectCommon);?> +project->common);?> id);?> diff --git a/module/gitlab/control.php b/module/gitlab/control.php index cd6d073e68..fc46a8057c 100644 --- a/module/gitlab/control.php +++ b/module/gitlab/control.php @@ -11,6 +11,20 @@ */ class gitlab extends control { + /** + * The gitlab constructor. + * @param string $moduleName + * @param string $methodName + */ + public function __construct($moduleName = '', $methodName = '') + { + parent::__construct($moduleName, $methodName); + + /* This is essential when changing tab(menu) from gitlab to repo. */ + /* Optional: common::setMenuVars('devops', $this->session->repoID); */ + $this->loadModel('ci')->setMenu(); + } + /** * Browse gitlab. * @@ -23,8 +37,6 @@ class gitlab extends control */ public function browse($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1) { - /* This is essential when changing tab(menu) from gitlab to repo. */ - common::setMenuVars('devops', $this->session->repoID); $this->app->loadClass('pager', $static = true); $pager = new pager($recTotal, $recPerPage, $pageID); diff --git a/module/group/control.php b/module/group/control.php index 68de141498..f303b89e2e 100644 --- a/module/group/control.php +++ b/module/group/control.php @@ -159,6 +159,8 @@ class group extends control foreach($this->lang->navGroup as $moduleName => $groupName) { if($groupName == $moduleName) continue; + if($moduleName == 'testcase') $moduleName = 'case'; + $navGroup[$groupName][$moduleName] = $moduleName; } $this->view->navGroup = $navGroup; diff --git a/module/mr/control.php b/module/mr/control.php index 6dac1f64ac..6b05a0b1f3 100644 --- a/module/mr/control.php +++ b/module/mr/control.php @@ -1,6 +1,20 @@ session->repoID); */ + $this->loadModel('ci')->setMenu(); + } + /** * Browse mr. * @@ -20,7 +34,6 @@ class mr extends control /* Save current URI to session. */ $this->session->set('mrList', $this->app->getURI(true), 'repo'); - common::setMenuVars('devops', $this->session->repoID); /* Sync GitLab MR to ZenTao Database. */ $MRList = $this->mr->batchSyncMR($MRList); @@ -103,12 +116,12 @@ class mr extends control */ public function delete($id, $confim = 'no') { - if($confim != 'yes') die(js::confirm($this->lang->gitlab->confirmDelete, inlink('delete', "id=$id&confirm=yes"))); + if($confim != 'yes') die(js::confirm($this->lang->mr->confirmDelete, inlink('delete', "id=$id&confirm=yes"))); $MR = $this->mr->getByID($id); $this->dao->delete()->from(TABLE_MR)->where('id')->eq($id)->exec(); - $this->mr->apiDeleteMR($MR->gitlabID, $MR->sourceProject, $MR->mriid); + $this->mr->apiDeleteMR($MR->gitlabID, $MR->targetProject, $MR->mriid); die(js::locate(inlink('browse'), 'parent')); } diff --git a/module/mr/lang/en.php b/module/mr/lang/en.php index b45a6dba32..7e3f3e35fa 100644 --- a/module/mr/lang/en.php +++ b/module/mr/lang/en.php @@ -23,6 +23,7 @@ $lang->mr->reviewer = 'Reviewer'; $lang->mr->mergeStatus = 'Merge status'; $lang->mr->commits = 'commits'; $lang->mr->changes = 'changes'; +$lang->mr->gitlabID = 'GitLab'; $lang->mr->statusList = array(); $lang->mr->statusList['opened'] = 'opened'; @@ -41,7 +42,7 @@ $lang->mr->sourceBranch = 'Source branch'; $lang->mr->targetProject = 'Target project'; $lang->mr->targetBranch = 'Target branch'; -$lang->mr->usersTips = 'Tip: If you cannot choose the assignee and reviewer, please go to the GitLab page to bind the user first.'; +$lang->mr->usersTips = 'Tip: If you cannot choose the assignee or reviewer, please go to the GitLab page to bind the user first.'; $lang->mr->notFound = "Merge Request does not exist!"; $lang->mr->apiError = new stdclass; diff --git a/module/mr/lang/zh-cn.php b/module/mr/lang/zh-cn.php index 49f3219081..0d5d1b1589 100644 --- a/module/mr/lang/zh-cn.php +++ b/module/mr/lang/zh-cn.php @@ -23,6 +23,7 @@ $lang->mr->reviewer = '评审人'; $lang->mr->mergeStatus = '是否可合并'; $lang->mr->commits = '提交数'; $lang->mr->changes = '更改数'; +$lang->mr->gitlabID = 'GitLab'; $lang->mr->statusList = array(); $lang->mr->statusList['opened'] = '开放中'; @@ -41,7 +42,7 @@ $lang->mr->sourceBranch = '源分支'; $lang->mr->targetProject = '目标项目'; $lang->mr->targetBranch = '目标分支'; -$lang->mr->usersTips = '提示:如果无法选择指派人和评审人,请先前往GitLab页面绑定用户。'; +$lang->mr->usersTips = '提示:如果无法选择指派人或评审人,请先前往GitLab页面绑定用户。'; $lang->mr->notFound = "此{$lang->mr->common}不存在。"; $lang->mr->apiError = new stdclass; diff --git a/module/mr/lang/zh-tw.php b/module/mr/lang/zh-tw.php index e858cc8217..0bbe8593e1 100644 --- a/module/mr/lang/zh-tw.php +++ b/module/mr/lang/zh-tw.php @@ -41,7 +41,7 @@ $lang->mr->sourceBranch = '源分支'; $lang->mr->targetProject = '目標項目'; $lang->mr->targetBranch = '目標分支'; -$lang->mr->usersTips = '提示:如果無法選擇指派人和評審人,請先前往GitLab頁面綁定用戶。'; +$lang->mr->usersTips = '提示:如果無法選擇指派人或評審人,請先前往GitLab頁面綁定用戶。'; $lang->mr->notFound = "此{$lang->mr->common}不存在。"; $lang->mr->apiError = new stdclass; diff --git a/module/mr/model.php b/module/mr/model.php index 4901bd1a8d..c92fe6ae55 100644 --- a/module/mr/model.php +++ b/module/mr/model.php @@ -99,7 +99,6 @@ class mrModel extends model $MRObject->title = $MR->title; $MRObject->description = $MR->description; $MRObject->assignee_ids = $MR->assignee; - $MRObject->reviewer_ids = $MR->reviewer; $rawMR = $this->apiCreateMR($this->post->gitlabID, $this->post->sourceProject, $MRObject); @@ -120,6 +119,9 @@ class mrModel extends model return array('result' => 'fail', 'message' => $this->lang->mr->createFailedFromAPI); } + /* Create a todo item for this MR. */ + $this->apiCreateMRTodo($this->post->gitlabID, $this->post->targetProject, $rawMR->iid); + $newMR = new stdclass; $newMR->mriid = $rawMR->iid; $newMR->status = $rawMR->state; @@ -128,7 +130,6 @@ class mrModel extends model /* Change gitlab user ID to zentao account. */ $gitlabUsers = $this->gitlab->getUserIdAccountPairs($MR->gitlabID); $newMR->assignee = zget($gitlabUsers, $MR->assignee, ''); - $newMR->reviewer = zget($gitlabUsers, $MR->reviewer, ''); /* Update MR in Zentao database. */ $this->dao->update(TABLE_MR)->data($newMR) @@ -157,7 +158,6 @@ class mrModel extends model $newMR->title = $MR->title; $newMR->description = $MR->description; $newMR->assignee_ids = $MR->assignee; - $newMR->reviewer_ids = $MR->reviewer; $newMR->target_branch = $MR->targetBranch; $oldMR = $this->getByID($MRID); @@ -168,7 +168,6 @@ class mrModel extends model /* Change gitlab user ID to zentao account. */ $gitlabUsers = $this->gitlab->getUserIdAccountPairs($oldMR->gitlabID); $MR->assignee = zget($gitlabUsers, $MR->assignee, ''); - $MR->reviewer = zget($gitlabUsers, $MR->reviewer, ''); /* Update MR in Zentao database. */ $this->dao->update(TABLE_MR)->data($MR) @@ -255,7 +254,7 @@ class mrModel extends model $value = ''; list($field, $optionType, $options) = explode('|', $config); - if($optionType == 'field') $value = $rawMR->$field; + if($optionType == 'field') $value = $rawMR->$field; if($optionType == 'userPairs') { $gitlabUserID = ''; @@ -269,7 +268,9 @@ class mrModel extends model if($value) $newMR->$syncField = $value; } - if(empty((array)$newMR)) continue; + /* For compatibility with PHP 5.4 . */ + $condition = (array)$newMR; + if(empty($condition)) continue; /* Update MR in Zentao database. */ $this->dao->update(TABLE_MR)->data($newMR) @@ -492,4 +493,19 @@ class mrModel extends model if(!empty($users[$zentaoUser])) return $users[$zentaoUser]; return ""; } + + /** + * Create a todo item for merge request. + * + * @param int $gitlabID + * @param int $projectID + * @param int $MRID + * @access public + * @return object + */ + public function apiCreateMRTodo($gitlabID, $projectID, $MRID) + { + $url = sprintf($this->gitlab->getApiRoot($gitlabID), "/projects/$projectID/merge_requests/$MRID/todo"); + return json_decode(commonModel::http($url, $data = null, $options = array(CURLOPT_CUSTOMREQUEST => 'POST'))); + } } diff --git a/module/mr/view/create.html.php b/module/mr/view/create.html.php index cbe72b23e6..2b863a9717 100644 --- a/module/mr/view/create.html.php +++ b/module/mr/view/create.html.php @@ -54,10 +54,6 @@ mr->assignee;?> - - mr->reviewer;?> - - mr->usersTips;?> diff --git a/module/mr/view/edit.html.php b/module/mr/view/edit.html.php index 0433e42579..ae7462cc96 100644 --- a/module/mr/view/edit.html.php +++ b/module/mr/view/edit.html.php @@ -55,16 +55,12 @@ mr->assignee;?> - - mr->reviewer;?> - - mr->usersTips;?> - + goback, '', 'class="btn btn-wide"');?> diff --git a/module/personnel/model.php b/module/personnel/model.php index a97a3a69bc..fc8314428c 100644 --- a/module/personnel/model.php +++ b/module/personnel/model.php @@ -87,7 +87,8 @@ class personnelModel extends model { foreach($userGroups[$account] as $groupID => $userGroup) { - $group = $groupInfo[$groupID]; + $group = isset($groupInfo[$groupID]) ? $groupInfo[$groupID] : ''; + if(!isset($group->programs)) { $programRight = true; diff --git a/module/product/model.php b/module/product/model.php index 54d363dcd5..438160fcf3 100644 --- a/module/product/model.php +++ b/module/product/model.php @@ -596,7 +596,6 @@ class productModel extends model if($oldProduct->bind) $this->config->product->edit->requiredFields = 'name'; $product = fixer::input('post') - ->setIF($this->post->acl == 'open', 'whitelist', '') ->setDefault('line', 0) ->join('whitelist', ',') ->stripTags($this->config->product->editor->edit['id'], $this->config->allowedTags) @@ -1555,7 +1554,7 @@ class productModel extends model ->fetchGroup('product', 'id'); $executionListKey = $this->config->systemMode == 'new' ? 'project' : 'productID'; - $executionList = $this->dao->select('t1.product as productID,t2.*')->from(TABLE_PROJECTPRODUCT)->alias('t1') + $executionList = $this->dao->select('t1.product as productID,t2.*')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.project=t2.id') ->where('type')->in('stage,sprint') ->beginIF($this->config->systemMode == 'new')->andWhere('t2.project')->in(array_keys($projectList))->fi() diff --git a/module/product/view/ajaxgetdropmenu.html.php b/module/product/view/ajaxgetdropmenu.html.php index 8d1560446a..67954d3a17 100644 --- a/module/product/view/ajaxgetdropmenu.html.php +++ b/module/product/view/ajaxgetdropmenu.html.php @@ -95,7 +95,7 @@ foreach($products as $programID => $programProducts) } else if($product->status == 'closed') { - $closedProductsHtml .= '
  • ' . html::a($linkHtml, $productName, '', "class='$selected' title='$productName' class='closed' data-key='" . zget($productsPinYin, $product->name, '') . "' data-app='$app->tab'") . '
  • '; + $closedProductsHtml .= '
  • ' . html::a($linkHtml, $productName, '', "class='$selected productName' title='$productName' class='closed' data-key='" . zget($productsPinYin, $product->name, '') . "' data-app='$app->tab'") . '
  • '; if($selected == 'selected') $tabActive = 'closed'; } diff --git a/module/program/js/kanban.js b/module/program/js/kanban.js index adf0bfa103..c1574a2fb1 100644 --- a/module/program/js/kanban.js +++ b/module/program/js/kanban.js @@ -67,12 +67,12 @@ function processKanbanData(key, programsData) $.each(doingProjects, function(_, project) { var projectID = project.id; - var projectItem = $.extend({}, project, {id: 'project-' + projectID, _id: projectID}); + var projectItem = $.extend({}, project, {id: 'project-' + projectID, _id: projectID, execution: null}); items.doingProject.push(projectItem); var execution = project.execution; if(!execution || !execution.id) return; - projectItem.execution = execution; + projectItem.execution = $.extend({}, execution, {id: 'execution-' + execution.id, _id: execution.id}); }); } diff --git a/module/program/model.php b/module/program/model.php index 8c4b96809b..8058c22595 100644 --- a/module/program/model.php +++ b/module/program/model.php @@ -147,7 +147,7 @@ class programModel extends model * Get kanban group data. * * @access public - * @return array + * @return array */ public function getKanbanGroup() { @@ -252,7 +252,7 @@ class programModel extends model /* Group data by program. */ foreach($programs as $programID => $programName) { - $programGroup = new stdclass(); + $programGroup = new stdclass(); $programGroup->name = $programName; $programGroup->products = zget($productGroup, $programID, array()); @@ -274,7 +274,7 @@ class programModel extends model * * @param string $account * @access public - * @return array + * @return array */ public function getInvolvedPrograms($account) { @@ -658,7 +658,6 @@ class programModel extends model ->setDefault('end', '') ->setIF($this->post->begin == '0000-00-00', 'begin', '') ->setIF($this->post->end == '0000-00-00', 'end', '') - ->setIF($this->post->acl == 'open', 'whitelist', '') ->setIF($this->post->delta == 999, 'end', LONG_TIME) ->setIF($this->post->future, 'budget', 0) ->setIF($this->post->budget != 0, 'budget', round($this->post->budget, 2)) @@ -740,9 +739,12 @@ class programModel extends model $this->processNode($programID, $program->parent, $oldProgram->path, $oldProgram->grade); /* Move product to new top program. */ - $oldTopProgram = $this->getTopByPath($oldProgram->path); - $newTopProgram = $this->getTopByID($programID); - if($oldTopProgram != $newTopProgram) $this->dao->update(TABLE_PRODUCT)->set('program')->eq($newTopProgram)->where('program')->eq($oldTopProgram)->exec(); + if($oldProgram->parent == 0) + { + $oldTopProgram = $this->getTopByPath($oldProgram->path); + $newTopProgram = $this->getTopByID($programID); + if($oldTopProgram != $newTopProgram) $this->dao->update(TABLE_PRODUCT)->set('program')->eq($newTopProgram)->where('program')->eq($oldTopProgram)->exec(); + } } return common::createChanges($oldProgram, $program); @@ -819,7 +821,7 @@ class programModel extends model while($program = $stmt->fetch()) { $link = $this->getLink($moduleName, $methodName, $program->id, $vars, $from); - $linkHtml = html::a($link, $program->name, '', "id='program$program->id' class='text-ellipsis' title=$program->name"); + $linkHtml = html::a($link, $program->name, '', "id='program$program->id' class='text-ellipsis programName' title=$program->name"); if(isset($programMenu[$program->id]) and !empty($programMenu[$program->id])) { diff --git a/module/program/view/ajaxgetdropmenu.html.php b/module/program/view/ajaxgetdropmenu.html.php index 38da2a55c0..47a7cf7534 100644 --- a/module/program/view/ajaxgetdropmenu.html.php +++ b/module/program/view/ajaxgetdropmenu.html.php @@ -54,6 +54,8 @@ $programsPinYin = common::convert2Pinyin($programNames); #programTree ul > li.has-list:after {width: 14px;} #programTree ul > li.item-meas a.selected{color: #0c64eb;} #showClosed1 + label{color: #3c4353} + +a.programName:focus, a.programName:hover {background: #0c64eb; color: #fff !important;}