Merge branch 'master' of https://gitlab.zcorp.cc/easycorp/zentaopms into sprint/189_tianshujie_task53655

This commit is contained in:
tianshujie
2022-05-11 13:38:11 +08:00
80 changed files with 1985 additions and 369 deletions
+13
View File
@@ -0,0 +1,13 @@
sonarqube:
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- sonar-scanner -Dsonar.inclusions=$(git diff --name-only HEAD~1|tr '\n' ',')
allow_failure: true
only:
- master # or the name of your main branch
+323
View File
@@ -89,3 +89,326 @@ REPLACE INTO `zt_zoutput` (`id`, `activity`, `name`, `content`, `optional`, `sta
DELETE FROM `zt_config` WHERE `section` = 'customMenu';
UPDATE `zt_story` SET `plan` = '' WHERE `plan` = 0;
UPDATE `zt_workflowfield` set `control` = 'datetime' WHERE `module` = 'testcase' and `field` IN ('openedDate','reviewedDate','lastEditedDate','lastRunDate');
UPDATE `zt_workflowfield` set `control` = 'date' WHERE `module` = 'testtask' and `field` IN ('begin','end');
UPDATE `zt_workflowfield` set `control` = 'richtext' WHERE `module` = 'testtask' and `field` = 'desc';
UPDATE `zt_workflowfield` set `control` = 'datetime' WHERE `module` IN ('testsuite','caselib') and `field` IN ('addedDate','lastEditedDate');
UPDATE `zt_workflowfield` set `control` = 'richtext' WHERE `module` = 'testsuite' and `field` = 'desc';
UPDATE `zt_workflowfield` set `control` = 'datetime' WHERE `module` = 'feedback' and `field` IN ('openedDate','reviewedDate','processedDate','closedDate','editedDate','assignedDate');
UPDATE `zt_workflowfield` SET `control` = 'datetime' WHERE `module` = 'product' and `field` = 'createdDate';
UPDATE `zt_workflowfield` SET `control` = 'date' WHERE `module` = 'productplan' and `field` = 'begin';
UPDATE `zt_workflowfield` SET `control` = 'date' WHERE `module` = 'productplan' and `field` = 'end';
UPDATE `zt_workflowfield` SET `control` = 'radio' WHERE `module` = 'productplan' and `field` = 'deleted';
UPDATE `zt_workflowfield` SET `control` = 'select', `options` = 12 WHERE `module` = 'productplan' and `field` = 'parent';
UPDATE `zt_workflowfield` SET `control` = 'date' WHERE `module` = 'release' and `field` = 'date';
UPDATE `zt_workflowfield` SET `control` = 'richtext' WHERE `module` = 'release' and `field` = 'desc';
UPDATE `zt_workflowfield` SET `control` = 'radio' WHERE `module` = 'release' and `field` = 'deleted';
UPDATE `zt_workflowfield` SET `control` = 'datetime' WHERE `module` = 'story' and `field` = 'assignedDate';
UPDATE `zt_workflowfield` SET `control` = 'datetime' WHERE `module` = 'story' and `field` = 'lastEditedDate';
UPDATE `zt_workflowfield` SET `control` = 'date' WHERE `module` = 'story' and `field` = 'reviewedDate';
UPDATE `zt_workflowfield` SET `control` = 'datetime' WHERE `module` = 'story' and `field` = 'closedDate';
UPDATE `zt_workflowfield` SET `control` = 'radio' WHERE `module` = 'story' and `field` = 'deleted';
UPDATE `zt_workflowaction` SET `position` = 'browse' WHERE `module` = 'product' and `action` = 'all' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `type` = 'batch', `position` = 'browse' WHERE `module` = 'product' and `action` = 'batchedit' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `position` = 'browse' WHERE `module` = 'product' and `action` = 'browse' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `show` = 'direct' WHERE `module` = 'product' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `type` = 'batch', `position` = 'browse' WHERE `module` = 'productplan' and `action` = 'batchedit' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `position` = 'browse' WHERE `module` = 'productplan' and `action` = 'browse' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `show` = 'direct' WHERE `module` = 'productplan' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `position` = 'browse' WHERE `module` = 'release' and `action` = 'browse' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `show` = 'direct' WHERE `module` = 'release' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `type` = 'batch' WHERE `module` = 'story' and `action` = 'batchcreate' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `type` = 'batch' WHERE `module` = 'story' and `action` = 'batchedit' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `position` = 'browse' WHERE `module` = 'story' and `action` = 'browse' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `position` = 'browse' WHERE `module` = 'story' and `action` = 'exporttemplate' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `position` = 'browse' WHERE `module` = 'story' and `action` = 'import' and `vision` = 'rnd';
UPDATE `zt_workflowaction` SET `position` = 'view' WHERE `module` = 'story' and `action` = 'view' and `vision` = 'rnd';
DELETE FROM `zt_workflowfield` WHERE `module`='execution' AND `field`='stage';
DELETE FROM `zt_workflowfield` WHERE `module`='program' AND `field`='stage';
DELETE FROM `zt_workflowfield` WHERE `module`='project' AND `field`='stage';
DELETE FROM `zt_workflowfield` WHERE `module`='bug' AND `field`='feedback';
DELETE FROM `zt_workflowfield` WHERE `module`='task' AND `field`='feedback';
ALTER TABLE `zt_workflowdatasource` ADD UNIQUE `code` (`code`);
REPLACE INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `datasource`, `view`, `keyField`, `valueField`) VALUES
('lang', '项目模型', 'projectModel', '1', 'projectModel', '', '', ''),
('lang', '反馈类型', 'feedbackType', '1', 'feedbackType', '', '', ''),
('lang', '反馈处理方案', 'feedbackSolution', '1', 'feedbackSolution', '', '', '');
REPLACE INTO `zt_workflowfield` (`module`, `field`, `type`, `length`, `name`, `control`, `expression`, `options`, `default`, `rules`, `placeholder`, `order`, `searchOrder`, `exportOrder`, `canExport`, `canSearch`, `isValue`, `readonly`, `buildin`, `desc`, `createdBy`, `createdDate`, `editedBy`, `editedDate`) VALUES
('execution', 'type', 'char', '30', '迭代类型', 'select', '', '16', 'sprint', '', '', 3, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'begin', 'date', '', '计划开始', 'date', '', '', '', '', '', 14, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'end', 'date', '', '计划完成', 'date', '', '', '', '', '', 15, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'days', 'smallint', '5', '可用工作日', 'integer', '', '', '', '', '', 18, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'pri', 'enum', '', '优先级', 'select', '', '[1,2,3,4]', '1', '', '', 20, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'desc', 'text', '', '迭代描述', 'richtext', '', '', '', '', '', 21, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'openedDate', 'datetime', '', '创建日期', 'datetime', '', '', '', '', '', 27, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'closedDate', 'datetime', '', '关闭日期', 'datetime', '', '', '', '', '', 32, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'canceledDate', 'datetime', '', '取消日期', 'datetime', '', '', '', '', '', 34, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'acl', 'char', '30', '访问控制', 'select', '', '18', 'open', '', '', 41, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'whitelist', 'text', '', '白名单', 'multi-select', '', 'user', '', '', '', 42, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'deleted', 'enum', '', '已删除', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 47, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'type', 'char', '30', '类型', 'select', '', '16', 'sprint', '', '', 2, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'begin', 'date', '', '计划开始', 'date', '', '', '', '', '', 15, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'end', 'date', '', '计划完成', 'date', '', '', '', '', '', 16, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'days', 'smallint', '5', '可用工作日', 'integer', '', '', '', '', '', 19, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'pri', 'enum', '', '优先级', 'select', '', '[1,2,3,4]', '1', '', '', 21, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'desc', 'text', '', '项目集描述', 'richtext', '', '', '', '', '', 22, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'openedDate', 'datetime', '', '创建日期', 'datetime', '', '', '', '', '', 28, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'closedDate', 'datetime', '', '关闭日期', 'datetime', '', '', '', '', '', 33, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'canceledDate', 'datetime', '', '取消日期', 'datetime', '', '', '', '', '', 35, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'whitelist', 'text', '', '白名单', 'multi-select', '', 'user', '', '', '', 43, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'deleted', 'enum', '', '已删除', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 48, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'type', 'char', '30', '项目类型', 'select', '', '16', 'sprint', '', '', 3, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'model', 'char', '30', '项目管理方式', 'input', '', '45', '', '', '', 2, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'name', 'varchar', '90', '项目名称', 'input', '', '', '', '', '', 15, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'begin', 'date', '', '计划开始', 'date', '', '', '', '', '', 16, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'end', 'date', '', '计划完成', 'date', '', '', '', '', '', 17, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'days', 'smallint', '5', '可用工作日', 'integer', '', '', '', '', '', 20, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'pri', 'enum', '', '优先级', 'select', '', '[1,2,3,4]', '1', '', '', 22, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'desc', 'text', '', '项目描述', 'richtext', '', '', '', '', '', 23, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'openedDate', 'datetime', '', '创建日期', 'datetime', '', '', '', '', '', 29, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'closedDate', 'datetime', '', '关闭日期', 'datetime', '', '', '', '', '', 34, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'canceledDate', 'datetime', '', '取消日期', 'datetime', '', '', '', '', '', 36, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'acl', 'char', '30', '访问控制', 'select', '', '18', 'open', '', '', 43, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'whitelist', 'text', '', '项目白名单', 'multi-select', '', 'user', '', '', '', 44, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'deleted', 'enum', '', '已删除', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 49, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('build', 'project', 'mediumint', '8', '所属项目', 'select', '', '2', '0', '', '', 4, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('build', 'date', 'date', '', '打包日期', 'date', '', '', '', '', '', 9, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('build', 'desc', 'text', '', '描述', 'richtext', '', '', '', '', '', 13, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('build', 'deleted', 'enum', '', '已删除', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 14, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'project', 'mediumint', '8', '所属项目', 'select', '', '2', '0', '', '', 2, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'storyVersion', 'smallint', '6', '研发需求版本', 'integer', '', '', '1', '', '', 9, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'toTask', 'mediumint', '8', '转任务', 'select', '', '5', '0', '', '', 11, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'toStory', 'mediumint', '8', '转研发需求', 'select', '', '4', '0', '', '', 12, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'steps', 'mediumtext', '', '重现步骤', 'richtext', '', '', '', '', '', 22, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'activatedDate', 'datetime', '', '激活日期', 'datetime', '', '', '', '', '', 27, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'openedDate', 'datetime', '', '创建日期', 'datetime', '', '', '', '', '', 32, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'openedBuild', 'varchar', '255', '影响版本', 'multi-select', '', '10', '', '', '', 33, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'assignedDate', 'datetime', '', '指派日期', 'datetime', '', '', '', '', '', 35, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'deadline', 'date', '', '截止日期', 'date', '', '', '', '', '', 36, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'resolvedDate', 'datetime', '30', '解决日期', 'datetime', '', '', '', '', '', 40, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'closedDate', 'datetime', '', '关闭日期', 'datetime', '', '', '', '', '', 42, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'case', 'mediumint', '8', '相关用例', 'select', '', '41', '', '', '', 45, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'caseVersion', 'smallint', '6', '用例版本', 'integer', '', '', '', '', '', 46, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'lastEditedDate', 'datetime', '30', '修改日期', 'datetime', '', '', '', '', '', 58, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'deleted', 'enum', '', '已删除', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 59, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'project', 'mediumint', '8', '所属项目', 'select', '', '2', '0', '', '', 2, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'storyVersion', 'smallint', '6', '研发需求版本', 'integer', '', '', '1', '', '', 7, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'estimate', 'float', '', '最初预计', 'decimal', '', '', '', '', '', 13, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'consumed', 'float', '', '总计消耗', 'decimal', '', '', '', '', '', 14, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'left', 'float', '', '预计剩余', 'decimal', '', '', '', '', '', 15, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'deadline', 'date', '', '截止日期', 'date', '', '', '', '', '', 16, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'desc', 'text', '', '任务描述', 'richtext', '', '', '', '', '', 20, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'openedDate', 'datetime', '', '创建日期', 'datetime', '', '', '', '', '', 23, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'assignedDate', 'datetime', '30', '指派日期', 'datetime', '', '', '', '', '', 25, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'estStarted', 'date', '', '预计开始', 'date', '', '', '', '', '', 26, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'realStarted', 'date', '', '实际开始', 'date', '', '', '', '', '', 27, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'finishedDate', 'datetime', '', '实际完成', 'datetime', '', '', '', '', '', 29, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'canceledDate', 'datetime', '', '取消时间', 'datetime', '', '', '', '', '', 32, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'closedDate', 'datetime', '', '关闭时间', 'datetime', '', '', '', '', '', 34, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'lastEditedDate', 'datetime', '', '最后修改日期', 'datetime', '', '', '', '', '', 39, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'deleted', 'enum', '', '已删除', 'select', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 48, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'lifetime', 'char', '30', 'lifetime', 'input', '', '', '', '', '', 4, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'lifetime', 'char', '30', 'lifetime', 'input', '', '', '', '', '', 3, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'designVersion', 'smallint', '6', '设计版本', 'integer', '', '', '', '', '', 8, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'attribute', 'varchar', '30', 'attribute', 'input', '', '', '0', '', '', 5, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'percent', 'float', '', 'percent', 'input', '', '', '0', '', '', 6, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'milestone', 'enum', '', 'milestone', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 7, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'output', 'text', '', 'output', 'textarea', '', '', '', '', '', 8, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'auth', 'char', '30', 'auth', 'input', '', '', '', '', '', 9, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'path', 'varchar', '255', 'path', 'input', '', '', '0', '', '', 10, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'grade', 'tinyint', '3', 'grade', 'integer', '', '', '0', '', '', 11, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'realBegan', 'date', '', '实际开始日期', 'date', '', '', '', '', '', 16, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'realEnd', 'date', '', '实际完成日期', 'date', '', '', '', '', '', 17, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'version', 'smallint', '6', 'version', 'integer', '', '', '', '', '', 22, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'parentVersion', 'smallint', '6', 'parentVersion', 'integer', '', '', '', '', '', 23, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'planDuration', 'int', '11', 'planDuration', 'integer', '', '', '', '', '', 24, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'realDuration', 'int', '11', 'realDuration', 'integer', '', '', '', '', '', 25, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'openedVersion', 'varchar', '20', 'openedVersion', 'input', '', '', '', '', '', 28, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'lastEditedBy', 'varchar', '30', 'lastEditedBy', 'select', '', 'user', '', '', '', 29, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'lastEditedDate', 'datetime', '', 'lastEditedDate', 'datetime', '', '', '', '', '', 30, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'suspendedDate', 'date', '', 'suspendedDate', 'date', '', '', '', '', '', 35, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'vision', 'varchar', '10', 'vision', 'input', '', '', 'rnd', '', '', 44, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'displayCards', 'enum', '', 'displayCards', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 45, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('execution', 'fluidBoard', 'enum', '', 'fluidBoard', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 46, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'output', 'text', '', 'output', 'textarea', '', '', '', '', '', 9, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'auth', 'char', '30', 'auth', 'input', '', '', '', '', '', 10, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'path', 'varchar', '255', 'path', 'input', '', '', '0', '', '', 12, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'grade', 'tinyint', '3', 'grade', 'integer', '', '', '0', '', '', 13, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'realBegan', 'date', '', 'realBegan', 'date', '', '', '', '', '', 17, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'realEnd', 'date', '', 'realEnd', 'date', '', '', '', '', '', 18, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'version', 'smallint', '6', 'version', 'integer', '', '', '', '', '', 23, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'parentVersion', 'smallint', '6', 'parentVersion', 'integer', '', '', '', '', '', 24, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'planDuration', 'int', '11', 'planDuration', 'integer', '', '', '', '', '', 25, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'realDuration', 'int', '11', 'realDuration', 'integer', '', '', '', '', '', 26, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'openedVersion', 'varchar', '20', 'openedVersion', 'input', '', '', '', '', '', 29, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'lastEditedBy', 'varchar', '30', 'lastEditedBy', 'select', '', 'user', '', '', '', 30, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'lastEditedDate', 'datetime', '', 'lastEditedDate', 'datetime', '', '', '', '', '', 31, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'suspendedDate', 'date', '', 'suspendedDate', 'date', '', '', '', '', '', 36, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'vision', 'varchar', '10', 'vision', 'input', '', '', 'rnd', '', '', 45, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'displayCards', 'enum', '', 'displayCards', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 46, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('program', 'fluidBoard', 'enum', '', 'fluidBoard', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 47, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'lifetime', 'char', '30', '项目周期', 'input', '', '', '', '', '', 4, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'attribute', 'varchar', '30', '阶段类型', 'input', '', '', '0', '', '', 7, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'percent', 'float', '', '工作量占比', 'input', '', '', '0', '', '', 8, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'milestone', 'enum', '', '里程碑', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 9, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'output', 'text', '', '输出', 'textarea', '', '', '', '', '', 10, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'auth', 'char', '30', '权限控制', 'input', '', '', '', '', '', 11, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'path', 'varchar', '255', '路径', 'input', '', '', '0', '', '', 13, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'grade', 'tinyint', '3', '层级', 'integer', '', '', '0', '', '', 14, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'realBegan', 'date', '', '实际开始日期', 'date', '', '', '', '', '', 18, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'realEnd', 'date', '', '实际完成日期', 'date', '', '', '', '', '', 19, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'version', 'smallint', '6', '版本', 'integer', '', '', '', '', '', 24, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'parentVersion', 'smallint', '6', '父版本', 'integer', '', '', '', '', '', 25, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'planDuration', 'int', '11', '计划周期天数', 'integer', '', '', '', '', '', 26, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'realDuration', 'int', '11', '实际周期天数', 'integer', '', '', '', '', '', 27, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'openedVersion', 'varchar', '20', '创建版本', 'input', '', '', '', '', '', 30, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'lastEditedBy', 'varchar', '30', '最后编辑人', 'select', '', 'user', '', '', '', 31, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'lastEditedDate', 'datetime', '', '最后编辑日期', 'datetime', '', '', '', '', '', 32, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'suspendedDate', 'date', '', 'suspendedDate', 'date', '', '', '', '', '', 37, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'vision', 'varchar', '10', 'vision', 'input', '', '', 'rnd', '', '', 46, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'displayCards', 'enum', '', 'displayCards', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 47, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('project', 'fluidBoard', 'enum', '', 'fluidBoard', 'radio', '', '[\"\\u672a\\u5220\\u9664\",\"\\u5df2\\u5220\\u9664\"]', '0', '', '', 48, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'hardware', 'varchar', '30', '硬件', 'input', '', '', '', '', '', 20, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'feedbackBy', 'varchar', '100', '反馈者', 'input', '', '', '', '', '', 28, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'notifyEmail', 'varchar', '100', '通知邮箱', 'input', '', '', '', '', '', 29, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'result', 'mediumint', '8', '结果', 'input', '', '', '', '', '', 47, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'repo', 'mediumint', '8', '所属版本库', 'input', '', '', '', '', '', 48, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'mr', 'mediumint', '8', 'mr', 'input', '', '', '', '', '', 49, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'entry', 'text', '', 'entry', 'input', '', '', '', '', '', 50, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'lines', 'varchar', '10', '代码行', 'input', '', '', '', '', '', 51, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'v1', 'varchar', '40', '版本1', 'input', '', '', '', '', '', 52, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'v2', 'varchar', '40', '版本2', 'input', '', '', '', '', '', 53, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'repoType', 'varchar', '30', '版本库类型', 'input', '', '', '', '', '', 54, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('bug', 'issueKey', 'varchar', '50', 'issueKey', 'input', '', '', '', '', '', 55, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'version', 'smallint', '6', '版本', 'integer', '', '', '', '', '', 21, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'planDuration', 'int', '11', '计划持续天数', 'integer', '', '', '', '', '', 35, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'realDuration', 'int', '11', '实际持续天数', 'integer', '', '', '', '', '', 36, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'activatedDate', 'datetime', '', '激活日期', 'datetime', '', '', '', '', '', 40, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'repo', 'mediumint', '8', 'repo', 'input', '', '', '', '', '', 41, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'mr', 'mediumint', '8', 'mr', 'input', '', '', '', '', '', 42, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'entry', 'text', '', 'entry', 'input', '', '', '', '', '', 43, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'lines', 'varchar', '10', 'lines', 'input', '', '', '', '', '', 44, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'v1', 'varchar', '40', 'v1', 'input', '', '', '', '', '', 45, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'v2', 'varchar', '40', 'v2', 'input', '', '', '', '', '', 46, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('task', 'vision', 'varchar', '10', 'vision', 'input', '', '', '', '', '', 47, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('feedback', 'type', 'char', '30', '反馈类型', 'select', '', '', '', '', '', 4, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('feedback', 'notifyEmail', 'varchar', '100', '通知邮箱', 'input', '', '', '', '', '', 9, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('feedback', 'faq', 'mediumint', '8', 'FAQ', 'input', '', '', '', '', '', 11, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('feedback', 'feedbackBy', 'varchar', '100', '反馈者', 'select', '', 'user', '', '', '', 25, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('feedback', 'solution', 'char', '30', '反馈处理方案', 'select', '', '', '', '', '', 5, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'auto', 'varchar', '10', '自动化测试用例', 'input', '', '', '', '', '', 15, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'frame', 'varchar', '10', '自动化测试框架', 'input', '', '', '', '', '', 16, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'howRun', 'char', '30', '测试方式', 'input', '', '', '', '', '', 18, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'scriptedBy', 'char', '30', '脚本由谁创建', 'select', '', 'user', '', '', '', 18, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'scriptedDate', 'char', '30', '脚本创建日期', 'date', '', '', '', '', '', 18, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'scriptStatus', 'char', '30', '脚本状态', 'input', '', '', '', '', '', 18, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'scriptLocation', 'char', '30', '脚本地址', 'input', '', '', '', '', '', 18, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'frequency', 'char', '30', '使用频率', 'input', '', '', '', '', '', 28, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'fromCaseID', 'char', '30', '用例来源ID', 'input', '', '', '', '', '', 28, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'fromCaseVersion', 'char', '30', '用例来源版本', 'input', '', '', '', '', '', 5, 0, 0, '0', '0', '0', '1', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00'),
('testcase', 'subStatus', 'varchar', '30', '子状态', 'select', '', '', '', '', '', 17, 0, 0, '0', '0', '0', '0', 1, '', '', '0000-00-00 00:00:00', '', '0000-00-00 00:00:00');
UPDATE `zt_workflowfield` SET `options`=(SELECT id FROM `zt_workflowdatasource` WHERE `code`='projectModel' LIMIT 1) WHERE `module`='project' AND `field`='model';
UPDATE `zt_workflowfield` SET `options`=(SELECT id FROM `zt_workflowdatasource` WHERE `code`='feedbackType' LIMIT 1) WHERE `module`='feedback' AND `field`='type';
UPDATE `zt_workflowfield` SET `options`=(SELECT id FROM `zt_workflowdatasource` WHERE `code`='feedbackSolution' LIMIT 1) WHERE `module`='feedback' AND `field`='solution';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='project' AND `action`='browse';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='project' AND `action`='create';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='project' AND `action`='batchedit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='project' AND `action`='edit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='side' WHERE `module`='project' AND `action`='view';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='dropdownlist', `open`='none', `layout`='normal' WHERE `module`='project' AND `action`='delete';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='dropdownlist', `open`='modal', `layout`='normal' WHERE `module`='project' AND `action`='close';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='dropdownlist', `open`='modal', `layout`='normal' WHERE `module`='project' AND `action`='activate';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='project' AND `action`='start';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='dropdownlist', `open`='modal', `layout`='normal' WHERE `module`='project' AND `action`='suspend';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='execution' AND `action`='task';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='execution' AND `action`='create';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='execution' AND `action`='batchedit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='execution' AND `action`='edit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='execution' AND `action`='view';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='execution' AND `action`='delete';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='execution' AND `action`='close';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='execution' AND `action`='activate';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='execution' AND `action`='start';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='execution' AND `action`='putoff';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='execution' AND `action`='suspend';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='execution' AND `action`='all';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='task' AND `action`='browse';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='task' AND `action`='create';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='task' AND `action`='batchcreate';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='task' AND `action`='batchedit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='exporttemplate';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='import';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='showimport';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='side' WHERE `module`='task' AND `action`='edit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='side' WHERE `module`='task' AND `action`='view';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='task' AND `action`='delete';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='close';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='activate';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='assignTo';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='task' AND `action`='confirmStoryChange';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='start';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='finish';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='pause';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='restart';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='cancel';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='build' AND `action`='browse';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='build' AND `action`='create';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='build' AND `action`='edit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='build' AND `action`='view';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='build' AND `action`='delete';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='bug' AND `action`='browse';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='bug' AND `action`='create';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='bug' AND `action`='batchcreate';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='bug' AND `action`='batchedit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='bug' AND `action`='exporttemplate';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='bug' AND `action`='import';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='bug' AND `action`='showimport';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='side' WHERE `module`='bug' AND `action`='edit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='side' WHERE `module`='bug' AND `action`='view';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='bug' AND `action`='delete';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='bug' AND `action`='close';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='bug' AND `action`='activate';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='bug' AND `action`='assignTo';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='bug' AND `action`='confirmBug';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='bug' AND `action`='resolve';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='bug' AND `action`='batchconfirm';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='bug' AND `action`='batchresolve';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='dropdownlist', `open`='modal', `layout`='normal' WHERE `module`='project' AND `action`='activate';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='project' AND `action`='batchedit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='project' AND `action`='browse';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='dropdownlist', `open`='modal', `layout`='normal' WHERE `module`='project' AND `action`='close';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='project' AND `action`='create';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='dropdownlist', `open`='none', `layout`='normal' WHERE `module`='project' AND `action`='delete';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='project' AND `action`='edit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='project' AND `action`='start';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='dropdownlist', `open`='modal', `layout`='normal' WHERE `module`='project' AND `action`='suspend';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='side' WHERE `module`='project' AND `action`='view';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='activate';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='assignTo';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='task' AND `action`='batchcreate';
UPDATE `zt_workflowaction` SET `type`='batch', `position`='browse', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='task' AND `action`='batchedit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='task' AND `action`='browse';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='cancel';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='close';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='task' AND `action`='confirmStoryChange';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='normal' WHERE `module`='task' AND `action`='create';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='none', `layout`='normal' WHERE `module`='task' AND `action`='delete';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='normal', `layout`='side' WHERE `module`='task' AND `action`='edit';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='exporttemplate';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='finish';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='import';
UPDATE `zt_workflowaction` SET `type`='single', `position`='view', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='pause';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='restart';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='showimport';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browseandview', `show`='direct', `open`='modal', `layout`='normal' WHERE `module`='task' AND `action`='start';
UPDATE `zt_workflowaction` SET `type`='single', `position`='browse', `show`='direct', `open`='normal', `layout`='side' WHERE `module`='task' AND `action`='view';
+7 -1
View File
@@ -416,7 +416,7 @@ class baseControl
$moduleName = basename(dirname(dirname(realpath($viewFile))));
$extPath = $this->app->getModuleExtPath('', $moduleName, 'view');
$checkedOrder = array('site', 'custom', 'vision', 'xuan', 'common');
$checkedOrder = array('site', 'saas', 'custom', 'vision', 'xuan', 'common');
$fileName = basename($viewFile);
foreach($checkedOrder as $checkedType)
{
@@ -805,6 +805,12 @@ class baseControl
$commonActionExtFile = $actionExtPath['custom'] . strtolower($methodName) . '.php';
if(file_exists($commonActionExtFile)) $file2Included = $commonActionExtFile;
if(!empty($actionExtPath['saas']))
{
$commonActionExtFile = $actionExtPath['saas'] . strtolower($methodName) . '.php';
if(file_exists($commonActionExtFile)) $file2Included = $commonActionExtFile;
}
if(!empty($actionExtPath['site']))
{
/**
+1
View File
@@ -260,6 +260,7 @@ class baseModel
$moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'model');
if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php';
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['custom'] . 'class/' . $extensionName . '.class.php';
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['saas'] . 'class/' . $extensionName . '.class.php';
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['vision'] . 'class/' . $extensionName . '.class.php';
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['xuan'] . 'class/' . $extensionName . '.class.php';
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['common'] . 'class/' . $extensionName . '.class.php';
+24 -2
View File
@@ -1390,6 +1390,9 @@ class baseRouter
if($this->checkModuleName($moduleName))
{
$modulePath = $this->getExtensionRoot() . 'saas' . DS . $moduleName . DS;
if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath;
/* 1. 最后尝试在定制开发中寻找。 Finally, try to find the module in the custom dir. */
$modulePath = $this->getExtensionRoot() . 'custom' . DS . $moduleName . DS;
if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath;
@@ -1432,8 +1435,10 @@ class baseRouter
*/
public function getModuleExtPath($appName, $moduleName, $ext)
{
$saasExtPath = $this->getExtensionRoot() . 'saas' . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
/* 检查失败或者extensionLevel为0,直接返回空。If check failed or extensionLevel == 0, return empty array. */
if(!$this->checkModuleName($moduleName) or $this->config->framework->extensionLevel == 0) return array();
if(!$this->checkModuleName($moduleName) or $this->config->framework->extensionLevel == 0) return array('saas' => $saasExtPath);
$paths = array();
@@ -1442,10 +1447,16 @@ class baseRouter
$paths['xuan'] = $this->getExtensionRoot() . 'xuan' . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
$paths['vision'] = $this->config->vision == 'rnd' ? '' : $this->basePath . 'extension' . DS . $this->config->vision . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
$paths['custom'] = $this->getExtensionRoot() . 'custom' . DS . $moduleName . DS . 'ext' . DS . $ext . DS;
if($this->config->framework->extensionLevel == 1) return $paths;
if($this->config->framework->extensionLevel == 1)
{
$paths['saas'] = $saasExtPath;
return $paths;
}
/* When extensionLevel == 2. */
$paths['site'] = empty($this->siteCode) ? '' : $this->getExtensionRoot() . $this->config->edition . DS . $moduleName . DS . 'ext' . DS . '_' . $this->siteCode . DS . $ext . DS;
$paths['saas'] = $saasExtPath;
return $paths;
}
@@ -1494,6 +1505,12 @@ class baseRouter
/* 如果扩展目录为空,不包含任何扩展文件。If there's no ext paths return false.*/
if(empty($moduleExtPaths)) return false;
if(!empty( $moduleExtPaths['saas']))
{
$this->extActionFile = $moduleExtPaths['saas'] . $this->methodName . '.php';
if(file_exists($this->extActionFile)) return true;
}
/* 1. 如果extensionLevel == 2,且扩展文件存在,返回该站点扩展文件。 If extensionLevel == 2 and site extensionFile exists, return it. */
if($this->config->framework->extensionLevel == 2 and !empty( $moduleExtPaths['site']))
{
@@ -2322,6 +2339,7 @@ class baseRouter
if(!empty($extConfigPath['common'])) $commonExtConfigFiles = helper::ls($extConfigPath['common'], '.php');
if(!empty($extConfigPath['xuan'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['xuan'], '.php'));
if(!empty($extConfigPath['vision'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['vision'], '.php'));
if(!empty($extConfigPath['saas'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['saas'], '.php'));
if(!empty($extConfigPath['custom'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['custom'], '.php'));
}
if($config->framework->extensionLevel == 2 and !empty($extConfigPath['site'])) $siteExtConfigFiles = helper::ls($extConfigPath['site'], '.php');
@@ -2427,6 +2445,9 @@ class baseRouter
{
$path = $moduleName . DS . 'lang' . DS . $this->clientLang . '.php';
$modulePath = $this->getExtensionRoot() . 'saas' . DS;
if(file_exists($modulePath . $path)) return $modulePath . $path;
/* 1. 如果通用版本里有此模块,优先使用。 If module is in the open edition, use it. */
$modulePath = $this->getModuleRoot($appName);
if(file_exists($modulePath . $path)) return $modulePath . $path;
@@ -2489,6 +2510,7 @@ class baseRouter
if(!empty($extLangPath['xuan'])) $commonExtLangFiles = array_merge($commonExtLangFiles, helper::ls($extLangPath['xuan'] . $this->clientLang, '.php'));
if(!empty($extLangPath['vision'])) $commonExtLangFiles = array_merge($commonExtLangFiles, helper::ls($extLangPath['vision'] . $this->clientLang, '.php'));
if(!empty($extLangPath['custom'])) $commonExtLangFiles = array_merge($commonExtLangFiles, helper::ls($extLangPath['custom'] . $this->clientLang, '.php'));
if(!empty($extLangPath['saas'])) $commonExtLangFiles = array_merge($commonExtLangFiles, helper::ls($extLangPath['saas'] . $this->clientLang, '.php'));
}
if($this->config->framework->extensionLevel == 2 and !empty($extLangPath['site'])) $siteExtLangFiles = helper::ls($extLangPath['site'] . $this->clientLang, '.php');
$extLangFiles = array_merge($commonExtLangFiles, $siteExtLangFiles);
+12 -6
View File
@@ -86,7 +86,7 @@ class control extends baseControl
/**
* Det default priv by workflow.
*
*
* @access public
* @return bool
*/
@@ -197,6 +197,7 @@ class control extends baseControl
$commonExtViewFile = $viewExtPath['common'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
$xuanExtViewFile = $viewExtPath['xuan'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
$visionExtViewFile = $viewExtPath['vision'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
$saasExtViewFile = $viewExtPath['saas'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
$customExtViewFile = $viewExtPath['custom'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
$siteExtViewFile = empty($viewExtPath['site']) ? '' : $viewExtPath['site'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
@@ -217,6 +218,10 @@ class control extends baseControl
{
$viewFile = $xuanExtViewFile;
}
else if(file_exists($saasExtViewFile))
{
$viewFile = $saasExtViewFile;
}
else if(file_exists($commonExtViewFile))
{
$viewFile = $commonExtViewFile;
@@ -235,10 +240,11 @@ class control extends baseControl
$commonExtHookFiles = glob($viewExtPath['vision'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
}
$xuanExtHookFiles = glob($viewExtPath['xuan'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
$saasExtHookFiles = glob($viewExtPath['saas'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
$customExtHookFiles = glob($viewExtPath['custom'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
$siteExtHookFiles = empty($viewExtPath['site']) ? '' : glob($viewExtPath['site'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
$extHookFiles = array_merge((array)$commonExtHookFiles, (array)$xuanExtHookFiles, (array)$customExtHookFiles, (array)$siteExtHookFiles);
$extHookFiles = array_merge((array)$commonExtHookFiles, (array)$xuanExtHookFiles, (array)$saasExtHookFiles, (array)$customExtHookFiles, (array)$siteExtHookFiles);
}
if(!empty($extHookFiles)) return array('viewFile' => $viewFile, 'hookFiles' => $extHookFiles);
@@ -320,7 +326,7 @@ class control extends baseControl
if(!isset($this->config->bizVersion)) return false;
$moduleName = $this->moduleName;
if($moduleName == 'bug' || $moduleName == 'feedback') return $this->$moduleName->buildOperateMenu($object, $type);
if(strpos(',bug,feedback,caselib,testsuite,testtask,testcase,product,productplan,', ",{$moduleName},") !== false) return $this->$moduleName->buildOperateMenu($object, $type);
$flow = $this->loadModel('workflow')->getByModule($moduleName);
return $this->loadModel('flow')->buildOperateMenu($flow, $object, $type);
@@ -342,9 +348,9 @@ class control extends baseControl
}
/**
* Set workflow export fields
*
* @param array $fields
* Set workflow export fields
*
* @param array $fields
* @access public
* @return array
*/
+19 -2
View File
@@ -83,7 +83,15 @@ class model extends baseModel
if(isset($this->config->bizVersion))
{
static $actions;
if(empty($actions)) $actions = $this->dao->select('*')->from(TABLE_WORKFLOWACTION)->where('module')->eq($moduleName)->andWhere('buildin')->eq('1')->andWhere('status')->eq('enable')->fetchAll('action');
if(empty($actions))
{
$actions = $this->dao->select('*')->from(TABLE_WORKFLOWACTION)
->where('module')->eq($moduleName)
->andWhere('buildin')->eq('1')
->andWhere('status')->eq('enable')
->beginIF(!empty($this->config->vision))->andWhere('vision')->eq($this->config->vision)->fi()
->fetchAll('action');
}
}
$enabled = true;
@@ -133,7 +141,16 @@ class model extends baseModel
if(strpos($module, '.') !== false) list($appName, $moduleName) = explode('.', $module);
static $actions;
if(empty($actions)) $actions = $this->dao->select('*')->from(TABLE_WORKFLOWACTION)->where('module')->eq($moduleName)->andWhere('buildin')->eq('0')->andWhere('status')->eq('enable')->orderBy('order_asc')->fetchAll();
if(empty($actions))
{
$actions = $this->dao->select('*')->from(TABLE_WORKFLOWACTION)
->where('module')->eq($moduleName)
->andWhere('buildin')->eq('0')
->andWhere('status')->eq('enable')
->beginIF(!empty($this->config->vision))->andWhere('vision')->eq($this->config->vision)->fi()
->orderBy('order_asc')
->fetchAll();
}
$menu = '';
if($show)
+1
View File
@@ -327,6 +327,7 @@ class router extends baseRouter
if(!empty($extConfigPath['common'])) $commonExtConfigFiles = helper::ls($extConfigPath['common'], '.php');
if(!empty($extConfigPath['xuan'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['xuan'], '.php'));
if(!empty($extConfigPath['vision'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['vision'], '.php'));
if(!empty($extConfigPath['saas'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['saas'], '.php'));
if(!empty($extConfigPath['custom'])) $commonExtConfigFiles = array_merge($commonExtConfigFiles, helper::ls($extConfigPath['custom'], '.php'));
}
if($config->framework->extensionLevel == 2 and !empty($extConfigPath['site'])) $siteExtConfigFiles = helper::ls($extConfigPath['site'], '.php');
+6
View File
@@ -60,3 +60,9 @@ $config->action->majorList['execution'] = array('opened', 'edited');
$config->action->needGetProjectType = 'build,task,bug,case,testcase,caselib,testtask,testsuite,testreport,doc,issue,release,risk,design,opportunity,trainplan,gapanalysis,researchplan,researchreport,';
$config->action->needGetRelateField = ',story,productplan,release,task,build,bug,testcase,case,testtask,testreport,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,researchplan,researchreport,meeting,kanbanlane,kanbancolumn,';
$config->action->noLinkModules = ',doclib,module,webhook,gitlab,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,';
$config->action->preferredTypeNum = 10;
$config->action->preferredType = new stdclass();
$config->action->preferredType->new = array('user', 'story', 'task', 'bug', 'case', 'doc', 'program', 'product', 'project', 'execution');
$config->action->preferredType->classic = array('user', 'story', 'task', 'bug', 'case', 'doc', 'product', 'execution', 'productplan', 'build');
+28 -8
View File
@@ -14,6 +14,7 @@ class action extends control
/**
* Trash.
*
* @param string $browseType
* @param string $type all|hidden
* @param string $orderBy
* @param int $recTotal
@@ -22,7 +23,7 @@ class action extends control
* @access public
* @return void
*/
public function trash($type = 'all', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function trash($browseType = 'all', $type = 'all', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->loadModel('backup');
@@ -60,18 +61,37 @@ class action extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = common::appendOrder($orderBy);
$trashes = $this->action->getTrashes($type, $sort, $pager);
$sort = common::appendOrder($orderBy);
$trashes = $this->action->getTrashes($browseType, $type, $sort, $pager);
$objectTypeList = $this->action->getTrashObjectTypes($type);
$objectTypeList = array_keys($objectTypeList);
$preferredType = array();
$moreType = array();
$preferredTypeConfig = $this->config->systemMode == 'new' ? $this->config->action->preferredType->new : $this->config->action->preferredType->classic;
foreach($objectTypeList as $objectType)
{
in_array($objectType, $preferredTypeConfig) ? $preferredType[$objectType] = $objectType : $moreType[$objectType] = $objectType;
}
if(count($preferredType) < $this->config->action->preferredTypeNum)
{
$toPreferredType = array_splice($moreType, 0, $this->config->action->preferredTypeNum - count($preferredType));
$preferredType = $preferredType + $toPreferredType;
}
/* Title and position. */
$this->view->title = $this->lang->action->trash;
$this->view->position[] = $this->lang->action->trash;
$this->view->trashes = $trashes;
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->trashes = $trashes;
$this->view->type = $type;
$this->view->currentObjectType = $browseType;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->preferredType = $preferredType;
$this->view->moreType = $moreType;
$this->view->preferredTypeConfig = $preferredTypeConfig;
$this->display();
}
+4
View File
@@ -1,3 +1,7 @@
.table-footer .table-actions {width: auto; visibility: visible; opacity: 1;}
.table-footer .table-actions .text {line-height: 28px;}
table tbody tr td {overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
#mainMenu .btn-toolbar .btn-group .dropdown-menu .btn-active-text:hover .text {color: #fff}
.dropdown-menu .btn-active-text:hover .text:after {border-bottom: 0px;}
+18 -2
View File
@@ -610,17 +610,19 @@ class actionModel extends model
/**
* Get deleted objects.
*
* @param string $objectType
* @param string $type all|hidden
* @param string $orderBy
* @param object $pager
* @access public
* @return array
*/
public function getTrashes($type, $orderBy, $pager)
public function getTrashes($objectType, $type, $orderBy, $pager)
{
$extra = $type == 'hidden' ? self::BE_HIDDEN : self::CAN_UNDELETED;
$extra = $type == 'hidden' ? self::BE_HIDDEN : self::CAN_UNDELETED;
$trashes = $this->dao->select('*')->from(TABLE_ACTION)
->where('action')->eq('deleted')
->beginIF($objectType != 'all')->andWhere('objectType')->eq($objectType)->fi()
->andWhere('extra')->eq($extra)
->andWhere('vision')->eq($this->config->vision)
->orderBy($orderBy)->page($pager)->fetchAll();
@@ -665,9 +667,23 @@ class actionModel extends model
$trash->objectName = isset($objectNames[$objectType][$trash->objectID]) ? $objectNames[$objectType][$trash->objectID] : '';
}
return $trashes;
}
/**
* Get object type list of trashes.
*
* @param string $type
* @access public
* @return array
*/
public function getTrashObjectTypes($type)
{
$extra = $type == 'hidden' ? self::BE_HIDDEN : self::CAN_UNDELETED;
return $this->dao->select('objectType')->from(TABLE_ACTION)->where('action')->eq('deleted')->andWhere('extra')->eq($extra)->andWhere('vision')->eq($this->config->vision)->fetchAll('objectType');
}
/**
* Get histories of an action.
*
+44
View File
@@ -12,6 +12,50 @@
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php $activeClass = $currentObjectType == 'all' ? 'btn-active-text' : '';?>
<?php echo html::a($this->createLink('action', 'trash', "objectType=all&type=$type"), "<span class='text'>" . $lang->all . "</span>", '', "class='btn btn-link $activeClass'");?>
<?php
/* Output the objectType order by preferredTypeConfig. */
foreach($preferredTypeConfig as $objectType)
{
if(in_array($objectType, $preferredType))
{
$activeClass = $objectType == $currentObjectType ? 'btn-active-text' : '';
echo html::a($this->createLink('action', 'trash', "objectType=$objectType&type=$type"), "<span class='text'>" . zget($lang->action->objectTypes, $objectType) . "</span>", '', "class='btn btn-link $activeClass'");
unset($preferredType[$objectType]);
}
}
/* Output the remaining types which transformed from more type. */
foreach($preferredType as $objectType)
{
$activeClass = $objectType == $currentObjectType ? 'btn-active-text' : '';
echo html::a($this->createLink('action', 'trash', "objectType=$objectType&type=$type"), "<span class='text'>" . zget($lang->action->objectTypes, $objectType) . "</span>", '', "class='btn btn-link $activeClass'");
}
/* Output the more types. */
if(!empty($moreType))
{
$moreLabel = $lang->more;
$moreLabelActive = '';
if(in_array($currentObjectType, $moreType))
{
$moreLabel = "<span class='text'>" . zget($lang->action->objectTypes, $currentObjectType) . "</span>";
$moreLabelActive = 'btn-active-text';
}
echo '<div class="btn-group" id="more">';
echo html::a('javascript:;', $moreLabel . " <span class='caret'></span>", '', "data-toggle='dropdown' class='btn btn-link $moreLabelActive'");
echo "<ul class='dropdown-menu'>";
foreach($moreType as $objectType)
{
$activeClass = $objectType == $currentObjectType ? 'btn-active-text' : '';
echo '<li>' . html::a($this->createLink('action', 'trash', "objectType=$objectType&type=$type"), "<span class='text'>" . zget($lang->action->objectTypes, $objectType) . "</span>", '', "class='btn btn-link $activeClass'") . '</li>';
}
echo '</ul></div>';
}
?>
</div>
<div class='btn-toolbar pull-right'>
<?php if($type == 'hidden') echo html::a(inLink('trash', "type=all"), $lang->goback, '', "class='btn'");?>
<?php if($type == 'all') echo html::a(inLink('trash', "type=hidden"), "<i class='icon-eye-close'></i> " . $lang->action->dynamic->hidden, '', "class='btn btn-danger'");?>
+1
View File
@@ -764,6 +764,7 @@ class apiModel extends model
public function createDemoData($name, $baseUrl, $version = '16.0')
{
/* Replace the doc lib name to api lib name. */
$this->app->loadLang('doc');
$this->lang->doclib->name = $this->lang->doclib->apiLibName;
$firstAccount = $this->dao->select('account')->from(TABLE_USER)->orderBy('id_asc')->limit(1)->fetch('account');
+2
View File
@@ -71,6 +71,7 @@ $config->bug->search['fields']['resolvedBy'] = $lang->bug->resolvedBy;
$config->bug->search['fields']['status'] = $lang->bug->status;
$config->bug->search['fields']['confirmed'] = $lang->bug->confirmed;
$config->bug->search['fields']['story'] = $lang->bug->story;
if($config->systemMode == 'new') $config->bug->search['fields']['project'] = $lang->bug->project;
$config->bug->search['fields']['product'] = $lang->bug->product;
@@ -116,6 +117,7 @@ $config->bug->search['params']['resolvedBy'] = array('operator' => '=',
$config->bug->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->bug->statusList);
$config->bug->search['params']['confirmed'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->bug->confirmedList);
$config->bug->search['params']['story'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
if($config->systemMode == 'new') $config->bug->search['params']['project'] = array('operator' => '=', 'control' => 'select', 'values' => '');
$config->bug->search['params']['product'] = array('operator' => '=', 'control' => 'select', 'values' => '');
+23 -3
View File
@@ -2730,6 +2730,26 @@ class bugModel extends model
/* Fix bug #2878. */
if(strpos($bugQuery, ' `resolvedDate` ') !== false) $bugQuery = str_replace(' `resolvedDate` ', " `resolvedDate` != '0000-00-00 00:00:00' AND `resolvedDate` ", $bugQuery);
if(strpos($bugQuery, ' `closedDate` ') !== false) $bugQuery = str_replace(' `closedDate` ', " `closedDate` != '0000-00-00 00:00:00' AND `closedDate` ", $bugQuery);
if(strpos($bugQuery, ' `story` ') !== false)
{
preg_match_all("/`story`[ ]+(NOT *)?LIKE[ ]+'%([^%]*)%'/Ui", $bugQuery, $out);
if(!empty($out[2]))
{
foreach($out[2] as $searchValue)
{
$story = $this->dao->select('id')->from(TABLE_STORY)->alias('t1')
->leftJoin(TABLE_STORYSPEC)->alias('t2')->on('t1.id=t2.story')
->where('t1.title')->like("%$searchValue%")
->orWhere('t1.keywords')->like("%$searchValue%")
->orWhere('t2.spec')->like("%$searchValue%")
->orWhere('t2.verify')->like("%$searchValue%")
->fetchPairs('id');
$bugQuery = preg_replace("/`story`[ ]+(NOT[ ]*)?LIKE[ ]+'%$searchValue%'/Ui", '`story` $1 IN (' . implode($story, ',') .')', $bugQuery);
}
}
$bugQuery .= ' AND `story` != 0';
}
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where($bugQuery)
->beginIF(!$this->app->user->admin)->andWhere('execution')->in('0,' . $this->app->user->view->sprints)->fi()
@@ -3234,9 +3254,9 @@ class bugModel extends model
/**
* Build bug menu.
*
* @param object $bug
* @param string $type
*
* @param object $bug
* @param string $type
* @access public
* @return string
*/
+1 -1
View File
@@ -96,7 +96,7 @@
<?php common::printBack($browseLink);?>
<?php if(!$bug->deleted):?>
<div class='divider'></div>
<?php echo $this->buildOperateMenu($bug, 'view');?>
<?php echo $this->bug->buildOperateMenu($bug, 'view');?>
<?php endif;?>
</div>
</div>
+2 -1
View File
@@ -25,7 +25,8 @@ $lang->caselib->exportTemplet = '导出模板';
$lang->caselib->batchCreateCase = '批量创建用例';
$lang->caselib->import = '导入';
$lang->caselib->showImport = '显示导入数据';
$lang->caselib->type = '类型';
$lang->caselib->product = '所属' . $lang->productCommon;
$lang->caselib->browseAction = '用例库列表';
$lang->caselib->deleteAction = "删除用例库";
$lang->caselib->importAction = '导入用例';
+61
View File
@@ -595,4 +595,65 @@ class caselibModel extends model
}
}
}
/**
* Build case lib menu.
*
* @param object $object
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($object, $type = 'view')
{
$function = 'buildOperate' . ucfirst($type) . 'Menu';
return $this->$function($object);
}
/**
* Build case lib view menu.
*
* @param object $lib
* @access public
* @return string
*/
public function buildOperateViewMenu($lib)
{
if($lib->deleted) return '';
$menu = '';
$params = "libID=$lib->id";
$menu .= $this->buildFlowMenu('caselib', $lib, 'view', 'direct');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('caselib', 'edit', $params, $lib, 'view');
$menu .= $this->buildMenu('caselib', 'delete', $params, $lib, 'view', 'trash', 'hiddenwin');
return $menu;
}
/**
* Build case lib browse menu.
*
* @param object $case
* @access public
* @return string
*/
public function buildOperateBrowseMenu($case)
{
$menu = '';
$params = "caseID=$case->id";
if($this->config->testcase->needReview || !empty($this->config->testcase->forceReview))
{
$menu .= $this->buildMenu('testcase', 'review', $params, $case, 'browse', 'glasses', '', 'iframe');
}
$menu .= $this->buildMenu('testcase', 'edit', $params, $case, 'browse');
if(common::hasPriv('testcase', 'delete'))
{
$deleteURL = helper::createLink('testcase', 'delete', "$params&confirm=yes");
$menu .= html::a("javascript:ajaxDelete(\"$deleteURL\", \"caseList\", confirmDelete)", '<i class="icon icon-trash"></i>', '', "title='{$this->lang->testcase->delete}' class='btn'");
}
return $menu;
}
}
+1 -9
View File
@@ -157,15 +157,7 @@ js::set('flow', $config->global->flow);
<td class='<?php if(isset($run)) echo $run->status;?> testcase-<?php echo $case->status?>'> <?php echo $this->processStatus('testcase', $case);?></td>
<?php foreach($extendFields as $extendField) echo "<td>" . $this->loadModel('flow')->getFieldValue($extendField, $case) . "</td>";?>
<td class='c-actions'>
<?php
if($config->testcase->needReview or !empty($config->testcase->forceReview)) common::printIcon('testcase', 'review', "caseID=$case->id", $case, 'list', 'glasses', '', 'iframe');
common::printIcon('testcase', 'edit', "caseID=$case->id", $case, 'list');
if(common::hasPriv('testcase', 'delete'))
{
$deleteURL = $this->createLink('testcase', 'delete', "caseID=$case->id&confirm=yes");
echo html::a("javascript:ajaxDelete(\"$deleteURL\", \"caseList\", confirmDelete)", '<i class="icon icon-trash"></i>', '', "title='{$lang->testcase->delete}' class='btn'");
}
?>
<?php echo $this->buildOperateMenu($case, 'browse');?>
</td>
</tr>
<?php endforeach;?>
+3 -9
View File
@@ -37,15 +37,9 @@
<div class='main-actions'>
<nav class="container"></nav>
<div class="btn-toolbar">
<?php
common::printBack($browseLink);
if(!$lib->deleted)
{
echo "<div class='divider'></div>";
common::printIcon('caselib', 'edit', "libID=$lib->id");
common::printIcon('caselib', 'delete', "libID=$lib->id", '', 'button', 'trash', 'hiddenwin');
}
?>
<?php common::printBack($browseLink);?>
<?php echo "<div class='divider'></div>";?>
<?php echo $this->buildOperateMenu($lib, 'view');?>
</div>
</div>
</div>
+2 -2
View File
@@ -48,8 +48,8 @@ $config->custom->fieldList['release']['create'] = 'desc';
$config->custom->fieldList['release']['edit'] = 'desc';
$config->custom->fieldList['execution']['create'] = 'days,desc,PO,PM,QD,RD';
$config->custom->fieldList['execution']['edit'] = 'days,desc,PO,PM,QD,RD';
$config->custom->fieldList['task']['create'] = 'story,pri,estimate,desc,estStarted,deadline';
$config->custom->fieldList['task']['edit'] = 'pri,estimate,estStarted,deadline';
$config->custom->fieldList['task']['create'] = 'module,story,pri,estimate,desc,estStarted,deadline';
$config->custom->fieldList['task']['edit'] = 'module,pri,estimate,estStarted,deadline';
$config->custom->fieldList['task']['finish'] = 'comment';
$config->custom->fieldList['task']['activate'] = 'assignedTo,comment';
$config->custom->fieldList['build'] = 'scmPath,filePath,desc';
+3 -1
View File
@@ -251,7 +251,9 @@ class customModel extends model
foreach($item['subMenu'] as $subMenu)
{
if(!isset($subMenu['link']) or strpos($subMenu['link'], '|') === false) continue;
list($subLabel, $module, $method, $vars) = explode('|', $subMenu['link']);
list($subLabel, $module, $method) = explode('|', $subMenu['link']);
if(count(explode('|', $subMenu['link'])) > 3) list($subLabel, $module, $method, $vars) = explode('|', $subMenu['link']);
$hasPriv = commonModel::hasPriv($module, $method);
if($hasPriv) break;
}
+11 -1
View File
@@ -3148,7 +3148,16 @@ class execution extends control
}
$projectExecutions = array();
foreach($orderedExecutions as $execution) $projectExecutions[$execution->project][] = $execution;
$parentIdList = array();
foreach($orderedExecutions as $execution)
{
$projectExecutions[$execution->project][] = $execution;
if($execution->type != 'stage') continue;
if($execution->grade == 2 and $execution->project != $execution->parent) $parentIdList[$execution->parent] = $execution->parent;
}
$parents = array();
if($parentIdList) $parents = $this->execution->getByIdList($parentIdList);
$this->view->link = $this->execution->getLink($module, $method, $extra);
$this->view->module = $module;
@@ -3157,6 +3166,7 @@ class execution extends control
$this->view->extra = $extra;
$this->view->projects = $projectPairs;
$this->view->executions = $projectExecutions;
$this->view->parents = $parents;
$this->display();
}
+10 -1
View File
@@ -328,6 +328,10 @@ if(!window.kanbanDropRules)
{
backlog: ['ready', 'backlog'],
ready: ['backlog', 'ready'],
tested: ['verified'],
verified: ['tested', 'released'],
released: ['verified', 'closed'],
closed: ['released'],
},
bug:
{
@@ -957,7 +961,12 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
/* Story lane. */
if(cardType == 'story')
{
if(toColType == 'ready' || toColType == 'backlog')
if(toColType == 'closed' && priv.canCloseStory)
{
var link = createLink('story', 'close', 'storyID=' + objectID, '', true);
showIframe = true;
}
else
{
if(toColType == 'ready' && typeof(reviewStoryParis[objectID]) != 'undefined')
{
+12 -3
View File
@@ -480,6 +480,10 @@ if(!window.kanbanDropRules)
{
backlog: ['ready'],
ready: ['backlog'],
tested: ['verified'],
verified: ['tested', 'released'],
released: ['verified', 'closed'],
closed: ['released'],
},
bug:
{
@@ -495,7 +499,7 @@ if(!window.kanbanDropRules)
{
'wait': ['developing', 'developed', 'canceled', 'closed'],
'developing': ['developed', 'pause', 'canceled'],
'developed': ['closed'],
'developed': ['developing', 'closed'],
'pause': ['developing'],
'canceled': ['developing'],
'closed': ['developing'],
@@ -683,7 +687,12 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
/* Story lane. */
if(cardType == 'story')
{
if(toColType == 'ready' || toColType == 'backlog')
if(toColType == 'closed' && priv.canCloseStory)
{
var link = createLink('story', 'close', 'storyID=' + objectID, '', true);
showIframe = true;
}
else
{
if(toColType == 'ready' && typeof(reviewStoryParis[objectID]) != 'undefined')
{
@@ -706,7 +715,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
updateKanban(browseType, kanbanGroup[groupBy]);
}
}
})
});
}
}
+64 -6
View File
@@ -2791,15 +2791,70 @@ class executionModel extends model
public function computeBurn()
{
$today = helper::today();
$burns = array();
$executions = $this->dao->select('id, code')->from(TABLE_EXECUTION)
->where('end')->ge($today)
->andWhere('type')->ne('ops')
->andWhere('type')->in('sprint,stage')
->andWhere('lifetime')->ne('ops')
->andWhere('status')->notin('done,closed,suspended')
->fetchPairs();
if(!$executions) return $burns;
if(!$executions) return array();
/* Update historical data of burn. */
$table = $this->config->edition == 'open' ? TABLE_TASKESTIMATE : TABLE_EFFORT;
$field = $this->config->edition == 'open' ? 'task' : 'objectID';
$totalConsumed = $this->dao->select("t2.execution as execution, t1.$field as task, t1.date as date, sum(t1.consumed) as totalConsumed")->from($table)->alias('t1')
->leftJoin(TABLE_TASK)->alias('t2')->on("t1.$field=t2.id")
->where('t2.execution')->in(array_keys($executions))
->beginIF($this->config->edition != 'open')->andWhere('t1.objectType')->eq('task')->fi()
->andWhere('t2.deleted')->eq('0')
->andWhere('t2.parent')->ge('0')
->andWhere('t2.status')->ne('cancel')
->andWhere('t1.date')->ne($today)
->groupBy("t1.$field, t1.date")
->orderBy('t1.id_desc')
->fetchGroup('task', 'date');
$latestIdList = $this->dao->select('max(id) as id')->from($table)
->where($field)->in(array_keys($totalConsumed))
->beginIF($this->config->edition != 'open')->andWhere('objectType')->eq('task')->fi()
->groupBy("$field,date")->fetchAll('id');
$latestLefts = $this->dao->select("$field, date, `left`")->from($table)
->where('id')->in(array_keys($latestIdList))
->fetchGroup($field, 'date');
$burnList = array();
foreach($totalConsumed as $taskID => $consumedList)
{
foreach($consumedList as $date => $consumed)
{
$executionID = $consumed->execution;
if(!isset($burnList[$executionID])) $burnList[$executionID] = array();
if(!isset($burnList[$executionID][$date]))
{
$burnList[$executionID][$date] = new stdclass();
$burnList[$executionID][$date]->consumed = 0;
$burnList[$executionID][$date]->left = 0;
}
$burnList[$executionID][$date]->consumed += $consumed->totalConsumed;
$burnList[$executionID][$date]->left += $latestLefts[$taskID][$date]->left;
}
}
foreach($burnList as $executionID => $burns)
{
foreach($burns as $date => $burn)
{
$burn->execution = $executionID;
$burn->date = $date;
$this->dao->replace(TABLE_BURN)->data($burn)->exec();
$burn->executionName = $executions[$burn->execution];
}
}
/* Update today's data of burn. */
$burns = $this->dao->select("execution, '$today' AS date, sum(estimate) AS `estimate`, sum(`left`) AS `left`, SUM(consumed) AS `consumed`")
->from(TABLE_TASK)
->where('execution')->in(array_keys($executions))
@@ -2819,8 +2874,9 @@ class executionModel extends model
->where('execution')->in(array_keys($executions))
->andWhere('deleted')->eq('0')
->andWhere('parent')->ge('0')
->andWhere('status')->eq('done')
->andWhere('status', true)->eq('done')
->orWhere('status')->eq('closed')
->markRight(1)
->groupBy('execution')
->fetchAll('execution');
$storyPoints = $this->dao->select('t1.project, sum(t2.estimate) AS `storyPoint`')->from(TABLE_PROJECTSTORY)->alias('t1')
@@ -2851,8 +2907,10 @@ class executionModel extends model
$this->dao->replace(TABLE_BURN)->data($burn)->exec();
$burn->executionName = $executions[$burn->execution];
$burnList[$executionID][$today] = $burn;
}
return $burns;
return $burnList;
}
/**
+11 -6
View File
@@ -46,7 +46,12 @@ foreach($executions as $projectID => $projectExecutions)
if($execution->status != 'done' and $execution->status != 'closed' and ($execution->PM == $this->app->user->account or isset($execution->teams[$this->app->user->account]))) $executionCounts[$projectID]['myExecution'] ++;
if($execution->status != 'done' and $execution->status != 'closed' and $execution->PM != $this->app->user->account and !isset($execution->teams[$this->app->user->account])) $executionCounts[$projectID]['others'] ++;
if($execution->status == 'done' or $execution->status == 'closed') $executionCounts[$projectID]['closed'] ++;
$executionNames[] = $execution->name;
$onlyChildStage = $execution->grade == 2 and $execution->project != $execution->parent;
$executionNames[$execution->id] = $execution->name;
if($onlyChildStage and isset($parents[$execution->parent]))
{
$executionNames[$execution->id] = $parents[$execution->parent]->name . '/' . $execution->name;
}
}
}
$executionsPinYin = common::convert2Pinyin($executionNames);
@@ -77,7 +82,7 @@ foreach($executions as $projectID => $projectExecutions)
$selected = $execution->id == $executionID ? 'selected' : '';
if($execution->status != 'done' and $execution->status != 'closed' and ($execution->PM == $this->app->user->account or isset($execution->teams[$this->app->user->account])))
{
$myExecutionsHtml .= '<li>' . html::a(sprintf($link, $execution->id), $execution->name, '', "class='$selected clickable' title='{$execution->name}' data-key='" . zget($executionsPinYin, $execution->name, '') . "' data-app='{$this->app->tab}'") . '</li>';
$myExecutionsHtml .= '<li>' . html::a(sprintf($link, $execution->id), $executionNames[$execution->id], '', "class='$selected clickable' title='{$execution->name}' data-key='" . zget($executionsPinYin, $execution->name, '') . "' data-app='{$this->app->tab}'") . '</li>';
if($selected == 'selected') $tabActive = 'myExecution';
@@ -85,7 +90,7 @@ foreach($executions as $projectID => $projectExecutions)
}
else if($execution->status != 'done' and $execution->status != 'closed' and $execution->PM != $this->app->user->account and !isset($execution->teams[$this->app->user->account]))
{
$normalExecutionsHtml .= '<li>' . html::a(sprintf($link, $execution->id), $execution->name, '', "class='$selected clickable' title='{$execution->name}' data-key='" . zget($executionsPinYin, $execution->name, '') . "' data-app='{$this->app->tab}'") . '</li>';
$normalExecutionsHtml .= '<li>' . html::a(sprintf($link, $execution->id), $executionNames[$execution->id], '', "class='$selected clickable' title='{$execution->name}' data-key='" . zget($executionsPinYin, $execution->name, '') . "' data-app='{$this->app->tab}'") . '</li>';
if($selected == 'selected') $tabActive = 'other';
@@ -93,7 +98,7 @@ foreach($executions as $projectID => $projectExecutions)
}
else if($execution->status == 'done' or $execution->status == 'closed')
{
$closedExecutionsHtml .= '<li>' . html::a(sprintf($link, $execution->id), $execution->name, '', "class='$selected clickable' title='$execution->name' data-key='" . zget($executionsPinYin, $execution->name, '') . "' data-app='{$this->app->tab}'") . '</li>';
$closedExecutionsHtml .= '<li>' . html::a(sprintf($link, $execution->id), $executionNames[$execution->id], '', "class='$selected clickable' title='$execution->name' data-key='" . zget($executionsPinYin, $execution->name, '') . "' data-app='{$this->app->tab}'") . '</li>';
if($selected == 'selected') $tabActive = 'closed';
}
@@ -171,7 +176,7 @@ $(function()
$('li.has-list div.hide-in-search').removeClass('hidden');
$('.nav-tabs li.active').find('span').show();
}
if($('.form-control.search-input').val().length > 0)
{
$('#closed').attr("hidden", false);
@@ -191,7 +196,7 @@ $(function()
$('#closed').attr("hidden", true);
$('#gray-line').attr("hidden", true);
}
var listItem = $(this).find('.has-list');
listItem.each(function ()
{
+1 -1
View File
@@ -22,7 +22,7 @@
<div class='btn-toolbar pull-left'>
<?php
$weekend = ($type == 'noweekend') ? 'withweekend' : "noweekend";
common::printLink('execution', 'computeBurn', 'reload=yes', '<i class="icon icon-refresh"></i> ' . $lang->execution->computeBurn, 'hiddenwin', "title='{$lang->execution->computeBurn}{$lang->execution->burn}' class='btn btn-primary' id='computeBurn'");
common::printLink('execution', 'computeBurn', 'reload=yes', '<i class="icon icon-refresh"></i> ' . $lang->execution->computeBurn, 'hiddenwin', "title='{$lang->execution->computeBurn}' class='btn btn-primary' id='computeBurn'");
echo '<div class="space"></div>';
echo html::a($this->createLink('execution', 'burn', "executionID=$executionID&type=$weekend&interval=$interval"), $lang->execution->$weekend, '', "class='btn btn-link'");
if(common::canModify('execution', $execution)) common::printLink('execution', 'fixFirst', "execution=$execution->id", $lang->execution->fixFirst, '', "class='btn btn-link iframe' data-width='700'");
+5 -2
View File
@@ -11,8 +11,11 @@
*/
?>
<?php
foreach($burns as $burn)
foreach($burns as $executionID => $burnList)
{
echo $burn->execution . "\t" . $burn->executionName . "\t" . $burn->date . "\t" . $burn->left . "\n";
foreach($burnList as $date => $burn)
{
echo $burn->execution . "\t" . $burn->executionName . "\t" . $burn->date . "\t" . $burn->left . "\n";
}
}
?>
+1
View File
@@ -105,6 +105,7 @@ js::set('priv',
'canEditStory' => common::hasPriv('story', 'edit'),
'canDeleteStory' => common::hasPriv('story', 'delete'),
'canChangeStory' => common::hasPriv('story', 'change'),
'canCloseStory' => common::hasPriv('story', 'close'),
'canUnlinkStory' => common::hasPriv('execution', 'unlinkStory'),
)
);
+2 -1
View File
@@ -193,7 +193,8 @@ js::set('priv',
'canStartTask' => common::hasPriv('task', 'start'),
'canAssignBug' => common::hasPriv('bug', 'assignto'),
'canConfirmBug' => common::hasPriv('bug', 'confirmBug'),
'canActivateBug' => common::hasPriv('bug', 'activate')
'canActivateBug' => common::hasPriv('bug', 'activate'),
'canCloseStory' => common::hasPriv('story', 'close')
)
);
?>
+30 -3
View File
@@ -1320,8 +1320,14 @@ class kanban extends control
{
$region = $this->kanban->getRegionByID($regionID);
$cards = $this->kanban->getCardsByObject('region', $regionID, 1);
foreach($this->config->kanban->fromType as $fromType)
{
$cards = $this->kanban->getImportedCards($region->kanban, $cards, $fromType, 1);
}
$this->view->kanban = $this->kanban->getByID($region->kanban);
$this->view->cards = $this->kanban->getCardsByObject('region', $regionID, 1);
$this->view->cards = $cards;
$this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$this->view->userIdPairs = $this->user->getPairs('noletter|nodeleted|showid');
$this->view->usersAvatar = $this->user->getAvatarPairs();
@@ -1374,13 +1380,20 @@ class kanban extends control
*/
public function deleteCard($cardID, $confirm = 'no')
{
$card = $this->kanban->getCardByID($cardID);
if($confirm == 'no')
{
return print(js::confirm($this->lang->kanbancard->confirmDelete, $this->createLink('kanban', 'deleteCard', "cardID=$cardID&confirm=yes")));
$confirmTip = $card->fromType == '' ? $this->lang->kanbancard->confirmDelete : $this->lang->kanbancard->confirmRemove;
return print(js::confirm($confirmTip, $this->createLink('kanban', 'deleteCard', "cardID=$cardID&confirm=yes")));
}
else
{
$this->kanban->delete(TABLE_KANBANCARD, $cardID);
if($card->fromType == '') $this->kanban->delete(TABLE_KANBANCARD, $cardID);
if($card->fromType != '')
{
$this->dao->delete()->from(TABLE_KANBANCARD)->where('id')->eq($cardID)->exec();
$this->loadModel('action')->create('kanbancard', $cardID, 'Deleted', '', $cardID);
}
if(isonlybody()) return print(js::reload('parent.parent'));
return print(js::reload('parent'));
@@ -1663,6 +1676,20 @@ class kanban extends control
->andWhere('`column`')->eq($toColID)
->exec();
$toColumn = $this->kanban->getColumnByID($toColID);
if($toColumn->laneType == 'story' and in_array($toColumn->type, array('tested', 'verified', 'released', 'closed')))
{
$data = new stdclass();
$data->stage = $toColumn->type;
if($toColumn->type == 'released')
{
$fromColumn = $this->kanban->getColumnByID($fromColID);
if($fromColumn->type == 'closed') $data->status = 'active';
}
$this->dao->update(TABLE_STORY)->data($data)->where('id')->eq($cardID)->exec();
$this->dao->update(TABLE_STORYSTAGE)->set('stage')->eq($toColumn->type)->where('story')->eq($cardID)->exec();
}
$kanbanGroup = $regionID == 0 ? $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy) : $this->kanban->getRDKanban($executionID, $browseType, $orderBy, $regionID, $groupBy);
echo json_encode($kanbanGroup);
}
+2 -2
View File
@@ -121,13 +121,13 @@
.label-closed, .label-terminate {background: #e5e5e5 !important; color: #b8b8b8;}
.label-deleted {background: #ff5d5d !important; color: #fff;}
.label-normal {background: #e1e8d9 !important; color: #9abe76;}
.kanban-item > .kanban-card > .execInfo > .user, .kanban-item > .kanban-card > .releaseInfo > .user, .kanban-item > .kanban-card > .productplanInfo > .user {position: absolute; right: 13px; bottom: 1px}
.kanban-item > .kanban-card > .execInfo > .user, .kanban-item > .kanban-card > .releaseInfo > .user, .kanban-item > .kanban-card > .productplanInfo > .user {float:right}
.kanban-item > .kanban-card > .execInfo > .user > .avatar, .kanban-item > .kanban-card > .releaseInfo > .user > .avatar, .kanban-item > .kanban-card > .productplanInfo > .user > .avatar {display: inline-block; width: 24px; height: 24px; line-height: 24px; margin-right: 0px; margin-left: 2px}
.kanban-card .releaseTitle .icon, .kanban-card .title .icon, .kanban-card .productplanTitle .icon, .kanban-card .title .icon, .kanban-card .buildTitle .icon{padding-right: 5px;}
.kanban-card .label-finish {margin-right: 7px; margin-top: -1px; padding: 3px 5px; float: left; background-color:#2a5f29;}
.kanban-card .releaseTitle, .kanban-card .productplanTitle {width: 100%; float: left;}
.progress-box {display: flex; flex-direction: row; margin-top: 10px;}
.progress-box {width:100%; display: flex; flex-direction: row; margin-top: 10px;}
.progress {flex: auto; margin: 5px auto 5px;}
.progress-number {padding-left: 5px;}
+89 -73
View File
@@ -278,6 +278,22 @@ function renderKanbanItem(item, $item)
var whiteStyle = null;
if(item.color == '#2a5f29' || item.color == '#b10b0b' || item.color == '#cfa227') whiteStyle = 'style="color:#FFFFFF"';
var privs = item.actions;
var printMoreBtn = (privs.includes('editCard') || privs.includes('archiveCard') || privs.includes('copyCard') || privs.includes('deleteCard') || privs.includes('moveCard') || privs.includes('setCardColor'));
var $actions = $item.children('.actions');
var $title = $item.children('.title');
if(printMoreBtn && !$actions.length)
{
$(
[
'<div class="actions" title="' + kanbanLang.more + '">',
'<a data-contextmenu="card"' + whiteStyle + 'data-id="' + item.id + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($item);
}
if(item.fromType == 'execution')
{
renderExecutionItem(item, $item);
@@ -296,53 +312,13 @@ function renderKanbanItem(item, $item)
}
else
{
var $title = $item.children('.title');
var privs = item.actions;
var printMoreBtn = (privs.includes('editCard') || privs.includes('archiveCard') || privs.includes('copyCard') || privs.includes('deleteCard') || privs.includes('moveCard') || privs.includes('setCardColor'));
if(privs.includes('sortCard')) $item.parent().addClass('sort');
if(!$title.length)
{
$('<div class="label label-finish">' + kanbanLang.finished + '</div>').appendTo($item);
if(privs.includes('viewCard')) $title = $('<a class="title iframe" data-toggle="modal" data-width="80%"></a>').appendTo($item).attr('href', createLink('kanban', 'viewCard', 'cardID=' + item.id, '', true));
if(!privs.includes('viewCard')) $title = $('<p class="title"></p>').appendTo($item);
}
$title.text(item.name).attr('title', item.name);
if(kanban.performable == 1 && item.status == 'done' )
{
var finishColor = '#2a5f29';
if(item.color == '#2a5f29') finishColor = '#FFFFFF';
$title.text(item.name).css('color', finishColor);
$item.children('.label-finish').show();
if(item.color == '#2a5f29')
{
$item.children('.label-finish').css({'background-color':'#FFFFFF','color':'#2a5f29'});
}
else
{
$item.children('.label-finish').css({'background-color':'','color':'#FFFFFF'});
}
}
else
{
$title.text(item.name).css('color', '');
$item.children('.label-finish').hide();
}
var $actions = $item.children('.actions');
if(printMoreBtn && !$actions.length)
{
$(
[
'<div class="actions" title="' + kanbanLang.more + '">',
'<a data-contextmenu="card"' + whiteStyle + 'data-id="' + item.id + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($item);
}
var $info = $item.children('.info');
if(!$info.length) $info = $(
[
@@ -353,11 +329,6 @@ function renderKanbanItem(item, $item)
'<div class="user"></div>',
'</div>'
].join('')).appendTo($item);
if(kanban.performable == 1)
{
var $progress = $item.children('.progress-box');
if(!$progress.length) $progress = $('<div class="progress-box"><div class="progress"><div class="progress-bar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: ' + item.progress + '%;"></div></div><div class="progress-number">' + item.progress + '%</div></div>').appendTo($item);
}
$item.data('card', item);
@@ -367,15 +338,6 @@ function renderKanbanItem(item, $item)
$info.children('.pri')
.attr('class', 'pri label-pri label-pri-' + item.pri)
.text(item.pri);
if(kanban.performable == 1)
{
$progress.find('.progress-bar').css('width', item.progress + '%');
$progress.find('.progress-number').html(item.progress + '%');
}
$item.css('background-color', item.color);
$item.toggleClass('has-color', item.color != '#fff' && item.color != '');
$item.find('.info > .label-light').css('background-color', item.color);
var $time = $info.children('.time');
if(item.end == '0000-00-00' && item.begin == '0000-00-00')
@@ -413,6 +375,41 @@ function renderKanbanItem(item, $item)
for(i = 0; i < assignedTo.length; i++) title.push(users[assignedTo[i]]);
$user.html(renderUsersAvatar(assignedTo, item.id)).attr('title', title);
}
if(kanban.performable == 1)
{
var $progress = $item.children('.progress-box');
if(!$progress.length) $progress = $('<div class="progress-box"><div class="progress"><div class="progress-bar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: ' + item.progress + '%;"></div></div><div class="progress-number">' + item.progress + '%</div></div>').appendTo($item);
$progress.find('.progress-bar').css('width', item.progress + '%');
$progress.find('.progress-number').html(item.progress + '%');
}
$item.css('background-color', item.color);
$item.toggleClass('has-color', item.color != '#fff' && item.color != '');
$item.find('.info > .label-light').css('background-color', item.color);
$title = item.fromType == 'execution' ? $item.children('.header').children('.executionName').children('.title') : $item.children('.title');
if(!$title.children('.label-finish').length) $title.prepend('<div class="label label-finish">' + kanbanLang.finished + '</div>');
var name = item.title ? item.title : item.name;
if(kanban.performable == 1 && item.status == 'done' )
{
var finishColor = '#2a5f29';
if(item.color == '#2a5f29') finishColor = '#FFFFFF';
$title.css('color', finishColor);
$title.children('.label-finish').show();
if(item.color == '#2a5f29')
{
$item.children('.label-finish').css({'background-color':'#FFFFFF','color':'#2a5f29'});
}
else
{
$item.children('.label-finish').css({'background-color':'','color':'#FFFFFF'});
}
}
else
{
$title.css('color', '');
$title.children('.label-finish').hide();
}
}
/**
@@ -445,14 +442,19 @@ function renderExecutionItem(item, $item)
/* Print execution name. */
var $title = $titleBox.children('.title');
var name = item.title ? item.title : item.name;
if(!$title.length)
{
var icon = mode == 'new' ? 'run' : 'project';
var viewMethod = item.execType == 'kanban' ? 'kanban' : 'view';
if(privs.includes('viewExecution') && item.deleted == '0') $title = $('<a class="title"><i class="icon icon-' + icon + '"></i>' + item.name + '</a>').appendTo($titleBox).attr('href', createLink('execution', viewMethod, 'executionID=' + item.fromID));
if(!privs.includes('viewExecution') || item.deleted == '1') $title = $('<div class="title"><i class="icon icon-' + icon + '"></i>' + item.name + '</div>').appendTo($titleBox);
if(privs.includes('viewExecution') && item.deleted == '0') $title = $('<a class="title"><i class="icon icon-' + icon + '"></i>' + name + '</a>').appendTo($titleBox).attr('href', createLink('execution', viewMethod, 'executionID=' + item.fromID));
if(!privs.includes('viewExecution') || item.deleted == '1') $title = $('<div class="title"><i class="icon icon-' + icon + '"></i>' + name + '</div>').appendTo($titleBox);
}
$title.attr('title', item.name);
if(!$title.children('i').length)
{
$title.append('<i class="icon icon-run"></i>' + item.title);
}
$title.attr('title', name);
if(item.delay)
{
@@ -477,7 +479,7 @@ function renderExecutionItem(item, $item)
{
if(item.deleted == '0')
{
$statusBox = $('<span class="execStatus label label-' + item.status + '">' + executionLang.statusList[item.status] + '</span>').appendTo($info);
$statusBox = $('<span class="execStatus label label-' + item.objectStatus + '">' + executionLang.statusList[item.objectStatus] + '</span>').appendTo($info);
}
else
{
@@ -519,12 +521,17 @@ function renderReleaseItem(item, $item)
/* Print name. */
var $title = $item.children('.releaseTitle');
var name = item.title ? item.title : item.name;
if(!$title.length)
{
if(privs.includes('viewRelease') && item.deleted == '0') $title = $('<a class="releaseTitle title"><i class="icon icon-publish"></i>' + item.name + '</a>').appendTo($item).attr('href', createLink('release', 'view', 'releaseID=' + item.fromID));
if(!privs.includes('viewRelease') || item.deleted == '1') $title = $('<div class="releaseTitle"><i class="icon icon-publish"></i>' + item.name + '</div>').appendTo($item);
if(privs.includes('viewRelease') && item.deleted == '0') $title = $('<a class="releaseTitle title"><i class="icon icon-publish"></i>' + name + '</a>').appendTo($item).attr('href', createLink('release', 'view', 'releaseID=' + item.fromID));
if(!privs.includes('viewRelease') || item.deleted == '1') $title = $('<div class="releaseTitle"><i class="icon icon-publish"></i>' + name + '</div>').appendTo($item);
}
$title.attr('title', item.name);
if(!$title.children('i').length)
{
$title.prepend('<i class="icon icon-publish"></i>');
}
$title.attr('title', name);
var $info = $item.children('.releaseInfo');
if(!$info.length) $info = $(
@@ -538,7 +545,7 @@ function renderReleaseItem(item, $item)
{
if(item.deleted == '0')
{
$statusBox = $('<span class="releaseStatus label label-' + item.status + '">' + releaseLang.statusList[item.status] + '</span>').appendTo($info);
$statusBox = $('<span class="releaseStatus label label-' + item.objectStatus + '">' + releaseLang.statusList[item.objectStatus] + '</span>').appendTo($info);
}
else
{
@@ -578,10 +585,15 @@ function renderProductplanItem(item, $item)
/* Print name. */
var $title = $item.children('.productplanTitle');
var name = item.title ? item.title : item.name;
if(!$title.length)
{
if(privs.includes('viewPlan') && item.deleted == '0') $title = $('<a class="productplanTitle title"><i class="icon icon-delay"></i>' + item.title + '</a>').appendTo($item).attr('href', createLink('productplan', 'view', 'productplanID=' + item.fromID));
if(!privs.includes('viewPlan') || item.deleted == '1') $title = $('<div class="productplanTitle"><i class="icon icon-delay"></i>' + item.title + '</div>').appendTo($item);
if(privs.includes('viewPlan') && item.deleted == '0') $title = $('<a class="productplanTitle title"><i class="icon icon-delay"></i>' + name + '</a>').appendTo($item).attr('href', createLink('productplan', 'view', 'productplanID=' + item.fromID));
if(!privs.includes('viewPlan') || item.deleted == '1') $title = $('<div class="productplanTitle"><i class="icon icon-delay"></i>' + name + '</div>').appendTo($item);
}
if(!$title.children('i').length)
{
$title.append('<i class="icon icon-delay"></i>' + name);
}
$title.attr('title', item.title);
@@ -597,7 +609,7 @@ function renderProductplanItem(item, $item)
{
if(item.deleted == '0')
{
$statusBox = $('<span class="productplanStatus label label-' + item.status + '">' + productplanLang.statusList[item.status] + '</span>').appendTo($info);
$statusBox = $('<span class="productplanStatus label label-' + item.objectStatus + '">' + productplanLang.statusList[item.objectStatus] + '</span>').appendTo($info);
}
else
{
@@ -646,12 +658,17 @@ function renderBuildItem(item, $item)
/* Print name. */
var $title = $item.children('.buildTitle');
var name = item.title ? item.title : item.name;
if(!$title.length)
{
if(privs.includes('viewBuild') && item.deleted == '0') $title = $('<a class="buildTitle" data-app="project"><i class="icon icon-ver"></i>' + item.name + '</a>').appendTo($item).attr('href', createLink('build', 'view', 'buildID=' + item.fromID));
if(!privs.includes('viewBuild') || item.deleted == '1') $title = $('<div class="buildTitle"><i class="icon icon-ver"></i>' + item.name + '</div>').appendTo($item);
if(privs.includes('viewBuild') && item.deleted == '0') $title = $('<a class="buildTitle title" data-app="project"><i class="icon icon-ver"></i>' + name + '</a>').appendTo($item).attr('href', createLink('build', 'view', 'buildID=' + item.fromID));
if(!privs.includes('viewBuild') || item.deleted == '1') $title = $('<div class="buildTitle title"><i class="icon icon-ver"></i>' + name + '</div>').appendTo($item);
}
$title.attr('title', item.name);
if(!$title.children('i').length)
{
$title.append('<i class="icon icon-ver"></i>' + item.title);
}
$title.attr('title', name);
var $info = $item.children('.info');
if(!$info.length) $info = $(
@@ -1069,7 +1086,7 @@ function createCardMenu(options)
if(!privs.length) return [];
var items = [];
if(privs.includes('editCard')) items.push({label: kanbanLang.editCard, icon: 'edit', url: createLink('kanban', 'editCard', 'cardID=' + card.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(privs.includes('editCard') && card.fromType == '') items.push({label: kanbanLang.editCard, icon: 'edit', url: createLink('kanban', 'editCard', 'cardID=' + card.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(privs.includes('performable') && kanban.performable == 1)
{
if(card.status == 'done')
@@ -1081,9 +1098,6 @@ function createCardMenu(options)
items.push({label: kanbanLang.finishCard, icon: 'checked', onClick: function(){finishCard(card.id, card.kanban, card.region);}});
}
}
if(privs.includes('archiveCard') && kanban.archived == '1') items.push({label: kanbanLang.archiveCard, icon: 'card-archive', url: createLink('kanban', 'archiveCard', 'cardID=' + card.id), attrs: {'target': 'hiddenwin'}});
if(privs.includes('copyCard')) items.push({label: kanbanLang.copyCard, icon: 'copy', url: createLink('kanban', 'copyCard', 'cardID=' + card.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal'}});
if(privs.includes('deleteCard')) items.push({label: kanbanLang.deleteCard, icon: 'trash', url: createLink('kanban', 'deleteCard', 'cardID=' + card.id), attrs: {'target': 'hiddenwin'}});
if(privs.includes('moveCard'))
{
var moveCardItems = [];
@@ -1107,10 +1121,11 @@ function createCardMenu(options)
moveCardItems = moveCardItems.reverse();
items.push({label: kanbanLang.moveCard, icon: 'move', items: moveCardItems});
}
if(privs.includes('archiveCard') && kanban.archived == '1') items.push({label: kanbanLang.archiveCard, icon: 'card-archive', url: createLink('kanban', 'archiveCard', 'cardID=' + card.id), attrs: {'target': 'hiddenwin'}});
if(privs.includes('setCardColor'))
{
var cardColoritems = [];
if(!card.color) color = "#fff";
if(!card.color) card.color = "#fff";
for(let i = 0 ; i < colorList.length ; i ++ )
{
var attr = card.color == colorList[i] ? '<i class="icon icon-check" style="margin-left: 5px"></i>' : '';
@@ -1120,6 +1135,7 @@ function createCardMenu(options)
};
items.push({label: kanbanLang.cardColor, icon: 'color', items: cardColoritems});
}
if(privs.includes('deleteCard')) items.push({label: card.fromType == '' ? kanbanLang.deleteCard : kanbanLang.removeCard, icon: card.fromType == '' ? 'trash' : 'unlink', url: createLink('kanban', 'deleteCard', 'cardID=' + card.id), attrs: {'target': 'hiddenwin'}});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
+2
View File
@@ -40,6 +40,7 @@ $lang->kanban->moveCard = 'Move Card';
$lang->kanban->cardColor = 'Card Color';
$lang->kanban->setCardColor = 'Set Card Color';
$lang->kanban->deleteCard = 'Delete Card';
$lang->kanban->removeCard = 'Remove Card';
$lang->kanban->assigntoCard = 'Assign';
$lang->kanban->setting = 'Setting';
$lang->kanban->enableArchived = 'Enable Archived';
@@ -365,6 +366,7 @@ $lang->kanbancard->confirmArchive = 'Are you sure to archive this card? After
$lang->kanbancard->confirmDelete = 'Are you sure to delete this card? After deleting the card, it will be deleted from the Kanban. You can only view it in the system recycle bin.';
$lang->kanbancard->confirmRestore = "Are you sure you want to restore this card? After the card is restored, the card will be restored to the '%s' Kanban column.";
$lang->kanbancard->confirmRestoreTip = "The card's column has been archived or deleted, please restore '%s' column first.";
$lang->kanbancard->confirmRemove = 'Are you sure to remove this card? After removing the card, the original data would not be affected.The card would be removed from the Kanban and counld not be restored.';
$lang->kanbancard->priList[1] = 1;
$lang->kanbancard->priList[2] = 2;
+2
View File
@@ -40,6 +40,7 @@ $lang->kanban->moveCard = '移动卡片';
$lang->kanban->cardColor = '卡片颜色';
$lang->kanban->setCardColor = '设置卡片颜色';
$lang->kanban->deleteCard = '删除卡片';
$lang->kanban->removeCard = '移除卡片';
$lang->kanban->assigntoCard = '指派';
$lang->kanban->setting = '设置';
$lang->kanban->enableArchived = '设置归档功能';
@@ -365,6 +366,7 @@ $lang->kanbancard->confirmArchive = '您确认归档该卡片吗?归档卡
$lang->kanbancard->confirmDelete = '您确认删除该卡片吗?删除卡片后,该卡片将从看板中删除,您只能通过系统回收站查看。';
$lang->kanbancard->confirmRestore = '您确定要还原该卡片吗?还原卡片后,该卡片将还原到“%s”看板列中。';
$lang->kanbancard->confirmRestoreTip = '该卡片所属的看板列已被归档或删除,请先还原“%s”看板列。';
$lang->kanbancard->confirmRemove = '您确认移除该卡片吗?移除卡片后,源数据不会受到影响,卡片将从看板中移除且无法恢复。';
$lang->kanbancard->priList[1] = 1;
$lang->kanbancard->priList[2] = 2;
+5 -2
View File
@@ -1113,16 +1113,17 @@ class kanbanModel extends model
* @param int $kanbanID
* @param object $cards
* @param string $fromType
* @param int $archived
* @access public
* @return array
*/
public function getImportedCards($kanbanID, $cards, $fromType)
public function getImportedCards($kanbanID, $cards, $fromType, $archived = 0)
{
/* Get imported cards based on imported object type. */
$objectCards = $this->dao->select('*')->from(TABLE_KANBANCARD)
->where('deleted')->eq(0)
->andWhere('kanban')->eq($kanbanID)
->andWhere('archived')->eq(0)
->andWhere('archived')->eq($archived)
->andWhere('fromType')->eq($fromType)
->fetchGroup('fromID', 'id');
@@ -1168,6 +1169,8 @@ class kanbanModel extends model
$objectCard->execType = $object->type;
}
$objectCard->objectStatus = $objectCard->status;
$objectCard->status = $objectCard->progress == 100 ? 'done' : 'doing';
$cards[$cardID] = $objectCard;
}
}
+31 -1
View File
@@ -31,6 +31,7 @@
#archivedCards .info > .users > span:after {right: -4px; content: ''; display: block; position: absolute; width: 2px; height: 2px; background-color: #8990a2; top: 0px; border-radius: 50%;}
#archivedCards .info > .users .avatar {display: inline-block; position: relative; border-radius: 50%; top: -5px; margin: 5px; right: -7px; margin-left: -4px;}
#archivedCards .cardName {word-wrap: break-word;}
#archivedCards .card-item .icon {margin-right:2px;}
#archivedCards .card-item .red {background-color: #b10b0b;}
#archivedCards .card-item .yellow {background-color: #cfa227;}
#archivedCards .card-item .green {background-color: #2a5f29;}
@@ -44,6 +45,12 @@
#archivedCards .progress-box {width: 97%;}
#performable {padding: 25px 10px !important;}
</style>
<?php
$app->loadLang('execution');
$app->loadLang('release');
$app->loadLang('build');
$app->loadLang('productplan');
?>
<div class='panel'>
<div class='panel-heading text-center'>
<strong><?php echo $lang->kanban->archivedCard;?></strong>
@@ -76,10 +83,21 @@
<?php if($card->status == 'done'):?>
<div class="label" style="<?php echo $labelColor;?>"><?php echo $lang->kanban->finished;?></div>
<?php endif;?>
<?php if(empty($card->fromType)):?>
<?php echo html::a($this->createLink('kanban', 'viewCard', "cardID=$card->id", '', true), $card->name, '', "class='cardName iframe' data-toggle='modal' data-width='80%' title='$card->name'");?>
<div class="info">
<span class="pri label-pri label-pri-<?php echo $card->pri;?>"><?php echo $card->pri;?></span>
<?php if($card->estimate and $card->estimate != 0) echo "<span class='text-gray'>{$card->estimate}h</span>";?>
<?php else:?>
<?php
$name = isset($card->title) ? $card->title : $card->name;
if(common::hasPriv($card->fromType, 'view')) echo html::a($this->createLink('productplan', 'view', "id=$card->fromID"), $name, '', "class='cardName' title='$name'");
if(!common::hasPriv($card->fromType, 'view')) echo "<div class='cardName' title='$name'>$name</div>";
echo "<div class='info $card->fromType'>";
if($card->fromType != 'release' and isset($lang->{$card->fromType}->statusList[$card->objectStatus])) echo "<span class='label label-$card->objectStatus'>" . $lang->{$card->fromType}->statusList[$card->objectStatus] . '</span>';
if(isset($card->date) and !helper::isZeroDate($card->date)) echo "<span class='time label label-light'>" . date("m/d", strtotime($card->date)) . "</span>"
?>
<?php endif;?>
<?php if(helper::isZeroDate($card->end) and !helper::isZeroDate($card->begin)):?>
<span class="time label label-light"><?php echo date("m/d", strtotime($card->begin)) . $lang->kanbancard->beginAB;?></span>
<?php endif;?>
@@ -137,7 +155,7 @@
if($canRestore) echo html::a(inlink('restoreCard', "cardID={$card->id}"), $lang->kanban->restore, '', "class='btn btn-xs btn-primary' target='hiddenwin'");
if($canDelete) echo html::a($this->createLink('kanban', 'deleteCard', "cardID=$card->id"), $lang->delete, '', "class='btn btn-xs delete-card' target='hiddenwin'");
if($canDelete) echo html::a($this->createLink('kanban', 'deleteCard', "cardID=$card->id"), $card->fromType == '' ? $lang->delete : $lang->unlink, '', "class='btn btn-xs delete-card' target='hiddenwin'");
?>
</div>
</div>
@@ -152,5 +170,17 @@ $(function()
{
$('#archivedCards').animate({right: -400}, 500);
});
$('.card-item').each(function()
{
var $item = $(this).children('.col-xs-10').children('.kanban-item');
var icon = '';
if($item.children('.build').length) icon = '<i class="icon icon-ver">';
if($item.children('.execution').length) icon = '<i class="icon icon-run">';
if($item.children('.productplan').length) icon = '<i class="icon icon-delay">';
if($item.children('.release').length) icon = '<i class="icon icon-publish">';
$item.children('.cardName').prepend(icon);
});
})
</script>
+30
View File
@@ -87,6 +87,36 @@ $config->product->search['params']['closedDate'] = array('operator' => '=',
$config->product->search['params']['lastEditedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->product->search['params']['activatedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->product->all = new stdclass();
$config->product->all->search['module'] = 'product';
$config->product->all->search['fields']['name'] = $lang->product->name;
$config->product->all->search['fields']['code'] = $lang->product->code;
$config->product->all->search['fields']['id'] = $lang->product->id;
if($config->systemMode == 'new') $config->product->all->search['fields']['program'] = $lang->product->program;
$config->product->all->search['fields']['line'] = $lang->product->line;
$config->product->all->search['fields']['desc'] = $lang->product->desc;
$config->product->all->search['fields']['PO'] = $lang->product->PO;
$config->product->all->search['fields']['QD'] = $lang->product->QD;
$config->product->all->search['fields']['RD'] = $lang->product->RD;
$config->product->all->search['fields']['reviewer'] = $lang->product->reviewer;
$config->product->all->search['fields']['type'] = $lang->product->type;
$config->product->all->search['fields']['createdDate'] = $lang->product->createdDate;
$config->product->all->search['fields']['createdBy'] = $lang->product->createdBy;
$config->product->all->search['params']['name'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->product->all->search['params']['code'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->product->all->search['params']['id'] = array('operator' => '=', 'control' => 'input', 'values' => '');
if($config->systemMode == 'new') $config->product->all->search['params']['program'] = array('operator' => '=', 'control' => 'select', 'values' => '');
$config->product->all->search['params']['line'] = array('operator' => '=', 'control' => 'select', 'values' => '');
$config->product->all->search['params']['desc'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->product->all->search['params']['PO'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->product->all->search['params']['QD'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->product->all->search['params']['RD'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->product->all->search['params']['reviewer'] = array('operator' => 'include', 'control' => 'select', 'values' => 'users');
$config->product->all->search['params']['type'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->product->typeList);
$config->product->all->search['params']['createdDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->product->all->search['params']['createdBy'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->product->create = new stdclass();
$config->product->edit = new stdclass();
$config->product->create->requiredFields = 'name,code';
+7 -2
View File
@@ -1197,12 +1197,14 @@ class product extends control
* @access public
* @return void
*/
public function all($browseType = 'noclosed', $orderBy = 'order_asc')
public function all($browseType = 'noclosed', $orderBy = 'order_asc', $param = 0)
{
/* Load module and set session. */
$this->loadModel('program');
$this->session->set('productList', $this->app->getURI(true), 'product');
$queryID = ($browseType == 'bySearch') ? (int)$param : 0;
if($this->app->viewType == 'mhtml')
{
$productID = $this->product->saveState(0, $this->products);
@@ -1210,9 +1212,12 @@ class product extends control
}
/* Process product structure. */
$productStats = $this->product->getStats($orderBy, '', $browseType, '', 'story');
$productStats = $this->product->getStats($orderBy, '', $browseType, '', 'story', '', $queryID);
$productStructure = $this->product->statisticProgram($productStats);
$actionURL = $this->createLink('product', 'all', "browseType=bySearch&orderBy=order_asc&queryID=myQueryID");
$this->product->buildProductSearchForm($param, $actionURL);
$this->view->title = $this->lang->product->common;
$this->view->position[] = $this->lang->product->common;
+100 -2
View File
@@ -275,6 +275,48 @@ class productModel extends model
->fetchAll('id');
}
/**
* Get list by search.
*
* @param int $queryID
* @access public
* @return array
*/
public function getListBySearch($queryID = 0)
{
if($queryID)
{
$query = $this->loadModel('search')->getQuery($queryID);
if($query)
{
$this->session->set('productQuery', $query->sql);
$this->session->set('productForm', $query->form);
}
else
{
$this->session->set('productQuery', ' 1 = 1');
}
}
else
{
if($this->session->productQuery == false) $this->session->set('productQuery', ' 1 = 1');
}
$productQuery = $this->session->productQuery;
$productQuery = preg_replace('/`(\w+)`/', 't1.`$1`', $productQuery);
$products = $this->dao->select('t1.id as id,t1.*')->from(TABLE_PRODUCT)->alias('t1')
->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id')
->where($productQuery)
->andWhere('t1.deleted')->eq(0)
->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->products)->fi()
->andWhere('t1.vision')->eq($this->config->vision)->fi()
->orderBy('t1.order_asc')
->fetchAll('id');
return $products;
}
/**
* Get product pairs.
*
@@ -1047,6 +1089,31 @@ class productModel extends model
$this->loadModel('search')->setSearchParams($this->config->product->search);
}
/**
* Build search form for all method of product module.
*
* @param int $queryID
* @param string $actionURL
* @access public
* @return void
*/
public function buildProductSearchForm($queryID, $actionURL)
{
$this->config->product->all->search['queryID'] = $queryID;
$this->config->product->all->search['actionURL'] = $actionURL;
$linePairs = $this->getLinePairs();
$this->config->product->all->search['params']['line']['values'] = array('' => '') + $linePairs;
if($this->config->systemMode == 'new')
{
$programPairs = $this->loadModel('program')->getTopPairs('', 'noclosed');
$this->config->product->all->search['params']['program']['values'] = array('' => '') + $programPairs;
}
$this->loadModel('search')->setSearchParams($this->config->product->all->search);
}
/**
* Get project pairs by product.
*
@@ -1567,13 +1634,13 @@ class productModel extends model
* @access public
* @return array
*/
public function getStats($orderBy = 'order_asc', $pager = null, $status = 'noclosed', $line = 0, $storyType = 'story', $programID = 0)
public function getStats($orderBy = 'order_asc', $pager = null, $status = 'noclosed', $line = 0, $storyType = 'story', $programID = 0, $param = 0)
{
$this->loadModel('report');
$this->loadModel('story');
$this->loadModel('bug');
$products = $this->getList($programID, $status, $limit = 0, $line);
$products = $status == 'bySearch' ? $this->getListBySearch($param) : $this->getList($programID, $status, $limit = 0, $line);
if(empty($products)) return array();
$productKeys = array_keys($products);
@@ -2022,6 +2089,37 @@ class productModel extends model
return true;
}
/**
* Build operate menu.
*
* @param object $product
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($product, $type = 'view')
{
$menu = '';
$params = "product=$product->id";
if($type == 'view')
{
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('product', $product, $type, 'direct');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('product', 'close', $params, $product, $type, '', '', '', '', "data-app='product'");
$menu .= "<div class='divider'></div>";
}
$menu .= $this->buildMenu('product', 'edit', $params, $product, $type);
if($type == 'view') $menu .= $this->buildMenu('product', 'delete', $params, $product, $type, 'trash', 'hiddenwin');
if($type == 'browse' && common::hasPriv('product', 'updateOrder')) $menu .= "<i class='icon icon-move text-blue'></i>";
return $menu;
}
/**
* Create the link from module,method,extra,branch.
*
+4 -10
View File
@@ -17,6 +17,7 @@
<?php $recTotalLabel = $browseType == $key ? " <span class='label label-light label-badge'>{$recTotal}</span>" : '';?>
<?php echo html::a(inlink("all", "browseType=$key&orderBy=$orderBy"), "<span class='text'>{$label}</span>" . $recTotalLabel, '', "class='btn btn-link' id='{$key}Tab'");?>
<?php endforeach;?>
<a class="btn btn-link querybox-toggle" id='bysearchTab'><i class="icon icon-search muted"></i> <?php echo $lang->product->searchStory;?></a>
</div>
<div class="btn-toolbar pull-right">
<?php common::printLink('product', 'export', "status=$browseType&orderBy=$orderBy", "<i class='icon-export muted'> </i>" . $lang->export, '', "class='btn btn-link export'", true, true)?>
@@ -31,11 +32,9 @@
</div>
<?php else:?>
<div class="main-col">
<div class="cell<?php if($browseType == 'bySearch') echo ' show';?>" id="queryBox" data-module='product'></div>
<form class="main-table table-product" data-ride="table" data-nested='true' id="productListForm" method="post" action='<?php echo inLink('batchEdit', '');?>' data-preserve-nested='false' data-expand-nest-child='true'>
<?php
$canOrder = common::hasPriv('product', 'updateOrder');
$canBatchEdit = common::hasPriv('product', 'batchEdit');
?>
<?php $canBatchEdit = common::hasPriv('product', 'batchEdit'); ?>
<table id="productList" class="table has-sort-head table-nested table-fixed">
<?php $vars = "browseType=$browseType&orderBy=%s";?>
<thead>
@@ -215,12 +214,7 @@
<td><?php echo $product->plans;?></td>
<td><?php echo $product->releases;?></td>
<?php foreach($extendFields as $extendField) echo "<td>" . $this->loadModel('flow')->getFieldValue($extendField, $product) . "</td>";?>
<td class='c-actions sort-handler'>
<?php common::printIcon('product', 'edit', "product=$product->id", $product, 'list', 'edit');?>
<?php if($canOrder):?>
<i class="icon icon-move text-blue"></i>
<?php endif;?>
</td>
<td class='c-actions sort-handler'><?php echo $this->buildOperateMenu($product, 'browse');?></td>
</tr>
<?php endforeach;?>
<?php endif;?>
+1 -15
View File
@@ -158,24 +158,10 @@
<div class='main-actions'>
<div class="btn-toolbar">
<?php
$params = "product=$product->id";
$browseLink = $this->session->productList ? $this->session->productList : inlink('browse', "productID=$product->id");
common::printBack($browseLink);
if(!$product->deleted)
{
echo $this->buildOperateMenu($product, 'view');
echo "<div class='divider'></div>";
if($product->status != 'closed')
{
common::printIcon('product', 'close', $params, $product, 'button', '', '', 'iframe', true);
echo "<div class='divider'></div>";
}
common::printIcon('product', 'edit', $params, $product);
common::printIcon('product', 'delete', $params, $product, 'button', 'trash', 'hiddenwin');
}
if(!$product->deleted) echo $this->buildOperateMenu($product, 'view');
?>
</div>
</div>
+98 -2
View File
@@ -1002,17 +1002,21 @@ class productplanModel extends model
*
* @param object $plan
* @param string $action
* @param string $module
* @access public
* @return void
*/
public static function isClickable($plan, $action)
public static function isClickable($plan, $action, $module = 'productplan')
{
$action = strtolower($action);
$clickable = commonModel::hasPriv('productplan', $action);
$clickable = commonModel::hasPriv($module, $action);
if(!$clickable) return false;
switch($action)
{
case 'create' :
if($plan->parent > 0 or strpos('done,closed', $plan->status) !== false) return false;
break;
case 'start' :
if($plan->status != 'wait' or $plan->parent < 0) return false;
break;
@@ -1025,8 +1029,100 @@ class productplanModel extends model
case 'activate' :
if($plan->status == 'wait' or $plan->status == 'doing' or $plan->parent < 0) return false;
break;
case 'delete' :
if($plan->parent < 0) return false;
break;
}
if($module == 'execution' && $action == 'create')
{
if($plan->parent < 0 || $plan->expired || in_array($plan->status, array('done', 'closed'))) return false;
$product = $this->loadModel('product')->getById($plan->product);
$branchList = $this->loadModel('branch')->getList($plan->product, 0, 'all');
$branchStatusList = array();
foreach($branchList as $productBranch) $branchStatusList[$productBranch->id] = $productBranch->status;
if($product->type != 'normal')
{
$branchStatus = isset($branchStatusList[$plan->branch]) ? $branchStatusList[$plan->branch] : '';
if($branchStatus == 'closed') return false;
}
}
return true;
}
/**
* Build operate menu.
*
* @param object $plan
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($plan, $type = 'view')
{
$params = "planID=$plan->id";
$menu = '';
$menu .= $this->buildMenu('productplan', 'start', $params, $plan, $type, 'play', 'hiddenwin', '', false, '', $this->lang->productplan->startAB);
$menu .= $this->buildMenu('productplan', 'finish', $params, $plan, $type, 'checked', 'hiddenwin', '', false, '', $this->lang->productplan->finishAB);
$menu .= $this->buildMenu('productplan', 'close', $params, $plan, $type, 'off', 'hiddenwin', 'iframe', true, '', $this->lang->productplan->closeAB);
if($type == 'view') $menu .= $this->buildMenu('productplan', 'activate', $params, $plan, $type, 'magic', 'hiddenwin', '', false, '', $this->lang->productplan->activateAB);
if($type == 'browse')
{
if($this->isClickable($plan, 'create', 'execution'))
{
$executionLink = $this->config->systemMode == 'new' ? '#projects' : helper::createLink('execution', 'create', "projectID=0&executionID=0&copyExecutionID=0&plan=$plan->id&confirm=no&productID=$plan->product");
if($this->config->systemMode == 'new')
{
$menu .= html::a($executionLink, '<i class="icon-plus"></i>', '', "data-toggle='modal' data-id='$plan->id' onclick='getPlanID(this, $plan->branch)' class='btn' title='{$this->lang->productplan->createExecution}'");
}
else
{
$menu .= html::a($executionLink, '<i class="icon-plus"></i>', '', "class='btn' title='{$this->lang->productplan->createExecution}'");
}
}
else
{
$menu .= "<button type='button' class='btn disabled'><i class='icon-plus' title='{$this->lang->productplan->createExecution}'></i></button>";
}
if(common::hasPriv('productplan', 'linkStory', $plan) and $plan->parent >= 0)
{
$menu .= $this->buildMenu('productplan', 'view', "{$params}&type=story&orderBy=id_desc&link=true", $plan, $type, 'link', '', '', '', '', $this->lang->productplan->linkStory);
}
else
{
$menu .= "<button type='button' class='disabled btn'><i class='icon-link' title='{$this->lang->productplan->linkStory}'></i></button>";
}
if(common::hasPriv('productplan', 'linkBug', $plan) and $plan->parent >= 0)
{
$menu .= $this->buildMenu('productplan', 'view', "{$params}&type=bug&orderBy=id_desc&link=true", $plan, $type, 'bug', '', '', '', '', $this->lang->productplan->linkBug);
}
else
{
$menu .= "<button type='button' class='disabled btn'><i class='icon-bug' title='{$this->lang->productplan->linkBug}'></i></button>";
}
$menu .= $this->buildMenu('productplan', 'edit', $params, $plan, $type);
}
$menu .= $this->buildMenu('productplan', 'create', "product={$plan->product}&branch={$plan->branch}&parent={$plan->id}", $plan, $type, 'split', '', '', '', '', $this->lang->productplan->children);
if($type == 'browse') $menu .= $this->buildMenu('productplan', 'delete', "{$params}&confirm=no", $plan, $type, 'trash', 'hiddenwin', '', '', $this->lang->productplan->delete);
if($type == 'view')
{
if(common::hasPriv('productplan', 'edit', $plan)) $menu .= html::a(helper::createLink('productplan', 'edit', $params), "<i class='icon-common-edit icon-edit'></i> " . $this->lang->edit, '', "class='btn btn-link' title='{$this->lang->edit}'");
if($plan->parent >= 0 && common::hasPriv('productplan', 'delete', $plan)) $menu .= html::a(helper::createLink('productplan', 'delete', $params), "<i class='icon-common-delete icon-trash'></i> " . $this->lang->delete, '', "class='btn btn-link' title='{$this->lang->delete}' target='hiddenwin'");
}
return $menu;
}
}
+1 -61
View File
@@ -144,67 +144,7 @@
<div title='<?php echo $desc;?>'><?php echo nl2br($desc);?></div>
</td>
<?php foreach($extendFields as $extendField) echo "<td>" . $this->loadModel('flow')->getFieldValue($extendField, $plan) . "</td>";?>
<td class='c-actions'>
<?php
if($plan->parent >= 0 )
{
$attr = "target='hiddenwin'";
common::printIcon('productplan', 'start', "planID=$plan->id", $plan, 'list', 'play', '', '', false, $attr);
common::printIcon('productplan', 'finish', "planID=$plan->id", $plan, 'list', 'checked', '', '', false, $attr);
common::printIcon('productplan', 'close', "planID=$plan->id", $plan, 'list', 'off', '', 'iframe', true);
}
$attr = $plan->expired ? "disabled='disabled'" : '';
$class = '';
if($product->type != 'normal')
{
$branchStatus = isset($branchStatusList[$plan->branch]) ? $branchStatusList[$plan->branch] : '';
if($branchStatus == 'closed') $class = 'disabled';
}
if(common::hasPriv('execution', 'create', $plan) and $plan->parent >= 0)
{
$disabled = '';
$executionLink = $config->systemMode == 'new' ? '#projects' : $this->createLink('execution', 'create', "projectID=0&executionID=0&copyExecutionID=0&plan=$plan->id&confirm=no&productID=$productID");
if(in_array($plan->status, array('done', 'closed'))) $disabled = 'disabled';
if($config->systemMode == 'new')
{
echo html::a($executionLink, '<i class="icon-plus"></i>', '', "data-toggle='modal' data-id='$plan->id' onclick='getPlanID(this, $plan->branch)' class='btn {$disabled} {$class}' title='{$lang->productplan->createExecution}' $attr");
}
else
{
echo html::a($executionLink, '<i class="icon-plus"></i>', '', "class='btn {$disabled}' title='{$lang->productplan->createExecution}' $attr");
}
}
if(common::hasPriv('productplan', 'linkStory', $plan) and $plan->parent >= 0) echo html::a(inlink('view', "planID=$plan->id&type=story&orderBy=id_desc&link=true"), '<i class="icon-link"></i>', '', "class='btn' title='{$lang->productplan->linkStory}'");
if(common::hasPriv('productplan', 'linkBug', $plan) and $plan->parent >= 0) echo html::a(inlink('view', "planID=$plan->id&type=bug&orderBy=id_desc&link=true"), '<i class="icon-bug"></i>', '', "class='btn' title='{$lang->productplan->linkBug}'");
common::printIcon('productplan', 'edit', "planID=$plan->id", $plan, 'list');
if(common::hasPriv('productplan', 'create', $plan))
{
if($plan->parent > 0 or strpos('done,closed', $plan->status) !== false)
{
echo "<button type='button' class='disabled btn'><i class='disabled icon-split' title='{$this->lang->productplan->children}'></i></button> ";
}
else
{
echo html::a($this->createLink('productplan', 'create', "product=$productID&branch=$branch&parent={$plan->id}"), "<i class='icon-split'></i>", '', "class='btn {$class}' title='{$this->lang->productplan->children}'");
}
}
if(common::hasPriv('productplan', 'delete', $plan))
{
$deleteURL = '###';
$disabled = 'disabled';
if($plan->parent >= 0)
{
$deleteURL = $this->createLink('productplan', 'delete', "planID=$plan->id&confirm=no");
$disabled = '';
}
echo html::a($deleteURL, '<i class="icon-trash"></i>', 'hiddenwin', "class='btn {$disabled}' title='{$lang->productplan->delete}'");
}
?>
</td>
<td class='c-actions'><?php echo $this->buildOperateMenu($plan, 'browse'); ?></td>
</tr>
<?php endforeach;?>
</tbody>
+1 -25
View File
@@ -38,31 +38,7 @@
</div>
</div>
<div class='btn-toolbar pull-right' id='actionsBox'>
<?php
if(!$plan->deleted && !isonlybody())
{
echo $this->buildOperateMenu($plan, 'view');
if($plan->parent >= 0)
{
$attr = "target='hiddenwin'";
$class = $plan->status == 'wait' ? '' : 'disabled';
common::printLink('productplan', 'start', "planID=$plan->id", "<i class='icon-play'></i>{$lang->productplan->startAB}", '', "class='btn btn-link {$class}'{$attr} title='{$lang->productplan->start}'", '', false, $plan);
$class = $plan->status == 'doing' ? '' : 'disabled';
common::printLink('productplan', 'finish', "planID=$plan->id", "<i class='icon-checked'></i>{$lang->productplan->finishAB}", '', "class='btn btn-link {$class}' target='hiddenwin' title='{$lang->productplan->finish}'", '', false, $plan);
$class = $plan->status !== 'closed' ? 'iframe' : 'disabled';
common::printLink('productplan', 'close', "planID=$plan->id", "<i class='icon-off'></i>{$lang->productplan->closeAB}", '', "class='btn btn-link {$class}' title='{$lang->productplan->close}'", '', true, $plan);
$class = in_array($plan->status, array('closed', 'done')) ? '' : 'disabled';
common::printLink('productplan', 'activate', "planID=$plan->id", "<i class='icon-magic'></i>{$lang->productplan->activateAB}", '', "class='btn btn-link {$class}' target='hiddenwin' title='{$lang->productplan->activate}'", '', false, $plan);
}
$class = (isset($branchStatus) and $branchStatus == 'closed') ? 'disabled' : '';
if(common::hasPriv('productplan', 'create', $plan) and $plan->parent <= 0 and strpos('wait,doing', $plan->status) !== false) echo html::a($this->createLink('productplan', 'create', "product={$plan->product}&branch={$plan->branch}&parent={$plan->id}"), "<i class='icon-split'></i> " . $this->lang->productplan->children , '', "class='btn btn-link {$class}' title='{$this->lang->productplan->children}'");
if(common::hasPriv('productplan', 'edit', $plan)) echo html::a($this->createLink('productplan', 'edit', "planID=$plan->id"), "<i class='icon-common-edit icon-edit'></i> " . $this->lang->edit, '', "class='btn btn-link' title='{$this->lang->edit}'");
if(common::hasPriv('productplan', 'delete', $plan) and $plan->parent >= 0) echo html::a($this->createLink('productplan', 'delete', "planID=$plan->id"), "<i class='icon-common-delete icon-trash'></i> " . $this->lang->delete, '', "class='btn btn-link' title='{$this->lang->delete}' target='hiddenwin'");
}
?>
<?php if(!$plan->deleted && !isonlybody()) echo $this->buildOperateMenu($plan, 'view'); ?>
</div>
</div>
<div id='mainContent' class='main-content'>
+5 -2
View File
@@ -41,15 +41,18 @@ class release extends control
* @access public
* @return void
*/
public function browse($productID, $branch = 'all', $type = 'all', $orderBy = 't1.date_desc')
public function browse($productID, $branch = 'all', $type = 'all', $orderBy = 't1.date_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$this->commonAction($productID, $branch);
$this->session->set('releaseList', $this->app->getURI(true), 'product');
$this->view->title = $this->view->product->name . $this->lang->colon . $this->lang->release->browse;
$this->view->position[] = $this->lang->release->browse;
$this->view->releases = $this->release->getList($productID, $branch, $type, $orderBy);
$this->view->releases = $this->release->getList($productID, $branch, $type, $orderBy, $pager);
$this->view->type = $type;
$this->view->pager = $pager;
$this->display();
}
-3
View File
@@ -121,9 +121,6 @@ class releaseModel extends model
/* Check build if build is required. */
if(strpos($this->config->release->create->requiredFields, 'build') !== false and $this->post->build == false) return dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->release->build);
/* Check date must be not more than today. */
if($this->post->date > date('Y-m-d')) return dao::$errors[] = $this->lang->release->errorDate;
$release = fixer::input('post')
->add('product', (int)$productID)
->add('branch', (int)$branch)
+3
View File
@@ -105,5 +105,8 @@
</tbody>
</table>
<?php endif;?>
<div class='table-footer'>
<?php $pager->show('right', 'pagerjs');?>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
-6
View File
@@ -75,10 +75,4 @@
</form>
</div>
</div>
<script>
$(function()
{
$(".form-date").datetimepicker('setEndDate', '<?php echo date(DT_DATE1)?>');
});
</script>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -1351,7 +1351,7 @@ class story extends control
if(isonlybody())
{
$execution = $this->execution->getByID($this->session->execution);
if($this->app->tab == 'execution' and $execution->type == 'kanban' and $this->app->tab == 'execution')
if($this->app->tab == 'execution' and $execution->type == 'kanban')
{
$this->loadModel('kanban')->updateLane($this->session->execution, 'story', $storyID);
$kanbanData = $this->kanban->getRDKanban($this->session->execution, $this->session->execLaneType ? $this->session->execLaneType : 'all');
+3
View File
@@ -1987,6 +1987,7 @@ class storyModel extends model
->add('lastEditedBy', $this->app->user->account)
->add('lastEditedDate', $now)
->add('assignedDate', $now)
->remove('comment')
->get();
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->checkFlow()->where('id')->eq((int)$storyID)->exec();
@@ -3038,6 +3039,8 @@ class storyModel extends model
public function closeParentRequirement($storyID)
{
$parentID = $this->dao->select('BID')->from(TABLE_RELATION)->where('AID')->eq($storyID)->fetch();
if(empty($parentID)) return;
$stories = $this->dao->select('t2.id, t2.status')->from(TABLE_RELATION)->alias('t1')
->leftJoin(TABLE_STORY)->alias('t2')->on('t2.id=t1.AID')
->where('t1.BType')->eq('requirement')
+114 -16
View File
@@ -1713,22 +1713,8 @@ class testcaseModel extends model
echo $case->stepNumber;
break;
case 'actions':
if($canBeChanged)
{
if($case->needconfirm or $browseType == 'needconfirm')
{
common::printIcon('testcase', 'confirmstorychange', "caseID=$case->id", $case, 'list', 'confirm', 'hiddenwin', '', '', '', $this->lang->confirm);
break;
}
common::printIcon('testtask', 'results', "runID=0&caseID=$case->id", $case, 'list', '', '', 'iframe', true, "data-width='95%'");
common::printIcon('testtask', 'runCase', "runID=0&caseID=$case->id&version=$case->version", $case, 'list', 'play', '', 'runCase iframe', false, "data-width='95%'");
common::printIcon('testcase', 'edit', "caseID=$case->id", $case, 'list');
if($this->config->testcase->needReview or !empty($this->config->testcase->forceReview)) common::printIcon('testcase', 'review', "caseID=$case->id", $case, 'list', 'glasses', '', 'iframe');
common::printIcon('testcase', 'createBug', "product=$case->product&branch=$case->branch&extra=caseID=$case->id,version=$case->version,runID=", $case, 'list', 'bug', '', 'iframe', '', "data-width='90%'");
common::printIcon('testcase', 'create', "productID=$case->product&branch=$case->branch&moduleID=$case->module&from=testcase&param=$case->id", $case, 'list', 'copy');
}
$case->browseType = $browseType;
echo $this->buildOperateMenu($case, 'browse');
break;
}
echo '</td>';
@@ -2014,4 +2000,116 @@ class testcaseModel extends model
return '';
}
/**
* Build test case menu.
*
* @param object $case
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($case, $type = 'view')
{
$function = 'buildOperate' . ucfirst($type) . 'Menu';
return $this->$function($case);
}
/**
* Build test case view menu.
*
* @param object $case
* @access public
* @return string
*/
public function buildOperateViewMenu($case)
{
if($case->deleted) return '';
$menu = '';
$params = "caseID=$case->id";
$extraParams = "runID=$case->runID&$params";
if(!$case->needconfirm)
{
if(!$case->isLibCase)
{
if($this->app->getViewType() == 'xhtml')
{
$menu .= $this->buildMenu('testtask', 'runCase', "$extraParams&version=$case->currentVersion", $case, 'view', 'play', '', 'showinonlybody', false, "data-width='95%'");
$menu .= $this->buildMenu('testtask', 'results', "$extraParams&version=$case->version", $case, 'view', '', '', 'showinonlybody', false, "data-width='95%'");
}
else
{
$menu .= $this->buildMenu('testtask', 'runCase', "$extraParams&version=$case->currentVersion", $case, 'view', 'play', '', 'showinonlybody iframe', false, "data-width='95%'");
$menu .= $this->buildMenu('testtask', 'results', "$extraParams&version=$case->version", $case, 'view', '', '', 'showinonlybody iframe', false, "data-width='95%'");
}
if($case->caseFails > 0)
{
$menu .= $this->buildMenu('testcase', 'createBug', "product=$case->product&branch=$case->branch&extra=$params,version=$case->version,runID=$case->runID", $case, 'view', 'bug', '', 'iframe', '', "data-width='90%'");
}
}
if($this->config->testcase->needReview || !empty($this->config->testcase->forceReview))
{
$menu .= $this->buildMenu('testcase', 'review', $params, $case, 'view', '', '', 'iframe', '', '', $this->lang->testcase->reviewAB);
}
}
else
{
$menu .= $this->buildMenu('testcase', 'confirmstorychange', $params, $case, 'view', 'confirm', 'hiddenwin', '', '', '', $this->lang->confirm);
}
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('testcase', $case, 'view', 'direct');
$menu .= "<div class='divider'></div>";
if(!$case->needconfirm)
{
if(!isonlybody()) $menu .= $this->buildMenu('testcase', 'edit', $params, $case, 'view', '', '', 'showinonlybody');
if(!$case->isLibCase && $case->auto != 'unit')
{
$menu .= $this->buildMenu('testcase', 'create', "productID=$case->product&branch=$case->branch&moduleID=$case->module&from=testcase&param=$case->id", $case, 'view', 'copy');
}
if($case->isLibCase && common::hasPriv('caselib', 'createCase'))
{
echo html::a(helper::createLink('caselib', 'createCase', "libID=$case->lib&moduleID=$case->module&param=$case->id"), "<i class='icon-copy'></i>", '', "class='btn' title='{$this->lang->testcase->copy}'");
}
$menu .= $this->buildMenu('testcase', 'delete', $params, $case, 'view', 'trash', 'hiddenwin', '');
}
return $menu;
}
/**
* Build test case browse menu.
*
* @param object $case
* @access public
* @return string
*/
public function buildOperateBrowseMenu($case)
{
$canBeChanged = common::canBeChanged('case', $case);
if(!$canBeChanged) return '';
$menu = '';
$params = "caseID=$case->id";
if($case->needconfirm || $case->browseType == 'needconfirm')
{
return $this->buildMenu('testcase', 'confirmstorychange', $params, $case, 'browse', 'confirm', 'hiddenwin', '', '', '', $this->lang->confirm);
}
$menu .= $this->buildMenu('testtask', 'results', "runID=0&$params", $case, 'browse', '', '', 'iframe', true, "data-width='95%'");
$menu .= $this->buildMenu('testtask', 'runCase', "runID=0&$params&version=$case->version", $case, 'browse', 'play', '', 'runCase iframe', false, "data-width='95%'");
$menu .= $this->buildMenu('testcase', 'edit', "caseID=$case->id", $case, 'browse');
if($this->config->testcase->needReview || !empty($this->config->testcase->forceReview))
{
common::printIcon('testcase', 'review', $params, $case, 'browse', 'glasses', '', 'iframe');
}
$menu .= $this->buildMenu('testcase', 'createBug', "product=$case->product&branch=$case->branch&extra=caseID=$case->id,version=$case->version,runID=", $case, 'browse', 'bug', '', 'iframe', '', "data-width='90%'");
$menu .= $this->buildMenu('testcase', 'create', "productID=$case->product&branch=$case->branch&moduleID=$case->module&from=testcase&param=$case->id", $case, 'browse', 'copy');
return $menu;
}
}
+3 -33
View File
@@ -112,41 +112,11 @@
<div class='main-actions'>
<div class="btn-toolbar">
<?php common::printBack($browseLink);?>
<?php if(!$case->deleted):?>
<?php if(!isonlybody()) echo "<div class='divider'></div>";?>
<?php if(!$case->needconfirm):?>
<?php
if(!$isLibCase)
{
if(!isonlybody()) echo "<div class='divider'></div>";
if($this->app->getViewType() == 'xhtml')
{
common::printIcon('testtask', 'runCase', "runID=$runID&caseID=$case->id&version=$case->currentVersion", $case, 'button', 'play', '', 'showinonlybody', false, "data-width='95%'");
common::printIcon('testtask', 'results', "runID=$runID&caseID=$case->id&version=$case->version", $case, 'button', '', '', 'showinonlybody', false, "data-width='95%'");
}
else
{
common::printIcon('testtask', 'runCase', "runID=$runID&caseID=$case->id&version=$case->currentVersion", $case, 'button', 'play', '', 'showinonlybody iframe', false, "data-width='95%'");
common::printIcon('testtask', 'results', "runID=$runID&caseID=$case->id&version=$case->version", $case, 'button', '', '', 'showinonlybody iframe', false, "data-width='95%'");
}
if($caseFails > 0) common::printIcon('testcase', 'createBug', "product=$case->product&branch=$case->branch&extra=caseID=$case->id,version=$case->version,runID=$runID", $case, 'button', 'bug', '', 'iframe', '', "data-width='90%'");
}
if($config->testcase->needReview or !empty($config->testcase->forceReview)) common::printIcon('testcase', 'review', "caseID=$case->id", $case, 'button', '', '', 'iframe', '', '', $lang->testcase->reviewAB);
?>
<?php $case->isLibCase = $isLibCase;?>
<?php $case->caseFails = $caseFails;?>
<?php $case->runID = $runID;?>
<?php echo $this->buildOperateMenu($case, 'view');?>
<?php
if(!isonlybody()) common::printIcon('testcase', 'edit',"caseID=$case->id", $case, 'button', '', '', 'showinonlybody');
if(!$isLibCase and $case->auto != 'unit') common::printIcon('testcase', 'create', "productID=$case->product&branch=$case->branch&moduleID=$case->module&from=testcase&param=$case->id", $case, 'button', 'copy');
if($isLibCase and common::hasPriv('caselib', 'createCase')) echo html::a($this->createLink('caselib', 'createCase', "libID=$case->lib&moduleID=$case->module&param=$case->id"), "<i class='icon-copy'></i>", '', "class='btn' title='{$lang->testcase->copy}'");
common::printIcon('testcase', 'delete', "caseID=$case->id", $case, 'button', 'trash', 'hiddenwin', '');
?>
<?php elseif($case->needconfirm):?>
<?php common::printIcon('testcase', 'confirmstorychange', "caseID=$case->id", $case, 'list', 'confirm', 'hiddenwin', '', '', '', $this->lang->confirm);?>
<?php endif;?>
<?php endif;?>
</div>
</div>
</div>
+23
View File
@@ -327,4 +327,27 @@ class testsuiteModel extends model
->page($pager)
->fetchAll('id');
}
/**
* Build testsuite menu.
*
* @param object $suite
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($suite, $type = 'view')
{
$menu = '';
$params = "suiteID=$suite->id";
if($type == 'view') $menu .= $this->buildFlowMenu('testsuite', $suite, $type, 'direct');
$menu .= $this->buildMenu('testsuite', 'linkCase', $params, $suite, $type, 'link', '', '', '', '', $this->lang->testsuite->linkCase);
$menu .= $this->buildMenu('testsuite', 'edit', $params, $suite, $type);
$menu .= $this->buildMenu('testsuite', 'delete', $params, $suite, $type, 'trash', 'hiddenwin');
return $menu;
}
}
+1 -5
View File
@@ -70,11 +70,7 @@
<td><?php echo $suite->addedDate;?></td>
<?php foreach($extendFields as $extendField) echo "<td>" . $this->loadModel('flow')->getFieldValue($extendField, $suite) . "</td>";?>
<td class='c-actions'>
<?php
common::printIcon('testsuite', 'linkCase', "suiteID=$suite->id", $suite, 'list', 'link');
common::printIcon('testsuite', 'edit', "suiteID=$suite->id", $suite, 'list');
common::printIcon('testsuite', 'delete', "suiteID=$suite->id", $suite, 'list', 'trash', 'hiddenwin');
?>
<?php echo $this->buildOperateMenu($suite, 'browse');?>
</td>
</tr>
<?php endforeach;?>
-4
View File
@@ -32,10 +32,6 @@
if(!$suite->deleted and $canBeChanged)
{
echo $this->buildOperateMenu($suite, 'view');
common::printLink('testsuite', 'linkCase', "suiteID=$suite->id", "<i class='icon icon-link'> </i> " . '', '', "class='btn btn-link' title='{$lang->testsuite->linkCase}'");
common::printIcon('testsuite', 'edit', "suiteID=$suite->id");
common::printIcon('testsuite', 'delete', "suiteID=$suite->id", '', 'button', 'trash', 'hiddenwin');
}
?>
</div>
+72
View File
@@ -2166,4 +2166,76 @@ class testtaskModel extends model
return array('suites' => $suites, 'cases' => $cases, 'results' => $results, 'suiteNames' => $suiteNames, 'caseTitles' => $caseTitles);
}
/**
* Build test task menu.
*
* @param object $task
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($task, $type = 'view')
{
$function = 'buildOperate' . ucfirst($type) . 'Menu';
return $this->$function($task);
}
/**
* Build test task view menu.
*
* @param object $task
* @access public
* @return string
*/
public function buildOperateViewMenu($task)
{
if($task->deleted) return '';
$menu = '';
$params = "taskID=$task->id";
$menu .= $this->buildMenu('testtask', 'start', $params, $task, 'view', '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('testtask', 'close', $params, $task, 'view', '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('testtask', 'block', $params, $task, 'view', 'pause', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('testtask', 'activate', $params, $task, 'view', 'magic', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('testtask', 'cases', $params, $task, 'view', 'sitemap');
$menu .= $this->buildMenu('testtask', 'linkCase', $params, $task, 'view', 'link');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('testtask', $task, 'view', 'direct');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('testtask', 'edit', $params, $task, 'view');
$menu .= $this->buildMenu('testtask', 'delete', $params, $task, 'view', 'trash', 'hiddenwin');
return $menu;
}
/**
* Build test task browse menu.
*
* @param object $task
* @access public
* @return string
*/
public function buildOperateBrowseMenu($task)
{
$menu = '';
$params = "taskID=$task->id";
$menu .= '<div id="action-divider">';
$menu .= $this->buildMenu('testtask', 'cases', $params, $task, 'browse', 'sitemap');
$menu .= $this->buildMenu('testtask', 'linkCase', "$params&type=all&param=myQueryID", $task, 'browse', 'link');
$menu .= $this->buildMenu('testreport', 'browse', "objectID=$task->product&objectType=product&extra=$task->id", $task, 'browse', 'flag');
$menu .= '</div>';
$menu .= $this->buildMenu('testtask', 'view', $params, $task, 'browse', 'list-alt', '', 'iframe', true, "data-width='90%'");
$menu .= $this->buildMenu('testtask', 'edit', $params, $task, 'browse');
if(common::hasPriv('testtask', 'delete', $task))
{
$deleteURL = helper::createLink('testtask', 'delete', "taskID=$task->id&confirm=yes");
$menu .= html::a("javascript:ajaxDelete(\"$deleteURL\",\"taskList\",confirmDelete)", '<i class="icon-common-delete icon-trash"></i>', '', "title='{$this->lang->testtask->delete}' class='btn'");
}
return $menu;
}
}
+1 -14
View File
@@ -102,20 +102,7 @@ $status = $this->session->testTaskVersionStatus;
</td>
<?php foreach($extendFields as $extendField) echo "<td>" . $this->loadModel('flow')->getFieldValue($extendField, $task) . "</td>";?>
<td class='c-actions'>
<?php
echo '<div id="action-divider">';
common::printIcon('testtask', 'cases', "taskID=$task->id", $task, 'list', 'sitemap');
common::printIcon('testtask', 'linkCase', "taskID=$task->id&type=all&param=myQueryID", $task, 'list', 'link');
common::printIcon('testreport', 'browse', "objectID=$task->product&objectType=product&extra=$task->id", $task, 'list', 'flag');
echo '</div>';
common::printIcon('testtask', 'view', "taskID=$task->id", '', 'list', 'list-alt', '', 'iframe', true, "data-width='90%'");
common::printIcon('testtask', 'edit', "taskID=$task->id", $task, 'list');
if(common::hasPriv('testtask', 'delete', $task))
{
$deleteURL = $this->createLink('testtask', 'delete', "taskID=$task->id&confirm=yes");
echo html::a("javascript:ajaxDelete(\"$deleteURL\",\"taskList\",confirmDelete)", '<i class="icon-common-delete icon-trash"></i>', '', "title='{$lang->testtask->delete}' class='btn'");
}
?>
<?php echo $this->buildOperateMenu($task, 'browse');?>
</td>
</tr>
<?php endforeach;?>
-17
View File
@@ -50,25 +50,8 @@
<div class='main-actions'>
<div class="btn-toolbar">
<?php echo html::backButton('<i class="icon icon-back icon-sm"></i> ' . $lang->goback, '', 'btn btn-secondary');?>
<?php if(!$task->deleted):?>
<div class='divider'></div>
<?php
common::printIcon('testtask', 'start', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('testtask', 'close', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('testtask', 'block', "taskID=$task->id", $task, 'button', 'pause', '', 'iframe showinonlybody', true);
common::printIcon('testtask', 'activate', "taskID=$task->id", $task, 'button', 'magic', '', 'iframe showinonlybody', true);
common::printIcon('testtask', 'cases', "taskID=$task->id", $task, 'button', 'sitemap');
common::printIcon('testtask', 'linkCase', "taskID=$task->id", $task, 'button', 'link');
?>
<?php echo $this->buildOperateMenu($task, 'view');?>
<div class='divider'></div>
<?php
common::printIcon('testtask', 'edit', "taskID=$task->id", $task);
common::printIcon('testtask', 'delete', "taskID=$task->id", $task, 'button', 'trash', 'hiddenwin');
?>
<?php endif;?>
</div>
</div>
</div>
+3
View File
@@ -0,0 +1,3 @@
sonar.projectKey=zentaopms
sonar.qualitygate.wait=true
sonar.coverage.exclusions=**/*.*
+299
View File
@@ -0,0 +1,299 @@
<?php
class mailTest
{
/**
* __construct.
*
* @access public
* @return void
*/
public function __construct()
{
global $tester;
global $dao;
$this->dao = $dao;
$this->tester = $tester;
$this->objectModel = $tester->loadModel('mail');
}
/**
* AutoDetect.
*
* @param int $email
* @access public
* @return object
*/
public function autoDetectTest($email)
{
$objects = $this->objectModel->autoDetect($email);
if(dao::isError()) return dao::getError();
if(!$objects->host) return '没有检测到相关信息';
return $objects;
}
/**
* Get config from provider.
*
* @param int $domain
* @param int $username
* @access public
* @return object
*/
public function getConfigFromProviderTest($domain, $username)
{
$object = $this->objectModel->getConfigFromProvider($domain, $username);
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Get config by MXRR.
*
* @param int $domain
* @param int $username
* @access public
* @return object
*/
public function getConfigByMXRRTest($domain, $username)
{
$object = $this->objectModel->getConfigByMXRR($domain, $username);
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Get config by detecting SMTP.
*
* @param int $domain
* @param int $username
* @param int $port
* @access public
* @return object
*/
public function getConfigByDetectingSMTPTest($domain, $username, $port)
{
$object = $this->objectModel->getConfigByDetectingSMTP($domain, $username, $port);
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Set MTA.
*
* @access public
* @return object
*/
public function setMTATest()
{
$object = $this->objectModel->setMTA();
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Set sendmail.
*
* @access public
* @return object
*/
public function setSendMailTest()
{
$object = $this->objectModel->setSendMail();
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Send.
*
* @param int $toList
* @param int $subject
* @param string $body
* @param string $ccList
* @param int $includeMe
* @param array $emails
* @access public
* @return object
*/
public function sendTest($toList, $subject, $body = '', $ccList = '', $includeMe = false, $emails = array())
{
$object = $this->objectModel->send($toList, $subject, $body, $ccList, $includeMe, $emails);
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Set CC.
*
* @param int $ccList
* @param int $emails
* @access public
* @return object
*/
public function setCCTest($ccList, $emails)
{
$this->objectModel->setCC($ccList, $emails);
return $this->setMTATest();
}
/**
* Set subject.
*
* @param int $subject
* @access public
* @return object
*/
public function setSubjectTest($subject)
{
$this->objectModel->setSubject($subject);
return $this->setMTATest();
}
/**
* Set body.
*
* @param int $body
* @access public
* @return object
*/
public function setBodyTest($body)
{
$this->objectModel->setBody($body);
return $this->setMTATest();
}
/**
* Mail exist.
*
* @access public
* @return object
*/
public function mailExistTest()
{
$object = $this->objectModel->mailExist();
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Add queue.
*
* @param int $toList
* @param int $subject
* @param string $body
* @param string $ccList
* @param int $includeMe
* @access public
* @return object
*/
public function addQueueTest($toList, $subject, $body = '', $ccList = '', $includeMe = false)
{
$this->objectModel->addQueue($toList, $subject, $body, $ccList, $includeMe);
if($toList and $subject)
{
$id = $this->dao->lastInsertID();
$object = $this->dao->select('*')->from(TABLE_NOTIFY)->where('id')->eq($id)->fetch();
}
if(!$object) return '没有数据提交';
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Get queue.
*
* @param string $status
* @param string $orderBy
* @param int $pager
* @access public
* @return object
*/
public function getQueueTest($status = '', $orderBy = 'id_desc', $pager = null)
{
$objects = $this->objectModel->getQueue($status = '', $orderBy = 'id_desc', $pager = null);
if(dao::isError()) return dao::getError();
return $objects;
}
/**
* Get queue by id.
*
* @param int $queueID
* @access public
* @return object
*/
public function getQueueByIdTest($queueID)
{
$object = $this->objectModel->getQueueById($queueID);
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Merge mails.
*
* @param int $user
* @access public
* @return object
*/
public function mergeMailsTest($user)
{
$mails = $this->dao->select('*')->from(TABLE_NOTIFY)
->where('objectType')->eq('mail')
->andWhere('toList')->eq($user)
->fetchAll();
$object = $this->objectModel->mergeMails($mails);
if(dao::isError()) return dao::getError();
return $object;
}
/**
* Get subject.
*
* @param int $objectType
* @param int $object
* @param int $title
* @param int $actionType
* @access public
* @return object
*/
public function getSubjectTest($objectType, $object, $title, $actionType)
{
$object = $this->tester->loadModel($objectType)->getByID($object);
$object = $this->objectModel->getSubject($objectType, $object, $title, $actionType);
if(dao::isError()) return dao::getError();
return $object;
}
}
+2 -2
View File
@@ -80,9 +80,9 @@ fields:
note: "邮箱"
fields:
- field: email1
range: 100-999:R
range: 1000-9999:2
- field: email2
range: 100-999:R
range: 1000-9999:2
- field: email3
range: [@qq.com,@163.com,@gmail.com]
- field: skype
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->addQueue();
cid=1
pid=1
*/
$mail = new mailTest();
$result1 = $mail->addQueueTest('','');
$result2 = $mail->addQueueTest('user3', '测试提交队列', '测试发信内容');
$result3 = $mail->addQueueTest('user3,admin', '测试提交队列', '测试发信内容');
$result4 = $mail->addQueueTest('user3,admin', '测试提交队列', '测试发信内容', '', true);
r($result1) && p() && e('没有数据提交'); //提交数据为空时
r($result2) && p('subject') && e('测试提交队列'); //获取添加的邮件主题
r($result3) && p('toList') && e('user3'); //获取收件人列表不包含自己
r($result4) && p('toList') && e('user3,admin'); //获取收件人列表包含自己
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->autoDetect();
cid=1
pid=1
*/
$mail = new mailTest();
$data = array('test@qq.com', 'test@163.com', 'test@sohu.com', 'test@vip.sohu.com', 'testm');
r($mail->autoDetectTest($data[0])) && p('host') && e('smtp.qq.com'); //检测qq邮箱信息
r($mail->autoDetectTest($data[1])) && p('host') && e('smtp.163.com'); //检测163邮箱信息
r($mail->autoDetectTest($data[2])) && p('host') && e('smtp.sohu.com'); //检测搜狐邮箱信息
r($mail->autoDetectTest($data[3])) && p('host') && e('smtp.vip.sohu.com'); //检测搜狐vip邮箱信息
r($mail->autoDetectTest($data[4])) && p() && e('没有检测到相关信息'); //检测不存在的邮箱服务商信息
@@ -0,0 +1,25 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->getConfigByDetectingSMTP();
cid=1
pid=1
*/
$mail = new mailTest();
$result1 = $mail->getConfigByDetectingSMTPTest('qq.com','122@qq.com',465);
$result2 = $mail->getConfigByDetectingSMTPTest('qq.com','122@qq.com',465);
$result3 = $mail->getConfigByDetectingSMTPTest('qq.com','122@qq.com',22);
$result4 = $mail->getConfigByDetectingSMTPTest('testqq.com','122@qq.com',22);
r($result1) && p('host') && e('smtp.qq.com'); //根据域名获取smtp
r($result2) && p('secure') && e('ssl'); //根据域名获取secure
r($result3) && p('secure') && e('0'); //当端口号不为465时获取secure
r($result4) && p() && e('0'); //当域名不存在时
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->getConfigByMXRR();
cid=1
pid=1
*/
$mail = new mailTest();
$result1 = $mail->getConfigByMXRRTest('qq.com','test');
$result2 = $mail->getConfigByMXRRTest('sohu.com','test');
r($result1) && p('host') && e('smtp.qq.com'); //获取qq相关信息
r($result2) && p('host') && e('smtp.sohu.com'); //获取sohu相关信息
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->getConfigFromProvider();
cid=1
pid=1
*/
$mail = new mailTest();
$result1 = $mail->getConfigFromProviderTest('qq.com','123');
$result2 = $mail->getConfigFromProviderTest('sohu.com','123');
r($result1) && p('host') && e('smtp.qq.com'); //获取qq相关信息
r($result2) && p('host') && e('smtp.sohu.com'); //获取sohu相关信息
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->getQueue();
cid=1
pid=1
*/
$mail = new mailTest();
$result1 = count($mail->getQueueTest());
$result2 = count($mail->getQueueTest('fail'));
$result3 = count($mail->getQueueTest('wait'));
$result4 = $mail->getQueueTest('wait');
r($result1) && p() && e('7'); //按照收件人分组获取邮件列表个数
r($result2) && p() && e('7'); //按照收件人分组获取发送失败的邮件数量
r($result3) && p() && e('7'); //按照收件人分组获取待发送的邮件数量
r($result4) && p('0:data') && e('测试发信内容'); //按照收件人分组获取第一条待发送的邮件内容
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->getQueueById();
cid=1
pid=1
*/
$mail = new mailTest();
$queueID = array(1,10,0);
r($mail->getQueueByIdTest($queueID[0])) && p('subject') && e('主题1'); //获取id为1的邮件主题
r($mail->getQueueByIdTest($queueID[1])) && p('toList') && e('top10'); //获取id为10的收件人
r($mail->getQueueByIdTest($queueID[2])) && p() && e('0'); //获取id不存在的邮件信息
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->getSubject();
cid=1
pid=1
*/
$mail = new mailTest();
$result1 = $mail->getSubjectTest('testtask', 1, '123', 'closed');
$result2 = $mail->getSubjectTest('doc', 1, 'test', 'created');
$result3 = $mail->getSubjectTest('story', 1, 'test', 'created');
$result4 = $mail->getSubjectTest('task', 1, 'test', 'created');
$result5 = $mail->getSubjectTest('bug', 1, 'test', 'created');
r($result1) && p() && e('admin关闭了测试单 #1:测试单1'); //获取关闭测试单时邮件主题
r($result2) && p() && e('admin创建了文档 #1:文档标题901'); //获取创建文档时的邮件主题
r($result3) && p() && e('STORY #1 test - 正常产品1'); //获取操作需求时的邮件主题
r($result4) && p() && e('TASK #1 test - 迭代1'); //获取操作任务时的邮件主题
r($result5) && p() && e('BUG #1 test - 正常产品1'); //获取操作BUG时的邮件主题
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->mailExist();
cid=1
pid=1
*/
$mail = new mailTest();
r($mail->mailExistTest()) && p('email') && e('10001000@qq.com'); //检查系统是否至少存在邮件
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->mergeMails();
cid=1
pid=1
*/
$mail = new mailTest();
r($mail->mergeMailsTest('admin')) && p('id') && e('6,1'); //获取邮件合并后的id
r($mail->mergeMailsTest('admin')) && p('subject') && e('主题6|主题1'); //获取邮件合并后的主题
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->send();
cid=1
pid=1
*/
$mail = new mailTest();
/* If you don't set SMTP Please input you SMTP setting. */
$mailConfig = new stdclass();
$mailConfig->smtp = new stdclass();
$mailConfig->turnon = 1;
$mailConfig->mta = 'smtp';
$mailConfig->async = 0;
$mailConfig->fromAddress = '';
$mailConfig->fromName = '';
$mailConfig->domain = '';
$mailConfig->smtp->host = '';
$mailConfig->smtp->port = '';
$mailConfig->smtp->auth = '1';
$mailConfig->smtp->username = '';
$mailConfig->smtp->password = '';
$mailConfig->smtp->secure = '';
$mailConfig->smtp->debug = 1;
$mailConfig->smtp->charset = 'utf-8';
global $tester;
if(!isset($tester->config->mail->turnon)) $tester->loadModel('setting')->setItems('system.mail', $mailConfig);
$result1 = $mail->sendTest('admin','test','test','',true);
r($result1) && p() && e('0');
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->sendmail();
cid=1
pid=1
*/
$mail = new mailTest();
r($mail->sendmailTest(48,48)) && p() && e('0'); //测试为dev4用户发送邮件通知
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->setCC();
cid=1
pid=1
*/
$mail = new mailTest();
r($mail->setBodyTest('test body')) && p('Body') && e('test body'); //测试设置内容
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->setCC();
cid=1
pid=1
*/
$mail = new mailTest();
r($mail->setMTATest()) && p('Host') && e('localhost'); //获取MTA主机
r($mail->setMTATest()) && p('SMTPDebug') && e('0'); //获取MTAdebug设置
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/mail.class.php';
su('admin');
/**
title=测试 mailModel->setCC();
cid=1
pid=1
*/
$mail = new mailTest();
r($mail->setSubjectTest('test subject')) && p('Subject') && e('test subject'); //测试设置主题
+3
View File
@@ -153,6 +153,9 @@ switch($argv[1])
case 'tutorial':
ztfRun('model/tutorial');
break;
case 'mail':
ztfRun('model/mail');
break;
case 'tree':
ztfRun('model/tree');
break;