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;}