Merge branch 'main' into feature/demand

This commit is contained in:
hufangzhou
2024-07-10 14:22:28 +08:00
161 changed files with 2074 additions and 1160 deletions
+37
View File
@@ -0,0 +1,37 @@
version: 1
kind: pipeline
spec:
stages:
- name: 质量检查
type: ci
spec:
steps:
- name: 配置scanner
type: run
spec:
container:
image: hub.zentao.net/ci/git:2.45.2
script:
- git diff --name-only --diff-filter=d ${DRONE_COMMIT_AFTER} ${DRONE_COMMIT_BEFORE} | tee /tmp/changes
- |
cat > sonar-project.properties <<EOL
sonar.projectKey=$(echo $CI_REPO_NAME | tr '/' ':')
sonar.sources=.
sonar.language=php
sonar.branch.name=${DRONE_TARGET_BRANCH}
sonar.inclusions=$(xargs < /tmp/changes | tr ' ' ',')
sonar.working.directory=/tmp
sonar.exclusions=**/extension/**/ext/**,**/*.bak,**/*.sql,**/*.js,**/*.css,**/*.yaml,**/*.zip,**/*.out,**/lang/*,doc/*
EOL
- cat sonar-project.properties
- name: 扫描
type: run
spec:
envs:
SONAR_HOST_URL: https://sq.qc.oop.cc/
container:
image: hub.zentao.net/app/sonar-scanner-cli:10
script:
- sonar-scanner -Dsonar.token=${{ secrets.get("sq_sonar_token") }}
+3 -3
View File
@@ -17,6 +17,7 @@ clean:
rm -fr zentaotask
rm -fr zentaotest
rm -fr *.tar.gz
rm -fr *.tar.xz
rm -fr *.zip
rm -fr build/linux/lampp
rm -rf buildroot/
@@ -84,7 +85,6 @@ zentaoxx:
cp -r xuan/xxb/module/client zentaoxx/extension/xuan/
cp -r xuan/xxb/module/conference zentaoxx/extension/xuan/
cp -r xuan/xxb/module/integration zentaoxx/extension/xuan/
cp -r xuan/xxb/module/license zentaoxx/extension/xuan/
mkdir -p zentaoxx/extension/xuan/common/view
cp -r xuan/xxb/module/common/view/header.modal.html.php zentaoxx/extension/xuan/common/view
cp -r xuan/xxb/module/common/view/marked.html.php zentaoxx/extension/xuan/common/view
@@ -207,7 +207,7 @@ package:
mkdir zentaopms/misc; cp misc/cn2tw.php zentaopms/misc; cd zentaopms/misc; php cn2tw.php
#rm -r zentaopms/module/misc/ext
rm -rf zentaopms/misc
rm -rf extension/xuanxuan
rm -rf zentaopms/extension/xuanxuan
find zentaopms/module -type d -name 'test' -maxdepth 2 -exec rm -rf {} +
pms:
make common
@@ -316,7 +316,7 @@ ciCommon:
rm -fr zentaoalm
# move pms zip to build and release path.
rm -f $(BUILD_PATH)/ZenTao*.zip $(RELEASE_PATH)/ZenTaoPMS.$(VERSION).zip $(RELEASE_PATH)/ZenTaoALM.$(VERSION).int.zip
rm -f $(BUILD_PATH)/ZenTao*.zip $(BUILD_PATH)/ZenTao*.tar.xz $(RELEASE_PATH)/ZenTaoPMS.$(VERSION).zip $(RELEASE_PATH)/ZenTaoALM.$(VERSION).int.zip $(RELEASE_PATH)/ZenTaoPMS.$(VERSION).tar.xz $(RELEASE_PATH)/ZenTaoALM.$(VERSION).int.tar.xz
cp ZenTaoPMS.$(VERSION).zip $(BUILD_PATH)
cp ZenTaoPMS.$(VERSION).zip ZenTaoALM.$(VERSION).int.zip $(RELEASE_PATH)
cizip:
+1 -1
View File
@@ -18,7 +18,7 @@
}
},
"image": {
"phpversion": "8.1.28",
"phpversion": "8.1.29",
"public": {
"namespace": {
"release": "app",
+8 -8
View File
@@ -610,14 +610,14 @@ REPLACE INTO `zt_searchindex` (`id`, `vision`, `objectType`, `objectID`, `title`
(33, 'rnd', 'case', 3, ' 26032 38395 20013 24515 30340 27979 35797 29992 20363', '', '2024-04-02 16:24:38', '2024-04-02 16:24:38'),
(34, 'rnd', 'case', 4, ' 25104 26524 23637 31034 27979 35797 29992 20363', '', '2024-04-02 16:25:09', '2024-04-02 16:25:09'),
(35, 'rnd', 'testtask', 1, ' 20225 19994 32593 31449 31532 19968 26399 27979 35797 20219 21153', '', '2024-04-02 16:26:35', '2024-04-02 16:26:35');
REPLACE INTO `zt_story` (`id`, `vision`, `parent`, `product`, `branch`, `module`, `plan`, `source`, `sourceNote`, `fromBug`, `feedback`, `title`, `keywords`, `type`, `category`, `pri`, `estimate`, `status`, `subStatus`, `color`, `stage`, `stagedBy`, `mailto`, `lib`, `fromStory`, `fromVersion`, `openedBy`, `openedDate`, `assignedTo`, `assignedDate`, `approvedDate`, `lastEditedBy`, `lastEditedDate`, `changedBy`, `changedDate`, `reviewedBy`, `reviewedDate`, `releasedDate`, `closedBy`, `closedDate`, `closedReason`, `activatedDate`, `toBug`, `childStories`, `linkStories`, `linkRequirements`, `twins`, `duplicateStory`, `version`, `storyChanged`, `feedbackBy`, `notifyEmail`, `BSA`, `duration`, `demand`, `submitedBy`, `roadmap`, `URChanged`, `retractedReason`, `retractedBy`, `retractedDate`, `deleted`) VALUES
(1, 'rnd', 0, 1, 0, 1, '1', '', '', 0, 0, '首页设计和开发', '', 'story', 'feature', 1, 1, 'active', '', '', 'developing', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, 'productManager', '2024-04-02 15:04:35', '', NULL, ',productManager', '2024-04-02 15:04:35', NULL, '', NULL, '', NULL, 0, '', '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(2, 'rnd', 0, 1, 0, 2, '1', '', '', 0, 0, '新闻中心的设计和开发。', '', 'story', 'feature', 1, 1, 'active', '', '', 'projected', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, 'productManager', '2024-04-02 15:04:35', '', NULL, ',productManager', '2024-04-02 15:04:35', NULL, '', NULL, '', NULL, 0, '', '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(3, 'rnd', 0, 1, 0, 3, '1', '', '', 0, 0, '成果展示的设计和开发', '', 'story', 'feature', 1, 0, 'active', '', '', 'developing', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, 'productManager', '2024-04-02 15:04:35', '', NULL, ',productManager', '2024-04-02 15:04:35', NULL, '', NULL, '', NULL, 0, '', '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(4, 'rnd', 0, 1, 0, 4, '1', '', '', 0, 0, '售后服务的设计和开发', '', 'story', 'feature', 1, 1, 'active', '', '', 'developed', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, 'productManager', '2024-04-02 15:04:35', '', NULL, ',productManager', '2024-04-02 15:04:35', NULL, '', NULL, '', NULL, 0, '', '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(5, 'rnd', 0, 1, 0, 5, '1', '', '', 0, 0, '诚聘英才的设计和开发', '', 'story', 'feature', 1, 1, 'reviewing', '', '', 'planned', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, '', NULL, '', NULL, '', NULL, NULL, '', NULL, '', NULL, 0, '', '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(6, 'rnd', 0, 1, 0, 6, '1', '', '', 0, 0, '合作洽谈的设计和开发', '', 'story', 'feature', 1, 1, 'reviewing', '', '', 'planned', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, '', NULL, '', NULL, '', NULL, NULL, '', NULL, '', NULL, 0, '', '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(7, 'rnd', 0, 1, 0, 7, '1', '', '', 0, 0, '关于我们的设计和开发', '', 'story', 'feature', 1, 1, 'reviewing', '', '', 'planned', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, '', NULL, '', NULL, '', NULL, NULL, '', NULL, '', NULL, 0, '', '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0');
REPLACE INTO `zt_story` (`id`, `vision`, `parent`, `product`, `branch`, `module`, `plan`, `source`, `sourceNote`, `fromBug`, `feedback`, `title`, `keywords`, `type`, `category`, `pri`, `estimate`, `status`, `subStatus`, `color`, `stage`, `stagedBy`, `mailto`, `lib`, `fromStory`, `fromVersion`, `openedBy`, `openedDate`, `assignedTo`, `assignedDate`, `approvedDate`, `lastEditedBy`, `lastEditedDate`, `changedBy`, `changedDate`, `reviewedBy`, `reviewedDate`, `releasedDate`, `closedBy`, `closedDate`, `closedReason`, `activatedDate`, `toBug`, `linkStories`, `linkRequirements`, `twins`, `duplicateStory`, `version`, `storyChanged`, `feedbackBy`, `notifyEmail`, `BSA`, `duration`, `demand`, `submitedBy`, `roadmap`, `URChanged`, `retractedReason`, `retractedBy`, `retractedDate`, `deleted`) VALUES
(1, 'rnd', 0, 1, 0, 1, '1', '', '', 0, 0, '首页设计和开发', '', 'story', 'feature', 1, 1, 'active', '', '', 'developing', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, 'productManager', '2024-04-02 15:04:35', '', NULL, ',productManager', '2024-04-02 15:04:35', NULL, '', NULL, '', NULL, 0, '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(2, 'rnd', 0, 1, 0, 2, '1', '', '', 0, 0, '新闻中心的设计和开发。', '', 'story', 'feature', 1, 1, 'active', '', '', 'projected', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, 'productManager', '2024-04-02 15:04:35', '', NULL, ',productManager', '2024-04-02 15:04:35', NULL, '', NULL, '', NULL, 0, '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(3, 'rnd', 0, 1, 0, 3, '1', '', '', 0, 0, '成果展示的设计和开发', '', 'story', 'feature', 1, 0, 'active', '', '', 'developing', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, 'productManager', '2024-04-02 15:04:35', '', NULL, ',productManager', '2024-04-02 15:04:35', NULL, '', NULL, '', NULL, 0, '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(4, 'rnd', 0, 1, 0, 4, '1', '', '', 0, 0, '售后服务的设计和开发', '', 'story', 'feature', 1, 1, 'active', '', '', 'developed', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, 'productManager', '2024-04-02 15:04:35', '', NULL, ',productManager', '2024-04-02 15:04:35', NULL, '', NULL, '', NULL, 0, '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(5, 'rnd', 0, 1, 0, 5, '1', '', '', 0, 0, '诚聘英才的设计和开发', '', 'story', 'feature', 1, 1, 'reviewing', '', '', 'planned', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, '', NULL, '', NULL, '', NULL, NULL, '', NULL, '', NULL, 0, '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(6, 'rnd', 0, 1, 0, 6, '1', '', '', 0, 0, '合作洽谈的设计和开发', '', 'story', 'feature', 1, 1, 'reviewing', '', '', 'planned', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, '', NULL, '', NULL, '', NULL, NULL, '', NULL, '', NULL, 0, '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0'),
(7, 'rnd', 0, 1, 0, 7, '1', '', '', 0, 0, '关于我们的设计和开发', '', 'story', 'feature', 1, 1, 'reviewing', '', '', 'planned', '', '', 0, 0, 1, 'admin', '2024-04-02 15:01:20', '', NULL, NULL, '', NULL, '', NULL, '', NULL, NULL, '', NULL, '', NULL, 0, '', '', '', 0, 1, '0', '', '', '', '', 0, '', '', '0', '', '', NULL, '0');
REPLACE INTO `zt_storyreview` (`story`, `version`, `reviewer`, `result`, `reviewDate`) VALUES
(1, 1, 'productManager', 'pass', '2024-04-02 15:04:35'),
(2, 1, 'productManager', 'pass', '2024-04-02 15:04:35'),
+5
View File
@@ -2501,11 +2501,16 @@ class baseSQL
public function groupBy($groupBy)
{
if($this->inCondition and !$this->conditionIsTrue) return $this;
//The dm database cannot use alias for group by
/*
if(!preg_match('/^\w+[a-zA-Z0-9_`.]+$/', $groupBy))
{
$groupBy = htmlspecialchars($groupBy);
helper::end("Group is bad query, The group is $groupBy");
}
*/
$this->sql .= ' ' . DAO::GROUPBY . " $groupBy";
return $this;
}
+5
View File
@@ -340,11 +340,16 @@ class sql extends baseSQL
public function groupBy($groupBy)
{
if($this->inCondition and !$this->conditionIsTrue) return $this;
//The dm database cannot use alias for group by
/*
if(!preg_match('/^[a-zA-Z0-9_`\.,\s\"]+$/', $groupBy))
{
$groupBy = htmlspecialchars($groupBy);
die("Group is bad query, The group is $groupBy");
}
*/
$this->sql .= ' ' . DAO::GROUPBY . " $groupBy";
return $this;
}
+7 -102
View File
@@ -73,24 +73,6 @@ class duckdb
*/
public $tmpPath;
/**
* 全局变量$fields
* The global fields variable.
*
* @var object
* @access public
*/
public $fields;
/**
* 全局变量$tables
* The global fields variable.
*
* @var object
* @access public
*/
public $tables;
/**
* 构造方法。
* The construct method.
@@ -119,7 +101,7 @@ class duckdb
public function setBinPath()
{
$duckdbBin = $this->getBinConfig();
$sourcePath = $this->baseRoot . 'bin' . DS . 'duckdb' . DS;
$sourcePath = $this->tmpRoot . 'duckdb' . DS;
$zboxPath = $duckdbBin['path'];
$file = $duckdbBin['file'];
@@ -170,15 +152,6 @@ class duckdb
*/
public function query($sql = '')
{
$sqlparser = dirname(dirname(__FILE__)) . '/sqlparser/sqlparser.class.php';
include_once $sqlparser;
$parser = new sqlparser($sql);
$statement = $parser->statements[0];
if(isset($statement->bodyParser->statements[0])) $statement = $statement->bodyParser->statements[0];
$this->fields = $this->getFields($statement);
$this->tables = $this->getTables($statement);
$sql = $this->replaceBackQuote($sql);
$sql = $this->replaceTable2Parquet($sql);
$sql = $this->standLimit($sql);
@@ -215,16 +188,14 @@ class duckdb
*/
private function replaceTable2Parquet($sql)
{
if(empty($this->tables)) return $sql;
$ztpattern = "/\b{$this->prefix}([a-zA-Z0-9_]+)\b/";
$ztvpattern = "/\bztv_([a-zA-Z0-9_]+)\b/";
$replace = "'__biPath__$0.parquet'";
foreach($this->tables as $table)
{
$pattern = " {$table} ";
$replace = "'{$this->tmpPath}{$table}.parquet'";
$sql = str_replace($pattern, $replace, $sql);
}
$sql = preg_replace($ztpattern, $replace, $sql);
$sql = preg_replace($ztvpattern, $replace, $sql);
$sql = str_replace('__biPath__', $this->tmpPath, $sql);
return $sql;
}
@@ -251,53 +222,6 @@ class duckdb
return $fields;
}
/**
* 获取sql中的表名。
* Get tables form sqlparser statment.
*
* @param object $statment
* @access public
* @return array
*/
private function getTables(object $statement)
{
$tables = array();
if($statement->from)
{
foreach($statement->from as $fromInfo)
{
if($fromInfo->table)
{
$tables[] = $fromInfo->table;
}
elseif($fromInfo->subquery)
{
$parser = new sqlparser($fromInfo->expr);
$subTables = $this->getTables($parser->statements[0]);
$tables = array_merge($tables, $subTables);
}
}
}
if($statement->join)
{
foreach($statement->join as $joinInfo)
{
if($joinInfo->expr->table)
{
$tables[] = $joinInfo->expr->table;
}
elseif($joinInfo->expr->subquery)
{
$parser = new sqlparser($joinInfo->expr->expr);
$subTables = $this->getTables($parser->statements[0]);
$tables = array_merge($tables, $subTables);
}
}
}
return array_filter(array_unique($tables));
}
/**
* 将LIMIT语句替换为duckdb可执行的格式。
* Standard LIMIT syntax.
@@ -326,8 +250,6 @@ class duckdb
*/
public function getResult()
{
$this->checkFieldNamesMatch();
$exec = "$this->binPath :memory: \"$this->sql\" -json 2>&1";
$output = shell_exec($exec);
@@ -344,23 +266,6 @@ class duckdb
}
}
/**
* 检查sql返回的字段不能与表名同名。
* Check sql fields can not same as table name.
*
* @access public
* @return this.
*/
public function checkFieldNamesMatch()
{
$flipTable = array_flip($this->tables);
foreach($this->fields as $field)
{
if(isset($flipTable[$field['column']])) return throw new Exception("sql fields can not named {$field['column']}");
if(isset($flipTable[$field['alias']])) return throw new Exception("sql fields can not named {$field['alias']}");
}
}
//-------------------- Fetch相关方法(Fetch related methods) -------------------//
/**
+56 -20
View File
@@ -345,6 +345,29 @@ class pivotState
$this->pivotFilters = array();
}
/**
* Clear drills.
*
* @access public
* @return void
*/
public function clearDrills()
{
$this->drills = array();
}
/**
* Sql changed.
*
* @access public
* @return void
*/
public function sqlChanged()
{
$this->queryCols = array();
$this->queryData = array();
}
/**
* Get filters.
*
@@ -462,25 +485,26 @@ class pivotState
*/
public function convertFiltersToWhere($filters)
{
if($this->isQueryFilter($filters)) return array_values($filters);
$filterWheres = array();
foreach($filters as $filter)
{
$field = $filter['field'];
$default = zget($filter, 'default', '');
$from = zget($filter, 'from', 'result');
$type = $filter['type'];
if($from == 'query' || empty($default)) continue;
if(empty($default)) continue;
switch($type)
{
case 'select':
if(is_array($default)) $default = implode("', '", array_filter($default, function($val){return trim($val) != '';}));
$value = "('" . $default . "')";
$filterWheres[$field] = array('operator' => 'IN', 'value' => $value);
$filterWheres[$field] = array('operator' => 'IN', 'type' => $type, 'value' => $value);
break;
case 'input':
$filterWheres[$field] = array('operator' => 'LIKE', 'value' => "'%$default%'");
$filterWheres[$field] = array('operator' => 'LIKE', 'type' => $type, 'value' => "'%$default%'");
break;
case 'date':
case 'datetime':
@@ -490,9 +514,9 @@ class pivotState
if(!empty($begin)) $begin = date('Y-m-d 00:00:00', strtotime($begin));
if(!empty($end)) $end = date('Y-m-d 23:59:59', strtotime($end));
if(!empty($begin) && empty($end)) $filterWheres[$field] = array('operator' => '>', 'value' => "'{$begin}'");
if( empty($begin) && !empty($end)) $filterWheres[$field] = array('operator' => '<', 'value' => "'{$end}'");
if(!empty($begin) && !empty($end)) $filterWheres[$field] = array('operator' => 'BETWEEN', 'value' => "'{$begin}' AND '{$end}'");
if(!empty($begin) && empty($end)) $filterWheres[$field] = array('operator' => '>', 'type' => $type, 'value' => "'{$begin}'");
if( empty($begin) && !empty($end)) $filterWheres[$field] = array('operator' => '<', 'type' => $type, 'value' => "'{$end}'");
if(!empty($begin) && !empty($end)) $filterWheres[$field] = array('operator' => 'BETWEEN', 'type' => $type, 'value' => "'{$begin}' AND '{$end}'");
break;
default:
break;
@@ -899,14 +923,17 @@ class pivotState
if(empty($fieldSettings)) return null;
$fields = array();
$keys = array('name', 'object', 'field', 'type');
foreach($fieldSettings as $fieldSetting)
$keys = array('object', 'field', 'type');
foreach($fieldSettings as $fieldKey => $fieldSetting)
{
$field = array();
foreach($keys as $key) if(isset($fieldSetting[$key])) $field[$key] = $fieldSetting[$key];
$fields[$fieldSetting['name']] = $field;
}
foreach($keys as $key)
{
if(isset($fieldSetting[$key])) $field[$key] = $fieldSetting[$key];
}
$fields[$fieldKey] = $field;
}
return $type == 'object' ? $fields : json_encode($fields);
}
@@ -924,13 +951,16 @@ class pivotState
if(empty($fieldSettings)) return null;
$langs = array();
$keys = array('name', 'object', 'field', 'type');
foreach($fieldSettings as $fieldSetting)
$keys = array('object', 'field', 'type');
foreach($fieldSettings as $fieldKey => $fieldSetting)
{
$lang = array();
foreach($fieldSetting as $key => $value) if(!in_array($key, $keys)) $lang[$key] = $value;
foreach($fieldSetting as $key => $value)
{
if(!in_array($key, $keys)) $lang[$key] = $value;
}
$langs[$fieldSetting['name']] = $lang;
$langs[$fieldKey] = $lang;
}
return $type == 'object' ? $langs : json_encode($langs);
@@ -1011,14 +1041,20 @@ class pivotState
{
if(!$this->isQueryFilter()) return;
$keepFilters = array();
foreach($this->filters as $filter)
$keepFilters = array();
$keepPivotFilters = array();
foreach($this->filters as $index => $filter)
{
$field = $filter['field'];
if(strpos($this->sql, "\$$field") !== false) $keepFilters[] = $filter;
if(strpos($this->sql, "\$$field") !== false)
{
$keepFilters[] = $filter;
$keepPivotFilters[] = $this->pivotFilters[0][$index];
}
}
$this->filters = $keepFilters;
$this->filters = $keepFilters;
$this->pivotFilters[0] = $keepPivotFilters;
}
/**
+1 -1
View File
@@ -57,7 +57,7 @@ class filter extends wg
protected function buildDatetimePicker(): datetimePicker|array
{
list($name, $value, $onChange) = $this->prop(array('name', 'valuea', 'onChange'));
list($name, $value, $onChange) = $this->prop(array('name', 'value', 'onChange'));
return datetimePicker
(
+2 -2
View File
@@ -250,8 +250,7 @@ class ThinkOptions extends zui.Component
_createRow(item, options)
{
var name = '';
if(options.data[item.index]) name = options.data[item.index];
const name = options.data[item.index] || '';
const $row = $
([
`<div class="think-options-row think-options-item" data-id="${item.id}">`,
@@ -361,6 +360,7 @@ class ThinkOptions extends zui.Component
const newItem = this.updateItem(item.name);
if(focus) this.focus(newItem.name);
this.$element.find(`.think-options-row[data-id="${newItem.id}"] .think-options-step-text`).val('');
}
moveAfter(fromName, toName)
+5 -5
View File
@@ -157,7 +157,7 @@ class thinkStepMenu extends wg
$transitionAction[] = array(
'key' => 'transition',
'icon' => 'transition',
'text' => $this->lang->thinkstep->actions['transition'],
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['transition'],
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'transition')"),
);
}
@@ -193,25 +193,25 @@ class thinkStepMenu extends wg
array(
'key' => 'radio',
'icon' => 'radio',
'text' => $this->lang->thinkstep->actions['radio'],
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['radio'],
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'radio')"),
),
array(
'key' => 'checkbox',
'icon' => 'checkbox',
'text' => $this->lang->thinkstep->actions['checkbox'],
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['checkbox'],
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'checkbox')"),
),
array(
'key' => 'input',
'icon' => 'input',
'text' => $this->lang->thinkstep->actions['input'],
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['input'],
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'input')"),
),
array(
'key' => 'tableInput',
'icon' => 'cell-input',
'text' => $this->lang->thinkstep->actions['tableInput'],
'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['tableInput'],
'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'tableInput')"),
),
));
+1 -1
View File
@@ -1,2 +1,2 @@
.form-horz .form-group {align-items: center;}
.result-width {width: calc(80% - 52px);}
.result-width {width: calc(100% - 148px);}
+1 -1
View File
@@ -59,7 +59,7 @@ window.addRow = function(e)
* @access public
* @return void
*/
$(document).on('click', '.btn-delete', function() {
$('.think-step').on('click', '.btn-delete', function() {
zui.Modal.confirm({message: deleteTip}).then((res) => {
if(res)
{
+3 -3
View File
@@ -58,7 +58,7 @@ class thinkTableInput extends thinkQuestion
setClass('flex items-center'),
div
(
setClass('text-right mr-2 w-1/5 line-clamp-2'),
setClass('text-right mr-2 w-24 line-clamp-2'),
$item,
set::title($item)
),
@@ -100,7 +100,7 @@ class thinkTableInput extends thinkQuestion
div
(
set::title($item),
setClass('mt-2 w-1/5'),
setClass('mt-2 w-24'),
textarea
(
set::rows('2'),
@@ -154,7 +154,7 @@ class thinkTableInput extends thinkQuestion
(
set::rows('2'),
setID('customFields'),
setClass('mt-2 w-1/5'),
setClass('mt-2 w-24'),
set::value(''),
set::placeholder($lang->thinkrun->placeholder->rowTitle)
),
+3
View File
@@ -527,6 +527,9 @@ class actionTao extends actionModel
case 'roadmap':
$paramString = "roadmapID={$action->extra}";
break;
case 'assetlib':
$paramString = "libID={$action->extra}";
break;
case 'demand':
$paramString = "demandID={$action->extra}";
break;
@@ -410,13 +410,14 @@ class actionTest
* @param string $objectType
* @param int $objectID
* @param string $actionType
* @param array $requirements
* @param array $requirements
* @param array $epics
* @access public
* @return string
*/
public function getObjectLabelTest($objectType, $objectID, $actionType, $requirements)
public function getObjectLabelTest($objectType, $objectID, $actionType, $requirements, $epics)
{
$object = $this->objectModel->getObjectLabel($objectType, $objectID, $actionType, $requirements);
$object = $this->objectModel->getObjectLabel($objectType, $objectID, $actionType, $requirements, $epics);
if(dao::isError()) return dao::getError();
+1 -2
View File
@@ -91,9 +91,8 @@ $version = $tester->dao->select('value')->from(TABLE_CONFIG)->where('`key`')->eq
$action->createTest($objectTypeList[0], $objectIDList[0], $storyActionTypeList[0], $comment[0], '', '', '', $versionList[0]);
r($tester->dao->select('COUNT(1) AS count')->from('zt_actionrecent')->fetch('count')) && p() && e('8'); //测试升级中的并且版本号小于18.7的情况,不创建actionrecent
unset(dao::$cache['zt_actionrecent']);
$action->createTest($objectTypeList[0], $objectIDList[0], $storyActionTypeList[0], $comment[0], '', '', '', $versionList[1]);
r($tester->dao->select('COUNT(1) AS count')->from('zt_actionrecent')->fetch('count')) && p() && e('9'); //测试升级中的并且版本号大于18.7的情况,创建actionrecent
$tester->dao->update(TABLE_CONFIG)->set('value')->eq($version)->where('`key`')->eq('version')->andWhere('owner')->eq('system')->andWhere('module')->eq('common')->exec();
$tester->app->upgrading = false;
$tester->app->upgrading = false;
@@ -57,8 +57,8 @@ $directionList = array('next', 'pre');
$action = new actionTest();
r($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[0])) && p('0:id,objectType;1:id,objectType') && e('64,testsuite;32,testcase'); // 获取排序为date倒序的所有动态
r($action->getDynamicBySearchTest($queryID[0], $orderByList[1], $limit, $dateList[0], $directionList[0])) && p('0:id,objectType;1:id,objectType') && e('65,caselib;33,case'); // 获取排序为date正序的所有动态
r($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[1])) && p('0:id,objectType;1:id,objectType') && e('64,testsuite;32,testcase'); // 获取排序为date倒序的今年之后的动态
r($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[1], $directionList[0])) && p('0:id,objectType;1:id,objectType') && e('63,module;31,bug'); // 获取排序为date倒序的今天之前的动态
r($action->getDynamicBySearchTest($queryID[1], $orderByList[0], $limit, $dateList[0], $directionList[1])) && p('0:id,objectType') && e('1,product'); // 获取排序为date倒序的今年之后的动态
r($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[0])) && p('0:id,objectType;1:id,objectType') && e('62,branch;31,bug'); // 获取排序为date倒序的所有动态
r($action->getDynamicBySearchTest($queryID[0], $orderByList[1], $limit, $dateList[0], $directionList[0])) && p('0:id,objectType;1:id,objectType') && e('63,module;32,testcase'); // 获取排序为date正序的所有动态
r($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[1])) && p('0:id,objectType;1:id,objectType') && e('62,branch;31,bug'); // 获取排序为date倒序的今年之后的动态
r($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[1], $directionList[0])) && p('0:id,objectType;1:id,objectType') && e('61,todo;30,build'); // 获取排序为date倒序的今天之前的动态
r($action->getDynamicBySearchTest($queryID[1], $orderByList[0], $limit, $dateList[0], $directionList[1])) && p('0:id,objectType') && e('1,product'); // 获取排序为date倒序的今年之后的动态
+3 -3
View File
@@ -183,10 +183,10 @@ r($action->getListTest($objectType[2], $bugID[1])) && p() && e('link'); // 测
r($action->getListTest($objectType[2], $bugID[2])) && p() && e('link'); // 测试获取对象类型 bug 对象ID 3 的动态信息
// 操作是 tostory 对象类型 feedback
r($action->getListTest($objectType[3], $feedbackID[0])) && p() && e('nochanged'); // 测试获取对象类型 feedback 对象ID 1 的动态信息
r($action->getListTest($objectType[3], $feedbackID[0])) && p() && e('link'); // 测试获取对象类型 feedback 对象ID 1 的动态信息
// 操作是 tostory 对象类型 ticket
r($action->getListTest($objectType[4], $hasOneID[0])) && p() && e('nochanged'); // 测试获取对象类型 ticket 对象ID 1 的动态信息
r($action->getListTest($objectType[4], $hasOneID[0])) && p() && e('link'); // 测试获取对象类型 ticket 对象ID 1 的动态信息
// 操作是 moved 对象类型是 task
r($action->getListTest($objectType[1], $taskID[0])) && p() && e('link'); // 测试获取对象类型 task 对象ID 1 的动态信息
@@ -285,7 +285,7 @@ r($action->getListTest($objectType[1], $taskID[9])) && p() && e('link'); // 测
r($action->getListTest($objectType[1], $taskID[10])) && p() && e('link'); // 测试获取对象类型 task 对象ID 11 的动态信息
// 操作 totask 对象类型 feedback
r($action->getListTest($objectType[3], $feedbackID[1])) && p() && e('nochanged'); // 测试获取对象类型 feedback 对象ID 2 的动态信息
r($action->getListTest($objectType[3], $feedbackID[1])) && p() && e('link'); // 测试获取对象类型 feedback 对象ID 2 的动态信息
// 操作 linkchildtask 对象类型 feedback
r($action->getListTest($objectType[3], $feedbackID[2])) && p() && e('nochanged'); // 测试获取对象类型 feedback 对象ID 3 的动态信息
+27 -24
View File
@@ -39,36 +39,39 @@ cid=1
global $lang, $app;
$lang->SRCommon = '研发需求';
$lang->URCommon = '用户需求';
$lang->ERCommon = '业务需求';
$app::$loadedLangs = array();
$app->loadLang('action');
$objectType = array('product', 'story', 'productplan', 'release', 'project', 'task', 'build', 'bug', 'testcase', 'case', 'testtask', 'user', 'doc', 'doclib', 'todo', 'branch', 'module', 'testsuite', 'caselib', 'testreport', 'entry', 'webhook', 'review');
$objectId = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,25);
$objectId = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,25,26);
$actionType = array('common', 'extra', 'opened', 'created', 'changed', 'edited', 'assigned', 'closed', 'deleted', 'deletedfile', 'editfile', 'erased', 'undeleted', 'hidden', 'commented', 'activated', 'blocked', 'moved', 'confirmed', 'caseconfirmed', 'bugconfirmed', 'frombug', 'started', 'delayed');
$requirements = array('25' => '25');
$epics = array('26' => '26');
$action = new actionTest();
r($action->getObjectLabelTest($objectType[0], $objectId[0], $actionType[0], $requirements)) && p() && e('产品|product|view|productID=%s'); // 测试获取objectType product objectId 1的动态信息
r($action->getObjectLabelTest($objectType[1], $objectId[1], $actionType[1], $requirements)) && p() && e('研发需求|story|view|storyID=%s'); // 测试获取objectType story objectId 2的动态信息
r($action->getObjectLabelTest($objectType[2], $objectId[2], $actionType[2], $requirements)) && p() && e('计划|productplan|view|productID=%s'); // 测试获取objectType productplan objectId 3的动态信息
r($action->getObjectLabelTest($objectType[3], $objectId[3], $actionType[3], $requirements)) && p() && e('发布|release|view|productID=%s'); // 测试获取objectType release objectId 4的动态信息
r($action->getObjectLabelTest($objectType[4], $objectId[4], $actionType[4], $requirements)) && p() && e('项目|project|index|projectID=%s'); // 测试获取objectType project objectId 5的动态信息
r($action->getObjectLabelTest($objectType[5], $objectId[5], $actionType[5], $requirements)) && p() && e('任务|task|view|taskID=%s'); // 测试获取objectType task objectId 6的动态信息
r($action->getObjectLabelTest($objectType[6], $objectId[6], $actionType[6], $requirements)) && p() && e('版本|build|view|buildID=%s'); // 测试获取objectType build objectId 7的动态信息
r($action->getObjectLabelTest($objectType[7], $objectId[7], $actionType[7], $requirements)) && p() && e('Bug|bug|view|bugID=%s'); // 测试获取objectType bug objectId 8的动态信息
r($action->getObjectLabelTest($objectType[8], $objectId[8], $actionType[8], $requirements)) && p() && e('testcase'); // 测试获取objectType testcase objectId 9的动态信息
r($action->getObjectLabelTest($objectType[9], $objectId[9], $actionType[9], $requirements)) && p() && e('用例|testcase|view|caseID=%s'); // 测试获取objectType case objectId 10的动态信息
r($action->getObjectLabelTest($objectType[10], $objectId[10], $actionType[10], $requirements)) && p() && e('测试单|testtask|view|caseID=%s'); // 测试获取objectType testtask objectId 11的动态信息
r($action->getObjectLabelTest($objectType[11], $objectId[11], $actionType[11], $requirements)) && p() && e('用户|user|view|account=%s'); // 测试获取objectType user objectId 12的动态信息
r($action->getObjectLabelTest($objectType[13], $objectId[13], $actionType[13], $requirements)) && p() && e('文档库|doc|teamspace|objectID=%s&libID=%s'); // 测试获取objectType doclib objectId 14的动态信息
r($action->getObjectLabelTest($objectType[14], $objectId[14], $actionType[14], $requirements)) && p() && e('待办|todo|view|todoID=%s'); // 测试获取objectType todo objectId 15的动态信息
r($action->getObjectLabelTest($objectType[15], $objectId[15], $actionType[15], $requirements)) && p() && e('branch'); // 测试获取objectType branch objectId 16的动态信息
r($action->getObjectLabelTest($objectType[16], $objectId[16], $actionType[16], $requirements)) && p() && e('模块|tree|browse|productid=%s&view=story&currentModuleID=0&branch=all'); // 测试获取objectType module objectId 17的动态信息
r($action->getObjectLabelTest($objectType[17], $objectId[17], $actionType[17], $requirements)) && p() && e('测试套件|testsuite|view|suiteID=%s'); // 测试获取objectType testsuite objectId 18的动态信息
r($action->getObjectLabelTest($objectType[18], $objectId[18], $actionType[18], $requirements)) && p() && e('用例库|caselib|view|libID=%s'); // 测试获取objectType caselib objectId 19的动态信息
r($action->getObjectLabelTest($objectType[19], $objectId[19], $actionType[19], $requirements)) && p() && e('报告|testreport|view|report=%s'); // 测试获取objectType testreport objectId 20的动态信息
r($action->getObjectLabelTest($objectType[20], $objectId[20], $actionType[20], $requirements)) && p() && e('应用|entry|browse|'); // 测试获取objectType entry objectId 21的动态信息
r($action->getObjectLabelTest($objectType[21], $objectId[21], $actionType[21], $requirements)) && p() && e('Webhook|webhook|browse|'); // 测试获取objectType webhook objectId 22的动态信息
r($action->getObjectLabelTest($objectType[22], $objectId[22], $actionType[22], $requirements)) && p() && e('review'); // 测试获取objectType review objectId 23的动态信息
r($action->getObjectLabelTest($objectType[1], $objectId[23], $actionType[23], $requirements)) && p() && e('用户需求|story|view|storyID=%s'); // 测试获取objectType story objectId 25的动态信息
r($action->getObjectLabelTest($objectType[0], $objectId[0], $actionType[0], $requirements, $epics)) && p() && e('产品|product|view|productID=%s'); // 测试获取objectType product objectId 1的动态信息
r($action->getObjectLabelTest($objectType[1], $objectId[1], $actionType[1], $requirements, $epics)) && p() && e('研发需求|story|storyView|storyID=%s'); // 测试获取objectType story objectId 2的动态信息
r($action->getObjectLabelTest($objectType[2], $objectId[2], $actionType[2], $requirements, $epics)) && p() && e('计划|productplan|view|productID=%s'); // 测试获取objectType productplan objectId 3的动态信息
r($action->getObjectLabelTest($objectType[3], $objectId[3], $actionType[3], $requirements, $epics)) && p() && e('发布|release|view|productID=%s'); // 测试获取objectType release objectId 4的动态信息
r($action->getObjectLabelTest($objectType[4], $objectId[4], $actionType[4], $requirements, $epics)) && p() && e('项目|project|index|projectID=%s'); // 测试获取objectType project objectId 5的动态信息
r($action->getObjectLabelTest($objectType[5], $objectId[5], $actionType[5], $requirements, $epics)) && p() && e('任务|task|view|taskID=%s'); // 测试获取objectType task objectId 6的动态信息
r($action->getObjectLabelTest($objectType[6], $objectId[6], $actionType[6], $requirements, $epics)) && p() && e('版本|build|view|buildID=%s'); // 测试获取objectType build objectId 7的动态信息
r($action->getObjectLabelTest($objectType[7], $objectId[7], $actionType[7], $requirements, $epics)) && p() && e('Bug|bug|view|bugID=%s'); // 测试获取objectType bug objectId 8的动态信息
r($action->getObjectLabelTest($objectType[8], $objectId[8], $actionType[8], $requirements, $epics)) && p() && e('testcase'); // 测试获取objectType testcase objectId 9的动态信息
r($action->getObjectLabelTest($objectType[9], $objectId[9], $actionType[9], $requirements, $epics)) && p() && e('用例|testcase|view|caseID=%s'); // 测试获取objectType case objectId 10的动态信息
r($action->getObjectLabelTest($objectType[10], $objectId[10], $actionType[10], $requirements, $epics)) && p() && e('测试单|testtask|view|caseID=%s'); // 测试获取objectType testtask objectId 11的动态信息
r($action->getObjectLabelTest($objectType[11], $objectId[11], $actionType[11], $requirements, $epics)) && p() && e('用户|user|view|account=%s'); // 测试获取objectType user objectId 12的动态信息
r($action->getObjectLabelTest($objectType[13], $objectId[13], $actionType[13], $requirements, $epics)) && p() && e('文档库|doc|teamspace|objectID=%s&libID=%s'); // 测试获取objectType doclib objectId 14的动态信息
r($action->getObjectLabelTest($objectType[14], $objectId[14], $actionType[14], $requirements, $epics)) && p() && e('待办|todo|view|todoID=%s'); // 测试获取objectType todo objectId 15的动态信息
r($action->getObjectLabelTest($objectType[15], $objectId[15], $actionType[15], $requirements, $epics)) && p() && e('branch'); // 测试获取objectType branch objectId 16的动态信息
r($action->getObjectLabelTest($objectType[16], $objectId[16], $actionType[16], $requirements, $epics)) && p() && e('模块|tree|browse|productid=%s&view=story&currentModuleID=0&branch=all'); // 测试获取objectType module objectId 17的动态信息
r($action->getObjectLabelTest($objectType[17], $objectId[17], $actionType[17], $requirements, $epics)) && p() && e('测试套件|testsuite|view|suiteID=%s'); // 测试获取objectType testsuite objectId 18的动态信息
r($action->getObjectLabelTest($objectType[18], $objectId[18], $actionType[18], $requirements, $epics)) && p() && e('用例库|caselib|view|libID=%s'); // 测试获取objectType caselib objectId 19的动态信息
r($action->getObjectLabelTest($objectType[19], $objectId[19], $actionType[19], $requirements, $epics)) && p() && e('报告|testreport|view|report=%s'); // 测试获取objectType testreport objectId 20的动态信息
r($action->getObjectLabelTest($objectType[20], $objectId[20], $actionType[20], $requirements, $epics)) && p() && e('应用|entry|browse|'); // 测试获取objectType entry objectId 21的动态信息
r($action->getObjectLabelTest($objectType[21], $objectId[21], $actionType[21], $requirements, $epics)) && p() && e('Webhook|webhook|browse|'); // 测试获取objectType webhook objectId 22的动态信息
r($action->getObjectLabelTest($objectType[22], $objectId[22], $actionType[22], $requirements, $epics)) && p() && e('评审|review|view|reviewID=%s'); // 测试获取objectType review objectId 23的动态信息
r($action->getObjectLabelTest($objectType[1], $objectId[23], $actionType[23], $requirements, $epics)) && p() && e('用户需求|story|storyView|storyID=%s'); // 测试获取objectType story objectId 25的动态信息
r($action->getObjectLabelTest($objectType[1], $objectId[24], $actionType[23], $requirements, $epics)) && p() && e('业务需求|story|storyView|storyID=%s'); // 测试获取objectType story objectId 26的动态信息
+1 -3
View File
@@ -72,7 +72,6 @@ $doclib = $tester->dao->select('*')->from('zt_doclib')->where('type')->eq('exec
r($result && strpos($userView->products . ',', ',1,') !== false && !$doclib->deleted) && p('') && e('1'); // 测试还原action 3, objectType execution 的数据,并且测试是否恢复了用户访问权限。
$result = $action->undeleteTest($actionIDList[4]);
unset(dao::$cache['zt_userview']);
$userView = $tester->dao->select('*')->from('zt_userview')->where('account')->eq('admin')->fetch();
r($result && strpos($userView->products . ',', ',1,') !== false && strpos($userView->sprints . ',', ',4,') !== false) && p('') && e('1'); // 测试还原action 3, objectType execution 的数据,并且测试是否恢复了用户访问权限。
@@ -83,7 +82,6 @@ r($action->undeleteTest($actionIDList[7])) && p('') && e('1');
$tester->dao->update('zt_userview')->set('sprints')->eq('')->set('programs')->eq('')->set('products')->eq('')->set('projects')->eq('')->where('account')->eq('admin')->exec();
$result = $action->undeleteTest($actionIDList[8]);
unset(dao::$cache['zt_userview']);
$userView = $tester->dao->select('*')->from('zt_userview')->where('account')->eq('admin')->fetch();
r($result && strpos($userView->products . ',', ',1,') !== false) && p('') && e('0'); // 测试还原action 8, objectType program 的数据,并且测试是否恢复了用户访问权限。
@@ -136,4 +134,4 @@ r($result && $parentPlan->parent == '0') && p('') && e('1');
$result = $action->undeleteTest($actionIDList[25]);
$task = $tester->dao->select('*')->from('zt_task')->where('id')->eq('1')->fetch();
r($result && $task->parent == '-1') && p('') && e('1'); // 测试还原action 25, objectType task 的数据。
r($result && $task->parent == '-1') && p('') && e('1'); // 测试还原action 25, objectType task 的数据。
+2 -1
View File
@@ -2258,8 +2258,9 @@ class aiModel extends model
}
$linkVars = vsprintf($varsConfig->format, $vars);
/* Override method for story drafts. */
/* Overrides for stories. */
if($module == 'story' && $method == 'change' && !empty($object->story) && $object->story->status == 'draft') $method = 'edit';
if($module == 'story' && $method == 'change' && !empty($object->story) && $object->story->type == 'epic') $module = 'epic';
return array(helper::createLink($module, $method, $linkVars) . (empty($varsConfig->app) ? '' : "#app=$varsConfig->app"), false);
}
-10
View File
@@ -268,16 +268,6 @@ class backup extends control
$this->backup->removeFileHeader($this->backupPath . $fileName . '.sql.php');
rename($this->backupPath . $fileName . '.sql.php', $this->backupPath . $fileName . '.sql');
}
if(file_exists($this->backupPath . $fileName . '.file.zip.php'))
{
$this->backup->removeFileHeader($this->backupPath . $fileName . '.file.zip.php');
rename($this->backupPath . $fileName . '.file.zip.php', $this->backupPath . $fileName . '.file.zip');
}
if(file_exists($this->backupPath . $fileName . '.code.zip.php'))
{
$this->backup->removeFileHeader($this->backupPath . $fileName . '.code.zip.php');
rename($this->backupPath . $fileName . '.code.zip.php', $this->backupPath . $fileName . '.code.zip');
}
return $this->send(array('result' => 'success', 'load' => true));
}
+3 -20
View File
@@ -162,20 +162,7 @@ class backupModel extends model
$return->result = true;
$return->error = '';
if(is_file($backupFile))
{
$oldDir = getcwd();
chdir($this->app->getTmpRoot());
$this->app->loadClass('pclzip', true);
$zip = new pclzip($backupFile);
if($zip->extract(PCLZIP_OPT_PATH, $this->app->getAppRoot() . 'www/data/', PCLZIP_OPT_TEMP_FILE_ON) == 0)
{
$return->result = false;
$return->error = $zip->errorInfo();
}
chdir($oldDir);
}
elseif(is_dir($backupFile))
if(is_dir($backupFile))
{
$zfile = $this->app->loadClass('zfile');
$zfile->copyDir($backupFile, $this->app->getAppRoot() . 'www/data/', $showDetails = false);
@@ -297,12 +284,8 @@ class backupModel extends model
if(file_exists($backupPath . $name . ".{$type}")) return $backupPath . $name . ".{$type}";
if(file_exists($backupPath . $name . ".{$type}.php")) return $backupPath . $name . ".{$type}.php";
}
else
{
if(file_exists($backupPath . $name . ".{$type}")) return $backupPath . $name . ".{$type}";
if(file_exists($backupPath . $name . ".{$type}.zip")) return $backupPath . $name . ".{$type}.zip";
if(file_exists($backupPath . $name . ".{$type}.zip.php")) return $backupPath . $name . ".{$type}.zip.php";
}
if(file_exists($backupPath . $name . ".{$type}")) return $backupPath . $name . ".{$type}";
return false;
}
+17 -4
View File
@@ -11,9 +11,20 @@ $config->bi->default->status = json_decode('{"lock":false,"hide":false}');
$config->bi->default->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestInterval":null,"requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
$config->bi->default->events = json_decode('{"baseEvent":{"click":null,"dblclick":null,"mouseenter":null,"mouseleave":null},"advancedEvents":{"vnodeMounted":null,"vnodeBeforeMount":null}}');
$config->bi->cdnUrl = 'https://dl.zentao.net/duckdb/';
$config->bi->duckdbHelp = 'https://www.zentao.net/book/zentaopms/1313.html';
$config->bi->duckdbBin = array();
$config->bi->duckdbBin['win'] = array('path' => '/bin/duckdb/', 'file' => 'duckdb.exe', 'extension' => 'mysql_scanner.duckdb_extension');
$config->bi->duckdbBin['linux'] = array('path' => '/opt/zbox/bin/', 'file' => 'duckdb', 'extension' => 'mysql_scanner.duckdb_extension');
$config->bi->duckdbBin['win'] = array('path' => '/bin/duckdb/', 'file' => 'duckdb.exe', 'fileUrl' => $config->bi->cdnUrl . 'win/duckdb.zip');
$config->bi->duckdbBin['linux'] = array('path' => '/opt/zbox/bin/', 'file' => 'duckdb', 'fileUrl' => $config->bi->cdnUrl . 'linux/amd64/duckdb.zip');
$config->bi->duckdbExt = array();
$config->bi->duckdbExt['win'] = array('dm' => 'sync2parquet.exe', 'mysql' => 'mysql_scanner.duckdb_extension');
$config->bi->duckdbExt['linux'] = array('dm' => 'sync2parquet', 'mysql' => 'mysql_scanner.duckdb_extension');
$config->bi->duckdbExtUrl = array();
$config->bi->duckdbExtUrl['win'] = array('dm' => $config->bi->cdnUrl . 'win/sync2parquet.exe', 'mysql' => $config->bi->cdnUrl . 'win/mysql_scanner.duckdb_extension.zip');
$config->bi->duckdbExtUrl['linux'] = array('dm' => $config->bi->cdnUrl . 'linux/amd64/sync2parquet', 'mysql' => $config->bi->cdnUrl . 'linux/amd64/mysql_scanner.duckdb_extension.zip');
$charts = array();
$charts['32'] = array("root" => 1, "name" => "产品", "grade" => 1);
@@ -91,7 +102,9 @@ $pivots['100'] = array("root" => 3, "name" => "Bug", "grade" => 2);
$config->bi->builtin->modules->charts = $charts;
$config->bi->builtin->modules->pivots = $pivots;
$config->bi->duckSQLTemp = "LOAD '{EXTENSIONPATH}';ATTACH 'host={HOST} user={USER} password={PASSWORD} port={PORT} database={DATABASE}' as mysqldb(TYPE MYSQL);USE mysqldb;{COPYSQL}";
$config->bi->duckSQLTemp = array();
$config->bi->duckSQLTemp['mysql'] = "LOAD '{EXTENSIONPATH}';ATTACH 'host={HOST} user={USER} password={PASSWORD} port={PORT} database={DATABASE}' as mysqldb(TYPE MYSQL);USE mysqldb;{COPYSQL}";
$config->bi->duckSQLTemp['dm'] = '{EXTENSIONPATH} --driver="{DRIVER}" --db="host={HOST} user={USER} password={PASSWORD} port={PORT} database={DATABASE}" --copy="{COPYSQL}"';
$config->bi->drivers = array('mysql', 'duckdb');
@@ -186,7 +199,7 @@ $config->bi->columnTypes->DATE = 'date';
$config->bi->duckdb = new stdclass();
$config->bi->duckdb->tables = array();
$config->bi->duckdb->tables['action'] = <<<EOT
SELECT id,objectType,objectID,product,project,execution,actor,action,date,read,vision,efforted FROM zt_action
SELECT id,objectType,objectID,product,project,execution,actor,action,date,vision,efforted FROM zt_action
EOT;
$config->bi->duckdb->tables['account'] = <<<EOT
SELECT * FROM zt_account
File diff suppressed because it is too large Load Diff
+8 -16
View File
@@ -380,7 +380,6 @@ EOT,
'settings' => array
(
'group1' => 'name',
'group2' => '',
'columnTotal' => 'sum',
'columns' => array
(
@@ -479,7 +478,6 @@ EOT,
'settings' => array
(
'group1' => 'name',
'group2' => '',
'columnTotal' => 'sum',
'columns' => array
(
@@ -570,7 +568,6 @@ EOT,
'settings' => array
(
'group1' => 'name',
'group2' => '',
'columnTotal' => 'sum',
'columns' => array
(
@@ -661,7 +658,6 @@ EOT,
'settings' => array
(
'group1' => 'name',
'group2' => '',
'columnTotal' => 'sum',
'columns' => array
(
@@ -700,7 +696,7 @@ select
(case when t3.multiple='1' then t1.name else '' end) as execution,
t2.id as taskID,
t1.status as projectstatus,
(case when t2.deadline < current_date()
(case when cast(t2.deadline as date) < current_date()
and t2.deadline is not null
and t2.status != 'closed'
and t2.status != 'done'
@@ -1657,7 +1653,6 @@ EOT,
'settings' => array
(
'group1' => 'name',
'group2' => '',
'columnTotal' => 'sum',
'columns' => array
(
@@ -1713,7 +1708,6 @@ EOT,
'settings' => array
(
'group1' => 'name',
'group2' => '',
'columnTotal' => 'sum',
'columns' => array
(
@@ -1765,8 +1759,8 @@ select
cast(date as date) as day
from zt_action
where "action"='login'
and (case when \$startDate='' then 1 else date>=cast(\$startDate as date) end)
and (case when \$endDate='' then 1 else date<=cast(\$endDate as date) end)
and (case when \$startDate='' then 1 else cast(date as date)>=cast(\$startDate as date) end)
and (case when \$endDate='' then 1 else cast(date as date)<=cast(\$endDate as date) end)
order by date asc, actor asc
EOT,
'settings' => array
@@ -1827,8 +1821,8 @@ from zt_effort as t1
left join zt_user as t2 on t1.account = t2.account
left join zt_dept as t3 on t2.dept = t3.id
where t1.`deleted` = '0'
and (case when \$startDate='' then 1 else t1.`date` >= cast(\$startDate as date) end)
and (case when \$endDate='' then 1 else t1.`date` <= cast(\$endDate as date) end)
and (case when \$startDate='' then 1 else cast(t1.`date` as date) >= cast(\$startDate as date) end)
and (case when \$endDate='' then 1 else cast(t1.`date` as date) <= cast(\$endDate as date) end)
and (case when \$dept='' then 1
else t3.path like concat((select path from zt_dept where id=coalesce(cast(nullif(\$dept, '') as integer), 0)), '%')
end)
@@ -1837,7 +1831,6 @@ EOT,
'settings' => array
(
'group1' => 'account',
'group2' => '',
'columnTotal' => 'sum',
'columns' => array
(
@@ -1993,20 +1986,19 @@ $config->bi->builtin->pivots[] = array
'sql' => <<<EOT
select
t1.*,
(case when \$product='' then 0 else t1.product end) as customproduct
(case when \$product='' then 0 else cast(t1.product as integer) end) as customproduct
from zt_bug as t1
left join zt_product as t2 on t1.product = t2.id
where t1.deleted='0'
and t2.deleted='0'
and t1.resolution!=''
and (case when \$startDate='' then 1 else t1.resolvedDate>=cast(\$startDate as date) end)
and (case when \$endDate='' then 1 else t1.resolvedDate<=cast(\$endDate as date) end)
and (case when \$startDate='' then 1 else cast(t1.resolvedDate as date)>=cast(\$startDate as date) end)
and (case when \$endDate='' then 1 else cast(t1.resolvedDate as date)<=cast(\$endDate as date) end)
and (case when \$product = '' then 1 else customproduct=\$product end)
EOT,
'settings' => array
(
'group1' => 'resolvedBy',
'group2' => '',
'columnTotal' => 'sum',
'columns' => array
(
+35 -24
View File
@@ -22,35 +22,17 @@ class bi extends control
public function syncParquetFile()
{
$startTime = microtime(true);
$duckdb = $this->bi->getDuckDBPath();
if(!$duckdb)
{
echo("DuckDB bin path not exists.");
return;
}
$duckdbTmpPath = $this->bi->getDuckDBTmpDir();
if(!$duckdbTmpPath)
{
echo("Create DuckDB tmp dir permission denied.");
return;
}
$copySQL = $this->bi->prepareCopySQL($duckdbTmpPath);
$command = $this->bi->prepareSyncCommand($duckdb->bin, $duckdb->extension, $copySQL);
$output = shell_exec($command);
$endTime = microtime(true);
$runTime = $endTime - $startTime;
$result = $this->bi->generateParquetFile();
$endTime = microtime(true);
$runTime = $endTime - $startTime;
echo "$runTime \n";
if(empty($output))
if($result !== true)
{
echo('success');
echo $result;
return;
}
echo($output);
echo 'success';
}
/**
@@ -68,4 +50,33 @@ class bi extends control
return print(json_encode($items));
}
/**
* 安装DuckDB引擎。
* AJAX: Install duckdb.
*
* @access public
* @return void
*/
public function ajaxInstallDuckdb()
{
ignore_user_abort(true);
set_time_limit(0);
session_write_close();
$this->bi->downloadDuckdb();
echo 'success';
}
/**
* 检查duckdb文件是否下载完成。
* AJAX: Check duckdb.
*
* @access public
* @return void
*/
public function ajaxCheckDuckdb()
{
$check = $this->bi->checkDuckdbInstall();
echo(json_encode($check));
}
}
+3
View File
@@ -0,0 +1,3 @@
<?php
$lang->bi->binNotExists = 'The DuckDB binary does not exist.';
$lang->bi->tmpPermissionDenied = 'The DuckDB tmp directory has no permissions, you need to change the permissions for the directory "%s". The <br /> command is: <br />chmod 777 -R %s.';
+3
View File
@@ -0,0 +1,3 @@
<?php
$lang->bi->binNotExists = 'The DuckDB binary does not exist.';
$lang->bi->tmpPermissionDenied = 'The DuckDB tmp directory has no permissions, you need to change the permissions for the directory "%s". The <br /> command is: <br />chmod 777 -R %s.';
+3
View File
@@ -0,0 +1,3 @@
<?php
$lang->bi->binNotExists = 'The DuckDB binary does not exist.';
$lang->bi->tmpPermissionDenied = 'The DuckDB tmp directory has no permissions, you need to change the permissions for the directory "%s". The <br /> command is: <br />chmod 777 -R %s.';
+3
View File
@@ -0,0 +1,3 @@
<?php
$lang->bi->binNotExists = 'DuckDB 二进制文件不存在。';
$lang->bi->tmpPermissionDenied = 'DuckDB tmp 目录没有权限, 需要修改目录 "%s" 的权限。<br />命令为:<br />chmod 755 -R %s。';
+3
View File
@@ -0,0 +1,3 @@
<?php
$lang->bi->binNotExists = 'DuckDB 二进制文件不存在。';
$lang->bi->tmpPermissionDenied = 'DuckDB tmp 目录没有权限, 需要修改目录 "%s" 的权限。<br />命令为:<br />chmod 755 -R %s。';
+302 -71
View File
@@ -584,6 +584,7 @@ class biModel extends model
if(isset($chart->filters)) $chart->filters = $this->jsonEncode($chart->filters);
if(isset($chart->fields)) $chart->fields = $this->jsonEncode($chart->fields);
if(isset($chart->langs)) $chart->langs = $this->jsonEncode($chart->langs);
if(!isset($chart->driver)) $chart->driver = 'mysql';
$exists = $this->dao->select('id')->from(TABLE_CHART)->where('id')->eq($chart->id)->fetch();
if(!$exists) $currentOperate = 'insert';
@@ -767,7 +768,7 @@ class biModel extends model
public function getDuckDBPath()
{
$duckdbBin = $this->getDuckdbBinConfig();
$sourcePath = $this->app->getBasePath() . 'bin' . DS . 'duckdb' . DS;
$sourcePath = $this->app->getTmpRoot() . 'duckdb' . DS;
$checkSourceCode = $this->checkDuckDBFile($sourcePath, $duckdbBin);
@@ -799,6 +800,7 @@ class biModel extends model
* 获取ducbDB的bin目录配置。
* Get duckdb bin config.
*
* @param string $driver
* @access public
* @return array
*/
@@ -806,6 +808,18 @@ class biModel extends model
{
$os = PHP_OS == 'WINNT' ? 'win' : 'linux';
$duckdbBin = $this->config->bi->duckdbBin[$os];
$driver = $this->config->db->driver;
/* 如果不是mysql数据库,那么统一使用达梦的扩展配置。*/
/* If it is not a mysql database, then use the same extension configuration of Dameng. */
if($driver !== 'mysql') $driver = 'dm';
$duckdbBin['extension'] = $this->config->bi->duckdbExt[$os][$driver];
$duckdbBin['extension_dm'] = $this->config->bi->duckdbExt[$os]['dm'];
$duckdbBin['extension_mysql'] = $this->config->bi->duckdbExt[$os]['mysql'];
$duckdbBin['extensionUrl_dm'] = $this->config->bi->duckdbExtUrl[$os]['dm'];
$duckdbBin['extensionUrl_mysql'] = $this->config->bi->duckdbExtUrl[$os]['mysql'];
if($os == 'win') $duckdbBin['path'] = dirname(dirname($this->app->getBasePath())) . $duckdbBin['path'];
@@ -819,9 +833,10 @@ class biModel extends model
* @access public
* @return string|false
*/
public function getDuckDBTmpDir()
public function getDuckDBTmpDir($static = false)
{
$duckdbTmpPath = $this->app->getTmpRoot() . 'duckdb' . DS . 'bi' . DS;
if($static) return $duckdbTmpPath;
if(!is_dir($duckdbTmpPath) && !mkdir($duckdbTmpPath, 0755, true)) return false;
return $duckdbTmpPath;
@@ -877,10 +892,12 @@ class biModel extends model
*/
public function prepareSyncCommand($binPath, $extensionPath, $copySQL)
{
$sqlContent = $this->config->bi->duckSQLTemp;
$dbConfig = $this->config->db;
$driver = $dbConfig->driver;
$sqlContent = $this->config->bi->duckSQLTemp[$driver];
$variables = array(
'{EXTENSIONPATH}' => $extensionPath,
'{DRIVER}' => $driver,
'{DATABASE}' => $dbConfig->name,
'{USER}' => $dbConfig->user,
'{PASSWORD}' => $dbConfig->password,
@@ -894,7 +911,30 @@ class biModel extends model
$sqlContent = str_replace($key, $value, $sqlContent);
}
return "$binPath :memory: \"$sqlContent\" 2>&1";
if($driver == 'mysql') return "$binPath :memory: \"$sqlContent\" 2>&1";
return "$sqlContent 2>&1";
}
/**
* Generate parquet file.
*
* @access public
* @return string|true
*/
public function generateParquetFile()
{
$duckdb = $this->getDuckDBPath();
if(!$duckdb) return $this->lang->bi->binNotExists;
$duckdbTmpPath = $this->getDuckDBTmpDir();
if(!$duckdbTmpPath) return sprintf($this->lang->bi->tmpPermissionDenied, $this->getDuckDBTmpDir(true), $this->getDuckDBTmpDir(true));
$copySQL = $this->prepareCopySQL($duckdbTmpPath);
$command = $this->prepareSyncCommand($duckdb->bin, $duckdb->extension, $copySQL);
$output = shell_exec($command);
if(!empty($output)) return $output;
return true;
}
/**
@@ -905,7 +945,7 @@ class biModel extends model
* @access public
* @return string
*/
public function processVars($sql, $filters = array())
public function processVars($sql, $filters = array(), $emptyValue = false)
{
foreach($filters as $index => $filter)
{
@@ -913,6 +953,9 @@ class biModel extends model
if(!isset($filter['from']) || $filter['from'] != 'query') continue;
$filters[$index]['default'] = $this->loadModel('pivot')->processDateVar($filter['default']);
if($filters[$index]['type'] == 'datetime') $filters[$index]['default'] .= ':00.000000000';
if($emptyValue) $filters[$index]['default'] = '';
}
$sql = $this->loadModel('chart')->parseSqlVars($sql, $filters);
$sql = trim($sql, ';');
@@ -1016,14 +1059,51 @@ class biModel extends model
*/
public function prepareColumns($sql, $statement, $driver)
{
$this->loadModel('chart');
$this->loadModel('dataview');
list($columnTypes, $columnFields) = $this->getSqlTypeAndFields($sql, $driver);
list($moduleNames, $aliasNames, $fieldPairs, $relatedObjects) = $this->getParams4Rebuild($sql, $statement, $columnFields);
$columns = array();
$clientLang = $this->app->getClientLang();
foreach($fieldPairs as $field => $langName)
{
$columns[$field] = array('name' => $field, 'field' => $field, 'type' => $columnTypes->$field, 'object' => $relatedObjects[$field], $clientLang => $langName);
}
$objectFields = $this->loadModel('dataview')->getObjectFields();
$columns = $this->rebuildFieldSettings($fieldPairs, $columnTypes, $relatedObjects, $columns, $objectFields);
return array($columns, $relatedObjects);
}
/**
* Get sql columnTypes and columnFields.
*
* @param string $sql
* @param string $driver
* @access public
* @return array
*/
public function getSqlTypeAndFields($sql, $driver)
{
$sqlColumns = $this->getColumns($sql, $driver);
$columnTypes = $this->getColumnsType($sql, $driver, $sqlColumns);
$columnFields = array();
foreach($columnTypes as $column => $type) $columnFields[$column] = $column;
return array($columnTypes, $columnFields);
}
/**
* Get params for rebuild fieldSetting.
*
* @param string $sql
* @param object $statement
* @param array $columnFields
* @access public
* @return array
*/
public function getParams4Rebuild($sql, $statement, $columnFields)
{
$tableAndFields = $this->getTableAndFields($sql);
$tables = $tableAndFields['tables'];
$fields = $tableAndFields['fields'];
@@ -1032,20 +1112,13 @@ class biModel extends model
$aliasNames = array();
if($tables)
{
$this->loadModel('dataview');
$moduleNames = $this->dataview->getModuleNames($tables);
$aliasNames = $this->dataview->getAliasNames($statement, $moduleNames);
}
list($fieldPairs, $relatedObjects) = $this->dataview->mergeFields($columnFields, $fields, $moduleNames, $aliasNames);
list($fields, $relatedObjects) = $this->dataview->mergeFields($columnFields, $fields, $moduleNames, $aliasNames);
$columns = array();
$clientLang = $this->app->getClientLang();
foreach($fields as $field => $langName)
{
$columns[$field] = array('name' => $field, 'field' => $field, 'type' => $columnTypes->$field, 'object' => $relatedObjects[$field], $clientLang => $langName);
}
return array($columns, $relatedObjects);
return array($moduleNames, $aliasNames, $fieldPairs, $relatedObjects);
}
/**
@@ -1060,7 +1133,7 @@ class biModel extends model
public function query($stateObj, $driver = 'mysql')
{
$dbh = $this->app->loadDriver($driver);
$sql = $this->processVars($stateObj->sql, $stateObj->getFilters());
$sql = $this->processVars($stateObj->sql, $stateObj->getFilters(), true);
$stateObj->beforeQuerySql();
@@ -1070,6 +1143,9 @@ class biModel extends model
$checked = $this->validateSql($sql, $driver);
if($checked !== true) return $stateObj->setError($checked);
$sql = $this->processVars($stateObj->sql, $stateObj->getFilters());
$statement = $this->sql2Statement($sql);
$recPerPage = $stateObj->pager->recPerPage;
$pageID = $stateObj->pager->pageID;
$limitSql = $this->prepareSqlPager($statement, $recPerPage, $pageID, $driver);
@@ -1159,6 +1235,71 @@ class biModel extends model
return $formData;
}
/**
* 重建透视表filedSettings字段
* Rebuild fieldSettings field of pivot.
*
* @param object $pivot
* @param array $fieldPairs
* @param object $columns
* @param array $relatedObject
* @param object $fieldSettings
* @param array $objectFields
* @access private
* @return object
*/
public function rebuildFieldSettings(array $fieldPairs, object $columns, array $relatedObject, object|array $fieldSettings, array $objectFields): object|array
{
$isArray = is_array($fieldSettings);
if($isArray) $fieldSettings = json_decode(json_encode($fieldSettings));
$fieldSettingsNew = new stdclass();
foreach($fieldPairs as $index => $field)
{
$defaultType = $columns->{$index};
$defaultObject = $relatedObject[$index];
if(isset($objectFields[$defaultObject][$index])) $defaultType = $objectFields[$defaultObject][$index]['type'] == 'object' ? 'string' : $objectFields[$defaultObject][$index]['type'];
if(!isset($fieldSettings->{$index}))
{
/* 如果字段设置中没有该字段,则使用默认的配置。 */
/* If the field is not set in the field settings, use the default value. */
$fieldItem = new stdclass();
$fieldItem->name = $field;
$fieldItem->object = $defaultObject;
$fieldItem->field = $index;
$fieldItem->type = $defaultType;
$fieldSettingsNew->{$index} = $fieldItem;
}
else
{
/* 兼容旧版本的字段设置,当为空或者为布尔值时,使用默认值 */
/* Compatible with old version of field settings, use default value when empty or boolean. */
if(!isset($fieldSettings->{$index}->object) || is_bool($fieldSettings->{$index}->object) || strlen($fieldSettings->{$index}->object) == 0) $fieldSettings->{$index}->object = $defaultObject;
/* 当字段设置中没有字段名时,使用默认的字段名配置。 */
/* When there is no field name in the field settings, use the default field name configuration. */
if(!isset($fieldSettings->{$index}->field) || strlen($fieldSettings->{$index}->field) == 0)
{
$fieldSettings->{$index}->field = $index;
$fieldSettings->{$index}->object = $defaultObject;
$fieldSettings->{$index}->type = 'string';
}
$object = $fieldSettings->{$index}->object;
$type = $fieldSettings->{$index}->type;
if($object == $defaultObject && $type != $defaultType) $fieldSettings->{$index}->type = $defaultType;
$fieldSettingsNew->{$index} = $fieldSettings->{$index};
}
}
if($isArray) $fieldSettingsNew = json_decode(json_encode($fieldSettingsNew), true);
return $fieldSettingsNew;
}
/**
* 把自定义透视表的数据转换为数据表格可以使用的格式。
* Convert the data of custom pivot to the format that can be used by data table.
@@ -1183,9 +1324,9 @@ class biModel extends model
$index = 0;
foreach($headerRow1 as $column)
{
/* 如果 colspan 属性不为空则且存在第二行表头表示该列包含切片字段。*/
/* 如果 colspan 属性不为空则且isSlice标记列为true且存在第二行表头表示该列包含切片字段。*/
/* If the colspan attribute is not empty, it means that the column contains slice fields. */
if(!empty($column->colspan) && $column->colspan > 1 && !empty($headerRow2))
if(!empty($column->colspan) && $column->isSlice && !empty($headerRow2))
{
/* 找到实际切片的字段。*/
/* Find the actual sliced field. */
@@ -1245,7 +1386,7 @@ class biModel extends model
$columnMaxLen[$field] = mb_strlen($column->label);
if(isset($column->colspan) && $column->colspan > 1) $columns[$field]['colspan'] = $column->colspan;
if(isset($column->colspan) && $column->isSlice) $columns[$field]['colspan'] = $column->colspan;
// if(isset($data->groups[$index])) $columns[$field]['fixed'] = 'left';
@@ -1365,79 +1506,169 @@ class biModel extends model
}
/**
* Query sql.
* 下载duckdb引擎。
* Download duckdb.
*
* @param string $sql
* @param int $recPerPage
* @param int $pageID
* @access public
* @return object
* @return string
*/
/*
public function querySql($sql, $recPerPage = 100, $pageID = 1)
public function downloadDuckdb(): string
{
$queryResult = new stdclass();
$queryResult->error = false;
$queryResult->errorMsg = '';
$queryResult->cols = array();
$queryResult->rows = array();
$queryResult->sql = '';
$queryResult->fieldSettings = array();
$check = $this->checkDuckdbInstall();
$sql = $this->processVars($sql);
if($check['loading']) return 'loading';
$statement = $this->sql2Statement($sql);
if(is_string($statement))
{
$queryResult->error = true;
$queryResult->errorMsg = $statement;
$this->loadModel('bi');
$binRoot = $this->app->getTmpRoot() . 'duckdb' . DS;
$duckdbBin = $this->getDuckdbBinConfig();
return $queryResult;
}
if(!is_dir($binRoot)) mkdir($binRoot, 0755, true);
$checked = $this->validateSql($sql);
if($checked !== true)
{
$queryResult->error = true;
$queryResult->errorMsg = $checked;
$this->updateDownloadingTagFile('file', 'create');
$this->updateDownloadingTagFile('extension_dm', 'create');
$this->updateDownloadingTagFile('extension_mysql', 'create');
return $queryResult;
}
$downloadDuckdb = $this->downloadFile($duckdbBin['fileUrl'], $binRoot, $duckdbBin['file']);
$downloadExtDM = $this->downloadFile($duckdbBin['extensionUrl_dm'], $binRoot, $duckdbBin['extension_dm']);
$downloadExtMysql = $this->downloadFile($duckdbBin['extensionUrl_mysql'], $binRoot, $duckdbBin['extension_mysql']);
$limitSql = $this->prepareSqlPager($statement, $recPerPage, $pageID);
$this->updateDownloadingTagFile('file', 'remove');
$this->updateDownloadingTagFile('extension_dm', 'remove');
$this->updateDownloadingTagFile('extension_mysql', 'remove');
try
return $downloadDuckdb && $downloadExtDM && $downloadExtMysql ? 'ok' : 'fail';
}
/**
* 检查 DuckDB 安装状态。
* Check duckdb install status.
*
* @access public
* @return array
*/
public function checkDuckdbInstall()
{
$checkDuckdb = $this->updateDownloadingTagFile('file', 'check');
$checkExtDM = $this->updateDownloadingTagFile('extension_dm', 'check');
$checkExtMysql = $this->updateDownloadingTagFile('extension_mysql', 'check');
$loading = $checkDuckdb == 'loading' || $checkExtDM == 'loading' || $checkExtMysql == 'loading';
$ok = $checkDuckdb == 'ok' && $checkExtDM == 'ok' && $checkExtMysql == 'ok';
$fail = $checkDuckdb == 'fail' || $checkExtDM == 'fail' || $checkExtMysql == 'fail';
return array('loading' => $loading, 'ok' => $ok, 'fail' => $fail, 'duckdb' => $checkDuckdb, 'ext_dm' => $checkExtDM, 'ext_mysql' => $checkExtMysql);
}
/**
* 更新tab文件下载状态。
* Update downloading tab file status.
*
* @param string $type
* @param string $action
* @access public
* @return string
*/
public function updateDownloadingTagFile(string $type = 'file', string $action = 'create'): string
{
$downloading = '.downloading';
$binRoot = $this->app->getTmpRoot() . 'duckdb' . DS;
$duckdbBin = $this->getDuckdbBinConfig();
$file = $binRoot . $duckdbBin[$type];
$tagFile = $file . $downloading;
if($action == 'create')
{
$queryResult->rows = $this->dbh->query($limitSql)->fetchAll();
$queryResult->rowsCount = $this->dbh->query("SELECT FOUND_ROWS() as count")->fetch()->count;
if(file_exists($tagFile)) return 'fail';
file_put_contents($tagFile, 'Downloading...');
return 'ok';
}
catch(Exception $e)
if($action == 'check')
{
if(file_exists($file)) return 'ok';
if(file_exists($tagFile)) return 'loading';
return 'fail';
}
if($action == 'remove')
{
$queryResult->error = true;
$queryResult->errorMsg = $e;
if(!file_exists($tagFile)) return 'fail';
unlink($tagFile);
}
return 'ok';
}
/**
* 解压文件。
* Unzip file.
*
* @param string $path
* @param string $file
* @param string $extractFile
* @access public
* @return bool
*/
public function unzipFile(string $path, string $file, string $extractFile): bool
{
$this->app->loadClass('pclzip', true);
$zip = new pclzip($file);
return $queryResult;
/* 限制解压的文件内容以阻止 ZIP 解压缩的目录穿越漏洞。*/
/* Limit the file content to prevent the directory traversal vulnerability of ZIP decompression. */
$extractFiles = array($extractFile);
return $zip->extract(PCLZIP_OPT_PATH, $path, PCLZIP_OPT_BY_NAME, $extractFiles) === 0;
}
/**
* 下载文件。
* Download file.
*
* @param string $url
* @param string $savePath
* @param string $finalFile
* @access public
* @return bool
*/
public function downloadFile(string $url, string $savePath, string $finalFile): bool
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$fileContents = curl_exec($ch);
if (curl_errno($ch))
{
curl_close($ch);
return false;
}
$columns = $this->prepareColumns($limitSql, $statement);
$clientLang = $this->app->getClientLang();
$result = json_decode($fileContents, true);
if(isset($result['error']))
{
curl_close($ch);
return false;
}
$fieldSettings = array();
foreach($columns as $field => $settings)
$filename = basename($url);
$filename = $savePath . $filename;
$result = file_put_contents($filename, $fileContents);
if($result === false)
{
$title = $settings['name'];
curl_close($ch);
return false;
}
curl_close($ch);
chmod($filename, 0755);
if(!isset($settings[$clientLang]) || empty($settings[$clientLang])) $settings[$clientLang] = $title;
$fieldSettings[$field] = $settings;
if(pathinfo($filename, PATHINFO_EXTENSION) === 'zip')
{
$this->unzipFile($savePath, $filename, $finalFile);
unlink($filename);
}
$queryResult->cols = $this->buildQueryResultTableColumns($fieldSettings);
$queryResult->fieldSettings = $fieldSettings;
$queryResult->sql = $limitSql;
return $queryResult;
return chmod($savePath . $finalFile, 0755);
}
*/
/**
* Encode json.
+1 -1
View File
@@ -12,7 +12,7 @@ window.handleRenderRow = function($row, index, row)
let stepDesc = "<input class='hidden' type='text' name='lib[" + row.id + "]' value='" + libID + "'/>";
let stepExpect = '';
let currentKey = row.id != undefined ? row.id :index + 1;
let currentKey = row.id != undefined ? row.id : index + 1;
const descs = stepData[currentKey]['desc'];
const expect = stepData[currentKey]['expect'];
if(descs.length)
+40 -20
View File
@@ -68,34 +68,54 @@ class caselibTao extends caselibModel
$caseData->status = $forceNotReview ? 'normal' : 'wait';
$this->dao->insert(TABLE_CASE)->data($caseData)->autoCheck()->exec();
if(dao::isError()) return false;
$caseID = $this->dao->lastInsertID();
if(isset($data->desc[$key]))
$caseID = $this->dao->lastInsertID();
$preGrade = 0;
$parentStepID = $grandPaStepID = 0;
foreach($data->desc[$key] as $stepKey => $stepDesc)
{
$parentStepID = 0;
foreach($data->desc[$key] as $id => $desc)
/* 跳过步骤描述为空的步骤。 */
if(empty($stepDesc)) continue;
/* 计算步骤类型和层级。 */
$stepType = $data->stepType[$key][$stepKey];
$grade = substr_count((string)$stepKey, '.');
/* 如果当前步骤层级为0,父ID和祖父ID清0。 */
if($grade == 0)
{
$desc = trim($desc);
if(empty($desc)) continue;
$stepData = new stdclass();
$stepData->type = $data->stepType[$key][$id] == 'item' && $parentStepID == 0 ? 'step' : $data->stepType[$key][$id];
$stepData->parent = $stepData->type == 'item' ? $parentStepID : 0;
$stepData->case = $caseID;
$stepData->version = 1;
$stepData->desc = htmlSpecialString($desc);
$stepData->expect = htmlSpecialString(!empty($data->expect[$key][$id]) ? trim($data->expect[$key][$id]) : '');
$this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec();
if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID();
if($stepData->type == 'step') $parentStepID = 0;
$parentStepID = $grandPaStepID = 0;
}
/* 如果前一个步骤的层级比当前步骤的层级大,将父ID设置为祖父ID,祖父ID清0。 */
elseif($preGrade > $grade)
{
$parentStepID = $grandPaStepID;
$grandPaStepID = 0;
}
/* 构建步骤数据,插入步骤。 */
$step = new stdClass();
$step->type = $stepType;
$step->parent = $parentStepID;
$step->case = $caseID;
$step->version = 1;
$step->desc = rtrim(htmlSpecialString($stepDesc));
$step->expect = $stepType == 'group' ? '' : rtrim(htmlSpecialString(zget($data->expect[$key], $stepKey, '')));
$this->dao->insert(TABLE_CASESTEP)->data($step)->autoCheck()->exec();
/* 如果步骤类型是group,将祖父ID设置为父ID,父ID设置为当前步骤ID。 */
if($stepType == 'group')
{
$grandPaStepID = $parentStepID;
$parentStepID = $this->dao->lastInsertID();
}
$preGrade = $grade;
}
$this->loadModel('action')->create('case', $caseID, 'Opened');
return $caseID;
}
+5 -4
View File
@@ -365,11 +365,12 @@ class caselibZen extends caselib
*/
protected function getFieldsForImport(): array
{
$fields = explode(',', $this->config->testcase->exportFields);
$fields = explode(',', $this->config->testcase->exportFields);
$libFields = array_flip($this->config->caselib->exportTemplateFields);
foreach($fields as $key => $fieldName)
{
$fieldName = trim($fieldName);
$fields[$fieldName] = zget($this->lang->testcase, $fieldName);
if(isset($libFields[$fieldName])) $fields[$fieldName] = zget($this->lang->testcase, $fieldName);
unset($fields[$key]);
}
@@ -396,10 +397,10 @@ class caselibZen extends caselib
}
$columns = array();
foreach($header as $title)
foreach($header as $i => $title)
{
if(!isset($fields[$title])) continue;
$columns[] = $fields[$title];
$columns[$i] = $fields[$title];
}
return $columns;
+21 -16
View File
@@ -8,26 +8,31 @@
function previewCharts()
{
const checkedList = $('#moduleMenu menu').zui('tree').$.getChecks();
if(checkedList.length > 0)
{
if(checkedList.length > maxPreviewCount)
const checkedCharts = [];
checkedList.forEach((itemKey, index) => {
if(itemKey.includes(':') && itemKey.includes('_'))
{
zui.Modal.alert(maxPreviewTips);
return false;
const keys = itemKey.split(':')[1].split('_');
checkedCharts.push({index, keys});
}
});
const form = new FormData();
checkedList.forEach((itemKey, index) => {
if(itemKey.includes(':') && itemKey.includes('_'))
{
const keys = itemKey.split(':')[1].split('_');
form.append('charts[' + index + '][groupID]', keys[0]);
form.append('charts[' + index + '][chartID]', keys[1]);
}
});
postAndLoadPage(previewUrl, form, '#chartPanel');
if(checkedCharts.length == 0) return;
if(checkedCharts.length > maxPreviewCount)
{
zui.Modal.alert(maxPreviewTips);
return false;
}
const form = new FormData();
checkedCharts.forEach(chart => {
const {index, keys} = chart;
form.append('charts[' + index + '][groupID]', keys[0]);
form.append('charts[' + index + '][chartID]', keys[1]);
});
postAndLoadPage(previewUrl, form, '#chartPanel');
}
/**
+1 -1
View File
@@ -29,7 +29,7 @@ $generateCharts = function() use($charts, $lang)
if($type == 'select' && !isset($options[$field]))
{
$fieldSetting = $chart->fieldSettings[$field];
$options[$field] = $this->chart->getSysOptions($fieldSetting['type'], $fieldSetting['object'], $fieldSetting['field'], $chart->sql, zget($filter, 'saveAs', ''));
$options[$field] = $this->chart->getSysOptions($fieldSetting['type'], $fieldSetting['object'], $fieldSetting['field'], $chart->sql, zget($filter, 'saveAs', ''), $chart->driver);
}
$filters[] = resultFilter(set(array('title' => $name, 'type' => $type, 'name' => $field, 'value' => $value, 'items' => zget($options, $field, array()))));
}
-1
View File
@@ -1332,7 +1332,6 @@ eof;
$method = strtolower($method);
parse_str($vars, $params);
if($config->vision == 'or' and $module == 'story') $module = 'requirement';
if(empty($app->user)) return false;
list($module, $method) = commonTao::getStoryModuleAndMethod($module, $method, $params);
list($module, $method) = commonTao::getBoardModuleAndMethod($module, $method, $params);
+10 -1
View File
@@ -94,6 +94,15 @@ window.deleteGrade = function()
{
$(this).closest('tr').remove();
$('#gradeList tbody').find('tr').last().find('.btn-add-grade').removeClass('hidden');
/* compute grade. */
let index = 1;
$('#gradeList tbody').find('.gradeTr').each(function()
{
$(this).find('td.index').text(index);
$(this).find('input[type=hidden]').val(index);
index ++;
});
}
window.addGrade = function()
@@ -104,7 +113,7 @@ window.addGrade = function()
newRow.find('input').val('');
newRow.find('.btn-delete-grade').attr('href', 'javascript:void').removeClass('ajax-submit hidden').on('click', deleteGrade);
newRow.find('.btn-close').remove();
newRow.find('.btn-close, .btn-active').remove();
newRow.find("input[type=hidden]").val(newIndex);
newRow.find('td.index').text(newIndex);
newRow.find('td.grade-status').text(enableLang);
+1
View File
@@ -572,6 +572,7 @@ if(in_array($module, array('story', 'requirement', 'epic')) && $field == 'grade'
if(common::hasPriv('custom', 'deleteGrade')) $items[] = array('icon' => 'trash', 'class' => "btn ghost btn-delete-grade ajax-submit $hidden", 'url' => inlink('deleteGrade', "type={$module}&id={$grade->grade}"));
$tbody[] = h::tr(
setClass('gradeTr'),
formHidden('grade[]', $grade->grade),
h::td($grade->grade, set::width('100px'), setClass('index')),
h::td(input(set::name('gradeName[]'), set::value($grade->name))),
+36
View File
@@ -12,3 +12,39 @@ $config->dataview->groupChild['company'] = array('user' => 'user,usercontact,use
$config->dataview->multipleMappingFields = array();
$config->dataview->multipleMappingFields[] = 'testcase-stage';
$config->dataview->fieldTypes = new stdclass();
$config->dataview->fieldTypes->TINY = 'number';
$config->dataview->fieldTypes->SHORT = 'number';
$config->dataview->fieldTypes->LONG = 'number';
$config->dataview->fieldTypes->FLOAT = 'number';
$config->dataview->fieldTypes->DOUBLE = 'number';
$config->dataview->fieldTypes->TIMESTAMP = 'string';
$config->dataview->fieldTypes->LONGLONG = 'string';
$config->dataview->fieldTypes->INT24 = 'number';
$config->dataview->fieldTypes->DATE = 'date';
$config->dataview->fieldTypes->TIME = 'string';
$config->dataview->fieldTypes->DATETIME = 'date';
$config->dataview->fieldTypes->YEAR = 'date';
$config->dataview->fieldTypes->ENUM = 'string';
$config->dataview->fieldTypes->SET = 'string';
$config->dataview->fieldTypes->TINYBLOB = 'string';
$config->dataview->fieldTypes->MEDIUMBLOB = 'string';
$config->dataview->fieldTypes->LONG_BLOB = 'string';
$config->dataview->fieldTypes->BLOB = 'string';
$config->dataview->fieldTypes->VAR_STRING = 'string';
$config->dataview->fieldTypes->STRING = 'string';
$config->dataview->fieldTypes->NULL = 'null';
$config->dataview->fieldTypes->NEWDATE = 'date';
$config->dataview->fieldTypes->INTERVAL = 'string';
$config->dataview->fieldTypes->GEOMETRY = 'string';
$config->dataview->fieldTypes->NEWDECIMAL = 'number';
$config->dataview->fieldTypes->int = 'number';
$config->dataview->fieldTypes->varchar = 'string';
$config->dataview->fieldTypes->text = 'string';
$config->dataview->fieldTypes->timestamp = 'string';
$config->dataview->fieldTypes->date = 'date';
$config->dataview->fieldTypes->time = 'string';
$config->dataview->fieldTypes->double = 'number';
$config->dataview->fieldTypes->number = 'number';
$config->dataview->fieldTypes->bigint = 'number';
+15
View File
@@ -364,6 +364,21 @@ class dataviewModel extends model
if(count($paths) > 1) return $this->genTreeOptions($child, $values, array_slice($paths, 1));
}
/**
* 获取默认的对象和对象的字段列表。
* Get default object and object fields.
*
* @access public
* @return array
*/
public function getObjectFields()
{
$objectFields = array();
foreach(array_keys($this->lang->dataview->objects) as $object) $objectFields[$object] = $this->getTypeOptions($object);
return $objectFields;
}
/**
* 检查按钮是否可以点击。
* Adjust the action is clickable.
+1
View File
@@ -32,6 +32,7 @@ $schema->fields['status'] = array('type' => 'option', 'name' => $this->lan
$schema->fields['stage'] = array('type' => 'option', 'name' => $this->lang->story->stage, 'options' => $this->lang->story->stageList);
$schema->fields['pri'] = array('type' => 'option', 'name' => $this->lang->story->pri, 'options' => $this->lang->story->priList);
$schema->fields['source'] = array('type' => 'option', 'name' => $this->lang->story->source, 'options' => $this->lang->story->sourceList);
$schema->fields['type'] = array('type' => 'option', 'name' => $this->lang->story->type, 'options' => $this->lang->story->typeList);
$schema->fields['category'] = array('type' => 'option', 'name' => $this->lang->story->category, 'options' => $this->lang->story->categoryList);
$schema->fields['product'] = array('type' => 'object', 'name' => $this->lang->story->product, 'object' => 'product', 'show' => 'product.name');
$schema->fields['storymodule'] = array('type' => 'object', 'name' => $this->lang->story->module, 'object' => 'storymodule', 'show' => 'storymodule.name');
+2 -2
View File
@@ -11,7 +11,7 @@ title=测试 devModel']']saveCustomedLang();
cid=1
pid=1
自定义公共语言项 >> 产品2|项目2|迭代2
自定义公共语言项 >> 产品2|项目2|迭代2|软需2|用需2
自定义一级菜单语言项 >> 我的地盘
重置一级导航语言项 >> null
自定义二级导航语言项 >> 仪表盘
@@ -34,7 +34,7 @@ $_POST['common_projectCommon'] = '项目2';
$_POST['common_executionCommon'] = '迭代2';
$_POST['common_URCommon'] = '用需2';
$_POST['common_SRCommon'] = '软需2';
r($devTester->saveCustomedLangTest('common', 'common', '', 'zh-cn')) && p() && e("产品2|项目2|迭代2|"); // 自定义公共语言项
r($devTester->saveCustomedLangTest('common', 'common', '', 'zh-cn')) && p() && e("产品2|项目2|迭代2|软需2|用需2"); // 自定义公共语言项
$_POST = array();
$_POST['common_mainNav_my'] = '我的地盘';
+12 -12
View File
@@ -5,12 +5,12 @@
title=测试 docModel->getDynamic();
cid=1
- 获取每页5条,第1页动态数据 @32;31;30;29;28;
- 获取每页5条,第2页动态数据 @27;26;25;24;23;
- 获取每页10条,第1页动态数据 @32;31;30;29;28;27;26;25;24;23;
- 获取每页10条,第2页动态数据 @22;21;20;19;18;50;17;49;16;48;
- 获取每页20条,第1页动态数据 @32;31;30;29;28;27;26;25;24;23;22;21;20;19;18;50;17;49;16;48;
- 获取每页20条,第2页动态数据 @15;47;14;46;13;45;12;44;11;43;10;42;9;41;8;40;7;39;6;38;
- 获取每页5条,第1页动态数据 @31;30;29;28;27;
- 获取每页5条,第2页动态数据 @26;25;24;23;22;
- 获取每页10条,第1页动态数据 @31;30;29;28;27;26;25;24;23;22;
- 获取每页10条,第2页动态数据 @21;20;19;50;18;49;17;48;16;47;
- 获取每页20条,第1页动态数据 @31;30;29;28;27;26;25;24;23;22;21;20;19;50;18;49;17;48;16;47;
- 获取每页20条,第2页动态数据 @15;46;14;45;13;44;12;43;11;42;10;41;9;40;8;39;7;38;6;37;
*/
@@ -27,9 +27,9 @@ $recPerPages = array(5, 10, 20);
$pagerIds = array(1, 2);
$docTester = new docTest();
r($docTester->getDynamicTest($recPerPages[0], $pagerIds[0])) && p() && e('32;31;30;29;28;'); // 获取每页5条,第1页动态数据
r($docTester->getDynamicTest($recPerPages[0], $pagerIds[1])) && p() && e('27;26;25;24;23;'); // 获取每页5条,第2页动态数据
r($docTester->getDynamicTest($recPerPages[1], $pagerIds[0])) && p() && e('32;31;30;29;28;27;26;25;24;23;'); // 获取每页10条,第1页动态数据
r($docTester->getDynamicTest($recPerPages[1], $pagerIds[1])) && p() && e('22;21;20;19;18;50;17;49;16;48;'); // 获取每页10条,第2页动态数据
r($docTester->getDynamicTest($recPerPages[2], $pagerIds[0])) && p() && e('32;31;30;29;28;27;26;25;24;23;22;21;20;19;18;50;17;49;16;48;'); // 获取每页20条,第1页动态数据
r($docTester->getDynamicTest($recPerPages[2], $pagerIds[1])) && p() && e('15;47;14;46;13;45;12;44;11;43;10;42;9;41;8;40;7;39;6;38;'); // 获取每页20条,第2页动态数据
r($docTester->getDynamicTest($recPerPages[0], $pagerIds[0])) && p() && e('31;30;29;28;27;'); // 获取每页5条,第1页动态数据
r($docTester->getDynamicTest($recPerPages[0], $pagerIds[1])) && p() && e('26;25;24;23;22;'); // 获取每页5条,第2页动态数据
r($docTester->getDynamicTest($recPerPages[1], $pagerIds[0])) && p() && e('31;30;29;28;27;26;25;24;23;22;'); // 获取每页10条,第1页动态数据
r($docTester->getDynamicTest($recPerPages[1], $pagerIds[1])) && p() && e('21;20;19;50;18;49;17;48;16;47;'); // 获取每页10条,第2页动态数据
r($docTester->getDynamicTest($recPerPages[2], $pagerIds[0])) && p() && e('31;30;29;28;27;26;25;24;23;22;21;20;19;50;18;49;17;48;16;47;'); // 获取每页20条,第1页动态数据
r($docTester->getDynamicTest($recPerPages[2], $pagerIds[1])) && p() && e('15;46;14;45;13;44;12;43;11;42;10;41;9;40;8;39;7;38;6;37;'); // 获取每页20条,第2页动态数据
+18
View File
@@ -2,4 +2,22 @@
global $app;
$app->loadConfig('story');
$config->epic = clone $config->story;
$config->epic->needReview = 1;
$config->epic->create = new stdclass();
$config->epic->edit = new stdclass();
$config->epic->change = new stdclass();
$config->epic->close = new stdclass();
$config->epic->review = new stdclass();
$config->epic->create->requiredFields = 'title';
$config->epic->edit->requiredFields = 'title';
$config->epic->change->requiredFields = 'title';
$config->epic->close->requiredFields = 'closedReason';
$config->epic->review->requiredFields = '';
$config->epic->custom = new stdclass();
$config->epic->custom->createFields = '';
$config->epic->custom->batchCreateFields = 'module,plan,spec,pri,estimate,review,%s';
$config->epic->custom->batchEditFields = 'branch,module,plan,estimate,pri,source,stage,closedBy,closedReason';
+1
View File
@@ -207,6 +207,7 @@ class execution extends control
$this->view->moduleTree = $this->tree->getTaskTreeMenu($executionID, (int)$productID, 0, array('treeModel', 'createTaskLink'), $showAllModule);
$this->view->memberPairs = $memberPairs;
$this->view->execution = $execution;
$this->view->productID = $productID;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->features = $this->execution->getExecutionFeatures($execution);
+4 -4
View File
@@ -287,10 +287,10 @@ $lang->execution->treeViewTask = 'Tree View Task';
$lang->execution->treeViewStory = 'Tree View Story';
$lang->execution->storyKanban = 'Story Kanban';
$lang->execution->storySort = 'Story sortieren';
$lang->execution->importPlanStory = "{$lang->executionCommon} is created!\nDo you want to import stories that have been linked to the plan? Only active " . $lang->SRCommon . ' can be imported.';
$lang->execution->importEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.";
$lang->execution->importBranchPlanStory = "{$lang->executionCommon} is created!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importBranchEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importPlanStory = "{$lang->executionCommon} is created!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only active %s can be imported.";
$lang->execution->importEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import {$lang->common->story} that have been linked to the plan? The %s in the draft will be automatically filtered out when imported.";
$lang->execution->importBranchPlanStory = "{$lang->executionCommon} is created!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only the activation %s of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importBranchEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only the activation %s of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->needLinkProducts = "The execution has not been linked with any {$lang->productCommon}, and the related functions cannot be used. Please link the {$lang->productCommon} first and try again.";
$lang->execution->iteration = 'Iteration';
$lang->execution->iterationInfo = '%s Iterationen';
+4 -4
View File
@@ -287,10 +287,10 @@ $lang->execution->treeViewTask = 'Tree View Task';
$lang->execution->treeViewStory = 'Tree View Story';
$lang->execution->storyKanban = 'Story Kanban';
$lang->execution->storySort = 'Rank Story';
$lang->execution->importPlanStory = "{$lang->executionCommon} is created!\nDo you want to import stories that have been linked to the plan? Only active " . $lang->SRCommon . ' can be imported.';
$lang->execution->importEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.";
$lang->execution->importBranchPlanStory = "{$lang->executionCommon} is created!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importBranchEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importPlanStory = "{$lang->executionCommon} is created!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only active %s can be imported.";
$lang->execution->importEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import {$lang->common->story} that have been linked to the plan? The %s in the draft will be automatically filtered out when imported.";
$lang->execution->importBranchPlanStory = "{$lang->executionCommon} is created!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only the activation %s of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importBranchEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only the activation %s of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->needLinkProducts = "The execution has not been linked with any {$lang->productCommon}, and the related functions cannot be used. Please link the {$lang->productCommon} first and try again.";
$lang->execution->iteration = 'Iterations';
$lang->execution->iterationInfo = '%s Iterations';
+4 -4
View File
@@ -287,10 +287,10 @@ $lang->execution->treeViewTask = 'Tree View Task';
$lang->execution->treeViewStory = 'Tree View Story';
$lang->execution->storyKanban = 'Story Kanban';
$lang->execution->storySort = 'Rank Story';
$lang->execution->importPlanStory = "{$lang->executionCommon} is created!\nDo you want to import stories that have been linked to the plan? Only active " . $lang->SRCommon . ' can be imported.';
$lang->execution->importEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.";
$lang->execution->importBranchPlanStory = "{$lang->executionCommon} is created!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importBranchEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importPlanStory = "{$lang->executionCommon} is created!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only active %s can be imported.";
$lang->execution->importEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import {$lang->common->story} that have been linked to the plan? The %s in the draft will be automatically filtered out when imported.";
$lang->execution->importBranchPlanStory = "{$lang->executionCommon} is created!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only the activation %s of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->importBranchEditPlanStory = "{$lang->executionCommon} is edited!\nDo you want to import {$lang->common->story} that have been linked to the plan? Only the activation %s of the branch associated with this " . $lang->executionCommon. ' will be associated with the import';
$lang->execution->needLinkProducts = "The execution has not been linked with any {$lang->productCommon}, and the related functions cannot be used. Please link the {$lang->productCommon} first and try again.";
$lang->execution->iteration = 'Iterations';
$lang->execution->iterationInfo = '%s Iterations';
+4 -4
View File
@@ -287,10 +287,10 @@ $lang->execution->treeViewTask = '树状图查看任务';
$lang->execution->treeViewStory = "树状图查看{$lang->SRCommon}";
$lang->execution->storyKanban = "{$lang->SRCommon}看板";
$lang->execution->storySort = "{$lang->SRCommon}排序";
$lang->execution->importPlanStory = "创建{$lang->executionCommon}成功!\n是否导入计划关联的相关" . $lang->SRCommon . '?导入时只能导入激活状态的' . $lang->SRCommon . '。';
$lang->execution->importEditPlanStory = "编辑{$lang->executionCommon}成功!\n是否导入计划关联的相关" . $lang->SRCommon . '?导入时将自动过滤掉草稿状态的' . $lang->SRCommon . '。';
$lang->execution->importBranchPlanStory = "创建{$lang->executionCommon}成功!\n是否导入计划关联的相关" . $lang->SRCommon . '?导入时将只关联本' . $lang->executionCommon . '所关联分支的激活需求。';
$lang->execution->importBranchEditPlanStory = "编辑{$lang->executionCommon}成功!\n是否导入计划关联的相关" . $lang->SRCommon . '?导入时将只关联本' . $lang->executionCommon . '所关联分支的激活需求。';
$lang->execution->importPlanStory = "创建{$lang->executionCommon}成功!\n是否导入计划关联的相关%s?导入时只能导入激活状态的" . $lang->common->story . '。';
$lang->execution->importEditPlanStory = "编辑{$lang->executionCommon}成功!\n是否导入计划关联的相关%s?导入时将自动过滤掉草稿状态的" . $lang->common->story . '。';
$lang->execution->importBranchPlanStory = "创建{$lang->executionCommon}成功!\n是否导入计划关联的相关%s?导入时将只关联本" . $lang->executionCommon . '所关联分支的激活需求。';
$lang->execution->importBranchEditPlanStory = "编辑{$lang->executionCommon}成功!\n是否导入计划关联的相关%s?导入时将只关联本" . $lang->executionCommon . '所关联分支的激活需求。';
$lang->execution->needLinkProducts = "该执行还未关联任何{$lang->productCommon},相关功能无法使用,请先关联{$lang->productCommon}后再试。";
$lang->execution->iteration = '版本迭代';
$lang->execution->iterationInfo = '迭代%s次';
@@ -36,7 +36,7 @@ $projectExecution->projectInfo->hasProduct = 0;
$executionTester = new executionTest();
r($executionTester->getExecutionFeaturesTest($opsExecution)) && p('story,task,qa,devops,burn,build,other,plan') && e('0,1,0,1,0,0,1,1'); // 获取运维类型迭代禁用的功能
r($executionTester->getExecutionFeaturesTest($requestStage)) && p('story,task,qa,devops,burn,build,other,plan') && e('0,1,0,0,1,0,0,0'); // 获取需求阶段禁用的功能
r($executionTester->getExecutionFeaturesTest($requestStage)) && p('story,task,qa,devops,burn,build,other,plan') && e('1,1,0,0,1,0,0,0'); // 获取需求阶段禁用的功能
r($executionTester->getExecutionFeaturesTest($designStage)) && p('story,task,qa,devops,burn,build,other,plan') && e('1,1,0,0,1,0,0,1'); // 获取设计阶段禁用的功能
r($executionTester->getExecutionFeaturesTest($reviewStage)) && p('story,task,qa,devops,burn,build,other,plan') && e('0,1,0,0,1,0,0,0'); // 获取评审阶段禁用的功能
r($executionTester->getExecutionFeaturesTest($reviewStage)) && p('story,task,qa,devops,burn,build,other,plan') && e('1,1,0,0,1,0,0,0'); // 获取评审阶段禁用的功能
r($executionTester->getExecutionFeaturesTest($projectExecution)) && p('story,task,qa,devops,burn,build,other,plan') && e('1,1,1,1,1,1,1,0'); // 获取项目型项目的执行禁用的功能
@@ -3,9 +3,21 @@
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/lib/execution.unittest.class.php';
/**
title=测试 executionModel::linkStories();
cid=1
- 敏捷执行关联需求 @1
- 瀑布执行关联需求 @0
- 看板执行关联需求 @1
*/
$execution = zenData('project');
$execution->id->range('1-5');
$execution->name->range('项目1,项目2,迭代1,迭代2,迭代3');
$execution->project->range('0,0,1,1,2');
$execution->type->range('project{2},sprint,waterfall,kanban');
$execution->status->range('doing{3},closed,doing');
$execution->parent->range('0,0,1,1,2');
+5 -3
View File
@@ -6,6 +6,7 @@ include dirname(__FILE__, 2) . '/lib/execution.unittest.class.php';
$execution = zenData('project');
$execution->id->range('1-5');
$execution->name->range('项目1,项目2,迭代1,迭代2,迭代3');
$execution->project->range('0,0,1,1,2');
$execution->type->range('project{2},sprint,waterfall,kanban');
$execution->status->range('doing{3},closed,doing');
$execution->parent->range('0,0,1,1,2');
@@ -13,6 +14,7 @@ $execution->grade->range('2{2},1{3}');
$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->storyType->range('story');
$execution->gen(5);
$projectstory = zenData('projectstory');
@@ -59,6 +61,6 @@ $execution = new executionTest();
r($execution->linkStoryTest($executionIDList[0], $count[0], $stories)) && p('0:project,product,story') && e('3,1,4'); // 敏捷执行关联需求
r($execution->linkStoryTest($executionIDList[1], $count[0], $stories)) && p('0:project,product,story') && e('4,1,4'); // 瀑布执行关联需求
r($execution->linkStoryTest($executionIDList[2], $count[0], $stories)) && p('0:project,product,story') && e('5,1,4'); // 看板执行关联需求
r($execution->linkStoryTest($executionIDList[0], $count[1], $stories)) && p() && e('3'); // 敏捷执行关联需求统计
r($execution->linkStoryTest($executionIDList[1], $count[1], $stories)) && p() && e('3'); // 瀑布执行关联需求统计
r($execution->linkStoryTest($executionIDList[2], $count[1], $stories)) && p() && e('3'); // 看板执行关联需求统计
r($execution->linkStoryTest($executionIDList[0], $count[1], $stories)) && p() && e('3'); // 敏捷执行关联需求统计
r($execution->linkStoryTest($executionIDList[1], $count[1], $stories)) && p() && e('3'); // 瀑布执行关联需求统计
r($execution->linkStoryTest($executionIDList[2], $count[1], $stories)) && p() && e('3'); // 看板执行关联需求统计
@@ -6,6 +6,7 @@ include dirname(__FILE__, 2) . '/lib/execution.unittest.class.php';
$execution = zenData('project');
$execution->id->range('1-5');
$execution->name->range('项目1,项目2,迭代1,迭代2,迭代3');
$execution->project->range('0,0,1,1,2');
$execution->type->range('project{2},sprint,waterfall,kanban');
$execution->status->range('doing{3},closed,doing');
$execution->parent->range('0,0,1,1,2');
@@ -13,6 +14,7 @@ $execution->grade->range('2{2},1{3}');
$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->storyType->range('story');
$execution->gen(5);
$projectstory = zenData('projectstory');
+2 -1
View File
@@ -128,12 +128,13 @@ toolbar
);
/* zin: Define the sidebar in main content. */
$activeKey = $browseType == 'byproduct' ? $productID : $moduleID;
sidebar
(
moduleMenu
(
set::modules($moduleTree),
set::activeKey($moduleID),
set::activeKey($activeKey),
set::settingLink(createLink('tree', 'browsetask', "rootID=$execution->id&productID=0")),
set::settingApp($execution->multiple ? 'execution' : 'project'),
set::closeLink(createLink('execution', 'task', "executionID={$executionID}")),
+19 -3
View File
@@ -2020,7 +2020,15 @@ class executionZen extends execution
$multiBranchProduct = false;
array_map(function($executionProduct) use(&$multiBranchProduct){if($executionProduct->type != 'normal') $multiBranchProduct = true;}, $executionProductList);
$importPlanStoryTips = $multiBranchProduct ? $this->lang->execution->importBranchPlanStory : $this->lang->execution->importPlanStory;
$storyType = '';
$project = $this->loadModel('project')->fetchByID($projectID);
if($project)
{
$this->loadModel('story');
foreach(explode(',', $project->storyType) as $type) $storyType .= $this->lang->story->typeList[$type] . ', ';
}
if(empty($storyType)) $storyType = $this->lang->story->common;
$importPlanStoryTips = sprintf($multiBranchProduct ? $this->lang->execution->importBranchPlanStory : $this->lang->execution->importPlanStory, trim($storyType, ', '));
if($execution->type == 'stage') $importPlanStoryTips = str_replace($this->lang->executionCommon, $this->lang->execution->stage, $importPlanStoryTips);
$confirmURL = inlink('create', "projectID=$projectID&executionID=$executionID&copyExecutionID=&planID=$planID&confirm=yes");
$cancelURL = inlink('create', "projectID=$projectID&executionID=$executionID");
@@ -2048,10 +2056,10 @@ class executionZen extends execution
*/
protected function updateLinkedPlans(int $executionID, string $newPlans = '', string $confirm = 'no')
{
$projectID = $this->dao->select('project')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch('project');
if(!empty($newPlans) and $confirm == 'yes')
{
$newPlans = explode(',', $newPlans);
$projectID = $this->dao->select('project')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch('project');
$this->loadModel('productplan')->linkProject($executionID, $newPlans);
$this->productplan->linkProject($projectID, $newPlans);
return $this->send(array('result' => 'success', 'load' => inlink('view', "executionID=$executionID")));
@@ -2069,7 +2077,15 @@ class executionZen extends execution
}
}
$linkPlanMsg = $multiBranchProduct ? $this->lang->execution->importBranchEditPlanStory : $this->lang->execution->importEditPlanStory;
$storyType = '';
$project = $this->loadModel('project')->fetchByID($projectID);
if($project)
{
$this->loadModel('story');
foreach(explode(',', $project->storyType) as $type) $storyType .= $this->lang->story->typeList[$type] . ', ';
}
if(empty($storyType)) $storyType = $this->lang->story->common;
$linkPlanMsg = sprintf($multiBranchProduct ? $this->lang->execution->importBranchEditPlanStory : $this->lang->execution->importEditPlanStory, trim($storyType, ', '));
$confirmURL = inlink('edit', "executionID=$executionID&action=edit&extra=&newPlans=$newPlans&confirm=yes");
$cancelURL = inlink('view', "executionID=$executionID");
return $this->send(array('result' => 'success', 'load' => array('confirm' => $linkPlanMsg, 'confirmed' => $confirmURL, 'canceled' => $cancelURL)));
+1
View File
@@ -197,6 +197,7 @@ class file extends control
}
if($this->post->kind == 'task' && $this->config->vision != 'lite') $output .= $this->lang->file->childTaskTips;
}
$output = htmlspecialchars_decode(htmlspecialchars_decode($output, ENT_NOQUOTES), ENT_NOQUOTES);
if(isset($_POST['encode']) && $this->post->encode != "utf-8") $output = helper::convertEncoding($output, 'utf-8', $this->post->encode . '//TRANSLIT');
$this->sendDownHeader($this->post->fileName, 'csv', $output);
@@ -7,13 +7,13 @@ cid=1
- 测试插入id为 10 的节假日时迭代 11 项目的planDuration @250
- 测试插入id为 10 的节假日时迭代 60 项目的planDuration @252
- 测试插入id为 10 的节假日时迭代 101 项目的planDuration @168
- 测试插入id为 10 的节假日时迭代 101 项目的planDuration @251
- 测试插入id为 5 的节假日时迭代 11 项目的planDuration @250
- 测试插入id为 5 的节假日时迭代 60 项目的planDuration @252
- 测试插入id为 5 的节假日时迭代 101 项目的planDuration @168
- 测试插入id为 5 的节假日时迭代 101 项目的planDuration @251
- 测试插入id为 5 的节假日时迭代 11 项目的planDuration @250
- 测试插入id为 5 的节假日时迭代 60 项目的planDuration @252
- 测试插入id为 5 的节假日时迭代 101 项目的planDuration @168
- 测试插入id为 5 的节假日时迭代 101 项目的planDuration @251
*/
declare(strict_types=1);
@@ -34,12 +34,12 @@ $holiday = new holidayTest();
r($holiday->updateProgramPlanDurationTest($projectIdList[0], $holidayIdList[0], $updateDuration[0])) && p() && e('250'); //测试插入id为 10 的节假日时迭代 11 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[1], $holidayIdList[0], $updateDuration[1])) && p() && e('252'); //测试插入id为 10 的节假日时迭代 60 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[2], $holidayIdList[0], $updateDuration[1])) && p() && e('168'); //测试插入id为 10 的节假日时迭代 101 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[2], $holidayIdList[0], $updateDuration[1])) && p() && e('251'); //测试插入id为 10 的节假日时迭代 101 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[0], $holidayIdList[1], $updateDuration[0])) && p() && e('250'); //测试插入id为 5 的节假日时迭代 11 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[1], $holidayIdList[1], $updateDuration[1])) && p() && e('252'); //测试插入id为 5 的节假日时迭代 60 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[2], $holidayIdList[1], $updateDuration[1])) && p() && e('168'); //测试插入id为 5 的节假日时迭代 101 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[2], $holidayIdList[1], $updateDuration[1])) && p() && e('251'); //测试插入id为 5 的节假日时迭代 101 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[0], $holidayIdList[2], $updateDuration[0])) && p() && e('250'); //测试插入id为 5 的节假日时迭代 11 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[1], $holidayIdList[2], $updateDuration[1])) && p() && e('252'); //测试插入id为 5 的节假日时迭代 60 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[2], $holidayIdList[2], $updateDuration[1])) && p() && e('168'); //测试插入id为 5 的节假日时迭代 101 项目的planDuration
r($holiday->updateProgramPlanDurationTest($projectIdList[2], $holidayIdList[2], $updateDuration[1])) && p() && e('251'); //测试插入id为 5 的节假日时迭代 101 项目的planDuration
@@ -7,13 +7,13 @@ cid=1
- 测试插入id为 10 的节假日时迭代 11 项目的实际工期 @250
- 测试插入id为 10 的节假日时迭代 60 项目的实际工期 @252
- 测试插入id为 10 的节假日时迭代 101 项目的实际工期 @168
- 测试插入id为 10 的节假日时迭代 101 项目的实际工期 @251
- 测试插入id为 5 的节假日时迭代 11 项目的实际工期 @250
- 测试插入id为 5 的节假日时迭代 60 项目的实际工期 @252
- 测试插入id为 5 的节假日时迭代 101 项目的实际工期 @168
- 测试插入id为 5 的节假日时迭代 101 项目的实际工期 @251
- 测试插入id为 5 的节假日时迭代 11 项目的实际工期 @250
- 测试插入id为 5 的节假日时迭代 60 项目的实际工期 @252
- 测试插入id为 5 的节假日时迭代 101 项目的实际工期 @168
- 测试插入id为 5 的节假日时迭代 101 项目的实际工期 @251
*/
declare(strict_types=1);
@@ -34,12 +34,12 @@ $holiday = new holidayTest();
r($holiday->updateProjectRealDurationTest($projectIdList[0], $holidayIdList[0], $updateDuration[0])) && p() && e('250'); //测试插入id为 10 的节假日时迭代 11 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[1], $holidayIdList[0], $updateDuration[1])) && p() && e('252'); //测试插入id为 10 的节假日时迭代 60 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[2], $holidayIdList[0], $updateDuration[1])) && p() && e('168'); //测试插入id为 10 的节假日时迭代 101 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[2], $holidayIdList[0], $updateDuration[1])) && p() && e('251'); //测试插入id为 10 的节假日时迭代 101 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[0], $holidayIdList[1], $updateDuration[0])) && p() && e('250'); //测试插入id为 5 的节假日时迭代 11 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[1], $holidayIdList[1], $updateDuration[1])) && p() && e('252'); //测试插入id为 5 的节假日时迭代 60 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[2], $holidayIdList[1], $updateDuration[1])) && p() && e('168'); //测试插入id为 5 的节假日时迭代 101 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[2], $holidayIdList[1], $updateDuration[1])) && p() && e('251'); //测试插入id为 5 的节假日时迭代 101 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[0], $holidayIdList[2], $updateDuration[0])) && p() && e('250'); //测试插入id为 5 的节假日时迭代 11 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[1], $holidayIdList[2], $updateDuration[1])) && p() && e('252'); //测试插入id为 5 的节假日时迭代 60 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[2], $holidayIdList[2], $updateDuration[1])) && p() && e('168'); //测试插入id为 5 的节假日时迭代 101 项目的实际工期
r($holiday->updateProjectRealDurationTest($projectIdList[2], $holidayIdList[2], $updateDuration[1])) && p() && e('251'); //测试插入id为 5 的节假日时迭代 101 项目的实际工期
@@ -5,6 +5,16 @@
title=测试 holidayModel->updateTaskPlanDuration();
cid=1
- 测试插入id为 10 的节假日时任务 1 任务的计划工期 @1
- 测试插入id为 10 的节假日时任务 2 任务的计划工期 @1
- 测试插入id为 10 的节假日时任务 3 任务的计划工期 @1
- 测试插入id为 5 的节假日时任务 1 任务的计划工期 @1
- 测试插入id为 5 的节假日时任务 2 任务的计划工期 @1
- 测试插入id为 5 的节假日时任务 3 任务的计划工期 @1
- 测试插入id为 5 的节假日时任务 1 任务的计划工期 @1
- 测试插入id为 5 的节假日时任务 2 任务的计划工期 @1
- 测试插入id为 5 的节假日时任务 3 任务的计划工期 @1
*/
declare(strict_types=1);
include dirname(__FILE__, 5) . '/test/lib/init.php';
+54
View File
@@ -99,6 +99,10 @@ class install extends control
$this->view->dataRootResult = $this->installZen->checkDataRoot();
$this->view->iniInfo = $this->install->getIniInfo();
$this->loadModel('bi');
$checkDuckdb = $this->bi->checkDuckdbInstall();
$this->view->duckdbResult = $checkDuckdb['ok'] ? 'ok' : 'fail';
$checkSession = ini_get('session.save_handler') == 'files';
$this->view->sessionResult = 'ok';
$this->view->checkSession = $checkSession;
@@ -622,4 +626,54 @@ class install extends control
$result = $this->loadModel('cne')->tryAllocate($resources);
return $this->send(array('result' => 'success', 'message' => '', 'code' => $result->code));
}
/**
* 安装DuckDB引擎界面。
* Install duckdb.
*
* @param int $download
* @access public
* @return void
*/
public function installDuckdb()
{
$this->loadModel('bi');
$this->bi->updateDownloadingTagFile('file', 'remove');
$this->bi->updateDownloadingTagFile('extension_dm', 'remove');
$this->bi->updateDownloadingTagFile('extension_mysql', 'remove');
$this->view->title = $this->lang->install->installDuckdb;
$this->view->duckdb = 'loading';
$this->view->ext_dm = 'loading';
$this->view->ext_mysql = 'loading';
$this->display();
}
/**
* 安装DuckDB引擎。
* AJAX: Install duckdb.
*
* @access public
* @return void
*/
public function ajaxInstallDuckdb()
{
ignore_user_abort(true);
set_time_limit(0);
session_write_close();
$this->loadModel('bi')->downloadDuckdb();
}
/**
* 检查duckdb文件是否下载完成。
* AJAX: Check duckdb.
*
* @access public
* @return void
*/
public function ajaxCheckDuckdb()
{
$check = $this->loadModel('bi')->checkDuckdbInstall();
echo(json_encode($check));
}
}
+43
View File
@@ -0,0 +1,43 @@
$(document).ready(function()
{
ajaxInstallDuckdb();
setTimeout(() => {ajaxCheckDuckdb()}, 1000);
});
/**
* 安装duckdb
* Ajax install duckdb.
*
* @access public
* @return void
*/
function ajaxInstallDuckdb()
{
let url = $.createLink('install', 'ajaxInstallDuckdb');
$.get(url);
}
/**
* 检查duckdb
* Ajax check duckdb.
*
* @access public
* @return void
*/
function ajaxCheckDuckdb()
{
let url = $.createLink('install', 'ajaxCheckDuckdb');
$.get(url, function(resp)
{
resp = JSON.parse(resp);
const {loading, ok, fail, duckdb, ext_dm, ext_mysql} = resp;
if(loading) setTimeout(() => {ajaxCheckDuckdb()}, 500);
$('#installDuckdb p').addClass('hidden');
$('#installDuckdb p.duckdb-' + duckdb).removeClass('hidden');
$('#installDuckdb p.ext_dm-' + ext_dm).removeClass('hidden');
$('#installDuckdb p.ext_mysql-' + ext_mysql).removeClass('hidden');
if(fail) $('#installDuckdb .help').removeClass('hidden');
});
}
+40 -30
View File
@@ -90,36 +90,46 @@ $lang->install->action = 'Aktionen';
$lang->install->phpVersion = 'PHP Version';
$lang->install->phpFail = 'PHP Version muss 5.2.0+ sein';
$lang->install->pdo = 'PDO';
$lang->install->pdoFail = 'Bearbeiten Sie die PHP ini Datei um die PDO Erweiterung zu laden.';
$lang->install->pdoMySQL = 'PDO_MySQL';
$lang->install->pdoMySQLFail = 'Bearbeiten Sie die PHP ini Datei um die PDO_MySQL Erweiterung zu laden.';
$lang->install->json = 'JSON Extension';
$lang->install->jsonFail = 'Bearbeiten Sie die PHP ini Datei um die JSON Erweiterung zu laden.';
$lang->install->openssl = 'OpenSSL Extension';
$lang->install->opensslFail = 'Bearbeiten Sie die PHP ini Datei um die openssl Erweiterung zu laden.';
$lang->install->mbstring = 'Mbstring Extension';
$lang->install->mbstringFail = 'Bearbeiten Sie die PHP ini Datei um die mbstring Erweiterung zu laden.';
$lang->install->zlib = 'Zlib Extension';
$lang->install->zlibFail = 'Bearbeiten Sie die PHP ini Datei um die zlib Erweiterung zu laden.';
$lang->install->curl = 'Curl Extension';
$lang->install->curlFail = 'Bearbeiten Sie die PHP ini Datei um die curl Erweiterung zu laden.';
$lang->install->filter = 'Filter Extension';
$lang->install->filterFail = 'Bearbeiten Sie die PHP ini Datei um die filter Erweiterung zu laden.';
$lang->install->gd = 'GD Extension';
$lang->install->gdFail = 'Bearbeiten Sie die PHP ini Datei um die gd Erweiterung zu laden.';
$lang->install->iconv = 'Iconv Extension';
$lang->install->iconvFail = 'Bearbeiten Sie die PHP ini Datei um die iconv Erweiterung zu laden.';
$lang->install->apcu = 'APCu Extension';
$lang->install->apcuFail = 'APCu nicht geladen. WENN der APCu geladen wurde, aktivieren Sie die Cache-Funktion.';
$lang->install->tmpRoot = 'Temp Directory';
$lang->install->dataRoot = 'Upload Dateiverzeichnis';
$lang->install->session = 'Session Speicherpfad';
$lang->install->sessionFail = 'Bearbeiten Sie die PHP ini Datei um den session.save_path zu setzen.';
$lang->install->mkdirWin = '<p>%s Verzeichnis muss erstellt werden.<br /> Befehl <code>mkdir %s</code> zur Erstellung.</p>';
$lang->install->chmodWin = ' "%s" Berechtigung muss geändert werden.';
$lang->install->mkdirLinux = '<p>%s Verzeichnis muss erstellt werden.<br /> Befehl <code>mkdir -p %s</code> zur Erstellung.</p>';
$lang->install->chmodLinux = ' "%s" Berechtigung muss geändert werden.<br /> Befehl <code>chmod 777 -R %s</code> für die Anpassung.';
$lang->install->pdo = 'PDO';
$lang->install->pdoFail = 'Bearbeiten Sie die PHP ini Datei um die PDO Erweiterung zu laden.';
$lang->install->pdoMySQL = 'PDO_MySQL';
$lang->install->pdoMySQLFail = 'Bearbeiten Sie die PHP ini Datei um die PDO_MySQL Erweiterung zu laden.';
$lang->install->json = 'JSON Extension';
$lang->install->jsonFail = 'Bearbeiten Sie die PHP ini Datei um die JSON Erweiterung zu laden.';
$lang->install->openssl = 'OpenSSL Extension';
$lang->install->opensslFail = 'Bearbeiten Sie die PHP ini Datei um die openssl Erweiterung zu laden.';
$lang->install->mbstring = 'Mbstring Extension';
$lang->install->mbstringFail = 'Bearbeiten Sie die PHP ini Datei um die mbstring Erweiterung zu laden.';
$lang->install->zlib = 'Zlib Extension';
$lang->install->zlibFail = 'Bearbeiten Sie die PHP ini Datei um die zlib Erweiterung zu laden.';
$lang->install->curl = 'Curl Extension';
$lang->install->curlFail = 'Bearbeiten Sie die PHP ini Datei um die curl Erweiterung zu laden.';
$lang->install->filter = 'Filter Extension';
$lang->install->filterFail = 'Bearbeiten Sie die PHP ini Datei um die filter Erweiterung zu laden.';
$lang->install->gd = 'GD Extension';
$lang->install->gdFail = 'Bearbeiten Sie die PHP ini Datei um die gd Erweiterung zu laden.';
$lang->install->iconv = 'Iconv Extension';
$lang->install->iconvFail = 'Bearbeiten Sie die PHP ini Datei um die iconv Erweiterung zu laden.';
$lang->install->apcu = 'APCu Extension';
$lang->install->apcuFail = 'APCu nicht geladen. WENN der APCu geladen wurde, aktivieren Sie die Cache-Funktion.';
$lang->install->tmpRoot = 'Temp Directory';
$lang->install->dataRoot = 'Upload Dateiverzeichnis';
$lang->install->session = 'Session Speicherpfad';
$lang->install->sessionFail = 'Bearbeiten Sie die PHP ini Datei um den session.save_path zu setzen.';
$lang->install->mkdirWin = '<p>%s Verzeichnis muss erstellt werden.<br /> Befehl <code>mkdir %s</code> zur Erstellung.</p>';
$lang->install->chmodWin = ' "%s" Berechtigung muss geändert werden.';
$lang->install->mkdirLinux = '<p>%s Verzeichnis muss erstellt werden.<br /> Befehl <code>mkdir -p %s</code> zur Erstellung.</p>';
$lang->install->chmodLinux = ' "%s" Berechtigung muss geändert werden.<br /> Befehl <code>chmod 777 -R %s</code> für die Anpassung.';
$lang->install->duckdb = 'DuckDB Engine';
$lang->install->duckdbLoading = 'Installing, please wait...';
$lang->install->duckdbFail = 'The duckdb engine failed to install. Please refer to the manual for manual installation.';
$lang->install->installDuckdb = 'Installing DuckDB';
$lang->install->installingDuckdb = 'Installing duckdb engine...';
$lang->install->installedDuckdb = 'Duckdb engine installed successfully!';
$lang->install->installingExtension = 'Installing the duckdb Engine extension [%s]...';
$lang->install->installedExtension = 'Duckdb Engine extension [%s] installed!';
$lang->install->installedFail = '[%s] Installation failed';
$lang->install->timezone = 'Set Timezone';
$lang->install->defaultLang = 'Standard Sprache';
+40 -30
View File
@@ -90,36 +90,46 @@ $lang->install->action = 'Action';
$lang->install->phpVersion = 'PHP Version';
$lang->install->phpFail = 'PHP Version should be 5.2.0+';
$lang->install->pdo = 'PDO';
$lang->install->pdoFail = 'Edit php.ini to load PDO extension.';
$lang->install->pdoMySQL = 'PDO_MySQL';
$lang->install->pdoMySQLFail = 'Edit php.ini to load PDO_MySQL extension.';
$lang->install->json = 'JSON Extension';
$lang->install->jsonFail = 'Edit php.ini to load JSON extension.';
$lang->install->openssl = 'OpenSSL Extension';
$lang->install->opensslFail = 'Edit php.ini to load openssl extension.';
$lang->install->mbstring = 'Mbstring Extension';
$lang->install->mbstringFail = 'Edit php.ini to load mbstring extension.';
$lang->install->zlib = 'Zlib Extension';
$lang->install->zlibFail = 'Edit php.ini to load zlib extension.';
$lang->install->curl = 'Curl Extension';
$lang->install->curlFail = 'Edit php.ini to load curl extension.';
$lang->install->filter = 'Filter Extension';
$lang->install->filterFail = 'Edit the php.ini file to load filter extension.';
$lang->install->gd = 'GD Extension';
$lang->install->gdFail = 'Edit the php.ini file to load gd extension.';
$lang->install->iconv = 'Iconv Extension';
$lang->install->iconvFail = 'Edit the php.ini file to load iconv extension.';
$lang->install->apcu = 'APCu Extension';
$lang->install->apcuFail = "APCu isn't loaded. IF loaded the APCu, then enable cache function.";
$lang->install->tmpRoot = 'Temp Directory';
$lang->install->dataRoot = 'Uploaded File Directory';
$lang->install->session = 'Session Save Path';
$lang->install->sessionFail = 'Edit the php.ini file to set session.save_path.';
$lang->install->mkdirWin = '<p>%s directory has to be created.<br /> Run <code>mkdir %s</code> to create it.</p>';
$lang->install->chmodWin = ' "%s" privilege has to be changed.';
$lang->install->mkdirLinux = '<p>%s directory has to be created.<br /> Run <code>mkdir -p %s</code> to create it.</p>';
$lang->install->chmodLinux = ' "%s" permison has to be changed.<br /> Run <code>chmod 777 -R %s</code> to change it.';
$lang->install->pdo = 'PDO';
$lang->install->pdoFail = 'Edit php.ini to load PDO extension.';
$lang->install->pdoMySQL = 'PDO_MySQL';
$lang->install->pdoMySQLFail = 'Edit php.ini to load PDO_MySQL extension.';
$lang->install->json = 'JSON Extension';
$lang->install->jsonFail = 'Edit php.ini to load JSON extension.';
$lang->install->openssl = 'OpenSSL Extension';
$lang->install->opensslFail = 'Edit php.ini to load openssl extension.';
$lang->install->mbstring = 'Mbstring Extension';
$lang->install->mbstringFail = 'Edit php.ini to load mbstring extension.';
$lang->install->zlib = 'Zlib Extension';
$lang->install->zlibFail = 'Edit php.ini to load zlib extension.';
$lang->install->curl = 'Curl Extension';
$lang->install->curlFail = 'Edit php.ini to load curl extension.';
$lang->install->filter = 'Filter Extension';
$lang->install->filterFail = 'Edit the php.ini file to load filter extension.';
$lang->install->gd = 'GD Extension';
$lang->install->gdFail = 'Edit the php.ini file to load gd extension.';
$lang->install->iconv = 'Iconv Extension';
$lang->install->iconvFail = 'Edit the php.ini file to load iconv extension.';
$lang->install->apcu = 'APCu Extension';
$lang->install->apcuFail = "APCu isn't loaded. IF loaded the APCu, then enable cache function.";
$lang->install->tmpRoot = 'Temp Directory';
$lang->install->dataRoot = 'Uploaded File Directory';
$lang->install->session = 'Session Save Path';
$lang->install->sessionFail = 'Edit the php.ini file to set session.save_path.';
$lang->install->mkdirWin = '<p>%s directory has to be created.<br /> Run <code>mkdir %s</code> to create it.</p>';
$lang->install->chmodWin = ' "%s" privilege has to be changed.';
$lang->install->mkdirLinux = '<p>%s directory has to be created.<br /> Run <code>mkdir -p %s</code> to create it.</p>';
$lang->install->chmodLinux = ' "%s" permison has to be changed.<br /> Run <code>chmod 777 -R %s</code> to change it.';
$lang->install->duckdb = 'DuckDB Engine';
$lang->install->duckdbLoading = 'Installing, please wait...';
$lang->install->duckdbFail = 'The duckdb engine failed to install. Please refer to the manual for manual installation.';
$lang->install->installDuckdb = 'Installing DuckDB';
$lang->install->installingDuckdb = 'Installing duckdb engine...';
$lang->install->installedDuckdb = 'Duckdb engine installed successfully!';
$lang->install->installingExtension = 'Installing the duckdb Engine extension [%s]...';
$lang->install->installedExtension = 'Duckdb Engine extension [%s] installed!';
$lang->install->installedFail = '[%s] Installation failed';
$lang->install->timezone = 'Set Timezone';
$lang->install->defaultLang = 'Default Language';
+40 -30
View File
@@ -90,36 +90,46 @@ $lang->install->action = 'Action';
$lang->install->phpVersion = 'PHP Version';
$lang->install->phpFail = 'PHP Version devrait être 5.2.0 ou plus';
$lang->install->pdo = 'PDO';
$lang->install->pdoFail = 'Editez php.ini pour charger PDO extension.';
$lang->install->pdoMySQL = 'PDO_MySQL';
$lang->install->pdoMySQLFail = 'Editez php.ini pour charger PDO_MySQL extension.';
$lang->install->json = 'JSON Extension';
$lang->install->jsonFail = 'Editez php.ini pour charger JSON extension.';
$lang->install->openssl = 'OpenSSL Extension';
$lang->install->opensslFail = 'Editez php.ini pour charger openssl extension.';
$lang->install->mbstring = 'Mbstring Extension';
$lang->install->mbstringFail = 'Editez php.ini pour charger mbstring extension.';
$lang->install->zlib = 'Zlib Extension';
$lang->install->zlibFail = 'Editez php.ini pour charger zlib extension.';
$lang->install->curl = 'Curl Extension';
$lang->install->curlFail = 'Editez php.ini pour charger curl extension.';
$lang->install->filter = 'Filter Extension';
$lang->install->filterFail = 'Editez le fichier php.ini pour charger filter extension.';
$lang->install->gd = 'GD Extension';
$lang->install->gdFail = 'Editez le fichier php.ini pour charger gd extension.';
$lang->install->iconv = 'Iconv Extension';
$lang->install->iconvFail = 'Editez le fichier php.ini pour charger iconv extension.';
$lang->install->apcu = 'APCu Extension';
$lang->install->apcuFail = "APCu n'est pas chargé. Si APCu est chargé, la fonction de mise en cache est activée.";
$lang->install->tmpRoot = 'Répertoire Temporaire';
$lang->install->dataRoot = "Répertoire d'Upload";
$lang->install->session = "Chemin d'enregistrement des sessions";
$lang->install->sessionFail = 'Editez le fichier php.ini pour définier session.save_path.';
$lang->install->mkdirWin = '<p>Le répertoire %s doit être créé.<br /> Exécutez <code>mkdir %s</code> pour le créer.</p>';
$lang->install->chmodWin = ' "%s" privilege has to be changed.';
$lang->install->mkdirLinux = '<p>Le répertoire %s doit être créé.<br /> Exécutez <code>mkdir -p %s</code> pour le créer.</p>';
$lang->install->chmodLinux = 'Les permission de "%s" doivent être modifiées.<br /> Exécutez <code>chmod 777 -R %s</code> pour les changer.';
$lang->install->pdo = 'PDO';
$lang->install->pdoFail = 'Editez php.ini pour charger PDO extension.';
$lang->install->pdoMySQL = 'PDO_MySQL';
$lang->install->pdoMySQLFail = 'Editez php.ini pour charger PDO_MySQL extension.';
$lang->install->json = 'JSON Extension';
$lang->install->jsonFail = 'Editez php.ini pour charger JSON extension.';
$lang->install->openssl = 'OpenSSL Extension';
$lang->install->opensslFail = 'Editez php.ini pour charger openssl extension.';
$lang->install->mbstring = 'Mbstring Extension';
$lang->install->mbstringFail = 'Editez php.ini pour charger mbstring extension.';
$lang->install->zlib = 'Zlib Extension';
$lang->install->zlibFail = 'Editez php.ini pour charger zlib extension.';
$lang->install->curl = 'Curl Extension';
$lang->install->curlFail = 'Editez php.ini pour charger curl extension.';
$lang->install->filter = 'Filter Extension';
$lang->install->filterFail = 'Editez le fichier php.ini pour charger filter extension.';
$lang->install->gd = 'GD Extension';
$lang->install->gdFail = 'Editez le fichier php.ini pour charger gd extension.';
$lang->install->iconv = 'Iconv Extension';
$lang->install->iconvFail = 'Editez le fichier php.ini pour charger iconv extension.';
$lang->install->apcu = 'APCu Extension';
$lang->install->apcuFail = "APCu n'est pas chargé. Si APCu est chargé, la fonction de mise en cache est activée.";
$lang->install->tmpRoot = 'Répertoire Temporaire';
$lang->install->dataRoot = "Répertoire d'Upload";
$lang->install->session = "Chemin d'enregistrement des sessions";
$lang->install->sessionFail = 'Editez le fichier php.ini pour définier session.save_path.';
$lang->install->mkdirWin = '<p>Le répertoire %s doit être créé.<br /> Exécutez <code>mkdir %s</code> pour le créer.</p>';
$lang->install->chmodWin = ' "%s" privilege has to be changed.';
$lang->install->mkdirLinux = '<p>Le répertoire %s doit être créé.<br /> Exécutez <code>mkdir -p %s</code> pour le créer.</p>';
$lang->install->chmodLinux = 'Les permission de "%s" doivent être modifiées.<br /> Exécutez <code>chmod 777 -R %s</code> pour les changer.';
$lang->install->duckdb = 'DuckDB Engine';
$lang->install->duckdbLoading = 'Installing, please wait...';
$lang->install->duckdbFail = 'The duckdb engine failed to install. Please refer to the manual for manual installation.';
$lang->install->installDuckdb = 'Installing DuckDB';
$lang->install->installingDuckdb = 'Installing duckdb engine...';
$lang->install->installedDuckdb = 'Duckdb engine installed successfully!';
$lang->install->installingExtension = 'Installing the duckdb Engine extension [%s]...';
$lang->install->installedExtension = 'Duckdb Engine extension [%s] installed!';
$lang->install->installedFail = '[%s] Installation failed';
$lang->install->timezone = 'Set Timezone';
$lang->install->defaultLang = 'Langue par défaut';
+40 -30
View File
@@ -90,36 +90,46 @@ $lang->install->action = '如何修改';
$lang->install->phpVersion = 'PHP版本';
$lang->install->phpFail = 'PHP版本必须大于5.2.0';
$lang->install->pdo = 'PDO扩展';
$lang->install->pdoFail = '修改PHP配置文件,加载PDO扩展。';
$lang->install->pdoMySQL = 'PDO_MySQL扩展';
$lang->install->pdoMySQLFail = '修改PHP配置文件,加载pdo_mysql扩展。';
$lang->install->json = 'JSON扩展';
$lang->install->jsonFail = '修改PHP配置文件,加载JSON扩展。';
$lang->install->openssl = 'OPENSSL扩展';
$lang->install->opensslFail = '修改PHP配置文件,加载OPENSSL扩展。';
$lang->install->mbstring = 'MBSTRING扩展';
$lang->install->mbstringFail = '修改PHP配置文件,加载MBSTRING扩展。';
$lang->install->zlib = 'ZLIB扩展';
$lang->install->zlibFail = '修改PHP配置文件,加载ZLIB扩展。';
$lang->install->curl = 'CURL扩展';
$lang->install->curlFail = '修改PHP配置文件,加载CURL扩展。';
$lang->install->filter = 'FILTER扩展';
$lang->install->filterFail = '修改PHP配置文件,加载FILTER扩展。';
$lang->install->gd = 'GD扩展';
$lang->install->gdFail = '修改PHP配置文件,加载GD扩展。';
$lang->install->iconv = 'ICONV扩展';
$lang->install->iconvFail = '修改PHP配置文件,加载ICONV扩展。';
$lang->install->apcu = 'APCu扩展';
$lang->install->apcuFail = '未加载APCu扩展。加载APCu扩展后,可以开启缓存功能。';
$lang->install->tmpRoot = '临时文件目录';
$lang->install->dataRoot = '上传文件目录';
$lang->install->session = 'Session存储目录';
$lang->install->sessionFail = '修改PHP配置文件,设置session.save_path。<br />如果使用宝塔面板,可以到宝塔Web面板中“软件商店”,打开PHP设置,到“Session配置”项,选择files,点击保存。老版本需要修改php配置文件。';
$lang->install->mkdirWin = '<p>需要创建目录%s。命令为:<br /> mkdir %s</p>';
$lang->install->chmodWin = '需要修改目录 "%s" 的权限。';
$lang->install->mkdirLinux = '<p>需要创建目录%s。<br /> 命令为:<br /> mkdir -p %s</p>';
$lang->install->chmodLinux = '需要修改目录 "%s" 的权限。<br />命令为:<br />chmod 777 -R %s';
$lang->install->pdo = 'PDO扩展';
$lang->install->pdoFail = '修改PHP配置文件,加载PDO扩展。';
$lang->install->pdoMySQL = 'PDO_MySQL扩展';
$lang->install->pdoMySQLFail = '修改PHP配置文件,加载pdo_mysql扩展。';
$lang->install->json = 'JSON扩展';
$lang->install->jsonFail = '修改PHP配置文件,加载JSON扩展。';
$lang->install->openssl = 'OPENSSL扩展';
$lang->install->opensslFail = '修改PHP配置文件,加载OPENSSL扩展。';
$lang->install->mbstring = 'MBSTRING扩展';
$lang->install->mbstringFail = '修改PHP配置文件,加载MBSTRING扩展。';
$lang->install->zlib = 'ZLIB扩展';
$lang->install->zlibFail = '修改PHP配置文件,加载ZLIB扩展。';
$lang->install->curl = 'CURL扩展';
$lang->install->curlFail = '修改PHP配置文件,加载CURL扩展。';
$lang->install->filter = 'FILTER扩展';
$lang->install->filterFail = '修改PHP配置文件,加载FILTER扩展。';
$lang->install->gd = 'GD扩展';
$lang->install->gdFail = '修改PHP配置文件,加载GD扩展。';
$lang->install->iconv = 'ICONV扩展';
$lang->install->iconvFail = '修改PHP配置文件,加载ICONV扩展。';
$lang->install->apcu = 'APCu扩展';
$lang->install->apcuFail = '未加载APCu扩展。加载APCu扩展后,可以开启缓存功能。';
$lang->install->tmpRoot = '临时文件目录';
$lang->install->dataRoot = '上传文件目录';
$lang->install->session = 'Session存储目录';
$lang->install->sessionFail = '修改PHP配置文件,设置session.save_path。<br />如果使用宝塔面板,可以到宝塔Web面板中“软件商店”,打开PHP设置,到“Session配置”项,选择files,点击保存。老版本需要修改php配置文件。';
$lang->install->mkdirWin = '<p>需要创建目录%s。命令为:<br /> mkdir %s</p>';
$lang->install->chmodWin = '需要修改目录 "%s" 的权限。';
$lang->install->mkdirLinux = '<p>需要创建目录%s。<br /> 命令为:<br /> mkdir -p %s</p>';
$lang->install->chmodLinux = '需要修改目录 "%s" 的权限。<br />命令为:<br />chmod 777 -R %s';
$lang->install->duckdb = 'DuckDB引擎';
$lang->install->duckdbLoading = '正在安装中,请稍后...';
$lang->install->duckdbFail = '安装duckdb引擎失败,请参考手册进行手动安装。';
$lang->install->installDuckdb = '安装DuckDB';
$lang->install->installingDuckdb = '正在安装duckdb引擎...';
$lang->install->installedDuckdb = '安装duckdb引擎成功!';
$lang->install->installingExtension = '正在安装duckdb引擎扩展[%s]...';
$lang->install->installedExtension = '安装duckdb引擎扩展[%s]成功!';
$lang->install->installedFail = '[%s]安装失败';
$lang->install->timezone = '时区设置';
$lang->install->defaultLang = '默认语言';
+20 -6
View File
@@ -232,7 +232,7 @@ class installModel extends model
$admin->password = md5($data->password);
$admin->gender = 'f';
$admin->visions = $visions;
$this->dao->replace(TABLE_USER)->data($admin)->exec();
$this->dao->insert(TABLE_USER)->data($admin)->exec();
return !dao::isError();
}
@@ -385,12 +385,18 @@ class installModel extends model
$this->loadModel('bi');
/* Prepare built-in sqls of bi. */
$chartSQLs = $this->bi->prepareBuiltinChartSQL();
$pivotSQLs = $this->bi->prepareBuiltinPivotSQL();
$metricSQLs = $this->bi->prepareBuiltinMetricSQL();
$screenSQLs = $this->bi->prepareBuiltinScreenSQL();
$insertTables = array_merge($chartSQLs, $pivotSQLs, $metricSQLs, $screenSQLs);
$insertTables = array();
if($this->config->db->driver == 'mysql')
{
$chartSQLs = $this->bi->prepareBuiltinChartSQL();
$pivotSQLs = $this->bi->prepareBuiltinPivotSQL();
$insertTables = array_merge($insertTables, $chartSQLs, $pivotSQLs);
}
$metricSQLs = $this->bi->prepareBuiltinMetricSQL();
$screenSQLs = $this->bi->prepareBuiltinScreenSQL();
$insertTables = array_merge($insertTables, $metricSQLs, $screenSQLs);
try
{
@@ -409,6 +415,14 @@ class installModel extends model
die;
}
/* Generate parquet file. */
$result = $this->bi->generateParquetFile();
if($result !== true)
{
a($result);
die;
}
return true;
}
+2 -1
View File
@@ -51,7 +51,8 @@ r($tester->install->fetchByID(1, 'stage')) && p('id,name,type') && e('1,需求,r
r($tester->install->fetchByID(1, 'lang')) && p('id,value') && e('1,支持过程'); // 检查lang表的语言项是否变更成对应的中文语言项。
r($tester->install->fetchByID(1, 'cron')) && p('id,remark') && e('1,更新燃尽图'); // 检查cron表的语言项是否变更成对应的中文语言项。
$app->clientLang = 'en';
$app::$loadedLangs = array();
$app->clientLang = 'en';
$app->loadLang('install');
$tester->install->updateLang();
r($tester->install->fetchByID(1, 'stage')) && p('id,name,type') && e('1,Story,request'); // 检查stage表的语言项是否变更成对应的英文语言项。
+85
View File
@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
/**
* The install duckdb view file of install 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 Yanyi Cao <caoyanyi@easycorp.ltd>
* @package install
* @link https://www.zentao.net
*/
namespace zin;
$icons = array
(
'loading' => 'spinner-indicator',
'ok' => 'check-circle',
'fail' => 'close'
);
$iconClass = array
(
'loading' => 'spin',
'ok' => 'text-success',
'fail' => 'text-danger'
);
$duckdbLang = array
(
'loading' => $lang->install->installingDuckdb,
'ok' => $lang->install->installedDuckdb,
'fail' => $lang->install->installedFail,
);
$extLang = array
(
'loading' => $lang->install->installingExtension,
'ok' => $lang->install->installedExtension,
'fail' => $lang->install->installedFail,
);
$fnGenerateInfo = function($type, $stus, $show = false) use ($lang, $icons, $iconClass, $duckdbLang, $extLang)
{
return p
(
setClass("$type-$stus" . ($show ? '' : ' hidden')),
icon
(
setClass($iconClass[$stus]),
$icons[$stus]
),
$type == 'duckdb' ? sprintf($duckdbLang[$stus], $lang->install->duckdb) : null,
$type == 'ext_dm' || $type == 'ext_mysql' ? sprintf($extLang[$stus], $type) : null,
);
};
$fnGenerateItems = function() use ($icons, $fnGenerateInfo, $duckdb, $ext_dm, $ext_mysql)
{
$items = array();
foreach(array_keys($icons) as $stus)
{
$items[] = $fnGenerateInfo('duckdb', $stus, $stus == $duckdb);
$items[] = $fnGenerateInfo('ext_dm', $stus, $stus == $ext_dm);
$items[] = $fnGenerateInfo('ext_mysql', $stus, $stus == $ext_mysql);
}
return $items;
};
div
(
setID('installDuckdb'),
$fnGenerateItems(),
span
(
setClass('help text-warning hidden'),
$lang->install->duckdbFail,
btn
(
set::type('link'),
set::url($config->bi->duckdbHelp),
set::target('blank'),
$config->bi->duckdbHelp
)
)
);
+27 -1
View File
@@ -65,6 +65,32 @@ foreach($dirItems as $dirItem)
);
}
/* Check duckdb bin. */
$checkTrs[] = h::tr
(
h::th($lang->install->duckdb),
h::td($duckdbResult == 'ok' ? $lang->install->loaded : $lang->install->unloaded),
h::td
(
setClass('duckdbResult text-white' . ($duckdbResult == 'ok' ? ' bg-success' : ' bg-danger')),
$lang->install->{$duckdbResult}
),
h::td
(
setClass('text-left'),
$duckdbResult == 'fail' ? modalTrigger
(
btn
(
set::size('sm'),
set::type('primary'),
$lang->install->installDuckdb
),
set::url(createLink('install', 'installDuckdb')),
) : null
)
);
div
(
setID('main'),
@@ -140,7 +166,7 @@ div
&& $tmpRootResult == 'ok' && $dataRootResult == 'ok' && $sessionResult == 'ok'
&& $jsonResult == 'ok' && $opensslResult == 'ok' && $mbstringResult == 'ok'
&& $zlibResult == 'ok' && $curlResult == 'ok' && $filterResult == 'ok'
&& $iconvResult == 'ok' ? btn
&& $iconvResult == 'ok' && $duckdbResult == 'ok' ? btn
(
setClass('px-6 mx-4'),
set::url(inlink('step2')),
+4 -4
View File
@@ -22,9 +22,9 @@ cid=1
- 属性name @卡片1
- 属性status @doing
- 属性progress @50
- 测试激活卡片1,进度100 @请输入正确的进度
- 测试激活卡片1,进度101 @请输入正确的进度
- 测试激活卡片1,进度-1 @请输入正确的进度
- 测试激活卡片1,进度100第progress条的0属性 @请输入正确的进度
- 测试激活卡片1,进度101第progress条的0属性 @请输入正确的进度
- 测试激活卡片1,进度-1第progress条的0属性 @请输入正确的进度
- 测试激活卡片2,进度0
- 属性id @2
- 属性name @卡片2
@@ -61,4 +61,4 @@ r($kanban->activateCardTest($cardIDList[0], $progressList[4])) && p('progress:0'
r($kanban->activateCardTest($cardIDList[1], $progressList[0])) && p('id,name,status,progress') && e('2,卡片2,doing,0'); // 测试激活卡片2,进度0
r($kanban->activateCardTest($cardIDList[2], $progressList[1])) && p('id,name,status,progress') && e('3,卡片3,doing,50'); // 测试激活卡片3,进度0
r($kanban->activateCardTest($cardIDList[3], $progressList[0])) && p('id,name,status,progress') && e('4,卡片4,doing,0'); // 测试激活卡片4,进度0
r($kanban->activateCardTest($cardIDList[4], $progressList[1])) && p('id,name,status,progress') && e('5,卡片5,doing,50'); // 测试激活卡片5,进度0
r($kanban->activateCardTest($cardIDList[4], $progressList[1])) && p('id,name,status,progress') && e('5,卡片5,doing,50'); // 测试激活卡片5,进度0
+11 -11
View File
@@ -47,11 +47,11 @@ cid=1
- 查看复制出来的区域下的分组数量 @5
- 查看复制出来的区域下的分组数量 @5
- 查看复制出来的区域下的分组数量 @5
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的看板列数量 @178
- 查看复制出来的区域下的看板列数量 @178
- 查看复制出来的区域下的看板列数量 @178
@@ -83,14 +83,14 @@ r(count($tester->kanban->getGroupGroupByRegions(array(13)), true)) && p() && e('
r(count($tester->kanban->getGroupGroupByRegions(array(14)), true)) && p() && e('5'); // 查看复制出来的区域下的分组数量
r(count($tester->kanban->getGroupGroupByRegions(array(15)), true)) && p() && e('5'); // 查看复制出来的区域下的分组数量
r(count($tester->kanban->getLaneGroupByRegions(array(11)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(12)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(13)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(14)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(15)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(11)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(12)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(13)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(14)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(15)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getColumnGroupByRegions(array(11)), true)) && p() && e('178'); // 查看复制出来的区域下的看板列数量
r(count($tester->kanban->getColumnGroupByRegions(array(12)), true)) && p() && e('178'); // 查看复制出来的区域下的看板列数量
r(count($tester->kanban->getColumnGroupByRegions(array(13)), true)) && p() && e('178'); // 查看复制出来的区域下的看板列数量
r(count($tester->kanban->getColumnGroupByRegions(array(14)), true)) && p() && e('178'); // 查看复制出来的区域下的看板列数量
r(count($tester->kanban->getColumnGroupByRegions(array(15)), true)) && p() && e('178'); // 查看复制出来的区域下的看板列数量
r(count($tester->kanban->getColumnGroupByRegions(array(15)), true)) && p() && e('178'); // 查看复制出来的区域下的看板列数量
+10 -10
View File
@@ -27,11 +27,11 @@ cid=1
- 查看复制出来的区域下的分组数量 @5
- 查看复制出来的区域下的分组数量 @5
- 查看复制出来的区域下的分组数量 @5
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @26
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的泳道数量 @28
- 查看复制出来的区域下的看板列数量 @178
- 查看复制出来的区域下的看板列数量 @178
- 查看复制出来的区域下的看板列数量 @178
@@ -63,11 +63,11 @@ r(count($tester->kanban->getGroupGroupByRegions(array(8)), true)) && p() && e('
r(count($tester->kanban->getGroupGroupByRegions(array(9)), true)) && p() && e('5'); // 查看复制出来的区域下的分组数量
r(count($tester->kanban->getGroupGroupByRegions(array(10)), true)) && p() && e('5'); // 查看复制出来的区域下的分组数量
r(count($tester->kanban->getLaneGroupByRegions(array(6)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(7)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(8)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(9)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(10)), true)) && p() && e('26'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(6)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(7)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(8)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(9)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getLaneGroupByRegions(array(10)), true)) && p() && e('28'); // 查看复制出来的区域下的泳道数量
r(count($tester->kanban->getColumnGroupByRegions(array(6)), true)) && p() && e('178'); // 查看复制出来的区域下的看板列数量
r(count($tester->kanban->getColumnGroupByRegions(array(7)), true)) && p() && e('178'); // 查看复制出来的区域下的看板列数量
+1 -1
View File
@@ -95,4 +95,4 @@ r($kanban->createTest($kanban2)) && p('name,space,owner,whitelist') && e('测试
r($kanban->createTest($kanban3)) && p('name,space,owner,whitelist') && e('测试创建公共看板,3,po17,~~'); // 创建公共空间
r($kanban->createTest($kanban4)) && p('name:0') && e('『看板名称』不能为空。'); // 创建没有名字的公共空间
r($kanban->createTest($kanban5)) && p('space:0') && e('『所属空间』不能为空。'); // 创建没有空间公共空间
r($kanban->createTest($kanban6)) && p('name:0') && e('『看板名称』已经有『测试创建公共看板』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。'); // 创建重名公共空间
r($kanban->createTest($kanban6)) && p('name:0') && e('『看板名称』已经有『测试创建公共看板』这条记录了。如果您确定该记录已删除,请到后台-系统设置-回收站还原。'); // 创建重名公共空间
@@ -12,19 +12,19 @@ title=测试 kanbanModel->createExecutionColumns();
timeout=0
cid=1
- 创建泳道 100001 执行101 需求的看板列 @11
- 创建泳道 100001 执行101 需求的看板列 @15
- 创建泳道 100001 执行101 任务的看板列 @7
- 创建泳道 100001 执行101 bug 的看板列 @9
- 创建泳道 100002 执行102 需求的看板列 @11
- 创建泳道 100002 执行102 需求的看板列 @15
- 创建泳道 100002 执行102 任务的看板列 @7
- 创建泳道 100002 执行102 bug 的看板列 @14
- 创建泳道 100003 执行103 需求的看板列 @11
- 创建泳道 100003 执行103 需求的看板列 @15
- 创建泳道 100003 执行103 任务的看板列 @7
- 创建泳道 100003 执行103 bug 的看板列 @9
- 创建泳道 100004 执行104 需求的看板列 @11
- 创建泳道 100004 执行104 需求的看板列 @15
- 创建泳道 100004 执行104 任务的看板列 @7
- 创建泳道 100004 执行104 bug 的看板列 @9
- 创建泳道 100005 执行105 需求的看板列 @11
- 创建泳道 100005 执行105 需求的看板列 @15
- 创建泳道 100005 执行105 任务的看板列 @7
- 创建泳道 100005 执行105 bug 的看板列 @9
@@ -36,18 +36,18 @@ $typeList = array('story', 'task', 'bug');
$kanban = new kanbanTest();
r($kanban->createExecutionColumnsTest($laneIDList[0], $typeList[0], $executionIDList[0])) && p() && e('11'); // 创建泳道 100001 执行101 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[0], $typeList[0], $executionIDList[0])) && p() && e('15'); // 创建泳道 100001 执行101 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[0], $typeList[1], $executionIDList[0])) && p() && e('7'); // 创建泳道 100001 执行101 任务的看板列
r($kanban->createExecutionColumnsTest($laneIDList[0], $typeList[2], $executionIDList[0])) && p() && e('9'); // 创建泳道 100001 执行101 bug 的看板列
r($kanban->createExecutionColumnsTest($laneIDList[1], $typeList[0], $executionIDList[1])) && p() && e('11'); // 创建泳道 100002 执行102 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[1], $typeList[0], $executionIDList[1])) && p() && e('15'); // 创建泳道 100002 执行102 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[1], $typeList[1], $executionIDList[1])) && p() && e('7'); // 创建泳道 100002 执行102 任务的看板列
r($kanban->createExecutionColumnsTest($laneIDList[1], $typeList[1], $executionIDList[1])) && p() && e('14'); // 创建泳道 100002 执行102 bug 的看板列
r($kanban->createExecutionColumnsTest($laneIDList[2], $typeList[0], $executionIDList[2])) && p() && e('11'); // 创建泳道 100003 执行103 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[2], $typeList[0], $executionIDList[2])) && p() && e('15'); // 创建泳道 100003 执行103 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[2], $typeList[1], $executionIDList[2])) && p() && e('7'); // 创建泳道 100003 执行103 任务的看板列
r($kanban->createExecutionColumnsTest($laneIDList[2], $typeList[2], $executionIDList[2])) && p() && e('9'); // 创建泳道 100003 执行103 bug 的看板列
r($kanban->createExecutionColumnsTest($laneIDList[3], $typeList[0], $executionIDList[3])) && p() && e('11'); // 创建泳道 100004 执行104 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[3], $typeList[0], $executionIDList[3])) && p() && e('15'); // 创建泳道 100004 执行104 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[3], $typeList[1], $executionIDList[3])) && p() && e('7'); // 创建泳道 100004 执行104 任务的看板列
r($kanban->createExecutionColumnsTest($laneIDList[3], $typeList[2], $executionIDList[3])) && p() && e('9'); // 创建泳道 100004 执行104 bug 的看板列
r($kanban->createExecutionColumnsTest($laneIDList[4], $typeList[0], $executionIDList[4])) && p() && e('11'); // 创建泳道 100005 执行105 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[4], $typeList[0], $executionIDList[4])) && p() && e('15'); // 创建泳道 100005 执行105 需求的看板列
r($kanban->createExecutionColumnsTest($laneIDList[4], $typeList[1], $executionIDList[4])) && p() && e('7'); // 创建泳道 100005 执行105 任务的看板列
r($kanban->createExecutionColumnsTest($laneIDList[4], $typeList[2], $executionIDList[4])) && p() && e('9'); // 创建泳道 100005 执行105 bug 的看板列
@@ -12,15 +12,15 @@ title=测试 kanbanModel->createExecutionLane();
timeout=0
cid=1
- 创建执行101的泳道 @3
- 创建执行101的泳道 @4
- 创建执行101 需求的泳道 @2
- 创建执行101的泳道 @3
- 创建执行101的泳道 @4
- 创建执行102 需求 按来源分组的泳道 @2
- 创建执行102 任务 按优先级分组的泳道 @3
- 创建执行103的泳道 @3
- 创建执行102 任务 按优先级分组的泳道 @5
- 创建执行103的泳道 @4
- 创建执行103 bug 按严重程度分组的泳道 @2
- 创建执行104的泳道 @3
- 创建执行105的泳道 @3
- 创建执行104的泳道 @4
- 创建执行105的泳道 @4
*/
@@ -29,12 +29,12 @@ $typeIDList = array('all', 'story', 'task', 'bug');
$kanban = new kanbanTest();
r($kanban->createExecutionLaneTest($executionIDList[0])) && p() && e('3'); // 创建执行101的泳道
r($kanban->createExecutionLaneTest($executionIDList[0])) && p() && e('4'); // 创建执行101的泳道
r($kanban->createExecutionLaneTest($executionIDList[0], $typeIDList[1])) && p() && e('2'); // 创建执行101 需求的泳道
r($kanban->createExecutionLaneTest($executionIDList[1])) && p() && e('3'); // 创建执行101的泳道
r($kanban->createExecutionLaneTest($executionIDList[1])) && p() && e('4'); // 创建执行101的泳道
r($kanban->createExecutionLaneTest($executionIDList[1], $typeIDList[1])) && p() && e('2'); // 创建执行102 需求 按来源分组的泳道
r($kanban->createExecutionLaneTest($executionIDList[1], $typeIDList[2])) && p() && e('3'); // 创建执行102 任务 按优先级分组的泳道
r($kanban->createExecutionLaneTest($executionIDList[2])) && p() && e('3'); // 创建执行103的泳道
r($kanban->createExecutionLaneTest($executionIDList[1], $typeIDList[2])) && p() && e('2'); // 创建执行102 任务 按优先级分组的泳道
r($kanban->createExecutionLaneTest($executionIDList[2])) && p() && e('4'); // 创建执行103的泳道
r($kanban->createExecutionLaneTest($executionIDList[2], $typeIDList[3])) && p() && e('2'); // 创建执行103 bug 按严重程度分组的泳道
r($kanban->createExecutionLaneTest($executionIDList[3])) && p() && e('3'); // 创建执行104的泳道
r($kanban->createExecutionLaneTest($executionIDList[4])) && p() && e('3'); // 创建执行105的泳道
r($kanban->createExecutionLaneTest($executionIDList[3])) && p() && e('4'); // 创建执行104的泳道
r($kanban->createExecutionLaneTest($executionIDList[4])) && p() && e('4'); // 创建执行105的泳道
+4 -4
View File
@@ -14,10 +14,10 @@ title=测试 kanbanModel->createRDColumn();
timeout=0
cid=1
- 测试创建执行161 区域101 泳道组101 泳道101 story执行看板的泳道列 @11
- 测试创建执行161 区域101 泳道组101 泳道101 story执行看板的泳道列 @15
- 测试创建执行161 区域101 泳道组102 泳道102 bug执行看板的泳道列 @9
- 测试创建执行161 区域101 泳道组103 泳道103 task执行看板的泳道列 @7
- 测试创建执行162 区域102 泳道组104 泳道104 story执行看板的泳道列 @11
- 测试创建执行162 区域102 泳道组104 泳道104 story执行看板的泳道列 @15
- 测试创建执行162 区域102 泳道组105 泳道105 bug执行看板的泳道列 @9
- 测试创建执行162 区域102 泳道组106 泳道106 task执行看板的泳道列 @7
@@ -31,9 +31,9 @@ $executionIDList = array('161', '162');
$kanban = new kanbanTest();
r($kanban->createRDColumnTest($regionIDList[0], $groupIDList[0], $laneIDList[0], $laneTypeList[0], $executionIDList[0])) && p() && e('11'); // 测试创建执行161 区域101 泳道组101 泳道101 story执行看板的泳道列
r($kanban->createRDColumnTest($regionIDList[0], $groupIDList[0], $laneIDList[0], $laneTypeList[0], $executionIDList[0])) && p() && e('15'); // 测试创建执行161 区域101 泳道组101 泳道101 story执行看板的泳道列
r($kanban->createRDColumnTest($regionIDList[0], $groupIDList[1], $laneIDList[1], $laneTypeList[1], $executionIDList[0])) && p() && e('9'); // 测试创建执行161 区域101 泳道组102 泳道102 bug执行看板的泳道列
r($kanban->createRDColumnTest($regionIDList[0], $groupIDList[2], $laneIDList[2], $laneTypeList[2], $executionIDList[0])) && p() && e('7'); // 测试创建执行161 区域101 泳道组103 泳道103 task执行看板的泳道列
r($kanban->createRDColumnTest($regionIDList[1], $groupIDList[3], $laneIDList[3], $laneTypeList[0], $executionIDList[1])) && p() && e('11'); // 测试创建执行162 区域102 泳道组104 泳道104 story执行看板的泳道列
r($kanban->createRDColumnTest($regionIDList[1], $groupIDList[3], $laneIDList[3], $laneTypeList[0], $executionIDList[1])) && p() && e('15'); // 测试创建执行162 区域102 泳道组104 泳道104 story执行看板的泳道列
r($kanban->createRDColumnTest($regionIDList[1], $groupIDList[4], $laneIDList[4], $laneTypeList[1], $executionIDList[1])) && p() && e('9'); // 测试创建执行162 区域102 泳道组105 泳道105 bug执行看板的泳道列
r($kanban->createRDColumnTest($regionIDList[1], $groupIDList[5], $laneIDList[5], $laneTypeList[2], $executionIDList[1])) && p() && e('7'); // 测试创建执行162 区域102 泳道组106 泳道106 task执行看板的泳道列
+16 -15
View File
@@ -4,6 +4,7 @@ include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/lib/kanban.unittest.class.php';
su('admin');
zenData('project')->loadYaml('execution')->gen(105);
zenData('kanbanregion')->gen(0);
zenData('kanbancolumn')->gen(0);
zenData('kanbanlane')->gen(0);
@@ -14,37 +15,37 @@ title=测试 kanbanModel->createRDKanban();
timeout=0
cid=1
- 测试创建执行10001的执行看板 @1,3,27
- 测试创建执行101的执行看板 @1,4,38
- 测试创建执行10002的执行看板 @1,3,27
- 测试创建执行102的执行看板 @1,4,38
- 测试创建执行10003的执行看板 @1,3,27
- 测试创建执行103的执行看板 @1,4,38
- 测试创建执行10004的执行看板 @1,3,27
- 测试创建执行104的执行看板 @1,4,38
- 测试创建执行10005的执行看板 @1,3,27
- 测试创建执行105的执行看板 @1,4,38
*/
$execution1 = new stdclass();
$execution1->id = '100001';
$execution1->id = '101';
$execution2 = new stdclass();
$execution2->id = '100002';
$execution2->id = '102';
$execution3 = new stdclass();
$execution3->id = '100003';
$execution3->id = '103';
$execution4 = new stdclass();
$execution4->id = '100004';
$execution4->id = '104';
$execution5 = new stdclass();
$execution5->id = '100005';
$execution5->id = '105';
$kanban = new kanbanTest();
r($kanban->createRDKanbanTest($execution1)) && p() && e('1,3,27'); // 测试创建执行10001的执行看板
r($kanban->createRDKanbanTest($execution2)) && p() && e('1,3,27'); // 测试创建执行10002的执行看板
r($kanban->createRDKanbanTest($execution3)) && p() && e('1,3,27'); // 测试创建执行10003的执行看板
r($kanban->createRDKanbanTest($execution4)) && p() && e('1,3,27'); // 测试创建执行10004的执行看板
r($kanban->createRDKanbanTest($execution5)) && p() && e('1,3,27'); // 测试创建执行10005的执行看板
r($kanban->createRDKanbanTest($execution1)) && p() && e('1,4,38'); // 测试创建执行101的执行看板
r($kanban->createRDKanbanTest($execution2)) && p() && e('1,4,38'); // 测试创建执行102的执行看板
r($kanban->createRDKanbanTest($execution3)) && p() && e('1,4,38'); // 测试创建执行103的执行看板
r($kanban->createRDKanbanTest($execution4)) && p() && e('1,4,38'); // 测试创建执行104的执行看板
r($kanban->createRDKanbanTest($execution5)) && p() && e('1,4,38'); // 测试创建执行105的执行看板
+6 -5
View File
@@ -4,6 +4,7 @@ include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/lib/kanban.unittest.class.php';
su('admin');
zenData('project')->loadYaml('execution')->gen(180);
zenData('kanbanregion')->gen(0);
zenData('kanbancolumn')->gen(0);
zenData('kanbanlane')->gen(0);
@@ -27,8 +28,8 @@ $regionIDList = array('101', '102', '103', '104', '105');
$kanban = new kanbanTest();
r($kanban->createRDLaneTest($executionIDList[0], $regionIDList[0])) && p() && e('3'); // 测试创建执行101的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[1], $regionIDList[1])) && p() && e('3'); // 测试创建执行102的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[2], $regionIDList[2])) && p() && e('3'); // 测试创建执行103的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[3], $regionIDList[3])) && p() && e('3'); // 测试创建执行104的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[4], $regionIDList[4])) && p() && e('3'); // 测试创建执行105的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[0], $regionIDList[0])) && p() && e('4'); // 测试创建执行101的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[1], $regionIDList[1])) && p() && e('4'); // 测试创建执行102的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[2], $regionIDList[2])) && p() && e('4'); // 测试创建执行103的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[3], $regionIDList[3])) && p() && e('4'); // 测试创建执行104的执行看板的泳道
r($kanban->createRDLaneTest($executionIDList[4], $regionIDList[4])) && p() && e('4'); // 测试创建执行105的执行看板的泳道
@@ -53,8 +53,8 @@ r($kanban->getExecutionKanbanTest($executionIDList[0], $browseTypeList[0]))
r($kanban->getExecutionKanbanTest($executionIDList[0], $browseTypeList[1])) && p() && e('columns:0, lanes:0, cards:0'); // 测试获取execution2 task的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[0], $browseTypeList[2])) && p() && e('columns:10, lanes:1, cards:0'); // 测试获取execution2 bug的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[1])) && p() && e('columns:0, lanes:0, cards:0'); // 测试获取execution3的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[1], $browseTypeList[0], $groupByList[0])) && p() && e('columns:0, lanes:1, cards:0'); // 测试获取execution3 story pri的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[1], $browseTypeList[0], $groupByList[1])) && p() && e('columns:0, lanes:1, cards:0'); // 测试获取execution3 story category的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[1], $browseTypeList[0], $groupByList[0])) && p() && e('columns:0, lanes:0, cards:0'); // 测试获取execution3 story pri的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[1], $browseTypeList[0], $groupByList[1])) && p() && e('columns:0, lanes:0, cards:0'); // 测试获取execution3 story category的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[2])) && p() && e('columns:0, lanes:0, cards:0'); // 测试获取execution4的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[2], $browseTypeList[1], $groupByList[2])) && p() && e('columns:2, lanes:1, cards:109'); // 测试获取execution4 task module的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[2], $browseTypeList[0], $groupByList[3])) && p() && e('columns:0, lanes:1, cards:0'); // 测试获取execution4 story source的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[2], $browseTypeList[1], $groupByList[2])) && p() && e('columns:0, lanes:0, cards:0'); // 测试获取execution4 task module的执行看板信息
r($kanban->getExecutionKanbanTest($executionIDList[2], $browseTypeList[0], $groupByList[3])) && p() && e('columns:0, lanes:0, cards:0'); // 测试获取execution4 story source的执行看板信息
+10 -10
View File
@@ -31,11 +31,11 @@ cid=1
- 查看获取到的kanban5下的泳道数据
- 第0条的title属性 @默认泳道
- 第0条的type属性 @common
- 测试获取到的kanban下的数据数量 @482
- 测试获取到的kanban下的数据数量 @482
- 测试获取到的kanban下的数据数量 @482
- 测试获取到的kanban下的数据数量 @482
- 测试获取到的kanban下的数据数量 @482
- 测试获取到的kanban下的数据数量 @491
- 测试获取到的kanban下的数据数量 @491
- 测试获取到的kanban下的数据数量 @491
- 测试获取到的kanban下的数据数量 @491
- 测试获取到的kanban下的数据数量 @491
*/
@@ -48,8 +48,8 @@ r($tester->kanban->getKanbanData(3)[0]['key']) && p('')
r($tester->kanban->getKanbanData(4)[0]['items'][0]['data']['cols']) && p('0:title,type') && e('未开始,column13'); // 查看获取到的kanban4下的列数据
r($tester->kanban->getKanbanData(5)[0]['items'][0]['data']['lanes']) && p('0:title,type') && e('默认泳道,common'); // 查看获取到的kanban5下的泳道数据
r(count($tester->kanban->getKanbanData(1), true)) && p() && e('482'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(2), true)) && p() && e('482'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(3), true)) && p() && e('482'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(4), true)) && p() && e('482'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(5), true)) && p() && e('482'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(1), true)) && p() && e('491'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(2), true)) && p() && e('491'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(3), true)) && p() && e('491'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(4), true)) && p() && e('491'); // 测试获取到的kanban下的数据数量
r(count($tester->kanban->getKanbanData(5), true)) && p() && e('491'); // 测试获取到的kanban下的数据数量
+3 -3
View File
@@ -11,8 +11,8 @@ title=测试 kanbanModel->getPageToolBar();
timeout=0
cid=1
- 查看普通用户获取操作按钮的长度 @161
- 查看管理员获取操作按钮的长度 @1282
- 查看普通用户获取操作按钮的字符长度 @161
- 查看管理员获取操作按钮的字符长度 @1282
*/
global $tester;
@@ -26,4 +26,4 @@ su('admin');
$toolbar2 = $tester->kanban->getPageToolBar($kanban2);
r(strlen($toolbar1)) && p('') && e('161'); // 查看普通用户获取操作按钮的字符长度
r(strlen($toolbar2)) && p('') && e('1282'); // 查看管理员获取操作按钮的字符长度
r(strlen($toolbar2)) && p('') && e('1282'); // 查看管理员获取操作按钮的字符长度
+2 -2
View File
@@ -15,7 +15,7 @@ timeout=0
cid=1
- 测试获取产品1的计划看板
- 第0条的name属性 @all
- 第0条的name属性 @laneall
- 第0条的title属性 @所有
- 测试获取产品2的计划看板
- 第1条的name属性 @5
@@ -41,4 +41,4 @@ r($kanban->getPlanKanbanTest($productIDList[0])[0]['items'][0]['data']['lanes'])
r($kanban->getPlanKanbanTest($productIDList[1])[0]['items'][0]['data']['items']['laneall']['wait']) && p('1:name,title') && e('5,1.1'); // 测试获取产品2的计划看板
r($kanban->getPlanKanbanTest($productIDList[2])[0]['items'][0]['data']['cols']) && p('0:name,title') && e('wait,未开始'); // 测试获取产品3的计划看板
r($kanban->getPlanKanbanTest($productIDList[0], $branchIDList[1])[0]['items'][0]['data']['cols']) && p('0:name,title') && e('wait,未开始'); // 测试获取产品1的计划看板,传入分支
r($kanban->getPlanKanbanTest($productIDList[1], $branchIDList[2])[0]['items'][0]['data']['cols']) && p('0:name,title') && e('wait,未开始'); // 测试获取产品2的计划看板,传入分支
r($kanban->getPlanKanbanTest($productIDList[1], $branchIDList[2])[0]['items'][0]['data']['cols']) && p('0:name,title') && e('wait,未开始'); // 测试获取产品2的计划看板,传入分支
+1 -1
View File
@@ -43,4 +43,4 @@ r($kanban->getRDKanbanTest($executionIDList[1])[0]['items'][0]['data']['items'][
r($kanban->getRDKanbanTest($executionIDList[2])[0]['items'][0]['data']['cols']) && p('0:id,title') && e('9,未开始'); // 获取执行3的看板信息
r($kanban->getRDKanbanTest($executionIDList[0], 'story')[0]['items']) && p('') && e('0'); // 获取执行1的看板信息,传入列类型
r($kanban->getRDKanbanTest($executionIDList[1], 'task')[0]['items'][0]['data']['lanes']) && p('0:id,title') && e('2,泳道2'); // 获取执行2的看板信息,传入列类型
r($kanban->getRDKanbanTest($executionIDList[2], 'bug')[0]['items']) && p('') && e('0'); // 获取执行3的看板信息,传入列类型
r($kanban->getRDKanbanTest($executionIDList[2], 'bug')[0]['items']) && p('') && e('0'); // 获取执行3的看板信息,传入列类型
@@ -32,4 +32,4 @@ su('user1');
$actions3 = $tester->kanban->getRDRegionActions(1, 1);
$actions4 = $tester->kanban->getRDRegionActions(2, 2);
r(count($actions3[0]['items'])) && p() && e('0'); // 查看user1可以获取到几个操作按钮
r(count($actions4[0]['items'])) && p() && e('0'); // 查看user1可以获取到几个操作按钮
r(count($actions4[0]['items'])) && p() && e('0'); // 查看user1可以获取到几个操作按钮
@@ -32,4 +32,4 @@ su('user1');
$actions3 = $tester->kanban->getRegionActions(1, 1, 2);
$actions4 = $tester->kanban->getRegionActions(2, 2, 2);
r(count($actions3[0]['items'])) && p() && e('1'); // 查看user1可以获取到几个操作按钮
r(count($actions4[0]['items'])) && p() && e('1'); // 查看user1可以获取到几个操作按钮
r(count($actions4[0]['items'])) && p() && e('1'); // 查看user1可以获取到几个操作按钮
+1 -1
View File
@@ -56,4 +56,4 @@ r($card1) && p('id,name,color') && e('1,卡片1,937c5a'); // 更新卡片1的颜
r($card2) && p('id,name,color') && e('2,卡片2,fc5959'); // 更新卡片2的颜色
r($card3) && p('id,name,color') && e('3,卡片3,ff9f46'); // 更新卡片3的颜色
r($card4) && p('id,name,color') && e('4,卡片4,937c5a'); // 更新卡片4的颜色
r($card5) && p('id,name,color') && e('5,卡片5,fc5959'); // 更新卡片5的颜色
r($card5) && p('id,name,color') && e('5,卡片5,fc5959'); // 更新卡片5的颜色
@@ -35,7 +35,7 @@ class count_of_assigned_bug_in_user extends baseCalc
->where('`assignedTo`')->ne('')
->andWhere('`assignedTo`')->ne('closed')
->andWhere('`assignedTo` IS NOT NULL')
->groupBy('`user`')
->groupBy('`assignedTo`')
->fetchAll();
}
@@ -35,7 +35,7 @@ class count_of_assigned_case_in_user extends baseCalc
->where('`status`')->ne('done')
->andWhere('`assignedTo` IS NOT NULL')
->andWhere('`assignedTo`')->ne('closed')
->groupBy('`user`')
->groupBy('`assignedTo`')
->fetchAll();
}
@@ -32,7 +32,7 @@ class count_of_assigned_feedback_in_user extends baseCalc
return $this->dao->select($select)->from($this->getSingleSql())
->where('`assignedTo` IS NOT NULL')
->andWhere('`assignedTo`')->ne('closed')
->groupBy('`user`')
->groupBy('`assignedTo`')
->fetchAll();
}
@@ -38,7 +38,7 @@ class count_of_assigned_task_in_user extends baseCalc
->andWhere("(`mode` = 'multi' and `teamStatus` != 'done')", true)
->orWhere('`mode`')->ne('multi')
->markRight(1)
->groupBy('`user`')
->groupBy('`assignedTo`')
->fetchAll();
}
@@ -30,12 +30,12 @@ class count_of_daily_finished_task_in_user extends baseCalc
public function singleQuery()
{
$select = "`finishedBy` as 'user', YEAR(`finishedDate`) as `year`, MONTH(`finishedDate`) as `month`, DAY(`finishedDate`) as `day`, COUNT(`finishedBy`) as `value`";
$select = "`finishedBy` as `user`, YEAR(`finishedDate`) as `year`, MONTH(`finishedDate`) as `month`, DAY(`finishedDate`) as `day`, COUNT(`finishedBy`) as `value`";
return $this->dao->select($select)->from($this->getSingleSql())
->where('`projectStatus`')->ne('suspended')
->andWhere('`executionStatus`')->ne('suspended')
->andWhere('`finishedDate` IS NOT NULL')
->groupBy("`finishedBy`, `year`, `month`, `day`")
->groupBy("`finishedBy`, YEAR(`finishedDate`), MONTH(`finishedDate`), DAY(`finishedDate`)")
->fetchAll();
}
@@ -34,7 +34,7 @@ class count_of_daily_fixed_bug_in_user extends baseCalc
return $this->dao->select($select)->from($this->getSingleSql())
->where('`resolvedBy`')->ne('')
->andWhere('`resolvedBy` IS NOT NULL')
->groupBy("`user`, `year`, `month`, `day`")
->groupBy("`resolvedBy`, YEAR(`resolvedDate`), MONTH(`resolvedDate`), DAY(`resolvedDate`)")
->fetchAll();
}
+1 -1
View File
@@ -10,7 +10,7 @@ $config->my->bugCounts = 10;
$config->my->storyCounts = 10;
$config->my->oaObjectType = 'attend,leave,makeup,overtime,lieu';
$config->my->noFlowAuditModules = array('story', 'requirement', 'epic', 'testcase', 'feedback', 'oa');
$config->my->noFlowAuditModules = array('story', 'requirement', 'epic', 'testcase', 'feedback', 'oa', 'demand');
$config->mobile = new stdclass();
$config->mobile->todoBar = array('today', 'yesterday', 'thisWeek', 'lastWeek', 'all');
+8 -7
View File
@@ -1035,13 +1035,14 @@ $config->my->execution->dtable->fieldList['id']['title'] = $lang->idAB;
$config->my->execution->dtable->fieldList['id']['type'] = 'id';
$config->my->execution->dtable->fieldList['id']['group'] = '1';
$config->my->execution->dtable->fieldList['name']['name'] = 'name';
$config->my->execution->dtable->fieldList['name']['title'] = $lang->execution->name;
$config->my->execution->dtable->fieldList['name']['type'] = 'title';
$config->my->execution->dtable->fieldList['name']['link'] = 'RAWJS<function(info){ if(info.row.data.isParent) return false; else return \'' . helper::createLink('execution', 'browse', 'id={id}&from=my') . '\'; }>RAWJS';
$config->my->execution->dtable->fieldList['name']['fixed'] = 'left';
$config->my->execution->dtable->fieldList['name']['group'] = '1';
$config->my->execution->dtable->fieldList['name']['show'] = true;
$config->my->execution->dtable->fieldList['name']['name'] = 'name';
$config->my->execution->dtable->fieldList['name']['title'] = $lang->execution->name;
$config->my->execution->dtable->fieldList['name']['type'] = 'nestedTitle';
$config->my->execution->dtable->fieldList['name']['link'] = 'RAWJS<function(info){ if(info.row.data.isParent) return false; else return \'' . helper::createLink('execution', 'browse', 'id={id}&from=my') . '\'; }>RAWJS';
$config->my->execution->dtable->fieldList['name']['fixed'] = 'left';
$config->my->execution->dtable->fieldList['name']['group'] = '1';
$config->my->execution->dtable->fieldList['name']['nestedToggle'] = true;
$config->my->execution->dtable->fieldList['name']['show'] = true;
$config->my->execution->dtable->fieldList['code']['name'] = 'code';
$config->my->execution->dtable->fieldList['code']['title'] = $lang->execution->code;
+12 -9
View File
@@ -1,16 +1,19 @@
const today = zui.formatDate(new Date(), 'yyyy-MM-dd');
window.onRenderExecutionCell = function(result, info)
{
if(info.col.name === 'name' && systemMode == 'ALM')
{
if(typeof result[0] == 'string')
{
result[0] = {html: result[0], className: 'flex-1 w-0'};
}
else
{
result[0].props.className = 'flex-1 w-0';
}
result.push({html: '<span class="label size-sm circle ' + (info.row.data.type == 'stage' ? 'warning' : 'secondary') + '-pale">' + typeList[info.row.data.type] + '</span>'}, {className:'row-reverse'});
const executionLink = $.createLink('execution', 'task', `executionID=${info.id}`);
const executionType = typeList[info.row.data.type];
let executionName = `<span class='label secondary-pale flex-none'>${executionType}</span> `;
executionName += '<div class="ml-1 clip" style="width: max-content;">';
executionName += (!info.row.data.isParent && typeof result[0] != 'string') ? `<a href="${executionLink}" class="text-primary">${info.row.data.name}</a>` : info.row.data.name;
executionName += '</div>';
executionName += (!['done', 'closed', 'suspended'].includes(info.row.data.status) && today > info.row.data.end) ? `<span class="label danger-pale ml-1 flex-none">${delayed}</span>` : '';
result[0] = {html: executionName, className: 'w-full flex items-center'};
return result;
}
return result;
+1
View File
@@ -12,6 +12,7 @@ namespace zin;
jsVar('systemMode', $config->systemMode);
jsVar('typeList', $lang->execution->typeList);
jsVar('delayed', $lang->execution->delayed);
featurebar
(

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