diff --git a/db/update17.0.sql b/db/update17.0.sql index 7cf03128bf..3dc0343f99 100644 --- a/db/update17.0.sql +++ b/db/update17.0.sql @@ -1,3 +1,6 @@ +DELETE from zt_report where `code` = 'product-invest'; +ALTER TABLE `zt_review` ADD docVersion smallint(6) NOT NULL AFTER `doc`; + DELETE FROM `zt_workflowaction` WHERE `module`='story' AND `action`='browse'; DELETE FROM `zt_workflowaction` WHERE `module`='task' AND `action`='browse'; -DELETE FROM `zt_workflowaction` WHERE `module`='build' AND `action`='browse'; +DELETE FROM `zt_workflowaction` WHERE `module`='build' AND `action`='browse'; \ No newline at end of file diff --git a/db/zentao.sql b/db/zentao.sql index 2c8f08a0f3..f35bf2e361 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -6791,7 +6791,6 @@ CREATE OR REPLACE VIEW `ztv_daybugresolve` AS select count(*) AS `bugresolve`,le CREATE OR REPLACE VIEW `ztv_dayactions` AS select count(*) AS `actions`,left(`zt_action`.`date`,10) AS `day` from `zt_action` group by left(`zt_action`.`date`,10); REPLACE INTO `zt_report` (`code`, `name`, `module`, `sql`, `vars`, `langs`, `params`, `step`, `desc`, `addedBy`, `addedDate`) VALUES -('product-invest', '{\"zh-cn\":\"\\u4ea7\\u54c1\\u6295\\u5165\\u8868\",\"zh-tw\":\"\\u7522\\u54c1\\u6295\\u5165\\u8868\",\"en\":\"Product Investment\"}', ',product', 'select t1.id,t1.name,1 as projects, round(t3.consumed,2) as consumed from TABLE_PRODUCT as t1\r\n left join TABLE_PROJECTPRODUCT as t2 on t1.id=t2.product\r\n left join ztv_projectsummary as t3 on t2.project=t3.project\r\n left join TABLE_PROJECT as t4 on t2.project=t4.id\r\n left join TABLE_PROGRAM as t5 on t1.program=t5.id\r\n where t1.deleted=\'0\' and t4.deleted=\'0\' and t4.type=\'project\'\r\norder by t5.`order` asc, t1.line desc, t1.`order` asc', '', '{\"projects\":{\"zh-cn\":\"\\u9879\\u76ee\\u6570\",\"zh-tw\":\"\\u9879\\u76ee\\u6570\",\"en\":\"Projects\"},\"consumed\":{\"zh-cn\":\"\\u5df2\\u6d88\\u8017\\u5de5\\u65f6\",\"zh-tw\":\"\\u5df2\\u6d88\\u8017\\u5de5\\u65f6\",\"en\":\"Cost(h)\"}}', '{\"group1\":\"name\",\"group2\":\"\",\"reportField\":[\"projects\",\"consumed\"],\"reportType\":[\"sum\",\"sum\"],\"sumAppend\":[\"projects\",\"consumed\"]}', 2, '{\"zh-cn\":\"\\u5217\\u51fa\\u6bcf\\u4e2a\\u4ea7\\u54c1\\u7684\\u9879\\u76ee\\u603b\\u6570\\uff0c\\u5df2\\u7ecf\\u6d88\\u8017\\u7684\\u5de5\\u65f6\\u3002\",\"zh-tw\":\"\\u5217\\u51fa\\u6bcf\\u500b\\u7522\\u54c1\\u7684\\u9805\\u76ee\\u7e3d\\u6578\\uff0c\\u5df2\\u7d93\\u6d88\\u8017\\u7684\\u5de5\\u6642\\u3002\",\"en\":\"Number of projects and consumed hours.\"}', 'admin', '2015-07-20 14:21:31'), ('product-progress', '{\"zh-cn\":\"\\u4ea7\\u54c1\\u5b8c\\u6210\\u5ea6\\u7edf\\u8ba1\\u8868\",\"zh-tw\":\"\\u7522\\u54c1\\u5b8c\\u6210\\u5ea6\\u7d71\\u8a08\\u8868\",\"en\":\"Product Progress\"}', ',product', 'select t1.*,t2.name, (case when t1.status = \'closed\' or t1.stage = \'released\' then 1 else 0 end) as done, 1 as count from TABLE_STORY as t1 \r\nleft join TABLE_PRODUCT as t2 on t1.product=t2.id \r\nleft join TABLE_PROGRAM as t3 on t2.program=t3.id \r\nwhere t1.deleted=\'0\' and t2.deleted=\'0\'\r\norder by t3.`order` asc, t2.line desc, t2.`order` asc', '', '{\"count\":{\"zh-cn\":\"\\u9700\\u6c42\\u6570\",\"zh-tw\":\"\\u9700\\u6c42\\u6570\",\"en\":\"Stories\"},\"done\":{\"zh-cn\":\"\\u5b8c\\u6210\\u6570\",\"zh-tw\":\"\\u5b8c\\u6210\\u6570\",\"en\":\"Done\"}}', '{\"group1\":\"name\",\"group2\":\"\",\"reportField\":[\"count\",\"done\"],\"reportType\":[\"sum\",\"sum\"],\"sumAppend\":[\"count\",\"done\"],\"percent\":{\"1\":\"1\"},\"contrast\":{\"1\":\"count\"},\"showAlone\":{\"1\":\"1\"}}', 2, '{\"zh-cn\":\"\\u6309\\u7167\\u4ea7\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u603b\\u6570\\uff0c\\u5b8c\\u6210\\u7684\\u603b\\u6570(\\u72b6\\u6001\\u662f\\u5173\\u95ed\\uff0c\\u6216\\u8005\\u7814\\u53d1\\u9636\\u6bb5\\u662f\\u53d1\\u5e03)\\uff0c\\u5b8c\\u6210\\u7684\\u767e\\u5206\\u6bd4\\u3002\",\"zh-tw\":\"\\u6309\\u7167\\u7522\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u7e3d\\u6578\\uff0c\\u5b8c\\u6210\\u7684\\u7e3d\\u6578(\\u72c0\\u614b\\u662f\\u95dc\\u9589\\uff0c\\u6216\\u8005\\u7814\\u767c\\u968e\\u6bb5\\u662f\\u767c\\u5e03)\\uff0c\\u5b8c\\u6210\\u7684\\u767e\\u5206\\u6bd4\\u3002\",\"en\":\"Number of total stories,done stories(state is closed, or stage is released), percent of completion.\"}', 'admin', '2015-07-21 15:07:48'), ('story-status', '{\"zh-cn\":\"\\u4ea7\\u54c1\\u9700\\u6c42\\u72b6\\u6001\\u5206\\u5e03\\u8868\",\"zh-tw\":\"\\u7522\\u54c1\\u9700\\u6c42\\u72c0\\u614b\\u5206\\u5e03\\u8868\",\"en\":\"Story Status\"}', ',product', 'select t1.*,t2.name from TABLE_STORY as t1\r\n left join TABLE_PRODUCT as t2 on t1.product=t2.id \r\nleft join TABLE_PROGRAM as t3 on t2.program=t3.id \r\nwhere t1.deleted=\'0\' and t2.deleted=\'0\'\r\norder by t3.`order` asc, t2.line desc, t2.`order` asc', '', '', '{\"group1\":\"name\",\"group2\":\"\",\"reportField\":[\"status\"],\"reportType\":[\"count\"],\"sumAppend\":[\"\"],\"reportTotal\":[\"1\"]}', 2, '{\"zh-cn\":\"\\u6309\\u7167\\u4ea7\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u603b\\u6570\\uff0c\\u72b6\\u6001\\u7684\\u5206\\u5e03\\u60c5\\u51b5\\u3002\",\"zh-tw\":\"\\u6309\\u7167\\u7522\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u7e3d\\u6578\\uff0c\\u72c0\\u614b\\u7684\\u5206\\u5e03\\u60c5\\u6cc1\\u3002\",\"en\":\"Total number and status distribution of stories.\"}', 'admin', '2015-07-21 15:35:38'), ('story-stage', '{\"zh-cn\":\"\\u4ea7\\u54c1\\u9700\\u6c42\\u9636\\u6bb5\\u5206\\u5e03\\u8868\",\"zh-tw\":\"\\u7522\\u54c1\\u9700\\u6c42\\u968e\\u6bb5\\u5206\\u5e03\\u8868\",\"en\":\"Story Stage\"}', ',product', 'select t1.*,t2.name from TABLE_STORY as t1\r\n left join TABLE_PRODUCT as t2 on t1.product=t2.id \r\nleft join TABLE_PROGRAM as t3 on t2.program=t3.id \r\nwhere t1.deleted=\'0\' and t2.deleted=\'0\'\r\norder by t3.`order` asc, t2.line desc, t2.`order` asc', '', '', '{\"group1\":\"name\",\"group2\":\"\",\"reportField\":[\"stage\"],\"reportType\":[\"count\"],\"sumAppend\":[\"\"],\"reportTotal\":[\"1\"]}', 2, '{\"zh-cn\":\"\\u6309\\u7167\\u4ea7\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u603b\\u6570\\uff0c\\u7814\\u53d1\\u9636\\u6bb5\\u7684\\u5206\\u5e03\\u60c5\\u51b5\\u3002\",\"zh-tw\":\"\\u6309\\u7167\\u7522\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u7e3d\\u6578\\uff0c\\u7814\\u767c\\u968e\\u6bb5\\u7684\\u5206\\u5e03\\u60c5\\u6cc1\\u3002\",\"en\":\"Total number and stage distribution of stories \"}', 'admin', '2015-07-21 15:38:34'), @@ -9420,6 +9419,7 @@ CREATE TABLE IF NOT EXISTS `zt_review` ( `object` mediumint(8) NOT NULL, `template` mediumint(8) NOT NULL, `doc` mediumint(8) DEFAULT NULL, + `docVersion` smallint(6) DEFAULT NULL, `status` char(30) NOT NULL, `reviewedBy` varchar(255) NOT NULL, `auditedBy` varchar(255) NOT NULL, diff --git a/framework/model.class.php b/framework/model.class.php index caf535c15a..45477bace2 100644 --- a/framework/model.class.php +++ b/framework/model.class.php @@ -105,7 +105,7 @@ class model extends baseModel $conditions = json_decode($action->conditions); if($action->extensionType == 'extend') { - $title = $action->name; + if($icon != 'copy' and $methodName != 'create') $title = $action->name; if($conditions) $enabled = $this->loadModel('flow')->checkConditions($conditions, $data); } else diff --git a/module/bug/model.php b/module/bug/model.php index 723d90e267..1a21378fb3 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -1878,6 +1878,8 @@ class bugModel extends model if(!empty($this->config->isINT)) $firstLetter = ''; $users[$account] = $firstLetter . ($user->realname ? $user->realname : $user->account); } + + $users = $this->loadModel('user')->processAccountSort($users); return array('' => '') + $users; } diff --git a/module/custom/view/required.html.php b/module/custom/view/required.html.php index 80dc184e47..eb83cf9aa6 100644 --- a/module/custom/view/required.html.php +++ b/module/custom/view/required.html.php @@ -16,8 +16,8 @@
custom->requiredModules); - foreach($requiredModules as $requiredModule) + ksort($config->custom->requiredModules); + foreach($config->custom->requiredModules as $requiredModule) { $requiredModuleName = zget($lang->custom->moduleName, $requiredModule, $lang->$requiredModule->common); echo html::a(inlink('required', "module=$requiredModule"), $requiredModuleName, '', "id='{$requiredModule}Tab'"); diff --git a/module/doc/control.php b/module/doc/control.php old mode 100644 new mode 100755 index 89c931c78e..101676c2de --- a/module/doc/control.php +++ b/module/doc/control.php @@ -792,6 +792,28 @@ class doc extends control return print($select); } + /** + * Ajax get docs by lib. + * + * @param int $libID + * @access public + * @return void + */ + public function ajaxGetDocs($libID) + { + if(!$libID) return print(html::select('doc', '', '', "class='form-control chosen'")); + + $docIdList = $this->doc->getPrivDocs($libID, 0); + $docs = $this->dao->select('id, title')->from(TABLE_DOC) + ->where('lib')->eq($libID) + ->andWhere('deleted')->eq(0) + ->andWhere('id')->in($docIdList) + ->orderBy('`order` asc') + ->fetchPairs(); + + return print(html::select('doc', $docs, '', "class='form-control chosen'")); + } + /** * Ajax save draft. * diff --git a/module/doc/view/content.html.php b/module/doc/view/content.html.php index 376811d81e..393cefc89d 100644 --- a/module/doc/view/content.html.php +++ b/module/doc/view/content.html.php @@ -102,7 +102,7 @@ } elseif($doc->contentType == 'markdown') { - echo ""; + echo ""; } else { diff --git a/module/doc/view/view.html.php b/module/doc/view/view.html.php index d83656bac5..c3776fa5e3 100644 --- a/module/doc/view/view.html.php +++ b/module/doc/view/view.html.php @@ -116,7 +116,7 @@ js::set('docID', $doc->id); } elseif($doc->contentType == 'markdown') { - echo ""; + echo ""; } else { diff --git a/module/execution/control.php b/module/execution/control.php index 6c0e162839..ac8760f403 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -1496,7 +1496,7 @@ class execution extends control $this->view->name = $name; $this->view->code = $code; $this->view->team = $team; - $this->view->teams = array(0 => '', $projectID => (isset($project->name) ? $project->name : '')) + $this->execution->getCanCopyObjects((int)$projectID); + $this->view->teams = array(0 => '') + $this->execution->getCanCopyObjects((int)$projectID); $this->view->allProjects = array(0 => '') + $this->project->getPairsByModel('all', 0, 'noclosed'); $this->view->executionID = $executionID; $this->view->productID = $productID; @@ -2730,7 +2730,7 @@ class execution extends control $currentMembers = $this->execution->getTeamMembers($executionID); $members2Import = $this->execution->getMembers2Import($team2Import, array_keys($currentMembers)); - $teams2Import = $this->loadModel('personnel')->getCopiedObjects($executionID, 'sprint'); + $teams2Import = $this->loadModel('personnel')->getCopiedObjects($executionID, 'sprint', true); $teams2Import = array('' => '') + $teams2Import; /* Append users for get users. */ diff --git a/module/execution/js/kanban.js b/module/execution/js/kanban.js index 5c143b7fc3..26ecb547fd 100644 --- a/module/execution/js/kanban.js +++ b/module/execution/js/kanban.js @@ -695,6 +695,17 @@ function renderCount($count, count, column) } } +/** + * Alert to link product. + * + * @access public + * @return void + */ +function tips() +{ + bootbox.alert(needLinkProducts); +} + /** * Render header of a column. */ @@ -729,10 +740,11 @@ function renderHeaderCol($column, column, $header, kanbanData) var printMoreBtn = (columnPrivs.includes('setColumn') || columnPrivs.includes('setWIP')); /* Render more menu. */ - if(((column.type == 'backlog' && hasStoryButton) || (column.type == 'wait' && hasTaskButton) || (column.type == 'unconfirmed' && hasBugButton)) && $actions.children('.text-primary').length == 0) + if(column.type == 'backlog' || column.type == 'wait' || column.type == 'unconfirmed' && $actions.children('.text-primary').length == 0) { + var tips = productID ? '' : 'onclick="tips()"'; $actions.append([ - '', + '', '', '' ].join('')); @@ -795,14 +807,16 @@ function updateRegion(regionID, regionData = []) if(!regionData) regionData = regions[regionID]; var data = groupBy == 'default' ? regionData.groups : regionData; - if(data == null) + if(data == null || data.length == 0) { - $("div[data-id^=" + regionID + "].region").hide(); + if(groupBy == 'default') $("div[data-id^=" + regionID + "].region").hide(); + if(groupBy != 'default') $("div[data-id^=" + regionID + "].kanban").hide(); return false; } else { - $("div[data-id^=" + regionID + "].region").show(); + if(groupBy == 'default') $("div[data-id^=" + regionID + "].region").show(); + if(groupBy != 'default') $("div[data-id^=" + regionID + "].kanban").show(); } $region.empty(); $region.data('zui.kanban').render(data); diff --git a/module/execution/js/taskkanban.js b/module/execution/js/taskkanban.js index f59eac1f7b..27da8b0eea 100644 --- a/module/execution/js/taskkanban.js +++ b/module/execution/js/taskkanban.js @@ -300,6 +300,17 @@ function renderColumnCount($count, count, col) } } +/** + * Alert to link product. + * + * @access public + * @return void + */ +function tips() +{ + bootbox.alert(needLinkProducts); +} + /** * Render header column * @param {JQuery} $col Header column element @@ -318,10 +329,11 @@ function renderHeaderCol($col, col, $header, kanban) if(priv.canCreateTask || priv.canBatchCreateTask) printTaskButton = true; if(priv.canCreateBug || priv.canBatchCreateBug) printBugButton = true; - if((col.type === 'backlog' && printStoryButton) || (col.type === 'wait' && printTaskButton) || (col.type == 'unconfirmed' && printBugButton)) + if(col.type === 'backlog' || col.type === 'wait' || col.type == 'unconfirmed') { + var tips = productID ? '' : 'onclick="tips()"'; $actions.append([ - '', + '', '', '' ].join('')); diff --git a/module/execution/lang/en.php b/module/execution/lang/en.php index a6653e2522..d9c7e13fdf 100644 --- a/module/execution/lang/en.php +++ b/module/execution/lang/en.php @@ -271,6 +271,7 @@ $lang->execution->treeOnlyStory = 'Show Story Only'; $lang->execution->storyKanban = 'Story Kanban'; $lang->execution->storySort = 'Rank Story'; $lang->execution->importPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan?'; +$lang->execution->needLinkProducts = 'The execution has not been linked with any product, and the related functions cannot be used. Please link the product first and try again.'; $lang->execution->iteration = 'Iterations'; $lang->execution->iterationInfo = '%s Iterations'; $lang->execution->viewAll = 'View All'; @@ -396,6 +397,7 @@ $lang->execution->projectNotEmpty = 'Project cannot be empty.'; $lang->execution->confirmStoryToTask = $lang->SRCommon . '%s are converted to tasks in the current. Do you want to convert them anyways?'; $lang->execution->ge = "『%s』should be >= actual begin『%s』."; $lang->execution->storyDragError = "The {$lang->SRCommon} is still a draft or has been changed, please drag it after the review"; +$lang->execution->countTip = ' (%s member)'; $lang->execution->pleaseInput = "Enter"; /* Statistics. */ diff --git a/module/execution/lang/zh-cn.php b/module/execution/lang/zh-cn.php index 42264d001a..8f25c9c16f 100644 --- a/module/execution/lang/zh-cn.php +++ b/module/execution/lang/zh-cn.php @@ -51,7 +51,7 @@ $lang->execution->canceledBy = '由谁取消'; $lang->execution->canceledDate = '取消日期'; $lang->execution->begin = '计划开始'; $lang->execution->end = '计划完成'; -$lang->execution->dateRange = '起始日期'; +$lang->execution->dateRange = '计划起止日期'; $lang->execution->realBeganAB = '实际开始'; $lang->execution->realEndAB = '实际完成'; $lang->execution->realBegan = '实际开始日期'; @@ -271,6 +271,7 @@ $lang->execution->treeOnlyStory = "树状图只看{$lang->SRCommon}"; $lang->execution->storyKanban = "{$lang->SRCommon}看板"; $lang->execution->storySort = "{$lang->SRCommon}排序"; $lang->execution->importPlanStory = '创建' . $lang->executionCommon . '成功!\n是否导入计划关联的相关' . $lang->SRCommon . '?'; +$lang->execution->needLinkProducts = '该执行还未关联任何产品,相关功能无法使用,请先关联产品后再试。'; $lang->execution->iteration = '版本迭代'; $lang->execution->iterationInfo = '迭代%s次'; $lang->execution->viewAll = '查看所有'; @@ -396,6 +397,7 @@ $lang->execution->projectNotEmpty = '所属项目不能为空。'; $lang->execution->confirmStoryToTask = '%s' . $lang->SRCommon . '已经在当前' . $lang->execution->common . '中转了任务,请确认是否重复转任务。'; $lang->execution->ge = "『%s』应当不小于实际开始时间『%s』。"; $lang->execution->storyDragError = "该{$lang->SRCommon}还是草稿或已变更状态,请评审通过后再拖动"; +$lang->execution->countTip = '(%s人)'; $lang->execution->pleaseInput = "请输入"; /* 统计。*/ diff --git a/module/execution/model.php b/module/execution/model.php index e39021bc1f..bb760866c5 100644 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -2753,14 +2753,31 @@ class executionModel extends model $objectPairs = $this->dao->select('id,name')->from(TABLE_PROJECT) ->where('deleted')->eq(0) + ->andWhere('project', true)->eq($projectID) ->andWhere('type')->ne('project') - ->andWhere('(project')->eq($projectID) - ->orWhere('id')->eq($projectID) ->markRight(1) + ->orWhere('id')->eq($projectID) ->orderBy('type_asc,openedDate_desc') ->limit('9') ->fetchPairs(); + $countPairs = $this->dao->select('root, COUNT(*) as count')->from(TABLE_TEAM) + ->where('( type')->eq('project') + ->andWhere('root')->eq($projectID) + ->markRight(1) + ->orWhere('( type')->eq('execution') + ->andWhere('root')->in(array_keys($objectPairs)) + ->markRight(1) + ->groupBy('root') + ->fetchPairs('root'); + + foreach($objectPairs as $objectID => $objectName) + { + $memberCount = zget($countPairs, $objectID, 0); + $countTip = $memberCount > 1 ? str_replace('member', 'members', $this->lang->execution->countTip) : $this->lang->execution->countTip; + $objectPairs[$objectID] = $objectName . sprintf($countTip, $memberCount); + } + return $objectPairs; } diff --git a/module/execution/view/kanban.html.php b/module/execution/view/kanban.html.php index a1c1615e0a..653ad0e4b4 100644 --- a/module/execution/view/kanban.html.php +++ b/module/execution/view/kanban.html.php @@ -52,6 +52,7 @@ js::set('vision', $this->config->vision); js::set('productCount', count($productNames)); js::set('executionID', $execution->id); js::set('reviewStoryParis', $reviewStoryParis); +js::set('needLinkProducts', $lang->execution->needLinkProducts); js::set('rdSearchValue', ''); $canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1; @@ -110,9 +111,6 @@ js::set('priv', 'canUnlinkStory' => common::hasPriv('execution', 'unlinkStory'), ) ); -js::set('hasStoryButton', $hasStoryButton); -js::set('hasBugButton', $hasBugButton); -js::set('hasTaskButton', $hasTaskButton); ?>