Merge remote-tracking branch 'origin/master'

This commit is contained in:
liuchenglong
2023-05-08 10:42:49 +00:00
132 changed files with 3791 additions and 2662 deletions
+3 -1
View File
@@ -1,4 +1,4 @@
FROM hub.qucheng.com/ci/zentao-ztf:php8-2023050806
FROM hub.qucheng.com/ci/zentao-ztf:php8-2023050810
# Install zentao
ARG VERSION
@@ -13,3 +13,5 @@ ENV MYSQL_PASSWORD=${MYSQL_PASSWORD}
SHELL ["/bin/bash", "-c"]
# Copy ZenTao
COPY --chown=33:33 . /apps/zentao/
RUN INIT_PHPINI_ONLY=true /etc/s6/s6-init/run
Vendored
+2 -2
View File
@@ -96,7 +96,7 @@ pipeline {
steps {
container('zentao') {
sh 'cat /usr/bin/initdb.php; initdb.php ; /apps/zentao/test/ztest init'
sh '/etc/s6/s6-init/run ; php --ini ; cat /usr/bin/initdb.php; initdb.php ; /apps/zentao/test/ztest init'
}
}
post {
@@ -138,7 +138,7 @@ pipeline {
stage('Run') {
steps {
container('zentao') {
sh 'initdb.php config'
sh '/etc/s6/s6-init/run ; php --ini ; initdb.php config'
sh '/apps/zentao/test/ztest extract ; /apps/zentao/test/ztest ${SEQUENCE} | tee /apps/zentao/test/${SEQUENCE}.log'
sh 'ls /apps/zentao/www ; cat /apps/zentao/www/coverage.php'
sh 'pipeline-unittest.sh /apps/zentao/test/${SEQUENCE}.log'
+28 -2
View File
@@ -6,6 +6,12 @@
font-size: 14px;
font-weight: 700;
line-height: 20px;
display: flex;
align-items: center;
}
.histories {
background: #FCFDFE;
}
.histories .btn-mini {
@@ -22,7 +28,8 @@
.histories .btn {
background-color: #fff;
display: inline-block;
display: inline-flex;
align-items: center;
margin-bottom: 0;
font-weight: 400;
text-align: center;
@@ -69,6 +76,7 @@
background: 0 0;
box-shadow: none;
border-color: transparent;
text-decoration: none;
}
.detail-content {
@@ -77,7 +85,6 @@
}
.histories-list {
padding-left: 15px;
margin-bottom: 0;
margin-top: 0;
list-style: decimal;
@@ -85,6 +92,24 @@
flex-direction: column;
}
.histories-list .timeline {
top: 13px;
bottom: 13px;
left: 10px;
background: #EEE;
}
.histories-list .marker {
color: #5E626D;
border-color: #EEE;
border-width: 1px;
box-shadow: none;
background: #FCFDFE;
height: 1.25rem;
width: 1.25rem;
white-space: nowrap;
}
.histories-list.sort-reverse {
flex-direction: column-reverse;
}
@@ -93,6 +118,7 @@
word-break: break-word;
word-wrap: break-word;
position: relative;
list-style: none;
}
.histories-list > li strong {
+98 -85
View File
@@ -14,10 +14,24 @@ class history extends wg
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
}
private function marker(int $num)
{
return span
(
setClass('marker', 'text-sm', 'rounded-full', 'aspect-square', 'inline-flex', 'justify-center', 'items-center', 'mr-2'),
$num
);
}
private function timeline()
{
return div(setClass('timeline w-px absolute'));
}
private function checkEditCommentPriv($action)
{
global $app;
$methodName = $this->prop('methodName') ?? data('methodName');
$methodName = $this->prop('methodName') === null ? $this->prop('methodName') : data('methodName');
return (!isset($canBeChanged) || !empty($canBeChanged))
&& end($actions) == $action
@@ -27,10 +41,9 @@ class history extends wg
&& common::hasPriv('action', 'editComment');
}
private function createExpandBtn($i)
private function expandBtn($i)
{
global $lang;
return button
(
setClass('btn btn-mini switch-btn btn-icon btn-expand'),
@@ -52,10 +65,9 @@ class history extends wg
);
}
private function createEditCommentBtn()
private function editCommentBtn()
{
global $lang;
return button
(
setClass('btn btn-link btn-icon btn-sm btn-edit-comment'),
@@ -64,10 +76,9 @@ class history extends wg
);
}
private function createHistoryChangesView($action, $i)
private function historyChanges($action, $i)
{
global $app;
return div
(
setClass('history-changes'),
@@ -76,18 +87,19 @@ class history extends wg
);
}
private function createActionItemView($action, $i)
private function actionItem($action, $i)
{
global $app;
return li
(
setClass('my-3'),
set::value($i),
$this->marker($i),
html($app->loadTarget('action')->renderAction($action))
);
}
private function generateComment($action)
private function getComment($action)
{
if(str_contains($action->comment, '<pre class="prettyprint lang-html">'))
{
@@ -102,10 +114,9 @@ class history extends wg
: $action->comment;
}
private function createCommentView($action)
private function comment($action)
{
$comment = $this->generateComment($action);
$comment = $this->getComment($action);
return div
(
setClass('article-content comment'),
@@ -117,10 +128,9 @@ class history extends wg
);
}
private function createCommentEditForm($action)
private function commentEditForm($action)
{
global $lang;
return form
(
setClass('comment-edit-form'),
@@ -156,51 +166,38 @@ class history extends wg
);
}
private function buildHistoriesList()
private function historyList()
{
$actions = $this->prop('actions') ?? data('actions');
$users = $this->prop('users') ?? data('users');
$historiesListView = h::ol(setClass('histories-list'));
$actions = $this->prop('actions') === null ? $this->prop('actions') : data('actions');
$users = $this->prop('users') === null ? $this->prop('users') : data('users');
$historiesListView = h::ol(setClass('histories-list relative'));
$i = 0;
$historiesListView->add($this->timeline());
foreach($actions as $action)
{
if($action->action === 'assigned' || $action->action === 'toaudit')
$action->extra = zget($users, $action->extra);
if($action->action === 'assigned' || $action->action === 'toaudit') $action->extra = zget($users, $action->extra);
$action->actor = zget($users, $action->actor);
if(str_contains($action->actor, ':'))
$action->actor = substr($action->actor, strpos($action->actor, ':') + 1);
if(str_contains($action->actor, ':')) $action->actor = substr($action->actor, strpos($action->actor, ':') + 1);
$i++;
$actionItemView = $this->createActionItemView($action, $i);
$actionItemView = $this->actionItem($action, $i);
if(!empty($action->history))
{
$allExpandBtn = $this->createExpandBtn($i);
$actionItemView->add($allExpandBtn);
$historyChangesView = $this->createHistoryChangesView($action, $i);
$actionItemView->add($historyChangesView);
$actionItemView->add($this->expandBtn($i));
$actionItemView->add($this->historyChanges($action, $i));
}
if(strlen(trim(($action->comment))) !== 0)
{
$canEditComment = $this->checkEditCommentPriv($action);
if($canEditComment)
{
$editCommentBtn = $this->createEditCommentBtn();
$actionItemView->add($editCommentBtn);
}
if($canEditComment) $actionItemView->add($this->editCommentBtn());
$commentView = $this->createCommentView($action);
$actionItemView->add($commentView);
$actionItemView->add($this->comment($action));
if($canEditComment)
{
$commentEditForm = $this->createCommentEditForm($action);
$actionItemView->add($commentEditForm);
}
if($canEditComment) $actionItemView->add($this->commentEditForm($action));
}
$historiesListView->add($actionItemView);
}
@@ -208,6 +205,62 @@ class history extends wg
return $historiesListView;
}
private function reverseBtn()
{
global $lang;
return btn
(
setClass('btn-mini btn-icon btn-reverse mr-2'),
set::title($lang->reverse),
set::icon('arrow-up'),
on::click(<<<REVERSE
document.querySelector('.histories-list').classList.toggle('sort-reverse');
var icon = e.target.querySelector('.icon');
icon.classList.toggle('icon-arrow-up');
icon.classList.toggle('icon-arrow-down');
REVERSE)
);
}
private function expandAllBtn()
{
global $lang;
return btn
(
setClass('btn-mini btn-icon btn-expand-all'),
set::title($lang->switchDisplay),
set::icon('plus'),
on::click(<<<EXPANDALL
var icon = e.target.querySelector('.icon');
var isExpand = icon.classList.contains('icon-plus');
var changeBoxs = document.querySelectorAll('[id^="changeBox"]');
if(isExpand) {
changeBoxs.forEach(function(box) {
box.classList.add('show');
});
} else {
changeBoxs.forEach(function(box) {
box.classList.remove('show');
});
}
icon.classList.toggle('icon-plus');
icon.classList.toggle('icon-minus');
EXPANDALL)
);
}
private function commentBtn()
{
global $lang;
return btn
(
setClass('btn-comment btn-link ml-4'),
set::icon('chat-line'),
set::iconClass('text-primary'),
$lang->action->create
);
}
protected function build()
{
global $lang;
@@ -221,51 +274,11 @@ class history extends wg
(
setClass('detail-title'),
span($lang->history),
button
(
setClass('btn btn-mini btn-icon btn-reverse'),
setStyle('margin-right', '4px'),
set::type('button'),
set::title($lang->reverse),
h::i(setClass('icon icon-arrow-up icon-sm')),
on::click(<<<REVERSE
document.querySelector('.histories-list').classList.toggle('sort-reverse');
var icon = e.target.querySelector('.icon');
icon.classList.toggle('icon-arrow-up');
icon.classList.toggle('icon-arrow-down');
REVERSE),
),
button
(
setClass('btn btn-mini btn-icon btn-expand-all'),
set::type('button'),
set::title($lang->switchDisplay),
h::i(setClass('icon icon-plus icon-sm')),
on::click(<<<EXPANDALL
var icon = e.target.querySelector('.icon');
var isExpand = icon.classList.contains('icon-plus');
var changeBoxs = document.querySelectorAll('[id^="changeBox"]');
if(isExpand) {
changeBoxs.forEach(function(box) {
box.classList.add('show');
});
} else {
changeBoxs.forEach(function(box) {
box.classList.remove('show');
});
}
icon.classList.toggle('icon-plus');
icon.classList.toggle('icon-minus');
EXPANDALL),
),
button
(
setClass('btn btn-link pull-right btn-comment'),
set::type('button'),
h::i(setClass('icon icon-chat-line'), ' ' . $lang->action->create)
),
$this->reverseBtn(),
$this->expandAllBtn(),
$this->commentBtn(),
),
div(setClass('detail-content'), $this->buildHistoriesList())
div(setClass('detail-content'), $this->historyList())
);
}
}
@@ -0,0 +1,7 @@
.productstatistic-block {background: #FCFDFE;}
.productstatistic-block .tab-content {width: 100%;}
.productstatistic-block .bg-secondary-pale {background: #F3F6FA;}
.productstatistic-block .nav-tabs .nav-item:hover {border-left: 2px solid #2E7FFF; background-color: #FCFDFE;}
.productstatistic-block .nav-tabs .nav-item.active {border-left: 2px solid #2E7FFF; background-color: #FCFDFE;}
.productstatistic-block .nav-tabs .nav-item:hover > a.link {display: flex!important;}
.productstatistic-block .nav-tabs .nav-item > a:before {background: transparent;}
+1 -44
View File
@@ -370,49 +370,6 @@ class blockModel extends model
return true;
}
/**
* Save a block.
*
* @param int $blockID
* @param string $type
* @param string $module
* @access public
* @return int|false
*/
public function save(int $blockID, string $type, string $module = 'my'): bool|int
{
$block = $blockID ? $this->getByID($blockID) : null;
$data = fixer::input('post')
->setIF($blockID, 'id', $blockID)
->add('account', $this->app->user->account)
->add('module', $module)
->add('order', $block ? $block->order : ($this->getMaxOrderByModule($module) + 1))
->add('hidden', 0)
->setDefault('vision', $this->config->vision)
->setDefault('grid', '4')
->setDefault('params', array())
->stripTags('html', $this->config->allowedTags)
->remove('uid,actionLink,modules,moduleBlock')
->get();
$data->source = $this->post->moduleBlock ? $this->post->modules : '';
$data->block = $this->post->moduleBlock ? $this->post->moduleBlock : $this->post->modules;
if($block) $data->height = $block->height;
if($type == 'html')
{
$uid = $this->post->uid;
$data = $this->loadModel('file')->processImgURL($data, 'html', $uid);
$data->params['html'] = $data->html;
unset($data->html);
unset($_SESSION['album'][$uid]);
}
$data->params = helper::jsonEncode($data->params);
$this->dao->replace(TABLE_BLOCK)->data($data)->exec();
}
/**
* Init block when account use first.
* 用户首次加载时初始化区块数据.
@@ -459,7 +416,7 @@ class blockModel extends model
*/
public function isLongBlock(object $block): bool
{
return (!empty($block) and $block->grid >= 6) ? true : false;
return (!empty($block->grid) and $block->grid >= 6) ? true : false;
}
/**
@@ -1,37 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->appendCountParams();
cid=1
pid=1
测试新增count param >> count:{name:数量,default:20,control:input};
测试新增count param 和 param test >> test:test;count:{name:数量,default:20,control:input};
测试新增count param 和 param par >> par:par;count:{name:数量,default:20,control:input};
测试新增count param 和 param append >> append:append;count:{name:数量,default:20,control:input};
测试新增count param 和 param 空 >> count:{name:数量,default:20,control:input};
*/
$param1 = new stdclass();
$param1->test = 'test';
$param2 = new stdclass();
$param2->par = 'par';
$param3 = new stdclass();
$param3->append = 'append';
$param4 = new stdclass();
$block = new blockTest();
r($block->appendCountParamsTest()) && p() && e('count:{name:数量,default:20,control:input};'); // 测试新增count param
r($block->appendCountParamsTest($param1)) && p() && e('test:test;count:{name:数量,default:20,control:input};'); // 测试新增count param 和 param test
r($block->appendCountParamsTest($param2)) && p() && e('par:par;count:{name:数量,default:20,control:input};'); // 测试新增count param 和 param par
r($block->appendCountParamsTest($param3)) && p() && e('append:append;count:{name:数量,default:20,control:input};'); // 测试新增count param 和 param append
r($block->appendCountParamsTest($param4)) && p() && e('count:{name:数量,default:20,control:input};'); // 测试新增count param 和 param 空
+9 -6
View File
@@ -19,14 +19,17 @@ function initData()
/**
title=测试 blockModel->checkAPI();
timeout=0
cid=1
pid=1
测试空哈希值 >> 0
测试正确的哈希值 >> 1
测试错误的哈希值 >> 0
sed: can't read /home/liuruogu/git/zentaopms/test/config/my.php: No such file or directory
- 测试空哈希值 @0
*/
- 测试正确的哈希值 @1
- 测试错误的哈希值 @0
*/
initData();
@@ -34,4 +37,4 @@ $block = new blockTest();
r($block->checkAPITest('')) && p('') && e('0'); // 测试空哈希值
r($block->checkAPITest('858640a724c2c981983935eb2bbc4ad8')) && p('') && e('1'); // 测试正确的哈希值
r($block->checkAPITest('858640a724c2c98198')) && p('') && e('0'); // 测试错误的哈希值
r($block->checkAPITest('858640a724c2c98198')) && p('') && e('0'); // 测试错误的哈希值
+36 -31
View File
@@ -5,61 +5,66 @@ include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
function initData()
{
$block = zdTable('block');
$block->id->range('2');
$block->account->range('test');
$block->vision->range('rnd');
$block->module->range('test');
$block->title->prefix('区块')->range('2');
$block->order->range('5');
$block->gen(1);
}
/**
title=测试 block 模块 model下的 create 方法
timeout=0
cid=39
- 执行block模块的getByID方法,参数是$newBlockID
- 测试获取正常的block的内容
- 属性account @admin
- 属性vision @rnd
- 属性dashboard @my
- 属性title @区块123
- 属性source @my
- 属性block @bug
- 属性order @5
- 属性module @project
- 属性code @statistic
- 属性title @项目统计区块
- 属性order @1
- 执行blockTest模块的create方法,参数是$accoutTooLangBlock,属性account @『所属用户』长度应当不超过『30』,且大于『0』。
- 测试所属模块为空 @『所属模块』不能为空。
- 测试所属区块为空 @『区块』不能为空。
- 测试区块名称为空 @『区块名称』不能为空。
- 测试account 字段字符超出长度 @『所属用户』长度应当不超过『30』,且大于『0』。
*/
global $tester;
$tester->loadModel('block');
initData();
$block = new stdclass();
$block->account = 'admin';
$block->vision = 'rnd';
$block->dashboard = 'my';
$block->title = '区块123';
$block->order = '5';
$block->module = 'project';
$block->code = 'statistic';
$block->title = '项目统计区块';
$block->order = '1';
$accoutTooLangBlock = new stdclass();
$accoutTooLangBlock->account = 'adminadminadminadminadminadminadminadmin1';
$accoutTooLangBlock->vision = 'rnd';
$accoutTooLangBlock->dashboard = 'my';
$accoutTooLangBlock->title = 'long account';
$accoutTooLangBlock->order = '5';
$accountTooLangBlock = new stdclass();
$accountTooLangBlock->account = 'adminadminadminadminadminadminadminadmin1';
$accountTooLangBlock->vision = 'rnd';
$accountTooLangBlock->dashboard = 'my';
$accountTooLangBlock->module = 'project';
$accountTooLangBlock->code = 'statistic';
$accountTooLangBlock->title = '项目统计区块2';
$accountTooLangBlock->order = '2';
$emptyModuleBlock = new stdclass();
$emptyModuleBlock->module = '';
$emptyCodeBlock = new stdclass();
$emptyCodeBlock->code = '';
$emptyTitleBlock = new stdclass();
$emptyTitleBlock->title = '';
$blockTest = new blockTest();
$newBlockID = $blockTest->createTest($block);
r($tester->block->getByID($newBlockID)) && p('account,vision,dashboard,title,order') && e('admin,rnd,my,区块123,5'); // 测试获取正常的block的内容
r($blockTest->createTest($accoutTooLangBlock)) && p('account:0') && e('『所属用户』长度应当不超过『30』,且大于『0』。'); // 测试account 字段字符超出长度
r($tester->block->getByID($newBlockID)) && p('account,vision,dashboard,module,code,title,order') && e('admin,rnd,my,project,statistic,项目统计区块,1'); // 测试获取正常的block的内容
r($blockTest->createTest($emptyModuleBlock)) && p('module:0') && e('『所属模块』不能为空。'); // 测试所属模块为空
r($blockTest->createTest($emptyCodeBlock)) && p('code:0') && e('『区块』不能为空。'); // 测试所属区块为空
r($blockTest->createTest($emptyTitleBlock)) && p('title:0') && e('『区块名称』不能为空。'); // 测试区块名称为空
r($blockTest->createTest($accountTooLangBlock)) && p('account:0') && e('『所属用户』长度应当不超过『30』,且大于『0』。'); // 测试account 字段字符超出长度
@@ -1,26 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getAssignToMeParams();
cid=1
pid=1
获取当前登陆用户待办的默认参数 >> 待办数,20,input
获取当前登陆用户任务的默认参数 >> 任务数,20,input
获取当前登陆用户Bug的默认参数 >> Bug数,20,input
获取当前登陆用户需求的默认参数 >> 需求数,20,input
*/
$block = new blockTest();
$data = $block->getAssignToMeParamsTest();
r($data) && p('todoCount:name,default,control') && e('待办数,20,input'); // 获取当前登陆用户待办的默认参数
r($data) && p('taskCount:name,default,control') && e('任务数,20,input'); // 获取当前登陆用户任务的默认参数
r($data) && p('bugCount:name,default,control') && e('Bug数,20,input'); // 获取当前登陆用户Bug的默认参数
r($data) && p('storyCount:name,default,control') && e('需求数,20,input'); // 获取当前登陆用户需求的默认参数
@@ -9,9 +9,6 @@ title=测试 blockModel->getAvailableBlocks();
cid=1
pid=1
通过获取项目的可用区块 >> 项目计划,最新动态
通过获取测试的可用区块 >> 测试统计,Bug列表,用例列表,版本列表
*/
$block = new blockTest();
-26
View File
@@ -1,26 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getBlock();
cid=1
pid=1
测试获取block的所属模块 >> my
测试获取block的名称 >> 项目统计
测试获取block的来源 >> project
测试获取block的区块 >> statistic
*/
$block = new blockTest();
$data = $block->getBlockTest(99);
r($data) && p('module') && e('my'); // 测试获取block的所属模块
r($data) && p('title') && e('项目统计'); // 测试获取block的名称
r($data) && p('source') && e('project'); // 测试获取block的来源
r($data) && p('block') && e('statistic'); // 测试获取block的区块
-21
View File
@@ -1,21 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getBlockList();
cid=1
pid=1
>> 测试统计;指派给我的Bug;指派给我的用例;待测版本列表
>> 项目计划;最新计划
*/
$block = new blockTest();
r($block->getBlockListTest('qa')) && p('107:title;108:title;109:title;110:title') && e('测试统计;指派给我的Bug;指派给我的用例;待测版本列表');
r($block->getBlockListTest('project', 'waterfall')) && p('105:title;106:title') && e('项目计划;最新计划');
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getBugParams();
cid=1
pid=1
测试通过模块获取Bug区块的参数 >> 数量;类型;排序
测试通过模块获取Bug区块的参数 >> 数量;类型;排序
*/
$block = new blockTest();
$data[0] = $block->getBugParamsTest('qa');
$data[1] = $block->getBugParamsTest('my');
r($data[0]) && p('count:name;type:name;orderBy:name') && e('数量;类型;排序'); //测试通过模块获取Bug区块的参数
r($data[1]) && p('count:name;type:name;orderBy:name') && e('数量;类型;排序'); //测试通过模块获取Bug区块的参数
@@ -1,20 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getBuildParams();
cid=1
pid=1
测试获取版本区块参数 >> 数量,20,input
*/
$block = new blockTest();
$data = $block->getBuildParamsTest();
r($data) && p('count:name,default,control') && e('数量,20,input'); //测试获取版本区块参数
+16 -14
View File
@@ -7,11 +7,13 @@ function initData()
{
$block = zdTable('block');
$block->id->range('2-5');
$block->account->range('admin,test1,test2');
$block->account->range('admin');
$block->vision->range('rnd,lite');
$block->module->range('my,qa,project');
$block->title->prefix('区块')->range('2-5');
$block->order->range('5-2');
$block->dashboard->range('my');
$block->module->range('project');
$block->code->range('list,statistic');
$block->title->prefix('区块名称')->range('2-5');
$block->order->range('1-4');
$block->gen(4);
}
@@ -22,18 +24,18 @@ title=测试 blockModel->getByID();
timeout=0
cid=1
- 执行block模块的getByID方法,参数是2
- 测试获取正常的block的内容
- 属性account @admin
- 属性vision @rnd
- 属性module @my
- 属性title @区块2
- 属性source @my
- 属性block @bug
- 属性order @5
- 属性dashboard @my
- 属性module @project
- 属性code @list
- 属性title @区块名称2
- 属性order @1
- 执行block模块的getByID方法,参数是4,属性order @3
- 执行block模块的getByID方法,参数是6 @0
- 测试获取正常的block的内容属性order @3
- 测试获取不存在的block的内容 @0
*/
@@ -42,6 +44,6 @@ $tester->loadModel('block');
initData();
r($tester->block->getByID(2)) && p('account,vision,module,title,order') && e('admin,rnd,my,区块2,5'); // 测试获取正常的block的内容
r($tester->block->getByID(2)) && p('account,vision,dashboard,module,code,title,order') && e('admin,rnd,my,project,list,区块名称2,1'); // 测试获取正常的block的内容
r($tester->block->getByID(4)) && p('order') && e('3'); // 测试获取正常的block的内容
r($tester->block->getByID(6)) && p('') && e('0'); // 测试获取不存在的block的内容
r($tester->block->getByID(6)) && p('') && e('0'); // 测试获取不存在的block的内容
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getCaseParams();
cid=1
pid=1
测试通过模块获取用例区块的参数 >> 数量;类型;排序
测试通过模块获取用例区块的参数 >> 数量;类型;排序
*/
$block = new blockTest();
$data[0] = $block->getCaseParamsTest('qa');
$data[1] = $block->getCaseParamsTest('my');
r($data[0]) && p('count:name;type:name;orderBy:name') && e('数量;类型;排序'); //测试通过模块获取用例区块的参数
r($data[1]) && p('count:name;type:name;orderBy:name') && e('数量;类型;排序'); //测试通过模块获取用例区块的参数
@@ -1,20 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getExecutionParams();
cid=1
pid=1
测试获取执行区块的参数 >> 数量;类型
*/
$block = new blockTest();
$data = $block->getExecutionParamsTest();
r($data) && p('count:name;type:name') && e('数量;类型'); //测试获取执行区块的参数
@@ -1,20 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getExecutionStatisticParams();
cid=1
pid=1
测试获取执行区块的参数 >> 数量;类型
*/
$block = new blockTest();
$data = $block->getExecutionStatisticParamsTest();
r($data) && p('count:name;type:name') && e('数量;类型'); //测试获取执行区块的参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getHiddenBlocks();
cid=1
pid=1
测试获取隐藏的区块 >> 未获取到隐藏的区块
*/
$block = new blockTest();
r($block->getHiddenBlocksTest('my')) && p('message') && e('未获取到隐藏的区块'); //测试获取隐藏的区块
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getLastKey();
cid=1
pid=1
测试通过qa模块获取区块的key >> 4
测试通过my模块获取区块的key >> 10
*/
$block = new blockTest();
$data[0] = $block->getLastKeyTest('qa');
$data[1] = $block->getLastKeyTest('my');
r($data[0]) && p('qa') && e('4'); //测试通过qa模块获取区块的key
r($data[1]) && p('my') && e('10'); //测试通过my模块获取区块的key
-26
View File
@@ -1,26 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getListParams();
cid=1
pid=1
测试通过模块获取区块的参数 >> 数量
测试通过模块获取区块的参数 >> 数量
测试通过模块获取区块的参数 >> 数量;类型;排序
*/
$block = new blockTest();
$data[0] = $block->getListParamsTest('my');
$data[1] = $block->getListParamsTest('qa');
$data[2] = $block->getListParamsTest('project');
r($data[0]) && p('count:name') && e('数量'); //测试通过模块获取区块的参数
r($data[1]) && p('count:name') && e('数量'); //测试通过模块获取区块的参数
r($data[2]) && p('count:name;type:name;orderBy:name') && e('数量;类型;排序'); //测试通过模块获取区块的参数
@@ -2,20 +2,19 @@
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
$userModel = new userModel();
$user = $userModel->identify('admin', 'Asd123456');
$userModel->login($user);
su('admin');
function initData()
{
$block = zdTable('block');
$block->id->range('2-5');
$block->account->range('admin,test1,test2');
$block->account->range('admin');
$block->vision->range('rnd,lite');
$block->module->range('my,qa,project');
$block->title->prefix('区块')->range('2-5');
$block->dashboard->range('my,qa');
$block->order->range('5-2');
$block->dashboard->range('my');
$block->module->range('project');
$block->code->range('list,statistic');
$block->title->prefix('区块名称')->range('2-5');
$block->order->range('1-4');
$block->gen(4);
}
@@ -26,16 +25,6 @@ title=测试 blockModel->getMaxOrderByDashboard();
timeout=0
cid=1
- 执行block模块的getByID方法,参数是2
- 属性account @admin
- 属性vision @rnd
- 属性module @my
- 属性title @区块2
- 属性source @my
- 属性block @bug
- 属性order @5
*/
global $tester;
@@ -43,6 +32,6 @@ $tester->loadModel('block');
initData();
r($tester->block->getMaxOrderByDashboard('my')) && p('') && e('5'); // 测试 my 模块下最大order 为 5
r($tester->block->getMaxOrderByDashboard('my')) && p('') && e('5'); // 测试 my 模块下最大order 为 5
r($tester->block->getMaxOrderByDashboard('my')) && p('') && e('4'); // 测试 my 模块下最大order 为 4
r($tester->block->getMaxOrderByDashboard('my')) && p('') && e('4'); // 测试 my 模块下最大order 为 4
r($tester->block->getMaxOrderByDashboard('bug')) && p('') && e('0'); // 测试数据库中不存在 模块的最大排序 为 0
+12 -17
View File
@@ -3,25 +3,19 @@
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
#su('admin');
$userModel = new userModel();
$user = $userModel->identify('admin', 'Asd123456');
$userModel->login($user);
su('admin');
function initData()
{
$block = zdTable('block');
$block->id->range('2-3');
$block->id->range('2-5');
$block->account->range('admin');
$block->vision->range('rnd');
$block->module->range('test,my');
$block->title->prefix('区块')->range('3,2');
$block->hidden->range('0-1');
$block->order->range('3-2');
$block->dashboard->range('test,my');
$block->vision->range('rnd,lite');
$block->dashboard->range('my,qa,project');
$block->title->prefix('区块')->range('2-5');
$block->order->range('5-2');
$block->gen(2);
$block->gen(4);
}
/**
@@ -36,7 +30,8 @@ $tester->loadModel('block');
initData();
r($tester->block->getMyDashboard('test')) && p('2:account,title') && e('admin,区块3'); // 测试我的 test 模块的面板
r($tester->block->getMyHiddenBlocks('my')) && p('3:account,title') && e('admin,区块2');// 测试我的 my 模块隐藏的面板
r($tester->block->getMyDashboard('noneModule')) && p('') && e('0');// 测试我的 不存在的 模块的面板
r($tester->block->getMyDashboard('')) && p('') && e('0');// 测试我的 空 模块的面板
r(count($tester->block->getMyDashboard('my'))) && p('') && e('1'); // 查询admin用户在我的地盘仪表盘的区块列表数量
r($tester->block->getMyDashboard('my')) && p('2:title') && e('区块2'); // 查询admin用户在我的地盘仪表盘的区块标题第2条的title属性
r(count($tester->block->getMyDashboard('my', 1))) && p('') && e('0'); // 查询admin用户在我的地盘仪表盘的隐藏区块列表数量
r(count($tester->block->getMyDashboard('asdas?'))) && p('') && e('0'); // 查询admin用户在不存在仪表盘的区块列表数量
r($tester->block->getMyDashboard('my')) && p('2:vision') && e('rnd'); // 查询admin用户在我的地盘仪表盘区块的vision第2条的vision属性
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getPlanParams();
cid=1
pid=1
获取计划参数 >> {"count":{"name":"\u6570\u91cf","default":20,"control":"input"}}
*/
$block = new blockTest();
r($block->getPlanParamsTest()) && p() && e('{"count":{"name":"\u6570\u91cf","default":20,"control":"input"}}'); // 获取计划参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getProductParams();
cid=1
pid=1
获取产品区块的参数 >> type:{name:类型,noclosed=>未关闭,closed=>已关闭,all=>全部,involved=>我参与,control:select};count:{name:数量,default:20,control:input};
*/
$block = new blockTest();
r($block->getProductParamsTest()) && p() && e('type:{name:类型,noclosed=>未关闭,closed=>已关闭,all=>全部,involved=>我参与,control:select};count:{name:数量,default:20,control:input};'); // 获取产品区块的参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getProductStatisticParams();
cid=1
pid=1
获取产品统计区块的参数 >> count:{name:数量,default:20,control:input};type:{name:类型,noclosed=>未关闭,closed=>已关闭,all=>全部,involved=>我参与,control:select};
*/
$block = new blockTest();
r($block->getProductStatisticParamsTest()) && p() && e('count:{name:数量,default:20,control:input};type:{name:类型,noclosed=>未关闭,closed=>已关闭,all=>全部,involved=>我参与,control:select};'); // 获取产品统计区块的参数
@@ -1,18 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getProjectDynamicParams();
cid=1
pid=1
获取项目动态区块参数 >> count:{name:数量,default:20,control:input};
*/
$block = new blockTest();
r($block->getProjectDynamicParamsTest()) && p() && e('count:{name:数量,default:20,control:input};'); // 获取项目动态区块参数
@@ -1,18 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getProjectParams();
cid=1
pid=1
获取计划参数 >> {"type":{"name":"\u7c7b\u578b","options":{"all":"\u6240\u6709","undone":"\u672a\u5b8c\u6210","wait":"\u672a\u5f00\u59cb","doing":"\u8fdb\u884c\u4e2d","suspended":"\u5df2\u6302\u8d77","closed":"\u5df2\u5173\u95ed"},"control":"select"},"orderBy":{"name":"\u6392\u5e8f","options":{"id_asc":"ID \u9012\u589e","id_desc":"ID \u9012\u51cf","status_asc":"\u72b6\u6001\u6b63\u5e8f","status_desc":"\u72b6\u6001\u5012\u5e8f"},"control":"select"},"count":{"name":"\u6570\u91cf","default":20,"control":"input"}}
*/
$block = new blockTest();
r($block->getProjectParamsTest()) && p() && e('{"type":{"name":"\u7c7b\u578b","options":{"all":"\u6240\u6709","undone":"\u672a\u5b8c\u6210","wait":"\u672a\u5f00\u59cb","doing":"\u8fdb\u884c\u4e2d","suspended":"\u5df2\u6302\u8d77","closed":"\u5df2\u5173\u95ed"},"control":"select"},"orderBy":{"name":"\u6392\u5e8f","options":{"id_asc":"ID \u9012\u589e","id_desc":"ID \u9012\u51cf","status_asc":"\u72b6\u6001\u6b63\u5e8f","status_desc":"\u72b6\u6001\u5012\u5e8f"},"control":"select"},"count":{"name":"\u6570\u91cf","default":20,"control":"input"}}'); //获取计划参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getProjectStatisticParams();
cid=1
pid=1
获取project静态param >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"undone":"未完成","doing":"进行中","all":"全部","involved":"我参与的"},"control":"select"}}
*/
$block = new blockTest();
r($block->getProjectStatisticParamsTest()) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"undone":"未完成","doing":"进行中","all":"全部","involved":"我参与的"},"control":"select"}}'); // 获取project静态param
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getProjectTeamParams();
cid=1
pid=1
测试获取项目团队 >> type:{name:类型,all=>所有,undone=>未完成,wait=>未开始,doing=>进行中,suspended=>已挂起,closed=>已关闭,control:select};orderBy:{name:排序,id_asc=>ID 递增,id_desc=>ID 递减,status_asc=>状态正序,status_desc=>状态倒序,control:select};count:{name:数量,default:20,control:input};
*/
$block = new blockTest();
r($block->getProjectTeamParamsTest()) && p() && e('type:{name:类型,all=>所有,undone=>未完成,wait=>未开始,doing=>进行中,suspended=>已挂起,closed=>已关闭,control:select};orderBy:{name:排序,id_asc=>ID 递增,id_desc=>ID 递减,status_asc=>状态正序,status_desc=>状态倒序,control:select};count:{name:数量,default:20,control:input};'); // 测试获取项目团队
@@ -1,21 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getQaStatisticParams();
cid=1
pid=1
测试name >> 类型
测试control >> select
*/
$block = new blockTest();
r($block->getQaStatisticParamsTest()) && p('type:name') && e('类型'); //测试name
r($block->getQaStatisticParamsTest()) && p('type:control') && e('select');//测试control
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getReleaseParams();
cid=1
pid=1
测试name >> 数量,20,input
*/
$block = new blockTest();
r($block->getReleaseParamsTest()) && p('name,default,control') && e('数量,20,input'); //测试name
@@ -1,27 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getScrumListParams();
cid=1
pid=1
测试name和select >> 类型,select
测试options >> 未完成
测试options >> 进行中
测试options >> 全部
测试options >> 我参与
*/
$block = new blockTest();
r($block->getScrumListParamsTest()) && p('name,control') && e('类型,select'); //测试name和select
r($block->getScrumListParamsTest()) && p('options:undone') && e('未完成'); //测试options
r($block->getScrumListParamsTest()) && p('options:doing') && e('进行中'); //测试options
r($block->getScrumListParamsTest()) && p('options:all') && e('全部'); //测试options
r($block->getScrumListParamsTest()) && p('options:involved') && e('我参与'); //测试options
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getScrumProductParams();
cid=1
pid=1
测试获取产品区块的参数 >> 数量;20;input
*/
$block = new blockTest();
r($block->getScrumProductParamsTest()) && p('count:name;count:default;count:control') && e('数量;20;input'); // 测试获取产品区块的参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getScrumRoadMapParams();
cid=1
pid=1
测试获取产品路线图区块的参数 >> 0
*/
$block = new blockTest();
r($block->getScrumRoadMapParamsTest()) && p() && e('0'); // 测试获取产品路线图区块的参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getScrumTestParams();
cid=1
pid=1
测试获取测试单区块的参数 >> 数量;类型
*/
$block = new blockTest();
r($block->getScrumTestParamsTest()) && p('count:name;type:name') && e('数量;类型'); // 测试获取测试单区块的参数
@@ -1,34 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getStatisticParams();
cid=1
pid=1
获取空区块的参数 >> {"count":{"name":"数量","default":20,"control":"input"}}
获取产品区块的参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"noclosed":"未关闭","closed":"已关闭","all":"全部","involved":"我参与"},"control":"select"}}
获取项目区块的参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"undone":"未完成","doing":"进行中","all":"全部","involved":"我参与的"},"control":"select"}}
获取执行区块的参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"undone":"未完成","doing":"进行中","all":"所有","involved":"我参与"},"control":"select"}}
获取测试区块的参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"noclosed":"未关闭","closed":"已关闭","all":"全部","involved":"我参与"},"control":"select"}}
*/
$block = new blockTest();
$data = array();
$data[0] = $block->getStatisticParamsTest('');
$data[1] = $block->getStatisticParamsTest('product');
$data[2] = $block->getStatisticParamsTest('project');
$data[3] = $block->getStatisticParamsTest('execution');
$data[4] = $block->getStatisticParamsTest('qa');
r($data[0]) && p() && e('{"count":{"name":"数量","default":20,"control":"input"}}'); // 获取空区块的参数
r($data[1]) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"noclosed":"未关闭","closed":"已关闭","all":"全部","involved":"我参与"},"control":"select"}}'); // 获取产品区块的参数
r($data[2]) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"undone":"未完成","doing":"进行中","all":"全部","involved":"我参与的"},"control":"select"}}'); // 获取项目区块的参数
r($data[3]) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"undone":"未完成","doing":"进行中","all":"所有","involved":"我参与"},"control":"select"}}'); // 获取执行区块的参数
r($data[4]) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"noclosed":"未关闭","closed":"已关闭","all":"全部","involved":"我参与"},"control":"select"}}'); // 获取测试区块的参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getStoryParams();
cid=1
pid=1
获取需求的参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"assignedTo":"指派给我","openedBy":"由我创建","reviewedBy":"由我评审","closedBy":"由我关闭"},"control":"select"},"orderBy":{"name":"排序","default":"id_desc","options":{"id_asc":"ID 递增","id_desc":"ID 递减","pri_asc":"优先级递增","pri_desc":"优先级递减","status_asc":"状态正序","status_desc":"状态倒序","stage_asc":"阶段正序","stage_desc":"阶段倒序"},"control":"select"}}
*/
$block = new blockTest();
r($block->getStoryParamsTest()) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"assignedTo":"指派给我","openedBy":"由我创建","reviewedBy":"由我评审","closedBy":"由我关闭"},"control":"select"},"orderBy":{"name":"排序","default":"id_desc","options":{"id_asc":"ID 递增","id_desc":"ID 递减","pri_asc":"优先级递增","pri_desc":"优先级递减","status_asc":"状态正序","status_desc":"状态倒序","stage_asc":"阶段正序","stage_desc":"阶段倒序"},"control":"select"}}'); // 获取需求的参数
@@ -1,29 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getStorysEstimateHours();
cid=1
pid=1
获取需求 1 的预计工时 >> 1
获取需求 2 的预计工时 >> 1
获取需求 3 的预计工时 >> 1
获取需求 4 的预计工时 >> 1
获取需求 5 的预计工时 >> 1
*/
$storyID = array(1, 2, 3, 4, 5);
$block = new blockTest();
r($block->getStorysEstimateHoursTest($storyID[0])) && p('estimate') && e('1'); // 获取需求 1 的预计工时
r($block->getStorysEstimateHoursTest($storyID[1])) && p('estimate') && e('1'); // 获取需求 2 的预计工时
r($block->getStorysEstimateHoursTest($storyID[2])) && p('estimate') && e('1'); // 获取需求 3 的预计工时
r($block->getStorysEstimateHoursTest($storyID[3])) && p('estimate') && e('1'); // 获取需求 4 的预计工时
r($block->getStorysEstimateHoursTest($storyID[4])) && p('estimate') && e('1'); // 获取需求 5 的预计工时
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getTaskParams();
cid=1
pid=1
获取task的参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"assignedTo":"指派给我","openedBy":"由我创建","finishedBy":"由我完成","closedBy":"由我关闭","canceledBy":"由我取消"},"control":"select"},"orderBy":{"name":"排序","default":"id_desc","options":{"id_asc":"ID 递增","id_desc":"ID 递减","pri_asc":"优先级递增","pri_desc":"优先级递减","estimate_asc":"预计时间递增","estimate_desc":"预计时间递减","status_asc":"状态正序","status_desc":"状态倒序","deadline_asc":"截止日期递增","deadline_desc":"截止日期递减"},"control":"select"}}
*/
$block = new blockTest();
r($block->getTaskParamsTest()) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"assignedTo":"指派给我","openedBy":"由我创建","finishedBy":"由我完成","closedBy":"由我关闭","canceledBy":"由我取消"},"control":"select"},"orderBy":{"name":"排序","default":"id_desc","options":{"id_asc":"ID 递增","id_desc":"ID 递减","pri_asc":"优先级递增","pri_desc":"优先级递减","estimate_asc":"预计时间递增","estimate_desc":"预计时间递减","status_asc":"状态正序","status_desc":"状态倒序","deadline_asc":"截止日期递增","deadline_desc":"截止日期递减"},"control":"select"}}'); // 获取task的参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getTesttaskParams();
cid=1
pid=1
获取testtask参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"wait":"待测版本","doing":"测试中版本","blocked":"阻塞版本","done":"已测版本","all":"全部"},"control":"select"}}
*/
$block = new blockTest();
r($block->getTesttaskParamsTest()) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":{"wait":"待测版本","doing":"测试中版本","blocked":"阻塞版本","done":"已测版本","all":"全部"},"control":"select"}}'); // 获取testtask参数
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getTodoParams();
cid=1
pid=1
获取todo参数 >> {"count":{"name":"数量","default":20,"control":"input"}}
*/
$block = new blockTest();
r($block->getTodoParamsTest()) && p() && e('{"count":{"name":"数量","default":20,"control":"input"}}'); // 获取todo参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getWaterfallIssueParams();
cid=1
pid=1
获取waterfall issue的参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":null,"control":"select"},"orderBy":{"name":"排序","options":{"id_asc":"ID 递增","id_desc":"ID 递减","status_asc":"状态正序","status_desc":"状态倒序"},"control":"select"}}
*/
$block = new blockTest();
r($block->getWaterfallIssueParamsTest()) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":null,"control":"select"},"orderBy":{"name":"排序","options":{"id_asc":"ID 递增","id_desc":"ID 递减","status_asc":"状态正序","status_desc":"状态倒序"},"control":"select"}}'); // 获取waterfall issue的参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getWaterfallRiskParams();
cid=1
pid=1
获取waterfall risk 参数 >> {"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":null,"control":"select"},"orderBy":{"name":"排序","options":{"id_asc":"ID 递增","id_desc":"ID 递减","status_asc":"状态正序","status_desc":"状态倒序"},"control":"select"}}
*/
$block = new blockTest();
r($block->getWaterfallRiskParamsTest()) && p() && e('{"count":{"name":"数量","default":20,"control":"input"},"type":{"name":"类型","options":null,"control":"select"},"orderBy":{"name":"排序","options":{"id_asc":"ID 递增","id_desc":"ID 递减","status_asc":"状态正序","status_desc":"状态倒序"},"control":"select"}}'); // 获取waterfall risk 参数
@@ -1,19 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->getWelcomeBlockData();
cid=1
pid=1
获取欢迎区块的数据 >> {"tasks":"0","doneTasks":"0","bugs":85,"stories":0,"executions":739,"products":80,"delayTask":0,"delayBug":74,"delayProject":0}
*/
$block = new blockTest();
r($block->getWelcomeBlockDataTest()) && p() && e('{"tasks":"0","doneTasks":"0","bugs":85,"stories":0,"executions":739,"products":80,"delayTask":0,"delayBug":74,"delayProject":0}'); // 获取欢迎区块的数据
+21 -44
View File
@@ -7,54 +7,31 @@ su('admin');
/**
title=测试 blockModel->initBlock();
timeout=0
cid=1
pid=1
获取敏捷项目的区块版本 >> 1;2
获取敏捷项目的区块数据 >> project;scrum
获取瀑布项目的区块版本 >> 1;2
获取瀑布项目的区块数据 >> project;waterfall
获取看板项目的区块版本 >> 1;2
获取看板项目的区块数据 >> project;kanban
获取产品的区块版本 >> 1;2
获取产品的区块数据 >> product;
获取执行的区块版本 >> 1;2
获取执行的区块数据 >> execution;
获取地盘的区块版本 >> 1;2
获取地盘的区块数据 >> my;
获取测试的区块版本 >> 1;2
获取测试的区块数据 >> qa;
获取空区块版本 >> 1;2
获取空区块数据 >> 0
- 测试获取正常的block的内容
- 属性account @admin
- 属性dashboard @my
- 属性module @welcome
- 属性code @welcome
- 属性title @欢迎
- 测试获取正常的block的内容
- 属性account @admin
- 属性dashboard @my
- 属性module @project
- 属性code @project
- 属性title @项目列表
*/
$block = new blockTest();
global $tester, $app;
$tester->loadModel('block');
$tester->block->reset('my');
$dataList = array();
$dataList[0] = $block->initBlockTest('project', 'scrum');
$dataList[1] = $block->initBlockTest('project', 'waterfall');
$dataList[2] = $block->initBlockTest('project', 'kanban');
$dataList[3] = $block->initBlockTest('product');
$dataList[4] = $block->initBlockTest('execution');
$dataList[5] = $block->initBlockTest('my');
$dataList[6] = $block->initBlockTest('qa');
$dataList[7] = $block->initBlockTest('');
r($dataList[0]) && p("blockInited;blockversion") && e('1;2'); // 获取敏捷项目的区块版本
r($dataList[0]) && p("blockData:module;blockData:type") && e('project;scrum'); // 获取敏捷项目的区块数据
r($dataList[1]) && p("blockInited;blockversion") && e('1;2'); // 获取瀑布项目的区块版本
r($dataList[1]) && p("blockData:module;blockData:type") && e('project;waterfall'); // 获取瀑布项目的区块数据
r($dataList[2]) && p("blockInited;blockversion") && e('1;2'); // 获取看板项目的区块版本
r($dataList[2]) && p("blockData:module;blockData:type") && e('project;kanban'); // 获取看板项目的区块数据
r($dataList[3]) && p("blockInited;blockversion") && e('1;2'); // 获取产品的区块版本
r($dataList[3]) && p("blockData:module;blockData:type") && e('product;'); // 获取产品的区块数据
r($dataList[4]) && p("blockInited;blockversion") && e('1;2'); // 获取执行的区块版本
r($dataList[4]) && p("blockData:module;blockData:type") && e('execution;'); // 获取执行的区块数据
r($dataList[5]) && p("blockInited;blockversion") && e('1;2'); // 获取地盘的区块版本
r($dataList[5]) && p("blockData:module;blockData:type") && e('my;'); // 获取地盘的区块数据
r($dataList[6]) && p("blockInited;blockversion") && e('1;2'); // 获取测试的区块版本
r($dataList[6]) && p("blockData:module;blockData:type") && e('qa;'); // 获取测试的区块数据
r($dataList[7]) && p("blockInited;blockversion") && e('1;2'); // 获取空区块版本
r($dataList[7]) && p("blockData") && e('0'); // 获取空区块数据
$tester->block->initBlock('my');
$newBlcoks = $tester->block->getMyDashboard('my');
r(reset($newBlcoks)) && p('account,dashboard,module,code,title') && e('admin,my,welcome,welcome,欢迎'); // 测试获取正常的block的内容
r(end($newBlcoks)) && p('account,dashboard,module,code,title') && e('admin,my,project,project,项目列表'); // 测试获取正常的block的内容
+30 -19
View File
@@ -2,35 +2,46 @@
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
function initData()
{
$block = zdTable('block');
$block->id->range('2-5');
$block->account->range('admin');
$block->vision->range('rnd,lite');
$block->dashboard->range('my');
$block->module->range('project');
$block->code->range('list,statistic');
$block->title->prefix('区块名称')->range('2-5');
$block->grid->range('4,8');
$block->order->range('1-4');
$block->gen(4);
}
/**
title=测试 blockModel->isLongBlock();
timeout=0
cid=1
pid=1
测试 空 对象 检查是否为长区块 >> 2
测试 对象grid为 5 的时候 检查是否为长区块 >> 2
测试 对象grid为 6 的时候 检查是否为长区块 >> 1
测试 对象grid为 7 的时候 检查是否为长区块 >> 1
- 测试 空 对象 检查是否为长区块 @0
- 测试 空 对象 检查是否为长区块 @0
- 测试 空 对象 检查是否为长区块 @1
*/
$block1 = new stdclass();
global $tester;
$tester->loadModel('block');
$block2 = new stdclass();
$block2->grid = 5;
initData();
$block3 = new stdclass();
$block3->grid = 6;
$emptyObject = new stdclass();
$block4 = new stdclass();
$block4->grid = 7;
$block = new blockTest();
r($block->isLongBlockTest($block1)) && p() && e('2'); // 测试 空 对象 检查是否为长区块
r($block->isLongBlockTest($block2)) && p() && e('2'); // 测试 对象grid为 5 的时候 检查是否为长区块
r($block->isLongBlockTest($block3)) && p() && e('1'); // 测试 对象grid为 6 的时候 检查是否为长区块
r($block->isLongBlockTest($block4)) && p() && e('1'); // 测试 对象grid为 7 的时候 检查是否为长区块
r($tester->block->isLongBlock($emptyObject)) && p() && e('0'); // 测试 空 对象 检查是否为长区块
r($tester->block->isLongBlock($tester->block->getByID(2))) && p() && e('0'); // 测试 空 对象 检查是否为长区块
r($tester->block->isLongBlock($tester->block->getByID(3))) && p() && e('1'); // 测试 空 对象 检查是否为长区块
-56
View File
@@ -1,56 +0,0 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/block.class.php';
su('admin');
/**
title=测试 blockModel->save();
cid=1
pid=1
测试修改block 名称 >> 欢迎test,my,8,welcome
测试修改block 模块 >> 我的贡献,my,8,dynamic
测试修改block 区块 >> 项目统计,projectteam,8,projectteam
测试修改block 变量 >> 未关闭的产品,statistic,8,statistic
*/
$blockID = array('95', '99', '113');
$source = 'product';
$changeTitle = new stdclass();
$changeTitle->modules = 'welcome';
$changeTitle->title = '欢迎test';
$changeTitle->grid = 8;
$changeTitle->actionLink = '/block-set-95-welcome-.html';
$changeModules = new stdclass();
$changeModules->modules = 'dynamic';
$changeModules->title = '我的贡献';
$changeModules->grid = 8;
$changeModules->actionLink = '/block-set-95-welcome-.html';
$changeBlock = new stdclass();
$changeBlock->modules = 'project';
$changeBlock->title = '项目统计';
$changeBlock->moduleBlock = 'projectteam';
$changeBlock->grid = 8;
$changeBlock->params = array('type' => 'all', 'orderBy' => 'id_asc', 'count' => 20);
$changeBlock->actionLink = '/block-set-95-welcome-.html';
$changeParams = new stdclass();
$changeParams->modules = 'product';
$changeParams->title = '未关闭的产品';
$changeParams->moduleBlock = 'statistic';
$changeParams->grid = 8;
$changeParams->params = array('type' => 'noclosed', 'count' => 30);
$changeParams->actionLink = '/block-set-95-welcome-.html';
$block = new blockTest();
r($block->saveTest($changeTitle, $blockID[0], $source, $changeTitle->modules)) && p('title,module,grid,block') && e('欢迎test,my,8,welcome'); // 测试修改block 名称
r($block->saveTest($changeModules, $blockID[0], $source, $changeModules->modules)) && p('title,module,grid,block') && e('我的贡献,my,8,dynamic'); // 测试修改block 模块
r($block->saveTest($changeBlock, $blockID[1], $source, $changeBlock->modules, $changeBlock->moduleBlock)) && p('title,module,grid,block') && e('项目统计,projectteam,8,projectteam'); // 测试修改block 区块
r($block->saveTest($changeParams, $blockID[2], $source, $changeParams->modules, $changeParams->moduleBlock)) && p('title,module,grid,block') && e('未关闭的产品,statistic,8,statistic'); // 测试修改block 变量
+58 -34
View File
@@ -8,14 +8,16 @@ su('admin');
function initData()
{
$block = zdTable('block');
$block->id->range('2');
$block->account->range('test');
$block->vision->range('rnd');
$block->module->range('test');
$block->title->prefix('区块')->range('2');
$block->order->range('5');
$block->id->range('2-5');
$block->account->range('admin');
$block->vision->range('rnd,lite');
$block->dashboard->range('my');
$block->module->range('project');
$block->code->range('list,statistic');
$block->title->prefix('区块名称')->range('2-5');
$block->order->range('1-4');
$block->gen(1);
$block->gen(4);
}
/**
@@ -24,18 +26,22 @@ title=测试 block 模块的update 方法
timeout=0
cid=39
- 执行block模块的getByID方法,参数是$newBlockID
- 属性account @admin
- 属性vision @rnd
- 属性module @my
- 属性title @区块123
- 属性source @my
- 属性block @bug
- 属性order @5
- 测试获取正常的block的内容
- 属性account @admin2
- 属性vision @lite
- 属性dashboard @scrumproject
- 属性module @project
- 属性code @statistic
- 属性title @项目统计区块
- 属性order @1
- 执行blockTest模块的create方法,参数是$accoutTooLangBlock,属性account @『所属用户』长度应当不超过『30』,且大于『0』。
- 测试所属模块为空 @『所属模块』不能为空。
- 测试所属区块为空 @『区块』不能为空。
- 测试区块名称为空 @『区块名称』不能为空。
- 测试account 字段字符超出长度 @『所属用户』长度应当不超过『30』,且大于『0』。
*/
@@ -44,25 +50,43 @@ $tester->loadModel('block');
initData();
$newBlock = new stdclass();
$newBlock->id = '2';
$newBlock->account = 'newadmin';
$newBlock->vision = 'lite';
$newBlock->module = 'newmy';
$newBlock->title = 'new区块';
$newBlock->params = '';
$newBlock->order = '5';
$block = new stdclass();
$block->id = '2';
$block->account = 'admin2';
$block->vision = 'lite';
$block->dashboard = 'scrumproject';
$block->module = 'project';
$block->code = 'statistic';
$block->title = '项目统计区块';
$block->order = '1';
$accoutTooLangBlock = new stdclass();
$accoutTooLangBlock->id = '2';
$accoutTooLangBlock->account = 'adminadminadminadminadminadminadminadmin1';
$accoutTooLangBlock->vision = 'rnd';
$accoutTooLangBlock->module = 'my';
$accoutTooLangBlock->title = 'long account';
$accoutTooLangBlock->order = '5';
$accountTooLangBlock = new stdclass();
$accountTooLangBlock->id = '3';
$accountTooLangBlock->account = 'adminadminadminadminadminadminadminadmin1';
$accountTooLangBlock->vision = 'rnd';
$accountTooLangBlock->dashboard = 'my';
$accountTooLangBlock->module = 'project';
$accountTooLangBlock->code = 'statistic';
$accountTooLangBlock->title = '项目统计区块2';
$accountTooLangBlock->order = '2';
$emptyModuleBlock = new stdclass();
$emptyModuleBlock->id = '4';
$emptyModuleBlock->module = '';
$emptyCodeBlock = new stdclass();
$emptyCodeBlock->id = '4';
$emptyCodeBlock->code = '';
$emptyTitleBlock = new stdclass();
$emptyTitleBlock->id = '4';
$emptyTitleBlock->title = '';
$blockTest = new blockTest();
$newBlockID = $blockTest->updateTest($newBlock);
$newBlockID = $blockTest->updateTest($block);
r($tester->block->getByID($newBlockID)) && p('account,vision,module,title,order') && e("newadmin,lite,newmy,new区块,5"); // 测试获取正常的block的内容
r($blockTest->updateTest($accoutTooLangBlock)) && p('account:0') && e('『所属用户』长度应当不超过『30』,且大于『0』。'); // 测试account 字段字符超出长度
r($tester->block->getByID($newBlockID)) && p('account,vision,dashboard,module,code,title,order') && e("admin2,lite,scrumproject,project,statistic,项目统计区块,1"); // 测试获取正常的block的内容
r($blockTest->updateTest($emptyModuleBlock)) && p('module:0') && e('『所属模块』不能为空。'); // 测试所属模块为空
r($blockTest->updateTest($emptyCodeBlock)) && p('code:0') && e('『区块』不能为空。'); // 测试所属区块为空
r($blockTest->updateTest($emptyTitleBlock)) && p('title:0') && e('『区块名称』不能为空。'); // 测试区块名称为空
r($blockTest->updateTest($accountTooLangBlock)) && p('account:0') && e('『所属用户』长度应当不超过『30』,且大于『0』。'); // 测试account 字段字符超出长度
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
su('admin');
function initData()
{
$block = zdTable('block');
$block->id->range('2-5');
$block->account->range('admin');
$block->vision->range('rnd,lite');
$block->dashboard->range('my,qa,project');
$block->title->prefix('区块')->range('2-5');
$block->order->range('5-2');
$block->gen(4);
}
/**
title=blockTao->fetchMyBlocks();
timeout=0
cid=2
*/
$tester->loadModel('block');
initData();
r(count($tester->block->fetchMyBlocks('my'))) && p('') && e('1'); // 查询admin用户在我的地盘仪表盘的区块列表数量
r($tester->block->fetchMyBlocks('my')) && p('2:title') && e('区块2'); // 查询admin用户在我的地盘仪表盘的区块标题第2条的title属性
r(count($tester->block->fetchMyBlocks('my', 1))) && p('') && e('0'); // 查询admin用户在我的地盘仪表盘的隐藏区块列表数量
r(count($tester->block->fetchMyBlocks('asdas?'))) && p('') && e('0'); // 查询admin用户在不存在仪表盘的区块列表数量
r($tester->block->fetchMyBlocks('my')) && p('2:vision') && e('rnd'); // 查询admin用户在我的地盘仪表盘区块的vision第2条的vision属性
@@ -0,0 +1,289 @@
<?php
declare(strict_types=1);
/**
* The product statistic block view file of block module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wangyuting <wangyuting@easycorp.ltd>
* @package block
* @link https://www.zentao.net
*/
namespace zin;
/**
* 获取区块左侧的产品列表.
* Get product tabs on the left side.
*
* @param array $products
* @param string $blockNavCode
* @access public
* @return array
*/
function getProductTabs($products, $blockNavCode): array
{
$navTabs = array();
$selected = key($products);
foreach($products as $product)
{
$navTabs[] = li
(
set('class', 'nav-item' . ($product->id == $selected ? ' active' : '')),
a
(
set('class', 'ellipsis'),
set('data-toggle', 'tab'),
set('href', "#tab3{$blockNavCode}Content{$product->id}"),
$product->name
),
a
(
set('class', 'link flex-1 text-right hidden'),
set('href', helper::createLink('product', 'browse', "productID=$product->id")),
icon
(
set('class', 'rotate-90 text-primary'),
'export'
)
)
);
}
return $navTabs;
}
/**
* 获取区块右侧显示的项目信息.
* Get product statistical information.
*
* @param object $products
* @param string $blockNavID
* @access public
* @return array
*/
function getProductInfo($products, $blockNavID): array
{
global $lang;
$selected = key($products);
$tabItems = array();
foreach($products as $product)
{
$tabItems[] = div
(
set('class', 'tab-pane' . ($product->id == $selected ? ' active' : '')),
set('id', "tab3{$blockNavID}Content{$product->id}"),
div
(
set('class', 'flex'),
cell
(
set('width', '68%'),
div
(
set('class', 'flex'),
cell
(
set('width', '45%'),
set('class', 'px-3 py-2'),
div
(
set('class', 'mx-6 my-4 bg-primary aspect-square text-center align-middle'),
span('需求交付率')
),
div
(
set('class', 'flex'),
cell
(
set('class', 'flex-1 text-center'),
div(span('8073')),
div(span('BUG总数'))
),
cell
(
set('class', 'flex-1 text-center'),
div(span('6549')),
div(span('已关闭'))
),
cell
(
set('class', 'flex-1 text-center'),
div(span('1542')),
div(span('未关闭'))
)
)
),
cell
(
set('width', '55%'),
set('class', 'py-2'),
div
(
set('class', 'py-2'),
span('BUG统计')
),
div
(
set('class', 'flex border-r py-1'),
cell
(
div(set('class', 'py-1'), span('昨日新增'), span(set('class', 'ml-1'), '12')),
div(set('class', 'py-1'), span('今日新增'), span(set('class', 'ml-1'), '6')),
),
cell
(
set('class', 'flex-1 px-3'),
div
(
set('class', 'py-1'),
div
(
set('class', 'progress'),
div
(
set('class', 'progress-bar'),
set('role', 'progressbar'),
setStyle(['width' => '36%']),
)
)
),
div
(
set('class', 'py-1'),
div
(
set('class', 'progress'),
div
(
set('class', 'progress-bar'),
set('role', 'progressbar'),
setStyle(['width' => '18%']),
)
)
)
)
),
div
(
set('class', 'flex border-r py-1'),
cell
(
div(set('class', 'py-1'), span('昨日解决'), span(set('class', 'ml-1'), '36')),
div(set('class', 'py-1'), span('今日解决'), span(set('class', 'ml-1'), '12')),
),
cell
(
set('class', 'flex-1 px-3'),
div
(
set('class', 'py-1'),
div
(
set('class', 'progress'),
div
(
set('class', 'progress-bar'),
set('role', 'progressbar'),
setStyle(['width' => '84%']),
)
)
),
div
(
set('class', 'py-1'),
div
(
set('class', 'progress'),
div
(
set('class', 'progress-bar'),
set('role', 'progressbar'),
setStyle(['width' => '28%']),
)
)
)
)
),
div
(
set('class', 'flex border-r py-1'),
cell
(
div(set('class', 'py-1'), span('昨日关闭'), span(set('class', 'ml-1'), '24')),
div(set('class', 'py-1'), span('今日关闭'), span(set('class', 'ml-1'), '12')),
),
cell
(
set('class', 'flex-1 px-3'),
div
(
set('class', 'py-1'),
div
(
set('class', 'progress'),
div
(
set('class', 'progress-bar'),
set('role', 'progressbar'),
setStyle(['width' => '60%']),
)
)
),
div
(
set('class', 'py-1'),
div
(
set('class', 'progress'),
div
(
set('class', 'progress-bar'),
set('role', 'progressbar'),
setStyle(['width' => '30%']),
)
)
)
)
)
)
)
),
cell
(
set('width', '32%'),
123
)
)
);
}
return $tabItems;
}
$blockNavCode = 'nav-' . uniqid();
div
(
set('class', 'productstatistic-block'),
div
(
set('class', 'flex'),
cell
(
set('width', '25%'),
set('class', 'of-hidden bg-secondary-pale'),
ul
(
set('class', 'nav nav-tabs nav-stacked'),
getProductTabs($products, $blockNavCode)
),
),
cell
(
set('class', 'tab-content'),
set('width', '75%'),
getProductInfo($products, $blockNavCode)
)
)
);
render();
+14 -16
View File
@@ -1,7 +1,7 @@
<?php
declare(strict_types=1);
/**
* The project block view file of block module of ZenTaoPMS.
* The project statistic block view file of block module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wangyuting <wangyuting@easycorp.ltd>
@@ -14,9 +14,9 @@ namespace zin;
/**
* 获取区块左侧的项目列表.
* Get project tabs on the left side.
*
* @param array $projects
* @param string $blockNavCode
*
* @param array $projects
* @param string $blockNavCode
* @access public
* @return array
*/
@@ -54,10 +54,10 @@ function getProjectTabs($projects, $blockNavCode): array
/**
* 获取区块右侧显示的项目信息.
* Get project statistical information.
*
* @param object $projects
* @param string $blockNavID
* Get project statistical information.
*
* @param object $projects
* @param string $blockNavID
* @access public
* @return array
*/
@@ -80,8 +80,8 @@ function getProjectInfo($projects, $blockNavID): array
/**
* 获取敏捷类项目的统计信息.
* Get scrum project info.
*
* @param object $project
*
* @param object $project
* @access public
* @return void
*/
@@ -89,7 +89,7 @@ function getScrumProjectInfo($project)
{
global $lang;
$scrumProjectInfo = div
return div
(
/* 区块右侧顶部的项目概况。 */
div
@@ -158,14 +158,13 @@ function getScrumProjectInfo($project)
getProjectStatisticItems($project)
)
);
return $scrumProjectInfo;
}
/**
* 获取项目的统计项.
* get project statistic items.
*
* @param object $project
*
* @param object $project
* @access public
* @return array
*/
@@ -233,7 +232,7 @@ function getWaterfallProjectInfo($project)
{
global $app, $lang;
$isChineseLang = in_array($app->getClientLang(), array('zh-cn','zh-tw'));
$waterfallProjectInfo = div
return div
(
/* 瀑布项目展示概况。 */
set('class', 'weekly-row'),
@@ -343,7 +342,6 @@ function getWaterfallProjectInfo($project)
)
)
);
return $waterfallProjectInfo;
}
$blockNavCode = 'nav-' . uniqid();
+1 -1
View File
@@ -18,7 +18,7 @@ foreach($projects as $projectID => $project)
$cards[] = cell
(
set('width', '33%'),
set('class', 'border m-2 p-4'),
set('class', 'border m-2 p-4'),
div
(
set('class', 'pb-2'),
+1 -3
View File
@@ -1118,9 +1118,7 @@ class blockZen extends block
$this->loadModel('weekly');
$this->app->loadLang('execution');
$projectID = $this->session->project;
$project = $this->loadModel('project')->getByID($projectID);
$projectID = $this->session->project;
$projectWeekly = $this->dao->select('*')->from(TABLE_WEEKLYREPORT)->where('project')->eq($projectID)->orderBy('weekStart_asc')->fetchAll('weekStart');
$charts['PV'] = '[';
+8 -9
View File
@@ -225,14 +225,11 @@ class bugTao extends bugModel
* @param int $bugID
* @param string $linkBug
* @param string $oldLinkBug
* @access private
* @access protected
* @return bool
*/
private function updateLinkBug(int $bugID, string $linkBug, string $oldLinkBug): bool
protected function updateLinkBug(int $bugID, string $linkBug, string $oldLinkBug): bool
{
return false;
$linkBugs = explode(',', $linkBug);
$oldLinkBugs = explode(',', $oldLinkBug);
$addedLinkBugs = array_diff($linkBugs, $oldLinkBugs);
@@ -242,19 +239,21 @@ class bugTao extends bugModel
foreach($changedLinkBugs as $changedBugID => $linkBugs)
{
if(in_array($changeBugID, $addedLinkBugs))
if(in_array($changedBugID, $addedLinkBugs))
{
$currentLinkBug = $bugID;
if(!empty($linkBugs)) $currentLinkBug = trim($linkBugs, ',') . ',' . $bugID;
$linkBugs = explode(',', $linkBugs);
if(!empty($linkBugs) && !in_array($bugID, $linkBugs)) $linkBugs[] = $bugID;
}
else
{
$linkBugs = explode(',', $linkBugs);
unset($linkBugs[array_search($bugID, $linkBugs)]);
$currentLinkBug = implode(',', $linkBugs);
}
$this->dao->update(TABLE_BUG)->set('linkBug')->eq($linkBug)->where('id')->eq($changeBugID)->exec();
$currentLinkBug = implode(',', array_filter($linkBugs));
$this->dao->update(TABLE_BUG)->set('linkBug')->eq($currentLinkBug)->where('id')->eq($changedBugID)->exec();
}
return !dao::isError();
+26
View File
@@ -2135,4 +2135,30 @@ class bugTest
return $ids;
}
}
/**
* The test for updatelinkbug function.
*
* @param string $bugID
* @param string $linkBug
* @param string $oldLinkBug
* @access public
* @return array
*/
public function updateLinkBugTest($bugID, $linkBug, $oldLinkBug)
{
$this->objectModel->updateLinkBug($bugID, $linkBug, $oldLinkBug);
$linkBugs = explode(',', $linkBug);
$oldLinkBugs = explode(',', $oldLinkBug);
$addedLinkBugs = array_diff($linkBugs, $oldLinkBugs);
$removedLinkBugs = array_diff($oldLinkBugs, $linkBugs);
$allRelatedLinkBugs = array_merge($addedLinkBugs, $removedLinkBugs, array($bugID));
global $tester;
$linkBugPairs = $tester->dao->select('id,linkBug')->from(TABLE_BUG)->where('id')->in(array_filter($allRelatedLinkBugs))->andWhere('deleted')->eq('0')->fetchPairs();
if(dao::isError()) return dao::getError();
return $linkBugPairs;
}
}
+32 -8
View File
@@ -42,6 +42,18 @@ cid=1
- 测试不更改bug类型 @没有数据更新
- 测试不输入Bug标题 @『Bug标题』不能为空。
- 测试通知邮件不合法 @『通知邮箱』应当为合法的EMAIL。
- 测试解决者不为空时,不输入解决方案 @『解决方案』不能为空。
- 测试由谁关闭不为空时,不输入解决方案 @『解决方案』不能为空。
- 测试解决方案为重复Bug时,不输入重复Bug值 @『重复Bug』不能为空。
- 测试解决方案为已修复时,不输入解决版本 @『解决版本』不能为空。
*/
@@ -50,13 +62,25 @@ initData();
$bugIdList = array('1', '2');
$t_uptitle = array('title' => 'john');
$t_uptype = array('type' => 'config');
$t_untitle = array('title' => 'john');
$t_untype = array('type' => 'config');
$t_uptitle = array('title' => 'john');
$t_uptype = array('type' => 'config');
$t_untitle = array('title' => 'john');
$t_untype = array('type' => 'config');
$t_titleRequire = array('title' => '');
$t_unnotifyEmail = array('notifyEmail' => '123');
$t_resolution1 = array('resolvedBy' => 'john', 'resolution' => '');
$t_resolution2 = array('closedBy' => 'john', 'resolution' => '');
$t_unduplicateBug = array('resolution' => 'duplicate', 'duplicateBug' => '');
$t_unresolvedBuild = array('resolution' => 'fixed', 'resolvedBuild' => '');
$bug = new bugTest();
r($bug->updateObject($bugIdList[0], $t_uptitle)) && p('0:field,old,new') && e('title,BUG1,john'); // 测试更新bug名称
r($bug->updateObject($bugIdList[0], $t_uptype)) && p('0:field,old,new') && e('type,codeerror,config'); // 测试更新bug类型
r($bug->updateObject($bugIdList[0], $t_untitle)) && p() && e('没有数据更新'); // 测试不更改bug名称
r($bug->updateObject($bugIdList[0], $t_untype)) && p() && e('没有数据更新'); // 测试不更改bug类型
r($bug->updateObject($bugIdList[0], $t_uptitle)) && p('0:field,old,new') && e('title,BUG1,john'); // 测试更新bug名称
r($bug->updateObject($bugIdList[0], $t_uptype)) && p('0:field,old,new') && e('type,codeerror,config'); // 测试更新bug类型
r($bug->updateObject($bugIdList[0], $t_untitle)) && p() && e('没有数据更新'); // 测试不更改bug名称
r($bug->updateObject($bugIdList[0], $t_untype)) && p() && e('没有数据更新'); // 测试不更改bug类型
r($bug->updateObject($bugIdList[0], $t_titleRequire)) && p('title:0') && e('『Bug标题』不能为空。'); // 测试不输入Bug标题
r($bug->updateObject($bugIdList[0], $t_unnotifyEmail)) && p('notifyEmail:0') && e('『通知邮箱』应当为合法的EMAIL。'); // 测试通知邮件不合法
r($bug->updateObject($bugIdList[0], $t_resolution1)) && p('resolution:0') && e('『解决方案』不能为空。'); // 测试解决者不为空时,不输入解决方案
r($bug->updateObject($bugIdList[0], $t_resolution2)) && p('resolution:0') && e('『解决方案』不能为空。'); // 测试由谁关闭不为空时,不输入解决方案
r($bug->updateObject($bugIdList[0], $t_unduplicateBug)) && p('duplicateBug:0') && e('『重复Bug』不能为空。'); // 测试解决方案为重复Bug时,不输入重复Bug值
r($bug->updateObject($bugIdList[0], $t_unresolvedBuild)) && p('resolvedBuild:0') && e('『解决版本』不能为空。'); // 测试解决方案为已修复时,不输入解决版本
@@ -0,0 +1,128 @@
---
fields:
- field: id
range: 1-5
- field: project
range: 0-5
- field: product
range: 1-5
- field: branch
range: 0-1
- field: module
range: 1821-1823,1825-1827,1831-1833,0{1000000}
- field: execution
range: 0-5
- field: plan
range: 1{3},4{3},7{3},0{1111111}
- field: story
range: 2-400:4
- field: storyVersion
range: 1
- field: task
range: 0
- field: toTask
range: 0
- field: toStory
range: 0
- field: title
range: 1-5
prefix: BUG
- field: keywords
range: '[]'
- field: severity
range: '[3]'
- field: pri
range: '[3]'
- field: type
range: '[codeerror]'
- field: os
range: '[]{2},[win10,windows,android,ios]'
- field: browser
range: '[]'
- field: hardware
range: '[]'
- field: found
range: '[]'
- field: steps
range: '[<p>【步骤】</p><br/><p>【结果】</p><br/><p>【期望】</p><br/>,【步骤】进入成果展示<br/>【结果】乱码<br/>【期望】正常显示<br/>]'
- field: status
range: '[active]'
- field: subStatus
range: '[]'
- field: color
range: '[#3da7f5,#75c941,#2dbdb2,#797ec9,#ffaf38,#ff4e3e]'
- field: confirmed
range: 0,1
- field: activatedCount
range: 0
- field: mailto
range: admin{100},[]{200}
- field: openedBy
range: admin
- field: openedDate
range: (-1M)-(+1w):-1D
type: timestamp
format: YYYY-MM-DD hh:mm:ss
- field: openedBuild
range: 1-5
- field: assignedTo
range: '[admin,dev1,test1]{30},[]{20},[admin,dev1,test1]{20},[]{10},closed{20}'
- field: assignedDate
range: (-1M)-(+1w):-1D
type: timestamp
format: YYYY-MM-DD
- field: deadline
range: (-1M)-(+1w):-1D
type: timestamp
prefix: ""
postfix: ""
loop: 0
format: YYYY-MM-DD
- field: resolvedBy
range: '[]{50},$assignedTo{30},admin{20}'
- field: resolution
range: '[]{50},bydesign{5},duplicate{5},external{5},fixed{5},notrepro{2},postponed{3},willnotfix{5},fixed{10},bydesign,external,notrepro,postponed,willnotfix,duplicate{5}'
- field: resolvedBuild
range: '[]{55},trunk{15},[]{25},trunk{5}'
- field: resolvedDate
range: (+1w)-(-2w):-1D
type: timestamp
prefix: ""
postfix: ""
loop: 0
format: YYYY-MM-DD
- field: closedBy
range: '[]{50},admin{30}'
- field: closedDate
range: ~
- field: duplicateBug
range: 0{55},1-5,0{40},10-15
- field: linkBug
range: ~
- field: case
range: 0
- field: caseVersion
range: 0
- field: result
range: 0
- field: repo
range: 0
- field: entry
range: ~
- field: lines
range: ~
- field: v1
range: ~
- field: v2
range: ~
- field: repoType
range: ~
- field: testtask
range: 0
- field: lastEditedBy
range: ~
- field: lastEditedDate
range: ~
- field: deleted
range: 0{40},1{10},0{50}
...
+120
View File
@@ -0,0 +1,120 @@
-- MySQL dump 10.13 Distrib 8.0.32, for Linux (x86_64)
--
-- Host: 127.0.0.1 Database: zentaopms184
-- ------------------------------------------------------
-- Server version 8.0.32-0ubuntu0.22.04.2
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `zt_bug`
--
DROP TABLE IF EXISTS `zt_bug`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `zt_bug` (
`id` mediumint NOT NULL AUTO_INCREMENT,
`project` mediumint unsigned NOT NULL DEFAULT '0',
`product` mediumint unsigned NOT NULL DEFAULT '0',
`injection` mediumint unsigned NOT NULL DEFAULT '0',
`identify` mediumint unsigned NOT NULL DEFAULT '0',
`branch` mediumint unsigned NOT NULL DEFAULT '0',
`module` mediumint unsigned NOT NULL DEFAULT '0',
`execution` mediumint unsigned NOT NULL DEFAULT '0',
`plan` mediumint unsigned NOT NULL DEFAULT '0',
`story` mediumint unsigned NOT NULL DEFAULT '0',
`storyVersion` smallint NOT NULL DEFAULT '1',
`task` mediumint unsigned NOT NULL DEFAULT '0',
`toTask` mediumint unsigned NOT NULL DEFAULT '0',
`toStory` mediumint NOT NULL DEFAULT '0',
`title` varchar(255) NOT NULL DEFAULT '',
`keywords` varchar(255) NOT NULL DEFAULT '',
`severity` tinyint NOT NULL DEFAULT '0',
`pri` tinyint unsigned NOT NULL DEFAULT '0',
`type` varchar(30) NOT NULL DEFAULT '',
`os` varchar(255) NOT NULL DEFAULT '',
`browser` varchar(255) NOT NULL DEFAULT '',
`hardware` varchar(30) NOT NULL DEFAULT '',
`found` varchar(30) NOT NULL DEFAULT '',
`steps` mediumtext,
`status` enum('active','resolved','closed') NOT NULL DEFAULT 'active',
`subStatus` varchar(30) NOT NULL DEFAULT '',
`color` char(7) NOT NULL DEFAULT '',
`confirmed` tinyint(1) NOT NULL DEFAULT '0',
`activatedCount` smallint NOT NULL DEFAULT '0',
`activatedDate` datetime DEFAULT NULL,
`feedbackBy` varchar(100) NOT NULL DEFAULT '',
`notifyEmail` varchar(100) NOT NULL DEFAULT '',
`mailto` text,
`openedBy` varchar(30) NOT NULL DEFAULT '',
`openedDate` datetime DEFAULT NULL,
`openedBuild` varchar(255) NOT NULL DEFAULT '',
`assignedTo` varchar(30) NOT NULL DEFAULT '',
`assignedDate` datetime DEFAULT NULL,
`deadline` date DEFAULT NULL,
`resolvedBy` varchar(30) NOT NULL DEFAULT '',
`resolution` varchar(30) NOT NULL DEFAULT '',
`resolvedBuild` varchar(30) NOT NULL DEFAULT '',
`resolvedDate` datetime DEFAULT NULL,
`closedBy` varchar(30) NOT NULL DEFAULT '',
`closedDate` datetime DEFAULT NULL,
`duplicateBug` mediumint unsigned NOT NULL DEFAULT '0',
`linkBug` varchar(255) NOT NULL DEFAULT '',
`case` mediumint unsigned NOT NULL DEFAULT '0',
`caseVersion` smallint NOT NULL DEFAULT '1',
`feedback` mediumint unsigned NOT NULL DEFAULT '0',
`result` mediumint unsigned NOT NULL DEFAULT '0',
`repo` mediumint unsigned NOT NULL DEFAULT '0',
`mr` mediumint unsigned NOT NULL DEFAULT '0',
`entry` text,
`lines` varchar(10) NOT NULL DEFAULT '',
`v1` varchar(40) NOT NULL DEFAULT '',
`v2` varchar(40) NOT NULL DEFAULT '',
`repoType` varchar(30) NOT NULL DEFAULT '',
`issueKey` varchar(50) NOT NULL DEFAULT '',
`testtask` mediumint unsigned NOT NULL DEFAULT '0',
`lastEditedBy` varchar(30) NOT NULL DEFAULT '',
`lastEditedDate` datetime DEFAULT NULL,
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `product` (`product`),
KEY `execution` (`execution`),
KEY `status` (`status`),
KEY `plan` (`plan`),
KEY `story` (`story`),
KEY `case` (`case`),
KEY `toStory` (`toStory`),
KEY `result` (`result`),
KEY `assignedTo` (`assignedTo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `zt_bug`
--
LOCK TABLES `zt_bug` WRITE;
/*!40000 ALTER TABLE `zt_bug` DISABLE KEYS */;
/*!40000 ALTER TABLE `zt_bug` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2023-05-08 10:24:51
+6
View File
@@ -0,0 +1,6 @@
INSERT INTO zt_bug(`id`, `project`, `product`, `branch`, `module`, `execution`, `plan`, `story`, `storyVersion`, `task`, `toTask`, `toStory`, `title`, `keywords`, `severity`, `pri`, `type`, `os`, `browser`, `hardware`, `found`, `steps`, `status`, `subStatus`, `color`, `confirmed`, `activatedCount`, `mailto`, `openedBy`, `openedDate`, `openedBuild`, `assignedTo`, `assignedDate`, `deadline`, `resolvedBy`, `resolution`, `resolvedBuild`, `resolvedDate`, `closedBy`, `closedDate`, `duplicateBug`, `linkBug`, `case`, `caseVersion`, `result`, `repo`, `entry`, `lines`, `v1`, `v2`, `repoType`, `testtask`, `lastEditedBy`, `lastEditedDate`, `deleted`)
VALUES ('1', '0', '1', '0', '1821', '0', '1', '2', '1', '0', '0', '0', 'BUG1', '', '3', '3', 'codeerror', '', '', '', '', '<p>【步骤】</p><br/><p>【结果】</p><br/><p>【期望】</p><br/>', 'active', '', '#3da7f5', '0', '0', 'admin', 'admin', '2023-04-08 10:24:51', '1', 'admin', '2023-04-08', '2023-04-08', '', '', '', '2023-05-15', '', '', '0', '', '0', '0', '0', '0', '', '', '', '', '', '0', '', '', '0'),
('2', '1', '2', '1', '1822', '1', '1', '6', '1', '0', '0', '0', 'BUG2', '', '3', '3', 'codeerror', '', '', '', '', '【步骤】进入成果展示<br/>【结果】乱码<br/>【期望】正常显示<br/>', 'active', '', '#75c941', '1', '0', 'admin', 'admin', '2023-04-09 10:24:51', '2', 'admin', '2023-04-09', '2023-04-09', '', '', '', '2023-05-14', '', '', '0', '', '0', '0', '0', '0', '', '', '', '', '', '0', '', '', '0'),
('3', '2', '3', '0', '1823', '2', '1', '10', '1', '0', '0', '0', 'BUG3', '', '3', '3', 'codeerror', 'win10', '', '', '', '<p>【步骤】</p><br/><p>【结果】</p><br/><p>【期望】</p><br/>', 'active', '', '#2dbdb2', '0', '0', 'admin', 'admin', '2023-04-10 10:24:51', '3', 'admin', '2023-04-10', '2023-04-10', '', '', '', '2023-05-13', '', '', '0', '', '0', '0', '0', '0', '', '', '', '', '', '0', '', '', '0'),
('4', '3', '4', '1', '1825', '3', '4', '14', '1', '0', '0', '0', 'BUG4', '', '3', '3', 'codeerror', 'windows', '', '', '', '【步骤】进入成果展示<br/>【结果】乱码<br/>【期望】正常显示<br/>', 'active', '', '#797ec9', '1', '0', 'admin', 'admin', '2023-04-11 10:24:51', '4', 'admin', '2023-04-11', '2023-04-11', '', '', '', '2023-05-12', '', '', '0', '', '0', '0', '0', '0', '', '', '', '', '', '0', '', '', '0'),
('5', '4', '5', '0', '1826', '4', '4', '18', '1', '0', '0', '0', 'BUG5', '', '3', '3', 'codeerror', 'android', '', '', '', '<p>【步骤】</p><br/><p>【结果】</p><br/><p>【期望】</p><br/>', 'active', '', '#ffaf38', '0', '0', 'admin', 'admin', '2023-04-12 10:24:51', '5', 'admin', '2023-04-12', '2023-04-12', '', '', '', '2023-05-11', '', '', '0', '', '0', '0', '0', '0', '', '', '', '', '', '0', '', '', '0');
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/bug.class.php';
su('admin');
function initData()
{
$data = zdTable('bug');
$data->id->range('1-5');
$data->product->range('1-5');
$data->branch->range('0-1');
$data->project->range('0-5');
$data->execution->range('0-5');
$data->title->prefix("BUG")->range('1-5');
$data->openedBuild->range('1-5');
$data->type->range("[codeerror]");
$data->status->range("[active]");
$data->pri->range("[3]");
$data->severity->range("[3]");
$data->gen(5);
}
/**
title=bugModel->updateLinkBug();
timeout=0
cid=1
- 测试关联bug2的关联bug同步更新为1属性2 @1
- 测试关联bug2的关联bug同步更新为空,bug3的关联bug为1
- 属性2 @~~
- 属性3 @1
- 测试关联bug2的关联bug同步更新为1属性2 @1
- 测试关联bug3的关联bug同步更新为1,2属性3 @1,2
*/
initData();
$bugIDList = array(1, 2, 3);
$bug = new bugTest();
r($bug->updateLinkBugTest($bugIDList[0], '2', '')) && p('2', ';') && e('1'); //测试关联bug2的关联bug同步更新为1
r($bug->updateLinkBugTest($bugIDList[0], '3', '2')) && p('2;3', ';') && e('~~;1'); //测试关联bug2的关联bug同步更新为空,bug3的关联bug为1
r($bug->updateLinkBugTest($bugIDList[0], '2,3', '3')) && p('2') && e('1'); //测试关联bug2的关联bug同步更新为1
r($bug->updateLinkBugTest($bugIDList[1], '1,3', '1')) && p('3', ';') && e('1,2'); //测试关联bug3的关联bug同步更新为1,2
+4 -2
View File
@@ -829,9 +829,11 @@ class bugZen extends bug
/* 获取影响版本列表和解决版本列表。*/
/* Get the openedBuilds and resolvedBuilds. */
$objectType = '';
if($bug->project) $objectType = 'project';
if($bug->execution) $objectType = 'execution';
$objectID = $bug->execution ? $bug->execution : $bug->project;
$objectType = $bug->project ? 'project' : '';
$objectType = $bug->execution ? 'execution' : $objectType;
$allBuilds = $this->loadModel('build')->getBuildPairs($bug->product, 'all', 'noempty');
$openedBuilds = $this->build->getBuildPairs($bug->product, $bug->branch, $params = 'noempty,noterminate,nodone,withbranch,noreleased', $objectID, $objectType, $bug->openedBuild);
$resolvedBuilds = $openedBuilds;
+3 -3
View File
@@ -380,7 +380,7 @@ class doc extends control
$libID = $this->post->lib;
$moduleID = $this->post->module;
if(empty($libID) and strpos($this->post->module, '_') !== false) list($libID, $moduleID) = explode('_', $this->post->module);
setcookie('lastDocModule', $moduleID, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false);
setcookie('lastDocModule', $moduleID, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, true);
$docResult = $this->doc->create();
if(!$docResult or dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
@@ -1043,7 +1043,7 @@ class doc extends control
{
$this->loadModel('file');
if(empty($viewType)) $viewType = !empty($_COOKIE['docFilesViewType']) ? $this->cookie->docFilesViewType : 'list';
setcookie('docFilesViewType', $viewType, $this->config->cookieLife, $this->config->webRoot, '', false, true);
setcookie('docFilesViewType', $viewType, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, true);
$objects = $this->doc->getOrderedObjects($type, 'nomerge', $objectID);
list($libs, $libID, $object, $objectID, $objectDropdown) = $this->doc->setMenuByType($type, $objectID, 0);
@@ -1271,7 +1271,7 @@ class doc extends control
$docSpaceParam->moduleID = $moduleID;
$docSpaceParam->browseType = $browseType;
$docSpaceParam->param = $param;
setCookie("docSpaceParam", json_encode($docSpaceParam), $this->config->cookieLife, $this->config->webRoot, '', false, true);
setCookie("docSpaceParam", json_encode($docSpaceParam), $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, true);
if($moduleID) $libID = $this->tree->getById($moduleID)->root;
$isFirstLoad = $libID == 0 ? true : false;
+1 -1
View File
@@ -255,7 +255,7 @@ class docModel extends model
->remove('uid,contactListMenu,libType')
->get();
if(defined('IN_UPGRADE') and (($this->config->edition == 'open' and version_compare($this->config->version, '18.4.alpha1', '<')) or ($this->config->edition == 'biz' and version_compare($this->config->version, 'biz8.4.alpha1', '<')) or ($this->config->edition == 'max' and version_compare($this->config->version, 'max4.4.alpha1', '<')))) unset($lib->addedBy, $lib->addedDate);
if(defined('IN_UPGRADE') and (($this->config->edition == 'open' and version_compare($this->config->version, '18.4.alpha1', '<')) or ($this->config->edition == 'biz' and version_compare($this->config->version, 'biz8.4.alpha1', '<')) or ($this->config->edition == 'max' and version_compare($this->config->version, 'max4.4.alpha1', '<')))) unset($data->addedBy, $data->addedDate);
$this->app->loadLang('api');
+1 -1
View File
@@ -13,7 +13,7 @@ if($config->systemMode == 'ALM') $config->product->custom->batchEditFields .= ',
$config->product->list = new stdclass();
$config->product->list->exportFields = 'id,program,line,name,manager,draftStories,activeStories,changedStories,reviewingStories,closedStories,storyCompleteRate,bugs,unResolvedBugs,assignToNullBugs,bugFixedRate,plans,releases';
$config->product->showBranchMethod = 'browse,project';
$config->product->showBranchMethod = ',browse,project,track,';
global $lang, $app;
$app->loadLang('story');
+11 -11
View File
@@ -43,29 +43,28 @@ class product extends control
/**
* Index page, to browse.
*
* @param string $locate locate to browse page or not. If not, display all products.
* @param int $productID
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @param string $locate auto|yes locate to browse page or not. If not, display all products.
* @param string $productID
* @access public
* @return void
*/
public function index($locate = 'auto', $productID = 0, $status = 'noclosed', $orderBy = 'order_desc', $recTotal = 0, $recPerPage = 10, $pageID = 1)
public function index(string $locate = 'auto', string $productID = '0')
{
if($locate == 'yes') $this->locate($this->createLink($this->moduleName, 'browse'));
if($this->app->getViewType() != 'mhtml') unset($this->lang->product->menu->index);
/* Check product id and get product branch. */
$productID = (int)$productID;
$productID = $this->product->saveVisitState($productID, $this->products);
$branch = (int)$this->cookie->preBranch;
/* Set Menu. */
if($this->app->getViewType() != 'mhtml') unset($this->lang->product->menu->index);
if($this->app->viewType == 'mhtml') $this->product->setMenu($productID, $branch);
/* Add create product button for view. */
if(common::hasPriv('product', 'create')) $this->lang->TRActions = html::a($this->createLink('product', 'create'), "<i class='icon icon-sm icon-plus'></i> " . $this->lang->product->create, '', "class='btn btn-primary'");
$this->view->title = $this->lang->product->index;
$this->view->position[] = $this->lang->product->index;
$this->view->title = $this->lang->product->index;
$this->display();
}
@@ -296,6 +295,7 @@ class product extends control
if($this->post->name)
{
/* 从POST中获取数据,并预处理数据。 */
$formConfig = $this->productZen->appendFlowFields($this->config->product->form->batchEdit, 'batch');
$data = form::data($formConfig);
$products = $this->productZen->prepareBatchEditExtras($data);
@@ -647,7 +647,7 @@ class product extends control
$programID = (int)$programID;
/* Set env data. */
$this->productZen->setEnvAll();
$this->productZen->setMenu4All();
/* Generate statistics of products and program. */
$this->app->loadClass('pager', true);
+57 -288
View File
@@ -26,150 +26,28 @@ class productModel extends model
const STORY_TYPE_REQ = 'requirement';
/**
* Get product module menu.
* 获取移动端1.5级导航。
* Get product drop menu in mobile.
*
* @param array $products
* @param int $productID
* @param string $extra
* @param string $branch
* @param string $module
* @param string $moduleType
* @param array $products
* @param int $productID
* @param string $extra
* @param string|int $branch
*
* @access public
* @return string
*/
public function getModuleNav($products, $productID, $extra, $branch, $module = 0, $moduleType = '')
public function getMobileDropMenu(array $products, int $productID, string $extra = '', string|int $branch = ''): string
{
$currentModule = $this->app->getModuleName();
$currentMethod = $this->app->getMethodName();
list($locateModule, $locateMethod) = $this->productTao->computeLocate4DropMenu();
/* init currentModule and currentMethod for report and story. */
if($currentModule == 'story')
{
$storyMethods = ",track,create,batchcreate,batchclose,";
if(strpos($storyMethods, "," . $currentMethod . ",") === false) $currentModule = 'product';
if($currentMethod == 'view' || $currentMethod == 'change' || $currentMethod == 'review') $currentMethod = 'browse';
}
if($currentMethod == 'report') $currentMethod = 'browse';
$selectHtml = $this->select($products, $productID, $currentModule, $currentMethod, $extra, $branch, $module, $moduleType);
$pageNav = '';
$isMobile = $this->app->viewType == 'mhtml';
if($isMobile)
{
$pageNav = html::a(helper::createLink('product', 'index'), $this->lang->product->index) . $this->lang->colon;
$pageNav .= $selectHtml;
}
else
{
$pageNav = $selectHtml;
}
$selectHtml = $this->productTao->buildSelect4Mobile($products, $productID, $locateModule, $locateMethod, $extra, $branch);
$pageNav = html::a(helper::createLink('product', 'index'), $this->lang->product->index) . $this->lang->colon;
$pageNav .= $selectHtml;
return $pageNav;
}
/**
* Create the select code of products.
*
* @param array $products
* @param int $productID
* @param string $currentModule
* @param string $currentMethod
* @param string $extra
* @param int|string $branch
* @param int $module
* @param string $moduleType
* @param bool $withBranch true|false
*
* @access public
* @return string
*/
public function select($products, $productID, $currentModule, $currentMethod, $extra = '', $branch = '', $module = 0, $moduleType = '', $withBranch = true)
{
$isQaModule = (strpos(',project,execution,', ",{$this->app->tab},") !== false and strpos(',bug,testcase,testtask,ajaxselectstory,', ",{$this->app->rawMethod},") !== false and isset($products[0])) ? true : false;
$isFeedbackModel = strpos(',feedback,', ",{$this->app->tab},") !== false ? true : false;
if(count($products) <= 2 and isset($products[0]))
{
unset($products[0]);
$productID = key($products);
}
if(empty($products)) return;
if($this->app->tab == 'project' and strpos(',zeroCase,browseUnits,groupCase,', ",$currentMethod,") !== false) $isQaModule = true;
$this->app->loadLang('product');
if(!$isQaModule and !$productID and !$isFeedbackModel)
{
unset($this->lang->product->menu->settings['subMenu']->branch);
return;
}
$isMobile = $this->app->viewType == 'mhtml';
$productID = $productID == 'all' ? 0 : $productID;
setcookie("lastProduct", $productID, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, true);
if($productID) $currentProduct = $this->getById($productID);
if($isQaModule and $this->app->tab == 'project')
{
if($this->app->tab == 'project') $extra = strpos(',testcase,groupCase,zeroCase,', ",$currentMethod,") !== false ? $extra : $this->session->project;
if($this->app->tab == 'execution') $extra = $this->session->execution;
}
if($isQaModule and !$productID)
{
$currentProduct = new stdclass();
$currentProduct->name = $products[$productID];
$currentProduct->type = 'normal';
}
if($isFeedbackModel and !$productID)
{
$currentProduct = new stdclass();
$currentProduct->name = isset($products[$productID]) ? $products[$productID] : current($products);
$currentProduct->type = 'normal';
}
$this->session->set('currentProductType', $currentProduct->type);
$output = '';
if(!empty($products))
{
$moduleName = 'product';
if($isQaModule) $moduleName = 'bug';
if($isFeedbackModel) $moduleName = 'feedback';
$dropMenuLink = helper::createLink($moduleName, 'ajaxGetDropMenu', "objectID=$productID&module=$currentModule&method=$currentMethod&extra=$extra");
$output = "<div class='btn-group angle-btn'><div class='btn-group'><button data-toggle='dropdown' type='button' class='btn btn-limit' id='currentItem' title='{$currentProduct->name}' style='width: 90%'><span class='text'>{$currentProduct->name}</span> <span class='caret'></span></button><div id='dropMenu' class='dropdown-menu search-list' data-ride='searchList' data-url='$dropMenuLink'>";
$output .= '<div class="input-control search-box has-icon-left has-icon-right search-example"><input type="search" class="form-control search-input" /><label class="input-control-icon-left search-icon"><i class="icon icon-search"></i></label><a class="input-control-icon-right search-clear-btn"><i class="icon icon-close icon-sm"></i></a></div>';
$output .= "</div></div>";
if($isMobile) $output = "<a id='currentItem' href=\"javascript:showSearchMenu('product', '$productID', '$currentModule', '$currentMethod', '$extra')\"><span class='text'>{$currentProduct->name}</span> <span class='icon-caret-down'></span></a><div id='currentItemDropMenu' class='hidden affix enter-from-bottom layer'></div>";
if($currentProduct->type == 'normal' || !$withBranch) unset($this->lang->product->menu->settings['subMenu']->branch);
if($currentProduct->type != 'normal' && $currentModule != 'programplan' && $withBranch)
{
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$currentProduct->type]);
$this->lang->product->menu->settings['subMenu']->branch = str_replace('@branch@', $this->lang->product->branch, $this->lang->product->menu->settings['subMenu']->branch);
$branches = $this->loadModel('branch')->getPairs($productID, 'all');
$branchName = $branches[$branch];
if(!$isMobile)
{
$dropMenuLink = helper::createLink('branch', 'ajaxGetDropMenu', "objectID=$productID&branch=$branch&module=$currentModule&method=$currentMethod&extra=$extra");
$output .= "<div class='btn-group'><button id='currentBranch' data-toggle='dropdown' type='button' class='btn btn-limit' title='{$branchName}' style='width: 90%'>{$branchName} <span class='caret'></span></button><div id='dropMenu' class='dropdown-menu search-list' data-ride='searchList' data-url='$dropMenuLink'>";
$output .= '<div class="input-control search-box has-icon-left has-icon-right search-example"><input type="search" class="form-control search-input" /><label class="input-control-icon-left search-icon"><i class="icon icon-search"></i></label><a class="input-control-icon-right search-clear-btn"><i class="icon icon-close icon-sm"></i></a></div>';
$output .= "</div></div>";
}
else
{
$output .= "<a id='currentBranch' href=\"javascript:showSearchMenu('branch', '$productID', '$currentModule', '$currentMethod', '$extra')\">{$branchName} <span class='icon-caret-down'></span></a><div id='currentBranchDropMenu' class='hidden affix enter-from-bottom layer'></div>";
}
}
if(!$isMobile) $output .= '</div>';
}
return $output;
}
/**
* Check privilege.
*
@@ -177,7 +55,7 @@ class productModel extends model
* @access public
* @return bool
*/
public function checkPriv($productID)
public function checkPriv(int $productID): bool
{
if(empty($productID)) return false;
@@ -525,30 +403,19 @@ class productModel extends model
}
/*
* 获取1.5级导航数据。
* Get product switcher.
*
* @param int $productID
* @param string $extra
* @param int|string $branch
* @param string|int $branch
* @access public
* @return void
* @return string
*/
public function getSwitcher($productID = 0, $extra = '', $branch = '')
public function getSwitcher(int $productID = 0, string $extra = '', string|int $branch = ''): string
{
$currentModule = $this->app->moduleName;
$currentMethod = $this->app->methodName;
/* Init currentModule and currentMethod for report and story. */
if($currentModule == 'story')
{
$storyMethods = ",create,batchcreate,batchclose,";
if(strpos($storyMethods, "," . $currentMethod . ",") === false) $currentModule = 'product';
if($currentMethod == 'view' or $currentMethod == 'change' or $currentMethod == 'review') $currentMethod = 'browse';
}
if($currentModule == 'testcase' and strpos(',view,edit,', ",$currentMethod,") !== false) $currentMethod = 'browse';
if($currentModule == 'bug' and $currentMethod == 'edit') $currentMethod = 'browse';
if($currentMethod == 'report') $currentMethod = 'browse';
/* 获取产品名称,产品类型。 */
$currentProduct = new stdclass();
$currentProductName = $this->lang->productCommon;
if($productID)
{
@@ -557,38 +424,21 @@ class productModel extends model
$this->session->set('currentProductType', $currentProduct->type);
}
$fromModule = $this->app->tab == 'qa' ? 'qa' : '';
$dropMenuLink = helper::createLink($this->app->tab == 'qa' ? 'product' : $this->app->tab, 'ajaxGetDropMenu', "objectID=$productID&module=$currentModule&method=$currentMethod&extra=$extra&from=$fromModule");
if($this->app->viewType == 'mhtml') return $this->getMobileDropMenu(array($productID => $currentProductName), $productID, $extra, $branch);
if($this->app->viewType == 'mhtml' and $productID) return $this->getModuleNav(array($productID => $currentProductName), $productID, $extra, $branch);
/* Init locateModule and locateMethod for report and story. */
list($locateModule, $locateMethod) = $this->productTao->computeLocate4DropMenu();
/* 生成异步获取产品下拉菜单的链接。*/
$fromModule = $this->app->tab == 'qa' ? 'qa' : '';
$dropMenuModule = $this->app->tab == 'qa' ? 'product' : $this->app->tab;
$dropMenuLink = helper::createLink($dropMenuModule, 'ajaxGetDropMenu', "objectID=$productID&module=$locateModule&method=$locateMethod&extra=$extra&from=$fromModule");
/* 构建产品1.5级导航数据。 */
$output = "<div class='btn-group header-btn' id='swapper'><button data-toggle='dropdown' type='button' class='btn' id='currentItem' title='{$currentProductName}'><span class='text'>{$currentProductName}</span> <span class='caret' style='margin-bottom: -1px'></span></button><div id='dropMenu' class='dropdown-menu search-list' data-ride='searchList' data-url='$dropMenuLink'>";
$output .= '<div class="input-control search-box has-icon-left has-icon-right search-example"><input type="search" class="form-control search-input" /><label class="input-control-icon-left search-icon"><i class="icon icon-search"></i></label><a class="input-control-icon-right search-clear-btn"><i class="icon icon-close icon-sm"></i></a></div>';
$output .= "</div></div>";
$notNormalProduct = (isset($currentProduct->type) and $currentProduct->type != 'normal');
if($notNormalProduct)
{
$isShowBranch = false;
if($currentModule == 'product' and $currentMethod == 'track') $isShowBranch = true;
if($currentModule == 'tree' and $currentMethod == 'browse') $isShowBranch = true;
if($currentModule == 'product' and strpos($this->config->product->showBranchMethod, $currentMethod) !== false) $isShowBranch = true;
if($this->app->tab == 'qa' and strpos(',testsuite,testreport,testtask,', ",$currentModule,") === false) $isShowBranch = true;
if($this->app->tab == 'qa' and $currentModule == 'testtask' and strpos(',create,edit,browseunits,importunitresult,unitcases,', ",$currentMethod,") === false) $isShowBranch = true;
if($currentModule == 'testcase' and $currentMethod == 'showimport') $isShowBranch = false;
if($currentModule == 'release' and strpos(',browse,create,', $currentMethod) !== false) $isShowBranch = true;
if($isShowBranch)
{
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$currentProduct->type]);
$branches = $this->loadModel('branch')->getPairs($productID, 'all');
$branchName = isset($branches[$branch]) ? $branches[$branch] : $branches[0];
$dropMenuLink = helper::createLink('branch', 'ajaxGetDropMenu', "objectID=$productID&branch=$branch&module=$currentModule&method=$currentMethod&extra=$extra");
$output .= "<div class='btn-group header-btn'><button id='currentBranch' data-toggle='dropdown' type='button' class='btn'><span class='text'>{$branchName}</span> <span class='caret' style='margin-bottom: -1px'></span></button><div id='dropMenu' class='dropdown-menu search-list' data-ride='searchList' data-url='$dropMenuLink'>";
$output .= '<div class="input-control search-box has-icon-left has-icon-right search-example"><input type="search" class="form-control search-input" /><label class="input-control-icon-left search-icon"><i class="icon icon-search"></i></label><a class="input-control-icon-right search-clear-btn"><i class="icon icon-close icon-sm"></i></a></div>';
$output .= "</div></div>";
}
}
$output .= $this->productTao->getBranchSelect4PC($currentProduct, $branch, $locateModule, $locateMethod, $extra);
return $output;
}
@@ -1272,7 +1122,7 @@ class productModel extends model
/**
* 获取产品路线图数据。
* Get roadmap of a proejct.
* Get roadmap of product.
*
* @param int $productID
* @param string $branch all|0|1
@@ -1282,85 +1132,8 @@ class productModel extends model
*/
public function getRoadmap(int $productID, string $branch = '0', int $count = 0): array
{
$plans = $this->loadModel('productplan')->getList($productID, $branch);
$releases = $this->loadModel('release')->getList($productID, $branch);
$roadmap = array();
$total = 0;
$parents = array();
$orderedPlans = array();
foreach($plans as $planID => $plan)
{
if($plan->parent == '-1')
{
$parents[$planID] = $plan->title;
unset($plans[$planID]);
continue;
}
if((!helper::isZeroDate($plan->end) and $plan->end < date('Y-m-d')) or $plan->end == '2030-01-01') continue;
$orderedPlans[$plan->end][] = $plan;
}
krsort($orderedPlans);
foreach($orderedPlans as $plans)
{
krsort($plans);
foreach($plans as $plan)
{
if($plan->parent > 0 and isset($parents[$plan->parent])) $plan->title = $parents[$plan->parent] . ' / ' . $plan->title;
$year = substr($plan->end, 0, 4);
$branchIdList = explode(',', trim($plan->branch, ','));
$branchIdList = array_unique($branchIdList);
foreach($branchIdList as $branchID)
{
if($branchID === '') continue;
$roadmap[$year][$branchID][] = $plan;
}
$total++;
if($count > 0 and $total >= $count) return $this->processRoadmap($roadmap, $branch);
}
}
$orderedReleases = array();
foreach($releases as $release) $orderedReleases[$release->date][] = $release;
krsort($orderedReleases);
foreach($orderedReleases as $releases)
{
krsort($releases);
foreach($releases as $release)
{
$year = substr($release->date, 0, 4);
$branchIdList = explode(',', trim($release->branch, ','));
$branchIdList = array_unique($branchIdList);
foreach($branchIdList as $branchID)
{
if($branchID === '') continue;
$roadmap[$year][$branchID][] = $release;
}
$total++;
if($count > 0 and $total >= $count) return $this->processRoadmap($roadmap, $branch);
}
}
if($count > 0) return $this->processRoadmap($roadmap, $branch);
$groupRoadmap = array();
foreach($roadmap as $year => $branchRoadmaps)
{
foreach($branchRoadmaps as $branch => $roadmaps)
{
$totalData = count($roadmaps);
$rows = ceil($totalData / 8);
$maxPerRow = ceil($totalData / $rows);
$groupRoadmap[$year][$branch] = array_chunk($roadmaps, $maxPerRow);
foreach($groupRoadmap[$year][$branch] as $row => $rowRoadmaps) krsort($groupRoadmap[$year][$branch][$row]);
}
}
/* Get group roadmap data. */
$groupRoadmap = $this->productTao->getGroupRoadmapData($productID, $branch, $count);
/* Get last 5 roadmap. */
$lastKeys = array_slice(array_keys($groupRoadmap), 0, 5);
@@ -1391,7 +1164,7 @@ class productModel extends model
* @access public
* @return array
*/
public function processRoadmap($roadmapGroups, $branch)
public function processRoadmap($roadmapGroups, $branch): array
{
$newRoadmap = array();
foreach($roadmapGroups as $year => $branchRoadmaps)
@@ -2243,19 +2016,18 @@ class productModel extends model
if(empty($this->session->product)) $productID = (int)key($products);
}
if(isset($products[$productID])) return $productID;
/* 产品ID已经被删除,不存在于产品列表中。*/
/* Product ID does not exsit in products list, it may be deleted. */
if(!isset($products[$productID]))
/* Confirm if product exist. */
$product = $this->product->getById($productID);
if(empty($product) or $product->deleted == 1) $productID = (int)key($products);
/* If product is invisable for curren user, then response access denied message. */
if($productID && !$this->checkPriv($productID))
{
/* Confirm if product exist. */
$product = $this->product->getById($productID);
if(empty($product) or $product->deleted == 1) $productID = (int)key($products);
/* If product is invisable for curren user, then response access denied message. */
if($productID && strpos(",{$this->app->user->view->products},", ",{$productID},") === false)
{
$productID = (int)key($products);
$this->accessDenied($this->lang->product->accessDenied);
}
$productID = (int)key($products);
$this->accessDenied($this->lang->product->accessDenied);
}
return $productID;
@@ -2269,7 +2041,7 @@ class productModel extends model
* @access private
* @return void
*/
public function accessDenied($tips)
public function accessDenied(string $tips)
{
if(defined('TUTORIAL')) return true;
@@ -2288,40 +2060,37 @@ class productModel extends model
* Set menu.
*
* @param int $productID
* @param int|string $branch
* @param int $module
* @param string $moduleType
* @param string|int $branch all|''|int
* @param string $extra
* @access public
* @return void
*/
public function setMenu($productID = 0, $branch = '', $module = 0, $moduleType = '', $extra = ''): void
public function setMenu(int $productID = 0, string|int $branch = '', string $extra = '')
{
if(!$this->app->user->admin and strpos(",{$this->app->user->view->products},", ",$productID,") === false and $productID != 0 and !defined('TUTORIAL'))
{
$this->accessDenied($this->lang->product->accessDenied);
return;
}
$product = $this->getByID($productID);
if(!defined('TUTORIAL') and $productID != 0 and !$this->checkPriv($productID)) return $this->accessDenied($this->lang->product->accessDenied);
/* 用真实数据替换导航配置的占位符,并删除无用配置项。 */
$params = array('branch' => $branch);
common::setMenuVars('product', $productID, $params);
if(strpos($extra, 'requirement') !== false) unset($this->lang->product->moreSelects['willclose']);
$product = $this->getByID($productID);
if(!$product) return;
/* 设置1.5级导航数据。*/
$this->lang->switcherMenu = $this->getSwitcher($productID, $extra, $branch);
/* 设置导航中分支的显示数据。*/
/* 如果产品类型是正常的,隐藏导航中分支的显示。*/
if($product->type == 'normal')
{
unset($this->lang->product->menu->settings['subMenu']->branch);
}
else
{
$branchLink = $this->lang->product->menu->settings['subMenu']->branch['link'];
$this->lang->product->menu->settings['subMenu']->branch['link'] = str_replace('@branch@', $this->lang->product->branchName[$product->type], $branchLink);
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
return;
}
if(strpos($extra, 'requirement') !== false) unset($this->lang->product->moreSelects['willclose']);
/* 如果产品类型是多分支、多平台的,将真实数据替换@branch@匹配符。*/
$branchLink = $this->lang->product->menu->settings['subMenu']->branch['link'];
$this->lang->product->menu->settings['subMenu']->branch['link'] = str_replace('@branch@', $this->lang->product->branchName[$product->type], $branchLink);
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
}
}
+348 -22
View File
@@ -431,27 +431,6 @@ class productTao extends productModel
return $products;
}
/**
* 格式化append参数,保证输出用逗号间隔的id列表。
* Format append param.
*
* @param string|array append
* @access protected
* @return string
*/
protected function formatAppendParam(string|array $append = ''): string
{
if(empty($append)) return '';
if(is_string($append)) $append = explode(',', $append);
$append = array_map(function($item){return (int)$item;}, $append);
$append = array_unique(array_filter($append));
sort($append);
return implode(',', $append);
}
/**
* 获取用于数据统计的研发需求和用户需求列表。
* Get dev stories and user requirements for statistics.
@@ -491,7 +470,121 @@ class productTao extends productModel
/* Story type is 'requirement'. */
if($storyType == static::STORY_TYPE_REQ) $stories = $requirements;
return [$stories, $requirements];
return array($stories, $requirements);
}
/**
* 获取PC端分支下拉的HTML
* Get branch select for mobile
*
* @param object $product
* @param string|int $branch
* @param string $currentModule
* @param string $currentMethod
* @param string $extra
* @access private
* @return string
*/
protected function getBranchSelect4PC(object $product, string|int $branch, string $currentModule, string $currentMethod, string $extra = ''): string
{
if(!isset($product->type) or $product->type == 'normal') return '';
/* 检测是否要显示分支。 */
$showBranch = false;
if($currentModule == 'testcase' and $currentMethod == 'showimport') $showBranch = false;
if($currentModule == 'tree' and $currentMethod == 'browse') $showBranch = true;
if($currentModule == 'product' and strpos($this->config->product->showBranchMethod, ",{$currentMethod},") !== false) $showBranch = true;
if($currentModule == 'release' and strpos(',browse,create,', ",{$currentMethod},") !== false) $showBranch = true;
if($this->app->tab == 'qa' and strpos(',testsuite,testreport,testtask,', ",$currentModule,") === false) $showBranch = true;
if($this->app->tab == 'qa' and $currentModule == 'testtask' and strpos(',create,edit,browseunits,importunitresult,unitcases,', ",$currentMethod,") === false) $showBranch = true;
if(!$showBranch) return '';
/* 生成异步请求地址。 */
$branches = $this->loadModel('branch')->getPairs($product->id, 'all');
$branchName = zget($branches, $branch, reset($branches));
$dropMenuLink = helper::createLink('branch', 'ajaxGetDropMenu', "objectID={$product->id}&branch=$branch&module=$currentModule&method=$currentMethod&extra=$extra");
/* 生成分支HTML代码。*/
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
$output = "<div class='btn-group header-btn'><button id='currentBranch' data-toggle='dropdown' type='button' class='btn'><span class='text'>{$branchName}</span> <span class='caret' style='margin-bottom: -1px'></span></button><div id='dropMenu' class='dropdown-menu search-list' data-ride='searchList' data-url='$dropMenuLink'>";
$output .= '<div class="input-control search-box has-icon-left has-icon-right search-example"><input type="search" class="form-control search-input" /><label class="input-control-icon-left search-icon"><i class="icon icon-search"></i></label><a class="input-control-icon-right search-clear-btn"><i class="icon icon-close icon-sm"></i></a></div>';
$output .= "</div></div>";
return $output;
}
/**
* 获取客户端分支下拉的HTML
* Get branch select for mobile
*
* @param object $product
* @param string $currentModule
* @param string $currentMethod
* @param string $extra
* @access private
* @return string
*/
protected function getBranchSelect4Mobile(object $product, string|int $branch, string $currentModule, string $currentMethod, string $extra = ''): string
{
if(!isset($product->type) or $product->type == 'normal') return '';
/* 修正分支显示语言项。 */
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
$this->lang->product->menu->settings['subMenu']->branch['link'] = str_replace('@branch@', $this->lang->product->branch, $this->lang->product->menu->settings['subMenu']->branch['link']);
/* 生成分支HTML代码。*/
$output = '';
$branches = $this->loadModel('branch')->getPairs($product->id, 'all');
$branchName = zget($branches, $branch, reset($branches));
if($branchName) $output .= "<a id='currentBranch' href=\"javascript:showSearchMenu('branch', '{$product->id}', '$currentModule', '$currentMethod', '$extra')\">{$branchName} <span class='icon-caret-down'></span></a><div id='currentBranchDropMenu' class='hidden affix enter-from-bottom layer'></div>";
return $output;
}
/**
* 计算在点击1.5级导航下拉选项后,跳转的模块名和方法名。
* Compute locate for drop menu in PC.
*
* @access protected
* @return array
*/
protected function computeLocate4DropMenu(): array
{
$currentModule = (string)$this->app->moduleName;
$currentMethod = (string)$this->app->methodName;
/* Init currentModule and currentMethod for report and story. */
if($currentModule == 'story')
{
if(strpos(",track,create,batchcreate,batchclose,", ",{$currentMethod},") === false) $currentModule = 'product';
if($currentMethod == 'view' or $currentMethod == 'change' or $currentMethod == 'review') $currentMethod = 'browse';
}
if($currentModule == 'testcase' and strpos(',view,edit,', ",$currentMethod,") !== false) $currentMethod = 'browse';
if($currentModule == 'bug' and $currentMethod == 'edit') $currentMethod = 'browse';
if($currentMethod == 'report') $currentMethod = 'browse';
return array($currentModule, $currentMethod);
}
/**
* 格式化append参数,保证输出用逗号间隔的id列表。
* Format append param.
*
* @param string|array append
* @access protected
* @return string
*/
protected function formatAppendParam(string|array $append = ''): string
{
if(empty($append)) return '';
if(is_string($append)) $append = explode(',', $append);
$append = array_map(function($item){return (int)$item;}, $append);
$append = array_unique(array_filter($append));
sort($append);
return implode(',', $append);
}
/**
@@ -629,4 +722,237 @@ class productTao extends productModel
->orderBy($orderBy . 't2.order asc')
->fetchAll();
}
/**
* 构建移动端的下拉HTML。
* Create the select code of products.
*
* @param array $products
* @param int $productID
* @param string $currentModule
* @param string $currentMethod
* @param string $extra
* @param int|string $branch
* @param bool $withBranch true|false
* @access protected
* @return string
*/
protected function buildSelect4Mobile(array $products, int $productID, string $currentModule, string $currentMethod, string $extra = '', string|int $branch = '', bool $withBranch = true): string
{
/* 处理数据。*/
if(isset($products[0])) unset($products[0]);
if(empty($products)) return '';
if(!isset($products[$productID])) $productID = (int)key($products);
/* 检测当前页面是否在项目或执行的测试二级导航中。*/
$isQaModule = (strpos(',project,execution,', ",{$this->app->tab},") !== false and stripos(',bug,testcase,testtask,ajaxselectstory,', ",{$this->app->rawMethod},") !== false) ? true : false;
if($this->app->tab == 'project' and stripos(',zeroCase,browseUnits,groupCase,', ",$currentMethod,") !== false) $isQaModule = true;
if($isQaModule)
{
if($this->app->tab == 'project') $extra = strpos(',testcase,groupCase,zeroCase,', ",$currentMethod,") !== false ? $extra : $this->session->project;
if($this->app->tab == 'execution') $extra = $this->session->execution;
}
/* 查询产品数据。*/
$currentProduct = $this->getById($productID);
$this->session->set('currentProductType', $currentProduct->type);
/* 生成异步获取下拉菜单的链接。*/
$moduleName = $isQaModule ? 'bug' : 'product';
if($this->app->tab == 'feedback') $moduleName = 'feedback';
$dropMenuLink = helper::createLink($moduleName, 'ajaxGetDropMenu', "objectID=$productID&module=$currentModule&method=$currentMethod&extra=$extra");
/* 构建产品1.5级导航代码。 */
$output = "<a id='currentItem' href=\"javascript:showSearchMenu('product', '$productID', '$currentModule', '$currentMethod', '$extra')\"><span class='text'>{$currentProduct->name}</span> <span class='icon-caret-down'></span></a><div id='currentItemDropMenu' class='hidden affix enter-from-bottom layer'></div>";
/* 处理分支。根据条件,判断是否追加分支1.5级导航代码。*/
if($currentProduct->type == 'normal' || !$withBranch) unset($this->lang->product->menu->settings['subMenu']->branch);
if($currentProduct->type != 'normal' && $withBranch && $currentModule != 'programplan') $output .= $this->getBranchSelect4Mobile($currentProduct, $branch, $currentModule, $currentMethod, $extra);
return $output;
}
/**
* 获取产品路线图的分组数据。
* Get group roadmap data of product.
*
* @param int $productID
* @param string $branch all|0|1
* @param int $count
* @access public
* @return array
*/
protected function getGroupRoadmapData(int $productID, string $branch, int $count): array
{
$roadmap = array();
$total = 0;
/* Get product plans. */
$plans = $this->loadModel('productplan')->getList($productID, $branch);
/* Get valid product plans and parent plans. */
list($orderedPlans, $parents) = $this->filterValidProductPlans($plans);
/* Get roadmaps of product plans. */
list($roadmap, $total, $return) = $this->getRoadmapsOfPlans($orderedPlans, $parents, $branch, $count);
if($return) return $roadmap;
/* Get roadmpas of product releases. */
$releases = $this->loadModel('release')->getList($productID, $branch);
list($roadmap, $subTotal, $return) = $this->getRoadmapsOfReleases($roadmap, $releases, $branch, $count);
if($return) return $roadmap;
$total += $subTotal;
$groupRoadmap = array();
foreach($roadmap as $year => $branchRoadmaps)
{
foreach($branchRoadmaps as $branch => $roadmaps)
{
$totalData = count($roadmaps);
$rows = ceil($totalData / 8);
$maxPerRow = ceil($totalData / $rows);
$groupRoadmap[$year][$branch] = array_chunk($roadmaps, (int)$maxPerRow);
foreach($groupRoadmap[$year][$branch] as $row => $rowRoadmaps) krsort($groupRoadmap[$year][$branch][$row]);
}
}
return $groupRoadmap;
}
/**
* 过滤有效的产品计划, 并返回所有父级计划。
* Filter valid product plans.
*
* @param array $plans
* @access private
* @return [array, array]
*/
private function filterValidProductPlans(array $plans): array
{
$parents = array();
$orderedPlans = array();
foreach($plans as $planID => $plan)
{
/* Remove parent plan. */
if($plan->parent == '-1')
{
$parents[$planID] = $plan->title;
unset($plans[$planID]);
continue;
}
/* Remove exceeded plan and long-time plan. */
if((!helper::isZeroDate($plan->end) and $plan->end < date('Y-m-d')) or $plan->end == '2030-01-01') continue;
$orderedPlans[$plan->end][] = $plan;
}
krsort($orderedPlans);
return array($orderedPlans, $parents);
}
/**
* 获取计划的路线图数据。
* Get roadmaps of plans.
*
* @param array $orderedPlans
* @param array $parents
* @param string $branch
* @param int $count
* @access private
* @return [array, int, bool]
*/
private function getRoadmapsOfPlans(array $orderedPlans, array $parents, string $branch, int $count): array
{
$return = false;
$total = 0;
$roadmap = array();
foreach($orderedPlans as $plans)
{
krsort($plans);
foreach($plans as $plan)
{
/* Attach parent plan. */
if($plan->parent > 0 and isset($parents[$plan->parent])) $plan->title = $parents[$plan->parent] . ' / ' . $plan->title;
$year = substr($plan->end, 0, 4);
$branchIdList = explode(',', trim($plan->branch, ','));
$branchIdList = array_unique($branchIdList);
foreach($branchIdList as $branchID)
{
if($branchID === '') continue;
$roadmap[$year][$branchID][] = $plan;
}
$total++;
/* Exceed requested count. */
if($count > 0 and $total >= $count)
{
$roadmap = $this->processRoadmap($roadmap, $branch);
$return = true;
break;
};
}
}
return array($roadmap, $total, $return);
}
/**
* 获取计划的路线图数据。
* Get roadmaps of plans.
*
* @param array $roadmaps
* @param array $parents
* @param string $branch
* @param int $count
* @access private
* @return [array, int, bool]
*/
private function getRoadmapsOfReleases(array $roadmaps, array $releases, string $branch, int $count): array
{
$total = 0;
$return = false;
$orderedReleases = array();
/* Collect releases. */
foreach($releases as $release) $orderedReleases[$release->date][] = $release;
krsort($orderedReleases);
foreach($orderedReleases as $releases)
{
krsort($releases);
foreach($releases as $release)
{
$year = substr($release->date, 0, 4);
$branchIdList = explode(',', trim($release->branch, ','));
$branchIdList = array_unique($branchIdList);
foreach($branchIdList as $branchID)
{
if($branchID === '') continue;
$roadmaps[$year][$branchID][] = $release;
}
$total++;
/* Exceed required count .*/
if($count > 0 and $total >= $count)
{
$roadmaps = $this->processRoadmap($roadmaps, $branch);
$return = true;
return array($roadmaps, $total, $return);
}
}
}
if($count > 0)
{
$roadmaps = $this->processRoadmap($roadmaps, $branch);
$return = true;
}
return array($roadmaps, $total, $return);
}
}
+16 -17
View File
@@ -3,8 +3,6 @@
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/product.class.php';
#su('admin');
/**
title=测试productModel->checkPriv();
@@ -26,21 +24,22 @@ pid=1
*/
$productIDList = array('1', '2', '3', '4', '5', '1000001');
$productIdList = array(1, 2, 3, 4, 5, 1000001);
$productTest = new productTest('admin');
r($productTest->checkPrivTest($productIDList[0])) && p() && e('1'); // 测试admin能否看到产品1
r($productTest->checkPrivTest($productIDList[1])) && p() && e('1'); // 测试admin能否看到产品2
r($productTest->checkPrivTest($productIDList[2])) && p() && e('1'); // 测试admin能否看到产品3
r($productTest->checkPrivTest($productIDList[3])) && p() && e('1'); // 测试admin能否看到产品4
r($productTest->checkPrivTest($productIDList[4])) && p() && e('1'); // 测试admin能否看到产品5
r($productTest->checkPrivTest($productIDList[5])) && p() && e('1'); // 测试admin能否看到不存在的产品
$productTest = new productTest('po1');
r($productTest->checkPrivTest($productIDList[0])) && p() && e('1'); // 测试po1能否看到产品1
r($productTest->checkPrivTest($productIDList[1])) && p() && e('1'); // 测试po1能否看到产品2
r($productTest->checkPrivTest($productIDList[2])) && p() && e('1'); // 测试po1能否看到产品3
r($productTest->checkPrivTest($productIDList[3])) && p() && e('1'); // 测试po1能否看到产品4
r($productTest->checkPrivTest($productIDList[4])) && p() && e('1'); // 测试po1能否看到产品5
r($productTest->checkPrivTest($productIDList[5])) && p() && e('2'); // 测试po1能否看到不存在的产品
$productTest->objectModel->app->user->admin = true;
r($productTest->checkPrivTest($productIdList[0])) && p() && e('1'); // 测试admin能否看到产品1
r($productTest->checkPrivTest($productIdList[1])) && p() && e('1'); // 测试admin能否看到产品2
r($productTest->checkPrivTest($productIdList[2])) && p() && e('1'); // 测试admin能否看到产品3
r($productTest->checkPrivTest($productIdList[3])) && p() && e('1'); // 测试admin能否看到产品4
r($productTest->checkPrivTest($productIdList[4])) && p() && e('1'); // 测试admin能否看到产品5
r($productTest->checkPrivTest($productIdList[5])) && p() && e('1'); // 测试admin能否看到不存在的产品
$productTest->objectModel->app->user->admin = false;
$productTest->objectModel->app->user->view->products = '1,2,3,4,5,6,7,8,9,20,21,48,49,50';
r($productTest->checkPrivTest($productIdList[0])) && p() && e('1'); // 测试po1能否看到产品1
r($productTest->checkPrivTest($productIdList[1])) && p() && e('1'); // 测试po1能否看到产品2
r($productTest->checkPrivTest($productIdList[2])) && p() && e('1'); // 测试po1能否看到产品3
r($productTest->checkPrivTest($productIdList[3])) && p() && e('1'); // 测试po1能否看到产品4
r($productTest->checkPrivTest($productIdList[4])) && p() && e('1'); // 测试po1能否看到产品5
r($productTest->checkPrivTest($productIdList[5])) && p() && e('0'); // 测试po1能否看到不存在的产品
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/product.class.php';
zdTable('product')->gen(10);
/**
title=测试productModel->getMobileDropMenu();
cid=1
pid=1
测试admin能否看到产品1 >> 1
测试admin能否看到产品2 >> 1
测试admin能否看到产品3 >> 1
测试admin能否看到产品4 >> 1
测试admin能否看到产品5 >> 1
测试admin能否看到不存在的产品 >> 1
测试po1能否看到产品1 >> 1
测试po1能否看到产品2 >> 1
测试po1能否看到产品3 >> 1
测试po1能否看到产品4 >> 1
测试po1能否看到产品5 >> 1
测试po1能否看到不存在的产品 >> 2
*/
global $tester;
$productTest = $tester->loadModel('product');
$products = $productTest->dao->select('*')->from(TABLE_PRODUCT)->where('deleted')->eq(0)->fetchPairs('id', 'name');
r(strpos($productTest->getMobileDropMenu($products, -1), 'showSearchMenu') !== false) && p() && e('1');
r(strpos($productTest->getMobileDropMenu($products, 0), 'showSearchMenu') !== false) && p() && e('1');
r(strpos($productTest->getMobileDropMenu($products, 1), 'showSearchMenu') !== false) && p() && e('1');
r(strpos($productTest->getMobileDropMenu(array(), -1), 'showSearchMenu') !== false) && p() && e('0');
r(strpos($productTest->getMobileDropMenu(array(), 0), 'showSearchMenu') !== false) && p() && e('0');
r(strpos($productTest->getMobileDropMenu(array(), 1), 'showSearchMenu') !== false) && p() && e('0');
+10 -10
View File
@@ -9,11 +9,11 @@ title=productModel->getRoadmap();
cid=1
pid=1
测试获取产品1的roadmap数量 >> 5
测试获取产品2的roadmap数量 >> 2
测试获取产品3的roadmap数量 >> 0
测试获取产品4的roadmap数量 >> 0
测试获取产品5的roadmap数量 >> 0
测试获取产品1的roadmap数量 >> 1
测试获取产品2的roadmap数量 >> 1
测试获取产品3的roadmap数量 >> 1
测试获取产品4的roadmap数量 >> 1
测试获取产品5的roadmap数量 >> 1
测试获取不存在产品的roadmap数量 >> 0
*/
@@ -22,9 +22,9 @@ $productIDList = array('1', '2', '3', '4', '5', '1000001');
$product = new productTest('admin');
r($product->getRoadmapTest($productIDList[0])) && p('total') && e('5'); // 测试获取产品1的roadmap数量
r($product->getRoadmapTest($productIDList[1])) && p('total') && e('2'); // 测试获取产品2的roadmap数量
r($product->getRoadmapTest($productIDList[2])) && p('total') && e('0'); // 测试获取产品3的roadmap数量
r($product->getRoadmapTest($productIDList[3])) && p('total') && e('0'); // 测试获取产品4的roadmap数量
r($product->getRoadmapTest($productIDList[4])) && p('total') && e('0'); // 测试获取产品5的roadmap数量
r($product->getRoadmapTest($productIDList[0])) && p('total') && e('1'); // 测试获取产品1的roadmap数量
r($product->getRoadmapTest($productIDList[1])) && p('total') && e('1'); // 测试获取产品2的roadmap数量
r($product->getRoadmapTest($productIDList[2])) && p('total') && e('1'); // 测试获取产品3的roadmap数量
r($product->getRoadmapTest($productIDList[3])) && p('total') && e('1'); // 测试获取产品4的roadmap数量
r($product->getRoadmapTest($productIDList[4])) && p('total') && e('1'); // 测试获取产品5的roadmap数量
r($product->getRoadmapTest($productIDList[5])) && p('total') && e('0'); // 测试获取不存在产品的roadmap数量
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/product.class.php';
zdTable('product')->gen(50);
/**
title=测试productModel->setMenu();
timeout=0
cid=1
*/
$product = new productTest('admin');
$product->objectModel->app->moduleName = 'product';
$product->objectModel->app->methodName = 'browse';
r($product->getSwitcherTest(0)) && p('hasProduct,hasBranch') && e('1,0');
r($product->getSwitcherTest(1)) && p('hasProduct,hasBranch') && e('1,0');
r($product->getSwitcherTest(0, '', 'all')) && p('hasProduct,hasBranch') && e('1,0');
r($product->getSwitcherTest(41,'', 'all')) && p('hasProduct,hasBranch') && e('1,1');
r($product->getSwitcherTest(0, '', 0)) && p('hasProduct,hasBranch') && e('1,0');
r($product->getSwitcherTest(41,'', 0)) && p('hasProduct,hasBranch') && e('1,1');
$product->objectModel->app->viewType = 'mhtml';
r($product->getSwitcherTest(0)) && p('hasProduct,hasBranch') && e('1,0');
r($product->getSwitcherTest(1)) && p('hasProduct,hasBranch') && e('1,0');
r($product->getSwitcherTest(0, '', 'all')) && p('hasProduct,hasBranch') && e('1,0');
r($product->getSwitcherTest(41,'', 'all')) && p('hasProduct,hasBranch') && e('1,1');
r($product->getSwitcherTest(0, '', 0)) && p('hasProduct,hasBranch') && e('1,0');
r($product->getSwitcherTest(41,'', 0)) && p('hasProduct,hasBranch') && e('1,1');
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/product.class.php';
zdTable('product')->gen(50);
/**
title=测试productModel->setMenu();
timeout=0
cid=1
*/
$product = new productTest('admin');
r($product->setMenuTest(0)) && p('idReplaced,branchReplaced,hasBranch,requirement,hasSwitch') && e('1,0,1,0,0');
r($product->setMenuTest(1)) && p('idReplaced,branchReplaced,hasBranch,requirement,hasSwitch') && e('1,0,0,0,1');
r($product->setMenuTest(0, '', 'requirement')) && p('idReplaced,branchReplaced,hasBranch,requirement,hasSwitch') && e('1,0,1,1,0');
r($product->setMenuTest(1, '', 'requirement')) && p('idReplaced,branchReplaced,hasBranch,requirement,hasSwitch') && e('1,0,0,1,1');
r($product->setMenuTest(0, 'all')) && p('idReplaced,branchReplaced,hasBranch,requirement,hasSwitch') && e('1,0,1,0,0');
r($product->setMenuTest(41, 'all')) && p('idReplaced,branchReplaced,hasBranch,requirement,hasSwitch') && e('1,1,1,0,1');
r($product->setMenuTest(0, 0, 'requirement')) && p('idReplaced,branchReplaced,hasBranch,requirement,hasSwitch') && e('1,0,1,1,0');
r($product->setMenuTest(41, 0, 'requirement')) && p('idReplaced,branchReplaced,hasBranch,requirement,hasSwitch') && e('1,1,1,1,1');
+92 -29
View File
@@ -5,7 +5,7 @@ class productTest
* @var productModel
* @access private
*/
private productModel $objectModel;
public productModel $objectModel;
/**
* __construct
@@ -372,20 +372,11 @@ class productTest
*
* @param int $productID
* @access public
* @return int
* @return bool
*/
public function checkPrivTest($productID)
public function checkPrivTest(int $productID): bool
{
$object = $this->objectModel->checkPriv($productID);
if(dao::isError())
{
return dao::getError();
}
else
{
return $object ? 1 : 2;
}
return $this->objectModel->checkPriv($productID);
}
/**
@@ -777,14 +768,9 @@ class productTest
{
$objects = $this->objectModel->getRoadmap($productID);
if(dao::isError())
{
return dao::getError();
}
else
{
return $objects;
}
if(dao::isError()) return dao::getError();
return $objects;
}
/**
@@ -803,14 +789,9 @@ class productTest
$objects = $this->objectModel->processRoadmap($roadmapGroups);
if(dao::isError())
{
return dao::getError();
}
else
{
return $objects;
}
if(dao::isError()) return dao::getError();
return $objects;
}
/**
@@ -1106,4 +1087,86 @@ class productTest
return $object;
}
}
/**
* 测试setMenu方法。
* Test setMenu
*
* @param int $productID
* @param string|int $branch
* @param string $extra
* @access public
* @return array
*/
public function setMenuTest(int $productID, string|int $branch = '', string $extra = ''): array
{
/* Reset data. */
unset($this->objectModel->lang->switcherMenu);
$this->objectModel->lang->product->moreSelects['willclose'] = 'willcose';
$this->objectModel->lang->product->menu->settings['link'] = "Settings|product|view|productID=%s";
$this->objectModel->lang->product->menu->settings['subMenu']->branch = array('link' => "@branch@|branch|manage|product=%s", 'subModule' => 'branch');
$this->objectModel->setMenu($productID, $branch, $extra);
$hasBranch = (int) isset($this->objectModel->lang->product->menu->settings['subMenu']->branch);
$requirement = (int)!isset($this->objectModel->lang->product->moreSelects['willclose']);
$hasSwitch = (int)!empty($this->objectModel->lang->switcherMenu);
$idReplaced = (int)(strpos($this->objectModel->lang->product->menu->settings['link'], '%s') === false);
$branchReplaced = (int)($hasBranch and strpos($this->objectModel->lang->product->menu->settings['subMenu']->branch['link'], '@branch@') === false);
return array('idReplaced' => $idReplaced, 'branchReplaced' => $branchReplaced, 'hasBranch' => $hasBranch, 'requirement' => $requirement, 'hasSwitch' => $hasSwitch);
}
/**
* 测试 getSwitcher 方法。
* Test getSwitcher.
*
* @param int $productID
* @param string $extra
* @param string|int $branch
* @access public
* @return array
*/
public function getSwitcherTest(int $productID, string $extra = '', string|int $branch = ''): array
{
$this->objectModel->lang->product->menu->settings['subMenu']->branch = array('link' => "@branch@|branch|manage|product=%s", 'subModule' => 'branch');
$switcher = $this->objectModel->getSwitcher($productID, $extra, $branch);
$product = $this->objectModel->dao->select('*')->from(TABLE_PRODUCT)->where('id')->eq($productID)->fetch();
$hasProduct = $product ? (int)(strpos($switcher, $product->name) !== false) : (int)(strpos($switcher, '产品') !== false);
$hasBranch = (int)(strpos($switcher, 'currentBranch') !== false);
return array('hasProduct' => $hasProduct, 'hasBranch' => $hasBranch);
}
/**
* 测试 buildSelect4Mobile 方法。
* Test buildSelect4Mobile
*
* @param array $products
* @param int $productID
* @param string $currentModule
* @param string $currentMethod
* @param string $extra
* @param string|int $branch
* @param bool $withBranch
* @access public
* @return array
*/
public function buildSelect4MobileTest(array $products, int $productID, string $currentModule, string $currentMethod, string $extra = '', string|int $branch = '', bool $withBranch = true): array
{
$this->objectModel->lang->product->menu->settings['subMenu']->branch = array('link' => "@branch@|branch|manage|product=%s", 'subModule' => 'branch');
$select = $this->objectModel->buildSelect4Mobile($products, $productID, $currentModule, $currentMethod, $extra, $branch, $withBranch);
$productName = zget($products, $productID, reset($products));
$branchName = '所有';
if(is_numeric($branch)) $branchName = $this->objectModel->dao->select('*')->from(TABLE_BRANCH)->where('id')->eq($branch)->fetch('name');
$hasProduct = (int)(strpos($select, 'currentItem') !== false and strpos($select, $productName) !== false);
$hasBranch = (int)(strpos($select, 'currentBranch') !== false and strpos($select, $branchName) !== false);
return array('hasProduct' => $hasProduct, 'hasBranch' => $hasBranch);
}
}
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/product.class.php';
zdTable('product')->gen(50);
$branch = zdTable('branch');
$branch->product->range('41');
$branch->gen(5);
/**
title=测试productModel->buildSelect4Mobile();
timeout=0
cid=1
*/
$productTest = new productTest('admin');
$productTest->objectModel->dao->update(TABLE_PRODUCT)->set('deleted')->eq(1)->where('id')->eq(49)->exec();
$products = $productTest->objectModel->dao->select('*')->from(TABLE_PRODUCT)->where('deleted')->eq(0)->fetchPairs('id', 'name');
$moduleName = 'product';
$methodName = 'browse';
r($productTest->buildSelect4MobileTest(array(), 0, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('0,0');
r($productTest->buildSelect4MobileTest($products, 0, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,0');
r($productTest->buildSelect4MobileTest($products, 1, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,0');
r($productTest->buildSelect4MobileTest($products, 49, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,0');
r($productTest->buildSelect4MobileTest($products, 41, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,1');
r($productTest->buildSelect4MobileTest($products, 41, $moduleName, $methodName, '', 1)) && p('hasProduct,hasBranch') && e('1,1');
r($productTest->buildSelect4MobileTest($products, 41, $moduleName, $methodName, '', '', false)) && p('hasProduct,hasBranch') && e('1,0');
r($productTest->buildSelect4MobileTest($products, 70, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,0');
$productTest->objectModel->app->tab = 'project';
$moduleName = 'project';
$methodName = 'testcase';
r($productTest->buildSelect4MobileTest($products, 41, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,1');
$moduleName = 'testcase';
$methodName = 'groupCase';
r($productTest->buildSelect4MobileTest($products, 41, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,1');
$productTest->objectModel->app->tab = 'execution';
$moduleName = 'execution';
$methodName = 'bug';
r($productTest->buildSelect4MobileTest($products, 41, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,1');
$methodName = 'task';
r($productTest->buildSelect4MobileTest($products, 41, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,1');
$productTest->objectModel->app->tab = 'feedback';
$moduleName = 'bug';
$methodName = 'view';
r($productTest->buildSelect4MobileTest($products, 41, $moduleName, $methodName)) && p('hasProduct,hasBranch') && e('1,1');
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
/**
title=测试productModel->computeLocate4DropMenu();
timeout=0
cid=1
*/
global $tester;
$product = $tester->loadModel('product');
$product->app->moduleName = 'product';
$product->app->methodName = 'browse';
r($product->computeLocate4DropMenu()) && p('0,1') && e('product,browse');
$product->app->moduleName = 'story';
$product->app->methodName = 'view';
r($product->computeLocate4DropMenu()) && p('0,1') && e('product,browse');
$product->app->moduleName = 'story';
$product->app->methodName = 'create';
r($product->computeLocate4DropMenu()) && p('0,1') && e('story,create');
$product->app->moduleName = 'story';
$product->app->methodName = 'report';
r($product->computeLocate4DropMenu()) && p('0,1') && e('product,browse');
$product->app->moduleName = 'product';
$product->app->methodName = 'report';
r($product->computeLocate4DropMenu()) && p('0,1') && e('product,browse');
$product->app->moduleName = 'bug';
$product->app->methodName = 'edit';
r($product->computeLocate4DropMenu()) && p('0,1') && e('bug,browse');
$product->app->moduleName = 'bug';
$product->app->methodName = 'view';
r($product->computeLocate4DropMenu()) && p('0,1') && e('bug,view');
$product->app->moduleName = 'bug';
$product->app->methodName = 'report';
r($product->computeLocate4DropMenu()) && p('0,1') && e('bug,browse');
$product->app->moduleName = 'testcase';
$product->app->methodName = 'edit';
r($product->computeLocate4DropMenu()) && p('0,1') && e('testcase,browse');
$product->app->moduleName = 'testcase';
$product->app->methodName = 'view';
r($product->computeLocate4DropMenu()) && p('0,1') && e('testcase,browse');
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
zdTable('product')->gen(50);
$branch = zdTable('branch');
$branch->product->range('41');
$branch->gen(5);
/**
title=测试productModel->getBranchSelect4Mobile();
timeout=0
cid=1
*/
global $tester;
$productTest = $tester->loadModel('product');
$product = $productTest->dao->select('*')->from(TABLE_PRODUCT)->where('id')->eq(1)->fetch();
$moduleName = 'product';
$methodName = 'browse';
r($productTest->getBranchSelect4Mobile(new stdclass(), '', $moduleName, $methodName)) && p() && e('0');
r($productTest->getBranchSelect4Mobile($product, '', $moduleName, $methodName)) && p() && e('0');
$product = $productTest->dao->select('*')->from(TABLE_PRODUCT)->where('id')->eq(41)->fetch();
$result = $productTest->getBranchSelect4Mobile($product, 'all', $moduleName, $methodName);
r((strpos($result, 'currentBranch') !== false and strpos($result, '所有') !== false)) && p() && e('1');
$result = $productTest->getBranchSelect4Mobile($product, 1, $moduleName, $methodName);
r((strpos($result, 'currentBranch') !== false and strpos($result, '分支1') !== false)) && p() && e('1');
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
zdTable('product')->gen(50);
$branch = zdTable('branch');
$branch->product->range('41');
$branch->gen(5);
/**
title=测试productModel->getBranchSelect4PC();
timeout=0
cid=1
*/
global $tester;
$productTest = $tester->loadModel('product');
$product = $productTest->dao->select('*')->from(TABLE_PRODUCT)->where('id')->eq(1)->fetch();
$moduleName = 'product';
$methodName = 'browse';
r($productTest->getBranchSelect4PC(new stdclass(), '', $moduleName, $methodName)) && p() && e('0');
r($productTest->getBranchSelect4PC($product, '', $moduleName, $methodName)) && p() && e('0');
$product = $productTest->dao->select('*')->from(TABLE_PRODUCT)->where('id')->eq(41)->fetch();
$result = $productTest->getBranchSelect4PC($product, '', $moduleName, $methodName);
r((strpos($result, 'currentBranch') !== false and strpos($result, '所有') !== false)) && p() && e('1');
$result = $productTest->getBranchSelect4PC($product, 1, $moduleName, $methodName);
r((strpos($result, 'currentBranch') !== false and strpos($result, '分支1') !== false)) && p() && e('1');
r((strpos($productTest->getBranchSelect4PC($product, 1, 'testcase', 'showimport'), 'currentBranch') !== false)) && p() && e('0');
r((strpos($productTest->getBranchSelect4PC($product, 1, 'tree', 'browse'), 'currentBranch') !== false)) && p() && e('1');
r((strpos($productTest->getBranchSelect4PC($product, 1, 'product', 'view'), 'currentBranch') !== false)) && p() && e('0');
r((strpos($productTest->getBranchSelect4PC($product, 1, 'release', 'create'), 'currentBranch') !== false)) && p() && e('1');
r((strpos($productTest->getBranchSelect4PC($product, 1, 'release', 'view'), 'currentBranch') !== false)) && p() && e('0');
$productTest->app->tab = 'qa';
r((strpos($productTest->getBranchSelect4PC($product, 1, 'bug', 'view'), 'currentBranch') !== false)) && p() && e('1');
r((strpos($productTest->getBranchSelect4PC($product, 1, 'testcase', 'view'), 'currentBranch') !== false)) && p() && e('1');
r((strpos($productTest->getBranchSelect4PC($product, 1, 'testtask', 'view'), 'currentBranch') !== false)) && p() && e('1');
r((strpos($productTest->getBranchSelect4PC($product, 1, 'testtask', 'create'), 'currentBranch') !== false)) && p() && e('0');
+7 -7
View File
@@ -19,7 +19,7 @@ class productZen extends product
* @access protected
* @return void
*/
protected function setEnvAll()
protected function setMenu4All()
{
/* Set redirect URI. */
$this->session->set('productList', $this->app->getURI(true), 'product');
@@ -28,7 +28,7 @@ class productZen extends product
if($this->app->viewType == 'mhtml')
{
$productID = $this->saveVisitState(0, $this->products);
$this->setMenu($productID);
$this->product->setMenu($productID);
}
}
@@ -48,7 +48,7 @@ class productZen extends product
setcookie('preBranch', $branch, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, true);
$this->session->set('createProjectLocate', $this->app->getURI(true), 'product');
$this->setMenu($productID, $branch);
$this->product->setMenu($productID, $branch);
}
/**
@@ -67,7 +67,7 @@ class productZen extends product
if($this->app->getViewType() != 'mhtml') return;
if($this->app->rawModule == 'projectstory' and $this->app->rawMethod == 'story') return $this->loadModel('project')->setMenu();
$this->setMenu();
$this->product->setMenu();
}
/**
@@ -82,7 +82,7 @@ class productZen extends product
protected function setEditMenu(int $productID, int $programID)
{
if($programID) return $this->loadModel('program')->setMenu($programID);
$this->setMenu($productID);
$this->product->setMenu($productID);
}
/**
@@ -97,7 +97,7 @@ class productZen extends product
*/
protected function setShowErrorNoneMenu(string $moduleName, string $activeMenu, int $objectID)
{
if($this->app->getViewType() == 'mhtml') return $this->setMenu();
if($this->app->getViewType() == 'mhtml') return $this->product->setMenu();
if($moduleName == 'qa') $this->setShowErrorNoneMenu4QA($activeMenu);
if($moduleName == 'project') $this->setShowErrorNoneMenu4Project($activeMenu, $objectID);
@@ -845,7 +845,7 @@ class productZen extends product
return;
}
$this->product->setMenu($productID, $branch, 0, '', "storyType=$storyType");
$this->product->setMenu($productID, $branch, "storyType=$storyType");
}
/**
+1
View File
@@ -54,6 +54,7 @@ class programplan extends control
if(!defined('RUN_MODE') || RUN_MODE != 'api') $projectID = $this->project->saveState($projectID, $this->project->getPairsByProgram());
/* 获取项目下产品并调整三级导航操作按钮。 */
$products = $this->loadModel('product')->getProducts($projectID);
if($this->session->hasProduct) $this->lang->modulePageNav = $this->product->select($products, $productID, 'programplan', 'browse', $type, 0, 0, '', false);
+2 -1
View File
@@ -1540,13 +1540,14 @@ class programplanModel extends model
}
/**
* 检查阶段是否是叶子阶段。
* Check if the stage is a leaf stage.
*
* @param int $planID
* @access public
* @return bool
*/
public function checkLeafStage($planID)
public function checkLeafStage(int $planID): bool
{
$subStageList = $this->dao->select('id')->from(TABLE_EXECUTION)->where('parent')->eq($planID)->andWhere('deleted')->eq(0)->fetchAll('id');
@@ -1,23 +1,16 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/programplan.class.php';
zdTable('user')->gen(5);
function initData()
{
zdTable('user')->gen(5);
zdTable('project')->config('checkleafstage')->gen(5);
}
su('admin');
$execution = zdTable('project');
$execution->id->range('1-5');
$execution->name->range('瀑布项目1,阶段a,阶段a子1,阶段a子1子1,阶段b');
$execution->type->range('project,stage{4}');
$execution->project->range('0,1{4}');
$execution->parent->range('0,1,2,3,1');
$execution->path->range("`,1,`,`,1,2,`,`,1,2,3,`,`,1,2,3,4,`,`,1,5,`");
$execution->status->range('doing,doing,doing,closed,suspended');
$execution->openedBy->range('admin,user1');
$execution->begin->range('20220112 000000:0')->type('timestamp')->format('YY/MM/DD');
$execution->end->range('20220212 000000:0')->type('timestamp')->format('YY/MM/DD');
$execution->realBegan->range('20220212 000000:0')->type('timestamp')->format('YY/MM/DD');
$execution->gen(5);
/**
@@ -25,8 +18,9 @@ title=测试programplanModel->checkLeafStage();
cid=1
pid=1
测试id为2判断是否为叶子节点 >> 0
测试id为5判断是否为叶子节点 >> 1
- 测试id为2判断是否为叶子节点 @0
- 测试id为5判断是否为叶子节点 @1
*/
@@ -0,0 +1,33 @@
title: programplanModel::checkLeafStage UTC.
desc: programplanModel::checkLeafStage UTC.
author: xushenjie
version: '1.0'
fields:
- field: name
range: '瀑布项目1,阶段a,阶段a子1,阶段a子1子1,阶段b'
- field: type
range: '0,1{4}'
- field: project
range: '0,1{4}'
- field: parent
range: '0,1,2,3,1'
- field: grade
range: 1
- field: path
range: "`,1,`,`,1,2,`,`,1,2,3,`,`,1,2,3,4,`,`,1,5,`"
- field: status
range: 'doing,doing,doing,closed,suspended'
- field: openedBy
range: 'admin,user1'
- field: begin
range: '20230501 000000:0'
type: 'timestamp'
format: 'YY/MM/DD'
- field: end
range: '20300501 000000:0'
type: 'timestamp'
format: 'YY/MM/DD'
- field: realBegan
range: '20230501 000000:0'
type: 'timestamp'
format: 'YY/MM/DD'
+8 -8
View File
@@ -630,10 +630,10 @@ class projectTao extends projectModel
* @param int $program
* @param string $path
* @param int $grade
* @access public
* @access protected
* @return string
*/
public function getParentProgram(string $path, int $grade): string
protected function getParentProgram(string $path, int $grade): string
{
$programList = $this->dao->select('id,name')->from(TABLE_PROGRAM)
->where('id')->in(trim($path, ','))
@@ -761,10 +761,10 @@ class projectTao extends projectModel
* Get the number of stories associated with the project.
*
* @param array $projectIdList
* @access public
* @access protected
* @return array
*/
public function getTotalStoriesByProject(array $projectIdList): array
protected function getTotalStoriesByProject(array $projectIdList): array
{
return $this->dao->select("t1.project, count(t2.id) as allStories, count(if(t2.status = 'active' or t2.status = 'changing', 1, null)) as leftStories, count(if(t2.status = 'closed' and t2.closedReason = 'done', 1, null)) as doneStories")->from(TABLE_PROJECTSTORY)->alias('t1')
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story=t2.id')
@@ -780,10 +780,10 @@ class projectTao extends projectModel
* Get the number of tasks associated with the project.
*
* @param array $projectIdList
* @access public
* @access protected
* @return array
*/
public function getTotalTaskByProject(array $projectIdList): array
protected function getTotalTaskByProject(array $projectIdList): array
{
return $this->dao->select("t1.project, count(t1.id) as allTasks, count(if(t1.status = 'wait', 1, null)) as waitTasks, count(if(t1.status = 'doing', 1, null)) as doingTasks, count(if(t1.status = 'done', 1, null)) as doneTasks, count(if(t1.status = 'wait' or t1.status = 'pause' or t1.status = 'cancel', 1, null)) as leftTasks, count(if(t1.status = 'done' or t1.status = 'closed', 1, null)) as litedoneTasks")->from(TABLE_TASK)->alias('t1')
->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.execution=t2.id')
@@ -812,10 +812,10 @@ class projectTao extends projectModel
* Get the number of bugs associated with the project.
*
* @param array $projectIdList
* @access public
* @access protected
* @return array
*/
public function getTotalBugByProject(array $projectIdList): array
protected function getTotalBugByProject(array $projectIdList): array
{
return $this->dao->select("project, count(id) as allBugs, count(if(status = 'active', 1, null)) as leftBugs, count(if(status = 'resolved', 1, null)) as doneBugs")->from(TABLE_BUG)
->where('project')->in($projectIdList)
+6 -10
View File
@@ -15,14 +15,10 @@ pid=1
global $tester;
$tester->loadModel('project');
$project1 = $tester->project->getProgramMaxEnd(1);
$project2 = $tester->project->getProgramMaxEnd(3);
$project3 = $tester->project->getProgramMaxEnd(6);
$project4 = $tester->project->getProgramMaxEnd(1);
$project5 = $tester->project->getProgramMaxEnd(3);
$project6 = $tester->project->getProgramMaxEnd(6);
a($project1);die;
r($project1) && p() && e(); //获取一级项目集最小开始时间
r($project1) && p() && e(); //获取一级项目集最大结束时间
r($project2) && p() && e(); //获取二级项目集最小开始时间
r($project2) && p() && e(); //获取二级项目集最大结束时间
r($project3) && p() && e(); //获取三级项目集最小开始时间
r($project3) && p() && e(); //获取三级项目集最大结束时间
r($project6) && p('maxEnd') && e('2023-06-12'); //获取一级项目集最大结束时间:尾级项目结束时间最大
r($project5) && p('maxEnd') && e('2023-07-03'); //获取二级项目集最大结束时间:顶级项目结束时间最大
r($project4) && p('maxEnd') && e('2023-05-08'); //获取三级项目集最大结束时间:开始时间等于结束时间
@@ -0,0 +1,24 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
su('admin');
$project = zdTable('project')->config('project')->gen(6);
/**
title=测试 projectModel->getProgramMinBegin();
cid=1
pid=1
*/
global $tester;
$tester->loadModel('project');
$project1 = $tester->project->getProgramMinBegin(1);
$project2 = $tester->project->getProgramMinBegin(3);
$project3 = $tester->project->getProgramMinBegin(6);
r($project1) && p('minBegin') && e('2023-05-01'); //获取一级项目集最小开始时间:顶级项目开始时间最小
r($project2) && p('minBegin') && e('2023-04-22'); //获取二级项目集最小开始时间:次级项目开始时间最小
r($project3) && p('minBegin') && e('2023-05-08'); //获取三级项目集最小开始时间:开始时间等于结束时间
@@ -22,7 +22,5 @@ fields:
range: '项目集1, 项目集1-1, 项目1-1-2, 项目集3, 项目3-4, 无迭代无产品项目6'
- field: code
range: 'program1, program1-1, project1-1-2, program3, project3-4, project6'
- field: begin
range: "'2023-05-01', '2023-05-07', '2023-05-20', '2023-04-04', '2023-05-01', '2023-05-08'"
- field: end
range: "'2023-06-01', '2023-05-19', '2023-06-12', '2023-07-03', '2023-06-03', '2023-05-08'"
range: '2023-06-01', '2023-05-19', '2023-06-12', '2023-07-03', '2023-06-03', '2023-05-08'
@@ -0,0 +1,26 @@
title: zt_project
author: liuhong
version: "1.0"
fields:
- field: id
range: 1-6
- field: project
range: 0
- field: model
range: ', , ,scrum, ,scrum,scrum'
- field: type
range: 'program,program,project,program,project,project'
- field: status
range: 'wait,doing,closed,suspended'
- field: parent
range: 0, 1, 2, 0, 3, 0
- field: grade
range: 1, 2, 3, 1, 2, 1
- field: path
range: '`,1,`, `,1,2,`, `,1,2,3,`, `,4,`, `,4,5,`, `,6,`'
- field: name
range: '项目集1, 项目集1-1, 项目1-1-2, 项目集3, 项目3-4, 无迭代无产品项目6'
- field: code
range: 'program1, program1-1, project1-1-2, program3, project3-4, project6'
- field: begin
range: '2023-05-01', '2023-05-07', '2023-05-20', '2023-05-01', '2023-04-22', '2023-05-08'
+8 -2
View File
@@ -25,15 +25,21 @@ $config->task->form->create['version'] = array('type' => 'int', 'required'
$config->task->form->assign = array();
$config->task->form->assign['assignedTo'] = array('type' => 'string', 'required' => false, 'default' => '');
$config->task->form->assign['left'] = array('type' => 'float', 'required' => true);
$config->task->form->assign['lastEditedBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account);
$config->task->form->assign['lastEditedDate'] = array('type' => 'string', 'required' => false, 'default' => helper::now());
$config->task->form->assign['assignedDate'] = array('type' => 'string', 'required' => false, 'default' => helper::now());
$config->task->form->manageTeam = array();
$config->task->form->manageTeam['status'] = array('type' => 'string', 'required' => false, 'default' => '');
$config->task->form->manageTeam['estimate'] = array('type' => 'float', 'required' => false, 'default' => 0);
$config->task->form->manageTeam['left'] = array('type' => 'float', 'required' => false, 'default' => 0);
$config->task->form->manageTeam['consumed'] = array('type' => 'float', 'required' => false, 'default' => 0);
$config->task->form->manageTeam['lastEditedDate'] = array('type' => 'string', 'required' => false, 'default' => helper::now());
$config->task->form->manageTeam['assignedDate'] = array('type' => 'string', 'required' => false, 'default' => helper::now());
$config->task->form->edit = array();
$config->task->form->edit['name'] = array('type' => 'string', 'required' => true);
$config->task->form->edit['color'] = array('type' => 'string', 'required' => false, 'default' => '');
$config->task->form->edit['desc'] = array('type' => 'string', 'required' => false, 'default' => '');
$config->task->form->edit['lastEditedBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account);
$config->task->form->edit['execution'] = array('type' => 'int', 'required' => true);
$config->task->form->edit['story'] = array('type' => 'int', 'required' => false, 'default' => 0);
$config->task->form->edit['module'] = array('type' => 'int', 'required' => false, 'default' => 0);
+41 -58
View File
@@ -174,16 +174,18 @@ class task extends control
}
/**
* 编辑任务。
* Edit a task.
*
* @param int $taskID
* @param string $taskID
* @param string $comment
* @param string $from
* @param string $from ''|taskkanban
* @access public
* @return void
*/
public function edit(string $taskID, string $comment = 'false', string $from = '')
public function edit(string $taskID, string $comment = '', string $from = '')
{
$taskID = (int)$taskID;
$this->commonAction($taskID);
if(!empty($_POST))
@@ -192,15 +194,17 @@ class task extends control
$changes = array();
$postDataFixer = form::data($this->config->task->form->edit);
$rawData = $postDataFixer->rawdata;
$rawData = $postDataFixer->rawdata;
if(!$comment or $comment == 'false')
/* Update task. */
if(empty($comment))
{
$task = $this->taskZen->prepareEdit($postDataFixer, $taskID);
$changes = $this->task->update($task, $rawData);
if(dao::isError()) return print(js::error(dao::getError()));
}
/* Record log. */
if($rawData->comment != '' or !empty($changes))
{
$action = !empty($changes) ? 'Edited' : 'Commented';
@@ -208,9 +212,9 @@ class task extends control
if(!empty($changes)) $this->action->logHistory($actionID, $changes);
}
/* Execute hooks and synchronize the status of related objects. */
$this->executeHooks($taskID);
if($rawData->status == 'doing') $this->loadModel('common')->syncPPEStatus($taskID);
if($task->status == 'doing') $this->loadModel('common')->syncPPEStatus($taskID);
$reponse = $this->taskZen->reponseAfterEdit($taskID, $from, $changes);
return is_array($reponse) ? $this->send($reponse) : $reponse;
@@ -359,47 +363,45 @@ class task extends control
}
/**
* 指派任务。
* Update assign of task
*
* @param int $requestID
* @param int $taskID
* @param string $kanbanGroup
* @param string $from
* @param stirng $executionID
* @param string $taskID
* @param string $from ''|taskkanban
* @access public
* @return void
*/
public function assignTo(int $executionID, int $taskID, string $kanbanGroup = 'default', string $from = '')
public function assignTo(string $executionID, string $taskID, string $from = '')
{
$executionID = (int)$executionID;
$taskID = (int)$taskID;
$this->commonAction($taskID);
$task = $this->task->getByID($taskID);
if(!empty($task->team) and $task->mode == 'multi' and strpos('done,cencel,closed', $task->status) === false)
{
return $this->editTeam($executionID, $taskID, $kanbanGroup, $from);
}
if(!empty($_POST))
{
$postDataFixer = form::data($this->config->task->form->assign);
$rawData = $postDataFixer->rawdata;
/* Assign task. */
$task = $this->taskZen->prepareAssignTo($postDataFixer, $taskID);
$changes = $this->task->assign($task);
$changes = $this->task->assign($task, $this->post->uid);
if(dao::isError())
{
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return print(js::error(dao::getError()));
}
$actionID = $this->loadModel('action')->create('task', $taskID, 'Assigned', $rawData->comment, $task->assignedTo);
/* Record log. */
$actionID = $this->loadModel('action')->create('task', $taskID, 'Assigned', $this->post->comment, $task->assignedTo);
$this->action->logHistory($actionID, $changes);
$this->executeHooks($taskID);
return $this->taskZen->reponseAfterAssignTo($taskID, $from);
$reponse = $this->taskZen->reponseAfterAssignTo($taskID, $from);
return is_array($reponse) ? $this->send($reponse) : $reponse;
}
$this->taskZen->buildAssignToForm($executionID, $task);
$this->taskZen->buildAssignToForm($executionID, $taskID);
}
/**
@@ -1902,24 +1904,29 @@ class task extends control
}
/**
* 管理多人任务的团队。
* Update assign of multi task.
*
* @param int $requestID
* @param object $taskID
* @param string $kanbanGroup
* @param string $from
* @param string $executionID
* @param string $taskID
* @param string $from ''|taskkanban
* @access public
* @return void
*/
public function editTeam($executionID, $taskID, $kanbanGroup = 'default', $from = '')
public function manageTeam(string $executionID, string $taskID, string $from = '')
{
$task = $this->task->getById($taskID);
$executionID = (int)$executionID;
$taskID = (int)$taskID;
$this->commonAction($taskID);
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->task->updateTeam($taskID);
/* Update assign of multi task. */
$postData = form::data($this->config->task->form->manageTeam);
$task = $this->taskZen->prepareManageTeam($postData, $taskID);
$changes = $this->task->updateTeam($task, $this->post->team, $this->post->teamSouce, $this->post->teamEstimate, $this->post->teamConsumed, $this->post->teamLeft);
if(dao::isError())
{
@@ -1927,43 +1934,19 @@ class task extends control
return print(js::error(dao::getError()));
}
/* Record log. */
$actionID = $this->action->create('task', $taskID, 'Edited');
$this->action->logHistory($actionID, $changes);
$this->executeHooks($taskID);
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success'));
if(isonlybody())
{
$execution = $this->execution->getByID($task->execution);
if(($this->app->tab == 'execution' or ($this->config->vision == 'lite' and $this->app->tab == 'project' and $this->session->kanbanview == 'kanban')) and $execution->type == 'kanban')
{
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
if($from == 'taskkanban')
{
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $this->session->execLaneType ? $this->session->execLaneType : 'all', 'id_desc', 0, $kanbanGroup, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this'));
}
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
$reponse = $this->taskZen->reponseAfterAssignTo($taskID, $from);
return is_array($reponse) ? $this->send($reponse) : $reponse;
}
$this->view->task = $task;
$this->view->members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');
$this->view->users = $this->loadModel('user')->getPairs();
$this->display('', 'editTeam');
$this->display('', 'editteam');
}
/**
+33 -34
View File
@@ -452,7 +452,7 @@ class taskModel extends model
* Manage multi task team members.
*
* @param string $mode
* @param object $taskID
* @param object $task
* @param array $teamList
* @param array $teamSourceList
* @param array $teamEstimateList
@@ -469,10 +469,10 @@ class taskModel extends model
$this->dao->delete()->from(TABLE_TASKTEAM)->where('task')->eq($task->id)->exec();
/* If status of the task is doing, get the person who did not complete the task. */
$undoneUsers = array();
if($task->status == 'doing')
{
$efforts = $this->getTaskEfforts($task->id);
$undoneUsers = array();
$efforts = $this->getTaskEfforts($task->id);
foreach($efforts as $effort)
{
if($effort->left != 0) $undoneUsers[$effort->account] = $effort->account;
@@ -1140,14 +1140,15 @@ class taskModel extends model
}
/**
* 任务指派用户。
* Assign a task to a user again.
*
* @param object $task
* @param int $taskID
* @access public
* @return array
* @return array|false
*/
public function assign(object $task): array|false
public function assign(object $task, string $uid): array|false
{
$oldTask = $this->getById($task->id);
@@ -1159,11 +1160,10 @@ class taskModel extends model
if($oldTask->parent > 0) $this->updateParentStatus($task->id);
$task = $this->loadModel('file')->processImgURL($task, $this->config->task->editor->assignto['id'], $this->post->uid);
$task = $this->loadModel('file')->processImgURL($task, $this->config->task->editor->assignto['id'], $uid);
$this->dao->update(TABLE_TASK)
->data($task)
->autoCheck()
->check('left', 'float')
->checkFlow()
->where('id')->eq($task->id)
->exec();
@@ -1172,56 +1172,46 @@ class taskModel extends model
}
/**
* Update a task team.
* 更新团队信息。
* Update team.
*
* @param int $taskID
* @param object $task
* @param array $team
* @param array $teamConsumed
* @param array $teamLeft
* @access public
* @return void
* @return array|false
*/
public function updateTeam($taskID)
public function updateTeam(object $task, array $team, array $teamSource, array $teamEstimate, array $teamConsumed, array $teamLeft): array|false
{
$taskID = $task->id;
$oldTask = $this->getById($taskID);
foreach($this->post->team as $i => $account)
/* Check team data. */
foreach($team as $i => $account)
{
if(!$account) continue;
if($this->post->teamConsumed[$i] == 0 and $this->post->teamLeft[$i] == 0)
if($teamConsumed[$i] == 0 and $teamLeft[$i] == 0)
{
dao::$errors[] = $this->lang->task->noticeTaskStart;
return false;
}
}
$now = helper::now();
$task = fixer::input('post')
->add('id', $taskID)
->setDefault('estimate, left, consumed', 0)
->setIF(is_numeric($this->post->estimate), 'estimate', (float)$this->post->estimate)
->setIF(is_numeric($this->post->consumed), 'consumed', (float)$this->post->consumed)
->setIF(is_numeric($this->post->left), 'left', (float)$this->post->left)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
->setDefault('assignedDate', $now)
->stripTags($this->config->task->editor->assignto['id'], $this->config->allowedTags)
->remove('comment,showModule,team,teamEstimate,teamConsumed,teamLeft,teamSource')
->get();
if(count(array_filter($this->post->team)) > 1)
/* Manage the team and calculate task work information. */
if(count(array_filter($team)) > 1)
{
$teams = $this->manageTaskTeam($oldTask->mode, $taskID, $task->status);
$teams = $this->manageTaskTeam($oldTask->mode, $task, $team, $teamSource, $teamEstimate, $teamConsumed, $teamLeft);
if(!empty($teams)) $task = $this->computeHours4Multiple($oldTask, $task);
}
if(empty($teams)) $task->mode = '';
if($oldTask->parent > 0) $this->updateParentStatus($taskID);
$task = $this->loadModel('file')->processImgURL($task, $this->config->task->editor->assignto['id'], $this->post->uid);
$this->dao->update(TABLE_TASK)
->data($task)
->autoCheck()
->checkIF($task->estimate != false, 'estimate', 'float')
->checkIF($task->left != false, 'left', 'float')
->checkIF($task->consumed != false, 'consumed', 'float')
->checkFlow()
->where('id')->eq($taskID)
->exec();
@@ -1955,6 +1945,7 @@ class taskModel extends model
*/
public function getByList($taskIDList = 0)
{
if(empty($taskIDList)) return array();
return $this->dao->select('*')->from(TABLE_TASK)
->where('deleted')->eq(0)
->beginIF($taskIDList)->andWhere('id')->in($taskIDList)->fi()
@@ -3339,7 +3330,14 @@ class taskModel extends model
$btnClass .= $task->assignedTo == 'closed' ? ' disabled' : '';
$btnClass .= ' iframe btn btn-icon-left btn-sm';
$assignToLink = $task->assignedTo == 'closed' ? '#' : helper::createLink('task', 'assignTo', "executionID=$task->execution&taskID=$task->id", '', true);
if(!empty($task->team) and $task->mode == 'multi' and strpos('done,cencel,closed', $task->status) === false)
{
$assignToLink = $task->assignedTo == 'closed' ? '#' : helper::createLink('task', 'manageTeam', "executionID=$task->execution&taskID=$task->id", '', true);
}
else
{
$assignToLink = $task->assignedTo == 'closed' ? '#' : helper::createLink('task', 'assignTo', "executionID=$task->execution&taskID=$task->id", '', true);
}
$assignToHtml = html::a($assignToLink, "<i class='icon icon-hand-right'></i> <span title='" . $assignedToTitle . "'>{$assignedToText}</span>", '', "class='$btnClass'");
echo !common::hasPriv('task', 'assignTo', $task) ? "<span style='padding-left: 21px' class='{$btnTextClass}'>{$assignedToText}</span>" : $assignToHtml;
@@ -3918,6 +3916,7 @@ class taskModel extends model
/* If both of lane id and column id are not empty, add task to the kanban cell. */
if($laneID and $columnID) $this->kanban->addKanbanCell($kanbanID, $laneID, $columnID, 'task', $task->id);
/* If lane id or column id is empty, update the task type lane of the kanban. */
if(!$laneID or !$columnID) $this->kanban->updateLane($kanbanID, 'task');
}
+1 -1
View File
@@ -545,7 +545,7 @@ class taskTao extends taskModel
}
elseif($task->status == 'doing')
{
$teamSource = $teamSourceList[$row];
$teamSource = zget($teamSourceList, $row);
if(!empty($teamSource) and $teamSource != $account and isset($undoneUsers[$teamSource])) $member->transfer = $teamSource;
if(isset($undoneUsers[$account]) and ($mode == 'multi' or ($mode == 'linear' and $minStatus != 'wait'))) $member->status = 'doing';
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/task.class.php';
su('admin');
$task = zdTable('task');
$task->id->range('1-4');
$task->type->range('test,devel,design,story');
$task->design->range('0{2},1,0{4}');
$task->name->range('测试,开发,设计,需求')->postfix('任务1');
$task->story->range('0{3},1');
$task->status->range('wait');
$task->gen(4);
$bug = zdTable('bug');
$bug->id->range(1);
$bug->product->range('1');
$bug->title->range('Bug1');
$bug->gen(1);
$todo = zdTable('todo');
$todo->id->range(1);
$todo->name->range('待办1');
$todo->gen(1);
$design = zdTable('design');
$design->id->range(1);
$design->name->range('设计1');
$design->gen(1);
$story = zdTable('story');
$story->id->range('1-5');
$story->product->range('1');
$story->title->range('1-5')->prefix('需求');
$story->type->range('story');
$story->gen(5);
/**
title=测试taskModel->afterCreate();
timeout=0
cid=1
*/
$taskIdList['test'] = array(1);
$taskIdList['todo'] = array(2);
$taskIdList['design'] = array(3);
$taskIdList['story'] = array(4);
$testTasks[2] = new stdclass();
$testTasks[2]->name = '测试子任务1';
$testTasks[2]->type = 'test';
$testTasks[2]->mailto = '';
$testTasks[3] = new stdclass();
$testTasks[3]->name = '测试子任务2';
$testTasks[3]->type = 'test';
$testTasks[3]->mailto = '';
$taskTester = new taskTest();
r($taskTester->afterCreateTest()) && p() && e('0'); // 测试空数据
r($taskTester->afterCreateTest(0, $taskIdList['test'])) && p() && e('0'); // 测试taskID为空的情况
r($taskTester->afterCreateTest(5, $taskIdList['test'])) && p() && e('0'); // 测试taskID不存在的情况
r($taskTester->afterCreateTest(1)) && p('name') && e('测试任务1'); // 测试taskIdList为空的情况
r($taskTester->afterCreateTest(1, array(), 1)) && p('title') && e('Bug1'); // 测试taskIdList为空,但是有BugID的情况
r($taskTester->afterCreateTest(1, array(), 0, 1)) && p('name') && e('待办1'); // 测试taskIdList为空,但是有todoID的情况
r($taskTester->afterCreateTest(1, array(), 0, 0, $testTasks)) && p('name') && e('测试任务1'); // 测试taskIdList为空,但是有testTasks的情况
r($taskTester->afterCreateTest(1, $taskIdList['test'], 1)) && p('toTask') && e('1'); // 测试Bug转任务后,更新Bug的信息
r($taskTester->afterCreateTest(2, $taskIdList['todo'], 0, 1)) && p('status') && e('done'); // 测试待办转任务后,更新待办的信息
r($taskTester->afterCreateTest(3, $taskIdList['design'])) && p('designVersion') && e('1'); // 测试任务关联设计后,更新任务中的designVersion字段
r($taskTester->afterCreateTest(4, $taskIdList['story'])) && p('stage') && e('planned'); // 测试任务关联需求后,更新需求的阶段
r($taskTester->afterCreateTest(1, $taskIdList['test'], 0, 0, $testTasks)) && p('parent') && e('-1'); // 测试任务创建子任务后,父任务的parent字段的值
+8 -10
View File
@@ -10,43 +10,41 @@ title=taskModel->assign();
timeout=0
cid=1
- 执行task模块的assign方法,参数是$taskIDlist[0],$waitTask
- wait状态任务指派
- 第0条的field属性 @assignedTo
- 第0条的old属性 @old1
- 第0条的new属性 @user92
- 执行task模块的assign方法,参数是$taskIDlist[0],$waitTaskLeft
- wait状态任务指派修改预计剩余
- 第1条的field属性 @left
- 第1条的old属性 @0
- 第1条的new属性 @1
- 执行task模块的assign方法,参数是$taskIDlist[1],$doingTask
- doing状态任务指派
- 第0条的field属性 @assignedTo
- 第0条的old属性 @old2
- 第0条的new属性 @user93
- 执行task模块的assign方法,参数是$taskIDlist[2],$doneTask
- done状态任务指派
- 第0条的field属性 @assignedTo
- 第0条的old属性 @old3
- 第0条的new属性 @user94
- 执行task模块的assign方法,参数是$taskIDlist[3],$pauseTask
- pause状态任务指派
- 第0条的field属性 @assignedTo
- 第0条的old属性 @old4
- 第0条的new属性 @user95
- 执行task模块的assign方法,参数是$taskIDlist[4],$cancelTask
- cancel状态任务指派
- 第0条的field属性 @assignedTo
- 第0条的old属性 @old5
- 第0条的new属性 @user96
- 执行task模块的assign方法,参数是$taskIDlist[5],$closedTask
- closed状态任务指派
- 第0条的field属性 @assignedTo
- 第0条的old属性 @old6
- 第0条的new属性 @user97
*/
function initData()
@@ -87,4 +85,4 @@ r($task->assignTest($taskIDlist[1],$doingTask)) && p('0:field,old,new') && e('a
r($task->assignTest($taskIDlist[2],$doneTask)) && p('0:field,old,new') && e('assignedTo,old3,user94'); // done状态任务指派
r($task->assignTest($taskIDlist[3],$pauseTask)) && p('0:field,old,new') && e('assignedTo,old4,user95'); // pause状态任务指派
r($task->assignTest($taskIDlist[4],$cancelTask)) && p('0:field,old,new') && e('assignedTo,old5,user96'); // cancel状态任务指派
r($task->assignTest($taskIDlist[5],$closedTask)) && p('0:field,old,new') && e('assignedTo,old6,user97'); // closed状态任务指派
r($task->assignTest($taskIDlist[5],$closedTask)) && p('0:field,old,new') && e('assignedTo,old6,user97'); // closed状态任务指派
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/task.class.php';
su('admin');
/**
title=taskModel->updateTeam();
timeout=0
cid=1
- 分配2个成员团队后任务
- 第1条的field属性 @estimate
- 第1条的old属性 @0
- 第1条的new属性 @3.5
- 分配1个成员团队后任务
- 第1条的field属性 @mode
- 第1条的old属性 @multi
- 第1条的new属性 @~~
- 分配预计剩余为0工时团队后任务 @总计消耗"和"预计剩余"不能同时为0
*/
function initData()
{
$task = zdTable('task');
$task->mode->range('multi');
$task->gen(6);
$user = zdTable('user')->gen(50);
}
initData();
$taskIDList = array('1','2','3','4','5','6');
$taskStatusList = array('doing','wait','done');
$teamList = array('user1','user2','user3');
$task = new taskTest();
r($task->updateTeamTest($taskIDList[0], $taskStatusList[1], array($teamList[0], $teamList[1]), array($teamList[0], $teamList[1]), $teamEstimateList = array(1, 2.5), $teamConsumedList = array(0, 0), $teamLeftList = array(1, 0.5))) && p('1:field,old,new') && e('estimate,0,3.5'); //分配2个成员团队后任务
r($task->updateTeamTest($taskIDList[1], $taskStatusList[1], array($teamList[0]), array($teamList[0]), $teamEstimateList = array(1), $teamConsumedList = array(0), $teamLeftList = array(1))) && p('1:field,old,new') && e('mode,multi,~~'); //分配1个成员团队后任务
r($task->updateTeamTest($taskIDList[2], $taskStatusList[1], array($teamList[0], $teamList[1]), array($teamList[0], $teamList[1]), $teamEstimateList = array(1, 2.5), $teamConsumedList = array(0, 0), $teamLeftList = array(0, 0))) && p() && e('"总计消耗"和"预计剩余"不能同时为0'); //分配预计剩余为0工时团队后任务
+11 -1
View File
@@ -19,6 +19,16 @@ title=taskModel->batchComputeProgress();
timeout=0
cid=2
sed: can't read /home/tianshujie/repo/zentaopms/test/config/my.php: No such file or directory
- 测试任务消耗工时为0,剩余工时为0的情况第1条的progress属性 @0
- 测试任务消耗工时不为0,剩余工时为0的情况第2条的progress属性 @100
- 测试任务消耗工时为15,剩余工时为1的情况第3条的progress属性 @94
- 测试任务消耗工时为20,剩余工时为5的情况第4条的progress属性 @80
- 测试任务消耗工时为25,剩余工时为10的情况第5条的progress属性 @71
*/
$tester->loadModel('task');
@@ -32,4 +42,4 @@ r($tester->task->batchComputeProgress($taskList)) && p('1:progress') && e('0');
r($tester->task->batchComputeProgress($taskList)) && p('2:progress') && e('100'); // 测试任务消耗工时不为0,剩余工时为0的情况
r($tester->task->batchComputeProgress($taskList)) && p('3:progress') && e('94'); // 测试任务消耗工时为15,剩余工时为1的情况
r($tester->task->batchComputeProgress($taskList)) && p('4:progress') && e('80'); // 测试任务消耗工时为20,剩余工时为5的情况
r($tester->task->batchComputeProgress($taskList)) && p('5:progress') && e('71'); // 测试任务消耗工时为25,剩余工时为10的情况
r($tester->task->batchComputeProgress($taskList)) && p('5:progress') && e('71'); // 测试任务消耗工时为25,剩余工时为10的情况
+20 -1
View File
@@ -22,6 +22,25 @@ title=taskModel->buildTaskTree();
timeout=0
cid=1
sed: can't read /home/tianshujie/repo/zentaopms/test/config/my.php: No such file or directory
- 测试空数据的情况 @0
- 测试数据中有父任务时,重构结构后的任务数量 @6
- 测试数据中有父任务时,重构结构后子任务的数据第children[4]条的name属性 @子任务4
- 测试数据中没有父任务时,重构结构后的任务数量 @6
- 测试数据中没有父任务时,重构结构后的子任务中父任务的名称第4条的parentName属性 @父任务1
- 测试数据中有父子任务时,重构结构后的子任务中父任务的名称第1条的name属性 @父任务1
- 测试数据中有父子任务时,重构结构后的任务数量 @2
- 测试数据中只有父任务时,重构结构后的父任务的名称第1条的name属性 @父任务1
- 测试数据中只有父任务时,重构结构后的任务数量 @3
*/
$taskTester = new taskTest();
@@ -49,4 +68,4 @@ r($notParentTasks) && p('4:parentName') && e('父任务1'); // 测试
r($hasParentTasks) && p('1:name') && e('父任务1'); // 测试数据中有父子任务时,重构结构后的子任务中父任务的名称
r($hasParentTasksCount) && p() && e('2'); // 测试数据中有父子任务时,重构结构后的任务数量
r($parentTasks) && p('1:name') && e('父任务1'); // 测试数据中只有父任务时,重构结构后的父任务的名称
r($parentTasksCount) && p() && e('3'); // 测试数据中只有父任务时,重构结构后的任务数量
r($parentTasksCount) && p() && e('3'); // 测试数据中只有父任务时,重构结构后的任务数量
@@ -65,92 +65,91 @@ title=taskModel->computeCurrentTaskStatus();
timeout=0
cid=1
- 执行$task1
sed: can't read /home/tianshujie/repo/zentaopms/test/config/my.php: No such file or directory
- 查询 task1 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态自动变更 没有工时消耗 团队成员members[0]
- 属性status @doing
- 属性assignedTo @user1
- 属性estimate @9
- 属性left @0
- 属性consumed @10
- 执行$task2
- 查询 task2 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态自动变更 没有工时消耗 团队成员members[0]
- 属性status @doing
- 属性assignedTo @user1
- 属性estimate @9
- 属性left @0
- 属性consumed @10
- 执行$task3
- 查询 task3 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态自动变更 没有工时消耗 团队成员members[0]
- 属性status @doing
- 属性assignedTo @user1
- 属性estimate @9
- 属性left @0
- 属性consumed @10
- 执行$task4
- 查询 task4 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态非自动变更 没有工时消耗 团队成员members[0]
- 属性status @doing
- 属性assignedTo @user1
- 属性estimate @9
- 属性left @0
- 属性consumed @10
- 执行$task5
- 查询 task5 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态非自动变更 有工时消耗 团队成员members[0]
- 属性status @doing
- 属性assignedTo @user1
- 属性estimate @9
- 属性left @0
- 属性consumed @10
- 执行$task6
- 查询 task6 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态非自动变更 有工时消耗 团队成员members[1]
- 属性status @doing
- 属性assignedTo @user1
- 属性estimate @9
- 属性left @0
- 属性consumed @10
- 执行$task7
- 查询 task7 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态自动变更 有工时消耗 团队成员members[0]
- 属性status @done
- 属性assignedTo @admin
- 属性estimate @8
- 属性left @8
- 属性consumed @0
- 执行$task8
- 查询 task8 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态自动变更 有工时消耗 团队成员members[0]
- 属性status @done
- 属性assignedTo @admin
- 属性estimate @8
- 属性left @8
- 属性consumed @0
- 执行$task9
- 查询 task9 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态自动变更 有工时消耗 团队成员members[0]
- 属性status @done
- 属性assignedTo @admin
- 属性estimate @8
- 属性left @8
- 属性consumed @0
- 执行$task10
- 查询 task10 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态非自动变更 有工时消耗 团队成员members[0]
- 属性status @done
- 属性assignedTo @admin
- 属性estimate @8
- 属性left @8
- 属性consumed @0
- 执行$task11
- 查询 task11 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态非自动变更 没有工时消耗 团队成员members[0]
- 属性status @done
- 属性assignedTo @admin
- 属性estimate @8
- 属性left @8
- 属性consumed @0
- 执行$task12
- 查询 task12 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态非自动变更 没有工时消耗 团队成员members[1]
- 属性status @done
- 属性assignedTo @admin
- 属性estimate @8
- 属性left @8
- 属性consumed @0
*/
$task = new taskTest();
+13 -1
View File
@@ -16,8 +16,20 @@ function initData()
/**
title=测试computeTaskProgress
timeout=0
cid=1
sed: can't read /home/tianshujie/repo/zentaopms/test/config/my.php: No such file or directory
- 测试任务消耗工时为0,剩余工时为0的情况 @0
- 测试任务消耗工时不为0,剩余工时为0的情况 @100
- 测试任务消耗工时为15,剩余工时为1的情况 @94
- 测试任务消耗工时为20,剩余工时为5的情况 @80
- 测试任务消耗工时为25,剩余工时为10的情况 @71
*/
initData();
@@ -30,4 +42,4 @@ r($tester->task->computeTaskProgress($taskList[1])) && p() && e('0'); //测试
r($tester->task->computeTaskProgress($taskList[2])) && p() && e('100'); //测试任务消耗工时不为0,剩余工时为0的情况
r($tester->task->computeTaskProgress($taskList[3])) && p() && e('94'); //测试任务消耗工时为15,剩余工时为1的情况
r($tester->task->computeTaskProgress($taskList[4])) && p() && e('80'); //测试任务消耗工时为20,剩余工时为5的情况
r($tester->task->computeTaskProgress($taskList[5])) && p() && e('71'); //测试任务消耗工时为25,剩余工时为10的情况
r($tester->task->computeTaskProgress($taskList[5])) && p() && e('71'); //测试任务消耗工时为25,剩余工时为10的情况

Some files were not shown because too many files have changed in this diff Show More