* Merge from 18.x

This commit is contained in:
王怡栋
2024-04-17 10:44:18 +08:00
parent 72c54223de
commit 890c88b07b
18 changed files with 107 additions and 59 deletions
+1 -1
View File
@@ -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];
}
+3 -4
View File
@@ -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')";
}
/**
+1
View File
@@ -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())
@@ -166,11 +166,11 @@ $(function()
<table class='status-count'>
<tr>
<td class='text-right'><?php echo $lang->story->total;?> :</td>
<td class='text-left'><?php echo empty($execution->totalStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&orderBy=order_desc&type=all"), $execution->totalStories);?></td>
<td class='text-left'><?php echo empty($execution->totalStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&storyType=story&orderBy=order_desc&type=all"), $execution->totalStories);?></td>
</tr>
<tr>
<td class='text-right'><?php echo $lang->story->unclosed;?> :</td>
<td class='text-left'><?php echo empty($execution->unclosedStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&orderBy=order_desc&type=unclosed"), $execution->unclosedStories);?></td>
<td class='text-left'><?php echo empty($execution->unclosedStories) ? 0 : html::a($this->createLink('execution', 'story', "executionID={$execution->id}&storyType=story&orderBy=order_desc&type=unclosed"), $execution->unclosedStories);?></td>
</tr>
</table>
</div>
+1 -1
View File
@@ -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));
+9 -10
View File
@@ -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');
}
/**
+30 -32
View File
@@ -1,5 +1,31 @@
#!/usr/bin/env php
<?php
/**
title=bugModel->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排序前面。
+2 -1
View File
@@ -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)
{
+1
View File
@@ -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';
}
+13 -1
View File
@@ -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');
+2 -1
View File
@@ -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()
+1
View File
@@ -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');
}
+1 -1
View File
@@ -322,7 +322,7 @@ featureBar
set::current($storyBrowseType),
set::link(createLink($app->rawModule, $app->rawMethod, $projectIDParam . "productID=$productID&branch=$branch&browseType={key}&param=$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
+1
View File
@@ -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);
}
+1 -1
View File
@@ -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);
+34 -1
View File
@@ -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;}
</style>
<?php js::set('customUrl', $this->createLink('programplan', 'ajaxCustom'));?>
<?php js::set('dateDetails', $dateDetails);?>
@@ -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 "<div class='delayed'>" + item.delay + "</div>";
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;
+2 -2
View File
@@ -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);
+2 -1
View File
@@ -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();