diff --git a/config/filter.php b/config/filter.php index c368da4405..95c366a47d 100755 --- a/config/filter.php +++ b/config/filter.php @@ -271,6 +271,7 @@ $filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy'; $filter->project->task->cookie['windowWidth'] = 'int'; $filter->project->export->cookie['checkedItem'] = 'reg::checked'; $filter->project->execution->cookie['pagerExecutionAll'] = 'int'; +$filter->project->execution->cookie['showTask'] = 'code'; $filter->projectstory->story->cookie['storyModuleParam'] = 'int'; $filter->projectstory->story->cookie['pagerProductBrowse'] = 'int'; diff --git a/db/update17.2.sql b/db/update17.2.sql index b179886a63..766260a619 100644 --- a/db/update17.2.sql +++ b/db/update17.2.sql @@ -14,3 +14,5 @@ ALTER TABLE `zt_doc` CHANGE `assignedDate` `assignedDate` datetime NOT NULL AFTE UPDATE `zt_approval` SET `createdDate` = ''; ALTER TABLE `zt_approval` CHANGE `createdDate` `createdDate` datetime NOT NULL AFTER `createdBy`; + +ALTER TABLE `zt_reviewissue` ADD `approval` MEDIUMINT NOT NULL AFTER `review`; diff --git a/db/zentao.sql b/db/zentao.sql index 14814badb6..0b05f10896 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -9526,6 +9526,7 @@ CREATE TABLE IF NOT EXISTS `zt_reviewissue` ( `id` mediumint(8) NOT NULL AUTO_INCREMENT, `project` mediumint(8) unsigned NOT NULL, `review` mediumint(8) NOT NULL, + `approval` mediumint(8) NOT NULL, `injection` mediumint(8) NOT NULL, `identify` mediumint(8) NOT NULL, `type` char(30) NOT NULL DEFAULT 'review', diff --git a/module/action/lang/de.php b/module/action/lang/de.php index 988570d7ce..d3f0dbfedd 100644 --- a/module/action/lang/de.php +++ b/module/action/lang/de.php @@ -209,6 +209,7 @@ $lang->action->desc->importedbuild = '$date, imported to $extraaction->desc->fromsonarqube = '$date, created by $actor from SonarQube Issue.' . "\n"; $lang->action->desc->tolib = '$date, imported by $actor .' . "\n"; $lang->action->desc->updatetolib = '$date, updated to ' . $lang->testcase->common . ' by $actor.' . "\n"; +$lang->action->desc->adjusttasktowait = '$date, System Reminder: The task status will be set to Not Started as the consumed work hour is adjusted to 0. ' . "\n"; /* Used to describe the history of operations related to parent-child tasks. */ $lang->action->desc->createchildren = '$date, $actor created a child task $extra。' . "\n"; @@ -305,7 +306,7 @@ $lang->action->label->suspended = 'suspended '; $lang->action->label->login = 'Login'; $lang->action->label->logout = "Logout"; $lang->action->label->notified = "Notified"; -$lang->action->label->deleteestimate = "deleted "; +$lang->action->label->deleteestimate = "deleted hours"; $lang->action->label->linked2build = "linked "; $lang->action->label->linked2bug = "linked "; $lang->action->label->linked2testtask = "linked"; diff --git a/module/action/lang/en.php b/module/action/lang/en.php index e195c73ad7..0ab354241a 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -209,6 +209,7 @@ $lang->action->desc->importedbuild = '$date, imported to $extraaction->desc->fromsonarqube = '$date, created by $actor from SonarQube Issue.' . "\n"; $lang->action->desc->tolib = '$date, imported by $actor .' . "\n"; $lang->action->desc->updatetolib = '$date, updated to ' . $lang->testcase->common . ' by $actor.' . "\n"; +$lang->action->desc->adjusttasktowait = '$date, System Reminder: The task status will be set to Not Started as the consumed work hour is adjusted to 0. ' . "\n"; /* Used to describe the history of operations related to parent-child tasks. */ $lang->action->desc->createchildren = '$date, $actor created a child task $extra。' . "\n"; @@ -305,7 +306,7 @@ $lang->action->label->suspended = 'suspended '; $lang->action->label->login = 'login'; $lang->action->label->logout = "logout"; $lang->action->label->notified = "Notified"; -$lang->action->label->deleteestimate = "deleted "; +$lang->action->label->deleteestimate = "deleted hours"; $lang->action->label->linked2build = "linked "; $lang->action->label->linked2bug = "linked "; $lang->action->label->linked2testtask = "linked"; diff --git a/module/action/lang/fr.php b/module/action/lang/fr.php index fb1c45ebc5..01c2a52222 100644 --- a/module/action/lang/fr.php +++ b/module/action/lang/fr.php @@ -209,6 +209,7 @@ $lang->action->desc->importedbuild = '$date, imported to $extraaction->desc->fromsonarqube = '$date, created by $actor from SonarQube Issue.' . "\n"; $lang->action->desc->tolib = '$date, Importé par $actor .' . "\n"; $lang->action->desc->updatetolib = '$date, MàJ de ' . $lang->testcase->common . ' par $actor.' . "\n"; +$lang->action->desc->adjusttasktowait = '$date, System Reminder: The task status will be set to Not Started as the consumed work hour is adjusted to 0. ' . "\n"; /* Used to describe the history of operations related to parent-child tasks. */ $lang->action->desc->createchildren = '$date, $actor a créé un sous-tâche $extra。' . "\n"; diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 4b332524af..a9011287c3 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -209,6 +209,7 @@ $lang->action->desc->importedbuild = '$date, 由 $actor $lang->action->desc->fromsonarqube = '$date, 由 $actorSonarQube问题转化而来。' . "\n"; $lang->action->desc->tolib = '$date, 由 $actor 导入。' . "\n"; $lang->action->desc->updatetolib = '$date, 由 $actor 从' . $lang->testcase->common . '更新。' . "\n"; +$lang->action->desc->adjusttasktowait = '$date, 系统判断由于消耗工时调整为0,将任务状态置为未开始。' . "\n"; /* 用来描述和父子任务相关的操作历史记录。*/ $lang->action->desc->createchildren = '$date, 由 $actor 创建子任务 $extra。' . "\n"; diff --git a/module/block/lang/en.php b/module/block/lang/en.php index 6d6f7ec078..835b29776e 100644 --- a/module/block/lang/en.php +++ b/module/block/lang/en.php @@ -38,7 +38,7 @@ $lang->block->lblBlock = 'Block'; $lang->block->lblNum = 'Number'; $lang->block->lblHtml = 'HTML'; $lang->block->dynamic = 'Dynamics'; -$lang->block->assignToMe = 'Todo'; +$lang->block->assignToMe = 'Work'; $lang->block->wait = 'Wait'; $lang->block->doing = 'Doing'; $lang->block->done = 'Done'; diff --git a/module/block/view/dynamic.html.php b/module/block/view/dynamic.html.php index 2aa17ecc16..e2e44aac2d 100644 --- a/module/block/view/dynamic.html.php +++ b/module/block/view/dynamic.html.php @@ -18,6 +18,7 @@ $i = 0; foreach($actions as $action) { + if($action->action == 'adjusttasktowait') continue; $user = zget($users, $action->actor); if($action->action == 'login' or $action->action == 'logout') $action->objectName = $action->objectLabel = ''; if($action->objectType == 'sonarqubeproject') $action->objectName = $action->extra; diff --git a/module/branch/control.php b/module/branch/control.php index e0816d28ff..6f497f7abb 100644 --- a/module/branch/control.php +++ b/module/branch/control.php @@ -274,7 +274,7 @@ class branch extends control { $branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : ''); } - if(!isset($branchTagOption[$oldBranch])) + if(is_numeric($oldBranch) and !isset($branchTagOption[$oldBranch])) { $branch = $this->branch->getById($oldBranch, $productID, ''); $branchTagOption[$oldBranch] = $oldBranch == BRANCH_MAIN ? $branch : ($branch->name . ($branch->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '')); diff --git a/module/bug/control.php b/module/bug/control.php index 38e223697c..1893bc67df 100755 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -1390,11 +1390,12 @@ class bug extends control * Update assign of bug. * * @param int $bugID - * @parm string $kanbanGroup + * @param string $kanbanGroup + * @param string $from taskkanban * @access public * @return void */ - public function assignTo($bugID, $kanbanGroup = 'default') + public function assignTo($bugID, $kanbanGroup = 'default', $from = '') { $bug = $this->bug->getById($bugID); $this->bug->checkBugExecutionPriv($bug); @@ -1414,28 +1415,24 @@ class bug extends control if(isonlybody()) { - $bug = $this->bug->getById($bugID); - $execution = $this->loadModel('execution')->getByID($bug->execution); - if($this->app->tab == 'execution') + $bug = $this->bug->getById($bugID); + $execution = $this->loadModel('execution')->getByID($bug->execution); + $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; + $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; + $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; + if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban') { - $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; - $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; - $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; - - if(isset($execution->type) and $execution->type == 'kanban') - { - $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', 0, $kanbanGroup, $rdSearchValue); - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)")); - } - else - { - $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); - $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); - $kanbanData = $kanbanData[$kanbanType]; - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); - } + $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', 0, $kanbanGroup, $rdSearchValue); + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)")); + } + elseif($from == 'taskkanban') + { + $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); + $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); + $kanbanData = $kanbanData[$kanbanType]; + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); } else { @@ -1627,10 +1624,11 @@ class bug extends control * * @param int $bugID * @param string $extra + * @param string $from taskkanban * @access public * @return void */ - public function confirmBug($bugID, $extra = '') + public function confirmBug($bugID, $extra = '', $from = '') { $bug = $this->bug->getById($bugID); if(!empty($_POST)) @@ -1646,28 +1644,24 @@ class bug extends control parse_str($extra, $output); if(isonlybody()) { - $execution = $this->loadModel('execution')->getByID($bug->execution); - if($this->app->tab == 'execution') + $execution = $this->loadModel('execution')->getByID($bug->execution); + $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; + $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; + $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; + if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban') { - $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; - $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; - $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; - - if(isset($execution->type) and $execution->type == 'kanban') - { - $regionID = !empty($output['regionID']) ? $output['regionID'] : 0; - $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue); - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)")); - } - else - { - $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); - $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); - $kanbanData = $kanbanData[$kanbanType]; - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); - } + $regionID = !empty($output['regionID']) ? $output['regionID'] : 0; + $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue); + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)")); + } + elseif($from == 'taskkanban') + { + $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); + $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); + $kanbanData = $kanbanData[$kanbanType]; + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); } else { @@ -1714,10 +1708,11 @@ class bug extends control * * @param int $bugID * @param string $extra + * @param string $from taskkanban * @access public * @return void */ - public function resolve($bugID, $extra = '') + public function resolve($bugID, $extra = '', $from = '') { $bug = $this->bug->getById($bugID); if($bug->execution) $execution = $this->loadModel('execution')->getByID($bug->execution); @@ -1753,27 +1748,23 @@ class bug extends control parse_str($extra, $output); if(isonlybody()) { - if($this->app->tab == 'execution') + $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; + $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; + $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; + if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban') { - $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; - $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; - $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; - if(isset($execution->type) and $execution->type == 'kanban') - { - $regionID = !empty($output['regionID']) ? $output['regionID'] : 0; - $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue); - $kanbanData = json_encode($kanbanData); - - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)")); - } - else - { - $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); - $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); - $kanbanData = $kanbanData[$kanbanType]; - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); - } + $regionID = !empty($output['regionID']) ? $output['regionID'] : 0; + $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue); + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)")); + } + elseif($from == 'taskkanban') + { + $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); + $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); + $kanbanData = $kanbanData[$kanbanType]; + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); } else { @@ -1842,10 +1833,11 @@ class bug extends control * * @param int $bugID * @param string $extra + * @param string $from taskkanban * @access public * @return void */ - public function activate($bugID, $extra = '') + public function activate($bugID, $extra = '', $from = '') { $bug = $this->bug->getById($bugID); if(!empty($_POST)) @@ -1866,27 +1858,23 @@ class bug extends control if(isonlybody()) { $execution = $this->loadModel('execution')->getByID($bug->execution); - if($this->app->tab == 'execution') + $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; + $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; + $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; + if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban') { - $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; - $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; - $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; - - if(isset($execution->type) and $execution->type == 'kanban') - { - $regionID = !empty($output['regionID']) ? $output['regionID'] : 0; - $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue); - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)")); - } - else - { - $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); - $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); - $kanbanData = $kanbanData[$kanbanType]; - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); - } + $regionID = !empty($output['regionID']) ? $output['regionID'] : 0; + $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue); + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)")); + } + elseif($from == 'taskkanban') + { + $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); + $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); + $kanbanData = $kanbanData[$kanbanType]; + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); } else { @@ -1917,10 +1905,11 @@ class bug extends control * * @param int $bugID * @param string $extra + * @param string $from taskkanban * @access public * @return void */ - public function close($bugID, $extra = '') + public function close($bugID, $extra = '', $from = '') { $bug = $this->bug->getById($bugID); if(!empty($_POST)) @@ -1937,28 +1926,24 @@ class bug extends control parse_str($extra, $output); if(isonlybody()) { - $execution = $this->loadModel('execution')->getByID($bug->execution); - if($this->app->tab == 'execution') + $execution = $this->loadModel('execution')->getByID($bug->execution); + $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; + $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; + $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; + if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban') { - $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; - $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; - $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; - if(isset($execution->type) and $execution->type == 'kanban') - { - $regionID = !empty($output['regionID']) ? $output['regionID'] : 0; - $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue); - $kanbanData = json_encode($kanbanData); - - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)")); - } - else - { - $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); - $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); - $kanbanData = $kanbanData[$kanbanType]; - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); - } + $regionID = !empty($output['regionID']) ? $output['regionID'] : 0; + $kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue); + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)")); + } + elseif($from == 'taskkanban') + { + $kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue); + $kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData); + $kanbanData = $kanbanData[$kanbanType]; + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)")); } else { @@ -2037,11 +2022,14 @@ class bug extends control /** * Batch close bugs. * + * @param int $releaseID + * @param string $viewType * @access public * @return void */ - public function batchClose() + public function batchClose($releaseID = '', $viewType = '') { + if($releaseID) $this->post->bugIDList = $this->post->unlinkBugs; if($this->post->bugIDList) { $bugIDList = $this->post->bugIDList; @@ -2066,6 +2054,10 @@ class bug extends control } $this->loadModel('score')->create('ajax', 'batchOther'); if(isset($skipBugs)) echo js::alert(sprintf($this->lang->bug->skipClose, join(',', $skipBugs))); + if($viewType) + { + return print(js::locate($this->createLink($viewType, 'view', "releaseID=$releaseID&type=bug"), 'parent')); + } } return print(js::reload('parent')); } diff --git a/module/caselib/model.php b/module/caselib/model.php index 54b4a8bb71..977bc56463 100644 --- a/module/caselib/model.php +++ b/module/caselib/model.php @@ -28,6 +28,8 @@ class caselibModel extends model if(!empty($products) and $this->session->product) $this->loadModel('qa')->setMenu($products, $this->session->product); if(empty($products)) $this->loadModel('qa')->setMenu(array(0 => ''), 0); + $this->lang->qa->menu->caselib['subModule'] .= ',testcase'; + if($libraries) { $libName = ''; diff --git a/module/common/lang/menu.php b/module/common/lang/menu.php index 0e4bf624be..12a8020011 100644 --- a/module/common/lang/menu.php +++ b/module/common/lang/menu.php @@ -245,7 +245,7 @@ $lang->scrum->menu->settings['subMenu']->group = array('link' => "{$lang-> /* Waterfall menu. */ $lang->waterfall->menu = new stdclass(); $lang->waterfall->menu->index = array('link' => "$lang->dashboard|project|index|project=%s"); -$lang->waterfall->menu->execution = array('link' => "{$lang->stage->common}|project|execution|status=all&projectID=%s", 'subModule' => 'programplan'); +$lang->waterfall->menu->execution = array('link' => "{$lang->stage->common}|project|execution|status=all&projectID=%s", 'subModule' => 'programplan,task'); $lang->waterfall->menu->story = array('link' => "$lang->SRCommon|projectstory|story|project=%s", 'subModule' => 'projectstory,tree', 'exclude' => 'projectstory-track'); $lang->waterfall->menu->design = array('link' => "{$lang->design->common}|design|browse|project=%s"); $lang->waterfall->menu->qa = array('link' => "{$lang->qa->common}|project|bug|projectID=%s", 'subModule' => 'testcase,testtask,bug,testreport', 'alias' => 'bug,testtask,testcase,testreport'); diff --git a/module/company/view/dynamic.html.php b/module/company/view/dynamic.html.php index d8f5338795..0b4a7e45ad 100644 --- a/module/company/view/dynamic.html.php +++ b/module/company/view/dynamic.html.php @@ -64,6 +64,7 @@
    $action):?> + action == 'adjusttasktowait') continue;?>
  • major) echo "class='active'";?>>
    diff --git a/module/custom/css/execution.css b/module/custom/css/execution.css new file mode 100644 index 0000000000..c72631bb78 --- /dev/null +++ b/module/custom/css/execution.css @@ -0,0 +1,2 @@ +#readOnlyOfExecution {font-size: 13px; color: #5e626d;} +#readOnlyOfExecution i {font-size: 14px; color: #0075ff; margin-right: 3px;} diff --git a/module/custom/css/kanban.css b/module/custom/css/kanban.css new file mode 100644 index 0000000000..92d15f96d7 --- /dev/null +++ b/module/custom/css/kanban.css @@ -0,0 +1,2 @@ +#readOnlyOfKanban {font-size: 13px; color: #5e626d;} +#readOnlyOfKanban i {font-size: 14px; color: #0075ff; margin-right: 3px;} diff --git a/module/custom/css/product.css b/module/custom/css/product.css new file mode 100644 index 0000000000..777bce5dff --- /dev/null +++ b/module/custom/css/product.css @@ -0,0 +1,2 @@ +#readOnlyOfProduct {font-size: 13px; color: #5e626d;} +#readOnlyOfProduct i {font-size: 14px; color: #0075ff; margin-right: 3px;} diff --git a/module/custom/view/browsestoryconcept.html.php b/module/custom/view/browsestoryconcept.html.php index 5327fdae85..7d2a4e7c2a 100644 --- a/module/custom/view/browsestoryconcept.html.php +++ b/module/custom/view/browsestoryconcept.html.php @@ -44,9 +44,10 @@ - custom->URSR ? "disabled=disabled" : '';?> + custom->URSR ? "disabled=disabled" : '';?> + createLink('custom', 'deleteStoryConcept', "id=$key") : '#';?> createLink('custom', 'editStoryConcept', "id=$key", '', true), "", '', "class='btn iframe' data-width=" . ($this->config->URAndSR ? "480px" : "330px") . " title={$lang->edit}");?> - createLink('custom', 'deleteStoryConcept', "id=$key"), "", 'hiddenwin', "class='btn' $disabled title={$lang->delete} data-group='admin'");?> + ", 'hiddenwin', "class='btn' $disabled title={$lang->delete} data-group='admin'");?> diff --git a/module/custom/view/execution.html.php b/module/custom/view/execution.html.php index 24d7b5a58c..2d42966221 100644 --- a/module/custom/view/execution.html.php +++ b/module/custom/view/execution.html.php @@ -21,7 +21,13 @@ - custom->notice->readOnlyOfExecution;?> + + + + + +  custom->notice->readOnlyOfExecution;?> + diff --git a/module/custom/view/kanban.html.php b/module/custom/view/kanban.html.php index 495bef43ea..a1a6446a49 100644 --- a/module/custom/view/kanban.html.php +++ b/module/custom/view/kanban.html.php @@ -21,7 +21,13 @@ - custom->notice->readOnlyOfKanban;?> + + + + + +  custom->notice->readOnlyOfKanban;?> + diff --git a/module/custom/view/product.html.php b/module/custom/view/product.html.php index bcdd452301..aa592a79fd 100644 --- a/module/custom/view/product.html.php +++ b/module/custom/view/product.html.php @@ -21,7 +21,13 @@ -
    custom->notice->readOnlyOfProduct;?>
    + + + + + +  custom->notice->readOnlyOfProduct;?> + diff --git a/module/dev/view/api.html.php b/module/dev/view/api.html.php index 6e2aae1da7..fd85489d17 100644 --- a/module/dev/view/api.html.php +++ b/module/dev/view/api.html.php @@ -54,9 +54,9 @@ - - - + + + @@ -78,9 +78,9 @@
    dev->params?>dev->type?>dev->desc?>dev->params?>dev->type?>dev->desc?>
    - - - + + + dev->postParams[$selectedModule][$methodName] as $paramName => $paramType):?> diff --git a/module/execution/control.php b/module/execution/control.php index 637f63c38b..965e8cdabd 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -107,7 +107,7 @@ class execution extends control * @access public * @return void */ - public function task($executionID = 0, $status = 'unclosed', $param = 0, $orderBy = '', $recTotal = 0, $recPerPage = 100, $pageID = 1) + public function task($executionID = 0, $status = 'all', $param = 0, $orderBy = '', $recTotal = 0, $recPerPage = 100, $pageID = 1) { $this->loadModel('tree'); $this->loadModel('search'); @@ -219,8 +219,14 @@ class execution extends control $showModule = !empty($this->config->datatable->executionTask->showModule) ? $this->config->datatable->executionTask->showModule : ''; $this->view->modulePairs = $showModule ? $this->tree->getModulePairs($executionID, 'task', $showModule) : array(); + $allTasksNum = $this->dao->select('COUNT(id) AS count')->from(TABLE_TASK) + ->where('execution')->eq($executionID) + ->andWhere('deleted')->eq(0) + ->fetch(); + /* Assign. */ $this->view->tasks = $tasks; + $this->view->allTasksNum = $allTasksNum; $this->view->summary = $this->execution->summary($tasks); $this->view->tabID = 'task'; $this->view->pager = $pager; @@ -1364,10 +1370,13 @@ class execution extends control * * @param int $executionID * @param string $type + * @param string $withWeekend + * @param string $begin + * @param string $end * @access public * @return void */ - public function cfd($executionID = 0, $type = 'story', $withWeekend = 'false') + public function cfd($executionID = 0, $type = 'story', $withWeekend = 'false', $begin = '', $end = '') { $execution = $this->commonAction($executionID); $executionID = $execution->id; @@ -1375,7 +1384,30 @@ class execution extends control $this->loadModel('kanban'); $this->app->loadClass('date'); - list($begin, $end) = $this->execution->getBeginEnd4CFD($execution); + if(!empty($_POST)) + { + $begin = htmlspecialchars($this->post->begin, ENT_QUOTES); + $end = htmlspecialchars($this->post->end, ENT_QUOTES); + + if(empty($begin)) return $this->sendError(sprintf($this->lang->error->notempty, $this->lang->execution->charts->cfd->begin)); + if(empty($end)) return $this->sendError(sprintf($this->lang->error->notempty, $this->lang->execution->charts->cfd->end)); + if($begin >= $end) return $this->sendError($this->lang->execution->charts->cfd->errorBegin); + if(date("Y-m-d", strtotime("-3 months", strtotime($end))) > $begin) return $this->sendError($this->lang->execution->charts->cfd->errorDateRange); + + $this->execution->computeCFD($executionID); + $this->execution->checkCFDData($executionID, $begin); + return print(js::locate($this->createLink('execution', 'cfd', "executionID=$executionID&type=$type&withWeekend=$withWeekend&begin=" . helper::safe64Encode(urlencode($begin)) . "&end=" . helper::safe64Encode(urlencode($end))), 'parent')); + } + + if($begin and $end) + { + $begin = urldecode(helper::safe64Decode($begin)); + $end = urldecode(helper::safe64Decode($end)); + } + else + { + list($begin, $end) = $this->execution->getBeginEnd4CFD($execution); + } $dateList = date::getDateList($begin, $end, 'Y-m-d', $withWeekend == 'false'? 'noweekend' : ''); //list($cycleTimeAvg, $throughput) = $this->execution->getCFDStatistics($executionID, $dateList, $type); @@ -1390,6 +1422,8 @@ class execution extends control $this->view->executionName = $execution->name; $this->view->executionID = $executionID; $this->view->chartData = $chartData; + $this->view->begin = $begin; + $this->view->end = $end; $this->display(); } @@ -2442,11 +2476,12 @@ class execution extends control unset($this->lang->kanban->type['all']); } - $kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy); + if($groupBy == 'story' and $browseType == 'task' and !isset($this->lang->kanban->orderList[$orderBy])) $orderBy = 'pri_asc'; + $kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy); if(empty($kanbanGroup)) { $this->kanban->createExecutionLane($executionID, $browseType, $groupBy); - $kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy); + $kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy); } /* Determines whether an object is editable. */ @@ -3194,15 +3229,16 @@ class execution extends control * @param int $executionID * @param int $storyID * @param string $confirm yes|no + * @param string $from taskkanban * @access public * @return void */ - public function unlinkStory($executionID, $storyID, $confirm = 'no') + public function unlinkStory($executionID, $storyID, $confirm = 'no', $from = '') { if($confirm == 'no') { $tip = $this->app->rawModule == 'projectstory' ? $this->lang->execution->confirmUnlinkExecutionStory : $this->lang->execution->confirmUnlinkStory; - return print(js::confirm($tip, $this->createLink('execution', 'unlinkstory', "executionID=$executionID&storyID=$storyID&confirm=yes"))); + return print(js::confirm($tip, $this->createLink('execution', 'unlinkstory', "executionID=$executionID&storyID=$storyID&confirm=yes&from=$from"))); } else { @@ -3224,26 +3260,23 @@ class execution extends control return $this->send($response); } - $execution = $this->execution->getByID($executionID); - if($this->app->tab == 'execution') + $execution = $this->execution->getByID($executionID); + $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; + $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; + $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; + if($this->app->tab == 'execution' and $execution->type == 'kanban') { - $execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all'; - $execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default'; - $rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : ''; - if($execution->type == 'kanban') - { - $kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue); - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent', '', "parent.updateKanban($kanbanData)")); - } - else - { - $kanbanData = $this->loadModel('kanban')->getExecutionKanban($executionID, $execLaneType, $execGroupBy); - $kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData); - $kanbanData = $kanbanData[$kanbanType]; - $kanbanData = json_encode($kanbanData); - return print(js::closeModal('parent', '', "parent.updateKanban(\"story\", $kanbanData)")); - } + $kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue); + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent', '', "parent.updateKanban($kanbanData)")); + } + elseif($from == 'taskkanban') + { + $kanbanData = $this->loadModel('kanban')->getExecutionKanban($executionID, $execLaneType, $execGroupBy); + $kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData); + $kanbanData = $kanbanData[$kanbanType]; + $kanbanData = json_encode($kanbanData); + return print(js::closeModal('parent', '', "parent.updateKanban(\"story\", $kanbanData)")); } return print(js::reload('parent')); @@ -3635,18 +3668,6 @@ class execution extends control $from = $this->app->tab; if($from == 'execution') $this->session->set('executionList', $this->app->getURI(true), 'execution'); - if($from == 'project') - { - $projects = $this->project->getPairsByProgram(); - $projectID = $this->project->saveState($projectID, $projects); - $project = $this->loadModel('project')->getByID($projectID); - $this->view->project = $project; - $this->project->setMenu($projectID); - - if(!$projectID) return print(js::locate($this->createLink('project', 'browse'))); - if(!empty($project->model) and $project->model == 'kanban' and !(defined('RUN_MODE') and RUN_MODE == 'api')) return print(js::locate($this->createLink('project', 'index', "projectID=$projectID"))); - } - if($this->app->viewType == 'mhtml') { if($this->app->rawModule == 'project' and $this->app->rawMethod == 'execution') @@ -3680,6 +3701,9 @@ class execution extends control $parents = array(); if($parentIdList) $parents = $this->execution->getByIdList($parentIdList); + $allExecutionsNum = $this->project->getStats($projectID, 'all'); + $this->view->allExecutionsNum = count($allExecutionsNum); + $this->view->executionStats = $executionStats; $this->view->productList = $this->loadModel('product')->getProductPairsByProject($projectID); $this->view->productID = $productID; @@ -3939,8 +3963,8 @@ class execution extends control $projectID = $this->dao->findByID($executionID)->from(TABLE_EXECUTION)->fetch('project'); $planStories = array_keys($planStory); - $this->execution->linkStory($executionID, $planStories, $planProducts, $extra); if($this->config->systemMode == 'new' and $executionID != $projectID) $this->execution->linkStory($projectID, $planStories, $planProducts); + $this->execution->linkStory($executionID, $planStories, $planProducts, $extra); } $moduleName = 'execution'; @@ -4072,11 +4096,15 @@ class execution extends control * @param string $groupBy * @param string $from execution|RD * @param string $searchValue + * @param string $orderBy * @access public * @return array */ - public function ajaxUpdateKanban($executionID = 0, $enterTime = '', $browseType = '', $groupBy = '', $from = 'execution', $searchValue = '') + public function ajaxUpdateKanban($executionID = 0, $enterTime = '', $browseType = '', $groupBy = '', $from = 'execution', $searchValue = '', $orderBy = 'id_asc') { + $this->loadModel('kanban'); + if($groupBy == 'story' and $browseType == 'task' and !isset($this->lang->kanban->orderList[$orderBy])) $orderBy = 'pri_asc'; + $enterTime = date('Y-m-d H:i:s', $enterTime); $lastEditedTime = $this->dao->select("max(lastEditedTime) as lastEditedTime")->from(TABLE_KANBANLANE)->where('execution')->eq($executionID)->fetch('lastEditedTime'); @@ -4084,7 +4112,7 @@ class execution extends control if($from == 'RD') $this->session->set('rdSearchValue', $searchValue); if(strtotime($lastEditedTime) < 0 or $lastEditedTime > $enterTime or $groupBy != 'default' or !empty($searchValue)) { - $kanbanGroup = $from == 'execution' ? $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType, $groupBy, $searchValue) : $this->loadModel('kanban')->getRDKanban($executionID, $browseType, 'id_asc', 0, $groupBy, $searchValue); + $kanbanGroup = $from == 'execution' ? $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, $searchValue, $orderBy) : $this->kanban->getRDKanban($executionID, $browseType, $orderBy, 0, $groupBy, $searchValue); return print(json_encode($kanbanGroup)); } } diff --git a/module/execution/css/cfd.css b/module/execution/css/cfd.css index 47646a87bd..8d2ea2ab32 100644 --- a/module/execution/css/cfd.css +++ b/module/execution/css/cfd.css @@ -1,4 +1,6 @@ .main-content > h2 {font-size: 16px;} +#mainContent h2 .icon-help {font-size: 16px; vertical-align: text-bottom;} +.tooltip-inner {font-size: 13px; letter-spacing: 1px; line-height: 22px; max-width: 400px !important; text-align: left;} .container {max-width: 1700px !important} .pull-left .input-control {margin-right: 10px;} @@ -29,3 +31,7 @@ #burnStatistics .bg-primary {margin-right: 5px;} #burnStatistics .icon-help {color: black;} #burnStatistics h3 {margin-top: 10px; text-indent: 10px; margin-bottom: 30px;} + +#cfdDateSelect {float: left;} +#cfdDateSelect .input-group {width: 300px; float: left;} +#datePreview {float: left; margin-left: 10px;} diff --git a/module/execution/css/kanban.css b/module/execution/css/kanban.css index 936d99e76e..7a47cf694f 100644 --- a/module/execution/css/kanban.css +++ b/module/execution/css/kanban.css @@ -36,6 +36,7 @@ .region .kanban-header-col > .title {max-width: 80% !important;} .region .kanban-header-col > .title {margin: 0} .region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial; max-width: 85px !important;} +.region .kanban-header-col > .title > span.storyColumn {max-width: 100% !important;} .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: 2px; font-size: 10px; padding-top: 1px; padding-right: 2px;} diff --git a/module/execution/css/taskkanban.css b/module/execution/css/taskkanban.css index 9cdad7095c..74960863c8 100644 --- a/module/execution/css/taskkanban.css +++ b/module/execution/css/taskkanban.css @@ -29,6 +29,7 @@ .kanban-affixed .kanban-header-col > .title > .text, .kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;} .kanban-header-col > .title > .text {max-width: 110px !important;} +.kanban-header-col > .title > .storyColumn.text {max-width: 100% !important;} #kanbanContainer {margin: 0;} #kanbanContainer > .panel-body {overflow: auto;} diff --git a/module/execution/js/cfd.js b/module/execution/js/cfd.js index 51637d3120..a15f5c8b2a 100644 --- a/module/execution/js/cfd.js +++ b/module/execution/js/cfd.js @@ -132,6 +132,11 @@ $(function() { var type = $('#type').val(); withWeekend = withWeekend == 'true' ? 'false' : 'true'; - location.href = createLink('execution', 'cfd', 'executionID=' + executionID + '&type=' + type + '&withWeekend=' + withWeekend); + var begin = Base64.encode(encodeURIComponent($('#begin').val())); + var end = Base64.encode(encodeURIComponent($('#end').val())); + location.href = createLink('execution', 'cfd', 'executionID=' + executionID + '&type=' + type + '&withWeekend=' + withWeekend + '&begin=' + begin + '&end=' + end); }); + + $("#end, #begin").datetimepicker('setEndDate', today) + $('.datetimepicker-days table tfoot').append(''); }); diff --git a/module/execution/js/kanban.js b/module/execution/js/kanban.js index 3c48a056e5..6c6036438a 100644 --- a/module/execution/js/kanban.js +++ b/module/execution/js/kanban.js @@ -322,7 +322,7 @@ function updateRegionName(regionID, name) */ function updateLaneName(laneID, name) { - $('.kanban-lane[data-id="' + laneID + '"] > .kanban-lane-name > span').text(name); + $('.kanban-lane[data-id="' + laneID + '"] > .kanban-lane-name > span').text(name).attr('title', name); } /** @@ -349,7 +349,7 @@ function updateLaneColor(laneID, color) */ function updateColumnName(columnID, name, color) { - $('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).css('color', color); + $('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).attr('title', name).css('color', color); } /** @@ -547,6 +547,15 @@ function renderEstStarted(estStarted, status) */ function renderStoryItem(item, $item, col) { + if(groupBy == 'story' && item.id == '0') + { + $('.storyCell').css('width', '100%'); + $parentItem = $item[0] == undefined ? $('.storyCell') : $item.parent(); + $parentItem.addClass('text-center storyCell'); + $parentItem.css('line-height', ($parentItem.parent().height() - 20) + 'px'); + $item.replaceWith('' + item.title + ''); + return; + } var scaleSize = window.kanbanScaleSize; if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize); @@ -749,6 +758,14 @@ addColumnRenderer('task', renderTaskItem); */ function renderCount($count, count, column) { + if(groupBy == 'story' && column.type == 'story') + { + $count.prev().addClass('storyColumn'); + $count.prev().parent().append(''); + $count.remove(); + return; + } + /* Render WIP. */ var limit = !column.limit || column.limit == '-1' ? '' : column.limit; if($count.parent().find('.limit').length) @@ -793,6 +810,9 @@ function tips() */ function renderHeaderCol($column, column, $header, kanbanData) { + if(groupBy == 'story' && column.type == 'story') return; + console.log(groupBy); + console.log(column.type); /* Render group header. */ var privs = kanbanData.actions; var columnPrivs = kanbanData.columns[0].actions; @@ -850,6 +870,11 @@ function renderHeaderCol($column, column, $header, kanbanData) */ function renderLaneName($lane, lane, $kanban, columns, kanban) { + if(groupBy == 'story') + { + $lane.hide(); + return; + } if(groupBy != 'default') return; var canEditLaneColor = lane.actions.includes('editLaneColor'); var canEditLaneName = lane.actions.includes('editLaneName'); @@ -1375,7 +1400,7 @@ function handleSortCards(event) } else { - $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD'), function(data) + $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD' + '&serachValue=' + rdSearchValue + '&orderBy=' + orderBy), function(data) { if(data && lastUpdateData !== data) { @@ -1567,6 +1592,12 @@ $(function() $('.color0 .cardcolor').css('border', '1px solid #fff'); }); + $(document).on('click', '#kanban span.caret.changeOrderBy', function(event) + { + orderBy = orderBy == 'pri_desc' ? 'pri_asc' : 'pri_desc'; + searchCards(rdSearchValue); + }) + /* Init sortable */ var sortType = ''; var $cards = null; @@ -1694,7 +1725,7 @@ $(function() { if(rdSearchValue == '') { - $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD&searchValue=' + rdSearchValue), function(data) + $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD&searchValue=' + rdSearchValue + '&orderBy=' + orderBy), function(data) { if(lastUpdateData == '') lastUpdateData = data; if(data && lastUpdateData !== data) @@ -1800,7 +1831,7 @@ function toggleRDSearchBox() function searchCards(value) { rdSearchValue = value; - $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD&searchValue=' + rdSearchValue), function(data) + $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD&searchValue=' + rdSearchValue + '&orderBy=' + orderBy), function(data) { lastUpdateData = data; kanbanData = $.parseJSON(data); diff --git a/module/execution/js/taskkanban.js b/module/execution/js/taskkanban.js index 2e5b48c0c7..00492e9cd5 100644 --- a/module/execution/js/taskkanban.js +++ b/module/execution/js/taskkanban.js @@ -9,7 +9,7 @@ */ function updateColumnName(columnID, name, color) { - $('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).css('color', color); + $('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).attr('title', name).css('color', color); } /** @@ -43,17 +43,17 @@ function renderUserAvatar(user, objectType, objectID, size, objectStatus) if(objectType == 'task') { if(!priv.canAssignTask && !user) return $noPrivAndNoAssigned; - var link = createLink('task', 'assignto', 'executionID=' + executionID + '&id=' + objectID, '', true); + var link = createLink('task', 'assignto', 'executionID=' + executionID + '&id=' + objectID + '&kanbanGroup=default&from=taskkanban', '', true); } if(objectType == 'story') { if(!priv.canAssignStory && !user) return $noPrivAndNoAssigned; - var link = createLink('story', 'assignto', 'id=' + objectID, '', true); + var link = createLink('story', 'assignto', 'id=' + objectID + '&kanbanGroup=default&from=taskkanban', '', true); } if(objectType == 'bug') { if(!priv.canAssignBug && !user) return $noPrivAndNoAssigned; - var link = createLink('bug', 'assignto', 'id=' + objectID, '', true); + var link = createLink('bug', 'assignto', 'id=' + objectID + '&kanbanGroup=default&from=taskkanban', '', true); } if(!user) return objectStatus == 'closed' ? '' : $(''); @@ -128,6 +128,15 @@ function renderEstStarted(estStarted, status) */ function renderStoryItem(item, $item, col) { + if(groupBy == 'story' && item.id == '0') + { + $('.storyCell').css('width', '100%'); + $parentItem = $item[0] == undefined ? $('.storyCell') : $item.parent(); + $parentItem.addClass('text-center storyCell'); + $parentItem.css('line-height', ($parentItem.parent().height() - 20) + 'px'); + $item.replaceWith('' + item.title + ''); + return; + } var scaleSize = window.kanbanScaleSize; if($item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize); @@ -330,6 +339,14 @@ addColumnRenderer('task', renderTaskItem); */ function renderColumnCount($count, count, col) { + if(groupBy == 'story' && col.type == 'story') + { + $count.prev().addClass('storyColumn'); + $count.prev().parent().append(''); + $count.remove(); + return; + } + var text = count + '/' + (col.limit < 0 ? '' : col.limit); $count.html(text + ''); @@ -370,7 +387,7 @@ function tips() function renderHeaderCol($col, col, $header, kanban) { if(col.asParent) $col = $col.children('.kanban-header-col'); - if($col.children('.actions').context != undefined) return; + if($col.children('.actions').context != undefined || (groupBy == 'story' && col.type == 'story')) return; var $actions = $('
    '); var printStoryButton = printTaskButton = printBugButton = false; @@ -410,6 +427,11 @@ function renderHeaderCol($col, col, $header, kanban) */ function renderLaneName($name, lane, $kanban, columns, kanban) { + if(groupBy == 'story') + { + $name.hide(); + return; + } if(lane.id != 'story' && lane.id != 'task' && lane.id != 'bug') return false; if(!$name.children('.actions').length && (priv.canSetLane || priv.canMoveLane)) { @@ -650,7 +672,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if((fromColType == 'developing' || fromColType == 'wait') && priv.canFinishTask) { - var link = createLink('task', 'finish', 'taskID=' + objectID, '', true); + var link = createLink('task', 'finish', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true); showIframe = true; } } @@ -658,7 +680,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if(fromColType == 'developing' && priv.canPauseTask) { - var link = createLink('task', 'pause', 'taskID=' + objectID, '', true); + var link = createLink('task', 'pause', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true); showIframe = true; } } @@ -666,17 +688,17 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if((fromColType == 'canceled' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask) { - var link = createLink('task', 'activate', 'taskID=' + objectID, '', true); + var link = createLink('task', 'activate', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true); showIframe = true; } if(fromColType == 'pause' && priv.canActivateTask) { - var link = createLink('task', 'restart', 'taskID=' + objectID, '', true); + var link = createLink('task', 'restart', 'taskID=' + objectID + '&from=' + 'taskkanban', '', true); showIframe = true; } if(fromColType == 'wait' && priv.canStartTask) { - var link = createLink('task', 'start', 'taskID=' + objectID, '', true); + var link = createLink('task', 'start', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true); showIframe = true; } } @@ -684,7 +706,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if((fromColType == 'developing' || fromColType == 'wait' || fromColType == 'pause') && priv.canCancelTask) { - var link = createLink('task', 'cancel', 'taskID=' + objectID, '', true); + var link = createLink('task', 'cancel', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true); showIframe = true; } } @@ -692,7 +714,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if((fromColType == 'developed' || fromColType == 'canceled') && priv.canCloseTask) { - var link = createLink('task', 'close', 'taskID=' + objectID, '', true); + var link = createLink('task', 'close', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true); showIframe = true; } } @@ -705,7 +727,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if(fromColType == 'unconfirmed' && priv.canConfirmBug) { - var link = createLink('bug', 'confirmBug', 'bugID=' + objectID, '', true); + var link = createLink('bug', 'confirmBug', 'bugID=' + objectID + '&extra=&from=taskkanban', '', true); showIframe = true; } } @@ -714,7 +736,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car if(fromColType == 'confirmed' || fromColType == 'unconfirmed') moveCard = true; if((fromColType == 'closed' || fromColType == 'fixed' || fromColType == 'testing' || fromColType == 'tested') && priv.canActivateBug) { - var link = createLink('bug', 'activate', 'bugID=' + objectID, '', true); + var link = createLink('bug', 'activate', 'bugID=' + objectID + '&extra=&from=taskkanban', '', true); showIframe = true; } } @@ -722,7 +744,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if(fromColType == 'fixing' || fromColType == 'confirmed' || fromColType == 'unconfirmed') { - var link = createLink('bug', 'resolve', 'bugID=' + objectID, '', true); + var link = createLink('bug', 'resolve', 'bugID=' + objectID + '&extra=&from=taskkanban', '', true); showIframe = true; } } @@ -738,7 +760,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if(fromColType == 'testing' || fromColType == 'tested') { - var link = createLink('bug', 'close', 'bugID=' + objectID, '', true); + var link = createLink('bug', 'close', 'bugID=' + objectID + '&extra=&from=taskkanban', '', true); showIframe = true; } } @@ -769,7 +791,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car { if(toColType == 'closed' && priv.canCloseStory) { - var link = createLink('story', 'close', 'storyID=' + objectID, '', true); + var link = createLink('story', 'close', 'storyID=' + objectID + '&from=taskkanban', '', true); showIframe = true; } else @@ -930,7 +952,7 @@ function createColumnCreateMenu(options) if(col.laneType == 'story') { - if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID, '', true), className: 'iframe'}); + if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&objectID=' + executionID, '', true), className: 'iframe'}); if(priv.canBatchCreateStory) items.push({label: executionLang.batchCreateStory, url: $.createLink('story', 'batchcreate', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}}); if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}}); if(priv.canLinkStoryByPlan) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal'}}); @@ -1167,7 +1189,7 @@ function handleSortCards(event) } else { - $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution'), function(data) + $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution' + '&searchValue=' + searchValue + '&orderBy=' + orderBy), function(data) { if(data && lastUpdateData !== data) { @@ -1252,6 +1274,12 @@ $(function() event.preventDefault(); }); + $(document).on('click', '#kanbans span.caret.changeOrderBy', function(event) + { + orderBy = orderBy == 'pri_desc' ? 'pri_asc' : 'pri_desc'; + searchCards(searchValue); + }) + /* Init contextmenu */ $('#kanbans').on('click', '[data-contextmenu]', function(event) { @@ -1311,7 +1339,7 @@ $(function() lastUpdateData = ''; setInterval(function() { - $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution&searchValue=' + searchValue), function(data) + $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution&searchValue=' + searchValue + '&orderBy' + orderBy), function(data) { if(lastUpdateData == '') lastUpdateData = data; if(data && lastUpdateData !== data) @@ -1447,7 +1475,7 @@ function toggleSearchBox() function searchCards(value) { searchValue = value; - $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution&searchValue=' + value), function(data) + $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution&searchValue=' + value + '&orderBy=' + orderBy), function(data) { lastUpdateData = data; var kanbanData = $.parseJSON(data); diff --git a/module/execution/lang/de.php b/module/execution/lang/de.php index 6cfcc90e3a..70ccf7c086 100644 --- a/module/execution/lang/de.php +++ b/module/execution/lang/de.php @@ -431,11 +431,22 @@ $lang->execution->charts->burn->graph->actuality = 'Aktualität'; $lang->execution->charts->burn->graph->delay = 'Delay'; $lang->execution->charts->cfd = new stdclass(); +$lang->execution->charts->cfd->cfdTip = "

    +1. The CFD(Cumulative Flow Diagram)reflects the trend of accumulated workload at each stage over time.
    +2. The horizontal axis represents the date, and the vertical axis represents the number of work items.
    +3. To learn about the team's delivery, you can calculate the WIP quantity, delivery rate and average lead time through the CFD. +

    "; $lang->execution->charts->cfd->cycleTime = 'Average cycle time'; $lang->execution->charts->cfd->cycleTimeTip = 'Average cycle time of each card from development start to completion'; $lang->execution->charts->cfd->throughput = 'Throughput Rate'; $lang->execution->charts->cfd->throughputTip = 'Throughput Rate = WIP / Average cycle time'; +$lang->execution->charts->cfd->begin = 'Begin'; +$lang->execution->charts->cfd->end = 'End'; +$lang->execution->charts->cfd->errorBegin = 'The start time cannot be greater than the end time.'; +$lang->execution->charts->cfd->errorDateRange = 'The Cumulative Flow Diagram(CFD) only provides data display within 3 months.'; +$lang->execution->charts->cfd->dateRangeTip = 'CFD only shows the data within 3 months'; + $lang->execution->placeholder = new stdclass(); $lang->execution->placeholder->code = 'Abkurzung des Projektnamens'; $lang->execution->placeholder->totalLeft = 'Schätzungen zu Beginn des Projekts.'; diff --git a/module/execution/lang/en.php b/module/execution/lang/en.php index 3a58f80179..b558ee70c8 100644 --- a/module/execution/lang/en.php +++ b/module/execution/lang/en.php @@ -431,11 +431,22 @@ $lang->execution->charts->burn->graph->actuality = 'Actual'; $lang->execution->charts->burn->graph->delay = 'Delay'; $lang->execution->charts->cfd = new stdclass(); +$lang->execution->charts->cfd->cfdTip = "

    +1. The CFD(Cumulative Flow Diagram)reflects the trend of accumulated workload at each stage over time.
    +2. The horizontal axis represents the date, and the vertical axis represents the number of work items.
    +3. To learn about the team's delivery, you can calculate the WIP quantity, delivery rate and average lead time through the CFD. +

    "; $lang->execution->charts->cfd->cycleTime = 'Average cycle time'; $lang->execution->charts->cfd->cycleTimeTip = 'Average cycle time of each card from development start to completion'; $lang->execution->charts->cfd->throughput = 'Throughput Rate'; $lang->execution->charts->cfd->throughputTip = 'Throughput Rate = WIP / Average cycle time'; +$lang->execution->charts->cfd->begin = 'Begin'; +$lang->execution->charts->cfd->end = 'End'; +$lang->execution->charts->cfd->errorBegin = 'The start time cannot be greater than the end time.'; +$lang->execution->charts->cfd->errorDateRange = 'The Cumulative Flow Diagram(CFD) only provides data display within 3 months.'; +$lang->execution->charts->cfd->dateRangeTip = 'CFD only shows the data within 3 months'; + $lang->execution->placeholder = new stdclass(); $lang->execution->placeholder->code = "Abbreviation of {$lang->executionCommon} name"; $lang->execution->placeholder->totalLeft = "Hours estimated on the first day of the {$lang->executionCommon}."; diff --git a/module/execution/lang/fr.php b/module/execution/lang/fr.php index 79ef8cbe87..1e5fe6bf27 100644 --- a/module/execution/lang/fr.php +++ b/module/execution/lang/fr.php @@ -434,11 +434,21 @@ $lang->execution->charts->burn->graph->actuality = 'Actuel'; $lang->execution->charts->burn->graph->delay = 'Delay'; $lang->execution->charts->cfd = new stdclass(); +$lang->execution->charts->cfd->cfdTip = "

    +1. Le CFD(Cumulative Flow Diagram)indique la tendance de la charge de travail cumulée de chaque étape au fil du temps.
    +2. L'axe horizontal représente la date et l'axe vertical représente le nombre de travaux.
    +3. Ce CFD vous permet de calculer les quantités de travail en cours (WIP), les taux de livraison et les délais moyens pour comprendre comment votre équipe travaille.

    "; $lang->execution->charts->cfd->cycleTime = 'Average cycle time'; $lang->execution->charts->cfd->cycleTimeTip = 'Average cycle time of each card from development start to completion'; $lang->execution->charts->cfd->throughput = 'Throughput Rate'; $lang->execution->charts->cfd->throughputTip = 'Throughput Rate = WIP / Average cycle time'; +$lang->execution->charts->cfd->begin = 'Begin'; +$lang->execution->charts->cfd->end = 'End'; +$lang->execution->charts->cfd->errorBegin = 'The start time cannot be greater than the end time.'; +$lang->execution->charts->cfd->errorDateRange = 'The Cumulative Flow Diagram(CFD) only provides data display within 3 months.'; +$lang->execution->charts->cfd->dateRangeTip = 'CFD only shows the data within 3 months'; + $lang->execution->placeholder = new stdclass(); $lang->execution->placeholder->code = "Abréviation du nom du {$lang->executionCommon}"; $lang->execution->placeholder->totalLeft = "Heures estimées le premier jour du {$lang->executionCommon}."; diff --git a/module/execution/lang/zh-cn.php b/module/execution/lang/zh-cn.php index 7323f9d837..b8ab7d649f 100644 --- a/module/execution/lang/zh-cn.php +++ b/module/execution/lang/zh-cn.php @@ -431,11 +431,21 @@ $lang->execution->charts->burn->graph->actuality = '实际'; $lang->execution->charts->burn->graph->delay = '延期'; $lang->execution->charts->cfd = new stdclass(); +$lang->execution->charts->cfd->cfdTip = "

    +1.累积流图反应各个阶段累积处理的工作项数量随时间的变化趋势。
    +2.横轴代表日期,纵轴代表工作项数量。
    +3.通过此图可计算出在制品数量,交付速率以及平均前置时间,从而了解团队的交付情况。

    "; $lang->execution->charts->cfd->cycleTime = '平均周期时间'; $lang->execution->charts->cfd->cycleTimeTip = '平均每个卡片从开发启动到完成的周期时间'; $lang->execution->charts->cfd->throughput = '吞吐率'; $lang->execution->charts->cfd->throughputTip = '吞吐率 = 在制品 / 平均周期时间'; +$lang->execution->charts->cfd->begin = '开始日期'; +$lang->execution->charts->cfd->end = '结束日期'; +$lang->execution->charts->cfd->errorBegin = '开始日期应小于结束日期'; +$lang->execution->charts->cfd->errorDateRange = '累积流图只提供3个月内的数据展示'; +$lang->execution->charts->cfd->dateRangeTip = '累积流图只展示3个月内的数据'; + $lang->execution->placeholder = new stdclass(); $lang->execution->placeholder->code = '团队内部的简称'; $lang->execution->placeholder->totalLeft = "{$lang->executionCommon}开始时的总预计工时"; diff --git a/module/execution/model.php b/module/execution/model.php index b9d90cbfa3..b9af4cc3eb 100644 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -79,7 +79,7 @@ class executionModel extends model $executions = $this->getPairs(0, 'all', 'nocode'); if(!$executionID and $this->session->execution) $executionID = $this->session->execution; if(!$executionID or !in_array($executionID, array_keys($executions))) $executionID = key($executions); - $this->session->set('execution', $executionID); + $this->session->set('execution', $executionID, $this->app->tab); /* Unset story, bug, build and testtask if type is ops. */ if($execution and $execution->type == 'stage' and $this->config->systemMode == 'new') @@ -3072,6 +3072,42 @@ class executionModel extends model } } + /** + * Check whether there is data on the specified date of execution, and there is no data with the latest date added. + * + * @param int $executionID + * @param string $date + * @access public + * @return void + */ + public function checkCFDData($executionID, $date) + { + $today = helper::today(); + if($date >= $today) return; + + $checkData = $this->dao->select("date, `count` AS value, `name`")->from(TABLE_CFD) + ->where('execution')->eq((int)$executionID) + ->andWhere('date')->eq($date) + ->orderBy('date DESC, id asc')->fetchGroup('name', 'date'); + if(!$checkData) + { + $closetoDate = $this->dao->select("max(date) as date")->from(TABLE_CFD)->where('execution')->eq((int)$executionID)->andWhere('date')->lt($date)->fetch('date'); + if($closetoDate) + { + $copyData = $this->dao->select("*")->from(TABLE_CFD) + ->where('execution')->eq((int)$executionID) + ->andWhere('date')->eq($closetoDate) + ->fetchAll(); + foreach($copyData as $data) + { + unset($data->id); + $data->date = $date; + $this->dao->replace(TABLE_CFD)->data($data)->exec(); + } + } + } + } + /** * Fix burn for first day. * @@ -4269,6 +4305,103 @@ class executionModel extends model return $html; } + /** + * Print execution nested list. + * + * @param object $execution + * @param int $isChild + * @param array $users + * @access public + * @return void + */ + public function printNestedList($execution, $isChild = false, $users, $productID) + { + $this->loadModel('task'); + $this->loadModel('execution'); + $this->loadModel('programplan'); + + if(!$isChild) + { + $trClass = 'is-top-level table-nest-child-hide'; + $trAttrs = "data-id='$execution->id' data-order='$execution->order' data-nested='true'"; + } + else + { + $trClass = 'is-nest-child'; + $trAttrs = "data-id={$execution->id} data-parent={$execution->parent}"; + $trAttrs .= " data-nest-parent='$execution->parent' data-order='$execution->order' data-nest-path=',$execution->parent,$execution->id,'"; + } + + $burns = join(',', $execution->burns); + echo "
    "; + echo "'; + echo "'; + echo ''; + echo ''; + echo ''; + echo "'; + echo "'; + echo "'; + echo ""; + echo ''; + echo ''; + + if(!empty($execution->children)) + { + foreach($execution->children as $child) $this->printNestedList($child, true, $users, $productID); + } + + if(!empty($execution->tasks)) + { + foreach($execution->tasks as $task) + { + $showmore = (count($execution->tasks) == 50) && ($task == end($execution->tasks)); + echo $this->task->buildNestedList($execution, $task, false, $showmore, $users); + } + } + } + /** * Update user view of execution and it's product. * diff --git a/module/execution/view/all.html.php b/module/execution/view/all.html.php index 5c9cc4abb9..b1aa1a8181 100644 --- a/module/execution/view/all.html.php +++ b/module/execution/view/all.html.php @@ -73,7 +73,7 @@ js::set('isCNLang', !$this->loadModel('common')->checkNotCN())

    execution->noExecutions : $lang->execution->noExecution;?> - + createLink('programplan', 'create', "projectID=$projectID&productID=$productID"), " " . $lang->programplan->create, '', "class='btn btn-info'");?> diff --git a/module/execution/view/cfd.html.php b/module/execution/view/cfd.html.php index bdefa324ba..4dd3a126c1 100644 --- a/module/execution/view/cfd.html.php +++ b/module/execution/view/cfd.html.php @@ -13,11 +13,14 @@ + execution->count); ?> execution->burnXUnit); ?> +execution->charts->cfd->dateRangeTip); ?> +

    -

    execution->cfdTypeList, $type) . $lang->execution->CFD;?>

    +

    execution->cfdTypeList, $type) . $lang->execution->CFD;?>

    diff --git a/module/execution/view/kanban.html.php b/module/execution/view/kanban.html.php index eaa09204f0..85767b6478 100644 --- a/module/execution/view/kanban.html.php +++ b/module/execution/view/kanban.html.php @@ -118,6 +118,11 @@ js::set('priv', ) ); ?> + + +
    dev->post;?>
    dev->params?>dev->type?>dev->desc?>dev->params?>dev->type?>dev->desc?>
    ' + dateRangeTip + '
    id class='table-nest-icon icon table-nest-toggle'>"; + if($this->config->systemMode == 'new') + { + $spanClass = $execution->type == 'stage' ? 'label-warning' : 'label-info'; + echo "{$this->lang->execution->typeList[$execution->type]} "; + } + echo html::a(helper::createLink('execution', 'view', "executionID=$execution->id"), $execution->name); + echo '' . zget($users, $execution->PM) . '" . zget($this->lang->project->statusList, $execution->status) . '' . html::ring($execution->hours->progress) . '' . $execution->begin . '' . $execution->end . '" . $execution->hours->totalEstimate . $this->lang->execution->workHourUnit . '" . $execution->hours->totalConsumed . $this->lang->execution->workHourUnit . '" . $execution->hours->totalLeft . $this->lang->execution->workHourUnit . ''; + common::printIcon('execution', 'start', "executionID={$execution->id}", $execution, 'list', '', '', 'iframe', true); + $class = !empty($execution->children) ? 'disabled' : ''; + common::printIcon('task', 'create', "executionID={$execution->id}", $execution, 'list', '', '', $class, false, "data-app='execution'"); + + if($execution->type == 'stage') + { + if($execution->grade == 1 && $this->loadModel('programplan')->isCreateTask($execution->id)) + { + common::printIcon('programplan', 'create', "program={$execution->parent}&productID=$productID&planID=$execution->id", $execution, 'list', 'split', '', '', '', '', $this->lang->programplan->createSubPlan); + } + else + { + $disabled = ($execution->grade == 2) ? ' disabled' : ''; + echo common::hasPriv('programplan', 'create') ? html::a('javascript:alert("' . $this->lang->programplan->error->createdTask . '");', '', '', 'class="btn ' . $disabled . '"') : ''; + } + } + + common::printIcon('programplan', 'edit', "stageID=$execution->id&projectID=$execution->project", $execution, 'list', '', '', 'iframe', true); + + $disabled = !empty($execution->children) ? ' disabled' : ''; + if($execution->status != 'closed' and common::hasPriv('execution', 'close', $execution)) + { + common::printIcon('execution', 'close', "stageID=$execution->id", $execution, 'list', 'off', 'hiddenwin' , $disabled . ' iframe', true, '', $this->lang->execution->close); + } + elseif($execution->status == 'closed' and common::hasPriv('execution', 'activate', $execution)) + { + common::printIcon('execution', 'activate', "stageID=$execution->id", $execution, 'list', 'magic', 'hiddenwin' , $disabled . ' iframe', true, '', $this->lang->execution->activate); + } + + if(common::hasPriv('execution', 'delete', $execution)) + { + common::printIcon('execution', 'delete', "stageID=$execution->id&confirm=no", $execution, 'list', 'trash', 'hiddenwin' , $disabled, '', '', $this->lang->delete); + } + echo '
    + id}&type=bug&link=$link¶m=$param&orderBy=%s";?> @@ -178,7 +179,7 @@ createLink('bug', 'view', "bugID=$bug->id", '', true);?>
    - +
    @@ -208,10 +209,21 @@