Merge branch 'feature/workflowgroup' into release/22.0.alpha1

This commit is contained in:
liyang
2025-12-16 09:16:13 +08:00
418 changed files with 8498 additions and 4034 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}}
if(!function_exists('getWebRoot')){function getWebRoot(){}}
/* 基本设置。Basic settings. */
$config->version = '21.7.8'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->version = '22.0'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->liteVersion = '1.2'; // 迅捷版版本。 The version of Lite.
$config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP.
$config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time.
+28 -26
View File
@@ -142,6 +142,7 @@ $filter->project->export = new stdclass();
$filter->project->task = new stdclass();
$filter->project->execution = new stdclass();
$filter->project->testcase = new stdclass();
$filter->project->deliverable = new stdclass();
$filter->projectstory->story = new stdclass();
$filter->projectstory->export = new stdclass();
$filter->qa->default = new stdclass();
@@ -319,31 +320,32 @@ $filter->program->browse->cookie['showClosed'] = 'code';
$filter->program->export->cookie['checkedItem'] = 'reg::checked';
$filter->program->ajaxgetdropmenu->cookie['showClosed'] = 'code';
$filter->project->default->cookie['lastProject'] = 'int';
$filter->project->default->cookie['lastPRJ'] = 'int';
$filter->project->default->cookie['projectMode'] = 'code';
$filter->project->default->cookie['kanbanview'] = 'code';
$filter->project->browse->cookie['involved'] = 'code';
$filter->project->browse->cookie['showProjectBatchEdit'] = 'int';
$filter->project->export->cookie['involved'] = 'code';
$filter->project->browse->cookie['projectType'] = 'code';
$filter->project->story->cookie['storyModuleParam'] = 'int';
$filter->project->story->cookie['storyPreProjectID'] = 'int';
$filter->project->story->cookie['storyProductParam'] = 'int';
$filter->project->story->cookie['storyBranchParam'] = 'reg::checked';
$filter->project->story->cookie['projectStoryOrder'] = 'reg::orderBy';
$filter->project->task->cookie['moduleBrowseParam'] = 'int';
$filter->project->task->cookie['preProjectID'] = 'int';
$filter->project->task->cookie['productBrowseParam'] = 'int';
$filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy';
$filter->project->task->cookie['windowWidth'] = 'int';
$filter->project->export->cookie['checkedItem'] = 'reg::checked';
$filter->project->execution->cookie['pagerExecutionAll'] = 'int';
$filter->project->execution->cookie['showTask'] = 'code';
$filter->project->execution->cookie['showStage'] = 'code';
$filter->project->testcase->cookie['showAutoCase'] = 'int';
$filter->project->testcase->cookie['onlyAutoCase'] = 'int';
$filter->project->testcase->cookie['onlyScene'] = 'code';
$filter->project->default->cookie['lastProject'] = 'int';
$filter->project->default->cookie['lastPRJ'] = 'int';
$filter->project->default->cookie['projectMode'] = 'code';
$filter->project->default->cookie['kanbanview'] = 'code';
$filter->project->browse->cookie['involved'] = 'code';
$filter->project->browse->cookie['showProjectBatchEdit'] = 'int';
$filter->project->export->cookie['involved'] = 'code';
$filter->project->browse->cookie['projectType'] = 'code';
$filter->project->story->cookie['storyModuleParam'] = 'int';
$filter->project->story->cookie['storyPreProjectID'] = 'int';
$filter->project->story->cookie['storyProductParam'] = 'int';
$filter->project->story->cookie['storyBranchParam'] = 'reg::checked';
$filter->project->story->cookie['projectStoryOrder'] = 'reg::orderBy';
$filter->project->task->cookie['moduleBrowseParam'] = 'int';
$filter->project->task->cookie['preProjectID'] = 'int';
$filter->project->task->cookie['productBrowseParam'] = 'int';
$filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy';
$filter->project->task->cookie['windowWidth'] = 'int';
$filter->project->export->cookie['checkedItem'] = 'reg::checked';
$filter->project->execution->cookie['pagerExecutionAll'] = 'int';
$filter->project->execution->cookie['showTask'] = 'code';
$filter->project->execution->cookie['showStage'] = 'code';
$filter->project->testcase->cookie['showAutoCase'] = 'int';
$filter->project->testcase->cookie['onlyAutoCase'] = 'int';
$filter->project->testcase->cookie['onlyScene'] = 'code';
$filter->project->deliverable->cookie['deliverableModule'] = 'int';
$filter->projectstory->story->cookie['storyModuleParam'] = 'int';
$filter->projectstory->story->cookie['pagerProductBrowse'] = 'int';
@@ -560,4 +562,4 @@ $filter->convert->mapjira2zentao->cookie['jiraRelation'] = 'reg::any';
$filter->admin->register->cookie['zentaosid'] = 'reg::any';
$filter->admin->getcaptcha->cookie['zentaosid'] = 'reg::any';
$filter->admin->sendcode->cookie['zentaosid'] = 'reg::any';
$filter->admin->giftpackage->cookie['zentaosid'] = 'reg::any';
$filter->admin->giftpackage->cookie['zentaosid'] = 'reg::any';
+31 -26
View File
@@ -366,6 +366,7 @@ $config->logonMethods[] = 'zanode.nodelist';
$config->logonMethods[] = 'screen.viewold';
$config->logonMethods[] = 'system.backupview';
$config->logonMethods[] = 'screen.staticdataold';
$config->logonMethods[] = 'stage.updateorder';
$config->openModules = array();
$config->openModules[] = 'install';
@@ -415,27 +416,30 @@ define('TABLE_PLANSTORY', '`' . $config->db->prefix . 'planstory`');
define('TABLE_RELEASE', '`' . $config->db->prefix . 'release`');
define('TABLE_RELEASERELATED', '`' . $config->db->prefix . 'releaserelated`');
define('TABLE_PROGRAM', '`' . $config->db->prefix . 'project`');
define('TABLE_PROJECT', '`' . $config->db->prefix . 'project`');
define('TABLE_EXECUTION', '`' . $config->db->prefix . 'project`');
define('TABLE_TASK', '`' . $config->db->prefix . 'task`');
define('TABLE_TASKSPEC', '`' . $config->db->prefix . 'taskspec`');
define('TABLE_TASKTEAM', '`' . $config->db->prefix . 'taskteam`');
define('TABLE_TEAM', '`' . $config->db->prefix . 'team`');
define('TABLE_PROJECTADMIN', '`' . $config->db->prefix . 'projectadmin`');
define('TABLE_PROJECTPRODUCT','`' . $config->db->prefix . 'projectproduct`');
define('TABLE_PROJECTSTORY', '`' . $config->db->prefix . 'projectstory`');
define('TABLE_PROJECTCASE', '`' . $config->db->prefix . 'projectcase`');
define('TABLE_TASKESTIMATE', '`' . $config->db->prefix . 'taskestimate`');
define('TABLE_EFFORT', '`' . $config->db->prefix . 'effort`');
define('TABLE_BURN', '`' . $config->db->prefix . 'burn`');
define('TABLE_CFD', '`' . $config->db->prefix . 'cfd`');
define('TABLE_BUILD', '`' . $config->db->prefix . 'build`');
define('TABLE_ACL', '`' . $config->db->prefix . 'acl`');
define('TABLE_PROGRAM', '`' . $config->db->prefix . 'project`');
define('TABLE_PROJECT', '`' . $config->db->prefix . 'project`');
define('TABLE_EXECUTION', '`' . $config->db->prefix . 'project`');
define('TABLE_TASK', '`' . $config->db->prefix . 'task`');
define('TABLE_TASKSPEC', '`' . $config->db->prefix . 'taskspec`');
define('TABLE_TASKTEAM', '`' . $config->db->prefix . 'taskteam`');
define('TABLE_TEAM', '`' . $config->db->prefix . 'team`');
define('TABLE_PROJECTADMIN', '`' . $config->db->prefix . 'projectadmin`');
define('TABLE_PROJECTPRODUCT', '`' . $config->db->prefix . 'projectproduct`');
define('TABLE_PROJECTDELIVERABLE','`' . $config->db->prefix . 'projectdeliverable`');
define('TABLE_PROJECTSTORY', '`' . $config->db->prefix . 'projectstory`');
define('TABLE_PROJECTCASE', '`' . $config->db->prefix . 'projectcase`');
define('TABLE_PROJECTCHANGE', '`' . $config->db->prefix . 'projectchange`');
define('TABLE_TASKESTIMATE', '`' . $config->db->prefix . 'taskestimate`');
define('TABLE_EFFORT', '`' . $config->db->prefix . 'effort`');
define('TABLE_BURN', '`' . $config->db->prefix . 'burn`');
define('TABLE_CFD', '`' . $config->db->prefix . 'cfd`');
define('TABLE_BUILD', '`' . $config->db->prefix . 'build`');
define('TABLE_ACL', '`' . $config->db->prefix . 'acl`');
define('TABLE_DESIGN', '`' . $config->db->prefix . 'design`');
define('TABLE_DESIGNSPEC', '`' . $config->db->prefix . 'designspec`');
define('TABLE_DELIVERABLE', '`' . $config->db->prefix . 'deliverable`');
define('TABLE_DELIVERABLESTAGE','`' . $config->db->prefix . 'deliverablestage`');
define('TABLE_DOCLIB', '`' . $config->db->prefix . 'doclib`');
define('TABLE_DOC', '`' . $config->db->prefix . 'doc`');
define('TABLE_DOCBLOCK', '`' . $config->db->prefix . 'docblock`');
@@ -443,6 +447,7 @@ define('TABLE_DEMANDPOOL', '`' . $config->db->prefix . 'demandpool`');
define('TABLE_DEMAND', '`' . $config->db->prefix . 'demand`');
define('TABLE_DEMANDSPEC', '`' . $config->db->prefix . 'demandspec`');
define('TABLE_DEMANDREVIEW', '`' . $config->db->prefix . 'demandreview`');
define('TABLE_DECISION', '`' . $config->db->prefix . 'decision`');
define('TABLE_API', '`' . $config->db->prefix . 'api`');
define('TABLE_API_SPEC', '`' . $config->db->prefix . 'apispec`');
define('TABLE_APISTRUCT', '`' . $config->db->prefix . 'apistruct`');
@@ -741,6 +746,9 @@ $config->objectTables['productline'] = TABLE_MODULE;
$config->objectTables['repocommit'] = TABLE_REPOHISTORY;
$config->objectTables['system'] = TABLE_SYSTEM;
$config->objectTables['mark'] = TABLE_MARK;
$config->objectTables['cm'] = TABLE_OBJECT;
$config->objectTables['baseline'] = TABLE_OBJECT;
$config->objectTables['projectchange'] = TABLE_PROJECTCHANGE;
$config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions', 'aiPrompts', 'promptDesign', 'promptExec');
$config->disabledFeatures = '';
@@ -771,18 +779,15 @@ $config->dtable = new stdclass();
$config->dtable->colVars = array('width', 'minWidth', 'type', 'flex', 'fixed', 'sortType', 'checkbox', 'nestedToggle', 'statusMap', 'actionsMap', 'group');
$config->featureGroup = new stdclass();
$config->featureGroup->my = array('score');
$config->featureGroup->product = array('roadmap', 'track', 'UR', 'ER');
$config->featureGroup->scrum = array();
$config->featureGroup->waterfall = array();
$config->featureGroup->agileplus = array();
$config->featureGroup->waterfallplus = array();
$config->featureGroup->assetlib = array();
$config->featureGroup->other = array('devops', 'kanban', 'setCode');
$config->featureGroup->my = array('score');
$config->featureGroup->product = array('roadmap', 'track', 'UR', 'ER');
$config->featureGroup->project = array();
$config->featureGroup->assetlib = array();
$config->featureGroup->other = array('devops', 'kanban', 'setCode');
$config->db->sqliteBlacklist = array('sqlite_queue', 'cron');
$config->hasDropmenuApps = array('program', 'project', 'product', 'execution', 'qa', 'admin', 'bi', 'feedback', 'demandpool');
$config->hasBranchMenuModules = array('product', 'story', 'release', 'bug', 'testcase', 'testtask', 'branch', 'tree');
$config->hasBranchMenuModules = array('product', 'story', 'release', 'bug', 'testcase', 'testtask', 'branch', 'tree', 'workflowgroup', 'deliverable', 'process', 'activity', 'review', 'reviewcl', 'stage', 'auditcl');
$config->excludeBranchMenu = array('product-dashboard', 'product-view', 'product-whitelist', 'product-addwhitelist', 'branch-manage', 'branch-batchedit');
$config->excludeDropmenuList = array('program-browse', 'program-productview', 'program-kanban', 'product-all', 'product-index', 'product-kanban', 'project-kanban', 'execution-all', 'execution-executionkanban', 'project-browse', 'project-template', 'project-createtemplate', 'project-batchedit', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit', 'metric-preview', 'metric-browse', 'metric-view', 'metriclib-browse', 'qa-index', 'caselib-create', 'feedback-batchedit', 'feedback-batchclose', 'feedback-showimport', 'dimension-browse', 'dataview-browse', 'dataview-create', 'dataview-query', 'ticket-batchedit', 'ticket-batchfinish', 'ticket-batchactivate', 'file-download');
$config->excludeDropmenuModules = array('reporttemplate');
+5 -1
View File
@@ -1,7 +1,11 @@
UPDATE `zt_risk` SET `pri` = 3 WHERE `pri` = 'middle';
UPDATE `zt_risk` SET `pri` = 1 WHERE `pri` = 'high';
UPDATE `zt_risk` SET `pri` = 2 WHERE `pri` = 'low';
ALTER TABLE `zt_demand` CHANGE `version` `version` smallint unsigned NOT NULL DEFAULT '1';
ALTER TABLE `zt_demand` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 3;
ALTER TABLE `zt_issue` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 3;
ALTER TABLE `zt_risk` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 3;
ALTER TABLE `zt_risk` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 2;
ALTER TABLE `zt_opportunity` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 3;
ALTER TABLE `zt_design` CHANGE `project` `project` int unsigned NOT NULL DEFAULT 0;
ALTER TABLE `zt_design` CHANGE `product` `product` int unsigned NOT NULL DEFAULT 0;
+2
View File
@@ -6,6 +6,8 @@ UPDATE `zt_opportunity` SET `pri` = 1 WHERE `pri` = 'high';
UPDATE `zt_opportunity` SET `pri` = 2 WHERE `pri` = 'middle';
UPDATE `zt_opportunity` SET `pri` = 3 WHERE `pri` = 'low';
UPDATE `zt_workflowlayout` SET `width` = '0' WHERE `width` = 'auto';
CREATE TABLE IF NOT EXISTS `zt_ai_knowledgelib` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`vision` varchar(10) NOT NULL DEFAULT 'rnd' COMMENT '所属界面',
+265
View File
@@ -0,0 +1,265 @@
ALTER TABLE `zt_workflowgroup` CHANGE `projectModel` `projectModel` varchar(30) NOT NULL DEFAULT '';
INSERT INTO `zt_workflowgroup` (`type`, `projectModel`, `projectType`, `name`, `code`, `status`, `vision`, `main`) VALUES
('project', 'agileplus', 'product', '融合敏捷式产品研发', 'agileplusproduct', 'normal', 'rnd', '1'),
('project', 'agileplus', 'project', '融合敏捷式项目研发', 'agileplusproject', 'normal', 'rnd', '1'),
('project', 'waterfallplus', 'product', '融合瀑布式产品研发', 'waterfallplusproduct', 'normal', 'rnd', '1'),
('project', 'waterfallplus', 'project', '融合瀑布式项目研发', 'waterfallplusproject', 'normal', 'rnd', '1'),
('project', 'kanban', 'product', '看板式产品研发', 'kanbanproduct', 'normal', 'rnd', '1'),
('project', 'kanban', 'project', '看板式项目研发', 'kanbanproject', 'normal', 'rnd', '1'),
('project', 'ipd', 'ipd', 'IPD集成产品研发', 'ipdproduct', 'normal', 'rnd', '1'),
('project', 'ipd', 'tpd', 'IPD预研产品研发', 'tpdproduct', 'normal', 'rnd', '1'),
('project', 'ipd', 'cbb', 'IPD平台产品研发', 'cbbproduct', 'normal', 'rnd', '1'),
('project', 'ipd', 'cpdproduct', 'IPD定制产品研发', 'cpdproduct', 'normal', 'rnd', '1'),
('project', 'ipd', 'cpdproject', 'IPD定制项目研发', 'cpdproject', 'normal', 'rnd', '1');
ALTER TABLE `zt_doc` ADD `isDeliverable` tinyint unsigned NOT NULL DEFAULT 0 AFTER `acl`;
ALTER TABLE `zt_deliverable` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `zt_deliverable` ADD `activity` int unsigned NOT NULL DEFAULT '0' AFTER `name`;
ALTER TABLE `zt_deliverable` ADD `trimmable` char(30) NOT NULL DEFAULT '0' AFTER `activity`;
ALTER TABLE `zt_deliverable` ADD `trimRule` varchar(255) NOT NULL AFTER `trimmable`;
ALTER TABLE `zt_deliverable` ADD `template` text NOT NULL AFTER `trimRule`;
ALTER TABLE `zt_deliverable` ADD `status` varchar(30) NOT NULL DEFAULT 'enabled' AFTER `name`;
ALTER TABLE `zt_deliverable` ADD `category` varchar(255) NOT NULL DEFAULT '' AFTER `lastEditedDate`;
ALTER TABLE `zt_deliverable` ADD `builtin` tinyint unsigned NOT NULL DEFAULT 0 AFTER `module`;
ALTER TABLE `zt_deliverable` ADD `systemList` tinyint unsigned NOT NULL DEFAULT 0 AFTER `builtin`;
CREATE TABLE IF NOT EXISTS `zt_deliverablestage` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`deliverable` int unsigned NOT NULL DEFAULT 0,
`stage` varchar(30) NOT NULL,
`required` varchar(30) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE UNIQUE INDEX `unique` ON `zt_deliverablestage`(`deliverable`,`stage`);
ALTER TABLE `zt_module` ADD `extra` varchar(30) NOT NULL DEFAULT '';
ALTER TABLE `zt_process` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `zt_process` ADD `module` int unsigned NOT NULL DEFAULT '0' AFTER `name`;
ALTER TABLE `zt_activity` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `process`;
UPDATE `zt_process` SET `editedDate` = NULL WHERE `editedDate` LIKE '1970-01-01%';
UPDATE `zt_process` SET `assignedDate` = NULL WHERE `assignedDate` LIKE '1970-01-01%';
UPDATE `zt_activity` SET `editedDate` = NULL WHERE `editedDate` LIKE '1970-01-01%';
UPDATE `zt_activity` SET `assignedDate` = NULL WHERE `assignedDate` LIKE '1970-01-01%';
ALTER TABLE `zt_searchindex` CHANGE `content` `content` longtext NULL;
CREATE TABLE IF NOT EXISTS `zt_projectdeliverable` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`project` int unsigned NOT NULL DEFAULT 0 COMMENT '所属项目',
`submitFrom` int unsigned NOT NULL DEFAULT 0 COMMENT '提交来源',
`review` int unsigned NOT NULL DEFAULT 0 COMMENT '关联评审',
`deliverable` varchar(30) NOT NULL,
`name` varchar(255) NOT NULL,
`doc` int unsigned NOT NULL DEFAULT '0',
`docVersion` smallint unsigned NOT NULL DEFAULT '0',
`status` varchar(30) NOT NULL DEFAULT '',
`hasApproval` tinyint unsigned NOT NULL DEFAULT 1 COMMENT '是否有评审流程',
`version` varchar(255) NULL,
`frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态',
`createdBy` varchar(30) NOT NULL DEFAULT '',
`createdDate` date NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE INDEX `project` ON `zt_projectdeliverable` (`project`);
CREATE UNIQUE INDEX `project_deliverable_doc` ON `zt_projectdeliverable` (`project`, `deliverable`, `doc`);
ALTER TABLE `zt_approvalflowobject` ADD `relatedBy` varchar(30) NOT NULL DEFAULT '' AFTER `objectID`;
ALTER TABLE `zt_approvalflowobject` ADD `relatedDate` datetime NULL AFTER `relatedBy`;
ALTER TABLE `zt_reviewcl` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`;
UPDATE `zt_lang` SET `value` = '单元测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'unittest' AND `value` = '单元测试阶段';
UPDATE `zt_lang` SET `value` = '功能测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'feature' AND `value` = '功能测试阶段';
UPDATE `zt_lang` SET `value` = '集成测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'intergrate' AND `value` = '集成测试阶段';
UPDATE `zt_lang` SET `value` = '系统测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'system' AND `value` = '系统测试阶段';
UPDATE `zt_lang` SET `value` = '冒烟测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'smoke' AND `value` = '冒烟测试阶段';
UPDATE `zt_lang` SET `value` = '版本验证环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'bvt' AND `value` = '版本验证阶段';
ALTER TABLE `zt_review` ADD `version` varchar(255) NOT NULL DEFAULT '' AFTER `docVersion`;
ALTER TABLE `zt_review` ADD `deliverable` int unsigned NOT NULL DEFAULT '0' AFTER `title`;
ALTER TABLE `zt_review` ADD `isBaseline` tinyint(1) DEFAULT '0' AFTER `status`;
ALTER TABLE `zt_review` ADD `type` varchar(30) NOT NULL DEFAULT '' AFTER `version`;
UPDATE `zt_review` SET `status` = 'reviewing' WHERE `status` = 'wait';
UPDATE `zt_review` SET `status` = 'pass' WHERE `status` = 'auditing' OR `status` = 'done';
UPDATE `zt_review` SET `type` = 'deliverable';
UPDATE `zt_review` AS t1
JOIN `zt_object` AS t2 ON t1.object = t2.id
SET t1.version = t2.version;
UPDATE `zt_reviewissue` AS t1
JOIN `zt_review` AS t2 ON t1.review = t2.id
SET t1.project = t2.project;
UPDATE `zt_object` SET `category` = 'intergrate' WHERE `category` = 'ITTC';
UPDATE `zt_object` SET `category` = 'system' WHERE `category` = 'STTC';
ALTER TABLE `zt_object` ADD `status` varchar(20) NOT NULL DEFAULT '' AFTER `type`;
ALTER TABLE `zt_object` ADD `approval` int unsigned NOT NULL DEFAULT '0' AFTER `status`;
ALTER TABLE `zt_object` ADD `reviewResult` varchar(20) NOT NULL DEFAULT '' AFTER `approval`;
ALTER TABLE `zt_object` ADD `reviewOpinion` text NULL AFTER `reviewResult`;
ALTER TABLE `zt_object` ADD `reviewers` text NULL AFTER `reviewResult`;
ALTER TABLE `zt_object` ADD `categoryVersion` text DEFAULT NULL AFTER `category`;
ALTER TABLE `zt_object` ADD `editedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '由谁编辑' AFTER `createdDate`;
ALTER TABLE `zt_object` ADD `editedDate` datetime DEFAULT NULL COMMENT '编辑时间' AFTER `editedBy`;
ALTER TABLE `zt_object` MODIFY `category` varchar(255) NOT NULL DEFAULT '';
ALTER TABLE `zt_object` MODIFY `createdDate` datetime DEFAULT NULL;
ALTER TABLE `zt_designspec` ADD `docs` text NULL AFTER `files`;
ALTER TABLE `zt_designspec` ADD `docVersions` text NULL AFTER `docs`;
UPDATE `zt_designspec` AS t1
JOIN `zt_design` AS t2 ON t1.design = t2.id AND t2.version = t1.version
SET t1.docs = t2.docs, t1.docVersions = t2.docVersions;
ALTER TABLE `zt_design` DROP COLUMN `docs`;
ALTER TABLE `zt_design` DROP COLUMN `docVersions`;
ALTER TABLE `zt_reviewissue` ADD `assignedTo` char(30) COLLATE 'utf8mb4_general_ci' NOT NULL DEFAULT '' AFTER `createdDate`;
ALTER TABLE `zt_reviewissue` ADD `assignedDate` datetime NULL AFTER `assignedTo`;
REPLACE INTO `zt_grouppriv`(`group`, `module`, `method`) SELECT `group`, `module`, 'active' as `method` FROM `zt_grouppriv` WHERE `module` = 'reviewissue' AND `method` = 'updateStatus';
REPLACE INTO `zt_grouppriv`(`group`, `module`, `method`) SELECT `group`, `module`, 'close' as `method` FROM `zt_grouppriv` WHERE `module` = 'reviewissue' AND `method` = 'updateStatus';
DELETE FROM `zt_grouppriv` WHERE `module` IN ('reviewissue', 'activity', 'process') AND `method` IN ('outputList', 'updateOrder');
DELETE FROM `zt_grouppriv` WHERE `module` = 'zoutput';
DELETE FROM `zt_grouppriv` WHERE `module` = 'design' AND `method` IN ('setPlusType', 'setType');
INSERT INTO `zt_metric` (`purpose`, `scope`, `object`, `stage`, `type`, `name`, `alias`, `code`, `unit`, `desc`, `definition`, `when`, `createdBy`, `createdDate`, `builtin`, `deleted`, `dateType`) VALUES
('scale', 'user', 'reviewissue', 'released', 'php', '按人员统计的被指派的评审意见数 ', '被指派的评审意见数', 'count_of_assigned_reviewissue_in_user', 'count', '按人员统计的被指派的评审意见数表示每个人被指派的评审意见数量之和,反映了每个人员需要处理的评审意见数量的规模。该数值越大,说明需要投入越多的时间处理评审意见。', '所有评审意见个数求和\r\n指派给为某人\r\n过滤已删除的评审意见\r\n过滤已关闭的评审意见\r\n过滤已删除项目的评审意见', '', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate');
ALTER TABLE `zt_approvalnode`
CHANGE `status` `status` varchar(30) NOT NULL DEFAULT 'wait',
CHANGE `result` `result` varchar(30) NOT NULL DEFAULT '';
CREATE TABLE IF NOT EXISTS `zt_decision` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`workflowGroup` int unsigned NOT NULL DEFAULT '0',
`stage` int unsigned NOT NULL DEFAULT '0',
`order` int unsigned NOT NULL DEFAULT '0',
`title` varchar(255) NOT NULL DEFAULT '',
`type` varchar(30) NOT NULL DEFAULT '',
`category` varchar(30) NOT NULL DEFAULT '',
`builtin` enum('0','1') NOT NULL DEFAULT '0',
`createdBy` char(30) NOT NULL DEFAULT '',
`createdDate` datetime NULL,
`editedBy` char(30) NOT NULL DEFAULT '',
`editedDate` datetime NULL,
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `zt_stage` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `zt_stage` ADD `order` int unsigned NOT NULL DEFAULT '0' AFTER `editedDate`;
ALTER TABLE `zt_object` ADD `execution` int unsigned NOT NULL DEFAULT '0' AFTER `project`;
ALTER TABLE `zt_object` ADD `categoryTitle` varchar(255) NOT NULL DEFAULT '' AFTER `category`;
ALTER TABLE `zt_object` MODIFY COLUMN `type` enum('reviewed','taged','decision') NOT NULL DEFAULT 'reviewed';
ALTER TABLE `zt_auditcl` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`;
UPDATE zt_activity SET optional = CASE
WHEN optional = 'yes' THEN 'no'
WHEN optional = 'no' THEN 'yes'
ELSE optional
END;
ALTER TABLE `zt_process` CHANGE `model` `model` varchar(30) NOT NULL DEFAULT '';
ALTER TABLE `zt_auditplan` ADD `cycleType` varchar(10) NOT NULL DEFAULT 'noCycle' COMMENT '周期类型' AFTER `status`;
ALTER TABLE `zt_auditplan` ADD `cycleConfig` varchar(100) NOT NULL DEFAULT '[]' COMMENT '周期设置' AFTER `cycleType`;
ALTER TABLE `zt_auditplan` ADD `cyclePlan` smallint unsigned NOT NULL DEFAULT 0 COMMENT '生成计划(提前的天数)' AFTER `cycleConfig`;
ALTER TABLE `zt_auditplan` ADD `deadline` date NULL COMMENT '过期时间' AFTER `cyclePlan`;
ALTER TABLE `zt_auditplan` ADD `templateID` int unsigned NOT NULL DEFAULT 0 COMMENT '周期性活动检查ID' AFTER `deadline`;
ALTER TABLE `zt_auditplan` DROP COLUMN `dateType`;
ALTER TABLE `zt_auditplan` DROP COLUMN `config`;
ALTER TABLE `zt_auditplan` DROP COLUMN `checkBy`;
INSERT INTO `zt_cron` (`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type`, `buildin`, `status`) VALUES('2','2','*','*','*','moduleName=auditplan&methodName=ajaxCreateCycleAuditplan','生成周期性活动检查','zentao',1,'normal');
UPDATE `zt_auditplan` SET `checkDate` = NULL WHERE `checkDate` = '0000-00-00';
CREATE TABLE IF NOT EXISTS `zt_projectchange` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`project` int unsigned NOT NULL DEFAULT 0 COMMENT '所属项目',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT '变更名称',
`urgency` varchar(30) NOT NULL DEFAULT '' COMMENT '变更等级',
`type` varchar(30) NOT NULL DEFAULT '' COMMENT '变更类型',
`deliverable` varchar(255) NOT NULL DEFAULT '' COMMENT '变更对象',
`status` varchar(30) NOT NULL DEFAULT '' COMMENT '变更状态',
`approval` int unsigned NOT NULL DEFAULT 0 COMMENT '审批流程',
`reviewResult` varchar(20) NOT NULL DEFAULT '' COMMENT '审批结果',
`reviewOpinion` text DEFAULT NULL COMMENT '审批意见',
`reviewers` text DEFAULT NULL COMMENT '评审人员',
`owner` varchar(30) NOT NULL DEFAULT '' COMMENT '负责人',
`reason` varchar(1000) NOT NULL DEFAULT '' COMMENT '变更原因',
`desc` text DEFAULT NULL COMMENT '变更描述',
`deadline` datetime DEFAULT NULL COMMENT '期望完成时间',
`createdBy` varchar(30) NOT NULL DEFAULT '' COMMENT '创建人',
`createdDate` datetime DEFAULT NULL COMMENT '创建时间',
`editedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '编辑人',
`editedDate` datetime DEFAULT NULL COMMENT '编辑时间',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE INDEX `project` ON `zt_projectchange` (`project`);
ALTER TABLE `zt_nc` ADD `execution` int unsigned NOT NULL DEFAULT 0 COMMENT '所属执行' AFTER `project`;
ALTER TABLE `zt_nc` ADD `deliverable` int unsigned NOT NULL DEFAULT 0 COMMENT '交付物' AFTER `auditplan`;
ALTER TABLE `zt_workflowgroup` ADD `disabledFeatures` varchar(255) NOT NULL DEFAULT '' COMMENT '项目流程关闭的功能' AFTER `disabledModules`;
ALTER TABLE `zt_stage` MODIFY COLUMN `projectType` varchar(30) NOT NULL DEFAULT '' COMMENT '所属项目流程的类型';
DELETE FROM `zt_doc` WHERE `templateType` IN ('PP', 'SRS', 'HLDS', 'DDS', 'ADS', 'DBDS', 'ITTC', 'STTC') AND `builtin` = '1';
ALTER TABLE `zt_object`
DROP `range`,
ADD `items` text NULL DEFAULT NULL COMMENT '评审条目列表' AFTER `data`,
DROP `storyEst`,
DROP `taskEst`,
DROP `requestEst`,
DROP `testEst`,
DROP `devEst`,
DROP `designEst`;
ALTER TABLE `zt_review`
DROP `auditedBy`,
DROP `lastAuditedBy`,
DROP `lastAuditedDate`,
DROP `toAuditBy`,
DROP `toAuditDate`,
DROP `auditResult`;
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`) VALUES
('lang', '基线状态', 'baselineStatus', '1', 'rnd', 'admin', null, 'baselineStatus', '', '', ''),
('lang', '基线审批结果', 'baselineReviewResult', '1', 'rnd', 'admin', null, 'baselineReviewResult', '', '', ''),
('lang', '项目变更紧急等级', 'projectchangeUrgencyList', '1', 'rnd', 'admin', null, 'projectchangeUrgencyList', '', '', ''),
('lang', '项目变更类型', 'projectchangeTypeList', '1', 'rnd', 'admin', null, 'projectchangeTypeList', '', '', ''),
('lang', '项目变更状态', 'projectchangeStatus', '1', 'rnd', 'admin', null, 'projectchangeStatus', '', '', ''),
('lang', '项目变更审批结果', 'projectchangeReviewResult', '1', 'rnd', 'admin', null, 'projectchangeReviewResult', '', '', ''),
('lang', '风险来源', 'riskSource', '1', 'rnd', 'admin', null, 'riskSource', '', '', ''),
('lang', '风险类型', 'riskCategory', '1', 'rnd', 'admin', null, 'riskCategory', '', '', ''),
('lang', '风险策略', 'riskStrategy', '1', 'rnd', 'admin', null, 'riskStrategy', '', '', ''),
('lang', '风险状态', 'riskStatus', '1', 'rnd', 'admin', null, 'riskStatus', '', '', ''),
('lang', '风险影响程度', 'riskImpact', '1', 'rnd', 'admin', null, 'riskImpact', '', '', ''),
('lang', '风险发生概率', 'riskProbability', '1', 'rnd', 'admin', null, 'riskProbability', '', '', ''),
('lang', '风险系数', 'riskRate', '1', 'rnd', 'admin', null, 'riskRate', '', '', ''),
('lang', '风险优先级', 'riskPri', '1', 'rnd', 'admin', null, 'riskPri', '', '', ''),
('lang', '风险取消原因', 'riskCancelReason', '1', 'rnd', 'admin', null, 'riskCancelReason', '', '', ''),
('lang', '问题优先级', 'issuePri', '1', 'rnd', 'admin', null, 'issuePri', '', '', ''),
('lang', '问题严重程度', 'issueSeverity', '1', 'rnd', 'admin', null, 'issueSeverity', '', '', ''),
('lang', '问题类型', 'issueType', '1', 'rnd', 'admin', null, 'issueType', '', '', ''),
('lang', '4问题解决方式', 'issueResolution', '1', 'rnd', 'admin', null, 'issueResolution', '', '', ''),
('lang', '问题状态', 'issueStatus', '1', 'rnd', 'admin', null, 'issueStatus', '', '', ''),
('lang', '机会来源', 'opportunitySource', '1', 'rnd', 'admin', null, 'opportunitySource', '', '', ''),
('lang', '机会类型', 'opportunityType', '1', 'rnd', 'admin', null, 'opportunityType', '', '', ''),
('lang', '机会策略', 'opportunityStrategy', '1', 'rnd', 'admin', null, 'opportunityStrategy', '', '', ''),
('lang', '机会状态', 'opportunityStatus', '1', 'rnd', 'admin', null, 'opportunityStatus', '', '', ''),
('lang', '机会影响程度', 'opportunityImpact', '1', 'rnd', 'admin', null, 'opportunityImpact', '', '', ''),
('lang', '机会发生概率', 'opportunityChance', '1', 'rnd', 'admin', null, 'opportunityChance', '', '', ''),
('lang', '机会优先级', 'opportunityPri', '1', 'rnd', 'admin', null, 'opportunityPri', '', '', ''),
('lang', '机会取消原因', 'opportunityCancelReason', '1', 'rnd', 'admin', null, 'opportunityCancelReason', '', '', '');
ALTER TABLE `zt_story` ADD `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态' AFTER `verifiedDate`;
ALTER TABLE `zt_design` ADD `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态' AFTER `desc`;
ALTER TABLE `zt_project` ADD `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态' AFTER `vision`;
ALTER TABLE `zt_doc` ADD `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态' AFTER `builtIn`;
+367 -1090
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
<?php
$config->admin->navsGroup['feature']['project'] .= 'story,';
@@ -0,0 +1,16 @@
<?php
foreach($lang->admin->menuList->feature['tabMenu']['project'] as $key => $value)
{
if(!in_array($key, array('project', 'execution', 'story')))
{
unset($lang->admin->menuList->feature['tabMenu']['project'][$key]);
}
}
foreach($lang->admin->menuList->feature['tabMenu']['menuOrder']['project'] as $key => $value)
{
if(!in_array($key, array('project', 'execution', 'story')))
{
unset($lang->admin->menuList->feature['tabMenu']['menuOrder']['project'][$key]);
}
}
@@ -236,7 +236,6 @@ toolbar
div
(
set::id('kanbanList'),
setClass('bg-canvas'),
zui::kanbanList
(
set('$replace', false),
-9
View File
@@ -167,15 +167,6 @@ class control extends baseControl
return;
}
elseif(!$this->getFormData)
{
$this->parseJSON($moduleName, $methodName);
ob_start();
echo $this->output;
return;
}
if(empty($moduleName)) $moduleName = $this->moduleName;
if(empty($methodName)) $methodName = $this->methodName;
+17 -36
View File
@@ -3519,45 +3519,26 @@ class baseRouter
}
}
if(version_compare(PHP_VERSION, '8.2.0', '>='))
{
#[\AllowDynamicProperties] // https://www.php.net/manual/zh/class.allowdynamicproperties.php
class config
{
public function set(string $key, $value)
{
helper::setMember('config', $key, $value);
}
}
}
else
#[AllowDynamicProperties]
class config
{
/**
* config类。
* The config class.
* 设置成员变量,成员可以是'db.user'类似的格式。
* Set the value of a member. the member can be the format like db.user.
*
* @package framework
* <code>
* <?php
* $config->set('db.user', 'wwccss');
* ?>
* </code>
* @param string $key the key of the member
* @param mixed $value the value
* @access public
* @return void
*/
class config
public function set(string $key, $value)
{
/**
* 设置成员变量,成员可以是'db.user'类似的格式。
* Set the value of a member. the member can be the format like db.user.
*
* <code>
* <?php
* $config->set('db.user', 'wwccss');
* ?>
* </code>
* @param string $key the key of the member
* @param mixed $value the value
* @access public
* @return void
*/
public function set(string $key, $value)
{
helper::setMember('config', $key, $value);
}
helper::setMember('config', $key, $value);
}
}
@@ -3766,7 +3747,7 @@ class EndResponseException extends \Exception
$exception = new self($content);
$exception->content = $content;
return $exception;
}
@@ -3938,4 +3919,4 @@ class ztSessionHandler implements SessionHandlerInterface
return $count;
}
}
}
+7 -7
View File
@@ -529,11 +529,11 @@ class control extends baseControl
$flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
if(!$flow) return $fields;
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group);
if(!$action || $action->extensionType != 'extend') return $fields;
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object);
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID);
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $flow->group);
/* 复制项目时显示被复制项目的工作流字段值。*/
if($moduleName == 'project' && $methodName == 'create')
@@ -569,11 +569,11 @@ class control extends baseControl
$flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
if(!$flow) return '';
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group);
if(!$action || $action->extensionType == 'none') return '';
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, !empty($action->action) ? $action->action: '', $object);
$fieldList = $this->loadModel('workflowaction')->getPageFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID, $groupID);
$fieldList = $this->loadModel('workflowaction')->getPageFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID, $flow->group);
$html = '';
if(!empty($flow->css)) $html .= "<style>$flow->css</style>";
@@ -613,13 +613,13 @@ class control extends baseControl
$flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
if(!$flow) return array();
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group);
if(!$action || $action->extensionType != 'extend') return array();
$uiID = is_object($object) ? $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object) : 0;
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, $object, $uiID, $groupID);
$layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID, $groupID);
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, $object, $uiID, $flow->group);
$layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID, $flow->group);
$notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
if($layouts)
+7 -10
View File
@@ -88,6 +88,11 @@ class helper extends baseHelper
{
global $config;
if(in_array($feature, array('waterfall', 'waterfallplus')))
{
return !str_contains(",$config->disabledFeatures,", ",{$feature},"); // 轻量级模式排除瀑布、融合瀑布模型
}
if(str_contains($feature, '_'))
{
$code = explode('_', $feature);
@@ -95,8 +100,6 @@ class helper extends baseHelper
return !str_contains(",$config->disabledFeatures,", ",{$code},");
}
if(in_array($feature, array('scrum', 'waterfall', 'agileplus', 'waterfallplus'))) return !str_contains(",$config->disabledFeatures,", ",{$feature},");
$hasFeature = false;
$canConfigFeature = false;
foreach($config->featureGroup as $group => $modules)
@@ -106,16 +109,10 @@ class helper extends baseHelper
if($feature != $group && $feature != $module) continue;
$canConfigFeature = true;
if(in_array($group, array('scrum', 'waterfall', 'agileplus', 'waterfallplus')))
{
$hasFeature |= (helper::hasFeature("{$group}") && helper::hasFeature("{$group}_{$module}"));
}
else
{
$hasFeature |= helper::hasFeature("{$group}_{$module}");
}
$hasFeature |= helper::hasFeature("{$group}_{$module}");
}
}
return !$canConfigFeature || ($hasFeature && !str_contains(",$config->disabledFeatures,", ",{$feature},"));
}
+1 -1
View File
@@ -259,7 +259,7 @@ class dao extends baseDAO
$flowAction = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '{$module}' AND `action` = '{$method}' AND `buildin` = '1' AND `extensionType` = 'extend' AND `vision` = '{$this->config->vision}' AND `group` = '{$groupID}'")->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' AND t1.vision = '{$this->config->vision}' AND t1.`group` = '{$groupID}'")->fetchAll();
$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' AND t1.vision = '{$this->config->vision}' AND t1.`group` = '{$groupID}' AND t2.`group` = '{$groupID}'")->fetchAll();
if(!$flowFields) return $this;
$rules = array();
+13 -10
View File
@@ -78,7 +78,7 @@ class form extends fixer
* @param int $flowGroupID
* @return form
*/
public static function data(?array $configObject = null, int $objectID = 0, int $flowGroupID = 0): form
public static function data(array $configObject = null, int $objectID = 0, int $flowGroupID = 0): form
{
global $app, $config;
@@ -143,8 +143,6 @@ class form extends fixer
if($moduleName == 'project' && $methodName == 'copyconfirm') $methodName = 'create';
if(empty($app->control)) return $configObject;
$flow = $app->control->loadModel('workflow')->getByModule($moduleName);
if(!$flow) return $configObject;
$app->control->loadModel('workflowgroup');
if($flowGroupID)
@@ -153,13 +151,16 @@ class form extends fixer
$groupID = (empty($group) || $group->main) ? 0 : $flowGroupID;
}
$groupID = isset($groupID) ? $groupID : $app->control->workflowgroup->getGroupIDByDataID($flow->module, $objectID);
$action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
$groupID = isset($groupID) ? $groupID : $app->control->workflowgroup->getGroupIDByDataID($moduleName, $objectID);
$flow = $app->control->loadModel('workflow')->getByModule($moduleName, false, $groupID);
if(!$flow) return $configObject;
$action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group);
if(!$action || $action->extensionType != 'extend') return $configObject;
$uiID = $app->control->loadModel('workflowlayout')->getUIByDataID($flow->module, $action->action, $objectID);
$fieldList = $app->control->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID);
$layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID, $groupID);
$fieldList = $app->control->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $flow->group);
$layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID, $flow->group);
$notEmptyRule = $app->control->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
if($layouts)
{
@@ -179,8 +180,10 @@ class form extends fixer
}
else
{
$type = $field->type == 'int' ? 'int' : 'string';
$configObject[$field->field] = array('required' => $required, 'type' => $type, 'default' => '');
$type = 'string';
if($field->type == 'int') $type = 'int';
if($field->type == 'decimal') $type = 'float';
$configObject[$field->field] = array('required' => $required, 'type' => $type, 'default' => $field->type == 'int' || $field->type == 'decimal' ? 0 : '');
if($field->control == 'richtext') $configObject[$field->field]['control'] = 'editor';
}
}
@@ -419,7 +422,7 @@ class form extends fixer
if(isset($config['filter'])) $data = $this->filter($data, $config['filter'], zget($config, 'separator', ','));
if(isset($config['required']) && $config['required'] && isset($this->rawdata->$field) && empty($data))
if(isset($config['required']) && $config['required'] && isset($this->rawdata->$field) && (is_null($this->rawdata->$field) || $this->rawdata->$field === '' || is_array($this->rawdata->$field)) && empty($data))
{
$rawModule = $app->rawModule == 'feedback' && in_array($app->rawMethod, array('touserstory', 'toepic')) ? 'story' : $app->rawModule;
$errorKey = isset($config['type']) && $config['type'] == 'array' ? "{$field}[]" : $field;
+1 -3
View File
@@ -2,6 +2,4 @@ This file was automatically generated by Composer Patches (https://github.com/cw
Patches applied to this directory:
Add return type declarations to Iterator methods
Source: patches/box-spout-php8-compatibility.patch
Source: patches/box-spout-php8-compatibility.patch
+7 -5
View File
@@ -50,7 +50,7 @@ class backBtn extends btn
'testsuite' => 'testsuite-browse,testsuite-view,testtask-cases,my-index,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view',
'testtask' => 'testtask-browse,testtask-cases,qa-index,testcase-browse,execution-build,my-index,my-effort,company-effort,company-calendar,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view,project-testcase,project-testtask,execution-testtask',
'testreport' => 'testreport-browse,project-testreport,execution-testreport,execution-testtask',
'tree' => 'product-browse,project-browse,execution-task,bug-browse,projectstory-story,host-browse,execution-story,feedback-admin,ticket-browse,testcase-browse,caselib-browse,dataview-browse',
'tree' => 'product-browse,project-browse,execution-task,bug-browse,projectstory-story,host-browse,execution-story,feedback-admin,ticket-browse,testcase-browse,caselib-browse,dataview-browse,deliverable-browse',
'doc' => 'doc-mySpace,doc-productSpace,doc-projectSpace,doc-teamSpace,doc-view,execution-doc',
'design' => 'design-browse,projectstory-track,my-effort,company-effort,company-calendar,my-index',
'release' => 'release-browse,release-view,product-roadmap,kanban-view,projectrelease-browse,story-view,my-index,my-effort,company-effort,company-calendar,product-dynamic,project-dynamic,project-view',
@@ -67,7 +67,7 @@ class backBtn extends btn
'store' => 'store-browse',
'space' => 'space-browse',
'serverroom' => 'serverroom-browse',
'project' => 'program-browse,program-project,project-browse,project-view,project-team,charter-view,project-template',
'project' => 'program-browse,program-project,project-browse,project-view,project-team,charter-view,project-template,project-deliverable',
'product' => 'product-all,program-productview,program-product,product-view,product-dynamic,my-index,charter-view,testtask-view',
'gitlab' => 'space-browse,gitlab-browseproject',
'gitfox' => 'space-browse',
@@ -90,7 +90,7 @@ class backBtn extends btn
'service' => 'service-browse,service-manage,service-view',
'domain' => 'domain-browse,domain-view',
'researchtask' => 'marketresearch-task,my-index,product-dynamic,project-dynamic,project-view,my-contribute',
'review' => 'review-browse,my-index,product-dynamic,project-dynamic,project-view,programplan-browse,project-execution,my-contribute',
'review' => 'review-browse,project-deliverable,my-index,product-dynamic,project-dynamic,project-view,programplan-browse,project-execution,my-contribute,my-audit',
'charter' => 'my-index,charter-browse,charter-view,my-audit,program-browse,project-view',
'roadmap' => 'charter-view',
'approvalflow' => 'approvalflow-browse',
@@ -98,9 +98,11 @@ class backBtn extends btn
'deploy' => 'deploy-browse',
'program' => 'program-browse,program-productview',
'workflowgroup' => 'workflowgroup-project,workflowgroup-deliverable',
'deliverable' => 'deliverable-browse',
'cm' => 'cm-browse,cm-view',
'deliverable' => 'deliverable-browse,project-deliverable,action-trash',
'weekly' => 'weekly-browse',
'milestone' => 'weekly-browse'
'milestone' => 'weekly-browse',
'nc' => 'nc-browse'
);
$props = parent::getProps();
+3 -1
View File
@@ -1 +1,3 @@
.deliverable-item {padding-left: 3.5rem !important}
.deliverable-category-name>.text:after { right: -0.5rem !important; }
.deliverable-item .form-tip { padding-left: 0.5rem; }
.deliverable-item-doc-picker { width: 18rem; }
+60 -11
View File
@@ -5,14 +5,18 @@
window.getDeliverableFileActions = function(file, deliverable)
{
let actions = [];
if(canDownload && typeof file.id != 'undefined') actions[0] = {icon: 'download', key: 'download', url: $.createLink('file', 'download', 'id=' + file.id), target: '_blank'};
actions[1] = {icon: 'edit', key: 'rename'};
actions[2] = {icon: 'trash', key: 'delete'};
/* 可以预览的文件。 */
if(['txt', 'jpg', 'jpeg', 'gif', 'png', 'bmp', 'mp4'].includes(file.extension) && canDownload && typeof file.id != 'undefined')
{
actions[3] = {icon: 'eye', key: 'view', url: $.createLink('file', 'download', `fileID=${file.id}&mouse=left`), 'data-toggle' : 'modal', 'data-size' : 'lg'};
actions[0] = {icon: 'eye', key: 'view', url: $.createLink('file', 'download', `fileID=${file.id}&mouse=left`), 'data-toggle' : 'modal', 'data-size' : 'lg'};
}
if(canDownload && typeof file.id != 'undefined') actions[1] = {icon: 'download', key: 'download', url: $.createLink('file', 'download', 'id=' + file.id), target: '_blank'};
if(!onlyShow)
{
actions[2] = {icon: 'edit', key: 'rename'};
if(!isTemplate) actions[3] = {icon: 'trash', key: 'delete'};
}
return actions;
@@ -25,9 +29,17 @@ window.getDeliverableFileActions = function(file, deliverable)
window.getDocActions = function(doc, deliverable)
{
let actions = [];
actions[0] = {icon: 'eye', key: 'view', url: $.createLink('doc', 'view', 'docID=' + doc.id)};
actions[1] = {icon: 'edit', key: 'rename'};
actions[2] = {icon: 'trash', key: 'delete'};
const url = $.createLink('doc', 'view', `docID=${doc.id}`);
if(isTemplate)
{
if(doc.canView) actions[0] = {icon: 'eye', key: 'view', url, target: '_blank'};
}
else
{
actions[0] = {icon: 'eye', key: 'view', url, target: '_blank'};
if(doc.editable) actions[1] = {icon: 'edit', key: 'rename'};
actions[2] = {icon: 'trash', key: 'delete'};
}
return actions;
}
@@ -35,10 +47,47 @@ window.getDocActions = function(doc, deliverable)
* 获取交付物的操作按钮。
* Get deliverable actions.
*/
window.getDeliverableActions = function(deliverable)
window.getDeliverableActions = function(deliverable, category)
{
let actions = [];
actions[0] = {text: addFile, icon: 'file', key: 'selectFile'};
if(deliverable.template > 0 && canDownload) actions[1] = {text: downloadTemplate, icon: 'download', key: 'downloadTemplate', url: $.createLink('file', 'download', 'templateID=' + deliverable.template), target: '_blank'};
if(isTemplate)
{
actions[0] = {text: addFile, icon: 'file', key: 'selectFile'}; // 后台交付物只有上传文件操作。
}
else
{
if(canCreateDoc) actions[0] = {text: createDoc, icon: 'doc', key: 'createDoc', 'onClick' : () => showBtnModal(deliverable, category)}; // 前台交付物有三个创建文档的操作。
}
return actions;
}
window.showBtnModal = function(deliverable, category)
{
const actions = [];
actions.push(`<div class="flex flex-row gap-4 justify-center items-center h-20">`);
actions.push(`<a class="btn btn-sm btn-primary w-28" target='_blank' href="${createDocUrl}"><i class="icon icon-doc"></i>${createDoc}</a>`);
actions.push(`<a class="btn btn-sm btn-primary w-28" data-toggle="modal" href="${uploadDocUrl}"><i class="icon icon-file"></i>${uploadFile}</a>`);
if(typeof category.template != 'undefined' && category.template.length > 0)
{
actions.push(`<button class="btn btn-sm btn-primary w-28" data-toggle="dropdown"><i class="icon icon-plus"></i>${createByTemplate} <span class="caret"></span></button>`);
actions.push(`<menu class="dropdown-menu menu">`);
for(const template of category.template)
{
actions.push(`<li class="menu-item"><a href="${template.url}" target="_blank">${template.text}</a></li>`);
}
actions.push(`</menu>`);
}
actions.push(`</div>`);
zui.Modal.open({
id: 'createDoc',
title: createDoc,
type: 'custom',
size: 'sm',
content: {html: actions.join('')},
});
}
+30 -8
View File
@@ -13,7 +13,7 @@ class deliverable extends wg
*/
protected static array $defineProps = array
(
'items: array', // 交付物条目。
'items: array', // 交付物条目。
'formName: string', // 表单名称。
'maxFileSize: string' // 最大文件大小。
);
@@ -30,11 +30,14 @@ class deliverable extends wg
{
global $lang, $app;
$app->loadLang('doc');
$app->loadLang('file');
jsVar('addFile', $lang->doc->addFile);
jsVar('downloadTemplate', $lang->doc->downloadTemplate);
$app->loadLang('deliverable');
jsVar('createByTemplate', $lang->deliverable->createByTemplate);
jsVar('createDoc', $lang->doc->create);
jsVar('uploadFile', $lang->doc->uploadFile);
jsVar('deleteItem', $lang->delete);
jsVar('otherLang', $lang->other);
jsVar('canDownload', hasPriv('file', 'download'));
jsVar('canCreateDoc', hasPriv('doc', 'create'));
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
}
@@ -47,7 +50,7 @@ class deliverable extends wg
* @access public
* @return string
*/
public static function getPageCSS(): ?string
public static function getPageCss(): ?string
{
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
}
@@ -64,8 +67,22 @@ class deliverable extends wg
global $lang, $app;
$app->loadLang('doc');
$app->loadLang('file');
$app->loadLang('deliverable');
$formName = $this->prop('formName') ? $this->prop('formName') : 'deliverable';
$formName = $this->prop('formName') ? $this->prop('formName') : 'deliverable';
$isTemplate = $this->prop('isTemplate') ? $this->prop('isTemplate') : false;
$onlyShow = $this->prop('onlyShow') ? $this->prop('onlyShow') : false;
$extraCategory = $this->prop('extraCategory') ? $this->prop('extraCategory') : array_column($this->prop('items'), 'category');
$categories = $this->prop('categories');
$projectID = $this->prop('projectID');
$createDocUrl = $this->prop('createDocUrl');
$uploadDocUrl = $this->prop('uploadDocUrl');
jsVar('addFile', $isTemplate ? $lang->deliverable->files : $lang->doc->addFile);
jsVar('createDocUrl', $createDocUrl);
jsVar('uploadDocUrl', $uploadDocUrl);
jsVar('isTemplate', $isTemplate);
jsVar('onlyShow', $onlyShow);
if(!$this->hasProp('maxFileSize'))
{
@@ -76,16 +93,21 @@ class deliverable extends wg
$this->setProp('maxFileSize', $maxFileSize);
}
$selectDocTips = $isTemplate ? $lang->deliverable->selectDoc : $lang->deliverable->selectDocInProject;
$docLink = $isTemplate ? helper::createLink('doc', 'ajaxGetTemplateDocs', "keyword={search}") : helper::createLink('doc', 'ajaxGetDeliverableDocs', "keyword={search}&projectID={$projectID}");
return zui::deliverableList
(
set::formName($formName),
set::items($this->prop('items')),
set::docPicker(array('placeholder' => $lang->doc->selectDoc, 'items' => helper::createLink('doc', 'ajaxGetMineDocs', 'keyword={search}'))),
set::docPicker(array('placeholder' => $selectDocTips, 'items' => $docLink, 'cache' => false)),
set::getFileActions(jsRaw('window.getDeliverableFileActions')),
set::getDocActions(jsRaw('window.getDocActions')),
set::getEmptyActions(jsRaw('window.getDeliverableActions')),
set::maxFileSize($this->prop('maxFileSize')),
set::extraCategory($lang->other)
set::isTemplate($isTemplate),
set::extraCategory($extraCategory),
set::categories($categories)
);
}
}
+6 -4
View File
@@ -275,6 +275,7 @@ CSS;
}
$titleActions = isset($item['titleActions']) ? $item['titleActions'] : null;
$className = isset($item['className']) ? $item['className'] : null;
if($titleActions) unset($item['titleActions']);
return div
@@ -289,7 +290,7 @@ CSS;
) : null,
div
(
setClass('detail-section-content py-1'),
setClass("detail-section-content py-1 {$className}"),
$item ? new content(is_array($item) ? set($item) : $item) : null,
)
);
@@ -374,16 +375,17 @@ CSS;
protected function buildMain()
{
$mainSections = $this->buildMainSections();
return div
(
setClass('detail-main flex-auto col gap-2 min-w-0'),
div
$mainSections ? div
(
setClass('detail-sections canvas shadow rounded px-6 py-4'),
setKey('main'),
$this->buildMainSections(),
$mainSections,
$this->block('main')
),
) : null,
$this->block('sections'),
$this->children(),
$this->buildHistory(),
+34 -7
View File
@@ -397,7 +397,7 @@ function handleSwitchView(mode, location, info)
}
}
function showDocBasicModal(parentID, docID, isDraft, modalType = 'doc')
function showDocBasicModal(parentID, docID, isDraft, modalType = 'doc', doc = null)
{
const docApp = getDocApp();
const spaceType = docApp.spaceType;
@@ -422,6 +422,10 @@ function showDocBasicModal(parentID, docID, isDraft, modalType = 'doc')
destroyOnHide: !docID,
cache : true,
request: {
method: "POST",
data: {
title: doc?.title,
},
error: error => {
if(docBasicModal.current) docBasicModal.current.hide();
showSaveFailedAlert(error);
@@ -608,7 +612,7 @@ function submitEditDoc(doc, formData)
*/
function handleCreateDoc(doc, spaceID, libID, moduleID)
{
return showDocBasicModal(0, 0, doc.status === 'draft').then((formData) => {
return showDocBasicModal(0, 0, doc.status === 'draft', 'doc', doc).then((formData) => {
return submitNewDoc(doc, spaceID, libID, moduleID, formData);
});
}
@@ -904,7 +908,7 @@ const actionsMap =
hasPriv('addChapter') && doc.parent != 0 ? {icon: 'plus', text: lang.addSameChapter, command: `addChapter/${doc.parent}`} : null,
!isTemplate && hasPriv('create') ? {icon: 'plus', text: lang.addSubDoc, command: `startCreateDoc/${doc.id}`} : null,
!isChapter && canMoveDoc(doc) ? {icon: 'folder-move', text: lang.moveDoc, command: `moveDoc/${doc.id}`} : null,
!isChapter && hasPriv('delete') ? {icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`} : null,
!doc.frozen && !isChapter && hasPriv('delete') ? {icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`} : null,
isChapter && hasPriv('editChapter') ? {icon: 'pencil', text: lang.editChapter, command: `editChapter/${doc.id}`} : null,
isChapter && hasPriv('deleteChapter') ? {icon: 'trash', text: lang.deleteChapter, command: `deleteDoc/${doc.id}`} : null,
];
@@ -916,7 +920,7 @@ const actionsMap =
const moreItems = [];
if(canModify && canMoveDoc(doc)) moreItems.push({icon: 'folder-move', text: lang.moveDoc, command: `moveDoc/${doc.id}`});
if(canModify && hasPriv('delete')) moreItems.push({icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`});
if(!doc.frozen && canModify && hasPriv('delete')) moreItems.push({icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`});
if(!isApi && canModify && hasPriv('effort')) moreItems.push({icon: 'time', text: lang.effort, command: `effortDoc/${doc.id}`});
if(!isApi && !isTemplate)
{
@@ -1123,6 +1127,7 @@ const actionsMap =
const items = canModify ?
[
canCreateDoc ? {icon: 'plus', text: lang.createDoc, command: `startCreateDoc/${docID}`} : null,
canUploadDoc ? {icon: 'import', text: lang.uploadDoc, command: 'uploadDoc'} : null,
canCreateDoc ? {type: 'divider'} : null,
canCreateOffice ? {icon: 'file-word', text: lang.createList.word, command: 'startCreateOffice/word'} : null,
canCreateOffice ? {icon: 'file-powerpoint', text: lang.createList.ppt, command: 'startCreateOffice/ppt'} : null,
@@ -1134,7 +1139,6 @@ const actionsMap =
return [
((canExportDoc && libID > 0) || canCreateDoc) ? {type: 'divider', style: {margin: '6px 0'}} : null,
canExportDoc && libID > 0 ? {icon: 'export', text: lang.export, command: 'exportWord'} : null, // 不在库中,无法导出
canUploadDoc ? {icon: 'import', text: lang.uploadDoc, command: 'uploadDoc'} : null,
items.length ? {icon: 'plus', type: 'dropdown', btnType: 'primary', size: 'md', text: lang.create, items: items} : null,
];
},
@@ -1306,14 +1310,24 @@ const commands =
if (typeof doc !== 'object') {
doc = docApp._treeMap.value.docs.get(doc)?.data;
}
if(doc.frozen) return zui.Modal.alert(getLang('frozenTips').replace('%s', getLang('edit')));
/** 如果是附件类型并且没有内容的文档,则跳转到上传文档页面进行编辑。 */
if(doc.type === 'attachment' && !doc.hasContent) {
const type = docApp.lib ? docApp.lib.data.type : docApp.spaceType;
const url = $.createLink('doc', 'uploadDocs', `docID=${doc.id}&objectType=${type}&objectID=${docApp.spaceID}&libID=${doc.lib}&moduleID=${doc.module}&type=attachment`);
zui.Modal.open({url: url});
return false;
}
return docApp.startEditDoc(doc, options);
},
/** 上传文档。Upload Doc. */
uploadDoc: function()
uploadDoc: function(_, args)
{
const docApp = getDocApp();
const docID = args[0] || docApp.docID
const type = docApp.lib ? docApp.lib.data.type : docApp.spaceType;
const url = $.createLink('doc', 'uploadDocs', `objectType=${type}&objectID=${docApp.spaceID}&libID=${docApp.libID}&moduleID=${docApp.moduleID}&type=attachment`);
const url = $.createLink('doc', 'uploadDocs', `docID=${docID}&objectType=${type}&objectID=${docApp.spaceID}&libID=${docApp.libID}&moduleID=${docApp.moduleID}&type=attachment`);
zui.Modal.open({url: url});
},
/** 创建 Office 文件。 Start create office file. */
@@ -2050,6 +2064,19 @@ function getTableOptions(options, info)
if(typeof actionHints[key] === 'string') value.hint = actionHints[key];
value.disabled = !privMap[key];
});
col.actionItemCreator = function(item, info)
{
const rowData = info.row.data;
item.command = item.command.replace('{id}', String(rowData.id));
if(rowData.frozen && (item.key === 'edit' || item.key === 'trash'))
{
const actionHint = item.key == 'trash' ? actionHints['delete'] : actionHints[item.key];
item.hint = lang.frozenTips.replace('%s', actionHint);
item.disabled = true;
}
return item;
}
}
});
options.onCheckChange = function(changes)
+3 -2
View File
@@ -1,6 +1,7 @@
#pick-pop-admin-menu {width: 145px!important;}
#pick-pop-admin-menu {width: 155px!important;}
#pick-pop-admin-menu .dropmenu-list {padding: 8px;}
#pick-pop-admin-menu .dropmenu-item + .dropmenu-item {margin-top: 4px}
#pick-pop-admin-menu .dropmenu-item.active {background-color: unset;}
#pick-pop-admin-menu .dropmenu-item:hover {color: rgba(var(--color-primary-500-rgb),var(--tw-text-opacity)); background-color: rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));}
.dropmenu-item .item-content .label {flex-shrink: 0;}
.dropmenu-item .item-content .label {flex-shrink: 0;}
.svg-icon {height: 24px; width: 24px; text-align: center;}
+108 -48
View File
@@ -66,6 +66,111 @@ class dropmenu extends wg
);
}
/**
* 构建子级下拉菜单。
* Build sub drop menu.
*
* @param string $module
* @param string $method
* @param string $tab
* @param string|int $objectID
* @param string|int $extra
* @access public
* @return array
*/
public function buildSubDropMenu(string $module, string $method, string $tab, string|int $objectID, string|int $extra): array
{
$app = data('app');
$subMenu = array();
if(!in_array($module, $app->config->hasBranchMenuModules) || in_array("{$module}-{$method}", $app->config->excludeBranchMenu)) return $subMenu;
if($tab == 'product' || $tab == 'qa') $subMenu[] = $this->buildBranchDropMenu($module, $method, $tab, $objectID, $extra);
if($tab == 'admin') $subMenu[] = $this->buildFlowGroupMenu($module, $method);
return $subMenu;
}
/**
* 构建分支子级下拉菜单。
* Build branch drop menu.
*
* @param string $module
* @param string $method
* @param string $tab
* @param string|int $objectID
* @param string|int $extra
* @access public
* @return object|null
*/
public function buildBranchDropMenu(string $module, string $method, string $tab, string|int $objectID, string|int $extra): object|null
{
$data = $this->prop('data');
$app = data('app');
$lang = data('lang');
$branchMenu = null;
if(!$objectID) return $branchMenu;
$product = $app->control->loadModel('product')->getByID((int)$objectID);
if($product->type != 'normal')
{
$branchID = '';
if(!is_null(data('branch'))) $branchID = data('branch');
if(!is_null(data('branchID'))) $branchID = data('branchID');
if(strpos((string)$branchID, ',') !== false)
{
$branches = explode(',', $branchID);
$branchID = (!empty($_SESSION['branch']) || $app->session->branch === '0') && in_array($app->session->branch, $branches) ? $app->session->branch : $branches[0];
}
$app->control->loadModel('branch');
/* Get current branch name. */
$branchName = '';
if($branchID == 'all' || $branchID === '')
{
$branchID = 'all';
$branchName = $lang->branch->all;
}
elseif($branchID == 0)
{
$branchName = $lang->branch->main;
}
elseif($branchID > 0)
{
$branchName = $app->control->branch->getByID((string)$branchID);
}
if($module == 'testtask' && $method == 'browse') $extra = data('type');
if($app->moduleName == 'tree' && $app->methodName == 'browse')
{
$module = 'tree';
$method = 'browse';
$extra = data('viewType');
}
$branchURL = createLink('branch', 'ajaxGetDropMenu', "objectID=$objectID&branch=$branchID&module=$module&method=$method&extra=$extra");
$branchMenu = $this->buildDropmenu('branch-dropmenu', 'branch-dropmenu', $branchURL, $branchName, $data, $branchID);
}
return $branchMenu;
}
/**
* 构造流程子级下拉菜单。
* Build flow group menu.
*
* @param string $module
* @param string $method
* @access public
* @return object|null
*/
public function buildFlowGroupMenu(string $module, string $method): object|null
{
$data = $this->prop('data');
$app = data('app');
$objectID = data('groupID');
if(!$objectID) return null;
$flowGroup = $app->control->loadModel('workflowGroup')->fetchByID((int)$objectID);
$flowURL = createLink('workflowGroup', 'ajaxGetDropMenu', "objectID=$objectID&module=$module&method=$method");
return $this->buildDropmenu('flow-dropmenu', 'flow-dropmenu', $flowURL, $flowGroup->name, $data, $objectID);
}
/**
* Override the build method.
*
@@ -107,52 +212,6 @@ class dropmenu extends wg
if($tab == 'dimension' && $module == 'tree' && $method == 'browsegroup') $extra = data('viewType');
$branchMenu = null;
if(($tab == 'product' || $tab == 'qa') && in_array($module, $app->config->hasBranchMenuModules) && !in_array("{$module}-{$method}", $app->config->excludeBranchMenu))
{
if($objectID)
{
$product = $app->control->loadModel('product')->getByID((int)$objectID);
if($product->type != 'normal')
{
$branchID = '';
if(!is_null(data('branch'))) $branchID = data('branch');
if(!is_null(data('branchID'))) $branchID = data('branchID');
if(strpos((string)$branchID, ',') !== false)
{
$branches = explode(',', $branchID);
$branchID = (!empty($_SESSION['branch']) || $app->session->branch === '0') && in_array($app->session->branch, $branches) ? $app->session->branch : $branches[0];
}
$app->control->loadModel('branch');
/* Get current branch name. */
$branchName = '';
if($branchID == 'all' || $branchID === '')
{
$branchID = 'all';
$branchName = $lang->branch->all;
}
elseif($branchID == 0)
{
$branchName = $lang->branch->main;
}
elseif($branchID > 0)
{
$branchName = $app->control->branch->getByID((string)$branchID);
}
if($module == 'testtask' && $method == 'browse') $extra = data('type');
if($app->moduleName == 'tree' && $app->methodName == 'browse')
{
$module = 'tree';
$method = 'browse';
$extra = data('viewType');
}
$branchURL = createLink('branch', 'ajaxGetDropMenu', "objectID=$objectID&branch=$branchID&module=$module&method=$method&extra=$extra");
$branchMenu = $this->buildDropmenu('branch-dropmenu', 'branch-dropmenu', $branchURL, $branchName, $data, $branchID);
}
}
}
if($module == 'testtask' && in_array($method, array('cases', 'view', 'report', 'groupcase', 'linkcase')) && $app->tab == 'qa')
{
$testtaskUrl = createLink('testtask', 'ajaxGetDropMenu', data('switcherParams'));
@@ -162,6 +221,7 @@ class dropmenu extends wg
if($tab == 'admin')
{
$currentMenuKey = $app->control->loadModel('admin')->getMenuKey();
$extra = $app->rawParams ? reset($app->rawParams) : '';
$text = isset($lang->admin->menuList->{$currentMenuKey}) ? $lang->admin->menuList->{$currentMenuKey}['name'] : $currentMenuKey;
$url = createLink('admin', 'ajaxGetDropMenu', "currentMenuKey={$currentMenuKey}");
$menuID = 'admin-menu';
@@ -172,7 +232,7 @@ class dropmenu extends wg
if($tab == 'feedback')
{
$objectID = data('productID');
$extra = data('viewType');
$extra = data('viewType') ? data('viewType') : '';
}
if($tab == 'demandpool') $objectID = data('poolID');
if($tab == 'market')
@@ -201,7 +261,7 @@ class dropmenu extends wg
return array
(
$this->buildDropmenu($menuID, $id, $url, $text, $data, $objectID),
$branchMenu,
$this->buildSubDropMenu($module, $method, $tab, (int)$objectID, $extra),
isset($testtaskMenu) ? $testtaskMenu : null
);
}
+1 -1
View File
@@ -94,7 +94,7 @@ class entityLabel extends wg
$entityName = $this->buildEntityName();
return div
(
setClass('entity-label space-x-1.5 overflow-hidden nowrap'),
setClass('entity-label space-x-1.5 overflow-hidden nowrap flex items-center'),
set($this->getRestProps()),
$prefix,
$reverse ? array($entityName, ' ', $entityID) : array($entityID, ' ', $entityName),
+9 -1
View File
@@ -58,7 +58,7 @@ class formBatchItem extends wg
*/
protected function build(): array
{
list($name, $label, $labelClass, $labelProps, $required, $tip, $tipClass, $tipProps, $tipIcon, $control, $width, $strong, $value, $disabled, $items, $placeholder, $ditto, $defaultDitto, $hidden, $readonly, $multiple) = $this->prop(array('name', 'label', 'labelClass', 'labelProps', 'required', 'tip', 'tipClass', 'tipProps', 'tipIcon', 'control', 'width', 'strong', 'value', 'disabled', 'items', 'placeholder', 'ditto', 'defaultDitto', 'hidden', 'readonly', 'multiple'));
list($name, $label, $labelClass, $labelProps, $required, $tip, $tipClass, $tipProps, $tipIcon, $control, $width, $strong, $value, $disabled, $items, $placeholder, $ditto, $defaultDitto, $hidden, $readonly, $multiple, $checkboxName, $checkboxValue, $checkboxText, $checkboxChecked) = $this->prop(array('name', 'label', 'labelClass', 'labelProps', 'required', 'tip', 'tipClass', 'tipProps', 'tipIcon', 'control', 'width', 'strong', 'value', 'disabled', 'items', 'placeholder', 'ditto', 'defaultDitto', 'hidden', 'readonly', 'multiple', 'checkboxName', 'checkboxValue', 'checkboxText', 'checkboxChecked'));
if($required === 'auto') $required = isFieldRequired($name);
@@ -110,6 +110,14 @@ class formBatchItem extends wg
toggle('tooltip', array('title' => $tip)),
set($tipProps),
set::icon($tipIcon)
),
empty($checkboxName) ? null : new checkbox
(
set::rootClass('inline-flex ml-4'),
set::name($checkboxName),
set::value($checkboxValue),
set::text($checkboxText),
set::checked($checkboxChecked)
)
),
h::td
+1
View File
@@ -83,6 +83,7 @@ class formPanel extends panel
if($moduleName == 'productplan') return data('plan');
if($moduleName == 'projectrelease') return data('release');
if($moduleName == 'projectbuild') return data('build');
if($moduleName == 'cm') return data('baseline');
if($moduleName == 'project' && ($methodName == 'create' or $methodName == 'createtemplate') && data('copyProject')) return data('copyProject');
return data($moduleName);
}
+3 -2
View File
@@ -390,7 +390,7 @@ function initGantt()
}
let getSubmitBtn = function(task)
{
if(task.type == 'point' && !task.rawStatus) return '<button class="btn btn-link submitBtn" title="' + ganttLang.submit + '"><i class="icon-confirm"></i></button>';
if(task.type == 'point' && (!task.rawStatus || task.rawStatus == 'draft')) return '<button class="btn btn-link submitBtn" title="' + ganttLang.submit + '"><i class="icon-confirm"></i></button>';
}
let isGanttExpand = false;
@@ -625,13 +625,14 @@ function initGantt()
{
let pointAttr = JSON.parse($('#gantt_here').attr('data-reviewpoints'));
let category = id.split("-")[1];
category = category.replace('point', '');
if(pointAttr[category]['disabled'])
{
zui.Messager.show({content: pointAttr[category]['message'], type: 'danger-outline', icon: 'exclamation-sign'});
return false;
}
loadPage($.createLink('review', 'create', 'projectID=' + projectID + '&object=' + category));
loadPage($.createLink('review', 'create', 'projectID=' + projectID + '&deliverable=0&reviewID=0&type=decision&objectID=' + category));
}
/* The id of task item is like executionID-taskID. e.g. 1507-37829, 37829 is task id. */
+7
View File
@@ -20,6 +20,13 @@ class heading extends wg
'dropmenu' => array('map' => 'dropmenu')
);
public static function getPageCSS(): ?string
{
return <<<CSS
#heading>[data-zui-dropmenu]+div .dropmenu-btn>.text,#heading>[data-zui-dropmenu]+div+div .dropmenu-btn>.text{max-width:120px}
CSS;
}
protected function buildAppName()
{
list($tab, $lang) = data(array('app.tab', 'lang'));
+1 -1
View File
@@ -23,7 +23,7 @@ class idLabel extends wg
$id = $this->prop('id');
return span
(
setClass('label label-id gray-300-outline size-sm rounded-full flex-none'),
setClass('label label-id gray-300-outline size-sm rounded-full flex-none ml-1'),
set($this->getRestProps()),
$id,
$this->children()
+8 -1
View File
@@ -82,7 +82,14 @@ class mainNavbar extends nav
$currentModule = $app->getModuleName();
$currentMethod = $app->getMethodName();
if($app->tab == 'admin') $app->control->loadModel('admin')->setMenu();
if($app->tab == 'admin')
{
$groupID = data('groupID');
$app->control->loadModel('admin')->setMenu((int)$groupID);
}
/* When use workflow then set rawModule to moduleName. */
if($currentModule == 'flow') $currentModule = $app->rawModule;
\commonModel::replaceMenuLang();
\commonModel::setMainMenu();
+3 -1
View File
@@ -28,7 +28,8 @@ class moduleMenu extends wg
'appendSettingItems?: array',
'onCheck?: function',
'toggleSidebar?: bool=true',
'isInModal?: bool=false'
'isInModal?: bool=false',
'onClickItem?: function'
);
protected static array $defineBlocks = array
@@ -311,6 +312,7 @@ class moduleMenu extends wg
set::hover(true),
set::lines(true),
set::preserve($preserve),
set::itemActions($this->prop('moduleActions')),
set($treeProps),
set($userTreeProps)
),
+4 -1
View File
@@ -153,7 +153,8 @@ class navbar extends wg
global $app, $lang, $config;
if($app->tab == 'admin')
{
$app->control->loadModel('admin')->setMenu();
$groupID = data('groupID') ? data('groupID') : 0;
$app->control->loadModel('admin')->setMenu($groupID);
$adminMenuKey = $app->control->loadModel('admin')->getMenuKey();
jsVar('adminMenuKey', $adminMenuKey);
}
@@ -288,7 +289,9 @@ class navbar extends wg
else
{
$subModule = isset($dropMenuItem['subModule']) ? explode(',', $dropMenuItem['subModule']) : array();
$subAlias = zget($dropMenuItem, 'alias', '');
if($subModule and in_array($currentModule, $subModule) and !str_contains(",$exclude,", ",$currentModule-$currentMethod,")) $activeMainMenu = true;
if(str_contains(",$subAlias,", ",$currentModule-$currentMethod,")) $activeMainMenu = true;
}
if($activeMenuID) $activeMainMenu = $dropMenuName == $activeMenuID;
+3 -3
View File
@@ -151,9 +151,9 @@ class picker extends wg
$name = isset($pickerProps['name']) ? $pickerProps['name'] : '';
if(isset($config->moreLinks[$name])) $pickerItems = $config->moreLinks[$name];
$pickerProps['_props'] = $restProps;
$pickerProps['items'] = $pickerItems;
$pickerProps['defaultValue'] = $defaultValue;
$pickerProps['_props'] = $restProps;
$pickerProps['items'] = $pickerItems;
$pickerProps['defaultValue'] = $defaultValue;
return $pickerProps;
}
+7 -3
View File
@@ -2,6 +2,7 @@
declare(strict_types=1);
namespace zin;
require_once dirname(__DIR__) . DS . 'toolbar' . DS . 'v1.php';
class section extends wg
{
protected static array $defineProps = array(
@@ -27,9 +28,10 @@ class section extends wg
private function title(): node
{
$title = $this->prop('title');
$actionsView = $this->block('actions');
$required = $this->prop('required');
$title = $this->prop('title');
$titleActions = $this->prop('titleActions');
$actionsView = $this->block('actions');
$required = $this->prop('required');
if(empty($actionsView))
{
@@ -40,6 +42,7 @@ class section extends wg
(
setClass('form-label required mr-1 pb-3'),
) : null,
$titleActions ? toolbar::create($titleActions) : null,
$title
);
}
@@ -54,6 +57,7 @@ class section extends wg
(
setClass('form-label required mr-1'),
) : null,
$titleActions ? toolbar::create($titleActions) : null,
$title
),
$actionsView
+4 -1
View File
@@ -73,6 +73,9 @@ $config->action->objectNameFields['miniprogram'] = 'name';
$config->action->objectNameFields['holiday'] = 'name';
$config->action->objectNameFields['system'] = 'name';
$config->action->objectNameFields['deliverable'] = 'name';
$config->action->objectNameFields['cm'] = 'title';
$config->action->objectNameFields['baseline'] = 'title';
$config->action->objectNameFields['projectchange'] = 'name';
$config->action->commonImgSize = 870;
@@ -132,4 +135,4 @@ $config->action->multipleObjectFields['testtask']['type'] = 'typeList';
$config->action->approvalFields['reviewStatus'] = 'reviewStatusList';
$config->action->approvalFields['reviewResult'] = 'reviewResultList';
$config->action->hiddenTrashObjects = 'object,cm';
$config->action->hiddenTrashObjects = 'object,cm,stage';
+1 -1
View File
@@ -133,7 +133,7 @@ class action extends control
public function undelete(int $actionID, string $browseType = 'all', string $confirmChange = 'no')
{
$result = $this->actionZen->checkActionExist($actionID);
if(is_array($result) && isset($result['result']) && $result['result'] == 'fail') return $this->send($result);
if(is_array($result) && $result['result'] == 'fail') return $this->send($result);
$oldAction = $result;
/* 当对象类型为program、project、execution、product时,需要检查是否有重复的对象。 */
+5 -3
View File
@@ -193,7 +193,10 @@ $lang->action->objectTypes['board'] = 'Board';
$lang->action->objectTypes['boardspace'] = 'Board Space';
$lang->action->objectTypes['productline'] = 'Product Line';
$lang->action->objectTypes['system'] = $lang->product->system;
$lang->action->objectTypes['deliverable'] = 'Deliverable';
$lang->action->objectTypes['deliverable'] = 'Deliverable Category';
$lang->action->objectTypes['cm'] = 'Baseline';
$lang->action->objectTypes['baseline'] = 'Baseline';
$lang->action->objectTypes['projectchange'] = 'Project Change';
/* Used to describe operation history. */
$lang->action->desc = new stdclass();
@@ -942,7 +945,6 @@ $lang->action->label->kanban = 'Kanban|kanban|view|kanbanID=%s';
$lang->action->label->kanbancolumn = 'Kanban Column|execution|kanban|execution=%s';
$lang->action->label->kanbanlane = 'Kanban Lane|execution|kanban|execution=%s&type=all';
$lang->action->label->kanbancard = 'Kanban Card|kanban|view|kanbanID=%s';
$lang->action->label->stage = 'Stage|stage|browse|';
$lang->action->label->module = 'Module|tree|browse|productid=%s&view=story&currentModuleID=0&branch=all';
$lang->action->label->ticket = 'Ticket|ticket|view|id=%s';
$lang->action->label->chartgroup = 'Group';
@@ -953,7 +955,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s';
$lang->action->label->prompt = 'ZenTao Agent|ai|promptview|id=%s';
$lang->action->label->miniprogram = 'General Agent|aiapp|browseminiprogram|id=%s';
$lang->action->label->holiday = 'Holiday|holiday|browse|';
$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s';
$lang->action->label->deliverable = 'Deliverable Category|deliverable|view|id=%s';
/* Object type. */
$lang->action->search = new stdclass();
+5 -3
View File
@@ -193,7 +193,10 @@ $lang->action->objectTypes['board'] = 'Board';
$lang->action->objectTypes['boardspace'] = 'Board Space';
$lang->action->objectTypes['productline'] = 'Product Line';
$lang->action->objectTypes['system'] = $lang->product->system;
$lang->action->objectTypes['deliverable'] = 'Deliverable';
$lang->action->objectTypes['deliverable'] = 'Deliverable Category';
$lang->action->objectTypes['cm'] = 'Baseline';
$lang->action->objectTypes['baseline'] = 'Baseline';
$lang->action->objectTypes['projectchange'] = 'Project Change';
/* Used to describe operation history. */
$lang->action->desc = new stdclass();
@@ -942,7 +945,6 @@ $lang->action->label->kanban = 'Kanban|kanban|view|kanbanID=%s';
$lang->action->label->kanbancolumn = 'Kanban Column|execution|kanban|execution=%s';
$lang->action->label->kanbanlane = 'Kanban Lane|execution|kanban|execution=%s&type=all';
$lang->action->label->kanbancard = 'Kanban Card|kanban|view|kanbanID=%s';
$lang->action->label->stage = 'Stage|stage|browse|';
$lang->action->label->module = 'Module|tree|browse|productid=%s&view=story&currentModuleID=0&branch=all';
$lang->action->label->ticket = 'Ticket|ticket|view|id=%s';
$lang->action->label->chartgroup = 'Group';
@@ -953,7 +955,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s';
$lang->action->label->prompt = 'ZenTao Agent|ai|promptview|id=%s';
$lang->action->label->miniprogram = 'General Agent|aiapp|browseminiprogram|id=%s';
$lang->action->label->holiday = 'Holiday|holiday|browse|';
$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s';
$lang->action->label->deliverable = 'Deliverable Category|deliverable|view|id=%s';
/* Object type. */
$lang->action->search = new stdclass();
+5 -3
View File
@@ -193,7 +193,10 @@ $lang->action->objectTypes['board'] = 'Board';
$lang->action->objectTypes['boardspace'] = 'Board Space';
$lang->action->objectTypes['productline'] = 'Product Line';
$lang->action->objectTypes['system'] = $lang->product->system;
$lang->action->objectTypes['deliverable'] = 'Deliverable';
$lang->action->objectTypes['deliverable'] = 'Deliverable Category';
$lang->action->objectTypes['cm'] = 'Baseline';
$lang->action->objectTypes['baseline'] = 'Baseline';
$lang->action->objectTypes['projectchange'] = 'Project Change';
/* Used to describe operation history. */
$lang->action->desc = new stdclass();
@@ -942,7 +945,6 @@ $lang->action->label->kanban = 'Kanban|kanban|view|kanbanID=%s';
$lang->action->label->kanbancolumn = 'Kanban Column|execution|kanban|execution=%s';
$lang->action->label->kanbanlane = 'Kanban Lane|execution|kanban|execution=%s&type=all';
$lang->action->label->kanbancard = 'Kanban Card|kanban|view|kanbanID=%s';
$lang->action->label->stage = 'Stage|stage|browse|';
$lang->action->label->module = 'Module|tree|browse|productid=%s&view=story&currentModuleID=0&branch=all';
$lang->action->label->ticket = 'Ticket|ticket|view|id=%s';
$lang->action->label->chartgroup = 'Group';
@@ -953,7 +955,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s';
$lang->action->label->prompt = 'ZenTao Agent|ai|promptview|id=%s';
$lang->action->label->miniprogram = 'General Agent|aiapp|browseminiprogram|id=%s';
$lang->action->label->holiday = 'Holiday|holiday|browse|';
$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s';
$lang->action->label->deliverable = 'Deliverable Category|deliverable|view|id=%s';
/* Object type. */
$lang->action->search = new stdclass();
+5 -3
View File
@@ -193,7 +193,10 @@ $lang->action->objectTypes['board'] = '白板';
$lang->action->objectTypes['boardspace'] = '白板空间';
$lang->action->objectTypes['productline'] = '产品线';
$lang->action->objectTypes['system'] = $lang->product->system;
$lang->action->objectTypes['deliverable'] = '交付物';
$lang->action->objectTypes['deliverable'] = '交付物类型';
$lang->action->objectTypes['cm'] = '基线';
$lang->action->objectTypes['baseline'] = '基线';
$lang->action->objectTypes['projectchange'] = '项目变更';
/* 用来描述操作历史记录。*/
$lang->action->desc = new stdclass();
@@ -942,7 +945,6 @@ $lang->action->label->kanban = '看板|kanban|view|kanbanID=%s';
$lang->action->label->kanbancolumn = '看板列|execution|kanban|execution=%s';
$lang->action->label->kanbanlane = '看板泳道|execution|kanban|execution=%s&type=all';
$lang->action->label->kanbancard = '看板卡片|kanban|view|kanbanID=%s';
$lang->action->label->stage = '瀑布模型的阶段|stage|browse|';
$lang->action->label->module = '模块|tree|browse|productid=%s&view=story&currentModuleID=0&branch=all';
$lang->action->label->ticket = '工单|ticket|view|id=%s';
$lang->action->label->chartgroup = '分组';
@@ -953,7 +955,7 @@ $lang->action->label->instance = '服务|instance|view|id=%s';
$lang->action->label->prompt = '禅道智能体|ai|promptview|id=%s';
$lang->action->label->miniprogram = '通用智能体|aiapp|browseminiprogram|id=%s';
$lang->action->label->holiday = '节假日|holiday|browse|';
$lang->action->label->deliverable = '交付物|deliverable|view|id=%s';
$lang->action->label->deliverable = '交付物类型|deliverable|view|id=%s';
/* Object type. */
$lang->action->search = new stdclass();
+15 -8
View File
@@ -613,6 +613,7 @@ class actionModel extends model
->orWhere('(objectType')->eq('execution')->andWhere('objectID')->notIn($noMultipleExecutions)
->markRight(2)
->andWhere('vision')->eq($this->config->vision)
->andWhere('objectType')->notIn($this->config->action->hiddenTrashObjects)
->fetchAll('objectType');
}
@@ -1373,6 +1374,14 @@ class actionModel extends model
$action->objectLabel = $this->lang->project->template;
$action->objectLink = helper::createLink('project', 'execution', "status=undone&projectID={$action->objectID}");
}
elseif($action->objectType == 'project' && $action->action == 'managedeliverable')
{
$action->objectLink = helper::createLink('project', 'deliverable', "projectID={$action->objectID}"); // 交付物链接
}
elseif($action->objectType == 'execution' && $action->action == 'managedeliverable')
{
$action->objectLink = helper::createLink('execution', 'view', "executionID={$action->objectID}"); // 交付物链接
}
}
return $actions;
}
@@ -1712,14 +1721,7 @@ class actionModel extends model
}
else
{
if($history->field == 'deliverable' && ($objectType == 'project' || $objectType == 'execution'))
{
$content .= $this->processDeliverableJson($objectType, $objectID, $history);
}
else
{
$content .= sprintf($this->lang->action->desc->diff1, $history->fieldLabel, $history->old, $history->new);
}
$content .= sprintf($this->lang->action->desc->diff1, $history->fieldLabel, $history->old, $history->new);
}
}
return $content;
@@ -1795,6 +1797,11 @@ class actionModel extends model
}
}
if($action->objectType == 'deliverable')
{
$this->dao->update(TABLE_DELIVERABLE)->set('status')->eq('disabled')->where('id')->eq($action->objectID)->exec();
}
$this->recoverRelatedData($action, $object);
/* 还原已删除的需求时重算OR需求和业用研需的阶段。 */
@@ -27,14 +27,6 @@ fields:
postfix: ""
loop: 0
format: "YYYY-MM-DD hh:mm:ss"
- field: lastAuditedDate
note: "计划完成时间"
range: "(-1M)-(w):1D"
type: timestamp
prefix: ""
postfix: ""
loop: 0
format: "YYYY-MM-DD hh:mm:ss"
- field: lastEditedDate
note: "计划完成时间"
range: "(-1M)-(w):1D"
@@ -39,14 +39,6 @@ fields:
postfix: ""
loop: 0
format: ""
- field: auditedBy
note: "由谁审计"
from: common.user.v1.yaml
use: user
prefix: ""
postfix: ""
loop: 0
format: ""
- field: createdBy
note: "由谁创建"
from: common.user.v1.yaml
@@ -87,22 +79,6 @@ fields:
postfix: ""
loop: 0
format: ""
- field: lastAuditedBy
note: "最后审计者"
from: common.user.v1.yaml
use: user
prefix: ""
postfix: ""
loop: 0
format: ""
- field: lastAuditedDate
note: "最后审计日期"
from: common.date.v1.yaml
use: dateB
prefix: ""
postfix: ""
loop: 0
format: ""
- field: lastEditedBy
note: "最后编辑者"
from: common.user.v1.yaml
@@ -126,13 +102,6 @@ fields:
postfix: ""
loop: 0
format: ""
- field: auditResult
note: "审计结果"
range: pass,fail,needfix
prefix: ""
postfix: ""
loop: 0
format: ""
- field: deleted
note: "是否删除"
range: 0
@@ -27,14 +27,6 @@ fields:
postfix: ""
loop: 0
format: "YYYY-MM-DD hh:mm:ss"
- field: lastAuditedDate
note: "最后审计日期"
range: "(-1M)-(w):1D"
type: timestamp
prefix: ""
postfix: ""
loop: 0
format: "YYYY-MM-DD hh:mm:ss"
- field: lastEditedDate
note: "最后编辑日期"
range: "(-1M)-(w):1D"
+1 -1
View File
@@ -34,7 +34,7 @@ featureBar
(
set::current($currentObjectType),
set::linkParams("objectType={key}&type={$type}&byQuery=&queryID=&orderBy={$orderBy}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"),
$currentObjectType != 'all' ? li(searchToggle(set::module('trash')), setClass($byQuery ? 'active' : '')) : null
$currentObjectType != 'all' && $currentObjectType != 'auditplan' ? li(searchToggle(set::module('trash')), setClass($byQuery ? 'active' : '')) : null
);
toolbar
+5 -12
View File
@@ -8,22 +8,17 @@ if(!isset($config->safe->weak)) $config->safe->weak = '123456,password,12345,123
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'admin|metriclib', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup');
$config->admin->menuGroup['company'] = array('dept', 'company', 'user', 'group', 'tutorial');
$config->admin->menuGroup['switch'] = array('admin|setmodule');
$config->admin->menuGroup['model'] = array('auditcl', 'stage', 'deliverable', 'design', 'cmcl', 'reviewcl', 'custom|required', 'custom|set', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting', 'custom|project');
$config->admin->menuGroup['feature'] = array('custom|set', 'custom|product', 'custom|execution', 'custom|required', 'custom|kanban', 'measurement', 'meetingroom', 'custom|browsestoryconcept', 'custom|kanban', 'sqlbuilder', 'report', 'custom|limittaskdate', 'measurement');
$config->admin->menuGroup['feature'] = array('stage|settype', 'baseline', 'design', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'holiday', 'reviewsetting', 'custom|project', 'custom|set', 'custom|product', 'custom|execution', 'custom|required', 'custom|kanban', 'measurement', 'meetingroom', 'custom|browsestoryconcept', 'custom|kanban', 'sqlbuilder', 'report', 'custom|limittaskdate', 'measurement');
$config->admin->menuGroup['message'] = array('mail', 'webhook', 'sms', 'message');
$config->admin->menuGroup['dev'] = array('dev', 'entry', 'editor');
$config->admin->menuGroup['extension'] = array('extension');
$config->admin->menuGroup['convert'] = array('convert');
$config->admin->menuGroup['adminregister'] = array('admin|register');
$config->admin->menuModuleGroup['model']['custom|set'] = array('project', 'issue', 'risk', 'opportunity', 'nc');
$config->admin->menuModuleGroup['model']['custom|required'] = array('project', 'build');
$config->admin->menuModuleGroup['feature']['custom|set'] = array('todo', 'block', 'epic', 'requirement', 'story', 'task', 'bug', 'testcase', 'testtask', 'feedback', 'user', 'ticket');
$config->admin->menuModuleGroup['feature']['custom|required'] = array('bug', 'doc', 'product', 'epic', 'requirement', 'story', 'productplan', 'release', 'task', 'testcase', 'testsuite', 'testtask', 'testreport', 'caselib', 'doc', 'feedback', 'user', 'execution');
$config->admin->menuModuleGroup['feature']['custom|set'] = array('project', 'issue', 'risk', 'opportunity', 'nc', 'todo', 'block', 'epic', 'requirement', 'story', 'task', 'bug', 'testcase', 'testtask', 'feedback', 'user', 'ticket', 'projectchange');
$config->admin->menuModuleGroup['feature']['custom|required'] = array('project', 'build', 'bug', 'doc', 'product', 'epic', 'requirement', 'story', 'productplan', 'release', 'task', 'testcase', 'testsuite', 'testtask', 'testreport', 'caselib', 'doc', 'feedback', 'user', 'execution');
if($config->vision == 'lite')
{
$config->admin->menuModuleGroup['model']['custom|set'] = array();
$config->admin->menuModuleGroup['model']['custom|required'] = array('build');
$config->admin->menuModuleGroup['feature']['custom|set'] = array('todo', 'block', 'task', 'story', 'user');
$config->admin->menuModuleGroup['feature']['custom|required'] = array('project', 'task', 'story', 'doc', 'user');
}
@@ -71,7 +66,7 @@ $config->admin->patchAPIURL = $config->admin->apiSite . 'extension-apiGetExte
$config->admin->downloadAPIURL = $config->admin->apiSite . 'download.json';
$config->admin->dynamicAPIURL = $config->admin->apiSite . 'dynamic-activities-%s.json';
$config->admin->videoAPIURL = $config->admin->apiSite . 'zentao.json';
$config->admin->liteMenuList = array('system', 'company', 'feature', 'message', 'extension', 'dev');
$config->admin->liteMenuList = array('system', 'company', 'feature', 'message', 'extension', 'dev', 'workflow', 'approvalFlow');
$config->admin->helpURL['system'] = 'https://www.zentao.net/book/zentaopms/538.html';
$config->admin->helpURL['company'] = 'https://www.zentao.net/book/zentaopms/38.html';
@@ -85,11 +80,9 @@ $config->admin->helpURL['convert'] = 'https://www.zentao.net/book/zentaopms/65
$config->admin->navsGroup['feature']['my'] = ',todo,block,';
$config->admin->navsGroup['feature']['product'] = ',product,epic,requirement,story,productplan,release,';
$config->admin->navsGroup['feature']['project'] = ',project,';
$config->admin->navsGroup['feature']['project'] = ',project,build,issue,risk,opportunity,nc,projectchange,';
$config->admin->navsGroup['feature']['execution'] = ',execution,task,';
$config->admin->navsGroup['feature']['project'] = ',project,story,';
$config->admin->navsGroup['feature']['qa'] = ',bug,testcase,testsuite,testtask,testreport,caselib,';
$config->admin->navsGroup['model']['common'] = ',project,build,issue,risk,opportunity,nc,';
if($config->vision == 'lite') $config->admin->navsGroup['feature']['my'] .= 'task,';
global $lang, $app;
+6 -6
View File
@@ -168,10 +168,6 @@ class admin extends control
}
}
$enableER = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productER', 0);
$URAndSR = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productUR', 0);
if($enableER && !$URAndSR) $this->send(array('result' => 'fail', 'message' => $this->lang->admin->notice->openUR));
foreach($closedFeatures as $closedFeature)
{
if(in_array($closedFeature, array('productER', 'productUR')))
@@ -180,6 +176,9 @@ class admin extends control
}
}
$enableER = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productER', 0);
$URAndSR = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productUR', 0);
$this->setting->setItem('system.common.closedFeatures', implode(',', $closedFeatures));
$this->setting->setItem('system.common.global.scoreStatus', zget($data->module, 'myScore', 0));
$this->setting->setItem('system.custom.enableER', $enableER);
@@ -477,15 +476,16 @@ class admin extends control
foreach($this->lang->admin->menuList as $menuKey => $menuGroup)
{
if($this->config->vision == 'lite' && !in_array($menuKey, $this->config->admin->liteMenuList)) continue;
$data[] = array(
'id' => $menuKey,
'name' => $menuKey,
'content' => array('html' => "<div class='flex items-center my-0.5'><img class='mr-2' src='static/svg/admin-{$menuKey}.svg'/> {$menuGroup['name']}</div>"),
'content' => array('html' => "<div class='flex items-center my-0.5'>" . (!empty($menuGroup['icon']) ? "<i class='icon icon-{$menuGroup['icon']} svg-icon mr-2 rounded-lg content-center bg-{$menuGroup['bg']} text-white'></i>" : "<img class='mr-2' src='static/svg/admin-{$menuKey}.svg'/>") . " {$menuGroup['name']}</div>"),
'text' => $menuGroup['name'],
'titleClass' => 'hidden',
'type' => 'item',
'disabled' => $menuGroup['disabled'],
'url' => $menuGroup['disabled'] || $currentMenuKey == $menuKey ? '' : $menuGroup['link'],
'url' => $menuGroup['link'],
'active' => $currentMenuKey == $menuKey,
'rootClass' => 'admin-menu-item',
'attrs' => array('disabled' => $menuGroup['disabled'])
+2
View File
@@ -2,3 +2,5 @@
.border-hover:hover {border-color: var(--color-primary-500)}
div.setting-box[data-id=platform] div.text-md {white-space: nowrap;}
.svg-icon {height: 24px; width: 24px; text-align: center;}
+229 -23
View File
@@ -1,33 +1,16 @@
function changeModule($target)
function changeModule($target, type = 'single')
{
const name = $target.attr('name');
if($target.prop('checked'))
{
$("input[type=hidden][name='" + name + "']").val('1').attr('disabled');
if(type == 'single') checkRelated(name, 'open');
}
else
{
if(name == 'module[productUR]' || name == 'module[productER]')
{
zui.Modal.confirm(
{
message: confirmDisableStoryType,
icon: 'icon-exclamation-sign',
iconClass: 'warning-pale rounded-full icon-2x'
}).then((res) =>
{
if(res)
{
$("input[type=hidden][name='" + name + "']").val('0').removeAttr('disabled');
return false;
}
$target.prop('checked', true);
});
return false;
}
$("input[type=hidden][name='" + name + "']").val('1').attr('disabled');
$("input[type=hidden][name='" + name + "']").val('0').removeAttr('disabled');
if(type == 'single') checkRelated(name, 'close');
}
};
@@ -42,7 +25,7 @@ function checkGroup()
$(this).closest('tr').find("input[type=checkbox][name^='module']").each(function()
{
$(this).prop('checked', checked);
changeModule($(this));
changeModule($(this), 'all');
});
};
@@ -53,6 +36,229 @@ function checkAll()
$(this).closest('table').find("input[type=checkbox][name^='module']").each(function()
{
$(this).prop('checked', checked);
changeModule($(this));
changeModule($(this), 'all');
});
};
window.submitForm = function()
{
const isCheckedUR = $('[name="module[productUR]"]').prop('checked');
const isCheckedER = $('[name="module[productER]"]').prop('checked');
let message = confirmDisableStoryType;
let storyType = '';
if(edition != 'ipd' && URAndSR && !isCheckedUR)
{
storyType += URCommon;
}
if(enableER && !isCheckedER)
{
if(storyType) storyType += ',';
storyType += ERCommon;
}
if(storyType)
{
zui.Modal.confirm(
{
message: message.replace(/{type}/g, storyType),
icon: 'icon-exclamation-sign',
iconClass: 'warning-pale rounded-full icon-2x'
}).then((res) =>
{
if(res)
{
realSubmitForm();
return false;
}
setModuleState('productUR', true);
setModuleState('productER', true);
});
return false;
}
realSubmitForm();
return false;
}
window.realSubmitForm = function()
{
const formData = new FormData($('#setModuleForm form')[0]);
const url = $.createLink('admin', 'setmodule');
$.ajaxSubmit({url: url, data: formData});
}
/**
* 设置模块的checkbox和hidden input状态
* @param {string} moduleName - 模块名称(如 'productUR', 'productER')
* @param {boolean} checked - 是否选中
*/
function setModuleState(moduleName, checked)
{
const checkboxSelector = `[name="module[${moduleName}]"]`;
const hiddenSelector = `#module${moduleName}[type=hidden]`;
$(checkboxSelector).prop('checked', checked);
$(hiddenSelector).val(checked ? '1' : '0');
}
/**
* 检查模块是否已启用
* @param {string} moduleName - 模块名称
* @returns {boolean} 是否已启用
*/
function isModuleEnabled(moduleName)
{
return $('[name="module[' + moduleName + ']"]').prop('checked');
}
/**
* 显示依赖关系确认对话框
* @param {string} message - 确认消息
* @param {Function} onConfirm - 确认回调
* @param {Function} onCancel - 取消回调
*/
function showDependencyConfirm(message, onConfirm, onCancel)
{
zui.Modal.confirm(
{
message: message,
icon: 'icon-exclamation-sign',
iconClass: 'warning-pale rounded-full icon-2x'
}).then((res) =>
{
if(res)
{
if(onConfirm) onConfirm();
return false;
}
if(onCancel) onCancel();
});
}
window.checkRelated = function(name, type)
{
if(type === 'open')
{
// 开启 业务需求 时,需要确保 用户需求 已开启
if(name.includes('productER') && edition !== 'ipd')
{
if(!isModuleEnabled('productUR'))
{
const message = openDependFeature.replace('{source}', ERCommon).replace('{target}', URCommon);
showDependencyConfirm(
message,
() => setModuleState('productUR', true),
() => setModuleState('productER', false)
);
return false;
}
}
// 开启 项目变更 时,需要确保 交付物 已开启
else if(name.includes('projectCm'))
{
if(!isModuleEnabled('projectDeliverable'))
{
const message = openDependFeature.replace('{source}', cmLang).replace('{target}', deliverableLang);
showDependencyConfirm(
message,
() => setModuleState('projectDeliverable', true),
() => setModuleState('projectCm', false)
);
return false;
}
}
// 开启 项目变更 时,需要确保 交付物 和 基线 都已开启
else if(name.includes('projectChange'))
{
const deliverableEnabled = isModuleEnabled('projectDeliverable');
const cmEnabled = isModuleEnabled('projectCm');
if(!deliverableEnabled || !cmEnabled)
{
let message = openDependFeature.replace('{source}', changeLang);
const missingLangs = [];
if(!deliverableEnabled) missingLangs.push(deliverableLang);
if(!cmEnabled) missingLangs.push(cmLang);
message = message.replace('{target}', missingLangs.join(','));
showDependencyConfirm(
message,
() =>
{
if(!deliverableEnabled) setModuleState('projectDeliverable', true);
if(!cmEnabled) setModuleState('projectCm', true);
},
() => setModuleState('projectChange', false)
);
return false;
}
}
}
else
{
// 关闭 用户需求 时,如果 业务需求 已开启,需要提示关闭 业务需求
if(name.includes('productUR') && edition !== 'ipd')
{
if(isModuleEnabled('productER'))
{
const message = closeDependFeature.replace('{source}', URCommon).replace('{target}', ERCommon);
showDependencyConfirm(
message,
() => setModuleState('productER', false),
() =>
{
setModuleState('productUR', true);
setModuleState('productER', true);
}
);
return false;
}
}
// 关闭 交付物 时,如果 项目变更 或 基线 已开启,需要提示关闭它们
else if(name.includes('projectDeliverable'))
{
const changeEnabled = isModuleEnabled('projectChange');
const cmEnabled = isModuleEnabled('projectCm');
if(changeEnabled || cmEnabled)
{
let message = closeDependFeature.replace('{source}', deliverableLang);
const activeLangs = [];
if(changeEnabled) activeLangs.push(changeLang);
if(cmEnabled) activeLangs.push(cmLang);
message = message.replace('{target}', activeLangs.join(','));
showDependencyConfirm(
message,
() =>
{
if(changeEnabled) setModuleState('projectChange', false);
if(cmEnabled) setModuleState('projectCm', false);
},
() => setModuleState('projectDeliverable', true)
);
}
}
// 关闭 基线 时,如果 项目变更 已开启,需要提示关闭 项目变更
else if(name.includes('projectCm'))
{
if(isModuleEnabled('projectChange'))
{
const message = closeDependFeature.replace('{source}', cmLang).replace('{target}', changeLang);
showDependencyConfirm(
message,
() => setModuleState('projectChange', false),
() => setModuleState('projectCm', true)
);
}
}
}
}
+11 -8
View File
@@ -34,6 +34,7 @@ $lang->admin->zentaoUsed = '';
$lang->admin->api = 'API';
$lang->admin->log = 'Log';
$lang->admin->setting = 'Einstellungen';
$lang->admin->setFlow = 'Set Flow';
$lang->admin->pluginRecommendation = 'Recommended plug-ins';
$lang->admin->zentaoInfo = 'ZenTao information';
$lang->admin->officialAccount = 'WeChat Official Account';
@@ -60,10 +61,12 @@ $lang->admin->info->account = 'Ihr ZenTao Konto ist %s.';
$lang->admin->info->log = 'Logs die über die Gültigen Tage hinausgehen werden gelöscht. Aufgabenplanung muss laufen (cron).';
$lang->admin->notice = new stdclass();
$lang->admin->notice->register = "Hinweiß: Sie haben sich nicht bei ZenTao (www.zentao.pm) registriert. %s um die aktuellen Updates und News zu erhalten.";
$lang->admin->notice->ignore = "Ignorieren";
$lang->admin->notice->int = "『%s』sollte eine positive Zahl sein.";
$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}.";
$lang->admin->notice->register = "Hinweiß: Sie haben sich nicht bei ZenTao (www.zentao.pm) registriert. %s um die aktuellen Updates und News zu erhalten.";
$lang->admin->notice->ignore = "Ignorieren";
$lang->admin->notice->int = "『%s』sollte eine positive Zahl sein.";
$lang->admin->notice->confirmDisableStoryType = "‘{type}’ Funktion geschlossen, das System wird alle Geschichten entfernen, die mit dem Projekt und der Ausführung verknüpkt sind, die Operation ist irreversibel";
$lang->admin->notice->openDependFeature = 'Using "{source}" function requires enabling "{target}" function synchronously.';
$lang->admin->notice->closeDependFeature = 'Closing "{source}" function requires synchronously closing "{target}" function.';
$lang->admin->registerNotice = new stdclass();
$lang->admin->registerNotice->common = 'Register Account';
@@ -89,10 +92,7 @@ $lang->admin->setModule->closed = 'Closed';
$lang->admin->setModule->my = 'My';
$lang->admin->setModule->product = $lang->productCommon;
$lang->admin->setModule->scrum = 'Scrum ' . $lang->projectCommon;
$lang->admin->setModule->waterfall = 'Waterfall ' . $lang->projectCommon;
$lang->admin->setModule->agileplus = 'Agile + ' . $lang->projectCommon;
$lang->admin->setModule->waterfallplus = 'Waterfall + ' . $lang->projectCommon;
$lang->admin->setModule->project = $lang->projectCommon;
$lang->admin->setModule->assetlib = 'Assetlib';
$lang->admin->setModule->other = 'Other';
@@ -108,6 +108,9 @@ $lang->admin->setModule->roadmap = 'Roadmap';
$lang->admin->setModule->track = 'Track';
$lang->admin->setModule->ER = $lang->ERCommon;
$lang->admin->setModule->UR = $lang->URCommon;
$lang->admin->setModule->deliverable = 'Deliverable';
$lang->admin->setModule->cm = 'Baseline';
$lang->admin->setModule->change = 'Project Change';
$lang->admin->setModule->researchplan = 'Researchplan';
$lang->admin->setModule->gapanalysis = 'Gapanalysis';
$lang->admin->setModule->storylib = 'Story Lib';
+11 -8
View File
@@ -34,6 +34,7 @@ $lang->admin->zentaoUsed = 'You have been using ZenTao';
$lang->admin->api = 'API';
$lang->admin->log = 'Log';
$lang->admin->setting = 'Setting';
$lang->admin->setFlow = 'Set Flow';
$lang->admin->pluginRecommendation = 'Recommended plug-ins';
$lang->admin->zentaoInfo = 'ZenTao information';
$lang->admin->officialAccount = 'WeChat Official Account';
@@ -60,10 +61,12 @@ $lang->admin->info->account = 'Your ZenTao account is %s.';
$lang->admin->info->log = 'Log that exceeds valid days will be deleted and you have to run cron.';
$lang->admin->notice = new stdclass();
$lang->admin->notice->register = "Note: You haven't registered in ZenTao official website(www.zentao.pm). %s then get the Latest ZenTao Upgrades and News.";
$lang->admin->notice->ignore = "Ignore";
$lang->admin->notice->int = "『%s』should be a positive integer.";
$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}.";
$lang->admin->notice->register = "Note: You haven't registered in ZenTao official website(www.zentao.pm). %s then get the Latest ZenTao Upgrades and News.";
$lang->admin->notice->ignore = "Ignore";
$lang->admin->notice->int = "『%s』should be a positive integer.";
$lang->admin->notice->confirmDisableStoryType = "‘{type}’ function closed, the system will remove all stories associated with the project and execution, the operation is irreversible";
$lang->admin->notice->openDependFeature = 'Using "{source}" function requires enabling "{target}" function synchronously.';
$lang->admin->notice->closeDependFeature = 'Closing "{source}" function requires synchronously closing "{target}" function.';
$lang->admin->registerNotice = new stdclass();
$lang->admin->registerNotice->common = 'Register Account';
@@ -89,10 +92,7 @@ $lang->admin->setModule->closed = 'Closed';
$lang->admin->setModule->my = 'My';
$lang->admin->setModule->product = $lang->productCommon;
$lang->admin->setModule->scrum = 'Scrum ' . $lang->projectCommon;
$lang->admin->setModule->waterfall = 'Waterfall ' . $lang->projectCommon;
$lang->admin->setModule->agileplus = 'Agile + ' . $lang->projectCommon;
$lang->admin->setModule->waterfallplus = 'Waterfall + ' . $lang->projectCommon;
$lang->admin->setModule->project = $lang->projectCommon;
$lang->admin->setModule->assetlib = 'Assetlib';
$lang->admin->setModule->other = 'Other';
@@ -108,6 +108,9 @@ $lang->admin->setModule->roadmap = 'Roadmap';
$lang->admin->setModule->track = 'Track';
$lang->admin->setModule->ER = $lang->ERCommon;
$lang->admin->setModule->UR = $lang->URCommon;
$lang->admin->setModule->deliverable = 'Deliverable';
$lang->admin->setModule->cm = 'Baseline';
$lang->admin->setModule->change = 'Project Change';
$lang->admin->setModule->researchplan = 'Researchplan';
$lang->admin->setModule->gapanalysis = 'Gapanalysis';
$lang->admin->setModule->storylib = 'Story Lib';
+11 -8
View File
@@ -34,6 +34,7 @@ $lang->admin->zentaoUsed = '';
$lang->admin->api = 'API';
$lang->admin->log = 'Log';
$lang->admin->setting = 'Paramétrage';
$lang->admin->setFlow = 'Set Flow';
$lang->admin->pluginRecommendation = 'Recommended plug-ins';
$lang->admin->zentaoInfo = 'ZenTao information';
$lang->admin->officialAccount = 'WeChat Official Account';
@@ -60,10 +61,12 @@ $lang->admin->info->account = 'Votre identifiant client ZenTao est %s.';
$lang->admin->info->log = 'Les logs qui dépassent la durée de conservation seront supprimés et vous devrez exécuter un cron.';
$lang->admin->notice = new stdclass();
$lang->admin->notice->register = "Note: Vous n'êtes pas enregistré sur le site officiel de ZenTao (www.zentao.pm). %s vous pourrez ensuite obtenir les dernières Mises à Jour de ZenTao et des informations.";
$lang->admin->notice->ignore = "Ignorer";
$lang->admin->notice->int = "『 %s 』 devrait être un entier positif.";
$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}.";
$lang->admin->notice->register = "Note: Vous n'êtes pas enregistré sur le site officiel de ZenTao (www.zentao.pm). %s vous pourrez ensuite obtenir les dernières Mises à Jour de ZenTao et des informations.";
$lang->admin->notice->ignore = "Ignorer";
$lang->admin->notice->int = "『 %s 』 devrait être un entier positif.";
$lang->admin->notice->confirmDisableStoryType = "‘{type}’ fonction fermée, le système supprimera toutes les histoires associées au projet et à l'exécution, l'opération est irréversible";
$lang->admin->notice->openDependFeature = 'Using "{source}" function requires enabling "{target}" function synchronously.';
$lang->admin->notice->closeDependFeature = 'Closing "{source}" function requires synchronously closing "{target}" function.';
$lang->admin->registerNotice = new stdclass();
$lang->admin->registerNotice->common = 'Register Account';
@@ -89,10 +92,7 @@ $lang->admin->setModule->closed = 'Closed';
$lang->admin->setModule->my = 'My';
$lang->admin->setModule->product = $lang->productCommon;
$lang->admin->setModule->scrum = 'Scrum ' . $lang->projectCommon;
$lang->admin->setModule->waterfall = 'Waterfall ' . $lang->projectCommon;
$lang->admin->setModule->agileplus = 'Agile + ' . $lang->projectCommon;
$lang->admin->setModule->waterfallplus = 'Waterfall + ' . $lang->projectCommon;
$lang->admin->setModule->project = $lang->projectCommon;
$lang->admin->setModule->assetlib = 'Assetlib';
$lang->admin->setModule->other = 'Other';
@@ -108,6 +108,9 @@ $lang->admin->setModule->roadmap = 'Roadmap';
$lang->admin->setModule->track = 'Track';
$lang->admin->setModule->ER = $lang->ERCommon;
$lang->admin->setModule->UR = $lang->URCommon;
$lang->admin->setModule->deliverable = 'Deliverable';
$lang->admin->setModule->cm = 'Baseline';
$lang->admin->setModule->change = 'Project Change';
$lang->admin->setModule->researchplan = 'Researchplan';
$lang->admin->setModule->gapanalysis = 'Gapanalysis';
$lang->admin->setModule->storylib = 'Story Lib';
+25 -78
View File
@@ -14,10 +14,6 @@ $lang->admin->menuList->company['name'] = $lang->admin->menuSetting['user']['na
$lang->admin->menuList->company['desc'] = $lang->admin->menuSetting['user']['desc'];
$lang->admin->menuList->company['order'] = 15;
$lang->admin->menuList->model['name'] = $lang->admin->menuSetting['model']['name'];
$lang->admin->menuList->model['desc'] = $lang->admin->menuSetting['model']['desc'];
$lang->admin->menuList->model['order'] = 20;
$lang->admin->menuList->feature['name'] = $lang->admin->menuSetting['feature']['name'];
$lang->admin->menuList->feature['desc'] = $lang->admin->menuSetting['feature']['desc'];
$lang->admin->menuList->feature['order'] = 25;
@@ -71,44 +67,10 @@ $lang->admin->menuList->company['menuOrder']['5'] = 'dept';
$lang->admin->menuList->company['menuOrder']['10'] = 'browseUser';
$lang->admin->menuList->company['menuOrder']['15'] = 'group';
$lang->admin->menuList->model['subMenu']['common'] = array('link' => "{$lang->globalSetting}|custom|required|module=project", 'subModule' => 'custom,subject,holiday,stage,deliverable,baseline', 'exclude' => 'stage-browse,stage-plusbrowse,stage-create,stage-edit,stage-batchcreate');
$lang->admin->menuList->model['subMenu']['scrum'] = array('link' => "{$lang->scrumModel}|auditcl|scrumbrowse|", 'subModule' => 'auditcl');
$lang->admin->menuList->model['subMenu']['waterfall'] = array('link' => "{$lang->waterfallModel}|stage|browse|", 'subModule' => 'stage', 'exclude' => 'baseline-templatetype,stage-settype,stage-plusbrowse');
$lang->admin->menuList->model['subMenu']['agileplus'] = array('link' => "{$lang->agilePlusModel}|auditcl|agileplusbrowse|", 'subModule' => 'auditcl');
$lang->admin->menuList->model['subMenu']['waterfallplus'] = array('link' => "{$lang->waterfallPlusModel}|stage|plusbrowse|", 'subModule' => 'stage', 'exclude' => 'baseline-templatetype,stage-settype,stage-browse');
$lang->admin->menuList->model['menuOrder']['5'] = 'common';
$lang->admin->menuList->model['menuOrder']['10'] = 'scrum';
$lang->admin->menuList->model['menuOrder']['15'] = 'waterfall';
$lang->admin->menuList->model['menuOrder']['20'] = 'agileplus';
$lang->admin->menuList->model['menuOrder']['25'] = 'waterfallplus';
$lang->admin->menuList->model['tabMenu']['common']['project'] = array('link' => "{$lang->project->common}|custom|required|module=project", 'alias' => 'project', 'exclude' => 'custom-required,baseline-templatetype', 'links' => array('custom|set|module=project&field=unitList'));
if(helper::hasFeature('waterfall') || helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['common']['stage'] = array('link' => "{$lang->stage->type}|stage|settype|", 'subModule' => 'stage');
if(helper::hasFeature('deliverable') && ($config->edition == 'max' || $config->edition == 'ipd')) $lang->admin->menuList->model['tabMenu']['common']['deliverable'] = array('link' => "{$lang->deliverable->common}|deliverable|browse|", 'subModule' => 'deliverable');
$lang->admin->menuList->model['tabMenu']['common']['build'] = array('link' => "{$lang->build->common}|custom|required|module=build", 'alias' => 'set', 'exclude' => 'custom');
$lang->admin->menuList->model['tabMenu']['common']['flow'] = array('link' => "{$lang->custom->flow}|custom|flow|", 'divider' => true);
$lang->admin->menuList->model['tabMenu']['common']['percent'] = array('link' => "{$lang->stage->percent}|custom|percent|");
$lang->admin->menuList->model['tabMenu']['common']['hours'] = array('link' => "{$lang->workingHour}|custom|hours|", 'subModule' => 'holiday', 'links' => array('holiday|browse|', 'custom|hours|'));
if(helper::hasFeature('waterfall')) $lang->admin->menuList->model['tabMenu']['waterfall']['stage'] = array('link' => "{$lang->stage->list}|stage|browse|", 'subModule' => 'stage', 'exclude' => 'stage-plusbrowse');
if(helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['waterfallplus']['stage'] = array('link' => "{$lang->stage->list}|stage|plusbrowse|", 'subModule' => 'stage', 'exclude' => 'stage-browse');
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['5'] = 'project';
if(helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['menuOrder']['common']['7'] = 'stage';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['10'] = 'build';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['35'] = 'flow';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['40'] = 'percent';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['45'] = 'hours';
if(helper::hasFeature('waterfall')) $lang->admin->menuList->model['tabMenu']['menuOrder']['waterfall']['5'] = 'stage';
if(helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['menuOrder']['waterfallplus']['5'] = 'stage';
if($config->edition == 'max' or $config->edition == 'ipd')
{
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['15'] = 'deliverable';
}
$lang->admin->menuList->feature['subMenu']['my'] = array('link' => "{$lang->my->common}|custom|set|module=todo&field=priList", 'exclude' => 'set,required');
$lang->admin->menuList->feature['subMenu']['product'] = array('link' => "{$lang->productCommon}|custom|required|module=product", 'exclude' => 'set,required', 'alias' => 'browsestoryconcept,product');
$lang->admin->menuList->feature['subMenu']['execution'] = array('link' => "{$lang->execution->common}|custom|required|module=execution", 'exclude' => 'required,set', 'alias' => 'execution,limittaskdate');
$lang->admin->menuList->feature['subMenu']['project'] = array('link' => "{$lang->projectCommon}|custom|required|module=project", 'alias' => 'flow,percent,hours,estimate,project', 'subModule' => 'subject,holiday,baseline,design,reviewcl', 'exclude' => 'set,required');
$lang->admin->menuList->feature['subMenu']['execution'] = array('link' => "{$lang->execution->common}|custom|required|module=execution", 'exclude' => 'required,set', 'alias' => 'execution,limittaskdate', 'subModule' => 'stage');
$lang->admin->menuList->feature['subMenu']['qa'] = array('link' => "{$lang->qa->common}|custom|required|module=bug", 'exclude' => 'set,required');
$lang->admin->menuList->feature['subMenu']['kanban'] = array('link' => "{$lang->kanban->common}|custom|kanban|");
$lang->admin->menuList->feature['subMenu']['user'] = array('link' => "{$lang->user->common}|custom|required|module=user", 'links' => array('custom|set|module=user&field=roleList'), 'exclude' => 'set,required');
@@ -116,14 +78,21 @@ $lang->admin->menuList->feature['subMenu']['user'] = array('link' => "{$l
$lang->admin->menuList->feature['tabMenu']['my']['todo'] = array('link' => "{$lang->todo->common}|custom|set|module=todo&field=priList", 'exclude' => 'custom-set');
$lang->admin->menuList->feature['tabMenu']['my']['block'] = array('link' => "{$lang->block->common}|custom|set|module=block&field=closed", 'exclude' => 'custom-set');
$lang->admin->menuList->feature['tabMenu']['product']['product'] = array('link' => "{$lang->productCommon}|custom|required|module=product", 'links' => array("custom|browsestoryconcept|", 'custom|product|'), 'alias' => 'browsestoryconcept,product', 'exclude' => 'custom-required');
$lang->admin->menuList->feature['tabMenu']['product']['product'] = array('link' => "{$lang->productCommon}|custom|required|module=product", 'links' => array("custom|browsestoryconcept|", 'custom|product|'), 'alias' => 'browsestoryconcept,product', 'exclude' => 'custom-required');
if($config->enableER) $lang->admin->menuList->feature['tabMenu']['product']['epic'] = array('link' => "{$lang->epic->common}|custom|required|module=epic", 'links' => array("custom|set|module=epic&field=categoryList"), 'exclude' => 'custom-required,custom-set');
if($config->URAndSR) $lang->admin->menuList->feature['tabMenu']['product']['requirement'] = array('link' => "{$lang->URCommon}|custom|required|module=requirement", 'links' => array("custom|set|module=requirement&field=categoryList"), 'exclude' => 'custom-required,custom-set');
$lang->admin->menuList->feature['tabMenu']['product']['story'] = array('link' => "{$lang->SRCommon}|custom|required|module=story", 'links' => array("custom|set|module=story&field=categoryList"), 'exclude' => 'custom-required,custom-set');
$lang->admin->menuList->feature['tabMenu']['product']['productplan'] = array('link' => "{$lang->productplan->shortCommon}|custom|required|module=productplan", 'exclude' => 'custom-required');
$lang->admin->menuList->feature['tabMenu']['product']['release'] = array('link' => "{$lang->release->common}|custom|required|module=release", 'exclude' => 'custom-required');
$lang->admin->menuList->feature['tabMenu']['project']['project'] = array('link' => "{$lang->project->common}|custom|required|module=project", 'alias' => 'project', 'exclude' => 'custom-required');
$lang->admin->menuList->feature['tabMenu']['project']['build'] = array('link' => "{$lang->build->common}|custom|required|module=build", 'exclude' => 'custom-required');
$lang->admin->menuList->feature['tabMenu']['project']['flow'] = array('link' => "{$lang->custom->flow}|custom|flow|");
$lang->admin->menuList->feature['tabMenu']['project']['percent'] = array('link' => "{$lang->stage->percent}|custom|percent|");
$lang->admin->menuList->feature['tabMenu']['project']['hours'] = array('link' => "{$lang->workingHour}|custom|hours|", 'subModule' => 'holiday', 'links' => array('holiday|browse|', 'custom|hours|'));
$lang->admin->menuList->feature['tabMenu']['execution']['execution'] = array('link' => "{$lang->execution->common}|custom|required|module=execution", 'links' => array("custom|execution|"), 'alias' => 'execution', 'exclude' => 'custom-required');
$lang->admin->menuList->feature['tabMenu']['execution']['stage'] = array('link' => "{$lang->stage->common}|stage|settype|", 'subModule' => 'stage', 'links' => array('stage|browse|'));
$lang->admin->menuList->feature['tabMenu']['execution']['task'] = array('link' => "{$lang->task->common}|custom|required|module=task", 'links' => array('custom|set|module=task&field=priList', 'custom|limittaskdate|'), 'alias' => 'limittaskdate','exclude' => 'custom-required');
$lang->admin->menuList->feature['tabMenu']['qa']['bug'] = array('link' => "{$lang->bug->common}|custom|required|module=bug", 'links' => array("custom|set|module=bug&field=priList"), 'exclude' => 'custom-required,custom-set');
@@ -141,8 +110,16 @@ if($config->URAndSR) $lang->admin->menuList->feature['tabMenu']['menuOrder']['p
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['20'] = 'story';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['25'] = 'productplan';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['30'] = 'release';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['5'] = 'project';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['10'] = 'build';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['35'] = 'flow';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['40'] = 'percent';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['45'] = 'hours';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['5'] = 'execution';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['10'] = 'task';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['10'] = 'stage';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['15'] = 'task';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['5'] = 'bug';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['10'] = 'testcase';
$lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['15'] = 'testsuite';
@@ -152,10 +129,12 @@ $lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['30'] = 'ca
$lang->admin->menuList->feature['menuOrder']['5'] = 'my';
$lang->admin->menuList->feature['menuOrder']['10'] = 'product';
$lang->admin->menuList->feature['menuOrder']['15'] = 'execution';
$lang->admin->menuList->feature['menuOrder']['20'] = 'qa';
$lang->admin->menuList->feature['menuOrder']['25'] = 'kanban';
$lang->admin->menuList->feature['menuOrder']['39'] = 'user';
$lang->admin->menuList->feature['menuOrder']['15'] = 'project';
$lang->admin->menuList->feature['menuOrder']['20'] = 'execution';
$lang->admin->menuList->feature['menuOrder']['25'] = 'qa';
$lang->admin->menuList->feature['menuOrder']['30'] = 'kanban';
$lang->admin->menuList->feature['menuOrder']['35'] = 'doc';
$lang->admin->menuList->feature['menuOrder']['40'] = 'user';
$lang->admin->menuList->feature['dividerMenu'] = ',user,';
@@ -186,29 +165,6 @@ $lang->admin->menuList->adminregister['desc'] = $lang->admin->menuSetting['admi
$lang->admin->menuList->adminregister['link'] = 'admin|register';
$lang->admin->menuList->adminregister['order'] = 61;
if($config->edition != 'max' and $config->edition != 'ipd')
{
unset($lang->admin->menuList->model['subMenu']['scrum']);
unset($lang->admin->menuList->model['subMenu']['agileplus']);
unset($lang->admin->menuList->model['menuOrder']['10']);
unset($lang->admin->menuList->model['menuOrder']['20']);
}
if(!helper::hasFeature('waterfall'))
{
unset($lang->admin->menuList->model['subMenu']['waterfall']);
unset($lang->admin->menuList->model['menuOrder']['15']);
}
if(!helper::hasFeature('waterfallplus'))
{
unset($lang->admin->menuList->model['subMenu']['waterfallplus']);
unset($lang->admin->menuList->model['menuOrder']['25']);
}
if($config->edition == 'max' or $config->edition == 'ipd')
{
if(!helper::hasFeature('scrum_auditplan') and !helper::hasFeature('scrum_process')) unset($lang->admin->menuList->model['subMenu']['scrum'], $lang->admin->menuList->model['menuOrder']['10']);
if(!helper::hasFeature('agileplus_auditplan') and !helper::hasFeature('agileplus_process')) unset($lang->admin->menuList->model['subMenu']['agileplus'], $lang->admin->menuList->model['menuOrder']['20']);
}
if($config->vision == 'lite')
{
unset($lang->admin->menuList->system['subMenu']['mode']);
@@ -218,15 +174,6 @@ if($config->vision == 'lite')
unset($lang->admin->menuList->system['menuOrder']['45']);
unset($lang->admin->menuList->system['menuOrder']['50']);
unset($lang->admin->menuList->model['subMenu']['scrum']);
unset($lang->admin->menuList->model['subMenu']['waterfall']);
unset($lang->admin->menuList->model['subMenu']['agileplus']);
unset($lang->admin->menuList->model['subMenu']['waterfallplus']);
unset($lang->admin->menuList->model['menuOrder']['10']);
unset($lang->admin->menuList->model['menuOrder']['15']);
unset($lang->admin->menuList->model['menuOrder']['20']);
unset($lang->admin->menuList->model['menuOrder']['25']);
unset($lang->admin->menuList->feature['subMenu']['product']);
unset($lang->admin->menuList->feature['subMenu']['execution']);
unset($lang->admin->menuList->feature['subMenu']['qa']);
+12 -11
View File
@@ -34,6 +34,7 @@ $lang->admin->zentaoUsed = '您已使用禅道';
$lang->admin->api = '接口';
$lang->admin->log = '日志';
$lang->admin->setting = '设置';
$lang->admin->setFlow = '流程配置';
$lang->admin->pluginRecommendation = '插件推荐';
$lang->admin->zentaoInfo = '禅道信息';
$lang->admin->officialAccount = '官方公众号';
@@ -60,10 +61,12 @@ $lang->admin->info->account = "您的禅道社区账户为%s。";
$lang->admin->info->log = '超出存天数的日志会被删除,需要开启计划任务。';
$lang->admin->notice = new stdclass();
$lang->admin->notice->register = "可%s禅道社区 www.zentao.net,及时获得禅道最新信息。";
$lang->admin->notice->ignore = "不再提示";
$lang->admin->notice->int = "『%s』应当是正整数。";
$lang->admin->notice->openUR = "请先开启{$lang->URCommon},才能使用{$lang->ERCommon}。";
$lang->admin->notice->register = "可%s禅道社区 www.zentao.net,及时获得禅道最新信息。";
$lang->admin->notice->ignore = "不再提示";
$lang->admin->notice->int = "『%s』应当是正整数。";
$lang->admin->notice->confirmDisableStoryType = "‘{type}’功能关闭后,系统会将项目和执行内已经关联的相应‘{type}’全部移除, 操作不可逆";
$lang->admin->notice->openDependFeature = '使用“{source}”功能时需要同步开启“{target}”功能。';
$lang->admin->notice->closeDependFeature = '关闭“{source}”功能时需要同步关闭“{target}”功能。';
$lang->admin->registerNotice = new stdclass();
$lang->admin->registerNotice->common = '注册新帐号';
@@ -89,12 +92,9 @@ $lang->admin->setModule->closed = '已关闭';
$lang->admin->setModule->my = '地盘';
$lang->admin->setModule->product = $lang->productCommon;
$lang->admin->setModule->scrum = '敏捷' . $lang->projectCommon;
$lang->admin->setModule->waterfall = '瀑布' . $lang->projectCommon;
$lang->admin->setModule->agileplus = '融合敏捷' . $lang->projectCommon;
$lang->admin->setModule->waterfallplus = '融合瀑布' . $lang->projectCommon;
$lang->admin->setModule->project = $lang->projectCommon;
$lang->admin->setModule->assetlib = '资产库';
$lang->admin->setModule->other = '通用功能';
$lang->admin->setModule->other = '其他功能';
$lang->admin->setModule->score = '积分';
$lang->admin->setModule->repo = '代码';
@@ -108,6 +108,9 @@ $lang->admin->setModule->roadmap = '路线图';
$lang->admin->setModule->track = '矩阵';
$lang->admin->setModule->ER = $lang->ERCommon;
$lang->admin->setModule->UR = $lang->URCommon;
$lang->admin->setModule->deliverable = '交付物';
$lang->admin->setModule->cm = '基线';
$lang->admin->setModule->change = '项目变更';
$lang->admin->setModule->researchplan = '调研';
$lang->admin->setModule->gapanalysis = '培训';
$lang->admin->setModule->storylib = '需求库';
@@ -171,8 +174,6 @@ $lang->admin->menuSetting['user']['name'] = '人员管理';
$lang->admin->menuSetting['user']['desc'] = '维护部门、添加人员、分组配置权限。';
$lang->admin->menuSetting['switch']['name'] = '功能开关';
$lang->admin->menuSetting['switch']['desc'] = '打开、关闭系统部分功能。';
$lang->admin->menuSetting['model']['name'] = '项目模型配置';
$lang->admin->menuSetting['model']['desc'] = '不同项目管理模型和项目通用要素配置。';
$lang->admin->menuSetting['feature']['name'] = '功能配置';
$lang->admin->menuSetting['feature']['desc'] = '按照功能菜单进行系统的要素配置。';
$lang->admin->menuSetting['template']['name'] = '文档模板';
+44 -12
View File
@@ -101,10 +101,11 @@ class adminModel extends model
* 设置后台二级导航。
* Set admin menu.
*
* @param int $groupID
* @access public
* @return void
*/
public function setMenu(): void
public function setMenu(int $groupID = 0): void
{
$this->checkPrivMenu();
@@ -117,6 +118,7 @@ class adminModel extends model
$this->setSwitcher($menuKey);
if(isset($this->lang->admin->menuList->$menuKey))
{
if(in_array($menuKey, array('projectflow', 'productflow')) && $groupID) $this->lang->admin->menuList->{$menuKey}['subMenu'] = $this->lang->admin->menuList->{$menuKey}['childMenu'];
if(isset($this->lang->admin->menuList->{$menuKey}['subMenu']))
{
$moduleName = $this->app->rawModule;
@@ -132,6 +134,23 @@ class adminModel extends model
if($firstParam == $subMenuKey) $subModule = 'custom';
}
/* 针对项目流程和产品流程菜单做特殊处理。 */
if(in_array($menuKey, array('projectflow', 'productflow')) && $groupID)
{
$workflowGroup = $this->fetchByID($groupID, 'workflowgroup');
if($subMenuKey == 'review' && $workflowGroup->projectModel == 'kanban') continue;
if($subMenuKey == 'stage' && in_array($workflowGroup->projectModel, array('kanban', 'scrum', 'agileplus'))) continue;
$subMenu['link'] = sprintf($subMenu['link'], $groupID);
if(isset($subMenu['subMenu']))
{
foreach($subMenu['subMenu'] as $subSubMenuKey => $subSubMenu)
{
$subMenu['subMenu'][$subSubMenuKey]['link'] = sprintf($subSubMenu['link'], $groupID);
}
}
}
if(!empty($subModule)) $subMenu['subModule'] = $subModule;
if(isset($this->lang->admin->menuList->{$menuKey}['tabMenu'][$subMenuKey]))
{
@@ -192,12 +211,6 @@ class adminModel extends model
if($dept && common::hasPriv('company', 'browse')) $menu['link'] = helper::createLink('company', 'browse');
}
/* Set links to authorized navigation. */
if(isset($menu['subMenu']))
{
$menu = $this->setSubMenu($menuKey, $menu);
}
if(!empty($menu['link']) && strpos($menu['link'], '|') !== false)
{
list($module, $method, $params) = explode('|', $menu['link'] . '|');
@@ -205,6 +218,12 @@ class adminModel extends model
if(($this->app->user->admin || $module . $method != 'adminregister') && common::hasPriv($module, $method)) $menu['disabled'] = false;
}
/* Set links to authorized navigation. */
if(!in_array($menuKey, array('productflow', 'projectflow')) && isset($menu['subMenu']))
{
$menu = $this->setSubMenu($menuKey, $menu);
}
$order = $menu['order'];
$orders[$order] = $menuKey;
@@ -235,7 +254,7 @@ class adminModel extends model
{
/* Reorder secondary navigation. */
$subMenuList = array();
$subMenuOrders = $menu['menuOrder'];
$subMenuOrders = !empty($menu['menuOrder']) ? $menu['menuOrder'] : array();
if(empty($subMenuOrders)) return array();
ksort($subMenuOrders);
foreach($subMenuOrders as $value)
@@ -280,8 +299,11 @@ class adminModel extends model
$menu['subMenu'][$subMenuKey]['link'] = substr($subMenu['link'], 0, strpos($subMenu['link'], '|') + 1) . $module . '|' . $method . '|' . $params;
/* Update the level 1 navigation link. */
if(empty($menu['link'])) $menu['link'] = helper::createLink($module, $method, $params);
$menu['disabled'] = false;
if(!empty($menu['disabled']))
{
$menu['link'] = helper::createLink($module, $method, $params);
$menu['disabled'] = false;
}
}
}
@@ -371,6 +393,11 @@ class adminModel extends model
$moduleName = $this->app->rawModule;
$methodName = $this->app->rawMethod;
$firstParam = $this->app->rawParams ? reset($this->app->rawParams) : '';
if($this->config->edition == 'open')
{
$this->config->admin->menuGroup['feature'][] = 'stage|browse';
$this->config->admin->menuGroup['feature'][] = 'stage|batchcreate';
}
foreach($this->config->admin->menuGroup as $menuKey => $menuGroup)
{
@@ -384,6 +411,11 @@ class adminModel extends model
{
if(in_array($firstParam, $this->config->admin->menuModuleGroup[$menuKey]["custom|$methodName"])) return $menuKey;
}
elseif($moduleName == 'workflowgroup' && $firstParam && !in_array($methodName, array('project', 'product')))
{
$workflowgroup = $this->loadModel('workflowgroup')->fetchByID($firstParam);
return $workflowgroup->type == 'project' ? 'projectflow' : 'productflow';
}
else
{
return $menuKey;
@@ -460,8 +492,8 @@ class adminModel extends model
{
if($this->config->vision == 'lite' and !in_array($menuKey, $this->config->admin->liteMenuList)) continue;
$class = $menuKey == $currentMenuKey ? "active" : '';
if($menuGroup['disabled']) $class .= ' disabled not-clear-menu';
$output .= "<li class='$class'>" . html::a($menuGroup['disabled'] ? '###' : $menuGroup['link'], "<img src='{$this->config->webRoot}static/svg/admin-{$menuKey}.svg'/>" . $menuGroup['name']) . "</li>";
if(!empty($menuGroup['disabled'])) $class .= ' disabled not-clear-menu';
$output .= "<li class='$class'>" . html::a(!empty($menuGroup['disabled']) ? '###' : (!empty($menuGroup['link']) ? $menuGroup['link'] : ''), (!empty($menuGroup['icon']) ? "<i class='icon icon-{$menuGroup['icon']} svg-icon bg-{$menuGroup['bg']}' style='height:24px;width:24px;text-align:center;border-radius:0.375rem;color:#fff;align-content:center;margin-left:6px;opacity:1;transform: scale(1);'></i>" : "<img src='{$this->config->webRoot}static/svg/admin-{$menuKey}.svg'/>") . $menuGroup['name']) . "</li>";
}
$output .= "</ul></div>";
+6 -5
View File
@@ -24,8 +24,9 @@ su('admin');
$adminTest = new adminTest();
r($adminTest->checkPrivMenuTest()) && p('success,hasMenuList') && e('1,1'); // 测试步骤1:基本菜单权限检查功能
r($adminTest->checkPrivMenuTest()) && p('hasOrderAttribute') && e('1'); // 测试步骤2:验证所有菜单项都有order属性
r($adminTest->checkPrivMenuTest()) && p('hasDisabledAttribute') && e('1'); // 测试步骤3:验证所有菜单项都有disabled属性
r($adminTest->checkPrivMenuTest()) && p('isSorted') && e('1'); // 测试步骤4:验证菜单按order排序
r($adminTest->checkPrivMenuTest()) && p('success') && e('1'); // 测试步骤5:验证菜单列表为对象类型
// 4. 强制要求:必须包含至少5个测试步骤
r($adminTest->checkPrivMenuTest()) && p('hasMenuList') && e('1'); // 步骤1:检查是否有菜单列表
r($adminTest->checkPrivMenuTest()) && p('menuCount') && e('11'); // 步骤2:检查菜单数量
r($adminTest->checkPrivMenuTest()) && p('hasLinkedMenu') && e('1'); // 步骤3:检查是否有已链接菜单
r($adminTest->checkPrivMenuTest()) && p('hasDisabledMenu') && e('1'); // 步骤4:检查是否有已禁用菜单
r($adminTest->checkPrivMenuTest()) && p('hasMenuList,menuCount') && e('1,11'); // 步骤5:验证菜单列表结构
+28 -28
View File
@@ -14,9 +14,9 @@ cid=14980
- 访问编辑用户页面 @company
- 访问权限列表页面 @company
- 访问功能设置页面 @switch
- 访问项目必填项页面 @model
- 访问阶段类型页面 @model
- 访问检查对象页面 @model
- 访问项目必填项页面 @feature
- 访问阶段类型页面 @feature
- 访问检查对象页面 @projectflow
- 访问自定义待办页面 @feature
- 访问自定义产品页面 @feature
- 访问自定义看板页面 @feature
@@ -44,28 +44,28 @@ $params1 = array('module' => 'project');
$params2 = array('module' => 'todo');
$params3 = array('module' => 'baseline');
r($admin->getMenuKeyTest('admin', 'index')) && p() && e('null'); //访问后台页面
r($admin->getMenuKeyTest('backup', 'index')) && p() && e('system'); //访问备份页面
r($admin->getMenuKeyTest('cron', 'index')) && p() && e('system'); //访问定时列表页面
r($admin->getMenuKeyTest('cron', 'create')) && p() && e('system'); //访问定时创建页面
r($admin->getMenuKeyTest('dept', 'browse')) && p() && e('company'); //访问部门页面
r($admin->getMenuKeyTest('user', 'edit')) && p() && e('company'); //访问编辑用户页面
r($admin->getMenuKeyTest('group', 'browse')) && p() && e('company'); //访问权限列表页面
r($admin->getMenuKeyTest('admin', 'setmodule')) && p() && e('switch'); //访问功能设置页面
r($admin->getMenuKeyTest('custom', 'required', $params1)) && p() && e('model'); //访问项目必填项页面
r($admin->getMenuKeyTest('stage', 'settype')) && p() && e('model'); //访问阶段类型页面
r($admin->getMenuKeyTest('auditcl', 'scrumbrowse')) && p() && e('model'); //访问检查对象页面
r($admin->getMenuKeyTest('custom', 'set', $params2)) && p() && e('feature'); //访问自定义待办页面
r($admin->getMenuKeyTest('custom', 'product')) && p() && e('feature'); //访问自定义产品页面
r($admin->getMenuKeyTest('custom', 'kanban')) && p() && e('feature'); //访问自定义看板页面
r($admin->getMenuKeyTest('mail', 'detect')) && p() && e('message'); //访问发信配置页面
r($admin->getMenuKeyTest('webhook', 'browse')) && p() && e('message'); //访问Webhook页面
r($admin->getMenuKeyTest('sms', 'index')) && p() && e('message'); //访问短信页面
r($admin->getMenuKeyTest('message', 'browser')) && p() && e('message'); //访问浏览器页面
r($admin->getMenuKeyTest('message', 'setting')) && p() && e('message'); //访问信息配置页面
r($admin->getMenuKeyTest('extension', 'browse')) && p() && e('extension'); //访问插件页面
r($admin->getMenuKeyTest('dev', 'api')) && p() && e('dev'); //访问API页面
r($admin->getMenuKeyTest('dev', 'db')) && p() && e('dev'); //访问数据库页面
r($admin->getMenuKeyTest('dev', 'editor')) && p() && e('dev'); //访问编辑器页面
r($admin->getMenuKeyTest('entry', 'browse')) && p() && e('dev'); //访问应用页面
r($admin->getMenuKeyTest('convert', 'convertjira')) && p() && e('convert'); //访问数据导入页面
r($admin->getMenuKeyTest('admin', 'index')) && p() && e('null'); //访问后台页面
r($admin->getMenuKeyTest('backup', 'index')) && p() && e('system'); //访问备份页面
r($admin->getMenuKeyTest('cron', 'index')) && p() && e('system'); //访问定时列表页面
r($admin->getMenuKeyTest('cron', 'create')) && p() && e('system'); //访问定时创建页面
r($admin->getMenuKeyTest('dept', 'browse')) && p() && e('company'); //访问部门页面
r($admin->getMenuKeyTest('user', 'edit')) && p() && e('company'); //访问编辑用户页面
r($admin->getMenuKeyTest('group', 'browse')) && p() && e('company'); //访问权限列表页面
r($admin->getMenuKeyTest('admin', 'setmodule')) && p() && e('switch'); //访问功能设置页面
r($admin->getMenuKeyTest('custom', 'required', $params1)) && p() && e('feature'); //访问项目必填项页面
r($admin->getMenuKeyTest('stage', 'settype')) && p() && e('feature'); //访问阶段类型页面
r($admin->getMenuKeyTest('auditcl', 'scrumbrowse')) && p() && e('projectflow'); //访问检查对象页面
r($admin->getMenuKeyTest('custom', 'set', $params2)) && p() && e('feature'); //访问自定义待办页面
r($admin->getMenuKeyTest('custom', 'product')) && p() && e('feature'); //访问自定义产品页面
r($admin->getMenuKeyTest('custom', 'kanban')) && p() && e('feature'); //访问自定义看板页面
r($admin->getMenuKeyTest('mail', 'detect')) && p() && e('message'); //访问发信配置页面
r($admin->getMenuKeyTest('webhook', 'browse')) && p() && e('message'); //访问Webhook页面
r($admin->getMenuKeyTest('sms', 'index')) && p() && e('message'); //访问短信页面
r($admin->getMenuKeyTest('message', 'browser')) && p() && e('message'); //访问浏览器页面
r($admin->getMenuKeyTest('message', 'setting')) && p() && e('message'); //访问信息配置页面
r($admin->getMenuKeyTest('extension', 'browse')) && p() && e('extension'); //访问插件页面
r($admin->getMenuKeyTest('dev', 'api')) && p() && e('dev'); //访问API页面
r($admin->getMenuKeyTest('dev', 'db')) && p() && e('dev'); //访问数据库页面
r($admin->getMenuKeyTest('dev', 'editor')) && p() && e('dev'); //访问编辑器页面
r($admin->getMenuKeyTest('entry', 'browse')) && p() && e('dev'); //访问应用页面
r($admin->getMenuKeyTest('convert', 'convertjira')) && p() && e('convert'); //访问数据导入页面
+1 -1
View File
@@ -30,4 +30,4 @@ r($adminTest->setMenuTest('admin', 'safe', array())) && p('hasSwitcherMenu') &&
r($adminTest->setMenuTest('user', 'browse', array())) && p('hasSwitcherMenu') && e('1'); // 步骤2:user模块在company组中的方法测试
r($adminTest->setMenuTest('custom', 'required', array('product'))) && p('hasSwitcherMenu') && e('1'); // 步骤3:custom模块在feature组中的特殊方法测试
r($adminTest->setMenuTest('admin', 'index', array())) && p('hasSwitcherMenu') && e('~~'); // 步骤4:admin模块不在任何组中的方法测试
r($adminTest->setMenuTest('nonexistent', 'test', array())) && p('hasSwitcherMenu') && e('~~'); // 步骤5:不存在模块的处理测试
r($adminTest->setMenuTest('nonexistent', 'test', array())) && p('hasSwitcherMenu') && e('~~'); // 步骤5:不存在模块的处理测试
+1 -1
View File
@@ -98,4 +98,4 @@ $devMenu = array(
'link' => '',
'disabled' => true
);
r($adminTest->setSubMenuTest('dev', $devMenu)) && p('subMenu,editor,link') && e('Editor|editor|index|');
r($adminTest->setSubMenuTest('dev', $devMenu)) && p('subMenu,editor,link') && e('Editor|editor|index|');
+1 -1
View File
@@ -26,4 +26,4 @@ r($adminTest->setSwitcherTest('system')) && p() && e('success'); // 步骤1:
r($adminTest->setSwitcherTest('company')) && p() && e('success'); // 步骤2:正常情况,company菜单
r($adminTest->setSwitcherTest('feature')) && p() && e('success'); // 步骤3:正常情况,feature菜单
r($adminTest->setSwitcherTest('')) && p() && e('0'); // 步骤4:空参数情况
r($adminTest->setSwitcherTest('nonexistent')) && p() && e('success'); // 步骤5:不存在菜单键测试异常处理
r($adminTest->setSwitcherTest('nonexistent')) && p() && e('success'); // 步骤5:不存在菜单键测试异常处理
+27 -5
View File
@@ -81,11 +81,12 @@ $buildUsed = function(int $amount, string $unit = ''): array
};
$settingItems = array();
$flowItems = array();
foreach($lang->admin->menuList as $menuKey => $menu)
{
if($config->vision == 'lite' and !in_array($menuKey, $config->admin->liteMenuList)) continue;
$settingItems[] = div
$items = div
(
setClass('pb-4 pr-4 h-32 w-1/' . ($config->vision == 'lite' ? 3 : 5)),
col
@@ -101,9 +102,9 @@ foreach($lang->admin->menuList as $menuKey => $menu)
div
(
setClass('flex gap-1 font-bold text-md'),
img(set::src("static/svg/admin-{$menuKey}.svg")),
!empty($menu['icon']) ? icon(setClass("svg-icon rounded-lg content-center bg-{$menu['bg']} text-white"), $menu['icon']) : img(set::src("static/svg/admin-{$menuKey}.svg")),
$menu['name'],
$menuKey != 'adminregister' ?
!empty($config->admin->helpURL[$menuKey]) ?
a
(
setClass('text-gray'),
@@ -122,6 +123,15 @@ foreach($lang->admin->menuList as $menuKey => $menu)
)
)
);
if(!empty($menu['group']) && $menu['group'] == 'flow')
{
$flowItems[] = $items;
}
else
{
$settingItems[] = $items;
}
}
$pluginItems = array();
@@ -248,7 +258,7 @@ if($config->edition != 'ipd')
div
(
set::style(array('width' => '70%')),
div
$settingItems ? div
(
setID('settings'),
setClass('bg-white rounded-md mb-4'),
@@ -259,7 +269,19 @@ div
on::click('redirectSetting'),
$settingItems
)
),
) : null,
$flowItems ? div
(
setID('flows'),
setClass('bg-white rounded-md mb-4'),
$buildHeader($lang->admin->setFlow),
div
(
setClass('flex flex-wrap pl-4'),
on::click('redirectSetting'),
$flowItems
)
) : null,
$pluginItems ? div
(
setID('plugin'),
+14 -1
View File
@@ -12,7 +12,17 @@ namespace zin;
global $app;
$app->loadLang('project');
jsVar('confirmDisableStoryType', $lang->project->confirmDisableStoryType);
jsVar('confirmDisableStoryType', $lang->admin->notice->confirmDisableStoryType);
jsVar('edition', $config->edition);
jsVar('enableER', $config->enableER);
jsVar('URAndSR', $config->URAndSR);
jsVar('ERCommon', $lang->ERCommon);
jsVar('URCommon', $lang->URCommon);
jsVar('changeLang', $lang->admin->setModule->change);
jsVar('deliverableLang', $lang->admin->setModule->deliverable);
jsVar('cmLang', $lang->admin->setModule->cm);
jsVar('openDependFeature', $lang->admin->notice->openDependFeature);
jsVar('closeDependFeature', $lang->admin->notice->closeDependFeature);
if(strpos(",$disabledFeatures,", ",productUR,") !== false) $disabledFeatures .= ',productER';
@@ -20,6 +30,7 @@ $rows = array();
foreach($config->featureGroup as $group => $features)
{
if(strpos(",$disabledFeatures,", ",$group,") !== false) continue;
if($config->systemMode == 'light' && $group == 'project') continue;
$hasData = false;
foreach($features as $feature)
@@ -84,8 +95,10 @@ foreach($config->featureGroup as $group => $features)
formPanel
(
set::id('setModuleForm'),
set::title($lang->admin->setModuleIndex),
set::actions(false),
set::ajax(array('beforeSubmit' => jsRaw("submitForm"))),
h::table
(
setClass('border w-full'),
+1 -1
View File
@@ -1048,7 +1048,7 @@ $lang->ai->formSchema['testcase']['create']->properties->type->type
$lang->ai->formSchema['testcase']['create']->properties->type->description = '用例的类型';
$lang->ai->formSchema['testcase']['create']->properties->type->enum = array('feature', 'performance', 'config', 'install', 'security', 'interface', 'unit', 'other');
$lang->ai->formSchema['testcase']['create']->properties->stage->type = 'string';
$lang->ai->formSchema['testcase']['create']->properties->stage->description = '用例适用阶段';
$lang->ai->formSchema['testcase']['create']->properties->stage->description = '用例适用环节';
$lang->ai->formSchema['testcase']['create']->properties->stage->enum = array('unittest', 'feature', 'intergrate', 'system', 'smoke', 'bvt');
$lang->ai->formSchema['testcase']['create']->properties->title->type = 'string';
$lang->ai->formSchema['testcase']['create']->properties->title->description = '用例的标题';
+1 -1
View File
@@ -247,7 +247,7 @@ $testcasefields['id'] = array('name' => '用例编号', 'field' =>
$testcasefields['title'] = array('name' => '用例标题', 'field' => 'title', 'object' => 'testcase', 'type' => 'string');
$testcasefields['pri'] = array('name' => '优先级', 'field' => 'pri', 'object' => 'testcase', 'type' => 'option');
$testcasefields['type'] = array('name' => '用例类型', 'field' => 'type', 'object' => 'testcase', 'type' => 'option');
$testcasefields['stage'] = array('name' => '适用阶段', 'field' => 'stage', 'object' => 'testcase', 'type' => 'option');
$testcasefields['stage'] = array('name' => '适用环节', 'field' => 'stage', 'object' => 'testcase', 'type' => 'option');
$testcasefields['status'] = array('name' => '用例状态', 'field' => 'status', 'object' => 'testcase', 'type' => 'option');
$testcasefields['version'] = array('name' => '用例版本', 'field' => 'version', 'object' => 'testcase', 'type' => 'number');
$testcasefields['product_id'] = array('name' => '编号', 'field' => 'id', 'object' => 'product', 'type' => 'object');
+13
View File
@@ -4145,6 +4145,19 @@ $config->bi->builtin->metrics[] = array
'definition' => "所有风险个数求和\n指派给为某人\n过滤已删除的风险\n过滤已关闭的风险\n过滤已删除项目的风险"
);
$reviewissueMetrics = array();
$reviewissueMetrics['name'] = '按人员统计的被指派的评审意见数';
$reviewissueMetrics['alias'] = '被指派的评审意见数';
$reviewissueMetrics['code'] = 'count_of_assigned_reviewissue_in_user';
$reviewissueMetrics['purpose'] = 'scale';
$reviewissueMetrics['scope'] = 'user';
$reviewissueMetrics['object'] = 'reviewissue';
$reviewissueMetrics['unit'] = 'count';
$reviewissueMetrics['dateType'] = 'nodate';
$reviewissueMetrics['desc'] = '按人员统计的被指派的评审意见数表示每个人被指派的评审意见数量之和,反映了每个人员需要处理的评审意见数量的规模。该数值越大,说明需要投入越多的时间处理评审意见';
$reviewissueMetrics['definition'] = "所有评审意见个数求和\n指派给为某人\n过滤已删除的评审意见\n过滤已关闭的评审意见\n过滤已删除项目的评审意见";
$config->bi->builtin->metrics[] = $reviewissueMetrics;
$config->bi->builtin->metrics[] = array
(
'name' => '按人员统计的被指派的问题数',
+6 -6
View File
@@ -7,12 +7,12 @@ if(in_array($config->edition, array('max', 'biz', 'ipd')))
$config->block->feedback = new stdclass();
$config->block->feedback->dtable = new stdclass();
$config->block->feedback->dtable->fieldList = array();
$config->block->feedback->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->feedback->dtable->fieldList['title'] = array('name' => 'title', 'title' => $lang->feedback->title, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'feedback', 'method' => $config->vision == 'lite' ? 'view' : 'adminView', 'params' => 'feedbackID={id}'));
$config->block->feedback->dtable->fieldList['product'] = array('name' => 'product', 'title' => $lang->feedback->product, 'type' => 'category', 'sort' => true);
$config->block->feedback->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => $lang->feedback->pri, 'type' => 'pri', 'sort' => true);
$config->block->feedback->dtable->fieldList['status'] = array('name' => 'status', 'title' => $lang->feedback->status, 'type' => 'status', 'sort' => true, 'statusMap' => $lang->feedback->statusList);
$config->block->feedback->dtable->fieldList['type'] = array('name' => 'type', 'title' => $lang->feedback->type, 'type' => 'category', 'sort' => true, 'map' => $lang->feedback->typeList);
$config->block->feedback->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->feedback->dtable->fieldList['title'] = array('name' => 'title', 'title' => zget($lang->feedback, 'title'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'feedback', 'method' => $config->vision == 'lite' ? 'view' : 'adminView', 'params' => 'feedbackID={id}'));
$config->block->feedback->dtable->fieldList['product'] = array('name' => 'product', 'title' => zget($lang->feedback, 'product'), 'type' => 'category', 'sort' => true);
$config->block->feedback->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => zget($lang->feedback, 'pri'), 'type' => 'pri', 'sort' => true);
$config->block->feedback->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->feedback, 'status'), 'type' => 'status', 'sort' => true, 'statusMap' => zget($lang->feedback, 'statusList', array()));
$config->block->feedback->dtable->fieldList['type'] = array('name' => 'type', 'title' => zget($lang->feedback, 'type'), 'type' => 'category', 'sort' => true, 'map' => zget($lang->feedback, 'typeList', array()));
$config->block->feedback->dtable->short = new stdclass();
$config->block->feedback->dtable->short->fieldList['id'] = $config->block->feedback->dtable->fieldList['id'];
+7 -7
View File
@@ -7,13 +7,13 @@ if(in_array($config->edition, array('max', 'ipd')))
$config->block->issue = new stdclass();
$config->block->issue->dtable = new stdclass();
$config->block->issue->dtable->fieldList = array();
$config->block->issue->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->issue->dtable->fieldList['title'] = array('name' => 'title', 'title' => $lang->issue->title, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'issue', 'method' => 'view', 'params' => 'issueID={id}'));
$config->block->issue->dtable->fieldList['type'] = array('name' => 'type', 'title' => $lang->issue->type, 'type' => 'category', 'sort' => true, 'map' => $lang->issue->typeList);
$config->block->issue->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => $lang->issue->priAB, 'type' => 'pri', 'sort' => true);
$config->block->issue->dtable->fieldList['severity'] = array('name' => 'severity', 'title' => $lang->issue->severity, 'type' => 'severity', 'sort' => true);
$config->block->issue->dtable->fieldList['owner'] = array('name' => 'owner', 'title' => $lang->issue->owner, 'type' => 'user', 'sort' => true);
$config->block->issue->dtable->fieldList['status'] = array('name' => 'status', 'title' => $lang->issue->status, 'type' => 'status', 'sort' => true, 'statusMap' => $lang->issue->statusList);
$config->block->issue->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->issue->dtable->fieldList['title'] = array('name' => 'title', 'title' => zget($lang->issue, 'title'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'issue', 'method' => 'view', 'params' => 'issueID={id}'));
$config->block->issue->dtable->fieldList['type'] = array('name' => 'type', 'title' => zget($lang->issue, 'type'), 'type' => 'category', 'sort' => true, 'map' => zget($lang->issue, 'typeList', array()));
$config->block->issue->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => zget($lang->issue, 'priAB'), 'type' => 'pri', 'sort' => true);
$config->block->issue->dtable->fieldList['severity'] = array('name' => 'severity', 'title' => zget($lang->issue, 'severity'), 'type' => 'severity', 'sort' => true);
$config->block->issue->dtable->fieldList['owner'] = array('name' => 'owner', 'title' => zget($lang->issue, 'owner'), 'type' => 'user', 'sort' => true);
$config->block->issue->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->issue, 'status'), 'type' => 'status', 'sort' => true, 'statusMap' => zget($lang->issue, 'statusList', array()));
$config->block->issue->dtable->short = new stdclass();
$config->block->issue->dtable->short->fieldList['id'] = $config->block->issue->dtable->fieldList['id'];
+6 -6
View File
@@ -7,12 +7,12 @@ if(in_array($config->edition, array('max', 'ipd')))
$config->block->meeting = new stdclass();
$config->block->meeting->dtable = new stdclass();
$config->block->meeting->dtable->fieldList = array();
$config->block->meeting->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->meeting->dtable->fieldList['name'] = array('name' => 'name', 'title' => $lang->meeting->name, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'meeting', 'method' => 'view', 'params' => 'meetingID={id}'));
$config->block->meeting->dtable->fieldList['mode'] = array('name' => 'mode', 'title' => $lang->meeting->mode, 'type' => 'category', 'sort' => true, 'map' => $lang->meeting->modeList);
$config->block->meeting->dtable->fieldList['dept'] = array('name' => 'dept', 'title' => $lang->meeting->dept, 'type' => 'category', 'sort' => true);
$config->block->meeting->dtable->fieldList['date'] = array('name' => 'date', 'title' => $lang->meeting->date, 'type' => 'date', 'sort' => true);
$config->block->meeting->dtable->fieldList['host'] = array('name' => 'host', 'title' => $lang->meeting->host, 'type' => 'user', 'sort' => true);
$config->block->meeting->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->meeting->dtable->fieldList['name'] = array('name' => 'name', 'title' => zget($lang->meeting, 'name'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'meeting', 'method' => 'view', 'params' => 'meetingID={id}'));
$config->block->meeting->dtable->fieldList['mode'] = array('name' => 'mode', 'title' => zget($lang->meeting, 'mode'), 'type' => 'category', 'sort' => true, 'map' => zget($lang->meeting, 'modeList', array()));
$config->block->meeting->dtable->fieldList['dept'] = array('name' => 'dept', 'title' => zget($lang->meeting, 'dept'), 'type' => 'category', 'sort' => true);
$config->block->meeting->dtable->fieldList['date'] = array('name' => 'date', 'title' => zget($lang->meeting, 'date'), 'type' => 'date', 'sort' => true);
$config->block->meeting->dtable->fieldList['host'] = array('name' => 'host', 'title' => zget($lang->meeting, 'host'), 'type' => 'user', 'sort' => true);
$config->block->meeting->dtable->short = new stdclass();
$config->block->meeting->dtable->short->fieldList['id'] = $config->block->meeting->dtable->fieldList['id'];
+20
View File
@@ -0,0 +1,20 @@
<?php
if(in_array($config->edition, array('max', 'ipd')))
{
global $lang, $app;
$app->loadLang('reviewissue');
$config->block->reviewissue = new stdclass();
$config->block->reviewissue->dtable = new stdclass();
$config->block->reviewissue->dtable->fieldList = array();
$config->block->reviewissue->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->reviewissue->dtable->fieldList['name'] = array('name' => 'title', 'title' => zget($lang->reviewissue, 'title'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'reviewissue', 'method' => 'view', 'params' => 'reviewissueID={id}'));
$config->block->reviewissue->dtable->fieldList['opinion'] = array('name' => 'opinion', 'title' => zget($lang->reviewissue, 'opinion'), 'type' => 'text', 'sort' => true);
$config->block->reviewissue->dtable->fieldList['type'] = array('name' => 'type', 'title' => zget($lang->reviewissue, 'type'), 'type' => 'category', 'map' => array('review' => $lang->reviewissue->common), 'sort' => true);
$config->block->reviewissue->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->reviewissue, 'status'), 'type' => 'status', 'statusMap' => zget($lang->reviewissue, 'statusList', array()), 'sort' => true);
$config->block->reviewissue->dtable->short = new stdclass();
$config->block->reviewissue->dtable->short->fieldList['id'] = $config->block->reviewissue->dtable->fieldList['id'];
$config->block->reviewissue->dtable->short->fieldList['name'] = $config->block->reviewissue->dtable->fieldList['name'];
$config->block->reviewissue->dtable->short->fieldList['status'] = $config->block->reviewissue->dtable->fieldList['status'];
}
+6 -6
View File
@@ -7,12 +7,12 @@ if(in_array($config->edition, array('max', 'ipd')))
$config->block->risk = new stdclass();
$config->block->risk->dtable = new stdclass();
$config->block->risk->dtable->fieldList = array();
$config->block->risk->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->risk->dtable->fieldList['name'] = array('name' => 'name', 'title' => $lang->risk->name, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'risk', 'method' => 'view', 'params' => 'riskID={id}'));
$config->block->risk->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => $lang->risk->priAB, 'type' => 'status', 'statusMap' => $lang->risk->priList, 'sort' => true);
$config->block->risk->dtable->fieldList['rate'] = array('name' => 'rate', 'title' => $lang->risk->rate, 'type' => 'number', 'width' => '100px', 'sort' => true);
$config->block->risk->dtable->fieldList['status'] = array('name' => 'status', 'title' => $lang->risk->status, 'type' => 'status', 'statusMap' => $lang->risk->statusList, 'sort' => true);
$config->block->risk->dtable->fieldList['strategy'] = array('name' => 'strategy', 'title' => $lang->risk->strategy, 'type' => 'status', 'statusMap' => $lang->risk->strategyList, 'sort' => true);
$config->block->risk->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->risk->dtable->fieldList['name'] = array('name' => 'name', 'title' => zget($lang->risk, 'name'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'risk', 'method' => 'view', 'params' => 'riskID={id}'));
$config->block->risk->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => zget($lang->risk, 'priAB'), 'type' => 'status', 'statusMap' => zget($lang->risk, 'priList', array()), 'sort' => true);
$config->block->risk->dtable->fieldList['rate'] = array('name' => 'rate', 'title' => zget($lang->risk, 'rate'), 'type' => 'number', 'width' => '100px', 'sort' => true);
$config->block->risk->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->risk, 'status'), 'type' => 'status', 'statusMap' => zget($lang->risk, 'statusList', array()), 'sort' => true);
$config->block->risk->dtable->fieldList['strategy'] = array('name' => 'strategy', 'title' => zget($lang->risk, 'strategy'), 'type' => 'status', 'statusMap' => zget($lang->risk, 'strategyList', array()), 'sort' => true);
$config->block->risk->dtable->short = new stdclass();
$config->block->risk->dtable->short->fieldList['id'] = $config->block->risk->dtable->fieldList['id'];
+6 -6
View File
@@ -7,12 +7,12 @@ if(in_array($config->edition, array('max', 'biz', 'ipd')))
$config->block->ticket = new stdclass();
$config->block->ticket->dtable = new stdclass();
$config->block->ticket->dtable->fieldList = array();
$config->block->ticket->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->ticket->dtable->fieldList['title'] = array('name' => 'title', 'title' => $lang->ticket->title, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'ticket', 'method' => 'view', 'params' => 'ticketID={id}'));
$config->block->ticket->dtable->fieldList['product'] = array('name' => 'product', 'title' => $lang->ticket->product, 'type' => 'category', 'sort' => true);
$config->block->ticket->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => $lang->ticket->priAB, 'type' => 'pri', 'sort' => true);
$config->block->ticket->dtable->fieldList['status'] = array('name' => 'status', 'title' => $lang->ticket->status, 'type' => 'status', 'sort' => true, 'statusMap' => $lang->ticket->statusList);
$config->block->ticket->dtable->fieldList['type'] = array('name' => 'type', 'title' => $lang->ticket->type, 'type' => 'category', 'sort' => true, 'map' => $lang->ticket->typeList);
$config->block->ticket->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number');
$config->block->ticket->dtable->fieldList['title'] = array('name' => 'title', 'title' => zget($lang->ticket, 'title'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'ticket', 'method' => 'view', 'params' => 'ticketID={id}'));
$config->block->ticket->dtable->fieldList['product'] = array('name' => 'product', 'title' => zget($lang->ticket, 'product'), 'type' => 'category', 'sort' => true);
$config->block->ticket->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => zget($lang->ticket, 'priAB'), 'type' => 'pri', 'sort' => true);
$config->block->ticket->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->ticket, 'status'), 'type' => 'status', 'sort' => true, 'statusMap' => zget($lang->ticket, 'statusList', array()));
$config->block->ticket->dtable->fieldList['type'] = array('name' => 'type', 'title' => zget($lang->ticket, 'type'), 'type' => 'category', 'sort' => true, 'map' => zget($lang->ticket, 'typeList', array()));
$config->block->ticket->dtable->short = new stdclass();
$config->block->ticket->dtable->short->fieldList['id'] = $config->block->ticket->dtable->fieldList['id'];
+1
View File
@@ -234,6 +234,7 @@ $lang->block->availableBlocks['build'] = 'Builds';
$lang->block->availableBlocks['testcase'] = 'Testcase';
$lang->block->availableBlocks['testtask'] = 'Testtask';
$lang->block->availableBlocks['risk'] = 'Risks';
$lang->block->availableBlocks['reviewissue'] = 'Review Issue';
$lang->block->availableBlocks['issue'] = 'Issues';
$lang->block->availableBlocks['meeting'] = 'Meetings';
$lang->block->availableBlocks['feedback'] = 'Feedbacks';
+1
View File
@@ -234,6 +234,7 @@ $lang->block->availableBlocks['build'] = 'Builds';
$lang->block->availableBlocks['testcase'] = 'Testcase';
$lang->block->availableBlocks['testtask'] = 'Testtask';
$lang->block->availableBlocks['risk'] = 'Risks';
$lang->block->availableBlocks['reviewissue'] = 'Review Issue';
$lang->block->availableBlocks['issue'] = 'Issues';
$lang->block->availableBlocks['meeting'] = 'Meetings';
$lang->block->availableBlocks['feedback'] = 'Feedbacks';
+1
View File
@@ -234,6 +234,7 @@ $lang->block->availableBlocks['build'] = 'Builds';
$lang->block->availableBlocks['testcase'] = 'Testcase';
$lang->block->availableBlocks['testtask'] = 'Testtask';
$lang->block->availableBlocks['risk'] = 'Risks';
$lang->block->availableBlocks['reviewissue'] = 'Review Issue';
$lang->block->availableBlocks['issue'] = 'Issues';
$lang->block->availableBlocks['meeting'] = 'Meetings';
$lang->block->availableBlocks['feedback'] = 'Feedbacks';
+1
View File
@@ -234,6 +234,7 @@ $lang->block->availableBlocks['build'] = '构建列表';
$lang->block->availableBlocks['testcase'] = '用例';
$lang->block->availableBlocks['testtask'] = '测试单';
$lang->block->availableBlocks['risk'] = '风险';
$lang->block->availableBlocks['reviewissue'] = '评审问题';
$lang->block->availableBlocks['issue'] = '问题';
$lang->block->availableBlocks['meeting'] = '会议';
$lang->block->availableBlocks['feedback'] = '反馈';
+1 -1
View File
@@ -1166,4 +1166,4 @@ class blockZenTest extends baseTest
if(dao::isError()) return dao::getError();
return $result;
}
}
}
@@ -0,0 +1,45 @@
#!/usr/bin/env php
<?php
/**
title=测试 blockZen::printAssignToMeBlock();
timeout=0
cid=15248
- 步骤1:测试方法执行成功返回success为true属性success @1
- 步骤2:测试返回对象包含权限验证信息属性hasViewPriv @1
- 步骤3:测试返回对象包含数据信息属性hasData @1
- 步骤4:测试总数量统计正确属性totalCount @14
- 步骤5:测试不传入block参数时使用默认参数属性success @1
*/
// 1. 导入依赖(路径固定,不可修改)
include dirname(__FILE__, 5) . '/test/lib/init.php';
include dirname(__FILE__, 2) . '/lib/block.unittest.class.php';
// 2. 用户登录(选择合适角色)
su('admin');
// 3. 创建测试实例(变量名与模块名一致)
$blockTest = new blockTest();
// 4. 准备测试数据
$block1 = new stdclass();
$block1->params = new stdclass();
$block1->params->count = 15;
$block2 = new stdclass();
$block2->params = new stdclass();
$block2->params->count = 10;
$block3 = new stdclass();
$block3->params = new stdclass();
$block3->params->count = 5;
// 5. 强制要求:必须包含至少5个测试步骤
r($blockTest->printAssignToMeBlockTest($block1)) && p('success') && e('1'); // 步骤1:测试方法执行成功返回success为true
r($blockTest->printAssignToMeBlockTest($block1)) && p('hasViewPriv') && e('1'); // 步骤2:测试返回对象包含权限验证信息
r($blockTest->printAssignToMeBlockTest($block1)) && p('hasData') && e('1'); // 步骤3:测试返回对象包含数据信息
r($blockTest->printAssignToMeBlockTest($block1)) && p('totalCount') && e('14'); // 步骤4:测试总数量统计正确
r($blockTest->printAssignToMeBlockTest()) && p('success') && e('1'); // 步骤5:测试不传入block参数时使用默认参数
@@ -101,4 +101,4 @@ $block3->width = 2;
$block3->height = 3;
$block3->left = 0;
$block3->top = 0;
r(count($blockTest->processBlockForRenderTest(array($block1, $block2, $block3), 0))) && p() && e('3');
r(count($blockTest->processBlockForRenderTest(array($block1, $block2, $block3), 0))) && p() && e('3');
+1 -1
View File
@@ -160,7 +160,7 @@ else
setID('metriclibModal'),
html($metriclibRemind)
);
};
}
}
}
+8 -6
View File
@@ -11,7 +11,9 @@ declare(strict_types=1);
namespace zin;
$cells = array();
$cells = array();
$hasRisk = helper::hasFeature('risk');
$hasIssue = helper::hasFeature('issue');
foreach($config->block->projectstatistic->items as $module => $items)
{
$cellItems = array();
@@ -129,11 +131,11 @@ panel
$project->status == 'closed' ? $lang->block->projectstatistic->projectClosed : $lang->block->projectstatistic->longTimeProject
)
),
$config->edition != 'open' && $config->edition != 'biz' ? cell
in_array($config->edition, array('max', 'ipd')) && ($hasIssue || $hasRisk) ? cell
(
setClass('flex-1 text-left' . (!$longBlock ? ' w-full' : '')),
icon('bullhorn text-warning'),
span
$hasRisk ? span
(
setClass('text-gray mr-5'),
$lang->block->projectstatistic->existRisks,
@@ -142,8 +144,8 @@ panel
setClass('font-bold text-warning'),
round(zget($project, 'risks', 0), 2)
)
),
span
) : null,
$hasIssue ? span
(
setClass('text-gray'),
$lang->block->projectstatistic->existIssues,
@@ -152,7 +154,7 @@ panel
setClass('font-bold text-warning'),
zget($project, 'issues', 0)
)
)
) : null
) : '',
(!empty($project->executions) && $project->multiple) ? cell
(
+29 -19
View File
@@ -200,10 +200,17 @@ class blockZen extends block
foreach($blocks as $key => $block)
{
/* 将没有开启功能区块过虑。 */
if($block->code == 'waterfallrisk' && !helper::hasFeature('waterfall_risk')) continue;
if($block->code == 'waterfallissue' && !helper::hasFeature('waterfall_issue')) continue;
if($block->code == 'scrumrisk' && !helper::hasFeature('scrum_risk')) continue;
if($block->code == 'scrumissue' && !helper::hasFeature('scrum_issue')) continue;
if(!helper::hasFeature('project_risk') && ($block->code == 'waterfallrisk' || $block->code == 'scrumrisk'))
{
unset($blocks[$key]);
continue;
}
if(!helper::hasFeature('project_issue') && ($block->code == 'waterfallissue' || $block->code == 'scrumissue'))
{
unset($blocks[$key]);
continue;
}
/* 过滤目前已经不存在的区块。 */
if(!isset($this->lang->block->moduleList[$block->code]) && !isset($this->lang->block->modules[$block->module]->availableBlocks[$block->code]))
@@ -2058,9 +2065,9 @@ class blockZen extends block
*
* @param object $block
* @access protected
* @return void
* @return array
*/
protected function printAssignToMeBlock(object $block): void
protected function printAssignToMeBlock(object $block): array
{
$hasIssue = helper::hasFeature('issue');
$hasRisk = helper::hasFeature('risk');
@@ -2098,13 +2105,14 @@ class blockZen extends block
if((common::hasPriv('task', 'view') && $this->config->vision == 'rnd') || (common::hasPriv('researchtask', 'view') && $this->config->vision == 'or')) $hasViewPriv['task'] = true;
if(common::hasPriv('story', 'view') && $this->config->vision != 'lite') $hasViewPriv['story'] = true;
if($this->config->URAndSR && common::hasPriv('story', 'view') && $this->config->vision != 'lite') $hasViewPriv['requirement'] = true;
if(common::hasPriv('bug', 'view') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['bug'] = true;
if(common::hasPriv('testcase', 'view') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['testcase'] = true;
if(common::hasPriv('testtask', 'cases') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['testtask'] = true;
if(common::hasPriv('risk', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasRisk) $hasViewPriv['risk'] = true;
if(common::hasPriv('issue', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasIssue) $hasViewPriv['issue'] = true;
if(common::hasPriv('meeting', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasMeeting) $hasViewPriv['meeting'] = true;
if((common::hasPriv('feedback', 'view') || common::hasPriv('feedback', 'adminView')) && in_array($this->config->edition, array('max', 'biz', 'ipd'))) $hasViewPriv['feedback'] = true;
if(common::hasPriv('bug', 'view') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['bug'] = true;
if(common::hasPriv('testcase', 'view') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['testcase'] = true;
if(common::hasPriv('testtask', 'cases') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['testtask'] = true;
if(common::hasPriv('risk', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasRisk) $hasViewPriv['risk'] = true;
if(common::hasPriv('reviewissue', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasRisk) $hasViewPriv['reviewissue'] = true;
if(common::hasPriv('issue', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasIssue) $hasViewPriv['issue'] = true;
if(common::hasPriv('meeting', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasMeeting) $hasViewPriv['meeting'] = true;
if((common::hasPriv('feedback', 'view') || common::hasPriv('feedback', 'adminView')) && in_array($this->config->edition, array('max', 'biz', 'ipd'))) $hasViewPriv['feedback'] = true;
if(common::hasPriv('ticket', 'view') && in_array($this->config->edition, array('max', 'biz', 'ipd')) && $this->config->vision != 'or') $hasViewPriv['ticket'] = true;
$objectList = array('todo' => 'todos', 'task' => 'tasks', 'bug' => 'bugs', 'story' => 'stories', 'requirement' => 'requirements');
@@ -2112,7 +2120,7 @@ class blockZen extends block
{
if($hasRisk) $objectList += array('risk' => 'risks');
if($hasIssue) $objectList += array('issue' => 'issues');
$objectList += array('feedback' => 'feedbacks', 'ticket' => 'tickets');
$objectList += array('feedback' => 'feedbacks', 'ticket' => 'tickets', 'reviewissue' => 'reviewissues');
if($this->config->edition == 'ipd' && $this->config->vision == 'or') $objectList += array('demand' => 'demands');
}
@@ -2129,7 +2137,7 @@ class blockZen extends block
->beginIF($objectType == 'story' || $objectType == 'requirement')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')->fi()
->beginIF($objectType == 'bug')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')->fi()
->beginIF($objectType == 'task')->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution=t2.id')->fi()
->beginIF($objectType == 'issue' || $objectType == 'risk')->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')->fi()
->beginIF(in_array($objectType, array('issue', 'risk', 'reviewissue')))->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')->fi()
->beginIF($objectType == 'ticket')->leftJoin(TABLE_USER)->alias('t2')->on('t1.openedBy = t2.account')->fi()
->beginIF($objectType == 'demand')->leftJoin(TABLE_DEMANDPOOL)->alias('t2')->on('t1.pool = t2.id')->fi()
->where('t1.deleted')->eq(0)
@@ -2141,7 +2149,7 @@ class blockZen extends block
->beginIF($objectType == 'todo')->andWhere('t1.cycle')->eq(0)->andWhere('t1.status')->eq('wait')->andWhere('t1.vision')->eq($this->config->vision)->fi()
->beginIF($objectType != 'todo')->andWhere('t1.status')->ne('closed')->fi()
->beginIF($objectType == 'feedback')->andWhere('t1.status')->in('wait, noreview')->fi()
->beginIF($objectType == 'issue' || $objectType == 'risk')->andWhere('t2.deleted')->eq(0)->fi()
->beginIF(in_array($objectType, array('issue', 'risk', 'reviewissue')))->andWhere('t2.deleted')->eq(0)->fi()
->beginIF($objectType == 'ticket')->andWhere('t1.status')->in('wait,doing,done')->fi()
->beginIF($objectType == 'demand')->andWhere('t2.deleted')->eq(0)->fi()
->orderBy($orderBy)
@@ -2196,9 +2204,10 @@ class blockZen extends block
}
}
if($objectType == 'bug') $this->app->loadLang('bug');
if($objectType == 'risk') $this->app->loadLang('risk');
if($objectType == 'issue') $this->app->loadLang('issue');
if($objectType == 'bug') $this->app->loadLang('bug');
if($objectType == 'risk') $this->app->loadLang('risk');
if($objectType == 'reviewissue') $this->app->loadLang('reviewissue');
if($objectType == 'issue') $this->app->loadLang('issue');
if($objectType == 'feedback' || $objectType == 'ticket')
{
@@ -2247,6 +2256,7 @@ class blockZen extends block
$this->view->products = $this->dao->select('id, name')->from(TABLE_PRODUCT)->where('deleted')->eq('0')->fetchPairs('id', 'name');
$this->view->projects = $this->dao->select('id, name')->from(TABLE_PROJECT)->where('deleted')->eq('0')->fetchPairs('id', 'name');
$this->view->count = $count;
return $count;
}
/**
+1
View File
@@ -171,6 +171,7 @@ $config->bug->dtable->fieldList['activatedCount']['sortType'] = true;
if($config->edition != 'open')
{
$app->loadLang('custom');
$config->bug->dtable->fieldList['relatedObject']['name'] = 'relatedObject';
$config->bug->dtable->fieldList['relatedObject']['title'] = $lang->custom->relateObject;
$config->bug->dtable->fieldList['relatedObject']['sortType'] = false;
+2 -2
View File
@@ -1,11 +1,11 @@
<?php
declare(strict_types=1);
/**
* The yyy view file of xxx module of ZenTaoPMS.
* The export view file of bug module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Tingting Dai <daitingting@easycorp.ltd>
* @package xxx
* @package bug
* @link https://www.zentao.net
*/
include '../../file/ui/export.html.php';
+1
View File
@@ -675,6 +675,7 @@ class bugZen extends bug
->setDefault('product', $oldBug->product)
->setDefault('deleteFiles', array())
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('resolvedDate', $oldBug->resolvedDate)
->add('lastEditedDate', $now)
->join('openedBuild,mailto,relatedBug,os,browser', ',')
->setIF($formData->data->assignedTo != $oldBug->assignedTo, 'assignedDate', $now)
-1
View File
@@ -133,7 +133,6 @@ $lang->featureswitch = new stdclass();
$lang->importdata = new stdclass();
$lang->systemsetting = new stdclass();
$lang->staffmanage = new stdclass();
$lang->modelconfig = new stdclass();
$lang->featureconfig = new stdclass();
$lang->doctemplate = new stdclass();
$lang->notifysetting = new stdclass();
+1 -1
View File
@@ -123,6 +123,7 @@ $lang->saveSuccess = 'Gespeichert';
$lang->importSuccess = 'Gespeichert';
$lang->fail = 'Fehlgeschlagen';
$lang->addFiles = 'Hinzufügen ';
$lang->delFiles = 'Deleted Files ';
$lang->deleteSuccess = 'Gelöscht';
$lang->deleted = 'Gelöscht';
$lang->files = 'Datei ';
@@ -302,7 +303,6 @@ $lang->featureswitch->common = 'Features On/Off';
$lang->importdata->common = 'Import data';
$lang->systemsetting->common = 'System setting';
$lang->staffmanage->common = 'User management';
$lang->modelconfig->common = 'Pattern setting';
$lang->featureconfig->common = 'Features config';
$lang->doctemplate->common = 'Doc template';
$lang->notifysetting->common = 'Notification';
+1 -1
View File
@@ -123,6 +123,7 @@ $lang->saveSuccess = 'Saved';
$lang->importSuccess = 'Saved';
$lang->fail = 'Fail';
$lang->addFiles = 'Added Files ';
$lang->delFiles = 'Deleted Files ';
$lang->deleteSuccess = 'Deleted Success';
$lang->deleted = 'Deleted';
$lang->files = 'Files ';
@@ -302,7 +303,6 @@ $lang->featureswitch->common = 'Features On/Off';
$lang->importdata->common = 'Import data';
$lang->systemsetting->common = 'System setting';
$lang->staffmanage->common = 'User management';
$lang->modelconfig->common = 'Pattern setting';
$lang->featureconfig->common = 'Features config';
$lang->doctemplate->common = 'Doc template';
$lang->notifysetting->common = 'Notification';
+1 -1
View File
@@ -123,6 +123,7 @@ $lang->saveSuccess = 'Sauvegardé';
$lang->importSuccess = 'Sauvé';
$lang->fail = 'Echec';
$lang->addFiles = 'Fichiers ajoutés ';
$lang->delFiles = 'Deleted Files ';
$lang->deleteSuccess = 'Supprimé';
$lang->deleted = 'Supprimé';
$lang->files = 'Fichiers ';
@@ -302,7 +303,6 @@ $lang->featureswitch->common = 'Features On/Off';
$lang->importdata->common = 'Import data';
$lang->systemsetting->common = 'System setting';
$lang->staffmanage->common = 'User management';
$lang->modelconfig->common = 'Pattern setting';
$lang->featureconfig->common = 'Features config';
$lang->doctemplate->common = 'Doc template';
$lang->notifysetting->common = 'Notification';
+1 -1
View File
@@ -123,6 +123,7 @@ $lang->saveSuccess = '保存成功';
$lang->importSuccess = '导入成功';
$lang->fail = '失败';
$lang->addFiles = '上传了附件 ';
$lang->delFiles = '删除了附件 ';
$lang->deleteSuccess = '删除成功';
$lang->deleted = '已删除';
$lang->files = '附件 ';
@@ -302,7 +303,6 @@ $lang->featureswitch->common = '功能开关';
$lang->importdata->common = '数据导入';
$lang->systemsetting->common = '系统设置';
$lang->staffmanage->common = '人员管理';
$lang->modelconfig->common = '项目模型配置';
$lang->featureconfig->common = '功能配置';
$lang->doctemplate->common = '文档模板';
$lang->notifysetting->common = '通知设置';
+8 -1
View File
@@ -822,7 +822,9 @@ class commonModel extends model
{
$subModule = isset($dropMenuItem['subModule']) ? explode(',', $dropMenuItem['subModule']) : array();
$subExclude = isset($dropMenuItem['exclude']) ? $dropMenuItem['exclude'] : $exclude;
$subAlias = zget($dropMenuItem, 'alias', '');
if($subModule and in_array($currentModule, $subModule) and strpos(",$subExclude,", ",$currentModule-$currentMethod,") === false) $activeMainMenu = true;
if(strpos(",$subAlias,", ",$currentModule-$currentMethod,") !== false) $activeMainMenu = true;
}
if($activeMainMenu) $activeMenu = $dropMenuName;
@@ -1902,11 +1904,16 @@ eof;
/* Check the project is closed. */
$productModuleList = array('story', 'bug', 'testcase', 'case', 'testtask', 'release');
if(!in_array($module, $productModuleList) and !empty($object->project) and is_numeric($object->project) and empty($config->CRProject))
if(!in_array($module, $productModuleList) and !empty($object->project) and is_numeric($object->project) and (empty($config->CRProject) || empty($config->CRExecution)))
{
if(!isset($projectsStatus[$object->project]))
{
$project = $commonModel->loadModel('project')->getByID((int)$object->project);
if($project && $project->status == 'closed')
{
/* 有的表项目和执行都存在project里。 */
return $project->type == 'project' ? !empty($config->CRProject) : !empty($config->CRExecution);
}
$projectsStatus[$object->project] = $project ? $project->status : '';
}
if($projectsStatus[$object->project] == 'closed') return false;
+1 -1
View File
@@ -255,7 +255,7 @@ class cron extends control
{
$roles = array();
$settings = $this->dao->select('*')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('cron')->fetchAll();
$settings = $this->dao->select('*')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('cron')->fetchAll('id', false);
$scheduler = array('execId' => 0, 'lastTime' => '');
$consumerCount = 0;
+3 -2
View File
@@ -45,8 +45,8 @@ class custom extends control
public function set(string $module = 'story', string $field = 'priList', string $lang = '')
{
if(empty($lang)) $lang = $this->app->getClientLang();
if($module == 'user' && $field == 'priList') $field = 'statusList';
if($module == 'block' && $field == 'priList')$field = 'closed';
if($module == 'user' && $field == 'priList') $field = 'statusList';
if($module == 'block' && $field == 'priList') $field = 'closed';
$currentLang = $this->app->getClientLang();
$this->customZen->assignVarsForSet($module, $field, $lang, $currentLang);
@@ -56,6 +56,7 @@ class custom extends control
$this->customZen->setFieldListForSet($module, $field);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if($module == 'baseline' && $field == 'objectList') return $this->sendSuccess(array('load' => true));
if($module == 'reviewcl') return $this->sendSuccess(array('load' => true));
return $this->sendSuccess(array('load' => $this->createLink('custom', 'set', "module=$module&field=$field&lang=" . ($lang == 'all' ? $lang : ''))));
}
+2 -1
View File
@@ -21,7 +21,7 @@ $lang->custom->score = 'Score';
$lang->custom->timezone = 'Timezone';
$lang->custom->scoreReset = 'Reset Score';
$lang->custom->scoreTitle = 'Point Feature';
$lang->custom->productName = $lang->productCommon;
$lang->custom->productName = $lang->productCommon . ' Close Setting';
$lang->custom->convertFactor = 'Convert factor';
$lang->custom->region = 'Interval';
$lang->custom->tips = 'Tips';
@@ -67,6 +67,7 @@ $lang->custom->beginAndEndDateRange = 'The Range Of Begin & End';
$lang->custom->limitTaskDateAction = 'Set start and end date required';
$lang->custom->closeSetting = 'Close Setting';
$lang->custom->gradeRule = 'Allow cross-level segmentation';
$lang->custom->setExecutionClose = 'Execution Close Setting';
$lang->custom->gradeRuleList['cross'] = 'Yes';
$lang->custom->gradeRuleList['stepwise'] = 'No';
+2 -1
View File
@@ -21,7 +21,7 @@ $lang->custom->score = 'Point';
$lang->custom->timezone = 'Timezone';
$lang->custom->scoreReset = 'Reset Points';
$lang->custom->scoreTitle = 'Point Feature';
$lang->custom->productName = $lang->productCommon;
$lang->custom->productName = $lang->productCommon . ' Close Setting';
$lang->custom->convertFactor = 'Convert factor';
$lang->custom->region = 'Interval';
$lang->custom->tips = 'Tips';
@@ -67,6 +67,7 @@ $lang->custom->beginAndEndDateRange = 'The Range Of Begin & End';
$lang->custom->limitTaskDateAction = 'Set start and end date required';
$lang->custom->closeSetting = 'Close Setting';
$lang->custom->gradeRule = 'Allow cross-level segmentation';
$lang->custom->setExecutionClose = 'Execution Close Setting';
$lang->custom->gradeRuleList['cross'] = 'Yes';
$lang->custom->gradeRuleList['stepwise'] = 'No';
+2 -1
View File
@@ -21,7 +21,7 @@ $lang->custom->score = 'Point';
$lang->custom->timezone = 'Timezone';
$lang->custom->scoreReset = 'Réinit Points';
$lang->custom->scoreTitle = 'Fonctionnalité des Points';
$lang->custom->productName = $lang->productCommon;
$lang->custom->productName = $lang->productCommon . ' Close Setting';
$lang->custom->convertFactor = 'Convert factor';
$lang->custom->region = 'Interval';
$lang->custom->tips = 'Tips';
@@ -67,6 +67,7 @@ $lang->custom->beginAndEndDateRange = 'The Range Of Begin & End';
$lang->custom->limitTaskDateAction = 'Set start and end date required';
$lang->custom->closeSetting = 'Close Setting';
$lang->custom->gradeRule = 'Allow cross-level segmentation';
$lang->custom->setExecutionClose = 'Execution Close Setting';
$lang->custom->gradeRuleList['cross'] = 'Yes';
$lang->custom->gradeRuleList['stepwise'] = 'No';
+3 -2
View File
@@ -21,7 +21,7 @@ $lang->custom->score = '积分';
$lang->custom->timezone = '时区';
$lang->custom->scoreReset = '重置积分';
$lang->custom->scoreTitle = '积分功能';
$lang->custom->productName = $lang->productCommon;
$lang->custom->productName = $lang->productCommon . '关闭设置';
$lang->custom->convertFactor = '换算系数';
$lang->custom->region = '区间';
$lang->custom->tips = '提示语';
@@ -67,6 +67,7 @@ $lang->custom->beginAndEndDateRange = '起止日期范围';
$lang->custom->limitTaskDateAction = '设置起止日期必填';
$lang->custom->closeSetting = '关闭设置';
$lang->custom->gradeRule = '是否允许跨层级细分';
$lang->custom->setExecutionClose = '执行关闭设置';
$lang->custom->gradeRuleList['cross'] = '是';
$lang->custom->gradeRuleList['stepwise'] = '否';
@@ -162,7 +163,7 @@ $lang->custom->testcase = new stdClass();
$lang->custom->testcase->fields['required'] = $lang->custom->required;
$lang->custom->testcase->fields['priList'] = '优先级';
$lang->custom->testcase->fields['typeList'] = '类型';
$lang->custom->testcase->fields['stageList'] = '阶段';
$lang->custom->testcase->fields['stageList'] = '适用环节';
$lang->custom->testcase->fields['resultList'] = '执行结果';
$lang->custom->testcase->fields['statusList'] = '状态';
$lang->custom->testcase->fields['review'] = '评审流程';
+1 -2
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env php
<?php
/**
title=测试 customZen::checkInvalidKeys();
@@ -32,4 +31,4 @@ r($customTest->checkInvalidKeysTest('user', 'roleList', 'zh-cn', array('short',
r($customTest->checkInvalidKeysTest('todo', 'typeList', 'zh-cn', array('short', 'verylongtypekey1'))) && p('message') && e('键的长度必须小于15个字符!');
r($customTest->checkInvalidKeysTest('story', 'sourceList', 'zh-cn', array('valid_key', 'verylongsourcekey1234'))) && p('message') && e('键的长度必须小于20个字符!');
r($customTest->checkInvalidKeysTest('story', 'sourceList', 'zh-cn', array('valid_key', 'test123', 'ABC_def'))) && p() && e('1');
r($customTest->checkInvalidKeysTest('story', 'priList', 'zh-cn', array())) && p() && e('1');
r($customTest->checkInvalidKeysTest('story', 'priList', 'zh-cn', array())) && p() && e('1');
+1 -2
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env php
<?php
/**
title=测试 customZen::checkKeysForSet();
@@ -32,4 +31,4 @@ r($customTest->checkKeysForSetTest('story', 'sourceList', 'zh-cn', array('valid_
r($customTest->checkKeysForSetTest('story', 'priList', 'zh-cn', array('1', '2'), array('高', ''), array('0', '0'))) && p('message') && e('值不能为空!');
r($customTest->checkKeysForSetTest('story', 'priList', 'zh-cn', array('1', '256'), array('高', '中'), array('0', '0'))) && p('message') && e('键值应为不大于255的数字');
r($customTest->checkKeysForSetTest('story', 'sourceList', 'zh-cn', array('customer', 'market', 'internal'), array('客户', '市场', '内部'), array('0', '0', '0'))) && p() && e('1');
r($customTest->checkKeysForSetTest('user', 'roleList', 'zh-cn', array('short', 'this_is_too_long_key'), array('角色1', '角色2'), array('0', '0'))) && p('message') && e('键的长度必须小于10个字符!');
r($customTest->checkKeysForSetTest('user', 'roleList', 'zh-cn', array('short', 'this_is_too_long_key'), array('角色1', '角色2'), array('0', '0'))) && p('message') && e('键的长度必须小于10个字符!');

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