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();