diff --git a/db/update17.4.sql b/db/update17.4.sql index 746f691ab8..7e62ab9c08 100644 --- a/db/update17.4.sql +++ b/db/update17.4.sql @@ -21,8 +21,8 @@ UPDATE `zt_workflowfield` SET `options`=(SELECT id FROM `zt_workflowdatasource` UPDATE `zt_project` SET `closedDate`='' AND `closedBy`='' WHERE `status` != 'closed'; UPDATE `zt_grouppriv` SET `method`='exportTemplate' WHERE `method` = 'exportTemplet'; -INSERT INTO `zt_grouppriv` (SELECT `group`,`module`,'reply' FROM `zt_grouppriv` WHERE `module` = 'feedback' AND `method` = 'comment'); -INSERT INTO `zt_grouppriv` (SELECT `group`,`module`,'ask' FROM `zt_grouppriv` WHERE `module` = 'feedback' AND `method` = 'comment'); +REPLACE INTO `zt_grouppriv` (SELECT `group`,`module`,'reply' FROM `zt_grouppriv` WHERE `module` = 'feedback' AND `method` = 'comment'); +REPLACE INTO `zt_grouppriv` (SELECT `group`,`module`,'ask' FROM `zt_grouppriv` WHERE `module` = 'feedback' AND `method` = 'comment'); CREATE TABLE `zt_chart` ( `id` mediumint NOT NULL AUTO_INCREMENT, diff --git a/module/action/lang/de.php b/module/action/lang/de.php index bb547129d4..806925b3f0 100644 --- a/module/action/lang/de.php +++ b/module/action/lang/de.php @@ -933,3 +933,5 @@ $lang->action->desc->deletemr = '$date, $actoraction->desc->mergedmr = '$date, $actor merged code.'; $lang->action->desc->approve = '$date, $actor approved.'; $lang->action->desc->reject = '$date, $actor rejected.'; +$lang->action->desc->linkedrepo = '$date, $actor linked repo $extra'; +$lang->action->desc->unlinkedrepo = '$date, $actor unlinked repo $extra'; diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 38619800bd..7a9ea70f9f 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -934,3 +934,5 @@ $lang->action->desc->deletemr = '$date, $actoraction->desc->mergedmr = '$date, $actor merged code.'; $lang->action->desc->approve = '$date, $actor approved.'; $lang->action->desc->reject = '$date, $actor rejected.'; +$lang->action->desc->linkedrepo = '$date, $actor linked repo $extra'; +$lang->action->desc->unlinkedrepo = '$date, $actor unlinked repo $extra'; diff --git a/module/action/lang/fr.php b/module/action/lang/fr.php index 47a856b1c9..8a90476553 100644 --- a/module/action/lang/fr.php +++ b/module/action/lang/fr.php @@ -932,3 +932,5 @@ $lang->action->desc->deletemr = '$date, $actoraction->desc->mergedmr = '$date, $actor merged code.'; $lang->action->desc->approve = '$date, $actor approved.'; $lang->action->desc->reject = '$date, $actor rejected.'; +$lang->action->desc->linkedrepo = '$date, $actor linked repo $extra'; +$lang->action->desc->unlinkedrepo = '$date, $actor unlinked repo $extra'; diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 1d1d61002d..ccc7617f63 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -933,3 +933,5 @@ $lang->action->desc->deletemr = '$date, 由 $actoraction->desc->mergedmr = '$date, 由 $actor 合并了 代码。'; $lang->action->desc->approve = '$date, 由 $actor 审核通过。'; $lang->action->desc->reject = '$date, 由 $actor 拒绝。'; +$lang->action->desc->linkedrepo = '$date, 由 $actor 关联代码库 $extra'; +$lang->action->desc->unlinkedrepo = '$date, 由 $actor 取消了项目与代码库 $extra 的关联'; diff --git a/module/branch/model.php b/module/branch/model.php index dbf7fac873..5c62d826d8 100644 --- a/module/branch/model.php +++ b/module/branch/model.php @@ -126,7 +126,7 @@ class branchModel extends model if($executionID) { - if(isset($executionBranches['0'])) $branches = array('0' => $this->lang->branch->main) + $branches; + $branches = array('all' => $this->lang->branch->all, '0' => $this->lang->branch->main) + $branches; return $branches; } diff --git a/module/bug/control.php b/module/bug/control.php index 5508217ca8..24069734a8 100755 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -286,6 +286,9 @@ class bug extends control } } + $project = $this->loadModel('project')->getByShadowProduct($productID); + if(!$project->multiple) unset($this->lang->bug->report->charts['bugsPerExecution']); + $this->qa->setMenu($this->products, $productID, $branchID); $this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->bug->common . $this->lang->colon . $this->lang->bug->reportChart; $this->view->position[] = html::a($this->createLink('bug', 'browse', "productID=$productID"), $this->products[$productID]); diff --git a/module/bug/model.php b/module/bug/model.php index 5d17a0c4af..e575b4daa3 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -1869,7 +1869,7 @@ class bugModel extends model ->where('t1.deleted')->eq(0) ->beginIF(empty($build))->andWhere('t1.project')->eq($projectID)->fi() ->beginIF(!empty($productID))->andWhere('t1.product')->eq($productID)->fi() - ->beginIF(!empty($productID))->andWhere('t1.branch')->eq($branchID)->fi() + ->beginIF(!empty($productID) and $branchID != 'all')->andWhere('t1.branch')->eq($branchID)->fi() ->beginIF($type == 'unresolved')->andWhere('t1.status')->eq('active')->fi() ->beginIF($type == 'noclosed')->andWhere('t1.status')->ne('closed')->fi() ->beginIF($type == 'assignedtome')->andWhere('t1.assignedTo')->eq($this->app->user->account)->fi() @@ -1888,19 +1888,19 @@ class bugModel extends model /** * Get bugs of a execution. * - * @param int $executionID - * @param int $productID - * @param int $branchID - * @param int $build - * @param string $type - * @param int $param - * @param string $orderBy - * @param string $excludeBugs - * @param object $pager + * @param int $executionID + * @param int $productID + * @param int $branchID + * @param string|array $builds + * @param string $type + * @param int $param + * @param string $orderBy + * @param string $excludeBugs + * @param object $pager * @access public * @return array */ - public function getExecutionBugs($executionID, $productID = 0, $branchID = 'all', $build = 0, $type = '', $param = 0, $orderBy = 'id_desc', $excludeBugs = '', $pager = null) + public function getExecutionBugs($executionID, $productID = 0, $branchID = 'all', $builds = 0, $type = '', $param = 0, $orderBy = 'id_desc', $excludeBugs = '', $pager = null) { $type = strtolower($type); if(strpos($orderBy, 'pri_') !== false) $orderBy = str_replace('pri_', 'priOrder_', $orderBy); @@ -1935,15 +1935,28 @@ class bugModel extends model } else { + $condition = ''; + if($builds) + { + if(!is_array($builds)) $builds = explode(',', $builds); + + $conditions = array(); + foreach($builds as $build) + { + if($build) $conditions[] = "FIND_IN_SET('$build', t1.openedBuild)"; + } + $condition = join(' OR ', $conditions); + $condition = "($condition)"; + } $bugs = $this->dao->select("t1.*, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder, IF(t1.`severity` = 0, {$this->config->maxPriValue}, t1.`severity`) as severityOrder")->from(TABLE_BUG)->alias('t1') ->leftJoin(TABLE_MODULE)->alias('t2')->on('t1.module=t2.id') ->where('t1.deleted')->eq(0) ->beginIF(!empty($productID) and $branchID !== 'all')->andWhere('t1.branch')->eq($branchID)->fi() - ->beginIF(empty($build))->andWhere('t1.execution')->eq($executionID)->fi() + ->beginIF(empty($builds))->andWhere('t1.execution')->eq($executionID)->fi() ->beginIF(!empty($productID))->andWhere('t1.product')->eq($productID)->fi() ->beginIF($type == 'unresolved')->andWhere('t1.status')->eq('active')->fi() ->beginIF($type == 'noclosed')->andWhere('t1.status')->ne('closed')->fi() - ->beginIF($build)->andWhere("CONCAT(',', t1.openedBuild, ',') like '%,$build,%'")->fi() + ->beginIF($condition)->andWhere("$condition")->fi() ->beginIF(!empty($param))->andWhere('t2.path')->like("%,$param,%")->andWhere('t2.deleted')->eq(0)->fi() ->beginIF($excludeBugs)->andWhere('t1.id')->notIN($excludeBugs)->fi() ->orderBy($orderBy) @@ -1979,7 +1992,7 @@ class bugModel extends model } if(empty($executionIdList)) return array(); - $executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->in($executionIdList)->fetch(); + $executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->in($executionIdList)->fetchAll(); $minBegin = ''; $maxEnd = ''; foreach($executions as $execution) @@ -2077,8 +2090,11 @@ class bugModel extends model * * @param int $buildID * @param int $productID + * @param string $branch + * @param string $linkedBugs + * @param object $pager * @access public - * @return object + * @return array */ public function getReleaseBugs($buildID, $productID, $branch = 0, $linkedBugs = '', $pager = null) { @@ -2086,6 +2102,7 @@ class bugModel extends model ->leftJoin(TABLE_BUILD)->alias('t2')->on('t1.id = t2.execution') ->where('t2.id')->in($buildID) ->fetchAll('id'); + if(empty($executions)) return array(); $condition = 'execution NOT ' . helper::dbIN(array_keys($executions)); $minBegin = ''; @@ -2101,7 +2118,7 @@ class bugModel extends model ->andWhere('product')->eq($productID) ->beginIF($linkedBugs)->andWhere('id')->notIN($linkedBugs)->fi() ->beginIF($branch)->andWhere('branch')->in("0,$branch")->fi() - ->andWhere("$condition)") + ->andWhere("($condition)") ->andWhere('deleted')->eq(0) ->orderBy('openedDate ASC') ->page($pager) diff --git a/module/bug/view/edit.html.php b/module/bug/view/edit.html.php index 24ccd1aa2e..f2411a486b 100644 --- a/module/bug/view/edit.html.php +++ b/module/bug/view/edit.html.php @@ -123,7 +123,7 @@ if($this->app->tab == 'project') js::set('objectID', $bug->project); - '> + multiple)) echo 'hide'?>'> bug->productplan;?> plan, "class='form-control chosen'");?> diff --git a/module/bug/view/view.html.php b/module/bug/view/view.html.php index b0ce7d2142..34648a28a8 100644 --- a/module/bug/view/view.html.php +++ b/module/bug/view/view.html.php @@ -161,7 +161,7 @@ bug->fromCase;?> case) echo html::a(helper::createLink('testcase', 'view', "caseID=$bug->case&version=$bug->caseVersion", '', true), " {$lang->bug->fromCase}$lang->colon$bug->case", '', isonlybody() ? '' : "data-toggle='modal' data-type='iframe' data-width='80%'");?> - '> + multiple)) echo 'hide'?>'> bug->productplan;?> plan or !common::printLink('productplan', 'view', "planID=$bug->plan&type=bug", $bug->planName)) echo $bug->planName;?> diff --git a/module/build/control.php b/module/build/control.php index 537ba935d0..0abe7eb54e 100644 --- a/module/build/control.php +++ b/module/build/control.php @@ -119,7 +119,7 @@ class build extends control $this->view->executionID = $executionID; $this->view->products = $products; $this->view->projectID = $projectID; - $this->view->lastBuild = $this->build->getLast($executionID); + $this->view->lastBuild = $this->build->getLast($executionID, $projectID); $this->view->productGroups = $productGroups; $this->view->users = $this->user->getPairs('nodeleted|noclosed'); $this->display(); @@ -172,7 +172,7 @@ class build extends control { $productGroups = $this->product->getProducts($build->project); $branches = $this->loadModel('branch')->getList($build->product, $build->project, 'all'); - $builds = $this->build->getBuildPairs($build->product, $build->branch, 'noempty,notrunk,separate,noproject'); + $builds = $this->build->getBuildPairs($build->product, $build->branch, 'noempty,notrunk,separate,noproject', $build->project, 'project'); } $executions = $this->product->getExecutionPairsByProduct($build->product, $build->branch, 'id_desc', $this->session->project, 'stagefilter'); @@ -292,7 +292,7 @@ class build extends control $this->view->storyPager = $storyPager; $generatedBugPager = new pager($type == 'generatedBug' ? $recTotal : 0, $recPerPage, $type == 'generatedBug' ? $pageID : 1); - $this->view->generatedBugs = $this->bug->getExecutionBugs($build->execution, $build->product, 'all', $build->id, $type, $param, $type == 'generatedBug' ? $sort : 'status_desc,id_desc', '', $generatedBugPager); + $this->view->generatedBugs = $this->bug->getExecutionBugs($build->execution, $build->product, 'all', "$build->id,{$build->builds}", $type, $param, $type == 'generatedBug' ? $sort : 'status_desc,id_desc', '', $generatedBugPager); $this->view->generatedBugPager = $generatedBugPager; $this->executeHooks($buildID); diff --git a/module/build/lang/de.php b/module/build/lang/de.php index 102f2a399f..39f5f213c7 100644 --- a/module/build/lang/de.php +++ b/module/build/lang/de.php @@ -57,6 +57,7 @@ $lang->build->notice = new stdclass(); $lang->build->notice->changeProduct = "The {$lang->SRCommon}, bug, or the version of the submitted test order has been linked, and its {$lang->productCommon} cannot be modified"; $lang->build->notice->changeExecution = "The version of the submitted test order cannot be modified {$lang->executionCommon}"; $lang->build->notice->changeBuilds = "The version of the submitted test order cannot be modified builds"; +$lang->build->notice->autoRelation = "The completed requirements, resolved bugs, and generated bugs under the relevant version will be automatically associated with the project version"; $lang->build->finishStories = " %s {$lang->SRCommon} sind abgeschlossen."; $lang->build->resolvedBugs = ' %s Bugs sind gelöst.'; diff --git a/module/build/lang/en.php b/module/build/lang/en.php index 2cad705a92..df6d81e8d1 100644 --- a/module/build/lang/en.php +++ b/module/build/lang/en.php @@ -57,6 +57,7 @@ $lang->build->notice = new stdclass(); $lang->build->notice->changeProduct = "The {$lang->SRCommon}, bug, or the version of the submitted test order has been linked, and its {$lang->productCommon} cannot be modified"; $lang->build->notice->changeExecution = "The version of the submitted test order cannot be modified {$lang->executionCommon}"; $lang->build->notice->changeBuilds = "The version of the submitted test order cannot be modified builds"; +$lang->build->notice->autoRelation = "The completed requirements, resolved bugs, and generated bugs under the relevant version will be automatically associated with the project version"; $lang->build->finishStories = " Finished {$lang->SRCommon} %s"; $lang->build->resolvedBugs = ' Resolved Bug %s'; diff --git a/module/build/lang/fr.php b/module/build/lang/fr.php index 5bf136f09b..30ed628f70 100644 --- a/module/build/lang/fr.php +++ b/module/build/lang/fr.php @@ -57,6 +57,7 @@ $lang->build->notice = new stdclass(); $lang->build->notice->changeProduct = "The {$lang->SRCommon}, bug, or the version of the submitted test order has been linked, and its {$lang->productCommon} cannot be modified"; $lang->build->notice->changeExecution = "The version of the submitted test order cannot be modified {$lang->executionCommon}"; $lang->build->notice->changeBuilds = "The version of the submitted test order cannot be modified builds"; +$lang->build->notice->autoRelation = "The completed requirements, resolved bugs, and generated bugs under the relevant version will be automatically associated with the project version"; $lang->build->finishStories = " {$lang->SRCommon} Terminées %s"; $lang->build->resolvedBugs = ' Bugs Résolus %s'; diff --git a/module/build/lang/vi.php b/module/build/lang/vi.php index c5a6f474d1..830fb9234d 100644 --- a/module/build/lang/vi.php +++ b/module/build/lang/vi.php @@ -53,6 +53,7 @@ $lang->build->notice = new stdclass(); $lang->build->notice->changeProduct = "The {$lang->SRCommon}, bug, or the version of the submitted test order has been linked, and its {$lang->productCommon} cannot be modified"; $lang->build->notice->changeExecution = "The version of the submitted test order cannot be modified {$lang->executionCommon}"; $lang->build->notice->changeBuilds = "The version of the submitted test order cannot be modified builds"; +$lang->build->notice->autoRelation = "The completed requirements, resolved bugs, and generated bugs under the relevant version will be automatically associated with the project version"; $lang->build->finishStories = " {$lang->SRCommon} đã kết thúc %s"; $lang->build->resolvedBugs = ' Bug đã giải quyết %s'; diff --git a/module/build/lang/zh-cn.php b/module/build/lang/zh-cn.php index b1bbf9d7ca..0be8d8c8b5 100644 --- a/module/build/lang/zh-cn.php +++ b/module/build/lang/zh-cn.php @@ -57,6 +57,7 @@ $lang->build->notice = new stdclass(); $lang->build->notice->changeProduct = "已经关联{$lang->SRCommon}、Bug或提交测试单的版本,不能修改其所属{$lang->productCommon}"; $lang->build->notice->changeExecution = "提交测试单的版本,不能修改其所属{$lang->executionCommon}"; $lang->build->notice->changeBuilds = "提交测试单的版本,不能修改关联版本"; +$lang->build->notice->autoRelation = "相关版本下完成的需求、解决的Bug、产生的Bug将会自动关联到项目版本中"; $lang->build->finishStories = " 本次共完成 %s 个{$lang->SRCommon}"; $lang->build->resolvedBugs = ' 本次共解决 %s 个Bug'; diff --git a/module/build/model.php b/module/build/model.php index 2ff01927dd..85e09241a4 100644 --- a/module/build/model.php +++ b/module/build/model.php @@ -284,14 +284,16 @@ class buildModel extends model * Get last build. * * @param int $executionID + * @param int $projectID * @access public * @return bool | object */ - public function getLast($executionID) + public function getLast($executionID = 0, $projectID = 0) { return $this->dao->select('id, name')->from(TABLE_BUILD) - ->where('execution')->eq((int)$executionID) - ->andWhere('deleted')->eq(0) + ->where('deleted')->eq(0) + ->beginIF($executionID)->andWhere('execution')->eq((int)$executionID)->fi() + ->beginIF($projectID)->andWhere('project')->eq((int)$projectID)->fi() ->orderBy('date DESC,id DESC') ->limit(1) ->fetch(); @@ -311,11 +313,16 @@ class buildModel extends model $build->stories = ''; $build->bugs = ''; - if($projectID) + if($projectID && !$executionID) { $project = $this->loadModel('project')->getByID($projectID); if(!$project->multiple) $executionID = $this->dao->select('id')->from(TABLE_EXECUTION)->where('project')->eq($projectID)->fetch('id'); } + else if($executionID && !$projectID) + { + $execution = $this->loadModel('execution')->getByID($executionID); + $projectID = $execution->project; + } $build = fixer::input('post') ->setDefault('project', $projectID) @@ -342,6 +349,7 @@ class buildModel extends model if(!dao::isError()) { $buildID = $this->dao->lastInsertID(); + $this->linkChildBuilds($buildID, $build->builds); $this->file->updateObjectID($this->post->uid, $buildID, 'build'); $this->file->saveUpload('build', $buildID); $this->loadModel('score')->create('build', 'create', $buildID); @@ -381,6 +389,8 @@ class buildModel extends model if(isset($build->branch) and $oldBuild->branch != $build->branch) $this->dao->update(TABLE_RELEASE)->set('branch')->eq($build->branch)->where('build')->eq($buildID)->exec(); if(!dao::isError()) { + $addBuilds = array_diff(explode(',', $build->builds), explode(',', $oldBuild->builds)); + if($addBuilds) $this->linkChildBuilds($buildID, $addBuilds); $this->file->updateObjectID($this->post->uid, $buildID, 'build'); return common::createChanges($oldBuild, $build); } @@ -556,6 +566,33 @@ class buildModel extends model foreach($this->post->unlinkBugs as $unlinkBugID) $this->action->create('bug', $unlinkBugID, 'unlinkedfrombuild', '', $buildID); } + /** + * Bugs and stories associated with child builds. + * + * @param int $buildID + * @param string $childBuildIDList + * @access public + * @return void + */ + public function linkChildBuilds($buildID, $childBuildIDList) + { + $build = $this->dao->select('bugs, stories')->from(TABLE_BUILD)->where('id')->eq($buildID)->fetch(); + $childBuilds = $this->dao->select('bugs, stories')->from(TABLE_BUILD)->where('id')->in($childBuildIDList)->fetchAll(); + + foreach($childBuilds as $childBuild) + { + if($childBuild->bugs) $build->bugs .= ",{$childBuild->bugs}"; + if($childBuild->stories) $build->stories .= ",{$childBuild->stories}"; + } + + $build->bugs = explode(',', $build->bugs); + $build->bugs = join(',', array_unique(array_filter($build->bugs))); + $build->stories = explode(',', $build->stories); + $build->stories = join(',', array_unique(array_filter($build->stories))); + + $this->dao->update(TABLE_BUILD)->data($build)->where('id')->eq($buildID)->exec(); + } + /** * Build action menu. * diff --git a/module/build/view/create.html.php b/module/build/view/create.html.php index 3297178af4..6a26488df3 100644 --- a/module/build/view/create.html.php +++ b/module/build/view/create.html.php @@ -45,7 +45,8 @@ tab == 'project' && !empty($multipleProject)):?> build->builds;?> - + + build->notice->autoRelation;?> diff --git a/module/build/view/edit.html.php b/module/build/view/edit.html.php index 8fc810e5aa..59e3009c6a 100644 --- a/module/build/view/edit.html.php +++ b/module/build/view/edit.html.php @@ -47,7 +47,13 @@ build->builds;?> builds, "class='form-control chosen' multiple $disabled");?> - build->notice->changeBuilds;?> + + + build->notice->changeBuilds;?> + + build->notice->autoRelation;?> + + build->execution;?> diff --git a/module/build/view/view.html.php b/module/build/view/view.html.php index 70f09e694a..9ca7217e2d 100644 --- a/module/build/view/view.html.php +++ b/module/build/view/view.html.php @@ -334,7 +334,7 @@ tbody tr td:first-child input {display: none;} execution):?> - build->execution;?> + build->project : $lang->build->execution;?> execution);?> @@ -343,7 +343,7 @@ tbody tr td:first-child input {display: none;} builds) as $buildID):?> - createLink('build', 'view', "buildID=$buildID") . "#app={$app->tab}", zget($buildPairs, $buildID)) . $lang->comma;?> + createLink('build', 'view', "buildID=$buildID") . "#app={$app->tab}", zget($buildPairs, $buildID)) . $lang->comma;?> comma);?> diff --git a/module/common/lang/menu.php b/module/common/lang/menu.php index 73dd7ce3dd..99fbd993c2 100644 --- a/module/common/lang/menu.php +++ b/module/common/lang/menu.php @@ -215,7 +215,7 @@ $lang->scrum->menu->devops = array('link' => "{$lang->repo->common}|repo|br $lang->scrum->menu->build = array('link' => "{$lang->build->common}|project|build|project=%s", 'subModule' => 'projectbuild'); $lang->scrum->menu->release = array('link' => "{$lang->release->common}|projectrelease|browse|project=%s", 'subModule' => 'projectrelease'); $lang->scrum->menu->dynamic = array('link' => "$lang->dynamic|project|dynamic|project=%s"); -$lang->scrum->menu->settings = array('link' => "$lang->settings|project|view|project=%s", 'subModule' => 'tree,stakeholder', 'alias' => 'edit,manageproducts,group,managemembers,manageview,managepriv,whitelist,addwhitelist,team,managerepo'); +$lang->scrum->menu->settings = array('link' => "$lang->settings|project|view|project=%s", 'subModule' => 'tree,stakeholder', 'alias' => 'edit,manageproducts,group,managemembers,manageview,managepriv,whitelist,addwhitelist,team,managerepo', 'exclude' => 'tree-browsetask'); $lang->scrum->menu->storyGroup['dropMenu'] = new stdclass(); $lang->scrum->menu->storyGroup['dropMenu']->story = array('link' => "{$lang->SRCommon}|projectstory|story|projectID=%s&productID=%s", 'subModule' => 'projectstory,tree'); @@ -312,7 +312,7 @@ $lang->waterfall->menu->design['subMenu']->bysearch = array('link' => 'kanban->common}|project|index|project=%s"); -$lang->kanbanProject->menu->build = array('link' => "{$lang->build->common}|project|build|project=%s"); +$lang->kanbanProject->menu->build = array('link' => "{$lang->build->common}|project|build|project=%s", 'subModule' => 'projectbuild'); $lang->kanbanProject->menu->settings = array('link' => "$lang->settings|project|view|project=%s", 'subModule' => 'tree,stakeholder', 'alias' => 'edit,manageproducts,group,managemembers,manageview,managepriv,whitelist,addwhitelist,team'); $lang->kanbanProject->dividerMenu = ''; @@ -343,7 +343,7 @@ $lang->execution->menu->view = array('link' => "$lang->view|execution|grouptas if($config->edition != 'open') $lang->execution->menu->view = array('link' => "$lang->view|execution|gantt|executionID=%s", 'alias' => 'grouptask,tree,taskeffort,gantt,calendar,relation,maintainrelation'); $lang->execution->menu->storyGroup = array('link' => "{$lang->common->story}|execution|story|executionID=%s",'class' => 'dropdown dropdown-hover', 'subModule' => 'story', 'alias' => 'batchcreate,storykanban'); -$lang->execution->menu->story = array('link' => "$lang->SRCommon|execution|story|executionID=%s", 'subModule' => 'story', 'alias' => 'storykanban'); +$lang->execution->menu->story = array('link' => "$lang->SRCommon|execution|story|executionID=%s", 'subModule' => 'story,execution', 'alias' => 'storykanban,linkstory'); $lang->execution->menu->qa = array('link' => "{$lang->qa->common}|execution|bug|executionID=%s", 'subModule' => 'bug,testcase,testtask,testreport', 'alias' => 'qa,bug,testcase,testtask,testreport'); $lang->execution->menu->devops = array('link' => "{$lang->repo->common}|repo|browse|repoID=0&branchID=&objectID=%s", 'subModule' => 'repo'); $lang->execution->menu->doc = array('link' => "{$lang->doc->common}|doc|tableContents|type=execution&objectID=%s", 'subModule' => 'doc'); @@ -794,6 +794,7 @@ if(!helper::hasFeature('devops')) unset($lang->mainNav->devops, $lang->mainNav->menuOrder[35]); unset($lang->scrum->menu->devops, $lang->scrum->menuOrder[25]); unset($lang->waterfall->menu->devops, $lang->waterfall->menuOrder[35]); + unset($lang->execution->menu->devops, $lang->execution->menuOrder[45]); } if(!helper::hasFeature('kanban')) diff --git a/module/common/model.php b/module/common/model.php index eb225fad72..e717621c0a 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -2420,9 +2420,9 @@ EOD; 'message' => array('ajaxgetmessage'), ); if(!empty($this->app->user->modifyPassword) and (!isset($beforeValidMethods[$module]) or !in_array($method, $beforeValidMethods[$module]))) return print(js::locate(helper::createLink('my', 'changepassword'))); - if($this->isOpenMethod($module, $method)) return true; if(!$this->loadModel('user')->isLogon() and $this->server->php_auth_user) $this->user->identifyByPhpAuth(); if(!$this->loadModel('user')->isLogon() and $this->cookie->za) $this->user->identifyByCookie(); + if($this->isOpenMethod($module, $method)) return true; if(isset($this->app->user)) { diff --git a/module/execution/control.php b/module/execution/control.php index 6d0e3736eb..4bc1ba9ad3 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -180,7 +180,7 @@ class execution extends control /* Save to session. */ $uri = $this->app->getURI(true); - $this->app->session->set('taskList', $uri, 'execution'); + $this->app->session->set('taskList', $uri . "#app={$this->app->tab}", 'execution'); /* Process the order by field. */ if(!$orderBy) $orderBy = $this->cookie->executionTaskOrder ? $this->cookie->executionTaskOrder : 'status,id_desc'; @@ -685,12 +685,12 @@ class execution extends control unset($this->config->bug->search['fields']['resolvedDate']); unset($this->config->bug->search['fields']['closedDate']); unset($this->config->bug->search['fields']['branch']); + if(empty($execution->multiple) && empty($execution->hasProduct)) unset($this->config->bug->search['fields']['plan']); if(empty($project->hasProduct)) { unset($this->config->bug->search['fields']['product']); if($project->model !== 'scrum') unset($this->config->bug->search['fields']['plan']); } - unset($this->config->bug->search['params']['resolvedBy']); unset($this->config->bug->search['params']['closedBy']); unset($this->config->bug->search['params']['status']); @@ -998,7 +998,7 @@ class execution extends control * @access public * @return void */ - public function bug($executionID = 0, $productID = 0, $branch = 0, $orderBy = 'status,id_desc', $build = 0, $type = 'all', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1) + public function bug($executionID = 0, $productID = 0, $branch = 'all', $orderBy = 'status,id_desc', $build = 0, $type = 'all', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1) { /* Load these two models. */ $this->loadModel('bug'); @@ -1165,7 +1165,7 @@ class execution extends control * @access public * @return void */ - public function testcase($executionID = 0, $productID = 0, $branchID = 0, $type = 'all', $moduleID = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1) + public function testcase($executionID = 0, $productID = 0, $branchID = 'all', $type = 'all', $moduleID = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1) { $this->loadModel('testcase'); $this->loadModel('testtask'); @@ -1733,10 +1733,28 @@ class execution extends control $this->view->linkedBranches = $linkedBranches; } + if(empty($project->hasProduct)) + { + $shadowProduct = $this->loadModel('product')->getShadowProductByProject($project->id); + $productPlan = $this->loadModel('productplan')->getPairs($shadowProduct->id, '0,0', 'noclosed,unexpired', true); + } + if(!empty($_POST)) { if(isset($_POST['attribute']) and in_array($_POST['attribute'], array('request', 'design', 'review'))) unset($_POST['plans']); + /* No product execution link plans. */ + if(empty($project->hasProduct) and !empty($_POST['plans'])) + { + $plansItem = array(); + foreach($_POST['plans'] as $planItem) + { + if(empty($planItem[0][0])) continue; + $plansItem[] = $planItem[0][0]; + } + $_POST['plans'] = array($_POST['products'][0] => array(0 => $plansItem)); + } + $executionID = $this->execution->create($copyExecutionID); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -1808,7 +1826,7 @@ class execution extends control $this->loadModel('product'); $allProducts = $this->product->getProductPairsByProject($projectID, 'noclosed'); - $copyProjects = $this->loadModel('project')->getPairsByProgram(isset($project->parent) ? $project->parent : '', 'noclosed', '', 'order_asc', '', isset($project->model) ? $project->model : '', true); + $copyProjects = $this->loadModel('project')->getPairsByProgram(isset($project->parent) ? $project->parent : '', 'noclosed', '', 'order_asc', '', isset($project->model) ? $project->model : '', 'multiple'); $copyProjectID = ($projectID == 0) ? key($copyProjects) : $projectID; if(!empty($project->hasProduct)) $allProducts = array(0 => '') + $allProducts; @@ -1826,7 +1844,7 @@ class execution extends control $this->view->code = $code; $this->view->team = $team; $this->view->teams = array(0 => '') + $this->execution->getCanCopyObjects((int)$projectID); - $this->view->allProjects = array(0 => '') + $this->project->getPairsByModel('all', 0, 'noclosed'); + $this->view->allProjects = array(0 => '') + $this->project->getPairsByModel('all', 0, 'noclosed,multiple'); $this->view->copyProjects = $copyProjects; $this->view->copyExecutions = array('' => '') + $this->execution->getList($copyProjectID); $this->view->executionID = $executionID; diff --git a/module/execution/lang/de.php b/module/execution/lang/de.php index 29c7ef95cb..ee29d08f90 100644 --- a/module/execution/lang/de.php +++ b/module/execution/lang/de.php @@ -345,6 +345,7 @@ $lang->execution->wbs = "Aufgaben aufteilen"; $lang->execution->batchWBS = "Mehrere aufteilen"; $lang->execution->howToUpdateBurn = "Hilfe "; $lang->execution->whyNoStories = "Keine Story kann verknüpft werden. Bitte prüfen Sie ob ein Story mit {$lang->executionCommon} verknüpft ist {$lang->productCommon} und stellen Sie sicher das diese geprüft ist."; +$lang->execution->projectNoStories = "No story can be linked. Please check whether there is any story in project and make sure it has been reviewed."; $lang->execution->productStories = "{$lang->executionCommon} verknüpfte Story ist ein Subset von {$lang->productCommon}, welche nur nach überprüfung verknüpft werden kann. Bitte Story verknüpfen。"; $lang->execution->haveDraft = "There are %s draft stories can't be linked."; $lang->execution->doneExecutions = 'Erledigt'; diff --git a/module/execution/lang/en.php b/module/execution/lang/en.php index 5c7d8daf50..276311554f 100644 --- a/module/execution/lang/en.php +++ b/module/execution/lang/en.php @@ -345,6 +345,7 @@ $lang->execution->wbs = "Create Task"; $lang->execution->batchWBS = "Batch Create Tasks"; $lang->execution->howToUpdateBurn = "Help "; $lang->execution->whyNoStories = "No story can be linked. Please check whether there is any story in {$lang->executionCommon} which is linked to {$lang->productCommon} and make sure it has been reviewed."; +$lang->execution->projectNoStories = "No story can be linked. Please check whether there is any story in project and make sure it has been reviewed."; $lang->execution->productStories = "Stories linked to {$lang->executionCommon} are the subeset of stories linked to {$lang->productCommon}. Stories can only be linked after they pass the review. Link Stories now."; $lang->execution->haveDraft = "%s stories in draft, so they can't be linked."; $lang->execution->doneExecutions = 'Finished'; diff --git a/module/execution/lang/fr.php b/module/execution/lang/fr.php index 3da5b42ffa..40c1dd6f2f 100644 --- a/module/execution/lang/fr.php +++ b/module/execution/lang/fr.php @@ -345,6 +345,7 @@ $lang->execution->wbs = "Créer Tâche"; $lang->execution->batchWBS = "Créer Tâche en lot"; $lang->execution->howToUpdateBurn = "Mise à jour "; $lang->execution->whyNoStories = "Aucune story ne peut être associée. Vérifiez s'il existe des stories dans {$lang->executionCommon} qui sont associées à {$lang->productCommon} et vérifiez qu'elles ont bien été validées."; +$lang->execution->projectNoStories = "No story can be linked. Please check whether there is any story in project and make sure it has been reviewed."; $lang->execution->productStories = "Les stories associées au {$lang->executionCommon} sont une portion des stories associées au {$lang->productCommon}. Les stories ne peuvent être associées à un {$lang->executionCommon} qu'après avoir été validées. Associer Stories maintenant."; $lang->execution->haveDraft = "%s stories sont encore en conception, elles ne peuvent pas être associées au {$lang->executionCommon} actuellement."; $lang->execution->doneExecutions = 'Terminé'; diff --git a/module/execution/lang/vi.php b/module/execution/lang/vi.php index 9d3885c16a..bd77c671f5 100644 --- a/module/execution/lang/vi.php +++ b/module/execution/lang/vi.php @@ -281,6 +281,7 @@ $lang->execution->wbs = "Tạo nhiệm vụ"; $lang->execution->batchWBS = "Tạo nhiệm vụ hàng loạt"; $lang->execution->howToUpdateBurn = "Giúp"; $lang->execution->whyNoStories = "Không có câu chuyện có thể liên kết. Vui lòng kiểm tra có câu chuyện trong {$lang->executionCommon} cái liên kết tới {$lang->productCommon} và chắc chắn nó đã được duyệt."; +$lang->execution->projectNoStories = "No story can be linked. Please check whether there is any story in project and make sure it has been reviewed."; $lang->execution->productStories = "Câu chuyện liên kết tới {$lang->executionCommon} là tập hợp con của câu chuyện liên kết tới {$lang->productCommon}. Các câu chuyện chỉ có thể liên kết sau khi chúng đã được duyệt. Liên kết câu chuyện now."; $lang->execution->haveDraft = "%s câu chuyện đang nháp, bởi vậy chúng không thể liên kết."; $lang->execution->doneExecutions = 'Kết thúc'; diff --git a/module/execution/lang/zh-cn.php b/module/execution/lang/zh-cn.php index 77f79dff1f..a11a6923d4 100644 --- a/module/execution/lang/zh-cn.php +++ b/module/execution/lang/zh-cn.php @@ -345,6 +345,7 @@ $lang->execution->wbs = "分解任务"; $lang->execution->batchWBS = "批量分解"; $lang->execution->howToUpdateBurn = "帮助 "; $lang->execution->whyNoStories = "看起来没有{$lang->SRCommon}可以关联。请检查下{$lang->executionCommon}关联的{$lang->productCommon}中有没有{$lang->SRCommon},而且要确保它们已经审核通过。"; +$lang->execution->projectNoStories = "看起来没有{$lang->SRCommon}可以关联。请检查下项目中有没有{$lang->SRCommon},而且要确保它们已经审核通过。"; $lang->execution->productStories = "{$lang->executionCommon}关联的{$lang->SRCommon}是{$lang->productCommon}{$lang->SRCommon}的子集,并且只有评审通过的{$lang->SRCommon}才能关联。请关联{$lang->SRCommon}。"; $lang->execution->haveDraft = "有%s条草稿状态的{$lang->SRCommon}无法关联到该执行"; $lang->execution->doneExecutions = '已结束'; diff --git a/module/execution/model.php b/module/execution/model.php index 3c2ff6c461..129b2aa23c 100755 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -142,6 +142,11 @@ class executionModel extends model $this->loadModel('project')->setNoMultipleMenu($executionID); if(isset($this->lang->execution->menu->storyGroup)) unset($this->lang->execution->menu->storyGroup); + if(isset($this->lang->execution->menu->story['dropMenu']) and $methodName == 'storykanban') + { + unset($this->lang->execution->menu->story['dropMenu']); + $this->lang->execution->menu->story['link'] = str_replace(array($this->lang->common->story, 'story'), array($this->lang->SRCommon, 'storykanban'), $this->lang->execution->menu->story['link']); + } } /** @@ -2473,7 +2478,7 @@ class executionModel extends model /* Link stories. */ $executionStories = $this->loadModel('story')->getExecutionStoryPairs($executionID); $lastOrder = (int)$this->dao->select('*')->from(TABLE_PROJECTSTORY)->where('project')->eq($executionID)->orderBy('order_desc')->limit(1)->fetch('order'); - $stories = $this->dao->findById($storyID)->fields("id as story, product, version")->from(TABLE_STORY)->where('id')->in(array_keys($executionStories))->fetchAll('story'); + $stories = $this->dao->select("id as story, product, version")->from(TABLE_STORY)->where('id')->in(array_keys($executionStories))->fetchAll('story'); foreach($stories as $storyID) { if(!isset($executionStories[$storyID])) @@ -4727,7 +4732,7 @@ class executionModel extends model } } } - if(!empty($execution->division)) echo "{$execution->productName}"; + if(!empty($execution->division) and $execution->hasProduct) echo "{$execution->productName}"; echo "" . zget($this->lang->project->statusList, $execution->status) . ''; echo '' . zget($users, $execution->PM) . ''; echo helper::isZeroDate($execution->begin) ? '' : '' . $execution->begin . ''; diff --git a/module/execution/view/build.html.php b/module/execution/view/build.html.php index e1c014cd0d..c1610c1616 100644 --- a/module/execution/view/build.html.php +++ b/module/execution/view/build.html.php @@ -51,7 +51,6 @@ build->id;?> build->product;?> build->name;?> - build->product;?> build->scmPath;?> build->filePath;?> build->date;?> @@ -69,7 +68,6 @@ branchName) echo "{$build->branchName}"?> createLink('build', 'view', "build=$build->id"), $build->name);?> - productName;?> scmPath, 'http') === 0 ? html::a($build->scmPath) : $build->scmPath;?> filePath, 'http') === 0 ? html::a($build->filePath) : $build->filePath;?> date?> diff --git a/module/execution/view/kanban.html.php b/module/execution/view/kanban.html.php index c51bd5fcdd..7c036b443d 100644 --- a/module/execution/view/kanban.html.php +++ b/module/execution/view/kanban.html.php @@ -74,7 +74,7 @@ $canBatchCreateBug = ($productID and common::hasPriv('bug', 'batchCreate') and $canImportBug = ($productID and common::hasPriv('execution', 'importBug')); $canCreateStory = ($productID and common::hasPriv('story', 'create')); $canBatchCreateStory = ($productID and common::hasPriv('story', 'batchCreate')); -$canLinkStory = ($productID and common::hasPriv('execution', 'linkStory')); +$canLinkStory = ($productID and common::hasPriv('execution', 'linkStory') and !empty($execution->hasProduct)); $canLinkStoryByPlan = ($productID and common::hasPriv('execution', 'importplanstories') and !empty($project->hasProduct)); $hasStoryButton = ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlan); $hasTaskButton = ($canCreateTask or $canBatchCreateTask or $canImportBug); diff --git a/module/execution/view/linkstory.html.php b/module/execution/view/linkstory.html.php index dccd6cc427..bc1af1e745 100644 --- a/module/execution/view/linkstory.html.php +++ b/module/execution/view/linkstory.html.php @@ -65,7 +65,11 @@ id => sprintf('%03d', $story->id)));?> id]", $story->product);?> - pri;?>' title='story->priList, $story->pri, $story->pri);?>'>story->priList, $story->pri, $story->pri);?> + + pri):?> + pri;?>' title='story->priList, $story->pri, $story->pri);?>'>story->priList, $story->pri, $story->pri);?> + + parent > 0) echo "{$lang->story->childrenAB}"; @@ -105,7 +109,8 @@
-

