Merge branch ztflow-sunguangming-main#sunguangming of zentao/zentaopms (#9032)
This commit is contained in:
+1
-1
@@ -39,7 +39,7 @@ spec:
|
||||
type: run
|
||||
spec:
|
||||
container:
|
||||
image: hub.zentao.net/ci/zentao-package:master
|
||||
image: hub.zentao.net/ci/zentao-package:master-250527
|
||||
script:
|
||||
- DOWNGRADE_CODE_DIR=$(mktemp -d)
|
||||
- mkdir $DOWNGRADE_CODE_DIR/diff
|
||||
|
||||
@@ -18,7 +18,13 @@ spec:
|
||||
- DOWNGRADE_GIT_REPO=$(jq -r .pkg.downgrade.gitRepo < ci.json)
|
||||
- DOWNGRADE_EXT_GIT_REPO=$(jq -r .pkg.downgradeExt.gitRepo <
|
||||
ci.json)
|
||||
- EXT_BRANCH=$(jq -r .pkg.zentaoext.gitVersion < ci.json)
|
||||
- MAX_BRANCH=$(jq -r .pkg.zentaomax.gitVersion < ci.json)
|
||||
- IPD_BRANCH=$(jq -r .pkg.zentaoipd.gitVersion < ci.json)
|
||||
- echo "XUANVERSION=${XUANVERSION}" >> $GITFOX_CUSTOM_ENV
|
||||
- echo "EXT_BRANCH=${EXT_BRANCH}" >> $GITFOX_CUSTOM_ENV
|
||||
- echo "MAX_BRANCH=${MAX_BRANCH}" >> $GITFOX_CUSTOM_ENV
|
||||
- echo "IPD_BRANCH=${IPD_BRANCH}" >> $GITFOX_CUSTOM_ENV
|
||||
- echo "export ZENTAOEXT_GIT_REPO=${ZENTAOEXT_GIT_REPO}" >>
|
||||
$GITFOX_CUSTOM_ENV
|
||||
- echo "export DOWNGRADE_GIT_REPO=${DOWNGRADE_GIT_REPO}" >>
|
||||
@@ -42,12 +48,12 @@ spec:
|
||||
script:
|
||||
- git clone -q http://${GITFOX_SERVER}/git/xuan/xuanxuan.git
|
||||
xuansrc && cd xuansrc && git checkout $XUANVERSION && cd -
|
||||
- git clone --depth 1 -q
|
||||
- git clone --depth 1 -q -b ${EXT_BRANCH}
|
||||
http://${GITFOX_SERVER}/git/${ZENTAOEXT_GIT_REPO}.git
|
||||
zentaoext
|
||||
- git clone --depth 1
|
||||
- git clone --depth 1 -b ${MAX_BRANCH}
|
||||
http://${GITFOX_SERVER}/git/zentao/zentaomax.git zentaomax
|
||||
- git clone --depth 1
|
||||
- git clone --depth 1 -b ${IPD_BRANCH}
|
||||
http://${GITFOX_SERVER}/git/zentao/zentaoipd.git zentaoipd
|
||||
- name: 版本变量设定
|
||||
type: run
|
||||
@@ -101,15 +107,13 @@ spec:
|
||||
-f misc/ci/Dockerfile.test .
|
||||
- name: unit test
|
||||
type: run
|
||||
when: |
|
||||
build.target == "main"
|
||||
spec:
|
||||
envs:
|
||||
MYSQL_INTERNAL: "true"
|
||||
container:
|
||||
image: hub.qc.oop.cc/zentao-ztf:${{ build.number }}-${{ build.after }}
|
||||
script:
|
||||
- git rev-list $(git rev-parse origin/main )..$(git rev-parse HEAD)
|
||||
- git rev-list $(git rev-parse origin/$GITFOX_TARGET_BRANCH)..$(git rev-parse HEAD)
|
||||
- sh -c 'nohup /usr/bin/entrypoint.sh >/dev/null 2>&1 &'
|
||||
- . /etc/s6/s6-init/envs
|
||||
- sleep 5
|
||||
|
||||
@@ -26,15 +26,13 @@ spec:
|
||||
- docker buildx build --pull --push --platform linux/amd64 --build-arg VERSION=${ZENTAO_VERSION} --build-arg MIRROR=true -t hub.qc.oop.cc/zentao-ztf:${GITFOX_BUILD_NUMBER}-${GITFOX_COMMIT} -f misc/ci/Dockerfile.test .
|
||||
- name: unit test
|
||||
type: run
|
||||
when: |
|
||||
build.target == "main"
|
||||
spec:
|
||||
envs:
|
||||
MYSQL_INTERNAL: "true"
|
||||
container:
|
||||
image: hub.qc.oop.cc/zentao-ztf:${{ build.number }}-${{ build.after }}
|
||||
script:
|
||||
- git rev-list $(git rev-parse origin/main )..$(git rev-parse HEAD)
|
||||
- git rev-list $(git rev-parse origin/$GITFOX_TARGET_BRANCH )..$(git rev-parse HEAD)
|
||||
- sh -c 'nohup /usr/bin/entrypoint.sh >/dev/null 2>&1 &'
|
||||
- . /etc/s6/s6-init/envs
|
||||
- sleep 5
|
||||
|
||||
@@ -84,7 +84,7 @@ class testresultsEntry extends entry
|
||||
$reals[$stepID] = $step->real;
|
||||
}
|
||||
$this->setPost('result', $results);
|
||||
$this->setPost('reals', $reals);
|
||||
$this->setPost('real', $reals);
|
||||
}
|
||||
|
||||
$control->runCase($runID, $caseID, $version);
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
},
|
||||
"zentaoext": {
|
||||
"gitRepo": "zentao/zentaoext",
|
||||
"gitVersion": "patch/11.7"
|
||||
"gitVersion": "release/12.0"
|
||||
},
|
||||
"zentaomax": {
|
||||
"gitVersion": "patch/6.7"
|
||||
"gitVersion": "release/7.0"
|
||||
},
|
||||
"zentaoipd": {
|
||||
"gitVersion": "patch/3.7"
|
||||
"gitVersion": "release/4.0"
|
||||
},
|
||||
"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'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
|
||||
$config->version = '21.7.1'; // 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.
|
||||
|
||||
@@ -429,6 +429,7 @@ define('TABLE_ACL', '`' . $config->db->prefix . 'acl`');
|
||||
|
||||
define('TABLE_DESIGN', '`' . $config->db->prefix . 'design`');
|
||||
define('TABLE_DESIGNSPEC', '`' . $config->db->prefix . 'designspec`');
|
||||
define('TABLE_DELIVERABLE', '`' . $config->db->prefix . 'deliverable`');
|
||||
define('TABLE_DOCLIB', '`' . $config->db->prefix . 'doclib`');
|
||||
define('TABLE_DOC', '`' . $config->db->prefix . 'doc`');
|
||||
define('TABLE_DOCBLOCK', '`' . $config->db->prefix . 'docblock`');
|
||||
@@ -672,6 +673,7 @@ $config->objectTables['demand'] = TABLE_DEMAND;
|
||||
$config->objectTables['demandpool'] = TABLE_DEMANDPOOL;
|
||||
$config->objectTables['demandspec'] = TABLE_DEMANDSPEC;
|
||||
$config->objectTables['demandreview'] = TABLE_DEMANDREVIEW;
|
||||
$config->objectTables['deliverable'] = TABLE_DELIVERABLE;
|
||||
$config->objectTables['todo'] = TABLE_TODO;
|
||||
$config->objectTables['custom'] = TABLE_LANG;
|
||||
$config->objectTables['branch'] = TABLE_BRANCH;
|
||||
|
||||
+2
-1
@@ -578,7 +578,8 @@ REPLACE INTO `zt_searchdict` (`key`, `value`) VALUES
|
||||
(39564, '验'),
|
||||
(39588, '骤'),
|
||||
(65292, ',');
|
||||
REPLACE INTO `zt_searchindex` (`id`, `vision`, `objectType`, `objectID`, `title`, `content`, `addedDate`, `editedDate`) VALUES
|
||||
TRUNCATE TABLE `zt_searchindex`;
|
||||
INSERT INTO `zt_searchindex` (`id`, `vision`, `objectType`, `objectID`, `title`, `content`, `addedDate`, `editedDate`) VALUES
|
||||
(1, 'rnd', 'program', 1, ' 20225 19994 31649 29702', '', '2024-04-02 14:25:12', '2024-04-02 14:25:12'),
|
||||
(2, 'rnd', 'product', 1, ' 20844 21496 20225 19994 32593 31449 24314 35774', ' 24314 31435 20844 21496 20225 19994 32593 31449 65292 21487 20197 26356 22909 23545 22806 23637 31034 12290', '2024-04-02 14:30:13', '2024-04-02 14:30:13'),
|
||||
(3, 'rnd', 'product', 2, ' 20225 19994 20869 37096 24037 26102 31649 29702 31995 32479', '', '2024-04-02 14:30:55', '2024-04-02 14:30:55'),
|
||||
|
||||
+2
-1
@@ -577,7 +577,8 @@ REPLACE INTO `zt_searchdict` (`key`, `value`) VALUES
|
||||
(39564, '验'),
|
||||
(39588, '骤'),
|
||||
(65292, ',');
|
||||
REPLACE INTO `zt_searchindex` (`id`, `vision`, `objectType`, `objectID`, `title`, `content`, `addedDate`, `editedDate`) VALUES
|
||||
TRUNCATE TABLE `zt_searchindex`;
|
||||
INSERT INTO `zt_searchindex` (`id`, `vision`, `objectType`, `objectID`, `title`, `content`, `addedDate`, `editedDate`) VALUES
|
||||
(1, 'rnd', 'program', 1, ' 20225 19994 31649 29702', '', '2024-04-02 14:25:12', '2024-04-02 14:25:12'),
|
||||
(2, 'rnd', 'product', 1, ' 20844 21496 20225 19994 32593 31449 24314 35774', ' 24314 31435 20844 21496 20225 19994 32593 31449 65292 21487 20197 26356 22909 23545 22806 23637 31034 12290', '2024-04-02 14:30:13', '2024-04-02 14:30:13'),
|
||||
(3, 'rnd', 'product', 2, ' 20225 19994 20869 37096 24037 26102 31649 29702 31995 32479', '', '2024-04-02 14:30:55', '2024-04-02 14:30:55'),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -4,4 +4,4 @@ ALTER TABLE `zt_storyspec` ADD COLUMN `docs` text NULL AFTER `files`;
|
||||
ALTER TABLE `zt_storyspec` ADD COLUMN `docVersions` text NULL AFTER `docs`;
|
||||
ALTER TABLE `zt_design` ADD COLUMN `docs` text NULL AFTER `storyVersion`;
|
||||
ALTER TABLE `zt_design` ADD COLUMN `docVersions` text NULL AFTER `docs`;
|
||||
ALTER TABLE `zt_project` ADD `taskDateLimit` varchar(30) NOT NULL DEFAULT 'auto' AFTER `linkType`;
|
||||
ALTER TABLE `zt_project` ADD `taskDateLimit` varchar(30) NOT NULL DEFAULT 'auto' AFTER `linkType`;
|
||||
@@ -0,0 +1,53 @@
|
||||
DELETE FROM `zt_workflowaction` WHERE `module` = 'story' AND `action` = 'browse';
|
||||
DELETE FROM `zt_workflowlayout` WHERE `module` = 'story' AND `action` = 'browse';
|
||||
|
||||
DELETE FROM `zt_workflowaction` WHERE `module` = 'epic' AND `action` = 'browse';
|
||||
DELETE FROM `zt_workflowlayout` WHERE `module` = 'epic' AND `action` = 'browse';
|
||||
|
||||
DELETE FROM `zt_workflowaction` WHERE `module` = 'requirement' AND `action` = 'browse';
|
||||
DELETE FROM `zt_workflowlayout` WHERE `module` = 'requirement' AND `action` = 'browse';
|
||||
|
||||
UPDATE `zt_workflowaction` SET `module` = 'story', `action` = 'browse' WHERE `module` = 'product' AND `action` = 'browse';
|
||||
UPDATE `zt_workflowlayout` SET `module` = 'story', `action` = 'browse' WHERE `module` = 'product' AND `action` = 'browse';
|
||||
|
||||
UPDATE `zt_workflowaction` SET `module` = 'requirement', `action` = 'browse' WHERE `module` = 'product' AND `action` = 'requirement';
|
||||
UPDATE `zt_workflowlayout` SET `module` = 'requirement', `action` = 'browse' WHERE `module` = 'product' AND `action` = 'requirement';
|
||||
|
||||
UPDATE `zt_workflowaction` SET `module` = 'epic', `action` = 'browse' WHERE `module` = 'product' AND `action` = 'epic';
|
||||
UPDATE `zt_workflowlayout` SET `module` = 'epic', `action` = 'browse' WHERE `module` = 'product' AND `action` = 'epic';
|
||||
|
||||
UPDATE `zt_workflowaction` SET `module` = 'build', `action` = 'browse' WHERE `module` = 'execution' AND `action` = 'build';
|
||||
UPDATE `zt_workflowlayout` SET `module` = 'build', `action` = 'browse' WHERE `module` = 'execution' AND `action` = 'build';
|
||||
|
||||
UPDATE `zt_workflowaction` SET `module` = 'task', `action` = 'browse' WHERE `module` = 'execution' AND `action` = 'task';
|
||||
UPDATE `zt_workflowlayout` SET `module` = 'task', `action` = 'browse' WHERE `module` = 'execution' AND `action` = 'task';
|
||||
|
||||
ALTER TABLE `zt_relationoftasks` ADD COLUMN `project` mediumint(8) unsigned NOT NULL DEFAULT 0 AFTER `id`;
|
||||
ALTER TABLE `zt_relationoftasks` MODIFY `execution` char(30) NOT NULL DEFAULT '' AFTER `project`;
|
||||
UPDATE `zt_relationoftasks` SET `project` = (SELECT `project` FROM `zt_task` WHERE `id` = `zt_relationoftasks`.`task`);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `zt_deliverable` (
|
||||
`id` int(8) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`module` varchar(30) NULL,
|
||||
`method` varchar(30) NULL,
|
||||
`model` text NULL,
|
||||
`type` enum('doc','file') NULL DEFAULT 'file',
|
||||
`desc` text NULL,
|
||||
`files` varchar(255) NULL,
|
||||
`createdBy` varchar(30) NULL,
|
||||
`createdDate` date NULL,
|
||||
`lastEditedBy` varchar(30) NULL,
|
||||
`lastEditedDate` date NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
ALTER TABLE `zt_workflowgroup` ADD `objectID` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `id`;
|
||||
ALTER TABLE `zt_workflowgroup` ADD COLUMN `deliverable` text NULL AFTER `editedDate`;
|
||||
ALTER TABLE `zt_project` ADD COLUMN `deliverable` text NULL AFTER `maxColWidth`;
|
||||
|
||||
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';
|
||||
+26
-5
@@ -578,6 +578,24 @@ CREATE TABLE IF NOT EXISTS `zt_screen` (
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_deliverable`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_deliverable` (
|
||||
`id` int(8) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`module` varchar(30) NULL,
|
||||
`method` varchar(30) NULL,
|
||||
`model` text NULL,
|
||||
`type` enum('doc','file') NULL DEFAULT 'file',
|
||||
`desc` text NULL,
|
||||
`files` varchar(255) NULL,
|
||||
`createdBy` varchar(30) NULL,
|
||||
`createdDate` date NULL,
|
||||
`lastEditedBy` varchar(30) NULL,
|
||||
`lastEditedDate` date NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_dimension`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_dimension` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
@@ -1596,6 +1614,7 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
|
||||
`colWidth` smallint(6) NOT NULL DEFAULT '264',
|
||||
`minColWidth` smallint(6) NOT NULL DEFAULT '200',
|
||||
`maxColWidth` smallint(6) NOT NULL DEFAULT '384',
|
||||
`deliverable` text NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -13234,6 +13253,7 @@ CREATE INDEX `order` ON `zt_workflow` (`order`);
|
||||
-- DROP TABLE IF EXISTS `zt_workflowgroup`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_workflowgroup` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`objectID` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`type` varchar(10) NOT NULL DEFAULT '',
|
||||
`projectModel` varchar(10) NOT NULL DEFAULT '',
|
||||
`projectType` varchar(10) NOT NULL DEFAULT '',
|
||||
@@ -13249,6 +13269,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowgroup` (
|
||||
`createdDate` datetime NULL,
|
||||
`editedBy` varchar(30) NOT NULL DEFAULT '',
|
||||
`editedDate` datetime NULL,
|
||||
`deliverable` text NULL,
|
||||
`deleted` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -13527,11 +13548,11 @@ REPLACE INTO `zt_workflowrule`(`type`, `name`, `rule`, `createdBy`, `createdDate
|
||||
('system','IP','ip','admin','2020-10-14 14:06:14');
|
||||
|
||||
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');
|
||||
('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');
|
||||
|
||||
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\"}]}', '', '', ''),
|
||||
|
||||
+135
@@ -1,3 +1,138 @@
|
||||
2025-05-30 21.7.1
|
||||
完成的需求
|
||||
开源版:
|
||||
74489 在文档编辑器顶部工具栏中打印上传附件按钮
|
||||
74488 在文档编辑器顶部工具栏中打印插入图片按钮
|
||||
74487 在文档编辑器顶部工具栏中打印超链接按钮
|
||||
74486 在文档编辑器顶部工具栏中打印待办事项列表按钮
|
||||
74485 在文档编辑器顶部工具栏中打印序号列表项相关的按钮
|
||||
74483 批量创建Bug页面增加所属计划字段
|
||||
73303 点击文档顶部工具栏中更多按钮,在下拉菜单中打印相关数据
|
||||
73293 调整文档中目录、章节和文档的图标和颜色
|
||||
73292 调整编辑和浏览文档时的可视区域宽度
|
||||
73247 在文档编辑器顶部工具栏中打印文字样式的相关按钮
|
||||
73013 在文档编辑器顶部工具栏中打印文字基础格式的相关按钮
|
||||
73011 文档顶部固定工具栏中打印插入更多数据的按钮
|
||||
73010 实现文档Blocksuite编辑器顶部工具栏常驻显示的组件
|
||||
60167 创建Bug页面增加所属计划字段
|
||||
14272 导出Bug时增加所属计划字段
|
||||
企业版:
|
||||
74602 项目中支持单独维护单个项目的项目流程
|
||||
74439 工作流的流程模板列表不展示项目自定义的流程
|
||||
74429 项目中支持配置项目流程
|
||||
73561 构建列表动作展示在构建工作流中
|
||||
73560 任务列表动作展示在任务工作流中
|
||||
73559 业务需求列表动作展示在业务需求工作流中
|
||||
73558 用户需求列表动作展示在用户需求工作流中
|
||||
73557 研发需求列表动作展示在研发需求工作流中
|
||||
63771 工单工作流增加审批设置
|
||||
旗舰版:
|
||||
74722 项目交付物维护页面隐藏创建时的交付物
|
||||
74712 隐藏创建项目页面中的交付物组件
|
||||
74710 调整交付物配置页面的帮助说明
|
||||
74709 交付物配置页面隐藏创建时的交付物列
|
||||
74708 后台编辑交付物页面置灰动作字段
|
||||
74707 后台添加交付物页面置灰动作字段
|
||||
74181 前台提交交付物的相关页面中增加提交文件限制
|
||||
74126 编辑已提交付物的独立执行和顶级执行不允许关联父执行
|
||||
74125 流程模板中交付物配置改变时不会影响已创建的项目
|
||||
74091 后台系统功能配置的通用功能中增加交付物功能开关
|
||||
74088 实现项目交付物历史记录功能
|
||||
74078 编辑项目页面增加项目流程字段
|
||||
74077 编辑已提交交付物的项目时禁用项目模型、项目类型或项目流程字段
|
||||
73952 项目流程模板详情的历史记录中记录交付物配置的动态
|
||||
73827 实现维护执行交付物的权限配置
|
||||
73826 实现维护项目交付物的权限配置
|
||||
73824 实现流程模板交付物配置的权限配置
|
||||
73822 实现后台交付物配置的权限配置
|
||||
73716 实现创建项目页面中的交付物组件
|
||||
73671 实现项目交付物维护页面
|
||||
73621 实现独立执行和顶级执行交付物维护弹窗
|
||||
73616 项目列表支持查看交付物的提交比例
|
||||
73607 独立执行和顶级执行列表支持查看交付物的提交比例
|
||||
73587 项目菜单打印交付物
|
||||
73585 关闭最后一个未关闭的子执行后引导用户关闭顶级执行
|
||||
73583 批量关闭执行时提示无法关闭进行中的执行
|
||||
73552 实现交付物列表的搜索功能
|
||||
73551 实现按标签筛选列表中的交付物
|
||||
73549 实现添加交付物页面模板字段的互斥逻辑
|
||||
73548 实现交付物页面适用范围下拉菜单与后台阶段类型同步
|
||||
73488 调整独立执行和顶级执行的类型导致交付物发生改变时保留已提交的交付物
|
||||
73475 关闭进行中的独立执行和顶级执行时支持提交交付物
|
||||
73457 关闭进行中的项目时支持提交交付物
|
||||
73453 创建项目时支持提交交付物
|
||||
73447 交付物配置页打印保存和返回按钮
|
||||
73446 交付物配置页始终显示帮助说明
|
||||
73445 交付物配置页标题可以依据不同流程模板命名
|
||||
73443 交付物配置页支持按对象动作关联交付物
|
||||
73442 项目流程模板列表操作列打印交付物配置按钮
|
||||
73439 实现删除交付物功能
|
||||
73438 实现编辑交付物功能
|
||||
73436 实现交付物详情页面
|
||||
73429 实现交付物列表页面
|
||||
73347 添加交付物页面支持定义交付物的基本信息
|
||||
73346 交付物列表页打印添加交付物的按钮
|
||||
73329 项目模型配置的通用菜单中打印交付物
|
||||
73327 后台模型配置名称调整为项目模型配置
|
||||
IPD版:
|
||||
73927 批量维护阶段页面增加提示语
|
||||
修复的Bug
|
||||
开源版:
|
||||
62317 创建bug页面完整版表单下控件位置建议优化
|
||||
62339 影子产品创建Bug页面简洁版显示了所属计划字段
|
||||
62662 需求批量转任务页面工时和优先级同上应该置灰取需求的工时和优先级
|
||||
62746 标题内容输入数字时工具栏会一直刷新
|
||||
62778 windows一键安装包退出登录报错
|
||||
62779 瀑布项目阶段列表页点击编辑按钮,页面报错
|
||||
62787 测试单关联用例页面,搜索表单没显示
|
||||
62794 全局创建文档页面顶部没有工具栏
|
||||
62797 批量修改需求所属计划没有空选项
|
||||
62812 任务看板中的卡片不能正常显示
|
||||
62813 普通用户无法进行项目的关闭设置
|
||||
62817 BUG列表批量编辑修改影响版本会导致BUG关联对象被清空 (Feedback#9383)
|
||||
62819 批量编辑任务会与最新版本的需求解绑
|
||||
62821 设置父子任务时间不符合时,页面显示了提示但是保存成功了
|
||||
62823 复制Bug后所属计划不会复制上
|
||||
62881 文档中切换禅道数据列表分页数有报错
|
||||
企业版:
|
||||
62725 执行列表中预计消耗剩余展示为空 (Feedback#9327)
|
||||
62741 父任务的起止日期修改为空,子任务的起止日期字段显示原有日期且置灰
|
||||
62766 升级后执行工作流中语言项错误
|
||||
62767 执行工作流下执行列表对应的动作显示所有
|
||||
62780 编辑项目管理方式,项目流程没有更新
|
||||
62788 编辑导出的多人任务后再导入只能导入一条数据
|
||||
62795 产品下导入需求报错
|
||||
62805 项目开启项目级流程后,编辑项目页面流程还显示原来的
|
||||
62811 工作流下自定义流程启用时选择单独启用后流程模板下的该流程也启用了
|
||||
62824 编辑项目页面没有根据项目管理方式判断是否显示项目流程字段
|
||||
62840 添加透视表使用手动输入SQL查询输入语句点查询报错502
|
||||
62842 配置office collabora时填写错误的地址保存有报错
|
||||
62855 编辑项目管理方式项目流程错误
|
||||
62856 创建 office文档弹窗中权限控制没有修改
|
||||
62860 编辑项目时项目流程下拉选项显示了其它项目的项目级流程
|
||||
62861 设计透视表报错
|
||||
旗舰版:
|
||||
62733 瀑布项目阶段列表页提交交付物后,保存报错
|
||||
62744 关闭进行中的执行页面没有显示交付物组件
|
||||
62755 复制瀑布项目完善阶段信息页保存时报错
|
||||
62796 安装oceanbase数据库,最后一步有报错
|
||||
62802 项目下交付物不能预览和下载
|
||||
62804 升级后项目下启用项目流程无法启用
|
||||
62806 进入项目下项目流程页面后二级导航的自定义流程不显示了
|
||||
62807 windows包项目下选择文档交付物,保存报错
|
||||
62810 关闭子阶段,提示去关闭父阶段,实际没跳转到父阶段关闭页面
|
||||
62815 创建项目报错
|
||||
62818 执行下维护交付物页面保存时报错
|
||||
62825 升级后进入项目概况页不显示流程名称
|
||||
62827 工作流流程下新建并发布流程后项目流程下未显示该流程
|
||||
62829 复制项目时默认带入的项目流程不正确
|
||||
62843 交付物数量在列表没显示
|
||||
62858 升级后工作流下产品/项目模板列表有报错
|
||||
62867 看板项目概况页显示了流程名称
|
||||
IPD版:
|
||||
62731 OR界面调研项目设置阶段报错
|
||||
62789 IPD项目下编辑阶段页面阶段类型可编辑
|
||||
|
||||
2025-05-16 21.7
|
||||
完成的需求
|
||||
开源版:
|
||||
|
||||
@@ -3403,7 +3403,11 @@ class baseRouter
|
||||
|
||||
$fh = fopen($sqlLog, 'a');
|
||||
fwrite($fh, date('Ymd H:i:s') . ": " . $this->getURI() . "\n");
|
||||
foreach(dao::$querys as $query) fwrite($fh, " $query\n");
|
||||
foreach(dbh::$queries as $key => $query)
|
||||
{
|
||||
fwrite($fh, " $query\n");
|
||||
fwrite($fh, ' └' . dbh::$traces[$key] . "\n");
|
||||
}
|
||||
fwrite($fh, "\n");
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
@@ -527,6 +527,8 @@ class control extends baseControl
|
||||
$flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
|
||||
if(!$flow) return $fields;
|
||||
|
||||
if($groupID && $moduleName == 'project' && $methodName == 'create') $groupID = 0; // 项目创建页面用通用的工作流配置
|
||||
|
||||
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
|
||||
if(!$action || $action->extensionType != 'extend') return $fields;
|
||||
|
||||
|
||||
+130
-14
@@ -1477,15 +1477,7 @@ class baseDAO
|
||||
**/
|
||||
else
|
||||
{
|
||||
/*
|
||||
* 使用$arg0, $arg1... 生成调用的参数。
|
||||
* Create the max counts of sql class methods, and then create $arg0, $arg1...
|
||||
**/
|
||||
for($i = 0; $i < SQL::MAX_ARGS; $i ++)
|
||||
{
|
||||
${"arg$i"} = isset($funcArgs[$i]) ? $funcArgs[$i] : null;
|
||||
}
|
||||
$this->sqlobj->$funcName($arg0, $arg1, $arg2);
|
||||
$this->sqlobj->$funcName(...$funcArgs);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1876,11 +1868,13 @@ class baseDAO
|
||||
}
|
||||
if(empty($profiles)) $profiles = $this->query('SHOW PROFILES')->fetchAll();
|
||||
|
||||
foreach($profiles as $profile)
|
||||
$basePath = $this->app->getBasePath();
|
||||
foreach($profiles as $key => $profile)
|
||||
{
|
||||
$profile->Duration = round((float)$profile->Duration, 4);
|
||||
$profile->Explain = [];
|
||||
$profile->Error = '';
|
||||
$profile->Code = str_replace($basePath, '', dbh::$traces[$key]);
|
||||
|
||||
$sql = trim($profile->Query);
|
||||
if(stripos($sql, 'select') !== 0
|
||||
@@ -1921,6 +1915,21 @@ class baseDAO
|
||||
|
||||
return $profiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时表。
|
||||
* Create temporary table.
|
||||
*
|
||||
* @param int $ids 用于创建临时表的 id 列表,字符串或数组。
|
||||
* @param bool $filterDuplicate 是否过滤重复的表名,默认值为 true。
|
||||
* @param int $limit 用于创建临时表的 id 列表的数量限制,数量小于这个值时不创建临时表,0 表示不限制数量。
|
||||
* @access public
|
||||
* @return false|string
|
||||
*/
|
||||
public function createTemporaryTable($idList, $filterDuplicate = true, $limit = 100)
|
||||
{
|
||||
return $this->sqlobj->createTemporaryTable($idList, $filterDuplicate, $limit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1947,6 +1956,15 @@ class baseSQL
|
||||
*/
|
||||
public $sql = '';
|
||||
|
||||
/**
|
||||
* 全局对象$app
|
||||
* The global app object.
|
||||
*
|
||||
* @var object
|
||||
* @access public
|
||||
*/
|
||||
public $app;
|
||||
|
||||
/**
|
||||
* 全局变量$dbh。
|
||||
* The global $dbh.
|
||||
@@ -2073,6 +2091,15 @@ class baseSQL
|
||||
*/
|
||||
public $currentTable;
|
||||
|
||||
/**
|
||||
* 已创建的临时表。
|
||||
* The temporary tables.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $tempTables = [];
|
||||
|
||||
/**
|
||||
* 构造方法。
|
||||
* The construct function.
|
||||
@@ -2082,7 +2109,8 @@ class baseSQL
|
||||
*/
|
||||
public function __construct($table = '')
|
||||
{
|
||||
global $dbh;
|
||||
global $app, $dbh;
|
||||
$this->app = $app;
|
||||
$this->dbh = $dbh;
|
||||
$this->data = new stdclass();
|
||||
$this->skipFields = '';
|
||||
@@ -2594,17 +2622,105 @@ class baseSQL
|
||||
* 创建IN部分。
|
||||
* Create in part.
|
||||
*
|
||||
* @param string|array $ids ','分割的字符串或者数组 list string by ',' or an array
|
||||
* @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.
|
||||
* @access public
|
||||
* @return static|sql the sql object.
|
||||
*/
|
||||
public function in($ids)
|
||||
public function in($ids, $useTemporaryTable = false, $filterDuplicate = true)
|
||||
{
|
||||
if($this->inCondition and !$this->conditionIsTrue) return $this;
|
||||
$this->sql .= is_null($ids) ? ' IS NULL' : helper::dbIN($ids);
|
||||
|
||||
if(is_null($ids))
|
||||
{
|
||||
$this->sql .= ' IS NULL';
|
||||
return $this;
|
||||
}
|
||||
|
||||
if($useTemporaryTable)
|
||||
{
|
||||
$tableName = $this->createTemporaryTable($ids, $filterDuplicate);
|
||||
if($tableName)
|
||||
{
|
||||
$this->sql .= " IN (SELECT id FROM $tableName)";
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
$this->sql .= helper::dbIN($ids);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时表。
|
||||
* Create temporary table.
|
||||
*
|
||||
* @param int $ids 用于创建临时表的 id 列表,字符串或数组。
|
||||
* @param bool $filterDuplicate 是否过滤重复的表名,默认值为 true。
|
||||
* @param int $limit 用于创建临时表的 id 列表的数量限制,数量小于这个值时不创建临时表,0 表示不限制数量。
|
||||
* @access public
|
||||
* @return false|string
|
||||
*/
|
||||
public function createTemporaryTable($ids, $filterDuplicate = true, $limit = 100)
|
||||
{
|
||||
if(!is_string($ids) && !is_array($ids))
|
||||
{
|
||||
$this->app->triggerError('The idList must be a string or an array.', __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(is_string($ids)) $ids = explode(',', $ids);
|
||||
|
||||
$idList = [];
|
||||
foreach($ids as $id)
|
||||
{
|
||||
if(empty($id)) continue;
|
||||
|
||||
$intID = (int)$id;
|
||||
if($intID != $id) continue;
|
||||
|
||||
$idList[$intID] = $intID;
|
||||
}
|
||||
|
||||
if($limit && count($idList) < $limit)
|
||||
{
|
||||
$this->app->triggerError("The idList count must be greater than $limit", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
$tableName = '';
|
||||
if($filterDuplicate)
|
||||
{
|
||||
/* 如果开启了唯一表名,那么将表名设置为 md5(idList). */
|
||||
asort($idList);
|
||||
$rows = '(' . implode('),(', $idList) . ')';
|
||||
$tableName = "temp_" . md5($rows);
|
||||
|
||||
if(isset($this->tempTables[$tableName])) return $tableName;
|
||||
}
|
||||
else
|
||||
{
|
||||
$tableName = 'temp_' . uniqid();
|
||||
$rows = '(' . implode('),(', $idList) . ')';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->dbh->exec("CREATE TEMPORARY TABLE IF NOT EXISTS {$tableName} (id int PRIMARY KEY)");
|
||||
$this->dbh->exec("INSERT INTO {$tableName} VALUES {$rows}");
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
$message = $e->getMessage() . ' ' . helper::checkDB2Repair($e);
|
||||
$this->app->triggerError($message, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
if($filterDuplicate) $this->tempTables[$tableName] = $tableName;
|
||||
|
||||
return $tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建'NOT IN'部分。
|
||||
* Create not in part.
|
||||
|
||||
Vendored
+51
-12
@@ -161,6 +161,7 @@ class cache
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->dao = $app->dao;
|
||||
$this->dbh = $app->dbh;
|
||||
$this->config = $app->config;
|
||||
|
||||
if(empty($this->config->cache->enable)) return $this->log('The cache is not enabled', __FILE__, __LINE__);
|
||||
@@ -369,12 +370,13 @@ class cache
|
||||
$objectIdList = $this->cache->get($setCacheKey);
|
||||
if(!is_array($objectIdList)) return;
|
||||
|
||||
$objectID = $this->dao->lastInsertID();
|
||||
$objectID = $this->dbh->lastInsertID();
|
||||
if(!$objectID) return $this->log('Failed to fetch last insert id.', __FILE__, __LINE__);
|
||||
|
||||
/* 获取新增的数据。Get the new data. */
|
||||
$object = $this->dao->select('*')->from($this->table)->where('id')->eq($objectID)->fetch();
|
||||
if(!$object) return $this->log('Failed to fetch new object. The sql is: ' . $this->dao->get(), __FILE__, __LINE__);
|
||||
$sql = $this->dao->select('*')->from($this->table)->where('id')->eq($objectID)->get();
|
||||
$object = $this->query($sql, 'fetch');
|
||||
if(!$object) return $this->log('Failed to fetch new object. The sql is: ' . $sql, __FILE__, __LINE__);
|
||||
|
||||
/* 把新增的数据保存到缓存中。Save the new data to cache. */
|
||||
$field = $this->getTableField();
|
||||
@@ -408,8 +410,9 @@ class cache
|
||||
$objectIdList = array_map(function($object) use ($field) { return $object->$field; }, $this->objects);
|
||||
|
||||
/* 获取更新后的数据。Get the updated data. */
|
||||
$objects = $this->dao->select('*')->from($this->table)->where($field)->in($objectIdList)->fetchAll($field, false);
|
||||
if(!$objects) return $this->log('Failed to fetch updated objects. The sql is: ' . $this->dao->get(), __FILE__, __LINE__);
|
||||
$sql = $this->dao->select('*')->from($this->table)->where($field)->in($objectIdList)->get();
|
||||
$objects = $this->query($sql, 'fetchAll', $field);
|
||||
if(!$objects) return $this->log('Failed to fetch updated objects. The sql is: ' . $sql, __FILE__, __LINE__);
|
||||
|
||||
/* 把更新后的数据保存到缓存中。Save the updated data to cache. */
|
||||
$values = [];
|
||||
@@ -506,6 +509,28 @@ class cache
|
||||
$this->cache->deleteMultiple($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据库查询操作。
|
||||
* Execute database query operation.
|
||||
*
|
||||
* @param string $sql SQL语句。
|
||||
* @param string $function 查询函数。
|
||||
* @param string $field 字段名。
|
||||
* @access private
|
||||
* @return array|object
|
||||
*/
|
||||
private function query($sql, $function = 'fetchAll', $field = ''): array|object
|
||||
{
|
||||
$rows = $this->dao->query($sql)->$function();
|
||||
if($function == 'fetchAll' && !empty($field))
|
||||
{
|
||||
$objects = [];
|
||||
foreach($rows as $row) $objects[$row->$field] = $row;
|
||||
return $objects;
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查表是否有缓存设置。
|
||||
* Check if the table has cache settings.
|
||||
@@ -583,7 +608,8 @@ class cache
|
||||
private function initTableCache(): array
|
||||
{
|
||||
$field = $this->getTableField();
|
||||
$objects = $this->dao->select('*')->from($this->table)->fetchAll($field, false);
|
||||
$sql = $this->dao->select('*')->from($this->table)->get();
|
||||
$objects = $this->query($sql, 'fetchAll', $field);
|
||||
if(!$objects) return [];
|
||||
|
||||
$values = [];
|
||||
@@ -693,7 +719,8 @@ class cache
|
||||
{
|
||||
$lostObjects = [];
|
||||
$diffIdList = array_keys(array_diff($keys, array_keys($objects)));
|
||||
$diffObjects = $this->dao->select('*')->from($table)->where('id')->in($diffIdList)->fetchAll('', false);
|
||||
$sql = $this->dao->select('*')->from($table)->where('id')->in($diffIdList)->get();
|
||||
$diffObjects = $this->query($sql, 'fetchAll');
|
||||
foreach($diffObjects as $object)
|
||||
{
|
||||
$rawCacheKey = $this->getRawCacheKey($code, $object->id);
|
||||
@@ -757,18 +784,29 @@ 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']);
|
||||
$idList = array_values($dao->fetchPairs());
|
||||
$sql = $dao->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'));
|
||||
$autoCache = $this->dao->select('1')->from(TABLE_AUTOCACHE)->where('code')->eq($code)->andWhere('fields')->eq($fields)->fetch();
|
||||
$sql = $this->dao->select('1')->from(TABLE_AUTOCACHE)->where('code')->eq($code)->andWhere('fields')->eq($fields)->get();
|
||||
$autoCache = $this->query($sql, 'fetch');
|
||||
if(!$autoCache)
|
||||
{
|
||||
$autoCache = new \stdClass();
|
||||
$autoCache->code = $code;
|
||||
$autoCache->fields = $fields;
|
||||
$this->dao->insert(TABLE_AUTOCACHE)->data($autoCache)->exec();
|
||||
$sql = $this->dao->insert(TABLE_AUTOCACHE)->data($autoCache)->get();
|
||||
try
|
||||
{
|
||||
$this->dbh->exec($sql);
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
$this->dao->sqlError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$idList) return [];
|
||||
@@ -980,7 +1018,8 @@ class cache
|
||||
|
||||
/* 执行操作后数据已经被修改,所以需要提前获取被影响的数据。*/
|
||||
$field = $this->getTableField();
|
||||
$objects = $this->dao->select('*')->from($table)->beginIF($where)->where($where)->fi()->fetchAll($field, false);
|
||||
$sql = $this->dao->select('*')->from($table)->beginIF($where)->where($where)->fi()->get();
|
||||
$objects = $this->query($sql, 'fetchAll', $field);
|
||||
|
||||
$this->setWhere($where);
|
||||
$this->setObjects($objects);
|
||||
@@ -1046,4 +1085,4 @@ class cache
|
||||
{
|
||||
return $this->cache->memory($type);
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
-6
@@ -59,6 +59,24 @@ class dbh
|
||||
*/
|
||||
private $sql;
|
||||
|
||||
/**
|
||||
* 这是用来记录SQL执行的文件和行号。
|
||||
* This is used to record the file and line number where the SQL was executed.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public static $traces = [];
|
||||
|
||||
/**
|
||||
* 执行的请求,所有的查询都保存在该数组。
|
||||
* The queries executed. Every query will be saved in this array.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public static $queries = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@@ -81,15 +99,25 @@ class dbh
|
||||
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$queries = [];
|
||||
/* Mysql driver include mysql and oceanbase. */
|
||||
if($driver == 'mysql')
|
||||
{
|
||||
$pdo->exec("SET NAMES {$config->encoding}");
|
||||
if(isset($config->strictMode) and $config->strictMode == false) $pdo->exec("SET @@sql_mode= ''");
|
||||
$queries[] = "SET NAMES {$config->encoding}";
|
||||
if(isset($config->strictMode) && empty($config->strictMode)) $queries[] = "SET @@sql_mode= ''";
|
||||
}
|
||||
else if($setSchema)
|
||||
{
|
||||
$pdo->exec("SET SCHEMA {$config->name}");
|
||||
$queries[] = "SET SCHEMA {$config->name}";
|
||||
}
|
||||
if(!empty($queries))
|
||||
{
|
||||
foreach($queries as $query)
|
||||
{
|
||||
$pdo->exec($query);
|
||||
dbh::$traces[] = 'vim +' . (__LINE__ - 1) . ' ' . __FILE__;
|
||||
dbh::$queries[] = "[$flag] " . $query;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pdo = $pdo;
|
||||
@@ -120,6 +148,8 @@ class dbh
|
||||
*/
|
||||
public function exec($sql)
|
||||
{
|
||||
$this->trace();
|
||||
|
||||
$sql = $this->formatSQL($sql);
|
||||
if(!$sql) return true;
|
||||
|
||||
@@ -127,7 +157,7 @@ class dbh
|
||||
|
||||
try
|
||||
{
|
||||
if(class_exists('dao')) dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
dbh::$queries[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
return $this->pdo->exec($sql);
|
||||
}
|
||||
catch(PDOException $e)
|
||||
@@ -146,10 +176,12 @@ class dbh
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
$this->trace();
|
||||
|
||||
$sql = $this->formatSQL($sql);
|
||||
try
|
||||
{
|
||||
if(class_exists('dao')) dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
dbh::$queries[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
return $this->pdo->query($sql);
|
||||
}
|
||||
catch(PDOException $e)
|
||||
@@ -191,6 +223,8 @@ class dbh
|
||||
*/
|
||||
public function execute($sql, $params)
|
||||
{
|
||||
$this->trace();
|
||||
|
||||
$this->statement = $this->prepare($sql);
|
||||
|
||||
try
|
||||
@@ -214,9 +248,11 @@ class dbh
|
||||
*/
|
||||
public function rawQuery($sql)
|
||||
{
|
||||
$this->trace();
|
||||
|
||||
try
|
||||
{
|
||||
if(class_exists('dao')) dao::$querys[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
dbh::$queries[] = "[$this->flag] " . dao::processKeywords($sql);
|
||||
return $this->pdo->query($sql);
|
||||
}
|
||||
catch(PDOException $e)
|
||||
@@ -225,6 +261,63 @@ class dbh
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录当前执行的 SQL 的文件和行号。
|
||||
* Record the file and line number of the currently executed SQL.
|
||||
*
|
||||
* @access private
|
||||
* @return bool
|
||||
*/
|
||||
private function trace()
|
||||
{
|
||||
global $app, $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;
|
||||
|
||||
$file = $trace['file'];
|
||||
$line = $trace['line'];
|
||||
|
||||
dbh::$traces[] = "vim +$line $file";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前执行的 SQL 的调用栈信息。
|
||||
* Get the call stack information of the currently executed SQL.
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getTrace()
|
||||
{
|
||||
$traces = array_reverse(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
|
||||
foreach($traces as $key => $trace)
|
||||
{
|
||||
$class = $trace['class'] ?? '';
|
||||
$function = $trace['function'] ?? '';
|
||||
if($class == 'settingModel' && strpos(',getItem,getItems,setItem,setItems,updateItem,deleteItems,', ",$function,") !== false) return $trace;
|
||||
if($class == 'baseDAO' && strpos(',exec,fetch,fetchPairs,fetchGroup,explain,showTables,getTableEngines,descTable,', ",$function,") !== false) return $trace;
|
||||
if($class == 'baseDAO' && $function == 'getProfiles') return $traces[$key + 1];
|
||||
if($class == 'baseDAO' && $function == 'fetchAll')
|
||||
{
|
||||
if($traces[$key + 1]['class'] == 'baseDAO' && $traces[$key + 1]['function'] == 'extractSQLFields') return $traces[$key + 2];
|
||||
return $trace;
|
||||
}
|
||||
if($class == 'baseDAO' && stripos($function, 'findBy') === 0) return $trace;
|
||||
if($class == 'baseRouter' && $function == 'dbQuery') return $trace;
|
||||
if($class == 'dbh' && strpos('exec,query,rawQuery', $function) !== false) return $trace;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attribute.
|
||||
*
|
||||
|
||||
@@ -43,7 +43,7 @@ class trace
|
||||
'method' => $this->app->server->request_method,
|
||||
'timeUsed' => round(getTime() - $this->app->startTime, 4) * 1000,
|
||||
'memory' => round(memory_get_peak_usage() / 1024, 1),
|
||||
'querys' => count(dao::$querys),
|
||||
'querys' => count(dbh::$queries),
|
||||
'caches' => 0,
|
||||
'files' => count(get_included_files()),
|
||||
'session' => session_id(),
|
||||
@@ -70,7 +70,7 @@ class trace
|
||||
*/
|
||||
public function getRequestSqls()
|
||||
{
|
||||
$this->trace['sqlQuery'] = dao::$querys;
|
||||
$this->trace['sqlQuery'] = dbh::$queries;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-1
@@ -249,6 +249,7 @@ function iconPicker(): iconPicker {return createWg('iconPicker', func_get_args()
|
||||
function relatedObjectList(): relatedObjectList {return createWg('relatedObjectList', func_get_args());}
|
||||
function taskAssignedTo(): taskAssignedTo {return createWg('taskAssignedTo', func_get_args());}
|
||||
function docList(): docList {return createWg('docList', func_get_args());}
|
||||
function deliverable(): deliverable {return createWg('deliverable', func_get_args());}
|
||||
|
||||
if(is_dir(__DIR__ . DS . 'wg' . DS . 'schedule'))
|
||||
{
|
||||
@@ -258,4 +259,4 @@ if(is_dir(__DIR__ . DS . 'wg' . DS . 'schedule'))
|
||||
if(is_dir(__DIR__ . DS . 'wg' . DS . 'boardeditor'))
|
||||
{
|
||||
function boardEditor(): boardEditor {return createWg('boardeditor', func_get_args());}
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,9 @@ class backBtn extends btn
|
||||
'approvalflow' => 'approvalflow-browse',
|
||||
'host' => 'host-browse,my-index',
|
||||
'deploy' => 'deploy-browse',
|
||||
'program' => 'program-browse,program-productview'
|
||||
'program' => 'program-browse,program-productview',
|
||||
'workflowgroup' => 'workflowgroup-project,workflowgroup-deliverable',
|
||||
'deliverable' => 'deliverable-browse'
|
||||
);
|
||||
|
||||
$props = parent::getProps();
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.deliverable-item {padding-left: 3.5rem !important}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 获取附件的操作按钮。
|
||||
* Get file actions.
|
||||
*/
|
||||
window.getDeliverableFileActions = function(file, deliverable)
|
||||
{
|
||||
let actions = [];
|
||||
if(canDownload && typeof file.id != 'undefined') actions[0] = {icon: 'download', key: 'download', url: $.createLink('file', 'download', 'id=' + file.id), target: '_blank'};
|
||||
actions[1] = {icon: 'edit', key: 'rename'};
|
||||
actions[2] = {icon: 'trash', key: 'delete'};
|
||||
|
||||
/* 可以预览的文件。 */
|
||||
if(['txt', 'jpg', 'jpeg', 'gif', 'png', 'bmp', 'mp4'].includes(file.extension) && canDownload && typeof file.id != 'undefined')
|
||||
{
|
||||
actions[3] = {icon: 'eye', key: 'view', url: $.createLink('file', 'download', `fileID=${file.id}&mouse=left`), 'data-toggle' : 'modal', 'data-size' : 'lg'};
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档的操作按钮。
|
||||
* Get file actions.
|
||||
*/
|
||||
window.getDocActions = function(doc, deliverable)
|
||||
{
|
||||
let actions = [];
|
||||
actions[0] = {icon: 'eye', key: 'view', url: $.createLink('doc', 'view', 'docID=' + doc.id)};
|
||||
actions[1] = {icon: 'edit', key: 'rename'};
|
||||
actions[2] = {icon: 'trash', key: 'delete'};
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取交付物的操作按钮。
|
||||
* Get deliverable actions.
|
||||
*/
|
||||
window.getDeliverableActions = function(deliverable)
|
||||
{
|
||||
let actions = [];
|
||||
actions[0] = {text: addFile, icon: 'file', key: 'selectFile'};
|
||||
if(deliverable.template > 0 && canDownload) actions[1] = {text: downloadTemplate, icon: 'download', key: 'downloadTemplate', url: $.createLink('file', 'download', 'templateID=' + deliverable.template), target: '_blank'};
|
||||
return actions;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
class deliverable extends wg
|
||||
{
|
||||
/**
|
||||
* 默认的组件属性。
|
||||
* Define widget properties.
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected static array $defineProps = array
|
||||
(
|
||||
'items: array', // 交付物条目。
|
||||
'formName: string', // 表单名称。
|
||||
'maxFileSize: string' // 最大文件大小。
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取JS。
|
||||
* Get page JS.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function getPageJS(): ?string
|
||||
{
|
||||
global $lang, $app;
|
||||
$app->loadLang('doc');
|
||||
$app->loadLang('file');
|
||||
jsVar('addFile', $lang->doc->addFile);
|
||||
jsVar('downloadTemplate', $lang->doc->downloadTemplate);
|
||||
jsVar('deleteItem', $lang->delete);
|
||||
jsVar('canDownload', hasPriv('file', 'download'));
|
||||
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取CSS。
|
||||
* Get page CSS.
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function getPageCSS(): ?string
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建组件。
|
||||
* Build.
|
||||
*
|
||||
* @access protected
|
||||
* @return zui
|
||||
*/
|
||||
protected function build(): zui
|
||||
{
|
||||
global $lang, $app;
|
||||
$app->loadLang('doc');
|
||||
$app->loadLang('file');
|
||||
|
||||
$formName = $this->prop('formName') ? $this->prop('formName') : 'deliverable';
|
||||
|
||||
if(!$this->hasProp('maxFileSize'))
|
||||
{
|
||||
$maxFileSize = ini_get('upload_max_filesize');
|
||||
$lastChar = substr($maxFileSize, -1);
|
||||
$fileSizeUnit = array('K', 'M', 'G', 'T');
|
||||
if(in_array($lastChar, $fileSizeUnit)) $maxFileSize .= 'B';
|
||||
$this->setProp('maxFileSize', $maxFileSize);
|
||||
}
|
||||
|
||||
return zui::deliverableList
|
||||
(
|
||||
set::formName($formName),
|
||||
set::items($this->prop('items')),
|
||||
set::docPicker(array('placeholder' => $lang->doc->selectDoc, 'items' => helper::createLink('doc', 'ajaxGetMineDocs', 'keyword={search}'))),
|
||||
set::getFileActions(jsRaw('window.getDeliverableFileActions')),
|
||||
set::getDocActions(jsRaw('window.getDocActions')),
|
||||
set::getEmptyActions(jsRaw('window.getDeliverableActions')),
|
||||
set::maxFileSize($this->prop('maxFileSize')),
|
||||
set::extraCategory($lang->other)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1135,10 +1135,11 @@ const actionsMap =
|
||||
const doc = info.doc;
|
||||
const file = info.data;
|
||||
const docApp = getDocApp();
|
||||
const isReadonly = docApp.mode === 'view';
|
||||
const privs = docApp.props.privs;
|
||||
const lang = getLang();
|
||||
const canEdit = privs.edit && (!doc.privs || doc.privs.edit !== false) && privs.editFile;
|
||||
const canDelete = privs.edit && (!doc.privs || doc.privs.edit !== false) && privs.deleteFile;
|
||||
const canEdit = !isReadonly && privs.edit && (!doc.privs || doc.privs.edit !== false) && privs.editFile;
|
||||
const canDelete = !isReadonly && privs.edit && (!doc.privs || doc.privs.edit !== false) && privs.deleteFile;
|
||||
const hasExtension = file.title.lastIndexOf('.' + file.extension) == (file.title.length - file.extension.length - 1);
|
||||
const canPreview = hasExtension && 'mp4,txt,jpg,jpeg,gif,png,bmp'.split(',').includes(file.extension);
|
||||
return [
|
||||
@@ -2091,6 +2092,34 @@ function getSpaceFetcher(spaceType, spaceID = 0)
|
||||
}
|
||||
}
|
||||
|
||||
function initOfficePreview(options)
|
||||
{
|
||||
const officeTypes = new Set(['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf']);
|
||||
zui.Editor.setGlobalConfig(
|
||||
{
|
||||
canPreviewFile: (file) => {
|
||||
const extension = file.name.split('.').pop();
|
||||
return officeTypes.has(extension);
|
||||
},
|
||||
onPreviewFile: (file) => {
|
||||
const doc = getDocApp().doc || {data: {id: 0}};
|
||||
$.getJSON(zui.formatString(options.fileInfoUrl, {
|
||||
gid: btoa(`g-${file.key}`),
|
||||
objectType: 'doc',
|
||||
objectID: doc.data.id,
|
||||
title: '',
|
||||
extra: '',
|
||||
})).then(res => {
|
||||
if(res.result === 'success' && res.data)
|
||||
{
|
||||
const editable = doc.data.editable !== false;
|
||||
window.open($.createLink('file', 'download', `fileID=${res.data.id}&mouse=left&edit=${editable ? '0' : 'never'}`), '_blank');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文档应用组件选项。
|
||||
* Set the doc app options.
|
||||
@@ -2102,6 +2131,8 @@ window.setDocAppOptions = function(_, options)
|
||||
userPrivs = options.privs;
|
||||
docLangData = options.langData;
|
||||
|
||||
if(options.canPreviewOffice && options.fileInfoUrl) initOfficePreview(options);
|
||||
|
||||
return $.extend(
|
||||
{
|
||||
defaultState : {listType: '', libReleaseMap: {}},
|
||||
|
||||
@@ -144,6 +144,7 @@ class docApp extends wg
|
||||
$libSummariesFetcher = createLink('doc', 'ajaxGetLibSummaries', 'spaceType={spaceType}&spaceList={spaceList}');
|
||||
$uploadUrl = createLink('file', 'ajaxUpload', 'uid={uid}&objectType={objectType}&objectID={objectID}&extra={extra}&field={field}&api={api}&gid={gid}');
|
||||
$downloadUrl = createLink('file', 'ajaxQuery', 'fileID={gid}&objectType={objectType}&objectID={objectID}&title={title}&extra={extra}');
|
||||
$fileInfoUrl = createLink('file', 'ajaxQuery', 'fileID={gid}&objectType={objectType}&objectID={objectID}&title={title}&extra={extra}', 'json');
|
||||
|
||||
/**
|
||||
* 定义文档界面上的文件下载链接。
|
||||
@@ -269,6 +270,7 @@ class docApp extends wg
|
||||
}
|
||||
|
||||
$historyPanelProps = array('fileListProps' => $fileListProps);
|
||||
$canPreviewOffice = $canDownload && isset($config->file->libreOfficeTurnon) and $config->file->libreOfficeTurnon == 1;
|
||||
|
||||
return zui::docApp
|
||||
(
|
||||
@@ -312,6 +314,9 @@ class docApp extends wg
|
||||
set::viewModeUrl($viewModeUrl),
|
||||
set::langData($langData),
|
||||
set::historyPanel($historyPanelProps),
|
||||
set::showToolbar(true),
|
||||
set::canPreviewOffice($canPreviewOffice),
|
||||
set::fileInfoUrl($fileInfoUrl),
|
||||
jsCall('setZentaoSlashMenu', $this->getZentaoListMenu(), $lang->doc->zentaoData, $config->vision, $config->doc->zentaoListMenuPosition)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class docList extends wg
|
||||
setClass('docItem flex items-center py-1'),
|
||||
span(setClass('mr-4 p-1 docTitle'), icon(setClass('mr-2'), 'file-text'), $link),
|
||||
div(setClass('w-24'), $mode == 'edit' ? picker(set::required(true), set::name("docVersions[$docID]"), set::items($oldDocVersions[$docID]), set::value($data->docVersions[$docID])) : "#{$data->docVersions[$docID]}"),
|
||||
$mode == 'edit' && $oldDocs[$docID]->version != $data->docVersions[$docID] ? label(setClass('ml-2 warning'), $lang->task->docSyncTips) : null,
|
||||
$mode == 'edit' && $oldDocs[$docID]->version != $data->docVersions[$docID] ? label(setClass('ml-2 warning w-80'), $lang->task->docSyncTips) : null,
|
||||
$mode == 'edit' ? input(setClass('hidden'), set::name("oldDocs[$docID]"), set::value($docID)) : null,
|
||||
$mode == 'edit' ? btn(setClass('ghost ml-2'), icon('trash'), setData(array('on' => 'click', 'call' => 'window.removeDocs', 'params' => 'event'))) : null
|
||||
);
|
||||
@@ -70,7 +70,7 @@ class docList extends wg
|
||||
set::multiple(true),
|
||||
set::maxItemsCount(50),
|
||||
set::menu(array('checkbox' => true)),
|
||||
!empty($items) ? set::toolbar(true) : null
|
||||
!empty($docs) ? set::toolbar(true) : null
|
||||
) : null,
|
||||
div(setClass('mt-2'), $docBox)
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pick-pop-admin-menu {width: 139px!important;}
|
||||
#pick-pop-admin-menu {width: 145px!important;}
|
||||
#pick-pop-admin-menu .dropmenu-list {padding: 8px;}
|
||||
#pick-pop-admin-menu .dropmenu-item + .dropmenu-item {margin-top: 4px}
|
||||
#pick-pop-admin-menu .dropmenu-item.active {background-color: unset;}
|
||||
|
||||
@@ -3,5 +3,3 @@
|
||||
|
||||
/* 半透明黄色 */
|
||||
.monaco-editor .yellow-decoration {background-color: rgba(255, 255, 0, 0.2);}
|
||||
|
||||
.monaco-editor .margin .line-numbers {padding-left: 16px;}
|
||||
|
||||
@@ -154,7 +154,7 @@ function resize(id)
|
||||
mainMenuHeight = mainMenuHeight ? mainMenuHeight : 0;
|
||||
mainNavbar = mainNavbar ? mainNavbar : 0;
|
||||
|
||||
var codeHeight = windowHeight - headerHeight - appsBarHeight - tabsHeight - appTabsHeight - mainMenuHeight - mainNavbar;
|
||||
var codeHeight = windowHeight - headerHeight - appsBarHeight - tabsHeight - appTabsHeight - mainMenuHeight - mainNavbar;
|
||||
|
||||
if(codeHeight > 0) $.cookie.set(id + 'Height', codeHeight);
|
||||
|
||||
|
||||
@@ -291,7 +291,8 @@ class StepsEditor extends zui.Component
|
||||
{
|
||||
const options = this.options;
|
||||
let $row = $rows.filter(`[data-id="${item.id}"]`);
|
||||
if(!$row.length) $row = this._createRow(item, options);
|
||||
let isNew = !$row.length;
|
||||
if(isNew) $row = this._createRow(item, options);
|
||||
|
||||
if($preRow) $row.insertAfter($preRow);
|
||||
else $list.prepend($row);
|
||||
@@ -303,20 +304,22 @@ class StepsEditor extends zui.Component
|
||||
.toggleClass('has-children', hasSub)
|
||||
.toggleClass('no-child', !hasSub)
|
||||
.removeClass('is-expired');
|
||||
$row.css('--name-indent', `${(item.level - 1) * 14}px`).find('.steps-editor-step-name').css('width', item.name.length * 12).text(item.name).css('paddingLeft', (item.level - 1) * 14);
|
||||
$row.find('.steps-editor-step-text').attr('name', `${options.name}[${item.name}]`);
|
||||
$row.find('.steps-editor-step-type').attr('name', `stepType[${item.name}]`).val(hasSub ? 'group' : (!!item.parent && item.parent != '0' ? 'item' : 'step'));
|
||||
if(options.postDataID) $row.find('.steps-editor-post-data-id').attr('name', `id[${item.name}]`);
|
||||
const $expect = $row.find('.steps-editor-step-expect').attr(
|
||||
{
|
||||
name: `${options.expectsName}[${item.name}]`,
|
||||
disabled: hasSub && options.expectDisabled ? 'disabled' : null
|
||||
}).toggleClass('disabled', hasSub && options.expectDisabled);
|
||||
$expect.parent().toggleClass('disabled', hasSub && options.expectDisabled);
|
||||
$row.find('.steps-editor-col-delete .btn').toggleClass('disabled', hasSub || this._items.length == 1);
|
||||
$row.find('.steps-editor-col-delete .btn.disabled').attr('title', options.deleteStepTip);
|
||||
$row.find('.steps-editor-col-add .btn-action[data-action="sub"]').toggleClass('disabled', item.level >= 3);
|
||||
$row.find('textarea').autoHeight();
|
||||
requestAnimationFrame(() => {
|
||||
$row.css('--name-indent', `${(item.level - 1) * 14}px`).find('.steps-editor-step-name').css('width', item.name.length * 12).text(item.name).css('paddingLeft', (item.level - 1) * 14);
|
||||
$row.find('.steps-editor-step-text').attr('name', `${options.name}[${item.name}]`);
|
||||
$row.find('.steps-editor-step-type').attr('name', `stepType[${item.name}]`).val(hasSub ? 'group' : (!!item.parent && item.parent != '0' ? 'item' : 'step'));
|
||||
if(options.postDataID) $row.find('.steps-editor-post-data-id').attr('name', `id[${item.name}]`);
|
||||
const $expect = $row.find('.steps-editor-step-expect').attr(
|
||||
{
|
||||
name: `${options.expectsName}[${item.name}]`,
|
||||
disabled: hasSub && options.expectDisabled ? 'disabled' : null
|
||||
}).toggleClass('disabled', hasSub && options.expectDisabled);
|
||||
$expect.parent().toggleClass('disabled', hasSub && options.expectDisabled);
|
||||
$row.find('.steps-editor-col-delete .btn').toggleClass('disabled', hasSub || this._items.length == 1);
|
||||
$row.find('.steps-editor-col-delete .btn.disabled').attr('title', options.deleteStepTip);
|
||||
$row.find('.steps-editor-col-add .btn-action[data-action="sub"]').toggleClass('disabled', item.level >= 3);
|
||||
if(isNew) $row.find('textarea').autoHeight({fast: true});
|
||||
});
|
||||
return $row;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ $config->action->objectNameFields['prompt'] = 'name';
|
||||
$config->action->objectNameFields['miniprogram'] = 'name';
|
||||
$config->action->objectNameFields['holiday'] = 'name';
|
||||
$config->action->objectNameFields['system'] = 'name';
|
||||
$config->action->objectNameFields['deliverable'] = 'name';
|
||||
|
||||
$config->action->commonImgSize = 870;
|
||||
|
||||
|
||||
@@ -191,6 +191,7 @@ $lang->action->objectTypes['board'] = 'Board';
|
||||
$lang->action->objectTypes['boardspace'] = 'Board Space';
|
||||
$lang->action->objectTypes['productline'] = 'Product Line';
|
||||
$lang->action->objectTypes['system'] = $lang->product->system;
|
||||
$lang->action->objectTypes['deliverable'] = 'Deliverable';
|
||||
|
||||
/* Used to describe operation history. */
|
||||
$lang->action->desc = new stdclass();
|
||||
@@ -937,6 +938,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s';
|
||||
$lang->action->label->prompt = 'Prompt|ai|promptview|id=%s';
|
||||
$lang->action->label->miniprogram = 'Mini Program|aiapp|browseminiprogram|id=%s';
|
||||
$lang->action->label->holiday = 'Holiday|holiday|browse|';
|
||||
$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s';
|
||||
|
||||
/* Object type. */
|
||||
$lang->action->search = new stdclass();
|
||||
|
||||
@@ -191,6 +191,7 @@ $lang->action->objectTypes['board'] = 'Board';
|
||||
$lang->action->objectTypes['boardspace'] = 'Board Space';
|
||||
$lang->action->objectTypes['productline'] = 'Product Line';
|
||||
$lang->action->objectTypes['system'] = $lang->product->system;
|
||||
$lang->action->objectTypes['deliverable'] = 'Deliverable';
|
||||
|
||||
/* Used to describe operation history. */
|
||||
$lang->action->desc = new stdclass();
|
||||
@@ -937,6 +938,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s';
|
||||
$lang->action->label->prompt = 'Prompt|ai|promptview|id=%s';
|
||||
$lang->action->label->miniprogram = 'Mini Program|aiapp|browseminiprogram|id=%s';
|
||||
$lang->action->label->holiday = 'Holiday|holiday|browse|';
|
||||
$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s';
|
||||
|
||||
/* Object type. */
|
||||
$lang->action->search = new stdclass();
|
||||
|
||||
@@ -191,6 +191,7 @@ $lang->action->objectTypes['board'] = 'Board';
|
||||
$lang->action->objectTypes['boardspace'] = 'Board Space';
|
||||
$lang->action->objectTypes['productline'] = 'Product Line';
|
||||
$lang->action->objectTypes['system'] = $lang->product->system;
|
||||
$lang->action->objectTypes['deliverable'] = 'Deliverable';
|
||||
|
||||
/* Used to describe operation history. */
|
||||
$lang->action->desc = new stdclass();
|
||||
@@ -937,6 +938,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s';
|
||||
$lang->action->label->prompt = 'Prompt|ai|promptview|id=%s';
|
||||
$lang->action->label->miniprogram = 'Mini Program|aiapp|browseminiprogram|id=%s';
|
||||
$lang->action->label->holiday = 'Holiday|holiday|browse|';
|
||||
$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s';
|
||||
|
||||
/* Object type. */
|
||||
$lang->action->search = new stdclass();
|
||||
|
||||
@@ -191,6 +191,7 @@ $lang->action->objectTypes['board'] = '白板';
|
||||
$lang->action->objectTypes['boardspace'] = '白板空间';
|
||||
$lang->action->objectTypes['productline'] = '产品线';
|
||||
$lang->action->objectTypes['system'] = $lang->product->system;
|
||||
$lang->action->objectTypes['deliverable'] = '交付物';
|
||||
|
||||
/* 用来描述操作历史记录。*/
|
||||
$lang->action->desc = new stdclass();
|
||||
@@ -937,6 +938,7 @@ $lang->action->label->instance = '服务|instance|view|id=%s';
|
||||
$lang->action->label->prompt = '提词|ai|promptview|id=%s';
|
||||
$lang->action->label->miniprogram = '小程序|aiapp|browseminiprogram|id=%s';
|
||||
$lang->action->label->holiday = '节假日|holiday|browse|';
|
||||
$lang->action->label->deliverable = '交付物|deliverable|view|id=%s';
|
||||
|
||||
/* Object type. */
|
||||
$lang->action->search = new stdclass();
|
||||
|
||||
+21
-7
@@ -197,7 +197,6 @@ class actionModel extends model
|
||||
$this->loadModel('workflow');
|
||||
foreach($actions as $actionID => $action)
|
||||
{
|
||||
$extra = $action->extra;
|
||||
$actionName = strtolower($action->action);
|
||||
if($this->config->edition != 'open' && !isset($flowList[$action->objectType])) $flowList[$action->objectType] = $this->workflow->getByModule($action->objectType);
|
||||
|
||||
@@ -940,6 +939,12 @@ class actionModel extends model
|
||||
foreach($actions as $action)
|
||||
{
|
||||
$item = new stdClass();
|
||||
if(strlen(trim(($action->comment))) !== 0)
|
||||
{
|
||||
$item->comment = $this->formatActionComment($action->comment);
|
||||
$currentUserCanEdit = $action->id == $endAction->id && $action->actor == $account && $action->action == 'commented';
|
||||
$item->commentEditable = $commentEditable && common::hasPriv('action', 'editComment') && $currentUserCanEdit;
|
||||
}
|
||||
|
||||
if($action->action === 'assigned' || $action->action === 'toaudit')
|
||||
{
|
||||
@@ -951,7 +956,7 @@ class actionModel extends model
|
||||
$action->actor = zget($users, $action->actor);
|
||||
if(str_contains($action->actor, ':')) $action->actor = substr($action->actor, strpos($action->actor, ':') + 1);
|
||||
|
||||
if(!empty($action->history)) $item->historyChanges = $this->renderChanges($action->objectType, $action->history);
|
||||
if(!empty($action->history)) $item->historyChanges = $this->renderChanges($action->objectType, $action->objectID, $action->history);
|
||||
|
||||
$item->id = $action->id;
|
||||
$item->action = $action->action;
|
||||
@@ -1166,7 +1171,7 @@ class actionModel extends model
|
||||
|
||||
/* If idList include ',*,' Format ',*,' to '*'. */
|
||||
$projectIdList = array();
|
||||
foreach($projectGroups as $key => $idList)
|
||||
foreach($projectGroups as $idList)
|
||||
{
|
||||
$idList = explode(',', (string)$idList);
|
||||
foreach($idList as $id) $projectIdList[] = $id;
|
||||
@@ -1452,12 +1457,13 @@ class actionModel extends model
|
||||
* Render histories of every action.
|
||||
*
|
||||
* @param string $objectType
|
||||
* @param int $objectID
|
||||
* @param array $histories
|
||||
* @param bool $canChangeTag
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function renderChanges(string $objectType, array $histories, bool $canChangeTag = true): string
|
||||
public function renderChanges(string $objectType, int $objectID, array $histories, bool $canChangeTag = true): string
|
||||
{
|
||||
if(empty($histories)) return '';
|
||||
|
||||
@@ -1507,7 +1513,14 @@ class actionModel extends model
|
||||
}
|
||||
else
|
||||
{
|
||||
$content .= sprintf($this->lang->action->desc->diff1, $history->fieldLabel, $history->old, $history->new);
|
||||
if($history->field == 'deliverable' && ($objectType == 'project' || $objectType == 'execution'))
|
||||
{
|
||||
$content .= $this->processDeliverableJson($objectType, $objectID, $history);
|
||||
}
|
||||
else
|
||||
{
|
||||
$content .= sprintf($this->lang->action->desc->diff1, $history->fieldLabel, $history->old, $history->new);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
@@ -1518,14 +1531,15 @@ class actionModel extends model
|
||||
* Print histories of every action.
|
||||
*
|
||||
* @param string $objectType
|
||||
* @param int $objectID
|
||||
* @param array $histories
|
||||
* @param bool $canChangeTag
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function printChanges(string $objectType, array $histories, bool $canChangeTag = true): void
|
||||
public function printChanges(string $objectType, int $objectID, array $histories, bool $canChangeTag = true): void
|
||||
{
|
||||
$content = $this->renderChanges($objectType, $histories, $canChangeTag);
|
||||
$content = $this->renderChanges($objectType, $objectID, $histories, $canChangeTag);
|
||||
if(is_string($content)) echo $content;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ if(!isset($config->safe->weak)) $config->safe->weak = '123456,password,12345,123
|
||||
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup');
|
||||
$config->admin->menuGroup['company'] = array('dept', 'company', 'user', 'group', 'tutorial');
|
||||
$config->admin->menuGroup['switch'] = array('admin|setmodule');
|
||||
$config->admin->menuGroup['model'] = array('auditcl', 'stage', 'design', 'cmcl', 'reviewcl', 'custom|required', 'custom|set', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting', 'custom|project');
|
||||
$config->admin->menuGroup['model'] = array('auditcl', 'stage', 'deliverable', 'design', 'cmcl', 'reviewcl', 'custom|required', 'custom|set', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting', 'custom|project');
|
||||
$config->admin->menuGroup['feature'] = array('custom|set', 'custom|product', 'custom|execution', 'custom|required', 'custom|kanban', 'measurement', 'meetingroom', 'custom|browsestoryconcept', 'custom|kanban', 'sqlbuilder', 'report', 'custom|limittaskdate', 'measurement');
|
||||
$config->admin->menuGroup['template'] = array('custom|set', 'baseline');
|
||||
$config->admin->menuGroup['message'] = array('mail', 'webhook', 'sms', 'message');
|
||||
|
||||
@@ -118,6 +118,7 @@ $lang->admin->setModule->opportunitylib = 'Opportunity Lib';
|
||||
$lang->admin->setModule->practicelib = 'Practice Lib';
|
||||
$lang->admin->setModule->componentlib = 'Component Lib';
|
||||
$lang->admin->setModule->devops = 'DevOps';
|
||||
$lang->admin->setModule->deliverable = 'Deliverable';
|
||||
$lang->admin->setModule->kanban = 'Kanban';
|
||||
$lang->admin->setModule->OA = 'OA';
|
||||
$lang->admin->setModule->deploy = 'Deploy';
|
||||
|
||||
@@ -118,6 +118,7 @@ $lang->admin->setModule->opportunitylib = 'Opportunity Lib';
|
||||
$lang->admin->setModule->practicelib = 'Practice Lib';
|
||||
$lang->admin->setModule->componentlib = 'Component Lib';
|
||||
$lang->admin->setModule->devops = 'DevOps';
|
||||
$lang->admin->setModule->deliverable = 'Deliverable';
|
||||
$lang->admin->setModule->kanban = 'Kanban';
|
||||
$lang->admin->setModule->OA = 'OA';
|
||||
$lang->admin->setModule->deploy = 'Deploy';
|
||||
|
||||
@@ -118,6 +118,7 @@ $lang->admin->setModule->opportunitylib = 'Opportunity Lib';
|
||||
$lang->admin->setModule->practicelib = 'Practice Lib';
|
||||
$lang->admin->setModule->componentlib = 'Component Lib';
|
||||
$lang->admin->setModule->devops = 'DevOps';
|
||||
$lang->admin->setModule->deliverable = 'Deliverable';
|
||||
$lang->admin->setModule->kanban = 'Kanban';
|
||||
$lang->admin->setModule->OA = 'OA';
|
||||
$lang->admin->setModule->deploy = 'Deploy';
|
||||
|
||||
@@ -74,7 +74,7 @@ $lang->admin->menuList->company['menuOrder']['5'] = 'dept';
|
||||
$lang->admin->menuList->company['menuOrder']['10'] = 'browseUser';
|
||||
$lang->admin->menuList->company['menuOrder']['15'] = 'group';
|
||||
|
||||
$lang->admin->menuList->model['subMenu']['common'] = array('link' => "{$lang->globalSetting}|custom|required|module=project", 'subModule' => 'custom,subject,holiday,stage', 'exclude' => 'stage-browse,stage-plusbrowse,stage-create,stage-edit,stage-batchcreate');
|
||||
$lang->admin->menuList->model['subMenu']['common'] = array('link' => "{$lang->globalSetting}|custom|required|module=project", 'subModule' => 'custom,subject,holiday,stage,deliverable', 'exclude' => 'stage-browse,stage-plusbrowse,stage-create,stage-edit,stage-batchcreate');
|
||||
$lang->admin->menuList->model['subMenu']['scrum'] = array('link' => "{$lang->scrumModel}|auditcl|scrumbrowse|", 'subModule' => 'auditcl');
|
||||
$lang->admin->menuList->model['subMenu']['waterfall'] = array('link' => "{$lang->waterfallModel}|stage|browse|", 'subModule' => 'stage', 'exclude' => 'stage-settype,stage-plusbrowse');
|
||||
$lang->admin->menuList->model['subMenu']['agileplus'] = array('link' => "{$lang->agilePlusModel}|auditcl|agileplusbrowse|", 'subModule' => 'auditcl');
|
||||
@@ -86,13 +86,14 @@ $lang->admin->menuList->model['menuOrder']['15'] = 'waterfall';
|
||||
$lang->admin->menuList->model['menuOrder']['20'] = 'agileplus';
|
||||
$lang->admin->menuList->model['menuOrder']['25'] = 'waterfallplus';
|
||||
|
||||
$lang->admin->menuList->model['tabMenu']['common']['project'] = array('link' => "{$lang->project->common}|custom|required|module=project", 'alias' => 'set,project', 'exclude' => 'custom-required', 'links' => array('custom|set|module=project&field=unitList'));
|
||||
if(helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['common']['stage'] = array('link' => "{$lang->stage->type}|stage|settype|", 'subModule' => 'stage');
|
||||
$lang->admin->menuList->model['tabMenu']['common']['build'] = array('link' => "{$lang->build->common}|custom|required|module=build", 'alias' => 'set', 'exclude' => 'custom');
|
||||
$lang->admin->menuList->model['tabMenu']['common']['flow'] = array('link' => "{$lang->custom->flow}|custom|flow|", 'divider' => true);
|
||||
$lang->admin->menuList->model['tabMenu']['common']['project'] = array('link' => "{$lang->project->common}|custom|required|module=project", 'alias' => 'set,project', 'exclude' => 'custom-required', 'links' => array('custom|set|module=project&field=unitList'));
|
||||
if(helper::hasFeature('waterfall') || helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['common']['stage'] = array('link' => "{$lang->stage->type}|stage|settype|", 'subModule' => 'stage');
|
||||
if(helper::hasFeature('deliverable') && ($config->edition == 'max' || $config->edition == 'ipd')) $lang->admin->menuList->model['tabMenu']['common']['deliverable'] = array('link' => "{$lang->deliverable->common}|deliverable|browse|", 'subModule' => 'deliverable');
|
||||
$lang->admin->menuList->model['tabMenu']['common']['build'] = array('link' => "{$lang->build->common}|custom|required|module=build", 'alias' => 'set', 'exclude' => 'custom');
|
||||
$lang->admin->menuList->model['tabMenu']['common']['flow'] = array('link' => "{$lang->custom->flow}|custom|flow|", 'divider' => true);
|
||||
|
||||
$lang->admin->menuList->model['tabMenu']['common']['percent'] = array('link' => "{$lang->stage->percent}|custom|percent|");
|
||||
$lang->admin->menuList->model['tabMenu']['common']['hours'] = array('link' => "{$lang->workingHour}|custom|hours|", 'subModule' => 'holiday', 'links' => array('holiday|browse|', 'custom|hours|'));
|
||||
$lang->admin->menuList->model['tabMenu']['common']['percent'] = array('link' => "{$lang->stage->percent}|custom|percent|");
|
||||
$lang->admin->menuList->model['tabMenu']['common']['hours'] = array('link' => "{$lang->workingHour}|custom|hours|", 'subModule' => 'holiday', 'links' => array('holiday|browse|', 'custom|hours|'));
|
||||
if(helper::hasFeature('waterfall')) $lang->admin->menuList->model['tabMenu']['waterfall']['stage'] = array('link' => "{$lang->stage->list}|stage|browse|", 'subModule' => 'stage', 'exclude' => 'stage-plusbrowse');
|
||||
if(helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['waterfallplus']['stage'] = array('link' => "{$lang->stage->list}|stage|plusbrowse|", 'subModule' => 'stage', 'exclude' => 'stage-browse');
|
||||
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['5'] = 'project';
|
||||
@@ -103,6 +104,10 @@ $lang->admin->menuList->model['tabMenu']['menuOrder']['common']['40'] = 'p
|
||||
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['45'] = 'hours';
|
||||
if(helper::hasFeature('waterfall')) $lang->admin->menuList->model['tabMenu']['menuOrder']['waterfall']['5'] = 'stage';
|
||||
if(helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['menuOrder']['waterfallplus']['5'] = 'stage';
|
||||
if($config->edition == 'max' or $config->edition == 'ipd')
|
||||
{
|
||||
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['15'] = 'deliverable';
|
||||
}
|
||||
|
||||
$lang->admin->menuList->feature['subMenu']['my'] = array('link' => "{$lang->my->common}|custom|set|module=todo&field=priList", 'exclude' => 'set,required');
|
||||
$lang->admin->menuList->feature['subMenu']['product'] = array('link' => "{$lang->productCommon}|custom|required|module=product", 'exclude' => 'set,required', 'alias' => 'browsestoryconcept,product');
|
||||
|
||||
@@ -118,6 +118,7 @@ $lang->admin->setModule->opportunitylib = '机会库';
|
||||
$lang->admin->setModule->practicelib = '最佳实践库';
|
||||
$lang->admin->setModule->componentlib = '组件库';
|
||||
$lang->admin->setModule->devops = 'DevOps';
|
||||
$lang->admin->setModule->deliverable = '交付物';
|
||||
$lang->admin->setModule->kanban = '通用看板';
|
||||
$lang->admin->setModule->OA = '办公';
|
||||
$lang->admin->setModule->deploy = '运维';
|
||||
@@ -170,7 +171,7 @@ $lang->admin->menuSetting['user']['name'] = '人员管理';
|
||||
$lang->admin->menuSetting['user']['desc'] = '维护部门、添加人员、分组配置权限。';
|
||||
$lang->admin->menuSetting['switch']['name'] = '功能开关';
|
||||
$lang->admin->menuSetting['switch']['desc'] = '打开、关闭系统部分功能。';
|
||||
$lang->admin->menuSetting['model']['name'] = '模型配置';
|
||||
$lang->admin->menuSetting['model']['name'] = '项目模型配置';
|
||||
$lang->admin->menuSetting['model']['desc'] = '不同项目管理模型和项目通用要素配置。';
|
||||
$lang->admin->menuSetting['feature']['name'] = '功能配置';
|
||||
$lang->admin->menuSetting['feature']['desc'] = '按照功能菜单进行系统的要素配置。';
|
||||
|
||||
@@ -34,14 +34,14 @@ $config->bug->list->allFields = 'id, module, execution, story, task,
|
||||
$config->bug->list->defaultFields = 'id,title,severity,pri,openedBy,assignedTo,resolvedBy,resolution';
|
||||
$config->bug->list->customCreateFields = 'execution,noticefeedbackBy,story,task,pri,severity,os,browser,deadline,mailto,keywords';
|
||||
$config->bug->list->customBatchEditFields = 'type,severity,pri,productplan,assignedTo,deadline,resolvedBy,resolution,os,browser,keywords';
|
||||
$config->bug->list->customBatchCreateFields = 'project,execution,steps,type,pri,deadline,severity,os,browser,keywords';
|
||||
$config->bug->list->customBatchCreateFields = 'project,execution,plan,steps,type,pri,deadline,severity,os,browser,keywords';
|
||||
|
||||
$config->bug->custom = new stdclass();
|
||||
$config->bug->custom->createFields = $config->bug->list->customCreateFields;
|
||||
$config->bug->custom->batchCreateFields = 'project,execution,deadline,steps,type,pri,severity,os,browser,%s';
|
||||
$config->bug->custom->batchEditFields = 'type,severity,pri,assignedTo,deadline,status,resolvedBy,resolution';
|
||||
|
||||
$config->bug->exportFields = 'id, product, branch, module, project, execution, story, task,
|
||||
$config->bug->exportFields = 'id, product, branch, module, project, execution, plan, story, task,
|
||||
title, keywords, severity, pri, type, os, browser,
|
||||
steps, status, deadline, activatedCount, confirmed, mailto,
|
||||
openedBy, openedDate, openedBuild,
|
||||
|
||||
@@ -10,6 +10,7 @@ $config->bug->form->create['openedBuild'] = array('required' => true, 'type' =>
|
||||
$config->bug->form->create['product'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->create['branch'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->create['module'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->create['plan'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->create['project'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->create['execution'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->create['assignedTo'] = array('required' => false, 'type' => 'string', 'default' => '');
|
||||
@@ -159,6 +160,7 @@ $config->bug->form->batchCreate['story'] = array('required' => false, 'typ
|
||||
$config->bug->form->batchCreate['branch'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->batchCreate['laneID'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->batchCreate['openedBuild'] = array('required' => true, 'type' => 'array', 'default' => '', 'filter' => 'join');
|
||||
$config->bug->form->batchCreate['plan'] = array('required' => false, 'type' => 'int', 'default' => 0);
|
||||
$config->bug->form->batchCreate['title'] = array('required' => true, 'type' => 'string', 'default' => '', 'base' => true);
|
||||
$config->bug->form->batchCreate['deadline'] = array('required' => false, 'type' => 'date', 'default' => null);
|
||||
$config->bug->form->batchCreate['steps'] = array('required' => false, 'type' => 'string', 'default' => '', 'control' => 'editor');
|
||||
|
||||
@@ -70,11 +70,12 @@ $config->bug->dtable->fieldList['execution']['group'] = 3;
|
||||
$config->bug->dtable->fieldList['execution']['dataSource'] = array('module' => 'execution', 'method' =>'getPairs', 'params' => ['projectID' => 0]);
|
||||
$config->bug->dtable->fieldList['execution']['sortType'] = true;
|
||||
|
||||
$config->bug->dtable->fieldList['plan']['title'] = $lang->bug->plan;
|
||||
$config->bug->dtable->fieldList['plan']['width'] = 120;
|
||||
$config->bug->dtable->fieldList['plan']['group'] = 3;
|
||||
$config->bug->dtable->fieldList['plan']['sortType'] = true;
|
||||
$config->bug->dtable->fieldList['plan']['hint'] = true;
|
||||
$config->bug->dtable->fieldList['plan']['title'] = $lang->bug->plan;
|
||||
$config->bug->dtable->fieldList['plan']['width'] = 120;
|
||||
$config->bug->dtable->fieldList['plan']['group'] = 3;
|
||||
$config->bug->dtable->fieldList['plan']['sortType'] = true;
|
||||
$config->bug->dtable->fieldList['plan']['hint'] = true;
|
||||
$config->bug->dtable->fieldList['plan']['dataSource'] = array('module' => 'productplan', 'method' =>'getPairs', 'params' => ['productIdList' => '$productIdList', 'branch' => '$branch']);
|
||||
|
||||
$config->bug->dtable->fieldList['openedBuild']['title'] = $lang->bug->openedBuild;
|
||||
$config->bug->dtable->fieldList['openedBuild']['type'] = 'text';
|
||||
|
||||
+11
-1
@@ -646,7 +646,17 @@ class bug extends control
|
||||
{
|
||||
/* 设置bug导出的参数。 */
|
||||
/* Set bug export params. */
|
||||
$this->session->set('bugTransferParams', array('productID' => $productID, 'executionID' => $executionID, 'branch' => 'all'));
|
||||
if($executionID)
|
||||
{
|
||||
$productIdList = $this->loadModel('product')->getProductPairsByProject($executionID);
|
||||
$productIdList = array_keys($productIdList);
|
||||
}
|
||||
else
|
||||
{
|
||||
$productIdList = array($productID);
|
||||
}
|
||||
|
||||
$this->session->set('bugTransferParams', array('productID' => $productID, 'executionID' => $executionID, 'branch' => 'all', 'productIdList' => $productIdList));
|
||||
|
||||
/* 设置导出数据源。 */
|
||||
/* Set export data source. */
|
||||
|
||||
@@ -40,3 +40,9 @@ function linkBug()
|
||||
|
||||
openUrl(link, {load: 'modal', size: 'lg'});
|
||||
}
|
||||
|
||||
function setDuplicateBug()
|
||||
{
|
||||
const productID = $('[name=product]').val();
|
||||
loadProductBugs(productID, bug.id);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class bugModel extends model
|
||||
if(!empty($bug->assignedTo)) $this->action->create('bug', $bugID, 'Assigned', '', $bug->assignedTo);
|
||||
|
||||
/* Add score for create. */
|
||||
$files = $this->loadModel('file')->saveUpload('bug', $bugID);
|
||||
$this->loadModel('file')->saveUpload('bug', $bugID);
|
||||
if(!empty($bug->case))
|
||||
{
|
||||
$this->loadModel('score')->create('bug', 'createFormCase', $bug->case);
|
||||
@@ -344,7 +344,7 @@ class bugModel extends model
|
||||
if($changes) $this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
|
||||
if($this->config->edition != 'open')
|
||||
if($this->config->edition != 'open' && $this->app->rawMethod != 'batchedit')
|
||||
{
|
||||
if($oldBug->story > 0 || $oldBug->task > 0 || $oldBug->case > 0)
|
||||
{
|
||||
@@ -771,7 +771,7 @@ class bugModel extends model
|
||||
$this->config->bug->search['queryID'] = $queryID;
|
||||
$this->config->bug->search['params']['project']['values'] = $projectParams;
|
||||
$this->config->bug->search['params']['product']['values'] = $productParams;
|
||||
$this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($productID);
|
||||
$this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($productID, '', '', true);
|
||||
$this->config->bug->search['params']['module']['values'] = $modules;
|
||||
$this->config->bug->search['params']['execution']['values'] = $this->loadModel('product')->getExecutionPairsByProduct($productID, '0', (int)$projectID);
|
||||
$this->config->bug->search['params']['severity']['values'] = array(0 => '') + $this->lang->bug->severityList; //Fix bug #939.
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace zin;
|
||||
|
||||
$defaultBug = array('module' => $moduleID, 'project' => $projectID, 'execution' => $executionID, 'openedBuild' => 'trunk', 'pri' => 3, 'severity' => 3);
|
||||
if($product->type != 'normal') $defaultBug['branch'] = $branch;
|
||||
if($product->shadow) unset($customFields['plan']);
|
||||
if(isset($executionType) && $executionType == 'kanban')
|
||||
{
|
||||
$defaultBug['region'] = $regionID;
|
||||
@@ -64,6 +65,9 @@ $items[] = array
|
||||
/* Field of openedBuild. */
|
||||
$items[] = array('name' => 'openedBuild', 'label' => $lang->bug->openedBuild, 'control' => 'picker', 'items' => $builds, 'value' => 'trunk', 'multiple' => true, 'width' => '200px', 'required' => true, 'ditto' => true);
|
||||
|
||||
/* Field of plan. */
|
||||
if(!$product->shadow) $items[] = array('name' => 'plan', 'label' => $lang->bug->plan, 'control' => 'picker', 'items' => $plans, 'value' => '', 'width' => '200px', 'required' => isset($requiredFields['plan']), 'ditto' => true);
|
||||
|
||||
/* Field of title. */
|
||||
$items[] = array( 'name' => 'title', 'label' => $lang->bug->title, 'width' => '240px', 'required' => true, 'control' => 'colorInput');
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ if(data('executionType') && data('executionType') == 'kanban')
|
||||
}
|
||||
|
||||
$fields->field('title')
|
||||
->className('full:w-full')
|
||||
->control('colorInput', array('colorValue' => data('bug.color')));
|
||||
|
||||
$fields->field('type')
|
||||
|
||||
@@ -27,6 +27,13 @@ $fields->field('execution')
|
||||
->className($isShadowProduct && $isOriginalProduct ? 'full:w-1/2' : 'full:w-1/4')
|
||||
->foldable(!$isShadowProduct);
|
||||
|
||||
$fields->field('plan')
|
||||
->label($lang->bug->plan)
|
||||
->className('w-1/2 full:w-1/2')
|
||||
->hidden($noMultipleProject)
|
||||
->items(data('plans'))
|
||||
->foldable();
|
||||
|
||||
if(common::hasPriv('build', 'create'))
|
||||
{
|
||||
$fields->field('openedBuild')
|
||||
|
||||
@@ -24,7 +24,8 @@ $fields->autoLoad('branch', 'module,execution,project,story,task,assignedTo')
|
||||
->autoLoad('allUsers', 'assignedTo')
|
||||
->autoLoad('region', 'lane');
|
||||
|
||||
if(!$product->shadow) $fields->fullModeOrders('module,project,execution');
|
||||
if(!$product->shadow) $fields->fullModeOrders('module,project,execution,plan', 'pri,title');
|
||||
$fields->sort('execution,plan');
|
||||
|
||||
jsVar('bug', $bug);
|
||||
jsVar('moduleID', $bug->moduleID);
|
||||
|
||||
@@ -517,7 +517,8 @@ detailBody
|
||||
set::name($lang->bug->duplicateBug),
|
||||
picker
|
||||
(
|
||||
set::items($productBugs),
|
||||
on::click('setDuplicateBug'),
|
||||
set::items(array()),
|
||||
set::name('duplicateBug'),
|
||||
set::value($bug->duplicateBug)
|
||||
)
|
||||
|
||||
+16
-6
@@ -379,6 +379,7 @@ class bugZen extends bug
|
||||
protected function getExportFields(int $executionID, object|bool $product): string
|
||||
{
|
||||
$exportFields = str_replace(' ', '', $this->config->bug->exportFields);
|
||||
$isShadow = false;
|
||||
if(isset($product->type) and $product->type == 'normal') $exportFields = str_replace(',branch,', ',', ",{$exportFields},");;
|
||||
if(!$product)
|
||||
{
|
||||
@@ -387,9 +388,16 @@ class bugZen extends bug
|
||||
foreach($products as $product)
|
||||
{
|
||||
if($product->type != 'normal') $hasBranch = true;
|
||||
if(!empty($product->shadow)) $isShadow = true;
|
||||
}
|
||||
if(!$hasBranch) $exportFields = str_replace(',branch,', ',', ",{$exportFields},");
|
||||
}
|
||||
else
|
||||
{
|
||||
$isShadow = $product->shadow;
|
||||
}
|
||||
|
||||
if($isShadow) $exportFields = str_replace(',plan,', ',', ",{$exportFields},");
|
||||
if($this->app->tab == 'project' or $this->app->tab == 'execution')
|
||||
{
|
||||
$execution = $this->loadModel('execution')->getByID($executionID);
|
||||
@@ -1104,6 +1112,7 @@ class bugZen extends bug
|
||||
$this->view->branchID = $bug->branch != 'all' ? $bug->branch : '0';
|
||||
$this->view->cases = $this->loadModel('testcase')->getPairsByProduct($this->session->product, array(0, $this->view->branchID));
|
||||
$this->view->copyBugID = isset($bugID) ? $bugID : 0;
|
||||
$this->view->plans = $this->loadModel('productplan')->getPairs($bug->productID, $bug->branch, 'noclosed', true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1133,8 +1142,6 @@ class bugZen extends bug
|
||||
/* Get bugs of current product. */
|
||||
$branch = '';
|
||||
if($product->type == 'branch') $branch = $bug->branch > 0 ? "{$bug->branch},0" : '0';
|
||||
$productBugs = $this->bug->getProductBugPairs($bug->product, $branch);
|
||||
unset($productBugs[$bug->id]);
|
||||
|
||||
/* Get execution pairs. */
|
||||
$unAllowedStage = array('request', 'design', 'review');
|
||||
@@ -1156,7 +1163,7 @@ class bugZen extends bug
|
||||
if($product->type != 'normal') $branchTagOption = $this->getBranchOptions($product->id);
|
||||
if($this->config->edition == 'max') $this->view->injectionList = $this->view->identifyList = $this->loadModel('review')->getPairs($bug->project, $bug->product, true);
|
||||
|
||||
$this->assignVarsForEdit($bug);
|
||||
$this->assignVarsForEdit($bug, $product);
|
||||
|
||||
$this->view->title = $this->lang->bug->edit . "BUG #$bug->id $bug->title - " . $this->products[$bug->product];
|
||||
$this->view->bug = $bug;
|
||||
@@ -1165,7 +1172,6 @@ class bugZen extends bug
|
||||
$this->view->projectID = $bug->project;
|
||||
$this->view->projects = $projects;
|
||||
$this->view->executions = $executions;
|
||||
$this->view->productBugs = $productBugs;
|
||||
$this->view->branchTagOption = $branchTagOption;
|
||||
$this->view->projectExecutionPairs = $this->loadModel('project')->getProjectExecutionPairs();
|
||||
}
|
||||
@@ -1175,10 +1181,11 @@ class bugZen extends bug
|
||||
* Assign variables for editing bug.
|
||||
*
|
||||
* @param object $bug
|
||||
* @param object $product
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function assignVarsForEdit(object $bug): void
|
||||
protected function assignVarsForEdit(object $bug, object $product): void
|
||||
{
|
||||
/* Add product related to the bug when it is not in the products. */
|
||||
if(!isset($this->products[$bug->product]))
|
||||
@@ -1401,6 +1408,7 @@ class bugZen extends bug
|
||||
$this->view->branch = $branch;
|
||||
$this->view->branches = $branches;
|
||||
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($product->id, 'bug', 0, $branch === 'all' ? 'all' : (string)$branch);
|
||||
$this->view->plans = $this->loadModel('productplan')->getPairs($product->id, $branch, 'noclosed', true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2450,7 +2458,9 @@ class bugZen extends bug
|
||||
$fields = array('projectID' => $bugInfo->project, 'moduleID' => $bugInfo->module, 'executionID' => $bugInfo->execution, 'taskID' => $bugInfo->task, 'storyID' => $isSameProduct ? $bugInfo->story : 0, 'buildID' => $bugInfo->openedBuild,
|
||||
'caseID' => $bugInfo->case, 'title' => $bugInfo->title, 'steps' => $bugInfo->steps, 'severity' => $bugInfo->severity, 'type' => $bugInfo->type, 'assignedTo' => $bugInfo->assignedTo, 'deadline' => (helper::isZeroDate($bugInfo->deadline) ? '' : $bugInfo->deadline),
|
||||
'os' => $bugInfo->os, 'browser' => $bugInfo->browser, 'mailto' => $bugInfo->mailto, 'keywords' => $bugInfo->keywords, 'color' => $bugInfo->color, 'testtask' => $bugInfo->testtask, 'feedbackBy' => $bugInfo->feedbackBy, 'notifyEmail' => $bugInfo->notifyEmail,
|
||||
'pri' => ($bugInfo->pri == 0 ? 3 : $bugInfo->pri));
|
||||
'pri' => ($bugInfo->pri == 0 ? 3 : $bugInfo->pri),
|
||||
'plan' => $bugInfo->plan
|
||||
);
|
||||
|
||||
$bug = $this->updateBug($bug, $fields);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
$lang->build->common = "Build";
|
||||
$lang->build->browse = "Build List";
|
||||
$lang->build->create = "Build erstellen";
|
||||
$lang->build->edit = "Bearbeiten";
|
||||
$lang->build->linkStory = "{$lang->SRCommon} verknüpfen";
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
$lang->build->common = "Build";
|
||||
$lang->build->browse = "Build List";
|
||||
$lang->build->create = "Create Build";
|
||||
$lang->build->edit = "Edit Build";
|
||||
$lang->build->linkStory = "Link {$lang->SRCommon}";
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
$lang->build->common = "Build";
|
||||
$lang->build->browse = "Liste Build";
|
||||
$lang->build->create = "Créer Build";
|
||||
$lang->build->edit = "Editer Build";
|
||||
$lang->build->linkStory = "Intégrer {$lang->SRCommon}";
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
$lang->build->common = "构建";
|
||||
$lang->build->browse = "构建列表";
|
||||
$lang->build->create = "创建构建";
|
||||
$lang->build->edit = "编辑构建";
|
||||
$lang->build->linkStory = "关联{$lang->SRCommon}";
|
||||
|
||||
@@ -245,8 +245,8 @@ class caselibZen extends caselib
|
||||
|
||||
if(empty($rows))
|
||||
{
|
||||
$row->typeValue = join("\n", $this->lang->testcase->typeList);
|
||||
$row->stageValue = join("\n", $this->lang->testcase->stageList);
|
||||
$row->typeValue = implode("\n", $this->lang->testcase->typeList);
|
||||
$row->stageValue = implode("\n", $this->lang->testcase->stageList);
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
@@ -444,7 +444,7 @@ class caselibZen extends caselib
|
||||
|
||||
$case->stage = array();
|
||||
foreach($stages as $stage) $case->stage[] = array_search($stage, $this->lang->testcase->stageList);
|
||||
$case->stage = join(',', $case->stage);
|
||||
$case->stage = implode(',', $case->stage);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -558,7 +558,6 @@ class caselibZen extends caselib
|
||||
{
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
$relatedModules = $this->loadModel('tree')->getModulePairs($libID, 'caselib');
|
||||
$relatedCases = $this->loadModel('testcase')->getRelatedCases($cases);
|
||||
$relatedSteps = $this->testcase->getRelatedSteps(array_keys($cases));
|
||||
$relatedFiles = $this->testcase->getRelatedFiles(array_keys($cases));
|
||||
|
||||
@@ -655,7 +654,7 @@ class caselibZen extends caselib
|
||||
{
|
||||
$case->stage = explode(',', $case->stage);
|
||||
foreach($case->stage as $key => $stage) $case->stage[$key] = isset($this->lang->testcase->stageList[$stage]) ? $this->lang->testcase->stageList[$stage] : $stage;
|
||||
$case->stage = join("\n", $case->stage);
|
||||
$case->stage = implode("\n", $case->stage);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -697,6 +696,6 @@ class caselibZen extends caselib
|
||||
$linkCaseID = trim($linkCaseID);
|
||||
$tmpLinkCases[] = isset($relatedCases[$linkCaseID]) ? $relatedCases[$linkCaseID] . "(#$linkCaseID)" : $linkCaseID;
|
||||
}
|
||||
$case->linkCase = join("; \n", $tmpLinkCases);
|
||||
$case->linkCase = implode("; \n", $tmpLinkCases);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,8 +250,28 @@ class cneModel extends model
|
||||
|
||||
$result = $this->apiGet('/api/cne/statistics/cluster', array('cluster' => ''), $this->config->CNE->api->headers);
|
||||
if($result->code != 200) return $statistics;
|
||||
$nodes = $this->apiGet('/api/cne/cluster/nodes', array('cluster' => ''), $this->config->CNE->api->headers);
|
||||
if($nodes->code != 200) return $statistics;
|
||||
|
||||
$readyNode = 0;
|
||||
$nodeError = '';
|
||||
foreach($nodes->data as $node)
|
||||
{
|
||||
if($node->ready)
|
||||
{
|
||||
$readyNode++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(empty($node->issues)) continue;
|
||||
$nodeError .= sprintf($this->lang->system->nodeNotice, $node->name, implode(',', array_unique($node->issues))) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
$statistics = $result->data;
|
||||
$statistics->node_count = count($nodes->data);
|
||||
$statistics->ready_count = $readyNode;
|
||||
$statistics->nodeError = $nodeError;
|
||||
$statistics->metrics->cpu->usage = max(round($statistics->metrics->cpu->usage, 4), 0);
|
||||
$statistics->metrics->cpu->capacity = max(round($statistics->metrics->cpu->capacity, 4), $statistics->metrics->cpu->usage);
|
||||
$statistics->metrics->cpu->allocatable = round($statistics->metrics->cpu->allocatable, 4);
|
||||
|
||||
@@ -7,8 +7,11 @@ title=测试 cneModel->cneMetrics();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 获取CNE平台的集群度量属性status @normal
|
||||
|
||||
- 获取CNE平台的集群度量状态属性status @unknown
|
||||
- 获取CNE平台的集群cpu数据属性usage @0
|
||||
- 获取CNE平台的集群cpu数据属性capacity @0
|
||||
- 获取CNE平台的集群内存属性usage @0
|
||||
- 获取CNE平台的集群内存属性capacity @0
|
||||
*/
|
||||
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
@@ -20,4 +23,8 @@ $config->CNE->app->domain = 'g79n.corp.cc';
|
||||
|
||||
$cneModel = $tester->loadModel('cne');
|
||||
|
||||
r($cneModel->cneMetrics()) && p('status') && e('normal'); // 获取CNE平台的集群度量
|
||||
r($cneModel->cneMetrics()) && p('status') && e('unknown'); // 获取CNE平台的集群度量状态
|
||||
r($cneModel->cneMetrics()->metrics->cpu) && p('usage') && e('0'); // 获取CNE平台的集群cpu数据
|
||||
r($cneModel->cneMetrics()->metrics->cpu) && p('capacity') && e('0'); // 获取CNE平台的集群cpu数据
|
||||
r($cneModel->cneMetrics()->metrics->memory) && p('usage') && e('0'); // 获取CNE平台的集群内存
|
||||
r($cneModel->cneMetrics()->metrics->memory) && p('capacity') && e('0'); // 获取CNE平台的集群内存
|
||||
|
||||
@@ -105,8 +105,12 @@ $lang->ops = new stdclass();
|
||||
$lang->domain = new stdclass();
|
||||
$lang->service = new stdclass();
|
||||
$lang->deployment = new stdclass();
|
||||
$lang->deliverable = new stdclass();
|
||||
$lang->metric = new stdclass();
|
||||
|
||||
$lang->projectDeliverable = new stdclass();
|
||||
$lang->executionDeliverable = new stdclass();
|
||||
|
||||
$lang->ai = new stdclass();
|
||||
$lang->aiapp = new stdclass();
|
||||
|
||||
|
||||
+34
-31
@@ -289,37 +289,40 @@ $lang->aiapp->common = 'AI';
|
||||
$lang->product->system = 'Application';
|
||||
$lang->configure->common = 'Configure';
|
||||
|
||||
$lang->programstakeholder->common = 'Stakeholder';
|
||||
$lang->featureswitch->common = 'Features On/Off';
|
||||
$lang->importdata->common = 'Import data';
|
||||
$lang->systemsetting->common = 'System setting';
|
||||
$lang->staffmanage->common = 'User management';
|
||||
$lang->modelconfig->common = 'Pattern setting';
|
||||
$lang->featureconfig->common = 'Features config';
|
||||
$lang->doctemplate->common = 'Doc template';
|
||||
$lang->notifysetting->common = 'Notification';
|
||||
$lang->bidesign->common = 'BI design';
|
||||
$lang->personalsettings->common = 'Personal setting ';
|
||||
$lang->projectsettings->common = 'Setting';
|
||||
$lang->dataaccess->common = 'Data permission';
|
||||
$lang->executiongantt->common = 'Gantt chart';
|
||||
$lang->executionkanban->common = 'Kanban';
|
||||
$lang->executionburn->common = 'Burndown chart';
|
||||
$lang->executioncfd->common = 'Cumulative Flow Diagram';
|
||||
$lang->executionstory->common = 'Story';
|
||||
$lang->executionqa->common = 'QA';
|
||||
$lang->executionsettings->common = 'Setting';
|
||||
$lang->generalcomment->common = 'Comment';
|
||||
$lang->generalping->common = 'Timeout prevention';
|
||||
$lang->generaltemplate->common = 'Template';
|
||||
$lang->generaleffort->common = 'General log';
|
||||
$lang->productsettings->common = 'Product setting';
|
||||
$lang->projectreview->common = 'Review';
|
||||
$lang->projecttrack->common = 'Matrix';
|
||||
$lang->projectqa->common = 'QA';
|
||||
$lang->holidayseason->common = 'Holiday';
|
||||
$lang->codereview->common = 'Review';
|
||||
$lang->repocode->common = 'Code';
|
||||
$lang->programstakeholder->common = 'Stakeholder';
|
||||
$lang->featureswitch->common = 'Features On/Off';
|
||||
$lang->importdata->common = 'Import data';
|
||||
$lang->systemsetting->common = 'System setting';
|
||||
$lang->staffmanage->common = 'User management';
|
||||
$lang->modelconfig->common = 'Pattern setting';
|
||||
$lang->featureconfig->common = 'Features config';
|
||||
$lang->doctemplate->common = 'Doc template';
|
||||
$lang->notifysetting->common = 'Notification';
|
||||
$lang->bidesign->common = 'BI design';
|
||||
$lang->personalsettings->common = 'Personal setting ';
|
||||
$lang->projectsettings->common = 'Setting';
|
||||
$lang->dataaccess->common = 'Data permission';
|
||||
$lang->executiongantt->common = 'Gantt chart';
|
||||
$lang->executionkanban->common = 'Kanban';
|
||||
$lang->executionburn->common = 'Burndown chart';
|
||||
$lang->executioncfd->common = 'Cumulative Flow Diagram';
|
||||
$lang->executionstory->common = 'Story';
|
||||
$lang->executionqa->common = 'QA';
|
||||
$lang->executionsettings->common = 'Setting';
|
||||
$lang->generalcomment->common = 'Comment';
|
||||
$lang->generalping->common = 'Timeout prevention';
|
||||
$lang->generaltemplate->common = 'Template';
|
||||
$lang->generaleffort->common = 'General log';
|
||||
$lang->productsettings->common = 'Product setting';
|
||||
$lang->projectreview->common = 'Review';
|
||||
$lang->projecttrack->common = 'Matrix';
|
||||
$lang->projectqa->common = 'QA';
|
||||
$lang->holidayseason->common = 'Holiday';
|
||||
$lang->codereview->common = 'Review';
|
||||
$lang->repocode->common = 'Code';
|
||||
$lang->deliverable->common = 'Deliverable';
|
||||
$lang->projectDeliverable->common = 'Project Deliverable';
|
||||
$lang->executionDeliverable->common = 'Execution Deliverable';
|
||||
|
||||
$lang->personnel->common = 'Member';
|
||||
$lang->personnel->invest = 'Investment';
|
||||
|
||||
+34
-31
@@ -289,37 +289,40 @@ $lang->aiapp->common = 'AI';
|
||||
$lang->product->system = 'Application';
|
||||
$lang->configure->common = 'Configure';
|
||||
|
||||
$lang->programstakeholder->common = 'Stakeholder';
|
||||
$lang->featureswitch->common = 'Features On/Off';
|
||||
$lang->importdata->common = 'Import data';
|
||||
$lang->systemsetting->common = 'System setting';
|
||||
$lang->staffmanage->common = 'User management';
|
||||
$lang->modelconfig->common = 'Pattern setting';
|
||||
$lang->featureconfig->common = 'Features config';
|
||||
$lang->doctemplate->common = 'Doc template';
|
||||
$lang->notifysetting->common = 'Notification';
|
||||
$lang->bidesign->common = 'BI design';
|
||||
$lang->personalsettings->common = 'Personal setting ';
|
||||
$lang->projectsettings->common = 'Setting';
|
||||
$lang->dataaccess->common = 'Data permission';
|
||||
$lang->executiongantt->common = 'Gantt chart';
|
||||
$lang->executionkanban->common = 'Kanban';
|
||||
$lang->executionburn->common = 'Burndown chart';
|
||||
$lang->executioncfd->common = 'Cumulative Flow Diagram';
|
||||
$lang->executionstory->common = 'Story';
|
||||
$lang->executionqa->common = 'QA';
|
||||
$lang->executionsettings->common = 'Setting';
|
||||
$lang->generalcomment->common = 'Comment';
|
||||
$lang->generalping->common = 'Timeout prevention';
|
||||
$lang->generaltemplate->common = 'Template';
|
||||
$lang->generaleffort->common = 'General log';
|
||||
$lang->productsettings->common = 'Product setting';
|
||||
$lang->projectreview->common = 'Review';
|
||||
$lang->projecttrack->common = 'Matrix';
|
||||
$lang->projectqa->common = 'QA';
|
||||
$lang->holidayseason->common = 'Holiday';
|
||||
$lang->codereview->common = 'Review';
|
||||
$lang->repocode->common = 'Code';
|
||||
$lang->programstakeholder->common = 'Stakeholder';
|
||||
$lang->featureswitch->common = 'Features On/Off';
|
||||
$lang->importdata->common = 'Import data';
|
||||
$lang->systemsetting->common = 'System setting';
|
||||
$lang->staffmanage->common = 'User management';
|
||||
$lang->modelconfig->common = 'Pattern setting';
|
||||
$lang->featureconfig->common = 'Features config';
|
||||
$lang->doctemplate->common = 'Doc template';
|
||||
$lang->notifysetting->common = 'Notification';
|
||||
$lang->bidesign->common = 'BI design';
|
||||
$lang->personalsettings->common = 'Personal setting ';
|
||||
$lang->projectsettings->common = 'Setting';
|
||||
$lang->dataaccess->common = 'Data permission';
|
||||
$lang->executiongantt->common = 'Gantt chart';
|
||||
$lang->executionkanban->common = 'Kanban';
|
||||
$lang->executionburn->common = 'Burndown chart';
|
||||
$lang->executioncfd->common = 'Cumulative Flow Diagram';
|
||||
$lang->executionstory->common = 'Story';
|
||||
$lang->executionqa->common = 'QA';
|
||||
$lang->executionsettings->common = 'Setting';
|
||||
$lang->generalcomment->common = 'Comment';
|
||||
$lang->generalping->common = 'Timeout prevention';
|
||||
$lang->generaltemplate->common = 'Template';
|
||||
$lang->generaleffort->common = 'General log';
|
||||
$lang->productsettings->common = 'Product setting';
|
||||
$lang->projectreview->common = 'Review';
|
||||
$lang->projecttrack->common = 'Matrix';
|
||||
$lang->projectqa->common = 'QA';
|
||||
$lang->holidayseason->common = 'Holiday';
|
||||
$lang->codereview->common = 'Review';
|
||||
$lang->repocode->common = 'Code';
|
||||
$lang->deliverable->common = 'Deliverable';
|
||||
$lang->projectDeliverable->common = 'Project Deliverable';
|
||||
$lang->executionDeliverable->common = 'Execution Deliverable';
|
||||
|
||||
$lang->personnel->common = 'Member';
|
||||
$lang->personnel->invest = 'Investment';
|
||||
|
||||
+34
-31
@@ -289,37 +289,40 @@ $lang->aiapp->common = 'AI';
|
||||
$lang->product->system = 'Application';
|
||||
$lang->configure->common = 'Configure';
|
||||
|
||||
$lang->programstakeholder->common = 'Stakeholder';
|
||||
$lang->featureswitch->common = 'Features On/Off';
|
||||
$lang->importdata->common = 'Import data';
|
||||
$lang->systemsetting->common = 'System setting';
|
||||
$lang->staffmanage->common = 'User management';
|
||||
$lang->modelconfig->common = 'Pattern setting';
|
||||
$lang->featureconfig->common = 'Features config';
|
||||
$lang->doctemplate->common = 'Doc template';
|
||||
$lang->notifysetting->common = 'Notification';
|
||||
$lang->bidesign->common = 'BI design';
|
||||
$lang->personalsettings->common = 'Personal setting ';
|
||||
$lang->projectsettings->common = 'Setting';
|
||||
$lang->dataaccess->common = 'Data permission';
|
||||
$lang->executiongantt->common = 'Gantt chart';
|
||||
$lang->executionkanban->common = 'Kanban';
|
||||
$lang->executionburn->common = 'Burndown chart';
|
||||
$lang->executioncfd->common = 'Cumulative Flow Diagram';
|
||||
$lang->executionstory->common = 'Story';
|
||||
$lang->executionqa->common = 'QA';
|
||||
$lang->executionsettings->common = 'Setting';
|
||||
$lang->generalcomment->common = 'Comment';
|
||||
$lang->generalping->common = 'Timeout prevention';
|
||||
$lang->generaltemplate->common = 'Template';
|
||||
$lang->generaleffort->common = 'General log';
|
||||
$lang->productsettings->common = 'Product setting';
|
||||
$lang->projectreview->common = 'Review';
|
||||
$lang->projecttrack->common = 'Matrix';
|
||||
$lang->projectqa->common = 'QA';
|
||||
$lang->holidayseason->common = 'Holiday';
|
||||
$lang->codereview->common = 'Review';
|
||||
$lang->repocode->common = 'Code';
|
||||
$lang->programstakeholder->common = 'Stakeholder';
|
||||
$lang->featureswitch->common = 'Features On/Off';
|
||||
$lang->importdata->common = 'Import data';
|
||||
$lang->systemsetting->common = 'System setting';
|
||||
$lang->staffmanage->common = 'User management';
|
||||
$lang->modelconfig->common = 'Pattern setting';
|
||||
$lang->featureconfig->common = 'Features config';
|
||||
$lang->doctemplate->common = 'Doc template';
|
||||
$lang->notifysetting->common = 'Notification';
|
||||
$lang->bidesign->common = 'BI design';
|
||||
$lang->personalsettings->common = 'Personal setting ';
|
||||
$lang->projectsettings->common = 'Setting';
|
||||
$lang->dataaccess->common = 'Data permission';
|
||||
$lang->executiongantt->common = 'Gantt chart';
|
||||
$lang->executionkanban->common = 'Kanban';
|
||||
$lang->executionburn->common = 'Burndown chart';
|
||||
$lang->executioncfd->common = 'Cumulative Flow Diagram';
|
||||
$lang->executionstory->common = 'Story';
|
||||
$lang->executionqa->common = 'QA';
|
||||
$lang->executionsettings->common = 'Setting';
|
||||
$lang->generalcomment->common = 'Comment';
|
||||
$lang->generalping->common = 'Timeout prevention';
|
||||
$lang->generaltemplate->common = 'Template';
|
||||
$lang->generaleffort->common = 'General log';
|
||||
$lang->productsettings->common = 'Product setting';
|
||||
$lang->projectreview->common = 'Review';
|
||||
$lang->projecttrack->common = 'Matrix';
|
||||
$lang->projectqa->common = 'QA';
|
||||
$lang->holidayseason->common = 'Holiday';
|
||||
$lang->codereview->common = 'Review';
|
||||
$lang->repocode->common = 'Code';
|
||||
$lang->deliverable->common = 'Deliverable';
|
||||
$lang->projectDeliverable->common = 'Project Deliverable';
|
||||
$lang->executionDeliverable->common = 'Execution Deliverable';
|
||||
|
||||
$lang->personnel->common = 'Member';
|
||||
$lang->personnel->invest = 'Investment';
|
||||
|
||||
@@ -240,7 +240,7 @@ if(helper::hasFeature('devops')) $lang->scrum->menu->devops = array('link'
|
||||
$lang->scrum->menu->build = array('link' => "{$lang->build->common}|projectbuild|browse|project=%s", 'subModule' => 'projectbuild');
|
||||
$lang->scrum->menu->release = array('link' => "{$lang->release->common}|projectrelease|browse|project=%s", 'subModule' => 'projectrelease,system');
|
||||
$lang->scrum->menu->dynamic = array('link' => "$lang->dynamic|project|dynamic|project=%s");
|
||||
$lang->scrum->menu->settings = array('link' => "$lang->settings|project|view|project=%s", 'subModule' => 'tree,stakeholder', 'alias' => 'edit,manageproducts,group,managemembers,manageview,managepriv,whitelist,addwhitelist,team', 'exclude' => 'tree-browsetask');
|
||||
$lang->scrum->menu->settings = array('link' => "$lang->settings|project|view|project=%s", 'subModule' => 'tree,stakeholder', 'alias' => 'edit,manageproducts,group,managemembers,manageview,managepriv,whitelist,addwhitelist,team,workflowgroup', 'exclude' => 'tree-browsetask');
|
||||
|
||||
$lang->scrum->dividerMenu = ',execution,programplan,doc,settings,';
|
||||
|
||||
@@ -783,6 +783,7 @@ $lang->navGroup->extension = 'admin';
|
||||
$lang->navGroup->action = 'admin';
|
||||
$lang->navGroup->convert = 'admin';
|
||||
$lang->navGroup->stage = 'admin';
|
||||
$lang->navGroup->deliverable = 'admin';
|
||||
$lang->navGroup->featureswitch = 'admin';
|
||||
$lang->navGroup->importdata = 'admin';
|
||||
$lang->navGroup->systemsetting = 'admin';
|
||||
|
||||
@@ -289,37 +289,40 @@ $lang->aiapp->common = 'AI';
|
||||
$lang->product->system = '应用';
|
||||
$lang->configure->common = '配置';
|
||||
|
||||
$lang->programstakeholder->common = '干系人';
|
||||
$lang->featureswitch->common = '功能开关';
|
||||
$lang->importdata->common = '数据导入';
|
||||
$lang->systemsetting->common = '系统设置';
|
||||
$lang->staffmanage->common = '人员管理';
|
||||
$lang->modelconfig->common = '模型配置';
|
||||
$lang->featureconfig->common = '功能配置';
|
||||
$lang->doctemplate->common = '文档模板';
|
||||
$lang->notifysetting->common = '通知设置';
|
||||
$lang->bidesign->common = 'BI设计';
|
||||
$lang->personalsettings->common = '个人设置';
|
||||
$lang->projectsettings->common = '设置';
|
||||
$lang->dataaccess->common = '数据权限';
|
||||
$lang->executiongantt->common = '甘特图';
|
||||
$lang->executionkanban->common = '看板';
|
||||
$lang->executionburn->common = '燃尽图';
|
||||
$lang->executioncfd->common = '累积流图';
|
||||
$lang->executionstory->common = '研发需求';
|
||||
$lang->executionqa->common = '测试';
|
||||
$lang->executionsettings->common = '设置';
|
||||
$lang->generalcomment->common = '备注';
|
||||
$lang->generalping->common = '防超时';
|
||||
$lang->generaltemplate->common = '模板';
|
||||
$lang->generaleffort->common = '通用日志';
|
||||
$lang->productsettings->common = '产品设置';
|
||||
$lang->projectreview->common = '评审';
|
||||
$lang->projecttrack->common = '矩阵';
|
||||
$lang->projectqa->common = '测试';
|
||||
$lang->holidayseason->common = '节假日';
|
||||
$lang->codereview->common = '问题';
|
||||
$lang->repocode->common = '代码';
|
||||
$lang->programstakeholder->common = '干系人';
|
||||
$lang->featureswitch->common = '功能开关';
|
||||
$lang->importdata->common = '数据导入';
|
||||
$lang->systemsetting->common = '系统设置';
|
||||
$lang->staffmanage->common = '人员管理';
|
||||
$lang->modelconfig->common = '项目模型配置';
|
||||
$lang->featureconfig->common = '功能配置';
|
||||
$lang->doctemplate->common = '文档模板';
|
||||
$lang->notifysetting->common = '通知设置';
|
||||
$lang->bidesign->common = 'BI设计';
|
||||
$lang->personalsettings->common = '个人设置';
|
||||
$lang->projectsettings->common = '设置';
|
||||
$lang->dataaccess->common = '数据权限';
|
||||
$lang->executiongantt->common = '甘特图';
|
||||
$lang->executionkanban->common = '看板';
|
||||
$lang->executionburn->common = '燃尽图';
|
||||
$lang->executioncfd->common = '累积流图';
|
||||
$lang->executionstory->common = '研发需求';
|
||||
$lang->executionqa->common = '测试';
|
||||
$lang->executionsettings->common = '设置';
|
||||
$lang->generalcomment->common = '备注';
|
||||
$lang->generalping->common = '防超时';
|
||||
$lang->generaltemplate->common = '模板';
|
||||
$lang->generaleffort->common = '通用日志';
|
||||
$lang->productsettings->common = '产品设置';
|
||||
$lang->projectreview->common = '评审';
|
||||
$lang->projecttrack->common = '矩阵';
|
||||
$lang->projectqa->common = '测试';
|
||||
$lang->holidayseason->common = '节假日';
|
||||
$lang->codereview->common = '问题';
|
||||
$lang->repocode->common = '代码';
|
||||
$lang->deliverable->common = '交付物';
|
||||
$lang->projectDeliverable->common = '项目交付物';
|
||||
$lang->executionDeliverable->common = '执行交付物';
|
||||
|
||||
$lang->personnel->common = '人员';
|
||||
$lang->personnel->invest = '投入人员';
|
||||
|
||||
@@ -516,7 +516,7 @@ class commonModel extends model
|
||||
{
|
||||
$info['timeUsed'] = round(getTime() - $startTime, 4) * 1000;
|
||||
$info['memory'] = round(memory_get_peak_usage() / 1024, 1);
|
||||
$info['querys'] = count(dao::$querys);
|
||||
$info['querys'] = count(dbh::$queries);
|
||||
vprintf($this->lang->runInfo, $info);
|
||||
return $info;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<?php if(!empty($action->history)):?>
|
||||
<button type='button' class='btn btn-mini switch-btn btn-icon btn-expand' title='<?php echo $lang->switchDisplay;?>'><i class='change-show icon icon-plus icon-sm'></i></button>
|
||||
<div class='history-changes' id='changeBox<?php echo $i;?>'>
|
||||
<?php echo $this->action->printChanges($action->objectType, $action->history);?>
|
||||
<?php echo $this->action->printChanges($action->objectType, $action->objectID, $action->history);?>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<?php if(strlen(trim(($action->comment))) != 0):?>
|
||||
|
||||
@@ -28,7 +28,7 @@ if(file_exists($extViewFile))
|
||||
<?php if(!empty($action->history)):?>
|
||||
<tr>
|
||||
<td style='padding: 10px;'>
|
||||
<div><?php $this->action->printChanges($action->objectType, $action->history, false);?></div>
|
||||
<div><?php $this->action->printChanges($action->objectType, $action->objectID, $action->history, false);?></div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
|
||||
@@ -595,7 +595,7 @@ class custom extends control
|
||||
$menus = json_decode($items);
|
||||
|
||||
$menuNames = array();
|
||||
foreach($menus as $key => $item) $menuNames[] = $item->name;
|
||||
foreach($menus as $item) $menuNames[] = $item->name;
|
||||
|
||||
foreach($oldMenus as $key => $item)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ $lang->custom->role = 'Role';
|
||||
$lang->custom->dept = 'Dept';
|
||||
$lang->custom->code = $lang->code;
|
||||
$lang->custom->setCode = 'Enable or Disable Code';
|
||||
$lang->custom->projectCommon = $lang->projectCommon . ' Setting';
|
||||
$lang->custom->executionCommon = 'Execution';
|
||||
$lang->custom->selectDefaultProgram = 'Please select default program';
|
||||
$lang->custom->defaultProgram = 'Default program';
|
||||
|
||||
@@ -55,6 +55,7 @@ $lang->custom->role = 'Role';
|
||||
$lang->custom->dept = 'Dept';
|
||||
$lang->custom->code = $lang->code;
|
||||
$lang->custom->setCode = 'Enable or Disable Code';
|
||||
$lang->custom->projectCommon = $lang->projectCommon . ' Setting';
|
||||
$lang->custom->executionCommon = 'Execution';
|
||||
$lang->custom->selectDefaultProgram = 'Please select default program';
|
||||
$lang->custom->defaultProgram = 'Default program';
|
||||
|
||||
@@ -55,6 +55,7 @@ $lang->custom->role = 'Role';
|
||||
$lang->custom->dept = 'Dept';
|
||||
$lang->custom->code = $lang->code;
|
||||
$lang->custom->setCode = 'Activer ou Désactiver le Code';
|
||||
$lang->custom->projectCommon = $lang->projectCommon . ' Setting';
|
||||
$lang->custom->executionCommon = 'Execution';
|
||||
$lang->custom->selectDefaultProgram = 'Please select default program';
|
||||
$lang->custom->defaultProgram = 'Default program';
|
||||
|
||||
@@ -55,6 +55,7 @@ $lang->custom->role = '职位';
|
||||
$lang->custom->dept = '部门';
|
||||
$lang->custom->code = $lang->code;
|
||||
$lang->custom->setCode = '是否启用代号';
|
||||
$lang->custom->projectCommon = $lang->projectCommon . '设置';
|
||||
$lang->custom->executionCommon = '执行';
|
||||
$lang->custom->selectDefaultProgram = '请选择一个默认项目集';
|
||||
$lang->custom->defaultProgram = '默认项目集';
|
||||
|
||||
+10
-12
@@ -446,25 +446,23 @@ class datatableModel extends model
|
||||
*/
|
||||
public function appendWorkflowFields(string $module, string $method): array
|
||||
{
|
||||
if(in_array($module, array('epic', 'story', 'requirement')))
|
||||
if($module == 'build' && $method == 'build')
|
||||
{
|
||||
$method = $module == 'story' ? 'browse' : $module; // 需求加载product-browse的layout配置。
|
||||
$module = 'product';
|
||||
$module = 'build';
|
||||
$method = 'browse'; // 版本加载build-browse的layout配置。
|
||||
}
|
||||
elseif($module == 'build')
|
||||
elseif($module == 'task' && $method == 'task')
|
||||
{
|
||||
$module = 'execution';
|
||||
$method = 'build'; // 版本加载execution-build的layout配置。
|
||||
}
|
||||
elseif($module == 'task')
|
||||
{
|
||||
$module = 'execution';
|
||||
$method = 'task'; // 任务加载execution-task的layout配置。
|
||||
$method = 'browse'; // 任务加载task-browse的layout配置。
|
||||
}
|
||||
elseif($module == 'bug' && $method == 'bug')
|
||||
{
|
||||
$method = 'browse'; // 执行bug列表加载bug-browse的layout配置。
|
||||
}
|
||||
elseif($module == 'story' && $method == 'story')
|
||||
{
|
||||
$method = 'browse'; // 执行需求列表加载story-browse的layout配置。
|
||||
}
|
||||
elseif($module == 'testcase' && $method == 'testcase')
|
||||
{
|
||||
$method = 'browse'; // 执行用例列表加载testcase-browse的layout配置。
|
||||
@@ -520,4 +518,4 @@ class datatableModel extends model
|
||||
|
||||
return $this->loadModel('flow')->buildDtableCols($fields, [], [], isset($flow->buildin) && !$flow->buildin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,6 +164,8 @@ class doc extends control
|
||||
return $this->sendError($this->lang->notFound);
|
||||
}
|
||||
|
||||
$this->app->loadClass('pager', true);
|
||||
|
||||
$this->view->title = sprintf($this->lang->doc->insertTitle, $this->lang->doc->zentaoList[$type]);
|
||||
$this->view->type = $type;
|
||||
$this->view->settings = $blockData->settings;
|
||||
@@ -172,6 +174,7 @@ class doc extends control
|
||||
$this->view->data = $blockData->content->data;
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted');
|
||||
$this->view->blockID = $blockID;
|
||||
$this->view->pager = new pager(count($this->view->data), 10);
|
||||
|
||||
if(strpos(',productStory,ER,UR,planStory,projectStory', $type) !== false) $this->docZen->assignStoryGradeData($type);
|
||||
|
||||
|
||||
@@ -253,6 +253,8 @@ $lang->doc->uploadFormat = 'Upload Format';
|
||||
$lang->doc->editedList = 'File editor';
|
||||
$lang->doc->moveTo = 'Move to';
|
||||
$lang->doc->notSupportExport = 'This document does not support export';
|
||||
$lang->doc->downloadTemplate = 'Download Template';
|
||||
$lang->doc->addFile = 'Add File';
|
||||
|
||||
$lang->doc->preview = 'Preview';
|
||||
$lang->doc->insertTitle = 'Insert %s list';
|
||||
@@ -439,6 +441,7 @@ $lang->doc->noCollectedDoc = 'Sie haben kein Dokument gesammelt.';
|
||||
$lang->doc->errorEmptyLib = 'No data in document library.';
|
||||
$lang->doc->confirmUpdateContent = 'You have a document that is not saved from last time. Do you want to continue editing it?';
|
||||
$lang->doc->selectLibType = 'Please select a type of doc library.';
|
||||
$lang->doc->selectDoc = 'Please select a doc';
|
||||
$lang->doc->noLibreOffice = 'You does not have access to office conversion settings!';
|
||||
$lang->doc->errorParentChapter = 'The parent chapter cannot be its own chapter or sub chapter!';
|
||||
$lang->doc->errorOthersCreated = 'There are documents created by others in this library. You cannot move it.';
|
||||
|
||||
@@ -253,6 +253,8 @@ $lang->doc->uploadFormat = 'Upload Format';
|
||||
$lang->doc->editedList = 'File editor';
|
||||
$lang->doc->moveTo = 'Move to';
|
||||
$lang->doc->notSupportExport = 'This document does not support export';
|
||||
$lang->doc->downloadTemplate = 'Download Template';
|
||||
$lang->doc->addFile = 'Add File';
|
||||
|
||||
$lang->doc->preview = 'Preview';
|
||||
$lang->doc->insertTitle = 'Insert %s list';
|
||||
@@ -439,6 +441,7 @@ $lang->doc->noCollectedDoc = 'You have not favorited any documents.
|
||||
$lang->doc->errorEmptyLib = 'No data in document library.';
|
||||
$lang->doc->confirmUpdateContent = 'You have a document that is not saved from last time. Do you want to continue editing it?';
|
||||
$lang->doc->selectLibType = 'Please select a type of doc library.';
|
||||
$lang->doc->selectDoc = 'Please select a doc';
|
||||
$lang->doc->noLibreOffice = 'You does not have access to office conversion settings!';
|
||||
$lang->doc->errorParentChapter = 'The parent chapter cannot be its own chapter or sub chapter!';
|
||||
$lang->doc->errorOthersCreated = 'There are documents created by others in this library. You cannot move it.';
|
||||
|
||||
@@ -253,6 +253,8 @@ $lang->doc->uploadFormat = 'Upload Format';
|
||||
$lang->doc->editedList = 'File editor';
|
||||
$lang->doc->moveTo = 'Move to';
|
||||
$lang->doc->notSupportExport = 'This document does not support export';
|
||||
$lang->doc->downloadTemplate = 'Download Template';
|
||||
$lang->doc->addFile = 'Add File';
|
||||
|
||||
$lang->doc->preview = 'Preview';
|
||||
$lang->doc->insertTitle = 'Insert %s list';
|
||||
@@ -439,6 +441,7 @@ $lang->doc->noCollectedDoc = "Vous avez aucun document dans vos fav
|
||||
$lang->doc->errorEmptyLib = 'No data in document library.';
|
||||
$lang->doc->confirmUpdateContent = 'You have a document that is not saved from last time. Do you want to continue editing it?';
|
||||
$lang->doc->selectLibType = 'Please select a type of doc library.';
|
||||
$lang->doc->selectDoc = 'Please select a doc';
|
||||
$lang->doc->noLibreOffice = 'You does not have access to office conversion settings!';
|
||||
$lang->doc->errorParentChapter = 'The parent chapter cannot be its own chapter or sub chapter!';
|
||||
$lang->doc->errorOthersCreated = 'There are documents created by others in this library. You cannot move it.';
|
||||
|
||||
@@ -253,6 +253,8 @@ $lang->doc->uploadFormat = '上传格式';
|
||||
$lang->doc->editedList = '文档编辑者';
|
||||
$lang->doc->moveTo = '移动至';
|
||||
$lang->doc->notSupportExport = '(此文档暂不支持导出)';
|
||||
$lang->doc->downloadTemplate = '下载模板';
|
||||
$lang->doc->addFile = '提交文件';
|
||||
|
||||
$lang->doc->preview = '预览';
|
||||
$lang->doc->insertTitle = '插入%s列表';
|
||||
@@ -439,6 +441,7 @@ $lang->doc->noCollectedDoc = '您还没有收藏任何文档。';
|
||||
$lang->doc->errorEmptyLib = '文档库暂无数据。';
|
||||
$lang->doc->confirmUpdateContent = '检查到您有未保存的文档内容,是否继续编辑?';
|
||||
$lang->doc->selectLibType = '请选择文档库类型';
|
||||
$lang->doc->selectDoc = '请选择文档';
|
||||
$lang->doc->noLibreOffice = '您还没有office转换设置访问权限!';
|
||||
$lang->doc->errorParentChapter = '父章节不能是自身章节及子章节!';
|
||||
$lang->doc->errorOthersCreated = '该库下其他人创建的文档暂不支持移动,是否确认移动?';
|
||||
|
||||
@@ -1571,12 +1571,12 @@ class docModel extends model
|
||||
}
|
||||
|
||||
$files = $this->loadModel('file')->getUpload();
|
||||
if($doc->type == 'attachment' && (empty($files) || isset($files['name']))) return dao::$errors['files'] = sprintf($this->lang->error->notempty, $this->lang->doc->uploadFile);
|
||||
if($doc->type == 'attachment' && empty($doc->copy) && (empty($files) || isset($files['name']))) return dao::$errors['files'] = sprintf($this->lang->error->notempty, $this->lang->doc->uploadFile);
|
||||
|
||||
$doc->draft = $isDraft ? $docContent->content : '';
|
||||
$doc->vision = $this->config->vision;
|
||||
$doc->version = $isDraft ? 0 : 1;
|
||||
$this->dao->insert(TABLE_DOC)->data($doc, 'content')->autoCheck()->batchCheck($requiredFields, 'notempty')->exec();
|
||||
$this->dao->insert(TABLE_DOC)->data($doc, 'content,copy')->autoCheck()->batchCheck($requiredFields, 'notempty')->exec();
|
||||
if(dao::isError()) return false;
|
||||
|
||||
$docID = $this->dao->lastInsertID();
|
||||
|
||||
@@ -21,6 +21,9 @@ $actions = array();
|
||||
$actions[] = array('icon' => 'menu-backend', 'text' => $lang->doc->zentaoAction['set'], 'data-toggle' => 'modal', 'url' => str_replace('{blockID}', "$blockID", $settings), 'data-size' => 'lg');
|
||||
$actions[] = array('icon' => 'trash', 'text' => $lang->doc->zentaoAction['delete'], 'zui-on-click' => "deleteZentaoList($blockID)");
|
||||
|
||||
$pagerSetting = usePager();
|
||||
unset($pagerSetting['linkCreator']);
|
||||
|
||||
div
|
||||
(
|
||||
set('data-id', $blockID),
|
||||
@@ -58,6 +61,9 @@ div
|
||||
set::colResize(true),
|
||||
set::customCols(false),
|
||||
set::onRenderCell(jsRaw('window.renderCell')),
|
||||
set::localPager(),
|
||||
set::footPager($pagerSetting),
|
||||
set::footer(array('flex', 'pager')),
|
||||
$type == 'productRelease' ? set::plugins(array('cellspan')) : null,
|
||||
$type == 'productRelease' ? set::getCellSpan(jsRaw('window.getCellSpan')) : null
|
||||
)
|
||||
|
||||
+10
-1
@@ -614,7 +614,16 @@ class docZen extends doc
|
||||
{
|
||||
$execution = $this->loadModel('execution')->getByID($objectID);
|
||||
$objects = $this->execution->getPairs($execution->project, 'all', "multiple,leaf,noprefix");
|
||||
$objects = $this->execution->resetExecutionSorts($objects, array(), $execution->project);
|
||||
|
||||
$parentExecutions = $childExecutions = array();
|
||||
$executions = $this->execution->fetchExecutionList($execution->project, 'all', 0, 0, 'order_asc');
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
if($execution->grade == 1) $parentExecutions[$execution->id] = $execution;
|
||||
if($execution->grade > 1 && $execution->parent) $childExecutions[$execution->parent][$execution->id] = $execution;
|
||||
}
|
||||
|
||||
$objects = $this->execution->resetExecutionSorts($objects, $parentExecutions, $childExecutions);
|
||||
}
|
||||
elseif($objectType == 'product')
|
||||
{
|
||||
|
||||
@@ -49,6 +49,17 @@ $config->execution->dtable->fieldList['status']['width'] = '80';
|
||||
$config->execution->dtable->fieldList['status']['group'] = '1';
|
||||
$config->execution->dtable->fieldList['status']['show'] = true;
|
||||
|
||||
if(in_array($config->edition, array('max', 'ipd')) && helper::hasFeature('deliverable'))
|
||||
{
|
||||
$config->execution->dtable->fieldList['deliverable']['title'] = $lang->execution->deliverableAbbr;
|
||||
$config->execution->dtable->fieldList['deliverable']['name'] = 'deliverable';
|
||||
$config->execution->dtable->fieldList['deliverable']['type'] = 'html';
|
||||
$config->execution->dtable->fieldList['deliverable']['width'] = '100px';
|
||||
$config->execution->dtable->fieldList['deliverable']['group'] = '1';
|
||||
$config->execution->dtable->fieldList['deliverable']['show'] = true;
|
||||
$config->execution->dtable->fieldList['deliverable']['sortType'] = false;
|
||||
}
|
||||
|
||||
$config->execution->dtable->fieldList['PM']['title'] = $lang->execution->execPM;
|
||||
$config->execution->dtable->fieldList['PM']['name'] = 'PM';
|
||||
$config->execution->dtable->fieldList['PM']['type'] = 'avatarBtn';
|
||||
|
||||
@@ -1362,7 +1362,7 @@ class execution extends control
|
||||
}
|
||||
|
||||
$executionIDList = $this->post->executionIDList;
|
||||
$executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->in($executionIDList)->fetchAll('id');
|
||||
$executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->in($executionIDList)->fetchAll('id', false);
|
||||
$relatedProjects = $this->dao->select('id,project')->from(TABLE_PROJECT)->where('id')->in($executionIDList)->fetchPairs(); /* 获取执行所属的项目列表。*/
|
||||
$projects = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->in($relatedProjects)->fetchAll('id'); /* 获取执行所属的项目列表中每个项目的项目信息。*/
|
||||
|
||||
@@ -1415,8 +1415,8 @@ class execution extends control
|
||||
$executionIdList = $this->post->executionIDList;
|
||||
if(is_string($executionIdList)) $executionIdList = explode(',', $executionIdList);
|
||||
|
||||
$filteredStages = $this->execution->batchChangeStatus($executionIdList, $status);
|
||||
if(!$filteredStages) return $this->sendSuccess(array('load' => true));
|
||||
$message = $this->execution->batchChangeStatus($executionIdList, $status);
|
||||
if(empty($message['byChild']) && empty($message['byDeliverable'])) return $this->sendSuccess(array('load' => true));
|
||||
|
||||
$alertMsg = '';
|
||||
if($status == 'wait')
|
||||
@@ -1426,18 +1426,28 @@ class execution extends control
|
||||
if(empty($project) or (!empty($project) and strpos($project->model, 'waterfall') !== false))
|
||||
{
|
||||
$executionLang = (empty($project) or (!empty($project) and $project->model == 'waterfallplus')) ? $this->lang->execution->common : $this->lang->stage->common;
|
||||
$alertMsg = sprintf($this->lang->execution->hasStartedTaskOrSubStage, $executionLang, $filteredStages);
|
||||
$alertMsg = sprintf($this->lang->execution->hasStartedTaskOrSubStage, $executionLang, $message['byChild']);
|
||||
}
|
||||
if(!empty($project) and strpos('agileplus,scrum', $project->model) !== false)
|
||||
{
|
||||
$executionLang = $project->model == 'scrum' ? $this->lang->executionCommon : $this->lang->execution->common;
|
||||
$alertMsg = sprintf($this->lang->execution->hasStartedTask, $executionLang, $filteredStages);
|
||||
$alertMsg = sprintf($this->lang->execution->hasStartedTask, $executionLang, $message['byChild']);
|
||||
}
|
||||
}
|
||||
if($status == 'suspended') $alertMsg = sprintf($this->lang->execution->hasSuspendedOrClosedChildren, $message['byChild']);
|
||||
if($status == 'closed')
|
||||
{
|
||||
if(!empty($message['byChild']))
|
||||
{
|
||||
$alertMsg .= sprintf($this->lang->execution->hasNotClosedChildren, $message['byChild']);
|
||||
}
|
||||
elseif(!empty($message['byDeliverable']))
|
||||
{
|
||||
$alertMsg .= sprintf($this->lang->execution->cannotCloseByDeliverable, $message['byDeliverable']);
|
||||
}
|
||||
}
|
||||
if($status == 'suspended') $alertMsg = sprintf($this->lang->execution->hasSuspendedOrClosedChildren, $filteredStages);
|
||||
if($status == 'closed') $alertMsg = sprintf($this->lang->execution->hasNotClosedChildren, $filteredStages);
|
||||
|
||||
return $this->sendSuccess(array('message' => $alertMsg, 'load' => true));
|
||||
return $this->send(array('load' => array('alert' => $alertMsg), 'result' => 'success'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1666,7 +1676,11 @@ class execution extends control
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
$project = $this->loadModel('project')->getById($execution->project);
|
||||
if(in_array($project->model, array('waterfall', 'waterfallplus', 'ipd'))) $this->loadModel('programplan')->computeProgress($executionID, 'close');
|
||||
if(in_array($project->model, array('waterfall', 'waterfallplus', 'ipd')))
|
||||
{
|
||||
$result = $this->loadModel('programplan')->computeProgress($executionID, 'close');
|
||||
if(is_array($result)) return $this->send($result);
|
||||
}
|
||||
|
||||
$this->executeHooks($executionID);
|
||||
|
||||
@@ -2739,11 +2753,11 @@ class execution extends control
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
if($execution->grade == 1) $topExecutions[$execution->id] = $execution->id;
|
||||
if($execution->grade > 1 && $execution->parent) $parentExecutions[$execution->parent] = $execution->parent;
|
||||
if($execution->grade > 1 && $execution->parent) $parentExecutions[$execution->parent][$execution->id] = $execution;
|
||||
}
|
||||
|
||||
/* 获取排序后的执行列表,并给每个执行设置团队信息。*/
|
||||
$executions = $this->execution->resetExecutionSorts($executions, $topExecutions);
|
||||
$executions = $this->execution->resetExecutionSorts($executions, $topExecutions, $parentExecutions);
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
$execution->teams = zget($teams, $execution->id, array());
|
||||
@@ -2893,6 +2907,7 @@ class execution extends control
|
||||
$this->app->loadLang('stage');
|
||||
$this->app->loadLang('programplan');
|
||||
$this->loadModel('product');
|
||||
$this->loadModel('project');
|
||||
$this->loadModel('datatable');
|
||||
|
||||
$from = $this->app->tab;
|
||||
@@ -3480,7 +3495,7 @@ class execution extends control
|
||||
$this->loadModel('task');
|
||||
|
||||
$result = array();
|
||||
foreach($array as $key => $object)
|
||||
foreach($array as $object)
|
||||
{
|
||||
$result[$object->id] = $object;
|
||||
$tasks = zget($object, 'tasks', array());
|
||||
|
||||
@@ -25,7 +25,16 @@ window.renderRowData = function($row, index, row)
|
||||
options.disabled = row.grade > 1 && parentType != 'mix';
|
||||
if(projectModel == 'ipd') options.disabled = true;
|
||||
}
|
||||
|
||||
$row.attr('data-parent', row.parent);
|
||||
|
||||
$row.find('[data-name="lifetime"]').find('.picker-box').on('inited', function(e, info)
|
||||
{
|
||||
let $attribute = info[0];
|
||||
$attribute.render({items: stageItems, required: true, name: 'attribute[' + row.id + ']', disabled: row.grade > 1 && parentType != 'mix'});
|
||||
if(typeof row != 'undefined' && typeof row.hasDeliverable != 'undefined') $attribute.render({disabled: true});
|
||||
$(e.target).attr('data-parent', row.parent);
|
||||
});
|
||||
}
|
||||
|
||||
$row.find('[data-name="lifetime"]').find('.picker-box').on('inited', function(e, info) { info[0].render(options); });
|
||||
|
||||
@@ -222,6 +222,8 @@ window.getItem = function(info)
|
||||
let assignedTo = '';
|
||||
let avatar = "<span class='avatar rounded-full size-xs ml-1 bg-gray-100 text-canvas' title='" + noAssigned + "'><i class='icon icon-person'></i></span>";
|
||||
|
||||
info.item.originalTitle = info.item.title;
|
||||
|
||||
if(begin < '1970-01-01' && end > '1970-01-01')
|
||||
{
|
||||
beginAndEnd = end + ' ' + cardLang.deadlineAB;
|
||||
@@ -315,7 +317,7 @@ window.getItem = function(info)
|
||||
info.item.titleUrl = $.createLink(info.laneInfo.type, 'view', `id=${info.item.id}&from=execution`);
|
||||
}
|
||||
|
||||
info.item.titleAttrs = {'data-toggle': 'modal', 'data-size': 'lg', 'title': info.item.title, 'class': 'card-title clip'};
|
||||
info.item.titleAttrs = {'data-toggle': 'modal', 'data-size': 'lg', 'title': info.item.originalTitle, 'class': 'card-title clip'};
|
||||
|
||||
info.item.content = {html: content};
|
||||
if(info.item.color && info.item.color != '#fff') info.item.className = 'color-' + info.item.color.replace('#', '');
|
||||
|
||||
+85
-36
@@ -539,17 +539,24 @@ class executionModel extends model
|
||||
* @param array $executionIdList
|
||||
* @param string $status
|
||||
* @access public
|
||||
* @return string 返回不符合条件被过滤了的执行,来提示执行下任务或子阶段已经开始,无法修改,已过滤。参见 story#41875。
|
||||
* @return array 返回不符合条件被过滤了的执行,来提示执行下任务或子阶段已经开始,无法修改,已过滤。参见 story#41875。
|
||||
*/
|
||||
public function batchChangeStatus(array $executionIdList, string $status): string
|
||||
public function batchChangeStatus(array $executionIdList, string $status): array
|
||||
{
|
||||
/* Sort the IDs, the child stage comes first, and the parent stage follows. */
|
||||
$executionIdList = $this->dao->select('id')->from(TABLE_EXECUTION)->where('id')->in($executionIdList)->orderBy('grade_desc')->fetchPairs();
|
||||
$executionList = $this->dao->select('id,name,status,grade,deliverable')->from(TABLE_EXECUTION)->where('id')->in($executionIdList)->orderBy('grade_desc')->fetchAll('id');
|
||||
|
||||
$this->loadModel('programplan');
|
||||
$filteredStages = '';
|
||||
foreach($executionIdList as $executionID)
|
||||
$message = array('byChild' => '', 'byDeliverable' => '');
|
||||
foreach($executionList as $executionID => $execution)
|
||||
{
|
||||
$needCheckDeliverable = $status == 'closed' && $execution->status == 'doing' && $execution->grade == 1;
|
||||
if(in_array($this->config->edition, array('max', 'ipd')) && $needCheckDeliverable && !$this->canCloseByDeliverable($execution))
|
||||
{
|
||||
$message['byDeliverable'] .= '#' . $execution->id . ' ' . $execution->name . "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The state of the parent stage or the sibling stage may be affected by the child stage before the change, so it cannot be checked in advance. */
|
||||
$selfAndChildrenList = $this->programplan->getSelfAndChildrenList($executionID);
|
||||
$selfAndChildren = $selfAndChildrenList[$executionID];
|
||||
@@ -557,7 +564,7 @@ class executionModel extends model
|
||||
|
||||
if($status == 'wait' and $execution->status != 'wait')
|
||||
{
|
||||
$filteredStages .= $this->changeStatus2Wait($executionID, $selfAndChildren);
|
||||
$message['byChild'] .= $this->changeStatus2Wait($executionID, $selfAndChildren);
|
||||
}
|
||||
|
||||
if($status == 'doing' and $execution->status != 'doing')
|
||||
@@ -567,11 +574,12 @@ class executionModel extends model
|
||||
|
||||
if(($status == 'suspended' and $execution->status != 'suspended') or ($status == 'closed' and $execution->status != 'closed'))
|
||||
{
|
||||
$filteredStages .= $this->changeStatus2Inactive($executionID, $status, $selfAndChildren);
|
||||
$message['byChild'] .= $this->changeStatus2Inactive($executionID, $status, $selfAndChildren);
|
||||
}
|
||||
}
|
||||
|
||||
return trim($filteredStages, ',');
|
||||
$message['byChild'] = trim($message['byChild'], ',');
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -906,9 +914,9 @@ class executionModel extends model
|
||||
* @param int $executionID
|
||||
* @param object $postData
|
||||
* @access public
|
||||
* @return array|false
|
||||
* @return int|false
|
||||
*/
|
||||
public function close(int $executionID, object $postData): array|false
|
||||
public function close(int $executionID, object $postData): int|false
|
||||
{
|
||||
$oldExecution = $this->fetchById($executionID); /* Save previous execution to variable for later compare. */
|
||||
|
||||
@@ -937,7 +945,7 @@ class executionModel extends model
|
||||
}
|
||||
|
||||
$this->loadModel('score')->create('execution', 'close', $oldExecution);
|
||||
return $changes;
|
||||
return $actionID;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1452,7 +1460,7 @@ class executionModel extends model
|
||||
->fi()
|
||||
->orderBy($orderBy)
|
||||
->page($pager, 't1.id')
|
||||
->fetchAll('id');
|
||||
->fetchAll('id', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1481,6 +1489,7 @@ class executionModel extends model
|
||||
$today = helper::today();
|
||||
$burns = $this->getBurnData($executions);
|
||||
$parentExecutions = $this->dao->select('parent,parent')->from(TABLE_EXECUTION)->where('parent')->ne(0)->andWhere('deleted')->eq(0)->fetchPairs();
|
||||
$statusGroup = $this->dao->select('parent,status')->from(TABLE_EXECUTION)->where('parent')->in(array_keys($executions))->andWhere('deleted')->eq(0)->fetchGroup('parent', 'status');
|
||||
|
||||
/* Get workingDays. */
|
||||
$earliestEnd = $today;
|
||||
@@ -1492,10 +1501,13 @@ class executionModel extends model
|
||||
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
$execution->productName = isset($productList[$execution->id]) ? trim($productList[$execution->id]->productName, ',') : '';
|
||||
$execution->product = $productID;
|
||||
$execution->productID = $productID;
|
||||
$execution->delay = 0;
|
||||
$execution->productName = isset($productList[$execution->id]) ? trim($productList[$execution->id]->productName, ',') : '';
|
||||
$execution->product = $productID;
|
||||
$execution->productID = $productID;
|
||||
$execution->delay = 0;
|
||||
$execution->totalEstimate = $execution->estimate;
|
||||
$execution->totalConsumed = $execution->consumed;
|
||||
$execution->totalLeft = $execution->left;
|
||||
if($execution->end) $execution->end = date(DT_DATE1, strtotime($execution->end));
|
||||
if(!isset($execution->projectName)) $execution->projectName = $project->name;
|
||||
if(!isset($execution->projectModel)) $execution->projectModel = $project->model;
|
||||
@@ -1503,6 +1515,13 @@ class executionModel extends model
|
||||
if(isset($parentExecutions[$execution->id])) $executions[$execution->id]->isParent = 1;
|
||||
if(empty($productID) && !empty($productList[$execution->id])) $execution->product = trim($productList[$execution->id]->product, ',');
|
||||
|
||||
/** 如果子执行都关闭了,则父执行可以手动关闭。 */
|
||||
if(isset($statusGroup[$execution->id]))
|
||||
{
|
||||
$childStatus = array_keys($statusGroup[$execution->id]);
|
||||
if(count($childStatus) == 1 && $childStatus[0] == 'closed') $execution->parentCanClose = true;
|
||||
}
|
||||
|
||||
/* Judge whether the execution is delayed. */
|
||||
if($execution->status != 'done' && $execution->status != 'closed' && $execution->status != 'suspended' && !empty($workingDays))
|
||||
{
|
||||
@@ -1806,11 +1825,14 @@ class executionModel extends model
|
||||
public function getChildExecutions(int $executionID, string $orderBy = 'id_desc', string $type = 'child'): array
|
||||
{
|
||||
$executionID = (int)$executionID;
|
||||
$path = '';
|
||||
if($type != 'child') $path = $this->dao->select('path')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch('path');
|
||||
|
||||
if(empty($executionID)) return array();
|
||||
if($type != 'child' && empty($path)) return array();
|
||||
|
||||
$path = '';
|
||||
if($type != 'child')
|
||||
{
|
||||
$path = $this->dao->select('path')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch('path');
|
||||
if(empty($path)) return array();
|
||||
}
|
||||
|
||||
return $this->dao->select('*')->from(TABLE_EXECUTION)
|
||||
->where('deleted')->eq(0)
|
||||
@@ -3827,7 +3849,7 @@ class executionModel extends model
|
||||
|
||||
$action = strtolower($action);
|
||||
if($action == 'start') return $execution->status == 'wait';
|
||||
if($action == 'close') return $execution->status != 'closed' && (!isset($execution->isParent) || (isset($execution->isParent) && !$execution->isParent));
|
||||
if($action == 'close') return $execution->status != 'closed' && (empty($execution->isParent) || !empty($execution->parentCanClose));
|
||||
if($action == 'suspend') return $execution->status == 'wait' || $execution->status == 'doing';
|
||||
if($action == 'putoff') return $execution->status == 'wait' || $execution->status == 'doing';
|
||||
if($action == 'activate') return $execution->status == 'suspended' || $execution->status == 'closed';
|
||||
@@ -4797,6 +4819,7 @@ class executionModel extends model
|
||||
$execution->id = 'pid' . (string)$execution->id;
|
||||
$execution->projectID = $execution->project;
|
||||
$execution->project = $execution->projectName;
|
||||
$execution->rawParent = $execution->parent;
|
||||
$execution->parent = (isset($executionList[$execution->parent]) && $execution->parent && $execution->grade > 1) ? 'pid' . (string)$execution->parent : '';
|
||||
$execution->hasChild = !empty($execution->isParent);
|
||||
$execution->isParent = !empty($execution->isParent) or !empty($execution->tasks);
|
||||
@@ -4841,6 +4864,8 @@ class executionModel extends model
|
||||
$execution->PMAvatar = zget($avatarList, $execution->PMAccount, '');
|
||||
}
|
||||
|
||||
if(in_array($this->config->edition, array('max', 'ipd'))) $execution->deliverable = $this->project->countDeliverable($execution, 'execution');
|
||||
|
||||
$rows[$execution->id] = $execution;
|
||||
|
||||
/* Append tasks and child stages. */
|
||||
@@ -5143,33 +5168,28 @@ class executionModel extends model
|
||||
*
|
||||
* @param array $executions
|
||||
* @param array $parentExecutions
|
||||
* @param int $projectID
|
||||
* @param array $childExecutions
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function resetExecutionSorts(array $executions, array $parentExecutions = array(), int $projectID = 0): array
|
||||
public function resetExecutionSorts(array $executions, array $parentExecutions = array(), array $childExecutions = array()): array
|
||||
{
|
||||
if(empty($executions)) return array();
|
||||
if(empty($parentExecutions))
|
||||
|
||||
if(empty($parentExecutions) && empty($childExecutions))
|
||||
{
|
||||
$execution = current($executions);
|
||||
$projectID = isset($execution->project) ? $execution->project : $projectID;
|
||||
$parentExecutions = $this->dao->select('*')->from(TABLE_EXECUTION)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('type')->in('kanban,sprint,stage')
|
||||
->andWhere('grade')->eq(1)
|
||||
->andWhere('project')->eq($projectID)
|
||||
->orderBy('order_asc')
|
||||
->fetchAll('id');
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
if($execution->grade == 1) $parentExecutions[$execution->id] = $execution;
|
||||
if($execution->grade > 1 && $execution->parent) $childExecutions[$execution->parent][$execution->id] = $execution;
|
||||
}
|
||||
}
|
||||
|
||||
$sortedExecutions = array();
|
||||
foreach($parentExecutions as $executionID => $execution)
|
||||
{
|
||||
if(!isset($sortedExecutions[$executionID]) and isset($executions[$executionID])) $sortedExecutions[$executionID] = $executions[$executionID];
|
||||
|
||||
$children = $this->getChildExecutions($executionID, 'order_asc');
|
||||
if(!empty($children)) $sortedExecutions += $this->resetExecutionSorts($executions, $children);
|
||||
if(!empty($childExecutions[$executionID])) $sortedExecutions += $this->resetExecutionSorts($executions, $childExecutions[$executionID], $childExecutions);
|
||||
}
|
||||
return $sortedExecutions;
|
||||
}
|
||||
@@ -5292,4 +5312,33 @@ class executionModel extends model
|
||||
|
||||
return array($toList, $ccList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查迭代是否可以关闭。
|
||||
* Check if the execution can be closed.
|
||||
*
|
||||
* @param object $execution
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function canCloseByDeliverable(object $execution): bool
|
||||
{
|
||||
if(empty($execution->deliverable)) return true;
|
||||
|
||||
$deliverables = json_decode($execution->deliverable, true);
|
||||
|
||||
foreach($deliverables as $methods)
|
||||
{
|
||||
foreach($methods as $itemList)
|
||||
{
|
||||
foreach($itemList as $item)
|
||||
{
|
||||
if(!empty($item['required']) && empty($item['file']) && empty($item['doc'])) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -142,8 +142,8 @@ class executionTao extends executionModel
|
||||
$executions[$executionID]->lastEditedBy = $this->app->user->account;
|
||||
$executions[$executionID]->lastEditedDate = helper::now();
|
||||
|
||||
if(isset($postData->code)) $executions[$executionID]->code = $executionCode;
|
||||
if(isset($postData->project)) $executions[$executionID]->project = zget($postData->project, $executionID, 0);
|
||||
if(isset($postData->code)) $executions[$executionID]->code = $executionCode;
|
||||
if(isset($postData->project)) $executions[$executionID]->project = zget($postData->project, $executionID, 0);
|
||||
if(isset($postData->attribute[$executionID])) $executions[$executionID]->attribute = zget($postData->attribute, $executionID, '');
|
||||
if(isset($postData->lifetime[$executionID])) $executions[$executionID]->lifetime = $postData->lifetime[$executionID];
|
||||
|
||||
|
||||
@@ -2633,30 +2633,18 @@ class executionTest
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function resetExecutionSortsTest(int $projectID, string $type = ''): string
|
||||
public function resetExecutionSortsTest(int $projectID): string
|
||||
{
|
||||
$executions = array();
|
||||
$executionIDList = '';
|
||||
$firstGradeExecutions = array();
|
||||
if($projectID)
|
||||
{
|
||||
$executions = $this->executionModel->dao->select('*')->from(TABLE_EXECUTION)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('project')->eq($projectID)
|
||||
->andWhere('type')->in('sprint,stage,kanban')
|
||||
->orderBy('order_asc')
|
||||
->fetchAll('id');
|
||||
$executions = $this->executionModel->dao->select('*')->from(TABLE_EXECUTION)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('project')->eq($projectID)
|
||||
->andWhere('type')->in('sprint,stage,kanban')
|
||||
->orderBy('order_asc')
|
||||
->fetchAll('id');
|
||||
|
||||
if($type == 'hasParent')
|
||||
{
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
if($execution->grade == 1) $firstGradeExecutions[$execution->id] = $execution->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$executions = $this->executionModel->resetExecutionSorts($executions, $firstGradeExecutions);
|
||||
$executions = $this->executionModel->resetExecutionSorts($executions);
|
||||
if(!empty($executions))
|
||||
{
|
||||
$executionIDList = array_keys($executions);
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/execution.unittest.class.php';
|
||||
|
||||
$execution = zenData('project')->gen(5);
|
||||
$execution->id->range('1-5');
|
||||
$execution->name->range('迭代1,迭代2,迭代3,迭代4,迭代5');
|
||||
$execution->type->range('stage');
|
||||
$execution->status->range('doing');
|
||||
$execution->parent->range('0,0,1,1,1');
|
||||
$execution->grade->range('1{2},2{3}');
|
||||
$execution->path->range('1,2,`1,3`,`1,4`,`1,5`')->prefix(',')->postfix(',');
|
||||
$execution->begin->range('20230102 000000:0')->type('timestamp')->format('YY/MM/DD');
|
||||
$execution->end->range('20230212 000000:0')->type('timestamp')->format('YY/MM/DD');
|
||||
$execution->deliverable->range('`{"product_waterfall_request":{"whenClosed":[{"deliverable":"1","required":true,"category":"test","template":""}]}}`');
|
||||
$execution->gen(5);
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=executionModel->getByID();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 查看执行1是否可关闭 @1
|
||||
- 查看执行2是否可关闭 @1
|
||||
- 查看执行3是否可关闭 @1
|
||||
- 查看执行4是否可关闭 @1
|
||||
- 查看执行5是否可关闭 @1
|
||||
|
||||
*/
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('execution');
|
||||
$execution1 = $tester->execution->getByID(1);
|
||||
$execution2 = $tester->execution->getByID(2);
|
||||
$execution3 = $tester->execution->getByID(3);
|
||||
$execution4 = $tester->execution->getByID(4);
|
||||
$execution5 = $tester->execution->getByID(5);
|
||||
|
||||
r($tester->execution->canCloseByDeliverable($execution1)) && p('') && e('1'); // 查看执行1是否可关闭
|
||||
r($tester->execution->canCloseByDeliverable($execution2)) && p('') && e('1'); // 查看执行2是否可关闭
|
||||
r($tester->execution->canCloseByDeliverable($execution3)) && p('') && e('1'); // 查看执行3是否可关闭
|
||||
r($tester->execution->canCloseByDeliverable($execution4)) && p('') && e('1'); // 查看执行4是否可关闭
|
||||
r($tester->execution->canCloseByDeliverable($execution5)) && p('') && e('1'); // 查看执行5是否可关闭
|
||||
@@ -27,11 +27,25 @@ title=测试executionModel->resetExecutionSorts();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 检查没有执行时的排序 @0
|
||||
- 检查项目 2 的执行排序 @3,5,7,8,6,4
|
||||
|
||||
- 检查项目 60 的执行排序 @106,112,107,113,108,114,109,115,110,116,111,117
|
||||
|
||||
- 检查项目 61 的执行排序 @118,119,120,121,122,123
|
||||
|
||||
- 检查项目 100 的执行排序 @124,125,126
|
||||
|
||||
*/
|
||||
|
||||
$projectList = array(0, 2);
|
||||
$projectList = array(0, 2, 60, 61, 100);
|
||||
|
||||
$executionTester = new executionTest();
|
||||
r($executionTester->resetExecutionSortsTest($projectList[0])) && p() && e('0'); // 检查没有执行时的排序
|
||||
r($executionTester->resetExecutionSortsTest($projectList[1])) && p() && e('3,5,7,8,6,4'); // 检查有执行时的排序
|
||||
r($executionTester->resetExecutionSortsTest($projectList[1], 'hasParent')) && p() && e('3,5,7,8,6,4'); // 检查有执行并且传parenExecutions时的排序
|
||||
r($executionTester->resetExecutionSortsTest($projectList[0])) && p() && e('0'); // 检查没有执行时的排序
|
||||
r($executionTester->resetExecutionSortsTest($projectList[1])) && p() && e('3,5,7,8,6,4'); // 检查项目 2 的执行排序
|
||||
|
||||
zenData('project')->loadYaml('execution')->gen('30');
|
||||
|
||||
r($executionTester->resetExecutionSortsTest($projectList[2])) && p() && e('106,112,107,113,108,114,109,115,110,116,111,117'); // 检查项目 60 的执行排序
|
||||
r($executionTester->resetExecutionSortsTest($projectList[3])) && p() && e('118,119,120,121,122,123'); // 检查项目 61 的执行排序
|
||||
r($executionTester->resetExecutionSortsTest($projectList[4])) && p() && e('124,125,126'); // 检查项目 100 的执行排序
|
||||
|
||||
@@ -11,7 +11,17 @@ title=测试executionModel->setKanbanMenu();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试替换看板执行的二级菜单第kanban条的link属性 @看板|execution|kanban|executionID=%s
|
||||
- 查看交付物菜单第deliverable条的link属性 @交付物|execution|deliverable|executionID=%s
|
||||
- 查看构建菜单第build条的link属性 @构建|execution|build|executionID=%s
|
||||
- 查看累积流图菜单第CFD条的link属性 @累积流图|execution|cfd|executionID=%s
|
||||
- 查看设置菜单第settings条的link属性 @设置|execution|view|executionID=%s
|
||||
|
||||
*/
|
||||
|
||||
$executionTester = new executionTest();
|
||||
r($executionTester->setKanbanMenuTest()) && p('kanban:link') && e('看板|execution|kanban|executionID=%s'); // 测试替换看板执行的二级菜单
|
||||
r($executionTester->setKanbanMenuTest()) && p('kanban:link') && e('看板|execution|kanban|executionID=%s'); // 测试替换看板执行的二级菜单
|
||||
r($executionTester->setKanbanMenuTest()) && p('deliverable:link') && e('交付物|execution|deliverable|executionID=%s'); // 查看交付物菜单
|
||||
r($executionTester->setKanbanMenuTest()) && p('build:link') && e('构建|execution|build|executionID=%s'); // 查看构建菜单
|
||||
r($executionTester->setKanbanMenuTest()) && p('CFD:link') && e('累积流图|execution|cfd|executionID=%s'); // 查看累积流图菜单
|
||||
r($executionTester->setKanbanMenuTest()) && p('settings:link') && e('设置|execution|view|executionID=%s'); // 查看设置菜单
|
||||
@@ -724,7 +724,7 @@ class extensionModel extends model
|
||||
{
|
||||
$commandTips[] = PHP_OS == 'Linux' ? "sudo rm -fr $appRoot$dir" : "rmdir $appRoot$dir /s /q";
|
||||
}
|
||||
elseif(!@rmdir($path))
|
||||
elseif(!$this->classFile->removeDir($path))
|
||||
{
|
||||
$commandTips[] = PHP_OS == 'Linux' ? "sudo rm -fr $appRoot$dir" : "rmdir $appRoot$dir /s /q";
|
||||
}
|
||||
|
||||
@@ -571,6 +571,11 @@ class file extends control
|
||||
}
|
||||
if($file)
|
||||
{
|
||||
if($this->viewType === 'json')
|
||||
{
|
||||
echo json_encode(array('result' => 'success', 'data' => array('id' => $file->id, 'title' => $file->title, 'extension' => $file->extension, 'size' => $file->realPath, 'gid' => $file->gid, 'addedBy' => $file->addedBy, 'addedDate' => $file->addedDate, 'objectType' => $file->objectType, 'objectID' => $file->objectID)));
|
||||
return;
|
||||
}
|
||||
$fileID = $file->id;
|
||||
return $this->fetch('file', 'read', "fileID=$fileID&stream=$stream");
|
||||
}
|
||||
@@ -582,6 +587,13 @@ class file extends control
|
||||
$file = $this->file->query($objectType, $objectID, $title, $extra);
|
||||
if(empty($file)) return $this->send(array('result' => 'fail', 'message' => $this->lang->file->fileNotFound, 'load' => helper::createLink('my', 'index'), 'closeModal' => true));
|
||||
$fileID = $file->id;
|
||||
|
||||
if($this->viewType === 'json')
|
||||
{
|
||||
echo json_encode(array('result' => 'success', 'data' => array('id' => $file->id, 'title' => $file->title, 'extension' => $file->extension, 'size' => $file->realPath, 'gid' => $file->gid, 'addedBy' => $file->addedBy, 'addedDate' => $file->addedDate, 'objectType' => $file->objectType, 'objectID' => $file->objectID)));
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->fetch('file', 'read', "fileID=$fileID&stream=$stream");
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ class fileModel extends model
|
||||
$file['objectID'] = $objectID;
|
||||
$file['addedBy'] = $this->app->user->account;
|
||||
$file['addedDate'] = $now;
|
||||
$file['extra'] = $extra;
|
||||
if($extra) $file['extra'] = $extra;
|
||||
unset($file['tmpname']);
|
||||
$this->dao->insert(TABLE_FILE)->data($file)->exec();
|
||||
$fileTitles[$this->dao->lastInsertId()] = $file['title'];
|
||||
@@ -279,6 +279,7 @@ class fileModel extends model
|
||||
$file['title'] = $purifier->purify($file['title']);
|
||||
$file['size'] = $size[$id];
|
||||
$file['tmpname'] = $tmp_name[$id];
|
||||
$file['extra'] = !empty($extra[$id]) ? $extra[$id] : '';
|
||||
$files[] = $file;
|
||||
}
|
||||
}
|
||||
@@ -464,12 +465,12 @@ class fileModel extends model
|
||||
/**
|
||||
* Set path name of the uploaded file to be saved.
|
||||
*
|
||||
* @param int $fileID
|
||||
* @param string $extension
|
||||
* @param string|int $fileID
|
||||
* @param string $extension
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function setPathName(int $fileID, string $extension): string
|
||||
public function setPathName(string|int $fileID, string $extension): string
|
||||
{
|
||||
$sessionID = session_id();
|
||||
$randString = substr($sessionID, mt_rand(0, strlen($sessionID) - 5), 3);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user