diff --git a/module/branch/model.php b/module/branch/model.php index 7ef8595578..17831e7d33 100644 --- a/module/branch/model.php +++ b/module/branch/model.php @@ -42,7 +42,9 @@ class branchModel extends model $branches = $this->dao->select('*')->from(TABLE_BRANCH)->where('product')->eq($productID)->andWhere('deleted')->eq(0)->orderBy('id_asc')->fetchPairs('id', 'name'); if(strpos($params, 'noempty') === false) { - $product = $this->loadModel('product')->getById($productID); + $product = $this->loadModel('product')->getById($productID); + if($product->type == 'normal') return array(); + $branches = array('0' => $this->lang->branch->all . $this->lang->product->branchName[$product->type]) + $branches; } return $branches; diff --git a/module/product/control.php b/module/product/control.php index 882173bacd..e9d3d67c79 100644 --- a/module/product/control.php +++ b/module/product/control.php @@ -183,13 +183,16 @@ class product extends control } $this->loadModel('search')->setSearchParams($this->config->product->search); + $storyIdList = array(); + foreach($stories as $story) $storyIdList[$story->id] = $story->id; + $this->view->productID = $productID; $this->view->productName = $this->products[$productID]; $this->view->moduleID = $moduleID; $this->view->stories = $stories; $this->view->plans = $this->loadModel('productplan')->getPairs($productID, $branch); $this->view->summary = $this->product->summary($stories); - $this->view->moduleTree = $this->tree->getTreeMenu($productID, $viewType = 'story', $startModuleID = 0, array('treeModel', 'createStoryLink')); + $this->view->moduleTree = $this->tree->getTreeMenu($productID, $viewType = 'story', $startModuleID = 0, array('treeModel', 'createStoryLink'), '', $branch); $this->view->parentModules = $this->tree->getParents($moduleID); $this->view->pager = $pager; $this->view->users = $this->user->getPairs('nodeleted|noletter|pofirst'); @@ -197,6 +200,8 @@ class product extends control $this->view->browseType = $browseType; $this->view->moduleID = $moduleID; $this->view->branch = $branch; + $this->view->branches = $this->loadModel('branch')->getPairs($productID); + $this->view->storyStages = $this->story->getStoryStages($storyIdList); $this->display(); } diff --git a/module/product/js/browse.js b/module/product/js/browse.js index 443d026e46..e65e846754 100644 --- a/module/product/js/browse.js +++ b/module/product/js/browse.js @@ -17,4 +17,7 @@ $(function() $option.toggleClass('hide', $option.text().toString().toLowerCase().indexOf(val) < 0 && $option.data('key').toString().toLowerCase().indexOf(val) < 0); }); }); + + $('.popoverStage').mouseover(function(){$(this).popover('show')}); + $('.popoverStage').mouseout(function(){$(this).popover('hide')}); }) diff --git a/module/product/view/browse.html.php b/module/product/view/browse.html.php index d04c67c570..635831fed4 100644 --- a/module/product/view/browse.html.php +++ b/module/product/view/browse.html.php @@ -58,7 +58,7 @@
-
icons['product']);?>
+
icons['product']);?>
@@ -100,14 +100,30 @@ id)); else printf('%03d', $story->id);?> story->priList, $story->pri, $story->pri);?>'>story->priList, $story->pri, $story->pri)?> - title);?> + + branch) echo "{$branches[$story->branch]}"?> + title);?> + planTitle;?> story->sourceList[$story->source];?> openedBy, $story->openedBy);?> assignedTo, $story->assignedTo);?> estimate;?> story->statusList[$story->status];?> - story->stageList[$story->stage];?> + + id]) ? " class='popoverStage' data-toggle='popover' data-placement='bottom' data-target='\$next'" : '') . "'>"; + echo $lang->story->stageList[$story->stage]; + if(isset($storyStages[$story->id])) echo ""; + echo '
'; + if(isset($storyStages[$story->id])) + { + echo "
"; + foreach($storyStages[$story->id] as $storyBranch => $storyStage) echo $branches[$storyBranch] . ": " . $lang->story->stageList[$storyStage->stage] . '
'; + echo "
"; + } + ?> + id}"; diff --git a/module/release/model.php b/module/release/model.php index 791410527a..7db0f3edf4 100644 --- a/module/release/model.php +++ b/module/release/model.php @@ -177,8 +177,8 @@ class releaseModel extends model $this->dao->update(TABLE_RELEASE)->set('stories')->eq($release->stories)->where('id')->eq((int)$releaseID)->exec(); if($release->stories) { - $this->dao->update(TABLE_STORY)->set('stage')->eq('released')->where('id')->in($release->stories)->exec(); - $this->dao->update(TABLE_STORYSTAGE)->set('stage')->eq('released')->where('story')->in($release->stories)->andWhere('branch')->eq($release->branch)->exec(); + $this->loadModel('story'); + foreach($this->post->stories as $storyID) $this->story->setStage($storyID); } } diff --git a/module/story/model.php b/module/story/model.php index 0586671dc6..8bd99e2f90 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -850,43 +850,46 @@ class storyModel extends model * Set stage of a story. * * @param int $storyID - * @param string $customStage * @access public * @return bool */ - public function setStage($storyID, $customStage = '') + public function setStage($storyID) { $storyID = (int)$storyID; - /* Custom stage defined, use it. */ - if($customStage) - { - $this->dao->update(TABLE_STORY)->set('stage')->eq($customStage)->where('id')->eq((int)$storyID)->exec(); - return true; - } /* Get projects which status is doing. */ $this->dao->delete()->from(TABLE_STORYSTAGE)->where('story')->eq($storyID)->exec(); + $story = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch(); + $product = $this->dao->findById($story->product)->from(TABLE_PRODUCT)->fetch(); $projects = $this->dao->select('t1.project,t3.branch')->from(TABLE_PROJECTSTORY)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t3')->on('t1.project = t3.project') ->where('t1.story')->eq($storyID) - ->andWhere('t2.status')->ne('done') ->andWhere('t2.deleted')->eq(0) ->fetchPairs('project', 'branch'); + $hasBranch = ($product->type != 'normal' and empty($story->branch)); + $stages = array(); + if($hasBranch and $story->plan) + { + $plans = $this->dao->select('*')->from(TABLE_PRODUCTPLAN)->where('id')->in($story->plan)->fetchPairs('branch', 'branch'); + foreach($plans as $branch) $stages[$branch] = 'planned'; + } + /* If no projects, in plan, stage is planned. No plan, wait. */ if(!$projects) { $this->dao->update(TABLE_STORY)->set('stage')->eq('wait')->where('id')->eq($storyID)->andWhere('plan')->eq('')->exec(); + + foreach($stages as $branch => $stage) $this->dao->insert(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq($stage)->exec(); $this->dao->update(TABLE_STORY)->set('stage')->eq('planned')->where('id')->eq($storyID)->andWhere('plan')->ne('')->exec(); return true; } - $story = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch(); - $product = $this->dao->findById($story->product)->from(TABLE_PRODUCT)->fetch(); - $branches = array(); - foreach($projects as $projectID => $branch) $branches[$branch] = $branch; - unset($branches[0]); + if($hasBranch) + { + foreach($projects as $projectID => $branch) $stages[$branch] = 'projected'; + } /* Search related tasks. */ $tasks = $this->dao->select('type,project,status')->from(TABLE_TASK) @@ -898,19 +901,14 @@ class storyModel extends model ->fetchGroup('type'); /* No tasks, then the stage is projected. */ - $hasBranch = ($product->type != 'normal' and empty($story->branch) and $branches); if(!$tasks) { - if($hasBranch) - { - foreach($branches as $branch) $this->dao->insert(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq('projected')->exec(); - } + foreach($stages as $branch => $stage) $this->dao->insert(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq('projected')->exec(); $this->dao->update(TABLE_STORY)->set('stage')->eq('projected')->where('id')->eq($storyID)->exec(); return true; } /* Get current stage and set as default value. */ - $taskProjects = array(); $currentStage = $story->stage; $stage = $currentStage; @@ -930,15 +928,14 @@ class storyModel extends model $branch = $projects[$task->project]; if(!isset($branchStatusList[$branch])) $branchStatusList[$branch] = $statusList; $branchStatusList[$branch][$task->type][$status] ++; - $taskProjects[$task->project] = $task->project; if($type == 'devel') { - if(!isset($develTasks[$branch])) $develTasks[$branch] = 0; + if(!isset($branchDevelTasks[$branch])) $branchDevelTasks[$branch] = 0; $branchDevelTasks[$branch] ++; } elseif($type == 'test') { - if(!isset($testTasks[$branch])) $testTasks[$branch] = 0; + if(!isset($branchTestTasks[$branch])) $branchTestTasks[$branch] = 0; $branchTestTasks[$branch] ++; } } @@ -963,14 +960,31 @@ class storyModel extends model if(($statusList['devel']['wait'] > 0 or $statusList['devel']['doing'] > 0) and $statusList['test']['done'] == $testTasks and $testTasks > 0) $stage = 'testing'; if($statusList['devel']['done'] == $develTasks and $develTasks > 0 and $statusList['test']['done'] == $testTasks and $testTasks > 0) $stage = 'tested'; - if($hasBranch and $branch) $this->dao->insert(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq($stage)->exec(); - $this->dao->update(TABLE_STORY)->set('stage')->eq($stage)->where('id')->eq($storyID)->exec(); + $stages[$branch] = $stage; } - foreach($projects as $projectID => $branch) + $releases = $this->dao->select('*')->from(TABLE_RELEASE)->where("CONCAT(',', stories, ',')")->like("%,$storyID,%")->andWhere('deleted')->eq(0)->fetchPairs('branch', 'branch'); + foreach($releases as $branch) $stages[$branch] = 'released'; + + if($hasBranch) { - if(isset($taskProjects[$projectID])) continue; - $this->dao->insert(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq('projected')->exec(); + $stageList = join(',', array_keys($this->lang->story->stageList)); + $minStagePos = strlen($stageList); + $minStage = ''; + foreach($stages as $branch => $stage) + { + $this->dao->insert(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq($stage)->exec(); + if(strpos($stageList, $stage) !== false and strpos($stageList, $stage) < $minStagePos) + { + $minStage = $stage; + $minStagePos = strpos($stageList, $stage); + } + } + $this->dao->update(TABLE_STORY)->set('stage')->eq($minStage)->where('id')->eq($storyID)->exec(); + } + else + { + $this->dao->update(TABLE_STORY)->set('stage')->eq(current($stages))->where('id')->eq($storyID)->exec(); } return; @@ -993,10 +1007,11 @@ class storyModel extends model { unset($branch[0]); $branch = join(',', $branch); + if($branch) $branch = "0,$branch"; } $stories = $this->dao->select('*')->from(TABLE_STORY) ->where('product')->in($productID) - ->beginIF($branch)->andWhere("branch")->in("0,$branch")->fi() + ->beginIF($branch)->andWhere("branch")->in("$branch")->fi() ->beginIF(!empty($moduleIds))->andWhere('module')->in($moduleIds)->fi() ->beginIF($status and $status != 'all')->andWhere('status')->in($status)->fi() ->andWhere('deleted')->eq(0) @@ -1476,6 +1491,20 @@ class storyModel extends model return $allStories; } + /** + * Get story stages. + * + * @param array $stories + * @access public + * @return array + */ + public function getStoryStages($stories) + { + return $this->dao->select('*')->from(TABLE_STORYSTAGE) + ->where('story')->in($stories) + ->fetchGroup('story', 'branch'); + } + /** * Check need confirm. * @@ -1826,16 +1855,27 @@ class storyModel extends model public function mergePlanTitle($productID, $stories, $branch = 0) { $query = $this->dao->get(); + if(is_array($branch)) + { + unset($branch[0]); + $branch = join(',', $branch); + if($branch) $branch = "0,$branch"; + } $plans = $this->dao->select('id,title')->from(TABLE_PRODUCTPLAN) ->where('product')->in($productID) - ->beginIF($branch)->andWhere('branch')->in("0,$branch")->fi() + ->beginIF($branch)->andWhere('branch')->in($branch)->fi() ->andWhere('deleted')->eq(0) ->fetchPairs('id', 'title'); + $stages = $this->dao->select('*')->from(TABLE_STORYSTAGE)->where('branch')->in($branch)->fetchGroup('story', 'branch'); + + $branch = trim(str_replace(',0,', '', ",$branch,"), ','); + $branch = empty($branch) ? 0 : $branch; foreach($stories as $story) { $story->planTitle = ''; $storyPlans = explode(',', trim($story->plan, ',')); foreach($storyPlans as $planID) $story->planTitle .= zget($plans, $planID) . ' '; + if(empty($story->branch) and isset($stages[$story->id][$branch])) $story->stage = $stages[$story->id][$branch]->stage; } /* For save session query. */ diff --git a/module/tree/control.php b/module/tree/control.php index 760f9a6797..c34909b326 100644 --- a/module/tree/control.php +++ b/module/tree/control.php @@ -30,7 +30,6 @@ class tree extends control $product = $this->loadModel('product')->getById($rootID); if($product->type != 'normal') $this->view->branches = $this->loadModel('branch')->getPairs($product->id); $this->view->root = $product; - $this->view->productModules = $this->tree->getOptionMenu($rootID, 'story'); } /* The viewType is doc. */ elseif(strpos($viewType, 'doc') !== false) @@ -65,6 +64,7 @@ class tree extends control $this->view->allProduct = $products; $this->view->currentProduct = $currentProduct; + $this->view->productModules = $this->tree->getOptionMenu($currentProduct, 'story'); $title = $product->name . $this->lang->colon . $this->lang->tree->manageProduct; $position[] = html::a($this->createLink('product', 'browse', "product=$rootID"), $product->name); diff --git a/module/tree/js/browse.js b/module/tree/js/browse.js index 9a9e395070..9ff2e8ba4e 100644 --- a/module/tree/js/browse.js +++ b/module/tree/js/browse.js @@ -22,7 +22,7 @@ function syncModule(rootID, type) { if(value) { - $('#sonModule .input-group:last').after($inputgroup); + $('#sonModule').append($inputgroup); $('#sonModule .input-group:last input').val(value); } }) diff --git a/module/tree/model.php b/module/tree/model.php index 547664fb03..ac40473241 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -271,15 +271,16 @@ class treeModel extends model { $branches = array($branch => ''); $manage = $userFunc[1] == 'createManageLink' ? true : false; + $product = $this->loadModel('product')->getById($rootID); if(strpos('story|bug|case', $type) !== false and empty($branch)) { - $product = $this->loadModel('product')->getById($rootID); if($product->type != 'normal') $branches = array('null' => '') + $this->loadModel('branch')->getPairs($rootID, 'noempty'); } /* Add for task #1945. check the module has case or no. */ if($type == 'case' and !empty($extra)) $this->loadModel('testtask'); - $lastMenu = ''; + $lastMenu = ''; + $firstBranch = true; foreach($branches as $branchID => $branch) { $treeMenu = array(); @@ -290,6 +291,11 @@ class treeModel extends model if(!empty($branchID) and $branch and $branchID != 'null') { $linkHtml = $manage ? html::a(inlink('browse', "root=$rootID&viewType=$type¤tModuleID=0&branch=$branchID"), $branch) : $this->createBranchLink($type, $rootID, $branchID, $branch); + if($firstBranch and $product->type != 'normal') + { + $linkHtml = $this->lang->product->branchName[$product->type] . '
  • ' . $linkHtml; + $firstBranch = false; + } $lastMenu .= "
  • $linkHtml
      " . @array_shift($treeMenu) . "
  • \n"; } else @@ -298,6 +304,7 @@ class treeModel extends model } } + if(!$firstBranch) $lastMenu .= '
'; $lastMenu = "
    $lastMenu
\n"; return $lastMenu; } diff --git a/module/tree/view/browse.html.php b/module/tree/view/browse.html.php index a5cd44701d..ed43bb7f75 100644 --- a/module/tree/view/browse.html.php +++ b/module/tree/view/browse.html.php @@ -86,7 +86,7 @@ echo html::input("modules[]", '', 'class="form-control"'); if($hasBranch) echo '' . html::select("branch[]", $branches, $branch, 'class="form-control"'); echo ""; - echo ""; + echo ""; echo '
'; } ?>