From 890c88b07b208ea796f71ec203cdc04f131514dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A1=E6=A0=8B?= Date: Wed, 17 Apr 2024 09:49:14 +0800 Subject: [PATCH] * Merge from 18.x --- lib/base/pager/pager.class.php | 2 +- module/action/tao.php | 7 +-- module/admin/control.php | 1 + .../view/executionstatisticblock.html.php | 4 +- module/bug/control.php | 2 +- module/bug/model.php | 19 +++--- module/bug/test/model/getproductbugpairs.php | 62 +++++++++---------- module/build/zen.php | 3 +- module/datatable/model.php | 1 + module/my/model.php | 14 ++++- module/product/model.php | 3 +- module/product/tao.php | 1 + module/product/ui/browse.html.php | 2 +- module/product/zen.php | 1 + module/productplan/model.php | 2 +- module/programplan/view/gantt.html.php | 35 ++++++++++- module/story/tao.php | 4 +- module/upgrade/model.php | 3 +- 18 files changed, 107 insertions(+), 59 deletions(-) diff --git a/lib/base/pager/pager.class.php b/lib/base/pager/pager.class.php index 5394182070..079448d12c 100644 --- a/lib/base/pager/pager.class.php +++ b/lib/base/pager/pager.class.php @@ -178,7 +178,7 @@ class basePager /* Set the cookie name. */ if(!(defined('RUN_MODE') and RUN_MODE == 'api')) { - $this->pageCookie = 'pager' . ucfirst($this->app->getModuleName()) . ucfirst($this->app->getMethodName()); + $this->pageCookie = 'pager' . ucfirst($this->app->rawModule) . ucfirst($this->app->rawMethod); if(isset($_COOKIE[$this->pageCookie])) $recPerPage = $_COOKIE[$this->pageCookie]; } diff --git a/module/action/tao.php b/module/action/tao.php index 65d0bfa95b..0c786d5683 100644 --- a/module/action/tao.php +++ b/module/action/tao.php @@ -623,17 +623,16 @@ class actionTao extends actionModel */ public function processEffortCondition(string &$condition, string $period, string $begin, string $end, string $beginDate): void { - $efforts = $this->dao->select('id')->from(TABLE_EFFORT) + $effortSQL = $this->dao->select('id')->from(TABLE_EFFORT) ->where($condition) ->beginIF($period != 'all') ->beginIF($begin)->andWhere('date')->gt($begin)->fi() ->beginIF($end)->andWhere('date')->lt($end)->fi() ->fi() ->beginIF($beginDate)->andWhere('date')->ge($beginDate)->fi() - ->fetchPairs(); - $efforts = !empty($efforts) ? implode(',', $efforts) : 0; + ->get(); - $condition .= " OR (`objectID` IN ({$efforts}) AND `objectType` = 'effort')"; + $condition .= " OR (`objectID` IN ({$effortSQL}) AND `objectType` = 'effort')"; } /** diff --git a/module/admin/control.php b/module/admin/control.php index 9903eb80f3..8ddd3beaf6 100755 --- a/module/admin/control.php +++ b/module/admin/control.php @@ -55,6 +55,7 @@ class admin extends control */ public function ajaxSetZentaoData() { + session_write_close(); if(helper::isIntranet()) return $this->send(array('result' => 'ignore')); if($this->admin->checkInternet()) diff --git a/module/block/view/executionstatisticblock.html.php b/module/block/view/executionstatisticblock.html.php index ff61feb789..a1fcc6899e 100644 --- a/module/block/view/executionstatisticblock.html.php +++ b/module/block/view/executionstatisticblock.html.php @@ -166,11 +166,11 @@ $(function() - + - +
story->total;?> :totalStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&orderBy=order_desc&type=all"), $execution->totalStories);?>totalStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&storyType=story&orderBy=order_desc&type=all"), $execution->totalStories);?>
story->unclosed;?> :unclosedStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&orderBy=order_desc&type=unclosed"), $execution->unclosedStories);?>unclosedStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&storyType=story&orderBy=order_desc&type=unclosed"), $execution->unclosedStories);?>
diff --git a/module/bug/control.php b/module/bug/control.php index aa240b5b18..ade48fd00a 100755 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -1492,7 +1492,7 @@ class bug extends control $product = $this->loadModel('product')->getById($productID); $bug = $this->bug->getById($bugID); $branch = $product->type == 'branch' ? ($bug->branch > 0 ? $bug->branch . ',0' : '0') : ''; - $productBugs = $this->bug->getProductBugPairs($productID, $branch, $search, $limit); + $productBugs = $this->bug->getProductBugPairs($productID, $branch, $search, $limit, 'all'); unset($productBugs[$bugID]); if($type == 'json') return print(helper::jsonEncode($productBugs)); diff --git a/module/bug/model.php b/module/bug/model.php index c8d6957204..5b3695fb6b 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -1029,27 +1029,26 @@ class bugModel extends model * @param int|string $branch * @param string $search * @param int $limit + * @param string $range single|all * @access public * @return array */ - public function getProductBugPairs(int $productID, int|string $branch = '', string $search = '', int $limit = 0): array + public function getProductBugPairs(int $productID, int|string $branch = '', string $search = '', int $limit = 0, string $range = 'single'): array { /* 获取产品的bugs。 */ /* Get product bugs. */ - $data = $this->dao->select('id, title')->from(TABLE_BUG) - ->where('product')->eq((int)$productID) + $productID = (int)$productID; + return $this->dao->select("id, CONCAT(IF(product = $productID, '', CONCAT('{$this->lang->product->common}#', product, '@')), id, ':', title) AS title, IF(product = $productID, 0, product) AS `order`")->from(TABLE_BUG) + ->where('deleted')->eq(0) + ->beginIF($range == 'single')->andWhere('product')->eq($productID)->fi() ->beginIF(!$this->app->user->admin)->andWhere('execution')->in('0,' . $this->app->user->view->sprints)->fi() + ->beginIF($range == 'all' && !$this->app->user->admin)->andWhere('product')->in($this->app->user->view->products)->fi() ->beginIF($branch !== '')->andWhere('branch')->in($branch)->fi() ->beginIF(strlen(trim($search)))->andWhere('title')->like('%' . $search . '%')->fi() ->andWhere('deleted')->eq(0) - ->orderBy('id desc') + ->orderBy('`order`, id desc') ->beginIF($limit)->limit($limit)->fi() - ->fetchAll(); - /* 将bugs转为bug键对。 */ - /* Convert bugs to bug pairs. */ - $bugs = array(); - foreach($data as $bug) $bugs[$bug->id] = $bug->id . ':' . $bug->title; - return $bugs; + ->fetchPairs('id', 'title'); } /** diff --git a/module/bug/test/model/getproductbugpairs.php b/module/bug/test/model/getproductbugpairs.php index 0c7b38c5b5..ca7a0656a7 100755 --- a/module/bug/test/model/getproductbugpairs.php +++ b/module/bug/test/model/getproductbugpairs.php @@ -1,5 +1,31 @@ #!/usr/bin/env php getProductBugPairs(); +cid=64 + +- 测试获取productID为1的bug @3:BUG3,2:BUG2,1:BUG1 + +- 测试获取productID为2的bug @6:BUG6,5:BUG5,4:BUG4 + +- 测试获取productID为3的bug @9:BUG9,8:bug8,7:缺陷!()(){}|+=^&*#测试bug名称到底可以有多长!#¥&*":.<>。?/();7 + +- 测试获取productID为4的bug @12:BUG12,11:BUG11,10:BUG10 + +- 测试获取productID为5的bug @15:缺陷!()(){}|+=^&*#测试bug名称到底可以有多长!#¥&*":.<>。?/();15,14:BUG14,13:BUG13 + +- 测试获取productID为6的bug @18:BUG18,17:BUG17,16:bug16 + +- 测试获取不存在的product的bug @0 +- 测试获取productID为45,主干分支的bug @133:BUG133 +- 测试获取productID为45,分支为9的bug @134:BUG134 +- 测试获取productID为45,标题匹配 BUG1 的bug @134:BUG134,133:BUG133 + +- 测试获取所有标题匹配 BUG1 的3条bug,productID为45的bug排序前面。 @134:BUG134,133:BUG133,产品35;164;1:BUG1 + +*/ include dirname(__FILE__, 5) . '/test/lib/init.php'; include dirname(__FILE__, 2) . '/bug.class.php'; @@ -8,42 +34,11 @@ zdTable('user')->gen(1); su('admin'); -/** - -title=bugModel->getProductBugPairs(); -cid=1 -pid=1 - -- 测试获取productID为1的bug @3:BUG3,2:BUG2,1:BUG1 - - -- 测试获取productID为2的bug @6:BUG6,5:BUG5,4:BUG4 - - -- 测试获取productID为3的bug @9:BUG9,8:bug8,7:缺陷!()(){}|+=^&*#测试bug名称到底可以有多长!#¥&*":.<>。?/();7 - - -- 测试获取productID为4的bug @12:BUG12,11:BUG11,10:BUG10 - - -- 测试获取productID为5的bug @15:缺陷!()(){}|+=^&*#测试bug名称到底可以有多长!#¥&*":.<>。?/();15,14:BUG14,13:BUG13 - - -- 测试获取productID为6的bug @18:BUG18,17:BUG17,16:bug16 - - -- 测试获取不存在的product的bug @0 - -- 测试获取productID为45,主干分支的bug @133:BUG133 - -- 测试获取productID为45,分支为9的bug @134:BUG134 - -*/ - $productIDList = array('1', '2', '3', '4','5', '6', '1000001', '45'); $branchIDList = array('9'); $bug=new bugTest(); +$bug->objectModel->app->user->admin = true; r($bug->getProductBugPairsTest($productIDList[0])) && p() && e('3:BUG3,2:BUG2,1:BUG1'); // 测试获取productID为1的bug r($bug->getProductBugPairsTest($productIDList[1])) && p() && e('6:BUG6,5:BUG5,4:BUG4'); // 测试获取productID为2的bug r($bug->getProductBugPairsTest($productIDList[2])) && p() && e('9:BUG9,8:bug8,7:缺陷!()(){}|+=^&*#测试bug名称到底可以有多长!#¥&*":.<>。?/();7'); // 测试获取productID为3的bug @@ -53,3 +48,6 @@ r($bug->getProductBugPairsTest($productIDList[5])) && p() && e r($bug->getProductBugPairsTest($productIDList[6])) && p() && e('0'); // 测试获取不存在的product的bug r($bug->getProductBugPairsTest($productIDList[7], 0)) && p() && e('133:BUG133'); // 测试获取productID为45,主干分支的bug r($bug->getProductBugPairsTest($productIDList[7], $branchIDList[0])) && p() && e('134:BUG134'); // 测试获取productID为45,分支为9的bug + +r(implode(',', $bug->objectModel->getProductBugPairs($productIDList[7], '', 'BUG1'))) && p() && e('134:BUG134,133:BUG133'); // 测试获取productID为45,标题匹配 BUG1 的bug +r(str_replace(array('@', '#'), array('64;', '35;'), implode(',', $bug->objectModel->getProductBugPairs($productIDList[7], '', 'BUG1', 3, 'all')))) && p() && e('134:BUG134,133:BUG133,产品35;164;1:BUG1'); // 测试获取所有标题匹配 BUG1 的3条bug,productID为45的bug排序前面。 diff --git a/module/build/zen.php b/module/build/zen.php index 4bd80ab424..c7d31e27ef 100644 --- a/module/build/zen.php +++ b/module/build/zen.php @@ -25,7 +25,8 @@ class buildZen extends build protected function assignCreateData(int $productID, int $executionID, int $projectID, string $status) { $productGroups = $branchGroups = array(); - $executions = $this->loadModel('execution')->getPairs($projectID, 'all', 'stagefilter|leaf|order_asc'); + $noClosedParam = (isset($this->config->CRExecution) && $this->config->CRExecution == 0) ? '|noclosed' : ''; + $executions = $this->loadModel('execution')->getPairs($projectID, 'all', 'stagefilter|leaf|order_asc' . $noClosedParam); $executionID = empty($executionID) && !empty($executions) ? (int)key($executions) : $executionID; if($executionID || $projectID) { diff --git a/module/datatable/model.php b/module/datatable/model.php index a0e800ddff..42799392ae 100644 --- a/module/datatable/model.php +++ b/module/datatable/model.php @@ -188,6 +188,7 @@ class datatableModel extends model if($field->buildin) continue; $this->config->$module->datatable->fieldList[$field->field]['title'] = $field->name; $this->config->$module->datatable->fieldList[$field->field]['width'] = '120'; + $this->config->$module->datatable->fieldList[$field->field]['type'] = 'html'; $this->config->$module->datatable->fieldList[$field->field]['fixed'] = 'no'; $this->config->$module->datatable->fieldList[$field->field]['required'] = 'no'; } diff --git a/module/my/model.php b/module/my/model.php index f138d0cc34..1fe2226de9 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -1089,14 +1089,26 @@ class myModel extends model while($object = $stmt->fetch()) $objectIdList[$object->objectType][$object->objectID] = $object->objectID; if($checkExists) return array_keys($objectIdList); + $this->loadModel('flow'); + $this->loadModel('workflowaction'); $flows = $this->dao->select('module,`table`,name,titleField')->from(TABLE_WORKFLOW)->where('module')->in(array_keys($objectIdList))->andWhere('buildin')->eq(0)->fetchAll('module'); $objectGroup = array(); foreach($objectIdList as $objectType => $idList) { $table = zget($this->config->objectTables, $objectType, ''); if(empty($table) && isset($flows[$objectType])) $table = $flows[$objectType]->table; + if(empty($table)) continue; - if(!empty($table)) $objectGroup[$objectType] = $this->dao->select('*')->from($table)->where('id')->in($idList)->fetchAll('id'); + $objectGroup[$objectType] = $this->dao->select('*')->from($table)->where('id')->in($idList)->fetchAll('id'); + + $action = $this->workflowaction->getByModuleAndAction($objectType, 'approvalreview'); + if($action) + { + foreach($objectGroup[$objectType] as $objectID => $object) + { + if(!$this->flow->checkConditions($action->conditions, $object)) unset($objectIdList[$objectType][$objectID], $objectGroup[$objectType][$objectID]); + } + } } $this->app->loadConfig('action'); diff --git a/module/product/model.php b/module/product/model.php index 53047286b6..ba8459089a 100755 --- a/module/product/model.php +++ b/module/product/model.php @@ -819,7 +819,7 @@ class productModel extends model * @param int $productID * @param string $branch 'all'|''|int * @param int $appendProject - * @param string $status all|noclosed + * @param string $status all|noclosed|closed * @param string $param multiple| * @access public * @return array @@ -836,6 +836,7 @@ class productModel extends model ->andWhere('t2.type')->eq('project') ->andWhere('t2.deleted')->eq('0') ->beginIF($status == 'noclosed')->andWhere('t2.status')->ne('closed')->fi() + ->beginIF($status == 'closed')->andWhere('t2.status')->eq('closed')->fi() ->beginIF(strpos($param, 'multiple') !== false)->andWhere('t2.multiple')->ne('0')->fi() ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi() ->beginIF($product->type != 'normal' and $branch !== '' and $branch != 'all')->andWhere('t1.branch')->in($branch)->fi() diff --git a/module/product/tao.php b/module/product/tao.php index 70e58be3e3..31593f36f0 100644 --- a/module/product/tao.php +++ b/module/product/tao.php @@ -440,6 +440,7 @@ class productTao extends productModel */ protected function filterNoCasesStory(array $storyIDList): array { + if(empty($storyIDList)) return array(); return $this->dao->select('story')->from(TABLE_CASE)->where('story')->in($storyIDList)->andWhere('deleted')->eq(0)->fetchAll('story'); } diff --git a/module/product/ui/browse.html.php b/module/product/ui/browse.html.php index f2350f7071..270cad844e 100644 --- a/module/product/ui/browse.html.php +++ b/module/product/ui/browse.html.php @@ -322,7 +322,7 @@ featureBar set::current($storyBrowseType), set::link(createLink($app->rawModule, $app->rawMethod, $projectIDParam . "productID=$productID&branch=$branch&browseType={key}¶m=$param&storyType=$storyType&orderBy=$orderBy&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}&projectID=$projectID")), set::queryMenuLinkCallback(array(fn($key) => str_replace('{queryID}', (string)$key, $queryMenuLink))), - li(searchToggle(set::open($browseType == 'bysearch'), set::module('story'))) + li(searchToggle(set::open($browseType == 'bysearch'), set::module($config->product->search['module']))) ); toolbar diff --git a/module/product/zen.php b/module/product/zen.php index 054420e399..8cc43ad2fb 100644 --- a/module/product/zen.php +++ b/module/product/zen.php @@ -1198,6 +1198,7 @@ class productZen extends product $actionURL = $this->createLink($this->app->rawModule, $this->app->rawMethod, $params . "productID=$productID&branch=$branch&browseType=bySearch&queryID=myQueryID&storyType=$storyType"); $this->config->product->search['onMenuBar'] = 'yes'; + if($this->app->rawModule != 'product') $this->config->product->search['module'] = $this->app->rawModule; $queryID = ($browseType == 'bysearch') ? $param : 0; $this->product->buildSearchForm($productID, $this->products, $queryID, $actionURL, $storyType, $branch, $projectID); } diff --git a/module/productplan/model.php b/module/productplan/model.php index f6e89e5f29..8c3ba91cdc 100644 --- a/module/productplan/model.php +++ b/module/productplan/model.php @@ -109,7 +109,7 @@ class productplanModel extends model if(empty($plans)) return array(); $plans = $this->reorder4Children($plans); - $planIdList = array_keys($plans); + $planIdList = array_filter(array_keys($plans)); $planProjects = $this->productplanTao->getPlanProjects($planIdList, strpos($param, 'noproduct') === false || $productID ? $productID : null); $product = $this->loadModel('product')->getById($productID); diff --git a/module/programplan/view/gantt.html.php b/module/programplan/view/gantt.html.php index 0621155a1a..6c465243fd 100755 --- a/module/programplan/view/gantt.html.php +++ b/module/programplan/view/gantt.html.php @@ -86,6 +86,7 @@ form {display: block; margin-top: 0em; margin-block-end: 1em;} #mainContent > .pull-right > .button-group .text{margin-left: 0px;} .pull-right .icon-plus.icon-sm:before{vertical-align: 4%;} #ganttView .gantt_resizer{min-width: unset !important;} +div.delayed {background: #FFEBEB; color: #FB2B2B;} createLink('programplan', 'ajaxCustom'));?> @@ -627,9 +628,41 @@ $(function() if(showFields.indexOf('realBegan') != -1) gantt.config.columns.push({name: 'realBegan', align: 'center', resize: true, width: 80}); if(showFields.indexOf('realEnd') != -1) gantt.config.columns.push({name: 'realEnd', align: 'center', resize: true, width: 80}); if(showFields.indexOf('consumed') != -1) gantt.config.columns.push({name: 'consumed', align: 'center', resize: true, width: 60}); - if(showFields.indexOf('delay') != -1) gantt.config.columns.push({name: 'delay', align: 'center', resize: true, width: 60}); + if(showFields.indexOf('delay') != -1) + { + gantt.config.columns.push({name: 'delay', align: 'center', resize: true, width: 60, template:function(item) + { + if(item.delayDays > 0) return "
" + item.delay + "
"; + return item.delay; + }}); + } if(showFields.indexOf('delayDays') != -1) gantt.config.columns.push({name: 'delayDays', align: 'center', resize: false, width: 60}); + gantt.config.layout = + { + css: "gantt_container", + cols: + [ + { + width:600, + rows: + [ + {view: "grid", scrollX: "gridScroll", scrollable: true, scrollY: "scrollVer"}, + {view: "scrollbar", id: "gridScroll", group:"horizontal"} + ] + }, + {resizer: true, width: 1}, + { + rows: + [ + {view: "timeline", scrollX: "scrollHor", scrollY: "scrollVer"}, + {view: "scrollbar", id: "scrollHor", group:"horizontal"} + ] + }, + {view: "scrollbar", id: "scrollVer"} + ] + }; + function getDeadlineBtn(task) { var date = task.deadline; diff --git a/module/story/tao.php b/module/story/tao.php index 57c62d9fd7..b4031f1771 100644 --- a/module/story/tao.php +++ b/module/story/tao.php @@ -432,9 +432,9 @@ class storyTao extends storyModel { /* 获取查询条件。 */ $rawModule = $this->app->rawModule; - $this->loadModel('search')->setQuery($rawModule == 'projectstory' ? 'story' : 'executionStory', $queryID); + $this->loadModel('search')->setQuery($rawModule == 'projectstory' ? 'projectstory' : 'executionStory', $queryID); if(!$this->session->executionStoryQuery) $this->session->set('executionStoryQuery', ' 1 = 1'); - if($rawModule == 'projectstory') $this->session->set('executionStoryQuery', $this->session->storyQuery); + if($rawModule == 'projectstory') $this->session->set('executionStoryQuery', $this->session->projectstoryQuery); /* 处理查询条件。 */ $storyQuery = $this->replaceAllProductQuery($this->session->executionStoryQuery); diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 346028e2a7..1f1a21c91d 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -339,7 +339,7 @@ class upgradeModel extends model $createHead = array_shift($lines); $createFoot = array_pop($lines); - preg_match_all('/CREATE TABLE `([^`]*)`/', $createHead, $out); + preg_match_all('/CREATE TABLE [^`]*`([^`]*)`/', $createHead, $out); if(!isset($out[1][0])) return $changes; $table = str_replace('zt_', $this->config->db->prefix, $out[1][0]); @@ -8582,6 +8582,7 @@ class upgradeModel extends model */ public function updateWorkflowFieldDefaultValue(): bool { + if($this->config->edition == 'open') return false; $this->loadModel('workflowfield'); $tables = $this->dao->select('module, `table`')->from(TABLE_WORKFLOW)->fetchPairs();