From e392b3f1831f0183aca2f92b37ac65ecb746bc92 Mon Sep 17 00:00:00 2001 From: zhouxin Date: Wed, 8 Dec 2021 08:48:54 +0000 Subject: [PATCH 01/44] Finish task #45792. --- module/design/control.php | 2 +- module/design/model.php | 2 +- module/design/view/view.html.php | 2 +- module/repo/view/revision.html.php | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/module/design/control.php b/module/design/control.php index 09b13a3465..1c2e1ec48c 100644 --- a/module/design/control.php +++ b/module/design/control.php @@ -401,7 +401,7 @@ class design extends control public function revision($repoID = 0, $projectID = 0) { $repo = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('id')->eq($repoID)->fetch(); - $repoURL = $this->createLink('repo', 'revision', "repoID=$repo->repo&objectID=$projectID&revistion=$repo->revision"); + $repoURL = $this->createLink('repo', 'revision', "repoID=$repo->repo&objectID=$projectID&revistion=$repo->revision", '', true); header("location:" . $repoURL); } diff --git a/module/design/model.php b/module/design/model.php index 9f5b86efb7..d946fefd32 100644 --- a/module/design/model.php +++ b/module/design/model.php @@ -248,7 +248,7 @@ class designModel extends model $design->commit = ''; $relations = $this->loadModel('common')->getRelations('design', $designID, 'commit'); - foreach($relations as $relation) $design->commit .= html::a(helper::createLink('design', 'revision', "repoID=$relation->BID&projectID={$design->project}"), "#$relation->BID", '_blank'); + foreach($relations as $relation) $design->commit .= html::a(helper::createLink('design', 'revision', "repoID=$relation->BID&projectID={$design->project}"), "#$relation->BID", '', "class=iframe"); return $this->loadModel('file')->replaceImgURL($design, 'desc'); } diff --git a/module/design/view/view.html.php b/module/design/view/view.html.php index e7b71d3a08..0f723b76e8 100644 --- a/module/design/view/view.html.php +++ b/module/design/view/view.html.php @@ -69,7 +69,7 @@ design->story;?> - story ? html::a($this->createLink('story', 'view', "id=$design->story"), zget($stories, $design->story)) : '';?> + story ? html::a($this->createLink('story', 'view', "id=$design->story", '', true), zget($stories, $design->story), '',"class=iframe") : '';?> design->submission;?> diff --git a/module/repo/view/revision.html.php b/module/repo/view/revision.html.php index a9a94bcc0c..09a5733a1e 100644 --- a/module/repo/view/revision.html.php +++ b/module/repo/view/revision.html.php @@ -19,9 +19,11 @@ $typeInfo = $type == 'file' ? '&type=file' : '';
@@ -36,7 +36,7 @@

bug->noBug;?> - createLink('bug', 'create', "productID=$productID&branch=$branchID&extra=executionID=$execution->id"), " " . $lang->bug->create, '', "class='btn btn-info' data-app='execution'");?> + createLink('bug', 'create', "productID=$defaultProduct&branch=$branchID&extra=executionID=$execution->id"), " " . $lang->bug->create, '', "class='btn btn-info' data-app='execution'");?>

