Merge branch feature/task-report-merge of zentao/zentaopms (#10261)
This commit is contained in:
@@ -64,7 +64,7 @@ spec:
|
||||
content: |-
|
||||
代码库: {{ .GITFOX_REPO_NAME }}
|
||||
|
||||
新版本: {{ .GITFOX_TAG }}
|
||||
新版本: {{ .GITFOX_TAG }}
|
||||
|
||||
旧版本: {{ .PREVIOUS_TAG }}
|
||||
|
||||
@@ -76,4 +76,4 @@ spec:
|
||||
# "0": "qishiyao"
|
||||
groups:
|
||||
"0": "38a4e39b-df5d-4606-a988-e22673bdc358"
|
||||
name: gitfox-xuanim-plugin
|
||||
name: gitfox-xuanim-plugin
|
||||
|
||||
@@ -30,6 +30,7 @@ docker
|
||||
composer.lock
|
||||
.editorconfig
|
||||
CodeSniffer.conf
|
||||
test/runtime
|
||||
test/runtime/*
|
||||
test/www
|
||||
!/**/.gitkeep
|
||||
|
||||
@@ -91,6 +91,7 @@ zentaoxx:
|
||||
cp -r xuan/xxb/module/client zentaoxx/extension/xuan/
|
||||
cp -r xuan/xxb/module/conference zentaoxx/extension/xuan/
|
||||
cp -r xuan/xxb/module/watermark zentaoxx/extension/xuan/
|
||||
cp -r xuan/xxb/module/log zentaoxx/extension/xuan/
|
||||
cp -r xuan/xxb/module/integration 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
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e # 遇到错误立即退出
|
||||
set -u # 使用未定义的变量时报错
|
||||
|
||||
rootDir="$(dirname "$(dirname "$(realpath "$0")")")"
|
||||
input_file="${rootDir}/db/zentao.sql"
|
||||
output_file="${rootDir}/config/zentaouniqueindexes.php"
|
||||
|
||||
# 检查输入文件是否存在
|
||||
if [ ! -f "${input_file}" ]; then
|
||||
echo "Error: Input file ${input_file} not found!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查输入文件是否可读
|
||||
if [ ! -r "${input_file}" ]; then
|
||||
echo "Error: Cannot read input file ${input_file}!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "${output_file}" ]; then
|
||||
rm "${output_file}" || {
|
||||
echo "Error: Failed to remove existing output file!" >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
# 提取索引并转换为PHP数组格式,通过管道排序,添加逗号分隔符,并删除空行
|
||||
(
|
||||
echo "<?php"
|
||||
echo "\$config->uniqueIndexes = ["
|
||||
grep -oP 'CREATE UNIQUE INDEX[^;]+;' "${input_file}" | while read -r line; do
|
||||
table="$(echo "${line}" | grep -oP 'ON `\K[^`]+' | sed "s/zt_/TABLE_/" | tr '[:lower:]' '[:upper:]')"
|
||||
array_items="$(echo "${line}" | grep -oP '\(\K[^)]+' | sed "s/\`,\`/\`, \`/g" | sed "s/\`/\'/g")"
|
||||
echo " ${table} => [${array_items}]"
|
||||
done | sort | sed '$!s/$/,/'
|
||||
echo "];"
|
||||
) | tr -s '\n' > "${output_file}" || {
|
||||
echo "Error: Failed to write to output file!" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Successfully generated ${output_file}"
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"pkg": {
|
||||
"xuanxuan": {
|
||||
"gitVersion": "1672c352e2fb7bccaf94f30cf9341c7027c4ce89",
|
||||
"gitVersion": "02da3cc63dd6a5b60d04726a97578646178ee3e6",
|
||||
"version": "9.3"
|
||||
},
|
||||
"zentaoext": {
|
||||
"gitRepo": "zentao/zentaoext",
|
||||
"gitVersion": "release/12.0"
|
||||
"gitVersion": "release/12.2"
|
||||
},
|
||||
"zentaomax": {
|
||||
"gitVersion": "release/7.0"
|
||||
"gitVersion": "release/7.2"
|
||||
},
|
||||
"zentaoipd": {
|
||||
"gitVersion": "release/4.0"
|
||||
"gitVersion": "release/4.2"
|
||||
},
|
||||
"devops": {
|
||||
"gitRepo": "zentao/devops"
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}}
|
||||
if(!function_exists('getWebRoot')){function getWebRoot(){}}
|
||||
|
||||
/* 基本设置。Basic settings. */
|
||||
$config->version = '21.7.1'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
|
||||
$config->version = '21.7.3'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
|
||||
$config->liteVersion = '1.2'; // 迅捷版版本。 The version of Lite.
|
||||
$config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP.
|
||||
$config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time.
|
||||
|
||||
@@ -79,6 +79,7 @@ $filter->projectplan = new stdclass();
|
||||
$filter->kanban = new stdclass();
|
||||
$filter->group = new stdclass();
|
||||
$filter->convert = new stdclass();
|
||||
$filter->admin = new stdclass();
|
||||
|
||||
$filter->index->index = new stdclass();
|
||||
$filter->block->default = new stdclass();
|
||||
@@ -205,6 +206,10 @@ $filter->caselib->default = new stdclass();
|
||||
$filter->requirement->batchchangeparent = new stdclass();
|
||||
$filter->requirement->export = new stdclass();
|
||||
$filter->convert->mapjira2zentao = new stdclass();
|
||||
$filter->admin->register = new stdclass();
|
||||
$filter->admin->getcaptcha = new stdclass();
|
||||
$filter->admin->sendcode = new stdclass();
|
||||
$filter->admin->giftpackage = new stdclass();
|
||||
|
||||
$filter->index->index->get['open'] = 'reg::base64';
|
||||
|
||||
@@ -538,3 +543,8 @@ $filter->bug->ajaxgetproductbugs->get['limit'] = 'int';
|
||||
|
||||
$filter->convert->mapjira2zentao->cookie['stepStatus'] = 'reg::any';
|
||||
$filter->convert->mapjira2zentao->cookie['jiraRelation'] = 'reg::any';
|
||||
|
||||
$filter->admin->register->cookie['zentaosid'] = 'reg::any';
|
||||
$filter->admin->getcaptcha->cookie['zentaosid'] = 'reg::any';
|
||||
$filter->admin->sendcode->cookie['zentaosid'] = 'reg::any';
|
||||
$filter->admin->giftpackage->cookie['zentaosid'] = 'reg::any';
|
||||
@@ -223,6 +223,10 @@ $config->openMethods[] = 'user.login';
|
||||
$config->openMethods[] = 'user.refreshrandom';
|
||||
$config->openMethods[] = 'user.reset';
|
||||
$config->openMethods[] = 'user.resetpassword';
|
||||
$config->openMethods[] = 'admin.register';
|
||||
$config->openMethods[] = 'admin.getcaptcha';
|
||||
$config->openMethods[] = 'admin.sendcode';
|
||||
$config->openMethods[] = 'admin.planmodal';
|
||||
|
||||
/* 登录用户可以使用的方法。The methods that can be used by the logged users. */
|
||||
$config->logonMethods = array();
|
||||
@@ -359,6 +363,7 @@ $config->logonMethods[] = 'user.deny';
|
||||
$config->logonMethods[] = 'user.logout';
|
||||
$config->logonMethods[] = 'zanode.nodelist';
|
||||
$config->logonMethods[] = 'screen.viewold';
|
||||
$config->logonMethods[] = 'system.backupview';
|
||||
$config->logonMethods[] = 'screen.staticdataold';
|
||||
|
||||
$config->openModules = array();
|
||||
@@ -447,6 +452,7 @@ define('TABLE_API_LIB_RELEASE', '`' . $config->db->prefix . 'api_lib_release`');
|
||||
define('TABLE_MODULE', '`' . $config->db->prefix . 'module`');
|
||||
define('TABLE_ACTION', '`' . $config->db->prefix . 'action`');
|
||||
define('TABLE_ACTIONRECENT', '`' . $config->db->prefix . 'actionrecent`');
|
||||
define('TABLE_ACTIONPRODUCT', '`' . $config->db->prefix . 'actionproduct`');
|
||||
define('TABLE_FILE', '`' . $config->db->prefix . 'file`');
|
||||
define('TABLE_HOLIDAY', '`' . $config->db->prefix . 'holiday`');
|
||||
define('TABLE_HISTORY', '`' . $config->db->prefix . 'history`');
|
||||
@@ -618,7 +624,6 @@ define('TABLE_METRICRECORDS', '`' . $config->db->prefix . 'metricrecords`');
|
||||
|
||||
define('TABLE_SPACE', '`' . $config->db->prefix . 'space`');
|
||||
define('TABLE_INSTANCE', '`' . $config->db->prefix . 'instance`');
|
||||
define('TABLE_SOLUTION', '`' . $config->db->prefix . 'solution`');
|
||||
define('TABLE_ARTIFACTREPO', '`' . $config->db->prefix . 'artifactrepo`');
|
||||
|
||||
define('TABLE_AI_PROMPT', '`' . $config->db->prefix . 'ai_prompt`');
|
||||
@@ -670,6 +675,7 @@ $config->objectTables['api'] = TABLE_API;
|
||||
$config->objectTables['doc'] = TABLE_DOC;
|
||||
$config->objectTables['doclib'] = TABLE_DOCLIB;
|
||||
$config->objectTables['docspace'] = TABLE_DOCLIB;
|
||||
$config->objectTables['doctemplate'] = TABLE_DOC;
|
||||
$config->objectTables['demand'] = TABLE_DEMAND;
|
||||
$config->objectTables['demandpool'] = TABLE_DEMANDPOOL;
|
||||
$config->objectTables['demandspec'] = TABLE_DEMANDSPEC;
|
||||
@@ -719,7 +725,6 @@ $config->objectTables['serverroom'] = TABLE_SERVERROOM;
|
||||
$config->objectTables['host'] = TABLE_ZAHOST;
|
||||
$config->objectTables['instance'] = TABLE_INSTANCE;
|
||||
$config->objectTables['space'] = TABLE_SPACE;
|
||||
$config->objectTables['solution'] = TABLE_SOLUTION;
|
||||
$config->objectTables['artifactrepo'] = TABLE_ARTIFACTREPO;
|
||||
$config->objectTables['metric'] = TABLE_METRIC;
|
||||
$config->objectTables['cron'] = TABLE_CRON;
|
||||
@@ -783,7 +788,7 @@ $config->db->sqliteBlacklist = array('sqlite_queue', 'cron');
|
||||
$config->hasDropmenuApps = array('program', 'project', 'product', 'execution', 'qa', 'admin', 'bi', 'feedback', 'demandpool');
|
||||
$config->hasBranchMenuModules = array('product', 'story', 'release', 'bug', 'testcase', 'testtask', 'branch', 'tree');
|
||||
$config->excludeBranchMenu = array('product-dashboard', 'product-view');
|
||||
$config->excludeDropmenuList = array('program-browse', 'program-productview', 'program-kanban', 'product-all', 'product-index', 'product-kanban', 'project-kanban', 'execution-all', 'execution-executionkanban', 'project-browse', 'project-batchedit', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit', 'metric-preview', 'metric-browse', 'metric-view', 'metriclib-browse', 'qa-index', 'caselib-create', 'feedback-batchedit', 'feedback-batchclose', 'feedback-showimport', 'dimension-browse', 'dataview-browse', 'dataview-create', 'dataview-query', 'ticket-batchedit', 'ticket-batchfinish', 'ticket-batchactivate', 'file-download');
|
||||
$config->excludeDropmenuList = array('program-browse', 'program-productview', 'program-kanban', 'product-all', 'product-index', 'product-kanban', 'project-kanban', 'execution-all', 'execution-executionkanban', 'project-browse', 'project-template', 'project-createtemplate', 'project-batchedit', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit', 'metric-preview', 'metric-browse', 'metric-view', 'metriclib-browse', 'qa-index', 'caselib-create', 'feedback-batchedit', 'feedback-batchclose', 'feedback-showimport', 'dimension-browse', 'dataview-browse', 'dataview-create', 'dataview-query', 'ticket-batchedit', 'ticket-batchfinish', 'ticket-batchactivate', 'file-download');
|
||||
$config->hasSwitcherModules = array('design');
|
||||
$config->hasSwitcherMethods = array('project-bug', 'project-testcase', 'execution-bug', 'execution-testcase', 'testtask-cases', 'testtask-view', 'testtask-report', 'testtask-groupcase', 'testtask-linkcase');
|
||||
$config->excludeSwitcherList = array();
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
$config->uniqueIndexes = [
|
||||
TABLE_ATTEND => ['date', 'account'],
|
||||
TABLE_ATTENDSTAT => ['month', 'account'],
|
||||
TABLE_BASICMEAS => ['code'],
|
||||
TABLE_CFD => ['execution', 'type', 'name', 'date'],
|
||||
TABLE_CONFIG => ['vision', 'owner', 'module', 'section', 'key'],
|
||||
TABLE_DEMANDREVIEW => ['demand', 'version', 'reviewer'],
|
||||
TABLE_DEMANDSPEC => ['demand', 'version'],
|
||||
TABLE_DEPLOYPRODUCT => ['deploy', 'product', 'release'],
|
||||
TABLE_DESIGNSPEC => ['design', 'version'],
|
||||
TABLE_DOCCONTENT => ['doc', 'version'],
|
||||
TABLE_DUCKDBQUEUE => ['object'],
|
||||
TABLE_EXTENSION => ['code'],
|
||||
TABLE_FEEDBACKVIEW => ['account', 'product'],
|
||||
TABLE_GAPANALYSIS => ['project', 'account'],
|
||||
TABLE_GROUPPRIV => ['group', 'module', 'method'],
|
||||
TABLE_INTERVENTION => ['project', 'activity'],
|
||||
TABLE_KANBANCELL => ['kanban', 'type', 'lane', 'column'],
|
||||
TABLE_LANG => ['lang', 'module', 'section', 'key', 'vision'],
|
||||
TABLE_MEASRECORDS => ['year', 'month', 'day', 'week'],
|
||||
TABLE_OAUTH => ['account', 'openID', 'providerType', 'providerID'],
|
||||
TABLE_PIVOTSPEC => ['pivot', 'version'],
|
||||
TABLE_PLANSTORY => ['plan', 'story'],
|
||||
TABLE_PRACTICE => ['code'],
|
||||
TABLE_PRIV => ['module', 'method'],
|
||||
TABLE_PRIVLANG => ['objectID', 'objectType', 'lang'],
|
||||
TABLE_PRIVRELATION => ['priv', 'type', 'relationPriv'],
|
||||
TABLE_PROJECTADMIN => ['group', 'account'],
|
||||
TABLE_PROJECTCASE => ['project', 'case'],
|
||||
TABLE_PROJECTSPEC => ['project', 'version'],
|
||||
TABLE_PROJECTSTORY => ['project', 'story'],
|
||||
TABLE_RELATION => ['product', 'relation', 'AType', 'BType', 'AID', 'BID'],
|
||||
TABLE_REPOBRANCH => ['repo', 'revision', 'branch'],
|
||||
TABLE_REPORT => ['code'],
|
||||
TABLE_REVIEWRESULT => ['review', 'reviewer', 'type'],
|
||||
TABLE_RISKISSUE => ['risk', 'issue'],
|
||||
TABLE_ROADMAPSTORY => ['roadmap', 'story'],
|
||||
TABLE_SEARCHINDEX => ['objectType', 'objectID'],
|
||||
TABLE_STORYESTIMATE => ['story', 'round'],
|
||||
TABLE_STORYREVIEW => ['story', 'version', 'reviewer'],
|
||||
TABLE_STORYSPEC => ['story', 'version'],
|
||||
TABLE_STORYSTAGE => ['story', 'branch'],
|
||||
TABLE_SUITECASE => ['suite', 'case'],
|
||||
TABLE_TASKSPEC => ['task', 'version'],
|
||||
TABLE_TEAM => ['root', 'type', 'account'],
|
||||
TABLE_TESTRUN => ['task', 'case'],
|
||||
TABLE_USER => ['account'],
|
||||
TABLE_USERGROUP => ['account', 'group'],
|
||||
TABLE_USERVIEW => ['account'],
|
||||
TABLE_WEEKLYREPORT => ['project', 'weekStart'],
|
||||
TABLE_WORKFLOW => ['group', 'app', 'module', 'vision'],
|
||||
TABLE_WORKFLOWACTION => ['group', 'module', 'action', 'vision'],
|
||||
TABLE_WORKFLOWFIELD => ['group', 'module', 'field'],
|
||||
TABLE_WORKFLOWLAYOUT => ['group', 'module', 'action', 'ui', 'field', 'vision'],
|
||||
TABLE_WORKFLOWLINKDATA => ['objectType', 'objectID', 'linkedType', 'linkedID'],
|
||||
TABLE_WORKFLOWRELATIONLAYOUT => ['prev', 'next', 'action', 'ui', 'field'],
|
||||
TABLE_WORKFLOWVERSION => ['module', 'version']
|
||||
];
|
||||
+1
-1
@@ -418,7 +418,7 @@ REPLACE INTO `zt_product` (`id`, `program`, `name`, `code`, `shadow`, `bind`, `l
|
||||
(2, 1, '企业内部工时管理系统', '', 0, '0', 0, 'normal', 'normal', '', '', 'productManager', '', 'productManager', '', '', 'open', '', '', '', NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 'admin', '2024-04-02 14:30:55', '20.0.beta2', NULL, 10, 'rnd', '0');
|
||||
REPLACE INTO `zt_productplan` (`id`, `product`, `branch`, `parent`, `title`, `status`, `desc`, `begin`, `end`, `finishedDate`, `closedDate`, `order`, `closedReason`, `createdBy`, `createdDate`, `deleted`) VALUES
|
||||
(1, 1, '0', 0, '1.0版本', 'wait', '<p><span style=\"font-size:13px;font-family:\'Source Han Sans CN\', PingFangSC, \'Microsoft YaHei\', HiraginoSansGB, Roboto, Helvetica, Tahoma, sans-serif;\">开发出企业网站1.0版本。</span></p>', '2030-01-01', '2030-01-01', NULL, NULL, '0', '', 'admin', '2024-04-02 14:42:47', '0');
|
||||
REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `storyType`, parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES
|
||||
REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `storyType`, `parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES
|
||||
(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 'story,requirement', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
ALTER TABLE `zt_project`ADD `isTpl` tinyint(1) unsigned NOT NULL DEFAULT '0' AFTER `project`;
|
||||
ALTER TABLE `zt_task` ADD `isTpl` tinyint(1) unsigned NOT NULL DEFAULT '0' AFTER `isParent`;
|
||||
|
||||
ALTER TABLE `zt_oauth` MODIFY `openID` varchar(100) NOT NULL DEFAULT '';
|
||||
ALTER TABLE `zt_doclib` MODIFY `id` mediumint(8) unsigned NOT NULL auto_increment;
|
||||
ALTER TABLE `zt_doclib` MODIFY `order` mediumint(8) unsigned NOT NULL DEFAULT '0';
|
||||
|
||||
ALTER TABLE `zt_repobranch` MODIFY `branch` varchar(100) NOT NULL DEFAULT '';
|
||||
|
||||
UPDATE `zt_workflowgroup` SET `name` = '瀑布式产品研发' WHERE `code` = 'waterfallproduct';
|
||||
UPDATE `zt_workflowgroup` SET `name` = '瀑布式项目研发' WHERE `code` = 'waterfallproject';
|
||||
UPDATE `zt_workflowgroup` SET `name` = '敏捷式产品研发' WHERE `code` = 'scrumproduct';
|
||||
UPDATE `zt_workflowgroup` SET `name` = '敏捷式项目研发' WHERE `code` = 'scrumproject';
|
||||
|
||||
CREATE OR REPLACE VIEW `ztv_projectnotpl` AS SELECT * FROM `zt_project` WHERE `deleted` = '0' AND `isTpl` = 0;
|
||||
CREATE OR REPLACE VIEW `ztv_tasknotpl` AS SELECT * FROM `zt_task` WHERE `deleted` = '0' AND `isTpl` = 0;
|
||||
|
||||
ALTER TABLE `zt_compile` ADD `branch` varchar(255) NOT NULL DEFAULT '' AFTER `status`;
|
||||
|
||||
ALTER TABLE `zt_doc`
|
||||
ADD `templateDesc` text NULL AFTER `templateType`,
|
||||
ADD `builtIn` enum('0','1') NOT NULL DEFAULT '0' AFTER `version`;
|
||||
|
||||
DROP TABLE IF EXISTS `zt_solution`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `zt_actionproduct` (
|
||||
`action` mediumint(8) unsigned NOT NULL,
|
||||
`product` mediumint(8) unsigned NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
CREATE INDEX `action_product` ON `zt_actionproduct`(`action`, `product`);
|
||||
CREATE INDEX `product` ON `zt_actionproduct`(`product`);
|
||||
|
||||
ALTER TABLE `zt_actionrecent` DROP INDEX `date`;
|
||||
ALTER TABLE `zt_actionrecent` ADD INDEX `vision_date` (`vision`, `date`);
|
||||
ALTER TABLE `zt_actionrecent` ADD INDEX `execution` (`execution`);
|
||||
|
||||
ALTER TABLE `zt_action` DROP INDEX `date`;
|
||||
ALTER TABLE `zt_action` DROP INDEX `project`;
|
||||
ALTER TABLE `zt_action` ADD INDEX `vision_date` (`vision`, `date`);
|
||||
ALTER TABLE `zt_action` ADD INDEX `execution` (`execution`);
|
||||
ALTER TABLE `zt_action` ADD INDEX `project` (`project`);
|
||||
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterLevel' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'level';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterCategory' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'category';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterMarket' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'market';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterCloseReason' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'closedReason';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewResult' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewedResult';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewStatus';
|
||||
|
||||
CREATE OR REPLACE VIEW `ztv_executionsummary` AS SELECT `zt_task`.`execution` AS `execution`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`execution`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
|
||||
|
||||
ALTER TABLE `zt_history` MODIFY `new` longtext NULL;
|
||||
ALTER TABLE `zt_history` MODIFY `old` longtext NULL;
|
||||
@@ -0,0 +1,25 @@
|
||||
ALTER TABLE `zt_project` ADD `tplAcl` char(30) NOT NULL DEFAULT 'open' AFTER `whitelist`;
|
||||
ALTER TABLE `zt_project` ADD `tplWhiteList` text NULL AFTER `tplAcl`;
|
||||
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项级别', 'charterLevel', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterLevel', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterLevel');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项类型', 'charterCategory', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCategory', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterCategory');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项适用市场', 'charterMarket', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterMarket', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterMarket');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项状态', 'charterStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterStatus', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterStatus');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项关闭原因', 'charterCloseReason', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCloseReason', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterCloseReason');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项审批结果', 'charterReviewResult', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewResult', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterReviewResult');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项审批状态', 'charterReviewStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewStatus', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterReviewStatus');
|
||||
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterLevel' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'level';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterCategory' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'category';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterMarket' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'market';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'status';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterCloseReason' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'closedReason';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewResult' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewedResult';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewStatus';
|
||||
@@ -51,3 +51,5 @@ UPDATE `zt_workflowgroup` SET `name` = '瀑布型产品研发' WHERE `code` = 'w
|
||||
UPDATE `zt_workflowgroup` SET `name` = '瀑布型项目研发' WHERE `code` = 'waterfallproject';
|
||||
UPDATE `zt_workflowgroup` SET `name` = '敏捷型产品研发' WHERE `code` = 'scrumproduct';
|
||||
UPDATE `zt_workflowgroup` SET `name` = '敏捷型项目研发' WHERE `code` = 'scrumproject';
|
||||
|
||||
UPDATE `zt_workflow` SET `name` = '构建' WHERE `name` = '版本' AND `role` = 'buildin' AND `module` = 'build';
|
||||
|
||||
+80
-73
@@ -30,11 +30,20 @@ CREATE TABLE IF NOT EXISTS `zt_action` (
|
||||
`efforted` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE INDEX `date` ON `zt_action`(`date`);
|
||||
CREATE INDEX `actor` ON `zt_action`(`actor`);
|
||||
CREATE INDEX `project` ON `zt_action`(`project`);
|
||||
CREATE INDEX `action` ON `zt_action`(`action`);
|
||||
CREATE INDEX `objectID` ON `zt_action`(`objectID`);
|
||||
CREATE INDEX `vision_date` ON `zt_action`(`vision`, `date`);
|
||||
CREATE INDEX `actor` ON `zt_action`(`actor`);
|
||||
CREATE INDEX `project` ON `zt_action`(`project`);
|
||||
CREATE INDEX `execution` ON `zt_action`(`execution`);
|
||||
CREATE INDEX `action` ON `zt_action`(`action`);
|
||||
CREATE INDEX `objectID` ON `zt_action`(`objectID`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_actionproduct`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_actionproduct` (
|
||||
`action` mediumint(8) unsigned NOT NULL,
|
||||
`product` mediumint(8) unsigned NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
CREATE INDEX `action_product` ON `zt_actionproduct`(`action`, `product`);
|
||||
CREATE INDEX `product` ON `zt_actionproduct`(`product`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_actionrecent`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_actionrecent` (
|
||||
@@ -55,11 +64,12 @@ CREATE TABLE IF NOT EXISTS `zt_actionrecent` (
|
||||
`efforted` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE INDEX `date` ON `zt_actionrecent`(`date`);
|
||||
CREATE INDEX `actor` ON `zt_actionrecent`(`actor`);
|
||||
CREATE INDEX `project` ON `zt_actionrecent`(`project`);
|
||||
CREATE INDEX `action` ON `zt_actionrecent`(`action`);
|
||||
CREATE INDEX `objectID` ON `zt_actionrecent`(`objectID`);
|
||||
CREATE INDEX `vision_date` ON `zt_actionrecent`(`vision`, `date`);
|
||||
CREATE INDEX `actor` ON `zt_actionrecent`(`actor`);
|
||||
CREATE INDEX `project` ON `zt_actionrecent`(`project`);
|
||||
CREATE INDEX `execution` ON `zt_actionrecent`(`execution`);
|
||||
CREATE INDEX `action` ON `zt_actionrecent`(`action`);
|
||||
CREATE INDEX `objectID` ON `zt_actionrecent`(`objectID`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_api_lib_release`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_api_lib_release` (
|
||||
@@ -636,6 +646,7 @@ CREATE TABLE IF NOT EXISTS `zt_compile` (
|
||||
`job` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`queue` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`status` varchar(100) NOT NULL DEFAULT '',
|
||||
`branch` varchar(255) NOT NULL DEFAULT '',
|
||||
`logs` longtext NULL,
|
||||
`atTime` varchar(10) NOT NULL DEFAULT '',
|
||||
`testtask` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
@@ -797,6 +808,7 @@ CREATE TABLE IF NOT EXISTS `zt_doc` (
|
||||
`lib` varchar(30) NOT NULL DEFAULT '',
|
||||
`template` varchar(30) NOT NULL DEFAULT '',
|
||||
`templateType` varchar(30) NOT NULL DEFAULT '',
|
||||
`templateDesc` text NULL,
|
||||
`chapterType` varchar(30) NOT NULL DEFAULT '',
|
||||
`module` varchar(30) NOT NULL DEFAULT '',
|
||||
`title` varchar(255) NOT NULL DEFAULT '',
|
||||
@@ -830,6 +842,7 @@ CREATE TABLE IF NOT EXISTS `zt_doc` (
|
||||
`readGroups` varchar(255) NOT NULL DEFAULT '',
|
||||
`readUsers` text NULL,
|
||||
`version` smallint(6) unsigned NOT NULL DEFAULT '1',
|
||||
`builtIn` enum('0','1') NOT NULL DEFAULT '0',
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -871,7 +884,7 @@ CREATE UNIQUE INDEX `doc_version` ON `zt_doccontent`(`doc`,`version`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_doclib`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_doclib` (
|
||||
`id` smallint(6) unsigned NOT NULL auto_increment,
|
||||
`id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`type` varchar(30) NOT NULL DEFAULT '',
|
||||
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
|
||||
`parent` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
@@ -886,7 +899,7 @@ CREATE TABLE IF NOT EXISTS `zt_doclib` (
|
||||
`main` enum('0','1') NOT NULL DEFAULT '0',
|
||||
`collector` text NULL,
|
||||
`desc` mediumtext NULL,
|
||||
`order` tinyint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`order` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`addedBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`addedDate` datetime NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
@@ -1045,9 +1058,9 @@ CREATE TABLE IF NOT EXISTS `zt_history` (
|
||||
`id` int(11) unsigned NOT NULL auto_increment,
|
||||
`action` mediumint(8) unsigned NOT NULL default '0',
|
||||
`field` varchar(30) NOT NULL default '',
|
||||
`old` text NULL,
|
||||
`old` longtext NULL,
|
||||
`oldValue` text NULL,
|
||||
`new` text NULL,
|
||||
`new` longtext NULL,
|
||||
`newValue` text NULL,
|
||||
`diff` mediumtext NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@@ -1291,7 +1304,7 @@ CREATE TABLE IF NOT EXISTS `zt_module` (
|
||||
`from` mediumint(8) unsigned NOT NULL default '0',
|
||||
`owner` varchar(30) NOT NULL DEFAULT '',
|
||||
`collector` text NULL,
|
||||
`short` varchar(30) NOT NULL DEFAULT '',
|
||||
`short` varchar(60) NOT NULL DEFAULT '',
|
||||
`deleted` enum('0','1') NOT NULL default '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -1372,7 +1385,7 @@ CREATE INDEX `status` ON `zt_notify`(`status`);
|
||||
-- DROP TABLE IF EXISTS `zt_oauth`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_oauth` (
|
||||
`account` varchar(30) NOT NULL DEFAULT '',
|
||||
`openID` varchar(255) NOT NULL DEFAULT '',
|
||||
`openID` varchar(100) NOT NULL DEFAULT '',
|
||||
`providerType` varchar(30) NOT NULL DEFAULT '',
|
||||
`providerID` mediumint(8) unsigned NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -1543,6 +1556,7 @@ CREATE INDEX `end` ON `zt_productplan` (`end`);
|
||||
CREATE TABLE IF NOT EXISTS `zt_project` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`project` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`isTpl` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||
`charter` mediumint(8) NOT NULL DEFAULT 0,
|
||||
`model` char(30) NOT NULL DEFAULT '',
|
||||
`type` char(30) NOT NULL DEFAULT 'sprint',
|
||||
@@ -1601,6 +1615,8 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
|
||||
`team` varchar(90) NOT NULL DEFAULT '',
|
||||
`acl` char(30) NOT NULL DEFAULT 'open',
|
||||
`whitelist` text NULL,
|
||||
`tplAcl` char(30) NOT NULL DEFAULT 'open',
|
||||
`tplWhiteList` text NULL,
|
||||
`order` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
|
||||
`stageBy` enum('project','product') NOT NULL DEFAULT 'product',
|
||||
@@ -1796,7 +1812,7 @@ CREATE TABLE IF NOT EXISTS `zt_repo` (
|
||||
CREATE TABLE IF NOT EXISTS `zt_repobranch` (
|
||||
`repo` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`revision` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`branch` varchar(255) NOT NULL DEFAULT ''
|
||||
`branch` varchar(100) NOT NULL DEFAULT ''
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE INDEX `branch` ON `zt_repobranch` (`branch`);
|
||||
CREATE INDEX `revision` ON `zt_repobranch` (`revision`);
|
||||
@@ -2061,6 +2077,7 @@ CREATE TABLE IF NOT EXISTS `zt_task` (
|
||||
`project` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`parent` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`isParent` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`isTpl` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
||||
`path` text NULL,
|
||||
`execution` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`module` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
@@ -2613,37 +2630,37 @@ CREATE TABLE IF NOT EXISTS `zt_report` (
|
||||
CREATE UNIQUE INDEX `code` ON `zt_report`(`code`);
|
||||
|
||||
-- DROP VIEW IF EXISTS `ztv_executionsummary`;
|
||||
CREATE OR REPLACE VIEW `ztv_executionsummary` AS select `zt_task`.`execution` AS `execution`,sum(if((`zt_task`.`parent` >= '0'),`zt_task`.`estimate`,0)) AS `estimate`,sum(if((`zt_task`.`parent` >= '0'),`zt_task`.`consumed`,0)) AS `consumed`,sum(if(((`zt_task`.`status` <> 'cancel') and (`zt_task`.`status` <> 'closed') and (`zt_task`.`parent` >= '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,sum(if(((`zt_task`.`status` <> 'done') and (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,sum(if(`zt_task`.`parent` >= '0',`zt_task`.`consumed`,0) + if(`zt_task`.`status` <> 'cancel' and `zt_task`.`status` <> 'closed' and `zt_task`.`parent` >= '0',`zt_task`.`left`,0)) AS `totalReal` from `zt_task` where (`zt_task`.`deleted` = '0') group by `zt_task`.`execution`;
|
||||
CREATE OR REPLACE VIEW `ztv_executionsummary` AS SELECT `zt_task`.`execution` AS `execution`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`execution`;
|
||||
-- DROP VIEW IF EXISTS `ztv_projectsummary`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectsummary` AS select `zt_task`.`project` AS `project`,sum(if(`zt_task`.`parent` >= '0',`zt_task`.`estimate`,0)) AS `estimate`,sum(if(`zt_task`.`parent` >= '0',`zt_task`.`consumed`,0)) AS `consumed`,sum(if((`zt_task`.`status` <> 'cancel') and (`zt_task`.`status` <> 'closed') and (`zt_task`.`parent` >= '0'),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,sum(if(((`zt_task`.`status` <> 'done') and (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,sum(if(`zt_task`.`parent` >= '0',`zt_task`.`consumed`,0) + if(`zt_task`.`status` <> 'cancel' and `zt_task`.`status` <> 'closed' and `zt_task`.`parent` >= '0',`zt_task`.`left`,0)) AS `totalReal` from `zt_task` where (`zt_task`.`deleted` = '0') group by `zt_task`.`project`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
|
||||
-- DROP VIEW IF EXISTS `ztv_projectstories`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectstories` AS select `t1`.`project` AS `execution`,COUNT(1) AS `stories`,sum(if((`t2`.`status` = 'closed'),0,1)) AS `undone` from ((`zt_projectstory` `t1` left join `zt_story` `t2` on((`t1`.`story` = `t2`.`id`))) left join `zt_project` `t3` on((`t1`.`project` = `t3`.`id`))) where ((`t2`.`deleted` = '0') and (`t3`.`type` in ('sprint','stage'))) group by `t1`.`project`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectstories` AS SELECT `t1`.`project` AS `execution`,COUNT(1) AS `stories`,SUM(IF((`t2`.`status` = 'closed'),0,1)) AS `undone` FROM ((`zt_projectstory` `t1` LEFT JOIN `zt_story` `t2` ON((`t1`.`story` = `t2`.`id`))) LEFT JOIN `zt_project` `t3` ON((`t1`.`project` = `t3`.`id`))) WHERE ((`t2`.`deleted` = '0') AND (`t3`.`type` IN ('sprint','stage'))) GROUP BY `t1`.`project`;
|
||||
-- DROP VIEW IF EXISTS `ztv_projectteams`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectteams` AS select `zt_team`.`root` AS `execution`,COUNT(1) AS `teams` from `zt_team` where (`zt_team`.`type` = 'execution') group by `zt_team`.`root`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectteams` AS SELECT `zt_team`.`root` AS `execution`,COUNT(1) AS `teams` FROM `zt_team` WHERE (`zt_team`.`type` = 'execution') GROUP BY `zt_team`.`root`;
|
||||
-- DROP VIEW IF EXISTS `ztv_projectbugs`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectbugs` AS select `zt_bug`.`execution` AS `execution`,COUNT(1) AS `bugs`,sum(if((`zt_bug`.`resolution` = ''),0,1)) AS `resolutions`,sum(if((`zt_bug`.`severity` <= 2),1,0)) AS `seriousBugs` from `zt_bug` where (`zt_bug`.`deleted` = '0') group by `zt_bug`.`execution`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectbugs` AS SELECT `zt_bug`.`execution` AS `execution`,COUNT(1) AS `bugs`,SUM(IF((`zt_bug`.`resolution` = ''),0,1)) AS `resolutions`,SUM(IF((`zt_bug`.`severity` <= 2),1,0)) AS `seriousBugs` FROM `zt_bug` WHERE (`zt_bug`.`deleted` = '0') GROUP BY `zt_bug`.`execution`;
|
||||
-- DROP VIEW IF EXISTS `ztv_productbugs`;
|
||||
CREATE OR REPLACE VIEW `ztv_productbugs` AS select `zt_bug`.`product` AS `product`,COUNT(1) AS `bugs`,sum(if((`zt_bug`.`resolution` = ''),0,1)) AS `resolutions`,sum(if((`zt_bug`.`severity` <= 2),1,0)) AS `seriousBugs` from `zt_bug` where (`zt_bug`.`deleted` = '0') group by `zt_bug`.`product`;
|
||||
CREATE OR REPLACE VIEW `ztv_productbugs` AS SELECT `zt_bug`.`product` AS `product`,COUNT(1) AS `bugs`,SUM(IF((`zt_bug`.`resolution` = ''),0,1)) AS `resolutions`,SUM(IF((`zt_bug`.`severity` <= 2),1,0)) AS `seriousBugs` FROM `zt_bug` WHERE (`zt_bug`.`deleted` = '0') GROUP BY `zt_bug`.`product`;
|
||||
-- DROP VIEW IF EXISTS `ztv_productstories`;
|
||||
CREATE OR REPLACE VIEW `ztv_productstories` AS select `zt_story`.`product` AS `product`,COUNT(1) AS `stories`,sum(if((`zt_story`.`status` = 'closed'),0,1)) AS `undone` from `zt_story` where (`zt_story`.`deleted` = '0') group by `zt_story`.`product`;
|
||||
CREATE OR REPLACE VIEW `ztv_productstories` AS SELECT `zt_story`.`product` AS `product`,COUNT(1) AS `stories`,SUM(IF((`zt_story`.`status` = 'closed'),0,1)) AS `undone` FROM `zt_story` WHERE (`zt_story`.`deleted` = '0') GROUP BY `zt_story`.`product`;
|
||||
-- DROP VIEW IF EXISTS `ztv_dayuserlogin`;
|
||||
CREATE OR REPLACE VIEW `ztv_dayuserlogin` AS select COUNT(1) AS `userlogin`,CAST(`zt_action`.`date` AS DATE) AS `day` from `zt_action` where ((`zt_action`.`objectType` = 'user') and (`zt_action`.`action` = 'login')) group by CAST(`zt_action`.`date` AS DATE);
|
||||
CREATE OR REPLACE VIEW `ztv_dayuserlogin` AS SELECT COUNT(1) AS `userlogin`,CAST(`zt_action`.`date` AS DATE) AS `day` FROM `zt_action` WHERE ((`zt_action`.`objectType` = 'user') AND (`zt_action`.`action` = 'login')) GROUP BY CAST(`zt_action`.`date` AS DATE);
|
||||
-- DROP VIEW IF EXISTS `ztv_dayeffort`;
|
||||
CREATE OR REPLACE VIEW `ztv_dayeffort` AS select round(sum(`zt_effort`.`consumed`),1) AS `consumed`,`zt_effort`.`date` AS `date` from `zt_effort` group by `zt_effort`.`date`;
|
||||
CREATE OR REPLACE VIEW `ztv_dayeffort` AS SELECT ROUND(SUM(`zt_effort`.`consumed`),1) AS `consumed`,`zt_effort`.`date` AS `date` FROM `zt_effort` GROUP BY `zt_effort`.`date`;
|
||||
-- DROP VIEW IF EXISTS `ztv_daystoryopen`;
|
||||
CREATE OR REPLACE VIEW `ztv_daystoryopen` AS select COUNT(1) AS `storyopen`,CAST(`zt_action`.`date` AS DATE) AS `day` from `zt_action` where ((`zt_action`.`objectType` = 'story') and (`zt_action`.`action` = 'opened')) group by CAST(`zt_action`.`date` AS DATE);
|
||||
CREATE OR REPLACE VIEW `ztv_daystoryopen` AS SELECT COUNT(1) AS `storyopen`,CAST(`zt_action`.`date` AS DATE) AS `day` FROM `zt_action` WHERE ((`zt_action`.`objectType` = 'story') AND (`zt_action`.`action` = 'opened')) GROUP BY CAST(`zt_action`.`date` AS DATE);
|
||||
-- DROP VIEW IF EXISTS `ztv_daystoryclose`;
|
||||
CREATE OR REPLACE VIEW `ztv_daystoryclose` AS select COUNT(1) AS `storyclose`,CAST(`zt_action`.`date` AS DATE) AS `day` from `zt_action` where ((`zt_action`.`objectType` = 'story') and (`zt_action`.`action` = 'closed')) group by CAST(`zt_action`.`date` AS DATE);
|
||||
CREATE OR REPLACE VIEW `ztv_daystoryclose` AS SELECT COUNT(1) AS `storyclose`,CAST(`zt_action`.`date` AS DATE) AS `day` FROM `zt_action` WHERE ((`zt_action`.`objectType` = 'story') AND (`zt_action`.`action` = 'closed')) GROUP BY CAST(`zt_action`.`date` AS DATE);
|
||||
-- DROP VIEW IF EXISTS `ztv_daytaskopen`;
|
||||
CREATE OR REPLACE VIEW `ztv_daytaskopen` AS select COUNT(1) AS `taskopen`,CAST(`zt_action`.`date` AS DATE) AS `day` from `zt_action` where ((`zt_action`.`objectType` = 'task') and (`zt_action`.`action` = 'opened')) group by CAST(`zt_action`.`date` AS DATE);
|
||||
CREATE OR REPLACE VIEW `ztv_daytaskopen` AS SELECT COUNT(1) AS `taskopen`,CAST(`zt_action`.`date` AS DATE) AS `day` FROM `zt_action` WHERE ((`zt_action`.`objectType` = 'task') AND (`zt_action`.`action` = 'opened')) GROUP BY CAST(`zt_action`.`date` AS DATE);
|
||||
-- DROP VIEW IF EXISTS `ztv_daytaskfinish`;
|
||||
CREATE OR REPLACE VIEW `ztv_daytaskfinish` AS select COUNT(1) AS `taskfinish`,CAST(`zt_action`.`date` AS DATE) AS `day` from `zt_action` where ((`zt_action`.`objectType` = 'task') and (`zt_action`.`action` = 'finished')) group by CAST(`zt_action`.`date` AS DATE);
|
||||
CREATE OR REPLACE VIEW `ztv_daytaskfinish` AS SELECT COUNT(1) AS `taskfinish`,CAST(`zt_action`.`date` AS DATE) AS `day` FROM `zt_action` WHERE ((`zt_action`.`objectType` = 'task') AND (`zt_action`.`action` = 'finished')) GROUP BY CAST(`zt_action`.`date` AS DATE);
|
||||
-- DROP VIEW IF EXISTS `ztv_daybugopen`;
|
||||
CREATE OR REPLACE VIEW `ztv_daybugopen` AS select COUNT(1) AS `bugopen`,CAST(`zt_action`.`date` AS DATE) AS `day` from `zt_action` where ((`zt_action`.`objectType` = 'bug') and (`zt_action`.`action` = 'opened')) group by CAST(`zt_action`.`date` AS DATE);
|
||||
CREATE OR REPLACE VIEW `ztv_daybugopen` AS SELECT COUNT(1) AS `bugopen`,CAST(`zt_action`.`date` AS DATE) AS `day` FROM `zt_action` WHERE ((`zt_action`.`objectType` = 'bug') AND (`zt_action`.`action` = 'opened')) GROUP BY CAST(`zt_action`.`date` AS DATE);
|
||||
-- DROP VIEW IF EXISTS `ztv_daybugresolve`;
|
||||
CREATE OR REPLACE VIEW `ztv_daybugresolve` AS select COUNT(1) AS `bugresolve`,CAST(`zt_action`.`date` AS DATE) AS `day` from `zt_action` where ((`zt_action`.`objectType` = 'bug') and (`zt_action`.`action` = 'resolved')) group by CAST(`zt_action`.`date` AS DATE);
|
||||
CREATE OR REPLACE VIEW `ztv_daybugresolve` AS SELECT COUNT(1) AS `bugresolve`,CAST(`zt_action`.`date` AS DATE) AS `day` FROM `zt_action` WHERE ((`zt_action`.`objectType` = 'bug') AND (`zt_action`.`action` = 'resolved')) GROUP BY CAST(`zt_action`.`date` AS DATE);
|
||||
-- DROP VIEW IF EXISTS `ztv_dayactions`;
|
||||
CREATE OR REPLACE VIEW `ztv_dayactions` AS select COUNT(1) AS `actions`,CAST(`zt_action`.`date` AS DATE) AS `day` from `zt_action` group by CAST(`zt_action`.`date` AS DATE);
|
||||
CREATE OR REPLACE VIEW `ztv_dayactions` AS SELECT COUNT(1) AS `actions`,CAST(`zt_action`.`date` AS DATE) AS `day` FROM `zt_action` GROUP BY CAST(`zt_action`.`date` AS DATE);
|
||||
-- DROP VIEW IF EXISTS `ztv_normalproduct`;
|
||||
CREATE OR REPLACE VIEW `ztv_normalproduct` AS SELECT * FROM `zt_product` WHERE `shadow` = 0;
|
||||
|
||||
@@ -13549,10 +13566,10 @@ REPLACE INTO `zt_workflowrule`(`type`, `name`, `rule`, `createdBy`, `createdDate
|
||||
|
||||
INSERT INTO `zt_workflowgroup` (`type`, `projectModel`, `projectType`, `name`, `code`, `status`, `vision`, `main`) VALUES
|
||||
('product', '', 'project', '默认流程', 'productproject', 'normal', 'rnd', '1'),
|
||||
('project', 'scrum', 'product', '敏捷型产品研发', 'scrumproduct', 'normal', 'rnd', '1'),
|
||||
('project', 'scrum', 'project', '敏捷型项目研发', 'scrumproject', 'normal', 'rnd', '1'),
|
||||
('project', 'waterfall', 'product', '瀑布型产品研发', 'waterfallproduct','normal', 'rnd', '1'),
|
||||
('project', 'waterfall', 'project', '瀑布型项目研发', 'waterfallproject','normal', 'rnd', '1');
|
||||
('project', 'scrum', 'product', '敏捷式产品研发', 'scrumproduct', 'normal', 'rnd', '1'),
|
||||
('project', 'scrum', 'project', '敏捷式项目研发', 'scrumproject', 'normal', 'rnd', '1'),
|
||||
('project', 'waterfall', 'product', '瀑布式产品研发', 'waterfallproduct','normal', 'rnd', '1'),
|
||||
('project', 'waterfall', 'project', '瀑布式项目研发', 'waterfallproject','normal', 'rnd', '1');
|
||||
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`) VALUES
|
||||
('system', '产品', 'products', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"product\",\"method\":\"getPairs\",\"methodDesc\":\"Get product pairs.\",\"params\":[{\"name\":\"mode\",\"type\":\"string\",\"desc\":\"\",\"value\":\"all\"}]}', '', '', ''),
|
||||
@@ -13637,7 +13654,14 @@ INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`
|
||||
('sql', '用户需求', 'requirements', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select `id`,`title` from zt_story where `deleted`=\'0\' and `type`=\'requirement\'', 'view_datasource_3', 'id', 'title'),
|
||||
('sql', '业务需求', 'epics', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select `id`,`title` from zt_story where `deleted`=\'0\' and `type`=\'epic\'', 'view_datasource_2', 'id', 'title'),
|
||||
('option', '颜色', 'color', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{"#ef4444":"#ef4444","#f97316":"#f97316","#eab308":"#eab308","#84cc16":"#84cc16","#22c55e":"#22c55e","#14b8a6":"#14b8a6","#0ea5e9":"#0ea5e9","#6366f1":"#6366f1","#a855f7":"#a855f7","#d946ef":"#d946ef","#ec4899":"#ec4899"}', '', '', ''),
|
||||
('option', '颜色', 'litecolor', '1', 'lite', 'admin', '1970-01-01 00:00:01', '{"#ef4444":"#ef4444","#f97316":"#f97316","#eab308":"#eab308","#84cc16":"#84cc16","#22c55e":"#22c55e","#14b8a6":"#14b8a6","#0ea5e9":"#0ea5e9","#6366f1":"#6366f1","#a855f7":"#a855f7","#d946ef":"#d946ef","#ec4899":"#ec4899"}', '', '', '');
|
||||
('option', '颜色', 'litecolor', '1', 'lite', 'admin', '1970-01-01 00:00:01', '{"#ef4444":"#ef4444","#f97316":"#f97316","#eab308":"#eab308","#84cc16":"#84cc16","#22c55e":"#22c55e","#14b8a6":"#14b8a6","#0ea5e9":"#0ea5e9","#6366f1":"#6366f1","#a855f7":"#a855f7","#d946ef":"#d946ef","#ec4899":"#ec4899"}', '', '', ''),
|
||||
('lang', '立项级别', 'charterLevel', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterLevel', '', '', ''),
|
||||
('lang', '立项类型 ', 'charterCategory', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCategory', '', '', ''),
|
||||
('lang', '立项适用市场 ', 'charterMarket', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterMarket', '', '', ''),
|
||||
('lang', '立项状态', 'charterStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterStatus', '', '', ''),
|
||||
('lang', '立项关闭原因 ', 'charterCloseReason', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCloseReason', '', '', ''),
|
||||
('lang', '立项审批结果', 'charterReviewResult', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewResult', '', '', ''),
|
||||
('lang', '立项审批状态', 'charterReviewStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewStatus', '', '', '');
|
||||
|
||||
DROP VIEW IF EXISTS `view_datasource_2`;
|
||||
DROP VIEW IF EXISTS `view_datasource_3`;
|
||||
@@ -13650,6 +13674,8 @@ DROP VIEW IF EXISTS `view_datasource_12`;
|
||||
DROP VIEW IF EXISTS `view_datasource_41`;
|
||||
DROP VIEW IF EXISTS `view_datasource_54`;
|
||||
DROP VIEW IF EXISTS `view_datasource_55`;
|
||||
DROP VIEW IF EXISTS `ztv_projectnotpl`;
|
||||
DROP VIEW IF EXISTS `ztv_tasknotpl`;
|
||||
|
||||
CREATE VIEW `view_datasource_2` AS select `id`,`title` from `zt_story` where `deleted` = '0' and type = 'epic';
|
||||
CREATE VIEW `view_datasource_3` AS select `id`,`title` from `zt_story` where `deleted` = '0' and type = 'requirement';
|
||||
@@ -13662,6 +13688,8 @@ CREATE VIEW `view_datasource_12` AS select `id`,`title` from `zt_productplan` wh
|
||||
CREATE VIEW `view_datasource_41` AS select `id`,`title` from `zt_case` where `deleted` = '0';
|
||||
CREATE VIEW `view_datasource_54` AS select `id`,`name` from `zt_task` where `deleted` = '0' and vision = 'lite';
|
||||
CREATE VIEW `view_datasource_55` AS select `id`,`title` from `zt_feedback` where `deleted` = '0';
|
||||
CREATE VIEW `ztv_projectnotpl` AS select * from `zt_project` where `deleted` = '0' and `isTpl` = 0;
|
||||
CREATE VIEW `ztv_tasknotpl` AS select * from `zt_task` where `deleted` = '0' and `isTpl` = 0;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_durationestimation`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_durationestimation` (
|
||||
@@ -15659,29 +15687,6 @@ CREATE TABLE IF NOT EXISTS `zt_instance` (
|
||||
KEY `k8name` (`k8name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_solution`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_solution` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` char(50) NOT NULL DEFAULT '',
|
||||
`appID` mediumint(8) unsigned NOT NULL DEFAULT 0,
|
||||
`appName` char(50) NOT NULL DEFAULT '',
|
||||
`appVersion` char(20) NOT NULL DEFAULT '',
|
||||
`version` char(50) NOT NULL DEFAULT '',
|
||||
`chart` char(50) NOT NULL DEFAULT '',
|
||||
`cover` varchar(255) NOT NULL DEFAULT '',
|
||||
`desc` text NULL,
|
||||
`introduction` varchar(255) NOT NULL DEFAULT '',
|
||||
`source` char(20) NOT NULL DEFAULT '',
|
||||
`channel` char(20) NOT NULL DEFAULT '',
|
||||
`components` text NULL,
|
||||
`status` char(20) NOT NULL DEFAULT '',
|
||||
`deleted` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`createdBy` char(30) NOT NULL DEFAULT '',
|
||||
`createdAt` datetime NULL,
|
||||
`updatedDate` datetime NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_demandpool`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_demandpool` (
|
||||
`id` int(8) NOT NULL AUTO_INCREMENT,
|
||||
@@ -16168,18 +16173,18 @@ CREATE TABLE IF NOT EXISTS `zt_metric` (
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_metriclib`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_metriclib` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`metricID` mediumint NOT NULL DEFAULT 0,
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`metricID` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`metricCode` varchar(100) NOT NULL DEFAULT '',
|
||||
`system` char(30) NOT NULL DEFAULT '0',
|
||||
`program` char(30) NOT NULL DEFAULT '',
|
||||
`project` char(30) NOT NULL DEFAULT '',
|
||||
`product` char(30) NOT NULL DEFAULT '',
|
||||
`execution` char(30) NOT NULL DEFAULT '',
|
||||
`system` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
||||
`program` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`project` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`product` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`execution` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`code` char(30) NOT NULL DEFAULT '',
|
||||
`pipeline` char(30) NOT NULL DEFAULT '',
|
||||
`repo` char(30) NOT NULL DEFAULT '',
|
||||
`user` text,
|
||||
`user` varchar(30) NOT NULL DEFAULT '',
|
||||
`dept` char(30) NOT NULL DEFAULT '',
|
||||
`year` char(4) NOT NULL DEFAULT '0',
|
||||
`month` char(2) NOT NULL DEFAULT '0',
|
||||
@@ -16192,10 +16197,12 @@ CREATE TABLE IF NOT EXISTS `zt_metriclib` (
|
||||
`deleted` ENUM('0', '1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE INDEX `metricID` ON `zt_metriclib`(`metricID`);
|
||||
CREATE INDEX `metricCode` ON `zt_metriclib`(`metricCode`);
|
||||
CREATE INDEX `date` ON zt_metriclib (date);
|
||||
CREATE INDEX `deleted` ON `zt_metriclib` (`deleted`);
|
||||
CREATE INDEX `metricCode_system_date` ON `zt_metriclib` (`metricCode`, `system`, `date`);
|
||||
CREATE INDEX `metricCode_program_date` ON `zt_metriclib` (`metricCode`, `program`, `date`);
|
||||
CREATE INDEX `metricCode_project_date` ON `zt_metriclib` (`metricCode`, `project`, `date`);
|
||||
CREATE INDEX `metricCode_product_date` ON `zt_metriclib` (`metricCode`, `product`, `date`);
|
||||
CREATE INDEX `metricCode_execution_date` ON `zt_metriclib` (`metricCode`, `execution`, `date`);
|
||||
CREATE INDEX `metricCode_user_date` ON `zt_metriclib` (`metricCode`, `user`, `date`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_duckdbqueue`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_duckdbqueue` (
|
||||
|
||||
+398
@@ -1,3 +1,401 @@
|
||||
2025-07-03 21.7.3
|
||||
完成的需求
|
||||
开源版:
|
||||
75768 优化编辑器固定工具栏中常显的按钮
|
||||
74527 在文档顶部工具栏的更多下拉菜单中打印插入禅道数据按钮
|
||||
74092 文档新编辑器中插入的简单表格支持拆分单元格
|
||||
74080 文档新编辑器中插入的简单表格支持合并单元格
|
||||
企业版:
|
||||
75451 甘特图组件支持通过插件的形式来一次性配置禅道定制样式
|
||||
75138 支持导出文档中插入的看板视图和表格视图中的标题
|
||||
70456 新编辑器文档支持前端直接导出 Word
|
||||
70053 导出文档时可以导出文档中的关键字
|
||||
70050 blocksuite编辑器中/命令插入的附件支持导出
|
||||
70017 文档导出Word支持代码块完整显示及自动分页
|
||||
旗舰版:
|
||||
75810 实现项目模板下甘特图维护任务依赖关系的功能
|
||||
75809 项目模板增加权限设置
|
||||
75320 复制项目的下一步页面重构
|
||||
75305 创建项目页面只展示使用中的项目模板
|
||||
75209 复制项目新增数据逻辑
|
||||
75035 隐藏执行的功能
|
||||
75019 从已有项目创建页面优化
|
||||
74203 由模板创建项目新增数据逻辑
|
||||
74194 实现项目模板下文档的功能
|
||||
74158 实现项目模板下审批的功能
|
||||
73893 实现项目模板的停用功能
|
||||
73892 实现项目模板的发布功能
|
||||
73889 实现项目模板的编辑功能
|
||||
修复的Bug
|
||||
旗舰版:
|
||||
63839 报告模板的二级导航名称错误应该是【报告模板】
|
||||
|
||||
2025-06-27 21.7.2
|
||||
完成的需求
|
||||
开源版:
|
||||
75355 文档模块中面包屑导航优化
|
||||
74089 文档新编辑器中插入的简单表格支持删除列
|
||||
74085 文档新编辑器中插入的简单表格支持删除行
|
||||
74082 文档新编辑器中插入的简单表格支持添加行
|
||||
74081 文档新编辑器中插入的简单表格支持添加列
|
||||
73879 优化执行任务列表中任务获取延期数据的方式
|
||||
73878 优化执行甘特图中任务获取延期数据的方式
|
||||
73876 优化瀑布项目甘特图中任务获取延期数据的方式
|
||||
73757 优化瀑布项目阶段列表中任务获取延期数据的方式
|
||||
73639 优化编辑任务页面性能
|
||||
72782 文档编辑器中支持输入/命令,创建简单表格
|
||||
旗舰版:
|
||||
75848 评审功能升级逻辑优化
|
||||
75826 实现评审对象的维护功能
|
||||
75544 与8种特殊评审对象关联的文档模板删除时增加提醒
|
||||
75357 添加文档模板类型的表单中,删除类型不可改位置的提示
|
||||
75316 升级时,记录检查分类和评审对象之间的关系
|
||||
75314 新安装用户,内置8种文档模板并关联评审对象
|
||||
75310 新安装用户内置文档模板分类
|
||||
75257 项目发起评审表单中,评审内容字段调整
|
||||
75139 从单个项目流程模板复制项目模板的逻辑
|
||||
75132 项目模板创建后,状态为使用中
|
||||
75090 实现项目模板下团队的功能
|
||||
75046 新打的基线详情页用blocksuit渲染
|
||||
75044 评审详情页可以正常渲染系统数据列表
|
||||
75042 文档模板编辑时,根据关联的评审对象限制可插入的筛选器
|
||||
75040 项目模板的设置隐藏区块
|
||||
75039 文档模板升级时,八种特殊模板类型下没有代入系统数据的wiki模板时,内置文档模板
|
||||
75025 项目发起评审选择系统模板生成时,表单逻辑调整
|
||||
74998 升级时,记录历史的文档模板和评审对象之间的关系
|
||||
74996 后台编辑文档模板的权限逻辑调整
|
||||
74969 文档模板类型和项目评审对象解绑
|
||||
74961 修改文档模板的类型语言项为分类
|
||||
74960 文档模板从回收站还原时,提示用户还原所属类型和范围
|
||||
74952 由模板创建的文档中禅道列表支持点击查看详情
|
||||
74949 历史文档模板类型的配置数据升级后放在一级类型下
|
||||
74947 后台增加文档模板维护范围的权限
|
||||
74924 文档插入禅道的业务需求列表时,业务需求记录版本号
|
||||
74923 文档插入禅道的用户需求列表时,用户需求记录版本号
|
||||
74904 OR界面中,文档模板历史数据的升级逻辑
|
||||
74903 模板类型在三个界面的数据做隔离
|
||||
74901 文档模板私有时,不设白名单
|
||||
74900 已有项目创建模板后的数据复制
|
||||
74899 点击项目模板名称进入模板详情
|
||||
74867 使用模板创建项目的数据复制逻辑
|
||||
74866 实现从已有项目创建的页面
|
||||
74865 打印从已有项目创建的按钮
|
||||
74787 隐藏多层级文档模板的代码
|
||||
74733 OR界面增加文档的模板广场功能
|
||||
74732 文档中插入的禅道列表支持点击查看详情
|
||||
74724 文档模板广场增加维护范围的功能
|
||||
74711 运营管理界面增加文档的模板广场功能
|
||||
74692 文档插入禅道的用例列表时,用例记录版本号
|
||||
74691 文档插入禅道的研发需求列表时,研发需求记录版本号
|
||||
74517 其他类型的wiki文档模板升级后用新的编辑器渲染
|
||||
74516 系统测试用例类型的wiki文档模板升级后用新的编辑器渲染
|
||||
74515 集成测试用例类型的wiki文档模板升级后用新的编辑器渲染
|
||||
74514 接口设计说明书类型的wiki文档模板升级后用新的编辑器渲染
|
||||
74513 发起项目计划评审表单增加评审内容字段
|
||||
74512 数据库设计说明书类型的wiki文档模板升级后用新的编辑器渲染
|
||||
74511 详细设计说明书类型的wiki文档模板升级后用新的编辑器渲染
|
||||
74509 概要设计说明书类型的wiki文档模板升级后用新的编辑器渲染
|
||||
74507 软件需求规格说明书类型的wiki文档模板升级后用新的编辑器渲染
|
||||
74452 历史版本的wiki文档模板在模板广场下正常渲染
|
||||
74412 项目计划类型的文档模板升级用新的编辑器渲染
|
||||
74410 新发起的项目计划评审,用blocksuit编辑器渲染成静态甘特图
|
||||
74408 新发起的评审,审计页面左侧区块用blocksuit渲染
|
||||
74407 新发起的评审,评审页面左侧区块用blocksuit渲染
|
||||
74406 新发起的评审,评审详情页左侧区块用blocksuit渲染
|
||||
74385 使用无层级的文档模板来代替原来的wiki类型的文档模板
|
||||
74384 markdown类型的文档模板升级后用新编辑器渲染
|
||||
74383 富文本类型的文档模板升级后用新编辑器渲染
|
||||
74260 研发管理界面中,文档模板跟随系统升级的交互
|
||||
74258 后台文档模板的配置数据升级至文档模板广场下
|
||||
74256 研发管理界面中,删除后台文档模块入口
|
||||
74206 项目/执行/任务统计选择功能过滤掉项目模板中的数据
|
||||
74201 由模板创建项目的创建项目页面改动
|
||||
74198 在创建项目页面增加项目模板
|
||||
74064 由模板创建的文档,发布文档后,未配置的动态区块展示占位符与提示文案
|
||||
73953 打印项目模板中阶段/迭代下的二级菜单
|
||||
73930 打印项目模板配置的二级菜单
|
||||
73891 实现项目模板的删除功能
|
||||
73884 实现项目模板列表功能
|
||||
73883 工作流下流程菜单改名为工作流
|
||||
73881 项目下二级菜单打印项目模板菜单
|
||||
73546 支持点击模版卡片的标题,展示文档模板预览弹窗
|
||||
73374 文档支持修改动态区块的参数配置
|
||||
73373 项目用例筛选器动态区块的配置参数
|
||||
73372 当分组表单选择“项目用例筛选器”时,表单内打印相关配置项
|
||||
73371 当分组表单选择“产品用例筛选器”时,表单内打印相关配置项
|
||||
73370 “/”下拉菜单中,用例筛选器增加集联菜单
|
||||
73360 bug筛选器动态区块的配置参数
|
||||
73359 产品用例筛选器动态区块的配置参数
|
||||
73358 任务筛选器动态区块的配置参数
|
||||
73357 接口设计筛选器动态区块的配置参数
|
||||
73356 数据库设计筛选器动态区块的配置参数
|
||||
73355 详细设计筛选器动态区块的配置参数
|
||||
73354 概要设计筛选器动态区块的配置参数
|
||||
73353 执行研发需求筛选器动态区块的配置参数
|
||||
73352 项目研发需求筛选器动态区块的配置参数
|
||||
73351 产品研发需求筛选器动态区块的配置参数
|
||||
73319 由模板创建的文档打印弹窗
|
||||
73211 若当前系统根据配置无生成的系统数据列表内容,展示空页面
|
||||
73199 由模板创建文档时,动态区块按照配置弹窗以及筛选器展示列表数据
|
||||
73195 由模板创建的文档,静态区块内容与文档模板中静态区块内容保持一致
|
||||
73194 在文档列表页创建按钮组打印“由模板创建文档”按钮
|
||||
73176 文档模板插入禅道数据选择设计筛选器时的表单项配置
|
||||
73052 当分组表单选择“执行研发需求筛选器”时,表单内打印相关配置项
|
||||
73051 当分组表单选择“项目研发需求筛选器”时,表单内打印相关配置项
|
||||
73050 当分组表单选择“产品研发需求筛选器”时,表单内打印相关配置项
|
||||
73047 文档模板支持修改插入的禅道数据
|
||||
73046 文档模板插入禅道数据选择bug筛选器时的表单项配置
|
||||
修复的Bug
|
||||
开源版:
|
||||
56122 目录树中对文档排序后,文档列表没有同步调整
|
||||
57792 文档库下左侧文档名称排序后刷新页面,右侧区域变成了原来的排序
|
||||
61424 导入用例时用例步骤不合法提示到具体行数 (Feedback#8950)
|
||||
61609 文档下导入需求弹窗中搜索结果时标签也高亮了
|
||||
63043 编辑测试单所属执行没有过滤父阶段和没有测试模块的阶段
|
||||
63083 编辑无迭代项目计划开始和计划完成保存时有报错
|
||||
63090 计划需求列表中执行关联需求操作后会导致需求已有排序错乱
|
||||
63120 无产品无迭代项目批量编辑任务时提示无权访问该项目
|
||||
63176 [内禅]文档中的表格丢失,变为空的3*3表格
|
||||
63178 文档升级后表格丢失
|
||||
63181 项目视图接口库下创建接口页面不正确
|
||||
63226 编辑运营界面项目有报错
|
||||
63228 文档面包屑内容较长时不显示切换按钮
|
||||
63229 未开启办公功能,运营界面显示办公模块
|
||||
63240 大桌面退出讨论组失败,禅道php.log有报错
|
||||
63244 已删除干系人详情页有sql报错
|
||||
63245 回收站干系人对象名称显示用户名
|
||||
63256 编辑用例时部分符号被转义了
|
||||
63257 简单表格中emoj表情升级不上来
|
||||
63258 禅道插入emoji表情保存报错
|
||||
63288 批量创建Bug,所属执行没有列出
|
||||
63289 开源版12.5.3升级到开源版21.7.2执行完sql后报错
|
||||
63290 插件安装的提示文案前后不一致
|
||||
63298 创建无产品无迭代项目报错
|
||||
63303 批量编辑任务页面指派给的下拉菜单中,维护团队成功按钮前面缺少“+”
|
||||
63309 编辑产品模块为空格报错
|
||||
63336 开启collabora服务,保存报错
|
||||
63342 使用新dell盒,启动一件包后,admin初始登录提示密码错误
|
||||
63361 创建周期性待办时,按年为周期的周期设置组件报错
|
||||
63400 项目代号长度较长时概况页项目名称和状态等会被挤掉
|
||||
63410 地盘里“欢迎总览”区块指派给我的数据和待处理里的对不上 (Feedback#9570)
|
||||
63423 项目下编辑文档页面刷新后返回到了文档列表
|
||||
63444 进入地盘日程下没有默认高亮标签
|
||||
63446 运营界面的看板和目标导航出现了研发效能插件的授权校验
|
||||
63450 运营界面项目设置团队中下拉框很短
|
||||
63465 指派bug后,地盘贡献页面空白
|
||||
63505 分组视图下批量创建用例后报错
|
||||
63508 执行空的用例没有被记录在action表里
|
||||
63510 后台进入加入禅道社区页面后没有1.5级导航
|
||||
63511 安装禅道加入社区步骤中,没有跳过按钮
|
||||
63512 后台解绑禅道社区后有报错
|
||||
63513 禅道用户体验改进计划弹窗页大小不一致
|
||||
63514 账号绑定禅道后,点击前往社区后没有自动登录官网
|
||||
63516 项目下不显示创建的和关联的bug
|
||||
63517 后台页面有报错
|
||||
63519 后台分配权限页面有报错
|
||||
63521 解绑禅道官网时弹窗空白太大
|
||||
63522 信息不填写也可以加入禅道社区
|
||||
63524 加入禅道社区帮助问号图标没有跳转链接
|
||||
63529 加入禅道社区页面字段没有必填标识,且必填提示信息不明确
|
||||
63533 加入禅道社区的卡片需要优化
|
||||
63534 解绑的弹窗页面按钮样式不正确
|
||||
63535 加入禅道社区后点前往社区没有自动登录官网
|
||||
63538 没有看板列设置权限的用户看板的更多按钮下显示空的下拉列表
|
||||
63540 火狐浏览器看板点击全屏后台报错
|
||||
63545 激活看板的icon显示的是关闭的
|
||||
63559 用例列表的相关需求鼠标悬停显示storyTitle
|
||||
63561 用例切换分组视图后切换1.5级导航报错
|
||||
63565 测试单中场景的指派给弹窗中存在报错
|
||||
63567 安装成功后,提示安装成功的禅道版本不友好
|
||||
63570 禅道用户体验改进计划标题不明显
|
||||
63586 测试单的用例列表不显示关联对象数据
|
||||
63588 测试-bug 编辑模块无法保存 (Feedback#9659)
|
||||
63598 开启debug,关掉session开关,登录页有报错
|
||||
63599 瀑布项目下设置阶段后点击保存报错
|
||||
63600 项目设置权限将成员全部取消勾选保存报错
|
||||
63601 无需求详情权限时,产品动态中需求链接没有置灰
|
||||
63603 开启聊天后,session文件增长过快
|
||||
63604 用户没有权限,地盘动态显示错误
|
||||
63605 安装21.7.2登录页面内容未居中
|
||||
63606 私有文档库和私有文档的操作,不显示动态
|
||||
63607 项目团队同步到执行后项目动态报错
|
||||
63612 用户1没有私有产品权限动态中其动态没有置灰
|
||||
63614 禅道ipd4.1版本安装成功页面提示文案不正确
|
||||
63615 执行下创建机会,没有默认带入执行名称
|
||||
63616 my.php的$config->customSession没有对齐
|
||||
63618 用户未分配研发需求详情权限时,项目动态中研发需求名称没有置灰
|
||||
63619 项目下创建测试报告,项目仪表盘最新动态区块报错
|
||||
63620 用户有项目集所有操作权限但操作列不显示操作按钮
|
||||
63623 没有研发需求详情权限,执行动态下需求名称显示链接
|
||||
63624 检查组织动态今天标签下,数据和显示不一致
|
||||
63625 没有权限项目下的设计动态打印了链接
|
||||
63626 有相关权限时,用户项目动态列表不显示基线评审的动态
|
||||
63627 动态标签下有数据,实际没显示
|
||||
63628 用户没有浏览bug权限时地盘动态列表显示了跳转链接
|
||||
63629 搜索按钮与排序组件没有间距
|
||||
63630 项目集投入人员页面点击1.5级导航下拉后有报错
|
||||
63631 动态收起后再展开,无法显示内容
|
||||
63632 组织动态页面按正序排列后执行搜索,搜索不成功
|
||||
63636 用户动态列表标签右侧数量样式需优化
|
||||
63638 非admin用户进入后台系统设置,有报错闪过
|
||||
63641 项目/执行下测试单列表勾选测试单后统计数据错误
|
||||
63646 执行下勾选测试单,统计数据不正确
|
||||
63650 点击关联需求和关联bug按钮,无反应且有报错
|
||||
63651 执行概况页报错
|
||||
63653 执行视图下树状图中点击对象名称没有反应
|
||||
63666 普通用户全部权限复制项目时未列出对应类型的项目
|
||||
63677 文档列表左侧目录树中排序底部应用刷新两次从目录树中查看文档详情显示没有权限访问
|
||||
63682 事务类型的任务去复制任务保存后无法新增任务 (Feedback#9671)
|
||||
63688 文档编辑器中简单表格不显示中文语言项
|
||||
企业版:
|
||||
63047 关联关系搜索条件选择全部产品/全部项目,搜索内容为空
|
||||
63062 bug解决表总计数据错误
|
||||
63078 提示没有授权,但是没有注明哪一个解决方案
|
||||
63145 执行下甘特图有报错
|
||||
63216 BI-透视表-自定义报表中 查询筛选器中设置默认值无效 (Feedback#9533)
|
||||
63222 不同任务类型统计图下,英文语言项Misc多了一个.
|
||||
63254 安装biz12.1-php7.4,安装完成后,跳转到了升级页面
|
||||
63262 导入的反馈记录了指派时间
|
||||
63263 待处理bug按所有项目搜索内容为空
|
||||
63270 反馈转化的非需求对象显示了所处阶段字段
|
||||
63273 导出工单模板,提示文案有多余符号
|
||||
63274 导出反馈模板,提示文案显示为英文逗号
|
||||
63275 导入的工单没有记录指派给时间
|
||||
63276 导出工单有报错
|
||||
63277 导出工单没有填写文件名称时,文件无法使用
|
||||
63285 没有ioncube时,安装付费禅道,保存账号报错,没有弹出ioncube校验
|
||||
63305 编辑任务时任务关系前后置任务可以设置同一个任务
|
||||
63314 安装禅道失败,提示pivot表主键冲突
|
||||
63399 产品-矩阵 导出需求,相同名称的需求导出数据缺失 (Feedback#9562)
|
||||
63402 工单扩展的必填字段提示重复显示
|
||||
63405 编辑工单修改新增字段信息不生效
|
||||
63418 升级到12.1版本,有sql报错
|
||||
63474 编辑无迭代项目有报错
|
||||
63477 创建无迭代项目,扩展字段有值也提示不能为空
|
||||
63488 反馈中的附件不能删除附件 (Feedback#9585)
|
||||
63552 立项审批的条件中,类型获取不到数据
|
||||
63553 无甘特图权限时,文档编辑器中/选择甘特图有报错
|
||||
63585 实现度量项弹窗页面有报错
|
||||
63589 工作流扩展测试单的子状态,测试单列表状态不显示子状态的值 (Feedback#9494)
|
||||
63591 审批流设置自动通过条件时,发起审批后保存报错 (Feedback#9510)
|
||||
63613 升级有sql报错
|
||||
63656 biz8.8升级到biz12.1有sql报错
|
||||
63686 bug批量导入,反馈者字段没有正常保存到 (Feedback#9634)
|
||||
63707 透视表无法修改筛选器进行查询
|
||||
63736 设计透视表的查询数据页面自定义SQl添加查询筛选器后再删掉会有报错
|
||||
63738 未安装加密扩展的弹窗,样式需优化
|
||||
旗舰版:
|
||||
52793 我的待处理区块的评审提交时间不正确
|
||||
59704 编辑文档发布后,详情页需刷新才显示最新内容
|
||||
59706 模板列表中文档模板标题过长时【草稿】icon会被挤掉
|
||||
59948 没有类型的时候点击列表中间创建模板时没有弹窗提示
|
||||
61234 文档模板中[禅道列表数据配置]分组显示位置不对
|
||||
61254 禅道筛选器配置弹窗中文案不正确
|
||||
61261 模板插入筛选器保存后详情页会显示仪表盘区块
|
||||
61285 模板广场落地页点创建模板直接进入了编辑器
|
||||
61397 编辑模板发布后详情页还是显示编辑前的内容
|
||||
61859 预览模板弹窗中切换版本或模板,url同步更新
|
||||
62084 任务筛选器所属执行为空时不显示必填提示信息
|
||||
62100 筛选器的[选择产品]字段显示为[所属产品]
|
||||
62692 产品/项目研发需求区块,父子需求平铺展示无展开收起按钮
|
||||
62701 流程模板交付物配置页面分割线没有对齐
|
||||
62785 编辑文档模板页面修改模板设置保存后会记录两次历史记录和2个版本
|
||||
62859 面包屑中点主页按钮,维护范围按钮位置变化
|
||||
62886 从已有项目模板创建,二级导航错误
|
||||
62889 从已有项目创建,显示“完善项目信息”
|
||||
62922 中文语言项升级时没有模板的类型不能升级上来
|
||||
62928 模板广场落地页点击创建文档模板按钮,直接进入了编辑器
|
||||
62929 项目模板二级导航下显示了从属于项目的工作流
|
||||
62930 发布态模板存为草稿后,详情页未显示最新的模板标题
|
||||
62934 项目模板设置概况页面最新动态区块的更多按钮建议去掉
|
||||
62952 项目模板阶段列表页导出模板和导入任务时无法选择所属执行
|
||||
62959 测试下提Bug页面所属产品没有过滤无产品无迭代项目模板
|
||||
62960 没有执行权限时文档中插入的任务列表标题不应该打印链接
|
||||
62961 从已有项目创建项目模板,项目模板中的任务编辑页所属执行和模块为空
|
||||
62992 完善项目信息页面隐藏计划开始时间和计划结束时间
|
||||
62997 由模板创建的文档中,禅道筛选器列表导出后显示为链接
|
||||
63013 使用项目模板创建项目,项目流程应置灰
|
||||
63032 删除的类型还原后在范围下底部刷新删除的模板不能显示
|
||||
63035 全局添加任务/执行模板相关数据没有过滤
|
||||
63036 添加任务类型待办没有过滤模板下的任务
|
||||
63037 待处理/贡献-Bug搜索表单所属项目下拉显示了项目模板
|
||||
63038 地盘-项目列表没有显示其他人创建的项目
|
||||
63039 贡献-任务列表、搜索表单所属项目、所属执行下拉显示项目模板数据
|
||||
63040 新增一个从属于项目的自定义工作流,进入流程后1.5和2级流程显示了项目模板的数据
|
||||
63044 组织团队成员没有过滤项目模板下的任务数据
|
||||
63045 组织日志下所属执行下拉无产品无迭代项目缺少分隔线
|
||||
63046 自定义工作流添加数据页面,所属项目下拉显示了项目模板
|
||||
63048 组织团队成员没有过滤项目模板下的执行数据
|
||||
63050 关联对象时可以选到项目模板的数据
|
||||
63054 组织团队成员下点击执行标签后部分标签不显示了
|
||||
63055 通用看板下导入计划/发布/工单页所属产品下拉未过滤影子产品
|
||||
63057 BI大屏/图表中没有过滤项目模板数据
|
||||
63061 动态列表点击项目模板下的不符合项动态链接后跳转到了项目下
|
||||
63063 项目模板下QA列表点击今日检查和本月检查标签后未展示数据
|
||||
63064 无模板的适用所有语言项的类型,升级后不显示
|
||||
63066 团队成员任务/执行列表没有过滤项目模板数据
|
||||
63068 项目模板执行概况页点击燃尽图更多按钮后没有对应导航
|
||||
63080 项目模板中删除的任务在回收站中名称显示错误
|
||||
63084 项目模板下删除迭代后回收站中不显示对象名称
|
||||
63117 项目下创建文档选择其它项目,保存后页面显示不正确
|
||||
63132 项目甘特图下切换为按指派给分组后是否延期和延期天数内容为显示
|
||||
63133 项目模板阶段列表维护交付物选择项目模板下的文档时,无法保存
|
||||
63138 项目限制子任务时间且子任务未设置截止日期父任务设置截止日期时甘特图中子任务没有计算延期天数
|
||||
63144 项目模板执行的二级导航显示了看板
|
||||
63151 通用看板导入执行页面交付物显示异常
|
||||
63156 从已有项目创建模板时完善项目信息页面迭代代号处提示文案错误
|
||||
63163 后台设置不显示瀑布项目下QA时项目模板下有报错
|
||||
63164 后台设置瀑布项目下只显示过程不显示QA时瀑布项目模板下不显示过程菜单
|
||||
63166 项目模板下执行下创建文档,所属库下拉为空,弹窗有报错
|
||||
63173 新增工作流弹窗,字段和必填项标识没显示
|
||||
63174 交付物维护页面文档下拉未过滤掉项目模板下的文档
|
||||
63180 单项目流程项目编辑报错
|
||||
63182 单项目流程模板创建项目,流程模板为通用模板
|
||||
63183 项目权限没有加上项目流程权限
|
||||
63185 创建任务/需求/设计页面相关文档下拉没有过滤项目模板下的文档
|
||||
63196 从已有项目创建项目模板后设置下三级导航未显示项目流程
|
||||
63198 项目迭代列表不显示产品下拉
|
||||
63200 复制单项目流程项目时带出的项目流程不正确
|
||||
63205 项目模板下任务列表页面的报表按钮应隐藏
|
||||
63210 项目模板阶段甘特图下显示了全部产品下拉
|
||||
63221 执行--视图页面代码报错
|
||||
63227 全局搜索项目模板数据结果页有报错
|
||||
63237 项目下启用项目流程时没有复制子表
|
||||
63238 项目模板下自定义工作流设计页面有报错
|
||||
63255 创建项目模板完善信息页提示未修改
|
||||
63266 后台未启用工作量占比但复制项目时完善阶段信息页展示了工作量占比字段
|
||||
63287 通用看板导入执行,显示了项目模板的执行
|
||||
63296 升级后原有项目下项目流程列表有报错
|
||||
63312 创建项目模板时完善阶段信息页选择不复制对应产品的阶段产品保存时控制台有报错
|
||||
63313 创建项目模板时完善阶段信息页切换产品后提示访问功能正在开发中
|
||||
63315 项目模板下删除父任务时没有删除其下的子任务
|
||||
63318 项目模板执行视图下二级导航显示了“项目模板”
|
||||
63319 从已有项目创建模板保存时提示阶段类型不符合格式
|
||||
63322 项目模板项目视图下切换为有所属项目集的模板后二级导航显示“项目”
|
||||
63327 瀑布项目模板下阶段列表相关产品错误
|
||||
63337 批量添加待办,提示字段必须是数字
|
||||
63345 创建文档模板提示先创建分类,但是没有创建分类的入口
|
||||
63360 无迭代项目模板任务列表切换1.5级导航后二级导航菜单未高亮
|
||||
63362 复制项目和按项目模板创建的项目,状态为进行中
|
||||
63363 项目下没有迭代,创建模板完善项目信息点击复制完成没反应
|
||||
63367 复制按产品创建的瀑布项目时完善阶段信息页面有报错
|
||||
63369 可用工作日提示不能超过1天
|
||||
63372 点击项目模板1.5级的homeBtn,跳转到了项目列表页
|
||||
63373 从无迭代项目模板切换到融合项目模板后,页面显示不正确
|
||||
63387 从已有项目创建项目模板完善项目模板信息的英文语言项无空格
|
||||
63396 后台敏捷模型配置页面内置的活动/文档数据不显示
|
||||
63398 无产品无迭代项目模板任务列表页导入任务不成功
|
||||
IPD版:
|
||||
63162 调研任务列表延期标识和详情页不一致
|
||||
63393 OR界面添加产品页面控件布局建议优化
|
||||
63485 创建ipd项目时关联的路标中的需求没有自动关联到项目中
|
||||
63546 阶段下的任务没有关闭,阶段可以正常关闭没有提示信息 (Feedback#9630)
|
||||
63547 IPD项目串行模式,大阶段下的全部子阶段关闭直接大阶段也关闭,不受评审点限制。 (Feedback#9626)
|
||||
63548 ipd模型项目阶段设置不允许并行,上个大阶段没有关闭,下一大阶段虽然不能直接开始,但是任务可以开始,同时带动大阶段也开始。 (Feedback#9625)
|
||||
63714 移动文档库页面有报错
|
||||
63717 OR界面移动库,所属空间列出了产品空间
|
||||
63721 阅读次数字段升降序按钮显示不全
|
||||
|
||||
2025-05-30 21.7.1
|
||||
完成的需求
|
||||
开源版:
|
||||
|
||||
@@ -23,11 +23,11 @@ $lang->mainNav->admin = "{$lang->navIcons['admin']} {$lang->admin->common}|a
|
||||
if($config->edition != 'open')
|
||||
{
|
||||
$lang->navIcons['feedback'] = "<i class='icon icon-feedback'></i>";
|
||||
$lang->navIcons['oa'] = "<i class='icon icon-oa'></i>";
|
||||
if(helper::hasFeature('OA')) $lang->navIcons['oa'] = "<i class='icon icon-oa'></i>";
|
||||
$lang->navIcons['workflow'] = "<i class='icon icon-flow'></i>";
|
||||
|
||||
$lang->mainNav->feedback = $lang->navIcons['feedback'] . ' 反馈|feedback|browse|browseType=unclosed';
|
||||
$lang->mainNav->oa = $lang->navIcons['oa'] . ' 办公|attend|personal|';
|
||||
if(helper::hasFeature('OA')) $lang->mainNav->oa = $lang->navIcons['oa'] . ' 办公|attend|personal|';
|
||||
$lang->mainNav->workflow = $lang->navIcons['workflow'] . ' 工作流|workflow|browseFlow|';
|
||||
|
||||
if($config->visions == ',lite,') unset($lang->mainNav->feedback);
|
||||
@@ -146,6 +146,13 @@ $lang->doc->menuOrder[15] = 'my';
|
||||
$lang->doc->menuOrder[20] = 'custom';
|
||||
$lang->doc->menuOrder[25] = 'project';
|
||||
|
||||
if(strpos(',max,ipd,', $config->edition) !== false)
|
||||
{
|
||||
$lang->doc->menu->template = array('link' => "模板广场|doc|browseTemplate|", 'alias' => 'browsetemplate');
|
||||
$lang->doc->menuOrder[30] = 'template';
|
||||
$lang->doc->dividerMenu .= ',template,';
|
||||
}
|
||||
|
||||
/* Admin menu. */
|
||||
$lang->admin->menu = new stdclass();
|
||||
|
||||
|
||||
@@ -570,31 +570,41 @@ $lang->task->methodOrder[125] = 'import';
|
||||
|
||||
/* Doc. */
|
||||
$lang->resource->doc = new stdclass();
|
||||
$lang->resource->doc->index = 'index';
|
||||
$lang->resource->doc->mySpace = 'mySpace';
|
||||
$lang->resource->doc->quick = 'quick';
|
||||
$lang->resource->doc->createSpace = 'createSpace';
|
||||
$lang->resource->doc->createLib = 'createLibAction';
|
||||
$lang->resource->doc->editLib = 'editLibAction';
|
||||
$lang->resource->doc->deleteLib = 'deleteLibAction';
|
||||
$lang->resource->doc->moveLib = 'moveLibAction';
|
||||
$lang->resource->doc->moveDoc = 'moveDocAction';
|
||||
$lang->resource->doc->create = 'create';
|
||||
$lang->resource->doc->view = 'view';
|
||||
$lang->resource->doc->edit = 'edit';
|
||||
$lang->resource->doc->delete = 'delete';
|
||||
$lang->resource->doc->deleteFile = 'deleteFile';
|
||||
$lang->resource->doc->collect = 'collectAction';
|
||||
$lang->resource->doc->projectSpace = 'projectSpace';
|
||||
$lang->resource->doc->teamSpace = 'teamSpace';
|
||||
$lang->resource->doc->showFiles = 'showFiles';
|
||||
$lang->resource->doc->addCatalog = 'addCatalog';
|
||||
$lang->resource->doc->editCatalog = 'editCatalog';
|
||||
$lang->resource->doc->sortDoclib = 'sortDoclib';
|
||||
$lang->resource->doc->sortCatalog = 'sortCatalog';
|
||||
$lang->resource->doc->sortDoc = 'sortDoc';
|
||||
$lang->resource->doc->deleteCatalog = 'deleteCatalog';
|
||||
// $lang->resource->doc->displaySetting = 'displaySetting';
|
||||
$lang->resource->doc->index = 'index';
|
||||
$lang->resource->doc->mySpace = 'mySpace';
|
||||
$lang->resource->doc->quick = 'quick';
|
||||
$lang->resource->doc->createSpace = 'createSpace';
|
||||
$lang->resource->doc->createLib = 'createLibAction';
|
||||
$lang->resource->doc->editLib = 'editLibAction';
|
||||
$lang->resource->doc->deleteLib = 'deleteLibAction';
|
||||
$lang->resource->doc->moveLib = 'moveLibAction';
|
||||
$lang->resource->doc->moveDoc = 'moveDocAction';
|
||||
$lang->resource->doc->create = 'create';
|
||||
$lang->resource->doc->view = 'view';
|
||||
$lang->resource->doc->edit = 'edit';
|
||||
$lang->resource->doc->delete = 'delete';
|
||||
$lang->resource->doc->deleteFile = 'deleteFile';
|
||||
$lang->resource->doc->collect = 'collectAction';
|
||||
$lang->resource->doc->projectSpace = 'projectSpace';
|
||||
$lang->resource->doc->teamSpace = 'teamSpace';
|
||||
$lang->resource->doc->showFiles = 'showFiles';
|
||||
$lang->resource->doc->addCatalog = 'addCatalog';
|
||||
$lang->resource->doc->editCatalog = 'editCatalog';
|
||||
$lang->resource->doc->sortDoclib = 'sortDoclib';
|
||||
$lang->resource->doc->sortCatalog = 'sortCatalog';
|
||||
$lang->resource->doc->sortDoc = 'sortDoc';
|
||||
$lang->resource->doc->deleteCatalog = 'deleteCatalog';
|
||||
$lang->resource->doc->browseTemplate = 'browseTemplate';
|
||||
$lang->resource->doc->createTemplate = 'createTemplate';
|
||||
$lang->resource->doc->editTemplate = 'editTemplate';
|
||||
$lang->resource->doc->moveTemplate = 'moveTemplate';
|
||||
$lang->resource->doc->sortTemplate = 'sortTemplate';
|
||||
$lang->resource->doc->deleteTemplate = 'deleteTemplate';
|
||||
$lang->resource->doc->viewTemplate = 'viewTemplate';
|
||||
$lang->resource->doc->addTemplateType = 'addTemplateType';
|
||||
$lang->resource->doc->editTemplateType = 'editTemplateType';
|
||||
$lang->resource->doc->deleteTemplateType = 'deleteTemplateType';
|
||||
$lang->resource->doc->manageScope = 'manageScope';
|
||||
|
||||
$lang->doc->methodOrder[5] = 'index';
|
||||
$lang->doc->methodOrder[10] = 'mySpace';
|
||||
|
||||
@@ -38,6 +38,7 @@ if(count($teams2Import) > 1)
|
||||
set::value($copyProjectID),
|
||||
set::items($teams2Import),
|
||||
set('data-placeholder', $lang->project->copyTeamTitle),
|
||||
set::width('200px'),
|
||||
on::change('choseTeam2Copy')
|
||||
)
|
||||
);
|
||||
@@ -61,6 +62,7 @@ featureBar
|
||||
set::name('dept'),
|
||||
set::value($dept),
|
||||
set::items($depts),
|
||||
set::width('200px'),
|
||||
set('data-placeholder', $lang->execution->selectDeptTitle),
|
||||
on::change('setDeptUsers')
|
||||
),
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
helper::importControl('story');
|
||||
class myStory extends story
|
||||
{
|
||||
/**
|
||||
* 关闭需求。
|
||||
* Close the story.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $from
|
||||
* @param string $storyType
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function close(int $storyID, string $from = '', string $storyType = 'story')
|
||||
{
|
||||
foreach($this->lang->{$storyType}->reasonList as $key => $value)
|
||||
{
|
||||
if(!in_array($key, array('done', 'duplicate', 'cancel'))) unset($this->lang->{$storyType}->reasonList[$key]);
|
||||
}
|
||||
|
||||
return parent::close($storyID, $from, $storyType);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
foreach($reasonList as $key => $value)
|
||||
{
|
||||
if(!in_array($key, array('done', 'duplicate', 'cancel'))) unset($reasonList[$key]);
|
||||
}
|
||||
@@ -317,34 +317,6 @@ function formatTime(string|null $time, string $format = ''): string
|
||||
return trim($time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把一个或多个数组附加到第一个数组,用于替换数组 + 运算符。
|
||||
* Append one or more arrays to the first array, used to replace the array + operator.
|
||||
*
|
||||
* reference: https://www.php.net/manual/en/language.operators.array.php.
|
||||
*
|
||||
* @param array ...$args
|
||||
* @return array
|
||||
*/
|
||||
function arrayUnion(...$args): array
|
||||
{
|
||||
$args = array_filter($args, function($arg){return is_array($arg);});
|
||||
|
||||
$count = count($args);
|
||||
if($count == 0) return [];
|
||||
if($count == 1) return reset($args);
|
||||
|
||||
$result = array_shift($args);
|
||||
foreach($args as $arg)
|
||||
{
|
||||
foreach($arg as $key => $value)
|
||||
{
|
||||
if(!isset($result[$key])) $result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix for session error.
|
||||
*
|
||||
|
||||
@@ -833,19 +833,6 @@ class baseHelper
|
||||
|
||||
session_write_close();
|
||||
session_id($sessionID);
|
||||
if(ini_get('session.save_handler') == 'user' and isset($_GET['tid']))
|
||||
{
|
||||
$ztSessionHandler = new ztSessionHandler($_GET['tid']);
|
||||
session_set_save_handler(
|
||||
$ztSessionHandler->open(...),
|
||||
$ztSessionHandler->close(...),
|
||||
$ztSessionHandler->read(...),
|
||||
$ztSessionHandler->write(...),
|
||||
$ztSessionHandler->destroy(...),
|
||||
$ztSessionHandler->gc(...)
|
||||
);
|
||||
register_shutdown_function('session_write_close');
|
||||
}
|
||||
session_start();
|
||||
|
||||
global $app;
|
||||
@@ -1473,3 +1460,31 @@ if(!interface_exists('JsonSerializable'))
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把一个或多个数组附加到第一个数组,用于替换数组 + 运算符。
|
||||
* Append one or more arrays to the first array, used to replace the array + operator.
|
||||
*
|
||||
* reference: https://www.php.net/manual/en/language.operators.array.php.
|
||||
*
|
||||
* @param array ...$args
|
||||
* @return array
|
||||
*/
|
||||
function arrayUnion(...$args): array
|
||||
{
|
||||
$args = array_filter($args, function($arg){return is_array($arg);});
|
||||
|
||||
$count = count($args);
|
||||
if($count == 0) return [];
|
||||
if($count == 1) return reset($args);
|
||||
|
||||
$result = array_shift($args);
|
||||
foreach($args as $arg)
|
||||
{
|
||||
foreach($arg as $key => $value)
|
||||
{
|
||||
if(!isset($result[$key])) $result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -342,6 +342,15 @@ class baseRouter
|
||||
*/
|
||||
public $mao;
|
||||
|
||||
/**
|
||||
* 性能分析的方式。
|
||||
* The profiling method.
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
public $profiling;
|
||||
|
||||
/**
|
||||
* 从数据库的句柄。
|
||||
* The slave database handler.
|
||||
@@ -856,11 +865,16 @@ class baseRouter
|
||||
*/
|
||||
protected function setupProfiling(): void
|
||||
{
|
||||
if(!empty($this->config->debug) && $this->config->debug >= 3 && $this->config->installed)
|
||||
{
|
||||
$this->dbh->exec('SET profiling_history_size = 200');
|
||||
$this->dbh->exec('SET profiling = 1');
|
||||
}
|
||||
if(empty($this->config->debug) || $this->config->debug < 3 || empty($this->config->installed)) return;
|
||||
|
||||
$performanceSchema = $this->dbh->query("SHOW VARIABLES LIKE 'performance_schema'")->fetch();
|
||||
$performanceSwitch = $performanceSchema->Value ?? 'OFF';
|
||||
$this->profiling = $performanceSwitch == 'ON' ? 'performance_schema' : 'show_profiles';
|
||||
|
||||
if($this->profiling != 'show_profiles') return;
|
||||
|
||||
$this->dbh->exec('SET profiling = 1');
|
||||
$this->dbh->exec('SET profiling_history_size = 200');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1206,28 +1220,23 @@ class baseRouter
|
||||
{
|
||||
if(defined('SESSION_STARTED')) return;
|
||||
|
||||
if(ini_get('session.save_handler') == 'files' and isset($_GET['tid']))
|
||||
if($this->config->customSession) session_save_path($this->getTmpRoot() . 'session');
|
||||
if(ini_get('session.save_handler') == 'files')
|
||||
{
|
||||
$savePath = ini_get('session.save_path');
|
||||
$writable = is_writable($savePath);
|
||||
if(!$writable)
|
||||
{
|
||||
$savePath = $this->getTmpRoot() . 'session';
|
||||
if(!is_dir($savePath)) mkdir($savePath, 0777, true);
|
||||
$writable = is_writable($savePath);
|
||||
if($writable) session_save_path($this->getTmpRoot() . 'session');
|
||||
}
|
||||
$savePath = $this->getTmpRoot() . 'session';
|
||||
if(!is_dir($savePath)) mkdir($savePath, 0777, true);
|
||||
|
||||
if($writable)
|
||||
if(is_writable($savePath))
|
||||
{
|
||||
$ztSessionHandler = new ztSessionHandler($_GET['tid']);
|
||||
session_save_path($this->getTmpRoot() . 'session');
|
||||
$ztSessionHandler = new ztSessionHandler();
|
||||
session_set_save_handler(
|
||||
$ztSessionHandler->open(...),
|
||||
$ztSessionHandler->close(...),
|
||||
$ztSessionHandler->read(...),
|
||||
$ztSessionHandler->write(...),
|
||||
$ztSessionHandler->destroy(...),
|
||||
$ztSessionHandler->gc(...)
|
||||
array($ztSessionHandler, 'open'),
|
||||
array($ztSessionHandler, 'close'),
|
||||
array($ztSessionHandler, 'read'),
|
||||
array($ztSessionHandler, 'write'),
|
||||
array($ztSessionHandler, 'destroy'),
|
||||
array($ztSessionHandler, 'gc')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1235,7 +1244,6 @@ class baseRouter
|
||||
$sessionName = $this->config->sessionVar;
|
||||
session_name($sessionName);
|
||||
session_set_cookie_params(0, $this->config->webRoot, '', $this->config->cookieSecure, true);
|
||||
if($this->config->customSession) session_save_path($this->getTmpRoot() . 'session');
|
||||
if(!session_id()) session_start();
|
||||
|
||||
$this->sessionID = isset($ztSessionHandler) ? $ztSessionHandler->getSessionID() : session_id();
|
||||
@@ -3406,7 +3414,7 @@ class baseRouter
|
||||
foreach(dbh::$queries as $key => $query)
|
||||
{
|
||||
fwrite($fh, " $query\n");
|
||||
fwrite($fh, ' └' . dbh::$traces[$key] . "\n");
|
||||
if(!empty(dbh::$traces)) fwrite($fh, ' └' . (dbh::$traces[$key] ?? '') . "\n");
|
||||
}
|
||||
fwrite($fh, "\n");
|
||||
fclose($fh);
|
||||
@@ -3540,31 +3548,45 @@ class baseRouter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* config类。
|
||||
* The config class.
|
||||
*
|
||||
* @package framework
|
||||
*/
|
||||
class config
|
||||
if(version_compare(PHP_VERSION, '8.2.0', '>='))
|
||||
{
|
||||
#[\AllowDynamicProperties] // https://www.php.net/manual/zh/class.allowdynamicproperties.php
|
||||
class config
|
||||
{
|
||||
public function set(string $key, $value)
|
||||
{
|
||||
helper::setMember('config', $key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* 设置成员变量,成员可以是'db.user'类似的格式。
|
||||
* Set the value of a member. the member can be the format like db.user.
|
||||
* config类。
|
||||
* The config class.
|
||||
*
|
||||
* <code>
|
||||
* <?php
|
||||
* $config->set('db.user', 'wwccss');
|
||||
* ?>
|
||||
* </code>
|
||||
* @param string $key the key of the member
|
||||
* @param mixed $value the value
|
||||
* @access public
|
||||
* @return void
|
||||
* @package framework
|
||||
*/
|
||||
public function set(string $key, $value)
|
||||
class config
|
||||
{
|
||||
helper::setMember('config', $key, $value);
|
||||
/**
|
||||
* 设置成员变量,成员可以是'db.user'类似的格式。
|
||||
* Set the value of a member. the member can be the format like db.user.
|
||||
*
|
||||
* <code>
|
||||
* <?php
|
||||
* $config->set('db.user', 'wwccss');
|
||||
* ?>
|
||||
* </code>
|
||||
* @param string $key the key of the member
|
||||
* @param mixed $value the value
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function set(string $key, $value)
|
||||
{
|
||||
helper::setMember('config', $key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3795,18 +3817,15 @@ class ztSessionHandler
|
||||
public $sessionFile;
|
||||
public $sessionID;
|
||||
public $sessionName;
|
||||
public $rawFile;
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param string $tagID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(public string $tagID = '')
|
||||
public function __construct()
|
||||
{
|
||||
ini_set('session.save_handler', 'files');
|
||||
register_shutdown_function('session_write_close');
|
||||
}
|
||||
|
||||
@@ -3828,19 +3847,15 @@ class ztSessionHandler
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getSessionFile(string $id): string
|
||||
public function getSessionFile($id): string
|
||||
{
|
||||
if(!empty($this->sessionFile)) return $this->sessionFile;
|
||||
if(!preg_match('/^\w+$/', $id)) return false;
|
||||
|
||||
$sessionID = $id;
|
||||
if($this->tagID) $sessionID = md5($id . $this->tagID);
|
||||
|
||||
$fileName = "sess_$sessionID";
|
||||
$fileName = "sess_{$id}";
|
||||
|
||||
$this->sessionFile = $this->sessSavePath . DS . $fileName;
|
||||
$this->sessionID = $sessionID;
|
||||
$this->rawFile = $this->sessSavePath . DS . "sess_$id";
|
||||
$this->sessionID = $id;
|
||||
return $this->sessionFile;
|
||||
}
|
||||
|
||||
@@ -3852,7 +3867,7 @@ class ztSessionHandler
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function open(string $savePath, string $sessionName): bool
|
||||
public function open($savePath, $sessionName): bool
|
||||
{
|
||||
$this->sessSavePath = $savePath;
|
||||
$this->sessionName = $sessionName;
|
||||
@@ -3877,18 +3892,11 @@ class ztSessionHandler
|
||||
* @access public
|
||||
* @return string|false
|
||||
*/
|
||||
public function read(string $id): string|false
|
||||
public function read($id): string|false
|
||||
{
|
||||
$sessFile = $this->getSessionFile($id);
|
||||
if(!$sessFile) return false;
|
||||
if(file_exists($sessFile)) return file_get_contents($sessFile);
|
||||
|
||||
if($this->tagID and file_exists($this->rawFile))
|
||||
{
|
||||
copy($this->rawFile, $sessFile);
|
||||
return file_get_contents($sessFile);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -3900,24 +3908,16 @@ class ztSessionHandler
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function write(string $id, string $sessData): bool
|
||||
public function write($id, $sessData): bool
|
||||
{
|
||||
$sessFile = $this->getSessionFile($id);
|
||||
if(!$sessFile) return false;
|
||||
if(!$sessFile) return true;
|
||||
|
||||
if(!is_file($sessFile) && !touch($sessFile)) return true;
|
||||
if(!is_writable($sessFile)) return true;
|
||||
if(md5_file($sessFile) == md5($sessData)) return true;
|
||||
|
||||
if(file_put_contents($sessFile, $sessData, LOCK_EX))
|
||||
{
|
||||
if(str_contains($sessData, 'user|'))
|
||||
{
|
||||
$rawSessContent = (string) file_get_contents($this->rawFile, false, null, 0, 1024 * 2);
|
||||
if(!str_contains($rawSessContent, 'user|')) file_put_contents($this->rawFile, $sessData, LOCK_EX);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if(file_put_contents($sessFile, $sessData, LOCK_EX)) return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3927,11 +3927,10 @@ class ztSessionHandler
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function destroy(string $id): bool
|
||||
public function destroy($id): bool
|
||||
{
|
||||
$sessFile = $this->getSessionFile($id);
|
||||
if(file_exists($sessFile)) unlink($sessFile);
|
||||
if(file_exists($this->rawFile)) unlink($this->rawFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3943,16 +3942,15 @@ class ztSessionHandler
|
||||
* @access public
|
||||
* @return int|false
|
||||
*/
|
||||
public function gc(int $maxlifeTime): int|false
|
||||
public function gc($maxlifeTime): int|false
|
||||
{
|
||||
$time = time();
|
||||
$count = 0;
|
||||
foreach(glob("$this->sessSavePath/sess_*") as $fileName)
|
||||
foreach(glob("{$this->sessSavePath}/sess_*") as $fileName)
|
||||
{
|
||||
if(is_writable($fileName) and filemtime($fileName) + $maxlifeTime < $time)
|
||||
if(filemtime($fileName) + $maxlifeTime < $time)
|
||||
{
|
||||
if(!unlink($fileName)) return false;
|
||||
$count++;
|
||||
if(unlink($fileName)) $count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -534,6 +534,16 @@ class control extends baseControl
|
||||
|
||||
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object);
|
||||
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID);
|
||||
|
||||
/* 复制项目时显示被复制项目的工作流字段值。*/
|
||||
if($moduleName == 'project' && $methodName == 'create')
|
||||
{
|
||||
foreach($fieldList as $field)
|
||||
{
|
||||
$objectValue = zget($object, $field->field, '');
|
||||
if($objectValue) $field->default = $objectValue;
|
||||
}
|
||||
}
|
||||
return $this->loadModel('flow')->buildFormFields($fields, $fieldList, array(), $object);
|
||||
}
|
||||
|
||||
|
||||
+18
-36
@@ -810,21 +810,31 @@ function getVisions(): array
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any message with any type to the php log file which prefix with 'php.<today>'.
|
||||
* Save debug log to the php log file which prefix with 'debug.<today>'.
|
||||
*
|
||||
* @param mixed $message
|
||||
* @param string $file
|
||||
* @param int $line
|
||||
* @param mixed $message
|
||||
* @return void
|
||||
*/
|
||||
function debug($message = '', $file = 'undefined', $line = 0)
|
||||
function debug($message = ''): void
|
||||
{
|
||||
if(!is_string($message)) $message = (string)json_encode($message, JSON_PRETTY_PRINT);
|
||||
static $times = [];
|
||||
$time = microtime(true);
|
||||
$duration = $times ? $time - end($times) : 0;
|
||||
$times[] = $time;
|
||||
|
||||
if(empty($file) || $file == 'undefined') $message .= (new Exception())->getTraceAsString();
|
||||
static $counts = [];
|
||||
$count = $counts ? end($counts) + 1 : 1;
|
||||
$counts[] = $count;
|
||||
|
||||
global $app;
|
||||
$app->saveError(E_USER_WARNING, $message, $file, $line);
|
||||
$logFile = $app->getLogRoot() . 'debug.' . date('Ymd') . '.log.php';
|
||||
$uid = $_SERVER['HTTP_X_ZIN_UID'] ?? '';
|
||||
$count = sprintf('%04d', $count);
|
||||
$time = date('H:i:s');
|
||||
$duration = round($duration, 3);
|
||||
if($duration < 0.001) $duration = 0.001;
|
||||
if(is_object($message) || is_array($message)) $message = json_encode($message, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
file_put_contents($logFile, $count . ($uid ? " uid=$uid" : '') . " time=$time duration={$duration}s message=$message\n", FILE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -859,31 +869,3 @@ function addPrefixToField(&$data, $fieldName, $suffix = '. ')
|
||||
$key++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把一个或多个数组附加到第一个数组,用于替换数组 + 运算符。
|
||||
* Append one or more arrays to the first array, used to replace the array + operator.
|
||||
*
|
||||
* reference: https://www.php.net/manual/en/language.operators.array.php.
|
||||
*
|
||||
* @param array ...$args
|
||||
* @return array
|
||||
*/
|
||||
function arrayUnion(...$args): array
|
||||
{
|
||||
$args = array_filter($args, function($arg){return is_array($arg);});
|
||||
|
||||
$count = count($args);
|
||||
if($count == 0) return [];
|
||||
if($count == 1) return reset($args);
|
||||
|
||||
$result = array_shift($args);
|
||||
foreach($args as $arg)
|
||||
{
|
||||
foreach($arg as $key => $value)
|
||||
{
|
||||
if(!isset($result[$key])) $result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
+157
-44
@@ -163,6 +163,15 @@ class baseDAO
|
||||
*/
|
||||
public $autoLang;
|
||||
|
||||
/**
|
||||
* 是否自动过滤模板数据。
|
||||
* If auto filter template data.
|
||||
*
|
||||
* @var string skip(本次不过滤)|always(总是过滤)|never(从不过滤)
|
||||
* @access public
|
||||
*/
|
||||
public static $filterTpl = 'always';
|
||||
|
||||
/**
|
||||
* 上一次插入的数据id。
|
||||
* Last insert id.
|
||||
@@ -179,7 +188,7 @@ class baseDAO
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
static public $querys = array();
|
||||
public static $querys = array();
|
||||
|
||||
/**
|
||||
* 执行fetchAll是否跳过text类型字段。
|
||||
@@ -188,7 +197,7 @@ class baseDAO
|
||||
* @var bool
|
||||
* @access public
|
||||
*/
|
||||
static public $autoExclude = true;
|
||||
public static $autoExclude = true;
|
||||
|
||||
/**
|
||||
* 存放错误的数组。
|
||||
@@ -197,7 +206,7 @@ class baseDAO
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
static public $errors = array();
|
||||
public static $errors = array();
|
||||
|
||||
/**
|
||||
* 实时记录日志设置,并设置记录文件。
|
||||
@@ -206,8 +215,8 @@ class baseDAO
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
static public $realTimeLog = false;
|
||||
static public $realTimeFile = '';
|
||||
public static $realTimeLog = false;
|
||||
public static $realTimeFile = '';
|
||||
|
||||
/**
|
||||
* 缓存已经查询过的表结构。
|
||||
@@ -301,6 +310,21 @@ class baseDAO
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置过滤模板数据的方式。
|
||||
* Set the way to filter template data.
|
||||
*
|
||||
* @param string $method skip(本次不过滤)|always(总是过滤)|never(从不过滤)
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function filterTpl($method = 'always')
|
||||
{
|
||||
if($method == 'skip' && dao::$filterTpl == 'never') return $this;
|
||||
dao::$filterTpl = $method;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置属性。
|
||||
* Reset the vars.
|
||||
@@ -820,13 +844,16 @@ class baseDAO
|
||||
* 处理sql语句,替换表和字段。
|
||||
* Process the sql, replace the table, fields.
|
||||
*
|
||||
* @param string $setIsTpl
|
||||
* @access public
|
||||
* @return string the sql string after process.
|
||||
*/
|
||||
public function processSQL()
|
||||
public function processSQL($filterTpl = true)
|
||||
{
|
||||
$sql = $this->sqlobj->get();
|
||||
|
||||
$needFilterTpl = $filterTpl && empty($this->app->installing) && empty($this->app->upgrading);
|
||||
|
||||
/* INSERT INTO table VALUES(...) */
|
||||
if($this->method == 'insert' and !empty($this->sqlobj->data))
|
||||
{
|
||||
@@ -858,6 +885,52 @@ class baseDAO
|
||||
|
||||
$sql .= '(`' . implode('`,`', array_keys($values)) . '`)' . ' VALUES(' . implode(',', $values) . ')';
|
||||
}
|
||||
elseif($this->method == 'select' && dao::$filterTpl == 'always' && $needFilterTpl)
|
||||
{
|
||||
/* 过滤模板类型的数据 */
|
||||
foreach(array('project', 'task') as $table)
|
||||
{
|
||||
$table = $this->config->db->prefix . $table;
|
||||
if(strpos($sql, "`$table`") === false) continue;
|
||||
|
||||
if(preg_match("/`isTpl`\s*=\s*('1'|1)/", $sql) || preg_match("/isTpl\s*=\s*('1'|1)/", $sql)) continue; // 指定查询模板类型的数据则不过滤
|
||||
preg_match_all('/\(\s*(SELECT\b.*?\bFROM\b.*?)(?=\)\s*(AND|\)|$))/is', $sql, $matches); // 匹配子查询
|
||||
if(!$matches[1])
|
||||
{
|
||||
$alias = preg_match("/`$table`\s+as\s+(\w+)/i", $sql, $matches) ? $matches[1] : '';
|
||||
|
||||
$replace = $alias ? "wHeRe ($alias.`isTpl` = '0' OR $alias.`isTpl` IS NULL) AND" : "wHeRe (`isTpl` = '0' OR `isTpl` IS NULL) AND";
|
||||
$sql = preg_replace("/wHeRE/i", $replace, $sql, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($matches[1] as $index => $subSQL)
|
||||
{
|
||||
$sql = str_ireplace($subSQL, "$$index", $sql);
|
||||
}
|
||||
|
||||
if(strpos($sql, "`$table`") !== false)
|
||||
{
|
||||
$alias = preg_match("/`$table`\s+as\s+(\w+)/i", $sql, $mainMatches) ? $mainMatches[1] : '';
|
||||
$replace = $alias ? "wHeRe ($alias.`isTpl` = '0' OR $alias.`isTpl` IS NULL) AND" : "wHeRe (`isTpl` = '0' OR `isTpl` IS NULL) AND";
|
||||
$sql = preg_replace("/wHeRE/i", $replace, $sql, 1);
|
||||
}
|
||||
|
||||
foreach($matches[1] as $index => $subSQL)
|
||||
{
|
||||
if(strpos($sql, "`$table`") !== false && !preg_match("/`isTpl`\s*=\s*('1'|1)/", $subSQL))
|
||||
{
|
||||
$alias = preg_match("/`$table`\s+as\s+(\w+)/i", $subSQL, $subMatches) ? $subMatches[1] : '';
|
||||
$replace = $alias ? "wHeRe ($alias.`isTpl` = '0' OR $alias.`isTpl` IS NULL) AND" : "wHeRe (`isTpl` = '0' OR `isTpl` IS NULL) AND";
|
||||
$subSQL = preg_replace("/wHeRE/i", $replace, $subSQL, 1);
|
||||
}
|
||||
$sql = str_ireplace("$$index", $subSQL, $sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dao::$filterTpl == 'skip') dao::$filterTpl = 'always';
|
||||
|
||||
/**
|
||||
* 如果是magic模式,处理表和字段。
|
||||
@@ -923,7 +996,7 @@ class baseDAO
|
||||
* @access public
|
||||
* @return string the sql string.
|
||||
*/
|
||||
static public function processKeywords($sql)
|
||||
public static function processKeywords($sql)
|
||||
{
|
||||
return str_replace(array(DAO::WHERE, DAO::GROUPBY, DAO::HAVING, DAO::ORDERBY, DAO::LIMIT), array('WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT'), $sql);
|
||||
}
|
||||
@@ -1224,7 +1297,7 @@ class baseDAO
|
||||
*/
|
||||
public function fetch($field = '')
|
||||
{
|
||||
$sql = $this->processSQL();
|
||||
$sql = $this->processSQL(false);
|
||||
$key = $this->createCacheKey('fetch', md5($sql));
|
||||
$result = $this->getCache($key);
|
||||
if($result === self::CACHE_MISS)
|
||||
@@ -1689,6 +1762,8 @@ class baseDAO
|
||||
{
|
||||
foreach($replaces as $replace)
|
||||
{
|
||||
if(is_array($replace)) $replace = implode(',', $replace);
|
||||
|
||||
$pos = strpos($error, '%s');
|
||||
if($pos === false) break;
|
||||
$error = substr($error, 0, $pos) . $replace . substr($error, $pos + 2);
|
||||
@@ -1851,10 +1926,8 @@ class baseDAO
|
||||
*/
|
||||
public function getProfiles()
|
||||
{
|
||||
$profiles = [];
|
||||
$performanceSchema = $this->query("SHOW VARIABLES LIKE 'performance_schema'")->fetch();
|
||||
$performanceSwitch = $performanceSchema->Value ?? 'OFF';
|
||||
if($performanceSwitch == 'ON')
|
||||
$profiles = [];
|
||||
if($this->app->profiling == 'performance_schema')
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1863,10 +1936,21 @@ class baseDAO
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
$profiles = $this->query('SHOW PROFILES')->fetchAll();
|
||||
$this->sqlError($e);
|
||||
}
|
||||
}
|
||||
if(empty($profiles)) $profiles = $this->query('SHOW PROFILES')->fetchAll();
|
||||
elseif($this->app->profiling == 'show_profiles')
|
||||
{
|
||||
$profiles = $this->query('SHOW PROFILES')->fetchAll();
|
||||
}
|
||||
if(empty($profiles)) return [];
|
||||
|
||||
$traces = dbh::$traces;
|
||||
if($this->app->profiling == 'show_profiles')
|
||||
{
|
||||
$count = count($profiles);
|
||||
if(count($traces) > $count) $traces = array_slice($traces, -$count);
|
||||
}
|
||||
|
||||
$basePath = $this->app->getBasePath();
|
||||
foreach($profiles as $key => $profile)
|
||||
@@ -1874,7 +1958,7 @@ class baseDAO
|
||||
$profile->Duration = round((float)$profile->Duration, 4);
|
||||
$profile->Explain = [];
|
||||
$profile->Error = '';
|
||||
$profile->Code = str_replace($basePath, '', dbh::$traces[$key]);
|
||||
$profile->Code = str_replace($basePath, '', $traces[$key] ?? '');
|
||||
|
||||
$sql = trim($profile->Query);
|
||||
if(stripos($sql, 'select') !== 0
|
||||
@@ -1916,19 +2000,32 @@ class baseDAO
|
||||
return $profiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库版本。
|
||||
* Get database version.
|
||||
*
|
||||
* @access public
|
||||
* @return string|void
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->dbh->getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时表。
|
||||
* Create temporary table.
|
||||
*
|
||||
* @param int $ids 用于创建临时表的 id 列表,字符串或数组。
|
||||
* @param bool $filterDuplicate 是否过滤重复的表名,默认值为 true。
|
||||
* @param int $limit 用于创建临时表的 id 列表的数量限制,数量小于这个值时不创建临时表,0 表示不限制数量。
|
||||
* @param int $ids 用于创建临时表的 id 列表,字符串或数组。
|
||||
* @param string $tableName 临时表的名称,默认值为空,由程序自动生成。
|
||||
* @param bool $filterTable 是否过滤重复的表名,默认值为 true。
|
||||
* @param int $limit 用于创建临时表的 id 列表的数量限制,数量小于这个值时不创建临时表,0 表示不限制数量。
|
||||
* @access public
|
||||
* @return false|string
|
||||
*/
|
||||
public function createTemporaryTable($idList, $filterDuplicate = true, $limit = 100)
|
||||
public function createTemporaryTable($idList, $tableName = '', $filterTable = true, $limit = 100)
|
||||
{
|
||||
return $this->sqlobj->createTemporaryTable($idList, $filterDuplicate, $limit);
|
||||
return $this->sqlobj->createTemporaryTable($idList, $tableName, $filterTable, $limit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2624,11 +2721,13 @@ class baseSQL
|
||||
*
|
||||
* @param string|array $ids ','分割的字符串或者数组。List string by ',' or an array.
|
||||
* @param bool $useTemporaryTable 是否使用临时表。 Use temporary table or not.
|
||||
* @param bool $filterDuplicate 是否过滤重复的表名,默认值为 true。Filter duplicate table name or not.
|
||||
* @param string $tableName 临时表的名称,默认值为空,由程序自动生成。The name of temporary table, default is empty, generated by program.
|
||||
* @param bool $filterTable 是否过滤重复的表名,默认值为 true。Filter duplicate table name or not.
|
||||
* @param bool $limit 用于创建临时表的 id 列表的数量限制,数量小于这个值时不创建临时表,0 表示不限制数量。Limit the count of ids to create temporary table, 0 means no limit.
|
||||
* @access public
|
||||
* @return static|sql the sql object.
|
||||
*/
|
||||
public function in($ids, $useTemporaryTable = false, $filterDuplicate = true)
|
||||
public function in($ids, $useTemporaryTable = false, $tableName = '', $filterTable = true, $limit = 100)
|
||||
{
|
||||
if($this->inCondition and !$this->conditionIsTrue) return $this;
|
||||
|
||||
@@ -2640,7 +2739,7 @@ class baseSQL
|
||||
|
||||
if($useTemporaryTable)
|
||||
{
|
||||
$tableName = $this->createTemporaryTable($ids, $filterDuplicate);
|
||||
$tableName = $this->createTemporaryTable($ids, $tableName, $filterTable, $limit, false);
|
||||
if($tableName)
|
||||
{
|
||||
$this->sql .= " IN (SELECT id FROM $tableName)";
|
||||
@@ -2656,18 +2755,20 @@ class baseSQL
|
||||
* 创建临时表。
|
||||
* Create temporary table.
|
||||
*
|
||||
* @param int $ids 用于创建临时表的 id 列表,字符串或数组。
|
||||
* @param bool $filterDuplicate 是否过滤重复的表名,默认值为 true。
|
||||
* @param int $limit 用于创建临时表的 id 列表的数量限制,数量小于这个值时不创建临时表,0 表示不限制数量。
|
||||
* @param int $ids 用于创建临时表的 id 列表,字符串或数组。
|
||||
* @param string $tableName 临时表的名称,默认值为空,由程序自动生成。
|
||||
* @param bool $filterTable 是否过滤重复的表名,默认值为 true。
|
||||
* @param int $limit 用于创建临时表的 id 列表的数量限制,数量小于这个值时不创建临时表,0 表示不限制数量。
|
||||
* @param bool $exit 是否停止程序执行,默认值为 true。
|
||||
* @access public
|
||||
* @return false|string
|
||||
* @return string
|
||||
*/
|
||||
public function createTemporaryTable($ids, $filterDuplicate = true, $limit = 100)
|
||||
public function createTemporaryTable($ids, $tableName = '', $filterTable = true, $limit = 100, $exit = true)
|
||||
{
|
||||
if(!is_string($ids) && !is_array($ids))
|
||||
{
|
||||
$this->app->triggerError('The idList must be a string or an array.', __FILE__, __LINE__);
|
||||
return false;
|
||||
$this->app->triggerError('The idList must be a string or an array.', __FILE__, __LINE__, $exit);
|
||||
return '';
|
||||
}
|
||||
|
||||
if(is_string($ids)) $ids = explode(',', $ids);
|
||||
@@ -2685,26 +2786,37 @@ class baseSQL
|
||||
|
||||
if($limit && count($idList) < $limit)
|
||||
{
|
||||
$this->app->triggerError("The idList count must be greater than $limit", __FILE__, __LINE__);
|
||||
return false;
|
||||
$this->app->triggerError("The idList count must be greater than $limit", __FILE__, __LINE__, $exit);
|
||||
return '';
|
||||
}
|
||||
|
||||
$tableName = '';
|
||||
if($filterDuplicate)
|
||||
$rows = '';
|
||||
if(empty($tableName))
|
||||
{
|
||||
/* 如果开启了唯一表名,那么将表名设置为 md5(idList). */
|
||||
asort($idList);
|
||||
$rows = '(' . implode('),(', $idList) . ')';
|
||||
$tableName = "temp_" . md5($rows);
|
||||
if($filterTable)
|
||||
{
|
||||
/* 如果开启了唯一表名,那么将表名设置为 md5(idList). */
|
||||
asort($idList);
|
||||
$rows = '(' . implode('),(', $idList) . ')';
|
||||
$tableName = "temp_" . md5($rows);
|
||||
|
||||
if(isset($this->tempTables[$tableName])) return $tableName;
|
||||
if(isset($this->tempTables[$tableName])) return $tableName;
|
||||
}
|
||||
else
|
||||
{
|
||||
$tableName = 'temp_' . uniqid();
|
||||
$rows = '(' . implode('),(', $idList) . ')';
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if(!preg_match('/^temp_\w+$/', $tableName))
|
||||
{
|
||||
$tableName = 'temp_' . uniqid();
|
||||
$rows = '(' . implode('),(', $idList) . ')';
|
||||
$this->app->triggerError("The table name should be like 'temp_xxx', where xxx is a string only containing letters, numbers and underscores.", __FILE__, __LINE__, $exit);
|
||||
return '';
|
||||
}
|
||||
|
||||
if($filterTable && isset($this->tempTables[$tableName])) return $tableName;
|
||||
|
||||
try
|
||||
{
|
||||
$this->dbh->exec("CREATE TEMPORARY TABLE IF NOT EXISTS {$tableName} (id int PRIMARY KEY)");
|
||||
@@ -2713,10 +2825,11 @@ class baseSQL
|
||||
catch(PDOException $e)
|
||||
{
|
||||
$message = $e->getMessage() . ' ' . helper::checkDB2Repair($e);
|
||||
$this->app->triggerError($message, __FILE__, __LINE__);
|
||||
$this->app->triggerError($message, __FILE__, __LINE__, $exit);
|
||||
return '';
|
||||
}
|
||||
|
||||
if($filterDuplicate) $this->tempTables[$tableName] = $tableName;
|
||||
if($filterTable) $this->tempTables[$tableName] = $tableName;
|
||||
|
||||
return $tableName;
|
||||
}
|
||||
|
||||
Vendored
+8
-8
@@ -374,7 +374,7 @@ class cache
|
||||
if(!$objectID) return $this->log('Failed to fetch last insert id.', __FILE__, __LINE__);
|
||||
|
||||
/* 获取新增的数据。Get the new data. */
|
||||
$sql = $this->dao->select('*')->from($this->table)->where('id')->eq($objectID)->get();
|
||||
$sql = $this->dao->select('*')->from($this->table)->where('id')->eq($objectID)->sqlobj->get();
|
||||
$object = $this->query($sql, 'fetch');
|
||||
if(!$object) return $this->log('Failed to fetch new object. The sql is: ' . $sql, __FILE__, __LINE__);
|
||||
|
||||
@@ -410,7 +410,7 @@ class cache
|
||||
$objectIdList = array_map(function($object) use ($field) { return $object->$field; }, $this->objects);
|
||||
|
||||
/* 获取更新后的数据。Get the updated data. */
|
||||
$sql = $this->dao->select('*')->from($this->table)->where($field)->in($objectIdList)->get();
|
||||
$sql = $this->dao->select('*')->from($this->table)->where($field)->in($objectIdList)->sqlobj->get();
|
||||
$objects = $this->query($sql, 'fetchAll', $field);
|
||||
if(!$objects) return $this->log('Failed to fetch updated objects. The sql is: ' . $sql, __FILE__, __LINE__);
|
||||
|
||||
@@ -608,7 +608,7 @@ class cache
|
||||
private function initTableCache(): array
|
||||
{
|
||||
$field = $this->getTableField();
|
||||
$sql = $this->dao->select('*')->from($this->table)->get();
|
||||
$sql = $this->dao->select('*')->from($this->table)->sqlobj->get();
|
||||
$objects = $this->query($sql, 'fetchAll', $field);
|
||||
if(!$objects) return [];
|
||||
|
||||
@@ -719,7 +719,7 @@ class cache
|
||||
{
|
||||
$lostObjects = [];
|
||||
$diffIdList = array_keys(array_diff($keys, array_keys($objects)));
|
||||
$sql = $this->dao->select('*')->from($table)->where('id')->in($diffIdList)->get();
|
||||
$sql = $this->dao->select('*')->from($table)->where('id')->in($diffIdList)->sqlobj->get();
|
||||
$diffObjects = $this->query($sql, 'fetchAll');
|
||||
foreach($diffObjects as $object)
|
||||
{
|
||||
@@ -784,14 +784,14 @@ class cache
|
||||
$field = $this->getTableField();
|
||||
$dao = $this->dao->select($field)->from($table)->where('1=1');
|
||||
foreach($conditions as $condition) $dao->andWhere($condition['field'])->eq($condition['value']);
|
||||
$sql = $dao->get();
|
||||
$sql = $dao->sqlobj->get();
|
||||
$objects = $this->query($sql, 'fetchAll');
|
||||
foreach($objects as $object) $idList[] = $object->$field;
|
||||
$this->saveByKey($cacheKey, $idList);
|
||||
|
||||
/* 把缓存键写入数据库以便执行 dao::exec() 时自动更新。Write the cache key to the database for automatic update when executing dao::exec(). */
|
||||
$fields = implode(',', array_column($conditions, 'field'));
|
||||
$sql = $this->dao->select('1')->from(TABLE_AUTOCACHE)->where('code')->eq($code)->andWhere('fields')->eq($fields)->get();
|
||||
$sql = $this->dao->select('1')->from(TABLE_AUTOCACHE)->where('code')->eq($code)->andWhere('fields')->eq($fields)->sqlobj->get();
|
||||
$autoCache = $this->query($sql, 'fetch');
|
||||
if(!$autoCache)
|
||||
{
|
||||
@@ -1018,7 +1018,7 @@ class cache
|
||||
|
||||
/* 执行操作后数据已经被修改,所以需要提前获取被影响的数据。*/
|
||||
$field = $this->getTableField();
|
||||
$sql = $this->dao->select('*')->from($table)->beginIF($where)->where($where)->fi()->get();
|
||||
$sql = $this->dao->select('*')->from($table)->beginIF($where)->where($where)->fi()->sqlobj->get();
|
||||
$objects = $this->query($sql, 'fetchAll', $field);
|
||||
|
||||
$this->setWhere($where);
|
||||
@@ -1085,4 +1085,4 @@ class cache
|
||||
{
|
||||
return $this->cache->memory($type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,17 +212,20 @@ class dao extends baseDAO
|
||||
/**
|
||||
* Check workFlow field rule.
|
||||
*
|
||||
* @param bool $skip true|false
|
||||
* @access public
|
||||
* @return object the dao object self.
|
||||
*/
|
||||
public function checkFlow()
|
||||
public function checkFlow($skip = false)
|
||||
{
|
||||
if($skip) return $this;
|
||||
if(!isset($this->config->bizVersion)) return $this;
|
||||
|
||||
$module = $this->app->getModuleName();
|
||||
$method = $this->app->getMethodName();
|
||||
if($module == 'story' && $this->app->rawModule == 'requirement') $module = 'requirement';
|
||||
if($module == 'story' && $this->app->rawModule == 'epic') $module = 'epic';
|
||||
if($module == 'project' && $method == 'createtemplate') $method = 'create';
|
||||
|
||||
$flowAction = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '{$module}' AND `action` = '{$method}' AND `buildin` = '1' AND `extensionType` = 'extend' AND `vision` = '{$this->config->vision}'")->fetch(PDO::FETCH_OBJ);
|
||||
if(!$flowAction) return $this;
|
||||
|
||||
+25
-6
@@ -270,13 +270,8 @@ class dbh
|
||||
*/
|
||||
private function trace()
|
||||
{
|
||||
global $app, $config;
|
||||
global $config;
|
||||
if(empty($config->debug) || $config->debug < 3) return false;
|
||||
if(!is_null($app))
|
||||
{
|
||||
if(!empty($app->installing) || !empty($app->upgrading)) return false;
|
||||
if(!isset($config->installed) || !$config->installed) return false;
|
||||
}
|
||||
|
||||
$trace = $this->getTrace();
|
||||
if(empty($trace)) return false;
|
||||
@@ -1008,4 +1003,28 @@ class dbh
|
||||
$missingPrivsSQL = implode(', ', $missingPrivs);
|
||||
return "GRANT {$missingPrivsSQL} ON `{$dbName}`.* TO {$user}@'{$host}';";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库版本。
|
||||
* Get version.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string
|
||||
{
|
||||
switch($this->config->driver)
|
||||
{
|
||||
case 'oceanbase':
|
||||
case 'mysql':
|
||||
$sql = "SELECT version() AS version";
|
||||
break;
|
||||
case 'dm':
|
||||
default:
|
||||
$sql = '';
|
||||
}
|
||||
|
||||
if(empty($sql)) return '';
|
||||
return $this->rawQuery($sql)->fetch()->version;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,10 +119,17 @@ class form extends fixer
|
||||
if($moduleName == 'projectrelease') $moduleName = 'release';
|
||||
if($moduleName == 'projectbuild') $moduleName = 'build';
|
||||
|
||||
/* 项目复制用项目创建的工作流。 */
|
||||
if($moduleName == 'project' && $methodName == 'copyconfirm') $methodName = 'create';
|
||||
if($moduleName == 'project' && $methodName == 'edittemplate') $methodName = 'edit';
|
||||
|
||||
/* 用户需求和业务需求用自己的工作流。*/
|
||||
if($moduleName == 'story' && $app->rawModule == 'requirement') $moduleName = 'requirement';
|
||||
if($moduleName == 'story' && $app->rawModule == 'epic') $moduleName = 'epic';
|
||||
|
||||
/* 复制项目使用项目创建的工作流。 */
|
||||
if($moduleName == 'project' && $methodName == 'copyconfirm') $methodName = 'create';
|
||||
|
||||
$flow = $app->control->loadModel('workflow')->getByModule($moduleName);
|
||||
if(!$flow) return $configObject;
|
||||
|
||||
@@ -229,6 +236,8 @@ class form extends fixer
|
||||
if($method == 'batchcreate') $method = 'create';
|
||||
if($method == 'batchedit') $method = 'edit';
|
||||
|
||||
if($module == 'project' && $module == 'copyconfirm') $method = 'create';
|
||||
|
||||
if(empty($config->$module->$method->requiredFields)) return $configObject;
|
||||
|
||||
$requiredFields = array_unique(array_filter(explode(',', $config->$module->$method->requiredFields)));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,10 @@ class GitRepo
|
||||
chdir($this->root);
|
||||
if(!empty($path)) $sub = ":$path";
|
||||
if(!empty($this->branch))$revision = $this->branch;
|
||||
|
||||
$cmd = escapeCmd("$this->client config core.quotepath false");
|
||||
execCmd($cmd . ' 2>&1');
|
||||
|
||||
$cmd = escapeCmd("$this->client ls-tree -l $revision$sub");
|
||||
$list = execCmd($cmd . ' 2>&1', 'array', $result);
|
||||
if($result) return array();
|
||||
|
||||
@@ -61,9 +61,9 @@ class scm
|
||||
* @param int $pageID
|
||||
* @return array
|
||||
*/
|
||||
public function branch(string $showDetail = '', string $orderBy = '', int $limit = 0, int $pageID = 1)
|
||||
public function branch(string $showDetail = '', string $orderBy = '', int $limit = 0, int $pageID = 1, string $label = '')
|
||||
{
|
||||
return $this->engine->branch($showDetail, $orderBy, $limit, $pageID);
|
||||
return $this->engine->branch($showDetail, $orderBy, $limit, $pageID, $label);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -569,7 +569,7 @@ class sqlparser
|
||||
$tables = array();
|
||||
$tables[] = $this->parseTable($from, 'from');
|
||||
|
||||
foreach($joins as $join) $tables[] = $this->parseTable($join->expr, 'join');
|
||||
if(!empty($joins)) foreach($joins as $join) $tables[] = $this->parseTable($join->expr, 'join');
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ubb2html support for php
|
||||
* @requires xhEditor
|
||||
*
|
||||
* @author Yanis.Wang<yanis.wang@gmail.com>
|
||||
* @site http://xheditor.com/
|
||||
* @licence LGPL(http://www.opensource.org/licenses/lgpl-license.php)
|
||||
*
|
||||
* @Version: 0.9.8 (build 100505)
|
||||
*/
|
||||
class ubb
|
||||
{
|
||||
public static function parseUBB($sUBB)
|
||||
{
|
||||
$sHtml=$sUBB;
|
||||
|
||||
global $emotPath,$cnum,$arrcode,$bUbb2htmlFunctionInit;$cnum=0;$arrcode=array();
|
||||
$emotPath='../xheditor_emot/';
|
||||
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function saveCodeArea($match)
|
||||
{
|
||||
global $cnum,$arrcode;
|
||||
$cnum++;$arrcode[$cnum]=$match[0];
|
||||
return "[\tubbcodeplace_".$cnum."\t]";
|
||||
}
|
||||
}
|
||||
|
||||
$sHtml=preg_replace_callback('/\[code\s*(?:=\s*((?:(?!")[\s\S])+?)(?:"[\s\S]*?)?)?\]([\s\S]*?)\[\/code\]/i','saveCodeArea',$sHtml);
|
||||
|
||||
//$sHtml=preg_replace("/&/",'&',$sHtml);
|
||||
//$sHtml=preg_replace("/</",'<',$sHtml);
|
||||
//$sHtml=preg_replace("/>/",'>',$sHtml);
|
||||
$sHtml=preg_replace("/\r?\n/",'<br />',$sHtml);
|
||||
|
||||
$sHtml=preg_replace("/\[(\/?)(b|u|i|s|sup|sub)\]/i",'<$1$2>',$sHtml);
|
||||
$sHtml=preg_replace('/\[color\s*=\s*([^\]"]+?)(?:"[^\]]*?)?\s*\]/i','<span style="color:$1;">',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getSizeName($match)
|
||||
{
|
||||
$arrSize=array('8pt','10pt','12pt','14pt','18pt','24pt','36pt');
|
||||
return '<span style="font-size:'.$arrSize[$match[1]-1].';">';
|
||||
}
|
||||
}
|
||||
$sHtml=preg_replace_callback("/\[size\s*=\s*(\d+?)\s*\]/i",'getSizeName',$sHtml);
|
||||
$sHtml=preg_replace('/\[font\s*=\s*([^\]"]+?)(?:"[^\]]*?)?\s*\]/i','<span style="font-family:$1;">',$sHtml);
|
||||
$sHtml=preg_replace('/\[back\s*=\s*([^\]"]+?)(?:"[^\]]*?)?\s*\]/i','<span style="background-color:$1;">',$sHtml);
|
||||
$sHtml=preg_replace("/\[\/(color|size|font|back)\]/i",'</span>',$sHtml);
|
||||
|
||||
for($i=0;$i<3;$i++)$sHtml=preg_replace('/\[align\s*=\s*([^\]"]+?)(?:"[^\]]*?)?\s*\](((?!\[align(?:\s+[^\]]+)?\])[\s\S])*?)\[\/align\]/','<p align="$1">$2</p>',$sHtml);
|
||||
$sHtml=preg_replace('/\[img\]\s*(((?!")[\s\S])+?)(?:"[\s\S]*?)?\s*\[\/img\]/i','<img src="$1" alt="" />',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getImg($match)
|
||||
{
|
||||
$alt=$match[1];$p1=$match[2];$p2=$match[3];$p3=$match[4];$src=$match[5];
|
||||
$a=$p3?$p3:(!is_numeric($p1)?$p1:'');
|
||||
return '<img src="'.$src.'" alt="'.$alt.'"'.(is_numeric($p1)?' width="'.$p1.'"':'').(is_numeric($p2)?' height="'.$p2.'"':'').($a?' align="'.$a.'"':'').' />';
|
||||
}
|
||||
}
|
||||
|
||||
$sHtml=preg_replace_callback('/\[img\s*=([^,\]]*)(?:\s*,\s*(\d*%?)\s*,\s*(\d*%?)\s*)?(?:,?\s*(\w+))?\s*\]\s*(((?!")[\s\S])+?)(?:"[\s\S]*)?\s*\[\/img\]/i','getImg',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getEmot($match)
|
||||
{
|
||||
global $emotPath;
|
||||
$arr=preg_split('/,/',$match[1]);
|
||||
if(!isset($arr[1])){$arr[1]=$arr[0];$arr[0]='default';}
|
||||
$path=$emotPath.$arr[0].'/'.$arr[1].'.gif';
|
||||
return '<img src="'.$path.'" alt="'.$arr[1].'" />';
|
||||
}
|
||||
}
|
||||
|
||||
$sHtml=preg_replace_callback('/\[emot\s*=\s*([^\]"]+?)(?:"[^\]]*?)?\s*\/\]/i','getEmot',$sHtml);
|
||||
$sHtml=preg_replace('/\[url\]\s*(((?!")[\s\S])*?)(?:"[\s\S]*?)?\s*\[\/url\]/i','<a href="$1">$1</a>',$sHtml);
|
||||
$sHtml=preg_replace('/\[url\s*=\s*([^\]"]+?)(?:"[^\]]*?)?\s*\]\s*([\s\S]*?)\s*\[\/url\]/i','<a href="$1">$2</a>',$sHtml);
|
||||
$sHtml=preg_replace('/\[email\]\s*(((?!")[\s\S])+?)(?:"[\s\S]*?)?\s*\[\/email\]/i','<a href="mailto:$1">$1</a>',$sHtml);
|
||||
$sHtml=preg_replace('/\[email\s*=\s*([^\]"]+?)(?:"[^\]]*?)?\s*\]\s*([\s\S]+?)\s*\[\/email\]/i','<a href="mailto:$1">$2</a>',$sHtml);
|
||||
$sHtml=preg_replace("/\[quote\]([\s\S]*?)\[\/quote\]/i",'<blockquote>$1</blockquote>',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getFlash($match)
|
||||
{
|
||||
$w=$match[1];$h=$match[2];$url=$match[3];
|
||||
if(!$w)$w=480;if(!$h)$h=400;
|
||||
return '<embed type="application/x-shockwave-flash" src="'.$url.'" wmode="opaque" quality="high" bgcolor="#ffffff" menu="false" play="true" loop="true" width="'.$w.'" height="'.$h.'" />';
|
||||
}
|
||||
}
|
||||
$sHtml=preg_replace_callback('/\[flash\s*(?:=\s*(\d+)\s*,\s*(\d+)\s*)?\]\s*(((?!")[\s\S])+?)(?:"[\s\S]*?)?\s*\[\/flash\]/i','getFlash',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getMedia($match)
|
||||
{
|
||||
$w=$match[1];$h=$match[2];$play=$match[3];$url=$match[4];
|
||||
if(!$w)$w=480;if(!$h)$h=400;
|
||||
return '<embed type="application/x-mplayer2" src="'.$url.'" enablecontextmenu="false" autostart="'.($play=='1'?'true':'false').'" width="'.$w.'" height="'.$h.'" />';
|
||||
}
|
||||
}
|
||||
$sHtml=preg_replace_callback('/\[media\s*(?:=\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d+)\s*)?)?\]\s*(((?!")[\s\S])+?)(?:"[\s\S]*?)?\s*\[\/media\]/i','getMedia',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getTable($match)
|
||||
{
|
||||
return '<table'.(isset($match[1])?' width="'.$match[1].'"':'').(isset($match[2])?' bgcolor="'.$match[2].'"':'').'>';
|
||||
}
|
||||
}
|
||||
$sHtml=preg_replace_callback('/\[table\s*(?:=(\d{1,4}%?)\s*(?:,\s*([^\]"]+)(?:"[^\]]*?)?)?)?\s*\]/i','getTable',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getTR($match){return '<tr'.(isset($match[1])?' bgcolor="'.$match[1].'"':'').'>';}
|
||||
}
|
||||
$sHtml=preg_replace_callback('/\[tr\s*(?:=(\s*[^\]"]+))?(?:"[^\]]*?)?\s*\]/i','getTR',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getTD($match)
|
||||
{
|
||||
$col=isset($match[1])?$match[1]:0;$row=isset($match[2])?$match[2]:0;$w=isset($match[3])?$match[3]:null;
|
||||
return '<td'.($col>1?' colspan="'.$col.'"':'').($row>1?' rowspan="'.$row.'"':'').($w?' width="'.$w.'"':'').'>';
|
||||
}
|
||||
}
|
||||
$sHtml=preg_replace_callback("/\[td\s*(?:=\s*(\d{1,2})\s*,\s*(\d{1,2})\s*(?:,\s*(\d{1,4}%?))?)?\s*\]/i",'getTD',$sHtml);
|
||||
$sHtml=preg_replace("/\[\/(table|tr|td)\]/i",'</$1>',$sHtml);
|
||||
$sHtml=preg_replace("/\[\*\]((?:(?!\[\*\]|\[\/list\]|\[list\s*(?:=[^\]]+)?\])[\s\S])+)/i",'<li>$1</li>',$sHtml);
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function getUL($match)
|
||||
{
|
||||
$str='<ul';
|
||||
if(isset($match[1]))$str.=' type="'.$match[1].'"';
|
||||
return $str.'>';
|
||||
}
|
||||
}
|
||||
$sHtml=preg_replace_callback('/\[list\s*(?:=\s*([^\]"]+))?(?:"[^\]]*?)?\s*\]/i','getUL',$sHtml);
|
||||
$sHtml=preg_replace("/\[\/list\]/i",'</ul>',$sHtml);
|
||||
|
||||
for($i=1;$i<=$cnum;$i++)$sHtml=str_replace("[\tubbcodeplace_".$i."\t]", $arrcode[$i],$sHtml);
|
||||
|
||||
if(!$bUbb2htmlFunctionInit)
|
||||
{
|
||||
function fixText($match)
|
||||
{
|
||||
$text=$match[2];
|
||||
$text=preg_replace("/\t/",' ',$text);
|
||||
$text=preg_replace("/ /",' ',$text);
|
||||
return $match[1].$text;
|
||||
}
|
||||
}
|
||||
$sHtml=preg_replace_callback('/(^|<\/?\w+(?:\s+[^>]*?)?>)([^<$]+)/i','fixText',$sHtml);
|
||||
|
||||
$bUbb2htmlFunctionInit=true;
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
}
|
||||
@@ -2,31 +2,7 @@
|
||||
declare(strict_types=1);
|
||||
namespace zin\utils;
|
||||
|
||||
/**
|
||||
* 对数据进行HTML实体转为字符。
|
||||
* Un HTML array.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
function unHTMLArray($data, $processed = array())
|
||||
{
|
||||
if(is_string($data)) return htmlspecialchars_decode($data);
|
||||
|
||||
if(is_array($data) || is_object($data))
|
||||
{
|
||||
if(in_array($data, $processed, true)) return $data;
|
||||
|
||||
$processed[] = $data;
|
||||
foreach($data as &$value) $value = unHTMLArray($value, $processed);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
function jsonEncode($data, $flags = 0, $depth = 512): string|false
|
||||
{
|
||||
$data = unHTMLArray($data);
|
||||
return json_encode($data, $flags, $depth);
|
||||
}
|
||||
|
||||
@@ -61,13 +61,13 @@ class backBtn extends btn
|
||||
'build' => 'execution-build,build-view,project-index,kanban-view,testtask-browse,projectbuild-browse,my-effort,company-effort,company-calendar,my-index,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'projectbuild' => 'projectbuild-browse,projectbuild-view,project-index',
|
||||
'repo' => 'repo-maintain,repo-log,repo-browse,repo-view,repo-review,mr-view,repo-browsewebhooks,repo-browserule',
|
||||
'mr' => 'pullreq-browse,mr-browse',
|
||||
'mr' => 'pullreq-browse,mr-browse,my-audit',
|
||||
'pullreq' => 'pullreq-browse',
|
||||
'compile' => 'compile-browse',
|
||||
'store' => 'store-browse',
|
||||
'space' => 'space-browse',
|
||||
'serverroom' => 'serverroom-browse',
|
||||
'project' => 'program-browse,program-project,project-browse,project-view,project-team,charter-view',
|
||||
'project' => 'program-browse,program-project,project-browse,project-view,project-team,charter-view,project-template',
|
||||
'product' => 'product-all,program-productview,program-product,product-view,product-dynamic,my-index,charter-view',
|
||||
'gitlab' => 'space-browse,gitlab-browseproject',
|
||||
'gitfox' => 'space-browse',
|
||||
|
||||
+139
-54
@@ -1,18 +1,21 @@
|
||||
let lastAppUrl = '';
|
||||
let originalDocumentTitle = document.title;
|
||||
let documentTitleSuffix = ' - ' + originalDocumentTitle.split(' - ').pop();
|
||||
let currentDocApp = null;
|
||||
let currentUser = null;
|
||||
let currentSpaceType = '';
|
||||
let userPrivs = {};
|
||||
let docLangData = null;
|
||||
let docBasicModal = {};
|
||||
const savingDocData = {};
|
||||
if(!window.docAppData) window.docAppData = {
|
||||
lastAppUrl: '',
|
||||
originalDocumentTitle: document.title,
|
||||
documentTitleSuffix: ' - ' + document.title.split(' - ').pop(),
|
||||
currentUser: null,
|
||||
currentSpaceType: '',
|
||||
userPrivs: {},
|
||||
};
|
||||
const docAppData = window.docAppData;
|
||||
let savingDocData = {};
|
||||
let docBasicModal = {};
|
||||
let currentDocApp = null;
|
||||
|
||||
window.setZentaoSlashMenu = function(menus, title, vision, position)
|
||||
{
|
||||
if(vision == 'or' || vision == 'lite') return;
|
||||
if(!menus?.length) return;
|
||||
|
||||
const slashMenus = menus.map(menu =>
|
||||
{
|
||||
if(menu.subMenu)
|
||||
@@ -20,7 +23,7 @@ window.setZentaoSlashMenu = function(menus, title, vision, position)
|
||||
menu.subMenu = menu.subMenu.map(subMenu => {
|
||||
subMenu.action = function (context) {
|
||||
zui.Modal.open({
|
||||
size: 'lg',
|
||||
size: subMenu.modalSize ? subMenu.modalSize : 'lg',
|
||||
url: $.createLink(subMenu.module, subMenu.method, subMenu.params)
|
||||
});
|
||||
}
|
||||
@@ -29,11 +32,24 @@ window.setZentaoSlashMenu = function(menus, title, vision, position)
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.action = function (context) {
|
||||
zui.Modal.open({
|
||||
size: 'lg',
|
||||
url: $.createLink(menu.module, menu.method, menu.params)
|
||||
});
|
||||
if(menu.key == 'gantt' && !menu.isModal)
|
||||
{
|
||||
menu.action = function (context) {
|
||||
const doc = getDocApp().doc;
|
||||
$.ajaxSubmit({
|
||||
url: $.createLink(menu.module, menu.method, menu.params),
|
||||
data: {templateID: doc.data.id}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.action = function (context) {
|
||||
zui.Modal.open({
|
||||
size: menu.modalSize ? menu.modalSize : 'lg',
|
||||
url: $.createLink(menu.module, menu.method, menu.params)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return menu;
|
||||
@@ -45,9 +61,28 @@ window.setZentaoSlashMenu = function(menus, title, vision, position)
|
||||
zui.Editor.setGlobalConfig({slashMenuStyles: [`link:${$.libRoot}zui.zentao.css`]});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更多菜单操作。
|
||||
* More menu action.
|
||||
*
|
||||
* @param {MoreMenuItem} menu
|
||||
*/
|
||||
window.moreMenuAction = function(menu)
|
||||
{
|
||||
if (menu.module && menu.method && menu.params)
|
||||
{
|
||||
zui.Modal.open({
|
||||
size: 'lg',
|
||||
url: $.createLink(menu.module, menu.method, menu.params)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取 ZentaoList 块属性。 */
|
||||
function getZentaoListProps(type, blockID, props)
|
||||
{
|
||||
if(props && props.isTemplate) blockID = '__TML_ZENTAOLIST__' + blockID;
|
||||
|
||||
const zentaoListLang = getLang('zentaoList');
|
||||
const exportUrl = $.createLink('doc', 'ajaxExportZentaoList', `blockID=${blockID}`);
|
||||
return $.extend(
|
||||
@@ -103,7 +138,7 @@ window.reloadZentaoList = function(blockID)
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
function getDocApp()
|
||||
window.getDocApp = function()
|
||||
{
|
||||
if(!currentDocApp)
|
||||
{
|
||||
@@ -122,13 +157,13 @@ function getDocApp()
|
||||
*/
|
||||
window.getLang = function(key, args)
|
||||
{
|
||||
if(!docLangData)
|
||||
if(!docAppData.docLangData)
|
||||
{
|
||||
const docApp = getDocApp();
|
||||
docLangData = docApp ? docApp.props.langData : {};
|
||||
docAppData.docLangData = docApp ? docApp.props.langData : {};
|
||||
}
|
||||
if(typeof key !== 'string') return docLangData;
|
||||
const value = docLangData[key];
|
||||
if(typeof key !== 'string') return docAppData.docLangData;
|
||||
const value = docAppData.docLangData[key];
|
||||
if(value === undefined) return;
|
||||
if(args)
|
||||
{
|
||||
@@ -144,12 +179,25 @@ window.getLang = function(key, args)
|
||||
* Check if the specified privilege exists, privileges are defined in $privs in module/doc/ui/app.html.php.
|
||||
* @param {string} priv
|
||||
*/
|
||||
function hasPriv(priv, value)
|
||||
window.hasPriv = function(priv, value)
|
||||
{
|
||||
const setting = userPrivs[priv];
|
||||
const setting = docAppData.userPrivs[priv];
|
||||
return value !== undefined ? setting === value : !!setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文档应用销毁事件,销毁文档基本信息对话框。
|
||||
* Handle the doc app destroy event, destroy the doc basic info dialog.
|
||||
*/
|
||||
function handleDocAppDestroy()
|
||||
{
|
||||
if(docBasicModal.current)
|
||||
{
|
||||
docBasicModal.current.destroy();
|
||||
docBasicModal = {};
|
||||
}
|
||||
}
|
||||
|
||||
function isApiLib(lib, docApp)
|
||||
{
|
||||
if(docApp && docApp.spaceType === 'api') return true;
|
||||
@@ -216,7 +264,7 @@ function processDocAppAction(action, docApp)
|
||||
*
|
||||
* @param {object|number} space
|
||||
*/
|
||||
function canModifySpace(space)
|
||||
window.canModifySpace = function(space)
|
||||
{
|
||||
const docApp = getDocApp();
|
||||
if(!docApp && !space) return false;
|
||||
@@ -296,6 +344,8 @@ function postLastViewedToServer(location)
|
||||
*/
|
||||
function handleSwitchView(mode, location, info)
|
||||
{
|
||||
if(!this.element || this.element.closest('.modal')) return;
|
||||
|
||||
const rawModule = config.rawModule.toLowerCase();
|
||||
const rawMethod = config.rawMethod.toLowerCase();
|
||||
const formatUrl = this.props.viewModeUrl;
|
||||
@@ -323,9 +373,9 @@ function handleSwitchView(mode, location, info)
|
||||
{
|
||||
url = zui.formatString(formatUrl, formatOptions).replace('&libID=0&moduleID=0&docID=0&mode=home&orderBy=id_desc&recTotal=0&recPerPage=0&pageID=1&filterType=&search=&noSpace=false', '').replace('&libID=0&moduleID=0&browseType=all¶m=&orderBy=&recTotal=0&recPerPage=20&pageID=1&mode=home&docID=0&search=', '');
|
||||
}
|
||||
if(url === lastAppUrl) return;
|
||||
if(lastAppUrl && !$.apps.getAppUrl().endsWith(url)) $.apps.updateAppUrl(url, info.title ? (info.title + documentTitleSuffix) : originalDocumentTitle);
|
||||
lastAppUrl = url;
|
||||
if(url === docAppData.lastAppUrl) return;
|
||||
if(docAppData.lastAppUrl && !$.apps.getAppUrl().endsWith(url)) $.apps.updateAppUrl(url, info.title ? (info.title + docAppData.documentTitleSuffix) : docAppData.originalDocumentTitle);
|
||||
docAppData.lastAppUrl = url;
|
||||
if(docBasicModal.current)
|
||||
{
|
||||
docBasicModal.current.destroy();
|
||||
@@ -352,9 +402,10 @@ function showDocBasicModal(parentID, docID, isDraft, modalType = 'doc')
|
||||
docBasicModal.doc = docID;
|
||||
zui.Modal.open(
|
||||
{
|
||||
key : `${config.rawModule}-${config.rawMethod}-${spaceType}-${docID}`,
|
||||
ref : docBasicModal,
|
||||
url : url,
|
||||
destroyOnHide: false,
|
||||
destroyOnHide: !docID,
|
||||
cache : true,
|
||||
request: {
|
||||
error: error => {
|
||||
@@ -395,7 +446,7 @@ function mergeDocFormData(doc, formData)
|
||||
return doc;
|
||||
}
|
||||
|
||||
function showSaveFailedAlert(error)
|
||||
window.showSaveFailedAlert = function(error)
|
||||
{
|
||||
zui.Modal.alert(
|
||||
{
|
||||
@@ -444,6 +495,7 @@ function submitNewDoc(doc, spaceID, libID, moduleID, formData, afterCreate)
|
||||
acl : 'private',
|
||||
space : spaceType,
|
||||
uid : (doc.uid || `doc${doc.id}`),
|
||||
template : doc.template
|
||||
};
|
||||
if(formData) mergeDocFormData(docData, formData);
|
||||
const getErrorMessage = (res) => {
|
||||
@@ -634,7 +686,7 @@ function handleSaveDoc(doc)
|
||||
docApp.isSavingDoc = false;
|
||||
$(docApp.element).find('[zui-command^="saveDoc"],[zui-command^="saveNewDoc"]').removeAttr('disabled');
|
||||
const {id, acl, users, addedBy} = doc;
|
||||
if(acl == 'private' && !users.includes(currentUser) && addedBy !== currentUser)
|
||||
if(acl == 'private' && !users.includes(docAppData.currentUser) && addedBy !== docAppData.currentUser)
|
||||
{
|
||||
if(docApp.hasEditingDoc)
|
||||
{
|
||||
@@ -817,6 +869,7 @@ const actionsMap =
|
||||
const doc = info.data;
|
||||
const canModify = canModifySpace();
|
||||
const isApi = isApiDoc(doc);
|
||||
const isTemplate = doc.templateType !== '';
|
||||
const canEditDoc = canModify && hasPriv(isApi ? 'editApi' : 'edit');
|
||||
const isChapter = doc.type === 'chapter';
|
||||
|
||||
@@ -828,7 +881,7 @@ const actionsMap =
|
||||
[
|
||||
hasPriv('addChapter') ? {icon: 'plus', text: lang.addSubChapter, command: `addChapter/${doc.id}`} : null,
|
||||
hasPriv('addChapter') && doc.parent != 0 ? {icon: 'plus', text: lang.addSameChapter, command: `addChapter/${doc.parent}`} : null,
|
||||
hasPriv('create') ? {icon: 'plus', text: lang.addSubDoc, command: `startCreateDoc/${doc.id}`} : null,
|
||||
!isTemplate && hasPriv('create') ? {icon: 'plus', text: lang.addSubDoc, command: `startCreateDoc/${doc.id}`} : null,
|
||||
!isChapter && canMoveDoc(doc) ? {icon: 'folder-move', text: lang.moveDoc, command: `moveDoc/${doc.id}`} : null,
|
||||
!isChapter && hasPriv('delete') ? {icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`} : null,
|
||||
isChapter && hasPriv('editChapter') ? {icon: 'pencil', text: lang.editChapter, command: `editChapter/${doc.id}`} : null,
|
||||
@@ -836,7 +889,7 @@ const actionsMap =
|
||||
];
|
||||
return [
|
||||
(!isChapter && hasPriv('sortDoc')) || (isChapter && hasPriv('sortChapter')) ? {icon: 'move cursor-move', className: 'sort-handler', hint: lang.sortDoc, size: 'xs'} : null,
|
||||
moreItems.length ? {icon: 'ellipsis-v', caret: false, placement: 'bottom-end', size: 'xs', items: moreItems} : null,
|
||||
moreItems.every(item => item === null) ? null : {icon: 'ellipsis-v', caret: false, placement: 'bottom-end', size: 'xs', items: moreItems},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -844,7 +897,7 @@ const actionsMap =
|
||||
if(canModify && canMoveDoc(doc)) moreItems.push({icon: 'folder-move', text: lang.moveDoc, command: `moveDoc/${doc.id}`});
|
||||
if(canModify && hasPriv('delete')) moreItems.push({icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`});
|
||||
if(!isApi && canModify && hasPriv('effort')) moreItems.push({icon: 'time', text: lang.effort, command: `effortDoc/${doc.id}`});
|
||||
if(!isApi)
|
||||
if(!isApi && !isTemplate)
|
||||
{
|
||||
const canExportWord = hasPriv('exportDoc');
|
||||
if(doc.contentType === 'doc' || doc.contentType === 'markdown')
|
||||
@@ -866,15 +919,15 @@ const actionsMap =
|
||||
}
|
||||
const docApp = this;
|
||||
const isOpen = isOpenVersion(config.version);
|
||||
if(!isOpen && docApp && docApp.mode === 'view' && doc.status != 'draft' && !doc.api)
|
||||
if(!isOpen && docApp && docApp.mode === 'view' && doc.status != 'draft' && !doc.api && !isTemplate)
|
||||
{
|
||||
moreItems.push({icon: 'link', text: getLang('relateObject'), className: 'relateObject-btn', command: 'toggleViewSideTab/relateObject'})
|
||||
}
|
||||
|
||||
const isToolbar = info.ui === 'toolbar';
|
||||
const actions = [
|
||||
const actions = isInModal ? [] : [
|
||||
isToolbar ? {hint: docApp.fullscreen ? lang.exitFullscreen : lang.enterFullscreen, icon: docApp.fullscreen ? 'fullscreen-exit' : 'fullscreen', command: 'toggleFullscreen'} : null,
|
||||
canEditDoc ? {icon: 'edit', type: 'ghost text-primary', hint: lang.edit, rounded: 'lg', command: 'startEditDoc'} : null,
|
||||
canEditDoc ? {icon: 'edit', class: doc.editable ? null : 'disabled', type: doc.editable ? 'ghost text-primary' : 'ghost text-gray', hint: doc.editable ? lang.edit : lang.needEditable, rounded: 'lg', command: doc.editable ? 'startEditDoc' : null} : null,
|
||||
(isToolbar && docApp.props.showDocOutline !== false) ? {hint: lang.docOutline, icon: 'list-box', command: 'toggleViewSideTab/outline'} : null,
|
||||
(isToolbar && docApp.props.showDocHistory !== false) ? {hint: lang.history, icon: 'history', command: 'toggleViewSideTab/history'} : null,
|
||||
moreItems.length ? {icon: 'icon-ellipsis-v', type: 'dropdown', rounded: 'lg', placement: 'bottom-end', caret: false, items: moreItems} : null,
|
||||
@@ -889,10 +942,11 @@ const actionsMap =
|
||||
*/
|
||||
lib: function(info)
|
||||
{
|
||||
const docApp = getDocApp();
|
||||
const lang = getLang();
|
||||
const items = [];
|
||||
const lib = info.data;
|
||||
const canModify = canModifySpace();
|
||||
const canModify = canModifySpace() || docApp === null;
|
||||
const canAddModule = canModify && hasPriv('addModule');
|
||||
|
||||
/* 获取侧边栏没有模块时的操作按钮。 Get actions when sidebar no module. */
|
||||
@@ -990,15 +1044,18 @@ const actionsMap =
|
||||
if(!canModify) return;
|
||||
|
||||
const canCreateApi = hasPriv('createApi');
|
||||
|
||||
const command = 'startCreateDoc/' + docID + '/api';
|
||||
|
||||
if(info.ui === 'doc-list-empty')
|
||||
{
|
||||
return canCreateApi ? [{icon: 'plus', text: lang.createApi, btnType: 'primary', command: 'startCreateDoc'}] : null;
|
||||
return canCreateApi ? [{icon: 'plus', text: lang.createApi, btnType: 'primary', command}] : null;
|
||||
}
|
||||
|
||||
const canCreateLib = hasPriv('createLib');
|
||||
const items =
|
||||
[
|
||||
canCreateApi ? {icon: 'plus', text: lang.createApi, command: 'startCreateDoc'} : null,
|
||||
canCreateApi ? {icon: 'plus', text: lang.createApi, command} : null,
|
||||
(canCreateApi && canCreateLib) ? {type: 'divider'} : null,
|
||||
canCreateLib ? {icon: 'wiki-lib', text: lang.createLib, command: 'createLib'} : null,
|
||||
].filter(Boolean);
|
||||
@@ -1169,15 +1226,17 @@ const commands =
|
||||
const docApp = getDocApp();
|
||||
if(!docApp.libList.length) return zui.Modal.alert(getLang('createLibFirst'));
|
||||
|
||||
const docType = (args && args[1]) || 'doc';
|
||||
const {spaceType} = docApp;
|
||||
if(spaceType === 'api') return docApp.startCreateDoc();
|
||||
if(spaceType === 'api' || docType === 'api') return docApp.startCreateDoc();
|
||||
|
||||
const templateID = args?.[1] ?? 0;
|
||||
const {spaceID, libID, moduleID} = docApp;
|
||||
return showDocBasicModal(args?.[0] ?? 0, 0, true, 'doc').then((formData) => {
|
||||
return showDocBasicModal(args?.[0] ?? 0, 0, true, docType).then((formData) => {
|
||||
zui.Editor.loadModule().then(() => {
|
||||
const emptyDoc = zui.Editor.createEmptyDoc();
|
||||
const snapshot = zui.Editor.toSnapshot(emptyDoc);
|
||||
const doc = {contentType: 'doc', status: 'draft', content: JSON.stringify(snapshot)};
|
||||
const doc = {contentType: 'doc', status: 'draft', content: JSON.stringify(snapshot), template: templateID};
|
||||
return submitNewDoc(doc, spaceID, libID, moduleID, formData, (newDoc) => {
|
||||
docApp.selectDoc(newDoc.id);
|
||||
docApp.startEditDoc(newDoc.id);
|
||||
@@ -1882,6 +1941,8 @@ function getTableOptions(options, info)
|
||||
if(col.name === 'actions' && col.actionsMap)
|
||||
{
|
||||
if(!canModify) col.actions = [];
|
||||
col.actions = col.actions.filter(action => hasPriv(action == 'move' ? 'moveDoc' : action));
|
||||
|
||||
const actionHints = {edit: lang.editDoc, move: lang.moveDoc, delete: lang.deleteDoc};
|
||||
const privMap = {edit: hasPriv('edit'), move: hasPriv('moveDoc'), delete: hasPriv('delete')};
|
||||
$.each(col.actionsMap, (key, value) =>
|
||||
@@ -1903,6 +1964,21 @@ function getTableOptions(options, info)
|
||||
options.cols.forEach(col =>
|
||||
{
|
||||
if(typeof tableCols[col.name] === 'string') col.title = tableCols[col.name];
|
||||
if(col.name == 'objectName')
|
||||
{
|
||||
const onRenderCellBackup = col.onRenderCell;
|
||||
col.onRenderCell = function(result, info)
|
||||
{
|
||||
const file = info.row.data;
|
||||
if(file.rawObjectType == 'stepResult')
|
||||
{
|
||||
const url = $.createLink('testtask', 'results', `runID=0&caseID=${file.objectID}`);
|
||||
result[0] = {html: `${file.objectName}<a href="${url}" data-toggle="modal" data-size="lg" title="${file.objectName}#${file.objectID} ${file.sourceName}">#${file.objectID} ${file.sourceName}</a>`};
|
||||
return result;
|
||||
}
|
||||
return onRenderCellBackup.call(this,result,info);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return options;
|
||||
@@ -1919,8 +1995,8 @@ function getFilterTypes(mode)
|
||||
{
|
||||
if(mode === 'home')
|
||||
{
|
||||
if(currentSpaceType === 'project') return getLang('projectFilterTypes');
|
||||
if(currentSpaceType === 'product') return getLang('productFilterTypes');
|
||||
if(docAppData.currentSpaceType === 'project') return getLang('projectFilterTypes');
|
||||
if(docAppData.currentSpaceType === 'product') return getLang('productFilterTypes');
|
||||
return getLang('spaceFilterTypes');
|
||||
}
|
||||
if(mode === 'list')
|
||||
@@ -1942,10 +2018,11 @@ function isMatchFilter(type, filterType, item)
|
||||
{
|
||||
if(type === 'doc')
|
||||
{
|
||||
if(filterType === 'createdByMe') return item.addedBy === currentUser;
|
||||
if(filterType === 'editedByMe') return item.editedBy === currentUser;
|
||||
if(filterType === 'createdByMe') return item.addedBy === docAppData.currentUser;
|
||||
if(filterType === 'editedByMe') return item.editedBy === docAppData.currentUser;
|
||||
if(filterType === 'draft') return item.status === 'draft';
|
||||
if(filterType === 'collect') return item.isCollector;
|
||||
if(filterType === 'released') return item.status === 'normal';
|
||||
}
|
||||
if(type === 'project' || type === 'product')
|
||||
{
|
||||
@@ -1963,7 +2040,7 @@ function isMatchFilter(type, filterType, item)
|
||||
*/
|
||||
function formatOrders(orders, useIndex)
|
||||
{
|
||||
const orderedKeys = useIndex ? [] : Array.from(orders).sort();
|
||||
const orderedKeys = useIndex ? [] : Array.from(orders).sort((a, b) => a - b);
|
||||
return orders.reduce((map, key, index) =>
|
||||
{
|
||||
map[key] = useIndex ? index : orderedKeys[index];
|
||||
@@ -2034,13 +2111,20 @@ function getSortableOptions(type)
|
||||
if(fromType !== 'doc') key = key.substring(1);
|
||||
orderedList.push(+key);
|
||||
});
|
||||
if(fromType === 'doc')
|
||||
{
|
||||
const docApp = getDocApp();
|
||||
const doc = docApp.getDoc(orderedList[0]);
|
||||
const lib = doc ? docApp.getLib(doc.lib) : null;
|
||||
if(lib && typeof lib.data.orderBy === 'string' && lib.data.orderBy.endsWith('desc')) orderedList.reverse();
|
||||
}
|
||||
sortItems(fromType, orderedList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkResponse(res)
|
||||
window.checkResponse = function(res)
|
||||
{
|
||||
if(typeof res === 'string') res = JSON.parse(res);
|
||||
if(typeof res !== 'object' || !res) return true;
|
||||
@@ -2126,10 +2210,10 @@ function initOfficePreview(options)
|
||||
*/
|
||||
window.setDocAppOptions = function(_, options)
|
||||
{
|
||||
currentUser = options.currentUser;
|
||||
currentSpaceType = options.spaceType;
|
||||
userPrivs = options.privs;
|
||||
docLangData = options.langData;
|
||||
docAppData.currentUser = options.currentUser;
|
||||
docAppData.currentSpaceType = options.spaceType;
|
||||
docAppData.userPrivs = options.privs;
|
||||
docAppData.docLangData = options.langData;
|
||||
|
||||
if(options.canPreviewOffice && options.fileInfoUrl) initOfficePreview(options);
|
||||
|
||||
@@ -2138,8 +2222,8 @@ window.setDocAppOptions = function(_, options)
|
||||
defaultState : {listType: '', libReleaseMap: {}},
|
||||
commands : commands,
|
||||
customRenders : customRenders,
|
||||
onCreateDoc : userPrivs.create ? handleCreateDoc : null,
|
||||
onSaveDoc : userPrivs.edit ? handleSaveDoc : null,
|
||||
onCreateDoc : docAppData.userPrivs.create ? handleCreateDoc : null,
|
||||
onSaveDoc : docAppData.userPrivs.edit ? handleSaveDoc : null,
|
||||
canMoveDoc : canMoveDoc,
|
||||
onSwitchView : handleSwitchView,
|
||||
getActions : getActions,
|
||||
@@ -2151,6 +2235,7 @@ window.setDocAppOptions = function(_, options)
|
||||
getSpaceFetcher : getSpaceFetcher,
|
||||
showDocOutline : true,
|
||||
showDocHistory : true,
|
||||
$onDestroy : handleDocAppDestroy,
|
||||
}, window.docAppOptions, options);
|
||||
};
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ class docApp extends wg
|
||||
'onSwitchView' => '?string', // 切换视图事件。
|
||||
'getDocViewSidebarTabs' => '?string', // 获取文档视图侧边栏选项。
|
||||
'formatDataItem' => '?string', // 格式化数据条目。
|
||||
'viewModeUrl' => '?string' // 应用视图 URL 格式。
|
||||
'viewModeUrl' => '?string', // 应用视图 URL 格式。
|
||||
'hasZentaoSlashMenu' => '?boolean' // 是否显示禅道数据。
|
||||
);
|
||||
|
||||
public static function getPageJS(): ?string
|
||||
@@ -124,6 +125,39 @@ class docApp extends wg
|
||||
return $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换禅道数据菜单格式
|
||||
* Convert zentao list menu format.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
protected function convertZentaoListMenu(array $list): array
|
||||
{
|
||||
$menus = array();
|
||||
|
||||
foreach($list as $item)
|
||||
{
|
||||
$menu = array();
|
||||
|
||||
$menu['text'] = $item['name'];
|
||||
$menu['icon'] = $item['icon'];
|
||||
$menu['module'] = $item['module'];
|
||||
$menu['method'] = $item['method'];
|
||||
$menu['params'] = $item['params'];
|
||||
$menu['priv'] = $item['priv'];
|
||||
|
||||
if(isset($item['subMenu']))
|
||||
{
|
||||
$menu['items'] = $this->convertZentaoListMenu($item['subMenu']);
|
||||
}
|
||||
|
||||
$menus[] = $menu;
|
||||
}
|
||||
|
||||
return $menus;
|
||||
}
|
||||
|
||||
protected function build()
|
||||
{
|
||||
global $app, $lang, $config;
|
||||
@@ -133,6 +167,7 @@ class docApp extends wg
|
||||
jsVar('enableHocuspocus', $config->docHocuspocus->enable);
|
||||
jsVar('needReadable', $lang->doc->needReadable);
|
||||
jsVar('vision', $config->vision);
|
||||
jsVar('isInModal', isInModal());
|
||||
|
||||
/**
|
||||
* 定义文档应用接口链接。
|
||||
@@ -184,6 +219,7 @@ class docApp extends wg
|
||||
$langData->createRelease = $lang->api->createRelease;
|
||||
$langData->libTypeList = $lang->api->libTypeList;
|
||||
$langData->latestVersion = $lang->api->latestVersion;
|
||||
$langData->template = $lang->doc->template;
|
||||
|
||||
/**
|
||||
* 通过语言项定义文档表格列显示名称。
|
||||
@@ -245,6 +281,9 @@ class docApp extends wg
|
||||
}
|
||||
}
|
||||
|
||||
$hasZentaoSlashMenu = $this->prop('hasZentaoSlashMenu');
|
||||
if($hasZentaoSlashMenu === null ) $hasZentaoSlashMenu = true;
|
||||
|
||||
$app->control->loadModel('file');
|
||||
|
||||
$canDownload = common::hasPriv('file', 'download');
|
||||
@@ -272,6 +311,13 @@ class docApp extends wg
|
||||
$historyPanelProps = array('fileListProps' => $fileListProps);
|
||||
$canPreviewOffice = $canDownload && isset($config->file->libreOfficeTurnon) and $config->file->libreOfficeTurnon == 1;
|
||||
|
||||
$zentaoListMenu = $hasZentaoSlashMenu ? $this->getZentaoListMenu() : array();
|
||||
|
||||
$moreMenus = array_merge([array(
|
||||
'text' => $lang->doc->zentaoData,
|
||||
'type' => 'heading',
|
||||
)], $this->convertZentaoListMenu($zentaoListMenu));
|
||||
|
||||
return zui::docApp
|
||||
(
|
||||
set::_class('shadow rounded ring canvas'),
|
||||
@@ -315,9 +361,11 @@ class docApp extends wg
|
||||
set::langData($langData),
|
||||
set::historyPanel($historyPanelProps),
|
||||
set::showToolbar(true),
|
||||
set::moreMenu($moreMenus),
|
||||
set::moreMenuAction(jsRaw('window.moreMenuAction')),
|
||||
set::canPreviewOffice($canPreviewOffice),
|
||||
set::fileInfoUrl($fileInfoUrl),
|
||||
jsCall('setZentaoSlashMenu', $this->getZentaoListMenu(), $lang->doc->zentaoData, $config->vision, $config->doc->zentaoListMenuPosition)
|
||||
$hasZentaoSlashMenu ? jsCall('setZentaoSlashMenu', $zentaoListMenu, $lang->doc->zentaoData, $config->vision, $config->doc->zentaoListMenuPosition) : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
#pick-pop-admin-menu .dropmenu-item + .dropmenu-item {margin-top: 4px}
|
||||
#pick-pop-admin-menu .dropmenu-item.active {background-color: unset;}
|
||||
#pick-pop-admin-menu .dropmenu-item:hover {color: rgba(var(--color-primary-500-rgb),var(--tw-text-opacity)); background-color: rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));}
|
||||
.dropmenu-item .item-content .label {flex-shrink: 0;}
|
||||
@@ -161,7 +161,6 @@ class dropmenu extends wg
|
||||
|
||||
if($tab == 'admin')
|
||||
{
|
||||
if($method == 'register') return array();
|
||||
$currentMenuKey = $app->control->loadModel('admin')->getMenuKey();
|
||||
$text = isset($lang->admin->menuList->{$currentMenuKey}) ? $lang->admin->menuList->{$currentMenuKey}['name'] : $currentMenuKey;
|
||||
$url = createLink('admin', 'ajaxGetDropMenu', "currentMenuKey={$currentMenuKey}");
|
||||
|
||||
@@ -25,3 +25,5 @@
|
||||
.dynamic-sm > li.blue:before, .dynamic-sm > li:before {top: 10px; left: -18px; width: 11px; height: 11px; background: 0 0; border: 1px solid #eee}
|
||||
.dynamic-sm > li > div {line-height: 20px}
|
||||
.dynamic-sm > li > div:after {top: 13px; left: -15px; width: 5px; height: 5px}
|
||||
|
||||
#showMoreDynamic:hover {background-color: #efefef;}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class dynamicItem
|
||||
{
|
||||
public static function getStatusClass(object $dynamic): string
|
||||
{
|
||||
$action = strtolower($dynamic->action);
|
||||
$objectType = strtolower($dynamic->objectType);
|
||||
|
||||
// if($dynamic->major) return 'active';
|
||||
|
||||
if($objectType == 'release' && $action == 'opened') return 'trophy';
|
||||
if($objectType == 'project' && $action == 'closed') return 'trophy';
|
||||
|
||||
if(strpos($action, 'assigned') !== false) return 'blue';
|
||||
if(strpos($action, 'releaseddoc') !== false) return 'green';
|
||||
if(strpos($action, 'finished') !== false || strpos($action, 'resolved') !== false || ($action == 'closed' && $objectType != 'product')) return 'green';
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function build(object $dynamic, array $users): node
|
||||
{
|
||||
global $config;
|
||||
$dynamicLabel = zget($dynamic, 'dynamicLabel', '');
|
||||
if(empty($dynamicLabel)) $dynamicLabel = zget($dynamic, 'actionLabel', '');
|
||||
|
||||
$objectLabel = array();
|
||||
if($dynamic->action != 'login' && $dynamic->action != 'logout')
|
||||
{
|
||||
$objectLabel[] = span
|
||||
(
|
||||
$dynamic->objectLabel
|
||||
);
|
||||
$objectID = $dynamic->objectID && strpos(',module,chartgroup,', ",$dynamic->objectType,") !== false && strpos(',created,edited,moved,', "$dynamic->action") !== false ? trim($dynamic->extra, ',') : $dynamic->objectID;
|
||||
$objectLabel[] = $objectID ? span
|
||||
(
|
||||
setClass('label gray-300-outline mx-2 font-sm'),
|
||||
$objectID
|
||||
) : null;
|
||||
|
||||
if(($config->edition == 'max' && strpos($config->action->assetType, ",{$dynamic->objectType},") !== false) && empty($dynamic->objectName))
|
||||
{
|
||||
$objectLabel[] = span("#{$dynamic->objectID}");
|
||||
}
|
||||
elseif(empty($dynamic->objectID) and $dynamic->extra)
|
||||
{
|
||||
$objectLabel[] = span("#{$dynamic->extra}");
|
||||
}
|
||||
elseif(empty($dynamic->objectLink))
|
||||
{
|
||||
$objectLabel[] = span($dynamic->objectName);
|
||||
}
|
||||
else
|
||||
{
|
||||
$objectLabel[] = a
|
||||
(
|
||||
set::href($dynamic->objectLink),
|
||||
set::title($dynamic->objectName),
|
||||
$dynamic->objectName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$dynamicClass = static::getStatusClass($dynamic);
|
||||
return li
|
||||
(
|
||||
setClass($dynamicClass),
|
||||
div
|
||||
(
|
||||
span
|
||||
(
|
||||
setClass('dynamic-tag p-1 text-gray'),
|
||||
isset($dynamic->time) ? $dynamic->time : $dynamic->date
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('dynamic-text flex flex-nowrap justify-between items-center'),
|
||||
div
|
||||
(
|
||||
setClass('clip p-1'),
|
||||
zget($users, $dynamic->actor),
|
||||
span
|
||||
(
|
||||
setClass('text-gray px-1'),
|
||||
$dynamicLabel
|
||||
),
|
||||
$objectLabel
|
||||
),
|
||||
$dynamicClass == 'trophy' ?
|
||||
div
|
||||
(
|
||||
setClass('w-0 h-0'),
|
||||
h::img
|
||||
(
|
||||
set::src('static/svg/trophy.svg'),
|
||||
setClass('ml-2')
|
||||
)
|
||||
) : null
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
window.showMore = function(e)
|
||||
{
|
||||
let $this = $(e.target);
|
||||
if($this.attr('id') != 'showMoreDynamic') $this = $this.closest('#showMoreDynamic');
|
||||
$this.find('i').removeClass('icon-chevron-down').addClass('spin icon-refresh');
|
||||
|
||||
let $appendTo = $this.closest('li').prev().find('ul');
|
||||
let lastActionID = $this.data('lastid');
|
||||
$.get($.createLink('action', 'ajaxGetMoreActions', "lastActionID=" + lastActionID), function(data)
|
||||
{
|
||||
$appendTo.closest('li').removeClass('collapsed');
|
||||
$appendTo.append(data);
|
||||
|
||||
let hasMore = $appendTo.children('#hasMore').html() == '1';
|
||||
$this.attr('data-lastid', $appendTo.children('#lastid').html());
|
||||
$this.find('i').addClass('icon-chevron-down').removeClass('spin icon-refresh');
|
||||
if(!hasMore) $this.closest('li').remove();
|
||||
|
||||
$appendTo.children('style').remove();
|
||||
$appendTo.children('script').remove();
|
||||
$appendTo.children('#lastid').remove();
|
||||
$appendTo.children('#hasMore').remove();
|
||||
})
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
helper::import(__DIR__ . DS . 'dynamicitem.php');
|
||||
class dynamic extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
@@ -15,104 +16,9 @@ class dynamic extends wg
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
||||
}
|
||||
|
||||
public function getStatusClass(object $dynamic): string
|
||||
public static function getPageJS(): ?string
|
||||
{
|
||||
$action = strtolower($dynamic->action);
|
||||
$objectType = strtolower($dynamic->objectType);
|
||||
|
||||
// if($dynamic->major) return 'active';
|
||||
|
||||
if($objectType == 'release' && $action == 'opened') return 'trophy';
|
||||
if($objectType == 'project' && $action == 'closed') return 'trophy';
|
||||
|
||||
if(strpos($action, 'assigned') !== false) return 'blue';
|
||||
if(strpos($action, 'releaseddoc') !== false) return 'green';
|
||||
if(strpos($action, 'finished') !== false || strpos($action, 'resolved') !== false || ($action == 'closed' && $objectType != 'product')) return 'green';
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function dynamicItem(object $dynamic, array $users): node
|
||||
{
|
||||
global $config;
|
||||
$dynamicLabel = zget($dynamic, 'dynamicLabel', '');
|
||||
if(empty($dynamicLabel)) $dynamicLabel = zget($dynamic, 'actionLabel', '');
|
||||
|
||||
$objectLabel = array();
|
||||
if($dynamic->action != 'login' && $dynamic->action != 'logout')
|
||||
{
|
||||
$objectLabel[] = span
|
||||
(
|
||||
$dynamic->objectLabel
|
||||
);
|
||||
$objectID = $dynamic->objectID && strpos(',module,chartgroup,', ",$dynamic->objectType,") !== false && strpos(',created,edited,moved,', "$dynamic->action") !== false ? trim($dynamic->extra, ',') : $dynamic->objectID;
|
||||
$objectLabel[] = $objectID ? span
|
||||
(
|
||||
setClass('label gray-300-outline mx-2 font-sm'),
|
||||
$objectID
|
||||
) : null;
|
||||
|
||||
if(($config->edition == 'max' && strpos($config->action->assetType, ",{$dynamic->objectType},") !== false) && empty($dynamic->objectName))
|
||||
{
|
||||
$objectLabel[] = span("#{$dynamic->objectID}");
|
||||
}
|
||||
elseif(empty($dynamic->objectID) and $dynamic->extra)
|
||||
{
|
||||
$objectLabel[] = span("#{$dynamic->extra}");
|
||||
}
|
||||
elseif(empty($dynamic->objectLink))
|
||||
{
|
||||
$objectLabel[] = span($dynamic->objectName);
|
||||
}
|
||||
else
|
||||
{
|
||||
$objectLabel[] = a
|
||||
(
|
||||
set::href($dynamic->objectLink),
|
||||
set::title($dynamic->objectName),
|
||||
$dynamic->objectName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$dynamicClass = $this->getStatusClass($dynamic);
|
||||
return li
|
||||
(
|
||||
setClass($dynamicClass),
|
||||
div
|
||||
(
|
||||
span
|
||||
(
|
||||
setClass('dynamic-tag p-1 text-gray'),
|
||||
isset($dynamic->time) ? $dynamic->time : $dynamic->date
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('dynamic-text flex flex-nowrap justify-between items-center'),
|
||||
div
|
||||
(
|
||||
setClass('clip p-1'),
|
||||
zget($users, $dynamic->actor),
|
||||
span
|
||||
(
|
||||
setClass('text-gray px-1'),
|
||||
$dynamicLabel
|
||||
),
|
||||
$objectLabel
|
||||
),
|
||||
$dynamicClass == 'trophy' ?
|
||||
div
|
||||
(
|
||||
setClass('w-0 h-0'),
|
||||
h::img
|
||||
(
|
||||
set::src('static/svg/trophy.svg'),
|
||||
setClass('ml-2')
|
||||
)
|
||||
) : null
|
||||
)
|
||||
)
|
||||
);
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
protected function build()
|
||||
@@ -130,7 +36,7 @@ class dynamic extends wg
|
||||
foreach($dynamics as $dynamic)
|
||||
{
|
||||
if($dynamic->action == 'adjusttasktowait') continue;
|
||||
$dynamicListView->add($this->dynamicItem($dynamic, $users));
|
||||
$dynamicListView->add(dynamicItem::build($dynamic, $users));
|
||||
}
|
||||
|
||||
return $dynamicListView;
|
||||
|
||||
@@ -77,11 +77,13 @@ class formPanel extends panel
|
||||
{
|
||||
global $app;
|
||||
$moduleName = $app->getModuleName();
|
||||
$methodName = $app->getMethodName();
|
||||
if($moduleName == 'caselib') return data('lib');
|
||||
if($moduleName == 'flow') return data('data');
|
||||
if($moduleName == 'productplan') return data('plan');
|
||||
if($moduleName == 'projectrelease') return data('release');
|
||||
if($moduleName == 'projectbuild') return data('build');
|
||||
if($moduleName == 'project' && ($methodName == 'create' or $methodName == 'createtemplate')) return data('copyProject') ? data('copyProject') : null;
|
||||
return data($moduleName);
|
||||
}
|
||||
|
||||
@@ -104,6 +106,9 @@ class formPanel extends panel
|
||||
}
|
||||
}
|
||||
|
||||
if($moduleName == 'project' && $methodName == 'createtemplate') $methodName = 'create';
|
||||
if($moduleName == 'project' && $methodName == 'edittemplate') $methodName = 'edit';
|
||||
|
||||
/* 反馈转化。 */
|
||||
if($moduleName == 'feedback')
|
||||
{
|
||||
|
||||
@@ -611,7 +611,7 @@ function initGantt()
|
||||
if(!canViewReview) return false;
|
||||
loadPage($.createLink('review', 'view', 'reviewID=' + task.reviewID));
|
||||
}
|
||||
if(task.type == 'plan' && !task.isParent)
|
||||
if(task.type == 'plan' && !task.isParent && !(task.executionType == 'kanban' && task.isTpl == '1'))
|
||||
{
|
||||
if(!canViewTaskList) return false;
|
||||
$.apps.open($.createLink('execution', 'task', 'id=' + task.id), 'execution');
|
||||
|
||||
@@ -41,6 +41,9 @@ class heading extends wg
|
||||
if($tab == 'product') $currentMethod = 'all';
|
||||
}
|
||||
|
||||
/* 项目模板appName点击后跳转到项目模板列表页面。 */
|
||||
if($lang->$tab->common == $lang->project->template) $currentMethod = 'template';
|
||||
|
||||
if(empty($currentModule) || empty($currentMethod)) return null;
|
||||
|
||||
$url = createLink($currentModule, $currentMethod);
|
||||
|
||||
@@ -85,10 +85,6 @@ class history extends wg
|
||||
$actions = data('actions');
|
||||
if(empty($actions) && !empty($objectID)) $actions = $app->loadTarget('action')->getList($objectType, $objectID);
|
||||
if(!empty($actions)) $actions = $app->loadTarget('action')->buildActionList($actions, $users, (bool)$this->prop('commentBtn'));
|
||||
foreach($actions as $action)
|
||||
{
|
||||
if(!empty($action->comment)) $action->comment = htmlentities($action->comment);
|
||||
}
|
||||
$this->setProp('actions', $actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class mainNavbar extends nav
|
||||
$badgeMap = $this->prop('badgeMap');
|
||||
$onRenderItem = $this->prop('onRenderItem');
|
||||
$itemProps = $this->prop('itemProps');
|
||||
$activeItem = $this->prop('active');
|
||||
$activeItem = $this->prop('active') ?: data('activeMenuItem');
|
||||
|
||||
foreach($menu as $key => $menuItem)
|
||||
{
|
||||
|
||||
@@ -76,6 +76,7 @@ function createMonaco(id, action, options, diffContent, onMouseDown, onMouseMove
|
||||
if(onMouseDown) editor.onMouseDown(function(obj){eval(onMouseDown + '(obj)')})
|
||||
if(onMouseMove) editor.onMouseMove(function(obj){eval(onMouseMove + '(obj)')})
|
||||
|
||||
let range = null;
|
||||
if(selectedLines)
|
||||
{
|
||||
const lines = selectedLines.split(',');
|
||||
@@ -98,12 +99,22 @@ function createMonaco(id, action, options, diffContent, onMouseDown, onMouseMove
|
||||
});
|
||||
}
|
||||
|
||||
const range = new monaco.Range(startLine, startCol, endLine, endCol);
|
||||
range = new monaco.Range(startLine, startCol, endLine, endCol);
|
||||
programmaticSelectionRange = range;
|
||||
editor.setSelection(range);
|
||||
editor.revealRangeInCenter(range);
|
||||
updateDecorations();
|
||||
}
|
||||
|
||||
editor.onMouseDown(function(e)
|
||||
{
|
||||
if(range)
|
||||
{
|
||||
programmaticSelectionRange = range;
|
||||
updateDecorations();
|
||||
}
|
||||
});
|
||||
|
||||
function updateDecorations()
|
||||
{
|
||||
editor.deltaDecorations(decorations, []);
|
||||
|
||||
@@ -53,7 +53,7 @@ class navbar extends wg
|
||||
if(empty($executionPairs)) return;
|
||||
if(in_array($project->model, array('waterfall', 'waterfallplus')))
|
||||
{
|
||||
$allExecutions = $app->control->dao->select('id,name,path')->from(TABLE_EXECUTION)->where('project')->eq($object->project)->andWhere('deleted')->eq('0')->fetchAll('id');
|
||||
$allExecutions = $app->control->dao->select('id,name,path,grade')->from(TABLE_EXECUTION)->where('project')->eq($object->project)->andWhere('deleted')->eq('0')->fetchAll('id');
|
||||
$orderedExecutions = $app->control->loadModel('execution')->resetExecutionSorts($executionPairs, $topExecutions);
|
||||
$executionPairs = array();
|
||||
foreach($orderedExecutions as $executionID => $executionName)
|
||||
|
||||
@@ -115,7 +115,7 @@ class queryFilterModal extends wg
|
||||
)
|
||||
)
|
||||
),
|
||||
on::change('.query-filter-row .form-control')->do($onChange)
|
||||
on::change('.query-filter-row .form-control, .query-filter-row .input-control')->do($onChange)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,8 @@ class relatedObjectList extends relatedList
|
||||
set::icon('treemap'),
|
||||
set::size('sm'),
|
||||
set::type('secondary'),
|
||||
setClass('my-2 pull-right'),
|
||||
setClass('my-2'),
|
||||
setClass($btn ? 'pull-right' : ''),
|
||||
setData(array('toggle' => 'modal', 'size' => 'lg')),
|
||||
setID('graphButton'),
|
||||
$app->loadLang('custom')->custom->relationGraph
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace zin;
|
||||
|
||||
class remotePicker extends wg
|
||||
{
|
||||
protected static array $defineProps = array(
|
||||
'label?: string', // 控件标签。
|
||||
'id?: string', // 控件 ID。
|
||||
'name?: string="users[]"', // 控件名称。
|
||||
'value?: string', // 控件默认值。
|
||||
'type?: string="user"', // 控件类型。
|
||||
'params?: string=""', // 额外参数,如user的pofirst。
|
||||
'items?: string|array', // picker 列表项或列表项获取方法。
|
||||
'menu?: array', // picker 附加的菜单选项。
|
||||
'toolbar?: boolean|array', // picker 列表工具栏。
|
||||
'multiple?: boolean|number=false', // picker 是否允许选择多个值,如果指定为数字,则限制多选的数目,默认 `false`。
|
||||
'inputGroupClass?: string=""' // inputGroup 的 class 属性。
|
||||
);
|
||||
|
||||
protected function created()
|
||||
{
|
||||
$items = $this->prop('items');
|
||||
if(!$items)
|
||||
{
|
||||
$type = $this->prop('type');
|
||||
$params = $this->prop('params') ? $this->prop('params') : 'noclosed,nodeleted';
|
||||
switch($type)
|
||||
{
|
||||
case 'user':
|
||||
$items = createLink('user', 'ajaxGetItems', 'params=' . $params);
|
||||
break;
|
||||
default:
|
||||
$items = createLink('user', 'ajaxGetItems', 'params=' . $params);
|
||||
break;
|
||||
}
|
||||
$this->setProp('items', $items);
|
||||
}
|
||||
|
||||
if(!$this->prop('id')) $this->setProp('id', '_' . $this->prop('name'));
|
||||
}
|
||||
|
||||
protected function build()
|
||||
{
|
||||
return zui::picker
|
||||
(
|
||||
set::_class('form-group-wrapper picker-box'),
|
||||
set::_map(array('value' => 'defaultValue', 'formID' => 'id')),
|
||||
set($this->props->pick(array('id', 'name', 'value', 'items', 'toolbar', 'menu', 'multiple'))),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,9 @@ class searchToggle extends wg
|
||||
'url?: string',
|
||||
'searchUrl?: string',
|
||||
'text?: string',
|
||||
'simple?: boolean' // 是否为简单模式,不包含保存搜索条件和已保存的查询条件侧边栏。
|
||||
'onSearch?: callback', // jsRaw("(result) => console.log('onSearchForm', result)")
|
||||
'searchLoader?: string|array',
|
||||
'simple?: boolean' // 是否为简单模式,不包含保存搜索条件和已保存的查询条件侧边栏。
|
||||
);
|
||||
|
||||
public static function getPageCSS(): ?string
|
||||
@@ -33,7 +35,7 @@ class searchToggle extends wg
|
||||
if(!common::hasPriv('search', 'buildForm')) return;
|
||||
|
||||
global $lang, $app, $config;
|
||||
list($target, $module, $open, $url, $searchUrl, $text, $simple) = $this->prop(array('target', 'module', 'open', 'url', 'searchUrl', 'text', 'simple'));
|
||||
list($target, $module, $open, $url, $searchUrl, $text, $simple, $onSearch, $searchLoader) = $this->prop(array('target', 'module', 'open', 'url', 'searchUrl', 'text', 'simple', 'onSearch', 'searchLoader'));
|
||||
|
||||
if(is_null($open))
|
||||
{
|
||||
@@ -49,14 +51,19 @@ class searchToggle extends wg
|
||||
if(is_null($searchUrl)) $searchUrl = createLink('search', 'buildZinQuery');
|
||||
}
|
||||
|
||||
$searchFormOptions = array('module' => $module, 'target' => $target, 'url' => $url);
|
||||
if(!empty($searchUrl)) $searchFormOptions['searchUrl'] = $searchUrl;
|
||||
if(!empty($simple)) $searchFormOptions['simple'] = $simple;
|
||||
if(!empty($onSearch)) $searchFormOptions['onSearch'] = $onSearch;
|
||||
if(!empty($searchLoader)) $searchFormOptions['searchLoader'] = $searchLoader;
|
||||
return btn
|
||||
(
|
||||
set::className('search-form-toggle rounded-full gray-300-outline size-sm'),
|
||||
set::icon('search'),
|
||||
set::active($open),
|
||||
set::text($text),
|
||||
toggle::searchform(array('module' => $module, 'target' => $target, 'url' => $url, 'searchUrl' => $searchUrl, 'simple' => $simple)),
|
||||
$open ? on::init()->call('zui.toggleSearchForm', array('module' => $module, 'target' => $target, 'show' => $open, 'url' => $url, 'searchUrl' => $searchUrl)) : on::init()->do('$element.closest(".show-search-form").removeClass("show-search-form").find(".search-form[data-module=' . $module . ']").remove()')
|
||||
toggle::searchform($searchFormOptions),
|
||||
$open ? on::init()->call('zui.toggleSearchForm', array_merge(array('show' => $open), $searchFormOptions)) : on::init()->do('$element.closest(".show-search-form").removeClass("show-search-form").find(".search-form[data-module=' . $module . ']").remove()')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class taskEffortInfo extends wg
|
||||
$items[$lang->task->realStarted] =
|
||||
helper::isZeroDate($task->realStarted) ? '' : substr($task->realStarted, 0, 19);
|
||||
$items[$lang->task->deadline] = array('content' => helper::isZeroDate($task->deadline) ? '' : $task->deadline, 'contentClass' => 'deadline-text');
|
||||
if(isset($task->delay)) $items[$lang->task->deadline]['children'] = label(html(sprintf($lang->task->delayWarning, $task->delay)), setClass('danger-pale circle'));
|
||||
if(!empty($task->delay)) $items[$lang->task->deadline]['children'] = label(html(sprintf($lang->task->delayWarning, $task->delay)), setClass('danger-pale circle'));
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class toggle
|
||||
*/
|
||||
function toggle(string $name, array $dataset = array()): set
|
||||
{
|
||||
$dataset['toggle'] = $name;
|
||||
return setData($dataset);
|
||||
$props = array('zui-toggle' => $name);
|
||||
if(!empty($dataset)) $props["zui-toggle-$name"] = $dataset;
|
||||
return set($props);
|
||||
}
|
||||
|
||||
+80
-100
@@ -1,16 +1,15 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include '../framework/helper.class.php';
|
||||
include '../lib/dbh/dbh.class.php';
|
||||
|
||||
$config = new stdclass();
|
||||
|
||||
$config->db = new stdclass();
|
||||
$config->default = new stdclass();
|
||||
function getWebRoot() {}
|
||||
include '../config/my.php';
|
||||
|
||||
if(!file_exists('dmconfig.php'))
|
||||
{
|
||||
if (!file_exists('dmconfig.php')) {
|
||||
print("Please create the config file named 'dmconfig.php' in this directory. Config example:");
|
||||
print <<<EOT
|
||||
|
||||
@@ -39,25 +38,23 @@ function formatSQL($sql)
|
||||
{
|
||||
$fieldsBegin = stripos($sql, 'select');
|
||||
$fieldsEnd = stripos($sql, 'from');
|
||||
$fields = substr($sql, $fieldsBegin+6, $fieldsEnd-$fieldsBegin-6);
|
||||
$fields = substr($sql, $fieldsBegin + 6, $fieldsEnd - $fieldsBegin - 6);
|
||||
$fieldList = preg_split("/,(?![^(]+\))/", $fields);
|
||||
foreach($fieldList as $key => $field)
|
||||
{
|
||||
foreach ($fieldList as $key => $field) {
|
||||
$aliasPos = stripos($field, ' AS ');
|
||||
$subField = substr($field, 0, $aliasPos);
|
||||
if(stripos($field, 'SUM(') === 0) $subField = substr($subField, 4, -1);
|
||||
if (stripos($field, 'SUM(') === 0) $subField = substr($subField, 4, -1);
|
||||
|
||||
$fieldParts = preg_split("/\+(?![^(]+\))/", $subField);
|
||||
foreach($fieldParts as $pkey => $fieldPart)
|
||||
{
|
||||
foreach ($fieldParts as $pkey => $fieldPart) {
|
||||
$originField = trim($fieldPart);
|
||||
if(stripos($originField, 'if(') === false) continue;
|
||||
if (stripos($originField, 'if(') === false) continue;
|
||||
$fieldParts[$pkey] = formatDmIfFunction($originField);
|
||||
}
|
||||
$fieldList[$key] = str_replace($subField, implode(' + ', $fieldParts), $field);
|
||||
}
|
||||
$fields = implode(',', $fieldList);
|
||||
return substr($sql, 0, $fieldsBegin+6) . $fields . substr($sql, $fieldsEnd);
|
||||
return substr($sql, 0, $fieldsBegin + 6) . $fields . substr($sql, $fieldsEnd);
|
||||
}
|
||||
|
||||
function formatDmIfFunction($field)
|
||||
@@ -65,17 +62,16 @@ function formatDmIfFunction($field)
|
||||
preg_match('/if\(.+\)+/i', $field, $matches);
|
||||
|
||||
$if = $matches[0];
|
||||
if(substr_count($if, '(') == 1)
|
||||
{
|
||||
if (substr_count($if, '(') == 1) {
|
||||
$pos = strpos($if, ')');
|
||||
$if = substr($if, 0, $pos+1);
|
||||
$if = substr($if, 0, $pos + 1);
|
||||
}
|
||||
|
||||
/* fix sum(if(..., 1, 0)) , count(if(..., 1, 0)) */
|
||||
if(substr($if, strlen($if)-2) == '))' and (stripos($field, 'sum(') == 0 or stripos($field, 'count(') == 0)) $if = substr($if, 0, strlen($if)-1);
|
||||
if (substr($if, strlen($if) - 2) == '))' and (stripos($field, 'sum(') == 0 or stripos($field, 'count(') == 0)) $if = substr($if, 0, strlen($if) - 1);
|
||||
|
||||
$parts = explode(',', substr($if, 3, strlen($if)-4)); // remove 'if(' and ')'
|
||||
$case = 'CASE WHEN ' . implode(',', array_slice($parts, 0, count($parts)-2)) . ' THEN ' . $parts[count($parts)-2] . ' ELSE ' . $parts[count($parts)-1] . ' END';
|
||||
$parts = explode(',', substr($if, 3, strlen($if) - 4)); // remove 'if(' and ')'
|
||||
$case = 'CASE WHEN ' . implode(',', array_slice($parts, 0, count($parts) - 2)) . ' THEN ' . $parts[count($parts) - 2] . ' ELSE ' . $parts[count($parts) - 1] . ' END';
|
||||
$field = str_ireplace($if, $case, $field);
|
||||
|
||||
return $field;
|
||||
@@ -91,25 +87,21 @@ $views = array();
|
||||
$indexes = array(); //普通索引
|
||||
$tablePrimary = array(); //表的主键
|
||||
|
||||
foreach($data as $table)
|
||||
{
|
||||
foreach ($data as $table) {
|
||||
$table = current($table);
|
||||
|
||||
/* Table. */
|
||||
if(strpos($table, 'zt_') === 0)
|
||||
{
|
||||
if (strpos($table, 'zt_') === 0) {
|
||||
$stmt = $mysql->prepare('DESC ' . $table);
|
||||
$stmt->execute();
|
||||
$fields = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
/* Table. */
|
||||
$tables[$table] = array();
|
||||
foreach($fields as $field)
|
||||
{
|
||||
foreach ($fields as $field) {
|
||||
$type = strtolower(explode('(', $field['Type'])[0]);
|
||||
$type = trim(str_replace('unsigned', '', $type));
|
||||
switch($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'int':
|
||||
case 'mediumint':
|
||||
case 'smallint':
|
||||
@@ -119,7 +111,7 @@ foreach($data as $table)
|
||||
case 'enum':
|
||||
case 'varchar':
|
||||
case 'char':
|
||||
$type = 'varchar(255)';
|
||||
$type = 'varchar(1000)';
|
||||
break;
|
||||
case 'mediumtext':
|
||||
case 'longtext':
|
||||
@@ -142,33 +134,28 @@ foreach($data as $table)
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if($result)
|
||||
{
|
||||
foreach($result as $item)
|
||||
{
|
||||
if ($result) {
|
||||
foreach ($result as $item) {
|
||||
$keyName = $item['Key_name'];
|
||||
|
||||
/* 主键索引在创建表的时候一起创建。 */
|
||||
if($keyName == 'PRIMARY')
|
||||
{
|
||||
if(!isset($tablePrimary[$table])) $tablePrimary[$table] = array();
|
||||
$tablePrimary[$table][(int)$item['Seq_in_index']-1] = $item['Column_name'];
|
||||
if ($keyName == 'PRIMARY') {
|
||||
if (!isset($tablePrimary[$table])) $tablePrimary[$table] = array();
|
||||
$tablePrimary[$table][(int)$item['Seq_in_index'] - 1] = $item['Column_name'];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 其他索引单独创建。 */
|
||||
if(!isset($indexes[$table . $keyName])) $indexes[$table . $keyName] = array('table' => $table, 'name' => $keyName, 'type' => $item['Index_type'], 'isUnique' => !$item['Non_unique'], 'cols' => array());
|
||||
if (!isset($indexes[$table . $keyName])) $indexes[$table . $keyName] = array('table' => $table, 'name' => $keyName, 'type' => $item['Index_type'], 'isUnique' => !$item['Non_unique'], 'cols' => array());
|
||||
|
||||
/* 达梦的全文索引只支持一个字段。 */
|
||||
if($item['Index_type'] == 'FULLTEXT' && $item['Column_name'] != 'title') continue;
|
||||
if ($item['Index_type'] == 'FULLTEXT' && $item['Column_name'] != 'title') continue;
|
||||
|
||||
$indexes[$table . $keyName]['cols'][(int)$item['Seq_in_index']-1] = $item['Column_name'];
|
||||
$indexes[$table . $keyName]['cols'][(int)$item['Seq_in_index'] - 1] = $item['Column_name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* View. */
|
||||
elseif(strpos($table, 'ztv_') === 0)
|
||||
{
|
||||
/* View. */ elseif (strpos($table, 'ztv_') === 0) {
|
||||
$stmt = $mysql->prepare('SHOW CREATE VIEW ' . $table);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
@@ -185,31 +172,28 @@ echo "初始化表空间和用户\n";
|
||||
$dm = new dbh($dmConfig, false);
|
||||
|
||||
/* Delete. */
|
||||
$tableSpace = strtoupper($dmConfig->name);
|
||||
$res = $dm->query("SELECT * FROM dba_users WHERE username = '{$dmConfig->name}'")->fetchAll();
|
||||
if(!empty($res))
|
||||
{
|
||||
if (!empty($res)) {
|
||||
$dropUser = "DROP USER \"{$dmConfig->name}\" CASCADE";
|
||||
$dm->rawQuery($dropUser);
|
||||
}
|
||||
|
||||
$tableSpace = strtoupper($dmConfig->name);
|
||||
$res = $dm->query("SELECT * FROM dba_data_files WHERE TABLESPACE_NAME = '$tableSpace'")->fetchAll();
|
||||
if(!empty($res))
|
||||
{
|
||||
if (!empty($res)) {
|
||||
$dropTableSpace = "DROP TABLESPACE \"$tableSpace\"";
|
||||
|
||||
$dm->rawQuery($dropTableSpace);
|
||||
}
|
||||
|
||||
/* Create. */
|
||||
$createTableSpace = "CREATE TABLESPACE \"$tableSpace\" DATAFILE '{$dmConfig->name}.DBF' size 150 AUTOEXTEND ON";
|
||||
$createUser = "CREATE USER \"{$dmConfig->name}\" IDENTIFIED by {$dmConfig->password} DEFAULT TABLESPACE \"{$dmConfig->name}\" DEFAULT INDEX TABLESPACE \"{$dmConfig->name}\"";
|
||||
//$createTableSpace = "CREATE TABLESPACE \"$tableSpace\" DATAFILE '{$dmConfig->name}.DBF' size 150 AUTOEXTEND ON";
|
||||
//$createUser = "CREATE USER \"{$dmConfig->name}\" IDENTIFIED by \"{$dmConfig->password}\" DEFAULT TABLESPACE \"{$dmConfig->name}\" DEFAULT INDEX TABLESPACE \"{$dmConfig->name}\"";
|
||||
//
|
||||
//$dm->query($createTableSpace);
|
||||
//$dm->query($createUser);
|
||||
|
||||
$dm->query($createTableSpace);
|
||||
$dm->query($createUser);
|
||||
|
||||
$createSchema = "CREATE SCHEMA \"{$dmConfig->name}\" AUTHORIZATION \"{$dmConfig->name}\"";
|
||||
$createSchema = "CREATE SCHEMA \"{$dmConfig->name}\" AUTHORIZATION \"{$dmConfig->user}\"";
|
||||
$dm->query($createSchema);
|
||||
|
||||
echo "生成达梦数据表结构\n";
|
||||
@@ -218,29 +202,26 @@ $dm = new dbh($dmConfig);
|
||||
$identityTables = array(); //标记有自增字段的表,插入数据的时候会判断
|
||||
|
||||
/* 插入表结构。 */
|
||||
foreach($tables as $table => $fields)
|
||||
{
|
||||
foreach ($tables as $table => $fields) {
|
||||
$sql = "CREATE TABLE IF NOT EXISTS \"$table\" (\n";
|
||||
|
||||
foreach(array_values($fields) as $key => $field)
|
||||
{
|
||||
foreach (array_values($fields) as $key => $field) {
|
||||
$sql .= '"' . $field['name'] . '" ' . $field['type'] . ($field['null'] ? ' NULL ' : ' NOT NULL ');
|
||||
if($field['default'] !== NULL) $sql .= " DEFAULT '{$field['default']}' ";
|
||||
if($field['default'] == '0000-00-00') $field['default'] = NULL;
|
||||
if($field['default'] !== NULL && $field['default'] != 'CURRENT_TIMESTAMP') $sql .= " DEFAULT '{$field['default']}' ";
|
||||
|
||||
if($field['autoIncrement'])
|
||||
{
|
||||
if ($field['autoIncrement']) {
|
||||
$sql .= " IDENTITY(1, 1) ";
|
||||
$identityTables[$table] = $table;
|
||||
}
|
||||
|
||||
$sql .= $key == count(array_values($fields))-1 && !isset($tablePrimary[$table]) ? "\n" : ",\n";
|
||||
$sql .= $key == count(array_values($fields)) - 1 && !isset($tablePrimary[$table]) ? "\n" : ",\n";
|
||||
}
|
||||
|
||||
if(isset($tablePrimary[$table]))
|
||||
{
|
||||
if (isset($tablePrimary[$table])) {
|
||||
$primary = array();
|
||||
ksort($tablePrimary[$table]);
|
||||
foreach($tablePrimary[$table] as $col) $primary[] = '"' . $col . '"';
|
||||
foreach ($tablePrimary[$table] as $col) $primary[] = '"' . $col . '"';
|
||||
|
||||
$sql .= "NOT CLUSTER PRIMARY KEY (" . implode(',', $primary) . ")"; // 只有NOT CLUSTER可以删除
|
||||
}
|
||||
@@ -251,25 +232,21 @@ foreach($tables as $table => $fields)
|
||||
}
|
||||
|
||||
echo "生成达梦表索引\n";
|
||||
foreach($indexes as $index)
|
||||
{
|
||||
foreach ($indexes as $index) {
|
||||
$name = strtolower($index['table']) . '_' . $index['name'];
|
||||
foreach($index['cols'] as $col)
|
||||
{
|
||||
foreach ($index['cols'] as $col) {
|
||||
$fields = array();
|
||||
ksort($index['cols']);
|
||||
foreach($index['cols'] as $col)
|
||||
{
|
||||
foreach ($index['cols'] as $col) {
|
||||
$fields[] = '"' . $col . '"';
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'CREATE ';
|
||||
if($index['isUnique']) // 唯一索引
|
||||
if ($index['isUnique']) // 唯一索引
|
||||
{
|
||||
$sql .= ' UNIQUE ';
|
||||
}
|
||||
elseif($index['type'] == 'FULLTEXT') // 全文索引
|
||||
} elseif ($index['type'] == 'FULLTEXT') // 全文索引
|
||||
{
|
||||
$sql .= ' CONTEXT ';
|
||||
}
|
||||
@@ -281,8 +258,7 @@ foreach($indexes as $index)
|
||||
|
||||
echo "生成达梦视图\n";
|
||||
/* 插入视图结构。 */
|
||||
foreach($views as $view => $info)
|
||||
{
|
||||
foreach ($views as $view => $info) {
|
||||
$sql = str_replace('convert(', '(', $info['sql']);
|
||||
$sql = str_replace('using utf8mb3', '', $sql);
|
||||
$sql = str_replace('using utf8_general_ci', '', $sql);
|
||||
@@ -290,44 +266,48 @@ foreach($views as $view => $info)
|
||||
}
|
||||
|
||||
echo "开始插入数据,请耐心等待...";
|
||||
foreach($tables as $table => $fields)
|
||||
{
|
||||
foreach ($tables as $table => $fields) {
|
||||
$dm->rawQuery("DELETE FROM `$table`");
|
||||
if ($table == 'zt_compile') continue;
|
||||
$rows = $mysql->query("SELECT * FROM `$table`")->fetchAll(PDO::FETCH_ASSOC);
|
||||
if(empty($rows)) continue;
|
||||
if (empty($rows)) continue;
|
||||
|
||||
$fs = array();
|
||||
foreach($fields as $field) $fs[] = '"' . $field['name'] . '"';
|
||||
foreach ($fields as $field) $fs[] = '"' . $field['name'] . '"';
|
||||
$fs = implode(',', $fs);
|
||||
|
||||
$template = "INSERT INTO \"$table\"($fs) VALUES";
|
||||
|
||||
$step = 500;
|
||||
$step = 5000;
|
||||
$begin = 0;
|
||||
$data = array();
|
||||
for($key = $begin; $key < count($rows); $key++)
|
||||
{
|
||||
$row = $rows[$key];
|
||||
$values = array();
|
||||
foreach($fields as $field)
|
||||
{
|
||||
$value = str_replace("'", "''''", $row[$field['name']]);
|
||||
$value = "'{$value}'";
|
||||
if($value == "'0000-00-00 00:00:00'" || $value == "'0000-00-00'")
|
||||
{
|
||||
$null = $tables[$table][$field['name']]['null'];
|
||||
$value = $null ? 'NULL': '1970-01-01';
|
||||
while ($begin < count($rows)) {
|
||||
$data = array();
|
||||
for ($key = $begin; $key < $begin + $step; $key++) {
|
||||
if (!isset($rows[$key])) continue;
|
||||
$row = $rows[$key];
|
||||
$values = array();
|
||||
foreach ($fields as $field) {
|
||||
$value = str_replace("'", "''''", $row[$field['name']]);
|
||||
$value = "'{$value}'";
|
||||
if ($value == "'0000-00-00 00:00:00'" || $value == "'0000-00-00'") {
|
||||
$null = $tables[$table][$field['name']]['null'];
|
||||
$value = $null ? 'NULL' : '1970-01-01';
|
||||
}
|
||||
$values[] = $value;
|
||||
}
|
||||
$values[] = $value;
|
||||
$values = '(' . implode(',', $values) . ')';
|
||||
$data[] = $values;
|
||||
}
|
||||
$values = '(' . implode(',', $values) . ')';
|
||||
$data[] = $values;
|
||||
|
||||
$begin += $step;
|
||||
if (empty($data)) continue;
|
||||
$sql = $template . implode(',', $data) . ';';
|
||||
|
||||
if (isset($identityTables[$table])) $sql = "SET IDENTITY_INSERT $table ON;" . $sql;
|
||||
|
||||
//file_put_contents('./a.sql', $sql. "\n", FILE_APPEND);
|
||||
$dm->rawQuery($sql);
|
||||
}
|
||||
|
||||
$sql = $template . implode(',', $data) . ';';
|
||||
|
||||
if(isset($identityTables[$table])) $sql = "SET IDENTITY_INSERT $table ON;" . $sql;
|
||||
|
||||
$dm->rawQuery($sql);
|
||||
}
|
||||
|
||||
echo "\n迁移成功,修改my.php测试一下吧\n\n";
|
||||
|
||||
@@ -163,7 +163,7 @@ foreach($config->bi->builtin->pivots as $pivot)
|
||||
if(!isset($pivot['langs'])) $pivotSpec->langs = null;
|
||||
if(!isset($pivot['vars'])) $pivotSpec->vars = null;
|
||||
|
||||
insert('zt_pivot', $pivot);
|
||||
if($pivot['version'] == '1') insert('zt_pivot', $pivot);
|
||||
insert('zt_pivotspec', $pivotSpec);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ $config->action->objectNameFields['boardspace'] = 'name';
|
||||
$config->action->objectNameFields['doc'] = 'title';
|
||||
$config->action->objectNameFields['doclib'] = 'name';
|
||||
$config->action->objectNameFields['docspace'] = 'name';
|
||||
$config->action->objectNameFields['doctemplate'] = 'title';
|
||||
$config->action->objectNameFields['todo'] = 'name';
|
||||
$config->action->objectNameFields['branch'] = 'name';
|
||||
$config->action->objectNameFields['module'] = 'name';
|
||||
@@ -87,8 +88,8 @@ $config->action->majorList['execution'] = array('opened', 'edited');
|
||||
$config->action->majorList['doc'] = array('releaseddoc');
|
||||
|
||||
$config->action->needGetProjectType = 'build,task,bug,case,testcase,caselib,testtask,testsuite,testreport,doc,issue,release,risk,design,opportunity,trainplan,gapanalysis,researchplan,researchreport,';
|
||||
$config->action->needGetRelateField = ',branch,story,epic,requirement,productplan,release,task,build,bug,testcase,case,testtask,testreport,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,researchplan,researchreport,meeting,kanbanlane,kanbancolumn,module,review,';
|
||||
$config->action->noLinkModules = ',doclib,module,webhook,gitlab,instance,gitea,gogs,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,user,entry,repo,pivot,scene,boardspace,auditplan,auditresult,productline,chapter,';
|
||||
$config->action->needGetRelateField = ',branch,story,epic,requirement,productplan,release,task,build,bug,testcase,case,testtask,testreport,design,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,researchplan,researchreport,meeting,kanbanlane,kanbancolumn,module,review,';
|
||||
$config->action->noLinkModules = ',doclib,module,webhook,gitlab,instance,gitea,gogs,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,user,entry,repo,pivot,scene,boardspace,auditplan,auditresult,productline,chapter,doc,';
|
||||
$config->action->ignoreObjectType4Dynamic = 'kanbanregion,kanbanlane,kanbancolumn';
|
||||
$config->action->ignoreActions4Dynamic = 'disconnectxuanxuan,reconnectxuanxuan,loginxuanxuan,logoutxuanxuan,editmr,removemr,syncdoingbyticket,syncdoingbystory,syncdoingbyuserstory,syncdoingbyepic,syncdoingbytask,syncdoingbybug,syncdoingbytodo,syncdoingbydemand';
|
||||
|
||||
|
||||
@@ -206,6 +206,31 @@ class action extends control
|
||||
$url = $this->createLink('action', 'trash', "browseType=boardspace");
|
||||
if($isDeleted) return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$this->lang->action->undeleteBoardTip}'}).then((res) => {if(res) loadPage('{$url}');});"));
|
||||
}
|
||||
elseif($oldAction->objectType == 'doctemplate')
|
||||
{
|
||||
$templateScopeAndType = $this->dao->select('id,lib,module')->from(TABLE_DOC)->where('id')->eq($oldAction->objectID)->fetchAll('id');
|
||||
$scopeID = zget($templateScopeAndType[$oldAction->objectID], 'lib', 0);
|
||||
$typeID = zget($templateScopeAndType[$oldAction->objectID], 'module', 0);
|
||||
if($confirmChange == 'no')
|
||||
{
|
||||
$isDeletedType = $this->dao->select('deleted')->from(TABLE_MODULE)->where('type')->eq('docTemplate')->andWhere('id')->eq($typeID)->fetch('deleted');
|
||||
$isDeletedScope = $this->dao->select('deleted')->from(TABLE_DOCLIB)->where('type')->eq('template')->andWhere('id')->eq($scopeID)->fetch('deleted');
|
||||
|
||||
if($isDeletedType == '1' || $isDeletedScope == '1')
|
||||
{
|
||||
$url = $this->createLink('action', 'undelete', "action={$actionID}&browseType={$browseType}&confirmChange=yes");
|
||||
return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$this->lang->action->undeleteTemplateTip}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'}).then((res) => {if(res) $.ajaxSubmit({url: '{$url}'});});"));
|
||||
}
|
||||
}
|
||||
|
||||
if($confirmChange == 'yes')
|
||||
{
|
||||
$this->dao->update(TABLE_DOCLIB)->set('deleted')->eq('0')->where('id')->eq($scopeID)->exec();
|
||||
$this->dao->update(TABLE_MODULE)->set('deleted')->eq('0')->where('id')->eq($typeID)->exec();
|
||||
$parentType = $this->dao->select('parent')->from(TABLE_MODULE)->where('id')->eq($typeID)->fetch('parent');
|
||||
if(!empty($parentType)) $this->dao->update(TABLE_MODULE)->set('deleted')->eq('0')->where('id')->eq($parentType)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->action->undelete($actionID);
|
||||
if(true !== $result) return $this->send(array('result' => 'fail', 'load' => array('confirm' => $result)));
|
||||
@@ -402,4 +427,23 @@ class action extends control
|
||||
{
|
||||
$this->action->cleanActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 Ajax 获取该日期的更多记录。
|
||||
* Get more actions by ajax.
|
||||
*
|
||||
* @param int $lastActionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxGetMoreActions(int $lastActionID)
|
||||
{
|
||||
$actions = $this->action->getMoreActions($lastActionID);
|
||||
$lastAction = end($actions);
|
||||
|
||||
$this->view->actions = $actions;
|
||||
$this->view->hasMore = $lastAction ? $this->action->hasMoreAction($lastAction) : false;
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noletter');
|
||||
$this->display();
|
||||
}
|
||||
}
|
||||
|
||||
+32
-17
@@ -58,23 +58,24 @@ $lang->action->refusescene = 'Before restoring the scene, please restore the pa
|
||||
$lang->action->refusemodule = 'Before restoring the module, please restore the parent module of this module ';
|
||||
$lang->action->refusekanban = 'Before restoring the Kanban board, make sure to restore the space it belongs to';
|
||||
|
||||
$lang->action->trashTips = 'Hinweis: Alle Löschungen in ZenTao sind logische Löschungen.';
|
||||
$lang->action->textDiff = 'Text Format';
|
||||
$lang->action->original = 'Original Format';
|
||||
$lang->action->confirmHideAll = 'Möchten Sie alle Einträge verstecken?';
|
||||
$lang->action->needEdit = '%s existiert bereits. Bitte bearbeiten.';
|
||||
$lang->action->historyEdit = 'Der Verlauf darf nicht leer sein.';
|
||||
$lang->action->noDynamic = 'Kein Verlauf. ';
|
||||
$lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.';
|
||||
$lang->action->executionNoProject = "The execution does not belong to a {$lang->projectCommon},please restore the {$lang->projectCommon} first";
|
||||
$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first';
|
||||
$lang->action->hasCreatedTask = 'Tasks have been created in this parent phase and cannot be restored!';
|
||||
$lang->action->hasDeletedParent = 'Restoring this phase requires restoring the deleted parent phase %s at the same time.';
|
||||
$lang->action->hasChangedAttr = "After recovery, the phase type will be adjusted to '%s' according to the parent phase type.";
|
||||
$lang->action->whetherToRestore = 'Confirm to restore?';
|
||||
$lang->action->undeleteModuleTip = 'Once the subdirectory has been restored, its parent directory will also be automatically recovered and synchronized, would you like to proceed with restoring the directory?';
|
||||
$lang->action->undeleteTaskTip = 'The execution associated with this task has been removed. If you choose to restore it, the task will no longer be visible. Would you like to proceed with restoring the task?';
|
||||
$lang->action->undeleteBoardTip = 'Before restoring the board, please restore its affiliated space first.';
|
||||
$lang->action->trashTips = 'Hinweis: Alle Löschungen in ZenTao sind logische Löschungen.';
|
||||
$lang->action->textDiff = 'Text Format';
|
||||
$lang->action->original = 'Original Format';
|
||||
$lang->action->confirmHideAll = 'Möchten Sie alle Einträge verstecken?';
|
||||
$lang->action->needEdit = '%s existiert bereits. Bitte bearbeiten.';
|
||||
$lang->action->historyEdit = 'Der Verlauf darf nicht leer sein.';
|
||||
$lang->action->noDynamic = 'Kein Verlauf. ';
|
||||
$lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.';
|
||||
$lang->action->executionNoProject = "The execution does not belong to a {$lang->projectCommon},please restore the {$lang->projectCommon} first";
|
||||
$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first';
|
||||
$lang->action->hasCreatedTask = 'Tasks have been created in this parent phase and cannot be restored!';
|
||||
$lang->action->hasDeletedParent = 'Restoring this phase requires restoring the deleted parent phase %s at the same time.';
|
||||
$lang->action->hasChangedAttr = "After recovery, the phase type will be adjusted to '%s' according to the parent phase type.";
|
||||
$lang->action->whetherToRestore = 'Confirm to restore?';
|
||||
$lang->action->undeleteModuleTip = 'Once the subdirectory has been restored, its parent directory will also be automatically recovered and synchronized, would you like to proceed with restoring the directory?';
|
||||
$lang->action->undeleteTaskTip = 'The execution associated with this task has been removed. If you choose to restore it, the task will no longer be visible. Would you like to proceed with restoring the task?';
|
||||
$lang->action->undeleteBoardTip = 'Before restoring the board, please restore its affiliated space first.';
|
||||
$lang->action->undeleteTemplateTip = 'The type (and scope) of this template has been deleted. Are you sure you want to synchronize and restore them?';
|
||||
|
||||
$lang->action->hasOtherType = array();
|
||||
$lang->action->hasOtherType['stage'] = "Only subtypes of {$lang->executionCommon} / Kanban types can be created at this parent stage, so the current stage cannot be restored.";
|
||||
@@ -133,6 +134,7 @@ $lang->action->objectTypes['doc'] = 'Dok';
|
||||
$lang->action->objectTypes['api'] = 'Interface';
|
||||
$lang->action->objectTypes['doclib'] = 'Dok Bibliothek';
|
||||
$lang->action->objectTypes['docspace'] = 'Dok Space';
|
||||
$lang->action->objectTypes['doctemplate'] = 'Doc Template';
|
||||
$lang->action->objectTypes['apistruct'] = 'API struct';
|
||||
$lang->action->objectTypes['todo'] = 'Todo';
|
||||
$lang->action->objectTypes['risk'] = 'Risk';
|
||||
@@ -294,6 +296,7 @@ $lang->action->desc->managedteam = '$date, by <strong>$actor</strong> m
|
||||
$lang->action->desc->syncexecutionteam = '$date, synchronise members when execution adds members.' . "\n";
|
||||
$lang->action->desc->syncprojectteam = '$date, synchronise members when project removes members' . "\n";
|
||||
$lang->action->desc->syncbycase = '$date, running the testcase sets the request status as Doing.' . "\n";
|
||||
$lang->action->desc->converttonewdoc = '$date, <strong>$actor</strong> completed the conversion.';
|
||||
|
||||
/* Used to describe the history of operations related to parent-child tasks. */
|
||||
$lang->action->desc->createchildren = '$date, <strong>$actor</strong> created a child task <strong>$extra</strong>。' . "\n";
|
||||
@@ -329,6 +332,9 @@ $lang->action->desc->releaseddoc = '$date, <strong>$actor</strong> released <str
|
||||
/* Describe the operation records of document conversion.。*/
|
||||
$lang->action->desc->convertdoc = '$date, <strong>$actor</strong> complete document conversion <strong>$extra</strong>.' . "\n";
|
||||
|
||||
/* Describe the operation records of template conversion. */
|
||||
$lang->action->desc->convertdoctemplate = '$date, <strong>$actor</strong> complete template conversion <strong>$extra</strong>.' . "\n";
|
||||
|
||||
/* This parameter describes historical operations that are performed when a document is collected or uncollected. */
|
||||
$lang->action->desc->collected = '$date, <strong>$actor</strong> collected <strong>$extra</strong>.' . "\n";
|
||||
$lang->action->desc->uncollected = '$date, <strong>$actor</strong> uncollected <strong>$extra</strong>.' . "\n";
|
||||
@@ -550,6 +556,7 @@ $lang->action->label->deletesnapshot = 'deleted snapshot';
|
||||
$lang->action->label->saveddraft = 'save draft';
|
||||
$lang->action->label->releaseddoc = 'released';
|
||||
$lang->action->label->convertdoc = 'Convert doc';
|
||||
$lang->action->label->convertdoctemplate = 'Convert template';
|
||||
$lang->action->label->collected = 'collected';
|
||||
$lang->action->label->uncollected = 'uncollected';
|
||||
$lang->action->label->online = 'online';
|
||||
@@ -581,6 +588,7 @@ $lang->action->label->manualdeletebackup = 'Manually clean up backup';
|
||||
$lang->action->label->syncexecutionteam = 'synchronized';
|
||||
$lang->action->label->syncprojectteam = 'synchronized';
|
||||
$lang->action->label->syncbycase = 'start';
|
||||
$lang->action->label->converttonewdoc = 'converted';
|
||||
|
||||
/* Dynamic information is grouped by object. */
|
||||
$lang->action->dynamicAction = new stdclass;
|
||||
@@ -710,6 +718,12 @@ $lang->action->dynamicAction->kanbancard['archived'] = 'Archive Card';
|
||||
$lang->action->dynamicAction->kanbancard['restore'] = 'Restore Card';
|
||||
$lang->action->dynamicAction->kanbancard['deleted'] = 'Delete Card';
|
||||
|
||||
$lang->action->dynamicAction->design['created'] = 'Create Design';
|
||||
$lang->action->dynamicAction->design['changed'] = 'Change Design';
|
||||
$lang->action->dynamicAction->design['confirmed'] = 'Confirm Design';
|
||||
$lang->action->dynamicAction->design['assigned'] = 'Assign Design';
|
||||
$lang->action->dynamicAction->design['deleted'] = 'Delete Design';
|
||||
|
||||
$lang->action->dynamicAction->team['managedTeam'] = 'Manage Team';
|
||||
|
||||
$lang->action->dynamicAction->task['opened'] = 'Create Task';
|
||||
@@ -911,6 +925,7 @@ $lang->action->label->todo = 'Todo|todo|view|todoID=%s';
|
||||
$lang->action->label->doclib = 'Dok Bibliothek|doc|teamspace|objectID=%s&libID=%s';
|
||||
$lang->action->label->docspace = 'Dok Space';
|
||||
$lang->action->label->doc = 'Dok|doc|view|docID=%s';
|
||||
$lang->action->label->doctemplate = 'Doc Template|doc|view|docID=%s';
|
||||
$lang->action->label->user = 'Benutzer|user|view|account=%s';
|
||||
$lang->action->label->testreport = 'Berichte|testreport|view|report=%s';
|
||||
$lang->action->label->entry = 'Eintrag|entry|browse|';
|
||||
|
||||
+32
-17
@@ -58,23 +58,24 @@ $lang->action->refusescene = 'Before restoring the scene, please restore the pa
|
||||
$lang->action->refusemodule = 'Before restoring the module, please restore the parent module of this module ';
|
||||
$lang->action->refusekanban = 'Before restoring the Kanban board, make sure to restore the space it belongs to';
|
||||
|
||||
$lang->action->trashTips = 'Note: Delete in ZenTao is logic.';
|
||||
$lang->action->textDiff = 'Text Format';
|
||||
$lang->action->original = 'Original Format';
|
||||
$lang->action->confirmHideAll = 'Do you want to hide all the records?';
|
||||
$lang->action->needEdit = '%s that you want to restore. Please edit it.';
|
||||
$lang->action->historyEdit = 'The history EditBy cannot be empty.';
|
||||
$lang->action->noDynamic = 'No dynamics. ';
|
||||
$lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.';
|
||||
$lang->action->executionNoProject = "The execution does not belong to a {$lang->projectCommon},please restore the {$lang->projectCommon} first";
|
||||
$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first';
|
||||
$lang->action->hasCreatedTask = 'Tasks have been created in this parent phase and cannot be restored!';
|
||||
$lang->action->hasDeletedParent = 'Restoring this phase requires restoring the deleted parent phase %s at the same time.';
|
||||
$lang->action->hasChangedAttr = "After recovery, the phase type will be adjusted to '%s' according to the parent phase type.";
|
||||
$lang->action->whetherToRestore = 'Confirm to restore?';
|
||||
$lang->action->undeleteModuleTip = 'Once the subdirectory has been restored, its parent directory will also be automatically recovered and synchronized, would you like to proceed with restoring the directory?';
|
||||
$lang->action->undeleteTaskTip = 'The execution associated with this task has been removed. If you choose to restore it, the task will no longer be visible. Would you like to proceed with restoring the task?';
|
||||
$lang->action->undeleteBoardTip = 'Before restoring the board, please restore its affiliated space first.';
|
||||
$lang->action->trashTips = 'Note: Delete in ZenTao is logic.';
|
||||
$lang->action->textDiff = 'Text Format';
|
||||
$lang->action->original = 'Original Format';
|
||||
$lang->action->confirmHideAll = 'Do you want to hide all the records?';
|
||||
$lang->action->needEdit = '%s that you want to restore. Please edit it.';
|
||||
$lang->action->historyEdit = 'The history EditBy cannot be empty.';
|
||||
$lang->action->noDynamic = 'No dynamics. ';
|
||||
$lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.';
|
||||
$lang->action->executionNoProject = "The execution does not belong to a {$lang->projectCommon},please restore the {$lang->projectCommon} first";
|
||||
$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first';
|
||||
$lang->action->hasCreatedTask = 'Tasks have been created in this parent phase and cannot be restored!';
|
||||
$lang->action->hasDeletedParent = 'Restoring this phase requires restoring the deleted parent phase %s at the same time.';
|
||||
$lang->action->hasChangedAttr = "After recovery, the phase type will be adjusted to '%s' according to the parent phase type.";
|
||||
$lang->action->whetherToRestore = 'Confirm to restore?';
|
||||
$lang->action->undeleteModuleTip = 'Once the subdirectory has been restored, its parent directory will also be automatically recovered and synchronized, would you like to proceed with restoring the directory?';
|
||||
$lang->action->undeleteTaskTip = 'The execution associated with this task has been removed. If you choose to restore it, the task will no longer be visible. Would you like to proceed with restoring the task?';
|
||||
$lang->action->undeleteBoardTip = 'Before restoring the board, please restore its affiliated space first.';
|
||||
$lang->action->undeleteTemplateTip = 'The category (and scope) of this template has been deleted. Are you sure you want to synchronize and restore them?';
|
||||
|
||||
$lang->action->hasOtherType = array();
|
||||
$lang->action->hasOtherType['stage'] = "Only subtypes of {$lang->executionCommon} / Kanban types can be created at this parent stage, so the current stage cannot be restored.";
|
||||
@@ -133,6 +134,7 @@ $lang->action->objectTypes['doc'] = 'Document';
|
||||
$lang->action->objectTypes['api'] = 'Interface';
|
||||
$lang->action->objectTypes['doclib'] = 'Document Library';
|
||||
$lang->action->objectTypes['docspace'] = 'Document Space';
|
||||
$lang->action->objectTypes['doctemplate'] = 'Doc Template';
|
||||
$lang->action->objectTypes['apistruct'] = 'API struct';
|
||||
$lang->action->objectTypes['todo'] = 'Todo';
|
||||
$lang->action->objectTypes['risk'] = 'Risk';
|
||||
@@ -294,6 +296,7 @@ $lang->action->desc->managedteam = '$date, by <strong>$actor</strong> m
|
||||
$lang->action->desc->syncexecutionteam = '$date, synchronise members when execution adds members.' . "\n";
|
||||
$lang->action->desc->syncprojectteam = '$date, synchronise members when project removes members' . "\n";
|
||||
$lang->action->desc->syncbycase = '$date, running the testcase sets the request status as Doing.' . "\n";
|
||||
$lang->action->desc->converttonewdoc = '$date, <strong>$actor</strong> completed the conversion.';
|
||||
|
||||
/* Used to describe the history of operations related to parent-child tasks. */
|
||||
$lang->action->desc->createchildren = '$date, <strong>$actor</strong> created a child task <strong>$extra</strong>。' . "\n";
|
||||
@@ -329,6 +332,9 @@ $lang->action->desc->releaseddoc = '$date, <strong>$actor</strong> released <str
|
||||
/* Describe the operation records of document conversion.。*/
|
||||
$lang->action->desc->convertdoc = '$date, <strong>$actor</strong> complete document conversion <strong>$extra</strong>.' . "\n";
|
||||
|
||||
/* Describe the operation records of template conversion. */
|
||||
$lang->action->desc->convertdoctemplate = '$date, <strong>$actor</strong> complete template conversion <strong>$extra</strong>.' . "\n";
|
||||
|
||||
/* This parameter describes historical operations that are performed when a document is collected or uncollected. */
|
||||
$lang->action->desc->collected = '$date, <strong>$actor</strong> collected <strong>$extra</strong>.' . "\n";
|
||||
$lang->action->desc->uncollected = '$date, <strong>$actor</strong> uncollected <strong>$extra</strong>.' . "\n";
|
||||
@@ -550,6 +556,7 @@ $lang->action->label->deletesnapshot = 'deleted snapshot';
|
||||
$lang->action->label->saveddraft = 'save draft';
|
||||
$lang->action->label->releaseddoc = 'released';
|
||||
$lang->action->label->convertdoc = 'Convert doc';
|
||||
$lang->action->label->convertdoctemplate = 'Convert template';
|
||||
$lang->action->label->collected = 'collected';
|
||||
$lang->action->label->uncollected = 'uncollected';
|
||||
$lang->action->label->online = 'online';
|
||||
@@ -581,6 +588,7 @@ $lang->action->label->manualdeletebackup = 'Manually clean up backup';
|
||||
$lang->action->label->syncexecutionteam = 'synchronized';
|
||||
$lang->action->label->syncprojectteam = 'synchronized';
|
||||
$lang->action->label->syncbycase = 'start';
|
||||
$lang->action->label->converttonewdoc = 'converted';
|
||||
|
||||
/* Dynamic information is grouped by object. */
|
||||
$lang->action->dynamicAction = new stdclass;
|
||||
@@ -710,6 +718,12 @@ $lang->action->dynamicAction->kanbancard['archived'] = 'Archive Card';
|
||||
$lang->action->dynamicAction->kanbancard['restore'] = 'Restore Card';
|
||||
$lang->action->dynamicAction->kanbancard['deleted'] = 'Delete Card';
|
||||
|
||||
$lang->action->dynamicAction->design['created'] = 'Create Design';
|
||||
$lang->action->dynamicAction->design['changed'] = 'Change Design';
|
||||
$lang->action->dynamicAction->design['confirmed'] = 'Confirm Design';
|
||||
$lang->action->dynamicAction->design['assigned'] = 'Assign Design';
|
||||
$lang->action->dynamicAction->design['deleted'] = 'Delete Design';
|
||||
|
||||
$lang->action->dynamicAction->team['managedTeam'] = 'Manage Team';
|
||||
|
||||
$lang->action->dynamicAction->task['opened'] = 'Create Task';
|
||||
@@ -911,6 +925,7 @@ $lang->action->label->todo = 'Todo|todo|view|todoID=%s';
|
||||
$lang->action->label->doclib = 'Doc Library|doc|teamspace|objectID=%s&libID=%s';
|
||||
$lang->action->label->docspace = 'Doc Space';
|
||||
$lang->action->label->doc = 'Doc|doc|view|docID=%s';
|
||||
$lang->action->label->doctemplate = 'Doc Template|doc|view|docID=%s';
|
||||
$lang->action->label->user = 'User|user|view|account=%s';
|
||||
$lang->action->label->testreport = 'Report|testreport|view|report=%s';
|
||||
$lang->action->label->entry = 'Application|entry|browse|';
|
||||
|
||||
+32
-17
@@ -58,23 +58,24 @@ $lang->action->refusescene = 'Before restoring the scene, please restore the pa
|
||||
$lang->action->refusemodule = 'Before restoring the module, please restore the parent module of this module ';
|
||||
$lang->action->refusekanban = 'Before restoring the Kanban board, make sure to restore the space it belongs to';
|
||||
|
||||
$lang->action->trashTips = 'Note: Les suppressions dans ZenTao sont purement logiques.';
|
||||
$lang->action->textDiff = 'Text Format';
|
||||
$lang->action->original = 'Original Format';
|
||||
$lang->action->confirmHideAll = 'Voulez-vous masquer tous les enregistrements ?';
|
||||
$lang->action->needEdit = '%s que vous voulez restaurer. SVP éditez la.';
|
||||
$lang->action->historyEdit = "l'historique Edité Par ne peut pas être vide.";
|
||||
$lang->action->noDynamic = "Pas d'historique.";
|
||||
$lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.';
|
||||
$lang->action->executionNoProject = "The execution does not belong to a {$lang->projectCommon},please restore the {$lang->projectCommon} first";
|
||||
$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first';
|
||||
$lang->action->hasCreatedTask = 'Tasks have been created in this parent phase and cannot be restored!';
|
||||
$lang->action->hasDeletedParent = 'Restoring this phase requires restoring the deleted parent phase %s at the same time.';
|
||||
$lang->action->hasChangedAttr = "After recovery, the phase type will be adjusted to '%s' according to the parent phase type.";
|
||||
$lang->action->whetherToRestore = 'Confirm to restore?';
|
||||
$lang->action->undeleteModuleTip = 'Once the subdirectory has been restored, its parent directory will also be automatically recovered and synchronized, would you like to proceed with restoring the directory?';
|
||||
$lang->action->undeleteTaskTip = 'The execution associated with this task has been removed. If you choose to restore it, the task will no longer be visible. Would you like to proceed with restoring the task?';
|
||||
$lang->action->undeleteBoardTip = 'Before restoring the board, please restore its affiliated space first.';
|
||||
$lang->action->trashTips = 'Note: Les suppressions dans ZenTao sont purement logiques.';
|
||||
$lang->action->textDiff = 'Text Format';
|
||||
$lang->action->original = 'Original Format';
|
||||
$lang->action->confirmHideAll = 'Voulez-vous masquer tous les enregistrements ?';
|
||||
$lang->action->needEdit = '%s que vous voulez restaurer. SVP éditez la.';
|
||||
$lang->action->historyEdit = "l'historique Edité Par ne peut pas être vide.";
|
||||
$lang->action->noDynamic = "Pas d'historique.";
|
||||
$lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.';
|
||||
$lang->action->executionNoProject = "The execution does not belong to a {$lang->projectCommon},please restore the {$lang->projectCommon} first";
|
||||
$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first';
|
||||
$lang->action->hasCreatedTask = 'Tasks have been created in this parent phase and cannot be restored!';
|
||||
$lang->action->hasDeletedParent = 'Restoring this phase requires restoring the deleted parent phase %s at the same time.';
|
||||
$lang->action->hasChangedAttr = "After recovery, the phase type will be adjusted to '%s' according to the parent phase type.";
|
||||
$lang->action->whetherToRestore = 'Confirm to restore?';
|
||||
$lang->action->undeleteModuleTip = 'Once the subdirectory has been restored, its parent directory will also be automatically recovered and synchronized, would you like to proceed with restoring the directory?';
|
||||
$lang->action->undeleteTaskTip = 'The execution associated with this task has been removed. If you choose to restore it, the task will no longer be visible. Would you like to proceed with restoring the task?';
|
||||
$lang->action->undeleteBoardTip = 'Before restoring the board, please restore its affiliated space first.';
|
||||
$lang->action->undeleteTemplateTip = 'The category (and scope) of this template has been deleted. Are you sure you want to synchronize and restore them?';
|
||||
|
||||
$lang->action->hasOtherType = array();
|
||||
$lang->action->hasOtherType['stage'] = "Only subtypes of {$lang->executionCommon} / Kanban types can be created at this parent stage, so the current stage cannot be restored.";
|
||||
@@ -133,6 +134,7 @@ $lang->action->objectTypes['doc'] = 'Document';
|
||||
$lang->action->objectTypes['api'] = 'Interface';
|
||||
$lang->action->objectTypes['doclib'] = 'Répertoire Documents';
|
||||
$lang->action->objectTypes['docspace'] = 'Répertoire Space';
|
||||
$lang->action->objectTypes['doctemplate'] = 'Doc Template';
|
||||
$lang->action->objectTypes['apistruct'] = 'API struct';
|
||||
$lang->action->objectTypes['todo'] = 'Todo';
|
||||
$lang->action->objectTypes['risk'] = 'Risk';
|
||||
@@ -294,6 +296,7 @@ $lang->action->desc->managedteam = '$date, by <strong>$actor</strong> m
|
||||
$lang->action->desc->syncexecutionteam = '$date, synchronise members when execution adds members.' . "\n";
|
||||
$lang->action->desc->syncprojectteam = '$date, synchronise members when project removes members' . "\n";
|
||||
$lang->action->desc->syncbycase = '$date, running the testcase sets the request status as Doing.' . "\n";
|
||||
$lang->action->desc->converttonewdoc = '$date, <strong>$actor</strong> completed the conversion.';
|
||||
|
||||
/* Used to describe the history of operations related to parent-child tasks. */
|
||||
$lang->action->desc->createchildren = '$date, <strong>$actor</strong> a créé un sous-tâche <strong>$extra</strong>。' . "\n";
|
||||
@@ -329,6 +332,9 @@ $lang->action->desc->releaseddoc = '$date, <strong>$actor</strong> released <str
|
||||
/* Describe the operation records of document conversion.。*/
|
||||
$lang->action->desc->convertdoc = '$date, <strong>$actor</strong> complete document conversion <strong>$extra</strong>.' . "\n";
|
||||
|
||||
/* Describe the operation records of template conversion. */
|
||||
$lang->action->desc->convertdoctemplate = '$date, <strong>$actor</strong> complete template conversion <strong>$extra</strong>.' . "\n";
|
||||
|
||||
/* This parameter describes historical operations that are performed when a document is collected or uncollected. */
|
||||
$lang->action->desc->collected = '$date, <strong>$actor</strong> collected <strong>$extra</strong>.' . "\n";
|
||||
$lang->action->desc->uncollected = '$date, <strong>$actor</strong> uncollected <strong>$extra</strong>.' . "\n";
|
||||
@@ -550,6 +556,7 @@ $lang->action->label->deletesnapshot = 'deleted snapshot';
|
||||
$lang->action->label->saveddraft = 'save draft';
|
||||
$lang->action->label->releaseddoc = 'released';
|
||||
$lang->action->label->convertdoc = 'Convert doc';
|
||||
$lang->action->label->convertdoctemplate = 'Convert template';
|
||||
$lang->action->label->collected = 'collected';
|
||||
$lang->action->label->uncollected = 'uncollected';
|
||||
$lang->action->label->online = 'online';
|
||||
@@ -581,6 +588,7 @@ $lang->action->label->manualdeletebackup = 'Manually clean up backup';
|
||||
$lang->action->label->syncexecutionteam = 'synchronized';
|
||||
$lang->action->label->syncprojectteam = 'synchronized';
|
||||
$lang->action->label->syncbycase = 'start';
|
||||
$lang->action->label->converttonewdoc = 'converted';
|
||||
|
||||
/* Dynamic information is grouped by object. */
|
||||
$lang->action->dynamicAction = new stdclass();
|
||||
@@ -710,6 +718,12 @@ $lang->action->dynamicAction->kanbancard['archived'] = 'Archive Card';
|
||||
$lang->action->dynamicAction->kanbancard['restore'] = 'Restore Card';
|
||||
$lang->action->dynamicAction->kanbancard['deleted'] = 'Delete Card';
|
||||
|
||||
$lang->action->dynamicAction->design['created'] = 'Create Design';
|
||||
$lang->action->dynamicAction->design['changed'] = 'Change Design';
|
||||
$lang->action->dynamicAction->design['confirmed'] = 'Confirm Design';
|
||||
$lang->action->dynamicAction->design['assigned'] = 'Assign Design';
|
||||
$lang->action->dynamicAction->design['deleted'] = 'Delete Design';
|
||||
|
||||
$lang->action->dynamicAction->team['managedTeam'] = 'Manage Team';
|
||||
|
||||
$lang->action->dynamicAction->task['opened'] = 'Créer Tâche';
|
||||
@@ -911,6 +925,7 @@ $lang->action->label->todo = 'Agenda|todo|view|todoID=%s';
|
||||
$lang->action->label->doclib = 'Bibliothèque|doc|teamspace|objectID=%s&libID=%s';
|
||||
$lang->action->label->docspace = 'Doc Space';
|
||||
$lang->action->label->doc = 'Document|doc|view|docID=%s';
|
||||
$lang->action->label->doctemplate = 'Doc Template|doc|view|docID=%s';
|
||||
$lang->action->label->user = 'Utilisateur|user|view|account=%s';
|
||||
$lang->action->label->testreport = 'Rapport|testreport|view|report=%s';
|
||||
$lang->action->label->entry = 'Application|entry|browse|';
|
||||
|
||||
@@ -58,23 +58,24 @@ $lang->action->refusescene = '还原场景之前,请先还原该场景的父
|
||||
$lang->action->refusemodule = '还原模块之前,请先还原该模块的父模块';
|
||||
$lang->action->refusekanban = '还原看板之前,请先还原该看板所属空间';
|
||||
|
||||
$lang->action->trashTips = '提示:为了保证系统的完整性,禅道系统的删除都是标记删除。';
|
||||
$lang->action->textDiff = '文本格式';
|
||||
$lang->action->original = '原始格式';
|
||||
$lang->action->confirmHideAll = '您确定要全部隐藏这些记录吗?';
|
||||
$lang->action->needEdit = '要还原%s的名称或代号已经存在,请编辑更改。';
|
||||
$lang->action->historyEdit = '历史记录编辑不能为空。';
|
||||
$lang->action->noDynamic = '暂时没有动态。';
|
||||
$lang->action->undeletedTips = '该数据在版本升级过程中未参与数据归并流程,不支持还原。';
|
||||
$lang->action->executionNoProject = "该执行没有所属的{$lang->projectCommon},请先还原{$lang->projectCommon}再还原执行";
|
||||
$lang->action->repoNoServer = '该代码库没有所属的服务器,请先还原服务器再还原代码库';
|
||||
$lang->action->hasCreatedTask = '该父阶段已创建任务,无法恢复!';
|
||||
$lang->action->hasDeletedParent = '恢复该阶段需要同时恢复已删除的父阶段%s,';
|
||||
$lang->action->hasChangedAttr = '恢复后阶段类型将根据父阶段类型均调整为“%s”,';
|
||||
$lang->action->whetherToRestore = '是否恢复?';
|
||||
$lang->action->undeleteModuleTip = '子目录恢复后,会同步恢复他的上级目录,确认要恢复吗?';
|
||||
$lang->action->undeleteTaskTip = '该任务所属执行已删除,还原后不能看到该任务,是否继续还原任务?';
|
||||
$lang->action->undeleteBoardTip = '还原白板之前,请先还原该白板的所属空间。';
|
||||
$lang->action->trashTips = '提示:为了保证系统的完整性,禅道系统的删除都是标记删除。';
|
||||
$lang->action->textDiff = '文本格式';
|
||||
$lang->action->original = '原始格式';
|
||||
$lang->action->confirmHideAll = '您确定要全部隐藏这些记录吗?';
|
||||
$lang->action->needEdit = '要还原%s的名称或代号已经存在,请编辑更改。';
|
||||
$lang->action->historyEdit = '历史记录编辑不能为空。';
|
||||
$lang->action->noDynamic = '暂时没有动态。';
|
||||
$lang->action->undeletedTips = '该数据在版本升级过程中未参与数据归并流程,不支持还原。';
|
||||
$lang->action->executionNoProject = "该执行没有所属的{$lang->projectCommon},请先还原{$lang->projectCommon}再还原执行";
|
||||
$lang->action->repoNoServer = '该代码库没有所属的服务器,请先还原服务器再还原代码库';
|
||||
$lang->action->hasCreatedTask = '该父阶段已创建任务,无法恢复!';
|
||||
$lang->action->hasDeletedParent = '恢复该阶段需要同时恢复已删除的父阶段%s,';
|
||||
$lang->action->hasChangedAttr = '恢复后阶段类型将根据父阶段类型均调整为“%s”,';
|
||||
$lang->action->whetherToRestore = '是否恢复?';
|
||||
$lang->action->undeleteModuleTip = '子目录恢复后,会同步恢复他的上级目录,确认要恢复吗?';
|
||||
$lang->action->undeleteTaskTip = '该任务所属执行已删除,还原后不能看到该任务,是否继续还原任务?';
|
||||
$lang->action->undeleteBoardTip = '还原白板之前,请先还原该白板的所属空间。';
|
||||
$lang->action->undeleteTemplateTip = '该文档模板的分类(和范围)已被删除,确认要同步还原吗?';
|
||||
|
||||
$lang->action->hasOtherType = array();
|
||||
$lang->action->hasOtherType['stage'] = "当前阶段的父级现在只允许创建{$lang->executionCommon}/看板类型的子级,故无法恢复当前阶段。";
|
||||
@@ -133,6 +134,7 @@ $lang->action->objectTypes['doc'] = '文档';
|
||||
$lang->action->objectTypes['api'] = '接口';
|
||||
$lang->action->objectTypes['doclib'] = '文档库';
|
||||
$lang->action->objectTypes['docspace'] = '文档空间';
|
||||
$lang->action->objectTypes['doctemplate'] = '文档模板';
|
||||
$lang->action->objectTypes['apistruct'] = '数据结构';
|
||||
$lang->action->objectTypes['todo'] = '待办';
|
||||
$lang->action->objectTypes['risk'] = '风险';
|
||||
@@ -294,6 +296,7 @@ $lang->action->desc->managedteam = '$date, 由 <strong>$actor</strong>
|
||||
$lang->action->desc->syncexecutionteam = '$date, 同步更新执行增加的团队成员。' . "\n";
|
||||
$lang->action->desc->syncprojectteam = '$date, 同步更新项目删除的团队成员。' . "\n";
|
||||
$lang->action->desc->syncbycase = '$date, 系统判断由于执行了用例,将测试单状态置为进行中。' . "\n";
|
||||
$lang->action->desc->converttonewdoc = '$date, 由 <strong>$actor</strong> 完成了文档转换。';
|
||||
|
||||
/* 用来描述和父子任务相关的操作历史记录。*/
|
||||
$lang->action->desc->createchildren = '$date, 由 <strong>$actor</strong> 创建子任务 <strong>$extra</strong>。' . "\n";
|
||||
@@ -329,6 +332,9 @@ $lang->action->desc->releaseddoc = '$date, 由 <strong>$actor</strong> 发布 <s
|
||||
/* 用来描述文档转换的操作记录。*/
|
||||
$lang->action->desc->convertdoc = '$date, 由 <strong>$actor</strong> 完成文档转换 <strong>$extra</strong>。' . "\n";
|
||||
|
||||
/* 用来描述文档模板转换的操作记录。*/
|
||||
$lang->action->desc->convertdoctemplate = '$date, 由 <strong>$actor</strong> 完成文档模板转换 <strong>$extra</strong>。' . "\n";
|
||||
|
||||
/* 用来描述文档收藏或取消收藏时的历史操作记录。*/
|
||||
$lang->action->desc->collected = '$date, 由 <strong>$actor</strong> 收藏 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->uncollected = '$date, 由 <strong>$actor</strong> 取消收藏 <strong>$extra</strong>。' . "\n";
|
||||
@@ -550,6 +556,7 @@ $lang->action->label->deletesnapshot = '编辑了快照';
|
||||
$lang->action->label->saveddraft = '存为草稿';
|
||||
$lang->action->label->releaseddoc = '发布了';
|
||||
$lang->action->label->convertdoc = '转为新文档';
|
||||
$lang->action->label->convertdoctemplate = '转为新文档模板';
|
||||
$lang->action->label->collected = '收藏了';
|
||||
$lang->action->label->uncollected = '取消收藏了';
|
||||
$lang->action->label->online = '上架了';
|
||||
@@ -581,6 +588,7 @@ $lang->action->label->manualdeletebackup = '手动删除备份';
|
||||
$lang->action->label->syncexecutionteam = '同步了';
|
||||
$lang->action->label->syncprojectteam = '同步了';
|
||||
$lang->action->label->syncbycase = '开始了';
|
||||
$lang->action->label->converttonewdoc = '转换了';
|
||||
|
||||
/* 动态信息按照对象分组 */
|
||||
$lang->action->dynamicAction = new stdclass();
|
||||
@@ -710,6 +718,12 @@ $lang->action->dynamicAction->kanbancard['archived'] = '归档看板卡片';
|
||||
$lang->action->dynamicAction->kanbancard['restore'] = '还原看板卡片';
|
||||
$lang->action->dynamicAction->kanbancard['deleted'] = '删除看板卡片';
|
||||
|
||||
$lang->action->dynamicAction->design['created'] = '创建设计';
|
||||
$lang->action->dynamicAction->design['changed'] = '变更设计';
|
||||
$lang->action->dynamicAction->design['confirmed'] = '确认设计';
|
||||
$lang->action->dynamicAction->design['assigned'] = '指派设计';
|
||||
$lang->action->dynamicAction->design['deleted'] = '删除设计';
|
||||
|
||||
$lang->action->dynamicAction->team['managedTeam'] = '维护团队';
|
||||
|
||||
$lang->action->dynamicAction->task['opened'] = '创建任务';
|
||||
@@ -911,6 +925,7 @@ $lang->action->label->todo = '待办|todo|view|todoID=%s';
|
||||
$lang->action->label->doclib = '文档库|doc|teamspace|objectID=%s&libID=%s';
|
||||
$lang->action->label->docspace = '文档空间';
|
||||
$lang->action->label->doc = '文档|doc|view|docID=%s';
|
||||
$lang->action->label->doctemplate = '文档模板|doc|view|docID=%s';
|
||||
$lang->action->label->user = '用户|user|view|account=%s';
|
||||
$lang->action->label->testreport = '报告|testreport|view|report=%s';
|
||||
$lang->action->label->entry = '应用|entry|browse|';
|
||||
|
||||
+365
-180
@@ -18,6 +18,7 @@ class actionModel extends model
|
||||
const BE_UNDELETED = 0; // The deleted object has been undeleted.
|
||||
const CAN_UNDELETED = 1; // The deleted object can be undeleted.
|
||||
const BE_HIDDEN = 2; // The deleted object has been hidden.
|
||||
const MAXCOUNT = 1000;
|
||||
|
||||
/**
|
||||
* 创建一个操作记录。
|
||||
@@ -79,7 +80,8 @@ class actionModel extends model
|
||||
$this->dao->insert(TABLE_ACTION)->data($action)->autoCheck()->exec();
|
||||
$actionID = $this->dao->lastInsertID();
|
||||
|
||||
$hasRecentTable = true;
|
||||
$hasProductTable = true;
|
||||
$hasRecentTable = true;
|
||||
if($this->app->upgrading)
|
||||
{
|
||||
$fromVersion = $this->loadModel('setting')->getItem('owner=system&module=common§ion=global&key=version');
|
||||
@@ -88,7 +90,20 @@ class actionModel extends model
|
||||
if(strpos($fromVersion, 'biz') !== false && version_compare($fromVersion, 'biz8.6', '<')) $hasRecentTable = false;
|
||||
if(strpos($fromVersion, 'max') !== false && version_compare($fromVersion, 'max4.6', '<')) $hasRecentTable = false;
|
||||
if(strpos($fromVersion, 'ipd') !== false && version_compare($fromVersion, 'ipd1.0.1', '<')) $hasRecentTable = false;
|
||||
|
||||
if(is_numeric($fromVersion[0]) && version_compare($fromVersion, '21.7.1', '<=')) $hasProductTable = false;
|
||||
if(strpos($fromVersion, 'pro') !== false) $hasProductTable = false;
|
||||
if(strpos($fromVersion, 'biz') !== false && version_compare($fromVersion, 'biz12.0', '<=')) $hasProductTable = false;
|
||||
if(strpos($fromVersion, 'max') !== false && version_compare($fromVersion, 'max7.0', '<=')) $hasProductTable = false;
|
||||
if(strpos($fromVersion, 'ipd') !== false && version_compare($fromVersion, 'ipd4.0', '<=')) $hasProductTable = false;
|
||||
}
|
||||
|
||||
if($hasProductTable)
|
||||
{
|
||||
$productIdList = array_filter(explode(',', $action->product));
|
||||
foreach($productIdList as $productID) $this->dao->insert(TABLE_ACTIONPRODUCT)->set('action')->eq($actionID)->set('product')->eq($productID)->exec();
|
||||
}
|
||||
|
||||
if($actionType == 'commented')
|
||||
{
|
||||
$oldAction = new stdclass();
|
||||
@@ -104,7 +119,11 @@ class actionModel extends model
|
||||
$changes = common::createChanges($oldAction, $newAction);
|
||||
if($changes) $this->logHistory($actionID, $changes);
|
||||
}
|
||||
if($hasRecentTable) $this->dao->insert(TABLE_ACTIONRECENT)->data($action)->autoCheck()->exec();
|
||||
if($hasRecentTable)
|
||||
{
|
||||
$action->id = $actionID;
|
||||
$this->dao->insert(TABLE_ACTIONRECENT)->data($action)->autoCheck()->exec();
|
||||
}
|
||||
|
||||
$this->file->updateObjectID($uid, $objectID, $objectType);
|
||||
|
||||
@@ -469,9 +488,16 @@ class actionModel extends model
|
||||
->where('t1.id')->in($objectIdList)
|
||||
->fetchPairs();
|
||||
}
|
||||
elseif($objectType == 'stakeholder')
|
||||
{
|
||||
$objectNames[$objectType] = $this->dao->select("t1.id, t2.realname AS name")->from($table)->alias('t1')
|
||||
->leftJoin(TABLE_USER)->alias('t2')->on('t1.user = t2.account')
|
||||
->where('t1.id')->in($objectIdList)
|
||||
->fetchPairs();
|
||||
}
|
||||
else
|
||||
{
|
||||
$objectNames[$objectType] = $this->dao->select("id, {$field} AS name")->from($table)->where('id')->in($objectIdList)->fetchPairs();
|
||||
$objectNames[$objectType] = $this->dao->select("id, {$field} AS name")->from($table)->where('id')->in($objectIdList)->filterTpl('skip')->fetchPairs();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,7 +631,7 @@ class actionModel extends model
|
||||
*/
|
||||
public function logHistory(int $actionID, array $changes): bool
|
||||
{
|
||||
if(empty($actionID)) return false;
|
||||
if(empty($actionID) || empty($changes)) return false;
|
||||
foreach($changes as $change)
|
||||
{
|
||||
$change = is_array($change) ? json_decode(json_encode($change)) : $change;
|
||||
@@ -870,22 +896,22 @@ class actionModel extends model
|
||||
{
|
||||
if($action->action == 'importstory' || $action->action == 'convertstory')
|
||||
{
|
||||
$story = $this->loadModel('story')->getById((int)$action->extra);
|
||||
$story = $this->loadModel('story')->fetchById((int)$action->extra);
|
||||
$link = helper::createLink('story', 'view', "storyID={$action->extra}");
|
||||
}
|
||||
if($action->action == 'convertrequirement' || $action->action == 'importrequirement')
|
||||
{
|
||||
$story = $this->loadModel('story')->getById((int)$action->extra);
|
||||
$story = $this->loadModel('story')->fetchById((int)$action->extra);
|
||||
$link = helper::createLink('requirement', 'view', "storyID={$action->extra}");
|
||||
}
|
||||
if($action->action == 'importdemand' || $action->action == 'convertdemand')
|
||||
{
|
||||
$story = $this->loadModel('demand')->getByID((int)$action->extra);
|
||||
$story = $this->loadModel('demand')->fetchByID((int)$action->extra);
|
||||
$link = helper::createLink('demand', 'view', "demandID={$action->extra}");
|
||||
}
|
||||
if($action->action == 'importepic' || $action->action == 'convertepic')
|
||||
{
|
||||
$story = $this->loadModel('story')->getByID((int)$action->extra);
|
||||
$story = $this->loadModel('story')->fetchByID((int)$action->extra);
|
||||
$link = helper::createLink('epic', 'view', "epicID={$action->extra}");
|
||||
}
|
||||
|
||||
@@ -1016,25 +1042,21 @@ class actionModel extends model
|
||||
/* Computer the begin and end date of a period. */
|
||||
$beginAndEnd = $this->computeBeginAndEnd($period, $date, $direction);
|
||||
|
||||
$actionCondition = $this->getActionCondition();
|
||||
if(!$actionCondition && !$this->app->user->admin && isset($this->app->user->rights['acls']['actions']))
|
||||
{
|
||||
$this->session->set('actionQueryCondition', null, $this->app->tab);
|
||||
return array();
|
||||
}
|
||||
|
||||
/* 构建权限搜索条件。 */
|
||||
/* Build has priv search condition. */
|
||||
$executions = array();
|
||||
$condition = !$this->app->user->admin ? $this->buildUserAclsSearchCondition($productID, $projectID, $executionID, $executions) : '1=1';
|
||||
|
||||
$actionCondition = $this->getActionCondition();
|
||||
if(!$actionCondition && !$this->app->user->admin && isset($this->app->user->rights['acls']['actions'])) return array();
|
||||
|
||||
$condition = "`objectType` IN ('doc', 'doclib')" . ($condition == '1=1' ? '' : "OR ({$condition})") . " OR `objectType` NOT IN ('program', 'effort')";
|
||||
|
||||
$programCondition = empty($this->app->user->view->programs) ? '0' : $this->app->user->view->programs;
|
||||
$condition .= " OR (`objectID` in ($programCondition) AND `objectType` = 'program')";
|
||||
$condition = "`objectType` != 'effort'";
|
||||
|
||||
$noMultipleExecutions = $this->dao->select('id')->from(TABLE_PROJECT)->where('multiple')->eq(0)->andWhere('type')->in('sprint,kanban')->fetchPairs();
|
||||
if($noMultipleExecutions) $condition = count($noMultipleExecutions) > 1 ? "({$condition}) AND (`objectType` != 'execution' OR (`objectID` NOT " . helper::dbIN($noMultipleExecutions) . " AND `objectType` = 'execution'))" : "({$condition}) AND (`objectType` != 'execution' OR (`objectID` !" . helper::dbIN($noMultipleExecutions) . " AND `objectType` = 'execution'))";
|
||||
if($noMultipleExecutions) $condition = "({$condition}) AND (`objectType` != 'execution' OR (`objectType` = 'execution' AND `objectID`" . (count($noMultipleExecutions) == 1 ? ' != ' . reset($noMultipleExecutions) : ' NOT ' . helper::dbIN($noMultipleExecutions)) . "))";
|
||||
|
||||
$condition = "({$condition})";
|
||||
|
||||
$actions = $this->actionTao->getActionListByCondition($condition, $date, $beginAndEnd['begin'], $beginAndEnd['end'], $account, $productID, $projectID, $executionID, $executions, $actionCondition, $orderBy, $limit);
|
||||
$actions = $this->actionTao->getActionListByCondition("({$condition})", $beginAndEnd['begin'], $beginAndEnd['end'], $account, $productID, $projectID, $executionID, $actionCondition, $orderBy, $limit);
|
||||
if(!$actions) return array();
|
||||
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'action');
|
||||
@@ -1042,6 +1064,82 @@ class actionModel extends model
|
||||
return $this->transformActions($actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过产品获取动态。
|
||||
* Get actions as dynamic by product.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param string $account
|
||||
* @param string $period
|
||||
* @param string $orderBy
|
||||
* @param int $limit
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDynamicByProduct(int $productID, string $account = 'all', string $period = 'all', string $orderBy = 'date_desc', int $limit = 50, string $date = '', string $direction = 'next'): array
|
||||
{
|
||||
return $this->getDynamic($account, $period, $orderBy, $limit, (int)$productID, 'all', 'all', $date, $direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过项目获取动态。
|
||||
* Get actions as dynamic by project.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param string $account
|
||||
* @param string $period
|
||||
* @param string $orderBy
|
||||
* @param int $limit
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDynamicByProject(int $projectID, string $account = 'all', string $period = 'all', string $orderBy = 'date_desc', int $limit = 50, string $date = '', string $direction = 'next'): array
|
||||
{
|
||||
return $this->getDynamic($account, $period, $orderBy, $limit, 'all', (int)$projectID, 'all', $date, $direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过执行获取动态。
|
||||
* Get actions as dynamic by execution.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param string $account
|
||||
* @param string $period
|
||||
* @param string $orderBy
|
||||
* @param int $limit
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDynamicByExecution(int $executionID, string $account = 'all', string $period = 'all', string $orderBy = 'date_desc', int $limit = 50, string $date = '', string $direction = 'next'): array
|
||||
{
|
||||
return $this->getDynamic($account, $period, $orderBy, $limit, 'all', 'all', (int)$executionID, $date, $direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过账户获取动态。
|
||||
* Get actions as dynamic by account.
|
||||
*
|
||||
* @param string $account
|
||||
* @param string $period
|
||||
* @param string $orderBy
|
||||
* @param int $limit
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDynamicByAccount(string $account = '', string $period = 'all', string $orderBy = 'date_desc', int $limit = 50, string $date = '', string $direction = 'next'): array
|
||||
{
|
||||
if(empty($account)) $account = $this->app->user->account;
|
||||
return $this->getDynamic($account, $period, $orderBy, $limit, 'all', 'all', 'all', $date, $direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过视野获取用户可访问的动态类型。
|
||||
* Get the action types that the user can access through the vision.
|
||||
@@ -1054,7 +1152,7 @@ class actionModel extends model
|
||||
{
|
||||
if($this->app->user->admin) return '';
|
||||
|
||||
$actionCondition = '';
|
||||
$actionCondition = array();
|
||||
if(isset($this->app->user->rights['acls']['actions']))
|
||||
{
|
||||
if(empty($this->app->user->rights['acls']['actions'])) return '1 != 1';
|
||||
@@ -1063,11 +1161,10 @@ class actionModel extends model
|
||||
{
|
||||
if($module && $module != $moduleName) continue;
|
||||
if(isset($this->lang->mainNav->{$moduleName}) && !empty($this->app->user->rights['acls']['views']) && !isset($this->app->user->rights['acls']['views'][$moduleName])) continue;
|
||||
$actionCondition .= "(`objectType` = '{$moduleName}' AND `action` " . helper::dbIN(array_keys($actions)) . ") OR ";
|
||||
$actionCondition[] = "(`objectType` = '{$moduleName}' AND `action` " . helper::dbIN(array_keys($actions)) . ")";
|
||||
}
|
||||
$actionCondition = trim($actionCondition, 'OR ');
|
||||
}
|
||||
return $actionCondition;
|
||||
return implode(' OR ', $actionCondition);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1112,7 +1209,7 @@ class actionModel extends model
|
||||
/* If the query condition include all executions, no limit execution. */
|
||||
if(strpos($actionQuery, $allExecutions) !== false) $actionQuery = str_replace($allExecutions, '1 = 1', $actionQuery);
|
||||
|
||||
$actionQuery = str_replace("`product` = '{$productID}'", "`product` LIKE '%,{$productID},%'", $actionQuery);
|
||||
if($productID) $actionQuery = str_replace(" `product` = '{$productID}'", " t2.`product` = '{$productID}'", $actionQuery);
|
||||
if($date) $actionQuery = "({$actionQuery}) AND " . ('date' . ($direction == 'next' ? '<' : '>') . "'{$date}'");
|
||||
|
||||
/* 如果当前版本为lite,则过滤掉产品相关的动态。 */
|
||||
@@ -1139,8 +1236,22 @@ class actionModel extends model
|
||||
public function getBySQL(string $sql, string $orderBy, int $limit = 50): array
|
||||
{
|
||||
$actionCondition = $this->getActionCondition();
|
||||
return $this->dao->select('*')->from(TABLE_ACTION)
|
||||
->where($sql)
|
||||
$actionCondition = str_replace(' `action`', ' action.`action`', $actionCondition);
|
||||
$hasProduct = preg_match('/t2\.(`?)product/', $sql);
|
||||
|
||||
/* 构建权限搜索条件。 */
|
||||
/* Build has priv search condition. */
|
||||
$condition = "`objectType` != 'effort'";
|
||||
|
||||
$noMultipleExecutions = $this->dao->select('id')->from(TABLE_PROJECT)->where('multiple')->eq(0)->andWhere('type')->in('sprint,kanban')->fetchPairs();
|
||||
if($noMultipleExecutions) $condition = "({$condition}) AND (`objectType` != 'execution' OR (`objectType` = 'execution' AND `objectID`" . (count($noMultipleExecutions) == 1 ? ' != ' . reset($noMultipleExecutions) : ' NOT ' . helper::dbIN($noMultipleExecutions)) . "))";
|
||||
|
||||
return $this->dao->select('action.*')->from(TABLE_ACTION)->alias('action')
|
||||
->beginIF($hasProduct)->leftJoin(TABLE_ACTIONPRODUCT)->alias('t2')->on('action.id=t2.action')->fi()
|
||||
->where('objectType')->notIN($this->config->action->ignoreObjectType4Dynamic)
|
||||
->andWhere('action.action')->notIN($this->config->action->ignoreActions4Dynamic)
|
||||
->andWhere("({$sql})")
|
||||
->andWhere("({$condition})")
|
||||
->beginIF(!empty($actionCondition))->andWhere("($actionCondition)")->fi()
|
||||
->orderBy($orderBy)
|
||||
->limit($limit)
|
||||
@@ -1178,16 +1289,49 @@ class actionModel extends model
|
||||
}
|
||||
if($projectIdList) $projectIdList = array_unique($projectIdList);
|
||||
|
||||
$parents = array();
|
||||
if($projectIdList) $parents = $this->dao->select('parent')->from(TABLE_PROJECT)->where('project')->in($projectIdList)->andWhere('deleted')->eq('0')->fetchPairs('parent', 'parent');
|
||||
if($projectIdList) $projects = $this->dao->select('id,name,isTpl')->from(TABLE_PROJECT)->where('project')->in($projectIdList)->andWhere('deleted')->eq('0')->fetchAll('id');
|
||||
|
||||
$docIdList = array();
|
||||
$apiIdList = array();
|
||||
$docLibIdList = array();
|
||||
foreach($actions as $action)
|
||||
{
|
||||
if($action->objectType == 'doc' || $action->objectType == 'doctemplate') $docIdList[$action->objectID] = $action->objectID;
|
||||
if($action->objectType == 'doclib') $docLibIdList[$action->objectID] = $action->objectID;
|
||||
if($action->objectType == 'api') $apiIdList[$action->objectID] = $action->objectID;
|
||||
}
|
||||
|
||||
/* 获取需要验证的元素列表。 */
|
||||
/* Get the list of elements that need to be verified. */
|
||||
$shadowProducts = $this->dao->select('id')->from(TABLE_PRODUCT)->where('shadow')->eq(1)->fetchPairs();
|
||||
$projectMultiples = $this->dao->select('id,type,multiple')->from(TABLE_PROJECT)->where('id')->in($projectIdList)->fetchAll('id');
|
||||
$docList = $this->loadModel('doc')->getPrivDocs(array(), 0, 'all');
|
||||
$apiList = $this->loadModel('api')->getPrivApis('all');
|
||||
$docLibList = $this->doc->getLibs('hasApi');
|
||||
|
||||
$docList = array();
|
||||
$apiList = array();
|
||||
$docLibList = array();
|
||||
$this->loadModel('doc');
|
||||
if($docIdList)
|
||||
{
|
||||
$docList = $this->dao->select("`id`,`addedBy`,`type`,`lib`,`acl`,`users`,`readUsers`,`groups`,`readGroups`,`status`,`path`,`deleted`")->from(TABLE_DOC)->where('id')->in($docIdList)->fetchAll('id');
|
||||
$docList = $this->doc->batchCheckPrivDoc($docList);
|
||||
}
|
||||
if($docLibIdList)
|
||||
{
|
||||
$docLibList = $this->dao->select('*')->from(TABLE_DOCLIB)->where('id')->in($docLibIdList)->fetchAll('id');
|
||||
foreach($docLibList as $docLib)
|
||||
{
|
||||
if(!$this->doc->checkPrivLib($docLib)) unset($docLibList[$docLib->id]);
|
||||
}
|
||||
}
|
||||
if($apiIdList)
|
||||
{
|
||||
$apiList = $this->dao->select('*')->from(TABLE_DOCLIB)->where('id')->in($docLibIdList)->fetchAll('id');
|
||||
foreach($apiList as $api)
|
||||
{
|
||||
if(!$this->doc->checkPrivLib($api)) unset($apiList[$api->id]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($actions as $i => $action)
|
||||
{
|
||||
/* 如果doc,api,doclib,product类型对应的对象不存在,则从actions中删除。*/
|
||||
@@ -1214,8 +1358,12 @@ class actionModel extends model
|
||||
/* 设置对象的名称和链接。 */
|
||||
/* Set object name and set object link. */
|
||||
$this->actionTao->addObjectNameForAction($action, $objectNames, $objectType);
|
||||
$this->setObjectLink($action, $deptUsers, $shadowProducts, zget($projectMultiples, $projectID, ''));
|
||||
if($action->objectType == 'execution' && isset($parents[$action->objectID])) $action->objectLink = '';
|
||||
$this->setObjectLink($action, $deptUsers, $shadowProducts, zget($projectMultiples, $projectID, ''), $projectIdList);
|
||||
if($action->objectType == 'project' && !empty($projects[$action->objectID]->isTpl))
|
||||
{
|
||||
$action->objectLabel = $this->lang->project->template;
|
||||
$action->objectLink = helper::createLink('project', 'execution', "status=undone&projectID={$action->objectID}");
|
||||
}
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
@@ -1311,6 +1459,41 @@ class actionModel extends model
|
||||
return $actionObjectLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Action是否设置链接。
|
||||
* Check if action has link.
|
||||
*
|
||||
* @param object $action
|
||||
* @param array $projectIdList
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function checkForSetLink(object $action, array $projectIdList = array()): bool
|
||||
{
|
||||
static $parents = array();
|
||||
if(empty($parents) && !empty($projectIdList)) $parents = $this->dao->select('parent')->from(TABLE_PROJECT)->where('project')->in($projectIdList)->andWhere('deleted')->eq('0')->fetchPairs('parent', 'parent');
|
||||
|
||||
if($action->objectType == 'execution' && isset($parents[$action->objectID])) return false;
|
||||
if($this->app->user->admin) return true;
|
||||
|
||||
if($action->execution && strpos(",{$this->app->user->view->sprints},", ",{$action->execution},") === false) return false;
|
||||
if($action->project && strpos(",{$this->app->user->view->projects},", ",{$action->project},") === false) return false;
|
||||
|
||||
$hasPriv = false;
|
||||
$products = array();
|
||||
if($action->product) $products = array_unique(array_filter(explode(',', $action->product)));
|
||||
if($products)
|
||||
{
|
||||
foreach($products as $productID)
|
||||
{
|
||||
if(strpos(",{$this->app->user->view->products},", ",{$productID},") !== false) $hasPriv = true;
|
||||
}
|
||||
if(!$hasPriv) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置action的objectLink属性。
|
||||
* Set the objectLink attribute of action.
|
||||
@@ -1319,21 +1502,28 @@ class actionModel extends model
|
||||
* @param array $deptUsers
|
||||
* @param array $shadowProducts
|
||||
* @param object|int $project
|
||||
* @param array $projectIdList
|
||||
* @access public
|
||||
* @return object|bool
|
||||
*/
|
||||
public function setObjectLink(object $action, array $deptUsers, array $shadowProducts, object|string $project = ""): object|bool
|
||||
public function setObjectLink(object $action, array $deptUsers, array $shadowProducts, object|string $project = "", array $projectIdList = array()): object|bool
|
||||
{
|
||||
$action->objectLink = $moduleName = $methodName = $params = '';
|
||||
$action->objectLabel = zget($this->lang->action->objectTypes, $action->objectLabel);
|
||||
|
||||
if(strpos($action->objectLabel, '|') !== false)
|
||||
$canSetLink = $this->checkForSetLink($action, $projectIdList);
|
||||
$canParseLink = strpos($action->objectLabel, '|') !== false;
|
||||
|
||||
if(!$canParseLink && !$canSetLink) return $action;
|
||||
if($canParseLink)
|
||||
{
|
||||
list($objectLabel, $moduleName, $methodName, $vars) = explode('|', $action->objectLabel);
|
||||
$action->objectLabel = $objectLabel;
|
||||
$action->product = trim((string)$action->product, ',');
|
||||
|
||||
if($action->objectType == 'module') return $action;
|
||||
if(!$canSetLink) return $action;
|
||||
|
||||
if(in_array($action->objectType, array('program', 'project', 'product', 'execution')))
|
||||
{
|
||||
$objectTable = zget($this->config->objectTables, $action->objectType);
|
||||
@@ -1354,15 +1544,20 @@ class actionModel extends model
|
||||
}
|
||||
elseif($action->objectType == 'story')
|
||||
{
|
||||
$story = $this->loadModel('story')->getByID($action->objectID);
|
||||
if(!empty($story) && isset($shadowProducts[$story->product]))
|
||||
$story = $this->loadModel('story')->fetchByID($action->objectID);
|
||||
if(!empty($story))
|
||||
{
|
||||
$moduleName = 'projectstory';
|
||||
$moduleName = $story->type;
|
||||
$methodName = 'view';
|
||||
if(isset($shadowProducts[$story->product]))
|
||||
{
|
||||
$moduleName = 'projectstory';
|
||||
$methodName = 'view';
|
||||
}
|
||||
}
|
||||
if(!empty($action->project) && !$project)
|
||||
{
|
||||
$project = $this->loadModel('project')->getById($action->project);
|
||||
$project = $this->loadModel('project')->fetchById($action->project);
|
||||
if(empty($project->multiple))
|
||||
{
|
||||
$moduleName = 'execution';
|
||||
@@ -1370,7 +1565,6 @@ class actionModel extends model
|
||||
}
|
||||
}
|
||||
}
|
||||
$action->objectLink = helper::createLink($moduleName, $methodName, $params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1379,7 +1573,7 @@ class actionModel extends model
|
||||
if($action->objectType == 'review') list($moduleName, $methodName, $params) = array('review', 'view', "reviewID={$action->objectID}");
|
||||
if($action->objectType == 'build' && $this->app->tab == 'project') $moduleName = 'projectbuild';
|
||||
|
||||
if(empty($action->hasLink)) $action->objectLink = !$this->actionTao->checkActionClickable($action, $deptUsers, $moduleName, $methodName) ? '' : helper::createLink($moduleName, $methodName, $params);
|
||||
if(empty($action->hasLink) && $this->actionTao->checkActionClickable($action, $deptUsers, $moduleName, $methodName)) $action->objectLink = helper::createLink($moduleName, $methodName, $params);
|
||||
|
||||
/* Set app for no multiple project. */
|
||||
if(!empty($action->objectLink) && !empty($project) && empty($project->multiple)) $action->objectLink .= '#app=project';
|
||||
@@ -1402,28 +1596,14 @@ class actionModel extends model
|
||||
{
|
||||
$period = strtolower($period);
|
||||
|
||||
/* 1. 确切的日期。 */
|
||||
/* 1. The exact date. */
|
||||
if($date)
|
||||
{
|
||||
if($direction == 'pre') return array('begin' => $date, 'end' => FUTURE_DATE);
|
||||
if($direction == 'next') return array('begin' => EPOCH_DATE, 'end' => $date);
|
||||
return array('begin' => $date, 'end' => $date);
|
||||
}
|
||||
$beginDate = '';
|
||||
$year = date('Y');
|
||||
|
||||
/* 2. 所有时间。 */
|
||||
/* 2. All time. */
|
||||
if($period == 'all')
|
||||
{
|
||||
$beginDate = '';
|
||||
$year = date('Y');
|
||||
|
||||
/* 查询所有动态时最多查询最后两年的数据。 */
|
||||
/* When query all dynamic then query the data of the last two years at most. */
|
||||
if($this->app->getMethodName() == 'dynamic') $year = $year - 1;
|
||||
$beginDate = $year . '-01-01';
|
||||
return array('begin' => $beginDate, 'end' => FUTURE_DATE);
|
||||
}
|
||||
/* 查询所有动态时最多查询最后两年的数据。 */
|
||||
/* When query all dynamic then query the data of the last two years at most. */
|
||||
if($this->app->getMethodName() == 'dynamic') $year = $year - 1;
|
||||
$beginDate = $year . '-01-01';
|
||||
$beginAndEnd = array('begin' => $beginDate, 'end' => FUTURE_DATE);
|
||||
|
||||
$this->app->loadClass('date');
|
||||
|
||||
@@ -1432,10 +1612,10 @@ class actionModel extends model
|
||||
$yesterday = date::yesterday();
|
||||
$twoDaysAgo = date::twoDaysAgo();
|
||||
|
||||
if($period == 'today') return array('begin' => $today, 'end' => $tomorrow);
|
||||
if($period == 'yesterday') return array('begin' => $yesterday, 'end' => $today);
|
||||
if($period == 'twodaysago') return array('begin' => $twoDaysAgo, 'end' => $yesterday);
|
||||
if($period == 'latest3days') return array('begin' => $twoDaysAgo, 'end' => $tomorrow);
|
||||
if($period == 'today') $beginAndEnd = array('begin' => $today, 'end' => $tomorrow);
|
||||
if($period == 'yesterday') $beginAndEnd = array('begin' => $yesterday, 'end' => $today);
|
||||
if($period == 'twodaysago') $beginAndEnd = array('begin' => $twoDaysAgo, 'end' => $yesterday);
|
||||
if($period == 'latest3days') $beginAndEnd = array('begin' => $twoDaysAgo, 'end' => $tomorrow);
|
||||
|
||||
/* 如果时间段为周,则给结束日期增加结束时间。 */
|
||||
/* If the period is by week, add the end time to the end date. */
|
||||
@@ -1443,13 +1623,21 @@ class actionModel extends model
|
||||
{
|
||||
$func = "get$period";
|
||||
extract(date::$func());
|
||||
return array('begin' => $begin, 'end' => $end);
|
||||
$beginAndEnd = array('begin' => $begin, 'end' => $end);
|
||||
}
|
||||
|
||||
if($period == 'thismonth') return date::getThisMonth();
|
||||
if($period == 'lastmonth') return date::getLastMonth();
|
||||
if($period == 'thismonth') $beginAndEnd = date::getThisMonth();
|
||||
if($period == 'lastmonth') $beginAndEnd = date::getLastMonth();
|
||||
|
||||
return array('begin' => EPOCH_DATE, 'end' => FUTURE_DATE);
|
||||
/* 确切的日期。 */
|
||||
/* The exact date. */
|
||||
if($date)
|
||||
{
|
||||
if($direction == 'pre') $beginAndEnd['begin'] = $date;
|
||||
if($direction == 'next') $beginAndEnd['end'] = $date;
|
||||
}
|
||||
|
||||
return $beginAndEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1480,11 +1668,13 @@ class actionModel extends model
|
||||
|
||||
/* 区别是否有diff信息,以便于将有diff信息的字段放在最后。 */
|
||||
/* Diff histories by hasing diff info or not. Thus we can to make sure the field with diff show at last. */
|
||||
if($objectType == 'doctemplate') $this->app->loadLang('doc');
|
||||
foreach($histories as $history)
|
||||
{
|
||||
$fieldName = $history->field;
|
||||
$history->fieldLabel = isset($this->lang->{$objectType}) && isset($this->lang->{$objectType}->{$fieldName}) ? $this->lang->{$objectType}->{$fieldName} : $fieldName;
|
||||
if($objectType == 'module') $history->fieldLabel = $this->lang->tree->{$fieldName};
|
||||
if($objectType == 'doctemplate') $history->fieldLabel = zget($this->lang->docTemplate, $fieldName, zget($this->lang->doc, $fieldName, $fieldName));
|
||||
if($fieldName == 'fileName') $history->fieldLabel = $this->lang->file->{$fieldName};
|
||||
if(($length = strlen($history->fieldLabel)) > $maxLength) $maxLength = $length;
|
||||
$history->diff ? $historiesWithDiff[] = $history : $historiesWithoutDiff[] = $history;
|
||||
@@ -1714,46 +1904,23 @@ class actionModel extends model
|
||||
public function buildDateGroup(array $actions, string $direction = 'next', string $orderBy = 'date_desc'): array
|
||||
{
|
||||
$dateGroup = array();
|
||||
$firstTime = 0;
|
||||
$timeStamp = 0;
|
||||
foreach($actions as $action)
|
||||
{
|
||||
$timeStamp = strtotime(isset($action->originalDate) ? $action->originalDate : $action->date);
|
||||
$date = date(DT_DATE3, $timeStamp);
|
||||
$action->time = date(DT_TIME2, $timeStamp);
|
||||
$dateGroup[$date][] = $action;
|
||||
}
|
||||
|
||||
/* 查询数据并且写入日期分组中。 */
|
||||
/* Query data and write into data packets. */
|
||||
if($dateGroup)
|
||||
{
|
||||
$lastDateActions = $this->dao->select('*')->from(TABLE_ACTION)->where($this->session->actionQueryCondition)->andWhere("(LEFT(`date`, 10) = '" . substr($action->originalDate, 0, 10) . "')")->orderBy($this->session->actionOrderBy)->fetchAll('id', false);
|
||||
if(count($dateGroup[$date]) < count($lastDateActions))
|
||||
{
|
||||
unset($dateGroup[$date]);
|
||||
$lastDateActions = $this->transformActions($lastDateActions);
|
||||
foreach($lastDateActions as $action)
|
||||
{
|
||||
$timeStamp = strtotime(isset($action->originalDate) ? $action->originalDate : $action->date);
|
||||
$date = date(DT_DATE3, $timeStamp);
|
||||
$action->time = date(DT_TIME2, $timeStamp);
|
||||
$dateGroup[$date][] = $action;
|
||||
}
|
||||
}
|
||||
if(empty($firstTime)) $firstTime = $timeStamp;
|
||||
}
|
||||
|
||||
/* 将日期的顺序修改正确。 */
|
||||
/* Modify date to the corrret order. */
|
||||
if($this->app->rawModule != 'company' && $direction != 'next')
|
||||
if($dateGroup && $firstTime < $timeStamp)
|
||||
{
|
||||
$dateGroup = array_reverse($dateGroup);
|
||||
}
|
||||
elseif($this->app->rawModule == 'company')
|
||||
{
|
||||
if($direction == 'pre') $dateGroup = array_reverse($dateGroup);
|
||||
if(($direction == 'next' && $orderBy == 'date_asc') || ($direction == 'pre' && $orderBy == 'date_desc'))
|
||||
{
|
||||
foreach($dateGroup as $key => $dateItem) $dateGroup[$key] = array_reverse($dateItem);
|
||||
}
|
||||
foreach($dateGroup as $key => $dateItem) $dateGroup[$key] = array_reverse($dateItem);
|
||||
}
|
||||
return $dateGroup;
|
||||
}
|
||||
@@ -1764,24 +1931,30 @@ class actionModel extends model
|
||||
*
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @param string $period
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPreOrNext(string $date, string $direction = 'next'): bool
|
||||
public function hasPreOrNext(string $date, string $direction = 'next', string $period = 'all'): bool
|
||||
{
|
||||
if(empty($date)) return false;
|
||||
|
||||
$condition = $this->session->actionQueryCondition;
|
||||
if(empty($condition)) return false;
|
||||
|
||||
/* 移除搜索中的时间筛选条件。 */
|
||||
/* Remove time filter from search. */
|
||||
$condition = preg_replace("/AND +`?date`? +(<|>|<=|>=) +'\d{4}\-\d{2}\-\d{2}'/", '', $condition);
|
||||
$count = $this->dao->select('COUNT(1) AS count')
|
||||
->from(TABLE_ACTION)
|
||||
$beginAndEnd = $this->computeBeginAndEnd($period, '', $direction);
|
||||
$hasProduct = preg_match('/t2\.(`?)product/', $condition);
|
||||
$condition = preg_replace("/AND +`?date`? +(<|>|<=|>=) +'\d{4}\-\d{2}\-\d{2}'/", '', $condition);
|
||||
$actions = $this->dao->select('action.id')->from(TABLE_ACTION)->alias('action')
|
||||
->beginIF($hasProduct)->leftJoin(TABLE_ACTIONPRODUCT)->alias('t2')->on('action.id=t2.action')->fi()
|
||||
->where($condition)
|
||||
->andWhere('date' . ($direction == 'next' ? '<' : '>') . "'{$date}'")
|
||||
->fetch('count');
|
||||
->andWhere('`date`' . ($direction == 'next' ? '<' : '>') . "'{$date}'")
|
||||
->beginIF($direction == 'next' && $beginAndEnd['begin'] != EPOCH_DATE)->andWhere('date')->ge($beginAndEnd['begin'])->fi()
|
||||
->beginIF($direction != 'next' && $beginAndEnd['end'] != FUTURE_DATE)->andWhere('date')->le($beginAndEnd['end'])->fi()
|
||||
->limit(1)
|
||||
->fetchAll();
|
||||
|
||||
return $count > 0;
|
||||
return count($actions) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2168,7 +2341,7 @@ class actionModel extends model
|
||||
}
|
||||
elseif(strpos(",{$this->config->action->needGetProjectType},", ",{$objectType},") !== false || $objectType == 'project' || $objectType == 'execution')
|
||||
{
|
||||
$objectInfo = $this->dao->select("id, project, {$field} AS name")->from($table)->where('id')->in($objectIdList)->orderBy('id_asc')->fetchAll();
|
||||
$objectInfo = $this->dao->select("id, project, {$field} AS name")->from($table)->where('id')->in($objectIdList)->filterTpl(false)->orderBy('id_asc')->fetchAll();
|
||||
foreach($objectInfo as $object)
|
||||
{
|
||||
$objectName[$object->id] = $objectType == 'gapanalysis' ? zget($users, $object->name) : $object->name; // Get user realname if objectType is gapanalysis.
|
||||
@@ -2179,7 +2352,7 @@ class actionModel extends model
|
||||
{
|
||||
if($objectType == 'team') $table = TABLE_PROJECT;
|
||||
$objectField = $objectType == 'story' ? 'id,title,type' : 'id,team AS title,type';
|
||||
$objectInfo = $this->dao->select($objectField)->from($table)->where('id')->in($objectIdList)->orderBy('id_asc')->fetchAll();
|
||||
$objectInfo = $this->dao->select($objectField)->from($table)->where('id')->in($objectIdList)->filterTpl(false)->orderBy('id_asc')->fetchAll();
|
||||
foreach($objectInfo as $object)
|
||||
{
|
||||
$objectName[$object->id] = $object->title;
|
||||
@@ -2199,7 +2372,7 @@ class actionModel extends model
|
||||
}
|
||||
else
|
||||
{
|
||||
$objectName = $this->dao->select("id, {$field} AS name")->from($table)->where('id')->in($objectIdList)->orderBy('id_asc')->fetchPairs();
|
||||
$objectName = $this->dao->select("id, {$field} AS name")->from($table)->where('id')->in($objectIdList)->filterTpl(false)->orderBy('id_asc')->fetchPairs();
|
||||
}
|
||||
return array($objectName, $relatedProject, $requirements, $epics);
|
||||
}
|
||||
@@ -2309,7 +2482,7 @@ class actionModel extends model
|
||||
|
||||
/* 恢复隐藏执行。 */
|
||||
/* Resotre hidden execution. */
|
||||
if($action->objectType == 'project' && !$object->multiple) $this->dao->update(TABLE_EXECUTION)->set('deleted')->eq('0')->where('project')->eq($object->id)->andWhere('multiple')->eq('0')->exec();
|
||||
if($action->objectType == 'project' && empty($object->multiple)) $this->dao->update(TABLE_EXECUTION)->set('deleted')->eq('0')->where('project')->eq($object->id)->andWhere('multiple')->eq('0')->exec();
|
||||
}
|
||||
if($action->objectType == 'doc' && $object->files) $this->dao->update(TABLE_FILE)->set('deleted')->eq('0')->where('id')->in($object->files)->exec();
|
||||
|
||||
@@ -2351,69 +2524,6 @@ class actionModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 生成用户访问权限检查sql。
|
||||
* Build user access check sql.
|
||||
*
|
||||
* @param string|int $productID
|
||||
* @param string|int $projectID
|
||||
* @param string|int $executionID
|
||||
* @param array $executions
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function buildUserAclsSearchCondition(string|int $productID, string|int $projectID, string|int $executionID, array &$executions): string
|
||||
{
|
||||
/* 验证用户的产品/项目/执行权限。 */
|
||||
/* Verify user's product/project/execution permissions。*/
|
||||
$aclViews = isset($this->app->user->rights['acls']['views']) ? $this->app->user->rights['acls']['views'] : array();
|
||||
if($productID == 'all' || $productID == 0) $grantedProducts = empty($aclViews) || !empty($aclViews['product']) ? $this->app->user->view->products : '0';
|
||||
if($projectID == 'all' || $projectID == 0) $grantedProjects = empty($aclViews) || !empty($aclViews['project']) ? $this->app->user->view->projects : '0';
|
||||
if($executionID == 'all' || $executionID == 0) $grantedExecutions = empty($aclViews) || !empty($aclViews['execution']) ? $this->app->user->view->sprints : '0';
|
||||
if(empty($grantedProducts)) $grantedProducts = '0';
|
||||
|
||||
/* If product is selected, show related projects and executions. */
|
||||
if($productID && is_numeric($productID))
|
||||
{
|
||||
$productID = (int)$productID;
|
||||
$projects = $this->loadModel('product')->getProjectPairsByProduct($productID);
|
||||
$executions = $this->product->getExecutionPairsByProduct($productID) + array(0 => 0);
|
||||
|
||||
$grantedProjects = isset($grantedProjects) ? array_intersect(array_keys($projects), explode(',', $grantedProjects)) : array_keys($projects);
|
||||
$grantedExecutions = isset($grantedExecutions) ? array_intersect(array_keys($executions), explode(',', $grantedExecutions)) : array_keys($executions);
|
||||
}
|
||||
|
||||
/* If project is selected, show related products and executions. */
|
||||
if($projectID && is_numeric($projectID))
|
||||
{
|
||||
$projectID = (int)$projectID;
|
||||
$products = $this->loadModel('product')->getProductPairsByProject($projectID);
|
||||
$executions = $this->loadModel('execution')->fetchPairs($projectID, 'all', false) + array(0);
|
||||
|
||||
$grantedProducts = isset($grantedProducts) ? array_intersect(array_keys($products), is_array($grantedProducts) ? $grantedProducts : explode(',', $grantedProducts)) : array_keys($products);
|
||||
$grantedExecutions = isset($grantedExecutions) ? array_intersect(array_keys($executions), is_array($grantedExecutions) ? $grantedExecutions : explode(',', $grantedExecutions)) : array_keys($executions);
|
||||
}
|
||||
|
||||
/* 组建产品/项目/执行搜索条件。 */
|
||||
/* Build product/project/execution search condition. */
|
||||
if(isset($grantedProducts))
|
||||
{
|
||||
if(is_string($grantedProducts)) $grantedProducts = explode(',', $grantedProducts);
|
||||
$productCondition = '';
|
||||
foreach($grantedProducts as $product) $productCondition = empty($productCondition) ? " OR (execution = '0' AND project = '0' AND (product LIKE '%,{$product},%'" : "{$productCondition} OR product LIKE '%,{$product},%'";
|
||||
if(!empty($productCondition)) $productCondition .= '))';
|
||||
}
|
||||
else
|
||||
{
|
||||
$productCondition = " OR (execution = '0' AND project = '0' AND product LIKE '%,{$productID},%')";
|
||||
}
|
||||
$projectCondition = isset($grantedProjects) ? "(execution = '0' AND project != '0' AND project " . helper::dbIN($grantedProjects) . ')' : "(execution = '0' AND project = '{$projectID}')";
|
||||
$executionCondition = isset($grantedExecutions) ? "(execution != '0' AND execution " . helper::dbIN($grantedExecutions) . ')' : "(execution != '0' AND execution = '{$executionID}')";
|
||||
|
||||
$condition = "((product =',0,' or product = '0' or product=',,') AND project = '0' AND execution = '0') {$productCondition} OR {$projectCondition} OR {$executionCondition}";
|
||||
return $condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行和项目相关操作记录的extra信息。
|
||||
* Build execution and project action extra info.
|
||||
@@ -2443,11 +2553,22 @@ class actionModel extends model
|
||||
*/
|
||||
public function getDynamicCount($period = 'all'): int
|
||||
{
|
||||
$condition = $this->session->actionQueryCondition ? $this->session->actionQueryCondition : '1=1';
|
||||
$condition = $this->session->actionQueryCondition;
|
||||
if(empty($condition)) return 0;
|
||||
|
||||
$table = $this->actionTao->getActionTable($period);
|
||||
$table = $this->actionTao->getActionTable($period);
|
||||
$hasProduct = preg_match('/t2\.(`?)product/', $condition);
|
||||
$beginAndEnd = $this->computeBeginAndEnd($period, '', 'next');
|
||||
$condition = preg_replace("/AND +`?date`? +(<|>|<=|>=) +'\d{4}\-\d{2}\-\d{2}'/", '', $condition);
|
||||
|
||||
return $this->dao->select('count(1) AS count')->from($table)->where($condition)->fetch('count');
|
||||
$actions = $this->dao->select('action.id')->from($table)->alias('action')
|
||||
->beginIF($hasProduct)->leftJoin(TABLE_ACTIONPRODUCT)->alias('t2')->on('action.id=t2.action')->fi()
|
||||
->where($condition)
|
||||
->beginIF($beginAndEnd['begin'] != EPOCH_DATE)->andWhere('date')->ge($beginAndEnd['begin'])->fi()
|
||||
->beginIF($beginAndEnd['end'] != FUTURE_DATE)->andWhere('date')->le($beginAndEnd['end'])->fi()
|
||||
->limit(self::MAXCOUNT)
|
||||
->fetchAll('id');
|
||||
return count($actions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2481,4 +2602,68 @@ class actionModel extends model
|
||||
{
|
||||
return $this->dao->select('*')->from(TABLE_ACTION)->orderBy('id')->limit(1)->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查该日期是否还有的更多日志。
|
||||
* Check has more actions for this date.
|
||||
*
|
||||
* @param object $lastAction
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function hasMoreAction(object $lastAction): bool
|
||||
{
|
||||
$condition = $this->session->actionQueryCondition;
|
||||
if(empty($condition)) return false;
|
||||
|
||||
$hasProduct = preg_match('/t2\.(`?)product/', $condition);
|
||||
$lastDate = substr($lastAction->originalDate, 0, 10);
|
||||
$condition = preg_replace("/AND +`?date`? +(<|>|<=|>=) +'\d{4}\-\d{2}\-\d{2}'/", '', $condition);
|
||||
$actions = $this->dao->select('action.id')->from(TABLE_ACTION)->alias('action')
|
||||
->beginIF($hasProduct)->leftJoin(TABLE_ACTIONPRODUCT)->alias('t2')->on('action.id=t2.action')->fi()
|
||||
->where($condition)
|
||||
->andWhere("`date`")->ge($lastDate)
|
||||
->andWhere("`date`")->lt($lastDate . ' 23:59:59')
|
||||
->andWhere("`date` < '{$lastAction->originalDate}'")
|
||||
->orderBy('date_desc')
|
||||
->limit(1)
|
||||
->fetchAll();
|
||||
return count($actions) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该日期的更多日志。
|
||||
* Get more actions for this date.
|
||||
*
|
||||
* @param int $lastActionID
|
||||
* @param int $limit
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getMoreActions(int $lastActionID, int $limit = 100): array
|
||||
{
|
||||
$condition = $this->session->actionQueryCondition;
|
||||
if(empty($condition)) return array();
|
||||
|
||||
$hasProduct = preg_match('/t2\.(`?)product/', $condition);
|
||||
$lastAction = $this->dao->select('*')->from(TABLE_ACTION)->where('id')->eq($lastActionID)->fetch();
|
||||
$lastDate = substr($lastAction->date, 0, 10);
|
||||
$actions = $this->dao->select('action.*')->from(TABLE_ACTION)->alias('action')
|
||||
->beginIF($hasProduct)->leftJoin(TABLE_ACTIONPRODUCT)->alias('t2')->on('action.id=t2.action')->fi()
|
||||
->where($condition)
|
||||
->andWhere("`date`")->ge($lastDate)
|
||||
->andWhere("`date`")->lt($lastDate . ' 23:59:59')
|
||||
->andWhere("`date` < '{$lastAction->date}'")
|
||||
->orderBy('date_desc')
|
||||
->limit($limit)
|
||||
->fetchAll('id', false);
|
||||
|
||||
$actions = $this->transformActions($actions);
|
||||
foreach($actions as $action)
|
||||
{
|
||||
$timeStamp = strtotime(isset($action->originalDate) ? $action->originalDate : $action->date);
|
||||
$action->time = date(DT_TIME2, $timeStamp);
|
||||
}
|
||||
return array_values($actions);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-14
@@ -679,45 +679,48 @@ class actionTao extends actionModel
|
||||
* Get action list by condition.
|
||||
*
|
||||
* @param string $condition
|
||||
* @param string $date
|
||||
* @param string $begin
|
||||
* @param string $end
|
||||
* @param string $account
|
||||
* @param string|int $productID
|
||||
* @param string|int $projectID
|
||||
* @param string|int $executionID
|
||||
* @param array $executions
|
||||
* @param string $actionCondition
|
||||
* @param string $orderBy
|
||||
* @param int $limit
|
||||
* @access public
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getActionListByCondition(string $condition, string $date, string $begin, string $end, string $account, string|int $productID, string|int $projectID, string|int $executionID, array|string $executions, string $actionCondition, string $orderBy, int $limit = 50): array|bool
|
||||
public function getActionListByCondition(string $condition, string $begin, string $end, string $account, string|int $productID, string|int $projectID, string|int $executionID, string $actionCondition, string $orderBy, int $limit = 50): array|bool
|
||||
{
|
||||
/* 获取最近一个月的动态用actionrecent表。 */
|
||||
$lastMonth = date('Y-m-d', strtotime('-1 month'));
|
||||
$actionTable = ($begin >= $lastMonth && $end >= $lastMonth) ? TABLE_ACTIONRECENT : TABLE_ACTION;
|
||||
|
||||
return $this->dao->select('*')->from($actionTable)
|
||||
$actionCondition = str_replace(' `action`', ' action.`action`', $actionCondition);
|
||||
$hasProduct = preg_match('/t2\.(`?)product/', $condition);
|
||||
if(is_numeric($productID) && $productID) $hasProduct = true;
|
||||
if($productID === 'notzero') $hasProduct = true;
|
||||
|
||||
return $this->dao->select('action.*')->from($actionTable)->alias('action')
|
||||
->beginIF($hasProduct)->leftJoin(TABLE_ACTIONPRODUCT)->alias('t2')->on('action.id=t2.action')->fi()
|
||||
->where('objectType')->notIN($this->config->action->ignoreObjectType4Dynamic)
|
||||
->andWhere('action')->notIN($this->config->action->ignoreActions4Dynamic)
|
||||
->andWhere('action.action')->notIN($this->config->action->ignoreActions4Dynamic)
|
||||
->andWhere('vision')->eq($this->config->vision)
|
||||
->beginIF($begin != EPOCH_DATE)->andWhere('date')->ge($begin)->fi()
|
||||
->beginIF($end != FUTURE_DATE)->andWhere('date')->le($end)->fi()
|
||||
->beginIF($account != 'all')->andWhere('actor')->eq($account)->fi()
|
||||
->beginIF(is_numeric($productID) && $productID)->andWhere('product')->like("%,$productID,%")->fi()
|
||||
->beginIF(is_numeric($productID) && $productID)->andWhere('t2.product')->eq($productID)->fi()
|
||||
->beginIF(is_numeric($projectID) && $projectID)->andWhere('project')->eq($projectID)->fi()
|
||||
->beginIF(!empty($executions))->andWhere('execution')->in(array_keys($executions))->fi()
|
||||
->beginIF(is_numeric($executionID) && $executionID)->andWhere('execution')->eq($executionID)->fi()
|
||||
/* lite模式下需要排除的一些类型。 */
|
||||
/* Types excluded from Lite. */
|
||||
->beginIF($this->config->vision == 'lite')->andWhere('objectType')->notin('product')->fi()
|
||||
->beginIF($this->config->systemMode == 'light')->andWhere('objectType')->notin('program')->fi()
|
||||
->beginIF($productID === 'notzero')->andWhere('product')->gt(0)->andWhere('product')->notlike('%,0,%')->fi()
|
||||
->beginIF($productID === 'notzero')->andWhere('t2.product')->gt(0)->fi()
|
||||
->beginIF($projectID === 'notzero')->andWhere('project')->gt(0)->fi()
|
||||
->beginIF($executionID === 'notzero')->andWhere('execution')->gt(0)->fi()
|
||||
->andWhere($condition)
|
||||
->beginIF($condition && $condition != '1=1')->andWhere($condition)->fi()
|
||||
->beginIF($actionCondition)->andWhere("($actionCondition)")->fi()
|
||||
->orderBy($orderBy)
|
||||
->limit($limit)
|
||||
@@ -751,7 +754,7 @@ class actionTao extends actionModel
|
||||
*/
|
||||
public function checkIsActionLegal(object $action, array $shadowProducts, array $docList, array $apiList, array $docLibList): bool
|
||||
{
|
||||
if($action->objectType == 'doc' && !isset($docList[$action->objectID])) return false;
|
||||
if(($action->objectType == 'doc' || $action->objectType == 'doctemplate') && !isset($docList[$action->objectID])) return false;
|
||||
if($action->objectType == 'api' && !isset($apiList[$action->objectID])) return false;
|
||||
if($action->objectType == 'doclib' && !isset($docLibList[$action->objectID])) return false;
|
||||
if($action->objectType == 'product' && isset($shadowProducts[$action->objectID])) return false;
|
||||
@@ -790,9 +793,9 @@ class actionTao extends actionModel
|
||||
elseif($action->objectType == 'kanbancard' && strpos($action->action, 'imported') !== false && $action->action != 'importedcard')
|
||||
{
|
||||
$objectType = str_replace('imported', '', $action->action);
|
||||
$objectTable = zget($this->config->objectTables, $objectType);
|
||||
$objectTable = zget($this->config->objectTables, $objectType, '');
|
||||
$objectName = ($objectType == 'productplan' || $objectType == 'ticket') ? 'title' : 'name';
|
||||
$action->objectName = $this->dao->select($objectName)->from($objectTable)->where('id')->eq($action->extra)->fetch($objectName);
|
||||
if($objectTable) $action->objectName = $this->dao->select($objectName)->from($objectTable)->where('id')->eq($action->extra)->fetch($objectName);
|
||||
}
|
||||
elseif(strpos(',module,chartgroup,', ",$action->objectType,") !== false && !empty($action->extra) && $action->action != 'deleted')
|
||||
{
|
||||
@@ -843,7 +846,13 @@ class actionTao extends actionModel
|
||||
$method = $this->config->action->assetViewMethod[$action->objectType];
|
||||
}
|
||||
|
||||
$action->objectLink = isset($method) ? helper::createLink('assetlib', $method, sprintf($vars, $action->objectID)) : helper::createLink($moduleName, $methodName, sprintf($vars, $action->objectID));
|
||||
if(isset($method))
|
||||
{
|
||||
$moduleName = 'assetlib';
|
||||
$methodName = $method;
|
||||
}
|
||||
|
||||
$action->objectLink = common::hasPriv($moduleName, $methodName) ? helper::createLink($moduleName, $methodName, sprintf($vars, $action->objectID)) : '';
|
||||
$action->hasLink = true;
|
||||
}
|
||||
|
||||
@@ -947,6 +956,7 @@ class actionTao extends actionModel
|
||||
if($todo && $todo->private == 1 && $todo->account != $this->app->user->account) return false;
|
||||
}
|
||||
|
||||
if($action->objectType == 'docTemplate' && !common::hasPriv('docTempalte', 'view')) return false;
|
||||
if($action->objectType == 'mr' && (empty($action->objectName) || $action->action == 'deleted')) return false;
|
||||
if($action->objectType == 'stakeholder' && $action->project == 0) return false;
|
||||
if($action->objectType == 'chartgroup') return false;
|
||||
@@ -1021,7 +1031,7 @@ class actionTao extends actionModel
|
||||
break;
|
||||
case 'program':
|
||||
case 'project':
|
||||
$field = 'id, acl, name, hasProduct';
|
||||
$field = 'id, acl, name, hasProduct, multiple';
|
||||
break;
|
||||
case 'doc':
|
||||
$table = TABLE_DOCCONTENT;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
class actionTest
|
||||
{
|
||||
private $objectModel;
|
||||
public $objectModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -28,14 +28,7 @@ class actionTest
|
||||
global $tester, $config;
|
||||
if($tester->app->upgrading && !empty($version))
|
||||
{
|
||||
global $tester;
|
||||
$data = new stdclass();
|
||||
$data->value = $version;
|
||||
$data->key = 'version';
|
||||
$data->owner = 'system';
|
||||
$data->module = 'common';
|
||||
$data->section = 'global';
|
||||
$tester->dao->replace(TABLE_CONFIG)->data($data)->exec();
|
||||
$tester->dao->update(TABLE_CONFIG)->set('value')->eq($version)->where('`key`')->eq('version')->andWhere('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('global')->exec();
|
||||
}
|
||||
|
||||
$_SERVER['HTTP_HOST'] = 'pms.zentao.com';
|
||||
@@ -120,17 +113,12 @@ class actionTest
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getListTest(string $objectType, int $objectID, string $edition = 'pms'): string
|
||||
public function getListTest(string $objectType, int $objectID): string
|
||||
{
|
||||
global $tester;
|
||||
$oldEdition = $tester->config->edition;
|
||||
$tester->config->edition = $edition;
|
||||
|
||||
$objects = $this->objectModel->getList($objectType, $objectID);
|
||||
|
||||
$tester->config->edition = $oldEdition;
|
||||
|
||||
global $tester;
|
||||
$modules = $objectType == 'module' ? $tester->dao->select('id')->from(TABLE_MODULE)->where('root')->in($objectID)->fetchPairs('id') : array();
|
||||
$actions = $this->objectModel->getActionListByTypeAndID($objectType, $objectID, $modules);
|
||||
|
||||
@@ -274,19 +262,20 @@ class actionTest
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
global $tester;
|
||||
$objects = $tester->dao->select('*')->from(TABLE_HISTORY)->where('action')->eq($actionID)->fetchAll();
|
||||
$objects = $tester->dao->select('*')->from(TABLE_HISTORY)->where('action')->eq($actionID)->fetchAll('', false);
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dynamic show action.
|
||||
*
|
||||
* @param string $module
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getActionConditionTest()
|
||||
public function getActionConditionTest($module = '')
|
||||
{
|
||||
$objects = $this->objectModel->getActionCondition();
|
||||
$objects = $this->objectModel->getActionCondition($module);
|
||||
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
@@ -347,6 +336,89 @@ class actionTest
|
||||
return count($objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getDynamiByProduct method.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param string $account
|
||||
* @param string $period
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return int|array
|
||||
*/
|
||||
public function getDynamicByProductTest($productID, $account = '', $period = 'all', $date = '', $direction = 'next')
|
||||
{
|
||||
$date = $date == 'today' ? date('Y-m-d', time()) : $date;
|
||||
$objects = $this->objectModel->getDynamicByProduct($productID, $account, $period, 'date_desc', 50, $date, $direction);
|
||||
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
return count($objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getDynamiByProject method.
|
||||
*
|
||||
* @param int $projectID
|
||||
* @param string $account
|
||||
* @param string $period
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return int|array
|
||||
*/
|
||||
public function getDynamicByProjectTest($projectID, $account = '', $period = 'all', $date = '', $direction = 'next')
|
||||
{
|
||||
$date = $date == 'today' ? date('Y-m-d', time()) : $date;
|
||||
$objects = $this->objectModel->getDynamicByProject($projectID, $account, $period, 'date_desc', 50, $date, $direction);
|
||||
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
return count($objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get actions as dynamic by execution.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param string $account
|
||||
* @param string $period
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return int|array
|
||||
*/
|
||||
public function getDynamicByExecutionTest($executionID, $account = '', $period = 'all', $date = '', $direction = 'next')
|
||||
{
|
||||
$date = $date == 'today' ? date('Y-m-d', time()) : $date;
|
||||
$objects = $this->objectModel->getDynamicByExecution($executionID, $account, $period, 'date_desc', 50, $date, $direction);
|
||||
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
return count($objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get actions as dynamic by account.
|
||||
*
|
||||
* @param string $account
|
||||
* @param string $period
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return int|array
|
||||
*/
|
||||
public function getDynamicByAccountTest($account = '', $period = 'all', $date = '', $direction = 'next')
|
||||
{
|
||||
$date = $date == 'today' ? date('Y-m-d', time()) : $date;
|
||||
$objects = $this->objectModel->getDynamicByAccount($account, $period, 'date_desc', 50, $date, $direction);
|
||||
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
return count($objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get actions by SQL.
|
||||
*
|
||||
@@ -428,29 +500,36 @@ class actionTest
|
||||
* Test compute the begin date and end date of a period.
|
||||
*
|
||||
* @param string $period
|
||||
* @param string $date
|
||||
* @param string $direction
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function computeBeginAndEndTest($period)
|
||||
public function computeBeginAndEndTest($period, $date = '', $direction = 'next')
|
||||
{
|
||||
$date = $this->objectModel->computeBeginAndEnd($period);
|
||||
$this->objectModel->app->methodName = 'dynamic';
|
||||
$result = $this->objectModel->computeBeginAndEnd($period, $date, $direction);
|
||||
|
||||
$today = date('Y-m-d');
|
||||
$tomorrow = date('Y-m-d', strtotime('+1 days'));
|
||||
$yesterday = date('Y-m-d', strtotime('-1 days'));
|
||||
$twoDaysAgo = date('Y-m-d', strtotime('-2 days'));
|
||||
|
||||
if($period == 'all') return $date['begin'] == '1970-01-01' and $date['end'] == '2030-01-01';
|
||||
if($period == 'today') return $date['begin'] == $today and $date['end'] == $tomorrow;
|
||||
if($period == 'yesterday') return $date['begin'] == $yesterday and $date['end'] == $today;
|
||||
if($period == 'twodaysago') return $date['begin'] == $twoDaysAgo and $date['end'] == $yesterday;
|
||||
if($period == 'latest3days') return $date['begin'] == $twoDaysAgo and $date['end'] == $tomorrow;
|
||||
if($period == 'thismonth') return $date == date::getThisMonth();
|
||||
if($period == 'lastmonth') return $date == date::getLastMonth();
|
||||
if(!empty($date) && empty($direction)) return $result['begin'] == (date('Y') - 1) . '-01-01' and $result['end'] == '2030-01-01';
|
||||
|
||||
if($direction == 'pre') return $result['begin'] == '2025-04-23' && $result['end'] == '2030-01-01';
|
||||
if($direction == 'next') return $result['begin'] == (date('Y') - 1) . '-01-01' && $result['end'] == '2025-04-23';
|
||||
if($period == 'all') return $result['begin'] == (date('Y') - 1) . '-01-01' and $result['end'] == '2030-01-01';
|
||||
if($period == 'today') return $result['begin'] == $today and $result['end'] == $tomorrow;
|
||||
if($period == 'yesterday') return $result['begin'] == $yesterday and $result['end'] == $today;
|
||||
if($period == 'twodaysago') return $result['begin'] == $twoDaysAgo and $result['end'] == $yesterday;
|
||||
if($period == 'latest3days') return $result['begin'] == $twoDaysAgo and $result['end'] == $tomorrow;
|
||||
if($period == 'thismonth') return $result == date::getThisMonth();
|
||||
if($period == 'lastmonth') return $result == date::getLastMonth();
|
||||
$func = "get$period";
|
||||
extract(date::$func());
|
||||
if($period == 'thisweek') return $date['begin'] == $begin and $date['end'] == $end;
|
||||
if($period == 'lastweek') return $date['begin'] == $begin and $date['end'] == $end;
|
||||
if($period == 'thisweek') return $result['begin'] == $begin && $result['end'] == $end;
|
||||
if($period == 'lastweek') return $result['begin'] == $begin && $result['end'] == $end;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -526,9 +605,7 @@ class actionTest
|
||||
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
global $tester;
|
||||
$objects = $tester->dao->select('*')->from(TABLE_ACTION)->where('action')->eq('deleted')->fetchAll();
|
||||
return $objects;
|
||||
return $this->objectModel->dao->select('id,extra')->from(TABLE_ACTION)->where('action')->eq('deleted')->fetchAll('', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -667,7 +744,7 @@ class actionTest
|
||||
if(!$stage || $stage->deleted) return false;
|
||||
}
|
||||
|
||||
if(isset($actionList[2]) && $actionList[2]->extra == '0') return true;
|
||||
if(isset($actionList[2]) && $actionList[2]->extra == '0') return $actionList;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -775,9 +852,9 @@ class actionTest
|
||||
* Test get first action.
|
||||
*
|
||||
* @access public
|
||||
* @return array|object
|
||||
* @return array|object|false
|
||||
*/
|
||||
public function getFirstActionTest(): array|object
|
||||
public function getFirstActionTest(): array|object|false
|
||||
{
|
||||
$object = $this->objectModel->getFirstAction();
|
||||
|
||||
@@ -813,4 +890,37 @@ class actionTest
|
||||
$history = $this->objectModel->dao->select('*')->from(TABLE_HISTORY)->where('id')->eq($historyID)->fetch();
|
||||
return $this->objectModel->processHistory($history);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染每一个action的历史记录。
|
||||
* Render histories of every action.
|
||||
*
|
||||
* @param string $objectType
|
||||
* @param int $historyID
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function renderChangesTest(string $objectType, int $historyID = 0): string
|
||||
{
|
||||
$histories = $this->objectModel->dao->select('*')->from(TABLE_HISTORY)->where('id')->eq($historyID)->fetchAll('id', false);
|
||||
$content = $this->objectModel->renderChanges($objectType, 0, $histories);
|
||||
$content = str_replace("\n", '', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试操作是否可点击。
|
||||
* Test check action clickable.
|
||||
*
|
||||
* @param object $action
|
||||
* @param array $deptUser
|
||||
* @param string $moduleName
|
||||
* @param string $methodName
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function checkActionClickableTest(object $action, array $deptUser, string $moduleName, string $methodName): bool
|
||||
{
|
||||
return $this->objectModel->checkActionClickable($action, $deptUser, $moduleName, $methodName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ use function zin\wg;
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
zenData('user')->gen(60);
|
||||
su('admin');
|
||||
zenData('action')->loadYaml('action')->gen(60);
|
||||
zenData('actionrecent')->loadYaml('action')->gen(60);
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(60, true, false);
|
||||
zenData('actionrecent')->loadYaml('action')->gen(60, true, false);
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
|
||||
$project = zenData('project');
|
||||
$project->project->range('0-4');
|
||||
$project->parent->range('0-4');
|
||||
$project->gen(5);
|
||||
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->checkForSetLink();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 检查超级管理员 @1
|
||||
- 传入父项目ID @0
|
||||
- 没有执行权限 @0
|
||||
- 没有项目权限 @0
|
||||
- 动态没有关联产品 @1
|
||||
- 没有产品权限 @0
|
||||
- 有所有权限 @1
|
||||
|
||||
*/
|
||||
|
||||
global $tester, $app;
|
||||
$actionModel = $tester->loadModel('action');
|
||||
|
||||
$app->user->admin = true;
|
||||
$app->user->view = new stdclass();
|
||||
$app->user->view->products = '1,2,3,4';
|
||||
$app->user->view->projects = '1,2,3,4';
|
||||
$app->user->view->sprints = '5,6,7,8';
|
||||
|
||||
$action = new stdclass();
|
||||
$action->objectType = 'execution';
|
||||
$action->objectID = '3';
|
||||
$action->project = '1';
|
||||
$action->execution = '5';
|
||||
$action->product = ',1,';
|
||||
|
||||
$projectIdList = array('1', '2', '3', '4');
|
||||
|
||||
r($actionModel->checkForSetLink($action)) && p() && e('1'); //检查超级管理员
|
||||
r($actionModel->checkForSetLink($action, $projectIdList)) && p() && e('0'); //传入父项目ID
|
||||
|
||||
$app->user->admin = false;
|
||||
$action->objectType = 'task';
|
||||
$action->execution = '10';
|
||||
r($actionModel->checkForSetLink($action)) && p() && e('0'); //没有执行权限
|
||||
|
||||
$action->execution = '5';
|
||||
$action->project = '15';
|
||||
r($actionModel->checkForSetLink($action)) && p() && e('0'); //没有项目权限
|
||||
|
||||
$action->project = '1';
|
||||
$action->product = ',0,';
|
||||
r($actionModel->checkForSetLink($action)) && p() && e('1'); //动态没有关联产品
|
||||
|
||||
$action->product = ',10,';
|
||||
r($actionModel->checkForSetLink($action)) && p() && e('0'); //没有产品权限
|
||||
|
||||
$action->product = ',1,';
|
||||
r($actionModel->checkForSetLink($action)) && p() && e('1'); //有所有权限
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
|
||||
su('admin');
|
||||
zenData('actionrecent')->gen(10);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->cleanActions();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 第一次执行,检查返回结果 @1
|
||||
- 清空缓存动态,检查条目数 @8
|
||||
- 重复清空缓存动态,检查返回结果 @1
|
||||
- 重复清空缓存动态,检查条目数 @8
|
||||
- 第三次执行,检查返回结果 @1
|
||||
- 第三次执行,检查条目数 @5
|
||||
|
||||
*/
|
||||
|
||||
global $tester, $app;
|
||||
$actionModel = $tester->loadModel('action');
|
||||
$actionModel->dao->update(TABLE_ACTIONRECENT)->set('date')->eq(date('Y-m-d H:i:s'))->exec();
|
||||
$actionModel->dao->update(TABLE_ACTIONRECENT)->set('date')->eq(date('Y-m-d H:i:s', strtotime('-1 month') - 24 * 3600))->where('id')->le('2')->exec();
|
||||
|
||||
unset($app->config->global->cleanActionsDate);
|
||||
$result = $actionModel->cleanActions();
|
||||
$count = $actionModel->dao->select('count(*) as count')->from(TABLE_ACTIONRECENT)->fetch('count');
|
||||
r($result) && p() && e('1'); //第一次执行,检查返回结果
|
||||
r($count) && p() && e('8'); //清空缓存动态,检查条目数
|
||||
|
||||
$app->config->global->cleanActionsDate = date('Y-m-d H:i:s');
|
||||
$result = $actionModel->cleanActions();
|
||||
$count = $actionModel->dao->select('count(*) as count')->from(TABLE_ACTIONRECENT)->fetch('count');
|
||||
r($result) && p() && e('1'); //重复清空缓存动态,检查返回结果
|
||||
r($count) && p() && e('8'); //重复清空缓存动态,检查条目数
|
||||
|
||||
unset($config->global->cleanActionsDate);
|
||||
$actionModel->dao->update(TABLE_ACTIONRECENT)->set('date')->eq(date('Y-m-d H:i:s', strtotime('-1 month') - 24 * 3600))->where('id')->le('5')->exec();
|
||||
$result = $actionModel->cleanActions();
|
||||
$count = $actionModel->dao->select('count(*) as count')->from(TABLE_ACTIONRECENT)->fetch('count');
|
||||
r($result) && p() && e('1'); //第三次执行,检查返回结果
|
||||
r($count) && p() && e('5'); //第三次执行,检查条目数
|
||||
@@ -10,7 +10,7 @@ title=测试 actionModel->computeBeginAndEnd();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试计算all的日期 @1
|
||||
- 测试计算all的日期 @0
|
||||
- 测试计算today的日期 @1
|
||||
- 测试计算yesterday的日期 @1
|
||||
- 测试计算twodaysago的日期 @1
|
||||
@@ -19,19 +19,27 @@ cid=1
|
||||
- 测试计算lastweek的日期 @1
|
||||
- 测试计算thismonth的日期 @1
|
||||
- 测试计算lastmonth的日期 @1
|
||||
- 测试返回具体的日期 @1
|
||||
- 测试返回具体日期的结束日期 @1
|
||||
- 测试返回具体日期的开始日期 @1
|
||||
|
||||
*/
|
||||
|
||||
$typeList = array('all', 'today', 'yesterday', 'twodaysago', 'latest3days', 'thisweek', 'lastweek', 'thismonth', 'lastmonth');
|
||||
$typeList = array('all', 'today', 'yesterday', 'twodaysago', 'latest3days', 'thisweek', 'lastweek', 'thismonth', 'lastmonth');
|
||||
$dateList = array('', '2025-04-23');
|
||||
$directionList = array('', 'pre', 'next');
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
r($action->computeBeginAndEndTest($typeList[0])) && p() && e('1'); // 测试计算all的日期
|
||||
r($action->computeBeginAndEndTest($typeList[1])) && p() && e('1'); // 测试计算today的日期
|
||||
r($action->computeBeginAndEndTest($typeList[2])) && p() && e('1'); // 测试计算yesterday的日期
|
||||
r($action->computeBeginAndEndTest($typeList[3])) && p() && e('1'); // 测试计算twodaysago的日期
|
||||
r($action->computeBeginAndEndTest($typeList[4])) && p() && e('1'); // 测试计算latest3days的日期
|
||||
r($action->computeBeginAndEndTest($typeList[5])) && p() && e('1'); // 测试计算thisweek的日期
|
||||
r($action->computeBeginAndEndTest($typeList[6])) && p() && e('1'); // 测试计算lastweek的日期
|
||||
r($action->computeBeginAndEndTest($typeList[7])) && p() && e('1'); // 测试计算thismonth的日期
|
||||
r($action->computeBeginAndEndTest($typeList[8])) && p() && e('1'); // 测试计算lastmonth的日期
|
||||
r($action->computeBeginAndEndTest($typeList[0], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算all的日期
|
||||
r($action->computeBeginAndEndTest($typeList[1], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算today的日期
|
||||
r($action->computeBeginAndEndTest($typeList[2], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算yesterday的日期
|
||||
r($action->computeBeginAndEndTest($typeList[3], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算twodaysago的日期
|
||||
r($action->computeBeginAndEndTest($typeList[4], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算latest3days的日期
|
||||
r($action->computeBeginAndEndTest($typeList[5], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算thisweek的日期
|
||||
r($action->computeBeginAndEndTest($typeList[6], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算lastweek的日期
|
||||
r($action->computeBeginAndEndTest($typeList[7], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算thismonth的日期
|
||||
r($action->computeBeginAndEndTest($typeList[8], $dateList[0], $directionList[0])) && p() && e('1'); // 测试计算lastmonth的日期
|
||||
r($action->computeBeginAndEndTest($typeList[0], $dateList[1], $directionList[0])) && p() && e('1'); // 测试返回具体的日期
|
||||
r($action->computeBeginAndEndTest($typeList[0], $dateList[1], $directionList[1])) && p() && e('1'); // 测试返回具体日期的结束日期
|
||||
r($action->computeBeginAndEndTest($typeList[0], $dateList[1], $directionList[2])) && p() && e('1'); // 测试返回具体日期的开始日期
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('actionrecent')->gen(0);
|
||||
zenData('action')->gen(0);
|
||||
zenData('actionrecent')->gen(0);
|
||||
zenData('actionproduct')->gen(0);
|
||||
zenData('file')->gen(0);
|
||||
zenData('task')->gen(1);
|
||||
zenData('story')->gen(1);
|
||||
@@ -54,8 +55,10 @@ cid=1
|
||||
- 属性objectID @1
|
||||
- 属性actor @系统
|
||||
- 属性action @synctwins
|
||||
- 测试升级中的并且版本号小于18.7的情况,不创建actionrecent @8
|
||||
- 测试升级中的并且版本号大于18.7的情况,创建actionrecent @9
|
||||
- 测试升级中的并且版本号小于18.7的情况,不创建actionrecent @7
|
||||
- 测试升级中的并且版本号小于21.7.2的情况,不创建actionproduct @7
|
||||
- 测试升级中的并且版本号大于18.7的情况,创建actionrecent @8
|
||||
- 测试升级中的并且版本号小于21.7.2的情况,创建actionproduct @8
|
||||
|
||||
*/
|
||||
$objectTypeList = array('task', 'project', 'user', 'bug', 'story');
|
||||
@@ -66,7 +69,7 @@ $actor = 'guest';
|
||||
$comment = array('', '测试备注', '<p><img src="ccreate?m=file&f=read&t=jpeg&fileID=1"></p>');
|
||||
$uid = array('', uniqid());
|
||||
$autoDelete = array(true, false);
|
||||
$versionList = array('18.1', '18.7');
|
||||
$versionList = array('18.1', '18.7', '21.7.2');
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
@@ -86,13 +89,17 @@ r($action->createTest($objectTypeList[4], $objectIDList[0], $storyActionTypeList
|
||||
|
||||
global $tester;
|
||||
$tester->app->upgrading = true;
|
||||
$version = $tester->dao->select('value')->from(TABLE_CONFIG)->where('`key`')->eq('version')->andWhere('owner')->eq('system')->andWhere('module')->eq('common')->fetch('value');
|
||||
$version = $tester->dao->select('value')->from(TABLE_CONFIG)->where('`key`')->eq('version')->andWhere('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('global')->fetch('value');
|
||||
|
||||
$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
|
||||
r($tester->dao->select('COUNT(1) AS count')->from('zt_actionrecent')->fetch('count')) && p() && e('7'); //测试升级中的并且版本号小于18.7的情况,不创建actionrecent
|
||||
r($tester->dao->select('COUNT(1) AS count')->from('zt_actionproduct')->fetch('count')) && p() && e('7'); //测试升级中的并且版本号小于21.7.2的情况,不创建actionproduct
|
||||
|
||||
$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
|
||||
r($tester->dao->select('COUNT(1) AS count')->from('zt_actionrecent')->fetch('count')) && p() && e('8'); //测试升级中的并且版本号大于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;
|
||||
$action->createTest($objectTypeList[0], $objectIDList[0], $storyActionTypeList[0], $comment[0], '', '', '', $versionList[2]);
|
||||
r($tester->dao->select('COUNT(1) AS count')->from('zt_actionproduct')->fetch('count')) && p() && e('8'); //测试升级中的并且版本号小于21.7.2的情况,创建actionproduct
|
||||
|
||||
$tester->dao->update(TABLE_CONFIG)->set('value')->eq($version)->where('`key`')->eq('version')->andWhere('owner')->eq('system')->andWhere('module')->eq('common')->andWhere('section')->eq('global')->exec();
|
||||
$tester->app->upgrading = false;
|
||||
@@ -10,10 +10,31 @@ title=测试 actionModel->getActionCondition();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 获取用户可以看到的动态的条件 @0
|
||||
- 超级管理员的动态 @0
|
||||
- 没有设置动态权限 @0
|
||||
- 没有任何动态权限 @1 != 1
|
||||
- 没有模块动态权限 @1 != 1
|
||||
- 所有条件动态权限 @(`objectType` = 'bug' AND `action` IN ('create','edit')) OR (`objectType` = 'product' AND `action` IN ('create','edit'))
|
||||
- Bug模块动态权限 @(`objectType` = 'bug' AND `action` IN ('create','edit'))
|
||||
|
||||
*/
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
r($action->getActionConditionTest()) && p() && e('0'); // 获取用户可以看到的动态的条件
|
||||
global $app;
|
||||
|
||||
$app->user->admin = true;
|
||||
r($action->getActionConditionTest()) && p() && e('0'); // 超级管理员的动态
|
||||
|
||||
$app->user->admin = false;
|
||||
unset($app->user->rights['acls']);
|
||||
r($action->getActionConditionTest()) && p() && e('0'); // 没有设置动态权限
|
||||
|
||||
$app->user->rights['acls']['actions'] = array();
|
||||
r($action->getActionConditionTest()) && p() && e('1 != 1'); // 没有任何动态权限
|
||||
|
||||
$app->user->rights['acls']['actions'] = array('bug' => array('create' => '1', 'edit' => '1'), 'product' => array('create' => '1', 'edit' => 1));
|
||||
r($action->getActionConditionTest('project')) && p() && e('1 != 1'); // 没有模块动态权限
|
||||
|
||||
r($action->getActionConditionTest()) && p() && e("(`objectType` = 'bug' AND `action` IN ('create','edit')) OR (`objectType` = 'product' AND `action` IN ('create','edit'))"); // 所有条件动态权限
|
||||
r($action->getActionConditionTest('bug')) && p() && e("(`objectType` = 'bug' AND `action` IN ('create','edit'))"); // Bug模块动态权限
|
||||
@@ -5,8 +5,9 @@ include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35);
|
||||
zenData('doclib')->loadYaml('doclib')->gen(15);
|
||||
zenData('doc')->loadYaml('doc')->gen(5);
|
||||
zenData('actionproduct')->loadYaml('actionproduct')->gen(35);
|
||||
zenData('doclib')->gen(15);
|
||||
zenData('doc')->gen(15);
|
||||
zenData('product')->gen(5);
|
||||
zenData('project')->loadYaml('execution')->gen(12);
|
||||
zenData('user')->loadYaml('user')->gen(3);
|
||||
@@ -18,16 +19,16 @@ title=测试 actionModel->getDynamic();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 查找全部动态 @36
|
||||
- 查找用户admin动态 @13
|
||||
- 查找全部动态 @35
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @1
|
||||
- 查找上周的动态 @7
|
||||
- 查找产品1的动态 @1
|
||||
- 查找产品2的动态 @1
|
||||
- 查找产品3的动态 @1
|
||||
- 查找产品1的动态 @7
|
||||
- 查找产品2的动态 @7
|
||||
- 查找产品3的动态 @7
|
||||
- 查找项目1的动态 @1
|
||||
- 查找项目2的动态 @1
|
||||
- 查找项目3的动态 @1
|
||||
@@ -36,15 +37,15 @@ cid=1
|
||||
- 查找执行3的动态 @0
|
||||
- 查找今天的动态 @0
|
||||
- 查找全部动态 @35
|
||||
- 查找用户admin动态 @11
|
||||
- 查找用户dev17动态 @13
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @5
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @0
|
||||
- 查找产品1的动态 @1
|
||||
- 查找产品2的动态 @1
|
||||
- 查找产品3的动态 @1
|
||||
- 查找产品1的动态 @7
|
||||
- 查找产品2的动态 @7
|
||||
- 查找产品3的动态 @7
|
||||
- 查找项目1的动态 @1
|
||||
- 查找项目2的动态 @1
|
||||
- 查找项目3的动态 @1
|
||||
@@ -55,6 +56,11 @@ cid=1
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('action');
|
||||
$tester->action->lang->SRCommon = '研发需求';
|
||||
$tester->action->lang->URCommon = '用户需求';
|
||||
|
||||
$accountList = array('all', 'admin', 'dev17', 'test18');
|
||||
$typeList = array('all', 'today', 'yesterday', 'lastweek');
|
||||
$productIDList = array('all', 1, 2, 3);
|
||||
@@ -64,9 +70,11 @@ $dateList = array('', 'today');
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
global $app;
|
||||
su('admin');
|
||||
r($action->getDynamicTest()) && p() && e('36'); // 查找全部动态
|
||||
r($action->getDynamicTest($accountList[1])) && p() && e('13'); // 查找用户admin动态
|
||||
$app->user->rights['acls'] = array();
|
||||
r($action->getDynamicTest()) && p() && e('35'); // 查找全部动态
|
||||
r($action->getDynamicTest($accountList[1])) && p() && e('12'); // 查找用户admin动态
|
||||
r($action->getDynamicTest($accountList[2])) && p() && e('12'); // 查找用户dev17动态
|
||||
r($action->getDynamicTest($accountList[3])) && p() && e('11'); // 查找用户test18动态
|
||||
|
||||
@@ -78,23 +86,24 @@ r($action->getDynamicTest($accountList[0], $typeList[3]))
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[1])) && p() && e('1'); // 查找产品1的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[2])) && p() && e('1'); // 查找产品2的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[3])) && p() && e('1'); // 查找产品3的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[1])) && p() && e('7'); // 查找产品1的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[2])) && p() && e('7'); // 查找产品2的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[3])) && p() && e('7'); // 查找产品3的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[1])) && p() && e('1'); // 查找项目1的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[2])) && p() && e('1'); // 查找项目2的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[3])) && p() && e('1'); // 查找项目3的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[1])) && p() && e('0'); // 查找执行1的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[2])) && p() && e('0'); // 查找执行2的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[3])) && p() && e('0'); // 查找执行3的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[0], $dateList[1])) && p() && e('0'); // 查找今天的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[0], $dateList[1])) && p() && e('0'); // 查找今天的动态
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
|
||||
su('dev17');
|
||||
$app->user->rights['acls'] = array();
|
||||
r($action->getDynamicTest()) && p() && e('35'); // 查找全部动态
|
||||
r($action->getDynamicTest($accountList[1])) && p() && e('11'); // 查找用户admin动态
|
||||
r($action->getDynamicTest($accountList[2])) && p() && e('13'); // 查找用户dev17动态
|
||||
r($action->getDynamicTest($accountList[1])) && p() && e('12'); // 查找用户admin动态
|
||||
r($action->getDynamicTest($accountList[2])) && p() && e('12'); // 查找用户dev17动态
|
||||
r($action->getDynamicTest($accountList[3])) && p() && e('11'); // 查找用户test18动态
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
@@ -102,13 +111,13 @@ zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
|
||||
r($action->getDynamicTest($accountList[0], $typeList[1])) && p() && e('5'); // 查找今天的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[3])) && p() && e('0'); // 查找上周的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[1])) && p() && e('1'); // 查找产品1的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[2])) && p() && e('1'); // 查找产品2的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[3])) && p() && e('1'); // 查找产品3的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[1])) && p() && e('7'); // 查找产品1的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[2])) && p() && e('7'); // 查找产品2的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[3])) && p() && e('7'); // 查找产品3的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[1])) && p() && e('1'); // 查找项目1的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[2])) && p() && e('1'); // 查找项目2的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[3])) && p() && e('1'); // 查找项目3的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[1])) && p() && e('0'); // 查找执行1的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[2])) && p() && e('0'); // 查找执行2的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[3])) && p() && e('0'); // 查找执行3的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[0], $dateList[1])) && p() && e('0'); // 查找今天的动态
|
||||
r($action->getDynamicTest($accountList[0], $typeList[0], $productIDList[0], $projectIDList[0], $executionIDList[0], $dateList[1])) && p() && e('0'); // 查找今天的动态
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35);
|
||||
zenData('doclib')->gen(15);
|
||||
zenData('doc')->gen(15);
|
||||
zenData('product')->gen(5);
|
||||
zenData('project')->loadYaml('execution')->gen(12);
|
||||
zenData('user')->loadYaml('user')->gen(3);
|
||||
zenData('userview')->loadYaml('userview')->gen(2);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->getDynamicByAccount();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @2
|
||||
- 查找今天的动态 @0
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @2
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @0
|
||||
- 查找今天的动态 @0
|
||||
|
||||
*/
|
||||
|
||||
$accountList = array('', 'admin', 'dev17', 'test18');
|
||||
$typeList = array('all', 'today', 'yesterday', 'lastweek');
|
||||
$dateList = array('', 'today');
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
su('admin');
|
||||
r($action->getDynamicByAccountTest($accountList[0])) && p() && e('12'); // 查找用户admin动态
|
||||
r($action->getDynamicByAccountTest($accountList[1])) && p() && e('12'); // 查找用户admin动态
|
||||
r($action->getDynamicByAccountTest($accountList[2])) && p() && e('12'); // 查找用户dev17动态
|
||||
r($action->getDynamicByAccountTest($accountList[3])) && p() && e('11'); // 查找用户test18动态
|
||||
|
||||
zenData('action')->loadYaml('action_week')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_week')->gen(35, true, false);
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
|
||||
r($action->getDynamicByAccountTest($accountList[0], $typeList[0], $dateList[1])) && p() && e('0'); // 查找今天的动态
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
|
||||
|
||||
su('dev17');
|
||||
r($action->getDynamicByAccountTest($accountList[1])) && p() && e('12'); // 查找用户admin动态
|
||||
r($action->getDynamicByAccountTest($accountList[2])) && p() && e('12'); // 查找用户dev17动态
|
||||
r($action->getDynamicByAccountTest($accountList[3])) && p() && e('11'); // 查找用户test18动态
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
|
||||
r($action->getDynamicByAccountTest($accountList[0], $typeList[1])) && p() && e('2'); // 查找今天的动态
|
||||
r($action->getDynamicByAccountTest($accountList[0], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($action->getDynamicByAccountTest($accountList[0], $typeList[3])) && p() && e('0'); // 查找上周的动态
|
||||
r($action->getDynamicByAccountTest($accountList[0], $typeList[0], $dateList[1])) && p() && e('0'); // 查找今天的动态
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
$action = zenData('action')->loadYaml('action_year');
|
||||
$action->execution->range('101');
|
||||
$action->gen(35);
|
||||
$actionrecent = zenData('actionrecent')->loadYaml('action_year');
|
||||
$actionrecent->execution->range('101');
|
||||
$actionrecent->gen(35);
|
||||
|
||||
zenData('doclib')->gen(15);
|
||||
zenData('doc')->gen(15);
|
||||
zenData('product')->gen(5);
|
||||
zenData('project')->loadYaml('execution')->gen(12);
|
||||
zenData('user')->loadYaml('user')->gen(3);
|
||||
zenData('userview')->loadYaml('userview')->gen(2);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->getDynamicByExecution();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 查找所有用户动态 @35
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @2
|
||||
- 查找今天的动态 @5
|
||||
|
||||
*/
|
||||
|
||||
$executionID = '101';
|
||||
$accountList = array('all', 'admin', 'dev17', 'test18');
|
||||
$typeList = array('all', 'today', 'yesterday', 'lastweek');
|
||||
$dateList = array('', 'today');
|
||||
|
||||
$actionTest = new actionTest();
|
||||
|
||||
su('admin');
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[0])) && p() && e('35'); // 查找所有用户动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1])) && p() && e('12'); // 查找用户admin动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[2])) && p() && e('12'); // 查找用户dev17动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[3])) && p() && e('11'); // 查找用户test18动态
|
||||
|
||||
$action = zenData('action')->loadYaml('action_week');
|
||||
$action->execution->range('101');
|
||||
$action->gen(35, true, false);
|
||||
$actionrecent = zenData('actionrecent')->loadYaml('action_week');
|
||||
$actionrecent->execution->range('101');
|
||||
$actionrecent->gen(35, true, false);
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
|
||||
|
||||
$action = zenData('action')->loadYaml('action_year');
|
||||
$action->execution->range('101');
|
||||
$action->gen(35, true, false);
|
||||
$actionrecent = zenData('actionrecent')->loadYaml('action_year');
|
||||
$actionrecent->execution->range('101');
|
||||
$actionrecent->gen(35, true, false);
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[0], $typeList[1])) && p() && e('5'); // 查找今天的动态
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35);
|
||||
zenData('actionproduct')->loadYaml('actionproduct')->gen(35);
|
||||
zenData('doclib')->loadYaml('doclib')->gen(15);
|
||||
zenData('doc')->loadYaml('doc')->gen(5);
|
||||
zenData('product')->gen(5);
|
||||
zenData('project')->loadYaml('execution')->gen(12);
|
||||
zenData('user')->loadYaml('user')->gen(3);
|
||||
zenData('userview')->loadYaml('userview')->gen(2);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->getDynamicByProduct();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 查找所有用户动态 @7
|
||||
- 查找用户admin动态 @3
|
||||
- 查找用户dev17动态 @2
|
||||
- 查找用户test18动态 @2
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @0
|
||||
- 查找今天的动态 @1
|
||||
|
||||
*/
|
||||
|
||||
$productID = '1';
|
||||
$accountList = array('all', 'admin', 'dev17', 'test18');
|
||||
$typeList = array('all', 'today', 'yesterday', 'lastweek');
|
||||
$dateList = array('', 'today');
|
||||
|
||||
$actionTest = new actionTest();
|
||||
|
||||
su('admin');
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[0])) && p() && e('7'); // 查找所有用户动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1])) && p() && e('3'); // 查找用户admin动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[2])) && p() && e('2'); // 查找用户dev17动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[3])) && p() && e('2'); // 查找用户test18动态
|
||||
|
||||
zenData('action')->loadYaml('action_week')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_week')->gen(35, true, false);
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[3])) && p() && e('0'); // 查找上周的动态
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[0], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
$action = zenData('action')->loadYaml('action_year');
|
||||
$action->project->range('101');
|
||||
$action->gen(35);
|
||||
$actionrecent = zenData('actionrecent')->loadYaml('action_year');
|
||||
$actionrecent->project->range('101');
|
||||
$actionrecent->gen(35);
|
||||
|
||||
zenData('doclib')->gen(15);
|
||||
zenData('doc')->gen(15);
|
||||
zenData('product')->gen(5);
|
||||
zenData('project')->loadYaml('execution')->gen(12);
|
||||
zenData('user')->loadYaml('user')->gen(3);
|
||||
zenData('userview')->loadYaml('userview')->gen(2);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->getDynamicByProject();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 查找所有用户动态 @35
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @2
|
||||
- 查找今天的动态 @5
|
||||
|
||||
*/
|
||||
|
||||
$projectID = '101';
|
||||
$accountList = array('all', 'admin', 'dev17', 'test18');
|
||||
$typeList = array('all', 'today', 'yesterday', 'lastweek');
|
||||
$dateList = array('', 'today');
|
||||
|
||||
$actionTest = new actionTest();
|
||||
|
||||
su('admin');
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[0])) && p() && e('35'); // 查找所有用户动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1])) && p() && e('12'); // 查找用户admin动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[2])) && p() && e('12'); // 查找用户dev17动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[3])) && p() && e('11'); // 查找用户test18动态
|
||||
|
||||
$action = zenData('action')->loadYaml('action_week');
|
||||
$action->project->range('101');
|
||||
$action->gen(35, true, false);
|
||||
$actionrecent = zenData('actionrecent')->loadYaml('action_week');
|
||||
$actionrecent->project->range('101');
|
||||
$actionrecent->gen(35, true, false);
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
|
||||
|
||||
$action = zenData('action')->loadYaml('action_year');
|
||||
$action->project->range('101');
|
||||
$action->gen(35, true, false);
|
||||
$actionrecent = zenData('actionrecent')->loadYaml('action_year');
|
||||
$actionrecent->project->range('101');
|
||||
$actionrecent->gen(35, true, false);
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[0], $typeList[1])) && p() && e('5'); // 查找今天的动态
|
||||
@@ -3,6 +3,15 @@
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(90);
|
||||
zenData('actionrecent')->gen(0);
|
||||
zenData('actionproduct')->loadYaml('actionproduct')->gen(90);
|
||||
zenData('doclib')->gen(1);
|
||||
zenData('doc')->gen(1);
|
||||
zenData('lang')->gen(0);
|
||||
zenData('product')->gen(1);
|
||||
zenData('userquery')->loadYaml('userquery')->gen(1);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->getDynamicBySearch();
|
||||
@@ -10,38 +19,31 @@ timeout=0
|
||||
cid=1
|
||||
|
||||
- 获取排序为date倒序的所有动态
|
||||
- 第0条的id属性 @64
|
||||
- 第0条的objectType属性 @testsuite
|
||||
- 第1条的id属性 @32
|
||||
- 第1条的objectType属性 @testcase
|
||||
- 获取排序为date正序的所有动态
|
||||
- 第0条的id属性 @65
|
||||
- 第0条的objectType属性 @caselib
|
||||
- 第1条的id属性 @33
|
||||
- 第1条的objectType属性 @case
|
||||
- 获取排序为date倒序的今年之后的动态
|
||||
- 第0条的id属性 @64
|
||||
- 第0条的objectType属性 @testsuite
|
||||
- 第1条的id属性 @32
|
||||
- 第1条的objectType属性 @testcase
|
||||
- 获取排序为date倒序的今天之前的动态
|
||||
- 第0条的id属性 @63
|
||||
- 第0条的objectType属性 @module
|
||||
- 第0条的id属性 @62
|
||||
- 第0条的objectType属性 @branch
|
||||
- 第1条的id属性 @31
|
||||
- 第1条的objectType属性 @bug
|
||||
- 获取排序为date正序的所有动态
|
||||
- 第0条的id属性 @63
|
||||
- 第0条的objectType属性 @module
|
||||
- 第1条的id属性 @32
|
||||
- 第1条的objectType属性 @testcase
|
||||
- 获取排序为date倒序的今年之后的动态
|
||||
- 第0条的id属性 @1
|
||||
- 第0条的objectType属性 @product
|
||||
- 第0条的id属性 @62
|
||||
- 第0条的objectType属性 @branch
|
||||
- 第1条的id属性 @31
|
||||
- 第1条的objectType属性 @bug
|
||||
- 获取排序为date正序的所有动态
|
||||
- 第0条的id属性 @61
|
||||
- 第0条的objectType属性 @todo
|
||||
- 第1条的id属性 @30
|
||||
- 第1条的objectType属性 @build
|
||||
- 获取排序为date倒序的今年之后的动态
|
||||
- 第0条的id属性 @31
|
||||
- 第0条的objectType属性 @bug
|
||||
|
||||
*/
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(90);
|
||||
zenData('doclib')->gen(1);
|
||||
zenData('doc')->gen(1);
|
||||
zenData('lang')->gen(0);
|
||||
zenData('product')->gen(1);
|
||||
zenData('userquery')->loadYaml('userquery')->gen(1);
|
||||
|
||||
global $lang, $app;
|
||||
$lang->SRCommon = '研发需求';
|
||||
$lang->URCommon = '用户需求';
|
||||
@@ -57,8 +59,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('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倒序的今年之后的动态
|
||||
r(array_values($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(array_values($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(array_values($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(array_values($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(array_values($action->getDynamicBySearchTest($queryID[1], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType') && e('31,bug'); // 获取排序为date倒序的今年之后的动态
|
||||
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(6);
|
||||
zenData('actionrecent')->gen(5);
|
||||
zenData('actionproduct')->gen(0);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->getDynamicCount();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试传入空SESSION @0
|
||||
- 测试传入条件 1=1。 @6
|
||||
- 测试传入条件 actor='admin'。 @2
|
||||
- 检查SQL。 @1
|
||||
- 测试传入条件 actor='admin' AND t2.product='1'。 @0
|
||||
- 检查SQL。 @1
|
||||
- 测试传入条件 1=1。 @0
|
||||
- 检查SQL。 @1
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$actionModel = $tester->loadModel('action');
|
||||
$actionModel->getDynamicCount('all');
|
||||
|
||||
$_SESSION['actionQueryCondition'] = '';
|
||||
r($actionModel->getDynamicCount('all')) && p() && e('0'); // 测试传入空SESSION
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "1=1";
|
||||
r($actionModel->getDynamicCount('all')) && p() && e('6'); // 测试传入条件 1=1。
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "actor='admin'";
|
||||
r($actionModel->getDynamicCount('all')) && p() && e('2'); // 测试传入条件 actor='admin'。
|
||||
|
||||
$sql = $actionModel->dao->get();
|
||||
r(strpos($sql, "LEFT JOIN `zt_actionproduct` AS t2 ON action.id=t2.action") === false) && p() && e('1'); // 检查SQL。
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "actor='admin' AND t2.product='1';";
|
||||
r($actionModel->getDynamicCount('all')) && p() && e('0'); // 测试传入条件 actor='admin' AND t2.product='1'。
|
||||
|
||||
$sql = $actionModel->dao->get();
|
||||
r(strpos($sql, "LEFT JOIN `zt_actionproduct` AS t2 ON action.id=t2.action") !== false) && p() && e('1'); // 检查SQL。
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "1=1";
|
||||
r($actionModel->getDynamicCount('today')) && p() && e('0'); // 测试传入条件 1=1。
|
||||
|
||||
$sql = $actionModel->dao->get();
|
||||
r(strpos($sql, "SELECT action.id FROM `zt_actionrecent` AS action") !== false) && p() && e('1'); // 检查SQL。
|
||||
@@ -5,6 +5,7 @@ include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('action')->gen('10');
|
||||
zenData('actionrecent')->gen('0');
|
||||
zenData('user')->gen(10);
|
||||
|
||||
su('admin');
|
||||
@@ -19,9 +20,15 @@ cid=1
|
||||
- 属性id @1
|
||||
- 属性objectID @1
|
||||
- 属性objectType @product
|
||||
- 属性action @common
|
||||
- 属性extra @1
|
||||
- 没有动态数据 @0
|
||||
|
||||
*/
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
r($action->getFirstActionTest()) && p('id,objectID,objectType') && e('1,1,product'); // 测试获取对象类型 story 对象ID 1 的动态信息
|
||||
r($action->getFirstActionTest()) && p('id,objectID,objectType,action,extra') && e('1,1,product,common,1'); // 测试获取对象类型 story 对象ID 1 的动态信息
|
||||
|
||||
$action->objectModel->dao->delete()->from(TABLE_ACTION)->exec();
|
||||
r($action->getFirstActionTest()) && p() && e('0'); // 没有动态数据
|
||||
@@ -4,9 +4,9 @@
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
su('admin');
|
||||
|
||||
zenData('project')->gen(5);
|
||||
zenData('actionrecent')->gen(0);
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(99);
|
||||
zenData('actionrecent')->gen(0);
|
||||
zenData('project')->gen(20, true, false);
|
||||
zenData('project')->loadYaml('execution')->gen(90, false, false);
|
||||
zenData('product')->loadYaml('product')->gen(50, true, false);
|
||||
@@ -106,10 +107,6 @@ cid=1
|
||||
- 测试获取对象类型 testtask 对象ID 1 的动态信息 @link
|
||||
- 测试获取对象类型 testtask 对象ID 2 的动态信息 @link
|
||||
- 测试获取对象类型 testtask 对象ID 3 的动态信息 @link
|
||||
- 测试获取对象类型 task 对象ID 12 的动态信息 @nochanged
|
||||
- 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息 @nochanged
|
||||
- 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息 @nochanged
|
||||
- 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息 @nochanged
|
||||
- 测试获取对象类型 task 对象ID 12 的动态信息 @link
|
||||
- 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息 @link
|
||||
- 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息 @link
|
||||
@@ -183,10 +180,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 +282,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 的动态信息
|
||||
@@ -328,29 +325,16 @@ r($action->getListTest($objectType[9], $testtaskID[2])) && p() && e('link'); //
|
||||
|
||||
// 开源版 导入资产库相关
|
||||
// 操作 importfromstorylib
|
||||
r($action->getListTest($objectType[1], $taskID[11])) && p() && e('nochanged'); // 测试获取对象类型 task 对象ID 12 的动态信息
|
||||
r($action->getListTest($objectType[1], $taskID[11])) && p() && e('link'); // 测试获取对象类型 task 对象ID 12 的动态信息
|
||||
|
||||
// 操作 importfromrisklib
|
||||
r($action->getListTest($objectType[10], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息
|
||||
r($action->getListTest($objectType[10], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息
|
||||
|
||||
// 操作 importfromissuelib
|
||||
r($action->getListTest($objectType[11], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息
|
||||
r($action->getListTest($objectType[11], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息
|
||||
|
||||
// 操作 importfromopportunitylib
|
||||
r($action->getListTest($objectType[12], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息
|
||||
|
||||
// 旗舰版 导入资产库相关
|
||||
// 操作 importfromstorylib
|
||||
r($action->getListTest($objectType[1], $taskID[11], 'max')) && p() && e('link'); // 测试获取对象类型 task 对象ID 12 的动态信息
|
||||
|
||||
// 操作 importfromrisklib
|
||||
r($action->getListTest($objectType[10], $hasOneID[0], 'max')) && p() && e('link'); // 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息
|
||||
|
||||
// 操作 importfromissuelib
|
||||
r($action->getListTest($objectType[11], $hasOneID[0], 'max')) && p() && e('link'); // 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息
|
||||
|
||||
// 操作 importfromopportunitylib
|
||||
r($action->getListTest($objectType[12], $hasOneID[0], 'max')) && p() && e('link'); // 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息
|
||||
r($action->getListTest($objectType[12], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息
|
||||
|
||||
// 操作 opened 对象类型 execution
|
||||
r($action->getListTest($objectType[13], $executionID[0])) && p() && e('link'); // 测试获取对象类型 execution 对象ID 1 的动态信息
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(6);
|
||||
zenData('actionrecent')->gen(5);
|
||||
zenData('actionproduct')->gen(0);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->getMoreActions();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试传入空SESSION @0
|
||||
- 测试传入条件 actor='admin'。 @0
|
||||
- 测试传入条件 actor='admin' AND t2.product='1'。 @0
|
||||
- 测试传入条件 1=1。 @2
|
||||
- 测试传入条件 actor='admin'。 @1
|
||||
- 测试传入条件 actor='admin' AND t2.product='1'。 @0
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$actionModel = $tester->loadModel('action');
|
||||
|
||||
$actionID = 3;
|
||||
|
||||
$_SESSION['actionOrderBy'] = '`date` desc';
|
||||
$_SESSION['actionQueryCondition'] = '';
|
||||
r(count($actionModel->getMoreActions($actionID))) && p() && e('0'); // 测试传入空SESSION
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "actor='admin'";
|
||||
r(count($actionModel->getMoreActions($actionID))) && p() && e('0'); // 测试传入条件 actor='admin'。
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "actor='admin' AND t2.product='1';";
|
||||
r(count($actionModel->getMoreActions($actionID))) && p() && e('0'); // 测试传入条件 actor='admin' AND t2.product='1'。
|
||||
|
||||
$actionModel->dao->update(TABLE_ACTION)->set("`date` = concat('2025-05-19 15:15:5', id)")->exec();
|
||||
$_SESSION['actionQueryCondition'] = "1=1";
|
||||
r(count($actionModel->getMoreActions($actionID))) && p() && e('2'); // 测试传入条件 1=1。
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "actor='admin'";
|
||||
r(count($actionModel->getMoreActions($actionID))) && p() && e('1'); // 测试传入条件 actor='admin'。
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "actor='admin' AND t2.product='1';";
|
||||
r(count($actionModel->getMoreActions($actionID))) && p() && e('0'); // 测试传入条件 actor='admin' AND t2.product='1'。
|
||||
@@ -2,6 +2,8 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('actionrecent')->gen(0);
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
@@ -11,12 +13,12 @@ timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试获取objectType product objectId 1的动态信息 @产品|product|view|productID=%s
|
||||
- 测试获取objectType story objectId 2的动态信息 @研发需求|story|view|storyID=%s
|
||||
- 测试获取objectType story objectId 2的动态信息 @研发需求|story|storyView|storyID=%s
|
||||
- 测试获取objectType productplan objectId 3的动态信息 @计划|productplan|view|productID=%s
|
||||
- 测试获取objectType release objectId 4的动态信息 @发布|release|view|productID=%s
|
||||
- 测试获取objectType project objectId 5的动态信息 @项目|project|index|projectID=%s
|
||||
- 测试获取objectType task objectId 6的动态信息 @任务|task|view|taskID=%s
|
||||
- 测试获取objectType build objectId 7的动态信息 @版本|build|view|buildID=%s
|
||||
- 测试获取objectType build objectId 7的动态信息 @构建|build|view|buildID=%s
|
||||
- 测试获取objectType bug objectId 8的动态信息 @Bug|bug|view|bugID=%s
|
||||
- 测试获取objectType testcase objectId 9的动态信息 @testcase
|
||||
- 测试获取objectType case objectId 10的动态信息 @用例|testcase|view|caseID=%s
|
||||
@@ -32,7 +34,8 @@ cid=1
|
||||
- 测试获取objectType entry objectId 21的动态信息 @应用|entry|browse|
|
||||
- 测试获取objectType webhook objectId 22的动态信息 @Webhook|webhook|browse|
|
||||
- 测试获取objectType review objectId 23的动态信息 @review
|
||||
- 测试获取objectType story objectId 25的动态信息 @用户需求|story|view|storyID=%s
|
||||
- 测试获取objectType story objectId 25的动态信息 @用户需求|story|storyView|storyID=%s
|
||||
- 测试获取objectType story objectId 26的动态信息 @业务需求|story|storyView|storyID=%s
|
||||
|
||||
*/
|
||||
|
||||
@@ -57,7 +60,7 @@ r($action->getObjectLabelTest($objectType[2], $objectId[2], $actionType[2], $
|
||||
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[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的动态信息
|
||||
@@ -72,6 +75,6 @@ r($action->getObjectLabelTest($objectType[18], $objectId[18], $actionType[18], $
|
||||
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'); // 测试获取objectType review objectId 23的动态信息
|
||||
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的动态信息
|
||||
|
||||
@@ -6,6 +6,7 @@ su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(20);
|
||||
zenData('testreport')->loadYaml('testreport')->gen(1);
|
||||
zenData('case')->gen(10);
|
||||
zenData('doc')->gen(1);
|
||||
zenData('story')->gen(1);
|
||||
zenData('product')->gen(1);
|
||||
|
||||
@@ -8,6 +8,7 @@ include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(20);
|
||||
zenData('actionrecent')->gen(0);
|
||||
zenData('userquery')->loadYaml('userquery')->gen(2);
|
||||
zenData('story')->gen(10);
|
||||
zenData('pipeline')->gen(5);
|
||||
|
||||
@@ -5,6 +5,7 @@ include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(20);
|
||||
zenData('actionrecent')->gen(0);
|
||||
|
||||
/**
|
||||
|
||||
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(6);
|
||||
zenData('actionrecent')->gen(5);
|
||||
zenData('actionproduct')->gen(0);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->hasMoreAction();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试传入空SESSION @0
|
||||
- 测试传入条件 actor='admin'。 @0
|
||||
- 检查SQL。 @1
|
||||
- 测试传入条件 actor='admin' AND t2.product='1'。 @0
|
||||
- 检查SQL。 @1
|
||||
- 测试传入条件 1=1。 @1
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$actionModel = $tester->loadModel('action');
|
||||
|
||||
$lastAction = new stdclass();
|
||||
$lastAction->id = 3;
|
||||
$lastAction->originalDate = '2025-05-19 15:15:53';
|
||||
|
||||
$_SESSION['actionOrderBy'] = '`date` desc';
|
||||
$_SESSION['actionQueryCondition'] = '';
|
||||
r($actionModel->hasMoreAction($lastAction)) && p() && e('0'); // 测试传入空SESSION
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "actor='admin'";
|
||||
r($actionModel->hasMoreAction($lastAction)) && p() && e('0'); // 测试传入条件 actor='admin'。
|
||||
|
||||
$sql = $actionModel->dao->get();
|
||||
r(strpos($sql, "LEFT JOIN `zt_actionproduct` AS t2 ON action.id=t2.action") === false) && p() && e('1'); // 检查SQL。
|
||||
|
||||
$_SESSION['actionQueryCondition'] = "actor='admin' AND t2.product='1';";
|
||||
r($actionModel->hasMoreAction($lastAction)) && p() && e('0'); // 测试传入条件 actor='admin' AND t2.product='1'。
|
||||
|
||||
$sql = $actionModel->dao->get();
|
||||
r(strpos($sql, "LEFT JOIN `zt_actionproduct` AS t2 ON action.id=t2.action") !== false) && p() && e('1'); // 检查SQL。
|
||||
|
||||
$actionModel->dao->update(TABLE_ACTION)->set("`date` = concat('2025-05-19 15:15:5', id)")->exec();
|
||||
$_SESSION['actionQueryCondition'] = "1=1";
|
||||
r($actionModel->hasMoreAction($lastAction)) && p() && e('1'); // 测试传入条件 1=1。
|
||||
@@ -2,9 +2,11 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(6);
|
||||
zenData('actionrecent')->gen(0);
|
||||
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(3);
|
||||
zenData('actionrecent')->gen(0);
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,13 +13,21 @@ title=测试 actionModel->hideAll();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 隐藏回收站全部信息
|
||||
- 隐藏回收站全部信息,并获取隐藏的产品信息
|
||||
- 第0条的id属性 @1
|
||||
- 第0条的extra属性 @2
|
||||
- 隐藏回收站全部信息,并获取隐藏的需求信息
|
||||
- 第1条的id属性 @2
|
||||
- 第1条的extra属性 @2
|
||||
- 隐藏回收站全部信息,并获取隐藏的计划信息
|
||||
- 第2条的id属性 @3
|
||||
- 第2条的extra属性 @2
|
||||
|
||||
*/
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
r($action->hideAllTest()) && p('0:extra;1:extra;2:extra') && e('2;2;2'); // 隐藏回收站全部信息
|
||||
$result = $action->hideAllTest();
|
||||
r($result) && p('0:id,extra') && e('1,2'); // 隐藏回收站全部信息,并获取隐藏的产品信息
|
||||
r($result) && p('1:id,extra') && e('2,2'); // 隐藏回收站全部信息,并获取隐藏的需求信息
|
||||
r($result) && p('2:id,extra') && e('3,2'); // 隐藏回收站全部信息,并获取隐藏的计划信息
|
||||
|
||||
@@ -5,6 +5,7 @@ include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(2);
|
||||
zenData('actionrecent')->gen(0);
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +14,10 @@ timeout=0
|
||||
cid=1
|
||||
|
||||
- 隐藏action1属性extra @2
|
||||
- 隐藏action2属性extra @1
|
||||
- 隐藏action2
|
||||
- 属性id @2
|
||||
- 属性action @created
|
||||
- 属性extra @1
|
||||
- 检查是否有action1的删除数据属性objectID @1
|
||||
- 检查是否有action2的删除数据 @0
|
||||
|
||||
@@ -25,7 +29,7 @@ $deleteAction2SuccessActionID = 4;
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
r($action->hideOneTest($actionIDList[0])) && p('extra') && e('2'); //隐藏action1
|
||||
r($action->hideOneTest($actionIDList[1])) && p('extra') && e('1'); //隐藏action2
|
||||
r($action->getByIdTest($deleteAction1SuccessActionID)) && p('objectID') && e('1'); //检查是否有action1的删除数据
|
||||
r($action->getByIdTest($deleteAction2SuccessActionID)) && p('') && e('0'); //检查是否有action2的删除数据
|
||||
r($action->hideOneTest($actionIDList[0])) && p('extra') && e('2'); //隐藏action1
|
||||
r($action->hideOneTest($actionIDList[1])) && p('id,action,extra') && e('2,created,1'); //隐藏action2
|
||||
r($action->getByIdTest($deleteAction1SuccessActionID)) && p('objectID') && e('1'); //检查是否有action1的删除数据
|
||||
r($action->getByIdTest($deleteAction2SuccessActionID)) && p('') && e('0'); //检查是否有action2的删除数据
|
||||
@@ -2,41 +2,47 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
|
||||
zenData('action')->loadYaml('action')->gen(10);
|
||||
zenData('actionrecent')->gen(0);
|
||||
zenData('history')->gen(0);
|
||||
su('admin');
|
||||
|
||||
zenData('action')->gen(5);
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->logHistory();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试新增actionID 10001 历史记录
|
||||
- 测试新增actionID 1 历史记录
|
||||
- 第0条的field属性 @name
|
||||
- 第0条的old属性 @变更前名称
|
||||
- 第0条的new属性 @变更后名称
|
||||
- 第1条的field属性 @code
|
||||
- 第1条的old属性 @变更前编号
|
||||
- 第1条的new属性 @变更后编号
|
||||
- 测试新增actionID 10002 历史记录
|
||||
- 测试新增actionID 2 历史记录
|
||||
- 第0条的field属性 @assignedTo
|
||||
- 第0条的old属性 @admin
|
||||
- 第0条的new属性 @test1
|
||||
- 测试新增actionID 10003 历史记录
|
||||
- 测试新增actionID 3 历史记录
|
||||
- 第0条的field属性 @name
|
||||
- 第0条的old属性 @name1
|
||||
- 第0条的new属性 @name2
|
||||
- 测试新增actionID 10004 历史记录
|
||||
- 测试新增actionID 4 历史记录
|
||||
- 第0条的field属性 @code
|
||||
- 第0条的old属性 @code1
|
||||
- 第0条的new属性 @code2
|
||||
- 测试新增actionID 10005 历史记录
|
||||
- 测试新增actionID 5 历史记录
|
||||
- 第0条的field属性 @assignedTo
|
||||
- 第0条的old属性 @test2
|
||||
- 第0条的new属性 @test1
|
||||
|
||||
*/
|
||||
|
||||
$actionIDList = array('10001', '10002', '10003', '10004', '10005');
|
||||
$actionIDList = array('1', '2', '3', '4', '5');
|
||||
|
||||
$changes1[0] = array('field' => 'name', 'old' => '变更前名称', 'new' => '变更后名称');
|
||||
$changes1[1] = array('field' => 'code', 'old' => '变更前编号', 'new' => '变更后编号');
|
||||
@@ -47,8 +53,8 @@ $changes5[0] = array('field' => 'assignedTo', 'old' => 'test2', 'new' => 'test1'
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
r($action->logHistoryTest($actionIDList[0], $changes1)) && p('0:field,old,new;1:field,old,new') && e('name,变更前名称,变更后名称;code,变更前编号,变更后编号'); // 测试新增actionID 10001 历史记录
|
||||
r($action->logHistoryTest($actionIDList[1], $changes2)) && p('0:field,old,new') && e('assignedTo,admin,test1'); // 测试新增actionID 10002 历史记录
|
||||
r($action->logHistoryTest($actionIDList[2], $changes3)) && p('0:field,old,new') && e('name,name1,name2'); // 测试新增actionID 10003 历史记录
|
||||
r($action->logHistoryTest($actionIDList[3], $changes4)) && p('0:field,old,new') && e('code,code1,code2'); // 测试新增actionID 10004 历史记录
|
||||
r($action->logHistoryTest($actionIDList[4], $changes5)) && p('0:field,old,new') && e('assignedTo,test2,test1'); // 测试新增actionID 10005 历史记录
|
||||
r($action->logHistoryTest($actionIDList[0], $changes1)) && p('0:field,old,new;1:field,old,new') && e('name,变更前名称,变更后名称;code,变更前编号,变更后编号'); // 测试新增actionID 1 历史记录
|
||||
r($action->logHistoryTest($actionIDList[1], $changes2)) && p('0:field,old,new') && e('assignedTo,admin,test1'); // 测试新增actionID 2 历史记录
|
||||
r($action->logHistoryTest($actionIDList[2], $changes3)) && p('0:field,old,new') && e('name,name1,name2'); // 测试新增actionID 3 历史记录
|
||||
r($action->logHistoryTest($actionIDList[3], $changes4)) && p('0:field,old,new') && e('code,code1,code2'); // 测试新增actionID 4 历史记录
|
||||
r($action->logHistoryTest($actionIDList[4], $changes5)) && p('0:field,old,new') && e('assignedTo,test2,test1'); // 测试新增actionID 5 历史记录
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/action.unittest.class.php';
|
||||
su('admin');
|
||||
|
||||
zenData('action')->gen(10);
|
||||
zenData('actionrecent')->gen(0);
|
||||
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
|
||||
zenData('product')->gen(5);
|
||||
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 actionModel->processExecutionAndProjectActionExtra();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 检查只有1个产品 @1
|
||||
- 检查extra中是否有id=1的产品 @1
|
||||
- 检查extra中是否有id=2的产品 @1
|
||||
- 没有extra属性extra @~~
|
||||
- vision 为lite属性extra @~~
|
||||
|
||||
*/
|
||||
|
||||
global $tester, $config;
|
||||
$actionModel = $tester->loadModel('action');
|
||||
|
||||
$action = new stdclass();
|
||||
$action->objectType = 'execution';
|
||||
$action->objectID = '3';
|
||||
$config->vision = 'rnd';
|
||||
$action->extra = '1';
|
||||
|
||||
$actionModel->processExecutionAndProjectActionExtra($action);
|
||||
r(strpos($action->extra, '#1 正常产品1') !== false) && p() && e('1'); //检查只有1个产品
|
||||
|
||||
$action->extra = '1,2';
|
||||
$actionModel->processExecutionAndProjectActionExtra($action);
|
||||
r(strpos($action->extra, '#1 正常产品1') !== false) && p() && e('1'); //检查extra中是否有id=1的产品
|
||||
r(strpos($action->extra, '#2 正常产品2') !== false) && p() && e('1'); //检查extra中是否有id=2的产品
|
||||
|
||||
$action->extra = '';
|
||||
$actionModel->processExecutionAndProjectActionExtra($action);
|
||||
r($action) && p('extra') && e('~~'); //没有extra
|
||||
|
||||
$config->vision = 'lite';
|
||||
$action->extra = '1,2';
|
||||
$actionModel->processExecutionAndProjectActionExtra($action);
|
||||
r($action) && p('extra') && e('~~'); //vision 为lite
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user