Merge branch 'sprint/189_mayue_task#53652'

This commit is contained in:
孙广明
2022-05-12 13:02:03 +08:00
257 changed files with 8492 additions and 1155 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
+1 -1
View File
@@ -20,7 +20,7 @@ class feedbackEntry extends Entry
*/
public function get($feedbackID)
{
$control = $this->loadController('feedback', 'adminiView');
$control = $this->loadController('feedback', 'adminView');
$control->adminView($feedbackID);
$data = $this->getData();
+2
View File
@@ -378,3 +378,5 @@ $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visi
$config->programPriv = new stdclass();
$config->programPriv->scrum = array('story', 'projectstory', 'projectrelease', 'project', 'execution', 'build', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask');
$config->programPriv->waterfall = array_merge($config->programPriv->scrum, array('task', 'workestimation', 'durationestimation', 'budget', 'programplan', 'review', 'reviewissue', 'weekly', 'cm', 'milestone', 'design', 'issue', 'risk', 'opportunity', 'measrecord', 'auditplan', 'trainplan', 'gapanalysis', 'pssp', 'researchplan', 'researchreport'));
$config->waterfallModules = array('workestimation', 'durationestimation', 'budget', 'programplan', 'review', 'reviewissue', 'weekly', 'cm', 'milestone', 'design', 'opportunity', 'auditplan', 'trainplan', 'gapanalysis', 'pssp', 'researchplan', 'researchreport');
+324
View File
@@ -89,3 +89,327 @@ 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';
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', '', '', ''),
('lang', '反馈关闭原因', 'feedbackclosedReason', '1', 'feedbackclosedReason', '', '', '');
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';
UPDATE `zt_workflowaction` SET `type`='batch' WHERE `module`IN ('feedback','caselib','testsuite','testtask','testcase') AND `action` IN ('admin','browse','batchedit','batchcreate','import','showimport');
UPDATE `zt_workflowaction` SET `position`='browse' WHERE `module`IN ('feedback','caselib','testsuite','testtask','testcase') AND `action` IN ('batchedit','batchcreate','import','showimport');
+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);
+108 -57
View File
@@ -42,8 +42,6 @@ class control extends baseControl
/* Code for task #9224. Set requiredFields for workflow. */
if($this->dbh and (defined('IN_USE') or (defined('RUN_MODE') and RUN_MODE == 'api')))
{
$this->checkRequireFlowField();
if(isset($this->config->{$this->moduleName}) and strpos($this->methodName, 'export') !== false)
{
if(isset($this->config->{$this->moduleName}->exportFields) or isset($this->config->{$this->moduleName}->list->exportFields))
@@ -82,31 +80,49 @@ class control extends baseControl
}
/* If workflow is created by a normal user, set priv. */
if(isset($this->app->user) and !$this->app->user->admin)
{
$actions = $this->dao->select('module, action')->from(TABLE_WORKFLOWACTION)->where('createdBy')->eq($this->app->user->account)->andWhere('buildin')->eq('0')->fetchGroup('module');
$labels = $this->dao->select('module, code')->from(TABLE_WORKFLOWLABEL)->where('createdBy')->eq($this->app->user->account)->andWhere('buildin')->eq('0')->fetchGroup('module');
if(!empty($actions))
{
foreach($actions as $module => $actionObj)
{
foreach($actionObj as $action) $this->app->user->rights['rights'][$module][$action->action] = 1;
}
}
if(isset($this->app->user) and !$this->app->user->admin) $this->setDefaultPrivByWorkflow();
}
}
if(!empty($labels))
/**
* Det default priv by workflow.
*
* @access public
* @return bool
*/
public function setDefaultPrivByWorkflow()
{
$actionList = $this->dao->select('module, action')->from(TABLE_WORKFLOWACTION)
->where('createdBy')->eq($this->app->user->account)
->andWhere('buildin')->eq('0')
->fetchGroup('module');
if($actionList)
{
foreach($actionList as $module => $actions)
{
foreach($actions as $action) $this->app->user->rights['rights'][$module][$action->action] = 1;
}
}
$labelList = $this->dao->select('module, code')->from(TABLE_WORKFLOWLABEL)
->where('createdBy')->eq($this->app->user->account)
->andWhere('buildin')->eq('0')
->fetchGroup('module');
if($labelList)
{
foreach($labelList as $module => $labels)
{
foreach($labels as $label)
{
foreach($labels as $module => $codeObj)
{
foreach($codeObj as $code)
{
$code = str_replace('browse', '', $code->code);
$this->app->user->rights['rights'][$module][$code] = 1;
}
}
$code = str_replace('browse', '', $label->code);
$this->app->user->rights['rights'][$module][$code] = 1;
}
}
}
return true;
}
/**
@@ -181,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";
@@ -201,6 +218,10 @@ class control extends baseControl
{
$viewFile = $xuanExtViewFile;
}
else if(file_exists($saasExtViewFile))
{
$viewFile = $saasExtViewFile;
}
else if(file_exists($commonExtViewFile))
{
$viewFile = $commonExtViewFile;
@@ -219,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);
@@ -291,6 +313,22 @@ class control extends baseControl
chdir($currentPWD);
}
/**
* Build operate menu of a method.
*
* @param object $object product|project|productplan|release|build|story|task|bug|testtask|testcase|testsuite
* @param string $displayOn view|browse
* @access public
* @return string
*/
public function buildOperateMenu($object, $type = 'view')
{
if(!isset($this->config->bizVersion)) return false;
$moduleName = $this->moduleName;
return $this->$moduleName->buildOperateMenu($object, $type);
}
/**
* Execute hooks of a method.
*
@@ -307,19 +345,18 @@ class control extends baseControl
}
/**
* Build operate menu of a method.
* Set workflow export fields
*
* @param object $object product|project|productplan|release|build|story|task|bug|testtask|testcase|testsuite
* @param string $displayOn view|browse
* @param array $fields
* @access public
* @return void
* @return array
*/
public function buildOperateMenu($object, $displayOn = 'view')
public function getFlowExportFields()
{
if(!isset($this->config->bizVersion)) return false;
if(!isset($this->config->bizVersion)) return array();
$flow = $this->loadModel('workflow')->getByModule($this->moduleName);
return $this->loadModel('flow')->buildOperateMenu($flow, $object, $displayOn);
$moduleName = $this->moduleName;
return $this->$moduleName->getFlowExportFields();
}
/**
@@ -332,14 +369,27 @@ class control extends baseControl
* position=left|right The position which the fields displayed in a page.
* inForm=0|1 The fields displayed in a form or not. The default is 1.
* inCell=0|1 The fields displayed in a div with class cell or not. The default is 0.
* @param bool $print
* @access public
* @return void
*/
public function printExtendFields($object, $type, $extras = '')
public function printExtendFields($object, $type, $extras = '', $print = false)
{
if(!isset($this->config->bizVersion)) return false;
echo $this->loadModel('flow')->printFields($this->moduleName, $this->methodName, $object, $type, $extras);
$moduleName = $this->app->getModuleName();
$methodName = $this->app->getMethodName();
$fields = $this->loadModel('flow')->printFields($moduleName, $methodName, $object, $type, $extras);
if(!$print) return $fields;
$jsRoot = $this->config->webRoot . "js/";
$picker = file_get_contents($this->app->getBasePath() . 'app/sys/common/view/picker.html.php');
$picker = substr($picker, strpos($picker, '<style>'));
js::import($jsRoot . 'picker/min.js');
css::import($jsRoot . 'picker/min.css');
echo $picker . $fields;
}
/**
@@ -357,6 +407,31 @@ class control extends baseControl
return $this->$moduleName->processStatus($module, $record);
}
/**
* Print view file.
*
* @param string $viewFile
* @access public
* @return bool|string
*/
public function printViewFile($viewFile)
{
if(!file_exists($viewFile)) return false;
$currentPWD = getcwd();
chdir(dirname($viewFile));
extract((array)$this->view);
ob_start();
include $viewFile;
$output = ob_get_contents();
ob_end_clean();
chdir($currentPWD);
return $output;
}
/**
* Check require with flow field when post data.
*
@@ -464,28 +539,4 @@ class control extends baseControl
if($message) $this->send(array('result' => 'fail', 'message' => $message));
}
/**
* Print view file.
*
* @param string $viewFile
* @access public
* @return void
*/
public function printViewFile($viewFile)
{
if(!file_exists($viewFile)) return false;
$currentPWD = getcwd();
chdir(dirname($viewFile));
extract((array)$this->view);
ob_start();
include $viewFile;
$output = ob_get_contents();
ob_end_clean();
chdir($currentPWD);
return $output;
}
}
+188 -7
View File
@@ -53,6 +53,136 @@ class model extends baseModel
return true;
}
/**
* Build menu of a module.
*
* @param string $moduleName
* @param string $methodName
* @param string $params
* @param object $data
* @param string $type
* @param string $icon
* @param string $target
* @param string $misc
* @param bool $onlyBody
* @access public
* @return string
*/
public function buildMenu($moduleName, $methodName, $params, $data, $type = 'view', $icon = '', $target = '', $class = '', $onlyBody = false, $misc = '' , $title = '')
{
if(strpos($moduleName, '.') !== false) list($appName, $moduleName) = explode('.', $moduleName);
if(strpos($methodName, '_') !== false && strpos($methodName, '_') > 0) list($module, $method) = explode('_', $methodName);
if(empty($module)) $module = $moduleName;
if(empty($method)) $method = $methodName;
if(!common::hasPriv($module, $method)) return '';
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')
->beginIF(!empty($this->config->vision))->andWhere('vision')->eq($this->config->vision)->fi()
->fetchAll('action');
}
}
$enabled = true;
if(isset($actions[$methodName]))
{
$action = $actions[$methodName];
if($action->extensionType == 'override') return $this->loadModel('flow')->buildActionMenu($moduleName, $action, $data, $type);
$conditions = json_decode($action->conditions);
if($action->extensionType == 'extend' && $conditions)
{
$enabled = $this->loadModel('flow')->checkConditions($conditions, $data);
$label = $action->name;
}
else
{
if(method_exists($this, 'isClickable')) $enabled = $this->isClickable($data, $method, $module);
}
}
else
{
if(method_exists($this, 'isClickable')) $enabled = $this->isClickable($data, $method, $module);
}
$html = '';
$type = $type == 'browse' ? 'list' : 'button';
$html = common::buildIconButton($module, $method, $params, $data, $type, $icon, $target, $class, $onlyBody, $misc, $title, '', $enabled);
return $html;
}
/**
* Build menu of actions created by workflow action.
*
* @param string $module
* @param int $data
* @param string $type browse | view
* @param string $show direct | dropdownlist
* @access public
* @return string
*/
public function buildFlowMenu($module, $data, $type = 'browse', $show = '')
{
if(!isset($this->config->bizVersion)) return '';
$moduleName = $module;
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')
->beginIF(!empty($this->config->vision))->andWhere('vision')->eq($this->config->vision)->fi()
->orderBy('order_asc')
->fetchAll();
}
$menu = '';
if($show)
{
foreach($actions as $action)
{
if(strpos($action->position, $type) === false || $action->show != $show) continue;
$menu .= $this->loadModel('flow')->buildActionMenu($moduleName, $action, $data, $type);
}
}
else
{
$dropdownMenu = '';
foreach($actions as $action)
{
if(strpos($action->position, $type) === false) continue;
if($type == 'view' || $action->show == 'direct') $menu .= $this->loadModel('flow')->buildActionMenu($moduleName, $action, $data, $type);
if($type == 'browse' && $action->show == 'dropdownlist') $dropdownMenu .= $this->loadModel('flow')->buildActionMenu($moduleName, $action, $data, $type);
}
if($type == 'browse' && $dropdownMenu)
{
$menu .= "<div class='dropdown'><a href='javascript:;' data-toggle='dropdown'>{$this->lang->more}<span class='caret'> </span></a>";
$menu .= "<ul class='dropdown-menu pull-right'>{$dropdownMenu}</ul></div>";
}
}
return $menu;
}
/**
* Process status of an object according to its subStatus.
*
@@ -68,6 +198,48 @@ class model extends baseModel
return $this->loadModel('workflowfield')->processSubStatus($module, $record);
}
/**
* Process workflow export data.
*
* @param object $data
* @access public
* @return object
*/
public function processExportData($data)
{
if(!isset($this->config->bizVersion)) return $data;
return $this->loadModel('workflowfield')->processExportData($data);
}
/**
* Process workflow export options.
*
* @param object $data
* @access public
* @return object
*/
public function processExportOptions($data)
{
if(!isset($this->config->bizVersion)) return $data;
return $this->loadModel('workflowfield')->processExportOptions($data);
}
/**
* Process workflow import data.
*
* @param object $data
* @access public
* @return object
*/
public function processImportData($data)
{
if(!isset($this->config->bizVersion)) return $data;
return $this->loadModel('workflowfield')->processImportData($data);
}
/**
* Get flow extend fields.
*
@@ -82,18 +254,16 @@ class model extends baseModel
}
/**
* Check flow rule.
* Set workflow export fields.
*
* @param object $field
* @param string $value
* @access public
* @return bool|string
* @return string
*/
public function checkFlowRule($field, $value)
public function getFlowExportFields()
{
if(!isset($this->config->bizVersion)) return false;
if(!isset($this->config->bizVersion)) return array();
return $this->loadModel('flow')->checkRule($field, $value);
return $this->loadModel('workflowfield')->getExportFields($this->app->getModuleName());
}
/**
@@ -113,6 +283,17 @@ class model extends baseModel
$action = $this->loadModel('workflowaction')->getByModuleAndAction($moduleName, $methodName);
if(empty($action) or $action->extensionType == 'none') return false;
$this->loadModel('file');
if($this->post->uid) $this->file->updateObjectID($this->post->uid, $objectID, $moduleName);
$fields = $this->workflowaction->getFields($moduleName, $action->action, false);
foreach($fields as $field)
{
if($field->control == 'file' && $field->show && !$field->readonly)
{
$this->file->saveUpload($moduleName, $objectID, $field->field, $field->field, $field->field);
}
}
$flow = $this->loadModel('workflow')->getByModule($moduleName);
if($flow && $action) $this->loadModel('workflowhook')->execute($flow, $action, $objectID);
}
+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');
+143
View File
@@ -31,6 +31,149 @@ class dao extends baseDAO
$skipFields .= ',uid';
return parent::data($data, $skipFields);
}
/**
* check workflow field rule.
*
* @access public
* @return object the dao object self.
*/
public function checkflow()
{
global $app, $config;
if(!isset($config->bizVersion)) return $this;
$module = $app->getmodulename();
$method = $app->getmethodname();
$flowaction = $this->dbh->query("select * from " . TABLE_WORKFLOWACTION . " where `module` = '{$module}' and `action` = '{$method}' and `buildin` = '1' and `extensiontype` = 'extend'")->fetch(PDO::FETCH_OBJ);
if(!$flowaction) return $this;
$flowfields = $this->dbh->query("select t2.name,t2.rules,t2.control,t2.field,t1.layoutrules from " . TABLE_WORKFLOWLAYOUT . " as t1 left join " . TABLE_WORKFLOWFIELD . " as t2 on t1.module = t2.module and t1.field = t2.field where t1.module = '{$module}' and t1.action = '{$method}' and t1.readonly = '0'")->fetchall();
if(!$flowfields) return $this;
$rules = array();
$rawrules = $this->dbh->query("select * from " . TABLE_WORKFLOWRULE)->fetchall();
foreach($rawrules as $rule) $rules[$rule->id] = $rule;
foreach($flowfields as $key => $field)
{
if(!$field)
{
unset($flowfields[$key]);
continue;
}
$ruleids = explode(',', trim($field->rules, ',') . ',' . trim($field->layoutrules, ','));
$ruleids = array_unique($ruleids);
$fieldrules = array();
foreach($ruleids as $ruleid)
{
if(!$ruleid || !isset($rules[$ruleid])) continue;
$fieldrules[] = $rules[$ruleid];
}
$field->ruledata = $fieldrules;
$field->rules = join(',', $ruleids);
}
return $this->checkextend($flowfields);
}
/**
* 检查工作流扩展字段
* check workflow extend field
*
* @param array $fields
* @access public
* @return object the dao object self
*/
public function checkextend($fields)
{
global $lang;
if(!$fields) return $this;
foreach($fields as $field)
{
/* if the field don't have rule, don't check it. */
if(empty($field->rules)) continue;
if($field->control == 'file')
{
foreach($field->ruledata as $rule)
{
if(empty($rule)) continue;
if($rule->type != 'system' || $rule->rule != 'notempty') continue;
$files = !empty($_files[$field->field]) ? $_files[$field->field] : '';
if(empty($files)) dao::$errors[$field->field][] = sprintf($lang->error->notempty, $field->name);
break;
}
continue;
}
if(!isset($this->sqlobj->data->{$field->field})) $this->sqlobj->data->{$field->field} = false;
/* check rules of fields. */
foreach($field->ruledata as $rule)
{
if(empty($rule)) continue;
if($rule->type == 'system')
{
if($rule->rule == 'unique')
{
if(empty($this->sqlobj->data->{$field->field})) continue;
$condition = isset($this->sqlobj->data->id) ? '`id` != ' . $this->sqlobj->data->id : '';
$this->check($field->field, $rule->rule, $condition);
}
else
{
if(is_numeric($this->sqlobj->data->{$field->field}) && $this->sqlobj->data->{$field->field} == 0 && $rule->rule == 'notempty' && !in_array($field->control, array('select', 'multi-select'))) continue;
$this->check($field->field, $rule->rule);
}
}
elseif($rule->type == 'regex')
{
$this->check($field->field, 'reg', $rule->rule);
}
}
}
return $this;
}
/**
* Get param real value
*
* @param int $param
* @access public
* @return string
*/
public function getParamRealValue($param)
{
if(empty($this->deptManager))
{
$dept = zget($this->app->user, 'dept', '');
$manager = $this->dbh->query("SELECT moderators FROM " . TABLE_CATEGORY . " WHERE `type` = 'dept' AND `id` = '{$dept}'")->fetch(PDO::FETCH_OBJ);
$this->deptManager = $manager ? trim($manager->moderators, ',') : '';
}
switch((string)$param)
{
case 'today' : return date('Y-m-d');
case 'now' :
case 'currentTime' : return date('Y-m-d H:i:s');
case 'actor' :
case 'currentUser' : return $this->app->user->account;
case 'currentDept' : return $this->app->user->dept ? $this->app->user->dept : $param;
case 'deptManager' : return $this->deptManager ? $this->deptManager : $param;
default : return $param;
}
}
}
/**
+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.
*
+47 -3
View File
@@ -12,9 +12,53 @@
?>
<?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'");?>
<?php if($type == 'hidden') echo html::a(inLink('trash', "browseType=all&type=all"), $lang->goback, '', "class='btn'");?>
<?php if($type == 'all') echo html::a(inLink('trash', "browseType=all&type=hidden"), "<i class='icon-eye-close'></i> " . $lang->action->dynamic->hidden, '', "class='btn btn-danger'");?>
</div>
</div>
@@ -31,7 +75,7 @@
</div>
<div class='main-table' data-ride='table'>
<table class='table has-sort-head'>
<?php $vars = "type=$type&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; ?>
<?php $vars = "browseType=$currentObjectType&type=$type&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; ?>
<thead>
<tr class='colhead'>
<th class='c-object-type'><?php common::printOrderLink('objectType', $orderBy, $vars, $lang->action->objectType);?></th>
+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' => '');
+122 -59
View File
@@ -94,6 +94,7 @@ class bugModel extends model
->autoCheck()
->checkIF($bug->notifyEmail, 'notifyEmail', 'email')
->batchCheck($this->config->bug->create->requiredFields, 'notempty')
->checkFlow()
->exec();
if(!dao::isError())
@@ -225,8 +226,6 @@ class bugModel extends model
if(is_array($bug->{$extendField->field})) $bug->{$extendField->field} = join(',', $bug->{$extendField->field});
$bug->{$extendField->field} = htmlSpecialString($bug->{$extendField->field});
$message = $this->checkFlowRule($extendField, $bug->{$extendField->field});
if($message) return print(js::alert($message));
}
/* Required field check. */
@@ -282,6 +281,7 @@ class bugModel extends model
$this->dao->insert(TABLE_BUG)->data($bug)
->autoCheck()
->batchCheck($this->config->bug->create->requiredFields, 'notempty')
->checkFlow()
->exec();
if(dao::isError()) return false;
@@ -675,6 +675,7 @@ class bugModel extends model
}
$now = helper::now();
$bug = fixer::input('post')
->add('id', $bugID)
->cleanInt('product,module,severity,project,execution,story,task,branch')
->stripTags($this->config->bug->editor->edit['id'], $this->config->allowedTags)
->setDefault('product,module,execution,story,task,duplicateBug,branch', 0)
@@ -717,6 +718,7 @@ class bugModel extends model
->checkIF($bug->notifyEmail, 'notifyEmail', 'email')
->checkIF($bug->resolution == 'duplicate', 'duplicateBug', 'notempty')
->checkIF($bug->resolution == 'fixed', 'resolvedBuild','notempty')
->checkFlow()
->where('id')->eq((int)$bugID)
->exec();
@@ -788,6 +790,7 @@ class bugModel extends model
$oldBug = $oldBugs[$bugID];
$bug = new stdclass();
$bug->id = $bugID;
$bug->lastEditedBy = $this->app->user->account;
$bug->lastEditedDate = $now;
$bug->type = $data->types[$bugID];
@@ -827,8 +830,6 @@ class bugModel extends model
if(is_array($bug->{$extendField->field})) $bug->{$extendField->field} = join(',', $bug->{$extendField->field});
$bug->{$extendField->field} = htmlSpecialString($bug->{$extendField->field});
$message = $this->checkFlowRule($extendField, $bug->{$extendField->field});
if($message) return print(js::alert($message));
}
$bugs[$bugID] = $bug;
@@ -845,6 +846,7 @@ class bugModel extends model
->batchCheck($this->config->bug->edit->requiredFields, 'notempty')
->checkIF($bug->resolvedBy, 'resolution', 'notempty')
->checkIF($bug->resolution == 'duplicate', 'duplicateBug', 'notempty')
->checkFlow()
->where('id')->eq((int)$bugID)
->exec();
@@ -930,6 +932,7 @@ class bugModel extends model
$now = helper::now();
$oldBug = $this->getById($bugID);
$bug = fixer::input('post')
->add('id', $bugID)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
->setDefault('assignedDate', $now)
@@ -940,6 +943,7 @@ class bugModel extends model
$this->dao->update(TABLE_BUG)
->data($bug)
->autoCheck()
->checkFlow()
->where('id')->eq($bugID)->exec();
if(!dao::isError()) return common::createChanges($oldBug, $bug);
@@ -962,6 +966,7 @@ class bugModel extends model
$oldBug = $this->getById($bugID);
$bug = fixer::input('post')
->add('id', $bugID)
->setDefault('confirmed', 1)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
@@ -970,7 +975,7 @@ class bugModel extends model
->join('mailto', ',')
->get();
$this->dao->update(TABLE_BUG)->data($bug)->where('id')->eq($bugID)->exec();
$this->dao->update(TABLE_BUG)->data($bug)->autoCheck()->checkFlow()->where('id')->eq($bugID)->exec();
if(!dao::isError())
{
@@ -1027,6 +1032,7 @@ class bugModel extends model
$now = helper::now();
$oldBug = $this->getById($bugID);
$bug = fixer::input('post')
->add('id', $bugID)
->add('status', 'resolved')
->add('confirmed', 1)
->setDefault('lastEditedBy', $this->app->user->account)
@@ -1100,6 +1106,7 @@ class bugModel extends model
->batchCheck($this->config->bug->resolve->requiredFields, 'notempty')
->checkIF($bug->resolution == 'duplicate', 'duplicateBug', 'notempty')
->checkIF($bug->resolution == 'fixed', 'resolvedBuild','notempty')
->checkFlow()
->where('id')->eq((int)$bugID)
->exec();
@@ -1310,6 +1317,7 @@ class bugModel extends model
->setDefault('lastEditedDate', $now)
->setDefault('activatedDate', $now)
->setDefault('activatedCount', (int)$oldBug->activatedCount)
->add('id', $bugID)
->add('resolution', '')
->add('status', 'active')
->add('resolvedDate', '0000-00-00')
@@ -1324,7 +1332,7 @@ class bugModel extends model
->remove('comment,files,labels')
->get();
$this->dao->update(TABLE_BUG)->data($bug)->autoCheck()->where('id')->eq((int)$bugID)->exec();
$this->dao->update(TABLE_BUG)->data($bug)->autoCheck()->checkFlow()->where('id')->eq((int)$bugID)->exec();
$this->dao->update(TABLE_BUG)->set('activatedCount = activatedCount + 1')->where('id')->eq((int)$bugID)->exec();
if($solveBuild)
@@ -1361,6 +1369,7 @@ class bugModel extends model
$now = helper::now();
$oldBug = $this->getById($bugID);
$bug = fixer::input('post')
->add('id', $bugID)
->add('assignedTo', 'closed')
->add('status', 'closed')
->add('confirmed', 1)
@@ -1372,7 +1381,7 @@ class bugModel extends model
->remove('comment')
->get();
$this->dao->update(TABLE_BUG)->data($bug)->autoCheck()->where('id')->eq((int)$bugID)->exec();
$this->dao->update(TABLE_BUG)->data($bug)->autoCheck()->checkFlow()->where('id')->eq((int)$bugID)->exec();
if($oldBug->execution)
{
$this->loadModel('kanban');
@@ -1670,12 +1679,7 @@ class bugModel extends model
}
}
$allProduct = "`product` = 'all'";
$bugQuery = $this->session->projectBugQuery;
if(strpos($this->session->projectBugQuery, $allProduct) !== false)
{
$bugQuery = str_replace($allProduct, '1', $this->session->projectBugQuery);
}
$bugQuery = $this->getBugQuery($this->session->projectBugQuery);
$bugs = $this->dao->select('*')->from(TABLE_BUG)
->where($bugQuery)
@@ -1737,12 +1741,7 @@ class bugModel extends model
}
}
$allProduct = "`product` = 'all'";
$bugQuery = $this->session->executionBugQuery;
if(strpos($this->session->executionBugQuery, $allProduct) !== false)
{
$bugQuery = str_replace($allProduct, '1', $this->session->executionBugQuery);
}
$bugQuery = $this->getBugQuery($this->session->executionBugQuery);
$bugs = $this->dao->select('*')->from(TABLE_BUG)
->where($bugQuery)
@@ -2694,34 +2693,15 @@ class bugModel extends model
if($this->session->bugQuery == false) $this->session->set('bugQuery', ' 1 = 1');
}
$allProduct = "`product` = 'all'";
$bugQuery = $this->session->bugQuery;
$bugQuery = $this->getBugQuery($this->session->bugQuery);
if(is_array($productIDList)) $productIDList = implode(',', $productIDList);
if(strpos($bugQuery, '`product` =') === false) $bugQuery .= ' AND `product` in (' . $productIDList . ')';
if(strpos($bugQuery, $allProduct) !== false)
{
$products = $this->app->user->view->products;
$bugQuery = str_replace($allProduct, '1', $bugQuery);
$bugQuery = $bugQuery . ' AND `product` ' . helper::dbIN($products);
}
$allBranch = "`branch` = 'all'";
if($branch !== 'all' and strpos($bugQuery, '`branch` =') === false) $bugQuery .= " AND `branch` in('0','$branch')";
if(strpos($bugQuery, $allBranch) !== false) $bugQuery = str_replace($allBranch, '1', $bugQuery);
$allProject = "`project` = 'all'";
if(strpos($bugQuery, $allProject) !== false)
{
$projectIDList = $this->getAllProjectIds();
if(is_array($projectIDList)) $projectIDList = implode(',', $projectIDList);
$bugQuery = str_replace($allProject, '1', $bugQuery);
$bugQuery = $bugQuery . ' AND `project` in (' . $projectIDList . ')';
}
/* 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);
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where($bugQuery)
->beginIF(!$this->app->user->admin)->andWhere('execution')->in('0,' . $this->app->user->view->sprints)->fi()
->beginIF($excludeBugs)->andWhere('id')->notIN($excludeBugs)->fi()
@@ -2738,6 +2718,58 @@ class bugModel extends model
return $bugs;
}
/**
* Get bug query.
*
* @param string $bugQuery
* @access public
* @return string
*/
public function getBugQuery($bugQuery)
{
$allProduct = "`product` = 'all'";
if(strpos($bugQuery, $allProduct) !== false)
{
$products = $this->app->user->view->products;
$bugQuery = str_replace($allProduct, '1', $bugQuery);
$bugQuery = $bugQuery . ' AND `product` ' . helper::dbIN($products);
}
$allProject = "`project` = 'all'";
if(strpos($bugQuery, $allProject) !== false)
{
$projectIDList = $this->getAllProjectIds();
if(is_array($projectIDList)) $projectIDList = implode(',', $projectIDList);
$bugQuery = str_replace($allProject, '1', $bugQuery);
$bugQuery = $bugQuery . ' AND `project` in (' . $projectIDList . ')';
}
/* 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';
}
return $bugQuery;
}
/**
* Form customed bugs.
*
@@ -2784,18 +2816,19 @@ class bugModel extends model
*
* @param string $bug
* @param string $action
* @param string $module
* @access public
* @return void
*/
public static function isClickable($object, $action)
public static function isClickable($object, $action, $module = 'bug')
{
$action = strtolower($action);
if($action == 'confirmbug') return $object->status == 'active' and $object->confirmed == 0;
if($action == 'resolve') return $object->status == 'active';
if($action == 'close') return $object->status == 'resolved';
if($action == 'activate') return $object->status != 'active';
if($action == 'tostory') return $object->status == 'active';
if($module == 'bug' && $action == 'confirmbug') return $object->status == 'active' and $object->confirmed == 0;
if($module == 'bug' && $action == 'resolve') return $object->status == 'active';
if($module == 'bug' && $action == 'close') return $object->status == 'resolved';
if($module == 'bug' && $action == 'activate') return $object->status != 'active';
if($module == 'bug' && $action == 'tostory') return $object->status == 'active';
return true;
}
@@ -3105,19 +3138,7 @@ class bugModel extends model
echo substr($bug->lastEditedDate, 5, 11);
break;
case 'actions':
$params = "bugID=$bug->id";
if($canBeChanged)
{
common::printIcon('bug', 'confirmBug', $params, $bug, 'list', 'ok', '', 'iframe', true);
common::printIcon('bug', 'resolve', $params, $bug, 'list', 'checked', '', 'iframe', true);
common::printIcon('bug', 'close', $params, $bug, 'list', '', '', 'iframe', true);
common::printIcon('bug', 'edit', $params, $bug, 'list');
common::printIcon('bug', 'create', "product=$bug->product&branch=$bug->branch&extra=$params", $bug, 'list', 'copy');
}
else
{
common::printIcon('bug', 'close', $params, $bug, 'list', '', '', 'iframe', true);
}
echo $this->buildOperateMenu($bug, 'browse');
break;
}
echo '</td>';
@@ -3233,4 +3254,46 @@ class bugModel extends model
->andWhere('deleted')->eq(0)
->fetchPairs('id');
}
/**
* Build bug menu.
*
* @param object $bug
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($bug, $type = 'view')
{
$menu = '';
$params = "bugID=$bug->id";
$extraParams = "extras=bugID=$bug->id";
if($this->app->tab == 'project') $extraParams .= ",projectID={$bug->project}";
if($this->app->tab == 'execution') $extraParams .= ",executionID={$bug->execution}";
$copyParams = "productID=$bug->product&branch=$bug->branch&$extraParams";
$convertParams = "productID=$bug->product&branch=$bug->branch&moduleID=0&from=bug&bugID=$bug->id";
$toStoryParams = "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id";
$menu .= $this->buildMenu('bug', 'confirmBug', $params, $bug, $type, 'ok', '', "iframe", true);
if($type == 'view') $menu .= $this->buildMenu('bug', 'assignTo', $params, $bug, $type, '', '', "iframe", true);
$menu .= $this->buildMenu('bug', 'resolve', $params, $bug, $type, 'checked', '', "iframe", true);
$menu .= $this->buildMenu('bug', 'close', $params, $bug, $type, '', '', "text-danger iframe showinonlybody", true);
if($type == 'view') $menu .= $this->buildMenu('bug', 'activate', $params, $bug, $type, '', '', "text-success iframe showinonlybody", true);
if($type == 'view' && $this->app->tab != 'product')
{
$menu .= $this->buildMenu('bug', 'toStory', $toStoryParams, $bug, $type, $this->lang->icons['story'], '', '', '', "data-app='product'", $this->lang->bug->toStory);
$menu .= $this->buildMenu('bug', 'createCase', $convertParams, $bug, $type, 'sitemap');
}
if($type == 'view')
{
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('bug', $bug, $type, 'direct');
$menu .= "<div class='divider'></div>";
}
$menu .= $this->buildMenu('bug', 'edit', $params, $bug, $type);
if($this->app->tab != 'product') $menu .= $this->buildMenu('bug', 'create', $copyParams, $bug, $type, 'copy');
if($type == 'view') $menu .= $this->buildMenu('bug', 'delete', $params, $bug, $type, 'trash', 'hiddenwin', "showinonlybody");
return $menu;
}
}
+1 -23
View File
@@ -96,29 +96,7 @@
<?php common::printBack($browseLink);?>
<?php if(!$bug->deleted):?>
<div class='divider'></div>
<?php
common::printIcon('bug', 'confirmBug', $params, $bug, 'button', 'ok', '', 'iframe', true);
common::printIcon('bug', 'assignTo', $params, $bug, 'button', '', '', 'iframe', true);
common::printIcon('bug', 'resolve', $params, $bug, 'button', 'checked', '', 'iframe showinonlybody', true);
common::printIcon('bug', 'close', $params, $bug, 'button', '', '', 'text-danger iframe showinonlybody', true);
common::printIcon('bug', 'activate', $params, $bug, 'button', '', '', 'text-success iframe showinonlybody', true);
if($this->app->tab != 'product')
{
common::printIcon('bug', 'toStory', "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id", $bug, 'button', $lang->icons['story'], '', '', '', "data-app='product'", $lang->bug->toStory);
common::printIcon('bug', 'createCase', $convertParams, $bug, 'button', 'sitemap');
}
echo $this->buildOperateMenu($bug, 'view');
echo "<div class='divider'></div>";
common::printIcon('bug', 'edit', $params, $bug);
if($this->app->tab != 'product')
{
common::printIcon('bug', 'create', $copyParams, $bug, 'button', 'copy');
}
common::printIcon('bug', 'delete', $params, $bug, 'button', 'trash', 'hiddenwin', 'showinonlybody');
?>
<?php echo $this->bug->buildOperateMenu($bug, 'view');?>
<?php endif;?>
</div>
</div>
+26 -1
View File
@@ -300,6 +300,7 @@ class buildModel extends model
->autoCheck()
->batchCheck($this->config->build->create->requiredFields, 'notempty')
->check('name', 'unique', "product = {$build->product} AND branch = {$build->branch} AND deleted = '0'")
->checkFlow()
->exec();
if(!dao::isError())
@@ -324,6 +325,7 @@ class buildModel extends model
$buildID = (int)$buildID;
$oldBuild = $this->dao->select('*')->from(TABLE_BUILD)->where('id')->eq($buildID)->fetch();
$build = fixer::input('post')->stripTags($this->config->build->editor->edit['id'], $this->config->allowedTags)
->add('id', $buildID)
->setIF(!isset($_POST['branch']), 'branch', $oldBuild->branch)
->setDefault('product', $oldBuild->product)
->cleanInt('product,branch,execution')
@@ -336,6 +338,7 @@ class buildModel extends model
->batchCheck($this->config->build->edit->requiredFields, 'notempty')
->where('id')->eq($buildID)
->check('name', 'unique', "id != $buildID AND product = {$build->product} AND branch = {$build->branch} AND deleted = '0'")
->checkFlow()
->exec();
if(isset($build->branch) and $oldBuild->branch != $build->branch) $this->dao->update(TABLE_RELEASE)->set('branch')->eq($build->branch)->where('build')->eq($buildID)->exec();
if(!dao::isError())
@@ -502,7 +505,6 @@ class buildModel extends model
*/
public function batchUnlinkBug($buildID)
{
$bugList = $this->post->unlinkBugs;
if(empty($bugList)) return true;
@@ -515,4 +517,27 @@ class buildModel extends model
$this->loadModel('action');
foreach($this->post->unlinkBugs as $unlinkBugID) $this->action->create('bug', $unlinkBugID, 'unlinkedfrombuild', '', $buildID);
}
/**
* Build action menu.
*
* @param object $build
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($build, $type = 'view')
{
$canBeChanged = common::canBeChanged('build', $build);
if($build->deleted || !$canBeChanged) return '';
$menu = '';
$params = "buildID=$build->id";
$menu .= $this->buildFlowMenu('build', $build, 'view', 'direct');
$menu .= $this->buildMenu('build', 'edit', $params, $build, $type);
$menu .= $this->buildMenu('build', 'delete', $params, $build, $type, 'trash', 'hiddenwin');
return $menu;
}
}
+1 -9
View File
@@ -48,15 +48,7 @@ tbody tr td:first-child input {display: none;}
</div>
<?php if(!isonlybody()):?>
<div class='btn-toolbar pull-right'>
<?php
if(!$build->deleted and $canBeChanged)
{
echo $this->buildOperateMenu($build, 'view');
if(common::hasPriv('build', 'edit')) echo html::a($this->createLink('build', 'edit', "buildID=$build->id"), "<i class='icon-common-edit icon-edit'></i> " . $this->lang->edit, '', "class='btn btn-link' title='{$this->lang->edit}' data-app='{$app->tab}'");
if(common::hasPriv('build', 'delete')) echo html::a($this->createLink('build', 'delete', "buildID=$build->id"), "<i class='icon-common-delete icon-trash'></i> " . $this->lang->delete, '', "class='btn btn-link' title='{$this->lang->delete}' target='hiddenwin' data-app='{$app->tab}'");
}
?>
<?php echo $this->build->buildOperateMenu($build, 'view');?>
</div>
<?php endif;?>
</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 = '导入用例';
+65
View File
@@ -94,6 +94,7 @@ class caselibModel extends model
$oldLib = $this->dao->select("*")->from(TABLE_TESTSUITE)->where('id')->eq((int)$libID)->fetch();
$lib = fixer::input('post')
->stripTags($this->config->caselib->editor->edit['id'], $this->config->allowedTags)
->add('id', $libID)
->add('lastEditedBy', $this->app->user->account)
->add('lastEditedDate', helper::now())
->remove('uid')
@@ -102,7 +103,9 @@ class caselibModel extends model
$this->dao->update(TABLE_TESTSUITE)->data($lib)
->autoCheck()
->batchcheck($this->config->caselib->edit->requiredFields, 'notempty')
->checkFlow()
->where('id')->eq($libID)
->checkFlow()
->exec();
if(!dao::isError())
{
@@ -186,6 +189,7 @@ class caselibModel extends model
$this->dao->insert(TABLE_TESTSUITE)->data($lib)
->batchcheck($this->config->caselib->create->requiredFields, 'notempty')
->check('name', 'unique', "deleted = '0'")
->checkFlow()
->exec();
if(!dao::isError())
{
@@ -591,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->caselib->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->caselib->buildOperateMenu($lib, 'view');?>
</div>
</div>
</div>
+7 -3
View File
@@ -1560,11 +1560,12 @@ EOD;
* @param string $extraClass
* @param bool $onlyBody
* @param string $misc
* @Param bool $extraEnabled
* @static
* @access public
* @return void
*/
public static function buildIconButton($module, $method, $vars = '', $object = '', $type = 'button', $icon = '', $target = '', $extraClass = '', $onlyBody = false, $misc = '', $title = '', $programID = 0)
public static function buildIconButton($module, $method, $vars = '', $object = '', $type = 'button', $icon = '', $target = '', $extraClass = '', $onlyBody = false, $misc = '', $title = '', $programID = 0, $extraEnabled = '')
{
if(isonlybody() and strpos($extraClass, 'showinonlybody') === false) return false;
@@ -1578,7 +1579,11 @@ EOD;
/* Judge the $method of $module clickable or not, default is clickable. */
$clickable = true;
if(is_object($object))
if(is_bool($extraEnabled))
{
$clickable = $extraEnabled;
}
else if(is_object($object))
{
if($app->getModuleName() != $module) $app->control->loadModel($module);
$modelClass = class_exists("ext{$module}Model") ? "ext{$module}Model" : $module . "Model";
@@ -1623,7 +1628,6 @@ EOD;
if(!$clickable) $class .= ' disabled';
if($icon) $class .= ' icon-' . $icon;
/* Create the icon link. */
if($clickable)
{
+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;
}
+1 -1
View File
@@ -61,7 +61,7 @@
<label></label>
</div>
</td>
<td class='versions c-name' title="<?php echo $repo->SCM == 'Git' ? substr($log->revision, 0, 10) : $log->revision;?>"><span class="revision"><?php echo html::a($this->repo->createLink('revision', "repoID=$repoID&revision={$log->revision}"), $repo->SCM == 'Git' ? substr($log->revision, 0, 10) : $log->revision);?></span></td>
<td class='versions c-name' title="<?php echo $repo->SCM == 'Git' ? substr($log->revision, 0, 10) : $log->revision;?>"><span class="revision"><?php echo html::a($this->repo->createLink('revision', "repoID=$repoID&objectID={$design->project}&revision={$log->revision}"), $repo->SCM == 'Git' ? substr($log->revision, 0, 10) : $log->revision);?></span></td>
<?php if($repo->SCM == 'Git'):?>
<td><?php echo $log->commit?></td>
<?php endif;?>
+20
View File
@@ -695,6 +695,26 @@ class doc extends control
}
}
/**
* Update order.
*
* @access public
* @return void
*/
public function updateOrder()
{
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
foreach($_POST['orders'] as $id => $order)
{
$this->dao->update(TABLE_DOC)->set('`order`')->eq($order)->where('id')->eq($id)->exec();
}
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success'));
}
}
/**
* Ajax get modules by libID.
*
+18
View File
@@ -20,3 +20,21 @@ span.dotted-line+a {display: block;}
.tree li.has-list.open:before {content: unset;}
#modules i.icon-file-text {color: #D0D2D6; font-size: 14px;}
.tree li>a {max-width: 80%;}
.sortable-sorting .module-name > a {cursor: move;}
.sortable-sorting li >.tree-group {opacity: .5;}
.sortable-sorting .drop-here .tree-group {background-color: #fff3e0;}
.sortable-sorting .drop-here .tree-group > * {opacity: .1;}
.sortable-sorting .drag-shadow .tree-group {opacity: 1!important;}
.sortable-sorting .drag-shadow .tree-actions {visibility: hidden;}
.is-sorting > li > .tree-group {opacity: 1; border-radius: 4px;}
.is-sorting > li ul {display: none!important;}
.tree-group {position: relative;}
.tree-group > .module-name {white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; display: block;}
.tree-group .tree-actions {display: none; position: absolute; right: 0; top: 0; background-color: #fff; white-space: nowrap;}
.tree-group:hover > .module-name {width: calc(100% - 20px);}
.tree-group:hover .tree-actions {display: block}
.tree li.has-list.open:before {left: 6px;}
.sortDoc>.tail-info{right: 20px;}
.sortDoc>.tree-actions{position: absolute; right: 0px; background: #fff;}
+77
View File
@@ -18,4 +18,81 @@ $(function()
})
$('.menu-actions > a').blur(function() {$(this).css('background', 'none');})
/* Make modules tree sortable. */
$('#modules').sortable(
{
trigger: '.module-name>a.sort-module, .tree-actions>.sortModule>.icon-move, a.sortDoc, .tree-actions>.sortDoc>.icon-move',
dropToClass: 'sort-to',
stopPropagation: true,
nested: true,
selector: 'li',
dragCssClass: 'drop-here',
canMoveHere: function($ele, $target)
{
var maxTop = $('.side-col > .cell > ul').height() - $ele.height();
if(parseFloat($('.drag-shadow').css('top')) < 0) $('.drag-shadow').css('top', '0');
if(parseFloat($('.drag-shadow').css('left')) != 0) $('.drag-shadow').css('left', '0');
if(parseFloat($('.drag-shadow').css('top')) > maxTop) $('.drag-shadow').css('top', maxTop + 'px');
return true;
},
targetSelector: function($ele, $root)
{
var $ul = $ele.closest('ul');
setTimeout(function()
{
if($('#modules').hasClass('sortable-sorting')) $ul.addClass('is-sorting');
}, 100);
if($ele.hasClass('sortDoc'))
{
return $ul.children('li.sortDoc');
}
else
{
return $ul.children('li.catalog');
}
},
always: function()
{
$('#modules,#modules .is-sorting').removeClass('is-sorting');
},
finish: function(e)
{
if(!e.changed) return;
var orders = {};
var link = '';
var elementClass = e.list.context.className;
if(elementClass.indexOf('sortDoc') >= 0)
{
$('#modules').find('li.sortDoc').each(function()
{
var $li = $(this);
var item = $li.data();
orders['orders[' + item.id + ']'] = $li.attr('data-order') || item.order;
});
link = createLink('doc', 'updateOrder');
}
else
{
$('#modules').find('li.can-sort').each(function()
{
var $li = $(this);
var item = $li.data();
'<?php echo $type;?>' == 'book' ? orders['sort[' + item.id + ']'] = $li.attr('data-order') || item.order : orders['orders[' + item.id + ']'] = $li.attr('data-order') || item.order;
});
link = createLink('tree', 'updateOrder');
}
$.post(link, orders, function(data){}).error(function()
{
bootbox.alert(lang.timeout);
});
}
});
})
+1
View File
@@ -93,6 +93,7 @@ $lang->doc->collector = 'Collector';
$lang->doc->main = 'Main Document Library';
$lang->doc->order = 'Order';
$lang->doc->doc = 'Document';
$lang->doc->updateOrder = 'Update Order';
$lang->doc->moduleDoc = 'By Module';
$lang->doc->searchDoc = 'Search';
+1
View File
@@ -93,6 +93,7 @@ $lang->doc->collector = '收藏者';
$lang->doc->main = '文档主库';
$lang->doc->order = '排序';
$lang->doc->doc = '文档';
$lang->doc->updateOrder = '更新排序';
$lang->doc->moduleDoc = '按模块浏览';
$lang->doc->searchDoc = '搜索';
+45 -19
View File
@@ -2379,6 +2379,7 @@ EOT;
$docs = $this->dao->select('*')->from(TABLE_DOC)
->where('lib')->eq($rootID)
->andWhere('deleted')->eq(0)
->orderBy('`order` asc')
->fetchAll();
$moduleDocs = array();
foreach($docs as $doc)
@@ -2410,25 +2411,36 @@ EOT;
{
if(!$docID and $currentMethod != 'tablecontents') $docID = $doc->id;
$treeMenu[0] .= '<li' . ($doc->id == $docID ? ' class="active"' : ' class="independent"') . " data-id=$doc->id>";
$class = common::hasPriv('doc', 'updateOrder') ? ' sortDoc' : '';
$treeMenu[0] .= '<li' . " class='" . ($doc->id == $docID ? 'active' : 'doc') . "$class'" . " data-id=$doc->id>";
if($currentMethod == 'tablecontents')
{
$treeMenu[0] .= '<span class="tail-info">' . zget($users, $doc->editedBy) . ' &nbsp;' . $doc->editedDate . '</span>';
$treeMenu[0] .= '<div class="tree-group"><span class="tail-info">' . zget($users, $doc->editedBy) . ' &nbsp;' . $doc->editedDate . '</span>';
if(common::hasPriv('doc', 'updateOrder'))
{
$treeMenu[0] .= "<div class='tree-actions'>";
$treeMenu[0] .= html::a('javascript:;', "<i class='icon icon-move sortDoc'></i>", '', "title='{$this->lang->doc->updateOrder}' class='sortDoc'");
$treeMenu[0] .= '</div>';
}
}
if($currentMethod == 'objectlibs')
{
$treeMenu[0] .= "<div class='tree-group'><span class='module-name'>" . html::a(inlink('objectLibs', "type=$type&objectID=$objectID&libID=$rootID&docID={$doc->id}"), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "data-app='{$this->app->tab}' class='doc-title' title='{$doc->title}'") . '</span>';
if(common::hasPriv('doc', 'edit'))
$class = common::hasPriv('doc', 'updateOrder') ? 'sortDoc' : '';
$treeMenu[0] .= "<div class='tree-group'><span class='module-name'>" . html::a(inlink('objectLibs', "type=$type&objectID=$objectID&libID=$rootID&docID={$doc->id}"), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "data-app='{$this->app->tab}' class='doc-title $class' title='{$doc->title}'") . '</span>';
if(common::hasPriv('doc', 'edit') or common::hasPriv('doc', 'updateOrder'))
{
$treeMenu[0] .= "<div class='tree-actions'>";
$treeMenu[0] .= html::a(helper::createLink('doc', 'edit', "docID={$doc->id}&comment=false&objectType=$type&objectID=$objectID&libID=$rootID"), "<i class='icon icon-edit'></i>", '', "title={$this->lang->doc->edit} data-app='{$this->app->tab}'");
if(common::hasPriv('doc', 'edit')) $treeMenu[0] .= html::a(helper::createLink('doc', 'edit', "docID={$doc->id}&comment=false&objectType=$type&objectID=$objectID&libID=$rootID"), "<i class='icon icon-edit'></i>", '', "title={$this->lang->doc->edit} data-app='{$this->app->tab}'");
if(common::hasPriv('doc', 'updateOrder')) $treeMenu[0] .= html::a('javascript:;', "<i class='icon icon-move sortDoc'></i>", '', "title='{$this->lang->doc->updateOrder}' class='sortDoc'");
$treeMenu[0] .= '</div></div>';
}
}
else
{
$treeMenu[0] .= html::a(inlink('objectLibs', "type=$type&objectID=$objectID&libID=$rootID&docID={$doc->id}"), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "data-app='{$this->app->tab}' class='doc-title' title='{$doc->title}'");
$class = common::hasPriv('doc', 'updateOrder') ? 'sortDoc' : '';
$treeMenu[0] .= html::a(inlink('objectLibs', "type=$type&objectID=$objectID&libID=$rootID&docID={$doc->id}"), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "data-app='{$this->app->tab}' class='doc-title $class' title='{$doc->title}'");
$treeMenu[0] .= '</div>';
}
$treeMenu[0] .= '</li>';
@@ -2477,27 +2489,38 @@ EOT;
else
{
if(!$docID and $currentMethod != 'tablecontents') $docID = $doc->id;
$treeMenu[$module->id] .= '<li' . ($doc->id == $docID ? ' class="active"' : ' class="doc"') . " data-id=$doc->id>";
$class = common::hasPriv('doc', 'updateOrder') ? ' sortDoc' : '';
$treeMenu[$module->id] .= '<li' . " class='" . ($doc->id == $docID ? 'active' : 'doc') . "$class'" . " data-id=$doc->id>";
if($currentMethod == 'tablecontents')
{
$treeMenu[$module->id] .= '<span class="tail-info">' . zget($users, $doc->editedBy) . ' &nbsp;' . $doc->editedDate . '</span>';
$treeMenu[$module->id] .= '<div class="tree-group"><span class="tail-info">' . zget($users, $doc->editedBy) . ' &nbsp;' . $doc->editedDate . '</span>';
if(common::hasPriv('doc', 'updateOrder'))
{
$treeMenu[$module->id] .= "<div class='tree-actions'>";
$treeMenu[$module->id] .= html::a('javascript:;', "<i class='icon icon-move sortDoc'></i>", '', "title='{$this->lang->doc->updateOrder}' class='sortDoc'");
$treeMenu[$module->id] .= '</div>';
}
}
if($currentMethod == 'objectlibs')
{
$treeMenu[$module->id] .= "<div class='tree-group'><span class='module-name'>" . html::a(inlink('objectLibs', "type=$type&objectID=$objectID&libID=$libID&docID={$doc->id}"), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "data-app='{$this->app->tab}' class='doc-title' title='{$doc->title}'") . '</span>';
if(common::hasPriv('doc', 'edit'))
$class = common::hasPriv('doc', 'updateOrder') ? 'sortDoc' : '';
$treeMenu[$module->id] .= "<div class='tree-group'><span class='module-name'>" . html::a(inlink('objectLibs', "type=$type&objectID=$objectID&libID=$libID&docID={$doc->id}"), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "data-app='{$this->app->tab}' class='doc-title $class' title='{$doc->title}'") . '</span>';
if(common::hasPriv('doc', 'edit') or common::hasPriv('doc', 'updateOrder'))
{
$treeMenu[$module->id] .= "<div class='tree-actions'>";
$treeMenu[$module->id] .= html::a(helper::createLink('doc', 'edit', "docID={$doc->id}&comment=false&objectType=$type&objectID=$objectID&libID=$libID"), "<i class='icon icon-edit'></i>", '', "title={$this->lang->doc->edit} data-app={$this->app->tab}");
if(common::hasPriv('doc', 'edit')) $treeMenu[$module->id] .= html::a(helper::createLink('doc', 'edit', "docID={$doc->id}&comment=false&objectType=$type&objectID=$objectID&libID=$libID"), "<i class='icon icon-edit'></i>", '', "title={$this->lang->doc->edit} data-app={$this->app->tab}");
if(common::hasPriv('doc', 'updateOrder')) $treeMenu[$module->id] .= html::a('javascript:;', "<i class='icon icon-move sortDoc'></i>", '', "title='{$this->lang->doc->updateOrder}' class='sortDoc'");
$treeMenu[$module->id] .= '</div>';
}
$treeMenu[$module->id] .= '</div>';
}
elseif($currentMethod == 'tablecontents')
{
$treeMenu[$module->id] .= html::a(inlink('objectLibs', "type=$type&objectID=$objectID&libID=$libID&docID={$doc->id}"), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "data-app='{$this->app->tab}' class='doc-title' title='{$doc->title}'");
$class = common::hasPriv('doc', 'updateOrder') ? 'sortDoc' : '';
$treeMenu[$module->id] .= html::a(inlink('objectLibs', "type=$type&objectID=$objectID&libID=$libID&docID={$doc->id}"), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "data-app='{$this->app->tab}' class='doc-title $class' title='{$doc->title}'");
$treeMenu[$module->id] .= '</div>';
}
$treeMenu[$module->id] .= '</li>';
@@ -2511,22 +2534,25 @@ EOT;
}
else
{
if($currentMethod == 'tablecontents')
$li = "<div class='tree-group'><span class='module-name'><a class='sort-module' title='{$module->name}'>" . $module->name . '</a></span>';
if($currentMethod == 'tablecontents' and common::hasPriv('tree', 'updateOrder'))
{
$li = "<a title='{$module->name}'>" . $module->name . '</a>';
$li .= "<div class='tree-actions'>";
$li .= html::a('javascript:;', "<i class='icon icon-move sortModule'></i>", '', "title='{$this->lang->doc->updateOrder}' class='sortModule'");
$li .= '</div>';
}
else
{
$li = "<div class='tree-group'><span class='module-name'><a class='sort-module' title='{$module->name}'>" . $module->name . '</a></span>';
if(common::hasPriv('tree', 'edit') or common::hasPriv('tree', 'browse'))
if(common::hasPriv('tree', 'edit') or common::hasPriv('tree', 'browse') or common::hasPriv('tree', 'browse') or common::hasPriv('tree', 'updateOrder'))
{
$li .= "<div class='tree-actions'>";
if(common::hasPriv('tree', 'edit')) $li .= html::a(helper::createLink('tree', 'edit', "module=$module->id&type=doc"), "<i class='icon icon-edit'></i>", '', "data-toggle='modal' title={$this->lang->doc->editType}");
if(common::hasPriv('tree', 'browse')) $li .= html::a(helper::createLink('tree', 'browse', "rootID=$libID&type=doc&module=$module->id", '', 1), "<i class='icon icon-split'></i>", '', "class='iframe' title={$this->lang->doc->editChildType}");
if(common::hasPriv('tree', 'edit')) $li .= html::a(helper::createLink('tree', 'edit', "module=$module->id&type=doc"), "<i class='icon icon-edit'></i>", '', "data-toggle='modal' title='{$this->lang->doc->editType}'");
if(common::hasPriv('tree', 'browse')) $li .= html::a(helper::createLink('tree', 'browse', "rootID=$libID&type=doc&module=$module->id", '', 1), "<i class='icon icon-split'></i>", '', "class='iframe' title='{$this->lang->doc->editChildType}'");
if(common::hasPriv('tree', 'updateOrder')) $li .= html::a('javascript:;', "<i class='icon icon-move sortModule'></i>", '', "title='{$this->lang->doc->updateOrder}' class='sortModule'");
$li .= '</div>';
}
$li .= '</div>';
}
$li .= '</div>';
}
if($treeMenu[$module->id])
{
+35 -9
View File
@@ -79,7 +79,7 @@ $sideWidth = common::checkNotCN() ? '270' : '238';
/* Make modules tree sortable */
$('#modules').sortable(
{
trigger: '.module-name>a.sort-module',
trigger: '.module-name>a.sort-module, .tree-actions>.sortModule>.icon-move, .module-name>a.sortDoc, .tree-actions>.sortDoc>.icon-move',
dropToClass: 'sort-to',
stopPropagation: true,
nested: true,
@@ -100,7 +100,15 @@ $sideWidth = common::checkNotCN() ? '270' : '238';
{
if($('#modules').hasClass('sortable-sorting')) $ul.addClass('is-sorting');
}, 100);
return $ul.children('li');
if($ele.hasClass('sortDoc'))
{
return $ul.children('li.sortDoc');
}
else
{
return $ul.children('li.catalog');
}
},
always: function()
{
@@ -110,16 +118,34 @@ $sideWidth = common::checkNotCN() ? '270' : '238';
{
if(!e.changed) return;
var orders = {};
$('#modules').find('li.can-sort').each(function()
var orders = {};
var link = '';
var elementClass = e.list.context.className;
if(elementClass.indexOf('sortDoc') >= 0)
{
var $li = $(this);
$('#modules').find('li.sortDoc').each(function()
{
var $li = $(this);
var item = $li.data();
'<?php echo $type;?>' == 'book' ? orders['sort[' + item.id + ']'] = $li.attr('data-order') || item.order : orders['orders[' + item.id + ']'] = $li.attr('data-order') || item.order;
});
var item = $li.data();
orders['orders[' + item.id + ']'] = $li.attr('data-order') || item.order;
});
link = createLink('doc', 'updateOrder');
}
else
{
$('#modules').find('li.can-sort').each(function()
{
var $li = $(this);
var item = $li.data();
'<?php echo $type;?>' == 'book' ? orders['sort[' + item.id + ']'] = $li.attr('data-order') || item.order : orders['orders[' + item.id + ']'] = $li.attr('data-order') || item.order;
});
link = '<?php echo $type?>' == 'book' ? createLink('doc', 'sortBookOrder') : createLink('tree', 'updateOrder');
}
var link = '<?php echo $type;?>' == 'book' ? createLink('doc', 'sortBookOrder') : createLink('tree', 'updateOrder');
$.post(link, orders, function(data){}).error(function()
{
bootbox.alert(lang.timeout);
+20 -4
View File
@@ -1677,9 +1677,15 @@ class execution extends control
if($this->app->tab == 'project')
{
$projectID = $this->session->project;
$projectID = $this->session->project;
$project = $this->project->getById($projectID);
$allProjects = $this->project->getPairsByModel($project->model, 0, 'noclosed', isset($projectID) ? $projectID : 0);
$this->project->setMenu($projectID);
$this->view->project = $this->project->getById($projectID);
$this->view->project = $project;
}
else
{
$allProjects = $this->project->getPairsByModel('all', 0, 'noclosed', isset($projectID) ? $projectID : 0);
}
if(!$this->post->executionIDList) return print(js::locate($this->session->executionList, 'parent'));
@@ -1717,7 +1723,7 @@ class execution extends control
$this->view->position[] = $this->lang->execution->batchEdit;
$this->view->executionIDList = $executionIDList;
$this->view->executions = $executions;
$this->view->allProjects = $this->project->getPairsByModel('all', 0, 'noclosed', isset($projectID) ? $projectID : 0);
$this->view->allProjects = $allProjects;
$this->view->pmUsers = $pmUsers;
$this->view->poUsers = $poUsers;
$this->view->qdUsers = $qdUsers;
@@ -3148,7 +3154,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 +3172,7 @@ class execution extends control
$this->view->extra = $extra;
$this->view->projects = $projectPairs;
$this->view->executions = $projectExecutions;
$this->view->parents = $parents;
$this->display();
}
+12 -11
View File
@@ -1,13 +1,14 @@
$(function()
function changeProject(obj, executionID, projectID)
{
$('[id^="projects"]').change(function()
var $td = $(obj).closest('td');
if($td.find('[id^="syncStories"]').length == 0)
{
var executionID = $(this).attr('id').replace('projects', '');
var $td = $(this).closest('td');
if($td.find('[id^="syncStories"]').length == 0)
{
$td.append("<input type='hidden' id='syncStories" + executionID + "' name='syncStories[" + executionID + "]' value='no' />");
}
$("#syncStories" + executionID).val(confirm(confirmSyncStories) ? 'yes' : 'no');
})
});
$td.append("<input type='hidden' id='syncStories" + executionID + "' name='syncStories[" + executionID + "]' value='no' />");
}
var confirmVal = confirm(confirmSync);
$("#syncStories" + executionID).val(confirmVal ? 'yes' : 'no');
if(!confirmVal)
{
$(obj).val(projectID).trigger("chosen:updated");
}
};
+1 -1
View File
@@ -71,6 +71,6 @@ $(function()
{
$('#submit').after("<input type='hidden' id='syncStories' name='syncStories' value='no' />");
}
$("#syncStories").val(confirm(confirmSyncStories) ? 'yes' : 'no');
$("#syncStories").val(confirm(confirmSync) ? 'yes' : 'no');
});
})
+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]);
}
}
})
});
}
}
+1 -1
View File
@@ -340,7 +340,7 @@ if($config->systemMode == 'new') $lang->execution->copyTeamTitle = "Choose a
$lang->execution->confirmDelete = "Do you want to delete the {$lang->executionCommon}[%s]?";
$lang->execution->confirmUnlinkMember = "Do you want to unlink this User from {$lang->executionCommon}?";
$lang->execution->confirmUnlinkStory = "After {$lang->SRCommon} is removed, cased linked to {$lang->SRCommon} will be reomoved and tasks linked to {$lang->SRCommon} will be cancelled. Do you want to continue?";
$lang->execution->confirmSyncStories = "After modifying the project, synchronize the execution related {$lang->SRCommon} to the new project?";
$lang->execution->confirmSync = "After modifying the project, in order to maintain the consistency of data, the data of products, requirements, teams and whitelist associated with the implementation will be synchronized to the new project. Please know.";
$lang->execution->confirmUnlinkExecutionStory = "Do you want to unlink this Story from the execution?";
$lang->execution->notAllowedUnlinkStory = "This {$lang->SRCommon} is linked to the {$lang->executionCommon} of the execution. Remove it from the {$lang->executionCommon}, then try again.";
$lang->execution->notAllowRemoveProducts = "The story of this product is linked with the {$lang->executionCommon}. Unlink it before doing any action.";
+1 -1
View File
@@ -340,7 +340,7 @@ if($config->systemMode == 'new') $lang->execution->copyTeamTitle = "选择
$lang->execution->confirmDelete = "您确定删除{$lang->executionCommon}[%s]吗?";
$lang->execution->confirmUnlinkMember = "您确定从该{$lang->executionCommon}中移除该用户吗?";
$lang->execution->confirmUnlinkStory = "移除该{$lang->SRCommon}后,该{$lang->SRCommon}关联的用例将被移除,该{$lang->SRCommon}关联的任务将被取消,请确认。";
$lang->execution->confirmSyncStories = "修改所属项目后,执行关联的{$lang->SRCommon}是否同步到新项目中?";
$lang->execution->confirmSync = "修改所属项目后,为了保持数据的一致性,该执行所关联的产品、需求、团队和白名单数据将会同步到新的项目中,请知悉。";
$lang->execution->confirmUnlinkExecutionStory = "您确定从该项目中移除该{$lang->SRCommon}吗?";
$lang->execution->notAllowedUnlinkStory = "该{$lang->SRCommon}已经与项目下{$lang->executionCommon}相关联,请从{$lang->executionCommon}中移除后再操作。";
$lang->execution->notAllowRemoveProducts = "该{$lang->productCommon}中的{$lang->SRCommon}已与该{$lang->executionCommon}进行了关联,请取消关联后再操作。";
+133 -13
View File
@@ -378,6 +378,7 @@ class executionModel extends model
->checkIF($sprint->begin != '', 'begin', 'date')
->checkIF($sprint->end != '', 'end', 'date')
->checkIF($sprint->end != '', 'end', 'ge', $sprint->begin)
->checkFlow()
->exec();
/* Add the creater to the team. */
@@ -468,6 +469,7 @@ class executionModel extends model
/* Get the data from the post. */
$execution = fixer::input('post')
->add('id', $executionID)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', helper::now())
->setIF(helper::isZeroDate($this->post->begin), 'begin', '')
@@ -514,7 +516,9 @@ class executionModel extends model
->checkIF($execution->end != '', 'end', 'ge', $execution->begin)
->checkIF((!empty($execution->name) and $this->config->systemMode == 'new'), 'name', 'unique', "id != $executionID and type in ('sprint','stage') and `project` = $executionProject")
->checkIF(!empty($execution->code), 'code', 'unique', "id != $executionID and type in ('sprint','stage')")
->checkFlow()
->where('id')->eq($executionID)
->checkFlow()
->limit(1)
->exec();
@@ -619,6 +623,7 @@ class executionModel extends model
$executionID = (int)$executionID;
$executions[$executionID] = new stdClass();
$executions[$executionID]->id = $executionID;
$executions[$executionID]->name = $executionName;
$executions[$executionID]->code = $executionCode;
$executions[$executionID]->PM = $data->PMs[$executionID];
@@ -669,13 +674,6 @@ class executionModel extends model
if(is_array($executions[$executionID]->{$extendField->field})) $executions[$executionID]->{$extendField->field} = join(',', $executions[$executionID]->{$extendField->field});
$executions[$executionID]->{$extendField->field} = htmlSpecialString($executions[$executionID]->{$extendField->field});
$message = $this->checkFlowRule($extendField, $executions[$executionID]->{$extendField->field});
if($message)
{
dao::$errors['message'][] = $message;
return false;
}
}
}
@@ -686,6 +684,27 @@ class executionModel extends model
$projectID = isset($execution->project) ? $execution->project : $oldExecution->project;
$project = $this->project->getByID($projectID);
if(isset($execution->project))
{
$executionProductList = $this->loadModel('product')->getProducts($executionID);
$projectProductList = $this->product->getProducts($execution->project);
$executionProductIdList = array_keys($executionProductList);
$projectProductIdList = array_keys($projectProductList);
$diffProductIdList = array_diff($executionProductIdList, $projectProductIdList);
if(!empty($diffProductIdList))
{
foreach($diffProductIdList as $key => $newProductID)
{
$data = $this->dao->select('*')->from(TABLE_PROJECTPRODUCT)
->where('project')->eq($executionID)
->andWhere('product')->eq($newProductID)
->fetch();
$data->project = $execution->project;
$this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec();
}
}
}
if($project and $execution->begin < $project->begin)
{
dao::$errors['begin'] = sprintf($this->lang->execution->errorLetterProject, $project->begin);
@@ -705,6 +724,7 @@ class executionModel extends model
->checkIF($execution->end != '', 'end', 'ge', $execution->begin)
->checkIF((!empty($execution->name) and $this->config->systemMode == 'new'), 'name', 'unique', "id != $executionID and type in ('sprint','stage') and `project` = $projectID")
->checkIF(!empty($execution->code), 'code', 'unique', "id != $executionID and type in ('sprint','stage')")
->checkFlow()
->where('id')->eq($executionID)
->limit(1)
->exec();
@@ -757,6 +777,7 @@ class executionModel extends model
$now = helper::now();
$execution = fixer::input('post')
->add('id', $executionID)
->setDefault('status', 'doing')
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
@@ -767,6 +788,7 @@ class executionModel extends model
->autoCheck()
->check($this->config->execution->start->requiredFields, 'notempty')
->checkIF($execution->realBegan != '', 'realBegan', 'le', helper::today())
->checkFlow()
->where('id')->eq((int)$executionID)
->exec();
@@ -789,6 +811,7 @@ class executionModel extends model
$now = helper::now();
$execution = fixer::input('post')
->add('id', $executionID)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
->remove('comment')
@@ -799,6 +822,7 @@ class executionModel extends model
$this->dao->update(TABLE_EXECUTION)->data($execution)
->autoCheck()
->checkFlow()
->where('id')->eq((int)$executionID)
->exec();
@@ -818,6 +842,7 @@ class executionModel extends model
$now = helper::now();
$execution = fixer::input('post')
->add('id', $executionID)
->setDefault('status', 'suspended')
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
@@ -825,6 +850,7 @@ class executionModel extends model
$this->dao->update(TABLE_EXECUTION)->data($execution)
->autoCheck()
->checkFlow()
->where('id')->eq((int)$executionID)
->exec();
@@ -844,6 +870,7 @@ class executionModel extends model
$now = helper::now();
$execution = fixer::input('post')
->add('id', $executionID)
->setDefault('realEnd', '')
->setDefault('status', 'doing')
->setDefault('lastEditedBy', $this->app->user->account)
@@ -859,6 +886,7 @@ class executionModel extends model
$this->dao->update(TABLE_EXECUTION)->data($execution)
->autoCheck()
->checkFlow()
->where('id')->eq((int)$executionID)
->exec();
@@ -913,6 +941,7 @@ class executionModel extends model
$now = helper::now();
$execution = fixer::input('post')
->add('id', $executionID)
->setDefault('status', 'closed')
->setDefault('closedBy', $this->app->user->account)
->setDefault('closedDate', $now)
@@ -928,6 +957,7 @@ class executionModel extends model
->check($this->config->execution->close->requiredFields,'notempty')
->checkIF($execution->realEnd != '', 'realEnd', 'le', helper::today())
->checkIF($execution->realEnd != '', 'realEnd', 'ge', $oldExecution->realBegan)
->checkFlow()
->where('id')->eq((int)$executionID)
->exec();
@@ -2782,15 +2812,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))
@@ -2810,8 +2895,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')
@@ -2842,8 +2928,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;
}
/**
@@ -3945,4 +4033,36 @@ class executionModel extends model
}
$this->dao->update(TABLE_PROJECT)->set('path')->eq($path['path'])->set('grade')->eq($path['grade'])->where('id')->eq($execution->id)->exec();
}
/**
* Build execution action menu.
*
* @param object $execution
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($execution, $type = 'view')
{
if($execution->deleted) return '';
$menu = '';
$params = "executionID=$execution->id";
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('execution', 'start', $params, $execution, $type, '', '', 'iframe', true);
$menu .= $this->buildMenu('execution', 'activate', $params, $execution, $type, '', '', 'iframe', true);
$menu .= $this->buildMenu('execution', 'putoff', $params, $execution, $type, '', '', 'iframe', true);
$menu .= $this->buildMenu('execution', 'suspend', $params, $execution, $type, '', '', 'iframe', true);
$menu .= $this->buildMenu('execution', 'close', $params, $execution, $type, '', '', 'iframe', true);
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('execution', $execution, 'view', 'direct');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('execution', 'edit', "execution=$execution->id", $execution);
$menu .= $this->buildMenu('execution', 'delete', "execution=$execution->id", $execution, 'button', 'trash', 'hiddenwin');
return $menu;
}
}
+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 ()
{
+2 -2
View File
@@ -83,7 +83,7 @@
<tr>
<td><?php echo sprintf('%03d', $executionID) . html::hidden("executionIDList[$executionID]", $executionID);?></td>
<?php if($config->systemMode == 'new' and isset($project) and $project->model == 'scrum'):?>
<td class='text-left' style='overflow:visible'><?php echo html::select("projects[$executionID]", $allProjects, $executions[$executionID]->project, "class='form-control picker-select'");?></td>
<td class='text-left' style='overflow:visible'><?php echo html::select("projects[$executionID]", $allProjects, $executions[$executionID]->project, "class='form-control picker-select' onchange='changeProject(this, $executionID, {$executions[$executionID]->project})'");?></td>
<?php endif;?>
<td title='<?php echo $executions[$executionID]->name?>'><?php echo html::input("names[$executionID]", $executions[$executionID]->name, "class='form-control'");?></td>
<td><?php echo html::input("codes[$executionID]", $executions[$executionID]->code, "class='form-control'");?></td>
@@ -137,5 +137,5 @@
</form>
</div>
<?php js::set('weekend', $config->execution->weekend);?>
<?php js::set('confirmSyncStories', $lang->execution->confirmSyncStories);?>
<?php js::set('confirmSync', $lang->execution->confirmSync);?>
<?php include '../../common/view/footer.html.php';?>
+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 -1
View File
@@ -220,6 +220,6 @@
<?php js::set('unmodifiableBranches', $unmodifiableBranches)?>
<?php js::set('multiBranchProducts', $multiBranchProducts);?>
<?php js::set('tip', $lang->execution->notAllowRemoveProducts);?>
<?php js::set('confirmSyncStories', $lang->execution->confirmSyncStories);?>
<?php js::set('confirmSync', $lang->execution->confirmSync);?>
<?php js::set('systemMode', $config->systemMode);?>
<?php include '../../common/view/footer.html.php';?>
+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')
)
);
?>
+3 -20
View File
@@ -146,26 +146,9 @@
</div>
<div class='main-actions'>
<div class="btn-toolbar">
<?php
$params = "execution=$execution->id";
$browseLink = $this->session->executionList ? $this->session->executionList : inlink('browse', "executionID=$execution->id");
common::printBack($browseLink);
if(!$execution->deleted)
{
echo "<div class='divider'></div>";
common::printIcon('execution', 'start', "executionID=$execution->id", $execution, 'button', '', '', 'iframe', true);
common::printIcon('execution', 'activate', "executionID=$execution->id", $execution, 'button', '', '', 'iframe', true);
common::printIcon('execution', 'putoff', "executionID=$execution->id", $execution, 'button', '', '', 'iframe', true);
common::printIcon('execution', 'suspend', "executionID=$execution->id", $execution, 'button', '', '', 'iframe', true);
common::printIcon('execution', 'close', "executionID=$execution->id", $execution, 'button', '', '', 'iframe', true);
echo $this->buildOperateMenu($execution, 'view');
echo "<div class='divider'></div>";
common::printIcon('execution', 'edit', $params, $execution);
common::printIcon('execution', 'delete', $params, $execution, 'button', 'trash', 'hiddenwin');
}
?>
<?php $browseLink = $this->session->executionList ? $this->session->executionList : inlink('browse', "executionID=$execution->id");?>
<?php common::printBack($browseLink);?>
<?php echo $this->execution->buildOperateMenu($execution, 'view');?>
</div>
</div>
</div>
+1 -1
View File
@@ -259,7 +259,7 @@ class gitlab extends control
$user = $this->gitlab->apiGetCurrentUser($gitlab->url, $gitlab->token, true);
if(!is_object($user)) return $this->send(array('result' => 'fail', 'message' => array('url' => array(sprintf($this->lang->gitlab->hostError, $this->config->gitlab->minCompatibleVersion)))));
if(isset($user->error)) return $this->send(array('result' => 'fail', 'message' => array('token' => array($this->lang->gitlab->tokenError))));
if(!isset($user->id)) return $this->send(array('result' => 'fail', 'message' => array('token' => array($this->lang->gitlab->tokenError))));
/* Verify version compatibility. */
$result = $this->gitlab->getVersion($gitlab->url, $gitlab->token);
+1
View File
@@ -1215,6 +1215,7 @@ $lang->resource->doc->objectLibs = 'objectLibs';
$lang->resource->doc->collect = 'collectAction';
$lang->resource->doc->tableContents = 'tableContents';
$lang->resource->doc->showFiles = 'showFiles';
$lang->resource->doc->updateOrder = 'updateOrder';
$lang->doc->methodOrder[0] = 'index';
$lang->doc->methodOrder[5] = 'browse';
+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;
}
}
+81 -29
View File
@@ -19,6 +19,7 @@
#archivedCards .kanban-item > .info {margin-top: 5px; position: relative;}
#archivedCards .kanban-item > .info > .pri {height: 14px; border-width: 1px; font-size: 12px; line-height: 12px; min-width: 14px; padding: 0 1px;}
#archivedCards .kanban-item > .info > .time {margin-left: 10px; font-size: 12px}
#archivedCards .kanban-item > .info.build > .time {margin-left:0px; font-size: 12px}
#archivedCards .kanban-item > .info > .user {position: absolute; right: 0; top: 0}
#archivedCards .card-actions {position: relative; padding: 15px 10px; padding-top: 0px;}
#archivedCards .card-actions > .btn {display: block;}
@@ -31,6 +32,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;}
@@ -40,10 +42,18 @@
#archivedCards .card-item .has-color .info > .label-pri,
#archivedCards .card-item .has-color .info > .estimate,
#archivedCards .card-item .has-color .info > .label-light {color: #FFFFFF;}
#archivedCards .info.execution .label-light, #archivedCards .info.productplan .label-light, #archivedCards .info.build .label-light, #archivedCards .info.release .label-light {background: #EFEFEF !important; color: #838A9D !important;}
#archivedCards .card-item .has-color .info > .label-pri {border-color: #FFFFFF;}
#archivedCards .card-item .has-color .progress-box > .progress-number {color: #FFFFFF;}
#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,48 +86,78 @@
<?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 if(helper::isZeroDate($card->end) and !helper::isZeroDate($card->begin)):?>
<?php else:?>
<?php
$name = isset($card->title) ? $card->title : $card->name;
if(common::hasPriv($card->fromType, 'view')) echo html::a($this->createLink($card->fromType, '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(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("Y-m-d", strtotime($card->date)) . "</span>"
?>
<?php endif;?>
<?php if(helper::isZeroDate($card->end) and !helper::isZeroDate($card->begin) and $card->begin != '2030-01-01' and $card->end != '2030-01-01'):?>
<span class="time label label-light"><?php echo date("m/d", strtotime($card->begin)) . $lang->kanbancard->beginAB;?></span>
<?php endif;?>
<?php if(helper::isZeroDate($card->begin) and !helper::isZeroDate($card->end)):?>
<?php if(helper::isZeroDate($card->begin) and !helper::isZeroDate($card->end) and $card->begin != '2030-01-01' and $card->end != '2030-01-01'):?>
<span class="time label label-light"><?php echo date("m/d", strtotime($card->end)) . $lang->kanbancard->deadlineAB;?></span>
<?php endif;?>
<?php if(!helper::isZeroDate($card->begin) and !helper::isZeroDate($card->end)):?>
<?php if(!helper::isZeroDate($card->begin) and !helper::isZeroDate($card->end) and $card->begin != '2030-01-01' and $card->end != '2030-01-01' and $card->fromType == ''):?>
<span class="time label label-light"><?php echo date("m/d", strtotime($card->begin)) . ' ~ ' . date("m/d", strtotime($card->end));?></span>
<?php endif;?>
<?php if(!helper::isZeroDate($card->begin) and !helper::isZeroDate($card->end) and $card->begin != '2030-01-01' and $card->end != '2030-01-01' and $card->fromType != ''):?>
<span class="time label label-light"><?php echo date("m-d", strtotime($card->begin)) . ' ' . $lang->{$card->fromType}->to . ' ' . date("m-d", strtotime($card->end));?></span>
<?php endif;?>
<?php
$assignedToList = explode(',', $card->assignedTo);
if($card->begin == '2030-01-01' or $card->end == '2030-01-01') echo '<span class="date label label-future" title="' . $lang->{$card->fromType}->future . '">' . $lang->{$card->fromType}->future . '</span>';
if($card->fromType == '') $assignedToList = explode(',', $card->assignedTo);
if($card->fromType == 'execution') $assignedToList = $card->PM;
if($card->fromType == 'build') $assignedToList = $card->builder;
if($card->fromType == 'release' or $card->fromType == 'productplan') $assignedToList = $card->createdBy;
$count = 0;
$members = '';
?>
<?php
foreach($assignedToList as $index => $account)
{
if(!isset($users[$account]) or !isset($usersAvatar[$account]))
{
unset($assignedToList[$index]);
continue;
}
$members .= $users[$account] . ',';
}
$userCount = count($assignedToList);
?>
<?php if($userCount > 0):?>
<div class='users pull-right' title="<?php echo trim($members, ',');?>">
<?php
foreach($assignedToList as $account)
{
if($count > 2) continue;
echo html::smallAvatar(array('avatar' => $usersAvatar[$account], 'account' => $account, 'name' => $users[$account]), 'avatar-circle');
$count ++;
}
?>
<?php if($count > 2) echo "<span>...</span>";?>
</div>
<?php if(is_array($assignedToList)):?>
<?php
foreach($assignedToList as $index => $account)
{
if(!isset($users[$account]) or !isset($usersAvatar[$account]))
{
unset($assignedToList[$index]);
continue;
}
$members .= $users[$account] . ',';
}
$userCount = count($assignedToList);
?>
<?php if($userCount > 0):?>
<div class='users pull-right' title="<?php echo trim($members, ',');?>">
<?php
foreach($assignedToList as $account)
{
if($count > 2) continue;
echo html::smallAvatar(array('avatar' => $usersAvatar[$account], 'account' => $account, 'name' => $users[$account]), 'avatar-circle');
$count ++;
}
?>
<?php if($count > 2) echo "<span>...</span>";?>
</div>
<?php endif;?>
<?php else:?>
<?php
if(isset($usersAvatar[$assignedToList]) and isset($users[$assignedToList]))
{
echo "<div class='users pull-right' title='" . $users[$assignedToList] . "'>";
echo html::smallAvatar(array('avatar' => $usersAvatar[$assignedToList], 'account' => $assignedToList, 'name' => $users[$assignedToList]), 'avatar-circle');
echo "</div>";
}
?>
<?php endif;?>
</div>
<?php if($kanban->performable):?>
@@ -137,7 +177,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 +192,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';
+12 -3
View File
@@ -301,7 +301,7 @@ class product extends control
$actionURL = $this->createLink($rawModule, $rawMethod, $params . "productID=$productID&branch=$branch&browseType=bySearch&queryID=myQueryID&storyType=$storyType");
$this->config->product->search['onMenuBar'] = 'yes';
$this->product->buildSearchForm($productID, $this->products, $queryID, $actionURL, $branch);
$this->product->buildSearchForm($productID, $this->products, $queryID, $actionURL, $branch, $projectID);
$showModule = !empty($this->config->datatable->productBrowse->showModule) ? $this->config->datatable->productBrowse->showModule : '';
@@ -1197,22 +1197,30 @@ class product extends control
* @access public
* @return void
*/
public function all($browseType = 'noclosed', $orderBy = 'order_asc')
public function all($browseType = 'noclosed', $orderBy = 'program_asc', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
/* 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);
$this->product->setMenu($productID);
}
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Process product structure. */
$productStats = $this->product->getStats($orderBy, '', $browseType, '', 'story');
$productStats = $this->product->getStats($orderBy, $pager, $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;
@@ -1221,6 +1229,7 @@ class product extends control
$this->view->productStructure = $productStructure;
$this->view->orderBy = $orderBy;
$this->view->browseType = $browseType;
$this->view->pager = $pager;
$this->display();
}
+112 -7
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.
*
@@ -609,6 +651,7 @@ class productModel extends model
->batchCheck($this->config->product->create->requiredFields, 'notempty')
->checkIF((!empty($product->name) and $this->config->systemMode == 'new'), 'name', 'unique', "`program` = $programID")
->checkIF(!empty($product->code), 'code', 'unique')
->checkFlow()
->exec();
if(!dao::isError())
@@ -683,6 +726,7 @@ class productModel extends model
if($oldProduct->bind) $this->config->product->edit->requiredFields = 'name';
$product = fixer::input('post')
->add('id', $productID)
->callFunc('name', 'trim')
->setDefault('line', 0)
->setIF(!isset($_POST['whitelist']), 'whitelist', '')
@@ -717,6 +761,7 @@ class productModel extends model
->batchCheck($this->config->product->edit->requiredFields, 'notempty')
->checkIF((!empty($product->name) and $this->config->systemMode == 'new'), 'name', 'unique', "id != $productID and `program` = $programID")
->checkIF(!empty($product->code), 'code', 'unique', "id != $productID")
->checkFlow()
->where('id')->eq($productID)
->exec();
@@ -764,6 +809,7 @@ class productModel extends model
$products[$productID]->status = $data->statuses[$productID];
$products[$productID]->desc = strip_tags($this->post->descs[$productID], $this->config->allowedTags);
$products[$productID]->acl = $data->acls[$productID];
$products[$productID]->id = $productID;
foreach($extendFields as $extendField)
{
@@ -771,8 +817,6 @@ class productModel extends model
if(is_array($products[$productID]->{$extendField->field})) $products[$productID]->{$extendField->field} = join(',', $products[$productID]->{$extendField->field});
$products[$productID]->{$extendField->field} = htmlSpecialString($products[$productID]->{$extendField->field});
$message = $this->checkFlowRule($extendField, $products[$productID]->{$extendField->field});
if($message) return print(js::alert($message));
}
}
if(dao::isError()) return print(js::error(dao::getError()));
@@ -789,6 +833,7 @@ class productModel extends model
->autoCheck()
->batchCheck($this->config->product->edit->requiredFields , 'notempty')
->checkIF((!empty($product->name) and $this->config->systemMode == 'new'), 'name', 'unique', "id != $productID and `program` = $programID")
->checkFlow()
->where('id')->eq($productID)
->exec();
if(dao::isError()) return print(js::error('product#' . $productID . dao::getError(true)));
@@ -820,11 +865,13 @@ class productModel extends model
$oldProduct = $this->getById($productID);
$now = helper::now();
$product= fixer::input('post')
->add('id', $productID)
->setDefault('status', 'closed')
->remove('comment')->get();
$this->dao->update(TABLE_PRODUCT)->data($product)
->autoCheck()
->checkFlow()
->where('id')->eq((int)$productID)
->exec();
@@ -970,13 +1017,15 @@ class productModel extends model
* @param int $queryID
* @param int $actionURL
* @param int $branch
* @param int $projectID
* @access public
* @return void
*/
public function buildSearchForm($productID, $products, $queryID, $actionURL, $branch = 0)
public function buildSearchForm($productID, $products, $queryID, $actionURL, $branch = 0, $projectID = 0)
{
$productIdList = ($this->app->tab == 'project' and empty($productID)) ? array_keys($products) : $productID;
$branchParam = ($this->app->tab == 'project' and empty($productID)) ? '' : $branch;
$projectID = ($this->app->tab == 'project' and empty($projectID)) ? $this->session->project : $projectID;
$this->config->product->search['actionURL'] = $actionURL;
$this->config->product->search['queryID'] = $queryID;
@@ -992,7 +1041,7 @@ class productModel extends model
if($productID)
{
$modules = array();
$branchList = $this->loadModel('branch')->getPairs($productID, '', $this->session->project);
$branchList = $this->loadModel('branch')->getPairs($productID, '', $projectID);
$branchModuleList = $this->tree->getOptionMenu($productID, 'story', 0, array_keys($branchList));
foreach($branchModuleList as $branchID => $branchModules) $modules[] = $branchModules;
}
@@ -1000,7 +1049,7 @@ class productModel extends model
{
$moduleList = array();
$modules = array('/');
$branchGroup = $this->loadModel('execution')->getBranchByProduct(array_keys($products), $this->session->project, '');
$branchGroup = $this->loadModel('execution')->getBranchByProduct(array_keys($products), $projectID, '');
foreach($products as $productID => $productName)
{
if(isset($branchGroup[$productID]))
@@ -1042,6 +1091,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.
*
@@ -1562,13 +1636,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);
@@ -2017,6 +2091,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, '', '', 'iframe', true, "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.
*
+8 -12
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)?>
@@ -26,16 +27,15 @@
</div>
<div id="mainContent" class="main-row fade">
<?php if(empty($productStructure)):?>
<div class="cell<?php if($browseType == 'bySearch') echo ' show';?>" id="queryBox" data-module='product'></div>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->product->noProduct;?></span></p>
</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 +215,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->product->buildOperateMenu($product, 'browse');?></td>
</tr>
<?php endforeach;?>
<?php endif;?>
@@ -228,17 +223,18 @@
<?php endforeach;?>
</tbody>
</table>
<?php if(!empty($product) and $canBatchEdit):?>
<div class='table-footer'>
<?php echo $pager->show('left', 'pagerjs');?>
<?php if(!empty($product) and $canBatchEdit):?>
<div class="checkbox-primary check-all"><label><?php echo $lang->selectAll?></label></div>
<div class="table-actions btn-toolbar">
<?php
$actionLink = $this->createLink('product', 'batchEdit');
echo html::commonButton($lang->edit, "data-form-action='$actionLink'");
?>
<?php endif;?>
</div>
</div>
<?php endif;?>
</form>
</div>
<?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->product->buildOperateMenu($product, 'view');
?>
</div>
</div>
+103 -5
View File
@@ -432,6 +432,7 @@ class productplanModel extends model
->autoCheck()
->batchCheck($this->config->productplan->create->requiredFields, 'notempty')
->checkIF(!$this->post->future && !empty($_POST['begin']) && !empty($_POST['end']), 'end', 'ge', $plan->begin)
->checkFlow()
->exec();
if(!dao::isError())
{
@@ -475,6 +476,7 @@ class productplanModel extends model
$plan = fixer::input('post')->stripTags($this->config->productplan->editor->edit['id'], $this->config->allowedTags)
->setIF($this->post->future or empty($_POST['begin']), 'begin', $this->config->productplan->future)
->setIF($this->post->future or empty($_POST['end']), 'end', $this->config->productplan->future)
->add('id', $planID)
->remove('delta,uid,future')
->get();
@@ -512,6 +514,7 @@ class productplanModel extends model
->autoCheck()
->batchCheck($this->config->productplan->edit->requiredFields, 'notempty')
->checkIF(!$this->post->future && !empty($_POST['begin']) && !empty($_POST['end']), 'end', 'ge', $plan->begin)
->checkFlow()
->where('id')->eq((int)$planID)
->exec();
if(dao::isError()) return false;
@@ -638,6 +641,7 @@ class productplanModel extends model
$plan->end = isset($data->end[$planID]) ? $data->end[$planID] : '';
$plan->status = isset($data->status[$planID]) ? $data->status[$planID] : $oldPlans[$planID]->status;
$plan->parent = $oldPlans[$planID]->parent;
$plan->id = $planID;
if(empty($plan->title)) return print(js::alter(sprintf($this->lang->productplan->errorNoTitle, $planID)));
if($plan->begin > $plan->end and !empty($plan->end)) return print(js::alert(sprintf($this->lang->productplan->beginGeEnd, $planID)));
@@ -651,8 +655,6 @@ class productplanModel extends model
if(is_array($plan->{$extendField->field})) $plan->{$extendField->field} = join(',', $plan->{$extendField->field});
$plan->{$extendField->field} = htmlSpecialString($plan->{$extendField->field});
$message = $this->checkFlowRule($extendField, $plan->{$extendField->field});
if($message) return print(js::alert($message));
}
$plans[$planID] = $plan;
@@ -695,7 +697,7 @@ class productplanModel extends model
if($change)
{
if($parentID > 0 and !isset($parents[$parentID])) $parents[$parentID] = $parentID;
$this->dao->update(TABLE_PRODUCTPLAN)->data($plan)->autoCheck()->where('id')->eq($planID)->exec();
$this->dao->update(TABLE_PRODUCTPLAN)->data($plan)->autoCheck()->checkFlow()->where('id')->eq($planID)->exec();
if(dao::isError()) return print(js::error(dao::getError()));
$changes[$planID] = $change;
}
@@ -1000,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;
@@ -1023,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->productplan->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->productplan->buildOperateMenu($plan, 'view'); ?>
</div>
</div>
<div id='mainContent' class='main-content'>
+28
View File
@@ -22,3 +22,31 @@ $config->program->sortFields = new stdclass();
$config->program->sortFields->id = 'id';
$config->program->sortFields->begin = 'begin';
$config->program->sortFields->end = 'end';
global $lang;
$config->program->search['module'] = 'program';
$config->program->search['fields']['name'] = $lang->program->name;
$config->program->search['fields']['status'] = $lang->program->status;
$config->program->search['fields']['desc'] = $lang->program->desc;
$config->program->search['fields']['PM'] = $lang->program->PM;
$config->program->search['fields']['openedDate'] = $lang->program->openedDate;
$config->program->search['fields']['begin'] = $lang->program->begin;
$config->program->search['fields']['end'] = $lang->program->end;
$config->program->search['fields']['openedBy'] = $lang->program->openedBy;
$config->program->search['fields']['lastEditedDate'] = $lang->program->lastEditedDate;
$config->program->search['fields']['realBegan'] = $lang->program->realBegin;
$config->program->search['fields']['realEnd'] = $lang->program->realEnd;
$config->program->search['fields']['closedDate'] = $lang->program->closedDate;
$config->program->search['params']['name'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->program->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => array('' => '') + $lang->program->statusList);
$config->program->search['params']['desc'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->program->search['params']['PM'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->program->search['params']['openedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->program->search['params']['begin'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->program->search['params']['end'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->program->search['params']['openedBy'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->program->search['params']['lastEditedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->program->search['params']['realBegan'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->program->search['params']['realEnd'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->program->search['params']['closedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+23 -2
View File
@@ -26,13 +26,16 @@ class program extends control
* @access public
* @return void
*/
public function browse($status = 'all', $orderBy = 'order_asc')
public function browse($status = 'all', $orderBy = 'order_asc', $recTotal = 0, $recPerPage = 10, $pageID = 1, $param = 0)
{
if(common::hasPriv('program', 'create')) $this->lang->pageActions = html::a($this->createLink('program', 'create'), "<i class='icon icon-plus'></i> " . $this->lang->program->create, '', "class='btn btn-primary create-program-btn'");
$this->session->set('programList', $this->app->getURI(true), 'program');
$this->session->set('projectList', $this->app->getURI(true), 'program');
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$programType = $this->cookie->programType ? $this->cookie->programType : 'bylist';
if($programType === 'bygrid')
@@ -41,7 +44,18 @@ class program extends control
}
else
{
$programs = $this->program->getList($status, $orderBy, null, true);
if(strtolower($status) == 'bysearch')
{
$queryID = (int)$param;
$programs = $this->program->getListBySearch($orderBy, $pager, $queryID);
}
else
{
$topPrograms = $this->program->getList($status, $orderBy, $pager, 'top');
$programIds = array();
foreach($topPrograms as $programID => $program) $programIds[] = $programID;
$programs = $this->program->getList($status, $orderBy, null, 'child', $programIds);
}
}
/* Get PM id list. */
@@ -52,12 +66,19 @@ class program extends control
}
$PMList = $this->loadModel('user')->getListByAccounts($accounts, 'account');
/* Build the search form. */
$actionURL = $this->createLink('program', 'browse', "status=bySearch&orderBy={$orderBy}&recTotal={$recTotal}&recPerPage={$recPerPage}&pageID={$pageID}&param=myQueryID");
$this->config->program->search['onMenuBar'] = 'yes';
$this->config->program->search['actionURL'] = $actionURL;
$this->loadModel('search')->setSearchParams($this->config->program->search);
$this->view->title = $this->lang->program->browse;
$this->view->position[] = $this->lang->program->browse;
$this->view->programs = $programs;
$this->view->status = $status;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->users = $this->user->getPairs('noletter');
$this->view->userIdPairs = $this->user->getPairs('noletter|showid');
$this->view->usersAvatar = $this->user->getAvatarPairs('');
+7
View File
@@ -43,6 +43,13 @@ $(function()
$('#programForm').removeClass('has-row-checked');
}
});
var myPager = $('.pager').data('zui.pager');
if(status != 'bySearch' && myPager)
{
myPager.lang.pageSize = pageSize;
myPager.set();
}
});
function showEditCheckbox(show)
+32 -28
View File
@@ -1,33 +1,36 @@
<?php
/* Fields. */
$lang->program->id = 'ID';
$lang->program->name = 'Name';
$lang->program->template = 'Template';
$lang->program->category = 'Type';
$lang->program->desc = 'Description';
$lang->program->status = 'Status';
$lang->program->PM = 'Manager';
$lang->program->budget = 'Budget';
$lang->program->budgetUnit = 'Budget Unit';
$lang->program->begin = 'Begin';
$lang->program->end = 'End';
$lang->program->stage = 'Stage';
$lang->program->type = 'Type';
$lang->program->pri = 'Priority';
$lang->program->parent = 'Parent';
$lang->program->exchangeRate = 'Exchange Rate';
$lang->program->openedBy = 'OpenedBy';
$lang->program->openedDate = 'OpenedDate';
$lang->program->closedBy = 'ClosedBy';
$lang->program->closedDate = 'ClosedDate';
$lang->program->canceledBy = 'CanceledBy';
$lang->program->canceledDate = 'CanceledDate';
$lang->program->team = 'Team';
$lang->program->order = 'Rank';
$lang->program->days = 'Days';
$lang->program->acl = 'Access Control';
$lang->program->whitelist = 'WhiteList';
$lang->program->deleted = 'Deleted';
$lang->program->id = 'ID';
$lang->program->name = 'Name';
$lang->program->template = 'Template';
$lang->program->category = 'Type';
$lang->program->desc = 'Description';
$lang->program->status = 'Status';
$lang->program->PM = 'Manager';
$lang->program->budget = 'Budget';
$lang->program->budgetUnit = 'Budget Unit';
$lang->program->begin = 'Begin';
$lang->program->end = 'End';
$lang->program->realBegin = 'Actual Begin';
$lang->program->realEnd = 'Actual End';
$lang->program->stage = 'Stage';
$lang->program->type = 'Type';
$lang->program->pri = 'Priority';
$lang->program->parent = 'Parent';
$lang->program->exchangeRate = 'Exchange Rate';
$lang->program->openedBy = 'OpenedBy';
$lang->program->openedDate = 'OpenedDate';
$lang->program->closedBy = 'ClosedBy';
$lang->program->closedDate = 'ClosedDate';
$lang->program->canceledBy = 'CanceledBy';
$lang->program->canceledDate = 'CanceledDate';
$lang->program->lastEditedDate = 'LastEditedDate';
$lang->program->team = 'Team';
$lang->program->order = 'Rank';
$lang->program->days = 'Days';
$lang->program->acl = 'Access Control';
$lang->program->whitelist = 'WhiteList';
$lang->program->deleted = 'Deleted';
/* Actions. */
$lang->program->common = 'Program';
@@ -71,6 +74,7 @@ $lang->program->moreProgram = 'More program';
$lang->program->stakeholderType = 'Stakeholder type';
$lang->program->parentBudget = 'Parent program surplus budget:';
$lang->program->isStakeholderKey = 'Key stakeholder';
$lang->program->pageSize = '<strong>{recPerPage}</strong> Top Programs per page';
$lang->program->stakeholderTypeList['inside'] = 'Inside';
$lang->program->stakeholderTypeList['outside'] = 'Outside';
+32 -28
View File
@@ -1,33 +1,36 @@
<?php
/* Fields. */
$lang->program->id = '编号';
$lang->program->name = '项目集名称';
$lang->program->template = '项目集模板';
$lang->program->category = '项目集类型';
$lang->program->desc = '项目集描述';
$lang->program->status = '状态';
$lang->program->PM = '负责人';
$lang->program->budget = '预算';
$lang->program->budgetUnit = '预算单位';
$lang->program->begin = '计划开始';
$lang->program->end = '计划完成';
$lang->program->stage = '阶段';
$lang->program->type = '类型';
$lang->program->pri = '优先级';
$lang->program->parent = '父项目集';
$lang->program->exchangeRate = '汇率';
$lang->program->openedBy = '由谁创建';
$lang->program->openedDate = '创建日期';
$lang->program->closedBy = '由谁关闭';
$lang->program->closedDate = '关闭日期';
$lang->program->canceledBy = '由谁取消';
$lang->program->canceledDate = '取消日期';
$lang->program->team = '团队';
$lang->program->order = '排序';
$lang->program->days = '可用工作日';
$lang->program->acl = '访问控制';
$lang->program->whitelist = '白名单';
$lang->program->deleted = '已删除';
$lang->program->id = '编号';
$lang->program->name = '项目集名称';
$lang->program->template = '项目集模板';
$lang->program->category = '项目集类型';
$lang->program->desc = '项目集描述';
$lang->program->status = '状态';
$lang->program->PM = '负责人';
$lang->program->budget = '预算';
$lang->program->budgetUnit = '预算单位';
$lang->program->begin = '计划开始';
$lang->program->end = '计划完成';
$lang->program->realBegin = '实际开始';
$lang->program->realEnd = '实际完成';
$lang->program->stage = '阶段';
$lang->program->type = '类型';
$lang->program->pri = '优先级';
$lang->program->parent = '父项目集';
$lang->program->exchangeRate = '汇率';
$lang->program->openedBy = '由谁创建';
$lang->program->openedDate = '创建日期';
$lang->program->closedBy = '由谁关闭';
$lang->program->closedDate = '关闭日期';
$lang->program->canceledBy = '由谁取消';
$lang->program->canceledDate = '取消日期';
$lang->program->lastEditedDate = '最后编辑';
$lang->program->team = '团队';
$lang->program->order = '排序';
$lang->program->days = '可用工作日';
$lang->program->acl = '访问控制';
$lang->program->whitelist = '白名单';
$lang->program->deleted = '已删除';
/* Actions. */
$lang->program->common = '项目集';
@@ -71,6 +74,7 @@ $lang->program->moreProgram = '更多项目集';
$lang->program->stakeholderType = '干系人类型';
$lang->program->parentBudget = '所属项目集剩余预算:';
$lang->program->isStakeholderKey = '关键干系人';
$lang->program->pageSize = '每页 <strong>{recPerPage}</strong> 项顶级项目集';
$lang->program->stakeholderTypeList['inside'] = '内部';
$lang->program->stakeholderTypeList['outside'] = '外部';
+66 -4
View File
@@ -130,11 +130,22 @@ class programModel extends model
* @param string $status
* @param string $orderBy
* @param object $pager
* @param string $type top|child
* @param mixed $idList
* @access public
* @return array
*/
public function getList($status = 'all', $orderBy = 'id_asc', $pager = NULL)
public function getList($status = 'all', $orderBy = 'id_asc', $pager = NULL, $type = '', $idList = '')
{
$projectIdList = array();
if($type === 'child')
{
foreach($idList as $topID)
{
$projectIdList += $this->dao->select('id')->from(TABLE_PROGRAM)->Where('path')->like(",$topID,%")->fetchPairs('id');
}
}
return $this->dao->select('*')->from(TABLE_PROGRAM)
->where('deleted')->eq(0)
->andWhere('vision')->eq($this->config->vision)
@@ -146,6 +157,54 @@ class programModel extends model
->markRight(2)
->beginIF($status != 'all')->andWhere('status')->eq($status)->fi()
->beginIF(!$this->cookie->showClosed)->andWhere('status')->ne('closed')->fi()
->beginIF($type === 'top')->andWhere('parent')->eq(0)->fi()
->beginIF(!empty($projectIdList))->andWhere('id')->in($projectIdList)->fi()
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
}
/**
* Get program list by search.
*
* @param string $orderBy
* @param object $pager
* @param int $queryID
* @access public
* @return void
*/
public function getListBySearch($orderBy = 'id_asc', $pager = NULL, $queryID = 0)
{
if($queryID)
{
$query = $this->loadModel('search')->getQuery($queryID);
if($query)
{
$this->session->set('programQuery', $query->sql);
$this->session->set('programForm', $query->form);
}
else
{
$this->session->set('programQuery', ' 1 = 1');
}
}
else
{
if($this->session->programQuery == false) $this->session->set('programQuery', ' 1 = 1');
}
$query = $this->session->programQuery;
return $this->dao->select('*')->from(TABLE_PROGRAM)
->where('deleted')->eq(0)
->andWhere('vision')->eq($this->config->vision)
->andWhere('((type')->eq('program')
->beginIF(!$this->app->user->admin and $this->app->rawMethod != 'browse')->andWhere('id')->in($this->app->user->view->programs)->fi()
->markRight(1)
->orWhere('(type')->eq('project')
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi()
->markRight(2)
->beginIF(!$this->cookie->showClosed)->andWhere('status')->ne('closed')->fi()
->beginIF($query)->andWhere($query)->fi()
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
@@ -240,15 +299,15 @@ class programModel extends model
foreach($products as $product)
{
$product->plans = zget($plans, $product->id, array());
/* Convert predefined HTML entities to characters. */
!empty($product->plans) && array_map(function($planVal)
{
return $planVal->title = htmlspecialchars_decode($planVal->title, ENT_QUOTES);
},
},
$product->plans);
$product->name = htmlspecialchars_decode($product->name, ENT_QUOTES);
$product->releases = zget($releases, $product->id, array());
$projects = zget($projectGroup, $product->id, array());
foreach($projects as $project)
@@ -657,6 +716,7 @@ class programModel extends model
->checkIF($program->end != '', 'end', 'date')
->checkIF($program->end != '', 'end', 'gt', $program->begin)
->checkIF(!empty($program->name), 'name', 'unique', "`type`='program' and `parent` = $program->parent")
->checkFlow()
->exec();
if(!dao::isError())
@@ -689,6 +749,7 @@ class programModel extends model
$oldProgram = $this->dao->findById($programID)->from(TABLE_PROGRAM)->fetch();
$program = fixer::input('post')
->add('id', $programID)
->setDefault('team', $this->post->name)
->setDefault('end', '')
->setIF($this->post->begin == '0000-00-00', 'begin', '')
@@ -755,6 +816,7 @@ class programModel extends model
->checkIF($program->end != '', 'end', 'date')
->checkIF($program->end != '', 'end', 'gt', $program->begin)
->checkIF(!empty($program->name), 'name', 'unique', "id!=$programID and `type`='program' and `parent` = $program->parent")
->checkFlow()
->where('id')->eq($programID)
->limit(1)
->exec();
+4
View File
@@ -15,6 +15,7 @@
<?php js::set('orderBy', $orderBy);?>
<?php js::set('edit', $lang->edit);?>
<?php js::set('selectAll', $lang->selectAll);?>
<?php js::set('pageSize', $lang->program->pageSize);?>
<?php if($programType == 'bygrid'):?>
<style> #mainMenu{padding-left: 10px; padding-right: 10px;} </style>
<?php endif;?>
@@ -27,6 +28,7 @@
<?php endforeach;?>
<?php echo html::checkbox('showClosed', array('1' => $lang->program->showClosed), '', $this->cookie->showClosed ? 'checked=checked' : '');?>
<?php if(common::hasPriv('project', 'batchEdit') and $programType != 'bygrid') echo html::checkbox('editProject', array('1' => $lang->project->edit), '', $this->cookie->editProject ? 'checked=checked' : '');?>
<a class="btn btn-link querybox-toggle" id='bysearchTab'><i class="icon icon-search muted"></i> <?php echo $lang->user->search;?></a>
</div>
<div class='pull-right'>
<?php if(common::hasPriv('project', 'create')) common::printLink('project', 'createGuide', "programID=0&from=PGM", '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-secondary" data-toggle="modal" data-target="#guideDialog"');?>
@@ -35,6 +37,7 @@
</div>
<div id='mainContent' class='main-row'>
<?php if(empty($programs)):?>
<div class="cell<?php if($status == 'bySearch') echo ' show';?>" id="queryBox" data-module='program'></div>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->program->noProgram;?></span>
@@ -43,6 +46,7 @@
</div>
<?php else:?>
<div class='main-col'>
<div class="cell<?php if($status == 'bySearch') echo ' show';?>" id="queryBox" data-module='program'></div>
<?php
if($programType == 'bygrid')
{
+4 -3
View File
@@ -119,9 +119,9 @@
</div>
<?php endif;?>
<?php $class = $program->model == 'kanban' ? 'disabled' : '';?>
<?php common::printIcon('project', 'edit', "projectID=$program->id", $program, 'list', 'edit', '', 'iframe', true);?>
<?php common::printIcon('project', 'team', "projectID=$program->id", $program, 'list', 'group', '', "$class", '', 'data-app="project"');?>
<?php common::printIcon('project', 'group', "projectID=$program->id", $program, 'list', 'lock', '', "$class", '', 'data-app="project"');?>
<?php common::printIcon('project', 'edit', "projectID=$program->id", $program, 'list', 'edit', '', 'iframe', true);?>
<?php common::printIcon('project', 'team', "projectID=$program->id", $program, 'list', 'group', '', "$class", '', 'data-app="project"');?>
<?php common::printIcon('project', 'group', "projectID=$program->id", $program, 'list', 'lock', '', "$class", '', 'data-app="project"');?>
<?php if(common::hasPriv('project', 'manageProducts') || common::hasPriv('project', 'whitelist') || common::hasPriv('project', 'delete')):?>
<div class='btn-group'>
<button type='button' class='btn dropdown-toggle' data-toggle='dropdown' title="<?php echo $this->lang->more;?>"><i class='icon-more-alt'></i></button>
@@ -138,6 +138,7 @@
<?php endforeach;?>
</tbody>
</table>
<div class='table-footer'><?php $pager->show('right', 'pagerjs');?></div>
</form>
<style>
#programTableList.sortable-sorting > tr {opacity: 0.7}
+37
View File
@@ -132,6 +132,43 @@ $config->project->maxCheckList = new stdclass();
$config->project->maxCheckList->scrum = array('bug', 'execution', 'build', 'doc', 'release', 'testtask', 'case', 'issue', 'risk', 'meeting');
$config->project->maxCheckList->waterfall = array('execution', 'design', 'doc', 'bug', 'case', 'build', 'release', 'testtask', 'review', 'build', 'researchplan', 'issue', 'risk', 'opportunity', 'auditplan', 'gapanalysis', 'meeting');
$config->project->search['module'] = 'project';
$config->project->search['fields']['name'] = $lang->project->name;
$config->project->search['fields']['code'] = $lang->project->code;
$config->project->search['fields']['id'] = $lang->project->id;
$config->project->search['fields']['model'] = $lang->project->model;
$config->project->search['fields']['parent'] = $lang->project->parent;
$config->project->search['fields']['status'] = $lang->project->status;
$config->project->search['fields']['desc'] = $lang->project->desc;
$config->project->search['fields']['PM'] = $lang->project->PM;
$config->project->search['fields']['openedDate'] = $lang->project->openedDate;
$config->project->search['fields']['begin'] = $lang->project->begin;
$config->project->search['fields']['end'] = $lang->project->end;
$config->project->search['fields']['realBegan'] = $lang->project->realBeganAB;
$config->project->search['fields']['realEnd'] = $lang->project->realEndAB;
$config->project->search['fields']['openedBy'] = $lang->project->openedBy;
$config->project->search['fields']['closedBy'] = $lang->project->closedBy;
$config->project->search['fields']['lastEditedDate'] = $lang->project->lastEditedDate;
$config->project->search['fields']['closedDate'] = $lang->project->closedDate;
$config->project->search['params']['name'] = array('operator' => 'include', 'control' => 'input' , 'values' => '');
$config->project->search['params']['code'] = array('operator' => '=' , 'control' => 'input' , 'values' => '');
$config->project->search['params']['id'] = array('operator' => '=' , 'control' => 'input' , 'values' => '');
$config->project->search['params']['model'] = array('operator' => '=' , 'control' => 'select', 'values' => $lang->project->modelList);
$config->project->search['params']['parent'] = array('operator' => '=' , 'control' => 'select', 'values' => '');
$config->project->search['params']['status'] = array('operator' => '=' , 'control' => 'select', 'values' => $lang->project->statusList);
$config->project->search['params']['desc'] = array('operator' => 'include', 'control' => 'input' , 'values' => '');
$config->project->search['params']['PM'] = array('operator' => '=' , 'control' => 'select', 'values' => 'users');
$config->project->search['params']['openedDate'] = array('operator' => '=' , 'control' => 'input' , 'values' => '', 'class' => 'date');
$config->project->search['params']['begin'] = array('operator' => '=' , 'control' => 'input' , 'values' => '', 'class' => 'date');
$config->project->search['params']['end'] = array('operator' => '=' , 'control' => 'input' , 'values' => '', 'class' => 'date');
$config->project->search['params']['realBegan'] = array('operator' => '=' , 'control' => 'input' , 'values' => '', 'class' => 'date');
$config->project->search['params']['realEnd'] = array('operator' => '=' , 'control' => 'input' , 'values' => '', 'class' => 'date');
$config->project->search['params']['openedBy'] = array('operator' => '=' , 'control' => 'select', 'values' => 'users');
$config->project->search['params']['closedBy'] = array('operator' => '=' , 'control' => 'select', 'values' => 'users');
$config->project->search['params']['lastEditedDate'] = array('operator' => '=' , 'control' => 'input' , 'values' => '', 'class' => 'date');
$config->project->search['params']['closedDate'] = array('operator' => '=' , 'control' => 'input' , 'values' => '', 'class' => 'date');
$config->project->includedPriv['project'] = array('edit', 'group', 'createGroup', 'managePriv', 'manageMembers', 'manageGroupMember', 'copyGroup', 'editGroup', 'start', 'suspend', 'close', 'activate', 'delete', 'view', 'whitelist', 'addWhitelist', 'unbindWhitelist', 'manageProducts', 'dynamic', 'build', 'bug', 'testcase', 'testtask', 'testreport', 'execution', 'team', 'unlinkMember');
$config->project->includedPriv['execution'] = array('create', 'start', 'delete', 'calendar', 'effortCalendar', 'effort', 'taskEffort', 'computeTaskEffort', 'deleterelation', 'maintainrelation', 'relation', 'gantt');
$config->project->includedPriv['task'] = array('create');
+6
View File
@@ -127,6 +127,7 @@ class project extends control
unset($projects[$project->id]);
}
$this->view->link = $this->project->getProjectLink($module, $method, $projectID);
$this->view->projectID = $projectID;
$this->view->projects = $orderedProjects;
$this->view->module = $module;
@@ -307,6 +308,11 @@ class project extends control
$programTitle = $this->loadModel('setting')->getItem('owner=' . $this->app->user->account . '&module=project&key=programTitle');
$projectStats = $this->loadModel('program')->getProjectStats($programID, $browseType, $queryID, $orderBy, $pager, $programTitle);
$actionURL = $this->createLink('project', 'browse', "&programID=$programID&browseType=bySearch&queryID=myQueryID");
$this->project->buildSearchFrom($queryID, $actionURL);
$projectQuery = $this->session->projectQuery;
if($browseType == 'bySearch') $projectStats = $this->project->getBySearch($projectQuery);
$this->view->title = $this->lang->project->browse;
$this->view->position[] = $this->lang->project->browse;
+2
View File
@@ -227,6 +227,7 @@ $lang->project->currencySymbol['NZD'] = 'NZ$';
$lang->project->currencySymbol['THB'] = '฿';
$lang->project->currencySymbol['SGD'] = 'S$';
$lang->project->modelList[''] = '';
$lang->project->modelList['scrum'] = "Scrum";
$lang->project->modelList['waterfall'] = "CMMI";
$lang->project->modelList['kanban'] = "Kanban";
@@ -257,6 +258,7 @@ $lang->project->kanbanSubAclList['program'] = 'Open in the program (all upper-le
$lang->project->authList['extend'] = 'Inherit (system privilege and project privilege)';
$lang->project->authList['reset'] = 'Reset (project privilege only)';
$lang->project->statusList[''] = '';
$lang->project->statusList['wait'] = 'Waiting';
$lang->project->statusList['doing'] = 'Doing';
$lang->project->statusList['suspended'] = 'Suspended';
+2
View File
@@ -227,6 +227,7 @@ $lang->project->currencySymbol['NZD'] = 'NZ$';
$lang->project->currencySymbol['THB'] = '฿';
$lang->project->currencySymbol['SGD'] = 'S$';
$lang->project->modelList[''] = "";
$lang->project->modelList['scrum'] = "Scrum";
$lang->project->modelList['waterfall'] = "瀑布";
$lang->project->modelList['kanban'] = "看板";
@@ -257,6 +258,7 @@ $lang->project->kanbanSubAclList['program'] = '项目集内公开(所有上级
$lang->project->authList['extend'] = '继承 (取系统权限与项目权限的合集)';
$lang->project->authList['reset'] = '重新定义 (只取项目权限)';
$lang->project->statusList[''] = '';
$lang->project->statusList['wait'] = '未开始';
$lang->project->statusList['doing'] = '进行中';
$lang->project->statusList['suspended'] = '已挂起';
+365 -58
View File
@@ -457,6 +457,202 @@ class projectModel extends model
return $projects;
}
/**
* Create the link from module,method.
*
* @param string $module
* @param string $method
* @param int $projectID
* @access public
* @return string
*/
public function getProjectLink($module, $method, $projectID)
{
$link = helper::createLink('project', 'index', "projectID=%s");
$project = $this->getByID($projectID);
if(strpos(',project,product,projectstory,story,bug,doc,testcase,testtask,testreport,build,projectrelease,stakeholder,issue,risk,meeting,report,measrecord', ',' . $module . ',') !== false)
{
if($module == 'project' and $method == 'execution')
{
$link = helper::createLink($module, $method, "status=all&projectID=%s");
}
elseif($module == 'project' and strpos(',bug,testcase,testtask,testreport,build,dynamic,view,manageproducts,team,managemembers,whitelist,addwhitelist,group,', ',' . $method . ','))
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'project' and $method == 'managePriv')
{
$link = helper::createLink($module, 'group', "projectID=%s");
}
elseif($module == 'product' and $method == 'showerrornone')
{
$link = helper::createLink('projectstory', 'story', "projectID=%s");
}
elseif($module == 'projectstory' and $method == 'story')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'projectstory' and $method == 'linkstory')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'projectstory' and $method = 'track')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'bug')
{
if($method == 'create')
{
$link = helper::createLink($module, $method, "productID=0&branch=0&extras=projectID=%s");
}
elseif($method == 'edit')
{
$link = helper::createLink('project', 'bug', "projectID=%s");
}
}
elseif($module == 'story')
{
if($method == 'create')
{
$link = helper::createLink($module, $method);
}
elseif($method == 'change')
{
$link = helper::createLink('projectstory', 'story', "projectID=%s");
}
elseif($method == 'zerocase')
{
$link = helper::createLink('project', 'testcase', "projectID=%s");
}
}
elseif($module == 'testcase' and $method == 'create')
{
if($method == 'create')
{
$link = helper::createLink($module, $method);
}
elseif($method == 'edit')
{
$link = helper::createLink('project', 'testcase', "projectID=%s");
}
}
elseif($module == 'testtask')
{
if($method == 'create')
{
$link = helper::createLink($module, $method, "product=0&executionID=0&build=0&projectID=%s");
}
else
{
$link = helper::createLink($module, 'browseunits');
}
}
elseif($module == 'testreport')
{
$link = helper::createLink('project', 'testreport', "projectID=%s");
}
elseif($module == 'repo')
{
if($method == 'create')
{
$link = helper::createLink($module, $method, "objectID=%s#app=project");
}
else
{
$link = helper::createLink($module, 'browse', "repoID=&branchID=&objectID=%s#app=project");
}
}
elseif($module == 'doc')
{
$link = helper::createLink($module, 'tablecontents', "type=project&objectID=%s#app=project");
}
elseif($module == 'build')
{
if($method == 'create')
{
$link = helper::createLink($module, $method, "executionID=&productID=&projectID=%s#app=project");
}
else
{
$link = helper::createLink('project', 'build', "projectID=%s");
}
}
elseif($module == 'projectrelease')
{
if($method == 'create')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
else
{
$link = helper::createLink('projectrelease', 'browse', "projectID=%s");
}
}
elseif($module == 'stakeholder')
{
if($method == 'create')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
else
{
$link = helper::createLink($module, 'browse', "projectID=%s");
}
}
elseif(strpos("issue,risk,meeting,report,measrecord", $module) !== false)
{
if($method == 'projectsummary')
{
$link = helper::createLink($module, $method, "projectID=%s#app=project");
}
else
{
$link = helper::createLink($module, 'browse', "projectID=%s");
}
}
}
if(in_array($module, $this->config->waterfallModules))
{
$link = helper::createLink($module, 'browse', "projectID=%s");
if($module == 'reviewissue')
{
$link = helper::createLink($module, 'issue', "projectID=%s");
}
elseif($module == 'cm' and $method = 'report')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'weekly' and $method = 'index')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'milestone' and $method = 'index')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'workestimation' and $method = 'index')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'durationestimation' and $method = 'index')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
elseif($module == 'budget' and $method = 'summary')
{
$link = helper::createLink($module, $method, "projectID=%s");
}
}
if($project->model == 'kanban') $link = helper::createLink('project', 'index', "projectID=%s");
return $link;
}
/**
* Get project stat data .
*
@@ -517,7 +713,7 @@ class projectModel extends model
->andWhere('deleted')->eq(0)
->andWhere('vision')->eq($this->config->vision)
->beginIF($programID !== '')->andWhere('parent')->eq($programID)->fi()
->beginIF($status != 'all' && $status != 'noclosed')->andWhere('status')->eq($status)->fi()
->beginIF($status != 'all' and $status != 'noclosed')->andWhere('status')->eq($status)->fi()
->beginIF($excludedModel)->andWhere('model')->ne($excludedModel)->fi()
->beginIF($status == 'noclosed')->andWhere('status')->ne('closed')->fi()
->beginIF(!$this->app->user->admin and !$isQueryAll)->andWhere('id')->in($this->app->user->view->projects)->fi()
@@ -629,6 +825,39 @@ class projectModel extends model
->fetchGroup('productID', 'branchID');
}
/*
* Get projects by search conditions.
*
* @param string $projectQuery
* @return array
* */
public function getBySearch($projectQuery)
{
return $this->dao->select('*')->from(TABLE_PROJECT)
->where($projectQuery)
->andWhere('type')->eq('project')
->fetchAll();
}
/*
* Build search form
*
* @param int $queryID
* @param string $actionURL
*
* @return 0
* */
public function buildSearchFrom($queryID, $actionURL)
{
$this->config->project->search['queryID'] = $queryID;
$this->config->project->search['actionURL'] = $actionURL;
$programPairs = array(0 => '');
$programPairs += $this->loadModel('program')->getPairs();
$this->config->project->search['params']['parent']['values'] = $programPairs;
$this->loadModel('search')->setSearchParams($this->config->project->search);
}
/**
* Build the query.
*
@@ -913,6 +1142,7 @@ class projectModel extends model
->checkIF(!empty($project->name), 'name', 'unique', "`type`='project' and `parent` = $project->parent and `model` = '{$project->model}'")
->checkIF(!empty($project->code), 'code', 'unique', "`type`='project' and `model` = '{$project->model}'")
->checkIF($project->end != '', 'end', 'gt', $project->begin)
->checkFlow()
->exec();
/* Add the creater to the team. */
@@ -980,7 +1210,7 @@ class projectModel extends model
$this->updateProducts($projectID);
if(isset($_POST['newProduct']) || (!$project->parent && empty($linkedProductsCount)))
if(isset($_POST['newProduct']) or (!$project->parent and empty($linkedProductsCount)))
{
/* If parent not empty, link products or create products. */
$product = new stdclass();
@@ -1066,6 +1296,7 @@ class projectModel extends model
$_POST['products'] = isset($_POST['products']) ? array_filter($_POST['products']) : $linkedProducts;
$project = fixer::input('post')
->add('id', $projectID)
->callFunc('name', 'trim')
->setDefault('team', substr($this->post->name, 0, 30))
->setDefault('lastEditedBy', $this->app->user->account)
@@ -1145,6 +1376,7 @@ class projectModel extends model
->checkIF($project->end != '', 'end', 'gt', $project->begin)
->checkIF(!empty($project->name), 'name', 'unique', "id != $projectID and `type` = 'project' and `parent` = $project->parent and `model` = '{$project->model}'")
->checkIF(!empty($project->code), 'code', 'unique', "id != $projectID and `type` = 'project' and `model` = '{$project->model}'")
->checkFlow()
->where('id')->eq($projectID)
->exec();
@@ -1237,6 +1469,7 @@ class projectModel extends model
$projects[$projectID] = new stdClass();
if(isset($data->parents[$projectID])) $projects[$projectID]->parent = $data->parents[$projectID];
$projects[$projectID]->id = $projectID;
$projects[$projectID]->name = $projectName;
$projects[$projectID]->PM = $data->PMs[$projectID];
$projects[$projectID]->begin = $data->begins[$projectID];
@@ -1267,8 +1500,6 @@ class projectModel extends model
if(is_array($projects[$projectID]->{$extendField->field})) $projects[$projectID]->{$extendField->field} = join(',', $projects[$projectID]->{$extendField->field});
$projects[$projectID]->{$extendField->field} = htmlSpecialString($projects[$projectID]->{$extendField->field});
$message = $this->checkFlowRule($extendField, $projects[$projectID]->{$extendField->field});
if($message) hepler::end(js::alert($message));
}
}
if(dao::isError()) return print(js::error(dao::getError()));
@@ -1286,6 +1517,7 @@ class projectModel extends model
->checkIF($project->end != '', 'end', 'gt', $project->begin)
->checkIF(!empty($project->name), 'name', 'unique', "id != $projectID and `type`='project' and `parent` = $parentID and `model` = '{$project->model}'")
->checkIF(!empty($project->code), 'code', 'unique', "id != $projectID and `type`='project' and `model` = '{$project->model}'")
->checkFlow()
->where('id')->eq($projectID)
->exec();
@@ -1319,6 +1551,7 @@ class projectModel extends model
$now = helper::now();
$project = fixer::input('post')
->add('id', $projectID)
->setDefault('status', 'doing')
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
@@ -1328,6 +1561,7 @@ class projectModel extends model
->autoCheck()
->check($this->config->project->start->requiredFields, 'notempty')
->checkIF($project->realBegan != '', 'realBegan', 'le', helper::today())
->checkFlow()
->where('id')->eq((int)$projectID)
->exec();
@@ -1350,6 +1584,7 @@ class projectModel extends model
$now = helper::now();
$project = fixer::input('post')
->add('id', $projectID)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
->remove('comment')
@@ -1357,6 +1592,7 @@ class projectModel extends model
$this->dao->update(TABLE_PROJECT)->data($project)
->autoCheck()
->checkFlow()
->where('id')->eq((int)$projectID)
->exec();
@@ -1374,6 +1610,7 @@ class projectModel extends model
{
$oldProject = $this->getById($projectID);
$project = fixer::input('post')
->add('id', $projectID)
->setDefault('status', 'suspended')
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', helper::now())
@@ -1382,6 +1619,7 @@ class projectModel extends model
$this->dao->update(TABLE_PROJECT)->data($project)
->autoCheck()
->checkFlow()
->where('id')->eq((int)$projectID)
->exec();
@@ -1401,6 +1639,7 @@ class projectModel extends model
$now = helper::now();
$project = fixer::input('post')
->add('id', $projectID)
->setDefault('realEnd','')
->setDefault('status', 'doing')
->setDefault('lastEditedBy', $this->app->user->account)
@@ -1417,6 +1656,7 @@ class projectModel extends model
$this->dao->update(TABLE_PROJECT)->data($project)
->autoCheck()
->checkFlow()
->where('id')->eq((int)$projectID)
->exec();
@@ -1471,6 +1711,7 @@ class projectModel extends model
$now = helper::now();
$project = fixer::input('post')
->add('id', $projectID)
->setDefault('status', 'closed')
->setDefault('closedBy', $this->app->user->account)
->setDefault('closedDate', $now)
@@ -1486,6 +1727,7 @@ class projectModel extends model
->check($this->config->project->close->requiredFields, 'notempty')
->checkIF($project->realEnd != '', 'realEnd', 'le', helper::today())
->checkIF($project->realEnd != '', 'realEnd', 'ge', $oldProject->realBegan)
->checkFlow()
->where('id')->eq((int)$projectID)
->exec();
@@ -1706,11 +1948,11 @@ class projectModel extends model
if($project->model === 'scrum') $prefix = "<span class='project-type-label label label-outline label-info'>{$this->lang->project->scrum}</span> ";
if($project->model === 'kanban') $prefix = "<span class='project-type-label label label-outline label-info'>{$this->lang->project->kanban}</span> ";
if(isset($project->delay)) $suffix = "<span class='label label-danger label-badge'>{$this->lang->project->statusList['delay']}</span>";
if(!empty($suffix) || !empty($prefix)) echo '<div class="project-name' . (empty($prefix) ? '' : ' has-prefix') . (empty($suffix) ? '' : ' has-suffix') . '">';
if(!empty($suffix) or !empty($prefix)) echo '<div class="project-name' . (empty($prefix) ? '' : ' has-prefix') . (empty($suffix) ? '' : ' has-suffix') . '">';
if(!empty($prefix)) echo $prefix;
echo html::a($projectLink, $project->name, '', "class='text-ellipsis text-primary'");
if(!empty($suffix)) echo $suffix;
if(!empty($suffix) || !empty($prefix)) echo '</div>';
if(!empty($suffix) or !empty($prefix)) echo '</div>';
break;
case 'code':
echo $project->code;
@@ -1750,54 +1992,8 @@ class projectModel extends model
echo html::ring($project->hours->progress);
break;
case 'actions':
$moduleName = $this->config->systemMode == 'classic' ? "execution" : "project";
if($project->status == 'wait' || $project->status == 'suspended') common::printIcon($moduleName, 'start', "projectID=$project->id", $project, 'list', 'play', '', 'iframe', true);
if($project->status == 'doing') common::printIcon($moduleName, 'close', "projectID=$project->id", $project, 'list', 'off', '', 'iframe', true);
if($project->status == 'closed') common::printIcon($moduleName, 'activate', "projectID=$project->id", $project, 'list', 'magic', '', 'iframe', true);
if(common::hasPriv($moduleName, 'suspend') || (common::hasPriv($moduleName, 'close') && $project->status != 'doing') || (common::hasPriv($moduleName, 'activate') && $project->status != 'closed'))
{
echo "<div class='btn-group'>";
echo "<button type='button' class='btn icon-caret-down dropdown-toggle' data-toggle='context-dropdown' title='{$this->lang->more}' style='width: 16px; padding-left: 0px; border-radius: 4px;'></button>";
echo "<ul class='dropdown-menu pull-right text-center' role='menu' style='position: unset; min-width: auto; padding: 5px 6px;'>";
common::printIcon($moduleName, 'suspend', "projectID=$project->id", $project, 'list', 'pause', '', 'iframe btn-action', true);
if($project->status != 'doing') common::printIcon($moduleName, 'close', "projectID=$project->id", $project, 'list', 'off', '', 'iframe btn-action', true);
if($project->status != 'closed') common::printIcon($moduleName, 'activate', "projectID=$project->id", $project, 'list', 'magic', '', 'iframe btn-action', true);
echo "</ul>";
echo "</div>";
}
$from = $project->from == 'project' ? 'project' : 'pgmproject';
$iframe = ($this->app->tab == 'program' or $project->model == 'kanban') ? 'iframe' : '';
$onlyBody = ($this->app->tab == 'program' or $project->model == 'kanban') ? true : '';
$dataApp = $this->config->systemMode == 'classic' ? "data-app=execution" : "data-app=project";
$attr = $project->model == 'kanban' ? " disabled='disabled'" : '';
common::printIcon($moduleName, 'edit', "projectID=$project->id", $project, 'list', 'edit', '', $iframe, $onlyBody, $dataApp);
if($this->config->vision != 'lite')
{
common::printIcon($moduleName, 'team', "projectID=$project->id", $project, 'list', 'group', '', '', '', $dataApp . $attr, $this->lang->execution->team);
if($this->config->systemMode == 'new') common::printIcon('project', 'group', "projectID=$project->id&programID=$programID", $project, 'list', 'lock', '', '', '', $dataApp . $attr);
if(common::hasPriv($moduleName, 'manageProducts') || common::hasPriv($moduleName, 'whitelist') || common::hasPriv($moduleName, 'delete'))
{
echo "<div class='btn-group'>";
echo "<button type='button' class='btn dropdown-toggle' data-toggle='context-dropdown' title='{$this->lang->more}'><i class='icon-more-alt'></i></button>";
echo "<ul class='dropdown-menu pull-right text-center' role='menu'>";
common::printIcon($moduleName, 'manageProducts', "projectID=$project->id", $project, 'list', 'link', '', 'btn-action', '', $attr, $this->lang->project->manageProducts);
if($this->config->systemMode == 'new') common::printIcon('project', 'whitelist', "projectID=$project->id&module=project&from=$from", $project, 'list', 'shield-check', '', 'btn-action', '', $dataApp . $attr);
if(common::hasPriv($moduleName, 'delete')) echo html::a(helper::createLink($moduleName, "delete", "projectID=$project->id"), "<i class='icon-trash'></i>", 'hiddenwin', "class='btn btn-action' title='{$this->lang->project->delete}'");
echo "</ul>";
echo "</div>";
}
}
else
{
common::printIcon($moduleName, 'team', "projectID=$project->id", $project, 'list', 'group', '', '', '', $dataApp, $this->lang->execution->team);
if($this->config->systemMode == 'new') common::printIcon('project', 'whitelist', "projectID=$project->id&module=project&from=$from", $project, 'list', 'shield-check', '', 'btn-action', '', $dataApp);
if(common::hasPriv($moduleName, 'delete')) echo html::a(helper::createLink($moduleName, "delete", "projectID=$project->id"), "<i class='icon-trash'></i>", 'hiddenwin', "class='btn btn-action' title='{$this->lang->project->delete}'");
}
$project->programID = $programID;
echo $this->buildOperateMenu($project, 'browse');
break;
}
echo '</td>';
@@ -2015,7 +2211,7 @@ class projectModel extends model
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->where('t1.type')->in('sprint,stage,kanban')
->beginIF($projectID != 0)->andWhere('t1.project')->eq($projectID)->fi()
->beginIF($projectID == 0 && $this->config->vision)->andWhere('t1.vision')->eq($this->config->vision)->fi()
->beginIF($projectID == 0 and $this->config->vision)->andWhere('t1.vision')->eq($this->config->vision)->fi()
->beginIF(!empty($myExecutionIDList))->andWhere('t1.id')->in(array_keys($myExecutionIDList))->fi()
->beginIF($status == 'undone')->andWhere('t1.status')->notIN('done,closed')->fi()
->beginIF($status != 'all' and $status != 'undone' and $status != 'involved')->andWhere('t1.status')->eq($status)->fi()
@@ -2285,14 +2481,14 @@ class projectModel extends model
$lang->project->dividerMenu = $lang->{$model}->dividerMenu;
}
$this->lang->switcherMenu = $this->getSwitcher($objectID, $this->app->rawModule, $this->app->rawMethod);
/* Reset project priv. */
$moduleName = $this->app->rawModule;
$methodName = $this->app->rawMethod;
$this->loadModel('common')->resetProjectPriv($objectID);
if(!$this->common->isOpenMethod($moduleName, $methodName) and !commonModel::hasPriv($moduleName, $methodName)) $this->common->deny($moduleName, $methodName, false);
$this->lang->switcherMenu = $this->getSwitcher($objectID, $this->app->rawModule, $this->app->rawMethod);
common::setMenuVars('project', $objectID);
}
@@ -2344,4 +2540,115 @@ class projectModel extends model
->fetch();
return $result;
}
/**
* Build project action menu.
*
* @param object $project
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($project, $type = 'view')
{
$function = 'buildOperate' . ucfirst($type) . 'Menu';
return $this->$function($project);
}
/**
* Build project view action menu.
*
* @param object $project
* @access public
* @return string
*/
public function buildOperateViewMenu($project)
{
if($project->deleted) return '';
$menu = '';
$params = "projectID=$project->id";
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('project', 'start', $params, $project, 'view', 'play', '', 'iframe', true, '', $this->lang->project->start);
$menu .= $this->buildMenu('project', 'activate', $params, $project, 'view', 'magic', '', 'iframe', true, '', $this->lang->project->activate);
$menu .= $this->buildMenu('project', 'suspend', $params, $project, 'view', 'pause', '', 'iframe', true, '', $this->lang->project->suspend);
$menu .= $this->buildMenu('project', 'close', $params, $project, 'view', 'off', '', 'iframe', true, '', $this->lang->close);
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('project', $project, 'view', 'direct');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('project', 'edit', "project=$project->id&from=view", $project, 'button', 'edit', '', '', '', '', $this->lang->edit);
$menu .= $this->buildMenu('project', 'delete', "project=$project->id", $project, 'button', 'trash', 'hiddenwin', '', '', '', $this->lang->delete);
return $menu;
}
/**
* Build project browse action menu.
*
* @param object $project
* @access public
* @return string
*/
public function buildOperateBrowseMenu($project)
{
$menu = '';
$params = "projectID=$project->id";
$moduleName = $this->config->systemMode == 'classic' ? "execution" : "project";
if($project->status == 'wait' || $project->status == 'suspended')
{
$menu .= $this->buildMenu($moduleName, 'start', $params, $project, 'browse', 'play', '', 'iframe', true);
}
if($project->status == 'doing') $menu .= $this->buildMenu($moduleName, 'close', $params, $project, 'browse', 'off', '', 'iframe', true);
if($project->status == 'closed') $menu .= $this->buildMenu($moduleName, 'activate', $params, $project, 'browse', 'magic', '', 'iframe', true);
if(common::hasPriv($moduleName, 'suspend') || (common::hasPriv($moduleName, 'close') && $project->status != 'doing') || (common::hasPriv($moduleName, 'activate') && $project->status != 'closed'))
{
$menu .= "<div class='btn-group'>";
$menu .= "<button type='button' class='btn icon-caret-down dropdown-toggle' data-toggle='context-dropdown' title='{$this->lang->more}' style='width: 16px; padding-left: 0px; border-radius: 4px;'></button>";
$menu .= "<ul class='dropdown-menu pull-right text-center' role='menu' style='position: unset; min-width: auto; padding: 5px 6px;'>";
$menu .= $this->buildMenu($moduleName, 'suspend', $params, $project, 'browse', 'pause', '', 'iframe btn-action', true);
if($project->status != 'doing') $menu .= $this->buildMenu($moduleName, 'close', $params, $project, 'browse', 'off', '', 'iframe btn-action', true);
if($project->status != 'closed') $menu .= $this->buildMenu($moduleName, 'activate', $params, $project, 'browse', 'magic', '', 'iframe btn-action', true);
$menu .= "</ul>";
$menu .= "</div>";
}
$from = $project->from == 'project' ? 'project' : 'pgmproject';
$iframe = ($this->app->tab == 'program' || $project->model == 'kanban') ? 'iframe' : '';
$onlyBody = ($this->app->tab == 'program' || $project->model == 'kanban') ? true : '';
$dataApp = $this->config->systemMode == 'classic' ? "data-app=execution" : "data-app=project";
$attr = $project->model == 'kanban' ? " disabled='disabled'" : '';
$menu .= $this->buildMenu($moduleName, 'edit', $params, $project, 'browse', 'edit', '', $iframe, $onlyBody, $dataApp);
if($this->config->vision != 'lite')
{
$menu .= $this->buildMenu($moduleName, 'team', $params, $project, 'browse', 'group', '', '', '', $dataApp . $attr, $this->lang->execution->team);
if($this->config->systemMode == 'new') $menu .= $this->buildMenu('project', 'group', "$params&programID={$project->programID}", $project, 'browse', 'lock', '', '', '', $dataApp . $attr);
if(common::hasPriv($moduleName, 'manageProducts') || common::hasPriv($moduleName, 'whitelist') || common::hasPriv($moduleName, 'delete'))
{
$menu .= "<div class='btn-group'>";
$menu .= "<button type='button' class='btn dropdown-toggle' data-toggle='context-dropdown' title='{$this->lang->more}'><i class='icon-more-alt'></i></button>";
$menu .= "<ul class='dropdown-menu pull-right text-center' role='menu'>";
$menu .= $this->buildMenu($moduleName, 'manageProducts', $params, $project, 'browse', 'link', '', 'btn-action', '', $attr, $this->lang->project->manageProducts);
if($this->config->systemMode == 'new') $menu .= $this->buildMenu('project', 'whitelist', "$params&module=project&from=$from", $project, 'browse', 'shield-check', '', 'btn-action', '', $dataApp . $attr);
$menu .= $this->buildMenu($moduleName, "delete", $params, $project, 'browse', 'trash', 'hiddenwin', 'btn-action');
$menu .= "</ul>";
$menu .= "</div>";
}
}
else
{
$menu .= $this->buildMenu($moduleName, 'team', $params, $project, 'browse', 'group', '', '', '', $dataApp, $this->lang->execution->team);
if($this->config->systemMode == 'new') $menu .= $this->buildMenu('project', 'whitelist', "$params&module=project&from=$from", $project, 'browse', 'shield-check', '', 'btn-action', '', $dataApp);
$menu .= $this->buildMenu($moduleName, "delete", $params, $project, 'browse', 'trash', 'hiddenwin', 'btn-action');
}
return $menu;
}
}
+19 -3
View File
@@ -30,6 +30,7 @@
<?php
$projectCounts = array();
$projectNames = array();
$defaultLink = $link;
$tabActive = '';
$myProjects = 0;
$others = 0;
@@ -69,9 +70,24 @@ foreach($projects as $programID => $programProjects)
foreach($programProjects as $index => $project)
{
$selected = $project->id == $projectID ? 'selected' : '';
$link = helper::createLink('project', 'index', "projectID=%s", '', '', $project->id);
$icon = '<i class="icon icon-sprint"></i> ';
$selected = $project->id == $projectID ? 'selected' : '';
$icon = '<i class="icon icon-sprint"></i> ';
if($project->model != 'waterfall' and (in_array($module, $config->waterfallModules) or $method == 'track'))
{
$link = helper::createLink('project', 'index', "projectID=%s");
}
elseif($project->model == 'kanban')
{
$link = helper::createLink('project', 'index', "projectID=%s");
}
else
{
$link = $defaultLink;
}
/* Set link when project redefines permissions. */
if($project->auth == 'reset') $link = helper::createLink('project', 'index', "projectID=%s");
if($project->model != 'scrum') $icon = "<i class='icon icon-{$project->model}'></i> ";
$projectName = $icon . $project->name;
@@ -40,6 +40,7 @@
<?php echo html::a(inlink('browse', "programID=$programID&browseType=$key"), $label, '', "class='btn btn-link $active'");?>
<?php endforeach;?>
<?php echo html::checkbox('involved', array('1' => $lang->project->mine), '', $this->cookie->involved ? 'checked=checked' : '');?>
<a class="btn btn-link querybox-toggle" id='bysearchTab'><i class="icon icon-search muted"></i> <?php echo $lang->search->common;?></a>
</div>
<div class="btn-toolbar pull-right">
<div class="btn-group panel-actions">
@@ -67,6 +68,7 @@
</div>
<?php endif;?>
<div class="main-col">
<div class="cell<?php if($browseType == 'bysearch') echo ' show';?>" id="queryBox" data-module='project'></div>
<?php if(empty($projectStats)):?>
<div class="table-empty-tip">
<p>
+3 -19
View File
@@ -96,25 +96,9 @@
</div>
<div class='main-actions'>
<div class="btn-toolbar">
<?php
$params = "project=$project->id";
$browseLink = $this->session->projectList ? $this->session->projectList : inlink('browse');
common::printBack($browseLink);
if(!$project->deleted)
{
echo "<div class='divider'></div>";
common::printIcon('project', 'start', "projectID=$project->id", $project, 'button', 'play', '', 'iframe', true, '', $lang->project->start);
common::printIcon('project', 'activate', "projectID=$project->id", $project, 'button', 'magic', '', 'iframe', true, '', $lang->project->activate);
common::printIcon('project', 'suspend', "projectID=$project->id", $project, 'button', 'pause', '', 'iframe', true, '', $lang->project->suspend);
common::printIcon('project', 'close', "projectID=$project->id", $project, 'button', 'off', '', 'iframe', true, '', $lang->close);
echo $this->buildOperateMenu($project, 'view');
echo "<div class='divider'></div>";
common::printIcon('project', 'edit', $params . '&from=view', $project, 'button', 'edit', '', '', '', '', $lang->edit);
common::printIcon('project', 'delete', $params, $project, 'button', 'trash', 'hiddenwin', '', '', '', $lang->delete);
}
?>
<?php $browseLink = $this->session->projectList ? $this->session->projectList : inlink('browse');?>
<?php common::printBack($browseLink);?>
<?php echo $this->project->buildOperateMenu($project, 'view');?>
</div>
</div>
</div>
+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();
}
+81 -5
View File
@@ -80,7 +80,7 @@ class releaseModel extends model
return $this->dao->select('id, name')->from(TABLE_RELEASE)
->where('product')->eq((int)$productID)
->beginIF($branch)->andWhere('branch')->eq($branch)->fi()
->orderBy('date DESC')
->orderBy('id DESC')
->limit(1)
->fetch();
}
@@ -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)
@@ -191,7 +188,8 @@ class releaseModel extends model
$this->dao->insert(TABLE_RELEASE)->data($release)
->autoCheck()
->batchCheck($this->config->release->create->requiredFields, 'notempty')
->check('name', 'unique', "product = '{$release->product}' AND branch = '{$release->branch}' AND deleted = '0'");
->check('name', 'unique', "product = '{$release->product}' AND branch = '{$release->branch}' AND deleted = '0'")
->checkFlow();
if(dao::isError())
{
@@ -248,6 +246,7 @@ class releaseModel extends model
$branch = $this->dao->select('branch')->from(TABLE_BUILD)->where('id')->eq((int)$this->post->build)->fetch('branch');
$release = fixer::input('post')->stripTags($this->config->release->editor->edit['id'], $this->config->allowedTags)
->add('id', $releaseID)
->add('branch', (int)$branch)
->join('mailto', ',')
->setIF(!$this->post->marker, 'marker', 0)
@@ -269,6 +268,7 @@ class releaseModel extends model
->autoCheck()
->batchCheck($this->config->release->edit->requiredFields, 'notempty')
->check('name', 'unique', "id != '$releaseID' AND product = '{$release->product}' AND branch = '$branch' AND deleted = '0'")
->checkFlow()
->where('id')->eq((int)$releaseID)
->exec();
if(!dao::isError())
@@ -670,4 +670,80 @@ class releaseModel extends model
$this->loadModel('mail')->send(implode(',', $toList), $subject, $mailContent, '', false, $emails);
}
}
/**
* Build release action menu.
*
* @param object $release
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($release, $type = 'view')
{
$function = 'buildOperate' . ucfirst($type) . 'Menu';
return $this->$function($release);
}
/**
* Build release view action menu.
*
* @param object $release
* @access public
* @return string
*/
public function buildOperateViewMenu($release)
{
$canBeChanged = common::canBeChanged('release', $release);
if($release->deleted || !$canBeChanged || isonlybody()) return '';
$menu = '';
$params = "releaseID=$release->id";
if(common::hasPriv('release', 'changeStatus', $release))
{
$changedStatus = $release->status == 'normal' ? 'terminate' : 'normal';
$menu .= html::a(inlink('changeStatus', "releaseID=$release->id&status=$changedStatus"), '<i class="icon-' . ($release->status == 'normal' ? 'pause' : 'play') . '"></i> ' . $this->lang->release->changeStatusList[$changedStatus], 'hiddenwin', "class='btn btn-link' title='{$this->lang->release->changeStatusList[$changedStatus]}'");
}
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('release', $release, 'view', 'direct');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('release', 'edit', $params, $release, 'view');
$menu .= $this->buildMenu('release', 'delete', $params, $release, 'view', 'trash', 'hiddenwin');
return $menu;
}
/**
* Build release browse action menu.
*
* @param object $release
* @access public
* @return string
*/
public function buildOperateBrowseMenu($release)
{
$canBeChanged = common::canBeChanged('release', $release);
if(!$canBeChanged) return '';
$menu = '';
$params = "releaseID=$release->id";
$changedStatus = $release->status == 'normal' ? 'terminate' : 'normal';
if(common::hasPriv('release', 'linkStory')) $menu .= html::a(inlink('view', "$params&type=story&link=true"), '<i class="icon-link"></i> ', '', "class='btn' title='{$this->lang->release->linkStory}'");
if(common::hasPriv('release', 'linkBug')) $menu .= html::a(inlink('view', "$params&type=bug&link=true"), '<i class="icon-bug"></i> ', '', "class='btn' title='{$this->lang->release->linkBug}'");
$menu .= $this->buildMenu('release', 'changeStatus', "$params&status=$changedStatus", $release, 'browse', $release->status == 'normal' ? 'pause' : 'play', 'hiddenwin', '', '', '',$this->lang->release->changeStatusList[$changedStatus]);
$menu .= $this->buildMenu('release', 'edit', "release=$release->id", $release, 'browse');
$menu .= $this->buildMenu('release', 'notify', "release=$release->id", $release, 'browse', 'bullhorn', '', 'iframe', true);
if(common::hasPriv('release', 'delete', $release))
{
$deleteURL = helper::createLink('release', 'delete', "releaseID=$release->id&confirm=yes");
$menu .= html::a("javascript:ajaxDelete(\"$deleteURL\", \"releaseList\", confirmDelete)", '<i class="icon-trash"></i>', '', "class='btn' title='{$this->lang->release->delete}'");
}
return $menu;
}
}
+7 -22
View File
@@ -16,9 +16,9 @@
<div id="mainMenu" class="clearfix">
<div class="btn-toolbar pull-left">
<?php
echo html::a(inlink('browse', "productID={$product->id}&branch=$branch&type=all"), "<span class='text'>{$lang->release->all}</span>" . ($type == 'all' ? ' <span class="label label-light label-badge">' . count($releases) . '</span>' : ''), '', "id='allTab' class='btn btn-link" . ('all' == $type ? ' btn-active-text' : '') . "'");
echo html::a(inlink('browse', "productID={$product->id}&branch=$branch&type=normal"), "<span class='text'>{$lang->release->statusList['normal']}</span>" . ($type == 'normal' ? ' <span class="label label-light label-badge">' . count($releases) . '</span>' : ''), '', "id='normalTab' class='btn btn-link" . ('normal' == $type ? ' btn-active-text' : '') . "'");
echo html::a(inlink('browse', "productID={$product->id}&branch=$branch&type=terminate"), "<span class='text'>{$lang->release->statusList['terminate']}</span>" . ($type == 'terminate' ? ' <span class="label label-light label-badge">' . count($releases) . '</span>' : ''), '', "id='terminateTab' class='btn btn-link" . ('terminate' == $type ? ' btn-active-text' : '') . "'");
echo html::a(inlink('browse', "productID={$product->id}&branch=$branch&type=all"), "<span class='text'>{$lang->release->all}</span>" . ($type == 'all' ? ' <span class="label label-light label-badge">' . $pager->recTotal . '</span>' : ''), '', "id='allTab' class='btn btn-link" . ('all' == $type ? ' btn-active-text' : '') . "'");
echo html::a(inlink('browse', "productID={$product->id}&branch=$branch&type=normal"), "<span class='text'>{$lang->release->statusList['normal']}</span>" . ($type == 'normal' ? ' <span class="label label-light label-badge">' . $pager->recTotal . '</span>' : ''), '', "id='normalTab' class='btn btn-link" . ('normal' == $type ? ' btn-active-text' : '') . "'");
echo html::a(inlink('browse', "productID={$product->id}&branch=$branch&type=terminate"), "<span class='text'>{$lang->release->statusList['terminate']}</span>" . ($type == 'terminate' ? ' <span class="label label-light label-badge">' . $pager->recTotal . '</span>' : ''), '', "id='terminateTab' class='btn btn-link" . ('terminate' == $type ? ' btn-active-text' : '') . "'");
?>
</div>
<div class="btn-toolbar pull-right">
@@ -80,30 +80,15 @@
</td>
<?php foreach($extendFields as $extendField) echo "<td>" . $this->loadModel('flow')->getFieldValue($extendField, $release) . "</td>";?>
<td class='c-actions'>
<?php
if($canBeChanged)
{
if(common::hasPriv('release', 'linkStory')) echo html::a(inlink('view', "releaseID=$release->id&type=story&link=true"), '<i class="icon-link"></i> ', '', "class='btn' title='{$lang->release->linkStory}'");
if(common::hasPriv('release', 'linkBug')) echo html::a(inlink('view', "releaseID=$release->id&type=bug&link=true"), '<i class="icon-bug"></i> ', '', "class='btn' title='{$lang->release->linkBug}'");
if(common::hasPriv('release', 'changeStatus', $release))
{
$changedStatus = $release->status == 'normal' ? 'terminate' : 'normal';
echo html::a($this->createLink('release', 'changeStatus', "releaseID=$release->id&status=$changedStatus"), '<i class="icon-' . ($release->status == 'normal' ? 'pause' : 'play') . '"></i> ', 'hiddenwin', "class='btn' title='{$lang->release->changeStatusList[$changedStatus]}'");
}
common::printIcon('release', 'edit', "release=$release->id", $release, 'list');
common::printIcon('release', 'notify', "release=$release->id", $release, 'list', 'bullhorn', '', 'iframe', true);
if(common::hasPriv('release', 'delete', $release))
{
$deleteURL = $this->createLink('release', 'delete', "releaseID=$release->id&confirm=yes");
echo html::a("javascript:ajaxDelete(\"$deleteURL\", \"releaseList\", confirmDelete)", '<i class="icon-trash"></i>', '', "class='btn' title='{$lang->release->delete}'");
}
}
?>
<?php echo $this->release->buildOperateMenu($release, 'browse');?>
</td>
</tr>
<?php endforeach;?>
</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';?>
+2 -16
View File
@@ -14,6 +14,7 @@
<?php include '../../common/view/tablesorter.html.php';?>
<?php js::set('confirmUnlinkStory', $lang->release->confirmUnlinkStory)?>
<?php js::set('confirmUnlinkBug', $lang->release->confirmUnlinkBug)?>
<?php $canBeChanged = common::canBeChanged('release', $release);?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php $browseLink = $this->session->releaseList ? $this->session->releaseList : inlink('browse', "productID=$release->product");?>
@@ -30,22 +31,7 @@
</div>
</div>
<div class='btn-toolbar pull-right'>
<?php
$canBeChanged = common::canBeChanged('release', $release);
if(!$release->deleted and $canBeChanged and !isonlybody())
{
echo $this->buildOperateMenu($release, 'view');
if(common::hasPriv('release', 'changeStatus', $release))
{
$changedStatus = $release->status == 'normal' ? 'terminate' : 'normal';
echo html::a(inlink('changeStatus', "releaseID=$release->id&status=$changedStatus"), '<i class="icon-' . ($release->status == 'normal' ? 'pause' : 'play') . '"></i> ' . $lang->release->changeStatusList[$changedStatus], 'hiddenwin', "class='btn btn-link' title='{$lang->release->changeStatusList[$changedStatus]}'");
}
if(common::hasPriv('release', 'edit')) echo html::a(inlink('edit', "releaseID=$release->id"), "<i class='icon-common-edit icon-edit'></i> " . $this->lang->edit, '', "class='btn btn-link' title='{$this->lang->edit}'");
if(common::hasPriv('release', 'delete')) echo html::a(inlink('delete', "releaseID=$release->id"), "<i class='icon-common-delete icon-trash'></i> " . $this->lang->delete, '', "class='btn btn-link' title='{$this->lang->delete}' target='hiddenwin'");
}
?>
<?php echo $this->release->buildOperateMenu($release, 'view');?>
</div>
</div>
<div id='mainContent' class='main-content'>
+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');
+111 -35
View File
@@ -247,6 +247,7 @@ class storyModel extends model
->autoCheck()
->checkIF($story->notifyEmail, 'notifyEmail', 'email')
->batchCheck($requiredFields, 'notempty')
->checkFlow()
->exec();
if(!dao::isError())
@@ -514,8 +515,6 @@ class storyModel extends model
if(is_array($story->{$extendField->field})) $story->{$extendField->field} = join(',', $story->{$extendField->field});
$story->{$extendField->field} = htmlSpecialString($story->{$extendField->field});
$message = $this->checkFlowRule($extendField, $story->{$extendField->field});
if($message) return print(js::alert($message));
}
foreach(explode(',', $this->config->story->create->requiredFields) as $field)
@@ -538,7 +537,7 @@ class storyModel extends model
foreach($data as $i => $story)
{
$this->dao->insert(TABLE_STORY)->data($story)->autoCheck()->exec();
$this->dao->insert(TABLE_STORY)->data($story)->autoCheck()->checkFlow()->exec();
if(dao::isError())
{
echo js::error(dao::getError());
@@ -676,6 +675,7 @@ class storyModel extends model
$story = fixer::input('post')
->callFunc('title', 'trim')
->setDefault('lastEditedBy', $this->app->user->account)
->add('id', $storyID)
->add('lastEditedDate', $now)
->setIF($specChanged, 'version', $oldStory->version + 1)
->setIF($specChanged and $oldStory->status == 'active' and $this->post->needNotReview == false, 'status', 'changed')
@@ -693,6 +693,7 @@ class storyModel extends model
$this->dao->update(TABLE_STORY)->data($story, 'spec,verify')
->autoCheck()
->batchCheck($this->config->story->change->requiredFields, 'notempty')
->checkFlow()
->where('id')->eq((int)$storyID)->exec();
if(!dao::isError())
@@ -772,6 +773,7 @@ class storyModel extends model
->cleanFloat('estimate')
->setDefault('assignedDate', $oldStory->assignedDate)
->setDefault('lastEditedBy', $this->app->user->account)
->add('id', $storyID)
->add('lastEditedDate', $now)
->setDefault('plan,notifyEmail', '')
->setDefault('status', $oldStory->status)
@@ -864,6 +866,7 @@ class storyModel extends model
->checkIF(isset($story->closedReason) and $story->closedReason == 'done', 'stage', 'notempty')
->checkIF(isset($story->closedReason) and $story->closedReason == 'duplicate', 'duplicateStory', 'notempty')
->checkIF($story->notifyEmail, 'notifyEmail', 'email')
->checkFlow()
->where('id')->eq((int)$storyID)->exec();
if(!dao::isError())
@@ -1209,6 +1212,7 @@ class storyModel extends model
$oldStory = $oldStories[$storyID];
$story = new stdclass();
$story->id = $storyID;
$story->lastEditedBy = $this->app->user->account;
$story->lastEditedDate = $now;
$story->status = $oldStory->status;
@@ -1245,8 +1249,6 @@ class storyModel extends model
if(is_array($story->{$extendField->field})) $story->{$extendField->field} = join(',', $story->{$extendField->field});
$story->{$extendField->field} = htmlSpecialString($story->{$extendField->field});
$message = $this->checkFlowRule($extendField, $story->{$extendField->field});
if($message) return print(js::alert($message));
}
$stories[$storyID] = $story;
@@ -1261,6 +1263,7 @@ class storyModel extends model
->checkIF($story->closedBy, 'closedReason', 'notempty')
->checkIF($story->closedReason == 'done', 'stage', 'notempty')
->checkIF($story->closedReason == 'duplicate', 'duplicateStory', 'notempty')
->checkFlow()
->where('id')->eq((int)$storyID)
->exec();
if($story->title != $oldStory->title)
@@ -1330,6 +1333,7 @@ class storyModel extends model
->removeIF($this->post->result == 'reject' and $this->post->closedReason != 'duplicate', 'duplicateStory')
->removeIF($this->post->result == 'reject' and $this->post->closedReason != 'subdivided', 'childStories')
->add('reviewedBy', $oldStory->reviewedBy . ',' . $this->app->user->account)
->add('id', $storyID)
->remove('result,preVersion,comment')
->get();
@@ -1353,6 +1357,7 @@ class storyModel extends model
->batchCheck($this->config->story->review->requiredFields, 'notempty')
->checkIF($this->post->result == 'reject', 'closedReason', 'notempty')
->checkIF($this->post->result == 'reject' and $this->post->closedReason == 'duplicate', 'duplicateStory', 'notempty')
->checkFlow()
->where('id')->eq($storyID)->exec();
if($this->post->result == 'revert')
{
@@ -1549,6 +1554,7 @@ class storyModel extends model
$oldStory = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch();
$now = helper::now();
$story = fixer::input('post')
->add('id', $storyID)
->add('assignedTo', 'closed')
->add('status', 'closed')
->add('stage', 'closed')
@@ -1576,6 +1582,7 @@ class storyModel extends model
->autoCheck()
->batchCheck($this->config->story->close->requiredFields, 'notempty')
->checkIF($story->closedReason == 'duplicate', 'duplicateStory', 'notempty')
->checkFlow()
->where('id')->eq($storyID)->exec();
/* Update parent story status and stage. */
@@ -1975,13 +1982,15 @@ class storyModel extends model
$assignedTo = $this->post->assignedTo;
if($assignedTo == $oldStory->assignedTo) return array();
$story = new stdclass();
$story->lastEditedBy = $this->app->user->account;
$story->lastEditedDate = $now;
$story->assignedTo = $assignedTo;
$story->assignedDate = $now;
$story = fixer::input('post')
->add('id', $storyID)
->add('lastEditedBy', $this->app->user->account)
->add('lastEditedDate', $now)
->add('assignedDate', $now)
->remove('comment')
->get();
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->where('id')->eq((int)$storyID)->exec();
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->checkFlow()->where('id')->eq((int)$storyID)->exec();
if(!dao::isError()) return common::createChanges($oldStory, $story);
return false;
}
@@ -2029,6 +2038,7 @@ class storyModel extends model
$oldStory = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch();
$now = helper::now();
$story = fixer::input('post')
->add('id', $storyID)
->add('closedBy', '')
->add('closedReason', '')
->add('closedDate', '0000-00-00')
@@ -2042,7 +2052,7 @@ class storyModel extends model
->setDefault('activatedDate', $now)
->remove('comment')
->get();
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->where('id')->eq($storyID)->exec();
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->checkFlow()->where('id')->eq($storyID)->exec();
if($this->post->status == 'active') $this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->exec();
@@ -3029,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')
@@ -3749,6 +3761,92 @@ class storyModel extends model
return true;
}
/**
* Build operate menu.
*
* @param object $story
* @param string $type
* @access public
* @return string
*/
public function buildOperateMenu($story, $type = 'view')
{
$menu = '';
$params = "storyID=$story->id";
if($type == 'browse')
{
if(common::canBeChanged('story', $story))
{
if($story->URChanged) return $this->buildMenu('story', 'processStoryChange', $params, $story, $type, 'search', '', 'iframe', true, '', $this->lang->confirm);
$menu .= $this->buildMenu('story', 'change', $params . "&from=$story->from", $story, $type, 'alter');
$menu .= $this->buildMenu('story', 'review', $params . "&from=$story->from", $story, $type, 'search');
$menu .= $this->buildMenu('story', 'recall', $params, $story, $type, 'undo', 'hiddenwin', '', '', '', $this->lang->story->recall);
$menu .= $this->buildMenu('story', 'close', $params, $story, $type, '', '', 'iframe', true);
$menu .= $this->buildMenu('story', 'edit', $params . "&from=$story->from", $story, $type);
if($story->type != 'requirement' and $this->config->vision != 'lite') $menu .= $this->buildMenu('story', 'createCase', "productID=$story->product&branch=$story->branch&module=0&from=&param=0&$params", $story, $type, 'sitemap', '', '', false, "data-app='qa'");
if($this->app->rawModule != 'projectstory' OR $this->config->vision == 'lite') $menu .= $this->buildMenu('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID={$this->session->project}", $story, $type, 'split', '', '', '', '', $this->lang->story->subdivide);
if($this->app->rawModule == 'projectstory' and $this->config->vision != 'lite') $menu .= $this->buildMenu('projectstory', 'unlinkStory', "projectID={$this->session->project}&$params", $story, $type, 'unlink', 'hiddenwin');
}
else
{
return $this->buildMenu('story', 'close', $params, $story, 'list', '', '', 'iframe', true);
}
}
if($type == 'view')
{
$menu .= $this->buildMenu('story', 'change', $params, $story, $type, 'alter', '', 'showinonlybody');
$menu .= $this->buildMenu('story', 'recall', $params, $story, $type, 'undo', '', 'showinonlybody');
$menu .= $this->buildMenu('story', 'review', $params, $story, $type, 'search', '', 'showinonlybody');
if(!isonlybody()) $menu .= $this->buildMenu('story', 'batchCreate', "productID=$story->product&branch=$story->branch&moduleID=$story->module&$params", $story, $type, 'split', '', 'divideStory', '', "data-toggle='modal' data-type='iframe' data-width='95%'", $this->lang->story->subdivide);
$menu .= $this->buildMenu('story', 'assignTo', $params, $story, $type, '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('story', 'close', $params, $story, $type, '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('story', 'activate', $params, $story, $type, '', '', 'iframe showinonlybody', true);
if($this->config->edition == 'max' and $this->app->tab == 'project' and common::hasPriv('story', 'importToLib')) $menu .= html::a('#importToLib', "<i class='icon icon-assets'></i> " . $this->lang->story->importToLib, '', 'class="btn" data-toggle="modal"');
/* Print testcate actions. */
if($story->parent >= 0 and $story->type != 'requirement' and (common::hasPriv('testcase', 'create', $story) or common::hasPriv('testcase', 'batchCreate', $story)))
{
$this->app->loadLang('testcase');
$menu .= "<div class='btn-group dropup'>";
$menu .= "<button type='button' class='btn dropdown-toggle' data-toggle='dropdown'><i class='icon icon-sitemap'></i> " . $this->lang->testcase->common . " <span class='caret'></span></button>";
$menu .= "<ul class='dropdown-menu' id='createCaseActionMenu'>";
$misc = "data-toggle='modal' data-type='iframe' data-width='95%'";
if(isonlybody()) $misc = '';
if(common::hasPriv('testcase', 'create', $story))
{
$link = helper::createLink('testcase', 'create', "productID=$story->product&branch=$story->branch&moduleID=0&from=&param=0&$params", '', true);
$menu .= "<li>" . html::a($link, $this->lang->testcase->create, '', $misc) . "</li>";
}
if(common::hasPriv('testcase', 'batchCreate'))
{
$link = helper::createLink('testcase', 'batchCreate', "productID=$story->product&branch=$story->branch&moduleID=0&$params", '', true);
$menu .= "<li>" . html::a($link, $this->lang->testcase->batchCreate, '', $misc) . "</li>";
}
$menu .= "</ul></div>";
}
if($this->app->tab == 'execution' and strpos('draft,closed', $story->status) === false) $menu .= $this->buildMenu('task', 'create', "execution={$this->session->project}&{$params}&moduleID=$story->module", $story, $type, 'plus', '', 'showinonly body');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('story', 'edit', $params, $story, $type);
$menu .= $this->buildMenu('story', 'create', "productID=$story->product&branch=$story->branch&moduleID=$story->module&{$params}&executionID=0&bugID=0&planID=0&todoID=0&extra=&type=$story->type", $story, $type, 'copy', '', '', ' ', "data-width='1050'");
$menu .= $this->buildMenu('story', 'delete', $params, $story, 'button', 'trash', 'hiddenwin', 'showinonlybody', true);
}
return $menu;
}
/**
* Merge plan title.
*
@@ -4153,29 +4251,7 @@ class storyModel extends model
echo $story->version;
break;
case 'actions':
$vars = "story={$story->id}";
$executionParams = $this->config->vision == 'lite' ? "&executionID={$this->session->project}" : '';
if($canBeChanged)
{
if($story->URChanged)
{
common::printIcon('story', 'processStoryChange', "storyID=$story->id", '', 'list', 'search', '', 'iframe', true, '', $this->lang->confirm);
break;
}
common::printIcon('story', 'change', $vars . "&from=$story->from", $story, 'list', 'alter', '', '', false, "data-group=$story->from");
common::printIcon('story', 'review', $vars . "&from=$story->from", $story, 'list', 'search', '', '', false, "data-group=$story->from");
common::printIcon('story', 'recall', $vars, $story, 'list', 'undo', 'hiddenwin', '', '', '', $this->lang->story->recall);
common::printIcon('story', 'close', $vars, $story, 'list', '', '', 'iframe', true);
common::printIcon('story', 'edit', $vars . "&from=$story->from", $story, 'list', '', '', '', false, "data-group=$story->from");
if($story->type != 'requirement' and $this->config->vision != 'lite') common::printIcon('story', 'createCase', "productID=$story->product&branch=$story->branch&module=0&from=&param=0&$vars", $story, 'list', 'sitemap', '', '', false, "data-app='qa'");
if($this->app->rawModule != 'projectstory' OR $this->config->vision == 'lite') common::printIcon('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&storyID=$story->id" . $executionParams, $story, 'list', 'split', '', '', '', '', $this->lang->story->subdivide);
if($this->app->rawModule == 'projectstory' and $this->config->vision != 'lite') common::printIcon('projectstory', 'unlinkStory', "projectID={$this->session->project}&storyID=$story->id", '', 'list', 'unlink', 'hiddenwin');
}
else
{
common::printIcon('story', 'close', $vars, $story, 'list', '', '', 'iframe', true);
}
echo $this->buildOperateMenu($story, 'browse');
break;
}
echo '</td>';
+1 -47
View File
@@ -200,53 +200,7 @@
<div class="btn-toolbar">
<?php common::printBack($browseLink);?>
<?php if(!isonlybody()) echo "<div class='divider'></div>";?>
<?php if(!$story->deleted):?>
<?php
common::printIcon('story', 'change', "storyID=$story->id", $story, 'button', 'alter', '', 'showinonlybody');
common::printIcon('story', 'recall', "storyID=$story->id", $story, 'button', 'undo', '', 'showinonlybody');
common::printIcon('story', 'review', "storyID=$story->id", $story, 'button', 'search', '', 'showinonlybody');
if($story->status == 'active' and $story->stage == 'wait' and $story->parent <= 0 and !isonlybody())
{
$divideLang = $lang->story->subdivide;
$misc = "class='btn divideStory' data-toggle='modal' data-type='iframe' data-width='95%'";
$link = $this->createLink('story', 'batchCreate', "productID=$story->product&branch=$story->branch&moduleID=$story->module&storyID=$story->id", '', true);
if(common::hasPriv('story', 'batchCreate', $story)) echo html::a($link, "<i class='icon icon-split'></i> " . $divideLang, '', $misc);
}
common::printIcon('story', 'assignTo', "storyID=$story->id", $story, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('story', 'close', "storyID=$story->id", $story, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('story', 'activate', "storyID=$story->id", $story, 'button', '', '', 'iframe showinonlybody', true);
if($this->config->edition == 'max' and $this->app->tab == 'project' and common::hasPriv('story', 'importToLib')) echo html::a('#importToLib', "<i class='icon icon-assets'></i> " . $this->lang->story->importToLib, '', 'class="btn" data-toggle="modal"');
if($story->parent >= 0 and $story->type != 'requirement' and (common::hasPriv('testcase', 'create', $story) or common::hasPriv('testcase', 'batchCreate', $story)))
{
$this->app->loadLang('testcase');
echo "<div class='btn-group dropup'>";
echo "<button type='button' class='btn dropdown-toggle' data-toggle='dropdown'><i class='icon icon-sitemap'></i> " . $lang->testcase->common . " <span class='caret'></span></button>";
echo "<ul class='dropdown-menu' id='createCaseActionMenu'>";
$misc = "data-toggle='modal' data-type='iframe' data-width='95%'";
if(isonlybody()) $misc = '';
$link = $this->createLink('testcase', 'create', "productID=$story->product&branch=$story->branch&moduleID=0&from=&param=0&storyID=$story->id", '', true);
if(common::hasPriv('testcase', 'create', $story)) echo "<li>" . html::a($link, $lang->testcase->create, '', $misc) . "</li>";
$link = $this->createLink('testcase', 'batchCreate', "productID=$story->product&branch=$story->branch&moduleID=0&storyID=$story->id", '', true);
if(common::hasPriv('testcase', 'batchCreate')) echo "<li>" . html::a($link, $lang->testcase->batchCreate, '', $misc) . "</li>";
echo "</ul>";
echo "</div>";
}
if($from == 'execution' and strpos('draft,closed', $story->status) === false) common::printIcon('task', 'create', "execution=$param&storyID=$story->id&moduleID=$story->module", $story, 'button', 'plus', '', 'showinonlybody');
echo $this->buildOperateMenu($story, 'view');
echo "<div class='divider'></div>";
common::printIcon('story', 'edit', "storyID=$story->id", $story);
common::printIcon('story', 'create', "productID=$story->product&branch=$story->branch&moduleID=$story->module&storyID=$story->id&executionID=0&bugID=0&planID=0&todoID=0&extra=&type=$story->type", $story, 'button', 'copy', '', '', '', "data-width='1050'");
common::printIcon('story', 'delete', "storyID=$story->id", $story, 'button', 'trash', 'hiddenwin', 'showinonlybody',true);
?>
<?php endif;?>
<?php if(!$story->deleted) echo $this->story->buildOperateMenu($story, 'view');?>
</div>
</div>
</div>
+103 -25
View File
@@ -138,6 +138,7 @@ class taskModel extends model
->batchCheck($requiredFields, 'notempty')
->checkIF($task->estimate != '', 'estimate', 'float')
->checkIF(!helper::isZeroDate($task->deadline), 'deadline', 'ge', $task->estStarted)
->checkFlow()
->exec();
if(dao::isError()) return false;
@@ -363,12 +364,6 @@ class taskModel extends model
if(is_array($data[$i]->{$extendField->field})) $data[$i]->{$extendField->field} = join(',', $data[$i]->{$extendField->field});
$data[$i]->{$extendField->field} = htmlSpecialString($data[$i]->{$extendField->field});
$message = $this->checkFlowRule($extendField, $data[$i]->{$extendField->field});
if($message)
{
dao::$errors['message'][] = sprintf($message);
return false;
}
}
}
@@ -423,6 +418,7 @@ class taskModel extends model
$this->dao->insert(TABLE_TASK)->data($task)
->autoCheck()
->checkIF($task->estimate != '', 'estimate', 'float')
->checkFlow()
->exec();
if(dao::isError()) return false;
@@ -908,6 +904,7 @@ class taskModel extends model
$now = helper::now();
$task = fixer::input('post')
->add('id', $taskID)
->setDefault('story, estimate, left, consumed', 0)
->setDefault('realStarted', '0000-00-00 00:00:00')
->setIF(is_numeric($this->post->estimate), 'estimate', (float)$this->post->estimate)
@@ -1048,6 +1045,7 @@ class taskModel extends model
->batchCheckIF($task->status == 'done', 'canceledBy, canceledDate', 'empty')
->batchCheckIF($task->closedReason == 'cancel', 'finishedBy, finishedDate', 'empty')
->checkFlow()
->where('id')->eq((int)$taskID)->exec();
if(!dao::isError())
@@ -1170,6 +1168,7 @@ class taskModel extends model
$oldTask = $oldTasks[$taskID];
$task = new stdclass();
$task->id = $taskID;
$task->color = $data->colors[$taskID];
$task->name = $data->names[$taskID];
$task->module = isset($data->modules[$taskID]) ? $data->modules[$taskID] : 0;
@@ -1217,8 +1216,6 @@ class taskModel extends model
if(is_array($task->{$extendField->field})) $task->{$extendField->field} = join(',', $task->{$extendField->field});
$task->{$extendField->field} = htmlSpecialString($task->{$extendField->field});
$message = $this->checkFlowRule($extendField, $task->{$extendField->field});
if($message) return print(js::alert($message));
}
if(isset($data->consumeds[$taskID]))
@@ -1353,6 +1350,7 @@ class taskModel extends model
->batchCheckIF($task->status == 'done', 'canceledBy, canceledDate', 'empty')
->batchCheckIF($task->closedReason == 'cancel', 'finishedBy, finishedDate', 'empty')
->checkFlow()
->where('id')->eq((int)$taskID)
->exec();
if(dao::isError())
@@ -1437,6 +1435,7 @@ class taskModel extends model
$now = helper::now();
$task = fixer::input('post')
->add('id', $taskID)
->cleanFloat('left')
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
@@ -1469,6 +1468,7 @@ class taskModel extends model
->data($task)
->autoCheck()
->check('left', 'float')
->checkFlow()
->where('id')->eq($taskID)
->exec();
@@ -1502,6 +1502,7 @@ class taskModel extends model
$now = helper::now();
$task = fixer::input('post')
->add('id', $taskID)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
->setDefault('status', 'doing')
@@ -1555,6 +1556,7 @@ class taskModel extends model
$this->dao->update(TABLE_TASK)->data($task)
->autoCheck()
->check('consumed,left', 'float')
->checkFlow()
->where('id')->eq((int)$taskID)->exec();
if($oldTask->parent > 0)
@@ -1727,6 +1729,7 @@ class taskModel extends model
}
$task = fixer::input('post')
->add('id', $taskID)
->setIF(is_numeric($this->post->consumed), 'consumed', (float)$this->post->consumed)
->setIF(!$this->post->realStarted and helper::isZeroDate($oldTask->realStarted), 'realStarted', $now)
->setDefault('left', 0)
@@ -1825,6 +1828,7 @@ class taskModel extends model
$this->dao->update(TABLE_TASK)->data($task)
->autoCheck()
->checkFlow()
->where('id')->eq((int)$taskID)
->exec();
@@ -1857,13 +1861,14 @@ class taskModel extends model
$oldTask = $this->getById($taskID);
$task = fixer::input('post')
->add('id', $taskID)
->setDefault('status', 'pause')
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', helper::now())
->remove('comment')
->get();
$this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('id')->eq((int)$taskID)->exec();
$this->dao->update(TABLE_TASK)->data($task)->autoCheck()->checkFlow()->where('id')->eq((int)$taskID)->exec();
if($oldTask->parent > 0) $this->updateParentStatus($taskID);
@@ -1890,6 +1895,7 @@ class taskModel extends model
$now = helper::now();
$task = fixer::input('post')
->add('id', $taskID)
->setDefault('status', 'closed')
->setDefault('assignedTo', 'closed')
->setDefault('assignedDate', $now)
@@ -1901,7 +1907,7 @@ class taskModel extends model
->remove('comment')
->get();
$this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('id')->eq((int)$taskID)->exec();
$this->dao->update(TABLE_TASK)->data($task)->autoCheck()->checkFlow()->where('id')->eq((int)$taskID)->exec();
if(!dao::isError())
{
@@ -1935,6 +1941,7 @@ class taskModel extends model
$now = helper::now();
$task = fixer::input('post')
->add('id', $taskID)
->setDefault('status', 'cancel')
->setDefault('assignedTo', $oldTask->openedBy)
->setDefault('assignedDate', $now)
@@ -1945,7 +1952,7 @@ class taskModel extends model
->remove('comment')
->get();
$this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('id')->eq((int)$taskID)->exec();
$this->dao->update(TABLE_TASK)->data($task)->autoCheck()->checkFlow()->where('id')->eq((int)$taskID)->exec();
if($oldTask->fromBug) $this->dao->update(TABLE_BUG)->set('toTask')->eq(0)->where('id')->eq($oldTask->fromBug)->exec();
if($oldTask->parent > 0) $this->updateParentStatus($taskID);
if($oldTask->parent == '-1')
@@ -1995,6 +2002,7 @@ class taskModel extends model
$oldTask = $this->getById($taskID);
if($oldTask->parent == '-1') $this->config->task->activate->requiredFields = '';
$task = fixer::input('post')
->add('id', $taskID)
->setIF(is_numeric($this->post->left), 'left', (float)$this->post->left)
->setDefault('left', 0)
->setDefault('status', 'doing')
@@ -2027,6 +2035,7 @@ class taskModel extends model
$this->dao->update(TABLE_TASK)->data($task)
->autoCheck()
->batchCheck($this->config->task->activate->requiredFields, 'notempty')
->checkFlow()
->where('id')->eq((int)$taskID)
->exec();
@@ -3390,20 +3399,7 @@ class taskModel extends model
echo helper::isZeroDate($task->activatedDate) ? '' : substr($task->activatedDate, 5, 11);
break;
case 'actions':
if($storyChanged)
{
common::printIcon('task', 'confirmStoryChange', "taskid=$task->id", $task, 'list', '', 'hiddenwin');
break;
}
if($task->status != 'pause') common::printIcon('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
if($task->status == 'pause') common::printIcon('task', 'restart', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
common::printIcon('task', 'recordEstimate', "taskID=$task->id", $task, 'list', 'time', '', 'iframe', true);
common::printIcon('task', 'edit', "taskID=$task->id", $task, 'list');
if($this->config->vision == 'rnd') common::printIcon('task', 'batchCreate', "execution=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id&ifame=0", $task, 'list', 'split', '', '', '', '', $this->lang->task->children);
echo $this->buildOperateMenu($task, 'browse');
break;
}
echo '</td>';
@@ -3526,4 +3522,86 @@ class taskModel extends model
}
return $members;
}
/**
* Build 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 task view menu.
*
* @param object $task
* @access public
* @return string
*/
public function buildOperateViewMenu($task)
{
if($task->deleted) return '';
$menu = '';
$params = "taskID=$task->id";
if((empty($task->team) || empty($task->children)) && $task->executionList->type != 'kanban')
{
$menu .= $this->buildMenu('task', 'batchCreate', "execution=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id", $task, 'view', 'split', '', '', '', "title='{$this->lang->task->children}'", $this->lang->task->children);
}
$menu .= $this->buildMenu('task', 'assignTo', "executionID=$task->execution&taskID=$task->id", $task, 'button', '', '', 'iframe', true, '', empty($task->team) ? $this->lang->task->assignTo : $this->lang->task->transfer);
$menu .= $this->buildMenu('task', 'start', $params, $task, 'view', '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('task', 'restart', $params, $task, 'view', '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('task', 'recordEstimate', $params, $task, 'view', '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('task', 'pause', $params, $task, 'view', '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('task', 'finish', $params, $task, 'view', '', '', 'iframe showinonlybody text-success', true);
$menu .= $this->buildMenu('task', 'activate', $params, $task, 'view', '', '', 'iframe showinonlybody text-success', true);
$menu .= $this->buildMenu('task', 'close', $params, $task, 'view', '', '', 'iframe showinonlybody', true);
$menu .= $this->buildMenu('task', 'cancel', $params, $task, 'view', '', '', 'iframe showinonlybody', true);
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('task', $task, 'view', 'direct');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildMenu('task', 'edit', $params, $task, 'view', '', '', 'showinonlybody');
$menu .= $this->buildMenu('task', 'create', "projctID={$task->execution}&storyID=0&moduleID=0&taskID=$task->id", $task, 'view', 'copy');
$menu .= $this->buildMenu('task', 'delete', "executionID=$task->execution&taskID=$task->id", $task, 'view', 'trash', 'hiddenwin', 'showinonlybody', true);
if($task->parent > 0) $menu .= $this->buildMenu('task', 'view', "taskID=$task->parent", $task, 'view', 'chevron-double-up', '', '', '', '', $this->lang->task->parent);
return $menu;
}
/**
* Build task browse action menu.
*
* @param object $task
* @access public
* @return string
*/
public function buildOperateBrowseMenu($task)
{
$menu = '';
$params = "taskID=$task->id";
$storyChanged = !empty($task->storyStatus) && $task->storyStatus == 'active' && $task->latestStoryVersion > $task->storyVersion && !in_array($task->status, array('cancel', 'closed'));
if($storyChanged) return $this->buildMenu('task', 'confirmStoryChange', $params, $task, 'browse', '', 'hiddenwin');
if($task->status != 'pause') $menu .= $this->buildMenu('task', 'start', $params, $task, 'browse', '', '', 'iframe', true);
if($task->status == 'pause') $menu .= $this->buildMenu('task', 'restart', $params, $task, 'browse', '', '', 'iframe', true);
$menu .= $this->buildMenu('task', 'close', $params, $task, 'browse', '', '', 'iframe', true);
$menu .= $this->buildMenu('task', 'finish', $params, $task, 'browse', '', '', 'iframe', true);
$menu .= $this->buildMenu('task', 'recordEstimate', $params, $task, 'browse', 'time', '', 'iframe', true);
$menu .= $this->buildMenu('task', 'edit', $params, $task, 'browse');
if($this->config->vision == 'rnd')
{
$menu .= $this->buildMenu('task', 'batchCreate', "execution=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id&ifame=0", $task, 'browse', 'split', '', '', '', '', $this->lang->task->children);
}
return $menu;
}
}
+2 -22
View File
@@ -169,28 +169,8 @@
<div class="btn-toolbar">
<?php common::printBack($browseLink);?>
<?php if(!isonlybody()) echo "<div class='divider'></div>";?>
<?php if(!$task->deleted):?>
<?php
if((empty($task->team) or empty($task->children)) and $execution->type != 'kanban') common::printIcon('task', 'batchCreate', "execution=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id", $task, 'button', 'split', '', '', '', "title='{$lang->task->children}'", $lang->task->children);
common::printIcon('task', 'assignTo', "executionID=$task->execution&taskID=$task->id", $task, 'button', '', '', 'iframe', true, '', empty($task->team) ? $lang->task->assignTo : $lang->task->transfer);
common::printIcon('task', 'start', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'restart', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'recordEstimate', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'pause', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'finish', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody text-success', true);
common::printIcon('task', 'activate', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody text-success', true);
common::printIcon('task', 'close', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'cancel', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
echo $this->buildOperateMenu($task, 'view');
if(!isonlybody()) echo "<div class='divider'></div>";
common::printIcon('task', 'edit', "taskID=$task->id", $task, 'button', '', '', 'showinonlybody');
common::printIcon('task', 'create', "projctID={$task->execution}&storyID=0&moduleID=0&taskID=$task->id", $task, 'button', 'copy');
common::printIcon('task', 'delete', "executionID=$task->execution&taskID=$task->id", $task, 'button', 'trash', 'hiddenwin', 'showinonlybody', true);
if($task->parent > 0) echo html::a(helper::createLink('task', 'view', "taskID=$task->parent"), "<i class='icon icon-chevron-double-up'></i>", '', "class='btn btn-link' title='{$lang->task->parent}'");
?>
<?php endif;?>
<?php $task->executionList = $execution;?>
<?php echo $this->task->buildOperateMenu($task, 'view');?>
</div>
</div>
</div>
+126 -26
View File
@@ -64,7 +64,7 @@ class testcaseModel extends model
/* Value of story may be showmore. */
$case->story = (int)$case->story;
$this->dao->insert(TABLE_CASE)->data($case)->autoCheck()->batchCheck($this->config->testcase->create->requiredFields, 'notempty')->exec();
$this->dao->insert(TABLE_CASE)->data($case)->autoCheck()->batchCheck($this->config->testcase->create->requiredFields, 'notempty')->checkFlow()->exec();
if(!$this->dao->isError())
{
$caseID = $this->dao->lastInsertID();
@@ -174,8 +174,6 @@ class testcaseModel extends model
if(is_array($data[$i]->{$extendField->field})) $data[$i]->{$extendField->field} = join(',', $data[$i]->{$extendField->field});
$data[$i]->{$extendField->field} = htmlSpecialString($data[$i]->{$extendField->field});
$message = $this->checkFlowRule($extendField, $data[$i]->{$extendField->field});
if($message) return print(js::alert($message));
}
foreach(explode(',', $this->config->testcase->create->requiredFields) as $field)
@@ -191,6 +189,7 @@ class testcaseModel extends model
$this->dao->insert(TABLE_CASE)->data($case)
->autoCheck()
->batchCheck($this->config->testcase->create->requiredFields, 'notempty')
->checkFlow()
->exec();
if(dao::isError())
@@ -715,6 +714,7 @@ class testcaseModel extends model
$version = $stepChanged ? $oldCase->version + 1 : $oldCase->version;
$case = fixer::input('post')
->add('id', $caseID)
->add('version', $version)
->setIF($this->post->story != false and $this->post->story != $oldCase->story, 'storyVersion', $this->loadModel('story')->getVersion($this->post->story))
->setIF(!$this->post->linkCase, 'linkCase', '')
@@ -737,7 +737,7 @@ class testcaseModel extends model
array_splice($requiredFieldsArr, $fieldIndex, 1);
$requiredFields = implode(',', $requiredFieldsArr);
}
$this->dao->update(TABLE_CASE)->data($case)->autoCheck()->batchCheck($requiredFields, 'notempty')->where('id')->eq((int)$caseID)->exec();
$this->dao->update(TABLE_CASE)->data($case)->autoCheck()->batchCheck($requiredFields, 'notempty')->checkFlow()->where('id')->eq((int)$caseID)->exec();
if(!$this->dao->isError())
{
$isLibCase = ($oldCase->lib and empty($oldCase->product));
@@ -835,6 +835,7 @@ class testcaseModel extends model
$now = helper::now();
$status = $this->getStatus('review', $oldCase);
$case = fixer::input('post')
->add('id', $caseID)
->remove('result,comment')
->setDefault('reviewedDate', substr($now, 0, 10))
->setDefault('lastEditedBy', $this->app->user->account)
@@ -843,7 +844,7 @@ class testcaseModel extends model
->join('reviewedBy', ',')
->get();
$this->dao->update(TABLE_CASE)->data($case)->autoCheck()->where('id')->eq($caseID)->exec();
$this->dao->update(TABLE_CASE)->data($case)->autoCheck()->checkFlow()->where('id')->eq($caseID)->exec();
if(dao::isError()) return false;
@@ -947,6 +948,7 @@ class testcaseModel extends model
foreach($caseIDList as $caseID)
{
$case = new stdclass();
$case->id = $caseID;
$case->lastEditedBy = $this->app->user->account;
$case->lastEditedDate = $now;
$case->pri = $data->pris[$caseID];
@@ -967,8 +969,6 @@ class testcaseModel extends model
if(is_array($case->{$extendField->field})) $case->{$extendField->field} = join(',', $case->{$extendField->field});
$case->{$extendField->field} = htmlSpecialString($case->{$extendField->field});
$message = $this->checkFlowRule($extendField, $case->{$extendField->field});
if($message) return print(js::alert($message));
}
$cases[$caseID] = $case;
@@ -983,6 +983,7 @@ class testcaseModel extends model
$this->dao->update(TABLE_CASE)->data($case)
->autoCheck()
->batchCheck($this->config->testcase->edit->requiredFields, 'notempty')
->checkFlow()
->where('id')->eq($caseID)
->exec();
@@ -1168,15 +1169,16 @@ class testcaseModel extends model
*
* @param object $case
* @param string $action
* @param string $module
* @access public
* @return void
*/
public static function isClickable($case, $action)
public static function isClickable($case, $action, $module = 'testcase')
{
$action = strtolower($action);
if($action == 'createbug') return $case->caseFails > 0;
if($action == 'review') return isset($case->caseStatus) ? $case->caseStatus == 'wait' : $case->status == 'wait';
if($module == 'testcase' && $action == 'createbug') return $case->caseFails > 0;
if($module == 'testcase' && $action == 'review') return isset($case->caseStatus) ? $case->caseStatus == 'wait' : $case->status == 'wait';
return true;
}
@@ -1712,22 +1714,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>';
@@ -2013,4 +2001,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;
}
}
+4 -34
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 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;?>
<?php $case->isLibCase = $isLibCase;?>
<?php $case->caseFails = $caseFails;?>
<?php $case->runID = $runID;?>
<?php echo $this->testcase->buildOperateMenu($case, 'view');?>
</div>
</div>
</div>
+26
View File
@@ -71,6 +71,7 @@ class testsuiteModel extends model
$suite = $this->loadModel('file')->processImgURL($suite, $this->config->testsuite->editor->create['id'], $this->post->uid);
$this->dao->insert(TABLE_TESTSUITE)->data($suite)
->batchcheck($this->config->testsuite->create->requiredFields, 'notempty')
->checkFlow()
->exec();
if(!dao::isError())
{
@@ -149,6 +150,7 @@ class testsuiteModel extends model
$oldSuite = $this->dao->select("*")->from(TABLE_TESTSUITE)->where('id')->eq((int)$suiteID)->fetch();
$suite = fixer::input('post')
->stripTags($this->config->testsuite->editor->edit['id'], $this->config->allowedTags)
->add('id', $suiteID)
->add('lastEditedBy', $this->app->user->account)
->add('lastEditedDate', helper::now())
->remove('uid')
@@ -157,6 +159,7 @@ class testsuiteModel extends model
$this->dao->update(TABLE_TESTSUITE)->data($suite)
->autoCheck()
->batchcheck($this->config->testsuite->edit->requiredFields, 'notempty')
->checkFlow()
->where('id')->eq($suiteID)
->exec();
if(!dao::isError())
@@ -324,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->testsuite->buildOperateMenu($suite, 'browse');?>
</td>
</tr>
<?php endforeach;?>

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