diff --git a/xuanxuan/module/message/ext/config/xuanxuan.php b/xuanxuan/module/message/ext/config/xuanxuan.php index 7e1ba6a874..3cb520eff5 100644 --- a/xuanxuan/module/message/ext/config/xuanxuan.php +++ b/xuanxuan/module/message/ext/config/xuanxuan.php @@ -3,5 +3,4 @@ $config->message->available['xuanxuan']['story'] = $config->message->objectTyp $config->message->available['xuanxuan']['task'] = $config->message->objectTypes['task']; $config->message->available['xuanxuan']['bug'] = $config->message->objectTypes['bug']; $config->message->available['xuanxuan']['todo'] = $config->message->objectTypes['todo']; -$config->message->available['xuanxuan']['release'] = $config->message->objectTypes['release']; $config->message->setting['xuanxuan']['setting'] = $config->message->available['xuanxuan']; From 76748224ebf932eabf5e6374d77f4b9f2eb85c96 Mon Sep 17 00:00:00 2001 From: liyuchun Date: Tue, 7 Dec 2021 16:15:21 +0800 Subject: [PATCH 09/44] * Finish task #45491. --- module/action/lang/en.php | 1 + module/action/lang/zh-cn.php | 1 + module/block/lang/en.php | 1 + module/block/lang/zh-cn.php | 1 + module/block/view/dynamic.html.php | 3 +- module/gitlab/config.php | 3 ++ module/gitlab/control.php | 33 +++++++++++++++++ module/gitlab/lang/en.php | 18 +++++++--- module/gitlab/lang/zh-cn.php | 18 +++++++--- module/gitlab/model.php | 45 ++++++++++++++++++++++++ module/gitlab/view/createbranch.html.php | 44 +++++++++++++++++++++++ module/group/lang/resource.php | 1 + module/repo/control.php | 1 + module/repo/view/browse.html.php | 1 + 14 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 module/gitlab/view/createbranch.html.php diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 6b7abcb988..3a9549771f 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -121,6 +121,7 @@ $lang->action->objectTypes['mr'] = 'Merge Request'; $lang->action->objectTypes['gitlabproject'] = 'GitLab Project'; $lang->action->objectTypes['gitlabuser'] = 'GitLab User'; $lang->action->objectTypes['gitlabgroup'] = 'GitLab Group'; +$lang->action->objectTypes['gitlabbranch'] = 'GitLab Branch'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 2b7b8023bf..559b01bf3b 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -121,6 +121,7 @@ $lang->action->objectTypes['mr'] = '合并请求'; $lang->action->objectTypes['gitlabproject'] = 'GitLab项目'; $lang->action->objectTypes['gitlabuser'] = 'GitLab用户'; $lang->action->objectTypes['gitlabgroup'] = 'GitLab群组'; +$lang->action->objectTypes['gitlabbranch'] = 'GitLab分支'; /* 用来描述操作历史记录。*/ $lang->action->desc = new stdclass(); diff --git a/module/block/lang/en.php b/module/block/lang/en.php index ce31de4f53..b0a9a555e3 100644 --- a/module/block/lang/en.php +++ b/module/block/lang/en.php @@ -91,6 +91,7 @@ $lang->block->refresh = 'Refresh'; $lang->block->nbsp = ' '; $lang->block->hidden = 'Hide'; $lang->block->dynamicInfo = "%s %s %s %s %s"; +$lang->block->noLinkDynamic = "%s %s %s %s %s"; $lang->block->cannotPlaceInLeft = 'Cannot place the block at left side.'; $lang->block->cannotPlaceInRight = 'Cannot place the block at right side.'; diff --git a/module/block/lang/zh-cn.php b/module/block/lang/zh-cn.php index ac77206f01..e5d8deface 100644 --- a/module/block/lang/zh-cn.php +++ b/module/block/lang/zh-cn.php @@ -91,6 +91,7 @@ $lang->block->refresh = '刷新'; $lang->block->nbsp = ''; $lang->block->hidden = '隐藏'; $lang->block->dynamicInfo = "%s %s %s %s %s"; +$lang->block->noLinkDynamic = "%s %s %s %s %s"; $lang->block->cannotPlaceInLeft = '此区块无法放置在左侧。'; $lang->block->cannotPlaceInRight = '此区块无法放置在右侧。'; diff --git a/module/block/view/dynamic.html.php b/module/block/view/dynamic.html.php index 2447f62507..215bb804d3 100644 --- a/module/block/view/dynamic.html.php +++ b/module/block/view/dynamic.html.php @@ -15,7 +15,8 @@ if($action->action == 'login' or $action->action == 'logout') $action->objectName = $action->objectLabel = ''; $class = $action->major ? "class='active'" : ''; echo "
  • "; - printf($lang->block->dynamicInfo, $action->date, $user, $action->actionLabel, $action->objectLabel, $action->objectLink, $action->objectName, $action->objectName); + if($action->objectLink) printf($lang->block->dynamicInfo, $action->date, $user, $action->actionLabel, $action->objectLabel, $action->objectLink, $action->objectName, $action->objectName); + if(!$action->objectLink) printf($lang->block->noLinkDynamic, $action->date, $action->objectName, $user, $action->actionLabel, $action->objectLabel, $action->objectName); echo "
  • "; $i++; } diff --git a/module/gitlab/config.php b/module/gitlab/config.php index 5f41d70ceb..bd6760c7cc 100644 --- a/module/gitlab/config.php +++ b/module/gitlab/config.php @@ -5,6 +5,9 @@ $config->gitlab->create->requiredFields = 'name,url,token'; $config->gitlab->edit = new stdclass; $config->gitlab->edit->requiredFields = 'name,url,token'; +$config->gitlab->createbranch = new stdclass; +$config->gitlab->createbranch->requiredFields = 'branch,ref'; + $config->gitlab->labelPattern = new stdclass; $config->gitlab->labelPattern->task = '/^zentao_task\/\d+$/'; $config->gitlab->labelPattern->bug = '/^zentao_bug\/\d+$/'; diff --git a/module/gitlab/control.php b/module/gitlab/control.php index e56cae595d..b1738b51fd 100644 --- a/module/gitlab/control.php +++ b/module/gitlab/control.php @@ -712,6 +712,39 @@ class gitlab extends control die(js::alert($reponse->message)); } + /** + * Creat a gitlab branch. + * + * @param int $gitlabID + * @param int $projectID + * @access public + * @return void + */ + public function createBranch($gitlabID, $projectID) + { + if($_POST) + { + $this->gitlab->createBranch($gitlabID, $projectID); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + + $locate = $this->session->gitlabBranchList ? $this->session->gitlabBranchList : inlink('browseBranch', "gitlibID=$gitlabID&projectID=$projectID"); + return $this->send(array('result' => 'success', 'message' => $this->lang->gitlab->createSuccess, 'locate' => $locate)); + } + + /* Get branches by api. */ + $branches = $this->gitlab->apiGetBranches($gitlabID, $projectID); + if(!is_array($branches)) $branches= array(); + + $branchPairs = array(); + foreach($branches as $branch) $branchPairs[$branch->name] = $branch->name; + + $this->view->title = $this->lang->gitlab->common . $this->lang->colon . $this->lang->gitlab->createBranch; + $this->view->gitlabID = $gitlabID; + $this->view->projectID = $projectID; + $this->view->branchPairs = $branchPairs; + $this->display(); + } + /** * Import gitlab issue to zentaopms. * diff --git a/module/gitlab/lang/en.php b/module/gitlab/lang/en.php index 07c7ad4e7f..3224521a51 100644 --- a/module/gitlab/lang/en.php +++ b/module/gitlab/lang/en.php @@ -37,6 +37,7 @@ $lang->gitlab->manageProjectMembers = 'Manage project member'; $lang->gitlab->createProject = 'Create GitLab project'; $lang->gitlab->editProject = 'Eidt GitLab project'; $lang->gitlab->deleteProject = 'Delete GitLab project'; +$lang->gitlab->createBranch = 'Create Branch'; $lang->gitlab->createGroup = 'Create group'; $lang->gitlab->editGroup = 'Edit group'; $lang->gitlab->deleteGroup = 'Delete group'; @@ -53,10 +54,12 @@ $lang->gitlab->token = 'Token'; $lang->gitlab->defaultProject = 'Default Project'; $lang->gitlab->private = 'MD5 Verify'; -$lang->gitlab->lblCreate = 'Create GitLab Server'; -$lang->gitlab->desc = 'Description'; -$lang->gitlab->tokenFirst = 'When the Token is not empty, the Token will be used first'; -$lang->gitlab->tips = 'When using a password, please disable the "Prevent cross-site request forgery" option in the GitLab global security settings.'; +$lang->gitlab->lblCreate = 'Create GitLab Server'; +$lang->gitlab->desc = 'Description'; +$lang->gitlab->tokenFirst = 'When the Token is not empty, the Token will be used first'; +$lang->gitlab->tips = 'When using a password, please disable the "Prevent cross-site request forgery" option in the GitLab global security settings.'; +$lang->gitlab->emptyError = "cannot be empty"; +$lang->gitlab->createSuccess = "Create success"; $lang->gitlab->placeholder = new stdclass; $lang->gitlab->placeholder->name = ''; @@ -82,11 +85,13 @@ $lang->gitlab->apiError[0] = 'internal is not allowed in a private group.'; $lang->gitlab->apiError[1] = 'public is not allowed in a private group.'; $lang->gitlab->apiError[2] = 'is too short (minimum is 8 characters)'; $lang->gitlab->apiError[3] = "can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"; +$lang->gitlab->apiError[4] = 'Branch already exists'; $lang->gitlab->errorLang[0] = 'You cannot set Internal as its Visibility Level, if it is private in GitLab.'; $lang->gitlab->errorLang[1] = 'You cannot set Public as its Visibility Level, if it is private in GitLab.'; $lang->gitlab->errorLang[2] = 'Password is too short (minimum is 8 characters)'; $lang->gitlab->errorLang[3] = 'It should contain only letters, digits, underscore, hyphen and period. It should not start with hypen, or end with .git or .atom.'; +$lang->gitlab->errorLang[4] = 'Branch already exists.'; $lang->gitlab->project = new stdclass; $lang->gitlab->project->id = "Project ID"; @@ -171,3 +176,8 @@ $lang->gitlab->group->memberName = 'Account'; $lang->gitlab->group->memberAccessLevel = 'Access Level'; $lang->gitlab->group->memberExpiresAt = 'Expiration time'; $lang->gitlab->group->repeatError = "Group members cannot be added repeatedly"; + +$lang->gitlab->branch = new stdclass(); +$lang->gitlab->branch->name = 'Branch Name'; +$lang->gitlab->branch->from = 'Create from'; +$lang->gitlab->branch->create = 'Create'; diff --git a/module/gitlab/lang/zh-cn.php b/module/gitlab/lang/zh-cn.php index d911572925..d23f272d0d 100644 --- a/module/gitlab/lang/zh-cn.php +++ b/module/gitlab/lang/zh-cn.php @@ -37,6 +37,7 @@ $lang->gitlab->manageProjectMembers = '项目成员管理'; $lang->gitlab->createProject = '添加GitLab项目'; $lang->gitlab->editProject = '编辑GitLab项目'; $lang->gitlab->deleteProject = '删除GitLab项目'; +$lang->gitlab->createBranch = '添加分支'; $lang->gitlab->createGroup = '添加群组'; $lang->gitlab->editGroup = '编辑群组'; $lang->gitlab->deleteGroup = '删除群组'; @@ -53,10 +54,12 @@ $lang->gitlab->token = 'Token'; $lang->gitlab->defaultProject = '默认项目'; $lang->gitlab->private = 'MD5验证'; -$lang->gitlab->lblCreate = '添加GitLab服务器'; -$lang->gitlab->desc = '描述'; -$lang->gitlab->tokenFirst = 'Token不为空时,优先使用Token。'; -$lang->gitlab->tips = '使用密码时,请在GitLab全局安全设置中禁用"防止跨站点请求伪造"选项。'; +$lang->gitlab->lblCreate = '添加GitLab服务器'; +$lang->gitlab->desc = '描述'; +$lang->gitlab->tokenFirst = 'Token不为空时,优先使用Token。'; +$lang->gitlab->tips = '使用密码时,请在GitLab全局安全设置中禁用"防止跨站点请求伪造"选项。'; +$lang->gitlab->emptyError = "不能为空"; +$lang->gitlab->createSuccess = "创建成功"; $lang->gitlab->placeholder = new stdclass; $lang->gitlab->placeholder->name = ''; @@ -82,11 +85,13 @@ $lang->gitlab->apiError[0] = 'internal is not allowed in a private group.'; $lang->gitlab->apiError[1] = 'public is not allowed in a private group.'; $lang->gitlab->apiError[2] = 'is too short (minimum is 8 characters)'; $lang->gitlab->apiError[3] = "can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"; +$lang->gitlab->apiError[4] = 'Branch already exists'; $lang->gitlab->errorLang[0] = '私有分组的项目,可见性级别不能设为内部。'; $lang->gitlab->errorLang[1] = '私有分组的项目,可见性级别不能设为公开。'; $lang->gitlab->errorLang[2] = '密码太短(最少8个字符)'; $lang->gitlab->errorLang[3] = "只能包含字母、数字、'.'-'和'.'。不能以'-'开头、以'.git'结尾或以'.atom'结尾。"; +$lang->gitlab->errorLang[4] = '分支名已存在。'; $lang->gitlab->project = new stdclass; $lang->gitlab->project->id = "项目ID"; @@ -171,3 +176,8 @@ $lang->gitlab->group->memberName = '账号'; $lang->gitlab->group->memberAccessLevel = '角色权限'; $lang->gitlab->group->memberExpiresAt = '过期时间'; $lang->gitlab->group->repeatError = "群组成员不能重复添加"; + +$lang->gitlab->branch = new stdclass(); +$lang->gitlab->branch->name = '分支名'; +$lang->gitlab->branch->from = '创建自'; +$lang->gitlab->branch->create = '创建'; diff --git a/module/gitlab/model.php b/module/gitlab/model.php index 437a2a05ce..6ca9804d8d 100644 --- a/module/gitlab/model.php +++ b/module/gitlab/model.php @@ -896,6 +896,24 @@ class gitlabModel extends model return json_decode(commonModel::http($url, array(), $options = array(CURLOPT_CUSTOMREQUEST => 'DELETE'))); } + /** + * Create a gitab user by api. + * + * @param int $gitlabID + * @param int $projectID + * @param object $branch + * @access public + * @return object + */ + public function apiCreateBranch($gitlabID, $projectID, $branch) + { + if(empty($branch->branch) or empty($branch->ref)) return false; + + $apiRoot = $this->getApiRoot($gitlabID); + $url = sprintf($apiRoot, "/projects/{$projectID}/repository/branches"); + return json_decode(commonModel::http($url, $branch)); + } + /** * Get single project by API. * @@ -2236,6 +2254,33 @@ class gitlabModel extends model return $this->apiErrorHandling($reponse); } + /** + * Create a gitlab branch. + * + * @param int $gitlabID + * @param int $projectID + * @access public + * @return bool + */ + public function createBranch($gitlabID, $projectID) + { + $branch = fixer::input('post')->get(); + + if(empty($branch->branch)) dao::$errors['branch'][] = $this->lang->gitlab->branch->name . $this->lang->gitlab->emptyError; + if(empty($branch->ref)) dao::$errors['ref'][] = $this->lang->gitlab->branch->from . $this->lang->gitlab->emptyError; + if(dao::isError()) return false; + + $reponse = $this->apiCreateBranch($gitlabID, $projectID, $branch); + + if(!empty($reponse->name)) + { + $this->loadModel('action')->create('gitlabbranch', 0, 'created', '', $reponse->name); + return true; + } + + return $this->apiErrorHandling($reponse); + } + /** * Api error handling. * diff --git a/module/gitlab/view/createbranch.html.php b/module/gitlab/view/createbranch.html.php new file mode 100644 index 0000000000..391f3ff94c --- /dev/null +++ b/module/gitlab/view/createbranch.html.php @@ -0,0 +1,44 @@ + + * @package gitlab + * @version $Id$ + * @link https://www.zentao.net + */ +?> + +
    +
    +
    +
    +

    gitlab->createBranch;?>

    +
    +
    + + + + + + + + + + + + + + +
    gitlab->branch->name;?>gitlab->branch->name}'");?>
    gitlab->branch->from;?>
    + gitlab->branch->create);?> + session->gitlabBranchList ? $this->session->gitlabBranchList : inlink('browseBranch', "gitlibID=$gitlabID&projectID=$projectID");?> + goback, '', 'class="btn btn-wide"');?> +
    +
    +
    +
    +
    + diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index 200c124cf0..76a10cdb03 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -1297,6 +1297,7 @@ $lang->resource->gitlab->browseUser = 'browseUser'; $lang->resource->gitlab->createUser = 'createUser'; $lang->resource->gitlab->editUser = 'editUser'; $lang->resource->gitlab->deleteUser = 'deleteUser'; +$lang->resource->gitlab->createBranch = 'createBranch'; $lang->resource->gitlab->webhook = 'webhook'; $lang->resource->gitlab->createWebhook = 'createWebhook'; $lang->resource->gitlab->manageProjectMembers = 'manageProjectMembers'; diff --git a/module/repo/control.php b/module/repo/control.php index 2bc9dd1f34..78c3294bb2 100644 --- a/module/repo/control.php +++ b/module/repo/control.php @@ -357,6 +357,7 @@ class repo extends control session_start(); $this->session->set('revisionList', $this->app->getURI(true)); + $this->session->set('gitlabBranchList', $this->app->getURI(true)); session_write_close(); /* Get repo and synchronous commit. */ diff --git a/module/repo/view/browse.html.php b/module/repo/view/browse.html.php index 5a5d289fe0..cca1e145c7 100644 --- a/module/repo/view/browse.html.php +++ b/module/repo/view/browse.html.php @@ -59,6 +59,7 @@
    repo->notice->lastSyncTime . $cacheTime?> repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID&path=" . $this->repo->encodePath($path) . "&revision=$revision&refresh=1"), " " . $lang->refresh, '', "class='btn btn-primary' data-app={$app->tab}");?> + SCM == 'Gitlab' and common::hasPriv('gitlab', 'createBranch')) echo html::a($this->createLink('gitlab', 'createBranch', "gitlabID={$repo->gitlab}&projectID={$repo->project}"), " " . $lang->gitlab->createBranch, '', "class='btn btn-primary'");?>
    From 30aa0714261031fd921fde95a61560c5baf3a585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E7=8E=89=E6=98=A5?= <563917701@qq.com> Date: Tue, 7 Dec 2021 08:50:57 +0000 Subject: [PATCH 10/44] Revert "Merge branch 'sprint/devops16_liyuchun_45491' into 'master'" This reverts merge request !803 --- module/action/lang/en.php | 1 - module/action/lang/zh-cn.php | 1 - module/block/lang/en.php | 1 - module/block/lang/zh-cn.php | 1 - module/block/view/dynamic.html.php | 3 +- module/gitlab/config.php | 3 -- module/gitlab/control.php | 33 ----------------- module/gitlab/lang/en.php | 18 +++------- module/gitlab/lang/zh-cn.php | 18 +++------- module/gitlab/model.php | 45 ------------------------ module/gitlab/view/createbranch.html.php | 44 ----------------------- module/group/lang/resource.php | 1 - module/repo/control.php | 1 - module/repo/view/browse.html.php | 1 - 14 files changed, 9 insertions(+), 162 deletions(-) delete mode 100644 module/gitlab/view/createbranch.html.php diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 3a9549771f..6b7abcb988 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -121,7 +121,6 @@ $lang->action->objectTypes['mr'] = 'Merge Request'; $lang->action->objectTypes['gitlabproject'] = 'GitLab Project'; $lang->action->objectTypes['gitlabuser'] = 'GitLab User'; $lang->action->objectTypes['gitlabgroup'] = 'GitLab Group'; -$lang->action->objectTypes['gitlabbranch'] = 'GitLab Branch'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 559b01bf3b..2b7b8023bf 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -121,7 +121,6 @@ $lang->action->objectTypes['mr'] = '合并请求'; $lang->action->objectTypes['gitlabproject'] = 'GitLab项目'; $lang->action->objectTypes['gitlabuser'] = 'GitLab用户'; $lang->action->objectTypes['gitlabgroup'] = 'GitLab群组'; -$lang->action->objectTypes['gitlabbranch'] = 'GitLab分支'; /* 用来描述操作历史记录。*/ $lang->action->desc = new stdclass(); diff --git a/module/block/lang/en.php b/module/block/lang/en.php index b0a9a555e3..ce31de4f53 100644 --- a/module/block/lang/en.php +++ b/module/block/lang/en.php @@ -91,7 +91,6 @@ $lang->block->refresh = 'Refresh'; $lang->block->nbsp = ' '; $lang->block->hidden = 'Hide'; $lang->block->dynamicInfo = "%s %s %s %s %s"; -$lang->block->noLinkDynamic = "%s %s %s %s %s"; $lang->block->cannotPlaceInLeft = 'Cannot place the block at left side.'; $lang->block->cannotPlaceInRight = 'Cannot place the block at right side.'; diff --git a/module/block/lang/zh-cn.php b/module/block/lang/zh-cn.php index e5d8deface..ac77206f01 100644 --- a/module/block/lang/zh-cn.php +++ b/module/block/lang/zh-cn.php @@ -91,7 +91,6 @@ $lang->block->refresh = '刷新'; $lang->block->nbsp = ''; $lang->block->hidden = '隐藏'; $lang->block->dynamicInfo = "%s %s %s %s %s"; -$lang->block->noLinkDynamic = "%s %s %s %s %s"; $lang->block->cannotPlaceInLeft = '此区块无法放置在左侧。'; $lang->block->cannotPlaceInRight = '此区块无法放置在右侧。'; diff --git a/module/block/view/dynamic.html.php b/module/block/view/dynamic.html.php index 215bb804d3..2447f62507 100644 --- a/module/block/view/dynamic.html.php +++ b/module/block/view/dynamic.html.php @@ -15,8 +15,7 @@ if($action->action == 'login' or $action->action == 'logout') $action->objectName = $action->objectLabel = ''; $class = $action->major ? "class='active'" : ''; echo "
  • "; - if($action->objectLink) printf($lang->block->dynamicInfo, $action->date, $user, $action->actionLabel, $action->objectLabel, $action->objectLink, $action->objectName, $action->objectName); - if(!$action->objectLink) printf($lang->block->noLinkDynamic, $action->date, $action->objectName, $user, $action->actionLabel, $action->objectLabel, $action->objectName); + printf($lang->block->dynamicInfo, $action->date, $user, $action->actionLabel, $action->objectLabel, $action->objectLink, $action->objectName, $action->objectName); echo "
  • "; $i++; } diff --git a/module/gitlab/config.php b/module/gitlab/config.php index bd6760c7cc..5f41d70ceb 100644 --- a/module/gitlab/config.php +++ b/module/gitlab/config.php @@ -5,9 +5,6 @@ $config->gitlab->create->requiredFields = 'name,url,token'; $config->gitlab->edit = new stdclass; $config->gitlab->edit->requiredFields = 'name,url,token'; -$config->gitlab->createbranch = new stdclass; -$config->gitlab->createbranch->requiredFields = 'branch,ref'; - $config->gitlab->labelPattern = new stdclass; $config->gitlab->labelPattern->task = '/^zentao_task\/\d+$/'; $config->gitlab->labelPattern->bug = '/^zentao_bug\/\d+$/'; diff --git a/module/gitlab/control.php b/module/gitlab/control.php index b1738b51fd..e56cae595d 100644 --- a/module/gitlab/control.php +++ b/module/gitlab/control.php @@ -712,39 +712,6 @@ class gitlab extends control die(js::alert($reponse->message)); } - /** - * Creat a gitlab branch. - * - * @param int $gitlabID - * @param int $projectID - * @access public - * @return void - */ - public function createBranch($gitlabID, $projectID) - { - if($_POST) - { - $this->gitlab->createBranch($gitlabID, $projectID); - if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - - $locate = $this->session->gitlabBranchList ? $this->session->gitlabBranchList : inlink('browseBranch', "gitlibID=$gitlabID&projectID=$projectID"); - return $this->send(array('result' => 'success', 'message' => $this->lang->gitlab->createSuccess, 'locate' => $locate)); - } - - /* Get branches by api. */ - $branches = $this->gitlab->apiGetBranches($gitlabID, $projectID); - if(!is_array($branches)) $branches= array(); - - $branchPairs = array(); - foreach($branches as $branch) $branchPairs[$branch->name] = $branch->name; - - $this->view->title = $this->lang->gitlab->common . $this->lang->colon . $this->lang->gitlab->createBranch; - $this->view->gitlabID = $gitlabID; - $this->view->projectID = $projectID; - $this->view->branchPairs = $branchPairs; - $this->display(); - } - /** * Import gitlab issue to zentaopms. * diff --git a/module/gitlab/lang/en.php b/module/gitlab/lang/en.php index 3224521a51..07c7ad4e7f 100644 --- a/module/gitlab/lang/en.php +++ b/module/gitlab/lang/en.php @@ -37,7 +37,6 @@ $lang->gitlab->manageProjectMembers = 'Manage project member'; $lang->gitlab->createProject = 'Create GitLab project'; $lang->gitlab->editProject = 'Eidt GitLab project'; $lang->gitlab->deleteProject = 'Delete GitLab project'; -$lang->gitlab->createBranch = 'Create Branch'; $lang->gitlab->createGroup = 'Create group'; $lang->gitlab->editGroup = 'Edit group'; $lang->gitlab->deleteGroup = 'Delete group'; @@ -54,12 +53,10 @@ $lang->gitlab->token = 'Token'; $lang->gitlab->defaultProject = 'Default Project'; $lang->gitlab->private = 'MD5 Verify'; -$lang->gitlab->lblCreate = 'Create GitLab Server'; -$lang->gitlab->desc = 'Description'; -$lang->gitlab->tokenFirst = 'When the Token is not empty, the Token will be used first'; -$lang->gitlab->tips = 'When using a password, please disable the "Prevent cross-site request forgery" option in the GitLab global security settings.'; -$lang->gitlab->emptyError = "cannot be empty"; -$lang->gitlab->createSuccess = "Create success"; +$lang->gitlab->lblCreate = 'Create GitLab Server'; +$lang->gitlab->desc = 'Description'; +$lang->gitlab->tokenFirst = 'When the Token is not empty, the Token will be used first'; +$lang->gitlab->tips = 'When using a password, please disable the "Prevent cross-site request forgery" option in the GitLab global security settings.'; $lang->gitlab->placeholder = new stdclass; $lang->gitlab->placeholder->name = ''; @@ -85,13 +82,11 @@ $lang->gitlab->apiError[0] = 'internal is not allowed in a private group.'; $lang->gitlab->apiError[1] = 'public is not allowed in a private group.'; $lang->gitlab->apiError[2] = 'is too short (minimum is 8 characters)'; $lang->gitlab->apiError[3] = "can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"; -$lang->gitlab->apiError[4] = 'Branch already exists'; $lang->gitlab->errorLang[0] = 'You cannot set Internal as its Visibility Level, if it is private in GitLab.'; $lang->gitlab->errorLang[1] = 'You cannot set Public as its Visibility Level, if it is private in GitLab.'; $lang->gitlab->errorLang[2] = 'Password is too short (minimum is 8 characters)'; $lang->gitlab->errorLang[3] = 'It should contain only letters, digits, underscore, hyphen and period. It should not start with hypen, or end with .git or .atom.'; -$lang->gitlab->errorLang[4] = 'Branch already exists.'; $lang->gitlab->project = new stdclass; $lang->gitlab->project->id = "Project ID"; @@ -176,8 +171,3 @@ $lang->gitlab->group->memberName = 'Account'; $lang->gitlab->group->memberAccessLevel = 'Access Level'; $lang->gitlab->group->memberExpiresAt = 'Expiration time'; $lang->gitlab->group->repeatError = "Group members cannot be added repeatedly"; - -$lang->gitlab->branch = new stdclass(); -$lang->gitlab->branch->name = 'Branch Name'; -$lang->gitlab->branch->from = 'Create from'; -$lang->gitlab->branch->create = 'Create'; diff --git a/module/gitlab/lang/zh-cn.php b/module/gitlab/lang/zh-cn.php index d23f272d0d..d911572925 100644 --- a/module/gitlab/lang/zh-cn.php +++ b/module/gitlab/lang/zh-cn.php @@ -37,7 +37,6 @@ $lang->gitlab->manageProjectMembers = '项目成员管理'; $lang->gitlab->createProject = '添加GitLab项目'; $lang->gitlab->editProject = '编辑GitLab项目'; $lang->gitlab->deleteProject = '删除GitLab项目'; -$lang->gitlab->createBranch = '添加分支'; $lang->gitlab->createGroup = '添加群组'; $lang->gitlab->editGroup = '编辑群组'; $lang->gitlab->deleteGroup = '删除群组'; @@ -54,12 +53,10 @@ $lang->gitlab->token = 'Token'; $lang->gitlab->defaultProject = '默认项目'; $lang->gitlab->private = 'MD5验证'; -$lang->gitlab->lblCreate = '添加GitLab服务器'; -$lang->gitlab->desc = '描述'; -$lang->gitlab->tokenFirst = 'Token不为空时,优先使用Token。'; -$lang->gitlab->tips = '使用密码时,请在GitLab全局安全设置中禁用"防止跨站点请求伪造"选项。'; -$lang->gitlab->emptyError = "不能为空"; -$lang->gitlab->createSuccess = "创建成功"; +$lang->gitlab->lblCreate = '添加GitLab服务器'; +$lang->gitlab->desc = '描述'; +$lang->gitlab->tokenFirst = 'Token不为空时,优先使用Token。'; +$lang->gitlab->tips = '使用密码时,请在GitLab全局安全设置中禁用"防止跨站点请求伪造"选项。'; $lang->gitlab->placeholder = new stdclass; $lang->gitlab->placeholder->name = ''; @@ -85,13 +82,11 @@ $lang->gitlab->apiError[0] = 'internal is not allowed in a private group.'; $lang->gitlab->apiError[1] = 'public is not allowed in a private group.'; $lang->gitlab->apiError[2] = 'is too short (minimum is 8 characters)'; $lang->gitlab->apiError[3] = "can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"; -$lang->gitlab->apiError[4] = 'Branch already exists'; $lang->gitlab->errorLang[0] = '私有分组的项目,可见性级别不能设为内部。'; $lang->gitlab->errorLang[1] = '私有分组的项目,可见性级别不能设为公开。'; $lang->gitlab->errorLang[2] = '密码太短(最少8个字符)'; $lang->gitlab->errorLang[3] = "只能包含字母、数字、'.'-'和'.'。不能以'-'开头、以'.git'结尾或以'.atom'结尾。"; -$lang->gitlab->errorLang[4] = '分支名已存在。'; $lang->gitlab->project = new stdclass; $lang->gitlab->project->id = "项目ID"; @@ -176,8 +171,3 @@ $lang->gitlab->group->memberName = '账号'; $lang->gitlab->group->memberAccessLevel = '角色权限'; $lang->gitlab->group->memberExpiresAt = '过期时间'; $lang->gitlab->group->repeatError = "群组成员不能重复添加"; - -$lang->gitlab->branch = new stdclass(); -$lang->gitlab->branch->name = '分支名'; -$lang->gitlab->branch->from = '创建自'; -$lang->gitlab->branch->create = '创建'; diff --git a/module/gitlab/model.php b/module/gitlab/model.php index 6ca9804d8d..437a2a05ce 100644 --- a/module/gitlab/model.php +++ b/module/gitlab/model.php @@ -896,24 +896,6 @@ class gitlabModel extends model return json_decode(commonModel::http($url, array(), $options = array(CURLOPT_CUSTOMREQUEST => 'DELETE'))); } - /** - * Create a gitab user by api. - * - * @param int $gitlabID - * @param int $projectID - * @param object $branch - * @access public - * @return object - */ - public function apiCreateBranch($gitlabID, $projectID, $branch) - { - if(empty($branch->branch) or empty($branch->ref)) return false; - - $apiRoot = $this->getApiRoot($gitlabID); - $url = sprintf($apiRoot, "/projects/{$projectID}/repository/branches"); - return json_decode(commonModel::http($url, $branch)); - } - /** * Get single project by API. * @@ -2254,33 +2236,6 @@ class gitlabModel extends model return $this->apiErrorHandling($reponse); } - /** - * Create a gitlab branch. - * - * @param int $gitlabID - * @param int $projectID - * @access public - * @return bool - */ - public function createBranch($gitlabID, $projectID) - { - $branch = fixer::input('post')->get(); - - if(empty($branch->branch)) dao::$errors['branch'][] = $this->lang->gitlab->branch->name . $this->lang->gitlab->emptyError; - if(empty($branch->ref)) dao::$errors['ref'][] = $this->lang->gitlab->branch->from . $this->lang->gitlab->emptyError; - if(dao::isError()) return false; - - $reponse = $this->apiCreateBranch($gitlabID, $projectID, $branch); - - if(!empty($reponse->name)) - { - $this->loadModel('action')->create('gitlabbranch', 0, 'created', '', $reponse->name); - return true; - } - - return $this->apiErrorHandling($reponse); - } - /** * Api error handling. * diff --git a/module/gitlab/view/createbranch.html.php b/module/gitlab/view/createbranch.html.php deleted file mode 100644 index 391f3ff94c..0000000000 --- a/module/gitlab/view/createbranch.html.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @package gitlab - * @version $Id$ - * @link https://www.zentao.net - */ -?> - -
    -
    -
    -
    -

    gitlab->createBranch;?>

    -
    -
    - - - - - - - - - - - - - - -
    gitlab->branch->name;?>gitlab->branch->name}'");?>
    gitlab->branch->from;?>
    - gitlab->branch->create);?> - session->gitlabBranchList ? $this->session->gitlabBranchList : inlink('browseBranch', "gitlibID=$gitlabID&projectID=$projectID");?> - goback, '', 'class="btn btn-wide"');?> -
    -
    -
    -
    -
    - diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index 76a10cdb03..200c124cf0 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -1297,7 +1297,6 @@ $lang->resource->gitlab->browseUser = 'browseUser'; $lang->resource->gitlab->createUser = 'createUser'; $lang->resource->gitlab->editUser = 'editUser'; $lang->resource->gitlab->deleteUser = 'deleteUser'; -$lang->resource->gitlab->createBranch = 'createBranch'; $lang->resource->gitlab->webhook = 'webhook'; $lang->resource->gitlab->createWebhook = 'createWebhook'; $lang->resource->gitlab->manageProjectMembers = 'manageProjectMembers'; diff --git a/module/repo/control.php b/module/repo/control.php index 78c3294bb2..2bc9dd1f34 100644 --- a/module/repo/control.php +++ b/module/repo/control.php @@ -357,7 +357,6 @@ class repo extends control session_start(); $this->session->set('revisionList', $this->app->getURI(true)); - $this->session->set('gitlabBranchList', $this->app->getURI(true)); session_write_close(); /* Get repo and synchronous commit. */ diff --git a/module/repo/view/browse.html.php b/module/repo/view/browse.html.php index cca1e145c7..5a5d289fe0 100644 --- a/module/repo/view/browse.html.php +++ b/module/repo/view/browse.html.php @@ -59,7 +59,6 @@
    repo->notice->lastSyncTime . $cacheTime?> repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID&path=" . $this->repo->encodePath($path) . "&revision=$revision&refresh=1"), " " . $lang->refresh, '', "class='btn btn-primary' data-app={$app->tab}");?> - SCM == 'Gitlab' and common::hasPriv('gitlab', 'createBranch')) echo html::a($this->createLink('gitlab', 'createBranch', "gitlabID={$repo->gitlab}&projectID={$repo->project}"), " " . $lang->gitlab->createBranch, '', "class='btn btn-primary'");?>
    From 7458b8ef92ab0f6bba97ae4e4bf357547be59472 Mon Sep 17 00:00:00 2001 From: tanghucheng Date: Tue, 7 Dec 2021 17:13:23 +0800 Subject: [PATCH 11/44] * Fix bug #17225. --- module/execution/view/all.html.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/module/execution/view/all.html.php b/module/execution/view/all.html.php index cabe069803..d9b45bb968 100644 --- a/module/execution/view/all.html.php +++ b/module/execution/view/all.html.php @@ -139,10 +139,10 @@ percent . '%';?> stage->typeList, $execution->attribute, '');?> - begin;?> - end;?> - realBegan;?> - realEnd;?> + begin) ? '' : $execution->begin;?> + end) ? '' : $execution->end;?> + realBegan) ? '' : $execution->realBegan;?> + realEnd) ? '' : $execution->realEnd;?> id}", $execution, 'list', '', '', 'iframe', true); @@ -213,10 +213,10 @@ percent . '%';?> stage->typeList, $child->attribute, '');?> - begin;?> - end;?> - realBegan;?> - realEnd;?> + begin) ? '' : $child->begin;?> + end) ? '' : $child->end;?> + realBegan) ? '' : $child->realBegan;?> + realEnd) ? '' : $child->realEnd;?> id}", $child, 'list', '', '', 'iframe', true); From 47c200091449ce84c1e715d8d984e5082f72be70 Mon Sep 17 00:00:00 2001 From: xieqiyu Date: Tue, 7 Dec 2021 17:18:40 +0800 Subject: [PATCH 12/44] * Fix bug#17193. --- module/repo/model.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/module/repo/model.php b/module/repo/model.php index 6664e6aa7c..a69ce07eab 100644 --- a/module/repo/model.php +++ b/module/repo/model.php @@ -230,7 +230,9 @@ class repoModel extends model ->autoCheck() ->exec(); - if(!dao::isError()) $this->rmClientVersionFile(); + if(dao::isError()) return false; + + $this->rmClientVersionFile(); $repoID = $this->dao->lastInsertID(); From 8e7432ad0851d473489b997555c85b509206e5f9 Mon Sep 17 00:00:00 2001 From: tianshujie Date: Tue, 7 Dec 2021 17:22:14 +0800 Subject: [PATCH 13/44] * Fix bug #17173,17173. --- module/execution/view/manageproducts.html.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/module/execution/view/manageproducts.html.php b/module/execution/view/manageproducts.html.php index bb7b0fbb5c..fcb8b30cad 100644 --- a/module/execution/view/manageproducts.html.php +++ b/module/execution/view/manageproducts.html.php @@ -28,8 +28,8 @@ $productName):?> - grade == 1 and in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches))) $attr = "disabled='disabled'";?> - grade == 1 and !(in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches)))) $attr = '';?> + grade < 2 and in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches))) $attr = "disabled='disabled'";?> + grade < 2 and !(in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches)))) $attr = '';?> execution->notAllowRemoveProducts : $productName;?>
    @@ -60,12 +60,13 @@
    '>
    - ";?> + ";?>
    - +
    +
    From f0e55ad2a49e767012d87e54e4435ed87cc12175 Mon Sep 17 00:00:00 2001 From: liumengyi Date: Tue, 7 Dec 2021 18:11:43 +0800 Subject: [PATCH 14/44] * Fix bug #17227. --- module/action/model.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/module/action/model.php b/module/action/model.php index b6a12fa9f3..eef1e4c8ac 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -882,31 +882,19 @@ class actionModel extends model } /* If the sql not include 'product', add check purview for product. */ - if(strpos($actionQuery, $allProducts) === false) - { - if(!in_array($productID, array_keys($products))) return array(); - } - else + if(strpos($actionQuery, $allProducts) !== false) { $actionQuery = str_replace($allProducts, '1', $actionQuery); } /* If the sql not include 'project', add check purview for project. */ - if(strpos($actionQuery, $allProjects) === false) - { - $actionQuery = $actionQuery . ' AND `project`' . helper::dbIN(array_keys($projects)); - } - else + if(strpos($actionQuery, $allProjects) !== false) { $actionQuery = str_replace($allProjects, '1', $actionQuery); } /* If the sql not include 'execution', add check purview for execution. */ - if(strpos($actionQuery, $allExecutions) === false) - { - $actionQuery = $actionQuery . ' AND `execution`' . helper::dbIN(array_keys($executions)); - } - else + if(strpos($actionQuery, $allExecutions) !== false) { $actionQuery = str_replace($allExecutions, '1', $actionQuery); } From 74958bd6b2e5d1b53f3ecd08ba28c8dbe0315465 Mon Sep 17 00:00:00 2001 From: tianshujie Date: Wed, 8 Dec 2021 08:44:41 +0800 Subject: [PATCH 15/44] * Fix bug #17208. --- module/upgrade/js/mergeprogram.js | 7 +++---- module/upgrade/model.php | 6 ------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/module/upgrade/js/mergeprogram.js b/module/upgrade/js/mergeprogram.js index 210e16600e..dde8bc3f65 100644 --- a/module/upgrade/js/mergeprogram.js +++ b/module/upgrade/js/mergeprogram.js @@ -441,17 +441,16 @@ $(function() if(lineID && $('[data-lineid=' + lineID + ']').length > 0 && !$('[data-lineid=' + lineID + ']').prop('checked')) $('[data-lineid=' + lineID + ']').prop('checked', true); $('#programName').val($("[lineid='" + lineID + "']").find('a').text()); - $('#programStatus').val('wait'); - $('#programStatus').trigger('chosen:updated'); } else { $('[data-product=' + value + ']').prop('checked', false) $('#programName').val(''); - $('#programStatus').val('wait'); - $('#programStatus').trigger('chosen:updated'); } + $('#programStatus').val('wait'); + $('#programStatus').trigger('chosen:updated'); + var checkedProject = true; if($('[id^=productLines]').length > 0) { diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 69bbef0ccd..8293c4e4f9 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -4369,12 +4369,6 @@ class upgradeModel extends model $this->loadModel('action')->create('program', $programID, 'openedbysystem'); if($data->programStatus == 'closed') $this->loadModel('action')->create('program', $programID, 'closedbysystem'); } - else - { - $programID = $data->programID ? $data->programID : $data->programs; - $this->dao->update(TABLE_PROGRAM)->set('status')->eq($data->programStatus)->where('id')->eq($programID)->exec(); - if($data->programStatus == 'closed') $this->loadModel('action')->create('program', $programID, 'openedbysystem'); - } if(isset($data->newLine)) { From 32c7a078a71d018574609198450fb267125c175e Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Wed, 8 Dec 2021 08:47:00 +0800 Subject: [PATCH 16/44] * Fix the code error. --- module/action/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/action/model.php b/module/action/model.php index eef1e4c8ac..d153117996 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -721,7 +721,7 @@ class actionModel extends model $actionDesc = str_replace('$extra', $action->extra, $desc['main']); } - if($action->objectType == 'story' and $action->action = 'reviewed' and strpos($action->extra, ',') !== false) + if($action->objectType == 'story' and $action->action == 'reviewed' and strpos($action->extra, ',') !== false) { list($extra, $reason) = explode(',', $extra); $desc['reason'] = $this->lang->$objectType->{$desc['reason']}; From 14b272a1af4ea58332151866d42c745bc038ec92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B9=BF=E6=98=8E?= Date: Wed, 8 Dec 2021 11:07:06 +0800 Subject: [PATCH 17/44] * Merge 169 untrack file to master. --- lib/scm/gitlab.class.php | 3 +++ module/api/control.php | 8 +++++--- module/api/lang/zh-cn.php | 2 +- module/api/model.php | 17 +++++++++-------- module/api/view/content.html.php | 2 +- module/api/view/create.html.php | 2 +- module/api/view/index.html.php | 4 +++- module/api/view/releases.html.php | 2 +- module/api/view/struct.html.php | 4 ++-- module/bug/control.php | 11 ++++++++++- module/bug/view/view.html.php | 2 +- module/ci/model.php | 1 + module/common/view/noticeimport.html.php | 2 ++ module/compile/model.php | 4 ++-- module/doc/js/objectlibs.js | 2 +- module/gitlab/model.php | 12 ++++++------ module/gitlab/view/view.html.php | 1 - module/job/control.php | 4 ++-- module/job/model.php | 9 +++++---- module/message/model.php | 5 +---- module/my/view/task.html.php | 10 +++++----- module/product/control.php | 1 + module/product/css/view.css | 4 ++-- module/product/js/create.js | 4 +++- module/product/lang/en.php | 1 + module/product/lang/zh-cn.php | 3 ++- module/product/model.php | 8 +++++++- module/product/view/browse.html.php | 2 +- module/product/view/create.html.php | 12 ++++++------ module/product/view/project.html.php | 10 ++++++---- module/productplan/css/browse.css | 2 +- module/productplan/js/browse.js | 12 +++++++++++- module/productplan/js/create.js | 7 +++---- module/productplan/js/edit.js | 5 ++++- module/productplan/lang/en.php | 5 +++-- module/productplan/lang/zh-cn.php | 5 +++-- module/productplan/model.php | 12 ++++++++++++ module/productplan/view/browse.html.php | 1 + module/productplan/view/create.html.php | 7 +++++-- module/productplan/view/edit.html.php | 1 + module/program/view/edit.html.php | 2 +- module/project/lang/en.php | 2 +- module/project/lang/zh-cn.php | 2 +- module/testreport/lang/en.php | 1 + module/testreport/lang/zh-cn.php | 1 + module/testreport/view/create.html.php | 7 ++++++- module/testreport/view/edit.html.php | 7 ++++++- module/testreport/view/view.html.php | 7 +++++-- module/todo/model.php | 3 ++- 49 files changed, 159 insertions(+), 82 deletions(-) diff --git a/lib/scm/gitlab.class.php b/lib/scm/gitlab.class.php index 1730ea27ce..82474eeb3c 100644 --- a/lib/scm/gitlab.class.php +++ b/lib/scm/gitlab.class.php @@ -90,6 +90,9 @@ class gitlab /** * Get files info. * + * The API path requested is: "GET /projects/:id/repository/files/:file_path". + * Known issue of GitLab API: if a '%' in 'file_path', GitLab API will show a error 'file_path should be a valid file path'. + * * @param string $path * @param string $ref * @access public diff --git a/module/api/control.php b/module/api/control.php index 4f70ef0c99..0f6655ce44 100755 --- a/module/api/control.php +++ b/module/api/control.php @@ -69,7 +69,7 @@ class api extends control $this->view->typeList = $this->api->getTypeList($libID); } - $this->setMenu($libID); + $this->setMenu($libID, $moduleID); $this->view->isRelease = $release > 0; $this->view->release = $release; @@ -462,6 +462,7 @@ class api extends control { $now = helper::now(); $params = fixer::input('post') + ->trim('title,path') ->remove('type') ->skipSpecial('params,response') ->add('addedBy', $this->app->user->account) @@ -601,10 +602,11 @@ class api extends control * Set doc menu by method name. * * @param int $libID + * @param int $moduleID * @access public * @return void */ - private function setMenu($libID = 0) + private function setMenu($libID = 0, $moduleID = 0) { common::setMenuVars('doc', $libID); @@ -629,7 +631,7 @@ class api extends control if(common::hasPriv('api', 'create')) { $menu .= "
  • "; - $menu .= html::a(helper::createLink('api', 'create', "libID=$libID"), " " . $this->lang->api->apiDoc, '', "data-app='{$this->app->tab}'"); + $menu .= html::a(helper::createLink('api', 'create', "libID=$libID&moduleID=$moduleID"), " " . $this->lang->api->apiDoc, '', "data-app='{$this->app->tab}'"); $menu .= "
  • "; } diff --git a/module/api/lang/zh-cn.php b/module/api/lang/zh-cn.php index b5506e0540..d8946ad167 100755 --- a/module/api/lang/zh-cn.php +++ b/module/api/lang/zh-cn.php @@ -41,7 +41,7 @@ $lang->api->noUniqueName = '接口库名已存在。'; $lang->api->noUniqueVersion = '版本已存在。'; $lang->api->version = '版本'; $lang->api->createStruct = '创建数据结构'; -$lang->api->editStruct = '修改数据结构'; +$lang->api->editStruct = '编辑数据结构'; $lang->api->deleteStruct = '删除数据结构'; $lang->api->create = '创建接口'; $lang->api->title = '接口名称'; diff --git a/module/api/model.php b/module/api/model.php index 5d5ad7997b..d0271b2cd1 100644 --- a/module/api/model.php +++ b/module/api/model.php @@ -209,20 +209,17 @@ class apiModel extends model $now = helper::now(); $account = $this->app->user->account; $data = fixer::input('post') - ->remove('type') ->skipSpecial('params,response') ->add('editedBy', $account) ->add('editedDate', $now) + ->add('version', $oldApi->version) ->setDefault('product,module', 0) + ->remove('type') ->get(); - $data->id = $oldApi->id; - $data->version = $oldApi->version + 1; - $apiSpec = $this->getApiSpecByData($data); + $changes = common::createChanges($oldApi, $data); + if(!empty($changes)) $data->version = $oldApi->version + 1; - $this->dao->replace(TABLE_API_SPEC)->data($apiSpec)->exec(); - - unset($data->id); $this->dao->update(TABLE_API) ->data($data) ->autoCheck() @@ -230,7 +227,11 @@ class apiModel extends model ->where('id')->eq($apiID) ->exec(); - return common::createChanges($oldApi, $data); + $data->id = $apiID; + $apiSpec = $this->getApiSpecByData($data); + $this->dao->replace(TABLE_API_SPEC)->data($apiSpec)->exec(); + + return $changes; } /** diff --git a/module/api/view/content.html.php b/module/api/view/content.html.php index b86262c5d2..dd9ddad90c 100644 --- a/module/api/view/content.html.php +++ b/module/api/view/content.html.php @@ -1,4 +1,4 @@ -
    +
    diff --git a/module/api/view/create.html.php b/module/api/view/create.html.php index 0bd168a361..4f5684931b 100644 --- a/module/api/view/create.html.php +++ b/module/api/view/create.html.php @@ -68,7 +68,7 @@ js::set('struct_paramsType', $lang->struct->paramsType); api->module;?> - + diff --git a/module/api/view/index.html.php b/module/api/view/index.html.php index bcad350858..ba0656c6c2 100644 --- a/module/api/view/index.html.php +++ b/module/api/view/index.html.php @@ -42,8 +42,10 @@

    +
    api->noModule;?>
    +
    @@ -71,7 +73,7 @@
    -
    +
      diff --git a/module/api/view/releases.html.php b/module/api/view/releases.html.php index ca0afb44b6..1c1e1a7d69 100644 --- a/module/api/view/releases.html.php +++ b/module/api/view/releases.html.php @@ -41,7 +41,7 @@ addedBy, '');?> addedDate;?> - createLink('api', 'deleteRelease', "libID=$libID&id=$release->id"), '', 'hiddenwin', "title='{$lang->api->delete}' class='btn'");?> + createLink('api', 'deleteRelease', "libID=$libID&id=$release->id"), '', 'hiddenwin', "title='{$lang->api->deleteRelease}' class='btn'");?> diff --git a/module/api/view/struct.html.php b/module/api/view/struct.html.php index d7f744ab1f..f1983a737b 100644 --- a/module/api/view/struct.html.php +++ b/module/api/view/struct.html.php @@ -49,8 +49,8 @@ addedDate;?> createLink('api', 'editStruct', "libID=$libID&structID=$struct->id"), '', '', "title='{$lang->api->edit}' class='btn'"); - if(common::hasPriv('api', 'deleteStruct')) echo html::a($this->createLink('api', 'deleteStruct', "libID=$libID&structID=$struct->id"), '', 'hiddenwin', "title='{$lang->api->delete}' class='btn'"); + if(common::hasPriv('api', 'editStruct')) echo html::a($this->createLink('api', 'editStruct', "libID=$libID&structID=$struct->id"), '', '', "title='{$lang->api->editStruct}' class='btn'"); + if(common::hasPriv('api', 'deleteStruct')) echo html::a($this->createLink('api', 'deleteStruct', "libID=$libID&structID=$struct->id"), '', 'hiddenwin', "title='{$lang->api->deleteStruct}' class='btn'"); ?> diff --git a/module/bug/control.php b/module/bug/control.php index 0beba6c819..b2e402475e 100644 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -407,7 +407,16 @@ class bug extends control if($this->app->tab == 'execution') { - $location = $this->session->bugList ? $this->session->bugList : $this->createLink('execution', 'bug', "executionID={$output['executionID']}"); + if(!preg_match("/(m=|\/)execution(&f=|-)bug(&|-|\.)?/", $this->session->bugList)) + { + $location = $this->session->bugList; + } + else + { + $executionID = $this->post->execution ? $this->post->execution : $output['executionID']; + $location = $this->createLink('execution', 'bug', "executionID=$executionID"); + } + } elseif($this->app->tab == 'project') { diff --git a/module/bug/view/view.html.php b/module/bug/view/view.html.php index 216fbacdf6..e34988bbdb 100644 --- a/module/bug/view/view.html.php +++ b/module/bug/view/view.html.php @@ -93,7 +93,7 @@ if($this->app->tab != 'product') { - common::printIcon('bug', 'toStory', "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id", $bug, 'button', $lang->icons['story'], '', '', '', "data-app='" . $this->app->tab . "'", $lang->bug->toStory); + common::printIcon('bug', 'toStory', "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id", $bug, 'button', $lang->icons['story'], '', '', '', "data-app='product'", $lang->bug->toStory); common::printIcon('bug', 'createCase', $convertParams, $bug, 'button', 'sitemap'); } diff --git a/module/ci/model.php b/module/ci/model.php index 7d632d1519..0bf3b7ca57 100644 --- a/module/ci/model.php +++ b/module/ci/model.php @@ -54,6 +54,7 @@ class ciModel extends model */ public function syncCompileStatus($compile) { + /* Max retry times is: 3. */ if($compile->times >= 3) { $this->dao->update(TABLE_COMPILE)->set('status')->eq('failure')->where('id')->eq($compile->id)->exec(); diff --git a/module/common/view/noticeimport.html.php b/module/common/view/noticeimport.html.php index 5f87dd9b43..600fcbea00 100644 --- a/module/common/view/noticeimport.html.php +++ b/module/common/view/noticeimport.html.php @@ -27,5 +27,7 @@ function submitForm(type) $('.modal-body #insert').val(type == 'insert' ? 1 : 0); $('#importNoticeModal .form-actions .btn').addClass('disabled'); $("button[data-target='#importNoticeModal']").closest('form').submit(); + + setTimeout(function(){$('#importNoticeModal .form-actions .btn').removeClass('disabled');}, 1000); } diff --git a/module/compile/model.php b/module/compile/model.php index f395bbb110..07738955a0 100644 --- a/module/compile/model.php +++ b/module/compile/model.php @@ -178,8 +178,8 @@ class compileModel extends model $this->dao->update(TABLE_COMPILE)->data($compile)->where('id')->eq($compileID)->exec(); $this->dao->update(TABLE_JOB) - ->set('lastStatus')->eq($build->status) - ->set('lastExec')->eq($build->updateDate) + ->set('lastStatus')->eq($compile->status) + ->set('lastExec')->eq($compile->updateDate) ->where('id')->eq($job->id) ->exec(); diff --git a/module/doc/js/objectlibs.js b/module/doc/js/objectlibs.js index 992cb3cae6..8830c91235 100644 --- a/module/doc/js/objectlibs.js +++ b/module/doc/js/objectlibs.js @@ -133,7 +133,7 @@ $(function() $('#content').on('click', '.outline .outline-toggle i.icon-angle-right', function() { - $('.article-content').width('85%'); + $('.article-content').css('width', '85%'); $('.outline').css({'min-width' : '180px', 'border-left' : '2px solid #efefef'}); $(this).removeClass('icon-angle-right').addClass('icon-angle-left').css('left', '-9px'); $('.outline-content').show(); diff --git a/module/gitlab/model.php b/module/gitlab/model.php index 437a2a05ce..b2af4366f7 100644 --- a/module/gitlab/model.php +++ b/module/gitlab/model.php @@ -411,7 +411,7 @@ class gitlabModel extends model if(is_numeric($host)) $host = $this->getApiRoot($host); if(strpos($host, 'http://') !== 0 and strpos($host, 'https://') !== 0) return false; - $url = sprintf($apiRoot, $api); + $url = sprintf($host, $api); return json_decode(commonModel::http($url, $data, $options)); } @@ -635,7 +635,7 @@ class gitlabModel extends model * * @param int $gitlabID * @access public - * @return void + * @return array */ public function apiGetProjects($gitlabID) { @@ -1715,9 +1715,9 @@ class gitlabModel extends model /** * Create webhook for zentao. * - * @param int $products - * @param int $gitlabID - * @param int $projectID + * @param array $products + * @param int $gitlabID + * @param int $projectID * @access public * @return bool */ @@ -1830,7 +1830,7 @@ class gitlabModel extends model if($value) $issue->$field = $value; } - if($isset($issue->assignee_id) and $issue->assignee_id == 'closed') unset($issue->assignee_id); + if(isset($issue->assignee_id) and $issue->assignee_id == 'closed') unset($issue->assignee_id); /* issue->state is null when creating it, we should put status_event when updating it. */ if(isset($issue->state) and $issue->state == 'closed') $issue->state_event = 'close'; diff --git a/module/gitlab/view/view.html.php b/module/gitlab/view/view.html.php index 30fc445968..b1cb6b07d3 100644 --- a/module/gitlab/view/view.html.php +++ b/module/gitlab/view/view.html.php @@ -27,4 +27,3 @@
    - diff --git a/module/job/control.php b/module/job/control.php index f4ba69c1dc..961f71c525 100644 --- a/module/job/control.php +++ b/module/job/control.php @@ -120,7 +120,7 @@ class job extends control $this->view->repoTypes = $repoTypes; $this->view->products = array(0 => '') + $this->loadModel('product')->getProductPairsByProject($this->projectID); - $this->view->jenkinsServerList = $this->loadModel('jenkins')->getPairs(); + $this->view->jenkinsServerList = array('' => '') + $this->loadModel('jenkins')->getPairs(); $this->display(); } @@ -352,7 +352,7 @@ class job extends control if($productLeft == $productRight) $matchedProducts[$productName] = $productRight; } } - die(json_encode($matchedProduct)); + die(json_encode($matchedProducts)); } $productName = $this->loadModel('product')->getByID($repo->product)->name; diff --git a/module/job/model.php b/module/job/model.php index 72514a4d49..2e69ab65f1 100644 --- a/module/job/model.php +++ b/module/job/model.php @@ -378,7 +378,7 @@ class jobModel extends model if($job->triggerType == 'tag') { - $lastTag = $this->getLastTagByRepo($repo); + $lastTag = $this->getLastTagByRepo($repo, $job); if($lastTag) { $build->tag = $lastTag; @@ -444,7 +444,7 @@ class jobModel extends model /** * Exec gitlab pipeline. * - * @param int $job + * @param object $job * @access public * @return void */ @@ -486,11 +486,12 @@ class jobModel extends model /** * Get last tag of one repo. * - * @param object $repo + * @param object $repo + * @param object $job * @access public * @return void */ - public function getLastTagByRepo($repo) + public function getLastTagByRepo($repo, $job) { if($repo->SCM == 'Subversion') { diff --git a/module/message/model.php b/module/message/model.php index 1a6df4425b..27d92e820f 100644 --- a/module/message/model.php +++ b/module/message/model.php @@ -47,10 +47,7 @@ class messageModel extends model $objectActions = array(); foreach($this->config->message->objectTypes as $objectType => $actions) { - foreach($actions as $action) - { - $objectActions[$objectType][$action] = str_replace($this->lang->webhook->trimWords, '', $this->lang->action->label->$action); - } + foreach($actions as $action) $objectActions[$objectType][$action] = $this->lang->message->label->$action; } return $objectActions; } diff --git a/module/my/view/task.html.php b/module/my/view/task.html.php index 3fa6265c48..58494e26f8 100644 --- a/module/my/view/task.html.php +++ b/module/my/view/task.html.php @@ -96,7 +96,7 @@ } else { - echo html::a($this->createLink('task', 'view', "taskID=$task->id", '', '', $task->project), $task->name, null, "style='color: $task->color' data-group='execution'"); + echo html::a($this->createLink('task', 'view', "taskID=$task->id", '', '', $task->project), $task->name, null, "style='color: $task->color'"); } ?> children)) echo '';?> @@ -104,7 +104,7 @@ systemMode == 'new'):?> createLink('project', 'index', "projectID=$task->project"), $task->projectName);?> - createLink('execution', 'task', "executionID=$task->execution"), $task->executionName, '', "data-group='execution'");?> + createLink('execution', 'task', "executionID=$task->execution"), $task->executionName, '');?> openedBy);?> @@ -171,7 +171,7 @@ systemMode == 'new'):?> createLink('project', 'view', "projectID=$child->project"), $child->projectName);?> - createLink('execution', 'task', "executionID=$child->project"), $child->executionName, '', "data-group='execution'");?> + createLink('execution', 'task', "executionID=$child->project"), $child->executionName, '');?> openedBy);?> @@ -208,8 +208,8 @@ common::printIcon('task', 'finish', "taskID=$child->id", $child, 'list', '', '', 'iframe', true, '', '', $child->project); common::printIcon('task', 'recordEstimate', "taskID=$child->id", $child, 'list', 'time', '', 'iframe', true, '', '', $child->project); - common::printIcon('task', 'edit', "taskID=$child->id", $child, 'list', '', '', '', '', 'data-group="execution"', '', $child->project); - common::printIcon('task', 'batchCreate', "executionID=$child->execution&storyID=$child->story&moduleID=$child->module&taskID=$child->id&iframe=true", $child, 'list', 'split', '', 'iframe', true, 'data-group="execution"', $this->lang->task->children, $child->project); + common::printIcon('task', 'edit', "taskID=$child->id", $child, 'list', '', '', '', '', '', '', $child->project); + common::printIcon('task', 'batchCreate', "executionID=$child->execution&storyID=$child->story&moduleID=$child->module&taskID=$child->id&iframe=true", $child, 'list', 'split', '', 'iframe', true, '', $this->lang->task->children, $child->project); } } ?> diff --git a/module/product/control.php b/module/product/control.php index 8dbc91141b..afa3f5c5d1 100644 --- a/module/product/control.php +++ b/module/product/control.php @@ -1266,6 +1266,7 @@ class product extends control if($_POST) { $this->product->manageLine(); + if(dao::isError()) die(js::error(dao::getError())); die(js::reload('parent')); } diff --git a/module/product/css/view.css b/module/product/css/view.css index e5c7c817ad..c1f6809c12 100644 --- a/module/product/css/view.css +++ b/module/product/css/view.css @@ -6,5 +6,5 @@ td.normal {color: #00da88;} td.acl {white-space: nowrap;} .row > .col-sm-12:first-child {padding-bottom: 20px;} .c-product ,.c-release ,.c-code {width: 100px !important;} -.c-openedBy ,.c-acl ,.c-common {width:110px !important;} -.c-bugs {width:120px !important;} +.c-openedBy ,.c-acl ,.c-prs ,.c-common {width:110px !important;} +.c-bugs ,.c-type {width:120px !important;} diff --git a/module/product/js/create.js b/module/product/js/create.js index ba883405c5..9c79b3cd40 100644 --- a/module/product/js/create.js +++ b/module/product/js/create.js @@ -61,12 +61,14 @@ function toggleLine(obj) { $('form .line-no-exist').removeClass('hidden'); $('form .line-exist').addClass('hidden'); + $('#line_chosen').addClass('hidden'); $line.attr('disabled', 'disabled'); } else { - $('form .line-exist').removeClass('hidden'); + $('#line').removeClass('hidden'); $('form .line-no-exist').addClass('hidden'); + $('#line_chosen').removeClass('hidden'); $line.removeAttr('disabled'); } } diff --git a/module/product/lang/en.php b/module/product/lang/en.php index b7ed529b21..77bc4261f4 100644 --- a/module/product/lang/en.php +++ b/module/product/lang/en.php @@ -87,6 +87,7 @@ $lang->product->programChangeTip = "The projects linked with this {$lang->pr $lang->product->notChangeProgramTip = "The {$lang->SRCommon} of {$lang->productCommon} has been linked to the following projects, please cancel the link before proceeding"; $lang->product->confirmChangeProgram = "The projects linked with this {$lang->productCommon}: %s is also linked with other products, whether to transfer projects to the modified program set."; $lang->product->changeProgramError = "The {$lang->SRCommon} of this {$lang->productCommon} has been linked to the project, please unlink it before proceeding"; +$lang->product->programEmpty = 'Program should not be empty!'; $lang->product->id = 'ID'; $lang->product->program = "Program"; diff --git a/module/product/lang/zh-cn.php b/module/product/lang/zh-cn.php index 7fc4525dd2..6d20c31f33 100644 --- a/module/product/lang/zh-cn.php +++ b/module/product/lang/zh-cn.php @@ -24,7 +24,7 @@ $lang->product->mine = '我负责'; $lang->product->other = '其他'; $lang->product->closed = '已关闭'; $lang->product->updateOrder = '排序'; -$lang->product->all = "{$lang->productCommon}列表"; +$lang->product->all = "所有{$lang->productCommon}"; $lang->product->manageLine = "维护{$lang->productCommon}线"; $lang->product->newLine = "新建{$lang->productCommon}线"; $lang->product->export = '导出数据'; @@ -87,6 +87,7 @@ $lang->product->programChangeTip = "如下项目只关联了该{$lang->produ $lang->product->notChangeProgramTip = "该{$lang->productCommon}的{$lang->SRCommon}已经关联到如下项目,请取消关联后再操作"; $lang->product->confirmChangeProgram = "如下项目既关联了该{$lang->productCommon}又关联了其他{$lang->productCommon},请确认是否继续关联该{$lang->productCommon},勾选后将取消与其他{$lang->productCommon}的关联关系,同时转移至新项目集下。"; $lang->product->changeProgramError = "该{$lang->productCommon}的{$lang->SRCommon}已经关联到项目,请取消关联后再操作"; +$lang->product->programEmpty = '项目集不能为空'; $lang->product->id = '编号'; $lang->product->program = "所属项目集"; diff --git a/module/product/model.php b/module/product/model.php index b7c7c80051..d3eb1d6139 100644 --- a/module/product/model.php +++ b/module/product/model.php @@ -810,7 +810,13 @@ class productModel extends model $maxOrder = $maxOrder ? $maxOrder : 0; foreach($data->modules as $id => $name) { - if(!$name) continue; + if(empty($name)) continue; + if($this->config->systemMode == 'new' and empty($data->programs[$id])) + { + dao::$errors[] = $this->lang->product->programEmpty; + return false; + } + $line->name = strip_tags(trim($name)); $line->root = $data->programs[$id]; diff --git a/module/product/view/browse.html.php b/module/product/view/browse.html.php index 8419b8a322..91d8e65fec 100644 --- a/module/product/view/browse.html.php +++ b/module/product/view/browse.html.php @@ -132,7 +132,7 @@ $projectIDParam = $isProjectStory ? "projectID=$projectID&" : '';
    app->rawModule != 'projectstory') common::printIcon('story', 'report', "productID=$productID&branchID=$branch&storyType=$storyType&browseType=$browseType&moduleID=$moduleID&chartType=pie", '', 'button', 'bar-chart muted'); ?>
    - +
    +productplan->projectNotEmpty)?> diff --git a/module/productplan/view/create.html.php b/module/productplan/view/create.html.php index c131aa5f34..7cb4809c12 100644 --- a/module/productplan/view/create.html.php +++ b/module/productplan/view/create.html.php @@ -27,7 +27,10 @@ productplan->parent;?> - title;?> + title;?> + begin);?> + end);?> + @@ -50,7 +53,7 @@ productplan->begin;?> -
    +
    diff --git a/module/productplan/view/edit.html.php b/module/productplan/view/edit.html.php index 8688cacca7..67c25b8426 100644 --- a/module/productplan/view/edit.html.php +++ b/module/productplan/view/edit.html.php @@ -14,6 +14,7 @@ execution->weekend);?> +
    diff --git a/module/program/view/edit.html.php b/module/program/view/edit.html.php index 06fe71d320..e04d68b45e 100644 --- a/module/program/view/edit.html.php +++ b/module/program/view/edit.html.php @@ -85,7 +85,7 @@ project->realBegan;?> - realBegan, "class='form-control form-date'");?> + realBegan) ? '' : $program->realBegan, "class='form-control form-date'");?> program->desc;?> diff --git a/module/project/lang/en.php b/module/project/lang/en.php index 3e26598ab8..b47bca0d24 100644 --- a/module/project/lang/en.php +++ b/module/project/lang/en.php @@ -172,7 +172,7 @@ $lang->project->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26' $lang->project->productNotEmpty = 'Please link products or create products.'; $lang->project->existProductName = 'Product name already exists.'; $lang->project->changeProgram = '%s > Change project'; -$lang->project->changeProgramTip = 'After modifying the project set, the products linked with the project will also modify the project set to which it belongs. Please confirm whether to modify it.'; +$lang->project->changeProgramTip = 'Once the program is edited, the product that is linked to this program will be changed. Do you want to edit it?'; $lang->project->linkedProjectsTip = 'Linked projects are as follows'; $lang->project->multiLinkedProductsTip = 'The following products linked to this project are also linked to other projects, please unlink before proceeding.'; $lang->project->linkStoryByPlanTips = "This action will associate all {$lang->SRCommon} under the selected plan to this project"; diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php index bcf5e8bc26..643d7f02d8 100644 --- a/module/project/lang/zh-cn.php +++ b/module/project/lang/zh-cn.php @@ -172,7 +172,7 @@ $lang->project->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26' $lang->project->productNotEmpty = '请关联产品或创建产品。'; $lang->project->existProductName = '产品名称已存在。'; $lang->project->changeProgram = '%s > 修改项目集'; -$lang->project->changeProgramTip = '修改项目集后,该项目关联的产品也会同时修改所属项目集,请确认是否修改。'; +$lang->project->changeProgramTip = '修改项目集后,该项目关联产品的项目集也会被修改,请确认是否修改。'; $lang->project->linkedProjectsTip = '关联的项目如下'; $lang->project->multiLinkedProductsTip = '该项目关联的如下产品还关联了其他项目,请取消关联后再操作'; $lang->project->linkStoryByPlanTips = "此操作会将所选计划下面的{$lang->SRCommon}全部关联到此项目中"; diff --git a/module/testreport/lang/en.php b/module/testreport/lang/en.php index 14dcbc879e..3297084c43 100644 --- a/module/testreport/lang/en.php +++ b/module/testreport/lang/en.php @@ -84,6 +84,7 @@ $lang->testreport->noTestTask = "No test requests for this {$lang->productCo $lang->testreport->noObjectID = "No test request or {$lang->executionCommon} is selected, so no report can be generated."; $lang->testreport->moreProduct = "Testing reports can only be generated for the same {$lang->productCommon}."; $lang->testreport->hiddenCase = "Hide %s use cases"; +$lang->testreport->goalTip = "Descriptive information about the {$lang->execution->common} of this build"; $lang->testreport->bugSummary = <<%s Bugs reported , diff --git a/module/testreport/lang/zh-cn.php b/module/testreport/lang/zh-cn.php index 6e609e0107..6d94df07b5 100644 --- a/module/testreport/lang/zh-cn.php +++ b/module/testreport/lang/zh-cn.php @@ -84,6 +84,7 @@ $lang->testreport->noTestTask = "该{$lang->productCommon}下还没有关联 $lang->testreport->noObjectID = "没有选定测试单或{$lang->executionCommon},无法创建测试报告!"; $lang->testreport->moreProduct = "只能对同一个{$lang->productCommon}生成测试报告。"; $lang->testreport->hiddenCase = "隐藏 %s 个用例"; +$lang->testreport->goalTip = "该版本所属{$lang->execution->common}的描述信息"; $lang->testreport->bugSummary = <<%s个Bug , diff --git a/module/testreport/view/create.html.php b/module/testreport/view/create.html.php index df18c2bd6f..f5cadb3e90 100644 --- a/module/testreport/view/create.html.php +++ b/module/testreport/view/create.html.php @@ -67,11 +67,16 @@ + desc)):?> testreport->goal?> - desc) ? $execution->desc : '';?> + + desc;?> + + + testreport->profile?> diff --git a/module/testreport/view/edit.html.php b/module/testreport/view/edit.html.php index 65d8589de0..2362b021d5 100644 --- a/module/testreport/view/edit.html.php +++ b/module/testreport/view/edit.html.php @@ -55,11 +55,16 @@ title, "class='form-control'")?> + desc)):?> testreport->goal?> - desc?> + + desc?> + + + testreport->profile?> diff --git a/module/testreport/view/view.html.php b/module/testreport/view/view.html.php index 98874cf91e..cddbd9bbd5 100644 --- a/module/testreport/view/view.html.php +++ b/module/testreport/view/view.html.php @@ -63,10 +63,13 @@ testreport->members?> members) as $member)echo zget($users, $member) . '   ';?> - + desc)):?> testreport->goal?> - desc?> + + desc?> + + diff --git a/module/todo/model.php b/module/todo/model.php index cb5f8782d3..5473018c90 100644 --- a/module/todo/model.php +++ b/module/todo/model.php @@ -515,7 +515,8 @@ class todoModel extends model $stmt = $this->dao->select('*')->from(TABLE_TODO) ->where('deleted')->eq('0') - ->andWhere('assignedTo', true)->eq($account) + ->beginIF($type == 'assignedtoother')->andWhere('account', true)->eq($account)->fi() + ->beginIF($type != 'assignedtoother')->andWhere('assignedTo', true)->eq($account)->fi() ->orWhere('finishedBy')->eq($account) ->markRight(1) ->beginIF($begin)->andWhere('date')->ge($begin)->fi() From 6219e2323b4b8e53cd95c2616b67e9d2ed67181b Mon Sep 17 00:00:00 2001 From: tianshujie Date: Wed, 8 Dec 2021 11:11:12 +0800 Subject: [PATCH 18/44] * Fix bug #17208. --- module/upgrade/js/mergeprogram.js | 3 +-- module/upgrade/model.php | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/module/upgrade/js/mergeprogram.js b/module/upgrade/js/mergeprogram.js index dde8bc3f65..133e75e72b 100644 --- a/module/upgrade/js/mergeprogram.js +++ b/module/upgrade/js/mergeprogram.js @@ -648,7 +648,7 @@ function toggleProgram(obj) $programs.attr('disabled', 'disabled'); $('.programStatus').show(); $('#programStatus').val('wait'); - $('#programStatus').removeAttr('disabled').trigger("chosen:updated"); + $('#programStatus').trigger("chosen:updated"); $('form #newProject0').prop('checked', true); $('form #newLine0').prop('checked', true); @@ -660,7 +660,6 @@ function toggleProgram(obj) $('form .pgm-exist').removeClass('hidden'); $('form .pgm-no-exist').addClass('hidden'); $('.programStatus').hide(); - $('#programStatus').attr('disabled', 'disabled'); if(!$('#newProgram0').prop('disabled')) { diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 8293c4e4f9..69bbef0ccd 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -4369,6 +4369,12 @@ class upgradeModel extends model $this->loadModel('action')->create('program', $programID, 'openedbysystem'); if($data->programStatus == 'closed') $this->loadModel('action')->create('program', $programID, 'closedbysystem'); } + else + { + $programID = $data->programID ? $data->programID : $data->programs; + $this->dao->update(TABLE_PROGRAM)->set('status')->eq($data->programStatus)->where('id')->eq($programID)->exec(); + if($data->programStatus == 'closed') $this->loadModel('action')->create('program', $programID, 'openedbysystem'); + } if(isset($data->newLine)) { From 6aca5f10c02fd3fca3a0ad33e79f8c2409437d94 Mon Sep 17 00:00:00 2001 From: xieqiyu Date: Wed, 8 Dec 2021 11:15:32 +0800 Subject: [PATCH 19/44] * Delete unused code. --- module/product/view/view.html.php | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/module/product/view/view.html.php b/module/product/view/view.html.php index 37ce60d20f..a9cc42d0f1 100644 --- a/module/product/view/view.html.php +++ b/module/product/view/view.html.php @@ -73,32 +73,20 @@ - code)):?> - - - - - code)):?> - - - - - code)):?> - + - From ddea40c56a38525040772ff564dab20c0fcccc3e Mon Sep 17 00:00:00 2001 From: xieqiyu Date: Wed, 8 Dec 2021 11:27:47 +0800 Subject: [PATCH 20/44] * Modify style. --- module/product/view/view.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/product/view/view.html.php b/module/product/view/view.html.php index a9cc42d0f1..5c1826193f 100644 --- a/module/product/view/view.html.php +++ b/module/product/view/view.html.php @@ -85,7 +85,7 @@ - + From 44bbe314405016e14a3324ecfe2e433ffc97e773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B9=BF=E6=98=8E?= Date: Wed, 8 Dec 2021 11:33:34 +0800 Subject: [PATCH 21/44] * Fix kanban bug. --- module/execution/js/kanban.js | 39 ++++++++++++++++------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/module/execution/js/kanban.js b/module/execution/js/kanban.js index 78024bbef0..e8c3247f50 100644 --- a/module/execution/js/kanban.js +++ b/module/execution/js/kanban.js @@ -283,30 +283,27 @@ function renderColumnCount($count, count, col) function renderHeaderCol($col, col, $header, kanban) { if(col.asParent) $col = $col.children('.kanban-header-col'); - if(!$col.children('.actions').length) + var $actions = $('
    '); + var printStoryButton = printTaskButton = printBugButton = false; + if(priv.canCreateStory || priv.canBatchCreateStory || priv.canLinkStory || priv.canLinkStoryByPlane) printStoryButton = true; + if(priv.canCreateTask || priv.canBatchCreateTask) printTaskButton = true; + if(priv.canCreateBug || priv.canBatchCreateBug) printBugButton = true; + + if((col.type === 'backlog' && printStoryButton) || (col.type === 'wait' && printTaskButton) || (col.type == 'unconfirmed' && printBugButton)) { - var $actions = $('
    '); - var printStoryButton = printTaskButton = printBugButton = false; - if(priv.canCreateStory || priv.canBatchCreateStory || priv.canLinkStory || priv.canLinkStoryByPlane) printStoryButton = true; - if(priv.canCreateTask || priv.canBatchCreateTask) printTaskButton = true; - if(priv.canCreateBug || priv.canBatchCreateBug) printBugButton = true; - - if((col.type === 'backlog' && printStoryButton) || (col.type === 'wait' && printTaskButton) || (col.type == 'unconfirmed' && printBugButton)) - { - $actions.append([ - '', - '', - '' - ].join('')); - } - $actions.append([ - '', - '', - '' + '', + '', + '' ].join('')); - $actions.appendTo($col); } + + $actions.append([ + '', + '', + '' + ].join('')); + $actions.appendTo($col); } /** @@ -612,7 +609,7 @@ function createColumnMenu(options) var items = []; if(priv.canEditName) items.push({label: executionLang.editName, url: $.createLink('kanban', 'setColumn', 'col=' + col.columnID + '&executionID=' + executionID), className: 'iframe', attrs: {'data-width': '500px'}}) if(priv.canSetWIP) items.push({label: executionLang.setWIP, url: $.createLink('kanban', 'setWIP', 'col=' + col.columnID + '&executionID=' + executionID), className: 'iframe', attrs: {'data-width': '500px'}}) - if(priv.canSortCards) items.push({label: executionLang.sortColumn, items: ['按ID倒序', '按ID顺序'], className: 'iframe', onClick: handleSortColCards}) + //if(priv.canSortCards) items.push({label: executionLang.sortColumn, items: ['按ID倒序', '按ID顺序'], className: 'iframe', onClick: handleSortColCards}) return items; } From 9db7d96139efd2e07fd0d5a24a45f35c77d483e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B9=BF=E6=98=8E?= Date: Wed, 8 Dec 2021 13:08:28 +0800 Subject: [PATCH 22/44] * Fix kanban bug. --- module/program/model.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/program/model.php b/module/program/model.php index 3115650a18..93cc167903 100644 --- a/module/program/model.php +++ b/module/program/model.php @@ -189,6 +189,7 @@ class programModel extends model ->where('t2.deleted')->eq(0) ->andWhere('t1.product')->in($productPairs) ->andWhere('t2.type')->eq('project') + ->andWhere('t2.status')->in('wait,doing') ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi() ->fetchGroup('product'); From 33971a4671a3ab153bf0beb444e59e2283d08726 Mon Sep 17 00:00:00 2001 From: tianshujie Date: Wed, 8 Dec 2021 13:36:22 +0800 Subject: [PATCH 23/44] * Fix bug #17243. --- module/upgrade/model.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 69bbef0ccd..e5a6565f0a 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -4687,16 +4687,28 @@ class upgradeModel extends model $projectProducts = $this->dao->select('product,branch,plan')->from(TABLE_PROJECTPRODUCT) ->where('project')->in($sprintIdList) ->andWhere('product')->in($productIdList) - ->fetchAll('product'); + ->fetchGroup('product', 'branch'); + foreach($productIdList as $productID) { $data = new stdclass(); $data->project = $projectID; $data->product = $productID; - $data->plan = ($_POST['projectType'] == 'project' and isset($projectProducts[$productID])) ? $projectProducts[$productID]->plan : 0; - $data->branch = isset($projectProducts[$productID]) ? $projectProducts[$productID]->branch : 0; - - $this->dao->replace(TABLE_PROJECTPRODUCT)->data($data)->exec(); + if(isset($projectProducts[$productID])) + { + foreach($projectProducts[$productID] as $branchID => $projectProduct) + { + $data->plan = ($_POST['projectType'] == 'project' and isset($projectProduct->plan)) ? $projectProduct->plan : 0; + $data->branch = $branchID; + $this->dao->replace(TABLE_PROJECTPRODUCT)->data($data)->exec(); + } + } + else + { + $data->plan = 0; + $data->branch = 0; + $this->dao->replace(TABLE_PROJECTPRODUCT)->data($data)->exec(); + } } } From eb3005cb9ba87d60069711d931cccfe1720a5c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B9=BF=E6=98=8E?= Date: Wed, 8 Dec 2021 13:47:24 +0800 Subject: [PATCH 24/44] * Fix program kanban error. --- module/program/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/program/model.php b/module/program/model.php index 93cc167903..b8eb49365d 100644 --- a/module/program/model.php +++ b/module/program/model.php @@ -183,7 +183,7 @@ class programModel extends model ->fetchGroup('product'); /* Get all products linked projects. */ - $projectGroup = $this->dao->select('t1.product, t2.id, t2.name, t2.status, t2.end')->from(TABLE_PROJECTPRODUCT)->alias('t1') + $projectGroup = $this->dao->select('DISTINCT t1.product, t2.id, t2.name, t2.status, t2.end')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2') ->on('t1.project = t2.id') ->where('t2.deleted')->eq(0) From 83cc442fc30052b019cdd570c93b98853e228acb Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Wed, 8 Dec 2021 15:29:13 +0800 Subject: [PATCH 25/44] * Fix bug #17261. --- module/tree/control.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/module/tree/control.php b/module/tree/control.php index 82c0f093d6..7100a8c09f 100644 --- a/module/tree/control.php +++ b/module/tree/control.php @@ -170,8 +170,6 @@ class tree extends control elseif($from == 'project') { $this->lang->navGroup->tree = 'project'; - $this->lang->tree->menu = $this->lang->project->menu; - $this->lang->tree->menuOrder = $this->lang->project->menuOrder; /* The project parameter needs to be present when the tree module belongs to the project grouping. */ if($this->session->docList && $this->session->project && strpos($this->session->docList, 'project') === false) $this->session->set('docList', $this->session->docList . '?project=' . $this->session->project, 'project'); From 9f168793fee9ac61e0f727e0880ade11e34c5c43 Mon Sep 17 00:00:00 2001 From: xieqiyu Date: Wed, 8 Dec 2021 16:00:28 +0800 Subject: [PATCH 26/44] * Fix bug#17267. --- module/testreport/control.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/testreport/control.php b/module/testreport/control.php index 52b14a3fa7..6960f3d3ae 100644 --- a/module/testreport/control.php +++ b/module/testreport/control.php @@ -189,7 +189,7 @@ class testreport extends control $taskPairs = array(); $scopeAndStatus[0] = 'local'; $scopeAndStatus[1] = 'totalStatus'; - $tasks = $this->testtask->getProductTasks($productID, empty($objectID) ? 0 : $task->branch, 'id_desc', null, $scopeAndStatus); + $tasks = $this->testtask->getProductTasks($productID, empty($objectID) ? 'all' : $task->branch, 'id_desc', null, $scopeAndStatus); foreach($tasks as $testTask) { if($testTask->build == 'trunk') continue; From d33c07117792b10a71e0a7f2cdaea89e1ecdfafd Mon Sep 17 00:00:00 2001 From: tianshujie Date: Wed, 8 Dec 2021 16:10:09 +0800 Subject: [PATCH 27/44] * Fix bug #17259. --- module/testcase/control.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/module/testcase/control.php b/module/testcase/control.php index 085b8ae52d..1f938d3fae 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -548,10 +548,6 @@ class testcase extends control $storyPairs = $this->loadModel('story')->getProductStoryPairs($productID, $branch === 'all' ? 0 : $branch); $storyPairs += $storyID ? array($storyID => $story->id . ':' . $story->title) : array(''); - /* Set module option menu. */ - $moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch === 'all' ? 0 : $branch); - $moduleOptionMenu['ditto'] = $this->lang->testcase->ditto; - /* Set custom. */ $product = $this->product->getById($productID); foreach(explode(',', $this->config->testcase->customBatchCreateFields) as $field) @@ -589,6 +585,11 @@ class testcase extends control $branches = $this->loadModel('branch')->getPairs($productID, 'active'); } + /* Set module option menu. */ + $moduleOptionMenu = $this->tree->getOptionMenu($productID, 'case', 0, $branch === 'all' ? 0 : $branch); + $moduleOptionMenu['ditto'] = $this->lang->testcase->ditto; + + $this->view->customFields = $customFields; $this->view->showFields = $showFields; From 4be580309c864c3389b25cdb902b50b4d3c292dc Mon Sep 17 00:00:00 2001 From: xieqiyu Date: Wed, 8 Dec 2021 16:13:42 +0800 Subject: [PATCH 28/44] * Fix bug#17251. --- module/productplan/control.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/productplan/control.php b/module/productplan/control.php index dd6e49e9a9..5a1721f66e 100644 --- a/module/productplan/control.php +++ b/module/productplan/control.php @@ -298,7 +298,7 @@ class productplan extends control if($plan->parent == '-1') $this->view->childrenPlans = $this->productplan->getChildren($plan->id); $this->loadModel('datatable'); - $this->view->modulePairs = $this->loadModel('tree')->getOptionMenu($plan->product, 'story'); + $this->view->modulePairs = $this->loadModel('tree')->getOptionMenu($plan->product, 'story', 0, $plan->branch); $this->view->title = "PLAN #$plan->id $plan->title/" . zget($products, $plan->product, ''); $this->view->position[] = $this->lang->productplan->view; $this->view->planStories = $planStories; From b2336fc180618f2ad306015610c82b284e33c431 Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Wed, 8 Dec 2021 16:20:08 +0800 Subject: [PATCH 29/44] * Fix bug #17263. --- module/doc/control.php | 10 ++++++++-- module/doc/js/editlib.js | 5 +++-- module/doc/model.php | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/module/doc/control.php b/module/doc/control.php index f0e7a12db7..4d9c57ef6e 100644 --- a/module/doc/control.php +++ b/module/doc/control.php @@ -201,13 +201,19 @@ class doc extends control $actionID = $this->action->create('docLib', $libID, 'edited'); $this->action->logHistory($actionID, $changes); } - $docLib = $this->doc->getLibById($libID); + $docLib = $this->doc->getLibById($libID); + $objectID = 0; + if(strpos('product,project,execution', $docLib->type) !== false) + { + $libType = $docLib->type; + $objectID = $docLib->$libType; + } $hasLibPriv = $this->doc->checkPrivLib($docLib) ? 1 : 0; $response['message'] = $this->lang->saveSuccess; $response['result'] = 'success'; $response['closeModal'] = true; - $response['callback'] = "redirectParentWindow($hasLibPriv, $libID)"; + $response['callback'] = "redirectParentWindow($hasLibPriv, $libID, $objectID)"; return $this->send($response); } diff --git a/module/doc/js/editlib.js b/module/doc/js/editlib.js index 0b25da1454..4c3b0a7242 100644 --- a/module/doc/js/editlib.js +++ b/module/doc/js/editlib.js @@ -8,11 +8,12 @@ $(function() * * @param int hasLibPriv * @param int libID + * @param int objectID * @access public * @return void */ -function redirectParentWindow(hasLibPriv, libID) +function redirectParentWindow(hasLibPriv, libID, objectID) { - var link = hasLibPriv ? createLink('doc', 'tableContents', 'type=' + libType + '&objectID=0&libID=' + libID) : createLink('doc', 'tableContents', 'type=' + libType); + var link = hasLibPriv ? createLink('doc', 'tableContents', 'type=' + libType + '&objectID=' + objectID + '&libID=' + libID) : createLink('doc', 'tableContents', 'type=' + libType); parent.location.href = link; } diff --git a/module/doc/model.php b/module/doc/model.php index bcf3982815..ad6020d82a 100644 --- a/module/doc/model.php +++ b/module/doc/model.php @@ -2562,6 +2562,7 @@ EOT; if($this->app->tab == 'doc') $this->app->rawMethod = $type; $object = $this->dao->select('id,name,status')->from($table)->where('id')->eq($objectID)->fetch(); + if(empty($object)) die(js::locate(helper::createLink($type, 'create'))); } From 147470bf292019a84074ec99fa3d66abaa077a9b Mon Sep 17 00:00:00 2001 From: mayue Date: Wed, 8 Dec 2021 16:31:24 +0800 Subject: [PATCH 30/44] * Fix bug #17248,17246. --- module/product/js/create.js | 2 +- module/story/model.php | 3 +-- module/task/control.php | 16 ++++++++++++---- module/task/js/batchcreate.js | 2 +- module/task/js/create.js | 2 +- module/task/view/batchcreate.html.php | 7 +++---- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/module/product/js/create.js b/module/product/js/create.js index 9c79b3cd40..3c83f0867d 100644 --- a/module/product/js/create.js +++ b/module/product/js/create.js @@ -1,6 +1,6 @@ $(function() { - if(programID) $('#line_chosen').addClass('hidden'); + if(programID || systemMode) $('#line_chosen').addClass('hidden'); $('#lineName').css('border-left-color', ''); }) /** diff --git a/module/story/model.php b/module/story/model.php index da911b2a70..0875f2a6f5 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -2706,12 +2706,11 @@ class storyModel extends model ->where('t1.project')->eq((int)$executionID) ->andWhere('t2.deleted')->eq(0) ->beginIF($productID)->andWhere('t2.product')->eq((int)$productID)->fi() - ->beginIF($branch != 'all')->andWhere('t2.branch')->eq($branch)->fi() + ->beginIF($branch !== 'all')->andWhere('t2.branch')->eq($branch)->fi() ->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi() ->beginIF($status == 'unclosed')->andWhere('t2.status')->ne('closed')->fi() ->orderBy('t1.`order` desc') ->fetchAll(); - return empty($stories) ? array() : $this->formatStories($stories, $type); } diff --git a/module/task/control.php b/module/task/control.php index 52adac1132..24a1e4dca6 100644 --- a/module/task/control.php +++ b/module/task/control.php @@ -312,10 +312,18 @@ class task extends control $this->view->customFields = $customFields; $this->view->showFields = $this->config->task->custom->batchCreateFields; + $story = $this->story->getByID($storyID); + if($story and empty($moduleID)) + { + $moduleID = $story->module; + $stories = $this->story->getExecutionStoryPairs($executionID, 0, 'all', $moduleID, 'short'); + } + else + { + $stories = $this->story->getExecutionStoryPairs($executionID, 0, 'all', 0, 'short'); + } - $stories = $this->story->getExecutionStoryPairs($executionID, 0, 0, 0, 'short'); - $members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted'); - + $members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted'); $showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : ''; $modules = $this->loadModel('tree')->getTaskOptionMenu($executionID, 0, 0, $showAllModule ? 'allModule' : ''); @@ -333,7 +341,7 @@ class task extends control $this->view->modules = $modules; $this->view->parent = $taskID; $this->view->storyID = $storyID; - $this->view->story = $this->story->getByID($storyID); + $this->view->story = $story; $this->view->storyTasks = $this->task->getStoryTaskCounts(array_keys($stories), $executionID); $this->view->members = $members; $this->view->moduleID = $moduleID; diff --git a/module/task/js/batchcreate.js b/module/task/js/batchcreate.js index 2b11382752..1c4416ea19 100755 --- a/module/task/js/batchcreate.js +++ b/module/task/js/batchcreate.js @@ -38,7 +38,7 @@ function toggleCheck(obj) /* Get select of stories.*/ function setStories(moduleID, executionID, num) { - link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=0&moduleID=' + moduleID + '&storyID=0&num=' + num + '&type=short'); + link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=all&moduleID=' + moduleID + '&storyID=0&num=' + num + '&type=short'); $.get(link, function(stories) { var storyID = $('#story' + num).val(); diff --git a/module/task/js/create.js b/module/task/js/create.js index d72685e7bc..691e4817e5 100644 --- a/module/task/js/create.js +++ b/module/task/js/create.js @@ -273,7 +273,7 @@ function loadModuleRelated() /* Get select of stories.*/ function setStories(moduleID, executionID) { - link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=0&moduleID=' + moduleID); + link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=all&moduleID=' + moduleID); $.get(link, function(stories) { var storyID = $('#story').val(); diff --git a/module/task/view/batchcreate.html.php b/module/task/view/batchcreate.html.php index 905c159823..723c676fff 100644 --- a/module/task/view/batchcreate.html.php +++ b/module/task/view/batchcreate.html.php @@ -91,18 +91,17 @@ $currentStory = $storyID; $type = ''; $member = ''; - $module = $story ? $story->module : $moduleID; } else { - $currentStory = $type = $member = $module = 'ditto'; + $currentStory = $type = $member = $moduleID = 'ditto'; } ?>
    type != 'ops'):?> @@ -171,7 +170,7 @@ From 5287e874102af50fcd1a1da9dd6fc9296b3d1d79 Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Wed, 8 Dec 2021 17:52:19 +0800 Subject: [PATCH 37/44] * Fix bug #17276. --- module/testsuite/model.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/testsuite/model.php b/module/testsuite/model.php index 360314084a..ae73a101fd 100644 --- a/module/testsuite/model.php +++ b/module/testsuite/model.php @@ -60,6 +60,7 @@ class testsuiteModel extends model public function create($productID) { $suite = fixer::input('post') + ->trim('name') ->stripTags($this->config->testsuite->editor->create['id'], $this->config->allowedTags) ->setIF($this->config->systemMode == 'new' and $this->lang->navGroup->testsuite != 'qa', 'project', $this->session->project) ->add('product', (int)$productID) From c7d5310496f8b67bed4dba8dc0ee42a4d377e67f Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Thu, 9 Dec 2021 09:53:54 +0800 Subject: [PATCH 38/44] * Fix bug #16792. --- module/program/control.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/program/control.php b/module/program/control.php index 1cf3695725..03d45e0b1f 100644 --- a/module/program/control.php +++ b/module/program/control.php @@ -513,7 +513,7 @@ class program extends control $this->view->dept = $dept; $this->view->depts = array('' => '') + $this->dept->getOptionMenu(); $this->view->stakeholders = $this->program->getStakeholders($programID, 't1.id_desc'); - $this->view->parentStakeholders = $this->program->getStakeholdersByList($parentIdList); + $this->view->parentStakeholders = $this->program->getStakeholdersByPrograms($parentIdList); $this->display(); } From 2030bbd11c6d9ed2767b584a6f55818b703a8a38 Mon Sep 17 00:00:00 2001 From: tianshujie Date: Thu, 9 Dec 2021 10:04:46 +0800 Subject: [PATCH 39/44] * Fix bug #17173. --- module/project/view/manageproducts.html.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/module/project/view/manageproducts.html.php b/module/project/view/manageproducts.html.php index cabdf182d6..5ce5182fc3 100644 --- a/module/project/view/manageproducts.html.php +++ b/module/project/view/manageproducts.html.php @@ -58,12 +58,13 @@
    '>
    - ";?> + ";?>
    - +
    + From 1517a800e21c28e0d36237e4baf7e6b59581394a Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Thu, 9 Dec 2021 10:12:08 +0800 Subject: [PATCH 40/44] * Fix an error for lang item. --- module/program/view/createstakeholder.html.php | 4 ++-- module/program/view/managemembers.html.php | 4 ++-- module/project/view/createstakeholder.html.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/module/program/view/createstakeholder.html.php b/module/program/view/createstakeholder.html.php index 51e697cbee..7fa676ff05 100644 --- a/module/program/view/createstakeholder.html.php +++ b/module/program/view/createstakeholder.html.php @@ -19,8 +19,8 @@ createLink('program', 'craetestakeholder', "programID={$programID}"), " {$lang->program->createStakeholder}");?>
    - project->selectDept?> - project->selectDeptTitle}'");?> + execution->selectDept?> + execution->selectDeptTitle}'");?>
    parent):?> createLink('program', 'createStakeholder', "programID=$programID&dept=&parent=$path"), $lang->program->importStakeholder, '', 'class="btn btn-primary"');?> diff --git a/module/program/view/managemembers.html.php b/module/program/view/managemembers.html.php index f8e96e254d..f2e4e9baa1 100644 --- a/module/program/view/managemembers.html.php +++ b/module/program/view/managemembers.html.php @@ -7,8 +7,8 @@ createLink('project', 'managemembers', "projectID={$project->id}"), " {$lang->project->manageMembers}");?>
    - project->selectDept?> - project->selectDeptTitle}'");?> + execution->selectDept?> + execution->selectDeptTitle}'");?>
    diff --git a/module/project/view/createstakeholder.html.php b/module/project/view/createstakeholder.html.php index fe64311604..969b0f7322 100644 --- a/module/project/view/createstakeholder.html.php +++ b/module/project/view/createstakeholder.html.php @@ -19,8 +19,8 @@ createLink('project', 'craetestakeholder', "projectID={$projectID}"), " {$lang->project->createStakeholder}");?>
    - project->selectDept?> - project->selectDeptTitle}'");?> + execution->selectDept?> + execution->selectDeptTitle}'");?>
    parent):?> createLink('project', 'createStakeholder', "projectID=$projectID&dept=&parent=$path"), $lang->project->importStakeholder, '', 'class="btn btn-primary"');?> From 8298f778e73dd73cec6fa47dfae4bf7d4682a482 Mon Sep 17 00:00:00 2001 From: xieqiyu Date: Thu, 9 Dec 2021 10:25:23 +0800 Subject: [PATCH 41/44] * Fix bug#17251. --- module/productplan/control.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/productplan/control.php b/module/productplan/control.php index 5a1721f66e..897c5199c7 100644 --- a/module/productplan/control.php +++ b/module/productplan/control.php @@ -298,7 +298,7 @@ class productplan extends control if($plan->parent == '-1') $this->view->childrenPlans = $this->productplan->getChildren($plan->id); $this->loadModel('datatable'); - $this->view->modulePairs = $this->loadModel('tree')->getOptionMenu($plan->product, 'story', 0, $plan->branch); + $this->view->modulePairs = $this->loadModel('tree')->getOptionMenu($plan->product, 'story', 0, 'all'); $this->view->title = "PLAN #$plan->id $plan->title/" . zget($products, $plan->product, ''); $this->view->position[] = $this->lang->productplan->view; $this->view->planStories = $planStories; From 12ce541d9e7778fd3836ab264b8215d569b8ed84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B9=BF=E6=98=8E?= Date: Thu, 9 Dec 2021 10:50:05 +0800 Subject: [PATCH 42/44] * Code for task #45818. --- config/zentaopms.php | 2 + db/update16.0.beta1.sql | 13 ++- db/zentao.sql | 13 ++- module/execution/control.php | 2 +- module/execution/model.php | 2 +- module/kanban/config.php | 3 + module/kanban/lang/zh-cn.php | 9 +- module/kanban/model.php | 210 ++++++++++++++++++++++++++++++++++- 8 files changed, 236 insertions(+), 18 deletions(-) diff --git a/config/zentaopms.php b/config/zentaopms.php index 1a77b84d0f..d8d93022cf 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -240,6 +240,7 @@ define('TABLE_REPOBRANCH', '`' . $config->db->prefix . 'repobranch`'); define('TABLE_KANBANSPACE', '`' . $config->db->prefix . 'kanbanspace`'); define('TABLE_KANBANLANE', '`' . $config->db->prefix . 'kanbanlane`'); define('TABLE_KANBANCOLUMN', '`' . $config->db->prefix . 'kanbancolumn`'); +define('TABLE_KANBANORDER', '`' . $config->db->prefix . 'kanbanorder`'); if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`'); if(!defined('TABLE_PROJECTSPEC')) define('TABLE_PROJECTSPEC', '`' . $config->db->prefix . 'projectspec`'); @@ -281,6 +282,7 @@ $config->objectTables['mr'] = TABLE_MR; $config->objectTables['kanbanspace'] = TABLE_KANBANSPACE; $config->objectTables['kanbancolumn'] = TABLE_KANBANCOLUMN; $config->objectTables['kanbanlane'] = TABLE_KANBANLANE; +$config->objectTables['kanbanorder'] = TABLE_KANBANORDER; $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme'); diff --git a/db/update16.0.beta1.sql b/db/update16.0.beta1.sql index aaae270517..12e9d024b0 100644 --- a/db/update16.0.beta1.sql +++ b/db/update16.0.beta1.sql @@ -84,22 +84,25 @@ CREATE TABLE `zt_kanbancard` ( -- DROP TABLE IF EXISTS `zt_kanbangroup`; CREATE TABLE `zt_kanbangroup` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) unsigned NOT NULL, `region` mediumint(8) unsigned NOT NULL, - `column` mediumint(8) unsigned NOT NULL, - `lane` mediumint(8) unsigned NOT NULL, - PRIMARY KEY (`column`, `lane`) + PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- DROP TABLE IF EXISTS `zt_kanbanorder`; CREATE TABLE `zt_kanbanorder` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, - `parent` mediumint(8) unsigned NOT NULL, + `parentID` mediumint(8) unsigned NOT NULL, `parentType` varchar(20) NOT NULL, - `type` varchar(20) NOT NULL, + `objectID` mediumint(8) unsigned NOT NULL, + `objectType` varchar(20) NOT NULL, `account` varchar(30) NOT NULL, `order` int NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; ALTER TABLE `zt_kanbanlane` ADD COLUMN `region` mediumint(8) unsigned NOT NULL AFTER `type`; +ALTER TABLE `zt_kanbanlane` ADD COLUMN `group` mediumint(8) unsigned NOT NULL AFTER `region`; ALTER TABLE `zt_kanbancolumn` ADD COLUMN `region` mediumint(8) unsigned NOT NULL AFTER `type`; +ALTER TABLE `zt_kanbancolumn` ADD COLUMN `group` mediumint(8) unsigned NOT NULL AFTER `region`; diff --git a/db/zentao.sql b/db/zentao.sql index c1daf4fa8b..820e0beb02 100644 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -725,17 +725,18 @@ CREATE TABLE `zt_kanbancard` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- DROP TABLE IF EXISTS `zt_kanbangroup`; CREATE TABLE `zt_kanbangroup` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) unsigned NOT NULL, `region` mediumint(8) unsigned NOT NULL, - `column` mediumint(8) unsigned NOT NULL, - `lane` mediumint(8) unsigned NOT NULL, - PRIMARY KEY (`column`, `lane`) + PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- DROP TABLE IF EXISTS `zt_kanbanorder`; CREATE TABLE `zt_kanbanorder` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, - `parent` mediumint(8) unsigned NOT NULL, + `parentID` mediumint(8) unsigned NOT NULL, `parentType` varchar(20) NOT NULL, - `type` varchar(20) NOT NULL, + `objectID` mediumint(8) NOT NULL, + `objectType` varchar(20) NOT NULL, `account` varchar(30) NOT NULL, `order` int NOT NULL, PRIMARY KEY (`id`) @@ -746,6 +747,7 @@ CREATE TABLE IF NOT EXISTS `zt_kanbanlane` ( `execution` mediumint(8) NOT NULL DEFAULT '0', `type` char(30) NOT NULL, `region` mediumint(8) unsigned NOT NULL, + `group` mediumint(8) unsigned NOT NULL, `groupby` char(30) NOT NULL, `extra` char(30) NOT NULL, `name` varchar(255) NOT NULL DEFAULT '', @@ -759,6 +761,7 @@ CREATE TABLE IF NOT EXISTS `zt_kanbanlane` ( CREATE TABLE IF NOT EXISTS `zt_kanbancolumn` ( `id` int(8) NOT NULL AUTO_INCREMENT, `lane` mediumint(8) NOT NULL DEFAULT '0', + `group` mediumint(8) NOT NULL DEFAULT '0', `parent` mediumint(8) NOT NULL DEFAULT '0', `type` char(30) NOT NULL, `region` mediumint(8) unsigned NOT NULL, diff --git a/module/execution/control.php b/module/execution/control.php index 62b5adcacf..dc878c71ac 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -1968,7 +1968,7 @@ class execution extends control $kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType, $groupBy); if(empty($kanbanGroup)) { - $this->kanban->createLanes($executionID, $browseType, $groupBy); + $this->kanban->createExecutionLane($executionID, $browseType, $groupBy); $kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy); } diff --git a/module/execution/model.php b/module/execution/model.php index c6df3884a1..c38420e3a0 100644 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -381,7 +381,7 @@ class executionModel extends model $creatorExists = false; $teamMembers = array(); - $this->loadModel('kanban')->createLanes($executionID); + $this->loadModel('kanban')->createExecutionLane($executionID); /* Save order. */ $this->dao->update(TABLE_EXECUTION)->set('`order`')->eq($executionID * 5)->where('id')->eq($executionID)->exec(); diff --git a/module/kanban/config.php b/module/kanban/config.php index 5fb2a5f1dc..3b3fe9a058 100644 --- a/module/kanban/config.php +++ b/module/kanban/config.php @@ -2,6 +2,9 @@ global $lang; $config->kanban = new stdclass(); +$config->kanban->require = new stdclass(); +$config->kanban->require->createregion = 'name'; + $config->kanban->setwip = new stdclass(); $config->kanban->setlane = new stdclass(); $config->kanban->setlaneColumn = new stdclass(); diff --git a/module/kanban/lang/zh-cn.php b/module/kanban/lang/zh-cn.php index 824c79666c..fe7d2ccb40 100644 --- a/module/kanban/lang/zh-cn.php +++ b/module/kanban/lang/zh-cn.php @@ -140,9 +140,16 @@ $lang->kanbanspace->featureBar['my'] = '我的空间'; $lang->kanbanspace->featureBar['other'] = '其他空间'; $lang->kanbanspace->featureBar['closed'] = '已关闭'; +$lang->kanban->defaultColumn = array('未开始', '进行中', '已完成', '已关闭'); + $lang->kanbancolumn = new stdclass(); $lang->kanbancolumn->name = $lang->kanban->columnName; $lang->kanbancolumn->limit = $lang->kanban->WIPCount; $lang->kanbanlane = new stdclass(); -$lang->kanbanlane->name = $lang->kanban->laneName; +$lang->kanbanlane->name = $lang->kanban->laneName; +$lang->kanbanlane->default = '默认泳道'; + +$lang->kanbanregion = new stdclass(); +$lang->kanbanregion->name = '看板区域'; +$lang->kanbanregion->default = '默认区域'; diff --git a/module/kanban/model.php b/module/kanban/model.php index 1406efa0d2..eaf58d6c35 100644 --- a/module/kanban/model.php +++ b/module/kanban/model.php @@ -13,6 +13,206 @@ getByID($kanbanID); + + $function = $from == 'default' ? 'createDefaultRegion' : 'createRegion'; + $this->$function($kanban); + if(dao::isError()) return false; + + $groupID = $this->createGroup($kanban->id, $kanban->region); + if(dao::isError()) return false; + + $this->createDefaultLane($kanban, $groupID); + if(dao::isError()) return false; + + $this->createDefaultColumns($kanban, $groupID); + if(dao::isError()) return false; + } + + /** + * Create a kanban group. + * + * @param int $kanbanID + * @param int $regionID + * @access public + * @return int + */ + public function createGroup($kanbanID, $regionID) + { + $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANORDER) + ->where('type')->eq('group') + ->andWhere('parent')->eq($regionID) + ->andWhere('parentType')->eq('region') + ->andWhere('account')->eq('') + ->fetch('maxOrder'); + + $order = $maxOrder ? $maxOrder + 1 : 1; + + $group = new stdclass(); + $group->kanban = $kanbanID; + $group->region = $regionID; + + $this->dao->insert(TABLE_KANBANGROUP)->data($group)->autoCheck()->exec(); + if(dao::isError()) return false; + + $groupID = $this->dao->lastInsertID(); + $this->saveOrder($regionID, 'region', $groupID, 'group', '', $order); + + return $groupID; + } + + /** + * Create a default kanban region. + * + * @param object $kanban + * @access public + * @return int + */ + public function createDefaultRegion($kanban) + { + $region = new stdclass(); + $region->name = $this->lang->kanbanregion->default; + $region->kanban = $kanban->id; + $region->space = $kanban->space; + $region->createdBy = $this->app->user->account; + $region->createdDate = helper::today(); + + $this->dao->insert(TABLE_KANBANREGION)->data($region)->exec(); + return $this->createRegion($kanban, $region); + } + + /** + * Create a new region. + * + * @param object $kanban + * @param object $region + * @access public + * @return int + */ + public function createRegion($kanban, $region = null) + { + $account = $this->app->user->account; + $order = 1; + + if(!$region) + { + $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANORDER) + ->where('type')->eq('region') + ->andWhere('parent')->eq($kanban->id) + ->andWhere('parentType')->eq('kanban') + ->andWhere('account')->eq('') + ->fetch('maxOrder'); + + $order = $maxOrder + 1; + + $region = fixer::input('post') + ->add('kanban', $kanban->id) + ->add('space', $kanban->space) + ->add('createdBy', $account) + ->add('createdDate', helper::today()) + ->get(); + } + + $this->dao->insert(TABLE_KANBANREGION)->data($region) + ->batchCheck($this->config->kanban->require->createregion, 'notempty') + ->check('name', 'unique', "kanban = {$kanban->id} AND deleted = '0'") + ->autoCheck() + ->exec(); + + $regionID = $this->dao->lastInsertID(); + $this->loadModel('action')->create('kanbanRegion', $regionID, 'Created'); + $this->saveOrder($kanban->id, 'kanban', $regionID, 'region', $account, $order); + + return $regionID; + } + + /** + * Create default lane. + * + * @param object $kanban + * @access public + * @return int + */ + public function createDefaultLane($kanban) + { + $lane = new stdclass(); + $lane->name = $this->lang->kanbanlane->default; + $lane->region = $kanban->region; + $lane->type = 'common'; + $lane->lastEditedTime = helper::now(); + + $this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec(); + $laneID = $this->dao->lastInsertId(); + + $this->saveOrder($kanban->region, 'region', $laneID, 'lane', '', 1); + return $laneID; + } + + /** + * Create default kanban columns. + * + * @param int $kanban + * @param int $laneID + * @access public + * @return void + */ + public function createDefaultColumns($kanban, $laneID) + { + foreach($this->lang->kanban->defaultColumn as $columnName) + { + $column = new stdclass(); + $column->region = $kanban->region; + $column->name = $columnName; + $column->lane = $laneID; + + $this->dao->insert(TABLE_KANBANCOLUMN)->data($column)->exec(); + $columnID = $this->dao->lastInsertId(); + + $kanbanGroup = new stdclass(); + $kanbanGroup->region = $kanban->region; + $kanbanGroup->column = $columnID; + $kanbanGroup->lane = $laneID; + + $this->dao->insert(TABLE_KANBANGROUP)->data($kanbanGroup)->exec(); + } + } + + /** + * Save kanban object order. + * + * @param int $parentID + * @param string $parentType + * @param int $objectID + * @param string $objectType + * @param string $account + * @param int $order + * @access public + * @return void + */ + public function saveOrder($parentID, $parentType, $objectID, $objectType, $account, $order) + { + $kanbanOrder = new stdclass(); + $kanbanOrder->parentID = $parent; + $kanbanOrder->parentType = $parentType; + $kanbanOrder->objectID = $objectID; + $kanbanOrder->objectType = $objectType; + $kanbanOrder->account = $account; + $kanbanOrder->order = $order; + + $this->dao->insert(TABLE_KANBANORDER)->data($kanbanOrder)->exec(); + return !dao::isError(); + } + /** * Get Kanban by execution id. * @@ -206,7 +406,7 @@ class kanbanModel extends model * @access public * @return void */ - public function createLanes($executionID, $type = 'all', $groupBy = 'default') + public function createExecutionLane($executionID, $type = 'all', $groupBy = 'default') { if($groupBy == 'default' or $type == 'all') { @@ -217,7 +417,7 @@ class kanbanModel extends model $this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec(); $laneID = $this->dao->lastInsertId(); - $this->createColumns($laneID, $type, $executionID); + $this->createExecutionColumns($laneID, $type, $executionID); } } else @@ -266,7 +466,7 @@ class kanbanModel extends model $this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec(); $laneID = $this->dao->lastInsertId(); - $this->createColumns($laneID, $type, $executionID, $groupBy, $groupKey); + $this->createExecutionColumns($laneID, $type, $executionID, $groupBy, $groupKey); } } } @@ -282,7 +482,7 @@ class kanbanModel extends model * @access public * @return void */ - public function createColumns($laneID, $type, $executionID, $groupBy = '', $groupValue = '') + public function createExecutionColumns($laneID, $type, $executionID, $groupBy = '', $groupValue = '') { $objects = array(); @@ -615,7 +815,7 @@ class kanbanModel extends model $this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec(); $laneID = $this->dao->lastInsertId(); - $this->createColumns($laneID, $type, $executionID, $groupBy, $groupKey); + $this->createExecutionColumns($laneID, $type, $executionID, $groupBy, $groupKey); } $this->resetLaneOrder($executionID, $type, $groupBy); From 9824f4c6ac18631058adc5e44cf3a711e05a5e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B9=BF=E6=98=8E?= Date: Thu, 9 Dec 2021 14:11:49 +0800 Subject: [PATCH 43/44] * Code for task #45818. --- config/zentaopms.php | 6 + module/kanban/control.php | 16 + module/kanban/css/view.css | 76 +++ module/kanban/js/view.js | 848 +++++++++++++++++++++++++++++++ module/kanban/lang/zh-cn.php | 4 + module/kanban/model.php | 305 ++++++++++- module/kanban/view/view.html.php | 55 ++ 7 files changed, 1284 insertions(+), 26 deletions(-) create mode 100644 module/kanban/css/view.css create mode 100644 module/kanban/js/view.js create mode 100644 module/kanban/view/view.html.php diff --git a/config/zentaopms.php b/config/zentaopms.php index d8d93022cf..d5e393d381 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -237,10 +237,13 @@ define('TABLE_RELATION', '`' . $config->db->prefix . 'relation`'); define('TABLE_REPOHISTORY', '`' . $config->db->prefix . 'repohistory`'); define('TABLE_REPOFILES', '`' . $config->db->prefix . 'repofiles`'); define('TABLE_REPOBRANCH', '`' . $config->db->prefix . 'repobranch`'); +define('TABLE_KANBAN', '`' . $config->db->prefix . 'kanban`'); define('TABLE_KANBANSPACE', '`' . $config->db->prefix . 'kanbanspace`'); +define('TABLE_KANBANREGION', '`' . $config->db->prefix . 'kanbanregion`'); define('TABLE_KANBANLANE', '`' . $config->db->prefix . 'kanbanlane`'); define('TABLE_KANBANCOLUMN', '`' . $config->db->prefix . 'kanbancolumn`'); define('TABLE_KANBANORDER', '`' . $config->db->prefix . 'kanbanorder`'); +define('TABLE_KANBANGROUP', '`' . $config->db->prefix . 'kanbangroup`'); if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`'); if(!defined('TABLE_PROJECTSPEC')) define('TABLE_PROJECTSPEC', '`' . $config->db->prefix . 'projectspec`'); @@ -279,10 +282,13 @@ $config->objectTables['job'] = TABLE_JOB; $config->objectTables['team'] = TABLE_TEAM; $config->objectTables['pipeline'] = TABLE_PIPELINE; $config->objectTables['mr'] = TABLE_MR; +$config->objectTables['kanban'] = TABLE_KANBAN; $config->objectTables['kanbanspace'] = TABLE_KANBANSPACE; +$config->objectTables['kanbanregion'] = TABLE_KANBANREGION; $config->objectTables['kanbancolumn'] = TABLE_KANBANCOLUMN; $config->objectTables['kanbanlane'] = TABLE_KANBANLANE; $config->objectTables['kanbanorder'] = TABLE_KANBANORDER; +$config->objectTables['kanbangroup'] = TABLE_KANBANGROUP; $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme'); diff --git a/module/kanban/control.php b/module/kanban/control.php index d2a25e2f5f..ee1a6fbdb5 100644 --- a/module/kanban/control.php +++ b/module/kanban/control.php @@ -84,6 +84,22 @@ class kanban extends control $this->display(); } + /** + * View a kanban. + * + * @param int $kanbanID + * @access public + * @return void + */ + public function view($kanbanID) + { + $kanban = $this->kanban->getByID($kanbanID); + + $this->view->regions = $this->kanban->getKanbanData($kanbanID); + $this->view->kanban = $kanban; + $this->display(); + } + /** * Set WIP. * diff --git a/module/kanban/css/view.css b/module/kanban/css/view.css new file mode 100644 index 0000000000..86e42e3574 --- /dev/null +++ b/module/kanban/css/view.css @@ -0,0 +1,76 @@ +body {background-color: #fff} + +#mainNavbar + #mainContent {background-color: #fff;} +#mainNavbar + #mainContent.scrollbar-hover {margin-top: 0px; overflow: scroll;} +#TRAction .icon {padding: 0px 6px; font-size: 14px} +#searchbox {display: none;} + +.has-error .form-control {border-color: #ff4268!important;} +.has-error .form-control:focus {box-shadow: inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 6px #f5b2a5!important;} +.has-error .form-control::placeholder {color: #ff4268!important;} + +.label.text-red {color: #ff4268;} + +.dropdown-menu > li {padding: 0 10px;} +.dropdown-menu > li > a > .icon {position: relative; left: -5px;} + +.region {border: 1px solid #dcdcdc; margin-bottom: 20px;} +.region .region-header {padding: 10px;} +.region .region-header span {font-size: 14px; color: #999EAB} +.region .region-header label {color: #999; background: transparent; border: 1px solid #ddd; margin-left: 10px; margin-right: 10px} +.region .region-header .action {float: right} +.region .region-header .icon-double-angle-up,.icon-double-angle-down {cursor: pointer;} +.region.sort .region-header {cursor: move;} + +.region .kanban {padding: 0px 10px 10px 10px; min-height: unset;} +.region .kanban .form-actions {margin: 0;} +.region .kanban .form-actions .btn {margin-right: 10px; min-width: 50px;} + +.region .kanban-board {overflow-x: auto; min-width: 100%!important;} +.region .kanban-board + .kanban-board {margin-top: 20px;} +.region .kanban-board > .kanban-header > .kanban-group-header {padding: 8px 2px; width: 20px;} +.region .kanban-board > .kanban-header > .kanban-group-header:hover {cursor: move;} +.region .kanban-board.sort > .kanban-header {padding-left: 0;} + +.region .kanban-header {border-bottom: none!important; min-width: max-content; min-width: -moz-max-content;} +.region .kanban-header-col > .title {margin: 0} +.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial;} +.region .kanban-header-col > .title > .text-grey {opacity: .5; font-weight: bold; color: #8b91a2;} +.region .kanban-header-col > .title > .count {opacity: .5; font-weight: bold; color: #8b91a2;} +.region .kanban-header-col > .title > .error {color: #333333; padding-left: 5px} + +.region .kanban-lane {position: relative; border-bottom: none!important; min-height: 200px} +.region .kanban-lane {min-width: max-content; min-width: -moz-max-content} + +.region .kanban-lane > .kanban-lane-name > .text {margin: auto 0; max-height: 100px;} +.region .kanban-lane > .kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;} +.region .kanban-lane > .kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;} +.region .kanban-lane > .kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;} +.region .kanban-lane > .kanban-lane-name:hover > .actions {opacity: 1;} +.region .kanban-lane.sort > .kanban-lane-name {cursor: move;} + +.region .kanban-lane-col {max-height: unset !important; overflow: auto!important; position: relative;} +.region .kanban-lane-col.drop-to {outline: 3px solid rgba(255, 190, 57, 0.5)!important; background-color: rgba(255, 190, 57, .1);} +.region .kanban-lane-col.can-drop-here {outline: none; background-color: rgba(61, 198, 253, .075);} +.region .kanban-lane-col.has-scrollbar {padding-bottom: 50px;} +.region .kanban-lane-col.has-scrollbar > .kanban-lane-actions {position: absolute; background-color: inherit; bottom: 0; left: 0; right: 0;} + +.region .kanban-lane-items {overflow: auto; padding-bottom: 10px;} + +.region .kanban-item {position: relative} +.region .kanban-item > .title {display: block; max-height: 38px; overflow: hidden; color: inherit; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;} +.region .kanban-item > .title:hover {color: #2272eb} +.region .kanban-item > .info {margin-top: 5px; position: relative;} +.region .kanban-item > .info > .pri {height: 14px; border-width: 1px; font-size: 12px; line-height: 12px; min-width: 14px; padding: 0 1px;} +.region .kanban-item > .info > .time {margin-left: 10px; font-size: 12px} +.region .kanban-item > .info > .user {position: absolute; right: 0; top: 0} +.region .kanban-item > .actions {position: absolute; top: 0px; right: 4px; opacity: 0;} +.region .kanban-item > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;} +.region .kanban-item > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;} +.region .kanban-item:hover > .actions {opacity: 1;} +.region .kanban-item.has-color .title, +.region .kanban-item.has-color .actions .icon-more-v, +.region .kanban-item.has-color .info > .label-pri, +.region .kanban-item.has-color .info > .label-light {color: #FFFFFF;} +.region .kanban-item.has-color .info > .label-pri {border-color: #FFFFFF;} +.region .kanban-item .item-more {position: absolute; right: 6px; top: 0px;} diff --git a/module/kanban/js/view.js b/module/kanban/js/view.js new file mode 100644 index 0000000000..d7a95b4d88 --- /dev/null +++ b/module/kanban/js/view.js @@ -0,0 +1,848 @@ +function loadMore(type) +{ + var method = 'browseArchived' + type; + var selector = '#more' + type + 's'; + var link = createLink('kanban', method, 'kanbanID=' + kanbanID); + $(selector).load(link, function() + { + var windowHeight = $(window).height(); + $(selector + ' .panel-body').css('height', windowHeight - 100); + $(selector + ' .avatar').renderAvatar(); + $(selector).animate({right: 0}, 500); + }); +} + +/** + * Display the kanban in full screen. + * + * @access public + * @return void + */ +function fullScreen() +{ + var element = document.getElementById('mainContent'); + var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullscreen; + if(requestMethod) + { + var afterEnterFullscreen = function() + { + $('#mainContent').addClass('scrollbar-hover'); + $.cookie('isFullScreen', 1); + }; + + var whenFailEnterFullscreen = function(error) + { + $.cookie('isFullScreen', 0); + }; + + try + { + var result = requestMethod.call(element); + if(result && (typeof result.then === 'function' || result instanceof window.Promise)) + { + result.then(afterEnterFullscreen).catch(whenFailEnterFullscreen); + } + else + { + afterEnterFullscreen(); + } + } + catch (error) + { + whenFailEnterFullscreen(error); + } + } +} + +/** + * Exit full screen. + * + * @access public + * @return void + */ +function exitFullScreen() +{ + $('#mainContent').removeClass('scrollbar-hover'); + $.cookie('isFullScreen', 0); +} + +document.addEventListener('fullscreenchange', function (e) +{ + if(!document.fullscreenElement) exitFullScreen(); +}); + +document.addEventListener('webkitfullscreenchange', function (e) +{ + if(!document.webkitFullscreenElement) exitFullScreen(); +}); + +document.addEventListener('mozfullscreenchange', function (e) +{ + if(!document.mozFullScreenElement) exitFullScreen(); +}); + +document.addEventListener('msfullscreenChange', function (e) +{ + if(!document.msfullscreenElement) exitFullScreen(); +}); + +/** + * Render header of a column. + */ +function renderHeaderCol($column, column, $header, kanbanData) +{ + /* Render group header. */ + var privs = kanbanData.actions; + if(privs.includes('sortGroup')) + { + var groups = regions[column.region].groups; + if($header.closest('.kanban').data('zui.kanban')) + { + groups = $header.closest('.kanban').data('zui.kanban').data; + } + if(groups.length > 1) + { + $column.closest('.kanban-board').addClass('sort'); + $column.closest('.kanban-header').find('.kanban-group-header').remove(); + $column.closest('.kanban-header').prepend('
    '); + } + } + + /* Render more menu. */ + if(!$column.children('.actions').length) $column.append('
    '); + var $actions = $column.children('.actions'); + $actions.attr('title', lang.more); + $actions.html(''); +} + +/** + * Render items count of a column. + */ +function renderCount($count, count, column) +{ + /* Render WIP. */ + var limit = !column.limit || column.limit == '-1' ? '' : column.limit; + if($count.parent().find('.limit').length) + { + $count.parent().find('.limit').html(limit); + } + else + { + $count.parent().find('.count').before("("); + $count.parent().find('.count').after("/" + limit + ")"); + } + + if(column.limit != -1 && column.limit < count) + { + $count.parents('.title').parent('.kanban-header-col').css('background-color', '#F6A1A1'); + $count.parents('.title').find('.text').css('max-width', $count.parents('.title').width() - 200); + $count.css('color', '#E33030'); + if(!$count.parent().find('.error').length) $count.parent().find('.include-last').after("(" + kanbanLang.limitExceeded + ")"); + } + else + { + $count.parents('.title').parent('.kanban-header-col').css('background-color', 'transparent'); + $count.parents('.title').find('.text').css('max-width', $count.parents('.title').width() - 120); + $count.css('color', '#8B91A2'); + $count.parent().find('.error').remove(); + } +} + +function renderLaneName($lane, lane, $kanban, columns, kanban) +{ + var canEdit = lane.actions.includes('editLane'); + var canSort = lane.actions.includes('sortLane') && kanban.lanes.length > 1; + var canDelete = lane.actions.includes('deleteLane'); + + $lane.parent().toggleClass('sort', canSort); + + if(!$lane.children('.actions').length && (canEdit || canDelete)) + { + $([ + '
    ', + '', + '', + '', + '
    ' + ].join('')).appendTo($lane); + } +} + +/** + * The function for rendering kanban item + */ +function renderKanbanItem(item, $item) +{ + if(item.status == 'done') + { + $item.addClass('gray'); + } + var $title = $item.children('.title'); + if(!$title.length) + { + $title = $('') + .appendTo($item); + } + $title.text(item.name); + $title.attr('href', createLink('sys.task', 'view', 'taskID=' + item.id + '&from=kanban')); + + var $more = $item.children('.actions'); + if(!$more.length) + { + $( + [ + '
    ', + '', + '
    ' + ].join('')).appendTo($item); + } + + var $info = $item.children('.info'); + if(!$info.length) $info = $( + [ + '
    ', + '', + '', + '
    ', + '
    ' + ].join('')).appendTo($item); + + $item.data('task', item); + + $info.children('.pri') + .attr('class', 'pri label-pri label-pri-' + item.pri) + .text(item.pri); + + var $time = $info.children('.time'); + if(item.deadline && item.deadline !== '0000-00-00') + { + var deadline = $.zui.createDate(item.deadline); + var today = $.zui.createDate(today); + var isExpired = deadline.getTime() < today.getTime(); + var dateFormat = (today.getFullYear() === deadline.getFullYear() ? 'MM-dd ' : 'yyyy-MM-dd ') + kanbanLang.deadline; + $time.text($.zui.formatDate(deadline, dateFormat)) + .toggleClass('text-red', isExpired) + .show(); + } + else + { + $time.hide(); + } + + var $user = $info.children('.user'); + if(item.assignedTo) + { + var member = members[item.assignedTo] || {account: item.assignedTo}; + var canAssign = item.status !== 'closed' && item.status !== 'cancel'; + var $avatar = $( + canAssign ? '' + : '
    ' + ).attr('data-name', member.realname || member.account) + .attr('data-id', member.id || member.account); + if(member.avatar) + { + $avatar.append(''); + } + $avatar.renderAvatar(); + if(canAssign) + { + $avatar.attr('href', createLink('sys.task', 'assignto', 'taskID=' + item.id + '&fromKanban=1')); + } + + $user.empty() + .append($avatar) + .attr('title', member.realname || member.account) + .show(); + } + else + { + $user.hide(); + } + + $item.css('background-color', item.color); + $item.toggleClass('has-color', item.color != '#FFFFFF'); + if(item.color == '#FFFFFF') + { + $item.find('.info > .label-light').css('background-color', '#F2F2F2'); + } + else + { + var colors = {'#E01B1B': '#DD5858', '#F7B500': '#ECC046', '#288427': '#609F60'} + $item.find('.info > .label-light').css('background-color', colors[item.color]); + } +} + +/** + * Show error message + * @param {string|object} message Message + */ +function showErrorMessager(message) +{ + var html = false; + if(message instanceof Error) + { + message = message.message; + } + else if(typeof message === 'object') + { + html = []; + $.each(message, function(key, msg) + { + html.push($.isArray(msg) ? msg.join('') : String(msg)); + }); + message = html.join('
    '); + } + else + { + message = String(message); + } + + if(typeof message === 'string' && message.length) + { + $.zui.messager.danger(message, {html: !!html}); + } +} + +/** + * Update kanban + * @param {string} [regionID] Region ID + * @param {string} [groupID] Group ID + * @param {function(Error)} [callback] Callback on completed + */ +function updateRegion(regionID, groupID, callback) +{ + if(!regionID) return false; + if(typeof groupID == 'function') + { + callback = groupID; + groupID = 0; + } + + var url = createLink('kanban', 'ajaxGetData', 'kanbanID=' + kanbanID + '®ionID=' + regionID + '&group=' + (groupID || 0)); + $.ajax( + { + method: 'get', + dataType: 'json', + url: url, + success: function(response) + { + var kanban = $('#kanban' + regionID).data('zui.kanban'); + if(groupID) kanban.renderKanban(response.data); + else kanban.render(response.data); + typeof callback === 'function' && callback(); + }, + error: function(xhr, status, error) + { + showErrorMessager(error || lang.timeout); + typeof callback === 'function' && callback(error); + } + }); +} + +/** + * Hide kanban action + */ +function hideKanbanAction() +{ + $('.kanban').attr('data-action-enabled', null); + $('.contextmenu').removeClass('contextmenu-show'); + $('.contextmenu .contextmenu-menu').removeClass('open').removeClass('in'); + $('#moreTasks, #moreColumns').animate({right: -400}, 500); +} + +/** + * Open form for adding task + * @param {JQuery} $element Trigger element + */ +function openAddTaskForm($element) +{ + var regionID = $element.closest('.kanban').data('id'); + var groupID = $element.closest('.kanban-board').data('id'); + var laneID = $element.closest('.kanban-lane').data('id'); + var columnID = $element.closest('.kanban-col').data('id'); + var status = $element.closest('.kanban-col').data('type'); + var modalUrl = createLink('sys.task', 'create', 'kanbanID=' + kanbanID + '&from=kanban®ionID=' + regionID + '&groupID=' + groupID + '&laneID=' + laneID + '&columnID=' + columnID + '&status=' + status); + $.zui.modalTrigger.show( + { + url: modalUrl, + width: '1000px' + }); + hideKanbanAction(); +} + +/** + * Reset lane height according to window height. + */ +function resetLaneHeight() +{ + var maxHeight = '360px'; + if(laneCount < 2) + { + var windowHeight = $(window).height(); + var marginTop = $('#mainContent').css('margin-top'); + var headerHeight = $('.kanban > .kanban-board:first > .kanban-header').outerHeight(); + var actionHeight = $('.kanban > .kanban-board:first > .kanban-lane > .kanban-col:first > .kanban-lane-actions').outerHeight(); + + maxHeight = windowHeight - parseInt(marginTop) - headerHeight - actionHeight; + } + $('.kanban-lane-items').css('max-height', maxHeight); +} + +/** + * Close modal and update kanban data. + */ +function closeModalAndUpdateKanban(regionID) +{ + setTimeout(function() + { + $.zui.closeModal(); + updateRegion(regionID); + }, 1200); +} + +/** + * Status change map + */ +var statusChangeMap = +{ + wait: ['doing', 'done', 'cancel'], + doing: ['done', 'cancel'], + done: ['doing', 'closed'], + cancel: ['doing', 'closed'], + closed: ['doing'] +}; + +/** + * Find drop columns + * @param {JQuery} $element Drag element + * @param {JQuery} $root Dnd root element + */ +function findDropColumns($element, $root) +{ + var $task = $element; + var task = $task.data('task'); + var status = task.status; + var $col = $task.closest('.kanban-col'); + var col = $col.data(); + var lane = $col.closest('.kanban-lane').data('lane'); + var allStatusCanChange = statusChangeMap[status]; + + hideKanbanAction(); + + return $root.find('.kanban-lane-col:not([data-type="EMPTY"],[data-type=""])').filter(function() + { + var $newCol = $(this); + var newCol = $newCol.data(); + var $newLane = $newCol.closest('.kanban-lane'); + var newLane = $newLane.data('lane'); + + $newCol.addClass('can-drop-here'); + return true; + }); +} + +/** + * Handle drop task + * @param {Object} event Drop event object + */ +function handleDropTask($element, event, kanban) +{ + if(!event.target || !event.isNew) return; + + var $task = $element; + var $oldCol = $task.closest('.kanban-col'); + var $newCol = $(event.target).closest('.kanban-col'); + var oldCol = $oldCol.data(); + var newCol = $newCol.data(); + var oldLane = $oldCol.closest('.kanban-lane').data('lane'); + var newLane = $newCol.closest('.kanban-lane').data('lane'); + var kanbanID = $task.closest('.kanban-board').data('id'); + var regionID = $task.closest('.kanban').data('id'); + + if(oldCol.id === newCol.id && newLane.id === oldLane.id) return false; + + var newStatus = newCol.type; + var task = $task.data('task'); + + /* Task status not change */ + if(newStatus === task.status && task.kanbanLane !== newLane.id) + { + var url = createLink('sys.task', 'move', 'taskID=' + task.id + '&groupID=' + newLane.group); + return $.ajax( + { + method: 'post', + dataType: 'json', + url: url, + data: {lane: newLane.id}, + success: function(data) + { + if(data && data.result === 'success') updateRegion(regionID); + else showErrorMessager(data && data.message); + }, + error: function(xhr, status, error) + { + showErrorMessager(error || lang.timeout); + } + }); + } + + /* Show dialog to user for changing status */ + var methodToChangeStatus = + { + wait: 'activate', + doing: 'start', + done: 'finish', + cancel: 'cancel', + closed: 'close', + }; + + $.getJSON(createLink('task', 'move', 'taskID=' + task.id + '&groupID=' + newLane.group + '&laneID=' + newLane.id + '&columnID=' + newCol.id), function(response) + { + if(response.result == 'success') updateRegion(regionID); + }); +} + +/** + * Handle finish drop task + */ +function handleFinishDrop() +{ + $('.kanban').find('.can-drop-here').removeClass('can-drop-here'); +} + +/** + * Adjust add button postion in column + */ +function adjustAddBtnPosition($kanban) +{ + if(!$kanban) + { + $('.kanban').children('.kanban-board').each(function() + { + adjustAddBtnPosition($(this)); + }); + return; + } + + $kanban.find('.kanban-lane-col:not([data-type="EMPTY"])').each(function() + { + var $col = $(this); + var items = $col.children('.kanban-lane-items')[0]; + $col.toggleClass('has-scrollbar', items.scrollHeight > items.clientHeight); + }); +} + +/** + * Kanban action handlers + */ +var kanbanActionHandlers = +{ + addItem: openAddTaskForm, + dropItem: handleDropTask +}; + +/** + * Handle kanban action + */ +function handleKanbanAction(action, $element, event, kanban) +{ + $('.kanban').attr('data-action-enabled', action); + var handler = kanbanActionHandlers[action]; + if(handler) handler($element, event, kanban); +} + +function processMinusBtn() +{ + var columnCount = $('#splitTable .child-column').size(); + if(columnCount > 2 && columnCount < 10) + { + $('#splitTable .btn-plus').show(); + $('#splitTable .btn-close').show(); + } + else if(columnCount <= 2) + { + $('#splitTable .btn-close').hide(); + } + else if(columnCount >= 10) + { + $('#splitTable .btn-plus').hide(); + } +} + +function createLaneMenu(options) +{ + var lane = options.$trigger.closest('.kanban-lane').data('lane'); + var privs = lane.actions; + if(!privs.length) return []; + + var items = []; + if(privs.includes('editLane')) items.push({label: kanbanLang.editLane, icon: '', url: createLink('kanban', 'editLane', 'lane=' + lane.id), attrs: {'data-toggle': 'modal'}}); + if(privs.includes('deleteLane')) items.push({label: kanbanLang.deleteLane, icon: '', url: createLink('kanban', 'deleteLane', 'lane=' + lane.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbanlaneLang.confirmDelete, 'data-confirmDetail': kanbanlaneLang.confirmDeleteDetail}}); + + var bounds = options.$trigger[0].getBoundingClientRect(); + items.$options = {x: bounds.right, y: bounds.top}; + return items; +} + +function createTaskMenu(options) +{ + var task = options.$trigger.closest('.kanban-item').data('task'); + var privs = task.actions; + if(!privs.length) return []; + + var actions = { + 'edit': {'label': taskLang.edit, 'icon': '', 'url': createLink('task', 'edit', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal', 'data-width': '1125px'}}, + 'move': {'label': taskLang.move, 'icon': '', 'url': createLink('task', 'move', "taskID=" + task.id + "&groupID=" + task.kanbanGroup), 'attrs': {'data-toggle': 'modal', 'data-width': 400}}, + 'copy': {'label': taskLang.copy, 'icon': '', 'url': createLink('task', 'copy', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal', 'data-width': '1000px'}}, + 'start': {'label': taskLang.start, 'icon': '', 'url': createLink('task', 'start', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}}, + 'finish': {'label': taskLang.finish, 'icon': '', 'url': createLink('task', 'finish', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}}, + 'activate': {'label': taskLang.activate, 'icon': '', 'url': createLink('task', 'activate', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}}, + 'cancel': {'label': taskLang.cancel, 'icon': '', 'url': createLink('task', 'cancel', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}}, + 'close': {'label': taskLang.close, 'icon': '', 'url': createLink('task', 'close', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}}, + 'archive': {'label': taskLang.archive, 'icon': '', 'url': createLink('task', 'archive', "taskID=" + task.id + "&from=kanban"), 'attrs': {'class': 'confirmer', 'data-confirmTitle': taskLang.confirmArchive, 'data-confirmDetail': taskLang.confirmArchiveDetail, 'data-confirmButton': taskLang.archive, 'data-confirming': taskLang.archiving}}, + 'delete': {'label': taskLang.delete, 'icon': '', 'url': createLink('task', 'delete', "taskID=" + task.id + '&from=kanban'), 'attrs': {'class': 'confirmer', 'data-confirmTitle': taskLang.confirmDelete, 'data-confirmDetail': taskLang.confirmDeleteDetail}}, + 'setColor': {'label': taskLang.setColor, 'icon': '', 'url': createLink('task', 'setColor', "taskID=" + task.id), 'attrs': {'data-toggle': 'modal', 'data-width': 400}}, + }; + + var items = []; + $.each(actions, function(actionKey, actionItem) + { + if(privs.includes(actionKey)) items.push({label: actionItem.label, icon: '', url: actionItem.url, attrs: actionItem.attrs}); + }); + + var bounds = options.$trigger[0].getBoundingClientRect(); + items.$options = {x: bounds.right, y: bounds.top}; + return items; +} + +function createColumnMenu(options) +{ + var column = options.$trigger.closest('.kanban-col').data('col'); + var privs = column.actions; + if(!privs.length) return []; + + var items = []; + if(privs.includes('setWIP')) items.push({label: kanbanLang.WIP, icon: '', url: createLink('kanban', 'setWIP', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}}); + if(privs.includes('editColumn')) items.push({label: kanbanLang.editColumn, icon: '', url: createLink('kanban', 'editColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}}); + if(privs.includes('createColumn')) + { + items.push({label: kanbanLang.createColumnOnLeft, icon: '', url: createLink('kanban', 'createColumn', 'columnID=' + column.id + '&position=left'), attrs: {'data-toggle': 'modal'}}); + items.push({label: kanbanLang.createColumnOnRight, icon: '', url: createLink('kanban', 'createColumn', 'columnID=' + column.id + '&position=right'), attrs: {'data-toggle': 'modal'}}); + } + if(privs.includes('copyColumn')) items.push({label: kanbanLang.copyColumn, icon: '', url: createLink('kanban', 'copyColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}}); + if(privs.includes('splitColumn')) items.push({label: kanbanLang.splitColumn, icon: '', url: createLink('kanban', 'splitColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}}); + if(privs.includes('archiveColumn')) items.push({label: kanbanLang.archiveColumn, icon: '', url: createLink('kanban', 'archiveColumn', 'columnID=' + column.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbancolumnLang.confirmArchive, 'data-confirmDetail': kanbancolumnLang.confirmArchiveDetail, 'data-confirmButton': lang.archive, 'data-confirming': lang.archiving}}); + if(privs.includes('deleteColumn')) items.push({label: kanbanLang.deleteColumn, icon: '', url: createLink('kanban', 'deleteColumn', 'columnID=' + column.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbancolumnLang.confirmDelete, 'data-confirmDetail': kanbancolumnLang.confirmDeleteDetail}}); + + var bounds = options.$trigger[0].getBoundingClientRect(); + items.$options = {x: bounds.right, y: bounds.top}; + return items; +} + +/* Define menu creators */ +window.menuCreators = +{ + task: createTaskMenu, + lane: createLaneMenu, + column: createColumnMenu +}; + +/** + * init Kanban + */ +function initKanban($kanban) +{ + var id = $kanban.data('id'); + var region = regions[id]; + + $kanban.kanban( + { + data: region.groups, + maxColHeight: 510, + createColumnText: kanbanLang.createColumn, + addItemText: '', + itemRender: renderKanbanItem, + onAction: handleKanbanAction, + onRenderKanban: adjustAddBtnPosition, + onRenderLaneName: renderLaneName, + onRenderHeaderCol: renderHeaderCol, + onRenderCount: renderCount, + droppable: + { + target: findDropColumns, + finish: handleFinishDrop, + mouseButton: 'left' + } + }).on('click', '.action-cancel', hideKanbanAction); +} + +/** + * Init when page ready + */ +$(function() +{ + if($.cookie('isFullScreen') == 1) fullScreen(); + + /* Init first kanban */ + $('.kanban').each(function() + { + initKanban($(this)); + }); + + $('.icon-double-angle-up,.icon-double-angle-down').on('click', function() + { + $(this).toggleClass('icon-double-angle-up icon-double-angle-down'); + $(this).parents('.region').find('.kanban').toggle(); + hideKanbanAction(); + }); + + $('.region-header').on('click', '.action', hideKanbanAction); + $('#TRAction').on('click', '.btn', hideKanbanAction); + + /* Hide action box when user click document */ + $(document).on('click', function(e) + { + $('.kanban').each(function() + { + var currentAction = $(this).kanban().attr('data-action-enabled'); + var canHideAction = (currentAction === 'headerMore' || currentAction === 'editLaneName') + && !$(e.target).closest('.action,.action-box').length; + if(canHideAction) hideKanbanAction(); + }); + }); + + /* Init contextmenu */ + $('#kanban').on('click', '[data-contextmenu]', function(event) + { + var $trigger = $(this); + var menuType = $trigger.data('contextmenu'); + var menuCreator = window.menuCreators[menuType]; + if(!menuCreator) return; + + var options = $.extend({event: event, $trigger: $trigger}, $trigger.data()); + var items = menuCreator(options); + if(!items || !items.length) return; + + $.zui.ContextMenu.show(items, items.$options || {event: event}); + }); + + /* Adjust the add button position on window resize */ + $(window).on('resize', function(a) + { + adjustAddBtnPosition(); + }); + + resetLaneHeight(); + + /* Hide contextmenu when page scroll */ + $(window).on('scroll', function() + { + $.zui.ContextMenu.hide(); + }); + + $(document).on('click', '#splitTable .btn-plus', function() + { + var tr = $(this).closest('tr'); + tr.after($('#childTpl').html().replace(/key/g, key)); + tr.next().find('input[name^=color]').colorPicker(); + key++; + processMinusBtn(); + return false; + }); + + /* Remove a trade detail item. */ + $(document).on('click', '#splitTable .btn-close', function() + { + $(this).closest('tr').remove(); + processMinusBtn(); + return false; + }); + + /* Init sortable */ + var sortType = ''; + $('#kanban').sortable( + { + selector: '.region, .kanban-board, .kanban-lane', + trigger: '.region.sort > .region-header, .kanban-board.sort > .kanban-header > .kanban-group-header, .kanban-lane.sort > .kanban-lane-name', + targetSelector: function($ele) + { + /* Sort regions */ + if($ele.hasClass('region')) + { + sortType = 'region'; + return $ele.parent().children('.region'); + } + + /* Sort boards */ + if($ele.hasClass('kanban-board')) + { + sortType = 'board'; + return $ele.parent().children('.kanban-board'); + } + + /* Sort lanes */ + if($ele.hasClass('kanban-lane')) + { + sortType = 'lane'; + return $ele.parent().children('.kanban-lane'); + } + + /* Sort lanes */ + if($ele.hasClass('kanban-item')) + { + sortType = 'item'; + return $ele.parent().children('.kanban-item'); + } + }, + start: function(e) + { + if(sortType == 'region') + { + $('.icon-double-angle-up').attr('class', 'icon-double-angle-down'); + $('.region').find('.kanban').hide(); + hideKanbanAction(); + } + }, + finish: function(e) + { + var url = ''; + var orders = []; + e.list.each(function(index, data) + { + orders.push(data.item.data('id')); + }); + + if(sortType == 'region') + { + $('.icon-double-angle-down').attr('class', 'icon-double-angle-up'); + $('.region').find('.kanban').show(); + } + if(sortType == 'board') + { + var region = e.element.parent().data('id'); + url = createLink('kanban', 'sortGroup', 'region=' + region + '&groups=' + orders.join(',')); + } + if(sortType == 'lane') + { + var region = e.element.parent().parent().data('id'); + url = createLink('kanban', 'sortLane', 'region=' + region + '&lanes=' + orders.join(',')); + } + if(sortType == 'item') + { + url = createLink('task', 'sort', 'kanbanID=' + kanbanID + '&tasks=' + orders.join(',')); + } + if(!url) return true; + + $.getJSON(url, function(response) + { + if(response.result == 'fail' && response.message.length) + { + bootbox.alert(response.message); + setTimeout(function(){return location.reload()}, 3000); + } + }); + } + }); +}); diff --git a/module/kanban/lang/zh-cn.php b/module/kanban/lang/zh-cn.php index fe7d2ccb40..d694d254b3 100644 --- a/module/kanban/lang/zh-cn.php +++ b/module/kanban/lang/zh-cn.php @@ -81,6 +81,7 @@ $lang->kanban->laneGroup = '泳道分组'; $lang->kanban->cardsSort = '卡片排序'; $lang->kanban->moreAction = '更多操作'; $lang->kanban->noGroup = '无'; +$lang->kanban->limitExceeded = '超出在制品限制'; $lang->kanban->error = new stdclass(); $lang->kanban->error->mustBeInt = '在制品数量必须是正整数。'; @@ -148,8 +149,11 @@ $lang->kanbancolumn->limit = $lang->kanban->WIPCount; $lang->kanbanlane = new stdclass(); $lang->kanbanlane->name = $lang->kanban->laneName; +$lang->kanbanlane->common = '泳道'; $lang->kanbanlane->default = '默认泳道'; $lang->kanbanregion = new stdclass(); $lang->kanbanregion->name = '看板区域'; $lang->kanbanregion->default = '默认区域'; + +$lang->kanbancard = new stdclass(); diff --git a/module/kanban/model.php b/module/kanban/model.php index eaf58d6c35..d1de6d6e51 100644 --- a/module/kanban/model.php +++ b/module/kanban/model.php @@ -25,17 +25,17 @@ class kanbanModel extends model { $kanban = $this->getByID($kanbanID); - $function = $from == 'default' ? 'createDefaultRegion' : 'createRegion'; - $this->$function($kanban); + $function = $type == 'default' ? 'createDefaultRegion' : 'createRegion'; + $regionID = $this->$function($kanban); if(dao::isError()) return false; - $groupID = $this->createGroup($kanban->id, $kanban->region); + $groupID = $this->createGroup($kanban->id, $regionID); if(dao::isError()) return false; - $this->createDefaultLane($kanban, $groupID); + $this->createDefaultLane($kanban, $regionID, $groupID); if(dao::isError()) return false; - $this->createDefaultColumns($kanban, $groupID); + $this->createDefaultColumns($kanban, $regionID, $groupID); if(dao::isError()) return false; } @@ -50,8 +50,8 @@ class kanbanModel extends model public function createGroup($kanbanID, $regionID) { $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANORDER) - ->where('type')->eq('group') - ->andWhere('parent')->eq($regionID) + ->where('objectType')->eq('group') + ->andWhere('parentID')->eq($regionID) ->andWhere('parentType')->eq('region') ->andWhere('account')->eq('') ->fetch('maxOrder'); @@ -87,7 +87,6 @@ class kanbanModel extends model $region->createdBy = $this->app->user->account; $region->createdDate = helper::today(); - $this->dao->insert(TABLE_KANBANREGION)->data($region)->exec(); return $this->createRegion($kanban, $region); } @@ -107,8 +106,8 @@ class kanbanModel extends model if(!$region) { $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANORDER) - ->where('type')->eq('region') - ->andWhere('parent')->eq($kanban->id) + ->where('objectType')->eq('region') + ->andWhere('parentID')->eq($kanban->id) ->andWhere('parentType')->eq('kanban') ->andWhere('account')->eq('') ->fetch('maxOrder'); @@ -140,51 +139,55 @@ class kanbanModel extends model * Create default lane. * * @param object $kanban + * @param int $regionID + * @param int $groupID * @access public * @return int */ - public function createDefaultLane($kanban) + public function createDefaultLane($kanban, $regionID, $groupID) { $lane = new stdclass(); $lane->name = $this->lang->kanbanlane->default; - $lane->region = $kanban->region; + $lane->group = $groupID; + $lane->region = $regionID; $lane->type = 'common'; $lane->lastEditedTime = helper::now(); $this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec(); $laneID = $this->dao->lastInsertId(); - $this->saveOrder($kanban->region, 'region', $laneID, 'lane', '', 1); + $this->saveOrder($regionID, 'region', $laneID, 'lane', '', 1); return $laneID; } /** * Create default kanban columns. * - * @param int $kanban - * @param int $laneID + * @param object $kanban + * @param int $regionID + * @param int $groupID * @access public * @return void */ - public function createDefaultColumns($kanban, $laneID) + public function createDefaultColumns($kanban, $regionID, $groupID) { + $index = 1; foreach($this->lang->kanban->defaultColumn as $columnName) { $column = new stdclass(); - $column->region = $kanban->region; + $column->region = $regionID; + $column->group = $groupID; $column->name = $columnName; - $column->lane = $laneID; + $column->type = $index; + $column->limit = -1; $this->dao->insert(TABLE_KANBANCOLUMN)->data($column)->exec(); - $columnID = $this->dao->lastInsertId(); - $kanbanGroup = new stdclass(); - $kanbanGroup->region = $kanban->region; - $kanbanGroup->column = $columnID; - $kanbanGroup->lane = $laneID; - - $this->dao->insert(TABLE_KANBANGROUP)->data($kanbanGroup)->exec(); + $this->saveOrder($regionID, 'region', $this->dao->lastInsertID(), 'column', '', $index); + $index ++; } + + return !dao::isError(); } /** @@ -202,7 +205,7 @@ class kanbanModel extends model public function saveOrder($parentID, $parentType, $objectID, $objectType, $account, $order) { $kanbanOrder = new stdclass(); - $kanbanOrder->parentID = $parent; + $kanbanOrder->parentID = $parentID; $kanbanOrder->parentType = $parentType; $kanbanOrder->objectID = $objectID; $kanbanOrder->objectType = $objectType; @@ -213,6 +216,182 @@ class kanbanModel extends model return !dao::isError(); } + /** + * Get kanban by id. + * + * @param int $kanbanID + * @access public + * @return object + */ + public function getByID($kanbanID) + { + return $this->dao->findByID($kanbanID)->from(TABLE_KANBAN)->fetch(); + } + + public function getKanbanData($kanbanID) + { + $kanbanData = array(); + $actions = array('sortGroup'); + $regions = $this->getRegionPairs($kanbanID); + $groupGroup = $this->getGroupGroupByRegions(array_keys($regions)); + $laneGroup = $this->getLaneGroupByRegions(array_keys($regions)); + $columnGroup = $this->getColumnGroupByRegions(array_keys($regions)); + //$taskGroup = $this->getTaskGroupByProject($kanbanID); + + foreach($regions as $regionID => $regionName) + { + $region = new stdclass(); + $region->id = $regionID; + $region->name = $regionName; + $region->laneCount = 0; + + $groups = zget($groupGroup, $regionID, array()); + foreach($groups as $group) + { + $lanes = zget($laneGroup, $group->id, array()); + if(!$lanes) continue; + + foreach($lanes as $lane) $lane->items = isset($taskGroup[$lane->id]) ? $taskGroup[$lane->id] : array(); + + $group->columns = zget($columnGroup, $group->id, array()); + $group->lanes = $lanes; + $group->actions = array(); + + foreach($actions as $action) + { + if(commonModel::hasPriv('kanban', $action)) $group->actions[] = $action; + } + + $region->groups[] = $group; + $region->laneCount += count($lanes); + } + + $kanbanData[$regionID] = $region; + } + + return $kanbanData; + } + + /** + * Get ordered region pairs. + * + * @param int $kanbanID + * @access public + * @return void + */ + public function getRegionPairs($kanbanID) + { + return $this->dao->select('id,name')->from(TABLE_KANBANREGION) + ->where('kanban')->eq($kanbanID) + ->andWhere('deleted')->eq('0') + ->orderBy('id_asc') + ->fetchPairs(); + } + + /** + * Get kanban group by regions. + * + * @param array $regions + * @access public + * @return array + */ + public function getGroupGroupByRegions($regions) + { + return $this->dao->select('*')->from(TABLE_KANBANGROUP) + ->where('region')->in($regions) + ->orderBy('id_asc') + ->fetchGroup('region'); + } + + /** + * Get lane group by regions. + * + * @param array $regions + * @access public + * @return array + */ + public function getLaneGroupByRegions($regions) + { + $laneGroup = $this->dao->select('*')->from(TABLE_KANBANLANE) + ->where('deleted')->eq('0') + ->andWhere('region')->in($regions) + ->orderBy('order') + ->fetchGroup('group'); + + $actions = array('editLane', 'sortLane', 'deleteLane'); + foreach($laneGroup as $lanes) + { + foreach($lanes as $lane) + { + $lane->actions = array(); + foreach($actions as $action) + { + if($this->isClickable($lane, $action)) $lane->actions[] = $action; + } + } + } + + return $laneGroup; + } + + /** + * Get column group by regions. + * + * @param array $regions + * @access public + * @return array + */ + public function getColumnGroupByRegions($regions) + { + $columnGroup = $this->dao->select("*")->from(TABLE_KANBANCOLUMN) + ->where('deleted')->eq('0') + //->andWhere('archived')->eq('0') + ->andWhere('region')->in($regions) + ->orderBy('order') + ->fetchGroup('group', 'id'); + + $actions = array('createColumn', 'copyColumn', 'editColumn', 'splitColumn', 'setWIP', 'archiveColumn', 'restoreColumn', 'deleteColumn'); + + $parentColumnGroup = array(); + foreach($columnGroup as $group => $columns) + { + foreach($columns as $column) + { + $column->actions = array(); + foreach($actions as $action) + { + if($this->isClickable($column, $action)) $column->actions[] = $action; + } + + if($column->parent) continue; + + $parentColumnGroup[$group][] = $column; + } + } + + $columnData = array(); + foreach($parentColumnGroup as $group => $parentColumns) + { + foreach($parentColumns as $parentColumn) + { + $columnData[$group][] = $parentColumn; + foreach($columnGroup[$group] as $column) + { + if($column->parent == $parentColumn->id) + { + $parentColumn->asParent = true; + + $column->parentType = 'column' . $column->parent; + + $columnData[$group][] = $column; + } + } + } + } + + return $columnData; + } + /** * Get Kanban by execution id. * @@ -1182,4 +1361,78 @@ class kanbanModel extends model } return $menus; } + + /** + * Check if user can execute an action. + * + * @param object $object + * @param string $action + * @access public + * @return bool + */ + public function isClickable($object, $action) + { + $action = strtolower($action); + $clickable = commonModel::hasPriv('kanban', $action); + if(!$clickable) return false; + + switch($action) + { + case 'sortlane' : + case 'deletelane' : + if($object->deleted != '0') return false; + + $count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANLANE) + ->where('deleted')->eq('0') + ->andWhere('region')->eq($object->region) + ->beginIF($action == 'sortlane')->andWhere('`group`')->eq($object->group)->fi() + ->fetch('count'); + return $count > 1; + case 'createcolumn' : + case 'copycolumn' : + case 'splitcolumn' : + if($object->parent) return false; // The current column is a child column. + + $count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN) + ->where('parent')->eq($object->id) + ->andWhere('deleted')->eq('0') + //->andWhere('archived')->eq('0') + ->fetch('count'); + return $count == 0; // The column has child columns. + case 'restoreColumn' : + if($object->parent) + { + $parent = $this->getColumnByID($object->parent); + if($parent->deleted == '1' || $parent->archived == '1') return false; + } + return $object->archived == '1'; + case 'archivecolumn' : + //if($object->archived != '0') return false; // The column has been archived. + case 'deletecolumn' : + if($object->deleted != '0') return false; + + if($object->parent) + { + $childrenCount = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN) + ->where('parent')->eq($object->parent) + ->andWhere('deleted')->eq('0') + //->andWhere('archived')->eq('0') + ->fetch('count'); + + return $childrenCount > 2; + } + + $count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN) + ->where('region')->eq($object->region) + ->andWhere('parent')->eq(0) + ->andWhere('`group`')->eq($object->group) + ->andWhere('deleted')->eq('0') + //->andWhere('archived')->eq('0') + ->fetch('count'); + + return $count > 1; + } + + return true; + } } diff --git a/module/kanban/view/view.html.php b/module/kanban/view/view.html.php new file mode 100644 index 0000000000..d08ee0bbf9 --- /dev/null +++ b/module/kanban/view/view.html.php @@ -0,0 +1,55 @@ + + + + +laneCount; + +js::set('regions', $regions); +js::set('kanbanLang', $lang->kanban); +js::set('kanbanlaneLang', $lang->kanbanlane); +js::set('kanbancolumnLang', $lang->kanbancolumn); +js::set('taskLang', $lang->kanbancard); +js::set('kanbanID', $kanban->id); +//js::set('members', $members); +js::set('laneCount', $laneCount); +js::set('today', helper::today()); + +$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1; +$canEditRegion = commonModel::hasPriv('kanban', 'editRegion'); +$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion'); +$canCreateLane = commonModel::hasPriv('kanban', 'createLane'); +?> + +
    + +
    + +
    +
    + +
    +
    +
    + From ad53ead5bad12a3b6f63c5c698a9ff711a0cd73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B9=BF=E6=98=8E?= Date: Thu, 9 Dec 2021 14:33:23 +0800 Subject: [PATCH 44/44] * Fix code details. --- module/kanban/css/view.css | 5 ++++ module/kanban/model.php | 11 +++++++- module/kanban/view/view.html.php | 47 +++++++++++++------------------- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/module/kanban/css/view.css b/module/kanban/css/view.css index 86e42e3574..aacdee8ede 100644 --- a/module/kanban/css/view.css +++ b/module/kanban/css/view.css @@ -74,3 +74,8 @@ body {background-color: #fff} .region .kanban-item.has-color .info > .label-light {color: #FFFFFF;} .region .kanban-item.has-color .info > .label-pri {border-color: #FFFFFF;} .region .kanban-item .item-more {position: absolute; right: 6px; top: 0px;} + +.kanban-col[data-type=ADD] {display: none;} +.kanban-col[data-type=EMPTY] {display: none;} +.kanban-item:hover .title {padding-right: 10px;} +.gray .actions {display:none;} diff --git a/module/kanban/model.php b/module/kanban/model.php index d1de6d6e51..cee5d5ce55 100644 --- a/module/kanban/model.php +++ b/module/kanban/model.php @@ -228,6 +228,13 @@ class kanbanModel extends model return $this->dao->findByID($kanbanID)->from(TABLE_KANBAN)->fetch(); } + /** + * Get kanban data. + * + * @param int $kanbanID + * @access public + * @return void + */ public function getKanbanData($kanbanID) { $kanbanData = array(); @@ -277,7 +284,7 @@ class kanbanModel extends model * * @param int $kanbanID * @access public - * @return void + * @return array */ public function getRegionPairs($kanbanID) { @@ -352,12 +359,14 @@ class kanbanModel extends model $actions = array('createColumn', 'copyColumn', 'editColumn', 'splitColumn', 'setWIP', 'archiveColumn', 'restoreColumn', 'deleteColumn'); + /* Group by parent. */ $parentColumnGroup = array(); foreach($columnGroup as $group => $columns) { foreach($columns as $column) { $column->actions = array(); + /* Judge column action priv. */ foreach($actions as $action) { if($this->isClickable($column, $action)) $column->actions[] = $action; diff --git a/module/kanban/view/view.html.php b/module/kanban/view/view.html.php index d08ee0bbf9..b0f35a4046 100644 --- a/module/kanban/view/view.html.php +++ b/module/kanban/view/view.html.php @@ -1,14 +1,5 @@ - - - + +
    - -
    - - +
    product->code;?> code;?>product->type;?>product->typeList, $product->type);?> story->openedBy?> createdBy);?>
    product->type;?> product->typeList, $product->type);?>productCommon . $lang->product->status;?>product->statusList, $product->status);?> story->openedDate?> createdDate, DT_DATE1);?>
    productCommon ." ". $lang->product->status;?>productCommon . $lang->product->status;?> product->statusList, $product->status);?> product->acl;?> code) ? "colspan='4'" : "colspan='2'";?>>product->aclList[$product->acl];?>
    createdDate, DT_DATE1);?>
    productCommon . $lang->product->status;?>productCommon . $lang->product->status;?> product->statusList, $product->status);?> product->acl;?> code) ? "colspan='4'" : "colspan='2'";?>>product->aclList[$product->acl];?>
    style='overflow:visible'> - id, $i)'")?> + id, $i)'")?>
    %s style='overflow:visible'> - id, \"%s\")'")?> + id, \"%s\")'")?> style='overflow: visible'> From 1111d59ed90def9862b488ce70d9ecedaecdb16c Mon Sep 17 00:00:00 2001 From: mayue Date: Wed, 8 Dec 2021 16:38:45 +0800 Subject: [PATCH 31/44] * Fix bug #17248,17246. --- module/product/css/create.css | 1 - module/product/js/create.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 module/product/css/create.css diff --git a/module/product/css/create.css b/module/product/css/create.css deleted file mode 100644 index 8fd15bd236..0000000000 --- a/module/product/css/create.css +++ /dev/null @@ -1 +0,0 @@ -#lineName {border-left-color: gainsboro !important;} diff --git a/module/product/js/create.js b/module/product/js/create.js index 3c83f0867d..5b806c7de8 100644 --- a/module/product/js/create.js +++ b/module/product/js/create.js @@ -1,6 +1,6 @@ $(function() { - if(programID || systemMode) $('#line_chosen').addClass('hidden'); + if(programID || systemMode == 'classic') $('#line_chosen').addClass('hidden'); $('#lineName').css('border-left-color', ''); }) /** From 7c3a8dc7531bd6f61c3caa96f3cf5c54f5f2de0a Mon Sep 17 00:00:00 2001 From: tianshujie Date: Wed, 8 Dec 2021 16:50:48 +0800 Subject: [PATCH 32/44] * Fix bug #17259. --- module/execution/control.php | 1 + module/execution/model.php | 5 +++-- module/execution/view/manageproducts.html.php | 4 ++-- module/project/control.php | 1 + module/project/view/manageproducts.html.php | 4 ++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/module/execution/control.php b/module/execution/control.php index 5d8052a3c0..62b5adcacf 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -2431,6 +2431,7 @@ class execution extends control $this->view->unmodifiableBranches = $unmodifiableBranches; $this->view->linkedBranches = $linkedBranches; $this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($allProducts), $this->config->systemMode == 'new' ? $execution->project : 0); + $this->view->allBranches = $this->execution->getBranchByProduct(array_keys($allProducts), $this->config->systemMode == 'new' ? $execution->project : 0, 'all'); $this->display(); } diff --git a/module/execution/model.php b/module/execution/model.php index 56e0a41dcd..c6df3884a1 100644 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -1676,12 +1676,13 @@ class executionModel extends model * * @param array $products * @param int $projectID + * @param string $param * @access public * @return array */ - public function getBranchByProduct($products, $projectID = 0) + public function getBranchByProduct($products, $projectID = 0, $param = 'noclosed') { - $branchGroups = $this->loadModel('branch')->getByProducts($products, 'noclosed'); + $branchGroups = $this->loadModel('branch')->getByProducts($products, $param); if($projectID) { diff --git a/module/execution/view/manageproducts.html.php b/module/execution/view/manageproducts.html.php index fcb8b30cad..ad4c9d192a 100644 --- a/module/execution/view/manageproducts.html.php +++ b/module/execution/view/manageproducts.html.php @@ -33,12 +33,12 @@ execution->notAllowRemoveProducts : $productName;?>
    -
    '> +
    '>
    ";?>
    - +
    diff --git a/module/project/control.php b/module/project/control.php index fbfcdfef1d..569d89868f 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -1797,6 +1797,7 @@ class project extends control $this->view->unmodifiableBranches = $unmodifiableBranches; $this->view->unmodifiableMainBranches = $unmodifiableMainBranches; $this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($allProducts), 'ignoreNormal|noclosed'); + $this->view->allBranches = $this->branch->getByProducts(array_keys($allProducts), 'ignoreNormal'); $this->display(); } diff --git a/module/project/view/manageproducts.html.php b/module/project/view/manageproducts.html.php index d42d76fe6a..cabdf182d6 100644 --- a/module/project/view/manageproducts.html.php +++ b/module/project/view/manageproducts.html.php @@ -32,12 +32,12 @@ project->notAllowRemoveProducts : $productName;?>
    -
    '> +
    '>
    ";?>
    - +
    From 559c3879c332f7f404be81a4e23636cac7565a9b Mon Sep 17 00:00:00 2001 From: mayue Date: Wed, 8 Dec 2021 16:52:50 +0800 Subject: [PATCH 33/44] * Optimize code #17248,17246. --- module/task/control.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/task/control.php b/module/task/control.php index 24a1e4dca6..4102345afa 100644 --- a/module/task/control.php +++ b/module/task/control.php @@ -313,7 +313,7 @@ class task extends control $this->view->showFields = $this->config->task->custom->batchCreateFields; $story = $this->story->getByID($storyID); - if($story and empty($moduleID)) + if($story) { $moduleID = $story->module; $stories = $this->story->getExecutionStoryPairs($executionID, 0, 'all', $moduleID, 'short'); From ec5d481dc888872adb83d4bf2c78f7f5c70f012e Mon Sep 17 00:00:00 2001 From: tianshujie Date: Wed, 8 Dec 2021 17:08:10 +0800 Subject: [PATCH 34/44] * Fix bug #17272. --- module/project/control.php | 2 +- module/project/view/browsebycard.html.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/project/control.php b/module/project/control.php index 569d89868f..777d341383 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -456,7 +456,7 @@ class project extends control $this->view->gobackLink = (isset($output['from']) and $output['from'] == 'global') ? $this->createLink('project', 'browse') : ''; $this->view->pmUsers = $this->loadModel('user')->getPairs('noclosed|nodeleted|pmfirst'); $this->view->users = $this->user->getPairs('noclosed|nodeleted'); - $this->view->copyProjects = $this->project->getPairsByModel(); + $this->view->copyProjects = $this->project->getPairsByModel($model); $this->view->products = $products; $this->view->allProducts = array('0' => '') + $this->program->getProductPairs($programID, 'assign', 'noclosed'); $this->view->productPlans = array('0' => '') + $productPlans; diff --git a/module/project/view/browsebycard.html.php b/module/project/view/browsebycard.html.php index 72d31a431f..a71c16e9ec 100644 --- a/module/project/view/browsebycard.html.php +++ b/module/project/view/browsebycard.html.php @@ -72,7 +72,7 @@  ", '', "class='btn btn-icon text-primary' title='{$lang->project->bycard}' id='switchButton' data-type='bycard'");?>
    " . $lang->export, '', "class='btn btn-link export'")?> - config->maxVersion) and !defined('TUTORIAL')):?> + ' . $lang->project->create, '', 'class="btn btn-primary create-project-btn" data-toggle="modal" data-target="#guideDialog"');?> ' . $lang->project->create, '', 'class="btn btn-primary create-project-btn"');?> From e408f91d1f1bb8a493079b6db867022667df5b01 Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Wed, 8 Dec 2021 17:16:22 +0800 Subject: [PATCH 35/44] * Fix bug #17269, bug #16975. --- module/block/control.php | 2 +- module/testcase/control.php | 3 ++- module/testcase/view/showimport.html.php | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/module/block/control.php b/module/block/control.php index fa837a08ef..7b9ecd383c 100644 --- a/module/block/control.php +++ b/module/block/control.php @@ -701,7 +701,7 @@ class block extends control $this->session->set('buildList', $this->app->getURI(true), 'execution'); if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) die(); - $this->view->testtasks = $this->dao->select('t1.*,t2.name as productName,t3.name as buildName,t4.name as projectName')->from(TABLE_TESTTASK)->alias('t1') + $this->view->testtasks = $this->dao->select('distinct t1.*,t2.name as productName,t3.name as buildName,t4.name as projectName')->from(TABLE_TESTTASK)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') ->leftJoin(TABLE_BUILD)->alias('t3')->on('t1.build=t3.id') ->leftJoin(TABLE_PROJECT)->alias('t4')->on('t1.execution=t4.id') diff --git a/module/testcase/control.php b/module/testcase/control.php index 1f938d3fae..983e45a2dd 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -1861,7 +1861,7 @@ class testcase extends control $branchModules = $this->loadModel('tree')->getOptionMenu($productID, 'case', 0, empty($branches) ? array(0) : array_keys($branches)); foreach($branchModules as $branchID => $moduleList) { - foreach($moduleList as $moduleID => $moduleName) $modules[$moduleID] = $moduleName; + foreach($moduleList as $moduleID => $moduleName) $modules[$branchID][$moduleID] = $moduleName; } if(!empty($maxImport) and file_exists($tmpFile)) @@ -2049,6 +2049,7 @@ class testcase extends control $this->view->product = $this->products[$productID]; $this->view->maxImport = $maxImport; $this->view->dataInsert = $insert; + $this->display(); } diff --git a/module/testcase/view/showimport.html.php b/module/testcase/view/showimport.html.php index 0b6cbc545a..6d3e9ae04b 100644 --- a/module/testcase/view/showimport.html.php +++ b/module/testcase/view/showimport.html.php @@ -81,7 +81,8 @@ $(function()
    title, ENT_QUOTES), "class='form-control'")?> - module) ? $case->module : ((!empty($case->id) and isset($cases[$case->id])) ? $cases[$case->id]->module : ''), "class='form-control chosen moduleChange'")?> + branch) and $case->branch != 0) ? $modules[BRANCH_MAIN] + $modules[$case->branch] : $modules[BRANCH_MAIN];?> + module) ? $case->module : ((!empty($case->id) and isset($cases[$case->id])) ? $cases[$case->id]->module : ''), "class='form-control chosen moduleChange'")?> story) ? $case->story : ((!empty($case->id) and isset($cases[$case->id])) ? $cases[$case->id]->story : '');?> From 0d7f5f6e2d49008116044d431103deecdd4236c6 Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Wed, 8 Dec 2021 17:26:12 +0800 Subject: [PATCH 36/44] * Adjust the code. --- module/testcase/view/showimport.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/testcase/view/showimport.html.php b/module/testcase/view/showimport.html.php index 6d3e9ae04b..ea49c6a892 100644 --- a/module/testcase/view/showimport.html.php +++ b/module/testcase/view/showimport.html.php @@ -75,7 +75,7 @@ $(function() echo "{$lang->testcase->new}"; } echo html::hidden("product[$key]", $productID); - if(!empty($branches)) echo html::hidden("branch[$key]", (isset($case->branch) and $case->branch !== '') ? $case->branch : ((!empty($case->id) and isset($cases[$case->id]) and !empty($cases[$case->id]->branch)) ? $cases[$case->id]->branch : $branch)); + if(!empty($branches)) echo html::hidden("branch[$key]", !empty($case->branch) ? $case->branch : ((!empty($case->id) and isset($cases[$case->id]) and !empty($cases[$case->id]->branch)) ? $cases[$case->id]->branch : $branch)); echo html::hidden("keywords[$key]", isset($case->keywords) ? $case->keywords : ""); ?>