Merge branch 'sprint/189_mayue_task#53652'
This commit is contained in:
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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']))
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 = '导入用例';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;?>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;?>
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
@@ -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) . ' ' . $doc->editedDate . '</span>';
|
||||
$treeMenu[0] .= '<div class="tree-group"><span class="tail-info">' . zget($users, $doc->editedBy) . ' ' . $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> " . $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> " . $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> " . $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> " . $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) . ' ' . $doc->editedDate . '</span>';
|
||||
$treeMenu[$module->id] .= '<div class="tree-group"><span class="tail-info">' . zget($users, $doc->editedBy) . ' ' . $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> " . $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> " . $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> " . $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> " . $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])
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
})
|
||||
|
||||
@@ -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')
|
||||
{
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ()
|
||||
{
|
||||
|
||||
@@ -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';?>
|
||||
|
||||
@@ -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'");
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -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';?>
|
||||
|
||||
@@ -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'),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -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')
|
||||
)
|
||||
);
|
||||
?>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;?>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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©ExecutionID=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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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©ExecutionID=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>
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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}¶m=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('');
|
||||
|
||||
@@ -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
@@ -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';
|
||||
|
||||
@@ -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'] = '外部';
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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')
|
||||
{
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';?>
|
||||
|
||||
@@ -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';?>
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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
@@ -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=¶m=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=¶m=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=¶m=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>';
|
||||
|
||||
@@ -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=¶m=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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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¶m=$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¶m=$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¶m=$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¶m=$case->id", $case, 'browse', 'copy');
|
||||
|
||||
return $menu;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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¶m=$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¶m=$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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user