{$app->rawModule}->whyNoStories;?>

+ rawModule == 'execution' and !$project->hasProduct) ? 'projectNoStories' : 'whyNoStories';?> +

{$app->rawModule}->{$emptyTips};?>

diff --git a/module/execution/view/story.html.php b/module/execution/view/story.html.php index a43f1cba34..e6883a207a 100644 --- a/module/execution/view/story.html.php +++ b/module/execution/view/story.html.php @@ -79,9 +79,9 @@ id", '', 'button', 'bar-chart muted'); - common::printLink('story', 'export', "productID=$productID&orderBy=id_desc&executionID=$execution->id", " " . $lang->story->export, '', "class='btn btn-link export iframe'"); + common::printLink('story', 'export', "productID=$productID&orderBy=id_desc&executionID=$execution->id&browseType=$type&storyType=$storyType", " " . $lang->story->export, '', "class='btn btn-link export iframe'"); - $canLinkStory = $execution->hasProduct or $execution->multiple; + $canLinkStory = $execution->multiple; if(common::canModify('execution', $execution)) { $this->lang->story->create = $this->lang->execution->createStory; @@ -221,7 +221,7 @@ moduleName . ucfirst($this->methodName); $useDatatable = (isset($config->datatable->$datatableId->mode) and $config->datatable->$datatableId->mode == 'datatable'); - $vars = "executionID={$execution->id}&orderBy=%s&type=$type¶m=$param&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; + $vars = "executionID={$execution->id}&storyType=$storyType&orderBy=%s&type=$type¶m=$param&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; if($useDatatable) include '../../common/view/datatable.html.php'; if(!$execution->hasProduct and !$execution->multiple) @@ -253,6 +253,8 @@ foreach($setting as $key => $value) { if(!$execution->hasProduct and !$execution->multiple and $value->id == 'plan') continue; + if(!$execution->hasProduct and !$execution->multiple and $value->id == 'stage') continue; + if(!$execution->hasProduct and !$execution->multiple and $storyType == 'requirement' and $value->id == 'taskCount') continue; if($value->show) { $this->datatable->printHead($value, $orderBy, $vars, $canBatchAction); @@ -271,6 +273,8 @@ $value) { if(!$execution->hasProduct and !$execution->multiple and $value->id == 'plan') continue; + if(!$execution->hasProduct and !$execution->multiple and $value->id == 'stage') continue; + if(!$execution->hasProduct and !$execution->multiple and $storyType == 'requirement' and $value->id == 'taskCount') continue; $this->story->printCell($value, $story, $users, $branchOption, $storyStages, $modulePairs, $storyTasks, $storyBugs, $storyCases, $useDatatable ? 'datatable' : 'table', $storyType, $execution, $showBranch); } ?> @@ -285,6 +289,8 @@ $value) { if(!$execution->hasProduct and !$execution->multiple and $value->id == 'plan') continue; + if(!$execution->hasProduct and !$execution->multiple and $value->id == 'stage') continue; + if(!$execution->hasProduct and !$execution->multiple and $storyType == 'requirement' and $value->id == 'taskCount') continue; $this->story->printCell($value, $child, $users, $branchOption, $storyStages, $modulePairs, $storyTasks, $storyBugs, $storyCases, $useDatatable ? 'datatable' : 'table', $storyType, $execution); }?> diff --git a/module/execution/view/storykanban.html.php b/module/execution/view/storykanban.html.php index cae4f2b63a..89bacb292c 100644 --- a/module/execution/view/storykanban.html.php +++ b/module/execution/view/storykanban.html.php @@ -12,8 +12,9 @@ execution->confirmUnlinkStory)?> app->user->account; +$cols = array('projected', 'developing', 'developed', 'testing', 'tested', 'verified', 'released'); +$account = $this->app->user->account; +$canLinkStory = $execution->hasProduct or $execution->multiple; ?>