Merge branch 'feature/workflow' into main

This commit is contained in:
sunguangming
2024-10-23 03:11:44 +00:00
77 changed files with 698 additions and 281 deletions
+2
View File
@@ -583,6 +583,7 @@ define('TABLE_TRAINRECORDS', '`' . $config->db->prefix . 'trainrecords
define('TABLE_TRIP', '`' . $config->db->prefix . 'trip`');
define('TABLE_WORKESTIMATION', '`' . $config->db->prefix . 'workestimation`');
define('TABLE_WORKFLOW', '`' . $config->db->prefix . 'workflow`');
define('TABLE_WORKFLOWGROUP', '`' . $config->db->prefix . 'workflowgroup`');
define('TABLE_WORKFLOWACTION', '`' . $config->db->prefix . 'workflowaction`');
define('TABLE_WORKFLOWDATASOURCE', '`' . $config->db->prefix . 'workflowdatasource`');
define('TABLE_WORKFLOWFIELD', '`' . $config->db->prefix . 'workflowfield`');
@@ -717,6 +718,7 @@ $config->objectTables['market'] = TABLE_MARKET;
$config->objectTables['marketreport'] = TABLE_MARKETREPORT;
$config->objectTables['marketresearch'] = TABLE_PROJECT;
$config->objectTables['researchstage'] = TABLE_PROJECT;
$config->objectTables['workflowgroup'] = TABLE_WORKFLOWGROUP;
$config->objectTables['productline'] = TABLE_MODULE;
$config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions', 'aiPrompts', 'promptDesign', 'promptExec');
+51
View File
@@ -1,2 +1,53 @@
ALTER TABLE zt_opportunity ADD `desc` mediumtext NULL AFTER `from`;
ALTER TABLE zt_taskteam MODIFY `status` enum('wait','doing','done','cancel','closed') NOT NULL DEFAULT 'wait';
CREATE TABLE IF NOT EXISTS `zt_workflowgroup` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(10) NOT NULL DEFAULT '',
`projectModel` varchar(10) NOT NULL DEFAULT '',
`projectType` varchar(10) NOT NULL DEFAULT '',
`name` varchar(30) NOT NULL DEFAULT '',
`desc` text NULL,
`disabledModules` text NULL,
`status` varchar(10) NOT NULL DEFAULT 'wait',
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
`createdBy` varchar(30) NOT NULL DEFAULT '',
`createdDate` datetime NULL,
`editedBy` varchar(30) NOT NULL DEFAULT '',
`editedDate` datetime NULL,
`deleted` enum('0', '1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE INDEX `type` ON `zt_workflowgroup` (`type`);
ALTER TABLE `zt_workflow` ADD `group` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `zt_workflow` ADD `role` varchar(10) NOT NULL DEFAULT 'custom' AFTER `buildin`;
ALTER TABLE `zt_workflowfield` ADD `group` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `zt_workflowaction` ADD `group` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `zt_workflowlabel` ADD `group` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `zt_workflowlayout` ADD `group` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `id`;
ALTER TABLE `zt_workflowui` ADD `group` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `id`;
UPDATE `zt_workflow` SET `role` = 'buildin' WHERE `buildin` = '1';
ALTER TABLE `zt_workflow` DROP INDEX `unique`;
CREATE UNIQUE INDEX `unique` ON `zt_workflow`(`group`,`app`,`module`,`vision`);
ALTER TABLE `zt_workflowfield` DROP INDEX `unique`;
CREATE UNIQUE INDEX `unique` ON `zt_workflowfield`(`group`,`module`,`field`);
ALTER TABLE `zt_workflowaction` DROP INDEX `unique`;
CREATE UNIQUE INDEX `unique` ON `zt_workflowaction`(`group`,`module`,`action`,`vision`);
ALTER TABLE `zt_workflowlayout` DROP INDEX `unique`;
CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`group`,`module`,`action`,`ui`,`field`,`vision`);
ALTER TABLE `zt_product` ADD `workflowGroup` int(8) NOT NULL DEFAULT '0' AFTER `ticket`;
ALTER TABLE `zt_project` ADD `workflowGroup` int(8) NOT NULL DEFAULT '0' AFTER `hasProduct`;
ALTER TABLE `zt_workflowgroup` ADD `main` enum('0','1') NOT NULL DEFAULT '0' AFTER `vision`;
ALTER TABLE `zt_workflowgroup` ADD `code` varchar(30) NOT NULL DEFAULT '' AFTER `name`;
DELETE FROM `zt_workflowgroup` WHERE `main` = '1';
INSERT INTO `zt_workflowgroup` (`type`, `projectModel`, `projectType`, `name`, `code`, `status`, `vision`, `main`) VALUES
('product', '', 'project', '默认流程', 'productproject', 'normal', 'rnd', '1'),
('project', 'scrum', 'product', '敏捷-产品型默认流程', 'scrumproduct', 'normal', 'rnd', '1'),
('project', 'scrum', 'project', '敏捷-项目型默认流程', 'scrumproject', 'normal', 'rnd', '1'),
('project', 'waterfall', 'product', '瀑布-产品型默认流程', 'waterfallproduct', 'normal', 'rnd', '1'),
('project', 'waterfall', 'project', '瀑布-项目型默认流程', 'waterfallproject', 'normal', 'rnd', '1');
+42 -4
View File
@@ -1379,6 +1379,7 @@ CREATE TABLE IF NOT EXISTS `zt_product` (
`RD` varchar(30) NOT NULL DEFAULT '',
`feedback` varchar(30) NOT NULL DEFAULT '',
`ticket` varchar(30) NOT NULL DEFAULT '',
`workflowGroup` int(8) NOT NULL DEFAULT '0',
`acl` enum('open','private','custom') NOT NULL DEFAULT 'open',
`groups` text NULL,
`whitelist` text NULL,
@@ -1469,6 +1470,7 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
`name` varchar(90) NOT NULL DEFAULT '',
`code` varchar(45) NOT NULL DEFAULT '',
`hasProduct` tinyint(1) unsigned NOT NULL DEFAULT 1,
`workflowGroup` int(8) NOT NULL DEFAULT '0',
`begin` date NULL,
`end` date NULL,
`firstEnd` date DEFAULT NULL,
@@ -13213,6 +13215,7 @@ SELECT `module`, `method`, 16 from `zt_grouppriv` where `group` = 9;
-- DROP TABLE IF EXISTS `zt_workflow`;
CREATE TABLE IF NOT EXISTS `zt_workflow` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`group` mediumint(8) unsigned NOT NULL DEFAULT '0',
`parent` varchar(30) NOT NULL DEFAULT '',
`child` varchar(30) NOT NULL DEFAULT '',
`type` varchar(10) NOT NULL DEFAULT 'flow',
@@ -13230,6 +13233,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflow` (
`css` text NULL,
`order` smallint(5) unsigned NOT NULL DEFAULT '0',
`buildin` tinyint(1) unsigned NOT NULL DEFAULT '0',
`role` varchar(10) NOT NULL DEFAULT 'custom',
`belong` varchar(50) NOT NULL DEFAULT '',
`administrator` text NULL,
`desc` text NULL,
@@ -13243,15 +13247,38 @@ CREATE TABLE IF NOT EXISTS `zt_workflow` (
`editedDate` datetime NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE UNIQUE INDEX `unique` ON `zt_workflow`(`app`,`module`,`vision`);
CREATE UNIQUE INDEX `unique` ON `zt_workflow`(`group`,`app`,`module`,`vision`);
CREATE INDEX `type` ON `zt_workflow` (`type`);
CREATE INDEX `app` ON `zt_workflow` (`app`);
CREATE INDEX `module` ON `zt_workflow` (`module`);
CREATE INDEX `order` ON `zt_workflow` (`order`);
-- DROP TABLE IF EXISTS `zt_workflowgroup`;
CREATE TABLE IF NOT EXISTS `zt_workflowgroup` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(10) NOT NULL DEFAULT '',
`projectModel` varchar(10) NOT NULL DEFAULT '',
`projectType` varchar(10) NOT NULL DEFAULT '',
`name` varchar(30) NOT NULL DEFAULT '',
`code` varchar(30) NOT NULL DEFAULT '',
`desc` text NULL,
`disabledModules` text NULL,
`status` varchar(10) NOT NULL DEFAULT 'wait',
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
`main` enum('0','1') NOT NULL DEFAULT '0',
`createdBy` varchar(30) NOT NULL DEFAULT '',
`createdDate` datetime NULL,
`editedBy` varchar(30) NOT NULL DEFAULT '',
`editedDate` datetime NULL,
`deleted` enum('0', '1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE INDEX `type` ON `zt_workflowgroup` (`type`);
-- DROP TABLE IF EXISTS `zt_workflowaction`;
CREATE TABLE IF NOT EXISTS `zt_workflowaction` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`group` mediumint(8) unsigned NOT NULL DEFAULT '0',
`module` varchar(30) NOT NULL DEFAULT '',
`action` varchar(50) NOT NULL DEFAULT '',
`method` varchar(50) NOT NULL DEFAULT '',
@@ -13284,7 +13311,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowaction` (
`editedDate` datetime NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE UNIQUE INDEX `unique` ON `zt_workflowaction`(`module`,`action`,`vision`);
CREATE UNIQUE INDEX `unique` ON `zt_workflowaction`(`group`,`module`,`action`,`vision`);
CREATE INDEX `module` ON `zt_workflowaction` (`module`);
CREATE INDEX `action` ON `zt_workflowaction` (`action`);
CREATE INDEX `order` ON `zt_workflowaction` (`order`);
@@ -13312,6 +13339,7 @@ CREATE INDEX `type` ON `zt_workflowdatasource` (`type`);
-- DROP TABLE IF EXISTS `zt_workflowfield`;
CREATE TABLE IF NOT EXISTS `zt_workflowfield` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`group` mediumint(8) unsigned NOT NULL DEFAULT '0',
`module` varchar(30) NOT NULL DEFAULT '',
`field` varchar(50) NOT NULL DEFAULT '',
`type` varchar(20) NOT NULL DEFAULT 'varchar',
@@ -13339,7 +13367,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowfield` (
`editedDate` datetime NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE UNIQUE INDEX `unique` ON `zt_workflowfield`(`module`, `field`);
CREATE UNIQUE INDEX `unique` ON `zt_workflowfield`(`group`, `module`, `field`);
CREATE INDEX `module` ON `zt_workflowfield` (`module`);
CREATE INDEX `field` ON `zt_workflowfield` (`field`);
CREATE INDEX `order` ON `zt_workflowfield` (`order`);
@@ -13347,6 +13375,7 @@ CREATE INDEX `order` ON `zt_workflowfield` (`order`);
-- DROP TABLE IF EXISTS `zt_workflowlayout`;
CREATE TABLE IF NOT EXISTS `zt_workflowlayout` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`group` mediumint(8) unsigned NOT NULL DEFAULT '0',
`module` varchar(30) NOT NULL DEFAULT '',
`action` varchar(50) NOT NULL DEFAULT '',
`ui` mediumint(8) NOT NULL DEFAULT 0,
@@ -13362,7 +13391,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowlayout` (
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`module`,`action`,`ui`,`field`,`vision`);
CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`group`,`module`,`action`,`ui`,`field`,`vision`);
CREATE INDEX `module` ON `zt_workflowlayout` (`module`);
CREATE INDEX `action` ON `zt_workflowlayout` (`action`);
CREATE INDEX `order` ON `zt_workflowlayout` (`order`);
@@ -13370,6 +13399,7 @@ CREATE INDEX `order` ON `zt_workflowlayout` (`order`);
-- DROP TABLE IF EXISTS `zt_workflowlabel`;
CREATE TABLE IF NOT EXISTS `zt_workflowlabel` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`group` mediumint(8) unsigned NOT NULL DEFAULT '0',
`module` varchar(30) NOT NULL DEFAULT '',
`action` varchar(30) NOT NULL DEFAULT 'browse',
`code` varchar(30) NOT NULL DEFAULT '',
@@ -13482,6 +13512,7 @@ CREATE INDEX `version` ON `zt_workflowversion` (`version`);
-- DROP TABLE IF EXISTS `zt_workflowui`;
CREATE TABLE IF NOT EXISTS `zt_workflowui` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`group` mediumint(8) unsigned NOT NULL DEFAULT '0',
`module` varchar(30) NOT NULL,
`action` varchar(50) NOT NULL,
`name` varchar(30) NOT NULL,
@@ -13516,6 +13547,13 @@ REPLACE INTO `zt_workflowrule`(`type`, `name`, `rule`, `createdBy`, `createdDate
('system','电话','phone','admin','2020-10-14 14:06:14'),
('system','IP','ip','admin','2020-10-14 14:06:14');
INSERT INTO `zt_workflowgroup` (`type`, `projectModel`, `projectType`, `name`, `code`, `status`, `vision`, `main`) VALUES
('product', '', 'project', '默认流程', 'productproject', 'normal', 'rnd', '1'),
('project', 'scrum', 'product', '敏捷-产品型默认流程', 'scrumproduct', 'normal', 'rnd', '1'),
('project', 'scrum', 'project', '敏捷-项目型默认流程', 'scrumproject', 'normal', 'rnd', '1'),
('project', 'waterfall', 'product', '瀑布-产品型默认流程', 'waterfallproduct', 'normal', 'rnd', '1'),
('project', 'waterfall', 'project', '瀑布-项目型默认流程', 'waterfallproject', 'normal', 'rnd', '1');
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`) VALUES
('system', '产品', 'products', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"product\",\"method\":\"getPairs\",\"methodDesc\":\"Get product pairs.\",\"params\":[{\"name\":\"mode\",\"type\":\"string\",\"desc\":\"\",\"value\":\"all\"}]}', '', '', ''),
('system', '项目', 'projects', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"project\",\"method\":\"getPairsByModel\",\"methodDesc\":\"Get project pairs by model and project.\",\"params\":[{\"name\":\"model\",\"type\":\"string\",\"desc\":\"all|scrum|waterfall\",\"value\":\"all\"},{\"name\":\"programID\",\"type\":\"int\",\"desc\":\"\",\"value\":\"0\"},{\"name\":\"param\",\"type\":\"\",\"desc\":\"\",\"value\":\"\"}]}', '', '', ''),
+20 -15
View File
@@ -525,16 +525,17 @@ class control extends baseControl
if(!empty($this->app->installing) || !empty($this->app->upgrading)) return $fields;
$moduleName = $moduleName ? $moduleName : $this->app->rawModule;
$methodName = $methodName ? $moduleName : $this->app->rawMethod;
$methodName = $methodName ? $methodName : $this->app->rawMethod;
$flow = $this->loadModel('workflow')->getByModule($moduleName);
$groupID = $this->loadModel('workflowgroup')->getGroupIDByData($moduleName, $object);
$flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
if(!$flow) return $fields;
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
if(!$action || $action->extensionType != 'extend') return $fields;
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object);
$fieldList = $this->workflowaction->getFields($flow->module, $action->action, true, null, $uiID);
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID);
return $this->loadModel('flow')->buildFormFields($fields, $fieldList, array(), $object);
}
@@ -556,14 +557,15 @@ class control extends baseControl
$moduleName = $moduleName ? $moduleName : $this->app->rawModule;
$methodName = $methodName ? $methodName : $this->app->rawMethod;
$flow = $this->loadModel('workflow')->getByModule($moduleName);
$groupID = $this->loadModel('workflowgroup')->getGroupIDByData($moduleName, $object);
$flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
if(!$flow) return '';
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
if(!$action || $action->extensionType == 'none') return '';
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, !empty($action->action) ? $action->action: '', $object);
$fieldList = $this->loadModel('workflowaction')->getFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID);
$fieldList = $this->loadModel('workflowaction')->getPageFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID, $groupID);
$html = '';
if(!empty($flow->css)) $html .= "<style>$flow->css</style>";
@@ -599,17 +601,17 @@ class control extends baseControl
$this->loadModel('flow');
$this->loadModel('workflowfield');
$flow = $this->loadModel('workflow')->getByModule($moduleName);
$groupID = $this->loadModel('workflowgroup')->getGroupIDByData($moduleName, $object);
$flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
if(!$flow) return array();
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
if(!$action || $action->extensionType != 'extend') return array();
$uiID = is_object($object) ? $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object) : 0;
$wrapControl = array('textarea', 'richtext', 'file');
$fieldList = $this->workflowaction->getFields($flow->module, $action->action, true, $object, $uiID);
$layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID);
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, $object, $uiID, $groupID);
$layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID, $groupID);
$notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
if($layouts)
@@ -631,8 +633,10 @@ class control extends baseControl
$field->required = $field->readonly || ($notEmptyRule && strpos(",$field->rules,", ",{$notEmptyRule->id},") !== false);
$field->control = $this->flow->buildFormControl($field);
$field->items = $field->options ? array_filter($field->options) : null;
$field->value = !empty($object) ? zget($object, $field->field, '') : '';
$field->value = !empty($object->{$field->field}) ? zget($object, $field->field, '') : '';
$field->width = $field->width != 'auto' ? $field->width : 'full';
if(!$field->value && $field->defaultValue) $field->value = $field->defaultValue;
}
return $fieldList;
@@ -695,9 +699,10 @@ class control extends baseControl
{
if($this->config->edition == 'open') return;
$groupID = $this->loadModel('workflowgroup')->getGroupIDByDataID($this->moduleName, $objectID);
$uiID = $this->loadModel('workflowlayout')->getUIByDataID($this->moduleName, $this->methodName, $objectID);
$fields = $this->loadModel('workflowaction')->getFields($this->moduleName, $this->methodName, true, null, $uiID);
$layouts = $this->loadModel('workflowlayout')->getFields($this->moduleName, $this->methodName, $uiID);
$fields = $this->loadModel('workflowaction')->getPageFields($this->moduleName, $this->methodName, true, null, $uiID, $groupID);
$layouts = $this->loadModel('workflowlayout')->getFields($this->moduleName, $this->methodName, $uiID, $groupID);
$notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
foreach($fields as $field)
{
+4 -3
View File
@@ -394,13 +394,14 @@ class model extends baseModel
$moduleName = $this->app->getModuleName();
$methodName = $this->app->getMethodName();
$action = $this->loadModel('workflowaction')->getByModuleAndAction($moduleName, $methodName);
$groupID = $this->loadModel('workflowgroup')->getGroupIDByDataID($moduleName, $objectID);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($moduleName, $methodName, $groupID);
if(empty($action) or $action->extensionType == 'none') return '';
$this->loadModel('file');
if($this->post->uid) $this->file->updateObjectID($this->post->uid, $objectID, $moduleName);
$uiID = $this->loadModel('workflowlayout')->getUIByDataID($moduleName, $methodName, $objectID);
$fields = $this->workflowaction->getFields($moduleName, $action->action, '', null, $uiID);
$fields = $this->workflowaction->getPageFields($moduleName, $action->action, '', null, $uiID, $groupID);
foreach($fields as $field)
{
if($field->control == 'file' && $field->show && !$field->readonly)
@@ -409,7 +410,7 @@ class model extends baseModel
}
}
$flow = $this->loadModel('workflow')->getByModule($moduleName);
$flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID);
if($flow && $action) return $this->loadModel('workflowhook')->execute($flow, $action, $objectID);
}
+6 -2
View File
@@ -223,8 +223,12 @@ class router extends baseRouter
/* When upgrading from version 12 to the paid version, the workflow table does not exist. */
try
{
$flows = $this->dbQuery('SELECT * FROM ' . TABLE_WORKFLOW . " WHERE `buildin` = 0 AND `vision` = '{$this->config->vision}' AND status = 'normal' AND type = 'flow' AND `navigator` = 'primary'")->fetchAll();
foreach($flows as $flow) $this->lang->mainNav->{$flow->module} = "{$this->lang->navIcons['workflow']} {$flow->name}|{$flow->module}|browse|";
$flows = $this->dbQuery('SELECT * FROM ' . TABLE_WORKFLOW . " WHERE `buildin` = 0 AND `vision` = '{$this->config->vision}' AND status = 'normal' AND type = 'flow'")->fetchAll();
foreach($flows as $flow)
{
if($flow->navigator == 'primary') $this->lang->mainNav->{$flow->module} = "{$this->lang->navIcons['workflow']} {$flow->name}|{$flow->module}|browse|";
if($flow->belong) $this->config->hasDropmenuApps[] = $flow->app; // 带有1.5级导航的应用
}
}
catch(PDOException){}
}
+1 -1
View File
@@ -94,7 +94,7 @@ class fixer extends baseFixer
$action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
if(!$action || $action->extensionType != 'extend') return parent::get($fields);
$fieldList = $app->control->workflowaction->getFields($flow->module, $action->action);
$fieldList = $app->control->workflowaction->getPageFields($flow->module, $action->action);
$layouts = $app->control->loadModel('workflowlayout')->getFields($moduleName, $methodName);
if($layouts)
{
+4 -3
View File
@@ -122,12 +122,13 @@ class form extends fixer
$flow = $app->control->loadModel('workflow')->getByModule($moduleName);
if(!$flow) return $configObject;
$action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName);
$groupID = $app->control->loadModel('workflowgroup')->getGroupIDByDataID($flow->module, $objectID);
$action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID);
if(!$action || $action->extensionType != 'extend') return $configObject;
$uiID = $app->control->loadModel('workflowlayout')->getUIByDataID($flow->module, $action->action, $objectID);
$fieldList = $app->control->workflowaction->getFields($flow->module, $action->action, true, null, $uiID);
$layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID);
$fieldList = $app->control->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID);
$layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID, $groupID);
$notEmptyRule = $app->control->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
if($layouts)
{
+1
View File
@@ -238,6 +238,7 @@ function queryBase(): queryBase {return createWg('queryBase', func_get_args());}
function queryFilterModal(): queryFilterModal {return createWg('queryFilterModal', func_get_args());}
function pivotTable(): pivotTable {return createWg('pivotTable', func_get_args());}
function pivotConfig(): pivotConfig {return createWg('pivotConfig', func_get_args());}
function iconPicker(): iconPicker {return createWg('iconPicker', func_get_args());}
if(is_dir(__DIR__ . DS . 'wg' . DS . 'schedule'))
{
+12 -3
View File
@@ -76,13 +76,22 @@ class dropmenu extends wg
{
list($url, $text, $objectID, $cache, $tab, $module, $method, $extra, $id, $data, $menuID) = $this->prop(array('url', 'text', 'objectID', 'cache', 'tab', 'module', 'method', 'extra', 'id', 'data', 'menuID'));
$app = data('app');
$lang = data('lang');
$app = data('app');
$lang = data('lang');
$config = data('config');
if(empty($menuID)) $menuID = $id . '-menu';
if(empty($tab)) $tab = $app->tab;
if(empty($module)) $module = $app->rawModule;
if(empty($method)) $method = $app->rawMethod;
/* 打印工作流1.5级导航. */
if($config->edition != 'open')
{
$flow = $app->control->loadModel('workflow')->getByModule($module);
if($flow && $flow->buildin == '0' && $flow->belong) $tab = $flow->belong;
}
if(empty($menuID)) $menuID = $id . '-menu';
if(empty($extra)) $extra = '';
if(empty($objectID)) $objectID = data($tab . 'ID');
if(empty($objectID))
+59 -24
View File
@@ -77,19 +77,69 @@ class formPanel extends panel
{
global $app;
$moduleName = $app->getModuleName();
if($moduleName == 'caselib') $moduleName = 'lib';
if($moduleName == 'productplan') $moduleName = 'plan';
if($moduleName == 'flow') return data('data');
if($moduleName == 'caselib') return data('lib');
if($moduleName == 'flow') return data('data');
if($moduleName == 'productplan') return data('plan');
if($moduleName == 'projectrelease') return data('release');
if($moduleName == 'projectbuild') return data('build');
return data($moduleName);
}
protected function getModuleAndMethodForExtend()
{
global $app;
$moduleName = $app->rawModule;
$methodName = $app->rawMethod;
/* 项目发布和项目版本用自己的工作流。 */
if($moduleName == 'projectrelease') $moduleName = 'release';
if($moduleName == 'projectplan') $moduleName = 'productplan';
if($moduleName == 'projectbuild')
{
$moduleName = 'build';
if($methodName == 'browse')
{
$moduleName = 'execution';
$methodName = 'build';
}
}
/* 反馈转化。 */
if($moduleName == 'feedback')
{
if($methodName == 'tostory')
{
$moduleName = 'story';
$methodName = 'create';
}
elseif($methodName == 'touserstory')
{
$moduleName = 'requirement';
$methodName = 'create';
}
elseif($methodName == 'toepic')
{
$moduleName = 'epic';
$methodName = 'create';
}
elseif($methodName == 'toticket')
{
$moduleName = 'ticket';
$methodName = 'create';
}
}
return array($moduleName, $methodName);
}
protected function created()
{
$fields = $this->prop('fields');
if(is_object($fields))
{
global $app;
$fields = $app->control->appendExtendFields($fields, '', '', $this->getData());
list($moduleName, $methodName) = $this->getModuleAndMethodForExtend();
$fields = $app->control->appendExtendFields($fields, $moduleName, $methodName, $this->getData());
$this->setProp('fields', $fields);
}
@@ -181,24 +231,7 @@ class formPanel extends panel
$layout = $this->prop('layout');
if($layout == 'grid') return null;
$moduleName = $app->rawModule;
$methodName = $app->rawMethod;
/* 项目发布和项目版本用自己的工作流。 */
if($moduleName == 'projectrelease') $moduleName = 'release';
if($moduleName == 'projectplan') $moduleName = 'productplan';
if($moduleName == 'projectbuild')
{
if($methodName == 'browse')
{
$moduleName = 'execution';
$methodName = 'build';
}
else
{
$moduleName = 'build';
}
}
list($moduleName, $methodName) = $this->getModuleAndMethodForExtend();
$data = $this->getData();
$fields = $app->control->appendExtendForm('info', $data, $moduleName, $methodName);
@@ -233,8 +266,9 @@ class formPanel extends panel
{
global $app;
list($moduleName, $methodName) = $this->getModuleAndMethodForExtend();
$data = $this->getData();
$fields = $app->control->appendExtendForm('info', $data);
$fields = $app->control->appendExtendForm('info', $data, $moduleName, $methodName);
$formBatchItem = array();
foreach($fields as $field)
@@ -331,12 +365,13 @@ class formPanel extends panel
{
global $app;
list($moduleName, $methodName) = $this->getModuleAndMethodForExtend();
return div
(
setClass('panel-body ' . $this->prop('bodyClass')),
set($this->prop('bodyProps')),
$this->buildContainer($this->buildForm()),
html($app->control->appendExtendCssAndJS('', '', $this->getData()))
html($app->control->appendExtendCssAndJS($moduleName, $methodName, $this->getData()))
);
}
}
+6
View File
@@ -0,0 +1,6 @@
window.selectIcon = function()
{
const icon = $(this).data('icon');
$(this).closest('#iconPicker').find('#iconPreview').html("<i class='icon icon-" + icon + "'></i>");
$(this).closest('#iconPicker').find('#icon').val(icon);
}
+72
View File
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* The iconPicker widget class file of zin module of ZenTaoPMS.
*
* @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(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 Gang Liu <liugang@easycorp.ltd>
* @package zin
* @link http://www.zentao.net
*/
namespace zin;
class iconPicker extends wg
{
protected static array $defineProps = array(
'name?: string="icon"', // 控件名称。
'value?: string="flow"', // 控件默认值。
'items?: array' // 图标列表项。
);
public static function getPageJS(): ?string
{
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
}
protected function buildIcons(): array
{
$icons = [];
$items = $this->prop('items', []);
foreach($items as $icon)
{
$icons[] = button
(
setClass('btn square ghost'),
setData(['icon' => $icon]),
on::click('selectIcon'),
icon($icon)
);
}
return $icons;
}
protected function build()
{
$name = $this->prop('name');
$icon = $this->prop('value');
return div
(
setID('iconPicker'),
button
(
setClass('btn'),
setData(['toggle' => 'dropdown']),
span
(
setID('iconPreview'),
setClass('mr-2'),
icon($icon)
),
icon('angle-down')
),
div
(
setClass('dropdown-menu menu w-64'),
$this->buildIcons()
),
formHidden($name, $icon, setID('icon'))
);
}
}
+2 -1
View File
@@ -22,6 +22,7 @@ class inputGroupAddon extends wg
{
protected function build()
{
return h::span(setClass('input-group-addon'), set($this->props), $this->children());
$class = $this->prop('class');
return h::span(setClass("input-group-addon {$class}"), set($this->props), $this->children());
}
}
+8 -7
View File
@@ -39,12 +39,13 @@ function getCurrentMainNavbarItems()
{
const $elm = $(element);
$a = $elm.find('a');
items.push(
{
name: $a.attr('data-id'),
order: index * 5
}
);
const menuItem = {};
menuItem.name = $a.attr('data-id'),
menuItem.order = index * 5;
if(typeof $elm.data('hidden') != 'undefined') menuItem.hidden = true;
items.push(menuItem);
}
);
return items;
@@ -157,7 +158,7 @@ $(document).on(
onClick: hideDisabled
? null
: () => {
$li.remove();
$li.hide().attr('data-hidden', '1');
saveMainNavbarToServer($item);
}
},
+10 -9
View File
@@ -42,16 +42,17 @@ function getCurrentNavbarItems()
$nav.children().each(
function(index, element)
{
const $elm = $(element)
const $a = $elm.find('a');
items.push(
{
name: $elm.is('.nav-divider') ? 'divider' : ($a.attr('data-id') || $a.attr('id')),
order: index * 5
}
);
const $elm = $(element)
const $a = $elm.find('a');
const menuItem = {};
menuItem.name = $elm.is('.nav-divider') ? 'divider' : ($a.attr('data-id') || $a.attr('id'));
menuItem.order = index * 5;
if(typeof $elm.data('hidden') != 'undefined') menuItem.hidden = true;
items.push(menuItem);
}
);
console.log(items);
return items;
}
@@ -198,7 +199,7 @@ $(document).on(
onClick: hideDisabled
? null
: () => {
$li.remove();
$li.hasClass('nav-divider') ? $li.remove() : $li.hide().attr('data-hidden', '1');
saveNavbarToServer();
}
},
+8 -4
View File
@@ -62,10 +62,14 @@ class actionModel extends model
if(empty($comment)) $comment = '';
$action->comment = fixer::stripDataTags($comment);
if($this->post->uid)
$uid = $this->post->uid;
if(is_string($uid)) $uid = array($uid);
if(!is_array($uid)) $uid = array();
$this->loadModel('file');
foreach($uid as $value)
{
$action = $this->loadModel('file')->processImgURL($action, 'comment', $this->post->uid);
if($autoDelete) $this->file->autoDelete($this->post->uid);
$action = $this->file->processImgURL($action, 'comment', $value);
if($autoDelete) $this->file->autoDelete($value);
}
/* 获取对象的产品项目以及执行。 */
@@ -89,7 +93,7 @@ class actionModel extends model
}
if($hasRecentTable) $this->dao->insert(TABLE_ACTIONRECENT)->data($action)->autoCheck()->exec();
if($this->post->uid) $this->file->updateObjectID($this->post->uid, $objectID, $objectType);
$this->file->updateObjectID($uid, $objectID, $objectType);
$this->loadModel('message')->send(strtolower($objectType), $objectID, $actionType, $actionID, $actor, $extra);
-1
View File
@@ -126,7 +126,6 @@ class actionZen extends action
$tab = '';
$canView = common::hasPriv($module, $methodName);
if($trash->objectType == 'meeting') $tab = $trash->project ? "data-app='project'" : "data-app='my'";
if($module == 'requirement') $module = 'story';
$trash->objectName = $canView ? html::a($this->createLink($module, $methodName, $params), $trash->objectName, '_self', "title='{$trash->objectName}' $tab") : "<span title='$trash->objectName'>$trash->objectName</span>";
}
}
+4
View File
@@ -0,0 +1,4 @@
window.reloadByProduct = function(e)
{
loadPage($.createLink('bug', 'create', 'productID=' + $(e.target).val() + '&branch=0&extra=projectID=' + projectID + ',executionID=' + executionID));
}
+5 -3
View File
@@ -16,8 +16,7 @@ include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php');
$fields = useFields('bug.create');
if(!empty($executionType) && $executionType == 'kanban') $fields->merge('bug.kanban');
$fields->autoLoad('product', array('items' => 'product,module,execution,project,story,task,assignedTo', 'updateOrders' => true))
->autoLoad('branch', 'module,execution,project,story,task,assignedTo')
$fields->autoLoad('branch', 'module,execution,project,story,task,assignedTo')
->autoLoad('module', 'assignedTo,story')
->autoLoad('project', 'project,execution,story,task,assignedTo,injection,identify')
->autoLoad('execution', 'execution,story,task,assignedTo')
@@ -30,6 +29,8 @@ if(!$product->shadow) $fields->fullModeOrders('module,project,execution');
jsVar('bug', $bug);
jsVar('moduleID', $bug->moduleID);
jsVar('methodName', $app->methodName);
jsVar('projectID', isset($projectID) ? $projectID : 0);
jsVar('executionID', isset($executionID) ? $executionID : 0);
jsVar('tab', $this->app->tab);
jsVar('createRelease', $lang->release->create);
jsVar('refresh', $lang->refreshIcon);
@@ -37,7 +38,8 @@ jsVar('projectExecutionPairs', $projectExecutionPairs);
formGridPanel
(
on::change('[name="product"], [name="branch"], [name="project"], [name="execution"]', 'loadBuilds'),
on::change('[name="product"]', 'reloadByProduct'),
on::change('[name="branch"], [name="project"], [name="execution"]', 'loadBuilds'),
set::title($lang->bug->create),
set::fields($fields),
set::loadUrl($loadUrl)
+26 -7
View File
@@ -621,18 +621,37 @@ class commonModel extends model
/* Ensure user has latest rights set. */
$app->user->rights = $app->control->loadModel('user')->authorize($app->user->account);
$menuOrder = array();
$menuOrder = $lang->mainNav->menuOrder;
$hasCustomMenu = false;
if(isset($config->customMenu->nav) && !$useDefault && !commonModel::isTutorialMode())
{
$items = json_decode($config->customMenu->nav);
foreach($items as $item) $menuOrder[$item->order] = $item->name;
$customMenuOrder = array();
$items = json_decode($config->customMenu->nav);
$hiddenItems = array();
foreach($items as $item)
{
if(!empty($item->hidden))
{
$hiddenItems[] = $item->name;
continue;
}
$customMenuOrder[$item->order] = $item->name;
}
$customMenuItems = array_values($customMenuOrder);
foreach($menuOrder as $order => $name)
{
if(in_array($name, $customMenuItems) || in_array($name, $hiddenItems)) continue;
while(isset($customMenuOrder[$order])) $order ++;
$customMenuOrder[$order] = $name;
}
$menuOrder = $customMenuOrder;
$hasCustomMenu = true;
}
else
{
$menuOrder = $lang->mainNav->menuOrder;
}
ksort($menuOrder);
$items = array();
+18
View File
@@ -586,6 +586,24 @@ class custom extends control
$menu = $this->post->menu; // 导航类型,nav(左侧主导航)|$app(顶部一级导航)|$app-home(项目集、项目的首页导航)|$app-$subMenu(顶部二级导航)|admin-$menuKey(后台导航)
$items = $this->post->items; // 导航项
$account = $this->app->user->account;
$oldMenu = isset($this->config->customMenu->{$menu}) ? $this->config->customMenu->{$menu} : '';
/* 之前隐藏的导航若没开启继续保持隐藏。 */
if($oldMenu)
{
$oldMenus = json_decode($oldMenu);
$menus = json_decode($items);
$menuNames = array();
foreach($menus as $key => $item) $menuNames[] = $item->name;
foreach($oldMenus as $key => $item)
{
if(!empty($item->hidden) && !in_array($item->name, $menuNames)) $menus[] = $item;
}
$items = json_encode($menus);
}
if($menu && $items) $this->loadModel('setting')->setItem("$account.common.customMenu.$menu@{$this->config->vision}", $items);
}
+3 -2
View File
@@ -181,7 +181,7 @@ class customModel extends model
*/
public static function setMenuByConfig(object|array $allMenu, string|array $customMenu, string $module = ''): array
{
global $app, $lang, $config;
global $app, $lang;
$tab = $app->tab;
list($customMenuMap, $order) = static::buildCustomMenuMap($allMenu, $customMenu, $module);
@@ -194,6 +194,7 @@ class customModel extends model
}
$menu = static::buildMenuItems($allMenu, $customMenuMap, $module, $order);
ksort($menu, SORT_NUMERIC);
if(!isset($lang->{$tab})) return array();
@@ -365,7 +366,7 @@ class customModel extends model
/* Process menu item's order and hidden attirbute. */
$menuItem = static::buildMenuItem($item, $customMenuMap, $name, $label, $itemLink, $isTutorialMode, $subMenu);
$menuItem->order = (isset($customMenuMap[$name]) && isset($customMenuMap[$name]->order) ? $customMenuMap[$name]->order : $order ++);
if(!empty($customMenuMap) && !isset($customMenuMap[$name])) $menuItem->hidden = true; // 自定义过滤掉的菜单不显示。
if(!empty($customMenuMap) && !empty($customMenuMap[$name]->hidden)) $menuItem->hidden = true; // 自定义过滤掉的菜单不显示。
if(isset($customMenuMap[$name]) && isset($customMenuMap[$name]->divider)) $menuItem->divider = true;
if($app->viewType == 'mhtml' && isset($config->custom->moblieHidden[$menuModuleName]) && in_array($name, $config->custom->moblieHidden[$menuModuleName])) $menuItem->hidden = 1; // Hidden menu by config in mobile.
while(isset($menu[$menuItem->order])) $menuItem->order ++;
+39 -31
View File
@@ -58,7 +58,7 @@ class datatableModel extends model
}
/* 加载工作流字段配置。 */
if($this->config->edition != 'open') $fieldList = $this->appendWorkflowFields($module, $method, $fieldList);
if($this->config->edition != 'open') $fieldList += $this->appendWorkflowFields($module, $method);
return $fieldList;
}
@@ -441,11 +441,10 @@ class datatableModel extends model
*
* @param string $module
* @param string $method
* @param array $fieldList
* @access public
* @return array
*/
public function appendWorkflowFields(string $module, string $method, array $fieldList): array
public function appendWorkflowFields(string $module, string $method): array
{
if(in_array($module, array('epic', 'story', 'requirement')))
{
@@ -475,39 +474,48 @@ class datatableModel extends model
$module = 'release'; // 项目发布加载release-browse的layout配置。
}
$flow = $this->loadModel('workflow')->getByModule($module);
if(empty($flow)) return $fieldList;
if($flow->buildin == 1)
$this->loadModel('workflow');
$this->loadModel('workflowgroup');
$this->loadModel('workflowaction');
if(($this->app->tab == 'project' || $this->app->tab == 'execution') && in_array($module, $this->config->workflowgroup->modules['product']))
{
$action = $this->loadModel('workflowaction')->getByModuleAndAction($module, $method);
if(!$action || (isset($action->extensionType) && $action->extensionType != 'extend')) return $fieldList; // 不扩展不追加字段。
}
$groupIdList = array();
$fields = array();
$projectID = $this->app->tab == 'execution' ? $this->session->execution : $this->session->project;
$products = $this->dao->select('t2.*')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id')
->where('t1.project')->eq((int)$projectID)
->fetchAll('id');
$fields = $this->loadModel('workflowaction')->getFields($module, $method);
if($flow->buildin == 1) return array_merge($fieldList, $this->loadModel('flow')->buildDtableCols($fields));
foreach($fields as $field)
{
if(!$field->show) continue;
$fieldList[$field->field]['name'] = $field->field;
$fieldList[$field->field]['title'] = $field->name;
$fieldList[$field->field]['show'] = true;
$fieldList[$field->field]['width'] = (empty($field->width) || $field->width == 'auto') ? '120' : $field->width;
if($field->field == 'id')
foreach($products as $product) $groupIdList[] = $product->workflowGroup;
foreach(array_unique($groupIdList) as $groupID)
{
$fieldList[$field->field]['fixed'] = 'left';
$fieldList[$field->field]['required'] = true;
}
elseif($field->field == 'actions')
{
$fieldList[$field->field]['fixed'] = 'right';
$fieldList[$field->field]['required'] = true;
$flow = $this->workflow->getByModule($module, false, $groupID);
if(empty($flow)) countinue;
if($flow->buildin)
{
$action = $this->workflowaction->getByModuleAndAction($module, $method, $groupID);
if(!$action || (isset($action->extensionType) && $action->extensionType != 'extend')) continue; // 不扩展不追加字段。
}
$fields += $this->workflowaction->getPageFields($module, $method, true, array(), 0, $groupID);
}
}
else
{
$groupID = $this->workflowgroup->getGroupIDBySession($module);
$flow = $this->workflow->getByModule($module, false, $groupID);
if(empty($flow)) return [];
return $fieldList;
if($flow->buildin)
{
$action = $this->workflowaction->getByModuleAndAction($module, $method, $groupID);
if(!$action || (isset($action->extensionType) && $action->extensionType != 'extend')) return []; // 不扩展不追加字段。
}
$fields = $this->workflowaction->getPageFields($module, $method, true, array(), 0, $groupID);
}
return $this->loadModel('flow')->buildDtableCols($fields, [], [], !$flow->buildin);
}
}
+2
View File
@@ -1006,6 +1006,7 @@ class execution extends control
if($executionID) return $this->executionZen->displayAfterCreated($projectID, $executionID, $planID, $confirm);
$allProjects = $this->project->getPairsByModel('all', 'noclosed,multiple');
$this->loadModel('project')->checkAccess($projectID, $allProjects);
if(empty($projectID)) $projectID = key($allProjects) ? key($allProjects) : 0;
$project = empty($projectID) ? null : $this->loadModel('project')->fetchByID($projectID);
@@ -1253,6 +1254,7 @@ class execution extends control
$allChanges = $this->execution->batchUpdate($postData);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->executeHooks(key($this->post->id));
if(!empty($allChanges))
{
foreach($allChanges as $executionID => $changes)
+6 -9
View File
@@ -56,15 +56,12 @@ $cols = $this->loadModel('datatable')->getSetting('execution');
if($execution->type != 'stage') unset($cols['design']);
$tableData = initTableData($tasks, $cols, $this->task);
$tableData = array_map(
function($task)
{
if(helper::isZeroDate($task->deadline)) $task->deadline = '';
if(helper::isZeroDate($task->estStarted)) $task->estStarted = '';
return $task;
},
$tableData
);
foreach($tableData as $task)
{
$task->status = $this->processStatus('task', $task);
if(helper::isZeroDate($task->deadline)) $task->deadline = '';
if(helper::isZeroDate($task->estStarted)) $task->estStarted = '';
}
if($config->edition == 'ipd')
{
+1 -1
View File
@@ -1576,7 +1576,7 @@ class executionZen extends execution
if($this->config->edition != 'open')
{
$flow = $this->loadModel('workflow')->getByModule($module);
if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . '#app=execution';
if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . "#app=$flow->app";
}
$link = helper::createLink($module, $method, "executionID=%s");
+17 -10
View File
@@ -875,23 +875,30 @@ class fileModel extends model
/**
* Update objectID.
*
* @param string $uid
* @param int $objectID
* @param string $objectType
* @param array|string|bool $uid
* @param int $objectID
* @param string $objectType
* @access public
* @return bool
*/
public function updateObjectID(string|bool $uid, int $objectID, string $objectType): bool
public function updateObjectID(array|string|bool $uid, int $objectID, string $objectType): bool
{
if(empty($uid)) return true;
if(empty($_SESSION['album']['used'][$uid])) return true;
$data = new stdclass();
$data->objectID = $objectID;
$data->objectType = $objectType;
if(!defined('RUN_MODE') || RUN_MODE != 'api') $data->extra = 'editor';
if(is_string($uid)) $uid = array($uid);
if(!is_array($uid)) return true;
$this->dao->update(TABLE_FILE)->data($data)->where('id')->in($_SESSION['album']['used'][$uid])->exec();
foreach($uid as $value)
{
if(empty($_SESSION['album']['used'][$value])) continue;
$data = new stdclass();
$data->objectID = $objectID;
$data->objectType = $objectType;
if(!defined('RUN_MODE') || RUN_MODE != 'api') $data->extra = 'editor';
$this->dao->update(TABLE_FILE)->data($data)->where('id')->in($_SESSION['album']['used'][$value])->exec();
}
return !dao::isError();
}
+1
View File
@@ -426,6 +426,7 @@ $lang->group->package->workflowLabel = 'Workflow Label';
$lang->group->package->workflowReport = 'Workflow Report';
$lang->group->package->workflowDatasource = 'Workflow Datasource';
$lang->group->package->workflowRule = 'Workflow Rule';
$lang->group->package->workflowGroup = 'Workflow Group';
$lang->group->package->workflow = 'Workflow';
$lang->group->package->downloadCode = 'Download Code';
$lang->group->package->dev = 'Dev';
+1
View File
@@ -426,6 +426,7 @@ $lang->group->package->workflowLabel = 'Workflow Label';
$lang->group->package->workflowReport = 'Workflow Report';
$lang->group->package->workflowDatasource = 'Workflow Datasource';
$lang->group->package->workflowRule = 'Workflow Rule';
$lang->group->package->workflowGroup = 'Workflow Group';
$lang->group->package->workflow = 'Workflow';
$lang->group->package->downloadCode = 'Download Code';
$lang->group->package->dev = 'Dev';
+1
View File
@@ -426,6 +426,7 @@ $lang->group->package->workflowLabel = 'Workflow Label';
$lang->group->package->workflowReport = 'Workflow Report';
$lang->group->package->workflowDatasource = 'Workflow Datasource';
$lang->group->package->workflowRule = 'Workflow Rule';
$lang->group->package->workflowGroup = 'Workflow Group';
$lang->group->package->workflow = 'Workflow';
$lang->group->package->downloadCode = 'Download Code';
$lang->group->package->dev = 'Dev';
+1
View File
@@ -426,6 +426,7 @@ $lang->group->package->workflowLabel = '工作流标签';
$lang->group->package->workflowReport = '工作流报表';
$lang->group->package->workflowDatasource = '工作流数据源';
$lang->group->package->workflowRule = '工作流验证规则';
$lang->group->package->workflowGroup = '工作流流程模板';
$lang->group->package->workflow = '工作流';
$lang->group->package->downloadCode = '下载代码';
$lang->group->package->dev = '二次开发';
+17
View File
@@ -2607,6 +2607,23 @@ $config->group->package->workflowRule->privs['workflowrule-edit'] = array('edi
$config->group->package->workflowRule->privs['workflowrule-view'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 20, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowRule->privs['workflowrule-delete'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 25, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup = new stdclass();
$config->group->package->workflowGroup->order = 65;
$config->group->package->workflowGroup->subset = 'workflow';
$config->group->package->workflowGroup->privs = array();
$config->group->package->workflowGroup->privs['workflowgroup-product'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-project'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 10, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-create'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 15, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-edit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 20, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-view'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 25, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-delete'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 30, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-design'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 35, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-release'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 40, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-deactivate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 45, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-setExclusive'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 50, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-activateFlow'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 55, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflowGroup->privs['workflowgroup-deactivateFlow'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 60, 'depend' => array('workflow-browseFlow'), 'recommend' => array());
$config->group->package->workflow = new stdclass();
$config->group->package->workflow->order = 5;
$config->group->package->workflow->subset = 'workflow';
+10 -12
View File
@@ -836,14 +836,15 @@ function getMenuNavData()
const data = [];
const $nav = $('#menuMainNav');
$nav.children().each(function(index, element) {
const $elm = $(element);
data.push(
{
name: $elm.is('.divider') ? 'divider' : $elm.data('app'),
order: index * 5
}
);
const $elm = $(element);
const menuItem = {};
menuItem.name = $elm.is('.divider') ? 'divider' : $elm.data('app');
menuItem.order = index * 5;
if(typeof $elm.data('hidden') != 'undefined') menuItem.hidden = true;
data.push(menuItem);
});
return data;
}
@@ -864,6 +865,7 @@ function restoreMenuNavToServer()
{
const url = $.createLink('custom', 'ajaxRestoreMenu');
$.ajaxSubmit({url, data: {menu: 'nav'}});
top.location.reload();
}
/**
@@ -979,8 +981,6 @@ $(document).on('contextmenu', '#menuMainNav .divider', function(event)
{
text: langData.restore,
onClick: () => {
initAppsMenu(allAppsItems);
refreshMenu();
restoreMenuNavToServer();
}
}
@@ -1070,7 +1070,7 @@ $(document).on('click', '.open-in-app,.show-in-app', function(e)
: () => {
closeApp(code);
const $li = $btn.closest('li');
$li.remove();
$li.hide().attr('data-hidden', '1');
refreshMenu();
saveMenuNavToServer();
},
@@ -1096,8 +1096,6 @@ $(document).on('click', '.open-in-app,.show-in-app', function(e)
{
text: langData.restore,
onClick: () => {
initAppsMenu(allAppsItems);
refreshMenu();
restoreMenuNavToServer();
}
}
+2
View File
@@ -389,6 +389,8 @@ class product extends control
/* Execute hooks. */
$this->executeHooks($productID);
if($this->config->edition != 'open') $this->view->workflowGroups = $this->loadModel('workflowgroup')->getPairs('product');
$this->view->title = $product->name . $this->lang->hyphen . $this->lang->product->view;
$this->view->product = $product;
$this->view->actions = $this->loadModel('action')->getList('product', $productID);
+1 -1
View File
@@ -2030,7 +2030,7 @@ class productModel extends model
if($this->config->edition != 'open')
{
$flow = $this->loadModel('workflow')->getByModule($module);
if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . '#app=product';
if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . "app=$flow->app";
}
if($module == 'execution' && in_array($method, array('bug', 'testcase'))) return helper::createLink($module, $method, "executionID={$params[0]}&productID=%s{$branchParam}");
+1 -1
View File
@@ -25,6 +25,6 @@ if($config->systemMode != 'light')
if(!empty($config->setCode))
{
$fields->field('code')->width('1/4');
$fields->field('code')->width('1/2');
$fields->field('type')->width('1/4');
}
+9 -2
View File
@@ -180,17 +180,24 @@ div
setClass('flex mt-4'),
in_array($this->config->systemMode, array('ALM', 'PLM')) && $product->program ? div
(
setClass('clip w-1/2'),
setClass('clip w-1/3'),
set::title($lang->product->program),
icon('program', setClass('pr-1')),
$product->programName
) : null,
$product->line ? div
(
setClass('clip w-1/2'),
setClass('clip w-1/3'),
set::title($lang->product->line),
icon('lane', setClass('pr-1')),
$product->lineName
) : null,
$config->edition != 'open' && $product->workflowGroup ? div
(
setClass('clip w-1/3'),
set::title($lang->product->workflowGroup),
icon('flow', setClass('pr-1')),
zget($workflowGroups, $product->workflowGroup)
) : null
),
div
+5
View File
@@ -253,6 +253,11 @@ class productZen extends product
if(isset($fields['program'])) $fields['program']['options'] = $this->loadModel('program')->getTopPairs('noclosed');
if(isset($fields['line'])) $fields['line']['options'] = $this->product->getLinePairs($programID, true);
if($this->config->edition != 'open' && isset($fields['workflowGroup']))
{
$fields['workflowGroup']['options'] = $this->loadModel('workflowGroup')->getPairs();
}
return $fields;
}
+4 -1
View File
@@ -23,9 +23,12 @@ class productplan extends control
*/
public function commonAction(int $productID, int $branch = 0)
{
$product = $this->loadModel('product')->getById($productID);
$product = $this->loadModel('product')->getById($productID);
$products = $this->product->getPairs('all', 0, '', 'all');
if(empty($product)) $this->locate($this->createLink('product', 'create'));
$this->product->checkAccess($productID, $products);
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
$this->app->loadConfig('execution');
+1 -1
View File
@@ -360,7 +360,7 @@ class programZen extends program
if($this->config->edition != 'open')
{
$flow = $this->loadModel('workflow')->getByModule($moduleName);
if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=$programID&moduleName=$moduleName") . '#app=program';
if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=$programID&moduleName=$moduleName") . "#app=$flow->app";
}
if($moduleName == 'project')
{
+22
View File
@@ -645,6 +645,8 @@ class project extends control
$this->executeHooks($projectID);
list($userPairs, $userList) = $this->projectZen->buildUsers();
if($this->config->edition != 'open') $this->view->workflowGroups = $this->loadModel('workflowgroup')->getPairs('project', $project->model, $project->hasProduct);
$this->view->title = $this->lang->project->view;
$this->view->projectID = $projectID;
$this->view->project = $project;
@@ -1714,4 +1716,24 @@ class project extends control
$this->view->currentMethod = $currentMethod;
$this->display();
}
/**
* Ajax get workflow group items.
*
* @param string $model
* @param int $hasProduct
* @access public
* @return void
*/
public function ajaxGetWorkflowGroups(string $model, int $hasProduct)
{
if($this->config->edition == 'open') return false;
$workflowGroups = $this->loadModel('workflowgroup')->getPairs('project', $model, $hasProduct);
$items = array();
foreach($workflowGroups as $id => $name) $items[] = array('text' => $name, 'value' => $id);
return $this->send(array('items' => array_values($items), 'defaultValue' => key($workflowGroups)));
}
}
+13 -1
View File
@@ -15,7 +15,8 @@ const ignoreTips = {
*/
function changeType()
{
if($(this).val() == 0)
const hasProduct = $(this).val();
if(hasProduct == 0)
{
if(!$('[name=charter]').length || ($('[name=charter]').length && !parseInt($('[name=charter]').val()))) $('.productsBox').addClass('hidden');
$('.stageByBox').addClass('hidden');
@@ -24,6 +25,17 @@ function changeType()
{
$('.productsBox').removeClass('hidden');
}
const link = $.createLink('project', 'ajaxGetWorkflowGroups', `model=${model}&hasProduct=${hasProduct}`);
$.getJSON(link, function(data)
{
if(data.items)
{
const $workflowGroup = $('[name=workflowGroup]').zui('picker');
$workflowGroup.render({items: data.items});
$workflowGroup.$.setValue(data.defaultValue);
}
})
}
/**
+1 -1
View File
@@ -571,7 +571,7 @@ class projectModel extends model
if($this->config->edition != 'open')
{
$flow = $this->loadModel('workflow')->getByModule($module);
if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . '#app=project';
if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . "#app=$flow->app";
}
if(in_array($module, $this->config->waterfallModules)) return helper::createLink($module, 'browse', "projectID=%s");
+2
View File
@@ -397,6 +397,8 @@ class projectTao extends projectModel
$product->createdVersion = $this->config->version;
$product->vision = zget($project, 'vision', 'rnd');
if($this->config->edition != 'open') $product->workflowGroup = $this->dao->select('id')->from(TABLE_WORKFLOWGROUP)->where('code')->eq('productproject')->fetch('id');
$this->app->loadLang('product');
$this->dao->insert(TABLE_PRODUCT)->data($product)
->check('name', 'notempty')
+1 -1
View File
@@ -67,7 +67,7 @@ foreach($projects as $programID => $programProjects)
$item['keys'] = zget(common::convert2Pinyin(array($project->name)), $project->name, '');
$item['url'] = sprintf($link, $project->id);
if(empty($project->multiple) || $project->type == 'kanban' || $project->model == 'kanban') $item['url'] = helper::createLink('project', 'index', "projectID={$project->id}");
if((empty($project->multiple) || $project->type == 'kanban' || $project->model == 'kanban') && strpos($link, 'ajaxSwitchBelong') === false) $item['url'] = helper::createLink('project', 'index', "projectID={$project->id}");
if(empty($activeGroup) && $projectID == $project->id) $activeGroup = $group;
+1 -1
View File
@@ -21,4 +21,4 @@ if(strpos($config->project->edit->requiredFields, 'budget') === false) $fields->
$fields->field('budget')->value(data('project.budget') !== null && data('project.budget') == 0 ? '' : data('project.budget'));
$fields->field('acl')->control(array('control' => 'aclBox', 'aclItems' => data('project.parent') ? $lang->project->subAclList : $lang->project->aclList, 'aclValue' => data('project.acl'), 'whitelistLabel' => $lang->project->whitelist, 'userValue' => data('project.whitelist')));
$fields->field('storyType')->foldable()->value(data('project.storyType'));
$fields->field('storyType')->width('full')->value(data('project.storyType'));
+12 -1
View File
@@ -273,7 +273,18 @@ row
)
)
),
div(setClass('flex mt-4 program'), div(setClass('clip programBox'), $programDom)),
div
(
setClass('flex mt-4 program'),
$programDom ? div(setClass('clip programBox w-1/2'), $programDom) : null,
$config->edition != 'open' ? div
(
setClass('clip w-1/2'),
set::title($lang->project->workflowGroup),
icon('flow', setClass('pr-1')),
zget($workflowGroups, $project->workflowGroup)
) : null
),
div
(
set::className('detail-content mt-4 overflow-hidden desc-box'),
+3
View File
@@ -327,6 +327,9 @@ class projectZen extends project
}
}
$hasProduct = isset($copyProject->hasProduct) ? $copyProject->hasProduct : 1;
if($this->config->edition != 'open') $this->view->workflowGroups = $this->loadModel('workflowgroup')->getPairs('project', $model, $hasProduct);
/* Get copy projects. */
$copyProjects = $this->project->getPairsByModel($model, '', 0, false);
$copyProjectPairs = !commonModel::isTutorialMode() ? array_combine(array_keys($copyProjects), array_column($copyProjects, 'name')) : $copyProjects;
+3 -2
View File
@@ -96,10 +96,11 @@ class projectrelease extends control
* Create a release.
*
* @param int $projectID
* @param int $productID
* @access public
* @return void
*/
public function create(int $projectID)
public function create(int $projectID, int $productID = 0)
{
/* Set create config. */
$this->config->projectrelease->create = $this->config->release->create;
@@ -136,7 +137,7 @@ class projectrelease extends control
/* Set menu. */
$this->project->setMenu($projectID);
$this->projectreleaseZen->commonAction($projectID);
$this->projectreleaseZen->commonAction($projectID, $productID);
unset($this->lang->release->statusList['fail']);
unset($this->lang->release->statusList['terminate']);
+1
View File
@@ -22,6 +22,7 @@ class projectreleaseZen extends projectrelease
/* 获取当前的产品。*/
/* Get current product. */
if(!$productID) $productID = key($this->products);
$this->loadModel('product')->checkAccess($productID, $this->products);
$product = $this->product->getByID($productID);
$this->view->products = $this->products;
+4 -1
View File
@@ -25,9 +25,12 @@ class release extends control
{
$this->loadModel('product')->setMenu($productID, $branch);
$product = $this->product->getById($productID);
$product = $this->product->getById($productID);
$products = $this->product->getPairs('all', 0, '', 'all');
if(empty($product)) $this->locate($this->createLink('product', 'create'));
$this->product->checkAccess($productID, $products);
$this->view->product = $product;
$this->view->branch = $branch;
$this->view->branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($product->id);
+2 -9
View File
@@ -1,14 +1,7 @@
function loadBuilds(event)
function changeProduct(event)
{
let productID = $(event.target).val();
$.get($.createLink('projectrelease', 'ajaxLoadBuilds', "projectID=" + projectID + "&productID=" + productID), function(data)
{
if(data)
{
data = JSON.parse(data);
$('[name*="build"]').zui('picker').render({items: data});
}
});
loadPage($.createLink('projectrelease', 'create', 'projectID=' + projectID + '&' + 'productID=' + productID));
}
window.changeStatus = function(e)
+1 -1
View File
@@ -15,7 +15,7 @@ jsVar('projectID', isset($projectID) ? $projectID : 0);
$productRow = array();
if(!empty($projectID))
{
$productRow[] = on::change('#product', 'loadBuilds');
$productRow[] = on::change('#product', 'changeProduct');
$productRow[] = formRow
(
setClass($product->shadow ? 'hidden' : ''),
+1 -1
View File
@@ -368,7 +368,7 @@ class story extends control
}
$stories = $this->storyZen->getStoriesByChecked();
if(!$stories) return $this->send(array('result' => 'success', 'load' => $this->session->storyList));
if(!$stories) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->story->batchEditError, 'locate' => $this->session->storyList)));
/* Set Custom*/
foreach(explode(',', $this->config->story->list->customBatchEditFields) as $field) $customFields[$field] = $this->lang->story->$field;
+2 -2
View File
@@ -38,8 +38,8 @@ window.loadProduct = function(e)
const productID = $this.val();
const $modal = $this.closest('.modal');
const inModal = $modal.length > 0;
if(inModal) loadModal($.createLink('story', 'create', 'productID=' + productID + '&' + createParams), $modal.attr('id'));
if(!inModal) loadPage($.createLink('story', 'create', 'productID=' + productID + '&' + createParams));
if(inModal) loadModal($.createLink(storyType, 'create', 'productID=' + productID + '&' + createParams), $modal.attr('id'));
if(!inModal) loadPage($.createLink(storyType, 'create', 'productID=' + productID + '&' + createParams));
};
window.setLane = function(e)
+1
View File
@@ -125,6 +125,7 @@ $lang->story->activateSyncTip = "Twin stories are activated synchronously";
$lang->story->relievedTwinsTip = "After {$lang->productCommon} adjustment, the twin relationship of this story will be automatically removed, and the story will no longer be synchronized. Do you want to save?";
$lang->story->batchEditTip = "{$lang->SRCommon} %sis twin stories, and this operation has been filtered.";
$lang->story->planTip = "{$lang->SRCommon} only supports single selection plan, other requirements can select multiple plans.";
$lang->story->batchEditError = "All selected {$lang->SRCommon} can not be edited.";
$lang->story->id = 'ID';
$lang->story->parent = 'Parent';
+1
View File
@@ -125,6 +125,7 @@ $lang->story->activateSyncTip = "Twin stories are activated synchronously";
$lang->story->relievedTwinsTip = "After {$lang->productCommon} adjustment, the twin relationship of this story will be automatically removed, and the story will no longer be synchronized. Do you want to save?";
$lang->story->batchEditTip = "{$lang->SRCommon} %sis twin stories, and this operation has been filtered.";
$lang->story->planTip = "{$lang->SRCommon} only supports single selection plan, other requirements can select multiple plans.";
$lang->story->batchEditError = "All selected {$lang->SRCommon} can not be edited.";
$lang->story->id = 'ID';
$lang->story->parent = 'Parent';
+1
View File
@@ -125,6 +125,7 @@ $lang->story->activateSyncTip = "Twin stories are activated synchronously";
$lang->story->relievedTwinsTip = "After {$lang->productCommon} adjustment, the twin relationship of this story will be automatically removed, and the story will no longer be synchronized. Do you want to save?";
$lang->story->batchEditTip = "{$lang->SRCommon} %sis twin stories, and this operation has been filtered.";
$lang->story->planTip = "{$lang->SRCommon} only supports single selection plan, other requirements can select multiple plans.";
$lang->story->batchEditError = "All selected {$lang->SRCommon} can not be edited.";
$lang->story->id = 'ID';
$lang->story->parent = 'Parent';
+1
View File
@@ -125,6 +125,7 @@ $lang->story->activateSyncTip = "孪生需求均同步激活";
$lang->story->relievedTwinsTip = "{$lang->productCommon}调整后,本需求自动解除孪生关系,需求不再同步,是否保存?";
$lang->story->batchEditTip = "{$lang->SRCommon} %s为孪生需求,本次操作已被过滤。";
$lang->story->planTip = "{$lang->SRCommon}只能单选计划,其他需求可多选计划。";
$lang->story->batchEditError = "所选需求皆不可编辑,本次操作已被过滤。";
$lang->story->id = '编号';
$lang->story->parent = '父需求';
+1 -1
View File
@@ -186,7 +186,7 @@ if($isInModal) $config->story->actionList['recall']['url'] = str_replace('&from=
if($story->status == 'changing') $config->story->actionList['recall']['text'] = $lang->story->recallChange;
$this->loadModel('repo');
$hasRepo = $this->repo->getListByProduct($story->product, implode(',', $config->repo->gitServiceTypeList), 1);
$actions = $story->deleted ? array() : $this->loadModel('common')->buildOperateMenu($story);
$actions = $story->deleted ? array() : $this->loadModel('common')->buildOperateMenu($story, $story->type);
$hasDivider = !empty($actions['mainActions']) && !empty($actions['suffixActions']);
if(!empty($actions)) $actions = array_merge($actions['mainActions'], $hasDivider ? array(array('type' => 'divider')) : array(), $actions['suffixActions']);
+1
View File
@@ -467,6 +467,7 @@ class storyZen extends story
if($product->type != 'normal') $branches = $this->loadModel('branch')->getPairs($productID, 'active');
}
$this->product->checkAccess($productID, $products);
return array($products, $branches);
}
+10
View File
@@ -64,3 +64,13 @@ window.readScriptContent = function(object)
reader.readAsText(object.file, 'UTF-8');
reader.onload = function(evt){$('[name=script]').val(evt.target.result);}
}
window.loadProduct = function(e)
{
const $this = $(e.target);
const productID = $this.val();
const $modal = $this.closest('.modal');
const inModal = $modal.length > 0;
if(inModal) loadModal($.createLink('testcase', 'create', 'productID=' + productID + '&' + createParams), $modal.attr('id'));
if(!inModal) loadPage($.createLink('testcase', 'create', 'productID=' + productID + '&' + createParams));
};
+4
View File
@@ -13,6 +13,9 @@ namespace zin;
include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php');
$params = $app->getParams();
array_shift($params);
jsVar('createParams', http_build_query($params));
jsVar('tab', $this->app->tab);
if($app->tab == 'execution') jsVar('objectID', $executionID);
if($app->tab == 'project') jsVar('objectID', $projectID);
@@ -34,5 +37,6 @@ formGridPanel
set::loadUrl(helper::createLink('testcase', 'create', "productID={product}&branch={branch}&moduleID={module}")),
!empty($gobackLink) ? set::backUrl($gobackLink) : null,
on::change('#story', 'changeStory'),
on::change('[name=product]', 'loadProduct'),
on::click('#auto', 'checkScript'),
);
+35 -24
View File
@@ -7,64 +7,75 @@ $config->testtask->dtable = new stdclass();
$config->testtask->dtable->fieldList['id']['name'] = 'id';
$config->testtask->dtable->fieldList['id']['title'] = $lang->idAB;
$config->testtask->dtable->fieldList['id']['type'] = 'id';
$config->testtask->dtable->fieldList['id']['show'] = true;
$config->testtask->dtable->fieldList['title']['name'] = 'name';
$config->testtask->dtable->fieldList['title']['title'] = $lang->testtask->name;
$config->testtask->dtable->fieldList['title']['type'] = 'title';
$config->testtask->dtable->fieldList['title']['link'] = array('module' => 'testtask', 'method' => 'cases', 'params' => 'taskID={id}');
$config->testtask->dtable->fieldList['title']['fixed'] = 'left';
$config->testtask->dtable->fieldList['name']['name'] = 'name';
$config->testtask->dtable->fieldList['name']['title'] = $lang->testtask->name;
$config->testtask->dtable->fieldList['name']['type'] = 'title';
$config->testtask->dtable->fieldList['name']['link'] = array('module' => 'testtask', 'method' => 'cases', 'params' => 'taskID={id}');
$config->testtask->dtable->fieldList['name']['fixed'] = 'left';
$config->testtask->dtable->fieldList['name']['show'] = true;
$config->testtask->dtable->fieldList['pri']['name'] = 'pri';
$config->testtask->dtable->fieldList['pri']['title'] = $lang->priAB;
$config->testtask->dtable->fieldList['pri']['type'] = 'pri';
$config->testtask->dtable->fieldList['pri']['show'] = true;
$config->testtask->dtable->fieldList['build']['name'] = 'buildName';
$config->testtask->dtable->fieldList['build']['title'] = $lang->testtask->build;
$config->testtask->dtable->fieldList['build']['type'] = 'text';
$config->testtask->dtable->fieldList['build']['link'] = array('module' => 'build', 'method' => 'view', 'params' => 'buildID={build}');
$config->testtask->dtable->fieldList['build']['data-app'] = 'execution';
$config->testtask->dtable->fieldList['build']['group'] = 'text';
$config->testtask->dtable->fieldList['buildName']['name'] = 'buildName';
$config->testtask->dtable->fieldList['buildName']['title'] = $lang->testtask->build;
$config->testtask->dtable->fieldList['buildName']['type'] = 'text';
$config->testtask->dtable->fieldList['buildName']['link'] = array('module' => 'build', 'method' => 'view', 'params' => 'buildID={build}');
$config->testtask->dtable->fieldList['buildName']['data-app'] = 'execution';
$config->testtask->dtable->fieldList['buildName']['group'] = 'text';
$config->testtask->dtable->fieldList['buildName']['show'] = true;
$config->testtask->dtable->fieldList['product']['name'] = 'productName';
$config->testtask->dtable->fieldList['product']['title'] = $lang->testtask->product;
$config->testtask->dtable->fieldList['product']['type'] = 'text';
$config->testtask->dtable->fieldList['product']['group'] = 'text';
$config->testtask->dtable->fieldList['productName']['name'] = 'productName';
$config->testtask->dtable->fieldList['productName']['title'] = $lang->testtask->product;
$config->testtask->dtable->fieldList['productName']['type'] = 'text';
$config->testtask->dtable->fieldList['productName']['group'] = 'text';
$config->testtask->dtable->fieldList['productName']['show'] = true;
$config->testtask->dtable->fieldList['execution']['name'] = 'executionName';
$config->testtask->dtable->fieldList['execution']['title'] = $lang->testtask->execution;
$config->testtask->dtable->fieldList['execution']['type'] = 'text';
$config->testtask->dtable->fieldList['execution']['group'] = 'text';
$config->testtask->dtable->fieldList['executionName']['name'] = 'executionName';
$config->testtask->dtable->fieldList['executionName']['title'] = $lang->testtask->execution;
$config->testtask->dtable->fieldList['executionName']['type'] = 'text';
$config->testtask->dtable->fieldList['executionName']['group'] = 'text';
$config->testtask->dtable->fieldList['executionName']['show'] = true;
$config->testtask->dtable->fieldList['owner']['name'] = 'owner';
$config->testtask->dtable->fieldList['owner']['title'] = $lang->testtask->owner;
$config->testtask->dtable->fieldList['owner']['type'] = 'user';
$config->testtask->dtable->fieldList['owner']['group'] = 'user';
$config->testtask->dtable->fieldList['owner']['show'] = true;
$config->testtask->dtable->fieldList['members']['name'] = 'members';
$config->testtask->dtable->fieldList['members']['title'] = $lang->testtask->members;
$config->testtask->dtable->fieldList['members']['type'] = 'text';
$config->testtask->dtable->fieldList['members']['group'] = 'user';
$config->testtask->dtable->fieldList['members']['show'] = true;
$config->testtask->dtable->fieldList['begin']['name'] = 'begin';
$config->testtask->dtable->fieldList['begin']['title'] = $lang->testtask->begin;
$config->testtask->dtable->fieldList['begin']['type'] = 'date';
$config->testtask->dtable->fieldList['begin']['group'] = 'user';
$config->testtask->dtable->fieldList['begin']['show'] = true;
$config->testtask->dtable->fieldList['end']['name'] = 'end';
$config->testtask->dtable->fieldList['end']['title'] = $lang->testtask->end;
$config->testtask->dtable->fieldList['end']['type'] = 'date';
$config->testtask->dtable->fieldList['end']['group'] = 'user';
$config->testtask->dtable->fieldList['end']['show'] = true;
$config->testtask->dtable->fieldList['status']['name'] = 'status';
$config->testtask->dtable->fieldList['status']['title'] = $lang->testtask->status;
$config->testtask->dtable->fieldList['status']['type'] = 'status';
$config->testtask->dtable->fieldList['status']['statusMap'] = $lang->testtask->statusList;
$config->testtask->dtable->fieldList['status']['group'] = 'status';
$config->testtask->dtable->fieldList['actions']['name'] = 'actions';
$config->testtask->dtable->fieldList['actions']['title'] = $lang->actions;
$config->testtask->dtable->fieldList['actions']['type'] = 'actions';
$config->testtask->dtable->fieldList['actions']['sortType'] = false;
$config->testtask->dtable->fieldList['actions']['width'] = '120px';
$config->testtask->dtable->fieldList['actions']['list'] = $config->testtask->actionList;
$config->testtask->dtable->fieldList['actions']['menu'] = array('cases', 'linkCase', 'report', 'view', 'edit', 'delete');
@@ -123,12 +134,12 @@ $config->testtask->browseUnits = new stdclass();
$config->testtask->browseUnits->dtable = new stdclass();
$config->testtask->browseUnits->dtable->fieldList['id'] = $config->testtask->dtable->fieldList['id'];
$config->testtask->browseUnits->dtable->fieldList['title'] = $config->testtask->dtable->fieldList['title'];
$config->testtask->browseUnits->dtable->fieldList['title']['title'] = $lang->testtask->unitName;
$config->testtask->browseUnits->dtable->fieldList['title']['link'] = array('module' => 'testtask', 'method' => 'unitCases', 'params' => 'taskID={id}');
$config->testtask->browseUnits->dtable->fieldList['name'] = $config->testtask->dtable->fieldList['name'];
$config->testtask->browseUnits->dtable->fieldList['name']['title'] = $lang->testtask->unitName;
$config->testtask->browseUnits->dtable->fieldList['name']['link'] = array('module' => 'testtask', 'method' => 'unitCases', 'params' => 'taskID={id}');
$config->testtask->browseUnits->dtable->fieldList['execution'] = $config->testtask->dtable->fieldList['execution'];
$config->testtask->browseUnits->dtable->fieldList['build'] = $config->testtask->dtable->fieldList['build'];
$config->testtask->browseUnits->dtable->fieldList['executionName'] = $config->testtask->dtable->fieldList['executionName'];
$config->testtask->browseUnits->dtable->fieldList['buildName'] = $config->testtask->dtable->fieldList['buildName'];
$config->testtask->browseUnits->dtable->fieldList['owner'] = $config->testtask->dtable->fieldList['owner'];
$config->testtask->browseUnits->dtable->fieldList['begin']['name'] = 'begin';
+1 -1
View File
@@ -12,7 +12,7 @@ namespace zin;
modalHeader();
form
formPanel
(
setClass('testtask-block-form'),
formGroup
+5 -5
View File
@@ -55,8 +55,6 @@ featureBar
)
);
if($product->shadow) unset($config->testtask->dtable->fieldList['product']);
$canCreate = common::canModify('product', $product) && common::hasPriv('testtask', 'create');
toolbar
@@ -70,18 +68,20 @@ toolbar
) : null
);
$tasks = initTableData($tasks, $config->testtask->dtable->fieldList, $this->testtask);
$cols = array_values($config->testtask->dtable->fieldList);
$cols = $this->loadModel('datatable')->getSetting('testtask');
$tasks = initTableData($tasks, $cols, $this->testtask);
$data = array_values($tasks);
$footerHTML = strtolower($status) == 'totalstatus' ? $allSummary : $pageSummary;
$beginTime = str_replace('-', '', $beginTime);
$endTime = str_replace('-', '', $endTime);
if($product->shadow) unset($cols['product']);
dtable
(
set::cols($cols),
set::data($data),
set::userMap($users),
set::fixedLeftWidth('20%'),
set::customCols(true),
set::orderBy($orderBy),
set::sortLink(createLink('testtask', 'browse', "productID={$product->id}&branch={$branch}&type={$type}&orderBy={name}_{sortType}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}&beginTime={$beginTime}&endTime={$endTime}")),
set::onRenderCell(jsRaw('window.onRenderCell')),
+1 -1
View File
@@ -12,7 +12,7 @@ namespace zin;
modalHeader();
form
formPanel
(
setClass('testtask-start-form'),
formGroup
+10 -4
View File
@@ -47,11 +47,17 @@ class transfer extends control
/* Get workflow fields by module. */
if($this->config->edition != 'open')
{
$appendFields = $this->transferZen->getWorkflowFieldsByModule($module);
foreach($appendFields as $appendField)
$groupID = $this->loadModel('workflowgroup')->getGroupIDByData($module, null);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($module, 'exportTemplate', $groupID);
if(!empty($action->extensionType) && $action->extensionType == 'extend')
{
$this->lang->$module->{$appendField->field} = $appendField->name;
$this->config->$module->templateFields .= ',' . $appendField->field;
$appendFields = $this->loadModel('workflowaction')->getPageFields($module, 'exportTemplate', true, null, 0, $groupID);
foreach($appendFields as $appendField)
{
$this->lang->$module->{$appendField->field} = $appendField->name;
$this->config->$module->templateFields .= ',' . $appendField->field;
}
}
}
+4 -6
View File
@@ -215,21 +215,19 @@ class transferModel extends model
$moduleName = $this->app->rawModule;
$methodName = $this->app->rawMethod;
$action = $this->dao->select('*')->from(TABLE_WORKFLOWACTION)->where('module')->eq($moduleName)->andWhere('action')->eq($methodName)->fetch();
$groupID = $this->loadModel('workflowgroup')->getGroupIDByData($moduleName, null);
$action = $this->loadModel('workflowaction')->getByModuleAndAction($moduleName, $methodName, $groupID);
if(empty($action)) return $fieldList;
if($action->extensionType == 'none' and $action->buildin == 1) return $fieldList;
$layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName);
$notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
$workflowFields = $this->loadModel('workflowaction')->getFields($moduleName, $methodName);
$notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
$workflowFields = $this->workflowaction->getPageFields($moduleName, $methodName, true, null, 0, $groupID);
foreach($workflowFields as $field)
{
if(empty($fieldList[$field->field])) continue;
if(!empty($field->buildin)) continue;
if(empty($field->show)) continue;
if(!isset($layouts[$field->field])) continue;
if($field->control == 'file')
{
unset($fieldList[$field->field]);
-22
View File
@@ -12,28 +12,6 @@ declare(strict_types=1);
class transferZen extends transfer
{
/**
* 获取工作流字段.
* Get workflow fields by module.
*
* @param string $module
* @access protected
* @return array
*/
protected function getWorkflowFieldsByModule(string $module): array
{
$this->app->loadConfig('workflowaction');
$relatedModule = isset($this->config->workflowaction->buildin->relatedModules[$module]['exporttemplate']) ? $this->config->workflowaction->buildin->relatedModules[$module]['exporttemplate'] : '';
return $this->dao->select('t2.*')->from(TABLE_WORKFLOWLAYOUT)->alias('t1')
->beginIF(!$relatedModule)->leftJoin(TABLE_WORKFLOWFIELD)->alias('t2')->on('t1.module=t2.module AND t1.field=t2.field')->fi()
->beginIF($relatedModule)->leftJoin(TABLE_WORKFLOWFIELD)->alias('t2')->on("t2.module='$relatedModule' AND t1.field=t2.field")->fi()
->where('t1.module')->eq($module)
->andWhere('t1.action')->eq('exporttemplate')
->andWhere('t2.buildin')->eq(0)
->orderBy('t1.order')
->fetchAll();
}
/**
* 将参数转成变量存到SESSION中。
* Set SESSION by params.
+1 -1
View File
@@ -182,7 +182,7 @@ class tree extends control
if($type == 'doc')
{
$docLib = $this->loadModel('doc')->getLibById($module->root);
$docLib = $this->loadModel('doc')->getLibById((int)$module->root);
$objectID = isset($docLib->{$docLib->type}) ? $docLib->{$docLib->type} : 0;
$this->view->libs = $this->doc->getLibs($docLib->type, '', '', $objectID, 'book');
}
+2 -1
View File
@@ -1975,7 +1975,8 @@ class treeModel extends model
if($repeatName)
{
$tips = in_array($self->type, array('doc', 'api')) ? $this->lang->tree->repeatDirName : $this->lang->tree->repeatName;
helper::end(js::alert(sprintf($tips, $repeatName)));
dao::$errors['name'] = sprintf($tips, $repeatName);
return false;
}
if((empty($module->root) || empty($module->name)) && in_array($self->type, array('doc', 'api')))
+1
View File
@@ -104,6 +104,7 @@ $config->upgrade->execFlow['20_4'] = array('functions' => 'createDefaultD
$config->upgrade->execFlow['20_5'] = array('functions' => 'fixWorkflowFieldOptions');
$config->upgrade->execFlow['20_6'] = array('functions' => 'processDemandFiles,processSqlbuilderTables');
$config->upgrade->execFlow['20_7'] = array('functions' => 'upgradeMyDocSpace');
$config->upgrade->execFlow['20_8'] = array('functions' => 'processWorkflowGroups');
if(!empty($config->isINT))
{
+30
View File
@@ -10030,4 +10030,34 @@ class upgradeModel extends model
$this->dao->update(TABLE_DOCLIB)->set('main')->eq(0)->where('id')->in(array_keys($spaces))->exec();
}
}
/**
* 历史产品、项目绑定默认工作流模板。
*
* @access public
* @return void
*/
public function processWorkflowGroups()
{
$workflowGroups = $this->dao->select('code, id')->from(TABLE_WORKFLOWGROUP)->where('main')->eq('1')->fetchPairs();
foreach($workflowGroups as $code => $groupID)
{
if($code == 'productproject')
{
$this->dao->update(TABLE_PRODUCT)->set('workflowGroup')->eq($groupID)->exec();
}
else
{
$this->dao->update(TABLE_PROJECT)
->set('workflowGroup')->eq($groupID)
->where('type')->eq('project')
->beginIF($code == 'scrumproduct')->andWhere('model')->eq('scrum')->andWhere('hasProduct')->eq('1')->fi()
->beginIF($code == 'scrumproject')->andWhere('model')->eq('scrum')->andWhere('hasProduct')->eq('0')->fi()
->beginIF($code == 'waterfallproduct')->andWhere('model')->eq('waterfall')->andWhere('hasProduct')->eq('1')->fi()
->beginIF($code == 'waterfallproject')->andWhere('model')->eq('waterfall')->andWhere('hasProduct')->eq('0')->fi()
->exec();
}
}
}
}
+33 -33
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long