Merge branch ztflow-sunguangming-main#sunguangming of zentao/zentaopms (#9032)

This commit is contained in:
刘刚
2025-05-29 16:29:30 +08:00
committed by Gitfox
192 changed files with 6685 additions and 623 deletions
+1 -1
View File
@@ -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
+10 -6
View File
@@ -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
+1 -3
View File
@@ -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
+1 -1
View File
@@ -1 +1 @@
21.7
21.7.1
+1 -1
View File
@@ -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);
+3 -3
View File
@@ -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
View File
@@ -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.
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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`;
+53
View File
@@ -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
View File
@@ -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
View File
@@ -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
完成的需求
开源版:
+5 -1
View File
@@ -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);
}
+2
View File
@@ -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
View File
@@ -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.
+51 -12
View File
@@ -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
View File
@@ -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.
*
+2 -2
View File
@@ -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
View File
@@ -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());}
}
}
+3 -1
View File
@@ -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();
+1
View File
@@ -0,0 +1 @@
.deliverable-item {padding-left: 3.5rem !important}
+44
View File
@@ -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;
}
+91
View File
@@ -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)
);
}
}
+33 -2
View File
@@ -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: {}},
+5
View File
@@ -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)
);
}
+2 -2
View File
@@ -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 -1
View File
@@ -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;}
-2
View File
@@ -3,5 +3,3 @@
/* 半透明黄色 */
.monaco-editor .yellow-decoration {background-color: rgba(255, 255, 0, 0.2);}
.monaco-editor .margin .line-numbers {padding-left: 16px;}
+1 -1
View File
@@ -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);
+18 -15
View File
@@ -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;
}
+1
View File
@@ -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;
+2
View File
@@ -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();
+2
View File
@@ -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();
+2
View File
@@ -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();
+2
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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');
+1
View File
@@ -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';
+1
View File
@@ -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';
+1
View File
@@ -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';
+12 -7
View File
@@ -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');
+2 -1
View File
@@ -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'] = '按照功能菜单进行系统的要素配置。';
+2 -2
View File
@@ -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,
+2
View File
@@ -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');
+6 -5
View File
@@ -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
View File
@@ -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. */
+6
View File
@@ -40,3 +40,9 @@ function linkBug()
openUrl(link, {load: 'modal', size: 'lg'});
}
function setDuplicateBug()
{
const productID = $('[name=product]').val();
loadProductBugs(productID, bug.id);
}
+3 -3
View File
@@ -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.
+4
View File
@@ -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');
+1
View File
@@ -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')
+7
View File
@@ -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')
+2 -1
View File
@@ -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);
+2 -1
View File
@@ -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
View File
@@ -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);
+1
View File
@@ -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";
+1
View File
@@ -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}";
+1
View File
@@ -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}";
+1
View File
@@ -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}";
+5 -6
View File
@@ -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);
}
}
+20
View File
@@ -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);
+10 -3
View File
@@ -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平台的集群内存
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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';
+2 -1
View File
@@ -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';
+34 -31
View File
@@ -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 = '投入人员';
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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):?>
+1 -1
View File
@@ -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;?>
+1 -1
View File
@@ -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)
{
+1
View File
@@ -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';
+1
View File
@@ -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';
+1
View File
@@ -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';
+1
View File
@@ -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
View File
@@ -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);
}
}
}
+3
View File
@@ -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);
+3
View File
@@ -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.';
+3
View File
@@ -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.';
+3
View File
@@ -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.';
+3
View File
@@ -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 = '该库下其他人创建的文档暂不支持移动,是否确认移动?';
+2 -2
View File
@@ -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();
+6
View File
@@ -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
View File
@@ -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')
{
+11
View File
@@ -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';
+27 -12
View File
@@ -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());
+9
View File
@@ -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); });
+3 -1
View File
@@ -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
View File
@@ -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;
}
}
+2 -2
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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'); // 查看设置菜单
+1 -1
View File
@@ -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";
}
+12
View File
@@ -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");
}
+5 -4
View File
@@ -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