Merge branch 'master' of https://git.zcorp.cc/easycorp/zentaopms
This commit is contained in:
+3
-11
@@ -1,12 +1,4 @@
|
||||
UPDATE `zt_block` SET `module` = 'scrumProject' WHERE `module` = 'project' and type = 'scrum';
|
||||
UPDATE `zt_block` SET `module` = 'kanbanProject' WHERE `module` = 'project' and type = 'kanban';
|
||||
UPDATE `zt_block` SET `module` = 'waterfallProject' WHERE `module` = 'project' and type = 'waterfall';
|
||||
DROP INDEX account_vision_module_type_order ON `zt_block`;
|
||||
CREATE UNIQUE INDEX `account_vision_module_order` ON `zt_block`(`account`,`vision`,`module`,`order`);
|
||||
|
||||
ALTER TABLE `zt_block` ADD `dashboard` varchar(20) NOT NULL DEFAULT '' AFTER `account`;
|
||||
ALTER TABLE `zt_block` CHANGE `module` `dashboard` varchar(20) NOT NULL DEFAULT '' AFTER `account`;
|
||||
ALTER TABLE `zt_block` DROP `type`;
|
||||
ALTER TABLE `zt_block` DROP `source`;
|
||||
ALTER TABLE `zt_block` CHANGE `block` `code` varchar(30) NOT NULL DEFAULT '' AFTER `dashboard`;
|
||||
ALTER TABLE `zt_block` MODIFY `vision` varchar(10) NOT NULL DEFAULT 'rnd' AFTER `hidden`;
|
||||
UPDATE `zt_block` SET `dashboard` = `module`;
|
||||
|
||||
ALTER TABLE `zt_todo` CHANGE `idvalue` `objectID` mediumint(8) unsigned default '0' NOT NULL AFTER `type`;
|
||||
|
||||
+1
-1
@@ -1935,8 +1935,8 @@ CREATE TABLE IF NOT EXISTS `zt_todo` (
|
||||
`end` smallint(4) unsigned zerofill NOT NULL DEFAULT '0',
|
||||
`feedback` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`type` char(15) NOT NULL DEFAULT '',
|
||||
`objectID` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`cycle` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`idvalue` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`pri` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`name` char(150) NOT NULL DEFAULT '',
|
||||
`desc` mediumtext NULL,
|
||||
|
||||
@@ -927,7 +927,12 @@ class baseControl
|
||||
}
|
||||
|
||||
if(empty($this->output)) $this->parse($moduleName, $methodName);
|
||||
echo $this->output;
|
||||
$trace = '';
|
||||
if($this->config->debug && $this->config->debug >= 2)
|
||||
{
|
||||
$trace = $this->app->loadClass('trace')->output();
|
||||
}
|
||||
echo $this->output . $trace;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -970,7 +975,7 @@ class baseControl
|
||||
$css = $this->getCSS($moduleName, $methodName, '.ui');
|
||||
$js = $this->getJS($moduleName, $methodName, '.ui');
|
||||
if($css) $this->view->pageCSS = $css;
|
||||
if($js) $this->view->pageJS = $js;
|
||||
if($js) $this->view->pageJS = $js;
|
||||
|
||||
/**
|
||||
* 切换到视图文件所在的目录,以保证视图文件里面的include语句能够正常运行。
|
||||
@@ -984,6 +989,13 @@ class baseControl
|
||||
*/
|
||||
\zin\zin::$data = (array)$this->view;
|
||||
|
||||
\zin\zin::$data['zinDebug'] = array();
|
||||
|
||||
if($this->config->debug && $this->config->debug >= 2)
|
||||
{
|
||||
\zin\zin::$data['zinDebug']['trace'] = $this->app->loadClass('trace')->getTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用extract安定ob方法渲染$viewFile里面的代码。
|
||||
* Use extract and ob functions to eval the codes in $viewFile.
|
||||
|
||||
@@ -356,6 +356,14 @@ class baseRouter
|
||||
*/
|
||||
public $siteCode;
|
||||
|
||||
/**
|
||||
* 请求开始时间。
|
||||
* The start time of the request.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
public $startTime;
|
||||
|
||||
/**
|
||||
* 构造方法, 设置路径,类,超级变量等。注意:
|
||||
* 1.应该使用createApp()方法实例化router类;
|
||||
@@ -408,6 +416,9 @@ class baseRouter
|
||||
if($this->config->framework->autoConnectDB) $this->connectDB();
|
||||
if($this->config->framework->multiLanguage) $this->setClientLang();
|
||||
|
||||
$this->setupProfiling();
|
||||
$this->setupXhprof();
|
||||
|
||||
$this->setEdition();
|
||||
$this->setVision();
|
||||
|
||||
@@ -435,6 +446,19 @@ class baseRouter
|
||||
return new $className($appName, $appRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求开始时间。
|
||||
* The start time of the request.
|
||||
*
|
||||
* @param float $startTime
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setStartTime(float $startTime)
|
||||
{
|
||||
$this->startTime = $startTime;
|
||||
}
|
||||
|
||||
//-------------------- 路径相关方法(Path related methods)--------------------//
|
||||
|
||||
/**
|
||||
@@ -700,6 +724,83 @@ class baseRouter
|
||||
if(!empty($this->config->debug)) error_reporting(E_ALL & ~ E_STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置数据库性能采样。
|
||||
* Setup database profiling.
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function setupProfiling(): void
|
||||
{
|
||||
if(!empty($this->config->debug) && $this->config->debug >= 3) $this->dbh->exec('SET profiling = 1');
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出数据库性能采样结果(Server-Timing)。
|
||||
* Output database profiling(Server-Timing).
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function outputProfiling(): void
|
||||
{
|
||||
if(empty($this->config->debug) || $this->config->debug < 3) return;
|
||||
|
||||
/* MySQL profiling. */
|
||||
$profiling = $this->dbh->query('SHOW PROFILES')->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach($profiling as $prof)
|
||||
{
|
||||
header('Server-Timing: db;desc="SQL: ' . $prof['Query'] . '";dur=' . $prof['Duration'] * 1000, false);
|
||||
}
|
||||
|
||||
header('Server-Timing: app;desc="PHP: Total";dur=' . (getTime() - $this->startTime) * 1000, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用Xhprof。
|
||||
* Setup xhprof.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setupXhprof(): void
|
||||
{
|
||||
if(!empty($this->config->debug) && $this->config->debug >= 4 && extension_loaded('xhprof')) xhprof_enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出Xhprof结果。
|
||||
* Output xhprof.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function outputXhprof(): bool
|
||||
{
|
||||
if(empty($this->config->debug) || $this->config->debug < 4 || !extension_loaded('xhprof')) return false;
|
||||
|
||||
$log = xhprof_disable();
|
||||
$xhprofPath = $this->getTmpRoot() . 'xhprof';
|
||||
$libUtilsPath = $xhprofPath . DS . 'xhprof_lib' . DS . 'utils' . DS;
|
||||
$outputDir = ini_get('xhprof.output_dir');
|
||||
|
||||
if(!is_dir($xhprofPath)) return false;
|
||||
|
||||
include_once $libUtilsPath . 'xhprof_lib.php';
|
||||
include_once $libUtilsPath . 'xhprof_runs.php';
|
||||
|
||||
if(!$outputDir)
|
||||
{
|
||||
$outputDir = $xhprofPath . DS . 'xhprof_runs';
|
||||
if(!is_dir($outputDir)) mkdir($outputDir, 0777, true);
|
||||
}
|
||||
|
||||
$xhprofRuns = new \XHProfRuns_Default($outputDir);
|
||||
$runID = $xhprofRuns->save_run($log, "{$this->moduleName}_{$this->methodName}");
|
||||
header("Xhprof-RunID: {$runID}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置版本。
|
||||
* Set edition.
|
||||
@@ -1926,7 +2027,7 @@ class baseRouter
|
||||
/* 将扩展文件的代码合并到代码中。Cycle all the extension files and merge them into target lines. */
|
||||
$extTargets = array();
|
||||
foreach($extFiles as $extFile) $extTargets[basename((string) $extFile)] = $extFile;
|
||||
foreach($extTargets as $extTarget) $targetLines .= self::removePHPTAG($extTarget);
|
||||
foreach($extTargets as $extTarget) $targetLines .= static::removePHPTAG($extTarget);
|
||||
|
||||
/* 做个标记,方便后面替换代码使用。Make a mark for replacing codes. */
|
||||
$replaceMark = '//**//';
|
||||
@@ -1982,7 +2083,7 @@ class baseRouter
|
||||
/* 通过文件名获得其对应的方法名。Get methods according it's filename. */
|
||||
$fileName = baseName((string) $hookFile);
|
||||
[$method] = explode('.', $fileName);
|
||||
$hookCodes[$method][] = self::removePHPTAG($hookFile);
|
||||
$hookCodes[$method][] = static::removePHPTAG($hookFile);
|
||||
}
|
||||
|
||||
/* 合并Hook文件。Cycle the hook methods and merge hook codes. */
|
||||
@@ -2059,7 +2160,7 @@ class baseRouter
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(empty($url)) return false;
|
||||
if(empty($url)) return '';
|
||||
return $url;
|
||||
}
|
||||
|
||||
@@ -2212,13 +2313,20 @@ class baseRouter
|
||||
public function loadModule()
|
||||
{
|
||||
try {
|
||||
if(is_null($this->params) and !$this->setParams()) return false;
|
||||
if(is_null($this->params) and !$this->setParams())
|
||||
{
|
||||
$this->outputProfiling();
|
||||
$this->outputXhprof();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 调用该方法 Call the method. */
|
||||
$module = $this->control;
|
||||
|
||||
call_user_func_array(array($module, $this->methodName), $this->params);
|
||||
$this->checkAPIFile();
|
||||
$this->outputProfiling();
|
||||
$this->outputXhprof();
|
||||
return $module;
|
||||
} catch (EndResponseException $endResponseException) {
|
||||
echo $endResponseException->getContent();
|
||||
@@ -2714,6 +2822,7 @@ class baseRouter
|
||||
*
|
||||
* @param object $params the database params.
|
||||
* @access public
|
||||
* @return object|bool
|
||||
*/
|
||||
public function connectByPDO(object $params): object|bool
|
||||
{
|
||||
|
||||
@@ -589,13 +589,16 @@ class baseDAO
|
||||
*
|
||||
* @param string $sql
|
||||
* @access public
|
||||
* @return void
|
||||
* @return array|void
|
||||
*/
|
||||
public function explain($sql = '')
|
||||
public function explain($sql = '', $exit = true)
|
||||
{
|
||||
$sql = empty($sql) ? $this->processSQL() : $sql;
|
||||
$result = $this->dbh->rawQuery('explain ' . $sql)->fetch();
|
||||
a($result);
|
||||
|
||||
if($exit) a($result);
|
||||
|
||||
return (array)$result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1615,6 +1618,7 @@ class baseSQL
|
||||
*/
|
||||
public static function factory($table = '')
|
||||
{
|
||||
dao::$errors = array(); /* Reset dao::errors before CRUD to prevent from last CRUD disturb new CRUD. */
|
||||
return new sql($table);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
class trace
|
||||
{
|
||||
protected $types = array(
|
||||
'Request' => '请求',
|
||||
'Files' => '文件',
|
||||
'SQL Query' => 'SQL 查询',
|
||||
'SQL Explain' => 'SQL Explain',
|
||||
);
|
||||
|
||||
public $trace = array();
|
||||
|
||||
protected $app;
|
||||
|
||||
protected $dao;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
global $app, $dao;
|
||||
$this->app = $app;
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
public function getRequestInfo()
|
||||
{
|
||||
$this->trace['Request'] = array(
|
||||
'start' => date('Y-m-d H:i:s', (int)$this->app->startTime),
|
||||
'url' => $this->app->getURI(true),
|
||||
'protocol' => $this->app->server->server_protocol,
|
||||
'method' => $this->app->server->request_method,
|
||||
'timeUsed' => round(getTime() - $this->app->startTime, 4) * 1000,
|
||||
'memory' => round(memory_get_peak_usage() / 1024, 1),
|
||||
'querys' => count(dao::$querys),
|
||||
'caches' => count(dao::$cache),
|
||||
'files' => count(get_included_files()),
|
||||
'session' => session_id()
|
||||
);
|
||||
}
|
||||
|
||||
public function getRequestFiles()
|
||||
{
|
||||
$this->trace['Files'] = get_included_files();
|
||||
}
|
||||
|
||||
public function getRequestSqls()
|
||||
{
|
||||
$explain = array();
|
||||
/**
|
||||
foreach(dao::$querys as $query)
|
||||
{
|
||||
$explain[] = $this->dao->explain($query, false);
|
||||
}
|
||||
*/
|
||||
$this->trace['SQL Query'] = dao::$querys;
|
||||
$this->trace['SQL Explain'] = $explain;
|
||||
}
|
||||
|
||||
public function getTrace()
|
||||
{
|
||||
$this->getRequestInfo();
|
||||
$this->getRequestFiles();
|
||||
$this->getRequestSqls();
|
||||
return $this->trace;
|
||||
}
|
||||
|
||||
public function output()
|
||||
{
|
||||
$this->getTrace();
|
||||
$lines = '';
|
||||
foreach($this->trace as $type => $content)
|
||||
{
|
||||
if($type == 'SQL Explain') continue;
|
||||
$lines .= $this->console($type, empty($content) ? array() : $content);
|
||||
}
|
||||
|
||||
$lines .= $this->printSQLProfile();
|
||||
|
||||
$js = <<<JS
|
||||
|
||||
<script type='text/javascript'>
|
||||
{$lines}
|
||||
</script>
|
||||
JS;
|
||||
return $js;
|
||||
}
|
||||
|
||||
protected function console(string $type, $content)
|
||||
{
|
||||
$traceTabs = array_keys($this->types);
|
||||
$line = array();
|
||||
$line[] = $type == $traceTabs[0] ? "console.group('{$type}');" : "console.groupCollapsed('{$type}');";
|
||||
|
||||
foreach((array) $content as $key => $item)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'SQL Query':
|
||||
$msg = str_replace("\n", '\n', addslashes($item));
|
||||
$style = "color:#009bb4;";
|
||||
$line[] = "console.log(\"%c{$msg}\", \"{$style}\");";
|
||||
|
||||
$explain = array();
|
||||
if(!empty($this->trace['SQL Explain']))
|
||||
{
|
||||
foreach($this->trace['SQL Explain'][$key] as $explainKey => $explainItem)
|
||||
{
|
||||
$explain[] = $explainKey . ': ' . $explainItem;
|
||||
}
|
||||
}
|
||||
|
||||
$msg = implode(', ', $explain);
|
||||
$style = "color:red;";
|
||||
$line[] = "console.log(\"%c{$msg}\", \"{$style}\");";
|
||||
break;
|
||||
default:
|
||||
$item = is_string($key) ? $key . ' ' . $item : $key + 1 . ' ' . $item;
|
||||
$msg = json_encode($item);
|
||||
$line[] = "console.log({$msg});";
|
||||
break;
|
||||
}
|
||||
}
|
||||
$line[] = "console.groupEnd();";
|
||||
return implode(PHP_EOL, $line);
|
||||
}
|
||||
|
||||
protected function printSQLProfile()
|
||||
{
|
||||
$lines = array();
|
||||
|
||||
$profiling = $this->dao->dbh->query('SHOW PROFILES')->fetchAll(PDO::FETCH_ASSOC);
|
||||
if(empty($profiling)) return '';
|
||||
|
||||
$lines[] = 'console.groupCollapsed("SQL Profile")';
|
||||
$lines[] = 'console.table(' . json_encode($profiling) . ')';
|
||||
$lines[] = 'console.groupEnd()';
|
||||
|
||||
return implode(PHP_EOL, $lines);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return json_encode($this->getTrace());
|
||||
}
|
||||
}
|
||||
@@ -47,12 +47,16 @@ class block extends control
|
||||
|
||||
$this->block->create($formData);
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => 1));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true, 'closeModal' => true, 'callback' => 'loadCurrentPage()'));
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->block->createBlock;
|
||||
$this->view->block = $block;
|
||||
$this->blockZen->buildCreateForm($dashboard, $module, $block);
|
||||
|
||||
$this->view->title = $this->lang->block->createBlock;
|
||||
$this->view->dashboard = $dashboard;
|
||||
$this->view->block = $block;
|
||||
$this->view->modules = $this->blockZen->getAvailableModules($dashboard);
|
||||
$this->view->blocks = $this->blockZen->getAvailableBlocks($dashboard, $module);
|
||||
$this->view->params = $this->blockZen->getAvailableParams($dashboard, $module, $block);
|
||||
$this->display();
|
||||
}
|
||||
|
||||
@@ -376,7 +380,7 @@ class block extends control
|
||||
*/
|
||||
public function printBlock($id, $module = 'my')
|
||||
{
|
||||
$block = $this->block->getByID($id);
|
||||
$block = $this->block->getByID((int)$id);
|
||||
|
||||
if(empty($block)) return false;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
function getForm()
|
||||
function getForm(event)
|
||||
{
|
||||
const field = $(event.target).attr('id');
|
||||
const module = $('#module').val();
|
||||
const block = $('#block').val();
|
||||
const block = field == 'module' ? '' : $('#block').val();
|
||||
const url = $.createLink('block', 'create', 'dashboard='+ dashboard +'&module=' + module + '&block=' + (block ? block : ''));
|
||||
loadPage(url, '#blockRow, #paramsRow');
|
||||
}
|
||||
|
||||
@@ -338,12 +338,12 @@ class blockModel extends model
|
||||
*/
|
||||
public function update(object $formData): int|false
|
||||
{
|
||||
$this->dao->update(TABLE_BLOCK)->data($formData)->exec();
|
||||
$this->dao->update(TABLE_BLOCK)->data($formData)->autoCheck()->exec();
|
||||
if(dao::isError()) return false;
|
||||
|
||||
$this->loadModel('score')->create('block', 'set');
|
||||
|
||||
return $formData->id;
|
||||
return (int)$formData->id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@ class blockTao extends blockModel
|
||||
* Get block list of current user.
|
||||
* 获取当前用户的区块列表.
|
||||
*
|
||||
* @param string $module
|
||||
* @param string $module
|
||||
* @param int $hidden 0|1
|
||||
* @access protected
|
||||
* @return int[]|false
|
||||
@@ -43,13 +43,13 @@ class blockTao extends blockModel
|
||||
/**
|
||||
* Insert a block data.
|
||||
*
|
||||
* @param object $formData
|
||||
* @param object $formData
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
protected function insert($formData): bool
|
||||
{
|
||||
$this->dao->insert(TABLE_BLOCK)->data($formData)->exec();
|
||||
$this->dao->insert(TABLE_BLOCK)->data($formData)->autoCheck()->exec();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,22 @@ class blockTest
|
||||
{
|
||||
$blockID = $this->objectModel->create($block);
|
||||
|
||||
if(dao::isError()) a(dao::getError());
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
return $blockID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a block.
|
||||
*
|
||||
* @param object $block
|
||||
* @access public
|
||||
* @return int|false
|
||||
*/
|
||||
public function updateTest($block)
|
||||
{
|
||||
$blockID = $this->objectModel->update($block);
|
||||
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
return $blockID;
|
||||
|
||||
@@ -4,6 +4,18 @@ include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/block.class.php';
|
||||
su('admin');
|
||||
|
||||
function initData()
|
||||
{
|
||||
$config = zdTable('config');
|
||||
$config->id->range('1');
|
||||
$config->owner->range('system');
|
||||
$config->module->range('sso');
|
||||
$config->key->range('key');
|
||||
$config->value->range('858640a724c2c981983935eb2bbc4ad8');
|
||||
|
||||
$config->gen(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=测试 blockModel->checkAPI();
|
||||
@@ -14,7 +26,9 @@ pid=1
|
||||
测试正确的哈希值 >> 1
|
||||
测试错误的哈希值 >> 0
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
initData();
|
||||
|
||||
$block = new blockTest();
|
||||
|
||||
|
||||
Regular → Executable
+15
-29
@@ -8,12 +8,21 @@ 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->source->range('my');
|
||||
$block->block->range('bug');
|
||||
$block->order->range('5');
|
||||
|
||||
$block->gen(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=18:41:52 ERROR: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry &
|
||||
title=14:11:23 ERROR: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry &
|
||||
timeout=0
|
||||
cid=39
|
||||
|
||||
@@ -26,7 +35,8 @@ cid=39
|
||||
- 属性block @bug
|
||||
- 属性order @5
|
||||
|
||||
- 执行blockTest模块的create方法,参数是$accoutTooLangBlock @
|
||||
- 执行blockTest模块的create方法,参数是$accoutTooLangBlock,属性account @『所属用户』长度应当不超过『30』,且大于『0』。
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -45,41 +55,17 @@ $block->source = 'my';
|
||||
$block->block = 'bug';
|
||||
$block->order = '5';
|
||||
|
||||
$repeatBlock = new stdclass();
|
||||
$repeatBlock->account = 'admin';
|
||||
$repeatBlock->vision = 'rnd';
|
||||
$repeatBlock->module = 'my';
|
||||
$repeatBlock->title = '区块123';
|
||||
$repeatBlock->source = 'my';
|
||||
$repeatBlock->block = 'bug';
|
||||
$repeatBlock->order = '5';
|
||||
|
||||
$accoutTooLangBlock = new stdclass();
|
||||
$accoutTooLangBlock->account = 'adminadminadminadminadminadmin1';
|
||||
$accoutTooLangBlock->account = 'adminadminadminadminadminadminadminadmin1';
|
||||
$accoutTooLangBlock->vision = 'rnd';
|
||||
$accoutTooLangBlock->module = 'my';
|
||||
$accoutTooLangBlock->title = '区块123';
|
||||
$accoutTooLangBlock->title = 'long account';
|
||||
$accoutTooLangBlock->source = 'my';
|
||||
$accoutTooLangBlock->block = 'bug';
|
||||
$accoutTooLangBlock->order = '5';
|
||||
|
||||
$titleTooLangBlock = new stdclass();
|
||||
$titleTooLangBlock->account = 'admin';
|
||||
$titleTooLangBlock->vision = 'rnd';
|
||||
$titleTooLangBlock->module = 'my';
|
||||
$titleTooLangBlock->title = '5个字区块5个字区块5个字区块5个字区块5个字区块1';
|
||||
$titleTooLangBlock->source = 'my';
|
||||
$titleTooLangBlock->block = 'bug';
|
||||
$titleTooLangBlock->order = '5';
|
||||
$blockTest = new blockTest();
|
||||
|
||||
$newBlockID = $blockTest->createTest($block);
|
||||
|
||||
r($tester->block->getByID($newBlockID)) && p('account,vision,module,title,source,block,order') && e('admin,rnd,my,区块123,my,bug,5'); // 测试获取正常的block的内容
|
||||
r($blockTest->createTest($repeatBlock)) && p('') && e(''); // 测试联合主键不能重复
|
||||
r($blockTest->createTest($accoutTooLangBlock)) && p('') && e(''); // 测试account 字段字符超出长度
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
r($blockTest->createTest($accoutTooLangBlock)) && p('account:0') && e('『所属用户』长度应当不超过『30』,且大于『0』。'); // 测试account 字段字符超出长度
|
||||
|
||||
@@ -46,4 +46,4 @@ initData();
|
||||
|
||||
r($tester->block->getByID(2)) && p('account,vision,module,title,source,block,order') && e('admin,rnd,my,区块2,my,bug,5'); // 测试获取正常的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的内容
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env php
|
||||
<?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');
|
||||
$block->account->range('test');
|
||||
$block->vision->range('rnd');
|
||||
$block->module->range('test');
|
||||
$block->title->prefix('区块')->range('2');
|
||||
$block->source->range('my');
|
||||
$block->block->range('bug');
|
||||
$block->order->range('5');
|
||||
|
||||
$block->gen(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=14:11:23 ERROR: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry &
|
||||
timeout=0
|
||||
cid=39
|
||||
|
||||
- 执行block模块的getByID方法,参数是$newBlockID
|
||||
- 属性account @admin
|
||||
- 属性vision @rnd
|
||||
- 属性module @my
|
||||
- 属性title @区块123
|
||||
- 属性source @my
|
||||
- 属性block @bug
|
||||
- 属性order @5
|
||||
|
||||
- 执行blockTest模块的create方法,参数是$accoutTooLangBlock,属性account @『所属用户』长度应当不超过『30』,且大于『0』。
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('block');
|
||||
|
||||
initData();
|
||||
|
||||
$newBlock = new stdclass();
|
||||
$newBlock->id = '2';
|
||||
$newBlock->account = 'newadmin';
|
||||
$newBlock->vision = 'lite';
|
||||
$newBlock->module = 'newmy';
|
||||
$newBlock->title = 'new区块';
|
||||
$newBlock->source = 'newmy';
|
||||
$newBlock->params = '';
|
||||
$newBlock->block = 'newbug';
|
||||
$newBlock->order = '5';
|
||||
|
||||
$accoutTooLangBlock = new stdclass();
|
||||
$accoutTooLangBlock->id = '2';
|
||||
$accoutTooLangBlock->account = 'adminadminadminadminadminadminadminadmin1';
|
||||
$accoutTooLangBlock->vision = 'rnd';
|
||||
$accoutTooLangBlock->module = 'my';
|
||||
$accoutTooLangBlock->title = 'long account';
|
||||
$accoutTooLangBlock->source = 'my';
|
||||
$accoutTooLangBlock->block = 'bug';
|
||||
$accoutTooLangBlock->order = '5';
|
||||
|
||||
$blockTest = new blockTest();
|
||||
$newBlockID = $blockTest->updateTest($newBlock);
|
||||
|
||||
r($tester->block->getByID($newBlockID)) && p('account,vision,module,title,source,block,order') && e("{$newBlock->account},{$newBlock->vision},{$newBlock->module},{$newBlock->title},{$newBlock->source},{$newBlock->block},{$newBlock->order}"); // 测试获取正常的block的内容
|
||||
r($blockTest->updateTest($accoutTooLangBlock)) && p('account:0') && e('『所属用户』长度应当不超过『30』,且大于『0』。'); // 测试account 字段字符超出长度
|
||||
@@ -1,12 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The ui 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 liuruogu<liuruogu@easycorp.ltd>
|
||||
* @package block
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
set::title($title);
|
||||
jsVar('dashboard', $dashboard);
|
||||
|
||||
$paramsRows = array();
|
||||
$param = $params['type'];
|
||||
|
||||
foreach($params as $code => $row)
|
||||
{
|
||||
@@ -21,28 +29,6 @@ foreach($params as $code => $row)
|
||||
'items' => isset($row['options']) ? $row['options'] : null
|
||||
))
|
||||
);
|
||||
if($code == 'type')
|
||||
{
|
||||
$paramsRows[] = formGroup
|
||||
(
|
||||
set::label($lang->block->name),
|
||||
set::name('title'),
|
||||
set::class('form-row'),
|
||||
set::control('input')
|
||||
);
|
||||
|
||||
$paramsRows[] = formGroup
|
||||
(
|
||||
set::label($lang->block->grid),
|
||||
set::name("grid"),
|
||||
set::class('form-row'),
|
||||
set::control(array
|
||||
(
|
||||
'type' => 'select',
|
||||
'items' => $lang->block->gridOptions
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
form
|
||||
@@ -80,7 +66,27 @@ form
|
||||
(
|
||||
set::id('paramsRow'),
|
||||
set::class('form-grid'),
|
||||
$paramsRows
|
||||
$paramsRows,
|
||||
$block
|
||||
? formGroup
|
||||
(
|
||||
set::label($lang->block->name),
|
||||
set::name('title'),
|
||||
set::class('form-row'),
|
||||
set::control('input')
|
||||
) : null,
|
||||
$block
|
||||
? formGroup
|
||||
(
|
||||
set::label($lang->block->grid),
|
||||
set::name("grid"),
|
||||
set::class('form-row'),
|
||||
set::control(array
|
||||
(
|
||||
'type' => 'select',
|
||||
'items' => $lang->block->gridOptions
|
||||
))
|
||||
) : null,
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
+32
-60
@@ -2,40 +2,17 @@
|
||||
class blockZen extends block
|
||||
{
|
||||
/**
|
||||
* Build a form for create block page.
|
||||
* 构造新增区块页面的表单
|
||||
* Get module options when adding or editing blocks.
|
||||
* 添加或编辑区块时获取模块选项
|
||||
*
|
||||
* @param string $module
|
||||
* @param string $dashboard
|
||||
* @access protected
|
||||
* @return void
|
||||
* @return string[]
|
||||
*/
|
||||
protected function buildCreateForm(string $dashboard, string $module, string $block)
|
||||
protected function getAvailableModules(string $dashboard): array
|
||||
{
|
||||
$this->buildCreateAndEditForm($dashboard, $module, $block);
|
||||
$this->view->title = $this->lang->block->createBlock;
|
||||
}
|
||||
if($dashboard != 'my') return array();
|
||||
|
||||
protected function buildEditForm(int $blockID, string $dashboard)
|
||||
{
|
||||
$this->buildCreateAndEditForm($dashboard);
|
||||
$this->view->title = $this->lang->block->editBlock;
|
||||
$this->view->block = $this->block->getByID($blockID);
|
||||
}
|
||||
|
||||
private function buildCreateAndEditForm($dashboard, $module, $block)
|
||||
{
|
||||
if($dashboard == 'my')
|
||||
{
|
||||
return $this->buildCreateAndEditFormByTerritory($dashboard, $module, $block);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->buildCreateAndEditFormByModule($dashboard);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildCreateAndEditFormByTerritory($dashboard, $module, $block)
|
||||
{
|
||||
$modules = $this->lang->block->moduleList;
|
||||
unset($modules['doc']);
|
||||
|
||||
@@ -69,37 +46,21 @@ class blockZen extends block
|
||||
|
||||
$hiddenBlocks = $this->block->getMyHiddenBlocks('my');
|
||||
foreach($hiddenBlocks as $block) $modules['hiddenBlock' . $block->id] = $block->title;
|
||||
$this->view->modules = $modules;
|
||||
$this->view->blocks = $this->getAvailableBlocks($dashboard);
|
||||
$this->view->params = $this->getAvailableParams($dashboard, $module, $block);
|
||||
$this->view->dashboard = $dashboard;
|
||||
$this->view->module = '';
|
||||
|
||||
return $modules;
|
||||
}
|
||||
|
||||
private function buildCreateAndEditFormByModule($dashboard)
|
||||
/**
|
||||
* Get block options when adding or editing blocks.
|
||||
* 添加或编辑区块时获取区块选项
|
||||
*
|
||||
* @param string $dashboard
|
||||
* @param string $module
|
||||
* @access protected
|
||||
* @return string[]|true
|
||||
*/
|
||||
protected function getAvailableBlocks($dashboard, $module): array|bool
|
||||
{
|
||||
if($this->config->edition == 'max' and strpos($dashboard, 'Project') !== false)
|
||||
{
|
||||
if($dashboard == 'scrumProject')
|
||||
{
|
||||
if(!helper::hasFeature("scrum_issue")) unset($this->lang->block->modules['scrum']['index']->availableBlocks->scrumissue);
|
||||
if(!helper::hasFeature("scrum_risk")) unset($this->lang->block->modules['scrum']['index']->availableBlocks->scrumrisk);
|
||||
}
|
||||
if($dashboard == 'waterfallProject')
|
||||
{
|
||||
if(!helper::hasFeature("waterfall_issue")) unset($this->lang->block->modules['waterfall']['index']->availableBlocks->waterfallissue);
|
||||
if(!helper::hasFeature("waterfall_risk")) unset($this->lang->block->modules['waterfall']['index']->availableBlocks->waterfallrisk);
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->blocks = $this->getAvailableBlocks($dashboard);
|
||||
$this->view->dashboard = $dashboard;
|
||||
$this->view->module = $dashboard;
|
||||
}
|
||||
|
||||
private function getAvailableBlocks($dashboard)
|
||||
{
|
||||
$module = $this->get->module;
|
||||
$blocks = $this->block->getAvailableBlocks($dashboard, $module);
|
||||
|
||||
if(!$this->selfCall)
|
||||
@@ -111,13 +72,24 @@ class blockZen extends block
|
||||
return !empty($blocks) ? $blocks : array();
|
||||
}
|
||||
|
||||
private function getAvailableParams(string $dashboard, string $module = '', string $block = '') : array
|
||||
/**
|
||||
* Get other form items when adding or editing blocks
|
||||
* 添加或编辑区块时获取其他表单项
|
||||
*
|
||||
* @param string $dashboard
|
||||
* @param string $module
|
||||
* @param string $block
|
||||
* @access protected
|
||||
* @return array[]
|
||||
*/
|
||||
protected function getAvailableParams(string $dashboard, string $module = '', string $block = ''): array
|
||||
{
|
||||
if(!isset($this->lang->block->moduleList[$module])) return array();
|
||||
|
||||
if(!$block) return array();
|
||||
|
||||
$params = $this->block->getParams($module, $module);
|
||||
return !empty($params) ? json_decode($params, true) : array();
|
||||
$params = json_decode($this->block->getParams($block, $module), true);
|
||||
|
||||
return !empty($params) ? $params : array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,3 +388,5 @@ $config->bug->colorList->severity[4] = '#cddc39';
|
||||
$config->bug->colorList->severity[5] = '#8bc34a';
|
||||
$config->bug->colorList->severity[6] = '#B6B4B4';
|
||||
$config->bug->colorList->severity[7] = '#BDBEBD';
|
||||
|
||||
include 'config/form.php';
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
global $lang;
|
||||
|
||||
$config->bug->createform = array();
|
||||
$config->bug->createform['title'] = array('required' => true, 'type' => 'string', 'filter' => 'trim');
|
||||
$config->bug->createform['openedBuild'] = array('required' => true, 'type' => 'array', 'filter' => 'join');
|
||||
|
||||
$config->bug->createform['product'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['branch'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['module'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['project'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['execution'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['assignedTo'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->bug->createform['deadline'] = array('required' => false, 'type' => 'date', 'default' => '');
|
||||
$config->bug->createform['feedbackBy'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->bug->createform['notifyEmail'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->bug->createform['type'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
|
||||
$config->bug->createform['os'] = array('required' => false, 'type' => 'array', 'default' => array(''), 'filter' => 'join');
|
||||
$config->bug->createform['browser'] = array('required' => false, 'type' => 'array', 'default' => array(''), 'filter' => 'join');
|
||||
$config->bug->createform['color'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->bug->createform['severity'] = array('required' => false, 'type' => 'int', 'default' => 3);
|
||||
$config->bug->createform['pri'] = array('required' => false, 'type' => 'int', 'default' => 3);
|
||||
$config->bug->createform['steps'] = array('required' => false, 'type' => 'string', 'default' => $lang->bug->tplStep . $lang->bug->tplResult . $lang->bug->tplExpect);
|
||||
|
||||
$config->bug->createform['story'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['task'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['oldTaskID'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['case'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['caseVersion'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['result'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->createform['testtask'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
|
||||
$config->bug->createform['mailto'] = array('required' => false, 'type' => 'array', 'default' => array(''), 'filter' => 'join');
|
||||
$config->bug->createform['keywords'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->bug->createform['status'] = array('required' => false, 'type' => 'string', 'default' => 'active');
|
||||
$config->bug->createform['issueKey'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->bug->createform['uid'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
@@ -171,7 +171,7 @@ class bug extends control
|
||||
$productIDList = $productID ? $productID : array_keys($this->products);
|
||||
|
||||
/* Get bugs. */
|
||||
$bugs = $this->bug->getBugs($productIDList, $executions, $branch, $browseType, $moduleID, $queryID, $sort, $pager, $this->projectID);
|
||||
$bugs = $this->bug->getList($browseType, $productIDList, $this->projectID, array_keys($executions), $branch, $moduleID, $queryID, $sort, $pager);
|
||||
|
||||
/* Process the sql, get the conditon partion, save it to session. */
|
||||
$this->loadModel('common')->saveQueryCondition($this->bug->dao->get(), 'bug', $browseType == 'needconfirm' ? false : true);
|
||||
@@ -359,16 +359,18 @@ class bug extends control
|
||||
{
|
||||
$response['result'] = 'success';
|
||||
|
||||
$formData = form::data($this->config->bug->createform);
|
||||
$bug = $this->bugZen->beforeCreate($formData);
|
||||
$bugResult = $this->bugZen->doCreate($bug);
|
||||
|
||||
/* Set from param if there is a object to transfer bug. */
|
||||
setcookie('lastBugModule', (int)$this->post->module, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false);
|
||||
$bugResult = $this->bug->create('', $extras);
|
||||
if(!$bugResult or dao::isError())
|
||||
{
|
||||
$response['result'] = 'fail';
|
||||
$response['message'] = dao::getError();
|
||||
return $this->send($response);
|
||||
}
|
||||
|
||||
$bugID = $bugResult['id'];
|
||||
if($bugResult['status'] == 'exists')
|
||||
{
|
||||
@@ -378,6 +380,9 @@ class bug extends control
|
||||
return $this->send($response);
|
||||
}
|
||||
|
||||
$bug->id = $bugID;
|
||||
$this->bugZen->afterCreate($bug, $formData, $extras);
|
||||
|
||||
/* Record related action, for example FromSonarqube. */
|
||||
$createAction = $from == 'sonarqube' ? 'fromSonarqube' : 'Opened';
|
||||
$actionID = $this->action->create('bug', $bugID, $createAction);
|
||||
|
||||
+36
-481
@@ -47,47 +47,15 @@ class bugModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bug.
|
||||
* Insert bug into zt_bug.
|
||||
* bug的入库操作
|
||||
*
|
||||
* @param string $from object that is transfered to bug.
|
||||
* @param string $extras.
|
||||
* @param object $bug
|
||||
* @access public
|
||||
* @return array|bool
|
||||
* @return array|false
|
||||
*/
|
||||
public function create($from = '', $extras = '')
|
||||
public function create(object $bug): array|false
|
||||
{
|
||||
$extras = str_replace(array(',', ' '), array('&', ''), $extras);
|
||||
parse_str($extras, $output);
|
||||
|
||||
$now = helper::now();
|
||||
$bug = fixer::input('post')
|
||||
->setDefault('openedBy', $this->app->user->account)
|
||||
->setDefault('openedDate', $now)
|
||||
->setDefault('project,execution,story,task,duplicateBug,linkBug', 0)
|
||||
->setDefault('openedBuild', '')
|
||||
->setDefault('notifyEmail', '')
|
||||
->setDefault('deadline', '0000-00-00')
|
||||
->setIF($this->lang->navGroup->bug != 'qa', 'project', $this->session->project)
|
||||
->setIF(strpos($this->config->bug->create->requiredFields, 'deadline') !== false, 'deadline', $this->post->deadline)
|
||||
->setIF($this->post->assignedTo != '', 'assignedDate', $now)
|
||||
->setIF($this->post->story != false, 'storyVersion', $this->loadModel('story')->getVersion($this->post->story))
|
||||
->setIF(strpos($this->config->bug->create->requiredFields, 'execution') !== false, 'execution', $this->post->execution)
|
||||
->stripTags($this->config->bug->editor->create['id'], $this->config->allowedTags)
|
||||
->cleanInt('product,execution,module,severity')
|
||||
->trim('title')
|
||||
->join('openedBuild', ',')
|
||||
->join('mailto', ',')
|
||||
->join('os', ',')
|
||||
->join('browser', ',')
|
||||
->remove('files,labels,uid,oldTaskID,contactListMenu,region,lane,ticket,deleteFiles,resultFiles')
|
||||
->get();
|
||||
|
||||
/* Check repeat bug. */
|
||||
$result = $this->loadModel('common')->removeDuplicate('bug', $bug, "product={$bug->product}");
|
||||
if($result and $result['stop']) return array('status' => 'exists', 'id' => $result['duplicate']);
|
||||
|
||||
$bug = $this->loadModel('file')->processImgURL($bug, $this->config->bug->editor->create['id'], $this->post->uid);
|
||||
|
||||
$this->dao->insert(TABLE_BUG)->data($bug)
|
||||
->autoCheck()
|
||||
->checkIF($bug->notifyEmail, 'notifyEmail', 'email')
|
||||
@@ -98,45 +66,6 @@ class bugModel extends model
|
||||
if(!dao::isError())
|
||||
{
|
||||
$bugID = $this->dao->lastInsertID();
|
||||
|
||||
if(isset($_POST['resultFiles']))
|
||||
{
|
||||
$resultFiles = $_POST['resultFiles'];
|
||||
if(isset($_POST['deleteFiles']))
|
||||
{
|
||||
foreach($_POST['deleteFiles'] as $deletedCaseFileID) $resultFiles = trim(str_replace(",$deletedCaseFileID,", ',', ",$resultFiles,"), ',');
|
||||
}
|
||||
$files = $this->dao->select('*')->from(TABLE_FILE)->where('id')->in($resultFiles)->fetchAll('id');
|
||||
foreach($files as $file)
|
||||
{
|
||||
unset($file->id);
|
||||
$file->objectType = 'bug';
|
||||
$file->objectID = $bugID;
|
||||
$this->dao->insert(TABLE_FILE)->data($file)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
$this->file->updateObjectID($this->post->uid, $bugID, 'bug');
|
||||
$this->file->saveUpload('bug', $bugID);
|
||||
empty($bug->case) ? $this->loadModel('score')->create('bug', 'create', $bugID) : $this->loadModel('score')->create('bug', 'createFormCase', $bug->case);
|
||||
|
||||
if($bug->execution)
|
||||
{
|
||||
$this->loadModel('kanban');
|
||||
|
||||
$laneID = isset($output['laneID']) ? $output['laneID'] : 0;
|
||||
if(!empty($_POST['lane'])) $laneID = $_POST['lane'];
|
||||
|
||||
$columnID = $this->kanban->getColumnIDByLaneID($laneID, 'unconfirmed');
|
||||
if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
|
||||
|
||||
if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($bug->execution, $laneID, $columnID, 'bug', $bugID);
|
||||
if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($bug->execution, 'bug');
|
||||
}
|
||||
|
||||
/* Callback the callable method to process the related data for object that is transfered to bug. */
|
||||
if($from && is_callable(array($this, $this->config->bug->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->bug->fromObjects[$from]['callback']), $bugID);
|
||||
|
||||
return array('status' => 'created', 'id' => $bugID);
|
||||
}
|
||||
return false;
|
||||
@@ -380,51 +309,49 @@ class bugModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs.
|
||||
* Get bug list by browse type.
|
||||
* 根据浏览类型获取bug列表。
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param array $executions
|
||||
* @param int|string $branch
|
||||
* @param string $browseType
|
||||
* @param int|array $productIdList
|
||||
* @param int $projectID
|
||||
* @param int[] $executionIdList
|
||||
* @param int|string $branch
|
||||
* @param int $moduleID
|
||||
* @param int $queryID
|
||||
* @param string $sort
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getBugs($productIDList, $executions, $branch, $browseType, $moduleID, $queryID, $sort, $pager, $projectID)
|
||||
public function getList(string $browseType, int|array $productIdList, int $projectID, array $executionIdList, int|string $branch = 'all', int $moduleID = 0, int $queryID = 0, string $orderBy = 'id_desc', object $pager = null): array
|
||||
{
|
||||
/* Set modules and browse type. */
|
||||
$modules = $moduleID ? $this->loadModel('tree')->getAllChildId($moduleID) : '0';
|
||||
$modules = $moduleID ? $this->loadModel('tree')->getAllChildId($moduleID) : 0;
|
||||
$browseType = ($browseType == 'bymodule' and $this->session->bugBrowseType and $this->session->bugBrowseType != 'bysearch') ? $this->session->bugBrowseType : $browseType;
|
||||
$browseType = $browseType == 'bybranch' ? 'bymodule' : $browseType;
|
||||
|
||||
if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
|
||||
if(strpos($sort, 'severity_') !== false) $sort = str_replace('severity_', 'severityOrder_', $sort);
|
||||
/* Set orderBy. */
|
||||
if(strpos($orderBy, 'pri_') !== false) $orderBy = str_replace('pri_', 'priOrder_', $orderBy);
|
||||
if(strpos($orderBy, 'severity_') !== false) $orderBy = str_replace('severity_', 'severityOrder_', $orderBy);
|
||||
|
||||
/* Get bugs by browse type. */
|
||||
$bugs = array();
|
||||
if($browseType == 'all') $bugs = $this->getAllBugs($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'bymodule') $bugs = $this->getModuleBugs($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'assigntome') $bugs = $this->getByAssigntome($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'openedbyme') $bugs = $this->getByOpenedbyme($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'resolvedbyme') $bugs = $this->getByResolvedbyme($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'assigntonull') $bugs = $this->getByAssigntonull($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'unconfirmed') $bugs = $this->getUnconfirmed($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'unresolved') $bugs = $this->getByStatus($productIDList, $branch, $modules, $executions, 'unresolved', $sort, $pager, $projectID);
|
||||
elseif($browseType == 'unclosed') $bugs = $this->getByStatus($productIDList, $branch, $modules, $executions, 'unclosed', $sort, $pager, $projectID);
|
||||
elseif($browseType == 'toclosed') $bugs = $this->getByStatus($productIDList, $branch, $modules, $executions, 'toclosed', $sort, $pager, $projectID);
|
||||
elseif($browseType == 'longlifebugs') $bugs = $this->getByLonglifebugs($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'postponedbugs') $bugs = $this->getByPostponedbugs($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'needconfirm') $bugs = $this->getByNeedconfirm($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'bysearch') $bugs = $this->getBySearch($productIDList, $branch, $queryID, $sort, '', $pager, $projectID);
|
||||
elseif($browseType == 'overduebugs') $bugs = $this->getOverdueBugs($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'assignedbyme') $bugs = $this->getByAssignedbyme($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
elseif($browseType == 'review') $bugs = $this->getReviewBugs($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
|
||||
$bugList = array();
|
||||
if($browseType == 'all')
|
||||
{
|
||||
$bugList = $this->bugTao->getAllBugs($productIdList, $projectID, $executionIdList, $branch, $modules, $orderBy, $pager);
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug');
|
||||
}
|
||||
elseif($browseType == 'review')
|
||||
{
|
||||
$bugList = $this->bugTao->getListByReviewToMe($productIdList, $projectID, $executionIdList, $branch, $modules, $orderBy, $pager);
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug');
|
||||
}
|
||||
elseif($browseType == 'needconfirm') $bugList = $this->bugTao->getListByNeedconfirm($productIdList, $projectID, $executionIdList, $branch, $modules, $orderBy, $pager);
|
||||
elseif($browseType == 'bysearch') $bugList = $this->getBySearch($productIdList, $branch, $queryID, $orderBy, '', $pager, $projectID);
|
||||
elseif(strpos(',bymodule,assigntome,openedbyme,resolvedbyme,assigntonull,unconfirmed,unresolved,unclosed,toclosed,longlifebugs,postponedbugs,overduebugs,assignedbyme,', ",$browseType,") !== false) $bugList = $this->bugTao->getListByBrowseType($browseType, $productIdList, $projectID, $executionIdList, $branch, $modules, $orderBy, $pager);
|
||||
|
||||
return $this->bugTao->checkDelayedBugs($bugs);
|
||||
return $this->bugTao->batchAppendDelayedDays($bugList);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,32 +374,6 @@ class bugModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs of a module.
|
||||
*
|
||||
* @param int|array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param string|array $moduleIdList
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getModuleBugs($productIDList, $branch = 0, $moduleIdList = 0, $executions = array(), $orderBy = 'id_desc', $pager = null, $projectID = 0)
|
||||
{
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('product')->in($productIDList)
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->eq($branch)->fi()
|
||||
->beginIF(!empty($moduleIdList))->andWhere('module')->in($moduleIdList)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bug list of a plan.
|
||||
*
|
||||
@@ -504,9 +405,9 @@ class bugModel extends model
|
||||
* @param int $bugID
|
||||
* @param bool $setImgSize
|
||||
* @access public
|
||||
* @return object
|
||||
* @return object|false
|
||||
*/
|
||||
public function getById(int $bugID, bool $setImgSize = false): object|false
|
||||
public function getByID(int $bugID, bool $setImgSize = false): object|false
|
||||
{
|
||||
$bug = $this->bugTao->fetchBugInfo($bugID);
|
||||
if(!$bug) return false;
|
||||
@@ -527,7 +428,7 @@ class bugModel extends model
|
||||
$bug->linkMRTitles = $this->mr->getLinkedMRPairs($bugID, 'bug');
|
||||
$bug->toCases = $this->bugTao->getCasesFromBug($bugID);
|
||||
$bug->files = $this->file->getByObject('bug', $bugID);
|
||||
return $this->bugTao->checkDelayBug($bug);
|
||||
return $this->bugTao->appendDelayedDays($bug);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2590,352 +2491,6 @@ class bugModel extends model
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bugs.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAllBugs($productIDList, $branch, $modules, $executions, $orderBy, $pager = null, $projectID = 0)
|
||||
{
|
||||
$bugs = $this->dao->select("t1.*, t2.title as planTitle, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder, IF(t1.`severity` = 0, {$this->config->maxPriValue}, t1.`severity`) as severityOrder")->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCTPLAN)->alias('t2')->on('t1.plan = t2.id')
|
||||
->where('t1.product')->in($productIDList)
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('t1.execution')->in(array_keys($executions))->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi()
|
||||
->beginIF($modules)->andWhere('t1.module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug');
|
||||
|
||||
return $bugs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs of assign to me.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByAssigntome($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('assignedTo')->eq($this->app->user->account)
|
||||
->andWhere('product')->in($productIDList)
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs of opened by me.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByOpenedbyme($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
return $this->dao->select("*,IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('openedBy')->eq($this->app->user->account)
|
||||
->andWhere('product')->in($productIDList)
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs of resolved by me.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByResolvedbyme($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
return $this->dao->select("*,IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('resolvedBy')->eq($this->app->user->account)
|
||||
->andWhere('product')->in($productIDList)
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs of nobody to do.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByAssigntonull($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('assignedTo')->eq('')
|
||||
->andWhere('product')->in($productIDList)
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unconfirmed bugs.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getUnconfirmed($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('product')->in($productIDList)
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('confirmed')->eq(0)
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs the overdueBugs is active or unclosed.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $status
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getOverdueBugs($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('product')->in($productIDList)
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->andWhere('status')->eq('active')
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('deadline')->ne('0000-00-00')
|
||||
->andWhere('deadline')->lt(helper::today())
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs the status is active or unclosed.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $status
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByStatus($productIDList, $branch, $modules, $executions, $status, $orderBy, $pager, $projectID)
|
||||
{
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('product')->in($productIDList)
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($status == 'unclosed')->andWhere('status')->ne('closed')->fi()
|
||||
->beginIF($status == 'unresolved')->andWhere('status')->eq('active')->fi()
|
||||
->beginIF($status == 'toclosed')->andWhere('status')->eq('resolved')->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)
|
||||
->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unclosed bugs for long time.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByLonglifebugs($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
$lastEditedDate = date(DT_DATE1, time() - $this->config->bug->longlife * 24 * 3600);
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('lastEditedDate')->lt($lastEditedDate)
|
||||
->andWhere('product')->in($productIDList)
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->andWhere('openedDate')->lt($lastEditedDate)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->andWhere('status')->ne('closed')->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get postponed bugs.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|sting $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByPostponedbugs($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('resolution')->eq('postponed')
|
||||
->andWhere('product')->in($productIDList)
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bugs need confirm.
|
||||
*
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByNeedconfirm($productIDList, $branch, $modules, $executions, $orderBy, $pager, $projectID)
|
||||
{
|
||||
return $this->dao->select("t1.*, t2.title AS storyTitle, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder, IF(t1.`severity` = 0, {$this->config->maxPriValue}, t1.`severity`) as severityOrder")->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
|
||||
->where("t2.status = 'active'")
|
||||
->andWhere('t1.product')->in($productIDList)
|
||||
->beginIF($branch !== 'all')->andWhere('t1.branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('t1.module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('t1.execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('t2.version > t1.storyVersion')
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get by assigned by me.
|
||||
* @param array $productIDList
|
||||
* @param int|string $branch
|
||||
* @param array $modules
|
||||
* @param array $executions
|
||||
* @param string $sort
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getByAssignedbyme($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID)
|
||||
{
|
||||
$actionIDList = $this->dao->select('objectID')->from(TABLE_ACTION)->where('objectType')->eq('bug')->andWhere('action')->eq('assigned')->andWhere('actor')->eq($this->app->user->account)->fetchPairs('objectID', 'objectID');
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('product')->in($productIDList)
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($modules)->andWhere('module')->in($modules)->fi()
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('status')->ne('closed')
|
||||
->andWhere('id')->in($actionIDList)
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($sort)
|
||||
->page($pager)
|
||||
->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get by Sonarqube id.
|
||||
*
|
||||
@@ -3554,7 +3109,7 @@ class bugModel extends model
|
||||
public function getToAndCcList($bug)
|
||||
{
|
||||
/* Set toList and ccList. */
|
||||
$toList = $bug->assignedTo;
|
||||
$toList = $bug->assignedTo ? $bug->assignedTo : '';
|
||||
$ccList = trim($bug->mailto, ',');
|
||||
if(empty($toList))
|
||||
{
|
||||
|
||||
+192
-27
@@ -7,7 +7,7 @@ class bugTao extends bugModel
|
||||
* 获取bug的详情,包含bug表的所有内容、所属执行名称、关联需求名称、关联需求状态、关联需求版本、关联任务名称、关联计划名称
|
||||
*
|
||||
* @param int $bugID
|
||||
* @access public
|
||||
* @access protected
|
||||
* @return object|false
|
||||
*/
|
||||
protected function fetchBugInfo(int $bugID): object|false
|
||||
@@ -21,12 +21,178 @@ class bugTao extends bugModel
|
||||
->where('t1.id')->eq((int)$bugID)->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bugs.
|
||||
* 获取所有的bug。
|
||||
*
|
||||
* @param int|array $productIdList
|
||||
* @param int|string $branch
|
||||
* @param int|array $moduleIdList
|
||||
* @param int[] $executionIdList
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getAllBugs(int|array $productIdList, int $projectID, array $executionIdList, int|string $branch, int|array $moduleIdList, string $orderBy, object $pager = null): array
|
||||
{
|
||||
return $this->dao->select("t1.*, t2.title as planTitle, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder, IF(t1.`severity` = 0, {$this->config->maxPriValue}, t1.`severity`) as severityOrder")->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCTPLAN)->alias('t2')->on('t1.plan = t2.id')
|
||||
->where('t1.deleted')->eq('0')
|
||||
->andWhere('t1.product')->in($productIdList)
|
||||
->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('t1.execution')->in($executionIdList)->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi()
|
||||
->beginIF($moduleIdList)->andWhere('t1.module')->in($moduleIdList)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bug list by browse type.
|
||||
* 通过浏览类型获取bug列表。
|
||||
*
|
||||
* @param string $browseType
|
||||
* @param int|array $productIdList
|
||||
* @param int|string $branch
|
||||
* @param int|array $moduleIdList
|
||||
* @param int[] $executionIdList
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param int $projectID
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getListByBrowseType(string $browseType, int|array $productIdList, int $projectID, array $executionIdList, int|string $branch, int|array $moduleIdList, string $orderBy, object $pager = null): array
|
||||
{
|
||||
$browseType = strtolower($browseType);
|
||||
$lastEditedDate = '';
|
||||
$bugIdListAssignedByMe = array();
|
||||
|
||||
if($browseType == 'longlifebugs') $lastEditedDate = date(DT_DATE1, time() - $this->config->bug->longlife * 24 * 3600);
|
||||
if($browseType == 'assignedbyme')
|
||||
{
|
||||
$bugIdListAssignedByMe = $this->dao->select('objectID')->from(TABLE_ACTION)
|
||||
->where('objectType')->eq('bug')
|
||||
->andWhere('action')->eq('assigned')
|
||||
->andWhere('actor')->eq($this->app->user->account)
|
||||
->fetchPairs('objectID', 'objectID');
|
||||
}
|
||||
|
||||
$bugList = $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)
|
||||
->where('deleted')->eq('0')
|
||||
->andWhere('product')->in($productIdList)
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('execution')->in($executionIdList)->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
|
||||
->beginIF($moduleIdList)->andWhere('module')->in($moduleIdList)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
|
||||
->beginIF($browseType == 'assigntome')->andWhere('assignedTo')->eq($this->app->user->account)->fi()
|
||||
->beginIF($browseType == 'openedbyme')->andWhere('openedBy')->eq($this->app->user->account)->fi()
|
||||
->beginIF($browseType == 'resolvedbyme')->andWhere('resolvedBy')->eq($this->app->user->account)->fi()
|
||||
->beginIF($browseType == 'assigntonull')->andWhere('assignedTo')->eq('')->fi()
|
||||
->beginIF($browseType == 'unconfirmed')->andWhere('confirmed')->eq(0)->fi()
|
||||
->beginIF($browseType == 'unclosed')->andWhere('status')->ne('closed')->fi()
|
||||
->beginIF($browseType == 'unresolved')->andWhere('status')->eq('active')->fi()
|
||||
->beginIF($browseType == 'toclosed')->andWhere('status')->eq('resolved')->fi()
|
||||
->beginIF($browseType == 'postponedbugs')->andWhere('resolution')->eq('postponed')->fi()
|
||||
|
||||
->beginIF($browseType == 'longlifebugs')
|
||||
->andWhere('lastEditedDate')->lt($lastEditedDate)
|
||||
->andWhere('openedDate')->lt($lastEditedDate)
|
||||
->andWhere('status')->ne('closed')
|
||||
->fi()
|
||||
|
||||
->beginIF($browseType == 'overduebugs')
|
||||
->andWhere('status')->eq('active')
|
||||
->andWhere('deadline')->lt(helper::today())
|
||||
->fi()
|
||||
|
||||
->beginIF($browseType == 'assignedbyme')
|
||||
->andWhere('status')->ne('closed')
|
||||
->andWhere('id')->in($bugIdListAssignedByMe)
|
||||
->fi()
|
||||
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
|
||||
return $bugList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bug list of story need confirm.
|
||||
* 获取需要确认需求变动的bug列表。
|
||||
*
|
||||
* @param int|array $productIdList
|
||||
* @param int $projectID
|
||||
* @param int[] $executionIdList
|
||||
* @param int|string $branch
|
||||
* @param int|array $moduleIdList
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getListByNeedconfirm(int|array $productIdList, int $projectID, array $executionIdList, int|string $branch, int|array $moduleIdList, string $orderBy, object $pager = null): array
|
||||
{
|
||||
return $this->dao->select("t1.*, t2.title AS storyTitle, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder, IF(t1.`severity` = 0, {$this->config->maxPriValue}, t1.`severity`) as severityOrder")->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
|
||||
->where('t1.deleted')->eq('0')
|
||||
->andWhere("t2.status = 'active'")
|
||||
->andWhere('t2.version > t1.storyVersion')
|
||||
->andWhere('t1.product')->in($productIdList)
|
||||
->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('t1.execution')->in($executionIdList)->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('t1.branch')->in($branch)->fi()
|
||||
->beginIF($moduleIdList)->andWhere('t1.module')->in($moduleIdList)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bug list to review.
|
||||
* 获取待我审批的bug列表。
|
||||
*
|
||||
* @param int|array $productIdList
|
||||
* @param int $projectID
|
||||
* @param int[] $executionIdList
|
||||
* @param int|string $branch
|
||||
* @param int|array $moduleIdList
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
protected function getListByReviewToMe(int|array $productIdList, int $projectID, array $executionIdList, int|string $branch, int|array $moduleIdList, string $orderBy, object $pager = null): array
|
||||
{
|
||||
return $this->dao->select("t1.*, t2.title as planTitle, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder")->from(TABLE_BUG)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCTPLAN)->alias('t2')->on('t1.plan = t2.id')
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('t1.product')->in($productIdList)
|
||||
->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
|
||||
->beginIF($this->app->tab !== 'qa')->andWhere('t1.execution')->in($executionIdList)->fi()
|
||||
->beginIF($branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi()
|
||||
->beginIF($moduleIdList)->andWhere('t1.module')->in($moduleIdList)->fi()
|
||||
->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.reviewers)")
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in('0,' . $this->app->user->view->projects)->fi()
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cases created by bug.
|
||||
* 获取bug建的用例.
|
||||
* 获取bug建的用例。
|
||||
*
|
||||
* @param int $bugID
|
||||
* @access public
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getCasesFromBug(int $bugID): array
|
||||
@@ -36,10 +202,10 @@ class bugTao extends bugModel
|
||||
|
||||
/**
|
||||
* Get an array of id and title pairs by buglist.
|
||||
* 传入一个buglist,获得bug的id和title键值对数组.
|
||||
* 传入一个buglist,获得bug的id和title键值对数组。
|
||||
*
|
||||
* @param int $bugList
|
||||
* @access public
|
||||
* @param string|array $bugList
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getBugPairsByList(string|array $bugList): array
|
||||
@@ -49,12 +215,12 @@ class bugTao extends bugModel
|
||||
|
||||
/**
|
||||
* Get object title/name base on the params.
|
||||
* 根据传入的参数,获取对象名称.
|
||||
* 根据传入的参数,获取对象名称。
|
||||
*
|
||||
* @param int $objectID
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @access public
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function getNameFromTable(int $objectID, string $table, string $field): string
|
||||
@@ -67,40 +233,39 @@ class bugTao extends bugModel
|
||||
* 循环调用checkDelayBug,检查bug是否延期
|
||||
*
|
||||
* @param array $bugs
|
||||
* @access public
|
||||
* @return array
|
||||
* @access protected
|
||||
* @return object[]
|
||||
*/
|
||||
protected function checkDelayedBugs(array $bugs): array
|
||||
protected function batchAppendDelayedDays(array $bugs): array
|
||||
{
|
||||
foreach ($bugs as $bug) $bug = $this->checkDelayBug($bug);
|
||||
foreach($bugs as $bug) $this->appendDelayedDays($bug);
|
||||
|
||||
return $bugs;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the bug is delayed, add the bug->delay field to show the delay time (day).
|
||||
* 如果bug延期,添加bug->delay字段,内容为延期的时长(天)
|
||||
* 添加bug->delay字段,内容为延期的时长(天),不延期则为0
|
||||
*
|
||||
* @param object $bug
|
||||
* @access public
|
||||
* @access protected
|
||||
* @return object
|
||||
*/
|
||||
protected function checkDelayBug(object $bug): object
|
||||
protected function appendDelayedDays(object $bug): object
|
||||
{
|
||||
/* Delayed or not? */
|
||||
if(!helper::isZeroDate($bug->deadline))
|
||||
{
|
||||
if($bug->resolvedDate and !helper::isZeroDate($bug->resolvedDate))
|
||||
{
|
||||
$delay = helper::diffDate(substr($bug->resolvedDate, 0, 10), $bug->deadline);
|
||||
}
|
||||
elseif($bug->status == 'active')
|
||||
{
|
||||
$delay = helper::diffDate(helper::today(), $bug->deadline);
|
||||
}
|
||||
if(helper::isZeroDate($bug->deadline)) return $bug;
|
||||
|
||||
if(isset($delay) and $delay > 0) $bug->delay = $delay;
|
||||
$delay = 0;
|
||||
if($bug->resolvedDate and !helper::isZeroDate($bug->resolvedDate))
|
||||
{
|
||||
$delay = helper::diffDate(substr($bug->resolvedDate, 0, 10), $bug->deadline);
|
||||
}
|
||||
elseif($bug->status == 'active')
|
||||
{
|
||||
$delay = helper::diffDate(helper::today(), $bug->deadline);
|
||||
}
|
||||
|
||||
if($delay > 0) $bug->delay = $delay;
|
||||
|
||||
return $bug;
|
||||
}
|
||||
|
||||
@@ -133,42 +133,6 @@ class bugTest
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get bugs.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getBugsTest($product, $branch, $browseType, $module)
|
||||
{
|
||||
global $tester;
|
||||
|
||||
/* Load pager. */
|
||||
$tester->app->loadClass('pager', $static = true);
|
||||
$pager = new pager(0, 20 ,1);
|
||||
|
||||
$projectID = 0;
|
||||
$sort = 'id_desc';
|
||||
$queryID = 0;
|
||||
$executions = $tester->loadModel('execution')->getPairs($projectID, 'all', 'empty|withdelete');
|
||||
|
||||
$bugs = $this->objectModel->getBugs($product, $executions, $branch, $browseType, $module, $queryID, $sort, $pager, $projectID);
|
||||
|
||||
$title = '';
|
||||
foreach($bugs as $bug) $title .= ',' . $bug->title;
|
||||
$title = trim($title, ',');
|
||||
$title = str_replace("'", '', $title);
|
||||
|
||||
if(dao::isError())
|
||||
{
|
||||
return dao::getError();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test check delay bug.
|
||||
*
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/bug.class.php';
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=bugModel->getBugs();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
查询正常产品 没有分支 查看全部 没有模块的全部bug的标题拼接1 >> BUG3,BUG2,BUG1
|
||||
查询正常产品 不存在的分支主干 查看全部 没有模块的全部bug的标题拼接 >> BUG3,BUG2,BUG1
|
||||
查询正常产品 没有分支 查看未关闭 没有模块的全部bug的标题拼接 >> BUG3,BUG2,BUG1
|
||||
查询正常产品 没有分支 查看全部 模块1821的全部bug的标题拼接 >> BUG1
|
||||
查询正常产品 没有分支 查看全部 模块不存在的全部bug的标题拼接 >> BUG3,BUG2,BUG1
|
||||
查询多分支产品 没有分支 查看全部 没有模块的全部bug的标题拼接59 >> BUG177,bug176,缺陷!@()(){}|+=%^&*$#测试bug名称到底可以有多长!@#¥%&*":.<>。?/();175
|
||||
查询多分支产品 主干 查看全部 没有模块的全部bug的标题拼接 >> BUG177,bug176,缺陷!@()(){}|+=%^&*$#测试bug名称到底可以有多长!@#¥%&*":.<>。?/();175
|
||||
查询多分支产品 分支37 查看全部 没有模块的全部bug的标题拼接 >> 0
|
||||
查询多分支产品 没有分支 查看未关闭 没有模块的全部bug的标题拼接 >> BUG177,bug176,缺陷!@()(){}|+=%^&*$#测试bug名称到底可以有多长!@#¥%&*":.<>。?/();175
|
||||
查询多分支产品 没有分支 查看全部 模块2053的全部bug的标题拼接 >> 0
|
||||
查询不存在的产品的全部bug的标题拼接 >> 0
|
||||
|
||||
*/
|
||||
|
||||
$productIDList = array('1', '59', '1000001');
|
||||
$branchList = array('0', 'trunk', '37', '1000001');
|
||||
$browseTypeList = array('all', 'unclosed');
|
||||
$moduleIDList = array('0', '1821', '2053', '1000001');
|
||||
|
||||
$bug=new bugTest();
|
||||
|
||||
r($bug->getBugsTest($productIDList[0], $branchList[0], $browseTypeList[0], $moduleIDList[0])) && p('title') && e('BUG3,BUG2,BUG1'); // 查询正常产品 没有分支 查看全部 没有模块的全部bug的标题拼接1
|
||||
r($bug->getBugsTest($productIDList[0], $branchList[1], $browseTypeList[0], $moduleIDList[0])) && p('title') && e('BUG3,BUG2,BUG1'); // 查询正常产品 不存在的分支主干 查看全部 没有模块的全部bug的标题拼接
|
||||
r($bug->getBugsTest($productIDList[0], $branchList[0], $browseTypeList[1], $moduleIDList[0])) && p('title') && e('BUG3,BUG2,BUG1'); // 查询正常产品 没有分支 查看未关闭 没有模块的全部bug的标题拼接
|
||||
r($bug->getBugsTest($productIDList[0], $branchList[0], $browseTypeList[0], $moduleIDList[1])) && p('title') && e('BUG1'); // 查询正常产品 没有分支 查看全部 模块1821的全部bug的标题拼接
|
||||
r($bug->getBugsTest($productIDList[0], $branchList[0], $browseTypeList[0], $moduleIDList[3])) && p('title') && e('BUG3,BUG2,BUG1'); // 查询正常产品 没有分支 查看全部 模块不存在的全部bug的标题拼接
|
||||
r($bug->getBugsTest($productIDList[1], $branchList[0], $browseTypeList[0], $moduleIDList[0])) && p('title') && e('BUG177,bug176,缺陷!@()(){}|+=%^&*$#测试bug名称到底可以有多长!@#¥%&*":.<>。?/();175'); // 查询多分支产品 没有分支 查看全部 没有模块的全部bug的标题拼接59
|
||||
r($bug->getBugsTest($productIDList[1], $branchList[1], $browseTypeList[0], $moduleIDList[0])) && p('title') && e('BUG177,bug176,缺陷!@()(){}|+=%^&*$#测试bug名称到底可以有多长!@#¥%&*":.<>。?/();175'); // 查询多分支产品 主干 查看全部 没有模块的全部bug的标题拼接
|
||||
r($bug->getBugsTest($productIDList[1], $branchList[2], $browseTypeList[0], $moduleIDList[0])) && p('title') && e('0'); // 查询多分支产品 分支37 查看全部 没有模块的全部bug的标题拼接
|
||||
r($bug->getBugsTest($productIDList[1], $branchList[0], $browseTypeList[1], $moduleIDList[0])) && p('title') && e('BUG177,bug176,缺陷!@()(){}|+=%^&*$#测试bug名称到底可以有多长!@#¥%&*":.<>。?/();175'); // 查询多分支产品 没有分支 查看未关闭 没有模块的全部bug的标题拼接
|
||||
r($bug->getBugsTest($productIDList[1], $branchList[0], $browseTypeList[0], $moduleIDList[2])) && p('title') && e('0'); // 查询多分支产品 没有分支 查看全部 模块2053的全部bug的标题拼接
|
||||
r($bug->getBugsTest($productIDList[2], $branchList[0], $browseTypeList[0], $moduleIDList[0])) && p('title') && e('0'); // 查询不存在的产品的全部bug的标题拼接
|
||||
@@ -28,8 +28,15 @@ cid=1
|
||||
- 属性pri @1
|
||||
- 属性type @codeerror
|
||||
|
||||
- 执行bug模块的getByID方法,参数是1,属性title @0
|
||||
- 执行bug模块的getByID方法,参数是3
|
||||
- 属性title @bug3
|
||||
- 属性status @active
|
||||
|
||||
- 执行bug模块的getByID方法,参数是4
|
||||
- 属性severity @3
|
||||
- 属性openedBuild @trunk
|
||||
|
||||
- 执行bug模块的getByID方法,参数是1,属性title @0
|
||||
|
||||
*/
|
||||
|
||||
@@ -38,6 +45,7 @@ $tester->loadModel('bug');
|
||||
|
||||
initData();
|
||||
|
||||
r($tester->bug->getByID(2)) && p('pri,type') && e('1,codeerror'); //获取ID等于2的bug
|
||||
r($tester->bug->getByID(1)) && p('title') && e('0'); //获取不存在的bug
|
||||
|
||||
r($tester->bug->getByID(2)) && p('pri,type') && e('1,codeerror'); //获取ID等于2的bug
|
||||
r($tester->bug->getByID(3)) && p('title,status') && e('bug3,active'); //获取ID等于3的bug
|
||||
r($tester->bug->getByID(4)) && p('severity,openedBuild') && e('3,trunk'); //获取ID等于4的bug
|
||||
r($tester->bug->getByID(1)) && p('title') && e('0'); //获取不存在的bug
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/bug.class.php';
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=bugModel->getList();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
*/
|
||||
|
||||
function initData()
|
||||
{
|
||||
$bug = zdTable('bug');
|
||||
$bug->id->range('1-10');
|
||||
$bug->product->range('1,2');
|
||||
$bug->branch->range('0,1');
|
||||
$bug->project->range('0,2');
|
||||
$bug->execution->range('0,3');
|
||||
$bug->module->range('1,0');
|
||||
$bug->status->range("resolved,active,closed");
|
||||
$bug->title->prefix("BUG")->range('1-10');
|
||||
$bug->plan->range('1,0');
|
||||
$bug->assignedTo->range('admin');
|
||||
$bug->openedBy->range('admin');
|
||||
$bug->resolvedBy->range('admin');
|
||||
$bug->confirmed->range('0,1');
|
||||
$bug->resolution->range('postponed,fixed');
|
||||
$bug->gen(10);
|
||||
|
||||
$productplan = zdTable('productplan');
|
||||
$productplan->id->range('1');
|
||||
$productplan->product->range('1');
|
||||
$productplan->title->range('计划1');
|
||||
$productplan->gen(1);
|
||||
}
|
||||
|
||||
initData();
|
||||
|
||||
$browseType = array('all', 'bymodule', 'assigntome', 'openedbyme', 'resolvedbyme', 'assigntonull', 'unconfirmed', 'unresolved', 'unclosed', 'toclosed', 'postponedbugs', 'assignedbyme');
|
||||
$productIdList = array('1', '2', '1000001');
|
||||
$projectID = array('0', '2', '1000001');
|
||||
$executionIdList = array(array(), array('3'), array('1000001'));
|
||||
$branch = array('0', '1', '1000001');
|
||||
$moduleID = array('0', '1', '1000001');
|
||||
|
||||
global $tester;
|
||||
$bug = $tester->loadModel('bug');
|
||||
r(count($bug->getList($browseType[0], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('5'); //获取全部产品1下的全部bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[1], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[1]))) && p('') && e('5'); //获取产品1模块下的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[2], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('5'); //获取产品1下指派给我的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[3], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('5'); //获取产品1下由我创建的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[4], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('5'); //获取产品1下由我解决的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[5], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('0'); //获取产品1下未指派的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[6], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('5'); //获取产品1下未确认的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[7], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('1'); //获取产品1下未解决的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[8], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('3'); //获取产品1下未关闭的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[9], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('2'); //获取产品1下待关闭的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[10], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('5'); //获取产品1下被延期的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[0], $productIdList[2], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('0'); //获取不存在产品ID的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[0], $productIdList[0], $projectID[2], $executionIdList[0], $branch[0], $moduleID[0]))) && p('') && e('0'); //获取不存在项目ID的bug列表,查看数量是否正确
|
||||
r(count($bug->getList($browseType[0], $productIdList[0], $projectID[0], $executionIdList[2], $branch[0], $moduleID[0]))) && p('') && e('0'); //获取不存在执行ID的bug列表,查看数量是否正确
|
||||
|
||||
r($bug->getList($browseType[0], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('1:planTitle') && e('计划1'); //获取全部产品1下的全部bug列表,查看ID为1的bug的计划名称是否正确
|
||||
r($bug->getList($browseType[0], $productIdList[1], $projectID[1], $executionIdList[1], $branch[1], $moduleID[0])) && p('2:title') && e('BUG2'); //获取全部产品1下的项目相关的全部bug列表,查看ID为2的bug的名称是否正确
|
||||
r($bug->getList($browseType[1], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[1])) && p('3:module') && e('1'); //获取产品1模块下的bug列表,查看ID为3的bug的module是否正确
|
||||
r($bug->getList($browseType[2], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('1:assignedTo') && e('admin'); //获取产品1下指派给我的bug列表,查看ID为1的bug的指派人是否正确
|
||||
r($bug->getList($browseType[3], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('3:openedBy') && e('admin'); //获取产品1下由我创建的bug列表,查看ID为3的bug的创建者是否正确
|
||||
r($bug->getList($browseType[4], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('5:resolvedBy') && e('admin'); //获取产品1下由我解决的bug列表,查看ID为5的bug的解决者是否正确
|
||||
r($bug->getList($browseType[6], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('3:confirmed') && e('0'); //获取产品1下未确认的bug列表,查看ID为3的bug的是否确认字段是否正确
|
||||
r($bug->getList($browseType[7], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('5:status') && e('active'); //获取产品1下久未处理的bug列表,查看ID为5的bug的状态是否正确
|
||||
r($bug->getList($browseType[8], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('7:status') && e('resolved'); //获取产品1下未关闭的bug列表,查看ID为7的bug的状态是否正确
|
||||
r($bug->getList($browseType[9], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('1:status') && e('resolved'); //获取产品1下待关闭的bug列表,查看ID为7的bug的状态是否正确
|
||||
r($bug->getList($browseType[10], $productIdList[0], $projectID[0], $executionIdList[0], $branch[0], $moduleID[0])) && p('3:resolution') && e('postponed'); //获取产品1下被延期的bug列表,查看数量是否正确
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
class bugZen extends bug
|
||||
{
|
||||
/**
|
||||
* 处理请求数据
|
||||
* Processing request data.
|
||||
*
|
||||
* @param object $formData
|
||||
* @access protected
|
||||
* @return object
|
||||
*/
|
||||
protected function beforeCreate(object $formData): object
|
||||
{
|
||||
$now = helper::now();
|
||||
$bug = $formData->setDefault('openedBy', $this->app->user->account)
|
||||
->setDefault('openedDate', $now)
|
||||
->setIF($this->lang->navGroup->bug != 'qa', 'project', $this->session->project)
|
||||
->setIF($this->post->assignedTo != '', 'assignedDate', $now)
|
||||
->setIF($this->post->story != false, 'storyVersion', $this->loadModel('story')->getVersion($this->post->story))
|
||||
->setIF(strpos($this->config->bug->create->requiredFields, 'deadline') !== false, 'deadline', $this->post->deadline)
|
||||
->setIF(strpos($this->config->bug->create->requiredFields, 'execution') !== false, 'execution', $this->post->execution)
|
||||
->stripTags($this->config->bug->editor->create['id'], $this->config->allowedTags)
|
||||
->cleanInt('product,execution,module,severity')
|
||||
->remove('files,labels,uid,oldTaskID,contactListMenu,region,lane,ticket,deleteFiles,resultFiles')
|
||||
->get();
|
||||
|
||||
$bug = $this->loadModel('file')->processImgURL($bug, $this->config->bug->editor->create['id'], $formData->rawdata->uid);
|
||||
|
||||
return $bug;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建bug。
|
||||
* Create a bug.
|
||||
*
|
||||
* @param object $bug
|
||||
* @access protected
|
||||
* @return array|false
|
||||
*/
|
||||
protected function doCreate(object $bug): array|false
|
||||
{
|
||||
/* Check repeat bug. */
|
||||
$result = $this->loadModel('common')->removeDuplicate('bug', $bug, "product={$bug->product}");
|
||||
if($result and $result['stop']) return array('status' => 'exists', 'id' => $result['duplicate']);
|
||||
|
||||
return $this->bug->create($bug);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建bug后数据处理
|
||||
* Do thing after create a bug.
|
||||
*
|
||||
* @param object $bug
|
||||
* @param object $formData
|
||||
* @param string $extra
|
||||
* @return void
|
||||
*/
|
||||
protected function afterCreate(object $bug, object $formData, string $extras): void
|
||||
{
|
||||
$bugID = $bug->id;
|
||||
$extras = str_replace(array(',', ' '), array('&', ''), $extras);
|
||||
parse_str($extras, $output);
|
||||
$from = isset($output['from']) ? $output['from'] : '';
|
||||
|
||||
if(isset($formData->rawdata->resultFiles))
|
||||
{
|
||||
$resultFiles = $formData->rawdata->resultFiles;
|
||||
if(isset($formData->rawdata->deleteFiles))
|
||||
{
|
||||
foreach($formData->rawdata->deleteFiles as $deletedCaseFileID) $resultFiles = trim(str_replace(",$deletedCaseFileID,", ',', ",$resultFiles,"), ',');
|
||||
}
|
||||
$files = $this->dao->select('*')->from(TABLE_FILE)->where('id')->in($resultFiles)->fetchAll('id');
|
||||
foreach($files as $file)
|
||||
{
|
||||
unset($file->id);
|
||||
$file->objectType = 'bug';
|
||||
$file->objectID = $bugID;
|
||||
$this->dao->insert(TABLE_FILE)->data($file)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
$this->file->updateObjectID($formData->rawdata->uid, $bugID, 'bug');
|
||||
$this->file->saveUpload('bug', $bugID);
|
||||
empty($bug->case) ? $this->loadModel('score')->create('bug', 'create', $bugID) : $this->loadModel('score')->create('bug', 'createFormCase', $bug->case);
|
||||
|
||||
if($bug->execution)
|
||||
{
|
||||
$this->loadModel('kanban');
|
||||
|
||||
$laneID = isset($output['laneID']) ? $output['laneID'] : 0;
|
||||
if(!empty($formData->rawdata->lane)) $laneID = $formData->rawdata->lane;
|
||||
|
||||
$columnID = $this->kanban->getColumnIDByLaneID($laneID, 'unconfirmed');
|
||||
if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
|
||||
|
||||
if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($bug->execution, $laneID, $columnID, 'bug', $bugID);
|
||||
if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($bug->execution, 'bug');
|
||||
}
|
||||
|
||||
/* Callback the callable method to process the related data for object that is transfered to bug. */
|
||||
if($from && is_callable(array($this, $this->config->bug->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->bug->fromObjects[$from]['callback']), $bugID);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -487,6 +487,7 @@ class file extends control
|
||||
*/
|
||||
public function read($fileID)
|
||||
{
|
||||
if(!$this->loadModel('user')->isLogon()) return print(js::locate($this->createLink('user', 'login')));
|
||||
$file = $this->file->getById($fileID);
|
||||
if(empty($file) or !$this->file->fileExists($file)) return false;
|
||||
|
||||
|
||||
@@ -318,3 +318,5 @@ $config->product->statisticFields['plans'] = array('plans');
|
||||
$config->product->statisticFields['releases'] = array('releases');
|
||||
|
||||
$config->product->skipRedirectMethod = ',create,index,showerrornone,ajaxgetdropmenu,kanban,all,manageline,export,ajaxgetplans,';
|
||||
|
||||
include dirname(__FILE__) . DS . 'config' . DS . 'form.php';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
global $lang;
|
||||
$config->product->form = new stdclass();
|
||||
$config->product->form->create = array();
|
||||
$config->product->form->create['program'] = array('type' => 'int', 'control' => 'select', 'required' => false, 'options' => array());
|
||||
$config->product->form->create['line'] = array('type' => 'int', 'control' => 'select', 'required' => false, 'default' => 0, 'options' => array());
|
||||
$config->product->form->create['lineName'] = array('type' => 'string', 'control' => 'input', 'required' => false, 'filter' => 'trim');
|
||||
$config->product->form->create['name'] = array('type' => 'string', 'control' => 'input', 'required' => true, 'filter' => 'trim');
|
||||
$config->product->form->create['code'] = array('type' => 'string', 'control' => 'input', 'required' => true, 'filter' => 'trim');
|
||||
$config->product->form->create['PO'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'options' => '');
|
||||
$config->product->form->create['QD'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'options' => '');
|
||||
$config->product->form->create['RD'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'options' => '');
|
||||
$config->product->form->create['reviewer'] = array('type' => 'string', 'control' => 'multi-select', 'required' => false, 'options' => 'users');
|
||||
$config->product->form->create['type'] = array('type' => 'string', 'control' => 'select', 'required' => false, 'default' => 'normal', 'options' => $lang->product->typeList);
|
||||
$config->product->form->create['status'] = array('type' => 'string', 'control' => 'hidden', 'required' => false, 'default' => 'normal');
|
||||
$config->product->form->create['desc'] = array('type' => 'string', 'control' => 'textarea', 'required' => false);
|
||||
$config->product->form->create['acl'] = array('type' => 'string', 'control' => 'acl', 'required' => false, 'default' => 'private', 'options' => $lang->product->aclList);
|
||||
$config->product->form->create['whitelist'] = array('type' => 'string', 'control' => 'multi-select', 'required' => false, 'options' => 'users');
|
||||
+40
-80
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The control file of product module of ZenTaoPMS.
|
||||
*
|
||||
@@ -393,6 +394,7 @@ class product extends control
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建产品。可以是顶级产品,也可以是项目集下的产品。
|
||||
* Create a product.
|
||||
*
|
||||
* @param int $programID
|
||||
@@ -400,10 +402,19 @@ class product extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function create($programID = 0, $extra = '')
|
||||
public function create(string $programID = '0', string $extra = '')
|
||||
{
|
||||
$programID = (int)$programID;
|
||||
|
||||
if(!empty($_POST))
|
||||
{
|
||||
$data = form::data($this->config->product->form->create);
|
||||
$data = $this->productZen->prepareCreateExtras($data);
|
||||
if(!$data) return $this->productZen->errorBeforeEdit();
|
||||
|
||||
$result = $this->product->create($data);
|
||||
if(!$result) return $this->productZen->errorAfterEdit();
|
||||
return $this->productZen->responseAfterEdit($result);
|
||||
$productID = $this->product->create();
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
$this->loadModel('action')->create('product', $productID, 'opened');
|
||||
@@ -421,57 +432,8 @@ class product extends control
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locate));
|
||||
}
|
||||
|
||||
if($this->app->tab == 'program') $this->loadModel('program')->setMenu($programID);
|
||||
if($this->app->getViewType() == 'mhtml')
|
||||
{
|
||||
if($this->app->rawModule == 'projectstory' and $this->app->rawMethod == 'story')
|
||||
{
|
||||
$this->loadModel('project')->setMenu();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->product->setMenu('');
|
||||
}
|
||||
}
|
||||
|
||||
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
|
||||
parse_str($extra, $output);
|
||||
|
||||
$this->loadModel('user');
|
||||
$poUsers = $this->user->getPairs('nodeleted|pofirst|noclosed', '', $this->config->maxCount);
|
||||
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PO"] = $this->config->user->moreLink;
|
||||
|
||||
$qdUsers = $this->user->getPairs('nodeleted|qdfirst|noclosed', '', $this->config->maxCount);
|
||||
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["QD"] = $this->config->user->moreLink;
|
||||
|
||||
$rdUsers = $this->user->getPairs('nodeleted|devfirst|noclosed', '', $this->config->maxCount);
|
||||
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["RD"] = $this->config->user->moreLink;
|
||||
|
||||
$lines = array();
|
||||
if($programID and $this->config->systemMode == 'ALM') $lines = array('') + $this->product->getLinePairs($programID);
|
||||
|
||||
if($this->app->tab == 'doc') unset($this->lang->doc->menu->product['subMenu']);
|
||||
|
||||
$gobackLink = '';
|
||||
if(isset($output['from']) and $output['from'] == 'qa') $gobackLink = $this->createLink('qa', 'index');
|
||||
if(isset($output['from']) and $output['from'] == 'global') $gobackLink = $this->createLink('product', 'all');
|
||||
|
||||
$this->view->title = $this->lang->product->create;
|
||||
$this->view->position[] = $this->view->title;
|
||||
$this->view->gobackLink = $gobackLink;
|
||||
$this->view->groups = $this->loadModel('group')->getPairs();
|
||||
$this->view->programID = $programID;
|
||||
$this->view->poUsers = $poUsers;
|
||||
$this->view->qdUsers = $qdUsers;
|
||||
$this->view->rdUsers = $rdUsers;
|
||||
$this->view->fields = $this->product->buildFormFields($this->config->product->create->fields);
|
||||
$this->view->users = $this->user->getPairs('nodeleted|noclosed');
|
||||
$this->view->programs = array('') + $this->loadModel('program')->getTopPairs('', 'noclosed');
|
||||
$this->view->lines = $lines;
|
||||
$this->view->URSRPairs = $this->loadModel('custom')->getURSRPairs();
|
||||
|
||||
unset($this->lang->product->typeList['']);
|
||||
$this->display();
|
||||
$this->productZen->setMenu4Create($programID);
|
||||
$this->productZen->buildCreateForm($programID, $extra);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1276,45 +1238,44 @@ class product extends control
|
||||
*
|
||||
* @param string $browseType
|
||||
* @param string $orderBy
|
||||
* @param int $param
|
||||
* @param int $recTotal
|
||||
* @param int $recPerPage
|
||||
* @param int $pageID
|
||||
* @param int $programID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function all($browseType = 'noclosed', $orderBy = 'program_asc', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1, $programID = 0)
|
||||
public function all(string $browseType = 'noclosed', string $orderBy = 'program_asc', string $param = '0', string $recTotal = '0', string $recPerPage = '20', string $pageID = '1', string $programID = '0')
|
||||
{
|
||||
/* Load module and set session. */
|
||||
$this->loadModel('program');
|
||||
$this->session->set('productList', $this->app->getURI(true), 'product');
|
||||
/* Convert string to int. */
|
||||
$param = (int)$param;
|
||||
$recTotal = (int)$recTotal;
|
||||
$recPerPage = (int)$recPerPage;
|
||||
$pageID = (int)$pageID;
|
||||
$programID = (int)$programID;
|
||||
|
||||
$queryID = ($browseType == 'bySearch' or !empty($param)) ? (int)$param : 0;
|
||||
/* Set env data. */
|
||||
$this->productZen->setEnvAll();
|
||||
|
||||
if($this->app->viewType == 'mhtml')
|
||||
{
|
||||
$productID = $this->product->saveState(0, $this->products);
|
||||
$this->product->setMenu($productID);
|
||||
}
|
||||
|
||||
$this->app->loadClass('pager', $static = true);
|
||||
$pager = new pager($recTotal, $recPerPage, $pageID);
|
||||
|
||||
/* Process product structure. */
|
||||
/* Generate statistics of products and program. */
|
||||
$this->app->loadClass('pager', true);
|
||||
$pager = new pager($recTotal, $recPerPage, $pageID);
|
||||
$queryID = ($browseType == 'bySearch' or !empty($param)) ? $param : 0;
|
||||
if($this->config->systemMode == 'light' and $orderBy == 'program_asc') $orderBy = 'order_asc';
|
||||
$productStats = $this->product->getStats($orderBy, $pager, $browseType, '', 'story', '', $queryID);
|
||||
|
||||
$productStats = $this->product->getStats($orderBy, $pager, $browseType, 0, 'story', 0, $queryID);
|
||||
$productStructure = $this->product->statisticProgram($productStats);
|
||||
$productLines = $this->dao->select('*')->from(TABLE_MODULE)->where('type')->eq('line')->andWhere('deleted')->eq(0)->orderBy('`order` asc')->fetchAll();
|
||||
$programLines = array();
|
||||
|
||||
foreach($productLines as $index => $productLine)
|
||||
{
|
||||
if(!isset($programLines[$productLine->root])) $programLines[$productLine->root] = array();
|
||||
$programLines[$productLine->root][$productLine->id] = $productLine->name;
|
||||
}
|
||||
|
||||
/* Save search form. */
|
||||
$actionURL = $this->createLink('product', 'all', "browseType=bySearch&orderBy=order_asc&queryID=myQueryID");
|
||||
$this->product->buildProductSearchForm($param, $actionURL);
|
||||
|
||||
$this->view->title = $this->lang->productCommon;
|
||||
$this->view->position[] = $this->lang->productCommon;
|
||||
/* Get product lines. */
|
||||
list($productLines, $programLines) = $this->getProductLines();
|
||||
|
||||
$this->view->title = $this->lang->productCommon;
|
||||
$this->view->position[] = $this->lang->productCommon;
|
||||
$this->view->recTotal = $pager->recTotal;
|
||||
$this->view->productStats = $productStats;
|
||||
$this->view->productStructure = $productStructure;
|
||||
@@ -1332,8 +1293,7 @@ class product extends control
|
||||
$this->view->pageID = $pageID;
|
||||
$this->view->programID = $programID;
|
||||
|
||||
//$this->display();
|
||||
$this->render();
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+39
-185
@@ -13,6 +13,15 @@
|
||||
<?php
|
||||
class productModel extends model
|
||||
{
|
||||
/* Constant status of product. */
|
||||
const ST_NOCLOSED = 'noclosed';
|
||||
const ST_BYSEARCH = 'bysearch';
|
||||
const ST_ALL = 'all';
|
||||
|
||||
/* OrderBy constant variables. */
|
||||
const OB_PROGRAM = 'program_asc';
|
||||
const OB_ORDER = 'order_asc';
|
||||
|
||||
/**
|
||||
* Get product module menu.
|
||||
*
|
||||
@@ -263,49 +272,6 @@ class productModel extends model
|
||||
return $this->dao->select('*')->from(TABLE_PRODUCT)->where('id')->in($productIDList)->fetchAll('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products.
|
||||
*
|
||||
* @param int $programID
|
||||
* @param string $status
|
||||
* @param int $limit
|
||||
* @param int $line
|
||||
* @param string|int $shadow all | 0 | 1
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getList($programID = 0, $status = 'all', $limit = 0, $line = 0, $shadow = 0)
|
||||
{
|
||||
$products = $this->dao->select('DISTINCT t1.*,t2.order')->from(TABLE_PRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
|
||||
->leftJoin(TABLE_PROJECTPRODUCT)->alias('t3')->on('t3.product = t1.id')
|
||||
->leftJoin(TABLE_TEAM)->alias('t4')->on("t4.root = t3.project and t4.type='project'")
|
||||
->where('t1.deleted')->eq(0)
|
||||
->beginIF($shadow !== 'all')->andWhere('t1.shadow')->eq((int)$shadow)->fi()
|
||||
->beginIF($programID)->andWhere('t1.program')->eq($programID)->fi()
|
||||
->beginIF($line > 0)->andWhere('t1.line')->eq($line)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->products)->fi()
|
||||
->andWhere('t1.vision')->eq($this->config->vision)->fi()
|
||||
->beginIF($status == 'noclosed')->andWhere('t1.status')->ne('closed')->fi()
|
||||
->beginIF(!in_array($status, array('all', 'noclosed', 'involved', 'review'), true))->andWhere('t1.status')->in($status)->fi()
|
||||
->beginIF($status == 'involved')
|
||||
->andWhere('t1.PO', true)->eq($this->app->user->account)
|
||||
->orWhere('t1.QD')->eq($this->app->user->account)
|
||||
->orWhere('t1.RD')->eq($this->app->user->account)
|
||||
->orWhere('t1.createdBy')->eq($this->app->user->account)
|
||||
->orWhere('t4.account')->eq($this->app->user->account)
|
||||
->markRight(1)
|
||||
->fi()
|
||||
->beginIF($status == 'review')
|
||||
->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.reviewers)")
|
||||
->andWhere('t1.reviewStatus')->eq('doing')
|
||||
->fi()
|
||||
->orderBy('t2.order_asc, t1.line_desc, t1.order_asc')
|
||||
->beginIF($limit > 0)->limit($limit)->fi()
|
||||
->fetchAll('id');
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list by search.
|
||||
@@ -1129,41 +1095,6 @@ class productModel extends model
|
||||
return $this->loadModel('story')->batchGetStoryStage($storyIdList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build form fields.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param object $project
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function buildFormFields($fields, $product = null)
|
||||
{
|
||||
$this->loadModel('user');
|
||||
$poUsers = $this->user->getPairs('nodeleted|pofirst|noclosed', '', $this->config->maxCount);
|
||||
$qdUsers = $this->user->getPairs('nodeleted|qdfirst|noclosed', '', $this->config->maxCount);
|
||||
$rdUsers = $this->user->getPairs('nodeleted|devfirst|noclosed', '', $this->config->maxCount);
|
||||
$users = $this->user->getPairs('nodeleted|noclosed');
|
||||
|
||||
foreach($fields as $field => $attr)
|
||||
{
|
||||
if(isset($attr['options']) and $attr['options'] == 'users') $fields[$field]['options'] = $users;
|
||||
$fields[$field]['name'] = $field;
|
||||
$fields[$field]['title'] = $this->lang->product->$field;
|
||||
if($product and isset($product->$field)) $fields[$field]['default'] = $product->$field;
|
||||
}
|
||||
|
||||
$fields['program']['options'] = array('') + $this->loadModel('program')->getTopPairs('', 'noclosed');
|
||||
$fields['PO']['options'] = $poUsers;
|
||||
$fields['QD']['options'] = $qdUsers;
|
||||
$fields['RD']['options'] = $rdUsers;
|
||||
|
||||
if($product and $product->program)$fields['line']['options'] = array('') + $this->getLinePairs($product->program);
|
||||
if(empty($product->program) or $this->config->systemMode != 'ALM') unset($fields['line']);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build search form.
|
||||
*
|
||||
@@ -1841,61 +1772,31 @@ class productModel extends model
|
||||
/**
|
||||
* Get product stats.
|
||||
*
|
||||
* @param string $orderBy
|
||||
* @param string $orderBy order_asc|program_asc
|
||||
* @param object $pager
|
||||
* @param string $status
|
||||
* @param int $line
|
||||
* @param string $storyType requirement|story
|
||||
* @param int $programID
|
||||
* @param int $param
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getStats($orderBy = 'order_asc', $pager = null, $status = 'noclosed', $line = 0, $storyType = 'story', $programID = 0, $param = 0)
|
||||
public function getStats(string $orderBy = 'order_asc', object $pager = null, string $status = 'noclosed', int $line = 0, string $storyType = 'story', int $programID = 0, int $param = 0): array
|
||||
{
|
||||
$this->loadModel('report');
|
||||
$this->loadModel('story');
|
||||
$this->loadModel('bug');
|
||||
|
||||
$products = $status == 'bySearch' ? $this->getListBySearch($param) : $this->getList($programID, $status, $limit = 0, $line);
|
||||
/* Fetch products list. */
|
||||
$products = strtolower($status) == static::ST_BYSEARCH ? $this->getListBySearch($param) : $this->productTao->getList($programID, $status, 0, $line);
|
||||
if(empty($products)) return array();
|
||||
|
||||
$productKeys = array_keys($products);
|
||||
if($orderBy == 'program_asc')
|
||||
{
|
||||
$products = $this->dao->select('t1.id as id, t1.*')->from(TABLE_PRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
|
||||
->where('t1.id')->in($productKeys)
|
||||
->orderBy('t2.order_asc, t1.line_desc, t1.order_asc')
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
}
|
||||
else
|
||||
{
|
||||
$products = $this->dao->select('*')->from(TABLE_PRODUCT)
|
||||
->where('id')->in($productKeys)
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
}
|
||||
if($orderBy == static::OB_PROGRAM) $products = $this->productTao->getPagerProductsWithProgramIn($productKeys, $pager);
|
||||
else $products = $this->productTao->getPagerProductsIn($productKeys, $pager, $orderBy);
|
||||
|
||||
$linePairs = $this->getLinePairs();
|
||||
foreach($products as $product) $product->lineName = zget($linePairs, $product->line, '');
|
||||
|
||||
$stories = $this->dao->select('product, status, count(status) AS count')
|
||||
->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('type')->eq('story')
|
||||
->andWhere('product')->in($productKeys)
|
||||
->groupBy('product, status')
|
||||
->fetchGroup('product', 'status');
|
||||
|
||||
$requirements = $this->dao->select('product, status, count(status) AS count')
|
||||
->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('type')->eq('requirement')
|
||||
->andWhere('product')->in($productKeys)
|
||||
->groupBy('product, status')
|
||||
->fetchGroup('product', 'status');
|
||||
$stories = $this->productTao->getStoriesTODO($productKeys);
|
||||
$requirements = $this->productTao->getRequirementsTODO($productKeys);
|
||||
|
||||
/* Padding the stories to sure all products have records. */
|
||||
$emptyStory = array_keys($this->lang->story->statusList);
|
||||
@@ -1925,68 +1826,19 @@ class productModel extends model
|
||||
|
||||
if($storyType == 'requirement') $stories = $requirements;
|
||||
|
||||
$finishClosedStory = $this->dao->select('product, count(1) as finish')->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('status')->eq('closed')
|
||||
->andWhere('type')->eq('story')
|
||||
->andWhere('closedReason')->eq('done')
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
$finishClosedStory = $this->productTao->getFinishClosedStoryTODO();
|
||||
$unclosedStory = $this->productTao->getUnClosedStoryTODO();
|
||||
$plans = $this->productTao->getPlansTODO($productKeys);
|
||||
$releases = $this->productTao->getReleasesTODO($productKeys);
|
||||
$bugs = $this->productTao->getBugsTODO($productKeys);
|
||||
$unResolved = $this->productTao->getUnResolvedTODO($productKeys);
|
||||
$fixedBugs = $this->productTao->getFixedBugsTODO($productKeys);
|
||||
$closedBugs = $this->productTao->getClosedBugsTODO($productKeys);
|
||||
|
||||
$unclosedStory = $this->dao->select('product, count(1) as unclosed')->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('type')->eq('story')
|
||||
->andWhere('status')->ne('closed')
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
$this->loadModel('report');
|
||||
$this->loadModel('story');
|
||||
$this->loadModel('bug');
|
||||
|
||||
$plans = $this->dao->select('product, count(*) AS count')
|
||||
->from(TABLE_PRODUCTPLAN)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productKeys)
|
||||
->andWhere('end')->gt(helper::now())
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
$releases = $this->dao->select('product, count(*) AS count')
|
||||
->from(TABLE_RELEASE)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productKeys)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
$bugs = $this->dao->select('product,count(*) AS conut')
|
||||
->from(TABLE_BUG)
|
||||
->where('product')->in($productKeys)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
$unResolved = $this->dao->select('product,count(*) AS count')
|
||||
->from(TABLE_BUG)
|
||||
->where('status')->eq('active')
|
||||
->orWhere('resolution')->eq('postponed')
|
||||
->andWhere('product')->in($productKeys)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
$fixedBugs = $this->dao->select('product,count(*) AS count')
|
||||
->from(TABLE_BUG)
|
||||
->where('status')->eq('closed')
|
||||
->andWhere('product')->in($productKeys)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('resolution')->eq('fixed')
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
$closedBugs = $this->dao->select('product,count(*) AS count')
|
||||
->from(TABLE_BUG)
|
||||
->where('status')->eq('closed')
|
||||
->andWhere('product')->in($productKeys)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
$this->app->loadClass('date', true);
|
||||
$weekDate = date::getThisWeek();
|
||||
@@ -2148,15 +2000,16 @@ class productModel extends model
|
||||
*
|
||||
* @param int $programID
|
||||
* @access public
|
||||
* @return array
|
||||
* @return int[]
|
||||
*/
|
||||
public function getLinePairs($programID = 0)
|
||||
public function getLinePairs(int $programID = 0): array
|
||||
{
|
||||
if($programID <= 0) return array();
|
||||
return $this->dao->select('id,name')->from(TABLE_MODULE)
|
||||
->where('type')->eq('line')
|
||||
->beginIF($programID)->andWhere('root')->eq($programID)->fi()
|
||||
->andWhere('root')->eq($programID)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetchPairs();
|
||||
->fetchPairs('id', 'name');
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2231,13 +2084,13 @@ class productModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics program data.
|
||||
* Statistics program data from statistics data of product.
|
||||
*
|
||||
* @param object $productStats
|
||||
* @param array $productStats
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function statisticProgram($productStats)
|
||||
public function statisticProgram(array $productStats): array
|
||||
{
|
||||
if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getProductStats();
|
||||
|
||||
@@ -2263,6 +2116,7 @@ class productModel extends model
|
||||
$productStructure[$product->program] = $this->statisticData('program', $productStructure, $product);
|
||||
}
|
||||
}
|
||||
|
||||
return $productStructure;
|
||||
}
|
||||
|
||||
@@ -2629,7 +2483,7 @@ class productModel extends model
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setMenu($productID, $branch = '', $module = 0, $moduleType = '', $extra = '')
|
||||
public function setMenu($productID = 0, $branch = '', $module = 0, $moduleType = '', $extra = '')
|
||||
{
|
||||
if(!$this->app->user->admin and strpos(",{$this->app->user->view->products},", ",$productID,") === false and $productID != 0 and !defined('TUTORIAL')) return $this->accessDenied($this->lang->product->accessDenied);
|
||||
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The model file of product 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 chen.tao<chentao@easycorp.ltd>
|
||||
* @package product
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
|
||||
class productTao extends productModel
|
||||
{
|
||||
/**
|
||||
* Get products with program data that in the ID list.
|
||||
*
|
||||
* @param array $productIDs
|
||||
* @param object $pager
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getPagerProductsWithProgramIn(array $productIDs, object|null $pager) :array
|
||||
{
|
||||
$products = $this->dao->select('t1.*')->from(TABLE_PRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
|
||||
->where('t1.id')->in($productIDs)
|
||||
->orderBy('t2.order_asc, t1.line_desc, t1.order_asc')
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products in the ID list.
|
||||
*
|
||||
* @param array $productIDs
|
||||
* @param object $pager
|
||||
* @param string $orderBy
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getPagerProductsIn(array $productIDs, object|null $pager, string $orderBy)
|
||||
{
|
||||
/* TODO list all fields? */
|
||||
$products = $this->dao->select('*')->from(TABLE_PRODUCT)
|
||||
->where('id')->in($productIDs)
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll('id');
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products list.
|
||||
*
|
||||
* @param int $programID
|
||||
* @param string $status
|
||||
* @param int $limit
|
||||
* @param int $line
|
||||
* @param string|int $shadow all | 0 | 1
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
protected function getList(int $programID = 0, string $status = 'all', int $limit = 0, int $line = 0, string|int $shadow = 0)
|
||||
{
|
||||
$products = $this->dao->select('DISTINCT t1.*,t2.order')->from(TABLE_PRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
|
||||
->leftJoin(TABLE_PROJECTPRODUCT)->alias('t3')->on('t3.product = t1.id')
|
||||
->leftJoin(TABLE_TEAM)->alias('t4')->on("t4.root = t3.project and t4.type='project'")
|
||||
->where('t1.deleted')->eq(0)
|
||||
->beginIF($shadow !== 'all')->andWhere('t1.shadow')->eq((int)$shadow)->fi()
|
||||
->beginIF($programID)->andWhere('t1.program')->eq($programID)->fi()
|
||||
->beginIF($line > 0)->andWhere('t1.line')->eq($line)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->products)->fi()
|
||||
->andWhere('t1.vision')->eq($this->config->vision)->fi()
|
||||
->beginIF($status == 'noclosed')->andWhere('t1.status')->ne('closed')->fi()
|
||||
->beginIF(!in_array($status, array('all', 'noclosed', 'involved', 'review'), true))->andWhere('t1.status')->in($status)->fi()
|
||||
->beginIF($status == 'involved')
|
||||
->andWhere('t1.PO', true)->eq($this->app->user->account)
|
||||
->orWhere('t1.QD')->eq($this->app->user->account)
|
||||
->orWhere('t1.RD')->eq($this->app->user->account)
|
||||
->orWhere('t1.createdBy')->eq($this->app->user->account)
|
||||
->orWhere('t4.account')->eq($this->app->user->account)
|
||||
->markRight(1)
|
||||
->fi()
|
||||
->beginIF($status == 'review')
|
||||
->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.reviewers)")
|
||||
->andWhere('t1.reviewStatus')->eq('doing')
|
||||
->fi()
|
||||
->orderBy('t2.order_asc, t1.line_desc, t1.order_asc')
|
||||
->beginIF($limit > 0)->limit($limit)->fi()
|
||||
->fetchAll('id');
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/* TODO move to story module. */
|
||||
protected function getStoriesTODO( array $productIDs): array
|
||||
{
|
||||
$stories = $this->dao->select('product, status, count(status) AS count')
|
||||
->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('type')->eq('story')
|
||||
->andWhere('product')->in($productIDs)
|
||||
->groupBy('product, status')
|
||||
->fetchGroup('product', 'status');
|
||||
|
||||
return $stories;
|
||||
}
|
||||
|
||||
/* TODO move to story module. */
|
||||
protected function getRequirementsTODO( array $productIDs): array
|
||||
{
|
||||
$requirements = $this->dao->select('product, status, count(status) AS count')
|
||||
->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('type')->eq('requirement')
|
||||
->andWhere('product')->in($productIDs)
|
||||
->groupBy('product, status')
|
||||
->fetchGroup('product', 'status');
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/* TODO move to story module. */
|
||||
protected function getFinishClosedStoryTODO(): array
|
||||
{
|
||||
$finishClosedStory = $this->dao->select('product, count(1) as finish')->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('status')->eq('closed')
|
||||
->andWhere('type')->eq('story')
|
||||
->andWhere('closedReason')->eq('done')
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
return $finishClosedStory;
|
||||
}
|
||||
|
||||
/* TODO move to story module. */
|
||||
protected function getUnClosedStoryTODO(): array
|
||||
{
|
||||
$unclosedStory = $this->dao->select('product, count(1) as unclosed')->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('type')->eq('story')
|
||||
->andWhere('status')->ne('closed')
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
return $unclosedStory;
|
||||
}
|
||||
|
||||
/* TODO move to productplan module. */
|
||||
protected function getPlansTODO( array $productIDs): array
|
||||
{
|
||||
$plans = $this->dao->select('product, count(*) AS count')
|
||||
->from(TABLE_PRODUCTPLAN)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productIDs)
|
||||
->andWhere('end')->gt(helper::now())
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
return $plans;
|
||||
}
|
||||
|
||||
/* TODO move to release module. */
|
||||
protected function getReleasesTODO( array $productIDs): array
|
||||
{
|
||||
$releases = $this->dao->select('product, count(*) AS count')
|
||||
->from(TABLE_RELEASE)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in($productIDs)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
return $releases;
|
||||
}
|
||||
|
||||
/* TODO move to bug module. */
|
||||
protected function getBugsTODO( array $productIDs): array
|
||||
{
|
||||
$bugs = $this->dao->select('product,count(*) AS conut')
|
||||
->from(TABLE_BUG)
|
||||
->where('product')->in($productIDs)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
return $bugs;
|
||||
}
|
||||
|
||||
/* TODO move to bug module. */
|
||||
protected function getUnResolvedTODO( array $productIDs): array
|
||||
{
|
||||
$unResolved = $this->dao->select('product,count(*) AS count')
|
||||
->from(TABLE_BUG)
|
||||
->where('status')->eq('active')
|
||||
->orWhere('resolution')->eq('postponed')
|
||||
->andWhere('product')->in($productIDs)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
return $unResolved;
|
||||
}
|
||||
|
||||
/* TODO move to bug module. */
|
||||
protected function getFixedBugsTODO( array $productIDs): array
|
||||
{
|
||||
$fixedBugs = $this->dao->select('product,count(*) AS count')
|
||||
->from(TABLE_BUG)
|
||||
->where('status')->eq('closed')
|
||||
->andWhere('product')->in($productIDs)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('resolution')->eq('fixed')
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
return $fixedBugs;
|
||||
}
|
||||
|
||||
/* TODO move to bug module. */
|
||||
protected function getClosedBugsTODO( array $productIDs): array
|
||||
{
|
||||
$closedBugs = $this->dao->select('product,count(*) AS count')
|
||||
->from(TABLE_BUG)
|
||||
->where('status')->eq('closed')
|
||||
->andWhere('product')->in($productIDs)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->groupBy('product')
|
||||
->fetchPairs();
|
||||
|
||||
return $closedBugs;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,28 +3,32 @@
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/product.class.php';
|
||||
|
||||
function initData()
|
||||
{
|
||||
$module = zdTable('module');
|
||||
$module->id->range('1-1000');
|
||||
$module->root->range('1-5'); //产品线所属项目集
|
||||
$module->name->prefix("产品线")->range('1-1000');
|
||||
$module->type->range("line");
|
||||
$module->parent->range("0");
|
||||
|
||||
$module->gen(10);
|
||||
}
|
||||
initData();
|
||||
|
||||
/**
|
||||
|
||||
title=productModel->getLinePairs();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
测试获取程序集1的信息 >> 产品线1,产品线11
|
||||
测试获取程序集2的信息 >> 产品线2,产品线12
|
||||
测试获取程序集3的信息 >> 产品线3,产品线13
|
||||
测试获取程序集4的信息 >> 产品线4,产品线14
|
||||
测试获取程序集5的信息 >> 产品线5,产品线15
|
||||
测试获取不存在程序集的信息 >> 0
|
||||
|
||||
*/
|
||||
|
||||
$programIDList = array('1', '2', '3', '4', '5', '1000001');
|
||||
|
||||
$product = new productTest('admin');
|
||||
|
||||
r($product->getLinePairsTest($programIDList[0])) && p('1,11') && e('产品线1,产品线11'); // 测试获取程序集1的信息
|
||||
r($product->getLinePairsTest($programIDList[1])) && p('2,12') && e('产品线2,产品线12'); // 测试获取程序集2的信息
|
||||
r($product->getLinePairsTest($programIDList[2])) && p('3,13') && e('产品线3,产品线13'); // 测试获取程序集3的信息
|
||||
r($product->getLinePairsTest($programIDList[3])) && p('4,14') && e('产品线4,产品线14'); // 测试获取程序集4的信息
|
||||
r($product->getLinePairsTest($programIDList[4])) && p('5,15') && e('产品线5,产品线15'); // 测试获取程序集5的信息
|
||||
r($product->getLinePairsTest($programIDList[5])) && p('1,11') && e('0'); // 测试获取不存在程序集的信息
|
||||
r($product->getLinePairsTest(-1)) && p() && e('0'); // 测试获取程序集-1的信息
|
||||
r($product->getLinePairsTest(0)) && p() && e('0'); // 测试获取程序集0的信息
|
||||
r($product->getLinePairsTest(1)) && p('1,6') && e('产品线1,产品线6'); // 测试获取程序集1的信息
|
||||
r($product->getLinePairsTest(2)) && p('2,7') && e('产品线2,产品线7'); // 测试获取程序集2的信息
|
||||
r($product->getLinePairsTest(3)) && p('3,8') && e('产品线3,产品线8'); // 测试获取程序集3的信息
|
||||
r($product->getLinePairsTest(4)) && p('4,9') && e('产品线4,产品线9'); // 测试获取程序集4的信息
|
||||
r($product->getLinePairsTest(10001)) && p() && e('0'); // 测试获取不存在程序集的信息
|
||||
|
||||
@@ -14,7 +14,7 @@ class productTest
|
||||
global $tester;
|
||||
su($user);
|
||||
$this->objectModel = $tester->loadModel('product');
|
||||
$tester->app->loadClass('dao');
|
||||
$tester->app->loadClass('dao');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ class productTest
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function createObject($param = array())
|
||||
public function createObject($param = array()): object
|
||||
{
|
||||
global $createFields;
|
||||
$whitelist = array();
|
||||
@@ -1031,6 +1031,23 @@ class productTest
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getPageProductsWithProgramIn function of tao file.
|
||||
* 测试tao文件中的 getPagerProductsWithProgramIn 函数。
|
||||
*
|
||||
* @param array $productIDs
|
||||
* @param object|null $pager
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getPagerProductsWithProgramInTest(array $productIDs, object|null $pager): array
|
||||
{
|
||||
$records = $this->objectModel->getPagerProductsWithProgramIn($productIDs, $pager);
|
||||
if(!ksort($records)) return [];
|
||||
|
||||
return $records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test change the projects set of the program.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/product.class.php';
|
||||
|
||||
function initData()
|
||||
{
|
||||
/* Generate product data. */
|
||||
$product = zdTable('product');
|
||||
$product->id->range('1000-1010');
|
||||
$product->program->range('1-10');
|
||||
$product->name->prefix('product_')->range('1-10');
|
||||
$product->code->prefix('product_code_')->range('1-10');
|
||||
$product->order->range('1-10');
|
||||
$product->gen(10);
|
||||
}
|
||||
initData();
|
||||
|
||||
/**
|
||||
|
||||
title=productTao->getPagerProductsIn();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
- 步骤1降序排序 @1009,10
|
||||
- 步骤2不存在的数据 @0
|
||||
- 步骤3升序排序 @1007,8
|
||||
- 步骤4分页取2行数据 @2
|
||||
|
||||
*/
|
||||
|
||||
$productIDs = array(1007, 1008, 1009, 10000);
|
||||
|
||||
$product = new productTest('admin');
|
||||
|
||||
/* Desc. */
|
||||
$result = $product->objectModel->getPagerProductsIn($productIDs, null, 'order_desc');
|
||||
r(array_shift($result)) && p('id,order') && e('1009,10');
|
||||
|
||||
/* Not exist data. */
|
||||
r(isset($result[10000])) && p('empty') && e('0');
|
||||
|
||||
/* Asc. */
|
||||
$result = $product->objectModel->getPagerProductsIn($productIDs, null, 'order_asc');
|
||||
r(array_shift($result)) && p('id,order') && e('1007,8');
|
||||
|
||||
/* Pager. */
|
||||
global $tester;
|
||||
$tester->app->loadClass('pager', true);
|
||||
$tester->app->setModuleName('product');
|
||||
$tester->app->setMethodName('all');
|
||||
$pager = new pager(0, 2, 1);
|
||||
|
||||
$result = $product->objectModel->getPagerProductsIn($productIDs, $pager, 'order_desc');
|
||||
r(count($result)) && p('') && e('2');
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/product.class.php';
|
||||
|
||||
function initData()
|
||||
{
|
||||
/* Generate product data. */
|
||||
$product = zdTable('product');
|
||||
$product->id->range('1000-1100');
|
||||
$product->program->range('1-10');
|
||||
$product->name->prefix('product_')->range('1-10');
|
||||
$product->code->prefix('product_code_')->range('1-10');
|
||||
$product->gen(5);
|
||||
|
||||
/* Generate program data. */
|
||||
$program = zdTable('project');
|
||||
$program->id->range('1-10');
|
||||
$program->name->prefix('program_')->range('1-10');
|
||||
$program->gen(5);
|
||||
}
|
||||
initData();
|
||||
|
||||
/**
|
||||
|
||||
title=productTao->getPagerProductsWithProgramIn();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
- 步骤1 @1000,0
|
||||
- 步骤2 @1001,2
|
||||
- 步骤3 @1002,3
|
||||
|
||||
*/
|
||||
|
||||
$productIDs = array(1000, 1001, 1002);
|
||||
|
||||
$product = new productTest('admin');
|
||||
$result = $product->getPagerProductsWithProgramInTest($productIDs, null);
|
||||
|
||||
r($result[1000]) && p('id,program') && e('1000,1');
|
||||
r($result[1001]) && p('id,program') && e('1001,2');
|
||||
r($result[1002]) && p('id,program') && e('1002,3');
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The control file of product 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 chen.tao<chentao@easycorp.ltd>
|
||||
* @package product
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
|
||||
class productZen extends product
|
||||
{
|
||||
/**
|
||||
* Set shared environment data for all function of control layer.
|
||||
* 为控制层的all函数设置共享环境数据。
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function setEnvAll()
|
||||
{
|
||||
/* Set redirect URI. */
|
||||
$this->session->set('productList', $this->app->getURI(true), 'product');
|
||||
|
||||
/* Set activated menu for mobile view. */
|
||||
if($this->app->viewType == 'mhtml')
|
||||
{
|
||||
$productID = $this->product->saveState(0, $this->products);
|
||||
$this->product->setMenu($productID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product lines and product lines of program.
|
||||
*
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getProductLines(): array
|
||||
{
|
||||
/* Get all product lines. */
|
||||
/* TODO use model of module. */
|
||||
$productLines = $this->dao->select('*')->from(TABLE_MODULE)->where('type')->eq('line')->andWhere('deleted')->eq(0)->orderBy('`order` asc')->fetchAll();
|
||||
|
||||
/* Collect product lines of program lines. */
|
||||
$programLines = array();
|
||||
foreach($productLines as $productLine)
|
||||
{
|
||||
if(!isset($programLines[$productLine->root])) $programLines[$productLine->root] = array();
|
||||
$programLines[$productLine->root][$productLine->id] = $productLine->name;
|
||||
}
|
||||
|
||||
return array($productLines, $programLines);
|
||||
}
|
||||
}
|
||||
@@ -596,7 +596,7 @@ class programModel extends model
|
||||
}
|
||||
|
||||
$query = str_replace('`id`','t1.id', $this->session->projectQuery);
|
||||
$projectList = $this->dao->select('t1.*')->from(TABLE_PROJECT)->alias('t1')
|
||||
$projectList = $this->dao->select('DISTINCT t1.*')->from(TABLE_PROJECT)->alias('t1')
|
||||
->leftJoin(TABLE_TEAM)->alias('t2')->on('t1.id=t2.root')
|
||||
->leftJoin(TABLE_STAKEHOLDER)->alias('t3')->on('t1.id=t3.objectID')
|
||||
->where('t1.deleted')->eq('0')
|
||||
|
||||
@@ -1516,8 +1516,9 @@ class project extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function team($projectID = 0)
|
||||
public function team(string $projectID = '0')
|
||||
{
|
||||
$projectID = (int)$projectID;
|
||||
$this->session->set('teamList', $this->app->getURI(true), 'project');
|
||||
|
||||
$this->app->loadLang('execution');
|
||||
@@ -1778,19 +1779,24 @@ class project extends control
|
||||
*/
|
||||
public function suspend(string $projectID)
|
||||
{
|
||||
$this->loadModel('action');
|
||||
$projectID = (int)$projectID;
|
||||
|
||||
if(!empty($_POST))
|
||||
{
|
||||
$changes = $this->project->suspend($projectID);
|
||||
$postData = form::data($this->config->project->form->suspend);
|
||||
|
||||
$postData = $this->projectZen->prepareSuspendExtras($projectID, $postData);
|
||||
|
||||
$changes = $this->project->suspend($projectID, $postData);
|
||||
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
|
||||
$comment = strip_tags($this->post->comment, $this->config->allowedTags);
|
||||
return $this->projectZen->responseAfterStart($project, $changes, $comment);
|
||||
$this->projectZen->responseAfterSuspend($projectID, $changes, $comment);
|
||||
return print(js::reload('parent.parent'));
|
||||
}
|
||||
|
||||
$this->projectZen->buildSuspendForm((int)$projectID);
|
||||
$this->projectZen->buildSuspendForm($projectID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2017,8 +2023,10 @@ class project extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function manageProducts($projectID, $from = 'project')
|
||||
public function manageProducts(string $projectID, $from = 'project')
|
||||
{
|
||||
$projectID = (int)$projectID;
|
||||
|
||||
$this->loadModel('product');
|
||||
$this->loadModel('program');
|
||||
$this->loadModel('execution');
|
||||
|
||||
Regular → Executable
+27
-71
@@ -180,24 +180,19 @@ class projectModel extends model
|
||||
|
||||
/**
|
||||
* Get a project by id.
|
||||
* 根据项目ID获取项目信息。
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param string $type project|sprint,stage
|
||||
* @access public
|
||||
* @return object
|
||||
* @return object|false
|
||||
*/
|
||||
public function getByID($projectID, $type = 'project')
|
||||
public function getByID(int $projectID): object|false
|
||||
{
|
||||
if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getProject();
|
||||
|
||||
$project = $this->dao->select('*')->from(TABLE_PROJECT)
|
||||
->where('id')->eq($projectID)
|
||||
->andWhere('`type`')->in($type)
|
||||
->fetch();
|
||||
|
||||
$project = $this->projectTao->fetchProjectInfo($projectID);
|
||||
if(!$project) return false;
|
||||
|
||||
if(helper::isZeroDate($project->end)) $project->end = '';
|
||||
$project = $this->loadModel('file')->replaceImgURL($project, 'desc');
|
||||
return $project;
|
||||
}
|
||||
@@ -1050,7 +1045,7 @@ class projectModel extends model
|
||||
public function buildMenuQuery($projectID = 0)
|
||||
{
|
||||
$path = '';
|
||||
$project = $this->getByID($projectID);
|
||||
$project = $this->projectTao->fetchProjectInfo($projectID);
|
||||
if($project) $path = $project->path;
|
||||
|
||||
return $this->dao->select('*')->from(TABLE_PROJECT)
|
||||
@@ -1734,7 +1729,7 @@ class projectModel extends model
|
||||
* @access public
|
||||
* @return array|false
|
||||
*/
|
||||
public function start(int $projectID, object $project): array|false
|
||||
public function start(int $projectID, object $project):array|false
|
||||
{
|
||||
$oldProject = $this->getById($projectID);
|
||||
|
||||
@@ -1754,86 +1749,46 @@ class projectModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* Put project off.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function putoff($projectID)
|
||||
{
|
||||
$oldProject = $this->getById($projectID);
|
||||
$now = helper::now();
|
||||
|
||||
$project = fixer::input('post')
|
||||
->add('id', $projectID)
|
||||
->setDefault('lastEditedBy', $this->app->user->account)
|
||||
->setDefault('lastEditedDate', $now)
|
||||
->remove('comment')
|
||||
->get();
|
||||
|
||||
$this->dao->update(TABLE_PROJECT)->data($project)
|
||||
->autoCheck()
|
||||
->checkFlow()
|
||||
->where('id')->eq((int)$projectID)
|
||||
->exec();
|
||||
|
||||
if(!dao::isError()) return common::createChanges($oldProject, $project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend project.
|
||||
* Suspend project and update status.
|
||||
* 暂停项目并更改其状态
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param object $project
|
||||
* @param string $type
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
* @return array|flase
|
||||
*/
|
||||
public function suspend($projectID, $type = 'project')
|
||||
public function suspend(int $projectID, object $project, string $type = 'project'): array|false
|
||||
{
|
||||
$editorIdList = $this->config->project->editor->suspend['id'];
|
||||
if($this->app->rawModule == 'program') $editorIdList = $this->config->program->editor->suspend['id'];
|
||||
|
||||
$oldProject = $this->getById($projectID, $type);
|
||||
$project = fixer::input('post')
|
||||
->add('id', $projectID)
|
||||
->setDefault('status', 'suspended')
|
||||
->setDefault('lastEditedBy', $this->app->user->account)
|
||||
->setDefault('lastEditedDate', helper::now())
|
||||
->setDefault('suspendedDate', helper::today())
|
||||
->stripTags($editorIdList, $this->config->allowedTags)
|
||||
->remove('comment')->get();
|
||||
|
||||
$project = $this->loadModel('file')->processImgURL($project, $editorIdList, $this->post->uid);
|
||||
$this->dao->update(TABLE_PROJECT)->data($project)
|
||||
->autoCheck()
|
||||
->checkFlow()
|
||||
->where('id')->eq((int)$projectID)
|
||||
->exec();
|
||||
|
||||
if(!dao::isError())
|
||||
{
|
||||
if(!$oldProject->multiple) $this->changeExecutionStatus($projectID, 'suspend');
|
||||
return common::createChanges($oldProject, $project);
|
||||
}
|
||||
$this->projectTao->doSuspend($projectID, $project);
|
||||
|
||||
if(!$oldProject->multiple) $this->changeExecutionStatus($projectID, 'suspend');
|
||||
return common::createChanges($oldProject, $project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate project.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param object $project
|
||||
* @access public
|
||||
* @return array $changes|false
|
||||
*/
|
||||
public function activate(object $project) :array|false
|
||||
public function activate(int $projectID, object $project) :array|false
|
||||
{
|
||||
$now = helper::now();
|
||||
$projectID = $project->id;
|
||||
$oldProject = $this->getById($projectID);
|
||||
$oldProject = $this->projectTao->fetchProjectInfo($projectID);
|
||||
|
||||
$this->projectTao->updateProject($project);
|
||||
|
||||
if(dao::isError()) return false;
|
||||
$daoSuccess = $this->projectTao->doActivate($projectID, $project);
|
||||
if(!$daoSuccess) return false;
|
||||
|
||||
if(empty($oldProject->multiple) and $oldProject->model != 'waterfall') $this->loadModel('execution')->syncNoMultipleSprint($projectID);
|
||||
|
||||
@@ -1851,6 +1806,7 @@ class projectModel extends model
|
||||
$this->product->activate($productID);
|
||||
}
|
||||
|
||||
$changes = common::createChanges($oldProject, $project);
|
||||
return common::createChanges($oldProject, $project);
|
||||
}
|
||||
|
||||
@@ -2013,7 +1969,7 @@ class projectModel extends model
|
||||
*/
|
||||
public function manageMembers($projectID)
|
||||
{
|
||||
$project = $this->getByID($projectID);
|
||||
$project = $this->projectTao->fetchProjectInfo($projectID);
|
||||
$data = (array)fixer::input('post')->get();
|
||||
|
||||
extract($data);
|
||||
@@ -2492,7 +2448,7 @@ class projectModel extends model
|
||||
$this->dao->update(TABLE_EXECUTION)->set('division')->eq('1')->where('project')->eq((int)$projectID)->exec();
|
||||
}
|
||||
|
||||
$project = $this->getByID($projectID);
|
||||
$project = $this->projectTao->fetchProjectInfo($projectID);
|
||||
if(!empty($project) and ($project->model == 'waterfall' or $project->model == 'waterfallplus') and empty($project->division) and !empty($executions))
|
||||
{
|
||||
$this->loadModel('execution');
|
||||
@@ -2533,7 +2489,7 @@ class projectModel extends model
|
||||
{
|
||||
if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getTeamMembers();
|
||||
|
||||
$project = $this->getByID($projectID);
|
||||
$project = $this->projectTao->fetchProjectInfo($projectID);
|
||||
if(empty($project)) return array();
|
||||
|
||||
return $this->dao->select("t1.*, t1.hours * t1.days AS totalHours, t2.id as userID, if(t2.deleted='0', t2.realname, t1.account) as realname")->from(TABLE_TEAM)->alias('t1')
|
||||
@@ -2770,14 +2726,14 @@ class projectModel extends model
|
||||
|
||||
$model = 'scrum';
|
||||
$objectID = (empty($objectID) and $this->session->project) ? $this->session->project : $objectID;
|
||||
$project = $this->getByID($objectID);
|
||||
$project = $this->projectTao->fetchProjectInfo($objectID);
|
||||
|
||||
if(!$project)
|
||||
{
|
||||
$execution = $this->loadModel('execution')->getByID($objectID);
|
||||
if($execution and $execution->project and !$execution->multiple)
|
||||
{
|
||||
$project = $this->getByID($execution->project);
|
||||
$project = $this->projectTao->fetchProjectInfo($execution->project);
|
||||
$objectID = $execution->project;
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
+52
-8
@@ -32,18 +32,39 @@ class projectTao extends projectModel
|
||||
}
|
||||
|
||||
/**
|
||||
* Update project.
|
||||
* Update project table when suspend a project.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param object $project
|
||||
*
|
||||
* @access protected
|
||||
* @return bool
|
||||
* @return bool
|
||||
*/
|
||||
protected function updateProject(object $project): bool
|
||||
protected function doSuspend(int $projectID, object $project): bool
|
||||
{
|
||||
$this->dao->update(TABLE_PROJECT)->data($project)
|
||||
->autoCheck()
|
||||
->checkFlow()
|
||||
->where('id')->eq((int)$project->id)
|
||||
->where('id')->eq($projectID)
|
||||
->exec();
|
||||
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update project.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param object $project
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
protected function doActivate(int $projectID ,object $project): bool
|
||||
{
|
||||
$this->dao->update(TABLE_PROJECT)->data($project , 'readjustTime, readjustTask, comment')
|
||||
->autoCheck()
|
||||
->checkFlow()
|
||||
->where('id')->eq((int)$projectID)
|
||||
->exec();
|
||||
|
||||
return !dao::isError();
|
||||
@@ -54,9 +75,9 @@ class projectTao extends projectModel
|
||||
*
|
||||
* @param int $projectID
|
||||
* @access protected
|
||||
* @return array
|
||||
* @return array|false
|
||||
*/
|
||||
protected function fetchUndoneTasks(int $projectID): array
|
||||
protected function fetchUndoneTasks(int $projectID): array|false
|
||||
{
|
||||
return $this->dao->select('id,estStarted,deadline,status')->from(TABLE_TASK)
|
||||
->where('deadline')->notZeroDate()
|
||||
@@ -70,9 +91,9 @@ class projectTao extends projectModel
|
||||
*
|
||||
* @param array $tasks
|
||||
* @access protected
|
||||
* @return void
|
||||
* @return bool
|
||||
*/
|
||||
protected function updateTasksStartAndEndDate(array $tasks) :void
|
||||
protected function updateTasksStartAndEndDate(array $tasks): bool
|
||||
{
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
@@ -93,6 +114,8 @@ class projectTao extends projectModel
|
||||
->set('deadline')->eq($deadline)
|
||||
->where('id')->eq($task->id)
|
||||
->exec();
|
||||
|
||||
if(dao::isError()) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -101,7 +124,28 @@ class projectTao extends projectModel
|
||||
|
||||
if($deadline > $project->end) $deadline = $project->end;
|
||||
$this->dao->update(TABLE_TASK)->set('deadline')->eq($deadline)->where('id')->eq($task->id)->exec();
|
||||
|
||||
if(dao::isError()) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project details, including all contents of the TABLE_PROJECT.
|
||||
* 获取项目的详情,包含project表的所有内容。
|
||||
*
|
||||
* @param int $projectID
|
||||
* @access protected
|
||||
* @return object|false
|
||||
*/
|
||||
protected function fetchProjectInfo(int $projectID): object|false
|
||||
{
|
||||
$project = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->eq($projectID)->fetch();
|
||||
|
||||
/* Filter the date is empty or 1970. */
|
||||
if($project and helper::isZeroDate($project->end)) $project->end = '';
|
||||
return $project;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/project.class.php';
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
@@ -9,36 +10,56 @@ title=测试 projectModel->activate();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
激活id为20的项目 >> object
|
||||
激活id为20的项目 >> object
|
||||
激活id为2的项目
|
||||
激活id为3的项目
|
||||
|
||||
*/
|
||||
|
||||
function initData()
|
||||
{
|
||||
$project = zdTable('project');
|
||||
$project->id->range('2-5');
|
||||
$project->project->range('2-5');
|
||||
$project->name->prefix("项目")->range('2-5');
|
||||
$project->code->prefix("project")->range('2-5');
|
||||
$project->model->range("scrum");
|
||||
$project->auth->range("[]");
|
||||
$project->path->range("[]");
|
||||
$project->type->range("project");
|
||||
$project->grade->range("1");
|
||||
$project->days->range("1");
|
||||
$project->status->range("closed, suspended");
|
||||
$project->desc->range("[]");
|
||||
$project->budget->range("100000,200000");
|
||||
$project->budgetUnit->range("CNY");
|
||||
$project->percent->range("0-0");
|
||||
|
||||
$project->gen(2);
|
||||
}
|
||||
|
||||
initData();
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('project');
|
||||
|
||||
$project = new Project();
|
||||
$data = new stdClass();
|
||||
|
||||
$data1 = array(
|
||||
'id' => 2,
|
||||
'begin'=> '2023-04-26',
|
||||
'end'=> '10001-01-07',
|
||||
'readjustTime'=> 1,
|
||||
'readjustTask'=> 1,
|
||||
'status'=> 'doing',
|
||||
'comment'=> 'sdfsdf'
|
||||
);
|
||||
$data->status = 'doing';
|
||||
$data->begin = '2022-10-10';
|
||||
$data->end = '2022-10-10';
|
||||
$data->status = 'doing';
|
||||
$data->comment = 'fgasgqasfdgasfgasg';
|
||||
$data->readjustTime = 1;
|
||||
$data->readjustTask = 1;
|
||||
|
||||
$data2 = array(
|
||||
'id' => 3,
|
||||
'begin'=> '2023-04-26',
|
||||
'end'=> '10001-01-07',
|
||||
'readjustTime'=> 1,
|
||||
'readjustTask'=> 1,
|
||||
'status'=> 'doing',
|
||||
'comment'=> 'sdfsdf'
|
||||
);
|
||||
|
||||
r($project->activate($data1)) && p('1:field,old,new') && e('status,closed,doing'); // 激活id为2状态是closed的项目
|
||||
r($project->activate($data2)) && p('1:field,old,new') && e('status,suspended,doing'); // 激活id为3状态是suspended的项目
|
||||
$changes2 = $project->activate(2, $data);
|
||||
$changes3 = $project->activate(3, $data);
|
||||
|
||||
r($changes2['0']) && p('field') && e('status');
|
||||
r($changes2['0']) && p('old') && e('closed');
|
||||
r($changes2['0']) && p('new') && e('doing');
|
||||
|
||||
r($changes3['0']) && p('field') && e('status');
|
||||
r($changes3['0']) && p('old') && e('suspended');
|
||||
r($changes3['0']) && p('new') && e('doing');
|
||||
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/project.class.php';
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 projectModel->activate();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
|
||||
*/
|
||||
|
||||
function initData()
|
||||
{
|
||||
$project = zdTable('project');
|
||||
$project->id->range('2-5');
|
||||
$project->project->range('2-5');
|
||||
$project->name->prefix("项目")->range('2-5');
|
||||
$project->code->prefix("project")->range('2-5');
|
||||
$project->model->range("scrum");
|
||||
$project->auth->range("[]");
|
||||
$project->path->range("[]");
|
||||
$project->type->range("project");
|
||||
$project->grade->range("1");
|
||||
$project->days->range("1");
|
||||
$project->status->range("closed, suspended");
|
||||
$project->desc->range("[]");
|
||||
$project->budget->range("100000,200000");
|
||||
$project->budgetUnit->range("CNY");
|
||||
$project->percent->range("0-0");
|
||||
|
||||
$project->gen(2);
|
||||
}
|
||||
|
||||
initData();
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('project');
|
||||
$project = new Project();
|
||||
$data = new stdClass();
|
||||
|
||||
$data->status = 'doing';
|
||||
$data->begin = '2022-10-10';
|
||||
$data->end = '2022-10-10';
|
||||
$data->status = 'doing';
|
||||
$data->comment = 'fgasgqasfdgasfgasg';
|
||||
$data->readjustTime = 1;
|
||||
$data->readjustTask = 1;
|
||||
|
||||
r(strlen($tester->project->doActivate(2, $data))) && p() && e(true); // 判断是否更新无报错 true
|
||||
r(strlen($tester->project->doActivate(3, $data))) && p() && e(true); // 判断是否更新无报错 true
|
||||
@@ -45,5 +45,5 @@ $tester->loadModel('project');
|
||||
|
||||
initData();
|
||||
|
||||
r($tester->project->getByID(2, 'project')) && p('code,type') && e('project2,project'); //获取ID等于11的项目
|
||||
r($tester->project->getByID(1, 'project')) && p('code') && e('0'); //获取不存在的项目
|
||||
r($tester->project->getByID(2)) && p('code,type') && e('project2,project'); //获取ID等于11的项目
|
||||
r($tester->project->getByID(1)) && p('code') && e('0'); //获取不存在的项目
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 projectModel->start();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
延期ID为81的项目,查看延期后的日期 >> 2023-07-01
|
||||
延期ID为0的项目,返回空 >> 0
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('project');
|
||||
$_POST['end'] = '2023-07-01';
|
||||
|
||||
$changes1 = $tester->project->putoff(81);
|
||||
$changes2 = $tester->project->putoff(0);
|
||||
|
||||
r($changes1[0]) && p('new') && e('2023-07-01'); // 延期ID为81的项目,查看延期后的日期
|
||||
r($changes2) && p() && e('0'); // 延期ID为0的项目,返回空
|
||||
@@ -22,12 +22,12 @@ title=测试 projectModel::getByID;
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行project模块的suspend方法,参数是2- ,属性0 @suspended
|
||||
@suspended
|
||||
- 执行project模块的suspend方法,参数是5- ,属性1 @suspendedDate
|
||||
@suspendedDate
|
||||
- 执行project模块的suspend方法,参数是4- ,属性0 @suspended
|
||||
@suspended
|
||||
- 执行project模块的suspend方法,参数是2, $project,属性0 @suspended
|
||||
|
||||
- 执行project模块的suspend方法,参数是5, $project,属性1 @suspendedDate
|
||||
|
||||
- 执行project模块的suspend方法,参数是4, $project,属性0 @suspended
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -37,6 +37,12 @@ $tester->loadModel('project');
|
||||
|
||||
initData();
|
||||
|
||||
r($tester->project->suspend(2)) && p('0:new') && e('suspended');
|
||||
r($tester->project->suspend(5)) && p('1:field') && e('suspendedDate');
|
||||
r($tester->project->suspend(4)) && p('0:new') && e('suspended');
|
||||
$project = new stdClass;
|
||||
$project->status = 'suspended';
|
||||
$project->lastEditedBy = 'admin';
|
||||
$project->lastEditedDate = '2023-04-27';
|
||||
$project->suspendedDate = '2023-05-03';
|
||||
|
||||
r($tester->project->suspend(2, $project)) && p('0:new') && e('suspended');
|
||||
r($tester->project->suspend(5, $project)) && p('1:field') && e('suspendedDate');
|
||||
r($tester->project->suspend(4, $project)) && p('0:new') && e('suspended');
|
||||
@@ -100,4 +100,30 @@ class Project
|
||||
if(dao::isError()) return array('message' => dao::getError());
|
||||
return $projectIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a project.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param object $project
|
||||
* @access public
|
||||
* @return array $changes
|
||||
*/
|
||||
public function activate($projectID, $project)
|
||||
{
|
||||
return $this->project->activate($projectID, $project);
|
||||
}
|
||||
|
||||
/**
|
||||
* doActivate a project.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param object $project
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function doActivate($projectID, $project)
|
||||
{
|
||||
return $this->project->doActivate($projectID, $project);
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/project.class.php';
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 projectTao::doSuspend();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行project模块的doSuspend方法,参数是2, $project @1
|
||||
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('project');
|
||||
|
||||
$project = new stdClass;
|
||||
$project->id = 2;
|
||||
$project->status = 'suspended';
|
||||
$project->lastEditedBy = 'admin';
|
||||
$project->lastEditedDate = '2023-04-27';
|
||||
$project->suspendedDate = '2023-04-27';
|
||||
|
||||
r($tester->project->doSuspend(2, $project)) && p() && e(1);
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
su('admin');
|
||||
|
||||
function initData()
|
||||
{
|
||||
$project = zdTable('project');
|
||||
$project->id->range('2-5');
|
||||
$project->project->range('2-5');
|
||||
$project->name->prefix("项目")->range('2-5');
|
||||
$project->code->prefix("project")->range('2-5');
|
||||
$project->model->range("scrum");
|
||||
$project->auth->range("[]");
|
||||
$project->path->range("[]");
|
||||
$project->type->range("project");
|
||||
$project->grade->range("1");
|
||||
$project->days->range("1");
|
||||
$project->status->range("wait");
|
||||
$project->desc->range("[]");
|
||||
$project->budget->range("100000,200000");
|
||||
$project->budgetUnit->range("CNY");
|
||||
$project->percent->range("0-0");
|
||||
|
||||
$project->gen(4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=测试 projectModel::fetchProjectInfo;
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('project');
|
||||
|
||||
initData();
|
||||
|
||||
r($tester->project->fetchProjectInfo(2)) && p('code,type') && e('project2,project'); //获取ID等于11的项目
|
||||
r($tester->project->fetchProjectInfo(1)) && p('code') && e('0'); //获取不存在的项目
|
||||
+51
-5
@@ -289,6 +289,29 @@ class projectZen extends project
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append extras data to post data.
|
||||
*
|
||||
* @param int $iprojectID
|
||||
* @param object $postData
|
||||
*
|
||||
* @access protected
|
||||
* @return object
|
||||
*/
|
||||
protected function prepareSuspendExtras(int $projectID, object $postData): object
|
||||
{
|
||||
$editorIdList = $this->config->project->editor->suspend['id'];
|
||||
if($this->app->rawModule == 'program') $editorIdList = $this->config->program->editor->suspend['id'];
|
||||
|
||||
return $postData->add('id', $projectID)
|
||||
->setDefault('status', 'suspended')
|
||||
->setDefault('lastEditedBy', $this->app->user->account)
|
||||
->setDefault('lastEditedDate', helper::now())
|
||||
->setDefault('suspendedDate', helper::today())
|
||||
->stripTags($editorIdList, $this->config->allowedTags)
|
||||
->remove('comment')->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send variables to view page.
|
||||
*
|
||||
@@ -312,7 +335,7 @@ class projectZen extends project
|
||||
* @param array $changes
|
||||
* @param string $comment
|
||||
* @access protected
|
||||
* @return void
|
||||
* @return void
|
||||
*/
|
||||
protected function responseAfterStart(object $project, array $changes, string $comment): void
|
||||
{
|
||||
@@ -327,19 +350,42 @@ class projectZen extends project
|
||||
$this->executeHooks($project->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* After suspending the project, do other operations.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param array $changes
|
||||
* @param string $comment
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function responseAfterSuspend(int $projectID, array $changes, string $comment): void
|
||||
{
|
||||
if($comment != '' or !empty($changes))
|
||||
{
|
||||
$actionID = $this->loadModel('action')->create('project', $projectID, 'Suspended', $comment);
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
|
||||
$this->loadModel('common')->syncPPEStatus($projectID);
|
||||
|
||||
$this->executeHooks($projectID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send variables to suspend page.
|
||||
*
|
||||
* @param object $project
|
||||
* @param int $projectID
|
||||
* @access protected
|
||||
* @return int
|
||||
*/
|
||||
protected function buildSuspendForm(object $project): void
|
||||
protected function buildSuspendForm(int $projectID): void
|
||||
{
|
||||
$this->view->title = $this->lang->project->suspend;
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noletter');
|
||||
$this->view->actions = $this->action->getList('project', $project->id);
|
||||
$this->view->project = $this->project->getByID($project->id);
|
||||
$this->view->actions = $this->loadModel('action')->getList('project', $projectID);
|
||||
$this->view->project = $this->project->getByID($projectID);
|
||||
$this->display();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
$config->task->form = new stdclass();
|
||||
|
||||
global $app;
|
||||
$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' => 'date', 'required' => false, 'default' => helper::now());
|
||||
$config->task->form->assign['assignedDate'] = array('type' => 'date', 'required' => false, 'default' => helper::now());
|
||||
$config->task->form->assign['comment'] = array('type' => 'text', 'required' => false, 'default' => '');
|
||||
+8
-54
@@ -808,68 +808,22 @@ class task extends control
|
||||
|
||||
if(!empty($_POST))
|
||||
{
|
||||
$this->loadModel('action');
|
||||
$changes = $this->task->assign($taskID);
|
||||
$data = form::data($this->config->task->form->assign)->get();
|
||||
$postComment = $data->comment;
|
||||
unset($data->comment);
|
||||
|
||||
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()));
|
||||
}
|
||||
$changes = $this->task->assign($data, $taskID);
|
||||
if(dao::isError()) return $this->taskZen->errorAfterAssignTo();
|
||||
|
||||
$actionID = $this->action->create('task', $taskID, 'Assigned', $this->post->comment, $this->post->assignedTo);
|
||||
$actionID = $this->loadModel('action')->create('task', $taskID, 'Assigned', $postComment, $this->post->assignedTo);
|
||||
$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())
|
||||
{
|
||||
$task = $this->task->getById($taskID);
|
||||
$execution = $this->execution->getByID($task->execution);
|
||||
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
|
||||
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
|
||||
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')
|
||||
{
|
||||
$taskSearchValue = $this->session->taskSearchValue ? $this->session->taskSearchValue : '';
|
||||
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $taskSearchValue);
|
||||
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
|
||||
$kanbanData = $kanbanData[$kanbanType];
|
||||
$kanbanData = json_encode($kanbanData);
|
||||
|
||||
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
|
||||
}
|
||||
return print(js::closeModal('parent.parent', 'this'));
|
||||
}
|
||||
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
|
||||
return $this->taskZen->reponseAfterAssignTo($changes);
|
||||
}
|
||||
|
||||
$members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');
|
||||
|
||||
/* Compute next assignedTo. */
|
||||
if(!empty($task->team) and strpos('done,cencel,closed', $task->status) === false)
|
||||
{
|
||||
$task->nextUser = $this->task->getAssignedTo4Multi($task->team, $task, 'next');
|
||||
$members = $this->task->getMemberPairs($task);
|
||||
}
|
||||
|
||||
if(!isset($members[$task->assignedTo])) $members[$task->assignedTo] = $task->assignedTo;
|
||||
if(isset($members['closed']) or $task->status == 'closed') $members['closed'] = 'Closed';
|
||||
|
||||
$this->view->title = $this->view->execution->name . $this->lang->colon . $this->lang->task->assign;
|
||||
$this->view->position[] = $this->lang->task->assign;
|
||||
$this->view->task = $task;
|
||||
$this->view->members = $members;
|
||||
$this->view->users = $this->loadModel('user')->getPairs();
|
||||
$this->display();
|
||||
$this->taskZen->buildAssignToForm($executionID, $task);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+23
-173
@@ -830,12 +830,11 @@ class taskModel extends model
|
||||
{
|
||||
$now = helper::now();
|
||||
$oldTeam = zget($oldTask, 'team', array());
|
||||
$members = array_map(function($member){ return $member->account; }, $team);
|
||||
$members = array_map(function($member){return $member->account;}, $team);
|
||||
$currentTask = !empty($task) ? $task : new stdclass();
|
||||
if(!isset($currentTask->status)) $currentTask->status = $oldTask->status;
|
||||
$oldTask->team = $team;
|
||||
|
||||
$currentTask->assignedTo = $oldTask->assignedTo;
|
||||
if(!empty($_POST['assignedTo']) and is_string($_POST['assignedTo']))
|
||||
{
|
||||
$currentTask->assignedTo = $this->post->assignedTo;
|
||||
@@ -844,6 +843,8 @@ class taskModel extends model
|
||||
{
|
||||
$currentTask->assignedTo = $this->getAssignedTo4Multi($members, $oldTask);
|
||||
if($oldTask->assignedTo != $currentTask->assignedTo) $currentTask->assignedDate = $now;
|
||||
|
||||
$oldTask->team = $oldTeam;
|
||||
}
|
||||
|
||||
$currentTask->estimate = 0;
|
||||
@@ -858,50 +859,7 @@ class taskModel extends model
|
||||
$currentTask->consumed = 0;
|
||||
foreach($efforts as $effort) $currentTask->consumed += (float)$effort->consumed;
|
||||
|
||||
$oldTask->team = $oldTeam;
|
||||
|
||||
if(!empty($task))
|
||||
{
|
||||
if(!$autoStatus) return $currentTask;
|
||||
|
||||
if($currentTask->consumed == 0 and empty($efforts))
|
||||
{
|
||||
if(!isset($task->status)) $currentTask->status = 'wait';
|
||||
$currentTask->finishedBy = '';
|
||||
$currentTask->finishedDate = '';
|
||||
}
|
||||
|
||||
if($currentTask->consumed > 0 && $currentTask->left > 0)
|
||||
{
|
||||
$currentTask->status = 'doing';
|
||||
$currentTask->finishedBy = '';
|
||||
$currentTask->finishedDate = '';
|
||||
}
|
||||
|
||||
if($currentTask->consumed > 0 and $currentTask->left == 0)
|
||||
{
|
||||
$finisedUsers = $this->getFinishedUsers($oldTask->id, $members);
|
||||
if(count($finisedUsers) != count($team))
|
||||
{
|
||||
if(strpos('cancel,pause', $oldTask->status) === false or ($oldTask->status == 'closed' and $oldTask->reason == 'done'))
|
||||
{
|
||||
$currentTask->status = 'doing';
|
||||
$currentTask->finishedBy = '';
|
||||
$currentTask->finishedDate = '';
|
||||
}
|
||||
}
|
||||
elseif(strpos('wait,doing,pause', $oldTask->status) !== false)
|
||||
{
|
||||
$currentTask->status = 'done';
|
||||
$currentTask->assignedTo = $oldTask->openedBy;
|
||||
$currentTask->assignedDate = $now;
|
||||
$currentTask->finishedBy = $this->app->user->account;
|
||||
$currentTask->finishedDate = $task->finishedDate;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentTask;
|
||||
}
|
||||
if(!empty($task)) return $this->taskTao->computeCurrentTaskStatus($currentTask, $oldTask, $task, $autoStatus, empty($efforts), $members);
|
||||
$this->dao->update(TABLE_TASK)->data($currentTask)->autoCheck()->where('id')->eq($oldTask->id)->exec();
|
||||
}
|
||||
}
|
||||
@@ -1637,24 +1595,16 @@ class taskModel extends model
|
||||
/**
|
||||
* Assign a task to a user again.
|
||||
*
|
||||
* @param object $task
|
||||
* @param int $taskID
|
||||
* @access public
|
||||
* @return void
|
||||
* @return array
|
||||
*/
|
||||
public function assign($taskID)
|
||||
public function assign($task, $taskID): array|false
|
||||
{
|
||||
$task->id = $taskID;
|
||||
$oldTask = $this->getById($taskID);
|
||||
|
||||
$now = helper::now();
|
||||
$task = fixer::input('post')
|
||||
->add('id', $taskID)
|
||||
->cleanFloat('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')
|
||||
->get();
|
||||
if($oldTask->status != 'done' and $oldTask->status != 'closed' and isset($task->left) and $task->left == 0)
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->task->left);
|
||||
@@ -2405,12 +2355,12 @@ class taskModel extends model
|
||||
->andWhere('t1.vision')->eq($this->config->vision)
|
||||
->fetch();
|
||||
if(!$task) return false;
|
||||
$task->openedDate = substr($task->openedDate, 0, 19);
|
||||
$task->finishedDate = substr($task->finishedDate, 0, 19);
|
||||
$task->canceledDate = substr($task->canceledDate, 0, 19);
|
||||
$task->closedDate = substr($task->closedDate, 0, 19);
|
||||
$task->lastEditedDate = substr($task->lastEditedDate, 0, 19);
|
||||
$task->realStarted = substr($task->realStarted, 0, 19);
|
||||
$task->openedDate = !empty($task->openedDate) ? substr($task->openedDate, 0, 19) : null;
|
||||
$task->finishedDate = !empty($task->finishedDate) ? substr($task->finishedDate, 0, 19) : null;
|
||||
$task->canceledDate = !empty($task->canceledDate) ? substr($task->canceledDate, 0, 19) : null;
|
||||
$task->closedDate = !empty($task->closedDate) ? substr($task->closedDate, 0, 19) : null;
|
||||
$task->lastEditedDate = !empty($task->lastEditedDate) ? substr($task->lastEditedDate, 0, 19) : null;
|
||||
$task->realStarted = !empty($task->realStarted) ? substr($task->realStarted, 0, 19) : null;
|
||||
|
||||
$children = $this->dao->select('*')->from(TABLE_TASK)->where('parent')->eq($taskID)->andWhere('deleted')->eq(0)->fetchAll('id');
|
||||
$task->children = $children;
|
||||
@@ -2432,10 +2382,6 @@ class taskModel extends model
|
||||
if($setImgSize) $task->desc = $this->file->setImgSize($task->desc);
|
||||
|
||||
if($task->assignedTo == 'closed') $task->assignedToRealName = 'Closed';
|
||||
foreach($task as $key => $value)
|
||||
{
|
||||
if((strpos($key, 'Date') !== false or strpos('estStarted|deadline', $key) !== false) and !(int)substr($value, 0, 4)) $task->$key = '';
|
||||
}
|
||||
$task->files = $this->loadModel('file')->getByObject('task', $taskID);
|
||||
|
||||
/* Get related test cases. */
|
||||
@@ -2521,32 +2467,15 @@ class taskModel extends model
|
||||
if(empty($tasks)) return array();
|
||||
|
||||
$parents = array();
|
||||
$taskTeam = $this->taskTao->getTeamByIdList(array_keys($tasks));
|
||||
$taskTeam = $this->taskTao->getTeamMembersByIdList(array_keys($tasks));
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if(isset($taskTeam[$task->id])) $tasks[$task->id]->team = $taskTeam[$task->id];
|
||||
if($task->parent > 0) $parents[$task->parent] = $task->parent;
|
||||
}
|
||||
$parents = $this->getByList($parents);
|
||||
|
||||
if($this->config->vision == 'lite') $tasks = $this->appendLane($tasks);
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if($task->parent > 0)
|
||||
{
|
||||
if(isset($tasks[$task->parent]))
|
||||
{
|
||||
$tasks[$task->parent]->children[$task->id] = $task;
|
||||
unset($tasks[$task->id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$parent = $parents[$task->parent];
|
||||
$task->parentName = $parent->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tasks = $this->taskTao->restructureHierarchy($tasks);
|
||||
return $this->processTasks($tasks);
|
||||
}
|
||||
|
||||
@@ -2615,42 +2544,17 @@ class taskModel extends model
|
||||
public function getUserTasks($account, $type = 'assignedTo', $limit = 0, $pager = null, $orderBy = "id_desc", $projectID = 0)
|
||||
{
|
||||
if(!$this->loadModel('common')->checkField(TABLE_TASK, $type)) return array();
|
||||
$orderBy = str_replace('pri_', 'priOrder_', $orderBy);
|
||||
$orderBy = str_replace('project_', 't1.project_', $orderBy);
|
||||
$tasks = $this->dao->select("t1.*, t4.id as project, t2.id as executionID, t2.name as executionName, t4.name as projectName, t2.multiple as executionMultiple, t2.type as executionType, t3.id as storyID, t3.title as storyTitle, t3.status AS storyStatus, t3.version AS latestStoryVersion, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder")
|
||||
->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_EXECUTION)->alias('t2')->on("t1.execution = t2.id")
|
||||
->leftJoin(TABLE_STORY)->alias('t3')->on('t1.story = t3.id')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t4')->on("t2.project = t4.id")
|
||||
->leftJoin(TABLE_TASKTEAM)->alias('t5')->on("t5.task = t1.id and t5.account = '{$account}'")
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->beginIF($this->config->vision)->andWhere('t1.vision')->eq($this->config->vision)->fi()
|
||||
->beginIF($this->config->vision)->andWhere('t2.vision')->eq($this->config->vision)->fi()
|
||||
->beginIF($type != 'closedBy' and $this->app->moduleName == 'block')->andWhere('t1.status')->ne('closed')->fi()
|
||||
->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.execution')->in($this->app->user->view->sprints)->fi()
|
||||
->beginIF($type == 'finishedBy')
|
||||
->andWhere('t1.finishedby', 1)->eq($account)
|
||||
->orWhere('t5.status')->eq("done")
|
||||
->markRight(1)
|
||||
->fi()
|
||||
->beginIF($type == 'assignedTo' and ($this->app->rawModule == 'my' or $this->app->rawModule == 'block'))->andWhere('t2.status', true)->ne('suspended')->orWhere('t4.status')->ne('suspended')->markRight(1)->fi()
|
||||
->beginIF($type != 'all' and $type != 'finishedBy' and $type != 'assignedTo')->andWhere("t1.`$type`")->eq($account)->fi()
|
||||
->beginIF($type == 'assignedTo')->andWhere("(t1.assignedTo = '{$account}' or (t1.mode = 'multi' and t5.`account` = '{$account}' and t1.status != 'closed' and t5.status != 'done') )")->fi()
|
||||
->beginIF($type == 'assignedTo' and $this->app->rawModule == 'my' and $this->app->rawMethod == 'work')->andWhere('t1.status')->notin('closed,cancel')->fi()
|
||||
->orderBy($orderBy)
|
||||
->beginIF($limit > 0)->limit($limit)->fi()
|
||||
->page($pager, 't1.id')
|
||||
->fetchAll('id');
|
||||
|
||||
$tasks = $this->taskTao->fetchUserTasksByType($account, $type, $orderBy, $projectID, $limit, $pager);
|
||||
|
||||
if(!$tasks) return array();
|
||||
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'task', false);
|
||||
|
||||
$taskTeam = $this->dao->select('*')->from(TABLE_TASKTEAM)->where('task')->in(array_keys($tasks))->fetchGroup('task');
|
||||
$taskTeam = $this->taskTao->getTeamMembersByIdList(array_keys($tasks));
|
||||
foreach($taskTeam as $taskID => $team) $tasks[$taskID]->team = $team;
|
||||
|
||||
if($tasks) return $this->processTasks($tasks);
|
||||
return array();
|
||||
return $this->processTasks($tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2732,29 +2636,7 @@ class taskModel extends model
|
||||
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->fetchAll('id');
|
||||
|
||||
$parents = array();
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if($task->parent > 0) $parents[$task->parent] = $task->parent;
|
||||
}
|
||||
$parents = $this->getByList($parents);
|
||||
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if($task->parent > 0)
|
||||
{
|
||||
if(isset($tasks[$task->parent]))
|
||||
{
|
||||
$tasks[$task->parent]->children[$task->id] = $task;
|
||||
unset($tasks[$task->id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$parent = $parents[$task->parent];
|
||||
$task->parentName = $parent->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
$tasks = $this->taskTao->restructureHierarchy($tasks);
|
||||
|
||||
return $this->taskTao->computeTasksProgress($tasks);
|
||||
}
|
||||
@@ -3949,38 +3831,6 @@ class taskModel extends model
|
||||
return array($toList, $ccList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next user.
|
||||
*
|
||||
* @param string $users
|
||||
* @param object $task
|
||||
* @param string $type current|next
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAssignedTo4Multi($users, $task, $type = 'current')
|
||||
{
|
||||
if(empty($task->team) or $task->mode != 'linear') return $task->assignedTo;
|
||||
|
||||
$teamHours = array_values($task->team);
|
||||
|
||||
/* Process user */
|
||||
if(!is_array($users)) $users = explode(',', trim($users, ','));
|
||||
$users = array_values($users);
|
||||
if(is_object($users[0])) $users = array_map(function($member){ return $member->account; }, $users);
|
||||
|
||||
foreach($users as $i => $account)
|
||||
{
|
||||
if(isset($teamHours[$i]) and $teamHours[$i]->status == 'done') continue;
|
||||
if($type == 'current') return $account;
|
||||
break;
|
||||
}
|
||||
if($type == 'next' and isset($users[$i + 1])) return $users[$i + 1];
|
||||
|
||||
return $task->openedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task's team member pairs.
|
||||
*
|
||||
|
||||
+175
-18
@@ -18,24 +18,13 @@ class taskTao extends taskModel
|
||||
*
|
||||
* @param object $task
|
||||
* @access private
|
||||
* @return int
|
||||
* @return float
|
||||
*/
|
||||
protected function computeTaskProgress(object $task): float
|
||||
{
|
||||
if($task->consumed == 0 and $task->left == 0)
|
||||
{
|
||||
$progress = 0;
|
||||
}
|
||||
elseif($task->consumed != 0 and $task->left == 0)
|
||||
{
|
||||
$progress = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
$progress = round($task->consumed / ($task->consumed + $task->left), 2) * 100;
|
||||
}
|
||||
|
||||
return $progress;
|
||||
if($task->consumed == 0 and $task->left == 0) return 0;
|
||||
if($task->consumed != 0 and $task->left == 0) return 100;
|
||||
return round($task->consumed / ($task->consumed + $task->left), 2) * 100;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +62,7 @@ class taskTao extends taskModel
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @access protected
|
||||
* @return array
|
||||
* @return object[]
|
||||
*/
|
||||
protected function fetchExecutionTasks(int $executionID, int $productID = 0, string|array $type = 'all', array $modules = array(), string $orderBy = 'status_asc, id_desc', object $pager = null): array
|
||||
{
|
||||
@@ -123,15 +112,58 @@ class taskTao extends taskModel
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch user tasks by type.
|
||||
*
|
||||
* @param string $account
|
||||
* @param string $type assignedTo|finishedBy|closedBy
|
||||
* @param string $orderBy
|
||||
* @param int $projectID
|
||||
* @access protected
|
||||
* @return object[]
|
||||
*/
|
||||
protected function fetchUserTasksByType(string $account, string $type, string $orderBy, int $projectID, int $limit, object|null $pager): array
|
||||
{
|
||||
$orderBy = str_replace('pri_', 'priOrder_', $orderBy);
|
||||
$orderBy = str_replace('project_', 't1.project_', $orderBy);
|
||||
|
||||
return $this->dao->select("t1.*, t4.id as project, t2.id as executionID, t2.name as executionName, t4.name as projectName, t2.multiple as executionMultiple, t2.type as executionType, t3.id as storyID, t3.title as storyTitle, t3.status AS storyStatus, t3.version AS latestStoryVersion, IF(t1.`pri` = 0, {$this->config->maxPriValue}, t1.`pri`) as priOrder")
|
||||
->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_EXECUTION)->alias('t2')->on("t1.execution = t2.id")
|
||||
->leftJoin(TABLE_STORY)->alias('t3')->on('t1.story = t3.id')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t4')->on("t2.project = t4.id")
|
||||
->leftJoin(TABLE_TASKTEAM)->alias('t5')->on("t5.task = t1.id and t5.account = '{$account}'")
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->beginIF($this->config->vision)->andWhere('t1.vision')->eq($this->config->vision)->fi()
|
||||
->beginIF($this->config->vision)->andWhere('t2.vision')->eq($this->config->vision)->fi()
|
||||
->beginIF($type != 'closedBy' and $this->app->moduleName == 'block')->andWhere('t1.status')->ne('closed')->fi()
|
||||
->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
|
||||
->beginIF(!$this->app->user->admin)->andWhere('t1.execution')->in($this->app->user->view->sprints)->fi()
|
||||
->beginIF($type == 'finishedBy')
|
||||
->andWhere('t1.finishedby', 1)->eq($account)
|
||||
->orWhere('t5.status')->eq("done")
|
||||
->markRight(1)
|
||||
->fi()
|
||||
->beginIF($type == 'assignedTo' and ($this->app->rawModule == 'my' or $this->app->rawModule == 'block'))->andWhere('t2.status', true)->ne('suspended')->orWhere('t4.status')->ne('suspended')->markRight(1)->fi()
|
||||
->beginIF($type != 'all' and $type != 'finishedBy' and $type != 'assignedTo')->andWhere("t1.`$type`")->eq($account)->fi()
|
||||
->beginIF($type == 'assignedTo')->andWhere("(t1.assignedTo = '{$account}' or (t1.mode = 'multi' and t5.`account` = '{$account}' and t1.status != 'closed' and t5.status != 'done') )")->fi()
|
||||
->beginIF($type == 'assignedTo' and $this->app->rawModule == 'my' and $this->app->rawMethod == 'work')->andWhere('t1.status')->notin('closed,cancel')->fi()
|
||||
->orderBy($orderBy)
|
||||
->beginIF($limit > 0)->limit($limit)->fi()
|
||||
->page($pager, 't1.id')
|
||||
->fetchAll('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task team by id list.
|
||||
* 通过任务ID列表查询任务团队信息。
|
||||
*
|
||||
* @param array $taskIdList
|
||||
* @access protected
|
||||
* @return array
|
||||
* @return object[]
|
||||
*/
|
||||
protected function getTeamByIdList(array $taskIdList): array
|
||||
protected function getTeamMembersByIdList(array $taskIdList): array
|
||||
{
|
||||
return $this->dao->select('*')->from(TABLE_TASKTEAM)->where('task')->in($taskIdList)->fetchGroup('task');
|
||||
}
|
||||
@@ -152,4 +184,129 @@ class taskTao extends taskModel
|
||||
->fetchAll('id');
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedTo for the multiply linear task.
|
||||
* 获取多人串行任务的指派人。
|
||||
*
|
||||
* @param string|array $members
|
||||
* @param object $task
|
||||
* @param string $type current|next
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function getAssignedTo4Multi(string|array $members, object $task, string $type = 'current'): string
|
||||
{
|
||||
if(empty($task->team) or $task->mode != 'linear') return $task->assignedTo;
|
||||
|
||||
/* Format task team members. */
|
||||
if(!is_array($members)) $members = explode(',', trim($members, ','));
|
||||
$members = array_values($members);
|
||||
if(is_object($members[0])) $members = array_map(function($member){return $member->account;}, $members);
|
||||
|
||||
/* Get the member of the first unfinished task. */
|
||||
$teamHours = array_values($task->team);
|
||||
foreach($members as $i => $account)
|
||||
{
|
||||
if(isset($teamHours[$i]) and $teamHours[$i]->status == 'done') continue;
|
||||
if($type == 'current') return $account;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the member of the second unfinished task. */
|
||||
if($type == 'next' and isset($members[$i + 1])) return $members[$i + 1];
|
||||
|
||||
return $task->openedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the hierarchy of tasks to a parent-child structure.
|
||||
* 将任务的层级改为父子结构。
|
||||
*
|
||||
* @param array $tasks
|
||||
* @access protected
|
||||
* @return object[]
|
||||
*/
|
||||
protected function restructureHierarchy(array $tasks): array
|
||||
{
|
||||
$parentIdList = array();
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if($task->parent <= 0 or isset($tasks[$task->parent]) or isset($parentIdList[$task->parent])) continue;
|
||||
$parentIdList[$task->parent] = $task->parent;
|
||||
}
|
||||
|
||||
$parents = $this->getByList($parentIdList);
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if($task->parent <= 0) continue;
|
||||
if(isset($tasks[$task->parent]))
|
||||
{
|
||||
$tasks[$task->parent]->children[$task->id] = $task;
|
||||
unset($tasks[$task->id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$parent = $parents[$task->parent];
|
||||
$task->parentName = $parent->name;
|
||||
}
|
||||
}
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the status of the current task.
|
||||
* 计算当前任务的状态。
|
||||
*
|
||||
* @param object $currentTask
|
||||
* @param object $oldTask
|
||||
* @param object $task
|
||||
* @param bool $condition true|false
|
||||
* @param bool $hasEfforts true|false
|
||||
* @param int $teamCount
|
||||
* @access protected
|
||||
* @return object
|
||||
*/
|
||||
protected function computeCurrentTaskStatus(object $currentTask, object $oldTask, object $task, bool $autoStatus, bool $hasEfforts, array $members): object
|
||||
{
|
||||
if(!$autoStatus) return $currentTask;
|
||||
|
||||
if($currentTask->consumed == 0 and $hasEfforts)
|
||||
{
|
||||
if(!isset($task->status)) $currentTask->status = 'wait';
|
||||
$currentTask->finishedBy = null;
|
||||
$currentTask->finishedDate = null;
|
||||
}
|
||||
|
||||
if($currentTask->consumed > 0 && $currentTask->left > 0)
|
||||
{
|
||||
$currentTask->status = 'doing';
|
||||
$currentTask->finishedBy = null;
|
||||
$currentTask->finishedDate = null;
|
||||
}
|
||||
|
||||
if($currentTask->consumed > 0 and $currentTask->left == 0)
|
||||
{
|
||||
$finisedUsers = $this->getFinishedUsers($oldTask->id, $members);
|
||||
if(count($finisedUsers) != count($members))
|
||||
{
|
||||
if(strpos('cancel,pause', $oldTask->status) === false or ($oldTask->status == 'closed' and $oldTask->reason == 'done'))
|
||||
{
|
||||
$currentTask->status = 'doing';
|
||||
$currentTask->finishedBy = null;
|
||||
$currentTask->finishedDate = null;
|
||||
}
|
||||
}
|
||||
elseif(strpos('wait,doing,pause', $oldTask->status) !== false)
|
||||
{
|
||||
$currentTask->status = 'done';
|
||||
$currentTask->assignedTo = $oldTask->openedBy;
|
||||
$currentTask->assignedDate = helper::now();
|
||||
$currentTask->finishedBy = $this->app->user->account;
|
||||
$currentTask->finishedDate = $task->finishedDate;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,31 +7,84 @@ su('admin');
|
||||
/**
|
||||
|
||||
title=taskModel->assign();
|
||||
timeout=0
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
wait状态任务指派 >> assignedTo,po82,user92
|
||||
doing状态任务指派 >> assignedTo,,user93
|
||||
done状态任务指派 >> assignedTo,,user94
|
||||
pause状态任务指派 >> assignedTo,,user95
|
||||
cancel状态任务指派 >> assignedTo,,user96
|
||||
closed状态任务指派 >> assignedTo,,user97
|
||||
- 执行task模块的assign方法,参数是$taskIDlist[0],$waitTask
|
||||
- 第0条的field属性 @assignedTo
|
||||
- 第0条的old属性 @old1
|
||||
- 第0条的new属性 @user92
|
||||
|
||||
- 执行task模块的assign方法,参数是$taskIDlist[0],$waitTaskLeft
|
||||
- 第1条的field属性 @left
|
||||
- 第1条的old属性 @0
|
||||
- 第1条的new属性 @1
|
||||
|
||||
- 执行task模块的assign方法,参数是$taskIDlist[1],$doingTask
|
||||
- 第0条的field属性 @assignedTo
|
||||
- 第0条的old属性 @old2
|
||||
- 第0条的new属性 @user93
|
||||
|
||||
- 执行task模块的assign方法,参数是$taskIDlist[2],$doneTask
|
||||
- 第0条的field属性 @assignedTo
|
||||
- 第0条的old属性 @old3
|
||||
- 第0条的new属性 @user94
|
||||
|
||||
- 执行task模块的assign方法,参数是$taskIDlist[3],$pauseTask
|
||||
- 第0条的field属性 @assignedTo
|
||||
- 第0条的old属性 @old4
|
||||
- 第0条的new属性 @user95
|
||||
|
||||
- 执行task模块的assign方法,参数是$taskIDlist[4],$cancelTask
|
||||
- 第0条的field属性 @assignedTo
|
||||
- 第0条的old属性 @old5
|
||||
- 第0条的new属性 @user96
|
||||
|
||||
- 执行task模块的assign方法,参数是$taskIDlist[5],$closedTask
|
||||
- 第0条的field属性 @assignedTo
|
||||
- 第0条的old属性 @old6
|
||||
- 第0条的new属性 @user97
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
function initData()
|
||||
{
|
||||
$task = zdTable('task');
|
||||
$task->id->range('1-6');
|
||||
$task->execution->range('2,3,3,4');
|
||||
$task->name->prefix("任务")->range('1-6');
|
||||
$task->left->range('0');
|
||||
$task->assignedTo->prefix("old")->range('1-6');
|
||||
$task->status->range("wait,doing,done,pause,cancel,closed");
|
||||
|
||||
$task->gen(6);
|
||||
|
||||
$user = zdTable('user');
|
||||
$user->id->range('1-100');
|
||||
$user->account->range('1-100')->prefix('user');
|
||||
$user->password->range('f8e41d6c31824c01e5d67c61a8ae49e9,e10adc3949ba59abbe56e057f20f883e');
|
||||
$user->realname->range('1-100')->prefix("开发");
|
||||
$user->gen(50);
|
||||
}
|
||||
|
||||
initData();
|
||||
$taskIDlist = array('1','2','3','4','5','6');
|
||||
|
||||
$waitTask = array('assignedTo' => 'user92','status' => 'wait', 'left' => '1');
|
||||
$doingTask = array('assignedTo' => 'user93','status' => 'doing');
|
||||
$doneTask = array('assignedTo' => 'user94','status' => 'done');
|
||||
$pauseTask = array('assignedTo' => 'user95','status' => 'pause');
|
||||
$cancelTask = array('assignedTo' => 'user96','status' => 'cancel');
|
||||
$closedTask = array('assignedTo' => 'user97','status' => 'closed');
|
||||
$waitTask = array('assignedTo' => 'user92','status' => 'wait');
|
||||
$waitTaskLeft = array('assignedTo' => 'user91','status' => 'wait', 'left' => '1');
|
||||
$doingTask = array('assignedTo' => 'user93','status' => 'doing');
|
||||
$doneTask = array('assignedTo' => 'user94','status' => 'done');
|
||||
$pauseTask = array('assignedTo' => 'user95','status' => 'pause');
|
||||
$cancelTask = array('assignedTo' => 'user96','status' => 'cancel');
|
||||
$closedTask = array('assignedTo' => 'user97','status' => 'closed');
|
||||
|
||||
$task = new taskTest();
|
||||
r($task->assignTest($taskIDlist[0],$waitTask)) && p('0:field,old,new') && e('assignedTo,po82,user92'); // wait状态任务指派
|
||||
r($task->assignTest($taskIDlist[1],$doingTask)) && p('0:field,old,new') && e('assignedTo,,user93'); // doing状态任务指派
|
||||
r($task->assignTest($taskIDlist[2],$doneTask)) && p('0:field,old,new') && e('assignedTo,,user94'); // done状态任务指派
|
||||
r($task->assignTest($taskIDlist[3],$pauseTask)) && p('0:field,old,new') && e('assignedTo,,user95'); // pause状态任务指派
|
||||
r($task->assignTest($taskIDlist[4],$cancelTask)) && p('0:field,old,new') && e('assignedTo,,user96'); // cancel状态任务指派
|
||||
r($task->assignTest($taskIDlist[5],$closedTask)) && p('0:field,old,new') && e('assignedTo,,user97'); // closed状态任务指派
|
||||
r($task->assignTest($taskIDlist[0],$waitTask)) && p('0:field,old,new') && e('assignedTo,old1,user92'); // wait状态任务指派
|
||||
r($task->assignTest($taskIDlist[0],$waitTaskLeft)) && p('1:field,old,new') && e('left,0,1'); // wait状态任务指派修改预计剩余
|
||||
r($task->assignTest($taskIDlist[1],$doingTask)) && p('0:field,old,new') && e('assignedTo,old2,user93'); // doing状态任务指派
|
||||
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状态任务指派
|
||||
@@ -2,102 +2,169 @@
|
||||
<?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-5');
|
||||
$task->name->range('1-5')->prefix('任务');
|
||||
$task->mode->range('multi');
|
||||
$task->status->range('wait,doing,done,pause,cancel,closed');
|
||||
$task->assignedTo->range('admin,user1');
|
||||
$task->openedBy->range('admin,user2,user1');
|
||||
$task->gen(5);
|
||||
|
||||
$taskTeam = zdTable('taskteam');
|
||||
$taskTeam->id->range('1-20');
|
||||
$taskTeam->task->range('1{2},2{3},3{2},4{3}');
|
||||
$taskTeam->account->range('admin,user1,admin,user1,user2');
|
||||
$taskTeam->estimate->range('1{2},2{3},3,4{2},5');
|
||||
$taskTeam->left->range('1{2},0{3},1{3},0{2}');
|
||||
$taskTeam->consumed->range('0{11},1{4},0{2},1{3}');
|
||||
$taskTeam->status->range('wait{11},doing,done,done,done,wait,wait,doing,done,done');
|
||||
$taskTeam->gen(20);
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('task');
|
||||
|
||||
$taskIdList = array(1, 2, 3, 4, 5);
|
||||
$tasks = array();
|
||||
$oldTasks = array();
|
||||
foreach($taskIdList as $id)
|
||||
{
|
||||
$task = $tester->task->getByID($id);
|
||||
$oldTasks[] = $task;
|
||||
$tasks[] = $task;
|
||||
}
|
||||
|
||||
$tasks[0]->status = 'doing';
|
||||
$tasks[0]->finishedDate = null;
|
||||
|
||||
$tasks[1]->status = 'done';
|
||||
$tasks[1]->finishedDate = '2023-04-27';
|
||||
|
||||
$members1 = new stdclass();
|
||||
$members1->account = 'admin';
|
||||
$members1->estimate = 1;
|
||||
$members1->left = 1;
|
||||
|
||||
$members2 = new stdclass();
|
||||
$members2->account = 'user1';
|
||||
$members2->estimate = 2;
|
||||
$members2->left = 2;
|
||||
|
||||
$members3 = new stdclass();
|
||||
$members3->account = 'user3';
|
||||
$members3->estimate = 3;
|
||||
$members3->left = 3;
|
||||
|
||||
$members = array(array($members1, $members2), array($members3));
|
||||
|
||||
/**
|
||||
|
||||
title=taskModel->computeHours4Multiple();
|
||||
timeout=0
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
task状态为wait只有老task计算多人工时 >> 1,po82,wait,3,3,3
|
||||
task状态为wait有新老task计算多人工时 >> 1,po82,wait,3,3,3
|
||||
task状态为wait有新老task和团队计算多人工时 >> 1,po82,doing,3,3,3
|
||||
task状态为done只有老task计算多人工时 >> 903,po82,done,3,3,3
|
||||
task状态为done有新老task计算多人工时 >> 903,po82,done,3,3,3
|
||||
task状态为done有新老task和团队计算多人工时 >> 903,po82,doing,3,3,3
|
||||
task状态为pause只有老task计算多人工时 >> 910,,pause,9,12,9
|
||||
task状态为pause有新老task计算多人工时 >> 910,,pause,9,12,9
|
||||
task状态为pause只有老task计算多人工时 >> 910,po82,doing,3,3,3
|
||||
老task不存在的情况有新老task和团队计算多人工时 >> 0
|
||||
老task不存在的情况有新老task计算多人工时 >> 0
|
||||
新task不存在的情况有新老task和团队计算多人工时 >> 10001,po82,doing,3,3,3
|
||||
- 执行task模块的computeHours4Multiple方法,参数是$oldTasks[0]
|
||||
- 属性id @1
|
||||
- 属性assignedTo @admin
|
||||
- 属性status @doing
|
||||
- 属性estimate @5
|
||||
- 属性consumed @0
|
||||
- 属性left @4
|
||||
|
||||
- 执行task模块的computeHours4Multiple方法,参数是$oldTasks[1]
|
||||
- 属性id @2
|
||||
- 属性assignedTo @user1
|
||||
- 属性status @done
|
||||
- 属性estimate @13
|
||||
- 属性consumed @0
|
||||
- 属性left @0
|
||||
|
||||
- 执行task模块的computeHours4Multiple方法,参数是$oldTasks[2]
|
||||
- 属性id @3
|
||||
- 属性assignedTo @admin
|
||||
- 属性status @done
|
||||
- 属性estimate @15
|
||||
- 属性consumed @0
|
||||
- 属性left @4
|
||||
|
||||
- 执行task模块的computeHours4Multiple方法,参数是$oldTasks[3]
|
||||
- 属性id @4
|
||||
- 属性assignedTo @user1
|
||||
- 属性status @pause
|
||||
- 属性estimate @17
|
||||
- 属性consumed @0
|
||||
- 属性left @2
|
||||
|
||||
- 执行task模块的computeHours4Multiple方法,参数是$oldTasks[4]
|
||||
- 属性id @5
|
||||
- 属性assignedTo @admin
|
||||
- 属性status @cancel
|
||||
- 属性estimate @0
|
||||
- 属性consumed @0
|
||||
- 属性left @0
|
||||
|
||||
- 执行task模块的computeHours4Multiple方法,参数是$oldTasks[0], $tasks[0]
|
||||
- 属性id @1
|
||||
- 属性assignedTo @admin
|
||||
- 属性status @doing
|
||||
- 属性estimate @5
|
||||
- 属性consumed @0
|
||||
- 属性left @4
|
||||
|
||||
- 执行task模块的computeHours4Multiple方法,参数是$oldTasks[1], $tasks[1]
|
||||
- 属性id @2
|
||||
- 属性assignedTo @user1
|
||||
- 属性status @done
|
||||
- 属性estimate @13
|
||||
- 属性consumed @0
|
||||
- 属性left @0
|
||||
|
||||
- 执行task模块的computehours4multiple方法,参数是$oldTasks[0], $tasks[0], $members[0]
|
||||
- 属性id @1
|
||||
- 属性assignedTo @admin
|
||||
- 属性status @doing
|
||||
- 属性estimate @3
|
||||
- 属性consumed @0
|
||||
- 属性left @3
|
||||
|
||||
- 执行task模块的computehours4multiple方法,参数是$oldTasks[1], $tasks[1], $members[1]
|
||||
- 属性id @2
|
||||
- 属性assignedTo @user1
|
||||
- 属性status @done
|
||||
- 属性estimate @3
|
||||
- 属性consumed @0
|
||||
- 属性left @3
|
||||
|
||||
- 执行task模块的computehours4multiple方法,参数是$oldTasks[0], $tasks[0], $members[0], false
|
||||
- 属性id @1
|
||||
- 属性assignedTo @admin
|
||||
- 属性status @doing
|
||||
- 属性estimate @3
|
||||
- 属性consumed @0
|
||||
- 属性left @3
|
||||
|
||||
- 执行task模块的computehours4multiple方法,参数是$oldTasks[1], $tasks[1], $members[1], false
|
||||
- 属性id @2
|
||||
- 属性assignedTo @user1
|
||||
- 属性status @done
|
||||
- 属性estimate @3
|
||||
- 属性consumed @0
|
||||
- 属性left @3
|
||||
|
||||
|
||||
|
||||
*/
|
||||
$task1 = new stdclass();
|
||||
$task1->id = 1;
|
||||
$task1->status = 'wait';
|
||||
$task1->assignedTo = '';
|
||||
$task1->openedBy = '';
|
||||
|
||||
$task2 = new stdclass();
|
||||
$task2->id = 1;
|
||||
$task2->status = 'wait';
|
||||
$task2->assignedTo = 'user92';
|
||||
$task2->openedBy = '';
|
||||
|
||||
$task3 = new stdclass();
|
||||
$task3->id = 903;
|
||||
$task3->status = 'done';
|
||||
$task3->assignedTo = '';
|
||||
$task3->openedBy = '';
|
||||
|
||||
$task4 = new stdclass();
|
||||
$task4->id = 903;
|
||||
$task4->status = 'done';
|
||||
$task4->assignedTo = 'po82';
|
||||
$task4->openedBy = '';
|
||||
|
||||
$task5 = new stdclass();
|
||||
$task5->id = 910;
|
||||
$task5->status = 'pause';
|
||||
$task5->assignedTo = '';
|
||||
$task5->openedBy = '';
|
||||
|
||||
$task6 = new stdclass();
|
||||
$task6->id = 910;
|
||||
$task6->status = 'pause';
|
||||
$task6->assignedTo = '';
|
||||
$task6->openedBy = '';
|
||||
|
||||
$task7 = new stdclass();
|
||||
$task7->id = 100001;
|
||||
$task7->status = 'done';
|
||||
$task7->assignedTo = '';
|
||||
$task7->openedBy = '';
|
||||
|
||||
$task8 = new stdclass();
|
||||
$task8->id = 10001;
|
||||
$task8->status = 'wait';
|
||||
$task8->assignedTo = '';
|
||||
$task8->openedBy = '';
|
||||
|
||||
$user1 = new stdclass();
|
||||
$user1->account = 'po82';
|
||||
$user1->estimate = 1;
|
||||
$user1->consumed = 1;
|
||||
$user1->left = 1;
|
||||
|
||||
$user2 = new stdclass();
|
||||
$user2->account = 'user92';
|
||||
$user2->estimate = 2;
|
||||
$user2->consumed = 2;
|
||||
$user2->left = 2;
|
||||
|
||||
$team = array($user1, $user2);
|
||||
|
||||
$autoStatusList = array(true, false);
|
||||
|
||||
$task = new taskTest();
|
||||
r($task->computeHours4MultipleTest($task1)) && p('id,assignedTo,status,estimate,consumed,left') && e('1,po82,wait,3,3,3'); // task状态为wait只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($task1, $task2)) && p('id,assignedTo,status,estimate,consumed,left') && e('1,po82,wait,3,3,3'); // task状态为wait有新老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($task1, $task2, $team)) && p('id,assignedTo,status,estimate,consumed,left') && e('1,po82,doing,3,3,3'); // task状态为wait有新老task和团队计算多人工时
|
||||
r($task->computeHours4MultipleTest($task3)) && p('id,assignedTo,status,estimate,consumed,left') && e('903,po82,done,3,3,3'); // task状态为done只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($task3, $task4)) && p('id,assignedTo,status,estimate,consumed,left') && e('903,po82,done,3,3,3'); // task状态为done有新老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($task3, $task4, $team)) && p('id,assignedTo,status,estimate,consumed,left') && e('903,po82,doing,3,3,3'); // task状态为done有新老task和团队计算多人工时
|
||||
r($task->computeHours4MultipleTest($task5)) && p('id,assignedTo,status,estimate,consumed,left') && e('910,,pause,9,12,9'); // task状态为pause只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($task5, $task1, array(), false)) && p('id,assignedTo,status,estimate,consumed,left') && e('910,,pause,9,12,9'); // task状态为pause有新老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($task5, $task6, $team)) && p('id,assignedTo,status,estimate,consumed,left') && e('910,po82,doing,3,3,3'); // task状态为pause只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($task7)) && p('id,assignedTo,status,estimate,consumed,left') && e('0'); // 老task不存在的情况有新老task和团队计算多人工时
|
||||
r($task->computeHours4MultipleTest($task7, $task8)) && p('id,assignedTo,status,estimate,consumed,left') && e('0'); // 老task不存在的情况有新老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($task1, $task8, $team)) && p('id,assignedTo,status,estimate,consumed,left') && e('10001,po82,doing,3,3,3'); // 新task不存在的情况有新老task和团队计算多人工时
|
||||
r($task->computeHours4MultipleTest($oldTasks[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,5,0,4'); // taskID 1 只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($oldTasks[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,13,0,0'); // taskID 2 只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($oldTasks[2])) && p('id,assignedTo,status,estimate,consumed,left') && e('3,admin,done,15,0,4'); // taskID 3 只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($oldTasks[3])) && p('id,assignedTo,status,estimate,consumed,left') && e('4,user1,pause,17,0,2'); // taskID 4 只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($oldTasks[4])) && p('id,assignedTo,status,estimate,consumed,left') && e('5,admin,cancel,0,0,0'); // taskID 5 只有老task计算多人工时
|
||||
r($task->computeHours4MultipleTest($oldTasks[0], $tasks[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,5,0,4'); // taskID 1 有传入task计算多人工时
|
||||
r($task->computeHours4MultipleTest($oldTasks[1], $tasks[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,13,0,0'); // taskID 2 有传入task计算多人工时
|
||||
r($task->computehours4multipletest($oldTasks[0], $tasks[0], $members[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,3,0,3'); // taskID 1 有传入task 传入members计算多人工时
|
||||
r($task->computehours4multipletest($oldTasks[1], $tasks[1], $members[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,3,0,3'); // taskID 2 有传入task 传入members计算多人工时
|
||||
r($task->computehours4multipletest($oldTasks[0], $tasks[0], $members[0], false)) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,3,0,3'); // taskID 1 有传入task 传入members 不自动更新状态计算多人工时
|
||||
r($task->computehours4multipletest($oldTasks[1], $tasks[1], $members[1], false)) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,3,0,3'); // taskID 2 有传入task 传入members 不自动更新状态计算多人工时
|
||||
@@ -2,20 +2,85 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/task.class.php';
|
||||
su('admin');
|
||||
|
||||
function initData()
|
||||
{
|
||||
$task = zdTable('task');
|
||||
$task->id->range('1-20');
|
||||
$task->name->range('1-20')->prefix('任务');
|
||||
$task->module->range('1-5');
|
||||
$task->parent->range('0{15},1{5}');
|
||||
$task->execution->range('3-5');
|
||||
$task->project->range('1');
|
||||
$task->story->range('1-10');
|
||||
$task->mode->range('[]{15},multi{3},linear{2}');
|
||||
$task->storyVersion->range('1');
|
||||
$task->deadline->range('20230212 000000:0')->type('timestamp')->format('YY/MM/DD');
|
||||
$task->status->range('wait,doing{2},done{2},pause,cancel,closed');
|
||||
$task->assignedTo->range('admin,user1');
|
||||
$task->finishedBy->range('[]{3},user1{5}');
|
||||
$task->closedBy->range('[]{7},user1{1}');
|
||||
$task->pri->range('1-4');
|
||||
$task->gen(20);
|
||||
|
||||
$execution = zdTable('project');
|
||||
$execution->id->range('1-5');
|
||||
$execution->name->range('项目1,项目2,迭代1,迭代2,迭代3');
|
||||
$execution->type->range('project{2},sprint,stage,kanban');
|
||||
$execution->status->range('doing{3},closed,doing');
|
||||
$execution->parent->range('0,0,1,1,2');
|
||||
$execution->project->range('0,0,1,1,2');
|
||||
$execution->grade->range('1');
|
||||
$execution->path->range('1,2,`1,3`,`1,4`,`2,5`')->prefix(',')->postfix(',');
|
||||
$execution->begin->range('20230102 000000:0')->type('timestamp')->format('YY/MM/DD');
|
||||
$execution->end->range('20230212 000000:0')->type('timestamp')->format('YY/MM/DD');
|
||||
$execution->gen(5);
|
||||
|
||||
$story = zdTable('story');
|
||||
$story->id->range('1-20');
|
||||
$story->title->range('1-20')->prefix('需求');
|
||||
$story->product->range('1-20');
|
||||
$story->branch->range('0');
|
||||
$story->version->range('1-2');
|
||||
$story->status->range('active{10},draft{5},reviewing{2},closed{2},changing');
|
||||
$story->gen(20);
|
||||
|
||||
zdTable('user')->gen(30);
|
||||
|
||||
$taskTeam = zdTable('taskteam');
|
||||
$taskTeam->id->range('1-5');
|
||||
$taskTeam->task->range('16{2},19{3}');
|
||||
$taskTeam->account->range('admin,user1,admin,user1,user2');
|
||||
$taskTeam->estimate->range('1{2},2{3}');
|
||||
$taskTeam->left->range('1{2},1{3}');
|
||||
$taskTeam->status->range('wait{2},doing{3}');
|
||||
$taskTeam->gen(5);
|
||||
|
||||
$module = zdTable('module');
|
||||
$module->root->range('1-5');
|
||||
$module->type->range('story');
|
||||
$module->gen(5);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=taskModel->getUserTasks();
|
||||
timeout(0);
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
根据指派人员查看任务 >> 开发任务12
|
||||
|
||||
*/
|
||||
|
||||
$taskID = '2';
|
||||
$assignedTo = 'user92';
|
||||
*/
|
||||
|
||||
su('admin');
|
||||
initData();
|
||||
$task = new taskTest();
|
||||
r($task->getUserTasksTest($taskID,$assignedTo)) && p('2:name') && e('开发任务12'); // 根据指派人员查看任务
|
||||
|
||||
r($task->getUserTasksTest('user1', 'assignedTo')) && p('20:name') && e('任务20'); // 查看指派给用户1的任务
|
||||
r(count($task->getUserTasksTest('user1', 'assignedTo'))) && p() && e('10'); // 检查指派给用户1的任务数量
|
||||
r($task->getUserTasksTest('user1', 'closedBy')) && p('16:name') && e('任务16'); // 查看由用户1关闭的任务
|
||||
r(count($task->getUserTasksTest('user1', 'closedBy'))) && p() && e('2'); // 检查由用户1关闭的任务数量
|
||||
r($task->getUserTasksTest('user1', 'finishedBy')) && p('20:name') && e('任务20'); // 查看由用户1完成的任务
|
||||
r(count($task->getUserTasksTest('user1', 'finishedBy'))) && p() && e('11'); // 检查由用户1完成的任务数量
|
||||
r(count($task->getUserTasksTest('user1', 'assignedTo', 8))) && p() && e('8'); // 查找8条指派给用户1的任务
|
||||
r($task->getUserTasksTest('user1', 'finishedBy', 0, null, 'id_desc', 1)) && p('20:name') && e('任务20'); // 查看项目1下由用户1完成的任务
|
||||
r(count($task->getUserTasksTest('user1', 'finishedBy', 0, null, 'id_desc', 1))) && p() && e('11'); // 查看项目1下由用户1完成的任务
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/task.class.php';
|
||||
|
||||
$task = zdTable('task');
|
||||
$task->id->range('1-5');
|
||||
$task->name->range('1-5')->prefix('任务');
|
||||
$task->mode->range('multi');
|
||||
$task->status->range('wait,doing,done,pause,cancel,closed');
|
||||
$task->assignedTo->range('admin,user1');
|
||||
$task->openedBy->range('admin,user2,user1');
|
||||
$task->gen(5);
|
||||
|
||||
$taskTeam = zdTable('taskteam');
|
||||
$taskTeam->id->range('1-20');
|
||||
$taskTeam->task->range('1{2},2{3},3{2},4{3}');
|
||||
$taskTeam->account->range('admin,user1,admin,user1,user2');
|
||||
$taskTeam->estimate->range('1{2},2{3},3,4{2},5');
|
||||
$taskTeam->left->range('1{2},0{3},1{3},0{2}');
|
||||
$taskTeam->consumed->range('0{11},1{4},0{2},1{3}');
|
||||
$taskTeam->status->range('wait{11},doing,done,done,done,wait,wait,doing,done,done');
|
||||
$taskTeam->gen(20);
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('task');
|
||||
|
||||
$taskIdList = array(1, 2, 3, 4, 5);
|
||||
|
||||
$tasks = array();
|
||||
$oldTasks = array();
|
||||
$currentTasks = array();
|
||||
foreach($taskIdList as $id)
|
||||
{
|
||||
$task = $tester->task->getByID($id);
|
||||
$oldTasks[] = $task;
|
||||
$tasks[] = $task;
|
||||
$currentTasks[] = $task;
|
||||
}
|
||||
|
||||
$tasks[0]->status = 'doing';
|
||||
$tasks[0]->finishedDate = null;
|
||||
|
||||
$tasks[1]->status = 'done';
|
||||
$tasks[1]->finishedDate = '2023-04-27';
|
||||
|
||||
$currentTasks[0]->assignedTo = 'user1';
|
||||
$currentTasks[0]->estimate = 9;
|
||||
$currentTasks[0]->left = 0;
|
||||
$currentTasks[0]->consumed = 10;
|
||||
|
||||
$currentTasks[1]->assignedTo = 'admin';
|
||||
$currentTasks[1]->estimate = 8;
|
||||
$currentTasks[1]->left = 8;
|
||||
$currentTasks[1]->consumed = 0;
|
||||
|
||||
$member1 = new stdclass();
|
||||
$members = array(array('admin', 'user1'), array('user2'));
|
||||
$autoStatus = array(true, false);
|
||||
$hasEfforts = array(true, false);
|
||||
|
||||
/**
|
||||
|
||||
title=taskModel->computeCurrentTaskStatus();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行$task1
|
||||
- 属性status @doing
|
||||
- 属性assignedTo @user1
|
||||
- 属性estimate @9
|
||||
- 属性left @0
|
||||
- 属性consumed @10
|
||||
|
||||
- 执行$task2
|
||||
- 属性status @doing
|
||||
- 属性assignedTo @user1
|
||||
- 属性estimate @9
|
||||
- 属性left @0
|
||||
- 属性consumed @10
|
||||
|
||||
- 执行$task3
|
||||
- 属性status @doing
|
||||
- 属性assignedTo @user1
|
||||
- 属性estimate @9
|
||||
- 属性left @0
|
||||
- 属性consumed @10
|
||||
|
||||
- 执行$task4
|
||||
- 属性status @doing
|
||||
- 属性assignedTo @user1
|
||||
- 属性estimate @9
|
||||
- 属性left @0
|
||||
- 属性consumed @10
|
||||
|
||||
- 执行$task5
|
||||
- 属性status @doing
|
||||
- 属性assignedTo @user1
|
||||
- 属性estimate @9
|
||||
- 属性left @0
|
||||
- 属性consumed @10
|
||||
|
||||
- 执行$task6
|
||||
- 属性status @doing
|
||||
- 属性assignedTo @user1
|
||||
- 属性estimate @9
|
||||
- 属性left @0
|
||||
- 属性consumed @10
|
||||
|
||||
- 执行$task7
|
||||
- 属性status @done
|
||||
- 属性assignedTo @admin
|
||||
- 属性estimate @8
|
||||
- 属性left @8
|
||||
- 属性consumed @0
|
||||
|
||||
- 执行$task8
|
||||
- 属性status @done
|
||||
- 属性assignedTo @admin
|
||||
- 属性estimate @8
|
||||
- 属性left @8
|
||||
- 属性consumed @0
|
||||
|
||||
- 执行$task9
|
||||
- 属性status @done
|
||||
- 属性assignedTo @admin
|
||||
- 属性estimate @8
|
||||
- 属性left @8
|
||||
- 属性consumed @0
|
||||
|
||||
- 执行$task10
|
||||
- 属性status @done
|
||||
- 属性assignedTo @admin
|
||||
- 属性estimate @8
|
||||
- 属性left @8
|
||||
- 属性consumed @0
|
||||
|
||||
- 执行$task11
|
||||
- 属性status @done
|
||||
- 属性assignedTo @admin
|
||||
- 属性estimate @8
|
||||
- 属性left @8
|
||||
- 属性consumed @0
|
||||
|
||||
- 执行$task12
|
||||
- 属性status @done
|
||||
- 属性assignedTo @admin
|
||||
- 属性estimate @8
|
||||
- 属性left @8
|
||||
- 属性consumed @0
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
$task = new taskTest();
|
||||
$task1 = $task->computeCurrentTaskStatusTest($currentTasks[0], $oldTasks[0], $tasks[0], $autoStatus[0], $hasEfforts[0], $members[0]);
|
||||
$task2 = $task->computeCurrentTaskStatusTest($currentTasks[0], $oldTasks[1], $tasks[0], $autoStatus[0], $hasEfforts[0], $members[0]);
|
||||
$task3 = $task->computeCurrentTaskStatusTest($currentTasks[0], $oldTasks[1], $tasks[1], $autoStatus[0], $hasEfforts[0], $members[0]);
|
||||
$task4 = $task->computeCurrentTaskStatusTest($currentTasks[0], $oldTasks[1], $tasks[1], $autoStatus[1], $hasEfforts[0], $members[0]);
|
||||
$task5 = $task->computeCurrentTaskStatusTest($currentTasks[0], $oldTasks[1], $tasks[1], $autoStatus[1], $hasEfforts[1], $members[0]);
|
||||
$task6 = $task->computeCurrentTaskStatusTest($currentTasks[0], $oldTasks[1], $tasks[1], $autoStatus[1], $hasEfforts[1], $members[1]);
|
||||
$task7 = $task->computeCurrentTaskStatusTest($currentTasks[1], $oldTasks[0], $tasks[0], $autoStatus[0], $hasEfforts[0], $members[0]);
|
||||
$task8 = $task->computeCurrentTaskStatusTest($currentTasks[1], $oldTasks[1], $tasks[0], $autoStatus[0], $hasEfforts[0], $members[0]);
|
||||
$task9 = $task->computeCurrentTaskStatusTest($currentTasks[1], $oldTasks[1], $tasks[1], $autoStatus[0], $hasEfforts[0], $members[0]);
|
||||
$task10 = $task->computeCurrentTaskStatusTest($currentTasks[1], $oldTasks[1], $tasks[1], $autoStatus[1], $hasEfforts[0], $members[0]);
|
||||
$task11 = $task->computeCurrentTaskStatusTest($currentTasks[1], $oldTasks[1], $tasks[1], $autoStatus[1], $hasEfforts[1], $members[0]);
|
||||
$task12 = $task->computeCurrentTaskStatusTest($currentTasks[1], $oldTasks[1], $tasks[1], $autoStatus[1], $hasEfforts[1], $members[1]);
|
||||
|
||||
r($task1) && p('status,assignedTo,estimate,left,consumed') && e('doing,user1,9,0,10'); // 查询 task1 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态自动变更 没有工时消耗 团队成员members[0]
|
||||
r($task2) && p('status,assignedTo,estimate,left,consumed') && e('doing,user1,9,0,10'); // 查询 task2 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态自动变更 没有工时消耗 团队成员members[0]
|
||||
r($task3) && p('status,assignedTo,estimate,left,consumed') && e('doing,user1,9,0,10'); // 查询 task3 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态自动变更 没有工时消耗 团队成员members[0]
|
||||
r($task4) && p('status,assignedTo,estimate,left,consumed') && e('doing,user1,9,0,10'); // 查询 task4 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态非自动变更 没有工时消耗 团队成员members[0]
|
||||
r($task5) && p('status,assignedTo,estimate,left,consumed') && e('doing,user1,9,0,10'); // 查询 task5 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态非自动变更 有工时消耗 团队成员members[0]
|
||||
r($task6) && p('status,assignedTo,estimate,left,consumed') && e('doing,user1,9,0,10'); // 查询 task6 情况的task信息 currentTask[0] taskID 1 currentTasksestimate 状态非自动变更 有工时消耗 团队成员members[1]
|
||||
r($task7) && p('status,assignedTo,estimate,left,consumed') && e('done,admin,8,8,0'); // 查询 task7 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态自动变更 有工时消耗 团队成员members[0]
|
||||
r($task8) && p('status,assignedTo,estimate,left,consumed') && e('done,admin,8,8,0'); // 查询 task8 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态自动变更 有工时消耗 团队成员members[0]
|
||||
r($task9) && p('status,assignedTo,estimate,left,consumed') && e('done,admin,8,8,0'); // 查询 task9 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态自动变更 有工时消耗 团队成员members[0]
|
||||
r($task10) && p('status,assignedTo,estimate,left,consumed') && e('done,admin,8,8,0'); // 查询 task10 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态非自动变更 有工时消耗 团队成员members[0]
|
||||
r($task11) && p('status,assignedTo,estimate,left,consumed') && e('done,admin,8,8,0'); // 查询 task11 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态非自动变更 没有工时消耗 团队成员members[0]
|
||||
r($task12) && p('status,assignedTo,estimate,left,consumed') && e('done,admin,8,8,0'); // 查询 task12 情况的task信息 currentTask[1] taskID 2 currentTasksestimate 状态非自动变更 没有工时消耗 团队成员members[1]
|
||||
@@ -16,8 +16,10 @@ function initData()
|
||||
/**
|
||||
|
||||
title=测试computeTasksProgress
|
||||
timeout=0
|
||||
cid=2
|
||||
|
||||
|
||||
*/
|
||||
$tester->loadModel('task');
|
||||
|
||||
@@ -26,7 +28,8 @@ initData();
|
||||
$taskIDList = range(1,5);
|
||||
$taskList = $tester->task->getByList($taskIDList);
|
||||
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('2:progress') && e('100'); //测试任务消耗工时不为0,剩余工时为0的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('1:progress') && e('0'); //测试任务消耗工时为0,剩余工时为0的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('2:progress') && e('100'); //测试任务消耗工时不为0,剩余工时为0的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('3:progress') && e('94'); //测试任务消耗工时为15,剩余工时为1的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('4:progress') && e('80'); //测试任务消耗工时为20,剩余工时为5的情况
|
||||
r($tester->task->computeTasksProgress($taskList)) && p('5:progress') && e('71'); //测试任务消耗工时为25,剩余工时为10的情况
|
||||
|
||||
@@ -64,101 +64,162 @@ title=taskModel->fetchExecutionTasks();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[0], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[0], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[1] @10
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[1] @2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[1] @1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[1] @2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[1] @1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[1] @1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[1] @2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[1] @1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[0]- ,属性11 @任务11
|
||||
@任务11
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[1] @1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[1] @2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[0]- ,属性17 @任务17
|
||||
@任务17
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[1] @2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[0]- ,属性17 @任务17
|
||||
@任务17
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[1] @2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[0]- ,属性1 @任务1
|
||||
@任务1
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[1] @10
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[0]- ,属性18 @任务18
|
||||
@任务18
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[1] @10
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[0]- ,属性2 @任务2
|
||||
@任务2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[1] @2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[0] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[1] @0
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[0]- ,属性2 @任务2
|
||||
@任务2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[1] @2
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[0]- ,属性20 @任务20
|
||||
@任务20
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[1] @10
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[0], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[0], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[1]属性 @10
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[1]属性 @1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[1]属性 @1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[1]属性 @1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[1]属性 @1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[0]第11条的name属性 @任务11
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[1]属性 @1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[0]第17条的name属性 @任务17
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[0]第17条的name属性 @任务17
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[0]第1条的name属性 @任务1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[1], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[1]属性 @10
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[0]第18条的name属性 @任务18
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[1]属性 @10
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[0]第6条的name属性 @任务6
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[0]第6条的name属性 @任务6
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[0]第6条的name属性 @任务6
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[1]属性 @1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[0]第6条的name属性 @任务6
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[0]第6条的name属性 @任务6
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[0]第6条的name属性 @任务6
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[0]第6条的name属性 @任务6
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[1]属性 @1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[0]第6条的name属性 @任务6
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[1]属性 @1
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[0]第2条的name属性 @任务2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[0]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[1]属性 @0
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[0]第2条的name属性 @任务2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[1]属性 @2
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[0]第20条的name属性 @任务20
|
||||
|
||||
- 执行task模块的fetchExecutionTasks方法,参数是$executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[1]属性 @10
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -215,20 +276,20 @@ r($task->fetchExecutionTasksTest($executionIdList[1], $productIdList[0], $type[
|
||||
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[0])) && p('18:name') && e('任务18'); // 测试获取执行ID 2 product 0 type all module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[0], $count[1])) && p() && e('10'); // 测试获取执行ID 2 product 0 type all module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type all module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type all module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[0])) && p('6:name') && e('任务6'); // 测试获取执行ID 2 product 1 type all module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[0], $modules[0], $orderBy[0], $count[1])) && p() && e('2'); // 测试获取执行ID 2 product 1 type all module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type assignedbyme module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[1], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type assignedbyme module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type myinvolved module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type myinvolved module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type undone module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type undone module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type needconfirm module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type needconfirm module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type assignedtome module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type assignedtome module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type finishedbyme module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type finishedbyme module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[0])) && p('6:name') && e('任务6'); // 测试获取执行ID 2 product 1 type myinvolved module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[2], $modules[0], $orderBy[0], $count[1])) && p() && e('2'); // 测试获取执行ID 2 product 1 type myinvolved module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[0])) && p('6:name') && e('任务6'); // 测试获取执行ID 2 product 1 type undone module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[3], $modules[0], $orderBy[0], $count[1])) && p() && e('1'); // 测试获取执行ID 2 product 1 type undone module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[0])) && p('6:name') && e('任务6'); // 测试获取执行ID 2 product 1 type needconfirm module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[4], $modules[0], $orderBy[0], $count[1])) && p() && e('2'); // 测试获取执行ID 2 product 1 type needconfirm module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[0])) && p('6:name') && e('任务6'); // 测试获取执行ID 2 product 1 type assignedtome module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[5], $modules[0], $orderBy[0], $count[1])) && p() && e('2'); // 测试获取执行ID 2 product 1 type assignedtome module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[0])) && p('6:name') && e('任务6'); // 测试获取执行ID 2 product 1 type finishedbyme module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[6], $modules[0], $orderBy[0], $count[1])) && p() && e('2'); // 测试获取执行ID 2 product 1 type finishedbyme module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type delayed module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[7], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type delayed module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[8], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type wait module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
@@ -237,12 +298,12 @@ r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[9], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type doing module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type done module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[10], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type done module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type pause module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type pause module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[0])) && p('6:name') && e('任务6'); // 测试获取执行ID 2 product 1 type pause module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[11], $modules[0], $orderBy[0], $count[1])) && p() && e('1'); // 测试获取执行ID 2 product 1 type pause module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type cancel module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[12], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type cancel module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 1 type array('wait', 'doing', 'done', 'pause', 'cancel') module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[1])) && p() && e('0'); // 测试获取执行ID 2 product 1 type array('wait', 'doing', 'done', 'pause', 'cancel') module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[0])) && p('6:name') && e('任务6'); // 测试获取执行ID 2 product 1 type array('wait', 'doing', 'done', 'pause', 'cancel') module 空 orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[1], $type[13], $modules[0], $orderBy[0], $count[1])) && p() && e('1'); // 测试获取执行ID 2 product 1 type array('wait', 'doing', 'done', 'pause', 'cancel') module 空 orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[0])) && p('2:name') && e('任务2'); // 测试获取执行ID 2 product 0 type all module array(2) orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[1], $orderBy[0], $count[1])) && p() && e('2'); // 测试获取执行ID 2 product 0 type all module array(2) orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[2], $orderBy[0], $count[0])) && p() && e('0'); // 测试获取执行ID 2 product 0 type all module array(8) orederBy 'status_asc, id_desc' 的任务
|
||||
@@ -250,4 +311,4 @@ r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[0])) && p('2:name') && e('任务2'); // 测试获取执行ID 2 product 0 type all module array(2,8) orederBy 'status_asc, id_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[3], $orderBy[0], $count[1])) && p() && e('2'); // 测试获取执行ID 2 product 0 type all module array(2,8) orederBy 'status_asc, id_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[0])) && p('20:name') && e('任务20'); // 测试获取执行ID 2 product 0 type all module 空 orederBy 'pri_desc' 的任务
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[1])) && p() && e('10'); // 测试获取执行ID 2 product 0 type all module 空 orederBy 'pri_desc' 的任务数量
|
||||
r($task->fetchExecutionTasksTest($executionIdList[2], $productIdList[0], $type[0], $modules[0], $orderBy[1], $count[1])) && p() && e('10'); // 测试获取执行ID 2 product 0 type all module 空 orederBy 'pri_desc' 的任务数量
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/task.class.php';
|
||||
|
||||
$task = zdTable('task');
|
||||
$task->id->range('1-4');
|
||||
$task->name->range('并行任务,串行任务1,串行任务2,普通任务');
|
||||
$task->mode->range('multi,linear{2},[]');
|
||||
$task->status->range('done,doing,done,wait');
|
||||
$task->assignedTo->range('admin');
|
||||
$task->openedBy->range('dev01');
|
||||
$task->gen(4);
|
||||
|
||||
$taskTeam = zdTable('taskteam');
|
||||
$taskTeam->id->range('1-7');
|
||||
$taskTeam->task->range('1{2},2{3},3{2}');
|
||||
$taskTeam->account->range('admin,dev01,admin,dev01,dev02,admin,dev01');
|
||||
$taskTeam->estimate->range('1{2},2{3},1{2}');
|
||||
$taskTeam->left->range('1{2},1{3},1{2}');
|
||||
$taskTeam->status->range('done{3},doing,wait,done{2}');
|
||||
$taskTeam->gen(7);
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=taskModel->getAssignedTo4Multi();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行taskTester模块的getAssignedTo4Multi方法,参数是$taskIdList[0] @admin
|
||||
- 执行taskTester模块的getAssignedTo4Multi方法,参数是$taskIdList[1] @dev01
|
||||
- 执行taskTester模块的getAssignedTo4Multi方法,参数是$taskIdList[1], next @dev02
|
||||
- 执行taskTester模块的getAssignedTo4Multi方法,参数是$taskIdList[2] @dev01
|
||||
- 执行taskTester模块的getAssignedTo4Multi方法,参数是$taskIdList[3] @admin
|
||||
|
||||
*/
|
||||
|
||||
$taskTester = new taskTest();
|
||||
|
||||
$taskIdList = range(1, 4);
|
||||
r($taskTester->getAssignedTo4MultiTest($taskIdList[0])) && p() && e('admin'); // 测试获取并行任务的指派人
|
||||
r($taskTester->getAssignedTo4MultiTest($taskIdList[1])) && p() && e('dev01'); // 测试获取串行任务的当前指派人
|
||||
r($taskTester->getAssignedTo4MultiTest($taskIdList[1], 'next')) && p() && e('dev02'); // 测试获取并行任务的下一个指派人
|
||||
r($taskTester->getAssignedTo4MultiTest($taskIdList[2])) && p() && e('dev01'); // 测试获取已完成的串行任务的当前指派人
|
||||
r($taskTester->getAssignedTo4MultiTest($taskIdList[3])) && p() && e('admin'); // 测试获取普通任务的指派人
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
|
||||
$taskTeam = zdTable('taskteam');
|
||||
$taskTeam->id->range('1-5');
|
||||
$taskTeam->task->range('1{2},2{3}');
|
||||
$taskTeam->account->range('admin,dev01,admin,dev01,dev02');
|
||||
$taskTeam->estimate->range('1{2},2{3}');
|
||||
$taskTeam->left->range('1{2},1{3}');
|
||||
$taskTeam->status->range('wait{2},doing{3}');
|
||||
$taskTeam->gen(5);
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=taskModel->getTeamByIdList();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行$emptyData @0
|
||||
- 执行count($taskTeamGroup) @2
|
||||
- 执行$firstTaskTeam- ,属性0 @1
|
||||
@admin
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('task');
|
||||
|
||||
$taskIdList = array(1, 2);
|
||||
$emptyData = $tester->task->getTeamByIdList(array());
|
||||
$taskTeamGroup = $tester->task->getTeamByIdList($taskIdList);
|
||||
$firstTaskTeam = current($taskTeamGroup);
|
||||
r($emptyData) && p() && e('0'); // 测试传入空的taskIdList
|
||||
r(count($taskTeamGroup)) && p() && e('2'); // 测试查询给定taskIdList的任务数量
|
||||
r($firstTaskTeam) && p('0:account') && e('admin'); // 测试查询任务id为1团队中第一个人的用户名
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
|
||||
$taskTeam = zdTable('taskteam');
|
||||
$taskTeam->id->range('1-5');
|
||||
$taskTeam->task->range('1{2},2{3}');
|
||||
$taskTeam->account->range('admin,dev01,admin,dev01,dev02');
|
||||
$taskTeam->estimate->range('1{2},2{3}');
|
||||
$taskTeam->left->range('1{2},1{3}');
|
||||
$taskTeam->status->range('wait{2},doing{3}');
|
||||
$taskTeam->gen(5);
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=taskModel->getTeamMembersByIdList();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行$emptyData属性 @0
|
||||
- 执行count($taskTeamGroup)属性 @2
|
||||
- 执行$firstTaskTeam第0条的account属性 @admin
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('task');
|
||||
|
||||
$taskIdList = array(1, 2);
|
||||
$emptyData = $tester->task->getTeamMembersByIdList(array());
|
||||
$taskTeamGroup = $tester->task->getTeamMembersByIdList($taskIdList);
|
||||
$firstTaskTeam = current($taskTeamGroup);
|
||||
|
||||
r($emptyData) && p() && e('0'); // 测试传入空的taskIdList
|
||||
r(count($taskTeamGroup)) && p() && e('2'); // 测试查询给定taskIdList的任务数量
|
||||
r($firstTaskTeam) && p('0:account') && e('admin'); // 测试查询任务id为1团队中第一个人的用户名
|
||||
@@ -290,7 +290,10 @@ class taskTest
|
||||
$createFields = array('assignedTo' => '', 'status' => '', 'comment' => '');
|
||||
foreach($createFields as $field => $defaultValue) $_POST[$field] = $defaultValue;
|
||||
foreach($param as $key => $value) $_POST[$key] = $value;
|
||||
$object = $this->objectModel->assign($taskID);
|
||||
|
||||
$task = $_POST;
|
||||
unset($task['comment']);
|
||||
$object = $this->objectModel->assign((object)$task, $taskID);
|
||||
unset($_POST);
|
||||
if(dao::isError())
|
||||
{
|
||||
@@ -511,13 +514,9 @@ class taskTest
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getUserTasksTest($taskID, $assignedTo)
|
||||
public function getUserTasksTest($account, $type = 'assignedTo', $limit = 0, $pager = null, $orderBy = 'id_desc', $projectID = 0)
|
||||
{
|
||||
$createFields = array('assignedTo' => $assignedTo, 'status' => 'doing', 'comment' => '');
|
||||
foreach($createFields as $field => $defaultValue) $_POST[$field] = $defaultValue;
|
||||
$this->objectModel->assign($taskID);
|
||||
$object = $this->objectModel->getUserTasks($assignedTo);
|
||||
unset($_POST);
|
||||
$object = $this->objectModel->getUserTasks($account, $type, $limit, $pager, $orderBy, $projectID);
|
||||
if(dao::isError())
|
||||
{
|
||||
return dao::getError();
|
||||
@@ -1517,14 +1516,14 @@ class taskTest
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param int $productID
|
||||
* @param string|array $type
|
||||
* @param string|array $type all|assignedbyme|myinvolved|undone|needconfirm|assignedtome|finishedbyme|delayed|review|wait|doing|done|pause|cancel|closed|array('wait','doing','done','pause','cancel','closed')
|
||||
* @param string $modules
|
||||
* @param string $orderBy
|
||||
* @param string $count
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function fetchExecutionTasksTest($executionID, $productID = 0, $type = 'all', $modules = array(), $orderBy = 'status_asc, id_desc', $count = '0'): array|int
|
||||
public function fetchExecutionTasksTest(int $executionID, int $productID = 0, string|array $type = 'all', array $modules = array(), string $orderBy = 'status_asc, id_desc', string $count = '0'): array|int
|
||||
{
|
||||
$tasks = $this->objectModel->fetchExecutionTasks($executionID, $productID, $type, $modules, $orderBy);
|
||||
if(dao::isError())
|
||||
@@ -1541,4 +1540,59 @@ class taskTest
|
||||
return $tasks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the hierarchy of tasks to a parent-child structure.
|
||||
*
|
||||
* @param array $taskIdList
|
||||
* @access public
|
||||
* @return object[]
|
||||
*/
|
||||
public function restructureHierarchyTest(array $taskIdList): array
|
||||
{
|
||||
$tasks = array();
|
||||
if(!empty($taskIdList)) $tasks = $this->objectModel->getByList($taskIdList);
|
||||
return $this->objectModel->restructureHierarchy($tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignedTo for the multiply linear task.
|
||||
*
|
||||
* @param int $taskID
|
||||
* @param string $type current|next
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAssignedTo4MultiTest(int $taskID, string $type = 'current'): string
|
||||
{
|
||||
$task = $this->objectModel->getByID($taskID);
|
||||
$members = empty($task->team) ? array() : $task->team;
|
||||
|
||||
return $this->objectModel->getAssignedTo4Multi($members, $task, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fetch tasks of a execution.
|
||||
*
|
||||
* @param object $currentTask
|
||||
* @param object $oldTask
|
||||
* @param object $task
|
||||
* @param bool $condition true|false
|
||||
* @param bool $hasEfforts true|false
|
||||
* @param int $teamCount
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function computeCurrentTaskStatusTest(object $currentTask, object $oldTask, object $task, bool $autoStatus, bool $hasEfforts, array $members): object
|
||||
{
|
||||
$task = $this->objectModel->computeCurrentTaskStatus($currentTask, $oldTask, $task, $autoStatus, $hasEfforts, $members);
|
||||
if(dao::isError())
|
||||
{
|
||||
return dao::getError();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* The assignto view of task of ZenTaoPMS.
|
||||
*/
|
||||
|
||||
/*
|
||||
======= Attention ======
|
||||
|
||||
This file is generated by zin-tool, you should check the following to-do list.
|
||||
|
||||
+ Familiar with the use of these widgets in zin: .
|
||||
+ Check the following variables which used in widgets: $assignedToOptions.
|
||||
+ Check the origin code in module/task/view/assignto.html.php, and ensure that all features have been implemented.
|
||||
+ Check the origin js code in module/task/js/common.js and module/task/js/assignto.js
|
||||
+ Check the origin css code in module/task/css/common.css and module/task/css/assignto.css
|
||||
+ Remove the comments which starts with "zin:"
|
||||
+ Test according to the new design draft and the original implementation
|
||||
*/
|
||||
|
||||
namespace zin;
|
||||
|
||||
global $lang;
|
||||
/* ====== Preparing and processing page data ====== */
|
||||
|
||||
$items = [];
|
||||
foreach($members as $key => $value)
|
||||
{
|
||||
$items[] = ['text' => $value, 'value' => $key];
|
||||
}
|
||||
/* zin: Set variables to define picker options for form */
|
||||
$formTitle = $task->name;
|
||||
$assignedToOptions = $members;
|
||||
|
||||
set::itemID($task->id);
|
||||
set::title($task->name);
|
||||
|
||||
form
|
||||
/* ====== Define the page structure with zin widgets ====== */
|
||||
|
||||
/* zin: Define the form in main content */
|
||||
formPanel
|
||||
(
|
||||
set::title($formTitle), // The form title is diffrent from the page title,
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->assignedToAB),
|
||||
set::name('assignedTo'),
|
||||
set::control(['type' => 'select', 'items' => $items]),
|
||||
set::width("1/3"),
|
||||
set::name("assignedTo"),
|
||||
set::label("指派"),
|
||||
set::value((empty($task->team) or strpos('done,cencel,closed', $task->status) !== false) ? $task->assignedTo : $task->nextUser),
|
||||
set::control("picker"),
|
||||
set::items($assignedToOptions)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->task->left),
|
||||
div
|
||||
set::width("1/3"),
|
||||
set::label("预计剩余"),
|
||||
inputGroup
|
||||
(
|
||||
setClass('input-control has-suffix'),
|
||||
input
|
||||
control(set(array
|
||||
(
|
||||
set::type('number'),
|
||||
set::min(0),
|
||||
set::name('left'),
|
||||
set::id('left'),
|
||||
),
|
||||
h::label
|
||||
(
|
||||
setClass('input-control-suffix'),
|
||||
$lang->workingHour
|
||||
)
|
||||
'name' => "left",
|
||||
'id' => "left",
|
||||
'value' => $task->left,
|
||||
'disabled' => false,
|
||||
'type' => "text"
|
||||
))),
|
||||
"小时"
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->comment),
|
||||
set::name('comment'),
|
||||
set::control(['type' => 'textarea']),
|
||||
),
|
||||
set::actions(['save'])
|
||||
set::width("2/3"),
|
||||
set::name("comment"),
|
||||
set::label("备注"),
|
||||
set::control("editor")
|
||||
)
|
||||
);
|
||||
|
||||
render('modalDialog');
|
||||
|
||||
/* ====== Render page ====== */
|
||||
|
||||
render();
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The zen file of task module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Shujie Tian <tianshujie@easysoft.ltd>
|
||||
* @package task
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
class taskZen extends task
|
||||
{
|
||||
/**
|
||||
* Reponse after assignto.
|
||||
*
|
||||
* @param int $taskID
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function reponseAfterAssignTo(int $taskID): int
|
||||
{
|
||||
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success'));
|
||||
if(isonlybody())
|
||||
{
|
||||
$task = $this->task->getById($taskID);
|
||||
$execution = $this->execution->getByID($task->execution);
|
||||
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
|
||||
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
|
||||
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')
|
||||
{
|
||||
$taskSearchValue = $this->session->taskSearchValue ? $this->session->taskSearchValue : '';
|
||||
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $taskSearchValue);
|
||||
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
|
||||
$kanbanData = $kanbanData[$kanbanType];
|
||||
$kanbanData = json_encode($kanbanData);
|
||||
|
||||
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
|
||||
}
|
||||
return print(js::closeModal('parent.parent', 'this'));
|
||||
}
|
||||
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the error after assignto.
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function errorAfterAssignTo(): int
|
||||
{
|
||||
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()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build AssignTo Form.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param object $task
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function buildAssignToForm(int $executionID, object $task): void
|
||||
{
|
||||
$this->loadModel('action');
|
||||
$members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');
|
||||
|
||||
/* Compute next assignedTo. */
|
||||
if(!empty($task->team) and strpos('done,cencel,closed', $task->status) === false)
|
||||
{
|
||||
$task->nextUser = $this->task->getAssignedTo4Multi($task->team, $task, 'next');
|
||||
$members = $this->task->getMemberPairs($task);
|
||||
}
|
||||
|
||||
if(!isset($members[$task->assignedTo])) $members[$task->assignedTo] = $task->assignedTo;
|
||||
if(isset($members['closed']) or $task->status == 'closed') $members['closed'] = 'Closed';
|
||||
|
||||
$this->view->title = $this->view->execution->name . $this->lang->colon . $this->lang->task->assign;
|
||||
$this->view->position[] = $this->lang->task->assign;
|
||||
$this->view->task = $task;
|
||||
$this->view->members = $members;
|
||||
$this->view->users = $this->loadModel('user')->getPairs();
|
||||
$this->display();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@ $config->todo->create->form['assignedDate'] = array('required' => false, 'type'
|
||||
$config->todo->create->form['assignedTo'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->todo->create->form['assignedBy'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->todo->create->form['vision'] = array('required' => false, 'type' => 'string', 'default' => $this->config->vision);
|
||||
$config->todo->create->form['idvalue'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->todo->create->form['objectID'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->todo->create->form['desc'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
$config->todo->create->form['uid'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
|
||||
$config->todo->edit->form = array();
|
||||
$config->todo->edit->form['name'] = array('required' => true, 'type' => 'string');
|
||||
|
||||
Regular → Executable
+29
-26
@@ -127,28 +127,36 @@ class todo extends control
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑待办数据
|
||||
* Edit a todo.
|
||||
*
|
||||
* @param int $todoID
|
||||
* @param string $todoID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function edit($todoID)
|
||||
public function edit(string $todoID)
|
||||
{
|
||||
if(!empty($_POST))
|
||||
{
|
||||
$changes = $this->todo->update($todoID);
|
||||
$formData = form::data($this->config->todo->edit->form);
|
||||
$todoID = (int)$todoID;
|
||||
|
||||
$todo = $this->todoZen->beforeEdit($todoID, $formData);
|
||||
if(dao::isError())
|
||||
{
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'message' => dao::getError()));
|
||||
return print(js::error(dao::getError()));
|
||||
}
|
||||
if($changes)
|
||||
|
||||
$changes = $this->todo->update($todoID, $todo);
|
||||
if(dao::isError())
|
||||
{
|
||||
$actionID = $this->loadModel('action')->create('todo', $todoID, 'edited');
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'message' => dao::getError()));
|
||||
return print(js::error(dao::getError()));
|
||||
}
|
||||
|
||||
$this->todoZen->afterEdit($todoID, $changes);
|
||||
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
|
||||
return print(js::locate($this->session->todoList, 'parent.parent'));
|
||||
}
|
||||
@@ -161,8 +169,6 @@ class todo extends control
|
||||
|
||||
$todo->date = date("Y-m-d", strtotime($todo->date));
|
||||
$this->view->title = $this->lang->todo->common . $this->lang->colon . $this->lang->todo->edit;
|
||||
$this->view->position[] = $this->lang->todo->common;
|
||||
$this->view->position[] = $this->lang->todo->edit;
|
||||
$this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
|
||||
$this->view->todo = $todo;
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted|noempty');
|
||||
@@ -279,45 +285,42 @@ class todo extends control
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启一个待办事项
|
||||
* Start a todo.
|
||||
*
|
||||
* @param int $todoID
|
||||
* @param string $todoID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function start($todoID)
|
||||
public function start(string $todoID)
|
||||
{
|
||||
$todo = $this->todo->getById($todoID);
|
||||
$todoID = (int)$todoID;
|
||||
$todo = $this->todo->getById($todoID);
|
||||
|
||||
if($todo->status == 'wait') $this->todo->start($todoID);
|
||||
if(in_array($todo->type, array('bug', 'task', 'story')))
|
||||
{
|
||||
$confirmNote = 'confirm' . ucfirst($todo->type);
|
||||
$confirmURL = $this->createLink($todo->type, 'view', "id=$todo->idvalue");
|
||||
$okTarget = isonlybody() ? 'parent' : 'window.parent.$.apps.open';
|
||||
if($todo->type == 'bug') $app = 'qa';
|
||||
if($todo->type == 'task') $app = 'execution';
|
||||
if($todo->type == 'story') $app = 'product';
|
||||
$cancelURL = $this->server->HTTP_REFERER;
|
||||
return print(js::confirm(sprintf($this->lang->todo->$confirmNote, $todo->idvalue), $confirmURL, $cancelURL, $okTarget, 'parent', $app));
|
||||
}
|
||||
if(in_array($todo->type, array('bug', 'task', 'story'))) return $this->todoZen->printConfirm($todo);
|
||||
if(isonlybody()) return print(js::reload('parent.parent'));
|
||||
|
||||
if(isonlybody())return print(js::reload('parent.parent'));
|
||||
echo js::reload('parent');
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活待办事项
|
||||
* Activated todo.
|
||||
*
|
||||
* @param $todoID
|
||||
* @param string $todoID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function activate($todoID)
|
||||
public function activate(string $todoID)
|
||||
{
|
||||
$todo = $this->todo->getById($todoID);
|
||||
$todoID = (int)$todoID;
|
||||
$todo = $this->todo->getById($todoID);
|
||||
|
||||
if($todo->status == 'done' or $todo->status == 'closed') $this->todo->activate($todoID);
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
|
||||
if(isonlybody()) return print(js::reload('parent.parent'));
|
||||
|
||||
echo js::reload('parent');
|
||||
}
|
||||
|
||||
|
||||
Regular → Executable
+26
-95
@@ -124,88 +124,24 @@ class todoModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新待办数据
|
||||
* update a todo.
|
||||
*
|
||||
* @param int $todoID
|
||||
* @param object $todo
|
||||
* @access public
|
||||
* @return void
|
||||
* @return array|false
|
||||
*/
|
||||
public function update($todoID)
|
||||
public function update(int $todoID, object $todo): array|false
|
||||
{
|
||||
$oldTodo = $this->dao->findById((int)$todoID)->from(TABLE_TODO)->fetch();
|
||||
$oldTodo = $this->dao->findById($todoID)->from(TABLE_TODO)->fetch();
|
||||
|
||||
$idvalue = 0;
|
||||
$objectType = $this->post->type;
|
||||
$hasObject = in_array($objectType, $this->config->todo->moduleList);
|
||||
if($hasObject && $objectType) $idvalue = $this->post->uid ? $this->post->$objectType : $this->post->idvalue;
|
||||
$todo = fixer::input('post')
|
||||
->cleanInt('pri, begin, end, private')
|
||||
->add('account', $oldTodo->account)
|
||||
->setIF(in_array($this->post->type, array('bug', 'task', 'story')), 'name', '')
|
||||
->setIF($hasObject && $objectType, 'idvalue', $idvalue)
|
||||
->setIF($this->post->date == false, 'date', '2030-01-01')
|
||||
->setIF($this->post->begin == false, 'begin', '2400')
|
||||
->setIF($this->post->end == false, 'end', '2400')
|
||||
->setIF($this->post->type == false, 'type', $oldTodo->type)
|
||||
->setDefault('private', 0)
|
||||
->stripTags($this->config->todo->editor->edit['id'], $this->config->allowedTags)
|
||||
->remove(implode(',', $this->config->todo->moduleList) . ',uid')
|
||||
->get();
|
||||
if(!$this->todoTao->updateRow($todoID, $todo)) return false;
|
||||
|
||||
if(in_array($todo->type, $this->config->todo->moduleList))
|
||||
{
|
||||
$type = $todo->type;
|
||||
$object = $this->loadModel($type)->getByID($objectType);
|
||||
if(isset($object->name)) $todo->name = $object->name;
|
||||
if(isset($object->title)) $todo->name = $object->title;
|
||||
}
|
||||
|
||||
if($todo->end < $todo->begin)
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!empty($oldTodo->cycle))
|
||||
{
|
||||
$todo->date = date('Y-m-d');
|
||||
|
||||
$todo->config['begin'] = $todo->date;
|
||||
if($todo->config['type'] == 'day')
|
||||
{
|
||||
unset($todo->config['week']);
|
||||
unset($todo->config['month']);
|
||||
}
|
||||
if($todo->config['type'] == 'week')
|
||||
{
|
||||
unset($todo->config['day']);
|
||||
unset($todo->config['month']);
|
||||
$todo->config['week'] = join(',', $todo->config['week']);
|
||||
}
|
||||
if($todo->config['type'] == 'month')
|
||||
{
|
||||
unset($todo->config['day']);
|
||||
unset($todo->config['week']);
|
||||
$todo->config['month'] = join(',', $todo->config['month']);
|
||||
}
|
||||
$todo->config['beforeDays'] = (int)$todo->config['beforeDays'];
|
||||
$todo->config = json_encode($todo->config);
|
||||
}
|
||||
|
||||
$todo = $this->loadModel('file')->processImgURL($todo, $this->config->todo->editor->edit['id'], $this->post->uid);
|
||||
$this->dao->update(TABLE_TODO)->data($todo)
|
||||
->autoCheck()
|
||||
->checkIF(in_array($todo->type, array('custom', 'feedback')), $this->config->todo->edit->requiredFields, 'notempty')
|
||||
->checkIF($hasObject && $todo->idvalue == 0, 'idvalue', 'notempty')
|
||||
->where('id')->eq($todoID)
|
||||
->exec();
|
||||
if(!dao::isError())
|
||||
{
|
||||
$this->file->updateObjectID($this->post->uid, $todoID, 'todo');
|
||||
if(!empty($oldTodo->cycle)) $this->createByCycle(array($todoID => $todo));
|
||||
if(($this->config->edition == 'biz' || $this->config->edition == 'max') && $todo->type == 'feedback' && $todo->idvalue) $this->loadModel('feedback')->updateStatus('todo', $todo->idvalue, $todo->status);
|
||||
return common::createChanges($oldTodo, $todo);
|
||||
}
|
||||
$this->loadModel('file')->updateObjectID($todo->uid, $todoID, 'todo');
|
||||
if(!empty($oldTodo->cycle)) $this->createByCycle(array($todoID => $todo));
|
||||
if(($this->config->edition == 'biz' || $this->config->edition == 'max') && $todo->type == 'feedback' && $todo->idvalue) $this->loadModel('feedback')->updateStatus('todo', $todo->idvalue, $todo->status);
|
||||
return common::createChanges($oldTodo, (array)$todo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,16 +226,19 @@ class todoModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启一个待办事项
|
||||
* Start one todo.
|
||||
*
|
||||
* @param string $todoID
|
||||
* @param int $todoID
|
||||
* @access public
|
||||
* @return void
|
||||
* @return bool
|
||||
*/
|
||||
public function start($todoID)
|
||||
public function start(int $todoID): bool
|
||||
{
|
||||
$this->dao->update(TABLE_TODO)->set('status')->eq('doing')->where('id')->eq((int)$todoID)->exec();
|
||||
$this->dao->update(TABLE_TODO)->set('status')->eq('doing')->where('id')->eq($todoID)->exec();
|
||||
$this->loadModel('action')->create('todo', $todoID, 'started');
|
||||
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
|
||||
@@ -654,41 +593,33 @@ class todoModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活待办事项
|
||||
* Activate todo.
|
||||
*
|
||||
* @param $todoID
|
||||
*
|
||||
* @param int $todoID
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function activate($todoID)
|
||||
public function activate(int $todoID): bool
|
||||
{
|
||||
$this->dao->update(TABLE_TODO)->set('status')->eq('wait')->where('id')->eq((int)$todoID)->exec();
|
||||
$this->dao->update(TABLE_TODO)->set('status')->eq('wait')->where('id')->eq($todoID)->exec();
|
||||
$this->loadModel('action')->create('todo', $todoID, 'activated', '', 'wait');
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closed todo.
|
||||
* Close todo. Update related feedback if edition is biz or max.
|
||||
*
|
||||
* @param $todoID
|
||||
* @param int $todoID
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function close($todoID)
|
||||
public function close(int $todoID): bool
|
||||
{
|
||||
$now = helper::now();
|
||||
$this->dao->update(TABLE_TODO)
|
||||
->set('status')->eq('closed')
|
||||
->set('closedBy')->eq($this->app->user->account)
|
||||
->set('closedDate')->eq($now)
|
||||
->set('assignedTo')->eq('closed')
|
||||
->set('assignedDate')->eq($now)
|
||||
->where('id')->eq((int)$todoID)
|
||||
->exec();
|
||||
$isClosed = $this->todoTao->closeTodo($todoID);
|
||||
|
||||
if(!dao::isError())
|
||||
if($isClosed)
|
||||
{
|
||||
$this->loadModel('action')->create('todo', $todoID, 'closed', '', 'closed');
|
||||
|
||||
|
||||
@@ -20,4 +20,282 @@ class todoTao extends todoModel
|
||||
|
||||
return (int)$this->dao->lastInsertID();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新待办数据
|
||||
* Update todo data.
|
||||
*
|
||||
* @param int $todoID
|
||||
* @param object $todo
|
||||
* @return bool
|
||||
*/
|
||||
protected function updateRow(int $todoID, object $todo): bool
|
||||
{
|
||||
$this->dao->update(TABLE_TODO)->data($todo)
|
||||
->autoCheck()
|
||||
->checkIF(in_array($todo->type, array('custom', 'feedback')), $this->config->todo->edit->requiredFields, 'notempty')
|
||||
->checkIF(in_array($todo->type, $this->config->todo->moduleList) && $todo->idvalue == 0, 'idvalue', 'notempty')
|
||||
->where('id')->eq($todoID)
|
||||
->exec();
|
||||
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close one todo.
|
||||
*
|
||||
* @param int $todoID
|
||||
* @return bool
|
||||
*/
|
||||
protected function closeTodo(int $todoID): bool
|
||||
{
|
||||
$now = helper::now();
|
||||
$this->dao->update(TABLE_TODO)
|
||||
->set('status')->eq('closed')
|
||||
->set('closedBy')->eq($this->app->user->account)
|
||||
->set('closedDate')->eq($now)
|
||||
->set('assignedTo')->eq('closed')
|
||||
->set('assignedDate')->eq($now)
|
||||
->where('id')->eq($todoID)
|
||||
->exec();
|
||||
return !dao::isError();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 处理要创建的todo的数据
|
||||
* Processing todo data.
|
||||
*
|
||||
* @param object $todoData
|
||||
* @return object|false
|
||||
*/
|
||||
protected function beforeCreate(object $todoData): object|false
|
||||
{
|
||||
$objectID = 0;
|
||||
$hasObject = in_array($todoData->type, $this->config->todo->moduleList);
|
||||
if($hasObject && $todoData->type) $objectID = $todoData->uid ? $todoData->type : $todoData->objectID;
|
||||
|
||||
$todoData->account = $this->app->user->account;
|
||||
$todoData->assignedTo = zget($todoData, 'assignedTo', $this->app->user->account);
|
||||
$todoData->assignedBy = zget($todoData, 'assignedBy', $this->app->user->account);
|
||||
|
||||
if($hasObject && $todoData->type) $todoData->objectID = $objectID;
|
||||
if($todoData->status == 'done') $todoData->finishedBy = $this->app->user->account;
|
||||
if($todoData->status == 'done') $todoData->finishedDate = helper::now();
|
||||
|
||||
if(!isset($todoData->pri) and in_array($todoData->type, $this->config->todo->moduleList) and !in_array($todoData->type, array('review', 'feedback')))
|
||||
{
|
||||
$todoData->pri = $this->dao->select('pri')->from($this->config->objectTables[$todoData->type])->where('id')->eq($todoData->objectID)->fetch('pri');
|
||||
|
||||
if($todoData->pri == 'high') $todoData->pri = 1;
|
||||
if($todoData->pri == 'middle') $todoData->pri = 2;
|
||||
if($todoData->pri == 'low') $todoData->pri = 3;
|
||||
}
|
||||
|
||||
if($todoData->type != 'custom' and $todoData->objectID)
|
||||
{
|
||||
$type = $todoData->type;
|
||||
$object = $this->loadModel($type)->getByID($todoData->{$type});
|
||||
if(isset($object->name)) $todoData->name = $object->name;
|
||||
if(isset($object->title)) $todoData->name = $object->title;
|
||||
}
|
||||
|
||||
if($todoData->end < $todoData->begin)
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!empty($todoData->cycle))
|
||||
{
|
||||
/* TODO confirmation. */
|
||||
$todoData = $this->setCycle($todoData);
|
||||
if(!$todoData) return false;
|
||||
}
|
||||
if(empty($todoData->cycle)) unset($todoData->config);
|
||||
|
||||
$todoData = $this->loadModel('file')->processImgURL($todoData, $this->config->todo->editor->create['id'], $this->post->uid);
|
||||
return $todoData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取周期待办列表
|
||||
* Get cycle list.
|
||||
* @param array $todoList
|
||||
* @param string $orderBy
|
||||
* @return array
|
||||
*/
|
||||
protected function getCycleList(array $todoList, string $orderBy = 'date_asc'): array
|
||||
{
|
||||
return $this->dao->select('*')
|
||||
->from(TABLE_TODO)->where('type')->eq('cycle')
|
||||
->andWhere('deleted')->eq('0')
|
||||
->andWhere('objectID')->in(array_keys($todoList))
|
||||
->orderBy($orderBy)
|
||||
->fetchAll('objectID');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过待办构建周期待办数据
|
||||
* Build cycle todo.
|
||||
* @param object $todo
|
||||
* @return stdclass
|
||||
*/
|
||||
protected function buildCycleTodo(object $todo): object
|
||||
{
|
||||
$newTodo = new stdclass();
|
||||
$newTodo->account = $todo->account;
|
||||
$newTodo->begin = $todo->begin;
|
||||
$newTodo->end = $todo->end;
|
||||
$newTodo->type = 'cycle';
|
||||
$newTodo->objectID = $todo->id;
|
||||
$newTodo->pri = $todo->pri;
|
||||
$newTodo->name = $todo->name;
|
||||
$newTodo->desc = $todo->desc;
|
||||
$newTodo->status = 'wait';
|
||||
$newTodo->private = $todo->private;
|
||||
$newTodo->assignedTo = $todo->assignedTo;
|
||||
$newTodo->assignedBy = $todo->assignedBy ;
|
||||
|
||||
return $newTodo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过周期待办,获取要生成待办的日期
|
||||
* Gets the date by the cycle todo.
|
||||
* @param object $todo
|
||||
* @param object $lastCycle
|
||||
* @param string $today
|
||||
* @return false|string
|
||||
*/
|
||||
protected function getCycleTodoDate(object $todo, object $lastCycle, string $today): false|string
|
||||
{
|
||||
$date = '';
|
||||
if($todo->config->type == 'day')
|
||||
{
|
||||
return $this->getCycleDailyTodoDate($todo, $lastCycle, $today);
|
||||
}
|
||||
elseif($todo->config->type == 'week')
|
||||
{
|
||||
$week = date('w', strtotime($today));
|
||||
if(strpos(",{$todo->config->week},", ",{$week},") !== false)
|
||||
{
|
||||
if(empty($lastCycle)) $date = $today;
|
||||
if($lastCycle and $lastCycle->date < $today) $date = $today;
|
||||
}
|
||||
}
|
||||
elseif($todo->config->type == 'month')
|
||||
{
|
||||
$day = date('j', strtotime($today));
|
||||
if(strpos(",{$todo->config->month},", ",{$day},") !== false)
|
||||
{
|
||||
if(empty($lastCycle)) $date = $today;
|
||||
if($lastCycle and $lastCycle->date < $today) $date = $today;
|
||||
}
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过周期待办,获取要生成每日待办的日期
|
||||
* Gets the daily todo date by the cycle todo.
|
||||
* @param object $todo
|
||||
* @param object $lastCycle
|
||||
* @param string $today
|
||||
* @return false|string
|
||||
*/
|
||||
private function getCycleDailyTodoDate(object $todo, object $lastCycle, string $today): false|string
|
||||
{
|
||||
$date = '';
|
||||
if(isset($todo->config->day))
|
||||
{
|
||||
$day = (int)$todo->config->day;
|
||||
if($day <= 0) return false;
|
||||
|
||||
/* If no data, judge the interval from the beginning time. */
|
||||
if(empty($lastCycle))
|
||||
{
|
||||
$todayTime = new DateTime($today);
|
||||
$beginTime = new DateTime($todo->config->begin);
|
||||
$interval = $todayTime->diff($beginTime)->days;
|
||||
|
||||
if($interval != $day) return false;
|
||||
$date = $today;
|
||||
}
|
||||
|
||||
/* If data is available, determine the interval of time since the previous cycle. */
|
||||
if(!empty($lastCycle->date))
|
||||
{
|
||||
$todayTime = new DateTime($today);
|
||||
$lastCycleTime = new DateTime($lastCycle->date);
|
||||
$interval = $todayTime->diff($lastCycleTime)->days;
|
||||
|
||||
if($interval != $day) return false;
|
||||
$date = date('Y-m-d', strtotime("{$lastCycle->date} +{$day} days"));
|
||||
}
|
||||
}
|
||||
if(isset($todo->config->specifiedDate))
|
||||
{
|
||||
$date = $today;
|
||||
$specifiedDate = $todo->config->specify->month + 1 . '-' . $todo->config->specify->day;
|
||||
|
||||
/* If not set cycle every year and have data, continue. */
|
||||
if(!empty($lastCycle) and !isset($todo->config->cycleYear)) return false;
|
||||
/* If set specified date, only judge month and day. */
|
||||
if(date('m-d', strtotime($date)) != $specifiedDate) return false;
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置周期待办数据
|
||||
* Set cycle todo data.
|
||||
*
|
||||
* @param object $todoData
|
||||
* @return false|object
|
||||
*/
|
||||
private function setCycle(object $todoData): false|object
|
||||
{
|
||||
$todoData->date = helper::today();
|
||||
$todoData->config['begin'] = $todoData->date;
|
||||
|
||||
if($todoData->config['type'] == 'day')
|
||||
{
|
||||
unset($todoData->config['week'], $todoData->config['month']);
|
||||
if(!$todoData->config['day'])
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->todo->cycleDaysLabel);
|
||||
return false;
|
||||
}
|
||||
if(!validater::checkInt($todoData->config['day']))
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->int[0], $this->lang->todo->cycleDaysLabel);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if($todoData->config['type'] == 'week')
|
||||
{
|
||||
unset($todoData->config['day'], $todoData->config['month']);
|
||||
$todoData->config['week'] = join(',', $todoData->config['week']);
|
||||
}
|
||||
if($todoData->config['type'] == 'month')
|
||||
{
|
||||
unset($todoData->config['day'], $todoData->config['week']);
|
||||
$todoData->config['month'] = join(',', $todoData->config['month']);
|
||||
}
|
||||
|
||||
if($todoData->config['beforeDays'] and !validater::checkInt($todoData->config['beforeDays']))
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->int[0], $this->lang->todo->beforeDaysLabel);
|
||||
return false;
|
||||
}
|
||||
$todoData->config['beforeDays'] = (int)$todoData->config['beforeDays'];
|
||||
$todoData->config = json_encode($todoData->config);
|
||||
$todoData->type = 'cycle';
|
||||
|
||||
return $todoData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,45 @@ include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/todo.class.php';
|
||||
su('admin');
|
||||
|
||||
function initData()
|
||||
{
|
||||
$todo = zdTable('todo');
|
||||
$todo->id->range('1-4');
|
||||
$todo->account->prefix('admin')->range('1-4');
|
||||
$todo->begin->range('1710');
|
||||
$todo->end->range('1740');
|
||||
$todo->feedback->range('0');
|
||||
$todo->type->range('custom');
|
||||
$todo->cycle->range('0');
|
||||
$todo->idvalue->range('0');
|
||||
$todo->pri->range("3");
|
||||
$todo->name->prefix('测试待办')->range('1-4');
|
||||
$todo->desc->range('描述');
|
||||
$todo->status->range('wait');
|
||||
$todo->private->range('0');
|
||||
$todo->assignedTo->prefix('admin')->range('1-4');
|
||||
$todo->assignedBy->prefix('admin')->range('1-4');
|
||||
$todo->finishedBy->prefix('admin')->range('1-4');
|
||||
$todo->closedBy->prefix('admin')->range('1-4');
|
||||
$todo->deleted->range('0');
|
||||
$todo->vision->range('1.0');
|
||||
|
||||
$todo->gen(4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=测试 todoModel->activate();
|
||||
timeout=0
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
激活一个状态为wait的todo >> wait
|
||||
激活一个状态为doing的todo >> wait
|
||||
激活一个状态为done的todo >> wait
|
||||
- 执行todo模块的activate方法,参数是$todoIDList[0]属性status @wait
|
||||
|
||||
- 执行todo模块的activate方法,参数是$todoIDList[1]属性status @wait
|
||||
|
||||
- 执行todo模块的activate方法,参数是$todoIDList[2]属性status @wait
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -20,6 +50,8 @@ $todoIDList = array('1', '2', '3');
|
||||
|
||||
$todo = new todoTest();
|
||||
|
||||
initData();
|
||||
|
||||
r($todo->activateTest($todoIDList[0])) && p('status') && e('wait'); // 激活一个状态为wait的todo
|
||||
r($todo->activateTest($todoIDList[1])) && p('status') && e('wait'); // 激活一个状态为doing的todo
|
||||
r($todo->activateTest($todoIDList[2])) && p('status') && e('wait'); // 激活一个状态为done的todo
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/todo.class.php';
|
||||
su('admin');
|
||||
@@ -10,16 +12,24 @@ title=测试 todoModel->close();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
关闭一个状态为wait的todo >> closed
|
||||
关闭一个状态为doing的todo >> closed
|
||||
关闭一个状态为done的todo >> closed
|
||||
|
||||
*/
|
||||
function initData()
|
||||
{
|
||||
$todo = zdTable('todo');
|
||||
$todo->id->range('1');
|
||||
$todo->name->prefix("待办")->range('1');
|
||||
$todo->date->range('`2023-04-23`');
|
||||
$todo->type->range('custom');
|
||||
$todo->status->range('wait');
|
||||
$todo->gen(1, '', true);
|
||||
}
|
||||
|
||||
$todoIDList = array('1', '2', '3');
|
||||
initData();
|
||||
|
||||
$todo = new todoTest();
|
||||
global $tester;
|
||||
$tester->loadModel('todo');
|
||||
|
||||
r($todo->closeTest($todoIDList[0])) && p('status') && e('closed'); // 关闭一个状态为wait的todo
|
||||
r($todo->closeTest($todoIDList[1])) && p('status') && e('closed'); // 关闭一个状态为doing的todo
|
||||
r($todo->closeTest($todoIDList[2])) && p('status') && e('closed'); // 关闭一个状态为done的todo
|
||||
|
||||
r($tester->todo->getByID(1)) && p('status') && e('wait');
|
||||
r($tester->todo->close(1)) && p() && e(1);
|
||||
r($tester->todo->getByID(1)) && p('status') && e('closed');
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/todo.class.php';
|
||||
su('admin');
|
||||
|
||||
function initData()
|
||||
{
|
||||
$todo = zdTable('todo');
|
||||
$todo->id->range('1');
|
||||
$todo->name->prefix("待办")->range('1');
|
||||
$todo->date->range('`2023-04-23`');
|
||||
$todo->type->range('custom');
|
||||
$todo->gen(1, '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=测试 todoModel->create();
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
创建没有名字的待办 >> 『待办名称』不能为空。
|
||||
创建自定义待办 >> 时间待定的月周期待办,custom,wait
|
||||
创建bug待办 >> 测试单转Bug13,bug,doing
|
||||
创建task待办 >> 开发任务11,task,done
|
||||
创建story待办 >> 用户需求1,story,closed
|
||||
|
||||
*/
|
||||
|
||||
$accountList = array('admin', 'dev1', 'test1');
|
||||
@@ -57,8 +63,43 @@ $todo4->uid = '1';
|
||||
|
||||
$todo = new todoTest();
|
||||
|
||||
r($todo->createTest($accountList[0], $b_noname)) && p() && e('『待办名称』不能为空。'); //创建没有名字的待办
|
||||
r($todo->createTest($accountList[1], $todo1)) && p('name,type,status') && e('时间待定的月周期待办,custom,wait'); //创建自定义待办
|
||||
r($todo->createTest($accountList[2], $todo2)) && p('name,type,status') && e('测试单转Bug13,bug,doing'); //创建bug待办
|
||||
r($todo->createTest($accountList[0], $todo3)) && p('name,type,status') && e('开发任务11,task,done'); //创建task待办
|
||||
r($todo->createTest($accountList[0], $todo4)) && p('name,type,status') && e('用户需求1,story,closed'); //创建story待办
|
||||
global $tester;
|
||||
$tester->loadModel('todo');
|
||||
|
||||
initData();
|
||||
|
||||
$todoWithoutName = new stdclass;
|
||||
$todoWithoutName->name = '';
|
||||
$todoWithoutName->date = date('Y-m-d');
|
||||
$todoWithoutName->type = 'custom';
|
||||
|
||||
$todoInvalidDate = new stdclass;
|
||||
$todoInvalidDate->name = 'todoInvalidDate';
|
||||
$todoInvalidDate->date = 'today';
|
||||
$todoInvalidDate->type = 'custom';
|
||||
|
||||
$todoValid = new stdclass;
|
||||
$todoValid->name = 'todoValid';
|
||||
$todoValid->date = date('Y-m-d');
|
||||
$todoValid->type = 'custom';
|
||||
|
||||
$todoValid1 = new stdclass;
|
||||
$todoValid1->name = 'todoValid1';
|
||||
$todoValid1->date = date('Y-m-d');
|
||||
$todoValid1->type = 'custom';
|
||||
|
||||
/**
|
||||
* 1. 如果r函数返回的是一个复杂的结构,比如混合内容的数组或者嵌套的数组,p函数是无法获取返回值中的某些特定值的,需要自己编写助手函数,比如在../todo.class.php中编写。
|
||||
* 2. 如果ztf执行没有生成注释,需要检查php脚本是否执行报错,建议先使用php执行编写的测试用例再用ztf执行。
|
||||
*/
|
||||
r($tester->todo->create($todoValid)) && p() && e('2');
|
||||
r($tester->todo->create($todoWithoutName)) && p() && e('0');
|
||||
r($tester->todo->create($todoInvalidDate)) && p() && e('0');
|
||||
r($tester->todo->create($todoValid1)) && p() && e('3');
|
||||
exit(0);
|
||||
|
||||
r($todo->createTest($accountList[0], $b_noname)) && p() && e('『待办名称』不能为空。');
|
||||
r($todo->createTest($accountList[1], $todo1)) && p('name,type,status') && e('时间待定的月周期待办,custom,wait');
|
||||
r($todo->createTest($accountList[2], $todo2)) && p('name,type,status') && e('测试单转Bug13,bug,doing');
|
||||
r($todo->createTest($accountList[0], $todo3)) && p('name,type,status') && e('开发任务11,task,done');
|
||||
r($todo->createTest($accountList[0], $todo4)) && p('name,type,status') && e('用户需求1,story,closed');
|
||||
|
||||
@@ -4,22 +4,48 @@ include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/todo.class.php';
|
||||
su('admin');
|
||||
|
||||
function initData()
|
||||
{
|
||||
$todo = zdTable('todo');
|
||||
$todo->id->range('1-5');
|
||||
$todo->account->prefix('admin')->range('1-5');
|
||||
$todo->begin->range('1710');
|
||||
$todo->end->range('1740');
|
||||
$todo->feedback->range('0');
|
||||
$todo->type->range('custom');
|
||||
$todo->cycle->range('0');
|
||||
$todo->idvalue->range('0');
|
||||
$todo->pri->range("3");
|
||||
$todo->name->prefix('测试待办')->range('1-5');
|
||||
$todo->desc->range('描述');
|
||||
$todo->status->range('wait');
|
||||
$todo->private->range('0');
|
||||
$todo->assignedTo->prefix('admin')->range('1-5');
|
||||
$todo->assignedBy->prefix('admin')->range('1-5');
|
||||
$todo->finishedBy->prefix('admin')->range('1-5');
|
||||
$todo->closedBy->prefix('admin')->range('1-5');
|
||||
$todo->deleted->range('0');
|
||||
$todo->vision->range('1.0');
|
||||
|
||||
$todo->gen(5);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=测试 todoModel->start();
|
||||
timeout=0
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
开始一个状态为wait的todo >> doing
|
||||
开始一个状态为doing的todo >> doing
|
||||
开始一个状态为done的todo >> doing
|
||||
读取文件/var/www/html/studyProject/zin//var/www/html/studyProject/zin/module/todo/test/model/data/todo_start.yaml失败。
|
||||
- 执行todo模块的start方法,参数是1,属性status @doing
|
||||
- 执行todo模块的start方法,参数是2,属性status @doing
|
||||
|
||||
|
||||
*/
|
||||
|
||||
$todoIDList = array('1', '2', '3');
|
||||
|
||||
$todo = new todoTest();
|
||||
|
||||
r($todo->startTest($todoIDList[0])) && p('status') && e('doing'); // 开始一个状态为wait的todo
|
||||
r($todo->startTest($todoIDList[1])) && p('status') && e('doing'); // 开始一个状态为doing的todo
|
||||
r($todo->startTest($todoIDList[2])) && p('status') && e('doing'); // 开始一个状态为done的todo
|
||||
initData();
|
||||
|
||||
r($todo->startTest(1)) && p('status') && e('doing');
|
||||
r($todo->startTest(2)) && p('status') && e('doing');
|
||||
@@ -4,29 +4,52 @@ include dirname(__FILE__, 5) . "/test/lib/init.php";
|
||||
include dirname(__FILE__, 2) . '/todo.class.php';
|
||||
su('admin');
|
||||
|
||||
function initData()
|
||||
{
|
||||
$todo = zdTable('todo');
|
||||
$todo->id->range('1');
|
||||
$todo->account->range('admin');
|
||||
$todo->date->range(date('Y-m-d'));
|
||||
$todo->begin->range('1000');
|
||||
$todo->end->range('2400');
|
||||
$todo->type->range('custom');
|
||||
$todo->name->range('这是一个待办');
|
||||
$todo->status->range('wait');
|
||||
$todo->vision->range('rnd');
|
||||
|
||||
$todo->gen(1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
title=测试 todoModel->update();
|
||||
title=测试 todoModel::update;
|
||||
timeout=0
|
||||
cid=1
|
||||
pid=1
|
||||
|
||||
测试更新todo名称 >> name,自定义1的待办,john
|
||||
测试更新todo类型 >> type,custom,bug
|
||||
测试更新todo名称和类型 >> type,bug,custom;name,BUG2的待办,jack
|
||||
测试不更新todo任何数据 >> 没有数据更新
|
||||
- 执行todo模块的update方法,参数是1, $t_upname
|
||||
- 第0条的field属性 @name
|
||||
- 第0条的old属性 @这是一个待办
|
||||
- 第0条的new属性 @john
|
||||
|
||||
- 执行todo模块的update方法,参数是1, $t_uptype
|
||||
- 第0条的field属性 @type
|
||||
- 第0条的old属性 @custom
|
||||
- 第0条的new属性 @bug
|
||||
|
||||
- 执行todo模块的update方法,参数是1, $t_unname @没有数据更新
|
||||
|
||||
*/
|
||||
|
||||
$todoIDList = array('1', '2');
|
||||
global $tester;
|
||||
$tester->loadModel('todo');
|
||||
|
||||
$t_upname = array('name' => 'john');
|
||||
$t_uptype = array('type' => 'bug', 'idvalue' => '1');
|
||||
$t_typename = array('name' => 'jack', 'type' => 'custom');
|
||||
$t_unname = array('name' => 'john');
|
||||
initData();
|
||||
|
||||
$t_upname = array('name' => 'john');
|
||||
$t_uptype = array('type' => 'bug', 'idvalue' => '1');
|
||||
$t_unname = array('name' => 'john');
|
||||
|
||||
$todo = new todoTest();
|
||||
|
||||
r($todo->updateTest($todoIDList[0], $t_upname)) && p('0:field,old,new') && e('name,自定义1的待办,john'); // 测试更新todo名称
|
||||
r($todo->updateTest($todoIDList[0], $t_uptype)) && p('0:field,old,new') && e('type,custom,bug'); // 测试更新todo类型
|
||||
r($todo->updateTest($todoIDList[1], $t_typename)) && p('0:field,old,new;1:field,old,new') && e('type,bug,custom;name,BUG2的待办,jack'); // 测试更新todo名称和类型
|
||||
r($todo->updateTest($todoIDList[0], $t_unname)) && p() && e('没有数据更新'); // 测试不更新todo任何数据
|
||||
r($todo->updateTest(1, $t_upname)) && p('0:field,old,new') && e('name,这是一个待办,john');
|
||||
r($todo->updateTest(1, $t_uptype)) && p('0:field,old,new') && e('type,custom,bug');
|
||||
r($todo->updateTest(1, $t_unname)) && p() && e('没有数据更新');
|
||||
@@ -79,24 +79,25 @@ class todoTest
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function updateTest($todoID, $param)
|
||||
public function updateTest(int $todoID, array $param)
|
||||
{
|
||||
global $tester;
|
||||
$object = $tester->dbh->query("SELECT * FROM " . TABLE_TODO ." WHERE id = $todoID")->fetch();
|
||||
|
||||
$todo = new stdClass();
|
||||
foreach($object as $field => $value)
|
||||
{
|
||||
if(in_array($field, array_keys($param)))
|
||||
{
|
||||
$_POST[$field] = $param[$field];
|
||||
$todo->$field = $param[$field];
|
||||
}
|
||||
else
|
||||
{
|
||||
$_POST[$field] = $value;
|
||||
$todo->$field = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$change = $this->objectModel->update($todoID);
|
||||
$change = $this->objectModel->update($todoID, $todo);
|
||||
if($change == array()) $change = '没有数据更新';
|
||||
|
||||
unset($_POST);
|
||||
|
||||
Regular → Executable
+119
-57
@@ -3,60 +3,6 @@ declare(strict_types=1);
|
||||
|
||||
class todoZen extends todo
|
||||
{
|
||||
/**
|
||||
* 处理请求数据
|
||||
* Processing request data.
|
||||
*
|
||||
* @param object $formData
|
||||
* @return object|false
|
||||
*/
|
||||
protected function beforeCreate(object $formData): object|bool
|
||||
{
|
||||
$formData = $formData->remove(implode(',', $this->config->todo->moduleList) . ',uid')->stripTags($this->config->todo->editor->create['id'], $this->config->allowedTags)->get();
|
||||
|
||||
$idvalue = 0;
|
||||
$hasObject = in_array($formData->type, $this->config->todo->moduleList);
|
||||
if($hasObject && $formData->type) $idvalue = $formData->uid ? $formData->type : $formData->idvalue;
|
||||
|
||||
$formData->account = $this->app->user->account;
|
||||
$formData->assignedTo = zget($formData, 'assignedTo', $this->app->user->account);
|
||||
$formData->assignedBy = zget($formData, 'assignedBy', $this->app->user->account);
|
||||
if($hasObject && $formData->type) $formData->idvalue = $idvalue;
|
||||
if($formData->status == 'done') $formData->finishedBy = $this->app->user->account;
|
||||
if($formData->status == 'done') $formData->finishedDate = helper::now();
|
||||
|
||||
if(!isset($formData->pri) and in_array($formData->type, $this->config->todo->moduleList) and $formData->type !== 'review' and $formData->type !== 'feedback')
|
||||
{
|
||||
// TODO
|
||||
$formData->pri = $this->dao->select('pri')->from($this->config->objectTables[$formData->type])->where('id')->eq($formData->idvalue)->fetch('pri');
|
||||
|
||||
if($formData->pri == 'high') $formData->pri = 1;
|
||||
if($formData->pri == 'middle') $formData->pri = 2;
|
||||
if($formData->pri == 'low') $formData->pri = 3;
|
||||
}
|
||||
|
||||
if($formData->type != 'custom' and $formData->idvalue)
|
||||
{
|
||||
$type = $formData->type;
|
||||
$object = $this->loadModel($type)->getByID($formData->$type);
|
||||
if(isset($object->name)) $formData->name = $object->name;
|
||||
if(isset($object->title)) $formData->name = $object->title;
|
||||
}
|
||||
|
||||
if($formData->end < $formData->begin)
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!empty($formData->cycle)) $formData = $this->setCycle($formData);
|
||||
else unset($formData->config);
|
||||
|
||||
$formData = $this->loadModel('file')->processImgURL($formData, $this->config->todo->editor->create['id'], $this->post->uid);
|
||||
|
||||
return $formData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建待办
|
||||
* Create a todo.
|
||||
@@ -70,8 +16,8 @@ class todoZen extends todo
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成待办后数据处理
|
||||
* Create a todo.
|
||||
* 创建完成待办后数据处理
|
||||
* Create a todo after data processing
|
||||
*
|
||||
* @param object $todo
|
||||
* @return object
|
||||
@@ -82,7 +28,6 @@ class todoZen extends todo
|
||||
|
||||
$this->loadModel('score')->create('todo', 'create', $todo->id);
|
||||
|
||||
// TODO
|
||||
if(!empty($todo->cycle)) $this->todo->createByCycle(array($todo->id => $todo));
|
||||
|
||||
$this->loadModel('action')->create('todo', $todo->id, 'opened');
|
||||
@@ -94,6 +39,103 @@ class todoZen extends todo
|
||||
return $todo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理编辑待办的请求数据
|
||||
* Processing edit request data.
|
||||
*
|
||||
* @param int $todoID
|
||||
* @param object $formData
|
||||
* @return object|false
|
||||
*/
|
||||
protected function beforeEdit(int $todoID, object $formData)
|
||||
{
|
||||
$oldTodo = $this->dao->findById($todoID)->from(TABLE_TODO)->fetch();
|
||||
|
||||
$idvalue = 0;
|
||||
$rowData = $formData->rawdata;
|
||||
$objectType = $rowData->type;
|
||||
$hasObject = in_array($objectType, $this->config->todo->moduleList);
|
||||
if($hasObject && $objectType) $idvalue = $rowData->uid ? $rowData->$objectType : $rowData->idvalue;
|
||||
|
||||
$todo = $formData->add('account', $oldTodo->account)
|
||||
->cleanInt('pri, begin, end, private')
|
||||
->setIF(in_array($rowData->type, array('bug', 'task', 'story')), 'name', '')
|
||||
->setIF($hasObject && $objectType, 'idvalue', $idvalue)
|
||||
->setIF($rowData->date == false, 'date', '2030-01-01')
|
||||
->setIF($rowData->begin == false, 'begin', '2400')
|
||||
->setIF($rowData->end == false, 'end', '2400')
|
||||
->setIF($rowData->type == false, 'type', $oldTodo->type)
|
||||
->setDefault('private', 0)
|
||||
->stripTags($this->config->todo->editor->edit['id'], $this->config->allowedTags)
|
||||
->remove(implode(',', $this->config->todo->moduleList) . ',uid')
|
||||
->get();
|
||||
|
||||
$todo = (object) array_merge((array) $todo, (array) $rowData);
|
||||
|
||||
if(in_array($todo->type, $this->config->todo->moduleList))
|
||||
{
|
||||
$type = $todo->type;
|
||||
$object = $this->loadModel($type)->getByID($objectType);
|
||||
if(isset($object->name)) $todo->name = $object->name;
|
||||
if(isset($object->title)) $todo->name = $object->title;
|
||||
}
|
||||
|
||||
if($todo->end < $todo->begin)
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!empty($oldTodo->cycle)) $this->handleCycleConfig($todo);
|
||||
|
||||
$todo = $this->loadModel('file')->processImgURL($todo, $this->config->todo->editor->edit['id'], $rowData->uid);
|
||||
|
||||
return $todo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑完成待办后数据处理
|
||||
* Handle data after edit todo.
|
||||
*
|
||||
* @param object $todo
|
||||
* @return void
|
||||
*/
|
||||
protected function afterEdit(int $todoID, array $changes): void
|
||||
{
|
||||
if(empty($changes)) return;
|
||||
|
||||
$actionID = $this->loadModel('action')->create('todo', $todoID, 'edited');
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理循环待办的配置文件
|
||||
* Handle cycle config.
|
||||
*
|
||||
* @param object $todo
|
||||
* @return void
|
||||
*/
|
||||
private function handleCycleConfig(object &$todo): void
|
||||
{
|
||||
$todo->date = date('Y-m-d');
|
||||
$todo->config['begin'] = $todo->date;
|
||||
|
||||
if($todo->config['type'] == 'day') unset($todo->config['week'], $todo->config['month']);
|
||||
if($todo->config['type'] == 'week')
|
||||
{
|
||||
unset($todo->config['day'], $todo->config['month']);
|
||||
$todo->config['week'] = join(',', $todo->config['week']);
|
||||
}
|
||||
if($todo->config['type'] == 'month')
|
||||
{
|
||||
unset($todo->config['day'], $todo->config['week']);
|
||||
$todo->config['month'] = join(',', $todo->config['month']);
|
||||
}
|
||||
|
||||
$todo->config['beforeDays'] = (int)$todo->config['beforeDays'];
|
||||
$todo->config = json_encode($todo->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置周期待办
|
||||
* Set cycle todo.
|
||||
@@ -142,4 +184,24 @@ class todoZen extends todo
|
||||
|
||||
return $formData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出确认弹框
|
||||
* Output confirm alert.
|
||||
*
|
||||
* @param object $todo
|
||||
* @access protected
|
||||
* @return int
|
||||
*/
|
||||
protected function printConfirm(object $todo): int
|
||||
{
|
||||
$confirmNote = 'confirm' . ucfirst($todo->type);
|
||||
$confirmURL = $this->createLink($todo->type, 'view', "id=$todo->idvalue");
|
||||
$okTarget = isonlybody() ? 'parent' : 'window.parent.$.apps.open';
|
||||
if($todo->type == 'bug') $app = 'qa';
|
||||
if($todo->type == 'task') $app = 'execution';
|
||||
if($todo->type == 'story') $app = 'product';
|
||||
$cancelURL = $this->server->HTTP_REFERER;
|
||||
return print(js::confirm(sprintf($this->lang->todo->$confirmNote, $todo->idvalue), $confirmURL, $cancelURL, $okTarget, 'parent', $app));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ sonar.sourceEncoding=UTF-8
|
||||
sonar.qualitygate.wait=true
|
||||
sonar.coverage.exclusions=**/*.*
|
||||
sonar.inclusions=**/**.php
|
||||
sonar.exclusions=**/*.bak,**/*.sql,**/*.js,**/*.css,**/*.yaml,**/*.zip,**/*.out,**/lang/*
|
||||
sonar.exclusions=**/*.bak,**/*.sql,**/*.js,**/*.css,**/*.yaml,**/*.zip,**/*.out,**/lang/*,**/test/*
|
||||
|
||||
+131
-98
@@ -16,6 +16,12 @@
|
||||
error_reporting(E_ALL);
|
||||
define('RUN_MODE', 'test');
|
||||
|
||||
if($argc > 1 && $argv[1] == '-extract')
|
||||
{
|
||||
parseScript();
|
||||
exit;
|
||||
}
|
||||
|
||||
$testPath = dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'test' . DIRECTORY_SEPARATOR;
|
||||
$frameworkRoot = dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'framework' . DIRECTORY_SEPARATOR;
|
||||
|
||||
@@ -69,8 +75,6 @@ if(!empty($config->test->account) and !empty($config->test->password) and !empty
|
||||
$token = $token->body;
|
||||
}
|
||||
|
||||
global $isExtractAction;
|
||||
$isExtractAction = $argc > 1 && $argv[1] == '-extract';
|
||||
|
||||
/**
|
||||
* Save variable to $_result.
|
||||
@@ -97,15 +101,10 @@ function r($result)
|
||||
function p($keys = '', $delimiter = ',')
|
||||
{
|
||||
global $_result;
|
||||
global $_keys;
|
||||
global $_delimiter;
|
||||
global $isExtractAction;
|
||||
|
||||
$_keys = $keys;
|
||||
$_delimiter = $delimiter;
|
||||
|
||||
if($isExtractAction) return true;
|
||||
|
||||
if(empty($_result)) return print(implode("\n", array_fill(0, substr_count($keys, $delimiter) + 1, 0)) . "\n");
|
||||
|
||||
if(is_array($_result) and isset($_result['code']) and $_result['code'] == 'fail') return print((string) $_result['message'] . "\n");
|
||||
@@ -131,86 +130,154 @@ function p($keys = '', $delimiter = ',')
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function parseScript($expect)
|
||||
function parseScript()
|
||||
{
|
||||
$debugInfo = debug_backtrace();
|
||||
if(!empty($debugInfo))
|
||||
{
|
||||
global $_keys;
|
||||
global $_delimiter;
|
||||
global $_current;
|
||||
|
||||
$keys = $_keys;
|
||||
$delimiter = $_delimiter;
|
||||
$file = $debugInfo[count($debugInfo)-1]['file'];
|
||||
$contents = file_get_contents($file);
|
||||
$rpeList = genParamsByRPE($contents);
|
||||
|
||||
list($moduleName, $methodName, $methodParam) = genParamsByRPE($contents);
|
||||
|
||||
$isGrup = false;
|
||||
$stepDesc = '';
|
||||
$expects = empty($expect) ? array() : explode($delimiter, $expect);
|
||||
|
||||
$object = '';
|
||||
$rowIndex = -1;
|
||||
$pos = strpos($keys, ':');
|
||||
if($pos)
|
||||
foreach($rpeList as $rpe)
|
||||
{
|
||||
$arrKey = substr($keys, 0, $pos);
|
||||
$keys = substr($keys, $pos + 1);
|
||||
$pos = strpos($arrKey, '[');
|
||||
list($moduleName, $methodName, $methodParam) = $rpe[0];
|
||||
$expectStr = trim($rpe[1], '"\'');
|
||||
$pParam = $rpe[2];
|
||||
$keys = $pParam[0];
|
||||
$delimiter = $pParam[1] ? $pParam[1] : ',';
|
||||
$isGrup = false;
|
||||
$stepDesc = '';
|
||||
$expects = '' === $expectStr ? array() : explode($delimiter, $expectStr);
|
||||
|
||||
$rowIndex = -1;
|
||||
$pos = strpos($keys, ':');
|
||||
if($pos)
|
||||
{
|
||||
$object = substr($arrKey, 0, $pos);
|
||||
$rowIndex = trim(substr($arrKey, $pos + 1), ']');
|
||||
$arrKey = substr($keys, 0, $pos);
|
||||
$keys = substr($keys, $pos + 1);
|
||||
$pos = strpos($arrKey, '[');
|
||||
$rowIndex = $pos ? trim(substr($arrKey, $pos + 1), ']') : $arrKey;
|
||||
}
|
||||
$keys = explode($delimiter, $keys);
|
||||
|
||||
if(count($keys) > 1) $isGrup = true;
|
||||
|
||||
if ($methodName === 0 && $methodParam === 0)
|
||||
{
|
||||
$stepDesc = "- 执行{$moduleName}" . ($isGrup ? "\n" : '');
|
||||
}
|
||||
else
|
||||
{
|
||||
$rowIndex = $arrKey;
|
||||
$stepDesc = "- 执行{$moduleName}模块的{$methodName}方法,参数是{$methodParam}" . ($isGrup ? "\n" : '');
|
||||
}
|
||||
}
|
||||
$keys = explode($delimiter, $keys);
|
||||
|
||||
if(count($keys) > 1) $isGrup = true;
|
||||
if(empty($keys)) $stepDesc .= " @{$expects[0]}\n";
|
||||
|
||||
if ($methodName === 0 && $methodParam === 0)
|
||||
{
|
||||
$stepDesc = "- 执行{$moduleName}" . ($isGrup ? "\n" : '');
|
||||
}
|
||||
else
|
||||
{
|
||||
$stepDesc = "- 执行{$moduleName}模块的{$methodName}方法,参数是{$methodParam}" . ($isGrup ? "\n" : '');
|
||||
}
|
||||
|
||||
if(empty($keys)) $stepDesc .= " @{$expects[0]}\n";
|
||||
|
||||
foreach($keys as $index => $row)
|
||||
{
|
||||
if(count($keys) < 2)
|
||||
foreach($keys as $index => $row)
|
||||
{
|
||||
$stepExpect = isset($expects[$index]) ? $expects[$index] : '';
|
||||
if(count($expects) == 1) $stepExpect = $expects[0];
|
||||
if($rowIndex == -1)
|
||||
{
|
||||
$stepDesc .= $row ? ",属性{$row}" : '';
|
||||
$stepDesc .= " @$expect";
|
||||
$stepDesc .= ($isGrup ? ' - ' : '') . "属性{$row} @{$stepExpect}\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$stepDesc .= ($rowIndex == -1 ? '' : ($isGrup ? ' - ' : '') . ",属性{$rowIndex} @{$expects[0]}\n");
|
||||
$stepDesc .= ($isGrup ? ' - ' : '') . "第{$rowIndex}条的{$row}属性 @{$stepExpect}\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
echo $stepDesc . ($isGrup ? "\n" : "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split function params.
|
||||
* 从p()函数提取传入参数
|
||||
*
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
function splitParam($params)
|
||||
{
|
||||
$newParams = array();
|
||||
foreach($params as $param)
|
||||
{
|
||||
$param = trim($param);
|
||||
$firstSymbol = substr($param, 0, 1);
|
||||
$paramArray = str_split($param);
|
||||
$delimiterIndex = -1;
|
||||
|
||||
foreach($paramArray as $i => $p)
|
||||
{
|
||||
if($i == 0) continue;
|
||||
if($p === $firstSymbol && (!isset($paramArray[$i-1]) || '\\' != $paramArray[$i-1]))
|
||||
{
|
||||
if($rowIndex == -1)
|
||||
{
|
||||
$stepDesc .= ($isGrup ? ' - ' : '- ') . "属性{$row} @{$expects[$index]}\n";
|
||||
}
|
||||
else {
|
||||
$stepDesc .= ($isGrup ? ' - ' : '- ') . "第{$rowIndex}条的{$row}属性 @{$expects[$index]}\n";
|
||||
}
|
||||
$delimiterIndex = $i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
echo $stepDesc . ($isGrup ? "\n" : "\n");
|
||||
|
||||
if($delimiterIndex === -1)
|
||||
{
|
||||
$newParams[] = array($param, '');
|
||||
continue;
|
||||
}
|
||||
|
||||
$firstParam = substr($param, 0, $delimiterIndex);
|
||||
$firstParam = trim(trim($firstParam), '\'"');
|
||||
$lastParam = substr($param, $delimiterIndex + 1);
|
||||
$lastParam = trim(trim($lastParam), '\'"');
|
||||
$newParams[] = array($firstParam, $lastParam);
|
||||
}
|
||||
|
||||
return $newParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate module,method,param from r function.
|
||||
* 从r()函数提取调用的moduleName,methodName,methodParam
|
||||
*
|
||||
* @param array $rParams
|
||||
* @return array
|
||||
*/
|
||||
function genModuleAndMethod($rParams)
|
||||
{
|
||||
$newParams = array();
|
||||
foreach($rParams as $index => $param)
|
||||
{
|
||||
$param = trim($param, "'");
|
||||
$objArrowCount = substr_count($param, '->');
|
||||
$rParamsStructureList = explode('->', $param);
|
||||
|
||||
if($objArrowCount == 1)
|
||||
{
|
||||
$moduleName = substr($rParamsStructureList[0], 1);
|
||||
$method = $rParamsStructureList[1];
|
||||
$methodName = substr(explode('(', $method)[0], 0, -4);
|
||||
$methodParam = substr(explode('(', $method)[1], 0, -1);
|
||||
$methodParam = trim($methodParam, "'");
|
||||
}
|
||||
elseif($objArrowCount == 2)
|
||||
{
|
||||
$moduleName = $rParamsStructureList[1];
|
||||
$method = $rParamsStructureList[2];
|
||||
$methodName = explode('(', $method)[0];
|
||||
$methodParam = trim(substr(explode('(', $method)[1], 0, -1), ")");
|
||||
$methodParam = trim($methodParam, "'");
|
||||
}
|
||||
else
|
||||
{
|
||||
$newParams[] = array($param, 0, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodParam = preg_replace("/,\s*'/", ', ', $methodParam);
|
||||
$newParams[] = array($moduleName, $methodName, $methodParam);
|
||||
}
|
||||
|
||||
return $newParams;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,7 +289,6 @@ function parseScript($expect)
|
||||
*/
|
||||
function genParamsByRPE($rpe)
|
||||
{
|
||||
global $_current;
|
||||
preg_match_all("/r\((.*?)\)\s*&&\s*p\((.*?)\)\s*&&\s*e\((.*?)\);/", $rpe, $matches);
|
||||
$rParams = !empty($matches[1]) ? $matches[1] : array();
|
||||
$pParams = !empty($matches[2]) ? $matches[2] : array();
|
||||
@@ -231,39 +297,13 @@ function genParamsByRPE($rpe)
|
||||
$pParams = is_array($pParams) ? $pParams : array($pParams);
|
||||
$eParams = is_array($eParams) ? $eParams : array($eParams);
|
||||
|
||||
$_current = intval($_current);
|
||||
$param = $rParams[$_current];
|
||||
$_current++;
|
||||
$param = trim($param, "'");
|
||||
$objArrowCount = substr_count($param, '->');
|
||||
$rParamsStructureList = explode('->', $param);
|
||||
$pParamsArray = splitParam($pParams);
|
||||
$rpeList = array();
|
||||
$rParamArray = genModuleAndMethod($rParams);
|
||||
|
||||
if($objArrowCount == 1)
|
||||
{
|
||||
$moduleName = substr($rParamsStructureList[0], 1);
|
||||
$method = $rParamsStructureList[1];
|
||||
$methodName = substr(explode('(', $method)[0], 0, -4);
|
||||
$methodParam = substr(explode('(', $method)[1], 0, -1);
|
||||
$methodParam = trim($methodParam, "'");
|
||||
}
|
||||
elseif($objArrowCount == 2)
|
||||
{
|
||||
$moduleName = $rParamsStructureList[1];
|
||||
$method = $rParamsStructureList[2];
|
||||
$methodName = explode('(', $method)[0];
|
||||
$methodParam = trim(substr(explode('(', $method)[1], 0, -1), ")");
|
||||
$methodParam = trim($methodParam, "'");
|
||||
}
|
||||
else
|
||||
{
|
||||
$moduleName = $param;
|
||||
$methodName = 0;
|
||||
$methodParam = 0;
|
||||
}
|
||||
foreach($rParamArray as $index => $param) $rpeList[] = array($param, $eParams[$index], $pParamsArray[$index]);
|
||||
|
||||
$methodParam = preg_replace("/,\s*'/", ', ', $methodParam);
|
||||
|
||||
return array($moduleName, $methodName, $methodParam);
|
||||
return $rpeList;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,13 +387,6 @@ function getValues($value, $keys, $delimiter)
|
||||
*/
|
||||
function e($expect)
|
||||
{
|
||||
global $isExtractAction;
|
||||
if($isExtractAction)
|
||||
{
|
||||
parseScript($expect);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-13
@@ -254,19 +254,20 @@ class yaml
|
||||
*
|
||||
* @param int $rows
|
||||
* @param string $dataDirYaml The yaml file names in the data directory
|
||||
* @param string $version
|
||||
* @param bool $isClear Truncate table if set isClear to true.
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function gen($rows, $dataDirYaml = '')
|
||||
public function gen($rows, $dataDirYaml = '', $isClear = true)
|
||||
{
|
||||
$runFileDir = dirname(getcwd() . DS . $_SERVER['SCRIPT_FILENAME']);
|
||||
$runFileName = str_replace(strrchr($_SERVER['SCRIPT_FILENAME'], "."), "", $_SERVER['SCRIPT_FILENAME']);
|
||||
|
||||
$pos = strripos($runFileName, DS);
|
||||
if($pos !== false) $runFileName = mb_substr($runFileName, $pos+1);
|
||||
|
||||
if(!is_dir("$runFileDir/data")) mkdir("$runFileDir/data", 0777, true);
|
||||
$runFileDir = dirname($runFileName);
|
||||
|
||||
if(!is_dir("{$runFileDir}/data")) mkdir("{$runFileDir}/data", 0777);
|
||||
$yamlFile = "{$runFileDir}/data/{$this->tableName}_{$runFileName}.yaml";
|
||||
|
||||
$yamlDataArr = array();
|
||||
@@ -287,7 +288,7 @@ class yaml
|
||||
yaml_emit_file($yamlFile, $yamlDataArr, YAML_UTF8_ENCODING);
|
||||
}
|
||||
|
||||
$this->insertDB($yamlFile, $this->tableName, $rows);
|
||||
$this->insertDB($yamlFile, $this->tableName, $rows, $isClear);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,7 +297,7 @@ class yaml
|
||||
* @param string $yamlFile
|
||||
* @param string $tableName
|
||||
* @param int $rows
|
||||
* @param bool $isClear
|
||||
* @param bool $isClear Truncate table if set isClear to true.
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
@@ -318,13 +319,15 @@ class yaml
|
||||
$dbUser = $this->config->db->user;
|
||||
$dbPWD = $this->config->db->password;
|
||||
|
||||
$setModeSql = "mysql -u%s -p%s -h%s -P%s %s -e \"SET global sql_mode = ''; \" 2>/dev/null";
|
||||
$command = "$zdPath -c %s -d %s -n %d -t %s -dns mysql://%s:%s@%s:%s/%s#utf8";
|
||||
if($isClear === true) $command .= ' --clear';
|
||||
$execYaml = sprintf($command, $configYaml, $yamlFile, $rows, $tableName, $dbUser, $dbPWD, $dbHost, $dbPort, $dbName);
|
||||
$execDump = sprintf($dumpCommand, $dbUser, $dbPWD, $dbHost, $dbPort, $dbName, $tableName);
|
||||
$execSetMode = sprintf($setModeSql, $dbUser, $dbPWD, $dbHost, $dbPort, $dbName);
|
||||
system($execSetMode);
|
||||
$command = "$zdPath -c %s -d %s -n %d -t %s -dns mysql://%s:%s@%s:%s/%s#utf8";
|
||||
if($isClear === true)
|
||||
{
|
||||
/* Truncate table to reset auto increment number. */
|
||||
system(sprintf("mysql -u%s -p%s -h%s -P%s %s -e 'truncate %s' 2>/dev/null", $dbUser, $dbPWD, $dbHost, $dbPort, $dbName, $tableName));
|
||||
$command .= ' --clear';
|
||||
}
|
||||
$execYaml = sprintf($command, $configYaml, $yamlFile, $rows, $tableName, $dbUser, $dbPWD, $dbHost, $dbPort, $dbName);
|
||||
$execDump = sprintf($dumpCommand, $dbUser, $dbPWD, $dbHost, $dbPort, $dbName, $tableName);
|
||||
system($execDump);
|
||||
system($execYaml);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ $config->installedVersion = $app->getInstalledVersion();
|
||||
if($config->version != $config->installedVersion) die(header('location: upgrade.php'));
|
||||
|
||||
/* Run the app. */
|
||||
$app->setStartTime($startTime);
|
||||
$common = $app->loadCommon();
|
||||
|
||||
/* Check the request is getconfig or not. */
|
||||
|
||||
+20
-7
@@ -77,25 +77,37 @@
|
||||
$('#zinbar').on('click', () => $('#zinErrorList').toggleClass('in'));
|
||||
}
|
||||
|
||||
function updatePerfInfo(options, stage, error)
|
||||
function updatePerfInfo(options, stage, info)
|
||||
{
|
||||
if(!DEBUG || isIndexPage) return;
|
||||
options[stage] = performance.now();
|
||||
const $perf = options.id === 'page' ? $('#pagePerf') : $('#partPerf');
|
||||
if(stage === 'requestBegin')
|
||||
{
|
||||
$perf.html(`<div class="opacity-50 pl-2">${options.id === 'page' ? 'PAGE' : (options.id === '#dtable' ? 'TABLE' : 'PART')}</div>`).append($('<div class="px-2 zin-perf-load">loading...</div>')).attr('title', `Loading from ${options.url}`);
|
||||
$perf.html(`<div class="opacity-50 pl-2">${options.id === 'page' ? 'PAGE' : (options.id === '#dtable' ? 'TABLE' : 'PART')}</div>`)
|
||||
.append($('<div class="px-2 zin-perf-load">loading...</div>'))
|
||||
.attr('title', `Loading from ${options.url}`);
|
||||
if(options.id === 'page') $('#partPerf').empty();
|
||||
}
|
||||
else if(stage === 'requestEnd')
|
||||
{
|
||||
const loadTime = options.requestEnd - options.requestBegin;
|
||||
$perf.find('.zin-perf-load').addClass('font-bold').html(`<i class="icon icon-arrow-down"></i>${loadTime.toFixed(2)}ms`).addClass(loadTime > 400 ? 'text-danger' : (loadTime > 100 ? 'text-warning' : 'text-success')).attr('title', `Load time for ${options.url}`);
|
||||
if(error) showErrors([{message: error.message}]);
|
||||
$perf.find('.zin-perf-load')
|
||||
.addClass('font-bold')
|
||||
.html(`<i class="icon icon-arrow-down"></i>${loadTime.toFixed(2)}ms`)
|
||||
.addClass(loadTime > 400 ? 'text-danger' : (loadTime > 100 ? 'text-warning' : 'text-success'))
|
||||
.attr('title', `Load time for ${options.url}`);
|
||||
if(info && info.dataLength)
|
||||
{
|
||||
$perf.append(`<div title="Load size"><i class="icon icon-cube"></i> ${zui.formatBytes(info.dataLength)}</div>`)
|
||||
.append(`<div title="load speed" class="ml-1"><i class="icon icon-run"></i> ${zui.formatBytes(info.dataLength / (loadTime / 1000))}/s</div>`);
|
||||
}
|
||||
if(info && info.error) showErrors([{message: info.error.message}]);
|
||||
}
|
||||
else if(stage === 'renderBegin')
|
||||
{
|
||||
$perf.append($('<div class="px-2 zin-perf-render">rendering...</div>').attr('title', `Renderring ${options.id}`));
|
||||
$perf.append($('<div class="px-2 zin-perf-render">rendering...</div>')
|
||||
.attr('title', `Renderring ${options.id}`));
|
||||
}
|
||||
else if(stage === 'renderEnd')
|
||||
{
|
||||
@@ -269,7 +281,7 @@
|
||||
},
|
||||
success: (data) =>
|
||||
{
|
||||
updatePerfInfo(options, 'requestEnd');
|
||||
updatePerfInfo(options, 'requestEnd', {dataLength: data.length});
|
||||
options.result = 'success';
|
||||
try{data = JSON.parse(data);}catch(e){data = [{name: data.includes('Fatal error') ? 'fatal' : 'html', data: data}];}
|
||||
if(options.updateUrl !== false) currentAppUrl = url;
|
||||
@@ -283,7 +295,7 @@
|
||||
},
|
||||
error: (xhr, type, error) =>
|
||||
{
|
||||
updatePerfInfo(options, 'requestEnd', error);
|
||||
updatePerfInfo(options, 'requestEnd', {error: error});
|
||||
if(type === 'abort') return console.log('[ZIN] ', 'Abord fetch data from ' + url, {xhr, type, error});;
|
||||
if(DEBUG) console.error('[ZIN] ', 'Fetch data failed from ' + url, {xhr, type, error});
|
||||
zui.Messager.show('ZIN: Fetch data failed from ' + url);
|
||||
@@ -504,6 +516,7 @@
|
||||
}).on('zui.locate', (e, data) =>
|
||||
{
|
||||
if(!data) return;
|
||||
if(data === true) return loadCurrentPage();
|
||||
if(typeof data === 'string') data = {url: data};
|
||||
loadPage(data.url, data.selector);
|
||||
});
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* The render function file of zin of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @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 Hao Sun <sunhao@easycorp.ltd>
|
||||
* @package zin
|
||||
* @version $Id
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
|
||||
@@ -13,14 +13,15 @@ namespace zin;
|
||||
|
||||
require_once 'zin.class.php';
|
||||
|
||||
function render($wgName = 'page', $options = NULL)
|
||||
function render(string $wgName = 'page', string|array $options = NULL)
|
||||
{
|
||||
$args = [];
|
||||
$args = array();
|
||||
foreach(zin::$globalRenderList as $item)
|
||||
{
|
||||
if(is_object($item) && isset($item->parent) && $item->parent) continue;
|
||||
$args[] = $item;
|
||||
}
|
||||
zin::$globalRenderList = array();
|
||||
|
||||
if(is_string($wgName) && isset(zin::$globalRenderMap[$wgName])) $wgName = zin::$globalRenderMap[$wgName];
|
||||
|
||||
@@ -32,12 +33,12 @@ function render($wgName = 'page', $options = NULL)
|
||||
if(isset($_SERVER['HTTP_X_ZIN_OPTIONS']) && !empty($_SERVER['HTTP_X_ZIN_OPTIONS']))
|
||||
{
|
||||
$setting = $_SERVER['HTTP_X_ZIN_OPTIONS'];
|
||||
$options = $setting[0] === '{' ? json_decode($setting, true) : ['selector' => $setting];
|
||||
$options = $setting[0] === '{' ? json_decode($setting, true) : array('selector' => $setting);
|
||||
}
|
||||
}
|
||||
|
||||
global $app;
|
||||
data('zinErrors', $app->zinErrors ?? []);
|
||||
data('zinErrors', isset($app->zinErrors) ? $app->zinErrors : array());
|
||||
|
||||
$wg = createWg($wgName, $args);
|
||||
if(!$isFullPage) $wg = fragment($wg);
|
||||
|
||||
Reference in New Issue
Block a user