* Merge from 18.x
This commit is contained in:
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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')";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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排序前面。
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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');
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user