diff --git a/VERSION b/VERSION index 0d68f8a0eb..f6eb05e3c6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -16.0 +16.2 diff --git a/config/config.php b/config/config.php index 340298ab1b..53923cebbd 100644 --- a/config/config.php +++ b/config/config.php @@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}} if(!function_exists('getWebRoot')){function getWebRoot(){}} /* 基本设置。Basic settings. */ -$config->version = '16.0'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it. +$config->version = '16.2'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it. $config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP. $config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time. $config->timezone = 'Asia/Shanghai'; // 时区设置。 The time zone setting, for more see http://www.php.net/manual/en/timezones.php. diff --git a/config/zentaopms.php b/config/zentaopms.php index d10231d254..b7b187ba3a 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -245,6 +245,7 @@ define('TABLE_KANBANCOLUMN', '`' . $config->db->prefix . 'kanbancolumn`'); define('TABLE_KANBANORDER', '`' . $config->db->prefix . 'kanbanorder`'); define('TABLE_KANBANGROUP', '`' . $config->db->prefix . 'kanbangroup`'); define('TABLE_KANBANCARD', '`' . $config->db->prefix . 'kanbancard`'); +define('TABLE_KANBANCELL', '`' . $config->db->prefix . 'kanbancell`'); if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`'); if(!defined('TABLE_PROJECTSPEC')) define('TABLE_PROJECTSPEC', '`' . $config->db->prefix . 'projectspec`'); @@ -296,5 +297,5 @@ $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme'); /* Program privs.*/ $config->programPriv = new stdclass(); -$config->programPriv->scrum = array('projectstory', 'projectrelease', 'project', 'build', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask'); +$config->programPriv->scrum = array('story', 'projectstory', 'projectrelease', 'project', 'build', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask'); $config->programPriv->waterfall = array_merge($config->programPriv->scrum, array('workestimation', 'durationestimation', 'budget', 'programplan', 'review', 'reviewissue', 'weekly', 'cm', 'milestone', 'design', 'issue', 'risk', 'opportunity', 'measrecord', 'auditplan', 'trainplan', 'gapanalysis', 'pssp', 'researchplan', 'researchreport')); diff --git a/db/standard/zentao16.1.sql b/db/standard/zentao16.1.sql new file mode 100644 index 0000000000..82b3456411 --- /dev/null +++ b/db/standard/zentao16.1.sql @@ -0,0 +1,1592 @@ +CREATE TABLE `zt_acl` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `objectType` char(30) NOT NULL, + `objectID` mediumint(9) NOT NULL DEFAULT '0', + `type` char(40) NOT NULL DEFAULT 'whitelist', + `source` char(30) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_action` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT '0', + `product` varchar(255) NOT NULL, + `project` mediumint(8) unsigned NOT NULL, + `execution` mediumint(8) unsigned NOT NULL, + `actor` varchar(100) NOT NULL DEFAULT '', + `action` varchar(30) NOT NULL DEFAULT '', + `date` datetime NOT NULL, + `comment` text NOT NULL, + `extra` text NOT NULL, + `read` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `date` (`date`), + KEY `actor` (`actor`), + KEY `project` (`project`), + KEY `action` (`action`), + KEY `objectID` (`objectID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_api` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `product` varchar(255) NOT NULL DEFAULT '', + `lib` int(10) unsigned NOT NULL DEFAULT '0', + `module` int(10) unsigned NOT NULL DEFAULT '0', + `title` varchar(100) NOT NULL DEFAULT '', + `path` varchar(255) NOT NULL DEFAULT '', + `protocol` varchar(10) NOT NULL DEFAULT '', + `method` varchar(10) NOT NULL DEFAULT '', + `requestType` varchar(100) NOT NULL DEFAULT '', + `responseType` varchar(100) NOT NULL DEFAULT '', + `status` varchar(20) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL DEFAULT '0', + `desc` text, + `version` smallint(5) unsigned NOT NULL DEFAULT '0', + `params` text, + `paramsExample` text, + `responseExample` text, + `response` text, + `commonParams` text, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '0', + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_api_lib_release` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `lib` int(10) unsigned NOT NULL DEFAULT '0', + `desc` varchar(255) NOT NULL DEFAULT '', + `version` varchar(255) NOT NULL DEFAULT '', + `snap` mediumtext NOT NULL, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apispec` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `doc` int(10) unsigned NOT NULL DEFAULT '0', + `module` int(10) unsigned NOT NULL DEFAULT '0', + `title` varchar(100) NOT NULL DEFAULT '', + `path` varchar(255) NOT NULL DEFAULT '', + `protocol` varchar(10) NOT NULL DEFAULT '', + `method` varchar(10) NOT NULL DEFAULT '', + `requestType` varchar(100) NOT NULL DEFAULT '', + `responseType` varchar(100) NOT NULL DEFAULT '', + `status` varchar(20) NOT NULL DEFAULT '', + `owner` varchar(255) NOT NULL DEFAULT '0', + `desc` text, + `version` smallint(5) unsigned NOT NULL DEFAULT '0', + `params` text, + `paramsExample` text, + `responseExample` text, + `response` text, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apistruct` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `lib` int(10) unsigned NOT NULL DEFAULT '0', + `name` varchar(30) NOT NULL DEFAULT '', + `type` varchar(50) NOT NULL DEFAULT '', + `desc` varchar(255) NOT NULL DEFAULT '', + `version` smallint(5) unsigned NOT NULL DEFAULT '0', + `attribute` text, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '0', + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apistruct_spec` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(50) NOT NULL DEFAULT '', + `desc` varchar(255) NOT NULL DEFAULT '', + `attribute` text, + `version` smallint(5) unsigned NOT NULL DEFAULT '0', + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_block` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `module` varchar(20) NOT NULL, + `type` char(30) NOT NULL, + `title` varchar(100) NOT NULL, + `source` varchar(20) NOT NULL, + `block` varchar(20) NOT NULL, + `params` text NOT NULL, + `order` tinyint(3) unsigned NOT NULL DEFAULT '0', + `grid` tinyint(3) unsigned NOT NULL DEFAULT '0', + `height` smallint(5) unsigned NOT NULL DEFAULT '0', + `hidden` tinyint(1) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `account_module_type_order` (`account`,`module`,`type`,`order`), + KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_branch` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `default` enum('0','1') NOT NULL DEFAULT '0', + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `desc` varchar(255) NOT NULL, + `createdDate` date NOT NULL, + `closedDate` date NOT NULL, + `order` smallint(5) unsigned NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_bug` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `plan` mediumint(8) unsigned NOT NULL DEFAULT '0', + `story` mediumint(8) unsigned NOT NULL DEFAULT '0', + `storyVersion` smallint(6) NOT NULL DEFAULT '1', + `task` mediumint(8) unsigned NOT NULL DEFAULT '0', + `toTask` mediumint(8) unsigned NOT NULL DEFAULT '0', + `toStory` mediumint(8) NOT NULL DEFAULT '0', + `title` varchar(255) NOT NULL, + `keywords` varchar(255) NOT NULL, + `severity` tinyint(4) NOT NULL DEFAULT '0', + `pri` tinyint(3) unsigned NOT NULL, + `type` varchar(30) NOT NULL DEFAULT '', + `os` varchar(30) NOT NULL DEFAULT '', + `browser` varchar(30) NOT NULL DEFAULT '', + `hardware` varchar(30) NOT NULL, + `found` varchar(30) NOT NULL DEFAULT '', + `steps` text NOT NULL, + `status` enum('active','resolved','closed') NOT NULL DEFAULT 'active', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL, + `confirmed` tinyint(1) NOT NULL DEFAULT '0', + `activatedCount` smallint(6) NOT NULL, + `activatedDate` datetime NOT NULL, + `feedbackBy` varchar(100) NOT NULL, + `notifyEmail` varchar(100) NOT NULL, + `mailto` text, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `openedBuild` varchar(255) NOT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime NOT NULL, + `deadline` date NOT NULL, + `resolvedBy` varchar(30) NOT NULL DEFAULT '', + `resolution` varchar(30) NOT NULL DEFAULT '', + `resolvedBuild` varchar(30) NOT NULL DEFAULT '', + `resolvedDate` datetime NOT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `duplicateBug` mediumint(8) unsigned NOT NULL, + `linkBug` varchar(255) NOT NULL, + `case` mediumint(8) unsigned NOT NULL, + `caseVersion` smallint(6) NOT NULL DEFAULT '1', + `result` mediumint(8) unsigned NOT NULL, + `repo` mediumint(8) unsigned NOT NULL, + `mr` mediumint(8) unsigned NOT NULL, + `entry` varchar(255) NOT NULL, + `lines` varchar(10) NOT NULL, + `v1` varchar(40) NOT NULL, + `v2` varchar(40) NOT NULL, + `repoType` varchar(30) NOT NULL DEFAULT '', + `testtask` mediumint(8) unsigned NOT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`), + KEY `status` (`status`), + KEY `plan` (`plan`), + KEY `story` (`story`), + KEY `case` (`case`), + KEY `toStory` (`toStory`), + KEY `result` (`result`), + KEY `assignedTo` (`assignedTo`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_build` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` char(150) NOT NULL, + `scmPath` char(255) NOT NULL, + `filePath` char(255) NOT NULL, + `date` date NOT NULL, + `stories` text NOT NULL, + `bugs` text NOT NULL, + `builder` char(30) NOT NULL DEFAULT '', + `desc` text NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_burn` ( + `execution` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `task` mediumint(8) unsigned NOT NULL DEFAULT '0', + `date` date NOT NULL, + `estimate` float NOT NULL, + `left` float NOT NULL, + `consumed` float NOT NULL, + `storyPoint` float NOT NULL, + PRIMARY KEY (`execution`,`date`,`task`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_case` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `lib` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `path` mediumint(8) unsigned NOT NULL DEFAULT '0', + `story` mediumint(30) unsigned NOT NULL DEFAULT '0', + `storyVersion` smallint(6) NOT NULL DEFAULT '1', + `title` varchar(255) NOT NULL, + `precondition` text NOT NULL, + `keywords` varchar(255) NOT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT '3', + `type` char(30) NOT NULL DEFAULT '1', + `auto` varchar(10) NOT NULL DEFAULT 'no', + `frame` varchar(10) NOT NULL, + `stage` varchar(255) NOT NULL, + `howRun` varchar(30) NOT NULL, + `scriptedBy` varchar(30) NOT NULL, + `scriptedDate` date NOT NULL, + `scriptStatus` varchar(30) NOT NULL, + `scriptLocation` varchar(255) NOT NULL, + `status` char(30) NOT NULL DEFAULT '1', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL, + `frequency` enum('1','2','3') NOT NULL DEFAULT '1', + `order` tinyint(30) unsigned NOT NULL DEFAULT '0', + `openedBy` char(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `reviewedBy` varchar(255) NOT NULL, + `reviewedDate` date NOT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `version` tinyint(3) unsigned NOT NULL DEFAULT '0', + `linkCase` varchar(255) NOT NULL, + `fromBug` mediumint(8) unsigned NOT NULL, + `fromCaseID` mediumint(8) unsigned NOT NULL, + `fromCaseVersion` mediumint(8) unsigned NOT NULL DEFAULT '1', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `lastRunner` varchar(30) NOT NULL, + `lastRunDate` datetime NOT NULL, + `lastRunResult` char(30) NOT NULL, + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `story` (`story`), + KEY `fromBug` (`fromBug`), + KEY `module` (`module`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_casestep` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `case` mediumint(8) unsigned NOT NULL DEFAULT '0', + `version` smallint(3) unsigned NOT NULL DEFAULT '0', + `type` varchar(10) NOT NULL DEFAULT 'step', + `desc` text NOT NULL, + `expect` text NOT NULL, + PRIMARY KEY (`id`), + KEY `case` (`case`), + KEY `version` (`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_company` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` char(120) DEFAULT NULL, + `phone` char(20) DEFAULT NULL, + `fax` char(20) DEFAULT NULL, + `address` char(120) DEFAULT NULL, + `zipcode` char(10) DEFAULT NULL, + `website` char(120) DEFAULT NULL, + `backyard` char(120) DEFAULT NULL, + `guest` enum('1','0') NOT NULL DEFAULT '0', + `admins` char(255) DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_compile` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `job` mediumint(8) unsigned NOT NULL, + `queue` mediumint(8) NOT NULL, + `status` varchar(255) NOT NULL, + `logs` text, + `atTime` varchar(10) NOT NULL, + `testtask` mediumint(8) unsigned NOT NULL, + `tag` varchar(255) NOT NULL, + `times` tinyint(3) unsigned NOT NULL DEFAULT '0', + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `updateDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_config` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `owner` char(30) NOT NULL DEFAULT '', + `module` varchar(30) NOT NULL, + `section` char(30) NOT NULL DEFAULT '', + `key` char(30) NOT NULL DEFAULT '', + `value` longtext NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique` (`owner`,`module`,`section`,`key`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_cron` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `m` varchar(20) NOT NULL, + `h` varchar(20) NOT NULL, + `dom` varchar(20) NOT NULL, + `mon` varchar(20) NOT NULL, + `dow` varchar(20) NOT NULL, + `command` text NOT NULL, + `remark` varchar(255) NOT NULL, + `type` varchar(20) NOT NULL, + `buildin` tinyint(1) NOT NULL DEFAULT '0', + `status` varchar(20) NOT NULL, + `lastTime` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `lastTime` (`lastTime`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_dept` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` char(60) NOT NULL, + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT '0', + `order` smallint(4) unsigned NOT NULL DEFAULT '0', + `position` char(30) NOT NULL DEFAULT '', + `function` char(255) NOT NULL DEFAULT '', + `manager` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `parent` (`parent`), + KEY `path` (`path`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_design` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` varchar(255) NOT NULL, + `product` varchar(255) NOT NULL, + `commit` text NOT NULL, + `commitedBy` varchar(30) NOT NULL, + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` varchar(255) NOT NULL, + `status` varchar(30) NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `assignedTo` varchar(30) NOT NULL, + `assignedBy` varchar(30) NOT NULL, + `assignedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `story` char(30) NOT NULL, + `desc` text NOT NULL, + `version` smallint(6) NOT NULL, + `type` char(30) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_designspec` ( + `design` mediumint(8) NOT NULL, + `version` smallint(6) NOT NULL, + `name` varchar(255) NOT NULL, + `desc` text NOT NULL, + `files` varchar(255) NOT NULL, + UNIQUE KEY `design` (`design`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doc` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `execution` mediumint(8) unsigned NOT NULL, + `lib` varchar(30) NOT NULL, + `module` varchar(30) NOT NULL, + `title` varchar(255) NOT NULL, + `keywords` varchar(255) NOT NULL, + `type` varchar(30) NOT NULL, + `views` smallint(5) unsigned NOT NULL, + `draft` longtext NOT NULL, + `collector` text NOT NULL, + `addedBy` varchar(30) NOT NULL, + `addedDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `mailto` text, + `acl` varchar(10) NOT NULL DEFAULT 'open', + `groups` varchar(255) NOT NULL, + `users` text NOT NULL, + `version` smallint(5) unsigned NOT NULL DEFAULT '1', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`), + KEY `lib` (`lib`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doccontent` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `doc` mediumint(8) unsigned NOT NULL, + `title` varchar(255) NOT NULL, + `digest` varchar(255) NOT NULL, + `content` longtext NOT NULL, + `files` text NOT NULL, + `type` varchar(10) NOT NULL, + `version` smallint(5) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `doc_version` (`doc`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doclib` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(30) NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `project` mediumint(8) unsigned NOT NULL, + `execution` mediumint(8) unsigned NOT NULL, + `name` varchar(60) NOT NULL, + `baseUrl` varchar(255) NOT NULL DEFAULT '', + `acl` varchar(10) NOT NULL DEFAULT 'open', + `groups` varchar(255) NOT NULL, + `users` text NOT NULL, + `main` enum('0','1') NOT NULL DEFAULT '0', + `collector` text NOT NULL, + `desc` text NOT NULL, + `order` tinyint(5) unsigned NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_effort` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `user` char(30) NOT NULL DEFAULT '', + `todo` enum('1','0') NOT NULL DEFAULT '1', + `date` date NOT NULL, + `begin` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `end` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `type` enum('1','2','3') NOT NULL DEFAULT '1', + `idvalue` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` char(30) NOT NULL DEFAULT '', + `desc` char(255) NOT NULL DEFAULT '', + `status` enum('1','2','3') NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `user` (`user`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_entry` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `account` varchar(30) NOT NULL DEFAULT '', + `code` varchar(20) NOT NULL, + `key` varchar(32) NOT NULL, + `freePasswd` enum('0','1') NOT NULL DEFAULT '0', + `ip` varchar(100) NOT NULL, + `desc` text NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `calledTime` int(10) unsigned NOT NULL DEFAULT '0', + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_expect` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `userID` mediumint(8) NOT NULL, + `project` mediumint(8) NOT NULL DEFAULT '0', + `expect` text NOT NULL, + `progress` text NOT NULL, + `createdBy` char(30) NOT NULL, + `createdDate` date NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_extension` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(150) NOT NULL, + `code` varchar(30) NOT NULL, + `version` varchar(50) NOT NULL, + `author` varchar(100) NOT NULL, + `desc` text NOT NULL, + `license` text NOT NULL, + `type` varchar(20) NOT NULL DEFAULT 'extension', + `site` varchar(150) NOT NULL, + `zentaoCompatible` varchar(100) NOT NULL, + `installedTime` datetime NOT NULL, + `depends` varchar(100) NOT NULL, + `dirs` mediumtext NOT NULL, + `files` mediumtext NOT NULL, + `status` varchar(20) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`), + KEY `name` (`name`), + KEY `installedTime` (`installedTime`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_file` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `pathname` char(100) NOT NULL, + `title` char(255) NOT NULL, + `extension` char(30) NOT NULL, + `size` int(10) unsigned NOT NULL DEFAULT '0', + `objectType` char(30) NOT NULL, + `objectID` mediumint(9) NOT NULL, + `addedBy` char(30) NOT NULL DEFAULT '', + `addedDate` datetime NOT NULL, + `downloads` mediumint(8) unsigned NOT NULL DEFAULT '0', + `extra` varchar(255) NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `objectType` (`objectType`), + KEY `objectID` (`objectID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_group` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` char(30) NOT NULL, + `role` char(30) NOT NULL DEFAULT '', + `desc` char(255) NOT NULL DEFAULT '', + `acl` text, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_grouppriv` ( + `group` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` char(30) NOT NULL DEFAULT '', + `method` char(30) NOT NULL DEFAULT '', + UNIQUE KEY `group` (`group`,`module`,`method`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_history` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `action` mediumint(8) unsigned NOT NULL DEFAULT '0', + `field` varchar(30) NOT NULL DEFAULT '', + `old` text NOT NULL, + `new` text NOT NULL, + `diff` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `action` (`action`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_holiday` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) NOT NULL DEFAULT '', + `type` enum('holiday','working') NOT NULL DEFAULT 'holiday', + `desc` text NOT NULL, + `year` char(4) NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + PRIMARY KEY (`id`), + KEY `year` (`year`), + KEY `name` (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_job` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `repo` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `frame` varchar(20) NOT NULL, + `engine` varchar(20) NOT NULL, + `server` mediumint(8) unsigned NOT NULL, + `pipeline` varchar(500) NOT NULL, + `triggerType` varchar(255) NOT NULL, + `svnDir` varchar(255) NOT NULL, + `atDay` varchar(255) DEFAULT NULL, + `atTime` varchar(10) DEFAULT NULL, + `customParam` text NOT NULL, + `comment` varchar(255) DEFAULT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `lastExec` datetime DEFAULT NULL, + `lastStatus` varchar(255) DEFAULT NULL, + `lastTag` varchar(255) DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanban` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `space` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `owner` varchar(30) NOT NULL, + `team` text NOT NULL, + `desc` text NOT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `archived` enum('0','1') NOT NULL DEFAULT '0', + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `order` mediumint(8) NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `closedBy` char(30) NOT NULL, + `closedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbancard` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) unsigned NOT NULL, + `region` mediumint(8) unsigned NOT NULL, + `group` mediumint(8) unsigned NOT NULL, + `lane` mediumint(8) unsigned NOT NULL, + `column` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `pri` mediumint(8) unsigned NOT NULL, + `assignedTo` text NOT NULL, + `desc` text NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `estimate` float unsigned NOT NULL, + `color` char(7) NOT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `order` mediumint(8) NOT NULL DEFAULT '0', + `archived` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `archivedBy` char(30) NOT NULL, + `archivedDate` datetime NOT NULL, + `assignedBy` char(30) NOT NULL, + `assignedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbancolumn` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `lane` mediumint(8) NOT NULL DEFAULT '0', + `parent` mediumint(8) NOT NULL DEFAULT '0', + `type` char(30) NOT NULL, + `region` mediumint(8) unsigned NOT NULL, + `group` mediumint(8) NOT NULL DEFAULT '0', + `name` varchar(255) NOT NULL DEFAULT '', + `color` char(30) NOT NULL, + `limit` smallint(6) NOT NULL DEFAULT '-1', + `order` mediumint(8) NOT NULL DEFAULT '0', + `cards` text, + `archived` enum('0','1') NOT NULL DEFAULT '0', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbangroup` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) unsigned NOT NULL, + `region` mediumint(8) unsigned NOT NULL, + `order` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanlane` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `execution` mediumint(8) NOT NULL DEFAULT '0', + `type` char(30) NOT NULL, + `region` mediumint(8) unsigned NOT NULL, + `group` mediumint(8) unsigned NOT NULL, + `groupby` char(30) NOT NULL, + `extra` char(30) NOT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `color` char(30) NOT NULL, + `order` smallint(6) NOT NULL DEFAULT '0', + `lastEditedTime` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanregion` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `space` mediumint(8) unsigned NOT NULL, + `kanban` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `order` mediumint(8) NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanspace` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `owner` varchar(30) NOT NULL, + `team` text NOT NULL, + `desc` text NOT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `order` mediumint(8) NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `closedBy` char(30) NOT NULL, + `closedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_lang` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `lang` varchar(30) NOT NULL, + `module` varchar(30) NOT NULL, + `section` varchar(30) NOT NULL, + `key` varchar(60) NOT NULL, + `value` text NOT NULL, + `system` enum('0','1') NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `lang` (`lang`,`module`,`section`,`key`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_log` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(30) NOT NULL, + `objectID` mediumint(8) unsigned NOT NULL, + `action` mediumint(8) unsigned NOT NULL, + `date` datetime NOT NULL, + `url` varchar(255) NOT NULL, + `contentType` varchar(30) NOT NULL, + `data` text NOT NULL, + `result` text NOT NULL, + PRIMARY KEY (`id`), + KEY `objectType` (`objectType`), + KEY `obejctID` (`objectID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_module` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `root` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` char(60) NOT NULL DEFAULT '', + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT '0', + `order` smallint(5) unsigned NOT NULL DEFAULT '0', + `type` char(30) NOT NULL, + `owner` varchar(30) NOT NULL, + `collector` text NOT NULL, + `short` varchar(30) NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `root` (`root`), + KEY `type` (`type`), + KEY `path` (`path`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_mr` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `gitlabID` mediumint(8) unsigned NOT NULL, + `sourceProject` int(10) unsigned NOT NULL, + `sourceBranch` varchar(100) NOT NULL, + `targetProject` int(10) unsigned NOT NULL, + `targetBranch` varchar(100) NOT NULL, + `mriid` int(10) unsigned NOT NULL, + `title` varchar(255) NOT NULL, + `description` text NOT NULL, + `assignee` varchar(255) NOT NULL, + `reviewer` varchar(255) NOT NULL, + `approver` varchar(255) NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `status` char(30) NOT NULL, + `mergeStatus` char(30) NOT NULL, + `approvalStatus` char(30) NOT NULL, + `needApproved` enum('0','1') NOT NULL DEFAULT '0', + `needCI` enum('0','1') NOT NULL DEFAULT '0', + `repoID` mediumint(8) unsigned NOT NULL, + `jobID` mediumint(8) unsigned NOT NULL, + `compileID` mediumint(8) unsigned NOT NULL, + `compileStatus` char(30) NOT NULL, + `removeSourceBranch` enum('0','1') NOT NULL DEFAULT '0', + `synced` enum('0','1') NOT NULL DEFAULT '1', + `syncError` varchar(255) NOT NULL, + `hasNoConflict` enum('0','1') NOT NULL DEFAULT '0', + `diffs` longtext, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_mrapproval` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `mrID` mediumint(8) unsigned NOT NULL, + `account` varchar(255) NOT NULL, + `date` datetime NOT NULL, + `action` char(30) NOT NULL, + `comment` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_notify` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(50) NOT NULL, + `objectID` mediumint(8) unsigned NOT NULL, + `action` mediumint(9) NOT NULL, + `toList` varchar(255) NOT NULL, + `ccList` text NOT NULL, + `subject` varchar(255) NOT NULL, + `data` text NOT NULL, + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `sendTime` datetime NOT NULL, + `status` varchar(10) NOT NULL DEFAULT 'wait', + `failReason` text NOT NULL, + PRIMARY KEY (`id`), + KEY `objectType_toList_status` (`objectType`,`toList`,`status`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_oauth` ( + `account` varchar(30) NOT NULL, + `openID` varchar(255) NOT NULL, + `providerType` varchar(30) NOT NULL, + `providerID` mediumint(8) unsigned NOT NULL, + KEY `account` (`account`), + KEY `providerType` (`providerType`), + KEY `providerID` (`providerID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_pipeline` ( + `id` smallint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` char(30) NOT NULL, + `name` varchar(50) NOT NULL, + `url` varchar(255) DEFAULT NULL, + `account` varchar(30) DEFAULT NULL, + `password` varchar(255) NOT NULL, + `token` varchar(255) DEFAULT NULL, + `private` char(32) DEFAULT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_planstory` ( + `plan` mediumint(8) unsigned NOT NULL, + `story` mediumint(8) unsigned NOT NULL, + `order` mediumint(9) NOT NULL, + UNIQUE KEY `plan_story` (`plan`,`story`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_product` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `program` mediumint(8) unsigned NOT NULL, + `name` varchar(90) NOT NULL, + `code` varchar(45) NOT NULL, + `bind` enum('0','1') NOT NULL DEFAULT '0', + `line` mediumint(8) NOT NULL, + `type` varchar(30) NOT NULL DEFAULT 'normal', + `status` varchar(30) NOT NULL DEFAULT '', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `desc` text NOT NULL, + `PO` varchar(30) NOT NULL, + `QD` varchar(30) NOT NULL, + `RD` varchar(30) NOT NULL, + `acl` enum('open','private','custom') NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `reviewer` text NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `createdVersion` varchar(20) NOT NULL, + `order` mediumint(8) unsigned NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `acl` (`acl`), + KEY `order` (`order`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_productplan` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL, + `parent` mediumint(9) NOT NULL DEFAULT '0', + `title` varchar(90) NOT NULL, + `status` enum('wait','doing','done','closed') NOT NULL DEFAULT 'wait', + `desc` text NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `order` text NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `end` (`end`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_project` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) NOT NULL DEFAULT '0', + `model` char(30) NOT NULL, + `type` char(30) NOT NULL DEFAULT 'sprint', + `lifetime` char(30) NOT NULL DEFAULT '', + `budget` varchar(30) NOT NULL DEFAULT '0', + `budgetUnit` char(30) NOT NULL DEFAULT 'CNY', + `attribute` varchar(30) NOT NULL DEFAULT '', + `percent` float unsigned NOT NULL DEFAULT '0', + `milestone` enum('0','1') NOT NULL DEFAULT '0', + `output` text NOT NULL, + `auth` char(30) NOT NULL, + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `path` varchar(255) NOT NULL, + `grade` tinyint(3) unsigned NOT NULL, + `name` varchar(90) NOT NULL, + `code` varchar(45) NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `realBegan` date NOT NULL, + `realEnd` date NOT NULL, + `days` smallint(5) unsigned NOT NULL, + `status` varchar(10) NOT NULL, + `subStatus` varchar(30) NOT NULL DEFAULT '', + `pri` enum('1','2','3','4') NOT NULL DEFAULT '1', + `desc` text NOT NULL, + `version` smallint(6) NOT NULL, + `parentVersion` smallint(6) NOT NULL, + `planDuration` int(11) NOT NULL, + `realDuration` int(11) NOT NULL, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `openedVersion` varchar(20) NOT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `canceledBy` varchar(30) NOT NULL DEFAULT '', + `canceledDate` datetime NOT NULL, + `suspendedDate` date NOT NULL, + `PO` varchar(30) NOT NULL DEFAULT '', + `PM` varchar(30) NOT NULL DEFAULT '', + `QD` varchar(30) NOT NULL DEFAULT '', + `RD` varchar(30) NOT NULL DEFAULT '', + `team` varchar(90) NOT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `order` mediumint(8) unsigned NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `parent` (`parent`), + KEY `begin` (`begin`), + KEY `end` (`end`), + KEY `status` (`status`), + KEY `acl` (`acl`), + KEY `order` (`order`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectcase` ( + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `case` mediumint(8) unsigned NOT NULL DEFAULT '0', + `count` mediumint(8) unsigned NOT NULL DEFAULT '1', + `version` smallint(6) NOT NULL DEFAULT '1', + `order` smallint(6) unsigned NOT NULL, + UNIQUE KEY `project` (`project`,`case`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectproduct` ( + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL, + `plan` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`project`,`product`,`branch`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectspec` ( + `project` mediumint(8) NOT NULL, + `version` smallint(6) NOT NULL, + `name` varchar(255) NOT NULL, + `milestone` enum('0','1') NOT NULL DEFAULT '0', + `begin` date NOT NULL, + `end` date NOT NULL, + UNIQUE KEY `project` (`project`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectstory` ( + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `product` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL, + `story` mediumint(8) unsigned NOT NULL DEFAULT '0', + `version` smallint(6) NOT NULL DEFAULT '1', + `order` smallint(6) unsigned NOT NULL, + UNIQUE KEY `project` (`project`,`story`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_relation` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) NOT NULL, + `product` mediumint(8) NOT NULL, + `execution` mediumint(8) NOT NULL, + `AType` char(30) NOT NULL, + `AID` mediumint(8) NOT NULL, + `AVersion` char(30) NOT NULL, + `relation` char(30) NOT NULL, + `BType` char(30) NOT NULL, + `BID` mediumint(8) NOT NULL, + `BVersion` char(30) NOT NULL, + `extra` char(30) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `relation` (`product`,`relation`,`AType`,`BType`,`AID`,`BID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_release` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `build` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `marker` enum('0','1') NOT NULL DEFAULT '0', + `date` date NOT NULL, + `stories` text NOT NULL, + `bugs` text NOT NULL, + `leftBugs` text NOT NULL, + `desc` text NOT NULL, + `mailto` text, + `notify` varchar(255) DEFAULT NULL, + `status` varchar(20) NOT NULL DEFAULT 'normal', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `build` (`build`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repo` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `product` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `path` varchar(255) NOT NULL, + `prefix` varchar(100) NOT NULL, + `encoding` varchar(20) NOT NULL, + `SCM` varchar(10) NOT NULL, + `client` varchar(100) NOT NULL, + `commits` mediumint(8) unsigned NOT NULL, + `account` varchar(30) NOT NULL, + `password` varchar(30) NOT NULL, + `encrypt` varchar(30) NOT NULL DEFAULT 'plain', + `acl` text NOT NULL, + `synced` tinyint(1) NOT NULL DEFAULT '0', + `lastSync` datetime NOT NULL, + `desc` text NOT NULL, + `extra` char(30) NOT NULL, + `preMerge` enum('0','1') NOT NULL DEFAULT '0', + `job` mediumint(8) unsigned NOT NULL, + `fileServerUrl` text, + `fileServerAccount` varchar(40) NOT NULL DEFAULT '', + `fileServerPassword` varchar(100) NOT NULL DEFAULT '', + `deleted` tinyint(1) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repobranch` ( + `repo` mediumint(8) unsigned NOT NULL, + `revision` mediumint(8) unsigned NOT NULL, + `branch` varchar(255) NOT NULL, + UNIQUE KEY `repo_revision_branch` (`repo`,`revision`,`branch`), + KEY `branch` (`branch`), + KEY `revision` (`revision`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repofiles` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `repo` mediumint(8) unsigned NOT NULL, + `revision` mediumint(8) unsigned NOT NULL, + `path` varchar(255) NOT NULL, + `parent` varchar(255) NOT NULL, + `type` varchar(20) NOT NULL, + `action` char(1) NOT NULL, + PRIMARY KEY (`id`), + KEY `path` (`path`), + KEY `parent` (`parent`), + KEY `repo` (`repo`), + KEY `revision` (`revision`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repohistory` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `repo` mediumint(9) NOT NULL, + `revision` varchar(40) NOT NULL, + `commit` mediumint(8) unsigned NOT NULL, + `comment` text NOT NULL, + `committer` varchar(100) NOT NULL, + `time` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `repo` (`repo`), + KEY `revision` (`revision`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_score` ( + `id` bigint(12) unsigned NOT NULL AUTO_INCREMENT, + `account` varchar(30) NOT NULL, + `module` varchar(30) NOT NULL DEFAULT '', + `method` varchar(30) NOT NULL, + `desc` varchar(250) NOT NULL DEFAULT '', + `before` int(11) NOT NULL DEFAULT '0', + `score` int(11) NOT NULL DEFAULT '0', + `after` int(11) NOT NULL DEFAULT '0', + `time` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `model` (`module`), + KEY `method` (`method`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_searchdict` ( + `key` smallint(5) unsigned NOT NULL, + `value` char(3) NOT NULL, + PRIMARY KEY (`key`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_searchindex` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `objectType` char(20) NOT NULL, + `objectID` mediumint(9) NOT NULL, + `title` text NOT NULL, + `content` text NOT NULL, + `addedDate` datetime NOT NULL, + `editedDate` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `object` (`objectType`,`objectID`), + KEY `addedDate` (`addedDate`), + FULLTEXT KEY `content` (`content`), + FULLTEXT KEY `title` (`title`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_stage` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `percent` varchar(255) NOT NULL, + `type` varchar(255) NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_stakeholder` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `objectID` mediumint(8) NOT NULL, + `objectType` char(30) NOT NULL, + `user` char(30) NOT NULL, + `type` char(30) NOT NULL, + `key` enum('0','1') NOT NULL, + `from` char(30) NOT NULL, + `createdBy` char(30) NOT NULL, + `createdDate` date NOT NULL, + `editedBy` char(30) NOT NULL, + `editedDate` date NOT NULL, + `deleted` enum('0','1') NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_story` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `parent` mediumint(9) NOT NULL DEFAULT '0', + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `plan` text, + `source` varchar(20) NOT NULL, + `sourceNote` varchar(255) NOT NULL, + `fromBug` mediumint(8) unsigned NOT NULL DEFAULT '0', + `title` varchar(255) NOT NULL, + `keywords` varchar(255) NOT NULL, + `type` varchar(30) NOT NULL DEFAULT 'story', + `category` varchar(30) NOT NULL DEFAULT 'feature', + `pri` tinyint(3) unsigned NOT NULL DEFAULT '3', + `estimate` float unsigned NOT NULL, + `status` enum('','changed','active','draft','closed') NOT NULL DEFAULT '', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL, + `stage` enum('','wait','planned','projected','developing','developed','testing','tested','verified','released','closed') NOT NULL DEFAULT 'wait', + `stagedBy` char(30) NOT NULL, + `mailto` text, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime NOT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `reviewedBy` varchar(255) NOT NULL, + `reviewedDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `closedReason` varchar(30) NOT NULL, + `toBug` mediumint(8) unsigned NOT NULL, + `childStories` varchar(255) NOT NULL, + `linkStories` varchar(255) NOT NULL, + `duplicateStory` mediumint(8) unsigned NOT NULL, + `version` smallint(6) NOT NULL DEFAULT '1', + `feedbackBy` varchar(100) NOT NULL, + `notifyEmail` varchar(100) NOT NULL, + `URChanged` enum('0','1') NOT NULL DEFAULT '0', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `status` (`status`), + KEY `assignedTo` (`assignedTo`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyestimate` ( + `story` mediumint(9) NOT NULL, + `round` smallint(6) NOT NULL, + `estimate` text NOT NULL, + `average` float NOT NULL, + `openedBy` varchar(30) NOT NULL, + `openedDate` datetime NOT NULL, + UNIQUE KEY `story` (`story`,`round`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyreview` ( + `story` mediumint(9) NOT NULL, + `version` smallint(6) NOT NULL, + `reviewer` varchar(30) NOT NULL, + `result` varchar(30) NOT NULL, + `reviewDate` datetime NOT NULL, + UNIQUE KEY `story` (`story`,`version`,`reviewer`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyspec` ( + `story` mediumint(9) NOT NULL, + `version` smallint(6) NOT NULL, + `title` varchar(255) NOT NULL, + `spec` text NOT NULL, + `verify` text NOT NULL, + UNIQUE KEY `story` (`story`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storystage` ( + `story` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL, + `stage` varchar(50) NOT NULL, + `stagedBy` char(30) NOT NULL, + UNIQUE KEY `story_branch` (`story`,`branch`), + KEY `story` (`story`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_suitecase` ( + `suite` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `case` mediumint(8) unsigned NOT NULL, + `version` smallint(5) unsigned NOT NULL, + UNIQUE KEY `suitecase` (`suite`,`case`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_task` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `parent` mediumint(8) NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `design` mediumint(8) unsigned NOT NULL, + `story` mediumint(8) unsigned NOT NULL DEFAULT '0', + `storyVersion` smallint(6) NOT NULL DEFAULT '1', + `designVersion` smallint(6) unsigned NOT NULL, + `fromBug` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` varchar(255) NOT NULL, + `type` varchar(20) NOT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT '0', + `estimate` float unsigned NOT NULL, + `consumed` float unsigned NOT NULL, + `left` float unsigned NOT NULL, + `deadline` date NOT NULL, + `status` enum('wait','doing','done','pause','cancel','closed') NOT NULL DEFAULT 'wait', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL, + `mailto` text, + `desc` text NOT NULL, + `version` smallint(6) NOT NULL, + `openedBy` varchar(30) NOT NULL, + `openedDate` datetime NOT NULL, + `assignedTo` varchar(30) NOT NULL, + `assignedDate` datetime NOT NULL, + `estStarted` date NOT NULL, + `realStarted` datetime NOT NULL, + `finishedBy` varchar(30) NOT NULL, + `finishedDate` datetime NOT NULL, + `finishedList` text NOT NULL, + `canceledBy` varchar(30) NOT NULL, + `canceledDate` datetime NOT NULL, + `closedBy` varchar(30) NOT NULL, + `closedDate` datetime NOT NULL, + `planDuration` int(11) NOT NULL, + `realDuration` int(11) NOT NULL, + `closedReason` varchar(30) NOT NULL, + `lastEditedBy` varchar(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `activatedDate` date NOT NULL, + `repo` mediumint(8) unsigned NOT NULL, + `mr` mediumint(8) unsigned NOT NULL, + `entry` varchar(255) NOT NULL, + `lines` varchar(10) NOT NULL, + `v1` varchar(40) NOT NULL, + `v2` varchar(40) NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `execution` (`execution`), + KEY `story` (`story`), + KEY `parent` (`parent`), + KEY `assignedTo` (`assignedTo`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_taskestimate` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `task` mediumint(8) unsigned NOT NULL DEFAULT '0', + `date` date NOT NULL, + `left` float unsigned NOT NULL DEFAULT '0', + `consumed` float unsigned NOT NULL, + `account` char(30) NOT NULL DEFAULT '', + `work` text, + PRIMARY KEY (`id`), + KEY `task` (`task`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_taskspec` ( + `task` mediumint(8) NOT NULL, + `version` smallint(6) NOT NULL, + `name` varchar(255) NOT NULL, + `estStarted` date NOT NULL, + `deadline` date NOT NULL, + UNIQUE KEY `task` (`task`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_team` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `root` mediumint(8) unsigned NOT NULL DEFAULT '0', + `type` enum('project','task','execution') NOT NULL DEFAULT 'project', + `account` char(30) NOT NULL DEFAULT '', + `role` char(30) NOT NULL DEFAULT '', + `limited` char(8) NOT NULL DEFAULT 'no', + `join` date NOT NULL DEFAULT '0000-00-00', + `days` smallint(5) unsigned NOT NULL, + `hours` float(3,1) unsigned NOT NULL DEFAULT '0.0', + `estimate` decimal(12,2) unsigned NOT NULL DEFAULT '0.00', + `consumed` decimal(12,2) unsigned NOT NULL DEFAULT '0.00', + `left` decimal(12,2) unsigned NOT NULL DEFAULT '0.00', + `order` tinyint(3) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `team` (`root`,`type`,`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `execution` mediumint(8) unsigned NOT NULL, + `tasks` varchar(255) NOT NULL, + `builds` varchar(255) NOT NULL, + `title` varchar(255) NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `owner` char(30) NOT NULL, + `members` text NOT NULL, + `stories` text NOT NULL, + `bugs` text NOT NULL, + `cases` text NOT NULL, + `report` text NOT NULL, + `objectType` varchar(20) NOT NULL, + `objectID` mediumint(8) unsigned NOT NULL, + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testresult` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `run` mediumint(8) unsigned NOT NULL, + `case` mediumint(8) unsigned NOT NULL, + `version` smallint(5) unsigned NOT NULL, + `job` mediumint(8) unsigned NOT NULL, + `compile` mediumint(8) unsigned NOT NULL, + `caseResult` char(30) NOT NULL, + `stepResults` text NOT NULL, + `lastRunner` varchar(30) NOT NULL, + `date` datetime NOT NULL, + `duration` float NOT NULL, + `xml` text NOT NULL, + PRIMARY KEY (`id`), + KEY `case` (`case`), + KEY `version` (`version`), + KEY `run` (`run`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testrun` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `task` mediumint(8) unsigned NOT NULL DEFAULT '0', + `case` mediumint(8) unsigned NOT NULL DEFAULT '0', + `version` tinyint(3) unsigned NOT NULL DEFAULT '0', + `assignedTo` char(30) NOT NULL DEFAULT '', + `lastRunner` varchar(30) NOT NULL, + `lastRunDate` datetime NOT NULL, + `lastRunResult` char(30) NOT NULL, + `status` char(30) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `task` (`task`,`case`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testsuite` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `desc` text NOT NULL, + `type` varchar(20) NOT NULL, + `addedBy` char(30) NOT NULL, + `addedDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL, + PRIMARY KEY (`id`), + KEY `product` (`product`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testtask` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `name` char(90) NOT NULL, + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `build` char(30) NOT NULL, + `type` varchar(255) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT '0', + `begin` date NOT NULL, + `end` date NOT NULL, + `realFinishedDate` datetime NOT NULL, + `mailto` text, + `desc` text NOT NULL, + `report` text NOT NULL, + `status` enum('blocked','doing','wait','done') NOT NULL DEFAULT 'wait', + `testreport` mediumint(8) unsigned NOT NULL, + `auto` varchar(10) NOT NULL DEFAULT 'no', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `build` (`build`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_todo` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `date` date NOT NULL, + `begin` smallint(4) unsigned zerofill NOT NULL, + `end` smallint(4) unsigned zerofill NOT NULL, + `type` char(10) NOT NULL, + `cycle` tinyint(3) unsigned NOT NULL DEFAULT '0', + `idvalue` mediumint(8) unsigned NOT NULL DEFAULT '0', + `pri` tinyint(3) unsigned NOT NULL, + `name` char(150) NOT NULL, + `desc` text NOT NULL, + `status` enum('wait','doing','done','closed') NOT NULL DEFAULT 'wait', + `private` tinyint(1) NOT NULL, + `config` varchar(255) NOT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime NOT NULL, + `finishedBy` varchar(30) NOT NULL DEFAULT '', + `finishedDate` datetime NOT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `assignedTo` (`assignedTo`), + KEY `finishedBy` (`finishedBy`), + KEY `date` (`date`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_user` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `company` mediumint(8) unsigned NOT NULL, + `type` char(30) NOT NULL DEFAULT 'inside', + `dept` mediumint(8) unsigned NOT NULL DEFAULT '0', + `account` char(30) NOT NULL DEFAULT '', + `password` char(32) NOT NULL DEFAULT '', + `role` char(10) NOT NULL DEFAULT '', + `realname` varchar(100) NOT NULL DEFAULT '', + `pinyin` varchar(255) NOT NULL DEFAULT '', + `nickname` char(60) NOT NULL DEFAULT '', + `commiter` varchar(100) NOT NULL, + `avatar` text NOT NULL, + `birthday` date NOT NULL DEFAULT '0000-00-00', + `gender` enum('f','m') NOT NULL DEFAULT 'f', + `email` char(90) NOT NULL DEFAULT '', + `skype` char(90) NOT NULL DEFAULT '', + `qq` char(20) NOT NULL DEFAULT '', + `mobile` char(11) NOT NULL DEFAULT '', + `phone` char(20) NOT NULL DEFAULT '', + `weixin` varchar(90) NOT NULL DEFAULT '', + `dingding` varchar(90) NOT NULL DEFAULT '', + `slack` varchar(90) NOT NULL DEFAULT '', + `whatsapp` varchar(90) NOT NULL DEFAULT '', + `address` char(120) NOT NULL DEFAULT '', + `zipcode` char(10) NOT NULL DEFAULT '', + `nature` text NOT NULL, + `analysis` text NOT NULL, + `strategy` text NOT NULL, + `join` date NOT NULL DEFAULT '0000-00-00', + `visits` mediumint(8) unsigned NOT NULL DEFAULT '0', + `ip` char(15) NOT NULL DEFAULT '', + `last` int(10) unsigned NOT NULL DEFAULT '0', + `fails` tinyint(5) NOT NULL DEFAULT '0', + `locked` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `ranzhi` char(30) NOT NULL DEFAULT '', + `score` int(11) NOT NULL DEFAULT '0', + `scoreLevel` int(11) NOT NULL DEFAULT '0', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `clientStatus` enum('online','away','busy','offline','meeting') NOT NULL DEFAULT 'offline', + `clientLang` varchar(10) NOT NULL DEFAULT 'zh-cn', + PRIMARY KEY (`id`), + UNIQUE KEY `account` (`account`), + KEY `dept` (`dept`), + KEY `email` (`email`), + KEY `commiter` (`commiter`), + KEY `deleted` (`deleted`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usercontact` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `listName` varchar(60) NOT NULL, + `userList` text NOT NULL, + PRIMARY KEY (`id`), + KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usergroup` ( + `account` char(30) NOT NULL DEFAULT '', + `group` mediumint(8) unsigned NOT NULL DEFAULT '0', + `project` text NOT NULL, + UNIQUE KEY `account` (`account`,`group`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_userquery` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `module` varchar(30) NOT NULL, + `title` varchar(90) NOT NULL, + `form` text NOT NULL, + `sql` text NOT NULL, + `shortcut` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `module` (`module`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usertpl` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `type` char(30) NOT NULL, + `title` varchar(150) NOT NULL, + `content` text NOT NULL, + `public` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_userview` ( + `account` char(30) NOT NULL, + `programs` mediumtext NOT NULL, + `products` mediumtext NOT NULL, + `projects` mediumtext NOT NULL, + `sprints` mediumtext NOT NULL, + UNIQUE KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_webhook` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(15) NOT NULL DEFAULT 'default', + `name` varchar(50) NOT NULL, + `url` varchar(255) NOT NULL, + `domain` varchar(255) NOT NULL, + `secret` varchar(255) NOT NULL, + `contentType` varchar(30) NOT NULL DEFAULT 'application/json', + `sendType` enum('sync','async') NOT NULL DEFAULT 'sync', + `products` text NOT NULL, + `executions` text NOT NULL, + `params` varchar(100) NOT NULL, + `actions` text NOT NULL, + `desc` text NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_weeklyreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `weekStart` date NOT NULL, + `pv` float(9,2) NOT NULL, + `ev` float(9,2) NOT NULL, + `ac` float(9,2) NOT NULL, + `sv` float(9,2) NOT NULL, + `cv` float(9,2) NOT NULL, + `staff` smallint(5) unsigned NOT NULL, + `progress` varchar(255) NOT NULL, + `workload` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `week` (`project`,`weekStart`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/db/standard/zentao16.2.sql b/db/standard/zentao16.2.sql new file mode 100644 index 0000000000..5129598beb --- /dev/null +++ b/db/standard/zentao16.2.sql @@ -0,0 +1,1604 @@ +CREATE TABLE `zt_acl` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `objectType` char(30) NOT NULL, + `objectID` mediumint(9) NOT NULL DEFAULT '0', + `type` char(40) NOT NULL DEFAULT 'whitelist', + `source` char(30) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_action` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT '0', + `product` varchar(255) NOT NULL, + `project` mediumint(8) unsigned NOT NULL, + `execution` mediumint(8) unsigned NOT NULL, + `actor` varchar(100) NOT NULL DEFAULT '', + `action` varchar(30) NOT NULL DEFAULT '', + `date` datetime NOT NULL, + `comment` text NOT NULL, + `extra` text NOT NULL, + `read` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `date` (`date`), + KEY `actor` (`actor`), + KEY `project` (`project`), + KEY `action` (`action`), + KEY `objectID` (`objectID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_api` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `product` varchar(255) NOT NULL DEFAULT '', + `lib` int(10) unsigned NOT NULL DEFAULT '0', + `module` int(10) unsigned NOT NULL DEFAULT '0', + `title` varchar(100) NOT NULL DEFAULT '', + `path` varchar(255) NOT NULL DEFAULT '', + `protocol` varchar(10) NOT NULL DEFAULT '', + `method` varchar(10) NOT NULL DEFAULT '', + `requestType` varchar(100) NOT NULL DEFAULT '', + `responseType` varchar(100) NOT NULL DEFAULT '', + `status` varchar(20) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL DEFAULT '0', + `desc` text, + `version` smallint(5) unsigned NOT NULL DEFAULT '0', + `params` text, + `paramsExample` text, + `responseExample` text, + `response` text, + `commonParams` text, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '0', + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_api_lib_release` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `lib` int(10) unsigned NOT NULL DEFAULT '0', + `desc` varchar(255) NOT NULL DEFAULT '', + `version` varchar(255) NOT NULL DEFAULT '', + `snap` mediumtext NOT NULL, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apispec` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `doc` int(10) unsigned NOT NULL DEFAULT '0', + `module` int(10) unsigned NOT NULL DEFAULT '0', + `title` varchar(100) NOT NULL DEFAULT '', + `path` varchar(255) NOT NULL DEFAULT '', + `protocol` varchar(10) NOT NULL DEFAULT '', + `method` varchar(10) NOT NULL DEFAULT '', + `requestType` varchar(100) NOT NULL DEFAULT '', + `responseType` varchar(100) NOT NULL DEFAULT '', + `status` varchar(20) NOT NULL DEFAULT '', + `owner` varchar(255) NOT NULL DEFAULT '0', + `desc` text, + `version` smallint(5) unsigned NOT NULL DEFAULT '0', + `params` text, + `paramsExample` text, + `responseExample` text, + `response` text, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apistruct` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `lib` int(10) unsigned NOT NULL DEFAULT '0', + `name` varchar(30) NOT NULL DEFAULT '', + `type` varchar(50) NOT NULL DEFAULT '', + `desc` varchar(255) NOT NULL DEFAULT '', + `version` smallint(5) unsigned NOT NULL DEFAULT '0', + `attribute` text, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '0', + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apistruct_spec` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(50) NOT NULL DEFAULT '', + `desc` varchar(255) NOT NULL DEFAULT '', + `attribute` text, + `version` smallint(5) unsigned NOT NULL DEFAULT '0', + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_block` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `module` varchar(20) NOT NULL, + `type` char(30) NOT NULL, + `title` varchar(100) NOT NULL, + `source` varchar(20) NOT NULL, + `block` varchar(20) NOT NULL, + `params` text NOT NULL, + `order` tinyint(3) unsigned NOT NULL DEFAULT '0', + `grid` tinyint(3) unsigned NOT NULL DEFAULT '0', + `height` smallint(5) unsigned NOT NULL DEFAULT '0', + `hidden` tinyint(1) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `account_module_type_order` (`account`,`module`,`type`,`order`), + KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_branch` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `default` enum('0','1') NOT NULL DEFAULT '0', + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `desc` varchar(255) NOT NULL, + `createdDate` date NOT NULL, + `closedDate` date NOT NULL, + `order` smallint(5) unsigned NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_bug` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `plan` mediumint(8) unsigned NOT NULL DEFAULT '0', + `story` mediumint(8) unsigned NOT NULL DEFAULT '0', + `storyVersion` smallint(6) NOT NULL DEFAULT '1', + `task` mediumint(8) unsigned NOT NULL DEFAULT '0', + `toTask` mediumint(8) unsigned NOT NULL DEFAULT '0', + `toStory` mediumint(8) NOT NULL DEFAULT '0', + `title` varchar(255) NOT NULL, + `keywords` varchar(255) NOT NULL, + `severity` tinyint(4) NOT NULL DEFAULT '0', + `pri` tinyint(3) unsigned NOT NULL, + `type` varchar(30) NOT NULL DEFAULT '', + `os` varchar(30) NOT NULL DEFAULT '', + `browser` varchar(30) NOT NULL DEFAULT '', + `hardware` varchar(30) NOT NULL, + `found` varchar(30) NOT NULL DEFAULT '', + `steps` text NOT NULL, + `status` enum('active','resolved','closed') NOT NULL DEFAULT 'active', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL, + `confirmed` tinyint(1) NOT NULL DEFAULT '0', + `activatedCount` smallint(6) NOT NULL, + `activatedDate` datetime NOT NULL, + `feedbackBy` varchar(100) NOT NULL, + `notifyEmail` varchar(100) NOT NULL, + `mailto` text, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `openedBuild` varchar(255) NOT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime NOT NULL, + `deadline` date NOT NULL, + `resolvedBy` varchar(30) NOT NULL DEFAULT '', + `resolution` varchar(30) NOT NULL DEFAULT '', + `resolvedBuild` varchar(30) NOT NULL DEFAULT '', + `resolvedDate` datetime NOT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `duplicateBug` mediumint(8) unsigned NOT NULL, + `linkBug` varchar(255) NOT NULL, + `case` mediumint(8) unsigned NOT NULL, + `caseVersion` smallint(6) NOT NULL DEFAULT '1', + `result` mediumint(8) unsigned NOT NULL, + `repo` mediumint(8) unsigned NOT NULL, + `mr` mediumint(8) unsigned NOT NULL, + `entry` varchar(255) NOT NULL, + `lines` varchar(10) NOT NULL, + `v1` varchar(40) NOT NULL, + `v2` varchar(40) NOT NULL, + `repoType` varchar(30) NOT NULL DEFAULT '', + `testtask` mediumint(8) unsigned NOT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`), + KEY `status` (`status`), + KEY `plan` (`plan`), + KEY `story` (`story`), + KEY `case` (`case`), + KEY `toStory` (`toStory`), + KEY `result` (`result`), + KEY `assignedTo` (`assignedTo`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_build` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` char(150) NOT NULL, + `scmPath` char(255) NOT NULL, + `filePath` char(255) NOT NULL, + `date` date NOT NULL, + `stories` text NOT NULL, + `bugs` text NOT NULL, + `builder` char(30) NOT NULL DEFAULT '', + `desc` text NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_burn` ( + `execution` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `task` mediumint(8) unsigned NOT NULL DEFAULT '0', + `date` date NOT NULL, + `estimate` float NOT NULL, + `left` float NOT NULL, + `consumed` float NOT NULL, + `storyPoint` float NOT NULL, + PRIMARY KEY (`execution`,`date`,`task`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_case` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `lib` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `path` mediumint(8) unsigned NOT NULL DEFAULT '0', + `story` mediumint(30) unsigned NOT NULL DEFAULT '0', + `storyVersion` smallint(6) NOT NULL DEFAULT '1', + `title` varchar(255) NOT NULL, + `precondition` text NOT NULL, + `keywords` varchar(255) NOT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT '3', + `type` char(30) NOT NULL DEFAULT '1', + `auto` varchar(10) NOT NULL DEFAULT 'no', + `frame` varchar(10) NOT NULL, + `stage` varchar(255) NOT NULL, + `howRun` varchar(30) NOT NULL, + `scriptedBy` varchar(30) NOT NULL, + `scriptedDate` date NOT NULL, + `scriptStatus` varchar(30) NOT NULL, + `scriptLocation` varchar(255) NOT NULL, + `status` char(30) NOT NULL DEFAULT '1', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL, + `frequency` enum('1','2','3') NOT NULL DEFAULT '1', + `order` tinyint(30) unsigned NOT NULL DEFAULT '0', + `openedBy` char(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `reviewedBy` varchar(255) NOT NULL, + `reviewedDate` date NOT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `version` tinyint(3) unsigned NOT NULL DEFAULT '0', + `linkCase` varchar(255) NOT NULL, + `fromBug` mediumint(8) unsigned NOT NULL, + `fromCaseID` mediumint(8) unsigned NOT NULL, + `fromCaseVersion` mediumint(8) unsigned NOT NULL DEFAULT '1', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `lastRunner` varchar(30) NOT NULL, + `lastRunDate` datetime NOT NULL, + `lastRunResult` char(30) NOT NULL, + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `story` (`story`), + KEY `fromBug` (`fromBug`), + KEY `module` (`module`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_casestep` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `case` mediumint(8) unsigned NOT NULL DEFAULT '0', + `version` smallint(3) unsigned NOT NULL DEFAULT '0', + `type` varchar(10) NOT NULL DEFAULT 'step', + `desc` text NOT NULL, + `expect` text NOT NULL, + PRIMARY KEY (`id`), + KEY `case` (`case`), + KEY `version` (`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_company` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` char(120) DEFAULT NULL, + `phone` char(20) DEFAULT NULL, + `fax` char(20) DEFAULT NULL, + `address` char(120) DEFAULT NULL, + `zipcode` char(10) DEFAULT NULL, + `website` char(120) DEFAULT NULL, + `backyard` char(120) DEFAULT NULL, + `guest` enum('1','0') NOT NULL DEFAULT '0', + `admins` char(255) DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_compile` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `job` mediumint(8) unsigned NOT NULL, + `queue` mediumint(8) NOT NULL, + `status` varchar(255) NOT NULL, + `logs` text, + `atTime` varchar(10) NOT NULL, + `testtask` mediumint(8) unsigned NOT NULL, + `tag` varchar(255) NOT NULL, + `times` tinyint(3) unsigned NOT NULL DEFAULT '0', + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `updateDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_config` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `owner` char(30) NOT NULL DEFAULT '', + `module` varchar(30) NOT NULL, + `section` char(30) NOT NULL DEFAULT '', + `key` char(30) NOT NULL DEFAULT '', + `value` longtext NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique` (`owner`,`module`,`section`,`key`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_cron` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `m` varchar(20) NOT NULL, + `h` varchar(20) NOT NULL, + `dom` varchar(20) NOT NULL, + `mon` varchar(20) NOT NULL, + `dow` varchar(20) NOT NULL, + `command` text NOT NULL, + `remark` varchar(255) NOT NULL, + `type` varchar(20) NOT NULL, + `buildin` tinyint(1) NOT NULL DEFAULT '0', + `status` varchar(20) NOT NULL, + `lastTime` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `lastTime` (`lastTime`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_dept` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` char(60) NOT NULL, + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT '0', + `order` smallint(4) unsigned NOT NULL DEFAULT '0', + `position` char(30) NOT NULL DEFAULT '', + `function` char(255) NOT NULL DEFAULT '', + `manager` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `parent` (`parent`), + KEY `path` (`path`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_design` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` varchar(255) NOT NULL, + `product` varchar(255) NOT NULL, + `commit` text NOT NULL, + `commitedBy` varchar(30) NOT NULL, + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` varchar(255) NOT NULL, + `status` varchar(30) NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `assignedTo` varchar(30) NOT NULL, + `assignedBy` varchar(30) NOT NULL, + `assignedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `story` char(30) NOT NULL, + `desc` text NOT NULL, + `version` smallint(6) NOT NULL, + `type` char(30) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_designspec` ( + `design` mediumint(8) NOT NULL, + `version` smallint(6) NOT NULL, + `name` varchar(255) NOT NULL, + `desc` text NOT NULL, + `files` varchar(255) NOT NULL, + UNIQUE KEY `design` (`design`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doc` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `execution` mediumint(8) unsigned NOT NULL, + `lib` varchar(30) NOT NULL, + `module` varchar(30) NOT NULL, + `title` varchar(255) NOT NULL, + `keywords` varchar(255) NOT NULL, + `type` varchar(30) NOT NULL, + `views` smallint(5) unsigned NOT NULL, + `draft` longtext NOT NULL, + `collector` text NOT NULL, + `addedBy` varchar(30) NOT NULL, + `addedDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `mailto` text, + `acl` varchar(10) NOT NULL DEFAULT 'open', + `groups` varchar(255) NOT NULL, + `users` text NOT NULL, + `version` smallint(5) unsigned NOT NULL DEFAULT '1', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`), + KEY `lib` (`lib`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doccontent` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `doc` mediumint(8) unsigned NOT NULL, + `title` varchar(255) NOT NULL, + `digest` varchar(255) NOT NULL, + `content` longtext NOT NULL, + `files` text NOT NULL, + `type` varchar(10) NOT NULL, + `version` smallint(5) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `doc_version` (`doc`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doclib` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(30) NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `project` mediumint(8) unsigned NOT NULL, + `execution` mediumint(8) unsigned NOT NULL, + `name` varchar(60) NOT NULL, + `baseUrl` varchar(255) NOT NULL DEFAULT '', + `acl` varchar(10) NOT NULL DEFAULT 'open', + `groups` varchar(255) NOT NULL, + `users` text NOT NULL, + `main` enum('0','1') NOT NULL DEFAULT '0', + `collector` text NOT NULL, + `desc` text NOT NULL, + `order` tinyint(5) unsigned NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_effort` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `user` char(30) NOT NULL DEFAULT '', + `todo` enum('1','0') NOT NULL DEFAULT '1', + `date` date NOT NULL, + `begin` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `end` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `type` enum('1','2','3') NOT NULL DEFAULT '1', + `idvalue` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` char(30) NOT NULL DEFAULT '', + `desc` char(255) NOT NULL DEFAULT '', + `status` enum('1','2','3') NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `user` (`user`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_entry` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `account` varchar(30) NOT NULL DEFAULT '', + `code` varchar(20) NOT NULL, + `key` varchar(32) NOT NULL, + `freePasswd` enum('0','1') NOT NULL DEFAULT '0', + `ip` varchar(100) NOT NULL, + `desc` text NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `calledTime` int(10) unsigned NOT NULL DEFAULT '0', + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_expect` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `userID` mediumint(8) NOT NULL, + `project` mediumint(8) NOT NULL DEFAULT '0', + `expect` text NOT NULL, + `progress` text NOT NULL, + `createdBy` char(30) NOT NULL, + `createdDate` date NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_extension` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(150) NOT NULL, + `code` varchar(30) NOT NULL, + `version` varchar(50) NOT NULL, + `author` varchar(100) NOT NULL, + `desc` text NOT NULL, + `license` text NOT NULL, + `type` varchar(20) NOT NULL DEFAULT 'extension', + `site` varchar(150) NOT NULL, + `zentaoCompatible` varchar(100) NOT NULL, + `installedTime` datetime NOT NULL, + `depends` varchar(100) NOT NULL, + `dirs` mediumtext NOT NULL, + `files` mediumtext NOT NULL, + `status` varchar(20) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`), + KEY `name` (`name`), + KEY `installedTime` (`installedTime`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_file` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `pathname` char(100) NOT NULL, + `title` char(255) NOT NULL, + `extension` char(30) NOT NULL, + `size` int(10) unsigned NOT NULL DEFAULT '0', + `objectType` char(30) NOT NULL, + `objectID` mediumint(9) NOT NULL, + `addedBy` char(30) NOT NULL DEFAULT '', + `addedDate` datetime NOT NULL, + `downloads` mediumint(8) unsigned NOT NULL DEFAULT '0', + `extra` varchar(255) NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `objectType` (`objectType`), + KEY `objectID` (`objectID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_group` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` char(30) NOT NULL, + `role` char(30) NOT NULL DEFAULT '', + `desc` char(255) NOT NULL DEFAULT '', + `acl` text, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_grouppriv` ( + `group` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` char(30) NOT NULL DEFAULT '', + `method` char(30) NOT NULL DEFAULT '', + UNIQUE KEY `group` (`group`,`module`,`method`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_history` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `action` mediumint(8) unsigned NOT NULL DEFAULT '0', + `field` varchar(30) NOT NULL DEFAULT '', + `old` text NOT NULL, + `new` text NOT NULL, + `diff` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `action` (`action`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_holiday` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) NOT NULL DEFAULT '', + `type` enum('holiday','working') NOT NULL DEFAULT 'holiday', + `desc` text NOT NULL, + `year` char(4) NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + PRIMARY KEY (`id`), + KEY `year` (`year`), + KEY `name` (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_job` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `repo` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `frame` varchar(20) NOT NULL, + `engine` varchar(20) NOT NULL, + `server` mediumint(8) unsigned NOT NULL, + `pipeline` varchar(500) NOT NULL, + `triggerType` varchar(255) NOT NULL, + `svnDir` varchar(255) NOT NULL, + `atDay` varchar(255) DEFAULT NULL, + `atTime` varchar(10) DEFAULT NULL, + `customParam` text NOT NULL, + `comment` varchar(255) DEFAULT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `lastExec` datetime DEFAULT NULL, + `lastStatus` varchar(255) DEFAULT NULL, + `lastTag` varchar(255) DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanban` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `space` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `owner` varchar(30) NOT NULL, + `team` text NOT NULL, + `desc` text NOT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `archived` enum('0','1') NOT NULL DEFAULT '0', + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `order` mediumint(8) NOT NULL DEFAULT '0', + `displayCards` smallint(6) NOT NULL default '0', + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `closedBy` char(30) NOT NULL, + `closedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbancard` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) unsigned NOT NULL, + `region` mediumint(8) unsigned NOT NULL, + `group` mediumint(8) unsigned NOT NULL, + `lane` mediumint(8) unsigned NOT NULL, + `column` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `pri` mediumint(8) unsigned NOT NULL, + `assignedTo` text NOT NULL, + `desc` text NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `estimate` float unsigned NOT NULL, + `color` char(7) NOT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `order` mediumint(8) NOT NULL DEFAULT '0', + `archived` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `archivedBy` char(30) NOT NULL, + `archivedDate` datetime NOT NULL, + `assignedBy` char(30) NOT NULL, + `assignedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbancell` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) NOT NULL, + `lane` mediumint(8) NOT NULL, + `column` mediumint(8) NOT NULL, + `type` char(30) NOT NULL, + `cards` text NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `card_group` (`kanban`,`type`,`lane`,`column`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbancolumn` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `lane` mediumint(8) NOT NULL DEFAULT '0', + `parent` mediumint(8) NOT NULL DEFAULT '0', + `type` char(30) NOT NULL, + `region` mediumint(8) unsigned NOT NULL, + `group` mediumint(8) NOT NULL DEFAULT '0', + `name` varchar(255) NOT NULL DEFAULT '', + `color` char(30) NOT NULL, + `limit` smallint(6) NOT NULL DEFAULT '-1', + `order` mediumint(8) NOT NULL DEFAULT '0', + `cards` text, + `archived` enum('0','1') NOT NULL DEFAULT '0', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbangroup` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) unsigned NOT NULL, + `region` mediumint(8) unsigned NOT NULL, + `order` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanlane` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `execution` mediumint(8) NOT NULL DEFAULT '0', + `type` char(30) NOT NULL, + `region` mediumint(8) unsigned NOT NULL, + `group` mediumint(8) unsigned NOT NULL, + `groupby` char(30) NOT NULL, + `extra` char(30) NOT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `color` char(30) NOT NULL, + `order` smallint(6) NOT NULL DEFAULT '0', + `lastEditedTime` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanregion` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `space` mediumint(8) unsigned NOT NULL, + `kanban` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `order` mediumint(8) NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanspace` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `owner` varchar(30) NOT NULL, + `team` text NOT NULL, + `desc` text NOT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `order` mediumint(8) NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `closedBy` char(30) NOT NULL, + `closedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_lang` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `lang` varchar(30) NOT NULL, + `module` varchar(30) NOT NULL, + `section` varchar(30) NOT NULL, + `key` varchar(60) NOT NULL, + `value` text NOT NULL, + `system` enum('0','1') NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `lang` (`lang`,`module`,`section`,`key`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_log` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(30) NOT NULL, + `objectID` mediumint(8) unsigned NOT NULL, + `action` mediumint(8) unsigned NOT NULL, + `date` datetime NOT NULL, + `url` varchar(255) NOT NULL, + `contentType` varchar(30) NOT NULL, + `data` text NOT NULL, + `result` text NOT NULL, + PRIMARY KEY (`id`), + KEY `objectType` (`objectType`), + KEY `obejctID` (`objectID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_module` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `root` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` char(60) NOT NULL DEFAULT '', + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT '0', + `order` smallint(5) unsigned NOT NULL DEFAULT '0', + `type` char(30) NOT NULL, + `owner` varchar(30) NOT NULL, + `collector` text NOT NULL, + `short` varchar(30) NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `root` (`root`), + KEY `type` (`type`), + KEY `path` (`path`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_mr` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `gitlabID` mediumint(8) unsigned NOT NULL, + `sourceProject` int(10) unsigned NOT NULL, + `sourceBranch` varchar(100) NOT NULL, + `targetProject` int(10) unsigned NOT NULL, + `targetBranch` varchar(100) NOT NULL, + `mriid` int(10) unsigned NOT NULL, + `title` varchar(255) NOT NULL, + `description` text NOT NULL, + `assignee` varchar(255) NOT NULL, + `reviewer` varchar(255) NOT NULL, + `approver` varchar(255) NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `status` char(30) NOT NULL, + `mergeStatus` char(30) NOT NULL, + `approvalStatus` char(30) NOT NULL, + `needApproved` enum('0','1') NOT NULL DEFAULT '0', + `needCI` enum('0','1') NOT NULL DEFAULT '0', + `repoID` mediumint(8) unsigned NOT NULL, + `jobID` mediumint(8) unsigned NOT NULL, + `compileID` mediumint(8) unsigned NOT NULL, + `compileStatus` char(30) NOT NULL, + `removeSourceBranch` enum('0','1') NOT NULL DEFAULT '0', + `synced` enum('0','1') NOT NULL DEFAULT '1', + `syncError` varchar(255) NOT NULL, + `hasNoConflict` enum('0','1') NOT NULL DEFAULT '0', + `diffs` longtext, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_mrapproval` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `mrID` mediumint(8) unsigned NOT NULL, + `account` varchar(255) NOT NULL, + `date` datetime NOT NULL, + `action` char(30) NOT NULL, + `comment` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_notify` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(50) NOT NULL, + `objectID` mediumint(8) unsigned NOT NULL, + `action` mediumint(9) NOT NULL, + `toList` varchar(255) NOT NULL, + `ccList` text NOT NULL, + `subject` varchar(255) NOT NULL, + `data` text NOT NULL, + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `sendTime` datetime NOT NULL, + `status` varchar(10) NOT NULL DEFAULT 'wait', + `failReason` text NOT NULL, + PRIMARY KEY (`id`), + KEY `objectType_toList_status` (`objectType`,`toList`,`status`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_oauth` ( + `account` varchar(30) NOT NULL, + `openID` varchar(255) NOT NULL, + `providerType` varchar(30) NOT NULL, + `providerID` mediumint(8) unsigned NOT NULL, + KEY `account` (`account`), + KEY `providerType` (`providerType`), + KEY `providerID` (`providerID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_pipeline` ( + `id` smallint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` char(30) NOT NULL, + `name` varchar(50) NOT NULL, + `url` varchar(255) DEFAULT NULL, + `account` varchar(30) DEFAULT NULL, + `password` varchar(255) NOT NULL, + `token` varchar(255) DEFAULT NULL, + `private` char(32) DEFAULT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_planstory` ( + `plan` mediumint(8) unsigned NOT NULL, + `story` mediumint(8) unsigned NOT NULL, + `order` mediumint(9) NOT NULL, + UNIQUE KEY `plan_story` (`plan`,`story`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_product` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `program` mediumint(8) unsigned NOT NULL, + `name` varchar(90) NOT NULL, + `code` varchar(45) NOT NULL, + `bind` enum('0','1') NOT NULL DEFAULT '0', + `line` mediumint(8) NOT NULL, + `type` varchar(30) NOT NULL DEFAULT 'normal', + `status` varchar(30) NOT NULL DEFAULT '', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `desc` text NOT NULL, + `PO` varchar(30) NOT NULL, + `QD` varchar(30) NOT NULL, + `RD` varchar(30) NOT NULL, + `acl` enum('open','private','custom') NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `reviewer` text NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `createdVersion` varchar(20) NOT NULL, + `order` mediumint(8) unsigned NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `acl` (`acl`), + KEY `order` (`order`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_productplan` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL, + `parent` mediumint(9) NOT NULL DEFAULT '0', + `title` varchar(90) NOT NULL, + `status` enum('wait','doing','done','closed') NOT NULL DEFAULT 'wait', + `desc` text NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `order` text NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `end` (`end`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_project` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) NOT NULL DEFAULT '0', + `model` char(30) NOT NULL, + `type` char(30) NOT NULL DEFAULT 'sprint', + `lifetime` char(30) NOT NULL DEFAULT '', + `budget` varchar(30) NOT NULL DEFAULT '0', + `budgetUnit` char(30) NOT NULL DEFAULT 'CNY', + `attribute` varchar(30) NOT NULL DEFAULT '', + `percent` float unsigned NOT NULL DEFAULT '0', + `milestone` enum('0','1') NOT NULL DEFAULT '0', + `output` text NOT NULL, + `auth` char(30) NOT NULL, + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `path` varchar(255) NOT NULL, + `grade` tinyint(3) unsigned NOT NULL, + `name` varchar(90) NOT NULL, + `code` varchar(45) NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `realBegan` date NOT NULL, + `realEnd` date NOT NULL, + `days` smallint(5) unsigned NOT NULL, + `status` varchar(10) NOT NULL, + `subStatus` varchar(30) NOT NULL DEFAULT '', + `pri` enum('1','2','3','4') NOT NULL DEFAULT '1', + `desc` text NOT NULL, + `version` smallint(6) NOT NULL, + `parentVersion` smallint(6) NOT NULL, + `planDuration` int(11) NOT NULL, + `realDuration` int(11) NOT NULL, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `openedVersion` varchar(20) NOT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `canceledBy` varchar(30) NOT NULL DEFAULT '', + `canceledDate` datetime NOT NULL, + `suspendedDate` date NOT NULL, + `PO` varchar(30) NOT NULL DEFAULT '', + `PM` varchar(30) NOT NULL DEFAULT '', + `QD` varchar(30) NOT NULL DEFAULT '', + `RD` varchar(30) NOT NULL DEFAULT '', + `team` varchar(90) NOT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text NOT NULL, + `order` mediumint(8) unsigned NOT NULL, + `displayCards` smallint(6) NOT NULL default '0', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `parent` (`parent`), + KEY `begin` (`begin`), + KEY `end` (`end`), + KEY `status` (`status`), + KEY `acl` (`acl`), + KEY `order` (`order`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectcase` ( + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `case` mediumint(8) unsigned NOT NULL DEFAULT '0', + `count` mediumint(8) unsigned NOT NULL DEFAULT '1', + `version` smallint(6) NOT NULL DEFAULT '1', + `order` smallint(6) unsigned NOT NULL, + UNIQUE KEY `project` (`project`,`case`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectproduct` ( + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL, + `plan` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`project`,`product`,`branch`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectspec` ( + `project` mediumint(8) NOT NULL, + `version` smallint(6) NOT NULL, + `name` varchar(255) NOT NULL, + `milestone` enum('0','1') NOT NULL DEFAULT '0', + `begin` date NOT NULL, + `end` date NOT NULL, + UNIQUE KEY `project` (`project`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectstory` ( + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `product` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL, + `story` mediumint(8) unsigned NOT NULL DEFAULT '0', + `version` smallint(6) NOT NULL DEFAULT '1', + `order` smallint(6) unsigned NOT NULL, + UNIQUE KEY `project` (`project`,`story`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_relation` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) NOT NULL, + `product` mediumint(8) NOT NULL, + `execution` mediumint(8) NOT NULL, + `AType` char(30) NOT NULL, + `AID` mediumint(8) NOT NULL, + `AVersion` char(30) NOT NULL, + `relation` char(30) NOT NULL, + `BType` char(30) NOT NULL, + `BID` mediumint(8) NOT NULL, + `BVersion` char(30) NOT NULL, + `extra` char(30) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `relation` (`product`,`relation`,`AType`,`BType`,`AID`,`BID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_release` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `build` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `marker` enum('0','1') NOT NULL DEFAULT '0', + `date` date NOT NULL, + `stories` text NOT NULL, + `bugs` text NOT NULL, + `leftBugs` text NOT NULL, + `desc` text NOT NULL, + `mailto` text, + `notify` varchar(255) DEFAULT NULL, + `status` varchar(20) NOT NULL DEFAULT 'normal', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `build` (`build`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repo` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `product` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `path` varchar(255) NOT NULL, + `prefix` varchar(100) NOT NULL, + `encoding` varchar(20) NOT NULL, + `SCM` varchar(10) NOT NULL, + `client` varchar(100) NOT NULL, + `commits` mediumint(8) unsigned NOT NULL, + `account` varchar(30) NOT NULL, + `password` varchar(30) NOT NULL, + `encrypt` varchar(30) NOT NULL DEFAULT 'plain', + `acl` text NOT NULL, + `synced` tinyint(1) NOT NULL DEFAULT '0', + `lastSync` datetime NOT NULL, + `desc` text NOT NULL, + `extra` char(30) NOT NULL, + `preMerge` enum('0','1') NOT NULL DEFAULT '0', + `job` mediumint(8) unsigned NOT NULL, + `fileServerUrl` text, + `fileServerAccount` varchar(40) NOT NULL DEFAULT '', + `fileServerPassword` varchar(100) NOT NULL DEFAULT '', + `deleted` tinyint(1) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repobranch` ( + `repo` mediumint(8) unsigned NOT NULL, + `revision` mediumint(8) unsigned NOT NULL, + `branch` varchar(255) NOT NULL, + UNIQUE KEY `repo_revision_branch` (`repo`,`revision`,`branch`), + KEY `branch` (`branch`), + KEY `revision` (`revision`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repofiles` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `repo` mediumint(8) unsigned NOT NULL, + `revision` mediumint(8) unsigned NOT NULL, + `path` varchar(255) NOT NULL, + `parent` varchar(255) NOT NULL, + `type` varchar(20) NOT NULL, + `action` char(1) NOT NULL, + PRIMARY KEY (`id`), + KEY `path` (`path`), + KEY `parent` (`parent`), + KEY `repo` (`repo`), + KEY `revision` (`revision`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repohistory` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `repo` mediumint(9) NOT NULL, + `revision` varchar(40) NOT NULL, + `commit` mediumint(8) unsigned NOT NULL, + `comment` text NOT NULL, + `committer` varchar(100) NOT NULL, + `time` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `repo` (`repo`), + KEY `revision` (`revision`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_score` ( + `id` bigint(12) unsigned NOT NULL AUTO_INCREMENT, + `account` varchar(30) NOT NULL, + `module` varchar(30) NOT NULL DEFAULT '', + `method` varchar(30) NOT NULL, + `desc` varchar(250) NOT NULL DEFAULT '', + `before` int(11) NOT NULL DEFAULT '0', + `score` int(11) NOT NULL DEFAULT '0', + `after` int(11) NOT NULL DEFAULT '0', + `time` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `model` (`module`), + KEY `method` (`method`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_searchdict` ( + `key` smallint(5) unsigned NOT NULL, + `value` char(3) NOT NULL, + PRIMARY KEY (`key`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_searchindex` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `objectType` char(20) NOT NULL, + `objectID` mediumint(9) NOT NULL, + `title` text NOT NULL, + `content` text NOT NULL, + `addedDate` datetime NOT NULL, + `editedDate` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `object` (`objectType`,`objectID`), + KEY `addedDate` (`addedDate`), + FULLTEXT KEY `content` (`content`), + FULLTEXT KEY `title` (`title`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_stage` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `percent` varchar(255) NOT NULL, + `type` varchar(255) NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_stakeholder` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `objectID` mediumint(8) NOT NULL, + `objectType` char(30) NOT NULL, + `user` char(30) NOT NULL, + `type` char(30) NOT NULL, + `key` enum('0','1') NOT NULL, + `from` char(30) NOT NULL, + `createdBy` char(30) NOT NULL, + `createdDate` date NOT NULL, + `editedBy` char(30) NOT NULL, + `editedDate` date NOT NULL, + `deleted` enum('0','1') NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_story` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `parent` mediumint(9) NOT NULL DEFAULT '0', + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `plan` text, + `source` varchar(20) NOT NULL, + `sourceNote` varchar(255) NOT NULL, + `fromBug` mediumint(8) unsigned NOT NULL DEFAULT '0', + `title` varchar(255) NOT NULL, + `keywords` varchar(255) NOT NULL, + `type` varchar(30) NOT NULL DEFAULT 'story', + `category` varchar(30) NOT NULL DEFAULT 'feature', + `pri` tinyint(3) unsigned NOT NULL DEFAULT '3', + `estimate` float unsigned NOT NULL, + `status` enum('','changed','active','draft','closed') NOT NULL DEFAULT '', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL, + `stage` enum('','wait','planned','projected','developing','developed','testing','tested','verified','released','closed') NOT NULL DEFAULT 'wait', + `stagedBy` char(30) NOT NULL, + `mailto` text, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime NOT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `reviewedBy` varchar(255) NOT NULL, + `reviewedDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `closedReason` varchar(30) NOT NULL, + `toBug` mediumint(8) unsigned NOT NULL, + `childStories` varchar(255) NOT NULL, + `linkStories` varchar(255) NOT NULL, + `duplicateStory` mediumint(8) unsigned NOT NULL, + `version` smallint(6) NOT NULL DEFAULT '1', + `feedbackBy` varchar(100) NOT NULL, + `notifyEmail` varchar(100) NOT NULL, + `URChanged` enum('0','1') NOT NULL DEFAULT '0', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `status` (`status`), + KEY `assignedTo` (`assignedTo`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyestimate` ( + `story` mediumint(9) NOT NULL, + `round` smallint(6) NOT NULL, + `estimate` text NOT NULL, + `average` float NOT NULL, + `openedBy` varchar(30) NOT NULL, + `openedDate` datetime NOT NULL, + UNIQUE KEY `story` (`story`,`round`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyreview` ( + `story` mediumint(9) NOT NULL, + `version` smallint(6) NOT NULL, + `reviewer` varchar(30) NOT NULL, + `result` varchar(30) NOT NULL, + `reviewDate` datetime NOT NULL, + UNIQUE KEY `story` (`story`,`version`,`reviewer`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyspec` ( + `story` mediumint(9) NOT NULL, + `version` smallint(6) NOT NULL, + `title` varchar(255) NOT NULL, + `spec` text NOT NULL, + `verify` text NOT NULL, + UNIQUE KEY `story` (`story`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storystage` ( + `story` mediumint(8) unsigned NOT NULL, + `branch` mediumint(8) unsigned NOT NULL, + `stage` varchar(50) NOT NULL, + `stagedBy` char(30) NOT NULL, + UNIQUE KEY `story_branch` (`story`,`branch`), + KEY `story` (`story`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_suitecase` ( + `suite` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `case` mediumint(8) unsigned NOT NULL, + `version` smallint(5) unsigned NOT NULL, + UNIQUE KEY `suitecase` (`suite`,`case`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_task` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `parent` mediumint(8) NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `design` mediumint(8) unsigned NOT NULL, + `story` mediumint(8) unsigned NOT NULL DEFAULT '0', + `storyVersion` smallint(6) NOT NULL DEFAULT '1', + `designVersion` smallint(6) unsigned NOT NULL, + `fromBug` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` varchar(255) NOT NULL, + `type` varchar(20) NOT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT '0', + `estimate` float unsigned NOT NULL, + `consumed` float unsigned NOT NULL, + `left` float unsigned NOT NULL, + `deadline` date NOT NULL, + `status` enum('wait','doing','done','pause','cancel','closed') NOT NULL DEFAULT 'wait', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL, + `mailto` text, + `desc` text NOT NULL, + `version` smallint(6) NOT NULL, + `openedBy` varchar(30) NOT NULL, + `openedDate` datetime NOT NULL, + `assignedTo` varchar(30) NOT NULL, + `assignedDate` datetime NOT NULL, + `estStarted` date NOT NULL, + `realStarted` datetime NOT NULL, + `finishedBy` varchar(30) NOT NULL, + `finishedDate` datetime NOT NULL, + `finishedList` text NOT NULL, + `canceledBy` varchar(30) NOT NULL, + `canceledDate` datetime NOT NULL, + `closedBy` varchar(30) NOT NULL, + `closedDate` datetime NOT NULL, + `planDuration` int(11) NOT NULL, + `realDuration` int(11) NOT NULL, + `closedReason` varchar(30) NOT NULL, + `lastEditedBy` varchar(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `activatedDate` date NOT NULL, + `repo` mediumint(8) unsigned NOT NULL, + `mr` mediumint(8) unsigned NOT NULL, + `entry` varchar(255) NOT NULL, + `lines` varchar(10) NOT NULL, + `v1` varchar(40) NOT NULL, + `v2` varchar(40) NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `execution` (`execution`), + KEY `story` (`story`), + KEY `parent` (`parent`), + KEY `assignedTo` (`assignedTo`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_taskestimate` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `task` mediumint(8) unsigned NOT NULL DEFAULT '0', + `date` date NOT NULL, + `left` float unsigned NOT NULL DEFAULT '0', + `consumed` float unsigned NOT NULL, + `account` char(30) NOT NULL DEFAULT '', + `work` text, + PRIMARY KEY (`id`), + KEY `task` (`task`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_taskspec` ( + `task` mediumint(8) NOT NULL, + `version` smallint(6) NOT NULL, + `name` varchar(255) NOT NULL, + `estStarted` date NOT NULL, + `deadline` date NOT NULL, + UNIQUE KEY `task` (`task`,`version`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_team` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `root` mediumint(8) unsigned NOT NULL DEFAULT '0', + `type` enum('project','task','execution') NOT NULL DEFAULT 'project', + `account` char(30) NOT NULL DEFAULT '', + `role` char(30) NOT NULL DEFAULT '', + `limited` char(8) NOT NULL DEFAULT 'no', + `join` date NOT NULL DEFAULT '0000-00-00', + `days` smallint(5) unsigned NOT NULL, + `hours` float(3,1) unsigned NOT NULL DEFAULT '0.0', + `estimate` decimal(12,2) unsigned NOT NULL DEFAULT '0.00', + `consumed` decimal(12,2) unsigned NOT NULL DEFAULT '0.00', + `left` decimal(12,2) unsigned NOT NULL DEFAULT '0.00', + `order` tinyint(3) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `team` (`root`,`type`,`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `execution` mediumint(8) unsigned NOT NULL, + `tasks` varchar(255) NOT NULL, + `builds` varchar(255) NOT NULL, + `title` varchar(255) NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `owner` char(30) NOT NULL, + `members` text NOT NULL, + `stories` text NOT NULL, + `bugs` text NOT NULL, + `cases` text NOT NULL, + `report` text NOT NULL, + `objectType` varchar(20) NOT NULL, + `objectID` mediumint(8) unsigned NOT NULL, + `createdBy` char(30) NOT NULL, + `createdDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testresult` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `run` mediumint(8) unsigned NOT NULL, + `case` mediumint(8) unsigned NOT NULL, + `version` smallint(5) unsigned NOT NULL, + `job` mediumint(8) unsigned NOT NULL, + `compile` mediumint(8) unsigned NOT NULL, + `caseResult` char(30) NOT NULL, + `stepResults` text NOT NULL, + `lastRunner` varchar(30) NOT NULL, + `date` datetime NOT NULL, + `duration` float NOT NULL, + `xml` text NOT NULL, + PRIMARY KEY (`id`), + KEY `case` (`case`), + KEY `version` (`version`), + KEY `run` (`run`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testrun` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `task` mediumint(8) unsigned NOT NULL DEFAULT '0', + `case` mediumint(8) unsigned NOT NULL DEFAULT '0', + `version` tinyint(3) unsigned NOT NULL DEFAULT '0', + `assignedTo` char(30) NOT NULL DEFAULT '', + `lastRunner` varchar(30) NOT NULL, + `lastRunDate` datetime NOT NULL, + `lastRunResult` char(30) NOT NULL, + `status` char(30) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `task` (`task`,`case`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testsuite` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `desc` text NOT NULL, + `type` varchar(20) NOT NULL, + `addedBy` char(30) NOT NULL, + `addedDate` datetime NOT NULL, + `lastEditedBy` char(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL, + PRIMARY KEY (`id`), + KEY `product` (`product`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testtask` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `name` char(90) NOT NULL, + `execution` mediumint(8) unsigned NOT NULL DEFAULT '0', + `build` char(30) NOT NULL, + `type` varchar(255) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT '0', + `begin` date NOT NULL, + `end` date NOT NULL, + `realFinishedDate` datetime NOT NULL, + `mailto` text, + `desc` text NOT NULL, + `report` text NOT NULL, + `status` enum('blocked','doing','wait','done') NOT NULL DEFAULT 'wait', + `testreport` mediumint(8) unsigned NOT NULL, + `auto` varchar(10) NOT NULL DEFAULT 'no', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `build` (`build`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_todo` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `date` date NOT NULL, + `begin` smallint(4) unsigned zerofill NOT NULL, + `end` smallint(4) unsigned zerofill NOT NULL, + `type` char(10) NOT NULL, + `cycle` tinyint(3) unsigned NOT NULL DEFAULT '0', + `idvalue` mediumint(8) unsigned NOT NULL DEFAULT '0', + `pri` tinyint(3) unsigned NOT NULL, + `name` char(150) NOT NULL, + `desc` text NOT NULL, + `status` enum('wait','doing','done','closed') NOT NULL DEFAULT 'wait', + `private` tinyint(1) NOT NULL, + `config` varchar(255) NOT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime NOT NULL, + `finishedBy` varchar(30) NOT NULL DEFAULT '', + `finishedDate` datetime NOT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `assignedTo` (`assignedTo`), + KEY `finishedBy` (`finishedBy`), + KEY `date` (`date`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_user` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `company` mediumint(8) unsigned NOT NULL, + `type` char(30) NOT NULL DEFAULT 'inside', + `dept` mediumint(8) unsigned NOT NULL DEFAULT '0', + `account` char(30) NOT NULL DEFAULT '', + `password` char(32) NOT NULL DEFAULT '', + `role` char(10) NOT NULL DEFAULT '', + `realname` varchar(100) NOT NULL DEFAULT '', + `pinyin` varchar(255) NOT NULL DEFAULT '', + `nickname` char(60) NOT NULL DEFAULT '', + `commiter` varchar(100) NOT NULL, + `avatar` text NOT NULL, + `birthday` date NOT NULL DEFAULT '0000-00-00', + `gender` enum('f','m') NOT NULL DEFAULT 'f', + `email` char(90) NOT NULL DEFAULT '', + `skype` char(90) NOT NULL DEFAULT '', + `qq` char(20) NOT NULL DEFAULT '', + `mobile` char(11) NOT NULL DEFAULT '', + `phone` char(20) NOT NULL DEFAULT '', + `weixin` varchar(90) NOT NULL DEFAULT '', + `dingding` varchar(90) NOT NULL DEFAULT '', + `slack` varchar(90) NOT NULL DEFAULT '', + `whatsapp` varchar(90) NOT NULL DEFAULT '', + `address` char(120) NOT NULL DEFAULT '', + `zipcode` char(10) NOT NULL DEFAULT '', + `nature` text NOT NULL, + `analysis` text NOT NULL, + `strategy` text NOT NULL, + `join` date NOT NULL DEFAULT '0000-00-00', + `visits` mediumint(8) unsigned NOT NULL DEFAULT '0', + `ip` char(15) NOT NULL DEFAULT '', + `last` int(10) unsigned NOT NULL DEFAULT '0', + `fails` tinyint(5) NOT NULL DEFAULT '0', + `locked` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `ranzhi` char(30) NOT NULL DEFAULT '', + `score` int(11) NOT NULL DEFAULT '0', + `scoreLevel` int(11) NOT NULL DEFAULT '0', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `clientStatus` enum('online','away','busy','offline','meeting') NOT NULL DEFAULT 'offline', + `clientLang` varchar(10) NOT NULL DEFAULT 'zh-cn', + PRIMARY KEY (`id`), + UNIQUE KEY `account` (`account`), + KEY `dept` (`dept`), + KEY `email` (`email`), + KEY `commiter` (`commiter`), + KEY `deleted` (`deleted`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usercontact` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `listName` varchar(60) NOT NULL, + `userList` text NOT NULL, + PRIMARY KEY (`id`), + KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usergroup` ( + `account` char(30) NOT NULL DEFAULT '', + `group` mediumint(8) unsigned NOT NULL DEFAULT '0', + `project` text NOT NULL, + UNIQUE KEY `account` (`account`,`group`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_userquery` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `module` varchar(30) NOT NULL, + `title` varchar(90) NOT NULL, + `form` text NOT NULL, + `sql` text NOT NULL, + `shortcut` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `module` (`module`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usertpl` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL, + `type` char(30) NOT NULL, + `title` varchar(150) NOT NULL, + `content` text NOT NULL, + `public` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_userview` ( + `account` char(30) NOT NULL, + `programs` mediumtext NOT NULL, + `products` mediumtext NOT NULL, + `projects` mediumtext NOT NULL, + `sprints` mediumtext NOT NULL, + UNIQUE KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_webhook` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(15) NOT NULL DEFAULT 'default', + `name` varchar(50) NOT NULL, + `url` varchar(255) NOT NULL, + `domain` varchar(255) NOT NULL, + `secret` varchar(255) NOT NULL, + `contentType` varchar(30) NOT NULL DEFAULT 'application/json', + `sendType` enum('sync','async') NOT NULL DEFAULT 'sync', + `products` text NOT NULL, + `executions` text NOT NULL, + `params` varchar(100) NOT NULL, + `actions` text NOT NULL, + `desc` text NOT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_weeklyreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL, + `weekStart` date NOT NULL, + `pv` float(9,2) NOT NULL, + `ev` float(9,2) NOT NULL, + `ac` float(9,2) NOT NULL, + `sv` float(9,2) NOT NULL, + `cv` float(9,2) NOT NULL, + `staff` smallint(5) unsigned NOT NULL, + `progress` varchar(255) NOT NULL, + `workload` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `week` (`project`,`weekStart`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/db/update0.sql b/db/update0.sql index c46c5fae5b..d01b1794e2 100644 --- a/db/update0.sql +++ b/db/update0.sql @@ -388,6 +388,19 @@ CREATE TABLE IF NOT EXISTS `zt_testPlan` ( PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE IF NOT EXISTS `zt_testTask` ( + `id` mediumint(8) unsigned NOT NULL auto_increment, + `name` char(90) NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `project` mediumint(8) unsigned NOT NULL default '0', + `build` char(30) NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `desc` text NOT NULL, + `status` char(30) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + CREATE TABLE IF NOT EXISTS `zt_todo` ( `id` mediumint(8) unsigned NOT NULL auto_increment, `account` char(30) NOT NULL, diff --git a/db/update16.1.sql b/db/update16.1.sql index 612305c8aa..64a39a5490 100644 --- a/db/update16.1.sql +++ b/db/update16.1.sql @@ -1,4 +1,15 @@ -ALTER TABLE `zt_project` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`; -ALTER TABLE `zt_product` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`; -ALTER TABLE `zt_doclib` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`; -ALTER TABLE `zt_kanban` ADD `project` mediumint(8) unsigned NOT NULL AFTER `space`; +CREATE TABLE `zt_kanbancell` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) NOT NULL, + `lane` mediumint(8) NOT NULL, + `column` mediumint(8) NOT NULL, + `type` char(30) NOT NULL, + `cards` text NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `card_group` (`kanban`,`type`,`lane`,`column`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +ALTER TABLE `zt_kanban` ADD `displayCards` smallint(6) NOT NULL default '0' AFTER `order`; +ALTER TABLE `zt_project` ADD `displayCards` smallint(6) NOT NULL default '0' AFTER `order`; + +UPDATE `zt_grouppriv` SET `method` = 'taskKanban' WHERE `module` = 'execution' AND `method` = 'kanban'; diff --git a/db/update16.3.sql b/db/update16.3.sql new file mode 100644 index 0000000000..ec89f6d176 --- /dev/null +++ b/db/update16.3.sql @@ -0,0 +1,4 @@ +ALTER TABLE `zt_project` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`; +ALTER TABLE `zt_product` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`; +ALTER TABLE `zt_doclib` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`; +ALTER TABLE `zt_task` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `deleted`; diff --git a/db/zentao.sql b/db/zentao.sql index 157128fa12..acd46db52b 100644 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -677,6 +677,7 @@ CREATE TABLE `zt_kanban` ( `archived` enum('0', '1') NOT NULL DEFAULT '0', `status` enum('active','closed') NOT NULL default 'active', `order` mediumint(8) NOT NULL DEFAULT '0', + `displayCards` smallint(6) NOT NULL default '0', `createdBy` char(30) NOT NULL, `createdDate` datetime NOT NULL, `lastEditedBy` char(30) NOT NULL, @@ -706,8 +707,6 @@ CREATE TABLE `zt_kanbancard` ( `kanban` mediumint(8) unsigned NOT NULL, `region` mediumint(8) unsigned NOT NULL, `group` mediumint(8) unsigned NOT NULL, - `lane` mediumint(8) unsigned NOT NULL, - `column` mediumint(8) unsigned NOT NULL, `name` varchar(255) NOT NULL, `pri` mediumint(8) unsigned NOT NULL, `assignedTo` text NOT NULL, @@ -731,6 +730,17 @@ CREATE TABLE `zt_kanbancard` ( `deleted` enum('0', '1') NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +-- DROP TABLE IF EXISTS `zt_kanbancell`; +CREATE TABLE `zt_kanbancell` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) NOT NULL, + `lane` mediumint(8) NOT NULL, + `column` mediumint(8) NOT NULL, + `type` char(30) NOT NULL, + `cards` text NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `card_group` (`kanban`,`type`,`lane`,`column`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- DROP TABLE IF EXISTS `zt_kanbangroup`; CREATE TABLE `zt_kanbangroup` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, @@ -1011,6 +1021,7 @@ CREATE TABLE IF NOT EXISTS `zt_project` ( `whitelist` text NOT NULL, `order` mediumint(8) unsigned NOT NULL, `vision` varchar(10) NOT NULL DEFAULT 'common', + `displayCards` smallint(6) NOT NULL default '0', `deleted` enum('0','1') NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `parent` (`parent`), diff --git a/doc/CHANGELOG b/doc/CHANGELOG index c8ec55ce3f..c1f588126f 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -1,3 +1,84 @@ +2022-01-17 16.2 +28397 选择项目管理方式弹窗中增加专业研发看板 +28398 实现专业研发看板项目的创建功能 +28403 实现研发看板的新建按钮组功能 +28405 实现专业研发看板在项目列表中的展示方式 +28406 实现专业研发看板的默认视图功能 +28420 打印研发看板的设置下拉菜单 +28421 实现看板新增区域功能 +29708 实现专业研发看板项目的看板视图 +29709 实现研发看板卡片的更多设置菜单 +29710 实现专业研发看板创建功能 +29714 实现专业研发看板设置功能 +29715 研发看板中实现区域的展开/收起功能 +29717 研发看板中打印区域更多设置下拉菜单 +29719 研发看板中实现区域的编辑功能 +29720 研发看板中实现删除区域功能 +29721 实现专业看板的全屏查看功能 +29723 研发看板中实现泳道拖动排序功能 +29724 研发看板中实现泳道组拖动排序功能 +29725 研发看板中需求不支持拆分子需求 +29726 研发看板中任务不支持拆分子任务 +29728 研发看板中实现新增泳道功能 +29729 研发看板中实现删除泳道功能 +29730 研发看板中实现看板的开始/延期/挂起/关闭/删除操作 +29732 鼠标悬浮到卡片头像上时显示人员姓名 +29767 执行列表中增加看板icon标识 +29890 看板的相关操作要记历史记录 +30042 实现通用看板设置泳道高度功能 +30043 实现执行看板设置泳道高度功能 +30053 实现研发看板设置泳道高度功能 +30068 通用看板中默认的模版横向不要有滚动条 + +2022-01-11 16.1 +46672 实现计划卡片的更多设置功能 +46671 计划看板中实现卡片的排序功能 +46670 实现计划卡片在不同状态列中的拖动功能 +46669 批量编辑计划页面增加状态设置 +46668 实现开启分支功能的计划看板视图 +46667 实现计划看板中卡片的展现样式 +46666 在计划看板中实现分支切换查看功能 +46665 计划列表中已完成和已关闭的计划不能创建执行 +46662 编辑计划页面增加状态设置 +46661 计划详情页中增加开始、完成、关闭、激活操作 +46660 计划列表的操作中增加开始、完成、关闭操作 +46659 计划看板泳道设置默认的颜色 +46658 实现未开启分支功能的计划看板视图 +46657 计划列表增加列表和看板视图切换控件 +46656 计划列表中增加已过期标签提醒 +46655 计划列表筛选标签调整为按状态显示 +46654 计划增加状态管理 +47057 重新配置构建任务并修改合并请求,构建任务成功后合并请求详情页构建任务状态为改变 +47053 构建任务的状态没有定时刷新 +47052 禅道中受保护标签没有受保护标识 +47051 合并请求时提示信息中有非中文字符 +47050 自动创建的合并请求关联需求、Bug、任务页面有代码报错 +47049 创建分支保护页面点击保存后有报错信息 +47048 构建日志页面参数标签处显示乱码 +47047 源分支存在重复未关闭的合并请求提示信息中建议把ID显示出来 +46556 实现在禅道中编辑GitLab标签保护功能 +46555 实现在禅道中展示标签保护列表的功能 +46554 在禅道GitLab项目列表中添加标签保护列表的入口 +46553 处理Z工具触发禅道创建GitLab合并请求失败的问题 +46552 实现禅道中创建合并请求检查此源分支是否存在其他未关闭合并请求的功能 +46551 实现在任务详情页面显示关联的合并请求信息的功能 +46550 实现Bug详情页面其他相关中展示相关联合并请求的功能 +46549 实现在需求详情页面相关信息中显示关联的合并请求信息的功能 +46548 实现VS Code禅道插件自动生成源代码管理中的提交信息功能 +46547 实现代码模块中版本信息中的注释支持点击跳转到对应需求、任务、Bug的功能 +46546 优化VS Code禅道插件查看功能需要修改禅道配置文件的问题 +46545 实现VS Code禅道插件提交信息自动补全的自定义触发功能 +46544 实现在禅道中添加GitLab标签保护功能 +46543 15.5版本项目设置项目权限重新定义不生效 +46531 需求详情页面中执行任务名称前显示任务状态 +46529 鼠标悬浮到卡片头像上时显示人员姓名 +46527 项目集、产品、项目、执行权限白名单用户设置时可以选择联系人 +46526 调整项目文档库的访问控制规则 +46521 老版本升级数据合并时把已删除的数据过滤掉。 +46520 待处理页面默认显示待处理项的数量 +46519 Bug列表设置中自定义列增加所属项目字段 +46518 测试单的报告增加重新激活Bug数的统计 + 2021-12-24 16.0 完成的需求 29649 实现禅道VS Code集成时判断所连接禅道版本兼容性功能 diff --git a/framework/base/helper.class.php b/framework/base/helper.class.php index ed495b35e5..bdc0fe9b38 100644 --- a/framework/base/helper.class.php +++ b/framework/base/helper.class.php @@ -917,3 +917,39 @@ function htmlSpecialString($string, $flags = '', $encoding = 'UTF-8') if(!$flags) $flags = defined('ENT_SUBSTITUTE') ? ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 : ENT_QUOTES; return htmlspecialchars($string, $flags, $encoding); } + +if (!function_exists('array_column')) +{ + function array_column(array $input, $columnKey, $indexKey = null) + { + $output = array(); + + foreach ($input as $row) { + $key = $value = null; + $keySet = $valueSet = false; + + if (null !== $indexKey && array_key_exists($indexKey, $row)) { + $keySet = true; + $key = (string) $row[$indexKey]; + } + + if (null === $columnKey) { + $valueSet = true; + $value = $row; + } elseif (\is_array($row) && \array_key_exists($columnKey, $row)) { + $valueSet = true; + $value = $row[$columnKey]; + } + + if ($valueSet) { + if ($keySet) { + $output[$key] = $value; + } else { + $output[] = $value; + } + } + } + + return $output; + } +} \ No newline at end of file diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 0d3eb6016d..e5ed0362ca 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -1964,23 +1964,37 @@ class baseRouter $defaultParams[$name] = $default; } - /** - * 根据PATH_INFO或者GET方式设置请求的参数。 - * Set params according PATH_INFO or GET. - */ - if($this->config->requestType != 'GET') + if ('cli' === PHP_SAPI) { - $this->setParamsByPathInfo($defaultParams); + if ($this->params) + { + $this->params = array_merge($defaultParams, $this->params); + } + else + { + $this->params = $defaultParams; + } } else { - $this->setParamsByGET($defaultParams); - } + /** + * 根据PATH_INFO或者GET方式设置请求的参数。 + * Set params according PATH_INFO or GET. + */ + if($this->config->requestType != 'GET') + { + $this->setParamsByPathInfo($defaultParams); + } + else + { + $this->setParamsByGET($defaultParams); + } - if($this->config->framework->filterParam == 2) - { - $_GET = validater::filterParam($_GET, 'get'); - $_COOKIE = validater::filterParam($_COOKIE, 'cookie'); + if($this->config->framework->filterParam == 2) + { + $_GET = validater::filterParam($_GET, 'get'); + $_COOKIE = validater::filterParam($_COOKIE, 'cookie'); + } } /* 调用该方法 Call the method. */ diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 701242c16e..93f9ddca4d 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -137,59 +137,60 @@ $lang->action->objectTypes['kanbancard'] = 'Kanban Card'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); -$lang->action->desc->common = '$date, $action by $actor.' . "\n"; -$lang->action->desc->extra = '$date, $action as $extra by $actor.' . "\n"; -$lang->action->desc->opened = '$date, created by $actor .' . "\n"; -$lang->action->desc->openedbysystem = '$date, opened by system.' . "\n"; -$lang->action->desc->created = '$date, created by $actor .' . "\n"; -$lang->action->desc->added = '$date, added by $actor .' . "\n"; -$lang->action->desc->changed = '$date, changed by $actor .' . "\n"; -$lang->action->desc->edited = '$date, edited by $actor .' . "\n"; -$lang->action->desc->assigned = '$date, $actor assigned to $extra.' . "\n"; -$lang->action->desc->closed = '$date, closed by $actor .' . "\n"; -$lang->action->desc->closedbysystem = '$date, closed by system.' . "\n"; -$lang->action->desc->deleted = '$date, deleted by $actor .' . "\n"; -$lang->action->desc->deletedfile = '$date, $actor deleted $extra.' . "\n"; -$lang->action->desc->editfile = '$date, $actor edited $extra.' . "\n"; -$lang->action->desc->erased = '$date, deleted by $actor .' . "\n"; -$lang->action->desc->undeleted = '$date, restored by $actor .' . "\n"; -$lang->action->desc->hidden = '$date, hidden by $actor .' . "\n"; -$lang->action->desc->commented = '$date, added by $actor.' . "\n"; -$lang->action->desc->activated = '$date, activated by $actor .' . "\n"; -$lang->action->desc->blocked = '$date, blocked by $actor .' . "\n"; -$lang->action->desc->moved = '$date, moved by $actor , which was "$extra".' . "\n"; -$lang->action->desc->confirmed = '$date, $actor confirmed the story change. The latest build is #$extra.' . "\n"; -$lang->action->desc->caseconfirmed = '$date, $actor confirmed the case change. The latest build is #$extra' . "\n"; -$lang->action->desc->bugconfirmed = '$date, $actor confirmed Bug.' . "\n"; -$lang->action->desc->frombug = '$date, converted from $actor. Its ID was $extra.'; -$lang->action->desc->started = '$date, started by $actor.' . "\n"; -$lang->action->desc->restarted = '$date, continued by $actor.' . "\n"; -$lang->action->desc->delayed = '$date, postponed by $actor.' . "\n"; -$lang->action->desc->suspended = '$date, suspended by $actor.' . "\n"; -$lang->action->desc->recordestimate = '$date, recorded by $actor and it cost $extra hours.'; -$lang->action->desc->editestimate = '$date, $actor edited Hour.'; -$lang->action->desc->deleteestimate = '$date, $actor deleted Hour.'; -$lang->action->desc->canceled = '$date, cancelled by $actor.' . "\n"; -$lang->action->desc->svncommited = '$date, $actor committed and the build is #$extra.' . "\n"; -$lang->action->desc->gitcommited = '$date, $actor committed and the build is #$extra.' . "\n"; -$lang->action->desc->finished = '$date, finished by $actor.' . "\n"; -$lang->action->desc->paused = '$date, paused by $actor.' . "\n"; -$lang->action->desc->verified = '$date, verified by $actor.' . "\n"; -$lang->action->desc->diff1 = '%s is changed. It was "%s" and it is "%s".
' . "\n"; -$lang->action->desc->diff2 = '%s is changed. The difference is ' . "\n" . "
%s
" . "\n
%s
"; -$lang->action->desc->diff3 = 'File Name %s was changed to %s .' . "\n"; -$lang->action->desc->linked2bug = '$date, linked to $extra by $actor'; -$lang->action->desc->linked2testtask = '$date, linked to $extra by $actor'; -$lang->action->desc->resolved = '$date, resolved by $actor ' . "\n"; -$lang->action->desc->managed = '$date, by $actor managed.' . "\n"; -$lang->action->desc->estimated = '$date, by $actor estimated.' . "\n"; -$lang->action->desc->run = '$date, by $actor executed.' . "\n"; -$lang->action->desc->syncprogram = '$date, started by $actor(starting the project sets the program status as Ongoing).' . "\n"; -$lang->action->desc->syncproject = '$date, starting the execution sets the project status as Ongoing.' . "\n"; -$lang->action->desc->syncexecution = '$date, starting the task sets the execution status as Ongoing.' . "\n"; -$lang->action->desc->importfromgitlab = '$date, Issue associate created from gitlab by $actor.' . "\n"; -$lang->action->desc->archived = '$date, archived by $actor.' . "\n"; -$lang->action->desc->restore = '$date, restore by $actor.' . "\n"; +$lang->action->desc->common = '$date, $action by $actor.' . "\n"; +$lang->action->desc->extra = '$date, $action as $extra by $actor.' . "\n"; +$lang->action->desc->opened = '$date, created by $actor .' . "\n"; +$lang->action->desc->openedbysystem = '$date, opened by system.' . "\n"; +$lang->action->desc->created = '$date, created by $actor .' . "\n"; +$lang->action->desc->added = '$date, added by $actor .' . "\n"; +$lang->action->desc->changed = '$date, changed by $actor .' . "\n"; +$lang->action->desc->edited = '$date, edited by $actor .' . "\n"; +$lang->action->desc->assigned = '$date, $actor assigned to $extra.' . "\n"; +$lang->action->desc->closed = '$date, closed by $actor .' . "\n"; +$lang->action->desc->closedbysystem = '$date, closed by system.' . "\n"; +$lang->action->desc->deleted = '$date, deleted by $actor .' . "\n"; +$lang->action->desc->deletedfile = '$date, $actor deleted $extra.' . "\n"; +$lang->action->desc->editfile = '$date, $actor edited $extra.' . "\n"; +$lang->action->desc->erased = '$date, deleted by $actor .' . "\n"; +$lang->action->desc->undeleted = '$date, restored by $actor .' . "\n"; +$lang->action->desc->hidden = '$date, hidden by $actor .' . "\n"; +$lang->action->desc->commented = '$date, added by $actor.' . "\n"; +$lang->action->desc->activated = '$date, activated by $actor .' . "\n"; +$lang->action->desc->blocked = '$date, blocked by $actor .' . "\n"; +$lang->action->desc->moved = '$date, moved by $actor , which was "$extra".' . "\n"; +$lang->action->desc->confirmed = '$date, $actor confirmed the story change. The latest build is #$extra.' . "\n"; +$lang->action->desc->caseconfirmed = '$date, $actor confirmed the case change. The latest build is #$extra' . "\n"; +$lang->action->desc->bugconfirmed = '$date, $actor confirmed Bug.' . "\n"; +$lang->action->desc->frombug = '$date, converted from $actor. Its ID was $extra.'; +$lang->action->desc->started = '$date, started by $actor.' . "\n"; +$lang->action->desc->restarted = '$date, continued by $actor.' . "\n"; +$lang->action->desc->delayed = '$date, postponed by $actor.' . "\n"; +$lang->action->desc->suspended = '$date, suspended by $actor.' . "\n"; +$lang->action->desc->recordestimate = '$date, recorded by $actor and it cost $extra hours.'; +$lang->action->desc->editestimate = '$date, $actor edited Hour.'; +$lang->action->desc->deleteestimate = '$date, $actor deleted Hour.'; +$lang->action->desc->canceled = '$date, cancelled by $actor.' . "\n"; +$lang->action->desc->svncommited = '$date, $actor committed and the build is #$extra.' . "\n"; +$lang->action->desc->gitcommited = '$date, $actor committed and the build is #$extra.' . "\n"; +$lang->action->desc->finished = '$date, finished by $actor.' . "\n"; +$lang->action->desc->paused = '$date, paused by $actor.' . "\n"; +$lang->action->desc->verified = '$date, verified by $actor.' . "\n"; +$lang->action->desc->diff1 = '%s is changed. It was "%s" and it is "%s".
' . "\n"; +$lang->action->desc->diff2 = '%s is changed. The difference is ' . "\n" . "
%s
" . "\n
%s
"; +$lang->action->desc->diff3 = 'File Name %s was changed to %s .' . "\n"; +$lang->action->desc->linked2bug = '$date, linked to $extra by $actor'; +$lang->action->desc->linked2testtask = '$date, linked to $extra by $actor'; +$lang->action->desc->unlinkedfromtesttask = '$date, unlinked from $extra by $actor'; +$lang->action->desc->resolved = '$date, resolved by $actor ' . "\n"; +$lang->action->desc->managed = '$date, by $actor managed.' . "\n"; +$lang->action->desc->estimated = '$date, by $actor estimated.' . "\n"; +$lang->action->desc->run = '$date, by $actor executed.' . "\n"; +$lang->action->desc->syncprogram = '$date, started by $actor(starting the project sets the program status as Ongoing).' . "\n"; +$lang->action->desc->syncproject = '$date, starting the execution sets the project status as Ongoing.' . "\n"; +$lang->action->desc->syncexecution = '$date, starting the task sets the execution status as Ongoing.' . "\n"; +$lang->action->desc->importfromgitlab = '$date, Issue associate created from gitlab by $actor.' . "\n"; +$lang->action->desc->archived = '$date, archived by $actor.' . "\n"; +$lang->action->desc->restore = '$date, restore by $actor.' . "\n"; /* Used to describe the history of operations related to parent-child tasks. */ $lang->action->desc->createchildren = '$date, $actor created a child task $extra。' . "\n"; diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index af08cddf89..13e034b34c 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -137,59 +137,60 @@ $lang->action->objectTypes['kanbancard'] = '看板卡片'; /* 用来描述操作历史记录。*/ $lang->action->desc = new stdclass(); -$lang->action->desc->common = '$date, $action by $actor。' . "\n"; -$lang->action->desc->extra = '$date, $action as $extra by $actor。' . "\n"; -$lang->action->desc->opened = '$date, 由 $actor 创建。' . "\n"; -$lang->action->desc->openedbysystem = '$date, 由系统创建。' . "\n"; -$lang->action->desc->created = '$date, 由 $actor 创建。' . "\n"; -$lang->action->desc->added = '$date, 由 $actor 添加。' . "\n"; -$lang->action->desc->changed = '$date, 由 $actor 变更。' . "\n"; -$lang->action->desc->edited = '$date, 由 $actor 编辑。' . "\n"; -$lang->action->desc->assigned = '$date, 由 $actor 指派给 $extra。' . "\n"; -$lang->action->desc->closed = '$date, 由 $actor 关闭。' . "\n"; -$lang->action->desc->closedbysystem = '$date, 由系统关闭。' . "\n"; -$lang->action->desc->deleted = '$date, 由 $actor 删除。' . "\n"; -$lang->action->desc->deletedfile = '$date, 由 $actor 删除了附件:$extra。' . "\n"; -$lang->action->desc->editfile = '$date, 由 $actor 编辑了附件:$extra。' . "\n"; -$lang->action->desc->erased = '$date, 由 $actor 删除。' . "\n"; -$lang->action->desc->undeleted = '$date, 由 $actor 还原。' . "\n"; -$lang->action->desc->hidden = '$date, 由 $actor 隐藏。' . "\n"; -$lang->action->desc->commented = '$date, 由 $actor 添加备注。' . "\n"; -$lang->action->desc->activated = '$date, 由 $actor 激活。' . "\n"; -$lang->action->desc->blocked = '$date, 由 $actor 阻塞。' . "\n"; -$lang->action->desc->moved = '$date, 由 $actor 移动,之前为 "$extra"。' . "\n"; -$lang->action->desc->confirmed = '$date, 由 $actor 确认' . $lang->SRCommon . '变动,最新版本为#$extra。' . "\n"; -$lang->action->desc->caseconfirmed = '$date, 由 $actor 确认用例变动,最新版本为#$extra。' . "\n"; -$lang->action->desc->bugconfirmed = '$date, 由 $actor 确认Bug。' . "\n"; -$lang->action->desc->frombug = '$date, 由 $actor Bug转化而来,Bug编号为 $extra。'; -$lang->action->desc->started = '$date, 由 $actor 启动。' . "\n"; -$lang->action->desc->restarted = '$date, 由 $actor 继续。' . "\n"; -$lang->action->desc->delayed = '$date, 由 $actor 延期。' . "\n"; -$lang->action->desc->suspended = '$date, 由 $actor 挂起。' . "\n"; -$lang->action->desc->recordestimate = '$date, 由 $actor 记录工时,消耗 $extra 小时。'; -$lang->action->desc->editestimate = '$date, 由 $actor 编辑工时。'; -$lang->action->desc->deleteestimate = '$date, 由 $actor 删除工时。'; -$lang->action->desc->canceled = '$date, 由 $actor 取消。' . "\n"; -$lang->action->desc->svncommited = '$date, 由 $actor 提交代码,版本为#$extra。' . "\n"; -$lang->action->desc->gitcommited = '$date, 由 $actor 提交代码,版本为#$extra。' . "\n"; -$lang->action->desc->finished = '$date, 由 $actor 完成。' . "\n"; -$lang->action->desc->paused = '$date, 由 $actor 暂停。' . "\n"; -$lang->action->desc->verified = '$date, 由 $actor 验收。' . "\n"; -$lang->action->desc->diff1 = '修改了 %s,旧值为 "%s",新值为 "%s"。
' . "\n"; -$lang->action->desc->diff2 = '修改了 %s,区别为:' . "\n" . "
%s
" . "\n
%s
"; -$lang->action->desc->diff3 = '将文件名 %s 改为 %s 。' . "\n"; -$lang->action->desc->linked2bug = '$date 由 $actor 关联到版本 $extra'; -$lang->action->desc->linked2testtask = '$date 由 $actor 关联到测试单 $extra'; -$lang->action->desc->resolved = '$date, 由 $actor 解决。' . "\n"; -$lang->action->desc->managed = '$date, 由 $actor 维护。' . "\n"; -$lang->action->desc->estimated = '$date, 由 $actor 估算。' . "\n"; -$lang->action->desc->run = '$date, 由 $actor 执行。' . "\n"; -$lang->action->desc->syncprogram = '$date, 由 $actor 启动(因项目开始而启动项目集)。' . "\n"; -$lang->action->desc->syncproject = '$date, 系统判断由于执行开始,将项目状态置为进行中。' . "\n"; -$lang->action->desc->syncexecution = '$date, 系统判断由于任务开始,将执行状态置为进行中。' . "\n"; -$lang->action->desc->importfromgitlab = '$date, 由 $actor 从Gitlab的Issue关联创建。' . "\n"; -$lang->action->desc->archived = '$date, 由 $actor 归档。' . "\n"; -$lang->action->desc->restore = '$date, 由 $actor 还原。' . "\n"; +$lang->action->desc->common = '$date, $action by $actor。' . "\n"; +$lang->action->desc->extra = '$date, $action as $extra by $actor。' . "\n"; +$lang->action->desc->opened = '$date, 由 $actor 创建。' . "\n"; +$lang->action->desc->openedbysystem = '$date, 由系统创建。' . "\n"; +$lang->action->desc->created = '$date, 由 $actor 创建。' . "\n"; +$lang->action->desc->added = '$date, 由 $actor 添加。' . "\n"; +$lang->action->desc->changed = '$date, 由 $actor 变更。' . "\n"; +$lang->action->desc->edited = '$date, 由 $actor 编辑。' . "\n"; +$lang->action->desc->assigned = '$date, 由 $actor 指派给 $extra。' . "\n"; +$lang->action->desc->closed = '$date, 由 $actor 关闭。' . "\n"; +$lang->action->desc->closedbysystem = '$date, 由系统关闭。' . "\n"; +$lang->action->desc->deleted = '$date, 由 $actor 删除。' . "\n"; +$lang->action->desc->deletedfile = '$date, 由 $actor 删除了附件:$extra。' . "\n"; +$lang->action->desc->editfile = '$date, 由 $actor 编辑了附件:$extra。' . "\n"; +$lang->action->desc->erased = '$date, 由 $actor 删除。' . "\n"; +$lang->action->desc->undeleted = '$date, 由 $actor 还原。' . "\n"; +$lang->action->desc->hidden = '$date, 由 $actor 隐藏。' . "\n"; +$lang->action->desc->commented = '$date, 由 $actor 添加备注。' . "\n"; +$lang->action->desc->activated = '$date, 由 $actor 激活。' . "\n"; +$lang->action->desc->blocked = '$date, 由 $actor 阻塞。' . "\n"; +$lang->action->desc->moved = '$date, 由 $actor 移动,之前为 "$extra"。' . "\n"; +$lang->action->desc->confirmed = '$date, 由 $actor 确认' . $lang->SRCommon . '变动,最新版本为#$extra。' . "\n"; +$lang->action->desc->caseconfirmed = '$date, 由 $actor 确认用例变动,最新版本为#$extra。' . "\n"; +$lang->action->desc->bugconfirmed = '$date, 由 $actor 确认Bug。' . "\n"; +$lang->action->desc->frombug = '$date, 由 $actor Bug转化而来,Bug编号为 $extra。'; +$lang->action->desc->started = '$date, 由 $actor 启动。' . "\n"; +$lang->action->desc->restarted = '$date, 由 $actor 继续。' . "\n"; +$lang->action->desc->delayed = '$date, 由 $actor 延期。' . "\n"; +$lang->action->desc->suspended = '$date, 由 $actor 挂起。' . "\n"; +$lang->action->desc->recordestimate = '$date, 由 $actor 记录工时,消耗 $extra 小时。'; +$lang->action->desc->editestimate = '$date, 由 $actor 编辑工时。'; +$lang->action->desc->deleteestimate = '$date, 由 $actor 删除工时。'; +$lang->action->desc->canceled = '$date, 由 $actor 取消。' . "\n"; +$lang->action->desc->svncommited = '$date, 由 $actor 提交代码,版本为#$extra。' . "\n"; +$lang->action->desc->gitcommited = '$date, 由 $actor 提交代码,版本为#$extra。' . "\n"; +$lang->action->desc->finished = '$date, 由 $actor 完成。' . "\n"; +$lang->action->desc->paused = '$date, 由 $actor 暂停。' . "\n"; +$lang->action->desc->verified = '$date, 由 $actor 验收。' . "\n"; +$lang->action->desc->diff1 = '修改了 %s,旧值为 "%s",新值为 "%s"。
' . "\n"; +$lang->action->desc->diff2 = '修改了 %s,区别为:' . "\n" . "
%s
" . "\n
%s
"; +$lang->action->desc->diff3 = '将文件名 %s 改为 %s 。' . "\n"; +$lang->action->desc->linked2bug = '$date 由 $actor 关联到版本 $extra'; +$lang->action->desc->linked2testtask = '$date 由 $actor 关联到测试单 $extra'; +$lang->action->desc->unlinkedfromtesttask = '$date 由 $actor 从测试单 $extra 中移除'; +$lang->action->desc->resolved = '$date, 由 $actor 解决。' . "\n"; +$lang->action->desc->managed = '$date, 由 $actor 维护。' . "\n"; +$lang->action->desc->estimated = '$date, 由 $actor 估算。' . "\n"; +$lang->action->desc->run = '$date, 由 $actor 执行。' . "\n"; +$lang->action->desc->syncprogram = '$date, 由 $actor 启动(因项目开始而启动项目集)。' . "\n"; +$lang->action->desc->syncproject = '$date, 系统判断由于执行开始,将项目状态置为进行中。' . "\n"; +$lang->action->desc->syncexecution = '$date, 系统判断由于任务开始,将执行状态置为进行中。' . "\n"; +$lang->action->desc->importfromgitlab = '$date, 由 $actor 从Gitlab的Issue关联创建。' . "\n"; +$lang->action->desc->archived = '$date, 由 $actor 归档。' . "\n"; +$lang->action->desc->restore = '$date, 由 $actor 还原。' . "\n"; /* 用来描述和父子任务相关的操作历史记录。*/ $lang->action->desc->createchildren = '$date, 由 $actor 创建子任务 $extra。' . "\n"; diff --git a/module/action/model.php b/module/action/model.php index 9cb6b0ca03..5aaabd6619 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -352,14 +352,18 @@ class actionModel extends model } elseif($actionName == 'linked2execution') { - $name = $this->dao->select('name')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch('name'); - if($name) $action->extra = common::hasPriv('execution', 'view') ? html::a(helper::createLink('execution', 'view', "executionID=$action->execution"), $name) : $name; + $execution = $this->dao->select('name,type')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch(); + $name = $execution->name; + $method = $execution->type == 'kanban' ? 'kanban' : 'view'; + if($name) $action->extra = common::hasPriv('execution', $method) ? html::a(helper::createLink('execution', $method, "executionID=$action->execution"), $name) : $name; } elseif($actionName == 'linked2project') { - $name = $this->dao->select('name')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch('name'); + $project = $this->dao->select('name,model')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch(); $productID = trim($action->product, ','); - if($name) $action->extra = common::hasPriv('project', 'view') ? html::a(helper::createLink('project', 'view', "projectID=$action->project"), $name) : $name; + $name = $project->name; + $method = $project->model == 'kanban' ? 'index' : 'view'; + if($name) $action->extra = common::hasPriv('project', $method) ? html::a(helper::createLink('project', $method, "projectID=$action->project"), $name) : $name; } elseif($actionName == 'linked2plan') { @@ -381,6 +385,11 @@ class actionModel extends model $name = $this->dao->select('name')->from(TABLE_RELEASE)->where('id')->eq($action->extra)->fetch('name'); if($name) $action->extra = common::hasPriv('release', 'view') ? html::a(helper::createLink('release', 'view', "releaseID=$action->extra&type={$action->objectType}"), $name) : $name; } + elseif($actionName == 'linked2testtask') + { + $name = $this->dao->select('name')->from(TABLE_TESTTASK)->where('id')->eq($action->extra)->fetch('name'); + if($name) $action->extra = common::hasPriv('testtask', 'view') ? html::a(helper::createLink('testtask', 'view', "taskID=$action->extra"), $name) : $name; + } elseif($actionName == 'moved') { $name = $this->dao->select('name')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch('name'); @@ -416,6 +425,11 @@ class actionModel extends model $title = $this->dao->select('title')->from(TABLE_PRODUCTPLAN)->where('id')->eq($action->extra)->fetch('title'); if($title) $action->extra = common::hasPriv('productplan', 'view') ? html::a(helper::createLink('productplan', 'view', "planID=$action->extra"), "#$action->extra " . $title) : "#$action->extra " . $title; } + elseif($actionName == 'unlinkedfromtesttask') + { + $name = $this->dao->select('name')->from(TABLE_TESTTASK)->where('id')->eq($action->extra)->fetch('name'); + if($name) $action->extra = common::hasPriv('testtask', 'view') ? html::a(helper::createLink('testtask', 'view', "taskID=$action->extra"), $name) : $name; + } elseif($actionName == 'tostory') { $title = $this->dao->select('title')->from(TABLE_STORY)->where('id')->eq($action->extra)->fetch('title'); @@ -611,6 +625,7 @@ class actionModel extends model foreach($typeTrashes as $objectType => $objectIdList) { if(!isset($this->config->objectTables[$objectType])) continue; + if(!isset($this->config->action->objectNameFields[$objectType])) continue; $objectIdList = array_unique($objectIdList); $table = $this->config->objectTables[$objectType]; @@ -852,7 +867,7 @@ class actionModel extends model /* Get actions. */ $actions = $this->dao->select('*')->from(TABLE_ACTION) - ->where(1) + ->where('objectType')->notIN('kanbanregion,kanbanlane,kanbancolumn') ->beginIF($period != 'all')->andWhere('date')->gt($begin)->fi() ->beginIF($period != 'all')->andWhere('date')->lt($end)->fi() ->beginIF($date)->andWhere('date' . ($direction == 'next' ? '<' : '>') . "'{$date}'")->fi() @@ -1023,12 +1038,16 @@ class actionModel extends model { $action->objectName .= $this->lang->action->label->startProgram; } - - if($action->objectType == 'branch' and $action->action == 'mergedbranch') + elseif($action->objectType == 'branch' and $action->action == 'mergedbranch') { if($action->objectID == 0) $action->objectName = $this->lang->branch->main; $action->objectName = '"' . $action->extra . ' "' . $this->lang->action->to . ' "' . $action->objectName . '"'; } + elseif($action->objectType == 'user') + { + $user = $this->dao->select('id,realname')->from(TABLE_USER)->where('id')->eq($action->objectID)->fetch(); + if($user) $action->objectName = $user->realname; + } $projectID = isset($relatedProjects[$action->objectType][$action->objectID]) ? $relatedProjects[$action->objectType][$action->objectID] : 0; @@ -1280,6 +1299,12 @@ class actionModel extends model } $action->objectLink = helper::createLink($moduleName, $methodName, $params); + if($action->objectType == 'execution') + { + $execution = $this->loadModel('execution')->getById($action->objectID); + if(!empty($execution) and $execution->type == 'kanban') $action->objectLink = helper::createLink('execution', 'kanban', "executionID={$action->objectID}"); + } + if($action->objectType == 'doclib') { $docLib = $this->dao->select('type,product,project,execution,deleted')->from(TABLE_DOCLIB)->where('id')->eq($action->objectID)->fetch(); diff --git a/module/api/view/createrelease.html.php b/module/api/view/createrelease.html.php index 994acef5eb..25d8c8956d 100644 --- a/module/api/view/createrelease.html.php +++ b/module/api/view/createrelease.html.php @@ -24,7 +24,7 @@ - + diff --git a/module/block/control.php b/module/block/control.php index 42f7a744d0..7265816bdf 100644 --- a/module/block/control.php +++ b/module/block/control.php @@ -905,7 +905,7 @@ class block extends control foreach($projects as $projectID => $project) { - if($project->model == 'scrum') + if($project->model == 'scrum' or $project->model == 'kanban') { $this->app->loadClass('pager', $static = true); $pager = pager::init(0, 3, 1); @@ -1809,7 +1809,7 @@ class block extends control /* load pager. */ $this->app->loadClass('pager', $static = true); $pager = new pager(0, 3, 1); - $this->view->projects = $this->loadModel('project')->getInfoList('all', 30, 'id_desc', $pager); + $this->view->projects = $this->loadModel('project')->getInfoList('all', 'id_desc', $pager); } /** diff --git a/module/block/lang/en.php b/module/block/lang/en.php index b0a9a555e3..28d45685d1 100644 --- a/module/block/lang/en.php +++ b/module/block/lang/en.php @@ -106,6 +106,25 @@ $lang->block->spent = 'Has Been Spent'; $lang->block->budget = 'Budget'; $lang->block->left = 'Residuals'; +$lang->block->titleList['flowchart'] = 'Flowchart'; +$lang->block->titleList['statistic'] = 'Statistic'; +$lang->block->titleList['recentproject'] = 'Recent Project'; +$lang->block->titleList['assigntome'] = 'Assign To Me'; +$lang->block->titleList['projectteam'] = 'Project Team'; +$lang->block->titleList['project'] = 'Project'; +$lang->block->titleList['dynamic'] = 'Dynamic'; +$lang->block->titleList['list'] = 'List'; +$lang->block->titleList['contribute'] = 'Contribute'; +$lang->block->titleList['scrumoverview'] = 'Scrum Overview'; +$lang->block->titleList['scrumtest'] = 'Scrum Test'; +$lang->block->titleList['scrumlist'] = 'Scrum List'; +$lang->block->titleList['sprint'] = 'Sprint'; +$lang->block->titleList['projectdynamic'] = 'Project Dynamic'; +$lang->block->titleList['bug'] = 'Bug'; +$lang->block->titleList['case'] = 'Case'; +$lang->block->titleList['testtask'] = 'Testtask'; + + $lang->block->default['waterfall']['project']['3']['title'] = 'Plan Gantt Chart'; $lang->block->default['waterfall']['project']['3']['block'] = 'waterfallgantt'; $lang->block->default['waterfall']['project']['3']['source'] = 'project'; diff --git a/module/block/lang/zh-cn.php b/module/block/lang/zh-cn.php index e5d8deface..3ca65ef783 100644 --- a/module/block/lang/zh-cn.php +++ b/module/block/lang/zh-cn.php @@ -106,6 +106,25 @@ $lang->block->spent = '已花费'; $lang->block->budget = '预算'; $lang->block->left = '剩余'; +$lang->block->titleList['flowchart'] = '流程图'; +$lang->block->titleList['statistic'] = '项目统计'; +$lang->block->titleList['recentproject'] = '我近期参与的项目'; +$lang->block->titleList['assigntome'] = '待处理'; +$lang->block->titleList['projectteam'] = '项目人力投入'; +$lang->block->titleList['project'] = '项目列表'; +$lang->block->titleList['dynamic'] = '最新动态'; +$lang->block->titleList['list'] = '我的待办'; +$lang->block->titleList['contribute'] = '我的贡献'; +$lang->block->titleList['scrumoverview'] = '项目概况'; +$lang->block->titleList['scrumtest'] = '待测版本'; +$lang->block->titleList['scrumlist'] = '迭代列表'; +$lang->block->titleList['sprint'] = '迭代总览'; +$lang->block->titleList['projectdynamic'] = '最新动态'; +$lang->block->titleList['bug'] = '指派给我的Bug'; +$lang->block->titleList['case'] = '指派给我的用例'; +$lang->block->titleList['testtask'] = '待测版本列表'; + + $lang->block->default['waterfall']['project']['3']['title'] = "项目计划"; $lang->block->default['waterfall']['project']['3']['block'] = 'waterfallgantt'; $lang->block->default['waterfall']['project']['3']['source'] = 'project'; diff --git a/module/block/view/dashboard.html.php b/module/block/view/dashboard.html.php index ccca29da24..4390aa91e2 100644 --- a/module/block/view/dashboard.html.php +++ b/module/block/view/dashboard.html.php @@ -35,7 +35,7 @@ $useGuest = $this->app->user->account == 'guest'; block != 'welcome');?>
-
title;?>
+
block->titleList, $block->block);?>
- + diff --git a/module/build/control.php b/module/build/control.php index 4433ef29cf..8eb3a41655 100644 --- a/module/build/control.php +++ b/module/build/control.php @@ -62,7 +62,8 @@ class build extends control elseif($this->app->tab == 'qa') { $execution = $this->execution->getByID($executionID); - $executions = $this->execution->getPairs($execution->project); + $projectID = $execution ? $execution->project : 0; + $executions = $this->execution->getPairs($projectID); } $executionList = empty($executions) ? array() : $this->execution->getByIdList(array_keys($executions)); diff --git a/module/caselib/css/createcase.css b/module/caselib/css/createcase.css index bc54a9a26d..e5c55436ab 100644 --- a/module/caselib/css/createcase.css +++ b/module/caselib/css/createcase.css @@ -5,7 +5,7 @@ .step-actions {width: 100px;} #steps .active td {transition: background-color .5s;} #steps .step-steps {resize: none; max-height: 30px;} -#steps .step-expects {display: none;} +#steps .step-expects {resize: none;} #steps .step-item .step-item-id {display: table-cell; min-width: 40px;} #steps .step-item .step-id {color: transparent;} #steps .step-id {font-weight: bold;} diff --git a/module/caselib/view/import.html.php b/module/caselib/view/import.html.php index 07204c6800..7f8f062e85 100644 --- a/module/caselib/view/import.html.php +++ b/module/caselib/view/import.html.php @@ -11,7 +11,7 @@ - + + + + + model != 'kanban'):?> + @@ -172,6 +175,7 @@ + model != 'kanban'):?> @@ -181,6 +185,7 @@ + + model != 'kanban'):?> + + type != 'kanban'):?> + diff --git a/module/execution/view/kanban.html.php b/module/execution/view/kanban.html.php index 28fb732228..df6732d2d8 100644 --- a/module/execution/view/kanban.html.php +++ b/module/execution/view/kanban.html.php @@ -1,15 +1,94 @@ * @package execution - * @version $Id: kanban.html.php $ + * @version $Id: kanban.html.php 935 2022-01-11 16:49:24Z $ + * @link https://www.zentao.net */ ?> + +laneCount; +} + +js::set('regions', $regions); +js::set('browseType', $browseType); +js::set('kanbanData', $kanbanData); +js::set('orderBy', $orderBy); +js::set('groupBy', $groupBy); +js::set('execution', $execution); +js::set('productID', $productID); +js::set('kanbanLang', $lang->kanban); +js::set('kanbanlaneLang', $lang->kanbanlane); +js::set('storyLang', $lang->story); +js::set('executionLang', $lang->execution); +js::set('bugLang', $lang->bug); +js::set('taskLang', $lang->task); +js::set('deadlineLang', $lang->task->deadlineAB); +js::set('kanbancolumnLang', $lang->kanbancolumn); +js::set('kanbancardLang', $lang->kanbancard); +js::set('executionID', $execution->id); +js::set('laneCount', $laneCount); +js::set('userList', $userList); +js::set('noAssigned', $lang->kanbancard->noAssigned); +js::set('users', $users); +js::set('displayCards', $execution->displayCards); +js::set('colorListLang', $lang->kanbancard->colorList); +js::set('colorList', $this->config->kanban->cardColorList); + +$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1; +$canEditRegion = commonModel::hasPriv('kanban', 'editRegion'); +$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion'); +$canCreateLane = commonModel::hasPriv('kanban', 'createLane'); +$canCreateTask = common::hasPriv('task', 'create'); +$canBatchCreateTask = common::hasPriv('task', 'batchCreate'); +$canCreateBug = common::hasPriv('bug', 'create'); +$canBatchCreateBug = common::hasPriv('bug', 'batchCreate'); +$canCreateStory = ($productID and common::hasPriv('story', 'create')); +$canBatchCreateStory = ($productID and common::hasPriv('story', 'batchCreate')); +$canLinkStory = ($productID and common::hasPriv('execution', 'linkStory')); +$canLinkStoryByPlane = ($productID and common::hasPriv('execution', 'importplanstories')); +$hasStoryButton = ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlane); +$hasTaskButton = ($canCreateTask or $canBatchCreateTask); +$hasBugButton = ($canCreateBug or $canBatchCreateBug); + +js::set('priv', + array( + 'canCreateTask' => $canCreateTask, + 'canBatchCreateTask' => $canBatchCreateTask, + 'canCreateBug' => $canCreateBug, + 'canBatchCreateBug' => $canBatchCreateBug, + 'canCreateStory' => $canCreateStory, + 'canBatchCreateStory' => $canBatchCreateStory, + 'canLinkStory' => $canLinkStory, + 'canLinkStoryByPlane' => $canLinkStoryByPlane, + 'canAssignTask' => common::hasPriv('task', 'assignto'), + 'canAssignStory' => common::hasPriv('story', 'assignto'), + 'canFinishTask' => common::hasPriv('task', 'finish'), + 'canPauseTask' => common::hasPriv('task', 'pause'), + 'canCancelTask' => common::hasPriv('task', 'cancel'), + 'canCloseTask' => common::hasPriv('task', 'close'), + 'canActivateTask' => common::hasPriv('task', 'activate'), + 'canStartTask' => common::hasPriv('task', 'start'), + 'canAssignBug' => common::hasPriv('bug', 'assignto'), + 'canConfirmBug' => common::hasPriv('bug', 'confirmBug'), + 'canActivateBug' => common::hasPriv('bug', 'activate') + ) +); +js::set('hasStoryButton', $hasStoryButton); +js::set('hasBugButton', $hasBugButton); +js::set('hasTaskButton', $hasTaskButton); +?> + - +
-
+
+ +
+ +
+
+ +
- + +
+
+
+
+
+ - - - - - - - common::hasPriv('kanban', 'setColumn'), - 'canSetWIP' => common::hasPriv('kanban', 'setWIP'), - 'canSetLane' => common::hasPriv('kanban', 'setLane'), - 'canMoveLane' => common::hasPriv('kanban', 'laneMove'), - 'canSortCards' => common::hasPriv('kanban', 'cardsSort'), - 'canCreateTask' => $canCreateTask, - 'canBatchCreateTask' => $canBatchCreateTask, - 'canCreateBug' => $canCreateBug, - 'canBatchCreateBug' => $canBatchCreateBug, - 'canCreateStory' => $canCreateStory, - 'canBatchCreateStory' => $canBatchCreateStory, - 'canLinkStory' => $canLinkStory, - 'canLinkStoryByPlane' => $canLinkStoryByPlane, - 'canAssignTask' => common::hasPriv('task', 'assignto'), - 'canAssignStory' => common::hasPriv('story', 'assignto'), - 'canFinishTask' => common::hasPriv('task', 'finish'), - 'canPauseTask' => common::hasPriv('task', 'pause'), - 'canCancelTask' => common::hasPriv('task', 'cancel'), - 'canCloseTask' => common::hasPriv('task', 'close'), - 'canActivateTask' => common::hasPriv('task', 'activate'), - 'canStartTask' => common::hasPriv('task', 'start'), - 'canAssignBug' => common::hasPriv('bug', 'assignto'), - 'canConfirmBug' => common::hasPriv('bug', 'confirmBug'), - 'canActivateBug' => common::hasPriv('bug', 'activate') - ) -); -?> -execution);?> -story);?> -task);?> -bug);?> -execution->editName);?> -execution->setWIP);?> -execution->sortColumn);?> -kanban);?> -task->deadlineAB);?> -task->noAssigned);?> - - diff --git a/module/execution/view/preview.html.php b/module/execution/view/preview.html.php index dd11f4304f..1f99b97981 100644 --- a/module/execution/view/preview.html.php +++ b/module/execution/view/preview.html.php @@ -87,7 +87,7 @@ $dataType = ''; id]->spec : $content->steps;?> -

"), 0, 90, 'utf8')?>
+

"), 0, 90, 'utf8')?>
+ +
+
+
+
+
+ + + + + + + + + common::hasPriv('kanban', 'setColumn'), + 'canSetWIP' => common::hasPriv('kanban', 'setWIP'), + 'canSetLane' => common::hasPriv('kanban', 'setLane'), + 'canMoveLane' => common::hasPriv('kanban', 'laneMove'), + 'canSortCards' => common::hasPriv('kanban', 'cardsSort'), + 'canCreateTask' => $canCreateTask, + 'canBatchCreateTask' => $canBatchCreateTask, + 'canCreateBug' => $canCreateBug, + 'canBatchCreateBug' => $canBatchCreateBug, + 'canCreateStory' => $canCreateStory, + 'canBatchCreateStory' => $canBatchCreateStory, + 'canLinkStory' => $canLinkStory, + 'canLinkStoryByPlane' => $canLinkStoryByPlane, + 'canAssignTask' => common::hasPriv('task', 'assignto'), + 'canAssignStory' => common::hasPriv('story', 'assignto'), + 'canFinishTask' => common::hasPriv('task', 'finish'), + 'canPauseTask' => common::hasPriv('task', 'pause'), + 'canCancelTask' => common::hasPriv('task', 'cancel'), + 'canCloseTask' => common::hasPriv('task', 'close'), + 'canActivateTask' => common::hasPriv('task', 'activate'), + 'canStartTask' => common::hasPriv('task', 'start'), + 'canAssignBug' => common::hasPriv('bug', 'assignto'), + 'canConfirmBug' => common::hasPriv('bug', 'confirmBug'), + 'canActivateBug' => common::hasPriv('bug', 'activate') + ) +); +?> +execution);?> +story);?> +task);?> +bug);?> +execution->editName);?> +execution->setWIP);?> +execution->sortColumn);?> +kanban);?> +task->deadlineAB);?> +task->noAssigned);?> + + +displayCards);?> + diff --git a/module/extension/view/deactivate.html.php b/module/extension/view/deactivate.html.php index 120c68221b..2932bc045e 100644 --- a/module/extension/view/deactivate.html.php +++ b/module/extension/view/deactivate.html.php @@ -23,7 +23,7 @@

extension->unremovedFiles;?>

- ');?> + ', $removeCommands);?>

diff --git a/module/extension/view/erase.html.php b/module/extension/view/erase.html.php index c5852376b8..0512b98a01 100644 --- a/module/extension/view/erase.html.php +++ b/module/extension/view/erase.html.php @@ -26,7 +26,7 @@

extension->unremovedFiles;?>

-

');?>

+

', $removeCommands);?>

extension->viewAvailable, 'onclick=parent.location.href="' . inlink('browse', 'type=available') . '"');?>

diff --git a/module/extension/view/uninstall.html.php b/module/extension/view/uninstall.html.php index f1e74c33bb..200a6c990d 100644 --- a/module/extension/view/uninstall.html.php +++ b/module/extension/view/uninstall.html.php @@ -51,7 +51,7 @@ if($removeCommands) { echo "

{$lang->extension->unremovedFiles}

"; - echo join($removeCommands, '
'); + echo join('
', $removeCommands); } echo "

" . html::commonButton($lang->extension->viewAvailable, 'onclick=parent.location.href="' . inlink('browse', 'type=available') . '"') . '

'; ?> diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index bd9da48739..e72e4e7126 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -674,6 +674,7 @@ $lang->resource->kanban->cardsSort = 'cardsSort'; $lang->resource->kanban->viewArchivedColumn = 'viewArchivedColumn'; $lang->resource->kanban->viewArchivedCard = 'viewArchivedCard'; $lang->resource->kanban->restoreCard = 'restoreCard'; +$lang->resource->kanban->setLaneHeight = 'setLaneHeight'; $lang->kanban->methodOrder[5] = 'space'; $lang->kanban->methodOrder[10] = 'createSpace'; @@ -718,6 +719,7 @@ $lang->kanban->methodOrder[195] = 'viewArchivedColumn'; $lang->kanban->methodorder[200] = 'viewArchivedCard'; $lang->kanban->methodorder[205] = 'archiveColumn'; $lang->kanban->methodorder[210] = 'restoreCard'; +$lang->kanban->methodorder[215] = 'setLaneHeight'; /* Execution. */ $lang->resource->execution = new stdclass(); @@ -759,7 +761,7 @@ $lang->resource->execution->linkStory = 'linkStory'; $lang->resource->execution->unlinkStory = 'unlinkStory'; $lang->resource->execution->batchUnlinkStory = 'batchUnlinkStory'; $lang->resource->execution->updateOrder = 'updateOrder'; -$lang->resource->execution->kanban = 'kanban'; +$lang->resource->execution->taskKanban = 'taskKanban'; $lang->resource->execution->printKanban = 'printKanbanAction'; $lang->resource->execution->tree = 'treeAction'; $lang->resource->execution->treeTask = 'treeOnlyTask'; @@ -775,6 +777,7 @@ $lang->resource->execution->addWhitelist = 'addWhitelist'; $lang->resource->execution->unbindWhitelist = 'unbindWhitelist'; $lang->resource->execution->storyEstimate = 'storyEstimate'; $lang->resource->execution->executionkanban = 'kanbanAction'; +$lang->resource->execution->kanban = 'RDKanban'; //if($config->systemMode == 'classic') $lang->resource->project->list = 'list'; //$lang->execution->methodOrder[0] = 'index'; @@ -816,7 +819,7 @@ $lang->execution->methodOrder[170] = 'linkStory'; $lang->execution->methodOrder[175] = 'unlinkStory'; $lang->execution->methodOrder[180] = 'batchUnlinkStory'; $lang->execution->methodOrder[185] = 'updateOrder'; -$lang->execution->methodOrder[190] = 'kanban'; +$lang->execution->methodOrder[190] = 'taskKanban'; $lang->execution->methodOrder[195] = 'printKanban'; $lang->execution->methodOrder[200] = 'kanbanHideCols'; $lang->execution->methodOrder[205] = 'kanbanColsColor'; @@ -832,6 +835,7 @@ $lang->execution->methodOrder[250] = 'addWhitelist'; $lang->execution->methodOrder[255] = 'unbindWhitelist'; $lang->execution->methodOrder[260] = 'storyEstimate'; $lang->execution->methodOrder[265] = 'executionkanban'; +$lang->execution->methodOrder[270] = 'kanban'; /* Task. */ $lang->resource->task = new stdclass(); diff --git a/module/group/model.php b/module/group/model.php index 379decbf3b..967dd0e306 100644 --- a/module/group/model.php +++ b/module/group/model.php @@ -501,7 +501,7 @@ class groupModel extends model $data = new stdclass(); $data->group = $groupID; $data->account = $account; - $data->project = implode($programs[$account], ','); + $data->project = implode(',', $programs[$account]); $this->dao->replace(TABLE_USERGROUP)->data($data)->exec(); foreach($programs[$account] as $programID) diff --git a/module/holiday/control.php b/module/holiday/control.php index e4dcf4fc8e..385e4b6091 100644 --- a/module/holiday/control.php +++ b/module/holiday/control.php @@ -95,7 +95,7 @@ class holiday extends control { if($confirm == 'no') { - die(js::confirm($this->lang->holiday->confirmDelete, inLink('delete', "id=$id&confirm=yes"))); + return print(js::confirm($this->lang->holiday->confirmDelete, inLink('delete', "id=$id&confirm=yes"))); } else { @@ -111,7 +111,7 @@ class holiday extends control $this->holiday->updateTaskRealDuration($holidayInformation->begin, $holidayInformation->end); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - die(js::reload('parent')); + return print(js::reload('parent')); } } } diff --git a/module/index/js/index.js b/module/index/js/index.js index a87638c5b8..3ff3942552 100644 --- a/module/index/js/index.js +++ b/module/index/js/index.js @@ -814,7 +814,7 @@ $(function() event.stopPropagation(); }); - $("#proLink").click(function(event) + $("#bizLink").click(function(event) { var $upgradeContent = $('#upgradeContent').toggle(); if(!$upgradeContent.is(':hidden')) diff --git a/module/index/view/index.html.php b/module/index/view/index.html.php index a7c864100c..da86abf8d3 100644 --- a/module/index/view/index.html.php +++ b/module/index/view/index.html.php @@ -77,7 +77,7 @@ js::set('showFeatures', $showFeatures);
+
api->version?>
api->desc;?>
bug->linkBug;?>createLink('bug', 'linkBugs', "bugID=$bug->id", '', true), $lang->bug->linkBugs, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'");?>createLink('bug', 'linkBugs', "bugID=$bug->id", '', true), $lang->bug->linkBugs, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'");?>
+ charsets[$this->cookie->lang], 'utf-8', "class='form-control'");?> diff --git a/module/ci/control.php b/module/ci/control.php index e38d453fc4..8c16c1ab1d 100644 --- a/module/ci/control.php +++ b/module/ci/control.php @@ -128,7 +128,7 @@ class ci extends control $productID = $this->dao->findById($firstCase->id)->from(TABLE_CASE)->fetch('product'); } } - if(empty($productID)) die(json_encode(array('result' => 'fail', 'message' => 'productID is not found'))); + if(empty($productID)) return print(json_encode(array('result' => 'fail', 'message' => 'productID is not found'))); /* Get testtaskID or create testtask. */ if(!empty($taskID)) @@ -179,6 +179,6 @@ class ci extends control $taskID = $this->testtask->processAutoResult($taskID, $productID, $data['suites'], $data['cases'], $data['results'], $data['suiteNames'], $data['caseTitles'], $testType); - die(json_encode(array('result' => 'success'))); + return print(json_encode(array('result' => 'success'))); } } diff --git a/module/common/lang/common.php b/module/common/lang/common.php index 63a6bbf985..cb73ca4326 100644 --- a/module/common/lang/common.php +++ b/module/common/lang/common.php @@ -117,6 +117,7 @@ $lang->icons['mail'] = 'envelope'; $lang->icons['trash'] = 'trash'; $lang->icons['extension'] = 'th-large'; $lang->icons['app'] = 'th-large'; +$lang->icons['kanban'] = 'kanban'; $lang->icons['results'] = 'list-alt'; $lang->icons['create'] = 'plus'; diff --git a/module/common/lang/en.php b/module/common/lang/en.php index 0d10844a5e..5ccd36fd7c 100644 --- a/module/common/lang/en.php +++ b/module/common/lang/en.php @@ -29,6 +29,7 @@ $lang->and = 'and'; $lang->zentaoPMS = 'ZenTao'; $lang->pmsName = 'ALM'; $lang->proName = 'Pro'; +$lang->bizName = 'Biz'; $lang->logoImg = 'zt-logo-en.png'; $lang->welcome = "%s ALM"; $lang->logout = 'Logout'; @@ -157,6 +158,7 @@ $lang->repo->common = 'Code'; $lang->report->common = 'Statistic'; $lang->system->common = 'System'; $lang->admin->common = 'Admin'; +$lang->story->common = 'Story'; $lang->task->common = 'Task'; $lang->bug->common = 'Bug'; $lang->testcase->common = 'Testcase'; diff --git a/module/common/lang/menu.php b/module/common/lang/menu.php index 3bf77d2863..547d96b935 100644 --- a/module/common/lang/menu.php +++ b/module/common/lang/menu.php @@ -289,6 +289,11 @@ $lang->waterfall->menu->design['subMenu']->dbds = array('link' => "{$lang->d $lang->waterfall->menu->design['subMenu']->ads = array('link' => "{$lang->design->ADS}|design|browse|projectID=%s&productID=0&browseType=ADS"); $lang->waterfall->menu->design['subMenu']->bysearch = array('link' => ' ' . $lang->searchAB . ''); +/* Kanban project menu. */ +$lang->kanban->menu = new stdclass(); +$lang->kanban->menuOrder = array(); +$lang->kanban->dividerMenu = ''; + /* Execution menu. */ $lang->execution->homeMenu = new stdclass(); $lang->execution->homeMenu->all = array('link' => "{$lang->execution->all}|execution|all|", 'alias' => 'batchedit'); @@ -296,7 +301,7 @@ if($config->systemMode == 'new') $lang->execution->homeMenu->executionkanban = a $lang->execution->menu = new stdclass(); $lang->execution->menu->task = array('link' => "{$lang->task->common}|execution|task|executionID=%s", 'subModule' => 'task,tree', 'alias' => 'importtask,importbug'); -$lang->execution->menu->kanban = array('link' => "$lang->executionKanban|execution|kanban|executionID=%s"); +$lang->execution->menu->kanban = array('link' => "$lang->executionKanban|execution|taskkanban|executionID=%s"); $lang->execution->menu->burn = array('link' => "$lang->burn|execution|burn|executionID=%s"); $lang->execution->menu->view = array('link' => "$lang->view|execution|grouptask|executionID=%s", 'alias' => 'grouptask,tree,taskeffort,gantt,calendar,relation,maintainrelation'); $lang->execution->menu->story = array('link' => "$lang->SRCommon|execution|story|executionID=%s", 'subModule' => 'story', 'alias' => 'batchcreate,linkstory,storykanban'); diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index 62af833823..37867196b2 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -29,6 +29,7 @@ $lang->and = '和'; $lang->zentaoPMS = '禅道'; $lang->pmsName = '开源版'; $lang->proName = '专业版'; +$lang->bizName = '企业版'; $lang->logoImg = 'zt-logo.png'; $lang->welcome = "%s项目管理系统"; $lang->logout = '退出'; @@ -157,6 +158,7 @@ $lang->repo->common = '代码'; $lang->report->common = '统计'; $lang->system->common = '组织'; $lang->admin->common = '后台'; +$lang->story->common = $lang->SRCommon; $lang->task->common = '任务'; $lang->bug->common = 'Bug'; $lang->testcase->common = '用例'; diff --git a/module/common/model.php b/module/common/model.php index 27eb1daaf8..88c9ec5b3e 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -532,11 +532,11 @@ class commonModel extends model $attr = "class='iframe' data-width='650px'"; break; case 'project': - if(isset($config->maxVersion) and!defined('TUTORIAL')) + if(!defined('TUTORIAL')) { $params = "programID=0©ProjectID=0&extra=from=global"; $createMethod = 'createGuide'; - $attr = 'data-toggle="modal" data-target="#guideDialog"'; + $attr = 'data-toggle="modal"'; } else { diff --git a/module/company/css/dynamic.css b/module/company/css/dynamic.css index aec7a0ab6f..d368aa2198 100644 --- a/module/company/css/dynamic.css +++ b/module/company/css/dynamic.css @@ -19,9 +19,5 @@ .dynamic.collapsed .dynamic-btn > .icon:before {content: '\f0d8';} .c-project, .c-product, .c-execution {width: 110px !important; overflow: unset;} -<<<<<<< HEAD -.c-user,.c-order {width: 90px !important; overflow: unset;} -======= .c-user, .c-order {width: 90px !important; overflow: unset;} ->>>>>>> xuan .c-date {width: 200px;} diff --git a/module/convert/view/index.html.php b/module/convert/view/index.html.php index 7acb751d44..eb2fb5860b 100644 --- a/module/convert/view/index.html.php +++ b/module/convert/view/index.html.php @@ -1,29 +1,29 @@ - - * @package ZenTaoPMS - * @version $Id: index.html.php 4129 2013-01-18 01:58:14Z wwccss $ - */ -?> - -
-
-
- - convert->common;?> -
-
-
-
- convert->desc);?> -
- createLink('convert', 'selectsource'), $lang->convert->start, '', 'class="btn btn-primary"');?> -
-
-
-
- + * @author Chunsheng Wang + * @package ZenTaoPMS + * @version $Id: index.html.php 4129 2013-01-18 01:58:14Z wwccss $ + */ +?> + +
+
+
+ + convert->common;?> +
+
+
+
+ convert->desc);?> +
+ createLink('convert', 'selectsource'), $lang->convert->start, '', 'class="btn btn-primary"');?> +
+
+
+
+ diff --git a/module/cron/control.php b/module/cron/control.php index 2486ba3f3f..ca2bfd7f84 100644 --- a/module/cron/control.php +++ b/module/cron/control.php @@ -134,9 +134,12 @@ class cron extends control */ public function ajaxExec($restart = false) { - ignore_user_abort(true); - set_time_limit(0); - session_write_close(); + if ('cli' !== PHP_SAPI) + { + ignore_user_abort(true); + set_time_limit(0); + session_write_close(); + } /* Check cron turnon. */ if(empty($this->config->global->cron)) die(); @@ -183,11 +186,21 @@ class cron extends control /* Skip cron that status is running and run time is less than max. */ if($cronInfo->status == 'running' and (time() - strtotime($cronInfo->lastTime)) < $this->config->cron->maxRunTime) continue; /* Skip cron that last time is more than this cron time. */ - if($cronInfo->lastTime > $cron['time']->format(DT_DATETIME1)) die(); + if ('cli' === PHP_SAPI) + { + if($cronInfo->lastTime >= $cron['time']->format(DT_DATETIME1)) continue; + } + else + { + if($cronInfo->lastTime > $cron['time']->format(DT_DATETIME1)) die(); + } if($now > $cron['time']) { - $this->cron->changeStatus($id, 'running'); + if (!$this->cron->changeStatusRunning($id, $cronInfo->lastTime)) + { + continue; + } $parsedCrons[$id]['time'] = $cron['cron']->getNextRunDate(); /* Execution command. */ @@ -239,11 +252,12 @@ class cron extends control sleep($sleepTime); /* Break while. */ - if(connection_status() != CONNECTION_NORMAL) break; + if('cli' !== PHP_SAPI && connection_status() != CONNECTION_NORMAL) break; if(((time() - $startedTime) / 3600 / 24) >= $this->config->cron->maxRunDays) break; } /* Revert cron status to stop. */ $this->cron->markCronStatus('stop', $configID); } + } diff --git a/module/cron/model.php b/module/cron/model.php index ab9822d8fc..e857b4dcca 100644 --- a/module/cron/model.php +++ b/module/cron/model.php @@ -94,6 +94,27 @@ class cronModel extends model return dao::isError() ? false : true; } + /** + * Change cron status to running + * + * @param int $cronID + * @param string $lastTime + * @access public + * @return bool|int + */ + public function changeStatusRunning($cronID, $lastTime) + { + $data = new stdclass(); + $data->status = 'running'; + $data->lastTime = date(DT_DATETIME1); + $rows = $this->dao->update(TABLE_CRON)->data($data) + ->where('id')->eq($cronID) + ->andWhere('status')->ne('running') + ->andWhere('lastTime')->eq($lastTime) + ->exec(); + return dao::isError() ? false : $rows; + } + /** * Log cron. * diff --git a/module/design/control.php b/module/design/control.php index cc945d7328..68f931e92a 100644 --- a/module/design/control.php +++ b/module/design/control.php @@ -298,7 +298,7 @@ class design extends control $repos = $this->loadModel('repo')->getRepoPairs('project', $design->project); $repoID = $repoID ? $repoID : key($repos); - if(empty($repoID)) die(js::locate(helper::createLink('repo', 'create', "objectID=$design->project"))); + if(empty($repoID)) return print(js::locate(helper::createLink('repo', 'create', "objectID=$design->project"))); $repo = $this->loadModel('repo')->getRepoByID($repoID); $revisions = $this->repo->getCommits($repo, '', 'HEAD', '', '', $begin, $end); @@ -360,13 +360,13 @@ class design extends control { if($confirm == 'no') { - die(js::confirm($this->lang->design->confirmUnlink, inlink('unlinkCommit', "designID=$designID&commitID=$commitID&confirm=yes"))); + return print(js::confirm($this->lang->design->confirmUnlink, inlink('unlinkCommit', "designID=$designID&commitID=$commitID&confirm=yes"))); } else { $this->design->unlinkCommit($designID, $commitID); - die(js::reload('parent')); + return print(js::reload('parent')); } } @@ -446,7 +446,7 @@ class design extends control { if($confirm == 'no') { - die(js::confirm($this->lang->design->confirmDelete, inlink('delete', "designID=$designID&confirm=yes"))); + return print(js::confirm($this->lang->design->confirmDelete, inlink('delete', "designID=$designID&confirm=yes"))); } else { @@ -454,7 +454,7 @@ class design extends control $this->dao->delete()->from(TABLE_RELATION)->where('Atype')->eq('design')->andWhere('AID')->eq($designID)->andWhere('Btype')->eq('commit')->andwhere('relation')->eq('completedin')->exec(); $this->dao->delete()->from(TABLE_RELATION)->where('Atype')->eq('commit')->andWhere('BID')->eq($designID)->andWhere('Btype')->eq('design')->andwhere('relation')->eq('completedfrom')->exec(); - die(js::locate($this->session->designList, 'parent')); + return print(js::locate($this->session->designList, 'parent')); } } @@ -470,7 +470,7 @@ class design extends control if($_POST) { $changes = $this->design->assign($designID); - if(dao::isError()) die(js::error(dao::getError())); + if(dao::isError()) return print(js::error(dao::getError())); $this->loadModel('action'); if(!empty($changes)) @@ -479,8 +479,8 @@ class design extends control $this->action->logHistory($actionID, $changes); } - if(isonlybody()) die(js::closeModal('parent.parent', 'this')); - die(js::locate($this->createLink('design', 'browse'), 'parent')); + if(isonlybody()) return print(js::closeModal('parent.parent', 'this')); + return print(js::locate($this->createLink('design', 'browse'), 'parent')); } $design = $this->design->getByID($designID); diff --git a/module/design/model.php b/module/design/model.php index d54581c02f..1eec165770 100644 --- a/module/design/model.php +++ b/module/design/model.php @@ -401,7 +401,7 @@ class designModel extends model * @access public * @return void */ - public function setMenu($projectID = 0, $products, $productID = 0) + public function setMenu($projectID, $products, $productID = 0) { if($this->app->rawMethod != 'browse') unset($this->lang->waterfall->menu->design['subMenu']->bysearch); if(empty($products) || !$productID) return ''; diff --git a/module/doc/control.php b/module/doc/control.php index 81c6979113..1850e42ea5 100644 --- a/module/doc/control.php +++ b/module/doc/control.php @@ -309,7 +309,7 @@ class doc extends control if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $docID)); $objectID = zget($lib, $lib->type, ''); $params = "type={$lib->type}&objectID=$objectID&libID={$lib->id}&docID=" . $docResult['id']; - $link = isonlybody() ? 'parent' : $this->createLink('doc', 'objectLibs', $params); + $link = isonlybody() ? 'parent' : $this->createLink('doc', 'objectLibs', $params, 'html'); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link)); } @@ -791,24 +791,18 @@ class doc extends control $doclib = $this->doc->getLibById($doclibID); $users = $this->user->getPairs('noletter|noempty|noclosed'); + $selectedUser = $doclib->users; if($doclib->acl != 'custom' and !empty($doclib->project) and $acl == 'custom') { - $project = $this->loadModel('project')->getById($doclib->project); - - $userList = array(); + $project = $this->loadModel('project')->getById($doclib->project); $projectTeams = $this->loadModel('user')->getTeamMemberPairs($doclib->project); $stakeholders = $this->loadModel('stakeholder')->getStakeHolderPairs($doclib->project); - foreach($stakeholders as $stakeholder) $userList[$stakeholder] = $users[$stakeholder]; + $whitelist = implode(',', array_keys($projectTeams + $stakeholders)) . $project->whitelist . ',' . $project->PM . ',' . $doclib->users; - $userList += $projectTeams; - $whitelist = implode(',', array_keys($userList)) . $project->whitelist . ',' .$project->PM . ',' . $doclib->users; + $selectedUser = $whitelist; + } - return print(html::select('users[]', $users, $whitelist, "class='form-control chosen' multiple")); - } - else - { - return print(html::select('users[]', $users, $doclib->users, "class='form-control chosen' multiple")); - } + return print(html::select('users[]', $users, $selectedUser, "class='form-control chosen' multiple")); } /** diff --git a/module/doc/lang/en.php b/module/doc/lang/en.php index 7c175fb4df..63d90c164d 100644 --- a/module/doc/lang/en.php +++ b/module/doc/lang/en.php @@ -106,7 +106,7 @@ $lang->doc->custom = 'Custom Document Library'; $lang->doc->customAB = 'Custom Doc Lib'; $lang->doc->createLib = 'Document Library'; $lang->doc->allLibs = 'Library List'; -$lang->doc->objectLibs = "{$lang->productCommon}/{$lang->executionCommon} Libraries"; +$lang->doc->objectLibs = "Document View of Library"; $lang->doc->showFiles = 'Attachments'; $lang->doc->editLib = 'Edit Document Library'; $lang->doc->deleteLib = 'Delete Document Library'; diff --git a/module/doc/lang/zh-cn.php b/module/doc/lang/zh-cn.php index c9d2957bcf..d602806276 100644 --- a/module/doc/lang/zh-cn.php +++ b/module/doc/lang/zh-cn.php @@ -106,7 +106,7 @@ $lang->doc->custom = '自定义文档库'; $lang->doc->customAB = '自定义库'; $lang->doc->createLib = '创建文档库'; $lang->doc->allLibs = '文档库列表'; -$lang->doc->objectLibs = "{$lang->productCommon}/{$lang->executionCommon}库列表"; +$lang->doc->objectLibs = "文档库文档详情"; $lang->doc->showFiles = '附件库'; $lang->doc->editLib = '编辑文档库'; $lang->doc->deleteLib = '删除文档库'; diff --git a/module/doc/model.php b/module/doc/model.php index 3c4bf25661..2f9349ae0b 100644 --- a/module/doc/model.php +++ b/module/doc/model.php @@ -917,11 +917,10 @@ class docModel extends model if($object->project and $object->acl == 'private') { - $stakeHolders = array(); - $project = $this->loadModel('project')->getById($object->project); - $projectTeams = $this->loadModel('user')->getTeamMemberPairs($object->project); - $stakeHolderList = $this->loadModel('stakeholder')->getStakeHolderPairs($object->project); - foreach($stakeHolderList as $stakeHolder) $stakeHolders[$stakeHolder] = $stakeHolder; + $stakeHolders = array(); + $project = $this->loadModel('project')->getById($object->project); + $projectTeams = $this->loadModel('user')->getTeamMemberPairs($object->project); + $stakeHolders = $this->loadModel('stakeholder')->getStakeHolderPairs($object->project); $authorizedUsers = $this->user->getProjectAuthedUsers($project, $stakeHolders, $projectTeams, array_flip(explode(",", $project->whitelist))); @@ -1336,9 +1335,9 @@ class docModel extends model /* Project permissions for DocLib whitelist. */ if($this->app->tab == 'doc') { - $myObjects = $this->dao->select('t2.id, t2.name')->from(TABLE_DOCLIB)->alias('t1') - ->leftjoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id') - ->where("CONCAT(',', t1.users, ',')")->like("%,{$this->app->user->account},%") + $myObjects = $this->dao->select('t1.id, t1.name')->from(TABLE_PROJECT)->alias('t1') + ->leftjoin(TABLE_DOCLIB)->alias('t2')->on('t2.project=t1.id') + ->where("CONCAT(',', t2.users, ',')")->like("%,{$this->app->user->account},%") ->fetchPairs(); } @@ -1378,7 +1377,7 @@ class docModel extends model $executions = $this->dao->select('*')->from(TABLE_EXECUTION) ->where('deleted')->eq(0) - ->andWhere('type')->in('sprint,stage') + ->andWhere('type')->in('sprint,stage,kanban') ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi() ->orderBy('order_asc') ->fetchAll('id'); diff --git a/module/doc/view/browse.html.php b/module/doc/view/browse.html.php index a705360b0c..6df4aa395b 100644 --- a/module/doc/view/browse.html.php +++ b/module/doc/view/browse.html.php @@ -49,7 +49,11 @@ collector, ',' . $this->app->user->account . ',') !== false ? 'icon-star text-yellow' : 'icon-star-empty';?> collector, ',' . $this->app->user->account . ',') !== false ? $lang->doc->cancelCollection : $lang->doc->collect;?>
createLink('doc', 'view', "docID=$doc->id&version=0", '', true), "  " . $doc->title, '', "title='{$doc->title}' class='iframe' data-width='90%'");?> {$doc->title}";?> objectType)) diff --git a/module/execution/config.php b/module/execution/config.php index ec4ef74386..69eeeb5f7d 100644 --- a/module/execution/config.php +++ b/module/execution/config.php @@ -11,6 +11,9 @@ $config->execution->list->exportFields = 'id,name,projectName,code,PM,end,status $config->execution->modelList['scrum'] = 'sprint'; $config->execution->modelList['waterfall'] = 'stage'; +$config->execution->modelList['kanban'] = 'kanban'; + +$config->execution->statusActions = array('start', 'putoff', 'suspend', 'close', 'activate'); global $lang, $app; $app->loadLang('task'); diff --git a/module/execution/control.php b/module/execution/control.php index 774d2fd3fe..dd8f36353c 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -24,7 +24,7 @@ class execution extends control * @var string * @access public */ - public $objectType; + public $objectType = 'execution'; /** * Construct function, Set executions. @@ -47,7 +47,6 @@ class execution extends control { if(!$this->executions and $this->methodName != 'index' and $this->methodName != 'create' and $this->app->getViewType() != 'mhtml') $this->locate($this->createLink('execution', 'create')); } - $this->objectType = $this->config->systemMode == 'classic' ? 'project' : 'execution'; } /** @@ -124,10 +123,13 @@ class execution extends control /* Set browse type. */ $browseType = strtolower($status); - /* Get products by execution. */ $execution = $this->commonAction($executionID, $status); $executionID = $execution->id; - $products = $this->product->getProductPairsByProject($executionID); + + if($execution->type == 'kanban') $this->locate($this->createLink('execution', 'kanban', "executionID=$executionID")); + + /* Get products by execution. */ + $products = $this->product->getProductPairsByProject($executionID); setcookie('preExecutionID', $executionID, $this->config->cookieLife, $this->config->webRoot, '', false, true); /* Save the recently five executions visited in the cookie. */ @@ -1263,6 +1265,15 @@ class execution extends control } $project = $this->project->getByID($projectID); + if(!empty($project) and $project->model == 'kanban') + { + global $lang; + $executionLang = $lang->execution->common; + $lang->executionCommon = $lang->execution->kanban; + $lang->execution->common = $lang->execution->kanban; + include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php'; + $lang->execution->common = $executionLang; + } $extra = str_replace(array(',', ' '), array('&', ''), $extra); parse_str($extra, $output); @@ -1289,6 +1300,7 @@ class execution extends control $this->view->tips = $this->fetch('execution', 'tips', "executionID=$executionID"); $this->view->executionID = $executionID; $this->view->projectID = $projectID; + $this->view->project = $project; $this->display(); exit; } @@ -1376,6 +1388,16 @@ class execution extends control } } + if(!empty($projectID) and $project->model == 'kanban') + { + $execution = $this->execution->getById($executionID); + $this->loadModel('kanban')->createRDKanban($execution); + + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + if($this->app->tab == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'index', "projectID=$projectID"))); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('kanban', "executionID=$executionID"))); + } + if(!empty($planID)) { return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('create', "projectID=$projectID&executionID=$executionID©ExecutionID=&planID=$planID&confirm=no"))); @@ -1430,6 +1452,7 @@ class execution extends control $this->view->copyExecution = isset($copyExecution) ? $copyExecution : ''; $this->view->from = $this->app->tab; $this->view->isStage = (isset($project->model) and $project->model == 'waterfall') ? true : false; + $this->view->project = $project; $this->display(); } @@ -1451,6 +1474,14 @@ class execution extends control $this->app->loadLang('stage'); $this->app->loadLang('programplan'); $browseExecutionLink = $this->createLink('execution', 'browse', "executionID=$executionID"); + $execution = $this->execution->getById($executionID); + + if($execution->type == 'kanban') + { + global $lang; + $lang->executionCommon = $lang->execution->kanban; + include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php'; + } if(!empty($_POST)) { @@ -1502,6 +1533,7 @@ class execution extends control $this->executeHooks($executionID); if($_POST['status'] == 'doing') $this->loadModel('common')->syncPPEStatus($executionID); + if($execution->type == 'kanban') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent')); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('view', "executionID=$executionID"))); } @@ -1509,10 +1541,8 @@ class execution extends control $this->execution->setMenu($executionID); $executions = array('' => '') + $this->executions; - $execution = $this->execution->getById($executionID); $managers = $this->execution->getDefaultManagers($executionID); - /* Remove current execution from the executions. */ unset($executions[$executionID]); @@ -1525,6 +1555,7 @@ class execution extends control $this->loadModel('productplan'); $productPlans = array(0 => ''); $linkedBranches = array(); + $linkedBranchList = array(); $linkedProducts = $this->product->getProducts($executionID); $branches = $this->project->getBranchesByProject($executionID); $plans = $this->productplan->getGroupByProduct(array_keys($linkedProducts), 'skipParent'); @@ -1538,6 +1569,7 @@ class execution extends control if(!isset($allProducts[$productID])) $allProducts[$productID] = $linkedProduct->name; foreach($branches[$productID] as $branchID => $branch) { + $linkedBranchList[$branchID] = $branchID; $linkedBranches[$productID][$branchID] = $branchID; $productPlans[$productID][$branchID] = isset($plans[$productID][$branchID]) ? $plans[$productID][$branchID] : array(); if($branchID != BRANCH_MAIN and isset($plans[$productID][BRANCH_MAIN])) $productPlans[$productID][$branchID] += $plans[$productID][BRANCH_MAIN]; @@ -1581,7 +1613,7 @@ class execution extends control $this->view->unmodifiableBranches = $unmodifiableBranches; $this->view->multiBranchProducts = $this->product->getMultiBranchPairs(); $this->view->productPlans = $productPlans; - $this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($linkedProducts), $this->config->systemMode == 'new' ? $execution->project : 0); + $this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($linkedProducts), $this->config->systemMode == 'new' ? $execution->project : 0, 'noclosed', $linkedBranchList); $this->display(); } @@ -1683,6 +1715,7 @@ class execution extends control { $execution = $this->commonAction($executionID); $executionID = $execution->id; + if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban; if(!empty($_POST)) { @@ -1755,6 +1788,7 @@ class execution extends control { $execution = $this->commonAction($executionID); $executionID = $execution->id; + if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban; if(!empty($_POST)) { @@ -1790,6 +1824,7 @@ class execution extends control { $execution = $this->commonAction($executionID); $executionID = $execution->id; + if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban; if(!empty($_POST)) { @@ -1832,6 +1867,7 @@ class execution extends control { $execution = $this->commonAction($executionID); $executionID = $execution->id; + if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban; if(!empty($_POST)) { @@ -1920,6 +1956,67 @@ class execution extends control /** * Kanban. * + * @param int $executionID + * @param string $browseType + * @param string $orderBy + * @param string $groupBy + * @access public + * @return void + */ + public function kanban($executionID, $browseType = 'all', $orderBy = 'id_asc', $groupBy = 'default') + { + if(empty($groupBy)) $groupBy = 'default'; + + $this->lang->execution->menu = new stdclass(); + $execution = $this->commonAction($executionID); + $kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $browseType, $orderBy, 0, $groupBy); + $executionActions = array(); + + foreach($this->config->execution->statusActions as $action) + { + if($this->execution->isClickable($execution, $action)) $executionActions[] = $action; + } + + $userList = array(); + $users = $this->loadModel('user')->getPairs('noletter|nodeleted'); + $avatarPairs = $this->user->getAvatarPairs(); + foreach($avatarPairs as $account => $avatar) + { + if(!isset($users[$account])) continue; + $userList[$account]['realname'] = $users[$account]; + $userList[$account]['avatar'] = $avatar; + } + + /* Get execution's product. */ + $productID = 0; + $products = $this->loadModel('product')->getProducts($execution->project); + if($products) $productID = key($products); + + $plans = $this->execution->getPlans($products); + $allPlans = array('' => ''); + if(!empty($plans)) + { + foreach($plans as $plan) $allPlans += $plan; + } + + $this->view->title = $this->lang->kanban->view; + $this->view->users = $users; + $this->view->regions = $kanbanData; + $this->view->execution = $execution; + $this->view->userList = $userList; + $this->view->browseType = $browseType; + $this->view->orderBy = $orderBy; + $this->view->groupBy = $groupBy; + $this->view->productID = $productID; + $this->view->allPlans = $allPlans; + $this->view->kanbanData = $kanbanData; + $this->view->executionActions = $executionActions; + $this->display(); + } + + /** + * Task kanban. + * * @param int $executionID * @param string $browseType story|bug|task|all * @param string $orderBy @@ -1927,18 +2024,16 @@ class execution extends control * @access public * @return void */ - public function kanban($executionID, $browseType = '', $orderBy = 'order_asc', $groupBy = '') + public function taskKanban($executionID, $browseType = '', $orderBy = 'order_asc', $groupBy = '') { if(empty($browseType)) $browseType = $this->session->kanbanType ? $this->session->kanbanType : 'all'; - if(empty($groupBy) and $browseType != 'all') $groupBy = $this->session->{'kanbanGroupBy' . $browseType} ? $this->session->{'kanbanGroupBy' . $browseType} : 'default'; - if(empty($groupBy) and $browseType == 'all') $groupBy = 'default'; + if(empty($groupBy)) $groupBy = 'default'; /* Save to session. */ $uri = $this->app->getURI(true); $this->app->session->set('taskList', $uri, 'execution'); $this->app->session->set('bugList', $uri, 'qa'); $this->app->session->set('kanbanType', $browseType, 'execution'); - $this->app->session->set('kanbanGroupBy' . $browseType, $groupBy, 'execution'); /* Load language. */ $this->app->loadLang('story'); @@ -2301,6 +2396,12 @@ class execution extends control if($tips) $tips = str_replace($this->lang->executionCommon, $this->lang->project->stage, $tips); $this->lang->execution->confirmDelete = str_replace($this->lang->executionCommon, $this->lang->project->stage, $this->lang->execution->confirmDelete); } + elseif($type == 'kanban') + { + global $lang; + $lang->executionCommon = $lang->execution->kanban; + include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php'; + } echo js::confirm($tips . sprintf($this->lang->execution->confirmDelete, $this->executions[$executionID]), $this->createLink('execution', 'delete', "executionID=$executionID&confirm=yes")); exit; @@ -2308,6 +2409,7 @@ class execution extends control else { /* Delete execution. */ + $execution = $this->execution->getByID($executionID); $this->dao->update(TABLE_EXECUTION)->set('deleted')->eq(1)->where('id')->eq($executionID)->exec(); $this->loadModel('action')->create('execution', $executionID, 'deleted', '', ACTIONMODEL::CAN_UNDELETED); $this->execution->updateUserView($executionID); @@ -2316,7 +2418,7 @@ class execution extends control $this->executeHooks($executionID); if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess)); - die(js::reload('parent')); + return print(js::reload('parent')); } } @@ -2526,16 +2628,17 @@ class execution extends control * @param int $recTotal * @param int $recPerPage * @param int $pageID + * @param string $extra * @access public * @return void */ - public function linkStory($objectID = 0, $browseType = '', $param = 0, $recTotal = 0, $recPerPage = 50, $pageID = 1) + public function linkStory($objectID = 0, $browseType = '', $param = 0, $recTotal = 0, $recPerPage = 50, $pageID = 1, $extra = '') { $this->loadModel('story'); $this->loadModel('product'); /* Get projects, executions and products. */ - $object = $this->project->getByID($objectID, $this->app->tab == 'project' ? 'project' : 'sprint,stage'); + $object = $this->project->getByID($objectID, $this->app->tab == 'project' ? 'project' : 'sprint,stage,kanban'); $products = $this->product->getProducts($objectID); $browseLink = $this->createLink($this->app->tab == 'project' ? 'projectstory' : 'execution', 'story', "objectID=$objectID"); @@ -2550,7 +2653,7 @@ class execution extends control if(!empty($_POST)) { - $this->execution->linkStory($objectID); + $this->execution->linkStory($objectID, array(), array(), $extra); if($object->type != 'project' and $object->project != 0) $this->execution->linkStory($object->project); if(isonlybody()) die(js::reload('parent')); @@ -2870,7 +2973,7 @@ class execution extends control $projects = $this->loadModel('program')->getProjectList(0, 'all', 0, 'order_asc', null, 0, 0, true); $executionGroups = $this->dao->select('*')->from(TABLE_EXECUTION) ->where('deleted')->eq(0) - ->andWhere('type')->in('sprint,stage') + ->andWhere('type')->in('sprint,stage,kanban') ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi() ->beginIF($this->config->systemMode == 'new')->andWhere('project')->in(array_keys($projects))->fi() ->orderBy('id_desc') @@ -3282,10 +3385,11 @@ class execution extends control * @param int $planID * @param int $productID * @param string $fromMethod + * @param string $extra * @access public * @return void */ - public function importPlanStories($executionID, $planID, $productID = 0, $fromMethod = 'story') + public function importPlanStories($executionID, $planID, $productID = 0, $fromMethod = 'story', $extra = '') { $planStories = $planProducts = array(); $planStory = $this->loadModel('story')->getPlanStories($planID); @@ -3308,7 +3412,7 @@ class execution extends control $projectID = $this->dao->findByID($executionID)->from(TABLE_EXECUTION)->fetch('project'); $planStories = array_keys($planStory); - $this->execution->linkStory($executionID, $planStories, $planProducts); + $this->execution->linkStory($executionID, $planStories, $planProducts, $extra); if($this->config->systemMode == 'new' and $executionID != $projectID) $this->execution->linkStory($projectID, $planStories, $planProducts); } @@ -3319,7 +3423,14 @@ class execution extends control $moduleName = 'projectstory'; $param = "projectID=$executionID"; } - if($count != 0) echo js::alert(sprintf($this->lang->execution->haveDraft, $count)) . js::locate($this->createLink($moduleName, 'story', $param)); + + if($execution->type == 'kanban') + { + global $lang; + $lang->executionCommon = $lang->execution->kanban; + include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php'; + } + if($count != 0) echo js::alert(sprintf($this->lang->execution->haveDraft, $count)) . js::locate($this->createLink($moduleName, $execution->type == 'sprint' ? 'story' : 'kanban', $param)); die(js::locate(helper::createLink($moduleName, $fromMethod, $param))); } @@ -3438,7 +3549,7 @@ class execution extends control $enterTime = date('Y-m-d H:i:s', $enterTime); $lastEditedTime = $this->dao->select("max(lastEditedTime) as lastEditedTime")->from(TABLE_KANBANLANE)->where('execution')->eq($executionID)->fetch('lastEditedTime'); - if($lastEditedTime > $enterTime) + if($lastEditedTime > $enterTime or $groupBy != 'default') { $kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType, $groupBy); die(json_encode($kanbanGroup)); diff --git a/module/execution/css/all.css b/module/execution/css/all.css index b01e13c585..ead807410e 100644 --- a/module/execution/css/all.css +++ b/module/execution/css/all.css @@ -22,3 +22,4 @@ td.flex span.project-type-label {margin-left: 5px; min-width: 36px;} .c-progress, .c-burn {width: 60px;} .c-percent, .c-realBegan, .c-end, .c-begin, .c-realEnd{width: 100px;} .c-action {text-align: center;} +.c-name > span {margin-right: 2px;} diff --git a/module/execution/css/kanban.css b/module/execution/css/kanban.css index 372183c4b4..d27e6e5b95 100644 --- a/module/execution/css/kanban.css +++ b/module/execution/css/kanban.css @@ -1,5 +1,84 @@ -#main {padding-bottom: 10px;} -#main > .container {max-width: 1960px!important; padding: 0 10px} +#TRAction .icon {padding: 0px 6px; font-size: 14px} + +.has-error .form-control {border-color: #ff4268!important;} +.has-error .form-control:focus {box-shadow: inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 6px #f5b2a5!important;} +.has-error .form-control::placeholder {color: #ff4268!important;} + +.label.text-red {background: #ff4268 !important; color: #ffffff;} + +.dropdown-menu > li {padding: 0 10px;} +.dropdown-menu > li > a > .icon {position: relative; left: -5px;} + +#kanbanContainer {padding-bottom: 0; margin-bottom: 0;} +#kanbanContainer.fullscreen {overflow: auto;} + +.region {border: 1px solid #dcdcdc; background-color: #fff;} +.region + .region {margin-top: 20px;} +.region .region-header {padding: 10px;} +.region .region-header span {font-size: 14px; color: #999EAB} +.region .region-header label {color: #999; background: transparent; border: 1px solid #ddd; margin-left: 10px; margin-right: 10px} +.region .region-header .action {float: right} +.region .region-header .icon-double-angle-up,.icon-double-angle-down {cursor: pointer;} +.region .kanban-header-sub-cols .kanban-header-col > .title {max-width: 100% !important;min-width:240px;} +.region .sort .region-header {cursor: move;} + +.region .kanban {padding: 0px 10px 10px 10px; min-height: unset; overflow: auto} +.region .kanban .form-actions {margin: 0;} +.region .kanban .form-actions .btn {margin-right: 10px; min-width: 50px;} + +.region .kanban-board + .kanban-board {margin-top: 20px;} +.region .kanban-board > .kanban-header > .kanban-group-header {padding: 8px 2px; width: 20px;} +.region .kanban-board > .kanban-header > .kanban-group-header:hover {cursor: move;} +.region .kanban-board.sort > .kanban-header {padding-left: 0;} + +.region .kanban-header {border-bottom: none!important; min-width: max-content; min-width: -moz-max-content;} +.region .kanban-header-col > .title {max-width: 80% !important;} +.region .kanban-header-col > .title {margin: 0} +.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial; max-width: 140px !important;} +.region .kanban-header-col > .title > .text-grey {opacity: .5; font-weight: bold; color: #8b91a2;} +.region .kanban-header-col > .title > .count {opacity: .5; font-weight: bold; color: #8b91a2;} +.region .kanban-header-col > .title > .error {color: #333333; padding-left: 2px; font-size: 10px; padding-top: 1px; padding-right: 2px;} +.kanban-affixed .kanban-header-col > .title > .text {color: #fff!important} +.region .kanban-header-col > .actions {position: relative; right: -30px;} +.region .kanban-header-parent-col > .actions {position: absolute; right: 0px;} +.region .kanban-header-sub-cols .actions {position: absolute; right: 0px;} + +.region .kanban-lane {position: relative; border-bottom: none!important; min-height: 200px !important} + +.region .kanban-lane > .kanban-lane-name > .text {margin: auto 0; max-height: 150px; overflow: hidden; text-overflow: ellipsis} +.region .kanban-lane > .kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;} +.region .kanban-lane > .kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;} +.region .kanban-lane > .kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;} +.region .kanban-lane > .kanban-lane-name:hover > .actions {opacity: 1;} +.region .kanban-lane.sort > .kanban-lane-name {cursor: move;} + +.region .kanban-lane-col {max-height: unset !important; overflow: auto!important; position: relative;} +.region .kanban-lane-col.has-scrollbar > .kanban-lane-actions {position: absolute; background-color: inherit; bottom: 0; left: 0; right: 0;} + +.region .kanban-lane-items {overflow: auto; padding-bottom: 10px;} + +.region .kanban-item {position: relative} + +.kanban-card {height: auto !important; padding: 8px 14px !important; min-height: 60px;} + +.cardcolor {width:40px; height: 14px; float: left; margin-left: 5px; margin-right: 5px; margin-top: 2px; border-radius: 2px;} +#cardcolormenu {padding:2px 2px; width: 100px;} + +.kanban-col[data-type=ADD] {display: none;} +.kanban-col[data-type=EMPTY] {display: none;} +.kanban-item:hover .title {padding-right: 10px;} +.gray .actions {display:none;} + +.c-type {width: 150px !important; overflow: unset;} +.c-group {width: 190px !important; overflow: unset;} +.c-type {margin-left: 0px !important;} + +#kanbanScaleControl {width: 100px;} +#kanbanScaleControl .input-group-addon {background: #fff; padding: 5px;} +#kanbanScaleControl > .input-group-btn:first-child > .btn {border-radius: 16px 0 0 16px; border-right-color: transparent;} +#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;} +#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;} +#kanbanActionMenu {top: 24px; right: auto;} .kanban + .kanban {margin-top: 15px} .kanban-card > .title {display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis} @@ -16,43 +95,13 @@ .kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;} .kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;} -.kanban-header-col > .actions {padding-top: 22px} -.kanban-header-parent-col .kanban-header-col > .actions {padding-top: 6px; padding-right: 2px;} -.kanban-header-col > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;} -.kanban-header-col > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;} +#kanban .kanban-card[data-scale-size="4"] {padding: 8px;} +#kanban .kanban-card[data-scale-size="3"] {padding: 3px 4px;} +#kanban .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;} +#kanban .kanban-card[data-scale-size="2"] {padding: 3px 4px; overflow: hidden;} +#kanban .kanban-card[data-scale-size="2"] > .title {display: inline; line-height: 18px; white-space: normal; max-height: 100%; word-break: break-all;} +#kanban .kanban-card[data-scale-size="2"] > .infos {display: inline-block; height: 18px; vertical-align: top; margin: 0;} +#kanban .kanban-card[data-scale-size="2"] > .infos > .info-pri {transform: scale(.8); vertical-align: top;} +#kanban .kanban-card[data-scale-size="2"] > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -2px; vertical-align: top; margin-right: -1px;} -.kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;} -.kanban-lane-name:hover > .actions {opacity: 1;} -.kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;} -.kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;} - -.kanban-affixed .kanban-header-col > .title > .text, -.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;} - -#kanbanContainer {margin: 0;} -#kanbanContainer > .panel-body {overflow: auto;} -#kanbans .kanban {min-height: initial;} -#kanbans .kanban-lane {min-height: 150px; margin-top: 2px;} -#kanbans .kanban-affixed > .kanban-header {top: 100px;} -#kanbans .kanban-card[data-scale-size="4"] {padding: 8px;} -#kanbans .kanban-card[data-scale-size="3"] {padding: 3px 4px;} -#kanbans .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;} -#kanbans .kanban-card[data-scale-size="2"] {padding: 3px 4px; overflow: hidden;} -#kanbans .kanban-card[data-scale-size="2"] > .title {display: inline; line-height: 18px; white-space: normal; max-height: 100%; word-break: break-all;} -#kanbans .kanban-card[data-scale-size="2"] > .infos {display: inline-block; height: 18px; vertical-align: top; margin: 0;} -#kanbans .kanban-card[data-scale-size="2"] > .infos > .info-pri {transform: scale(.8); vertical-align: top;} -#kanbans .kanban-card[data-scale-size="2"] > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -2px; vertical-align: top; margin-right: -1px;} - -#kanbanScaleControl {width: 100px;} -#kanbanScaleControl .input-group-addon {background: #fff; padding: 5px;} -#kanbanScaleControl > .input-group-btn:first-child > .btn {border-radius: 16px 0 0 16px; border-right-color: transparent;} -#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;} -#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;} - -#type_chosen .icon-kanban {padding-right: 10px; font-size: 15px;} - -.dropdown-menu a {text-align: left;} -.scrollbar-hover {max-height: 2000px; overflow: scroll !important;} -.c-type {width: 150px !important; overflow: unset;} -.c-group {width: 190px !important; overflow: unset;} -.c-type {margin-left: 0px !important;} +#mainMenu .divider {margin: 10px} diff --git a/module/execution/css/taskkanban.css b/module/execution/css/taskkanban.css new file mode 100644 index 0000000000..372183c4b4 --- /dev/null +++ b/module/execution/css/taskkanban.css @@ -0,0 +1,58 @@ +#main {padding-bottom: 10px;} +#main > .container {max-width: 1960px!important; padding: 0 10px} + +.kanban + .kanban {margin-top: 15px} +.kanban-card > .title {display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis} +.kanban-card > .infos {position: relative; margin-top: 5px} +.kanban-card > .infos > .info + .info {margin-left: 8px} +.kanban-card > .infos > .info-id, +.kanban-card > .infos > .info-deadline, +.kanban-card > .infos > .info-estimate {font-size: 12px; position: relative; top: 2px} +.kanban-card > .infos > .label-pri {min-width: 16px; line-height: 14px; height: 16px; padding: 0} +.kanban-card > .infos > .label-severity {transform: scale(.75)} +.kanban-card > .infos > .avatar {position: absolute; right: 0; top: 0} +.kanban-card > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;} +.kanban-card:hover > .actions {opacity: 1;} +.kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;} +.kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;} + +.kanban-header-col > .actions {padding-top: 22px} +.kanban-header-parent-col .kanban-header-col > .actions {padding-top: 6px; padding-right: 2px;} +.kanban-header-col > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;} +.kanban-header-col > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;} + +.kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;} +.kanban-lane-name:hover > .actions {opacity: 1;} +.kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;} +.kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;} + +.kanban-affixed .kanban-header-col > .title > .text, +.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;} + +#kanbanContainer {margin: 0;} +#kanbanContainer > .panel-body {overflow: auto;} +#kanbans .kanban {min-height: initial;} +#kanbans .kanban-lane {min-height: 150px; margin-top: 2px;} +#kanbans .kanban-affixed > .kanban-header {top: 100px;} +#kanbans .kanban-card[data-scale-size="4"] {padding: 8px;} +#kanbans .kanban-card[data-scale-size="3"] {padding: 3px 4px;} +#kanbans .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;} +#kanbans .kanban-card[data-scale-size="2"] {padding: 3px 4px; overflow: hidden;} +#kanbans .kanban-card[data-scale-size="2"] > .title {display: inline; line-height: 18px; white-space: normal; max-height: 100%; word-break: break-all;} +#kanbans .kanban-card[data-scale-size="2"] > .infos {display: inline-block; height: 18px; vertical-align: top; margin: 0;} +#kanbans .kanban-card[data-scale-size="2"] > .infos > .info-pri {transform: scale(.8); vertical-align: top;} +#kanbans .kanban-card[data-scale-size="2"] > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -2px; vertical-align: top; margin-right: -1px;} + +#kanbanScaleControl {width: 100px;} +#kanbanScaleControl .input-group-addon {background: #fff; padding: 5px;} +#kanbanScaleControl > .input-group-btn:first-child > .btn {border-radius: 16px 0 0 16px; border-right-color: transparent;} +#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;} +#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;} + +#type_chosen .icon-kanban {padding-right: 10px; font-size: 15px;} + +.dropdown-menu a {text-align: left;} +.scrollbar-hover {max-height: 2000px; overflow: scroll !important;} +.c-type {width: 150px !important; overflow: unset;} +.c-group {width: 190px !important; overflow: unset;} +.c-type {margin-left: 0px !important;} diff --git a/module/execution/js/create.js b/module/execution/js/create.js index 4420527143..2e76285f06 100644 --- a/module/execution/js/create.js +++ b/module/execution/js/create.js @@ -1,6 +1,6 @@ function setCopyProject(executionID) { - location.href = createLink('execution', 'create', 'projectID=&executionID=0©ExecutionID=' + executionID); + location.href = createLink('execution', 'create', 'projectID=' + projectID + '&executionID=0©ExecutionID=' + executionID); } $(function() diff --git a/module/execution/js/kanban.js b/module/execution/js/kanban.js index daf1724e18..b689c4576d 100644 --- a/module/execution/js/kanban.js +++ b/module/execution/js/kanban.js @@ -1,7 +1,314 @@ -function changeView(view) +/** + * Display the kanban in full screen. + * + * @access public + * @return void + */ +function fullScreen() { - var link = createLink('execution', 'kanban', "executionID=" + executionID + '&type=' + view); + var element = document.getElementById('kanbanContainer'); + var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullscreen; + + if(requestMethod) + { + var afterEnterFullscreen = function() + { + $('#kanbanContainer').addClass('fullscreen') + .on('scroll', tryUpdateKanbanAffix); + $('.actions').hide(); + $('.action').hide(); + $('.avatar').removeAttr('data-toggle'); + $('#kanbanContainer a.iframe').each(function() + { + if($(this).hasClass('iframe')) + { + var href = $(this).attr('href'); + $(this).removeClass('iframe'); + $(this).attr('href', 'javascript:void(0)'); + $(this).attr('href-bak', href); + } + }) + $('.kanban-group-header').hide(); + $(".title").attr("disabled", true).css("pointer-events", "none"); + $.cookie('isFullScreen', 1); + }; + + var whenFailEnterFullscreen = function(error) + { + exitFullScreen(); + }; + + try + { + var result = requestMethod.call(element); + if(result && (typeof result.then === 'function' || result instanceof window.Promise)) + { + result.then(afterEnterFullscreen).catch(whenFailEnterFullscreen); + } + else + { + afterEnterFullscreen(); + } + } + catch (error) + { + whenFailEnterFullscreen(error); + } + } +} + +/** + * Exit full screen. + * + * @access public + * @return void + */ +function exitFullScreen() +{ + $('#kanbanContainer').removeClass('fullscreen') + .off('scroll', tryUpdateKanbanAffix); + $('.actions').show(); + $('.action').show(); + $('.avatar').attr('data-toggle', 'modal'); + $('#kanbanContainer a').each(function() + { + var hrefBak = $(this).attr('href-bak'); + if(hrefBak) + { + $(this).addClass('iframe'); + $(this).attr('href', hrefBak); + } + }) + $('.kanban-group-header').show(); + $(".title").attr("disabled", false).css("pointer-events", "auto"); + $.cookie('isFullScreen', 0); +} + +document.addEventListener('fullscreenchange', function (e) +{ + if(!document.fullscreenElement) exitFullScreen(); +}); + +document.addEventListener('webkitfullscreenchange', function (e) +{ + if(!document.webkitFullscreenElement) exitFullScreen(); +}); + +document.addEventListener('mozfullscreenchange', function (e) +{ + if(!document.mozFullScreenElement) exitFullScreen(); +}); + +document.addEventListener('msfullscreenChange', function (e) +{ + if(!document.msfullscreenElement) exitFullScreen(); +}); + +/** Change kanban scale size */ +function changeKanbanScaleSize(newScaleSize) +{ + var newScaleSize = Math.max(1, Math.min(4, newScaleSize)); + if(newScaleSize === window.kanbanScaleSize) return; + + window.kanbanScaleSize = newScaleSize; + $.zui.store.set('executionKanbanScaleSize', newScaleSize); + $('#kanbanScaleSize').text(newScaleSize); + $('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', newScaleSize >= 4 ? 'disabled' : null); + $('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', newScaleSize <= 1 ? 'disabled' : null); + + $('#kanban').children('.region').children("div[id^='kanban']").each(function() + { + var kanban = $(this).data('zui.kanban'); + if(!kanban) return; + kanban.setOptions({cardsPerRow: newScaleSize, cardHeight: getCardHeight()}); + }); + + return newScaleSize; +} + +/** Get card height */ +function getCardHeight() +{ + return [59, 59, 62, 62, 47][window.kanbanScaleSize]; +} + +$('#type').change(function() +{ + var type = $('#type').val(); + if(type != 'all') + { + $('.c-group').show(); + $.get(createLink('execution', 'ajaxGetGroup', 'type=' + type), function(data) + { + $('#group_chosen').remove(); + $('#group').replaceWith(data); + $('#group').chosen(); + }) + } + + var link = createLink('execution', 'kanban', "executionID=" + executionID + '&browseType=' + type); location.href = link; +}); + +$('.c-group').change(function() +{ + $('.c-group').show(); + + var type = $('#type').val(); + var group = $('#group').val(); + var link = createLink('execution', 'kanban', 'executionID=' + executionID + '&type=' + type + '&orderBy=id_asc' + '&groupBy=' + group); + location.href = link; +}); + +/** + * Create lane menu. + * + * @param object $options + * @access public + * @return void + */ +function createLaneMenu(options) +{ + var lane = options.$trigger.closest('.kanban-lane').data('lane'); + var privs = lane.actions; + if(!privs.length) return []; + + var items = []; + if(privs.includes('setLane')) items.push({label: kanbanLang.setLane, icon: 'edit', url: createLink('kanban', 'setLane', 'laneID=' + lane.id + '&executionID=0&from=kanban'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '635px'}}); + if(privs.includes('deleteLane')) items.push({label: kanbanLang.deleteLane, icon: 'trash', url: createLink('kanban', 'deleteLane', 'lane=' + lane.id), attrs: {'target': 'hiddenwin'}}); + + var bounds = options.$trigger[0].getBoundingClientRect(); + items.$options = {x: bounds.right, y: bounds.top}; + return items; +} + +function createColumnMenu(options) +{ + var column = options.$trigger.closest('.kanban-col').data('col'); + var privs = column.actions; + if(!privs.length) return []; + + var items = []; + if(privs.includes('setColumn')) items.push({label: kanbanLang.editColumn, icon: 'edit', url: createLink('kanban', 'setColumn', 'columnID=' + column.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal'}}); + if(privs.includes('setWIP')) items.push({label: kanbanLang.setWIP, icon: 'alert', url: createLink('kanban', 'setWIP', 'columnID=' + column.id), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width' : '500px'}}); + + var bounds = options.$trigger[0].getBoundingClientRect(); + items.$options = {x: bounds.right, y: bounds.top}; + return items; +} + +/** + * Create column create button menu + * @returns {Object[]} + */ +function createColumnCreateMenu(options) +{ + var $col = options.$trigger.closest('.kanban-col'); + var col = $col.data('col'); + var items = []; + var laneID = col.$kanbanData.lanes[0].id ? col.$kanbanData.lanes[0].id : 0; + + if(col.type == 'backlog') + { + if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&objectID=' + executionID + '&bugID=0&planID=0&todoID=0&extra=laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}}); + if(priv.canBatchCreateStory) items.push({label: executionLang.batchCreateStroy, url: $.createLink('story', 'batchcreate', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&executionID=' + executionID + '&plan=0&type=story&extra=laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '90%'}}); + if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID + '&browseType=¶m=0&recTotal=0&recPerPage=50,&pageID=1&extra=laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '90%'}}); + if(priv.canLinkStoryByPlane) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal', 'data-target': '#linkStoryByPlan','data-col' : col.id, 'data-lane' : laneID, 'class' : 'linkStoryByPlanButton'}}); + } + else if(col.type == 'unconfirmed') + { + if(priv.canCreateBug) items.push({label: bugLang.create, url: $.createLink('bug', 'create', 'productID=0&moduleID=0&extra=laneID=' + laneID + ',columnID=' + col.id + ',executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}}); + if(priv.canBatchCreateBug) items.push({label: bugLang.batchCreate, url: $.createLink('bug', 'batchcreate', 'productID=' + productID + '&branch=&executionID=' + executionID + '&module=0&extra=laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '90%'}}); + } + else if(col.type == 'wait') + { + if(priv.canCreateTask) items.push({label: taskLang.create, url: $.createLink('task', 'create', 'executionID=' + executionID + "&storyID=0&moduleID=0&taskID=0&todoID=0&extra=laneID=" + laneID + ",columnID=" + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}}); + if(priv.canBatchCreateTask) items.push({label: taskLang.batchCreate, url: $.createLink('task', 'batchcreate', 'executionID=' + executionID + "&storyID=0&moduleID=0&taskID=0&iframe=0&extra=laneID=" + laneID + ",columnID=" + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}}); + } + return items; +} + +/** + * Hide kanban action + */ +function hideKanbanAction() +{ + $('.kanban').attr('data-action-enabled', null); + $('.contextmenu').removeClass('contextmenu-show'); + $('.contextmenu .contextmenu-menu').removeClass('open').removeClass('in'); + $('#moreTasks, #moreColumns').animate({right: -400}, 500); +} + +/** + * Handle finish drop task + */ +function handleFinishDrop() +{ + $('.kanban').find('.can-drop-here').removeClass('can-drop-here'); +} + +/* Define drag and drop rules */ +if(!window.kanbanDropRules) +{ + window.kanbanDropRules = + { + story: + { + backlog: ['ready', 'backlog'], + ready: ['backlog', 'ready'], + }, + bug: + { + 'unconfirmed': ['unconfirmed', 'confirmed', 'fixing', 'fixed'], + 'confirmed': ['confirmed', 'fixing', 'fixed'], + 'fixing': ['fixing', 'fixed'], + 'fixed': ['fixed', 'testing', 'tested', 'fixing'], + 'testing': ['testing', 'tested', 'closed', 'fixing'], + 'tested': ['tested', 'closed', 'fixing'], + 'closed': ['closed', 'fixing'], + }, + task: + { + 'wait': ['wait', 'developing', 'developed', 'canceled'], + 'developing': ['developing', 'developed', 'pause', 'canceled'], + 'developed': ['developed', 'developing', 'closed'], + 'pause': ['pause', 'developing', 'developed', 'canceled'], + 'canceled': ['canceled', 'developing', 'closed'], + 'closed': ['closed', 'developing'], + } + } +} + +/* + * Find drop columns + * @param {JQuery} $element Drag element + * @param {JQuery} $root Dnd root element + */ +function findDropColumns($element, $root) +{ + var $col = $element.closest('.kanban-col'); + var col = $col.data(); + var laneType = $element.closest('.kanban-lane').data().lane.type; + var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules[laneType] : null; + + if(!kanbanRules) return $root.find('.kanban-lane-col:not([data-type="' + col.type + '"])'); + + var colRules = kanbanRules[col.type]; + var groupID = $col.closest('.kanban-board').data().id; + return $root.find('.kanban-lane-col').filter(function() + { + if(!colRules) return false; + if(colRules === true) return true; + if($.cookie('isFullScreen') == 1) return false; + + var $newCol = $(this); + var newCol = $newCol.data(); + var newGroupID = $newCol.closest('.kanban-board').data().id; + + var canDropHere = colRules.indexOf(newCol.type) > -1 && newGroupID === groupID; + if(canDropHere) $newCol.addClass('can-drop-here'); + return canDropHere; + }); } /** @@ -29,17 +336,17 @@ function renderUserAvatar(user, objectType, objectID, size) var link = createLink('bug', 'assignto', 'id=' + objectID, '', true); } - if(!user) return $(''); + if(!user) return $(''); if(typeof user === 'string') user = {account: user}; - if(!user.avatar && window.userList && window.userList[user.account]) user = window.userList[user.account]; + if(!user.avatar && window.userList && window.userList[user.account]) user = {avatar: userList[user.account].avatar, account: user.account, realname: userList[user.account].realname}; var $noPrivAvatar = $('
').avatar({user: user}); if(objectType == 'task' && !priv.canAssignTask) return $noPrivAvatar; if(objectType == 'story' && !priv.canAssignStory) return $noPrivAvatar; if(objectType == 'bug' && !priv.canAssignBug) return $noPrivAvatar; - return $('').avatar({user: user}); + return $('').avatar({user: user}).attr('data-toggle', 'modal').attr('data-width', '80%'); } /** @@ -81,7 +388,7 @@ function renderStoryItem(item, $item, col) if(!$title.length) { $title = $('' + (scaleSize <= 1 ? ' ' : '') + '') - .attr('href', $.createLink('story', 'view', 'storyID=' + item.id, '', true)); + .attr('href', $.createLink('story', 'view', 'storyID=' + item.id + '&version=0¶m=' + execution.id, '', true)).attr('data-toggle', 'modal').attr('data-width', '80%'); $title.appendTo($item); } $title.attr('title', item.title).find('.text').text(item.title); @@ -110,7 +417,7 @@ function renderStoryItem(item, $item, col) if(scaleSize <= 1) { var $actions = $item.find('.actions'); - if(!$actions.length && item.menus.length) + if(!$actions.length && item.menus && item.menus.length) { $actions = $([ '
', @@ -143,7 +450,7 @@ function renderBugItem(item, $item, col) if(!$title.length) { $title = $('' + (scaleSize <= 1 ? ' ' : '') + '') - .attr('href', $.createLink('bug', 'view', 'bugID=' + item.id, '', true)); + .attr('href', $.createLink('bug', 'view', 'bugID=' + item.id, '', true)).attr('data-toggle', 'modal').attr('data-width', '80%'); $title.appendTo($item); } $title.attr('title', item.title).find('.text').text(item.title); @@ -174,7 +481,7 @@ function renderBugItem(item, $item, col) if(scaleSize <= 1) { var $actions = $item.find('.actions'); - if(!$actions.length && item.menus.length) + if(!$actions.length && item.menus && item.menus.length) { $actions = $([ '
', @@ -207,7 +514,7 @@ function renderTaskItem(item, $item, col) if(!$title.length) { $title = $('' + (scaleSize <= 1 ? ' ' : '') + '') - .attr('href', $.createLink('task', 'view', 'taskID=' + item.id, '', true)); + .attr('href', $.createLink('task', 'view', 'taskID=' + item.id, '', true)).attr('data-toggle', 'modal').attr('data-width', '80%'); $title.appendTo($item); } $title.attr('title', item.name).find('.text').text(item.name); @@ -238,7 +545,7 @@ function renderTaskItem(item, $item, col) if(scaleSize <= 1) { var $actions = $item.find('.actions'); - if(!$actions.length && item.menus.length) + if(!$actions.length && item.menus && item.menus.length) { $actions = $([ '
', @@ -261,278 +568,219 @@ addColumnRenderer('bug', renderBugItem); addColumnRenderer('task', renderTaskItem); /** - * Render column count - * @param {JQuery} $count Kanban count element - * @param {number} count Column cards count - * @param {number} col Column object - * @param {Object} kanban Kanban intance + * Render items count of a column. */ -function renderColumnCount($count, count, col) +function renderCount($count, count, column) { - var text = count + '/' + (col.limit < 0 ? '' : col.limit); - $count.html(text + ''); + /* Render WIP. */ + var limit = !column.limit || column.limit == '-1' ? '' : column.limit; + if($count.parent().find('.limit').length) + { + $count.parent().find('.limit').html(limit); + } + else + { + $count.parent().find('.count').before("("); + $count.parent().find('.count').after("/" + limit + ")"); + } + + if(column.limit != -1 && column.limit < count) + { + $count.parents('.title').parent('.kanban-header-col').css('background-color', '#F6A1A1'); + $count.parents('.title').find('.text').css('max-width', $count.parents('.title').width() - 200); + $count.css('color', '#E33030'); + if(!$count.parent().find('.error').length) $count.parent().find('.include-last').after(""); + } + else + { + $count.parents('.title').parent('.kanban-header-col').css('background-color', 'transparent'); + $count.parents('.title').find('.text').css('max-width', $count.parents('.title').width() - 120); + $count.css('color', '#8B91A2'); + $count.parent().find('.error').remove(); + } } /** - * Render header column - * @param {JQuery} $col Header column element - * @param {Object} col Header column object - * @param {JQuery} $header Header element - * @param {Object} kanban Kanban object + * Render header of a column. */ -function renderHeaderCol($col, col, $header, kanban) +function renderHeaderCol($column, column, $header, kanbanData) { - if(col.asParent) $col = $col.children('.kanban-header-col'); - var $actions = $('
'); - var printStoryButton = printTaskButton = printBugButton = false; - if(priv.canCreateStory || priv.canBatchCreateStory || priv.canLinkStory || priv.canLinkStoryByPlane) printStoryButton = true; - if(priv.canCreateTask || priv.canBatchCreateTask) printTaskButton = true; - if(priv.canCreateBug || priv.canBatchCreateBug) printBugButton = true; + if(groupBy != 'default') return; - if((col.type === 'backlog' && printStoryButton) || (col.type === 'wait' && printTaskButton) || (col.type == 'unconfirmed' && printBugButton)) + /* Render group header. */ + var privs = kanbanData.actions; + var columnPrivs = kanbanData.columns[0].actions; + var $actions = $column.children('.actions'); + + if(column.parent == -1) + { + $column.append('
'); + $actions = $column.children('.actions'); + } + + if(privs.includes('sortGroup')) + { + var groups = regions[column.region].groups; + if($header.closest('.kanban').data('zui.kanban')) + { + groups = $header.closest('.kanban').data('zui.kanban').data; + } + if(groups.length > 1) + { + $column.closest('.kanban-board').addClass('sort'); + $column.closest('.kanban-header').find('.kanban-group-header').remove(); + $column.closest('.kanban-header').prepend('
'); + } + } + + var printMoreBtn = (columnPrivs.includes('setColumn') || columnPrivs.includes('setWIP')); + + /* Render more menu. */ + if(((column.type == 'backlog' && hasStoryButton) || (column.type == 'wait' && hasTaskButton) || (column.type == 'unconfirmed' && hasBugButton)) && $actions.children('.text-primary').length == 0) { $actions.append([ - '', + '', '', '' ].join('')); } - - $actions.append([ - '', - '', - '' - ].join('')); - $actions.appendTo($col); -} - -/** - * Render lane name - * @param {JQuery} $name Name element - * @param {Object} lane Lane object - * @param {JQuery} $kanban $kanban element - * @param {Object} columns Kanban columns - * @param {Object} kanban Kanban object - */ -function renderLaneName($name, lane, $kanban, columns, kanban) -{ - if(lane.id != 'story' && lane.id != 'task' && lane.id != 'bug') return false; - if(!$name.children('.actions').length && (priv.canSetLane || priv.canMoveLane)) + if(printMoreBtn && $actions.children('.btn').length == 0) { - $([ - '
', - '', - '', - '', - '
' - ].join('')).appendTo($name); + $actions.append(' '); } } /** - * Updata kanban data - * @param {string} kanbanID Kanban id - * @param {Object} data Kanban data - */ -function updateKanban(kanbanID, data) -{ - var $kanban = $('#kanban-' + kanbanID); - if(!$kanban.length) return; - - $kanban.data('zui.kanban').render(data); -} - -/** - * Create kanban in page - * @param {string} kanbanID Kanban id - * @param {Object} data Kanban data - * @param {Object} options Kanban options - */ -function createKanban(kanbanID, data, options) -{ - var $kanban = $('#kanban-' + kanbanID); - if($kanban.length) return updateKanban(kanbanID, data); - - $kanban = $('
').appendTo('#kanbans'); - $kanban.kanban($.extend({data: data}, options)); -} - -function fullScreen() -{ - var element = document.getElementById('kanbanContainer'); - var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen; - if(requestMethod) - { - var afterEnterFullscreen = function() - { - $('#kanbanContainer').addClass('scrollbar-hover'); - $('.actions').hide(); - $('#kanbanContainer a.iframe').each(function() - { - if($(this).hasClass('iframe')) - { - var href = $(this).attr('href'); - $(this).removeClass('iframe'); - $(this).attr('href', 'javascript:void(0)'); - $(this).attr('href-bak', href); - } - }) - $.cookie('isFullScreen', 1); - } - - var whenFailEnterFullscreen = function() - { - exitFullScreen(); - } - - try - { - var result = requestMethod.call(element); - if(result && (typeof result.then === 'function' || result instanceof window.Promise)) - { - result.then(afterEnterFullscreen).catch(whenFailEnterFullscreen); - } - else - { - afterEnterFullscreen(); - } - } - catch (error) - { - whenFailEnterFullscreen(error); - } - } -} - -/** - * Exit full screen. + * Render lane name. * + * @param object $lane + * @param int lane + * @param object $kanban + * @param array columns + * @param object $kanban * @access public * @return void */ -function exitFullScreen() +function renderLaneName($lane, lane, $kanban, columns, kanban) { - $('#kanbanContainer').removeClass('scrollbar-hover'); - $('.actions').show(); - $('#kanbanContainer a').each(function() + if(groupBy != 'default') return; + var canSet = lane.actions.includes('setLane'); + var canSort = lane.actions.includes('sortLane') && kanban.lanes.length > 1; + var canDelete = lane.actions.includes('deleteLane'); + + $lane.parent().toggleClass('sort', canSort); + + if(!$lane.children('.actions').length && (canSet || canDelete)) { - var hrefBak = $(this).attr('href-bak'); - if(hrefBak) - { - $(this).addClass('iframe'); - $(this).attr('href', hrefBak); - } - }) - $.cookie('isFullScreen', 0); -} - -document.addEventListener('fullscreenchange', function (e) -{ - if(!document.fullscreenElement) exitFullScreen(); -}); - -document.addEventListener('webkitfullscreenchange', function (e) -{ - if(!document.webkitFullscreenElement) exitFullScreen(); -}); - -document.addEventListener('mozfullscreenchange', function (e) -{ - if(!document.mozFullScreenElement) exitFullScreen(); -}); - -document.addEventListener('msfullscreenChange', function (e) -{ - if(!document.msfullscreenElement) exitFullScreen(); -}); - -/* Define drag and drop rules */ -if(!window.kanbanDropRules) -{ - window.kanbanDropRules = - { - story: - { - backlog: ['ready'], - ready: ['backlog'], - }, - bug: - { - 'unconfirmed': ['confirmed', 'fixing', 'fixed'], - 'confirmed': ['fixing', 'fixed'], - 'fixing': ['fixed'], - 'fixed': ['testing', 'tested', 'fixing'], - 'testing': ['tested', 'closed', 'fixing'], - 'tested': ['closed', 'fixing'], - 'closed': ['fixing'], - }, - task: - { - 'wait': ['developing', 'developed', 'canceled', 'closed'], - 'developing': ['developed', 'pause'], - 'developed': ['canceled', 'closed'], - 'pause': ['developing'], - 'canceled': ['developing'], - 'closed': ['developing'], - } + $([ + '
', + '', + '', + '', + '
' + ].join('')).appendTo($lane); } } -/* - * Find drop columns - * @param {JQuery} $element Drag element - * @param {JQuery} $root Dnd root element +/** + * Update a region. + * + * @param int regionID + * @param array regionData + * @access public + * @return boolean */ -function findDropColumns($element, $root) +function updateRegion(regionID, regionData = []) { - var $col = $element.closest('.kanban-col'); - var col = $col.data(); - var kanbanID = $root.data('id'); - var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules[kanbanID] : null; + if(!regionID) return false; - if(!kanbanRules) return $root.find('.kanban-lane-col:not([data-type="' + col.type + '"])'); + var $region = $('#kanban'+ regionID).kanban(); - var colRules = kanbanRules[col.type]; - var lane = $col.closest('.kanban-lane').data('lane'); - return $root.find('.kanban-lane-col').filter(function() - { - if(!colRules) return false; - if(colRules === true) return true; + if(!$region.length) return false; + if(!regionData) regionData = regions[regionID]; - var $newCol = $(this); - var newCol = $newCol.data(); - if(newCol.id === col.id) return false; - - var $newLane = $newCol.closest('.kanban-lane'); - var newLane = $newLane.data('lane'); - var canDropHere = colRules.indexOf(newCol.type) > -1 && newLane.id === lane.id; - if(canDropHere) $newCol.addClass('can-drop-here'); - return canDropHere; - }); + $region.data('zui.kanban').render(regionData.groups); + return true; } /** - * Change column type for a card - * @param {Object} card Card object - * @param {String} fromColType The column type before change - * @param {String} toColType The column type after change - * @param {String} kanbanID Kanban ID + * Handle drop task. + * + * @param object $element + * @param object $event + * @param object $kanban + * @access public + * @return void */ -function changeCardColType(card, fromColType, toColType, kanbanID) +function handleDropTask($element, event, kanban) { - if(typeof card == 'undefined') return false; - var objectID = card.id; + if(!event.target || !event.isNew) return; + + var $card = $element; + var $oldCol = $card.closest('.kanban-col'); + var $newCol = $(event.target).closest('.kanban-col'); + var oldCol = $oldCol.data(); + var newCol = $newCol.data(); + var oldLane = $oldCol.closest('.kanban-lane').data('lane'); + var newLane = $newCol.closest('.kanban-lane').data('lane'); + var cardType = $card.find('.kanban-card').data('type'); + + if(oldCol.id === newCol.id && newLane.id === oldLane.id) return false; + + var cardID = $card.data().id; + var fromColType = $oldCol.data('type'); + var toColType = $newCol.data('type'); + var regionID = $card.closest('.region').data().id; + + changeCardColType(cardID, oldCol.id, newCol.id, oldLane.id, newLane.id, cardType, fromColType, toColType, regionID); +} + +var kanbanActionHandlers = +{ + dropItem: handleDropTask +}; + +/** + * Handle kanban action + */ +function handleKanbanAction(action, $element, event, kanban) +{ + if(groupBy && groupBy != 'default') return false; + + $('.kanban').attr('data-action-enabled', action); + var handler = kanbanActionHandlers[action]; + if(handler) handler($element, event, kanban); +} + +/** + * changeCardColType + * + * @param int cardID + * @param int fromColID + * @param int toColID + * @param int fromLaneID + * @param int toLaneID + * @param string cardType + * @param string fromColType + * @param string toColType + * @param int regionID + * @access public + * @return void + */ +function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, cardType, fromColType, toColType, regionID = 0) +{ + var objectID = cardID; var showIframe = false; var moveCard = false; /* Task lane. */ - if(kanbanID == 'task') + if(cardType == 'task') { if(toColType == 'developed') { if((fromColType == 'developing' || fromColType == 'wait') && priv.canFinishTask) { - var link = createLink('task', 'finish', 'taskID=' + objectID, '', true); + var link = createLink('task', 'finish', 'taskID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } @@ -540,20 +788,20 @@ function changeCardColType(card, fromColType, toColType, kanbanID) { if(fromColType == 'developing' && priv.canPauseTask) { - var link = createLink('task', 'pause', 'taskID=' + objectID, '', true); + var link = createLink('task', 'pause', 'taskID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } else if(toColType == 'developing') { - if((fromColType == 'pause' || fromColType == 'cancel' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask) + if((fromColType == 'pause' || fromColType == 'canceled' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask) { - var link = createLink('task', 'activate', 'taskID=' + objectID, '', true); + var link = createLink('task', 'activate', 'taskID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } if(fromColType == 'wait' && priv.canStartTask) { - var link = createLink('task', 'start', 'taskID=' + objectID, '', true); + var link = createLink('task', 'start', 'taskID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } @@ -561,7 +809,7 @@ function changeCardColType(card, fromColType, toColType, kanbanID) { if((fromColType == 'developing' || fromColType == 'wait' || fromColType == 'pause') && priv.canCancelTask) { - var link = createLink('task', 'cancel', 'taskID=' + objectID, '', true); + var link = createLink('task', 'cancel', 'taskID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } @@ -569,20 +817,20 @@ function changeCardColType(card, fromColType, toColType, kanbanID) { if((fromColType == 'developed' || fromColType == 'canceled') && priv.canCloseTask) { - var link = createLink('task', 'close', 'taskID=' + objectID, '', true); + var link = createLink('task', 'close', 'taskID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } } /* Bug lane. */ - if(kanbanID == 'bug') + if(cardType == 'bug') { if(toColType == 'confirmed') { if(fromColType == 'unconfirmed' && priv.canConfirmBug) { - var link = createLink('bug', 'confirmBug', 'bugID=' + objectID, '', true); + var link = createLink('bug', 'confirmBug', 'bugID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } @@ -591,15 +839,15 @@ function changeCardColType(card, fromColType, toColType, kanbanID) if(fromColType == 'confirmed' || fromColType == 'unconfirmed') moveCard = true; if((fromColType == 'closed' || fromColType == 'fixed' || fromColType == 'testing' || fromColType == 'tested') && priv.canActivateBug) { - var link = createLink('bug', 'activate', 'bugID=' + objectID, '', true); + var link = createLink('bug', 'activate', 'bugID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } else if(toColType == 'fixed') { - if(fromColType == 'fixing' || fromColType == 'confirmed' || fromColType == 'unconfirmed') + if(fromColType == 'fixing' || fromColType == 'confirmed' || fromColType == 'unconfirmed') { - var link = createLink('bug', 'resolve', 'bugID=' + objectID, '', true); + var link = createLink('bug', 'resolve', 'bugID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } @@ -615,55 +863,51 @@ function changeCardColType(card, fromColType, toColType, kanbanID) { if(fromColType == 'testing' || fromColType == 'tested') { - var link = createLink('bug', 'close', 'bugID=' + objectID, '', true); + var link = createLink('bug', 'close', 'bugID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID, '', true); showIframe = true; } } - if(moveCard) + if(moveCard || (fromLaneID != toLaneID && fromColID == toColID)) { - var colID = card.$col.columnID; - var link = createLink('kanban', 'ajaxMoveCard', 'cardID=' + objectID + '&colID=' + colID + '&toColType=' + toColType + '&execitionID=' + executionID + '&browseType=' + browseType + '&groupBy=' + groupBy); - $.get(link, function(data) + var link = createLink('kanban', 'ajaxMoveCard', 'cardID=' + objectID + '&fromColID=' + fromColID + '&toColID=' + toColID + '&fromLaneID=' + fromLaneID + '&toLaneID=' + toLaneID + '&execitionID=' + executionID + '&browseType=' + browseType + '&groupBy=' + groupBy + '®ionID=' + regionID + '&orderBy=' + orderBy ); + $.ajax( { - if(data) + method: 'post', + dataType: 'json', + url: link, + success: function(data) { - kanbanGroup = $.parseJSON(data); - if(groupBy == 'default') - { - updateKanban('bug', kanbanGroup.bug); - } - else - { - updateKanban(browseType, kanbanGroup[groupBy]); - } + updateRegion(regionID, data[regionID]); + }, + error: function(xhr, status, error) + { + showErrorMessager(error || lang.timeout); } - }) + }); } } /* Story lane. */ - if(kanbanID == 'story') + if(cardType == 'story') { if(toColType == 'ready' || toColType == 'backlog') { - var colID = card.$col.columnID; - var link = createLink('kanban', 'ajaxMoveCard', 'cardID=' + objectID + '&colID=' + colID + '&toColType=' + toColType + '&execitionID=' + executionID + '&browseType=' + browseType + '&groupBy=' + groupBy); - $.get(link, function(data) + var link = createLink('kanban', 'ajaxMoveCard', 'cardID=' + objectID + '&fromColID=' + fromColID + '&toColID=' + toColID + '&fromLaneID=' + fromLaneID + '&toLaneID=' + toLaneID + '&execitionID=' + executionID + '&browseType=' + browseType + '&groupBy=' + groupBy + '®ionID=' + regionID+ '&orderBy=' + orderBy ); + $.ajax( { - if(data) + method: 'post', + dataType: 'json', + url: link, + success: function(data) { - kanbanGroup = $.parseJSON(data); - if(groupBy == 'default') - { - updateKanban('story', kanbanGroup.story); - } - else - { - updateKanban(browseType, kanbanGroup[groupBy]); - } + updateRegion(regionID, data[regionID]); + }, + error: function(xhr, status, error) + { + showErrorMessager(error || lang.timeout); } - }) + }); } } @@ -674,313 +918,89 @@ function changeCardColType(card, fromColType, toColType, kanbanID) } } -/** - * Handle finish drop task - * @param {Object} event Event object - * @returns {void} - */ -function handleFinishDrop(event) +function processMinusBtn() { - var $card = $(event.element); // The drag card - var $dragCol = $card.closest('.kanban-lane-col'); - var $dropCol = $(event.target); - - /* Get d-n-d(drag and drop) infos */ - var card = $card.data('item'); - var fromColType = $dragCol.data('type'); - var toColType = $dropCol.data('type'); - var kanbanID = $card.closest('.kanban').data('id'); - - changeCardColType(card, fromColType, toColType, kanbanID); - - $('#kanbans').find('.can-drop-here').removeClass('can-drop-here'); -} - -/** Handle sort cards in column */ -function handleSortColCards() -{ - /* TODO: handle sort cards from column contextmenu */ - return false; -} - -/** - * Create column menu - * @returns {Object[]} - */ -function createColumnMenu(options) -{ - var $col = options.$trigger.closest('.kanban-col'); - var col = $col.data('col'); - var kanbanID = options.kanban; - - var items = []; - if(priv.canEditName) items.push({label: executionLang.editName, url: $.createLink('kanban', 'setColumn', 'col=' + col.columnID + '&executionID=' + executionID + '&from=execution'), className: 'iframe', attrs: {'data-width': '500px'}}) - if(priv.canSetWIP) items.push({label: executionLang.setWIP, url: $.createLink('kanban', 'setWIP', 'col=' + col.columnID + '&executionID=' + executionID + '&from=execution'), className: 'iframe', attrs: {'data-width': '500px'}}) - //if(priv.canSortCards) items.push({label: executionLang.sortColumn, items: ['按ID倒序', '按ID顺序'], className: 'iframe', onClick: handleSortColCards}) - return items; -} - -/** - * Create column create button menu - * @returns {Object[]} - */ -function createColumnCreateMenu(options) -{ - var $col = options.$trigger.closest('.kanban-col'); - var col = $col.data('col'); - var items = []; - - if(col.laneType == 'story') + var columnCount = $('#splitTable .child-column').size(); + if(columnCount > 2 && columnCount < 10) { - if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID, '', true), className: 'iframe'}); - if(priv.canBatchCreateStory) items.push({label: executionLang.batchCreateStroy, url: $.createLink('story', 'batchcreate', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}}); - if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}}); - if(priv.canLinkStoryByPlane) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal'}}); + $('#splitTable .btn-plus').show(); + $('#splitTable .btn-close').show(); } - else if(col.laneType == 'bug') + else if(columnCount <= 2) { - if(priv.canCreateBug) items.push({label: bugLang.create, url: $.createLink('bug', 'create', 'productID=0&moduleID=0&extra=executionID=' + executionID, '', true), className: 'iframe'}); - if(priv.canBatchCreateBug) items.push({label: bugLang.batchCreate, url: $.createLink('bug', 'batchcreate', 'productID=' + productID + '&moduleID=0&executionID=' + executionID, '', true), className: 'iframe'}); + $('#splitTable .btn-close').hide(); } - else + else if(columnCount >= 10) { - if(priv.canCreateTask) items.push({label: taskLang.create, url: $.createLink('task', 'create', 'executionID=' + executionID, '', true), className: 'iframe'}); - if(priv.canBatchCreateTask) items.push({label: taskLang.batchCreate, url: $.createLink('task', 'batchcreate', 'executionID=' + executionID, '', true), className: 'iframe'}); + $('#splitTable .btn-plus').hide(); } - return items; -} - -/** - * Create lane menu - * @returns {Object[]} - */ -function createLaneMenu(options) -{ - var $lane = options.$trigger.closest('.kanban-lane'); - var $kanban = $lane.closest('.kanban'); - var lane = $lane.data('lane'); - var kanbanID = options.kanban; - var upTargetKanban = $kanban.prev('.kanban').length ? $kanban.prev('.kanban').data('id') : ''; - var downTargetKanban = $kanban.next('.kanban').length ? $kanban.next('.kanban').data('id') : ''; - - var items = []; - if(priv.canSetLane) items.push({label: kanbanLang.setLane, icon: 'edit', url: $.createLink('kanban', 'setLane', 'lane=' + lane.laneID + '&executionID=' + executionID + '&from=execution'), className: 'iframe'}); - if(priv.canMoveLane) items.push( - {label: kanbanLang.moveUp, icon: 'arrow-up', url: $.createLink('kanban', 'laneMove', 'executionID=' + executionID + '¤tLane=' + lane.id + '&targetLane=' + upTargetKanban), className: 'iframe', disabled: !$kanban.prev('.kanban').length}, - {label: kanbanLang.moveDown, icon: 'arrow-down', url: $.createLink('kanban', 'laneMove', 'executionID=' + executionID + '¤tLane=' + lane.id + '&targetLane=' + downTargetKanban), className: 'iframe', disabled: !$kanban.next('.kanban').length} - ); - - var bounds = options.$trigger[0].getBoundingClientRect(); - items.$options = {x: bounds.right, y: bounds.top}; - return items; -} - -/** - * Create story menu - * @returns {Object[]} - */ -function createStoryMenu(options) -{ - var $card = options.$trigger.closest('.kanban-item'); - var story = $card.data('item'); - - var items = []; - $.each(story.menus, function() - { - var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}}; - if(this.size) item.attrs['data-width'] = this.size; - - if(this.icon == 'unlink') item = {label: this.label, icon: this.icon, url: this.url, attrs: {'target': 'hiddenwin'}}; - items.push(item); - }); - - return items; -} - -/** - * Create bug menu - * @returns {Object[]} - */ -function createBugMenu(options) -{ - var $card = options.$trigger.closest('.kanban-item'); - var bug = $card.data('item'); - - var items = []; - $.each(bug.menus, function() - { - var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}}; - if(this.size) item.attrs['data-width'] = this.size; - - items.push(item); - }); - - return items; -} - - /** - * Create task menu - * @returns {Object[]} - */ -function createTaskMenu(options) -{ - var $card = options.$trigger.closest('.kanban-item'); - var task = $card.data('item'); - - var items = []; - $.each(task.menus, function() - { - var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}}; - if(this.size) item.attrs['data-width'] = this.size; - - items.push(item); - }); - - return items; -} - -/** Resize kanban container size */ -function resizeKanbanContainer() -{ - var $container = $('#kanbanContainer'); - var maxHeight = window.innerHeight - 98 - 15; - if($.cookie('isFullScreen') == 1) maxHeight = window.innerHeight - 15; - $container.children('.panel-body').css('max-height', maxHeight); } /* Define menu creators */ window.menuCreators = { - column: createColumnMenu, - columnCreate: createColumnCreateMenu, lane: createLaneMenu, - story: createStoryMenu, - bug: createBugMenu, - task: createTaskMenu, + column: createColumnMenu, + columnCreate: createColumnCreateMenu }; -/* Set kanban affix container */ -window.kanbanAffixContainer = '#kanbanContainer>.panel-body'; - -/* Overload kanban default options */ -$.extend($.fn.kanban.Constructor.DEFAULTS, +/** + * init Kanban + */ +/** + * Init kanban. + * + * @param object $kanban + * @access public + * @return void + */ +function initKanban($kanban) { - onRender: function() + var id = $kanban.data('id'); + var region = regions[id]; + var displayCards = window.displayCards == 'undefined' ? 2 : window.displayCards; + + $kanban.kanban( { - var maxWidth = 0; - $('#kanbans .kanban-board').each(function() - { - maxWidth = Math.max(maxWidth, $(this).outerWidth()); - }); - $('#kanbans').css('min-width', maxWidth); - } -}); - -/** Get card height */ -function getCardHeight() -{ - return [59, 59, 62, 62, 47][window.kanbanScaleSize]; -} - -/** Change kanban scale size */ -function changeKanbanScaleSize(newScaleSize) -{ - var newScaleSize = Math.max(1, Math.min(4, newScaleSize)); - if(newScaleSize === window.kanbanScaleSize) return; - - window.kanbanScaleSize = newScaleSize; - $.zui.store.set('executionKanbanScaleSize', newScaleSize); - $('#kanbanScaleSize').text(newScaleSize); - $('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', newScaleSize >= 4 ? 'disabled' : null); - $('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', newScaleSize <= 1 ? 'disabled' : null); - - $('#kanbans').children('.kanban').each(function() - { - var kanban = $(this).data('zui.kanban'); - if(!kanban) return; - kanban.setOptions({cardsPerRow: newScaleSize, cardHeight: getCardHeight()}); - }); - - return newScaleSize; -} - -/* Example code: */ -$(function() -{ - $.cookie('isFullScreen', 0); - - window.kanbanScaleSize = +$.zui.store.get('executionKanbanScaleSize', 1); - $('#kanbanScaleSize').text(window.kanbanScaleSize); - $('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', window.kanbanScaleSize >= 4 ? 'disabled' : null); - $('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', window.kanbanScaleSize <= 1 ? 'disabled' : null); - - /* Common options */  - var commonOptions = - { - maxColHeight: 'auto', - minColWidth: 240, - maxColWidth: 240, - cardHeight: getCardHeight(), - showCount: true, - showZeroCount: true, - fluidBoardWidth: false, - cardsPerRow: window.kanbanScaleSize, - virtualize: true, - virtualRenderOptions: {container: '#kanbanContainer>.panel-body'}, + data: groupBy == 'default' ? region.groups : kanbanData[groupBy], + maxColHeight: 510, + calcColHeight: calcColHeight, + fluidBoardWidth: false, + minColWidth: 300, + maxColWidth: 300, + cardHeight: 60, + displayCards: displayCards, + createColumnText: kanbanLang.createColumn, + addItemText: '', + cardsPerRow: window.kanbanScaleSize, + onAction: handleKanbanAction, + onRenderLaneName: renderLaneName, + onRenderHeaderCol: renderHeaderCol, + onRenderCount: renderCount, droppable: { target: findDropColumns, finish: handleFinishDrop, mouseButton: 'left' - }, - onRenderHeaderCol: renderHeaderCol, - onRenderLaneName: renderLaneName, - onRenderCount: renderColumnCount - }; - - /* Create kanban */ - if(groupBy == 'default') - { - var kanbanLane = ''; - for(var i in kanbanList) - { - if(kanbanList[i] == 'story') kanbanLane = kanbanGroup.story; - if(kanbanList[i] == 'bug') kanbanLane = kanbanGroup.bug; - if(kanbanList[i] == 'task') kanbanLane = kanbanGroup.task; - - if(browseType == kanbanList[i] || browseType == 'all') createKanban(kanbanList[i], kanbanLane, commonOptions); } - } - else - { - /* Create kanban by group. */ - createKanban(browseType, kanbanGroup[groupBy], commonOptions); - } - - /* Init iframe modals */ - $(document).on('click', '#kanbans .iframe,.contextmenu-menu .iframe', function(event) - { - var $link = $(this); - if($link.data('zui.modaltrigger')) return; - $link.modalTrigger({show: true}); - event.preventDefault(); }); - /* Init contextmenu */ - $('#kanbans').on('click', '[data-contextmenu]', function(event) + $kanban.on('click', '.action-cancel', hideKanbanAction); + $kanban.on('scroll', function() { - var $trigger = $(this); - var menuType = $trigger.data('contextmenu'); - - var menuCreator = window.menuCreators[menuType]; - if(!menuCreator) return; - - var options = $.extend({event: event, $trigger: $trigger}, $trigger.data()); - var items = menuCreator(options); - if(!items || !items.length) return; - - $.zui.ContextMenu.show(items, items.$options || {event: event}); + $.zui.ContextMenu.hide(); }); +} + +/** + * Init when page ready + */ +$(function() +{ + window.kanbanScaleSize = +$.zui.store.get('executionKanbanScaleSize', 1); + $('#kanbanScaleSize').text(window.kanbanScaleSize); + $('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', window.kanbanScaleSize >= 4 ? 'disabled' : null); + $('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', window.kanbanScaleSize <= 1 ? 'disabled' : null); /* Make kanbanScaleControl works */ $('#kanbanScaleControl').on('click', '.btn', function() @@ -988,9 +1008,48 @@ $(function() changeKanbanScaleSize(window.kanbanScaleSize + ($(this).data('type') === '+' ? 1 : -1)); }); - /* Resize kanban container on window resize */ - resizeKanbanContainer(); - $(window).on('resize', resizeKanbanContainer); + /* Init first kanban */ + $('.kanban').each(function() + { + initKanban($(this)); + }); + + $('.icon-chevron-double-up,.icon-chevron-double-down').on('click', function() + { + $(this).toggleClass('icon-chevron-double-up icon-chevron-double-down'); + $(this).parents('.region').find('.kanban').toggle(); + hideKanbanAction(); + }); + + $('.region-header').on('click', '.action', hideKanbanAction); + $('#TRAction').on('click', '.btn', hideKanbanAction); + + /* Hide action box when user click document */ + $(document).on('click', function(e) + { + $('.kanban').each(function() + { + var currentAction = $(this).kanban().attr('data-action-enabled'); + var canHideAction = (currentAction === 'headerMore' || currentAction === 'editLaneName') + && !$(e.target).closest('.action,.action-box').length; + if(canHideAction) hideKanbanAction(); + }); + }); + + /* Init contextmenu */ + $('#kanban').on('click', '[data-contextmenu]', function(event) + { + var $trigger = $(this); + var menuType = $trigger.data('contextmenu'); + var menuCreator = window.menuCreators[menuType]; + if(!menuCreator) return; + + var options = $.extend({event: event, $trigger: $trigger}, $trigger.data()); + var items = menuCreator(options); + if(!items || !items.length) return; + + $.zui.ContextMenu.show(items, items.$options || {event: event}); + }); /* Hide contextmenu when page scroll */ $(window).on('scroll', function() @@ -1003,68 +1062,162 @@ $(function() var planID = $('#plan').val(); if(planID) { - location.href = createLink('execution', 'importPlanStories', 'executionID=' + executionID + '&planID=' + planID + '&productID=0&fromMethod=kanban'); + var vars = $('.linkStoryByPlanButton').data('lane') != null ? '&extra=laneID='+ $('.linkStoryByPlanButton').data('lane') + ',columnID=' + $('.linkStoryByPlanButton').data('col') : ''; + location.href = createLink('execution', 'importPlanStories', 'executionID=' + executionID + '&planID=' + planID + '&productID=0&fromMethod=kanban' + vars); + $.closeModal(); } }); - $('#type_chosen .chosen-single span').prepend(''); - $('#group_chosen .chosen-single span').prepend(kanbanLang.laneGroup + ': '); - - /* Ajax update kanban. */ - var lastUpdateData; - setInterval(function() + $(document).on('click', '#splitTable .btn-plus', function() { - $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy), function(data) + var tr = $(this).closest('tr'); + tr.after($('#childTpl').html().replace(/key/g, key)); + tr.next().find('input[name^=color]').colorPicker(); + key++; + processMinusBtn(); + return false; + }); + + /* Remove a trade detail item. */ + $(document).on('click', '#splitTable .btn-close', function() + { + $(this).closest('tr').remove(); + processMinusBtn(); + return false; + }); + + /* Mofidy dafault color's border color. */ + $(document).on('mouseout', '.color0', function() + { + $('.color0 .cardcolor').css('border', '1px solid #b0b0b0'); + }); + + /* Mofidy dafault color's border color. */ + $(document).on('mouseover', '.color0', function() + { + $('.color0 .cardcolor').css('border', '1px solid #fff'); + }); + + /* Init sortable */ + var sortType = ''; + var $cards = null; + $('#kanban').sortable( + { + selector: '.region, .kanban-board, .kanban-lane', + trigger: '.region.sort > .region-header, .kanban-board.sort > .kanban-header > .kanban-group-header, .kanban-lane.sort > .kanban-lane-name', + container: function($ele) { - if(data && lastUpdateData !== data) + return $ele.parent(); + }, + targetSelector: function($ele) + { + /* Sort regions */ + if($ele.hasClass('region')) { - lastUpdateData = data; - kanbanGroup = $.parseJSON(data); - if(groupBy == 'default') - { - var kanbanLane = ''; - for(var i in kanbanList) - { - if(kanbanList[i] == 'story') kanbanLane = kanbanGroup.story; - if(kanbanList[i] == 'bug') kanbanLane = kanbanGroup.bug; - if(kanbanList[i] == 'task') kanbanLane = kanbanGroup.task; - - if(browseType == kanbanList[i] || browseType == 'all') updateKanban(kanbanList[i], kanbanLane); - } - } - else - { - updateKanban(browseType, kanbanGroup[groupBy]); - } + sortType = 'region'; + return $ele.parent().children('.region'); } - }); - }, 10000); -}); -$('#type').change(function() -{ - var type = $('#type').val(); - if(type != 'all') - { - $('.c-group').show(); - $.get(createLink('execution', 'ajaxGetGroup', 'type=' + type), function(data) + /* Sort boards */ + if($ele.hasClass('kanban-board')) + { + sortType = 'board'; + return $ele.parent().children('.kanban-board'); + } + + /* Sort lanes */ + if($ele.hasClass('kanban-lane')) + { + sortType = 'lane'; + $cards = $ele.find('.kanban-item'); + + return $ele.parent().children('.kanban-lane'); + } + + /* Sort lanes */ + if($ele.hasClass('kanban-item')) + { + sortType = 'item'; + return $ele.parent().children('.kanban-item'); + } + }, + start: function(e) { - $('#group_chosen').remove(); - $('#group').replaceWith(data); - $('#group').chosen(); - }) - } + if(sortType == 'region') + { + showRegionIdList = ''; + $('.icon-chevron-double-up').each(function() + { + showRegionIdList += $(this).attr('data-id') + ','; + $(this).attr('class', 'icon-chevron-double-down'); + }); - var link = createLink('execution', 'kanban', "executionID=" + executionID + '&type=' + type); - location.href = link; + $('.region').find('.kanban').hide(); + hideKanbanAction(); + } + }, + finish: function(e) + { + var url = ''; + var orders = []; + e.list.each(function(index, data) + { + orders.push(data.item.data('id')); + }); + + if(sortType == 'region') + { + $('.region').each(function() + { + if(showRegionIdList.includes($(this).attr('data-id'))) + { + $(this).find('.icon-chevron-double-down').attr('class', 'icon-chevron-double-up'); + $(this).find('.kanban').show(); + } + }) + + url = createLink('kanban', 'sortRegion', 'regions=' + orders.join(',')); + } + if(sortType == 'board') + { + var region = e.element.parent().data('id'); + url = createLink('kanban', 'sortGroup', 'region=' + region + '&groups=' + orders.join(',')); + } + if(sortType == 'lane') + { + var region = e.element.parent().parent().data('id'); + url = createLink('kanban', 'sortLane', 'region=' + region + '&lanes=' + orders.join(',')); + } + if(sortType == 'item') + { + url = createLink('task', 'sort', 'kanbanID=' + kanbanID + '&tasks=' + orders.join(',')); + } + if(!url) return true; + + $.getJSON(url, function(response) + { + if(response.result == 'fail' && response.message.length) + { + bootbox.alert(response.message); + setTimeout(function(){return location.reload()}, 3000); + } + }); + }, + always: function(e) + { + if(sortType == 'lane') $cards.show(); + } + }); }); -$('.c-group').change(function() +/** Calculate column height */ +function calcColHeight(col, lane, colCards, colHeight, kanban) { - $('.c-group').show(); + var options = kanban.options; + if(!options.displayCards) return 0; - var type = $('#type').val(); - var group = $('#group').val(); - var link = createLink('execution', 'kanban', 'executionID=' + executionID + '&type=' + type + '&orderBy=order_asc' + '&groupBy=' + group); - location.href = link; -}); + var displayCards = +(options.displayCards || 2); + + if (typeof displayCards !== 'number' || displayCards < 2) displayCards = 2; + return (displayCards * (options.cardHeight + options.cardSpace) + options.cardSpace); +} diff --git a/module/execution/js/taskkanban.js b/module/execution/js/taskkanban.js new file mode 100644 index 0000000000..84f1fbfb03 --- /dev/null +++ b/module/execution/js/taskkanban.js @@ -0,0 +1,1133 @@ +function changeView(view) +{ + var link = createLink('execution', 'taskKanban', "executionID=" + executionID + '&type=' + view); + location.href = link; +} + +/** + * Render user avatar + * @param {String|{account: string, avatar: string}} user User account or user object + * @returns {string} + */ +function renderUserAvatar(user, objectType, objectID, size) +{ + var avatarSizeClass = 'avatar-' + (size || 'sm'); + var $noPrivAndNoAssigned = $('
'); + if(objectType == 'task') + { + if(!priv.canAssignTask && !user) return $noPrivAndNoAssigned; + var link = createLink('task', 'assignto', 'executionID=' + executionID + '&id=' + objectID, '', true); + } + if(objectType == 'story') + { + if(!priv.canAssignStory && !user) return $noPrivAndNoAssigned; + var link = createLink('story', 'assignto', 'id=' + objectID, '', true); + } + if(objectType == 'bug') + { + if(!priv.canAssignBug && !user) return $noPrivAndNoAssigned; + var link = createLink('bug', 'assignto', 'id=' + objectID, '', true); + } + + if(!user) return $(''); + + if(typeof user === 'string') user = {account: user}; + if(!user.avatar && window.userList && window.userList[user.account]) user = window.userList[user.account]; + + var $noPrivAvatar = $('
').avatar({user: user}); + if(objectType == 'task' && !priv.canAssignTask) return $noPrivAvatar; + if(objectType == 'story' && !priv.canAssignStory) return $noPrivAvatar; + if(objectType == 'bug' && !priv.canAssignBug) return $noPrivAvatar; + + return $('').avatar({user: user}); +} + +/** + * Render deadline + * @param {String|Date} deadline Deadline + * @returns {JQuery} + */ +function renderDeadline(deadline) +{ + if(deadline == '0000-00-00') return; + + var date = $.zui.createDate(deadline); + var now = new Date(); + now.setHours(0); + now.setMinutes(0); + now.setSeconds(0); + now.setMilliseconds(0); + var isEarlyThanToday = date.getTime() < now.getTime(); + var deadlineDate = $.zui.formatDate(date, 'MM-dd'); + + return $('').text(deadlineLang + ' ' + deadlineDate).addClass(isEarlyThanToday ? 'text-red' : 'text-muted'); +} + +/** + * Render story item + * @param {Object} item Story item object + * @param {JQuery} $item Kanban item element + * @param {Object} col Column object + * @returns {JQuery} $item Kanban item element + */ +function renderStoryItem(item, $item, col) +{ + var scaleSize = window.kanbanScaleSize; + if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize); + + if(scaleSize <= 3) + { + var $title = $item.find('.title'); + if(!$title.length) + { + $title = $('' + (scaleSize <= 1 ? ' ' : '') + '') + .attr('href', $.createLink('story', 'view', 'storyID=' + item.id, '', true)); + $title.appendTo($item); + } + $title.attr('title', item.title).find('.text').text(item.title); + } + + if(scaleSize <= 2) + { + var idHtml = scaleSize <= 1 ? ('#' + item.id + '') : ''; + var priHtml = '' + item.pri + ''; + var hoursHtml = (item.estimate && scaleSize <= 1) ? ('' + item.estimate + 'h') : ''; + var avatarHtml = renderUserAvatar(item.assignedTo, 'story', item.id); + var $infos = $item.find('.infos'); + if(!$infos.length) $infos = $('
'); + $infos.html([idHtml, priHtml, hoursHtml].join('')); + + $infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml); + if(scaleSize <= 1) $infos.appendTo($item); + else if(scaleSize === 2) $infos.prependTo($item); + else $infos.prependTo($item.find('.title')); + } + else if(scaleSize === 4) + { + $item.html(renderUserAvatar(item.assignedTo, 'story', item.id, 'md')); + } + + if(scaleSize <= 1) + { + var $actions = $item.find('.actions'); + if(!$actions.length && item.menus && item.menus.length) + { + $actions = $([ + '
', + '', + '', + '', + '
' + ].join('')).appendTo($item); + } + } + + return $item.attr('data-type', 'story').addClass('kanban-item-story'); +} + +/** + * Render bug item + * @param {Object} item Bug item object + * @param {JQuery} $item Kanban item element + * @param {Object} col Column object + * @returns {JQuery} $item Kanban item element + */ +function renderBugItem(item, $item, col) +{ + var scaleSize = window.kanbanScaleSize; + if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize); + + if(scaleSize <= 3) + { + var $title = $item.find('.title'); + if(!$title.length) + { + $title = $('' + (scaleSize <= 1 ? ' ' : '') + '') + .attr('href', $.createLink('bug', 'view', 'bugID=' + item.id, '', true)); + $title.appendTo($item); + } + $title.attr('title', item.title).find('.text').text(item.title); + } + + if(scaleSize <= 2) + { + var idHtml = scaleSize <= 1 ? ('#' + item.id + '') : ''; + var severityHtml = scaleSize <= 1 ? ('') : ''; + var priHtml = '' + item.pri + ''; + var avatarHtml = renderUserAvatar(item.assignedTo, 'bug', item.id); + + var $infos = $item.find('.infos'); + if(!$infos.length) $infos = $('
'); + $infos.html([idHtml, severityHtml, priHtml].join('')); + if(item.deadline && scaleSize <= 1) $infos.append(renderDeadline(item.deadline)); + $infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml); + + if(scaleSize <= 1) $infos.appendTo($item); + else if(scaleSize === 2) $infos.prependTo($item); + else $infos.prependTo($item.find('.title')); + } + else if(scaleSize === 4) + { + $item.html(renderUserAvatar(item.assignedTo, 'bug', item.id, 'md')); + } + + if(scaleSize <= 1) + { + var $actions = $item.find('.actions'); + if(!$actions.length && item.menus && item.menus.length) + { + $actions = $([ + '
', + '', + '', + '', + '
' + ].join('')).appendTo($item); + } + } + + return $item.attr('data-type', 'bug').addClass('kanban-item-bug'); +} + +/** + * Render task item + * @param {Object} item Task item object + * @param {JQuery} $item Kanban item element + * @param {Object} col Column object + * @returns {JQuery} $item Kanban item element + */ +function renderTaskItem(item, $item, col) +{ + var scaleSize = window.kanbanScaleSize; + if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize); + + if(scaleSize <= 3) + { + var $title = $item.find('.title'); + if(!$title.length) + { + $title = $('' + (scaleSize <= 1 ? ' ' : '') + '') + .attr('href', $.createLink('task', 'view', 'taskID=' + item.id, '', true)); + $title.appendTo($item); + } + $title.attr('title', item.name).find('.text').text(item.name); + } + + if(scaleSize <= 2) + { + var idHtml = scaleSize <= 1 ? ('#' + item.id + '') : ''; + var priHtml = '' + item.pri + ''; + var hoursHtml = (item.estimate && scaleSize <= 1) ? ('' + item.estimate + 'h') : ''; + var avatarHtml = renderUserAvatar(item.assignedTo, 'task', item.id); + + var $infos = $item.find('.infos'); + if(!$infos.length) $infos = $('
'); + $infos.html([idHtml, priHtml, hoursHtml].join('')); + if(item.deadline && scaleSize <= 1) $infos.append(renderDeadline(item.deadline)); + $infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml); + + if(scaleSize <= 1) $infos.appendTo($item); + else if(scaleSize === 2) $infos.prependTo($item); + else $infos.prependTo($item.find('.title')); + } + else if(scaleSize === 4) + { + $item.html(renderUserAvatar(item.assignedTo, 'task', item.id, 'md')); + } + + if(scaleSize <= 1) + { + var $actions = $item.find('.actions'); + if(!$actions.length && item.menus && item.menus.length) + { + $actions = $([ + '
', + '', + '', + '', + '
' + ].join('')).appendTo($item); + } + } + + $item.attr('data-type', 'task').addClass('kanban-item-task'); + + return $item; +} + +/* Add column renderer */ +addColumnRenderer('story', renderStoryItem); +addColumnRenderer('bug', renderBugItem); +addColumnRenderer('task', renderTaskItem); + +/** + * Render column count + * @param {JQuery} $count Kanban count element + * @param {number} count Column cards count + * @param {number} col Column object + * @param {Object} kanban Kanban intance + */ +function renderColumnCount($count, count, col) +{ + var text = count + '/' + (col.limit < 0 ? '' : col.limit); + $count.html(text + ''); +} + +/** + * Render header column + * @param {JQuery} $col Header column element + * @param {Object} col Header column object + * @param {JQuery} $header Header element + * @param {Object} kanban Kanban object + */ +function renderHeaderCol($col, col, $header, kanban) +{ + if(col.asParent) $col = $col.children('.kanban-header-col'); + var $actions = $('
'); + var printStoryButton = printTaskButton = printBugButton = false; + if(priv.canCreateStory || priv.canBatchCreateStory || priv.canLinkStory || priv.canLinkStoryByPlane) printStoryButton = true; + if(priv.canCreateTask || priv.canBatchCreateTask) printTaskButton = true; + if(priv.canCreateBug || priv.canBatchCreateBug) printBugButton = true; + + if((col.type === 'backlog' && printStoryButton) || (col.type === 'wait' && printTaskButton) || (col.type == 'unconfirmed' && printBugButton)) + { + $actions.append([ + '', + '', + '' + ].join('')); + } + + $actions.append([ + '', + '', + '' + ].join('')); + $actions.appendTo($col); +} + +/** + * Render lane name + * @param {JQuery} $name Name element + * @param {Object} lane Lane object + * @param {JQuery} $kanban $kanban element + * @param {Object} columns Kanban columns + * @param {Object} kanban Kanban object + */ +function renderLaneName($name, lane, $kanban, columns, kanban) +{ + if(lane.id != 'story' && lane.id != 'task' && lane.id != 'bug') return false; + if(!$name.children('.actions').length && (priv.canSetLane || priv.canMoveLane)) + { + $([ + '
', + '', + '', + '', + '
' + ].join('')).appendTo($name); + } +} + +/** + * Updata kanban data + * @param {string} kanbanID Kanban id + * @param {Object} data Kanban data + */ +function updateKanban(kanbanID, data) +{ + var $kanban = $('#kanban-' + kanbanID); + if(!$kanban.length) return; + + $kanban.data('zui.kanban').render(data); +} + +/** + * Create kanban in page + * @param {string} kanbanID Kanban id + * @param {Object} data Kanban data + * @param {Object} options Kanban options + */ +function createKanban(kanbanID, data, options) +{ + var $kanban = $('#kanban-' + kanbanID); + var displayCards = window.displayCards == 'undefined' ? 2 : window.displayCards; + if($kanban.length) return updateKanban(kanbanID, data); + + $kanban = $('
').appendTo('#kanbans'); + $kanban.kanban($.extend({data: data, calcColHeight: calcColHeight, displayCards: displayCards}, options)); +} + +function fullScreen() +{ + var element = document.getElementById('kanbanContainer'); + var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen; + if(requestMethod) + { + var afterEnterFullscreen = function() + { + $('#kanbanContainer').addClass('scrollbar-hover'); + $('.actions').hide(); + $('#kanbanContainer a.iframe').each(function() + { + if($(this).hasClass('iframe')) + { + var href = $(this).attr('href'); + $(this).removeClass('iframe'); + $(this).attr('href', 'javascript:void(0)'); + $(this).attr('href-bak', href); + } + }) + $.cookie('isFullScreen', 1); + } + + var whenFailEnterFullscreen = function() + { + exitFullScreen(); + } + + try + { + var result = requestMethod.call(element); + if(result && (typeof result.then === 'function' || result instanceof window.Promise)) + { + result.then(afterEnterFullscreen).catch(whenFailEnterFullscreen); + } + else + { + afterEnterFullscreen(); + } + } + catch (error) + { + whenFailEnterFullscreen(error); + } + } +} + +/** + * Exit full screen. + * + * @access public + * @return void + */ +function exitFullScreen() +{ + $('#kanbanContainer').removeClass('scrollbar-hover'); + $('.actions').show(); + $('#kanbanContainer a').each(function() + { + var hrefBak = $(this).attr('href-bak'); + if(hrefBak) + { + $(this).addClass('iframe'); + $(this).attr('href', hrefBak); + } + }) + $.cookie('isFullScreen', 0); +} + +document.addEventListener('fullscreenchange', function (e) +{ + if(!document.fullscreenElement) exitFullScreen(); +}); + +document.addEventListener('webkitfullscreenchange', function (e) +{ + if(!document.webkitFullscreenElement) exitFullScreen(); +}); + +document.addEventListener('mozfullscreenchange', function (e) +{ + if(!document.mozFullScreenElement) exitFullScreen(); +}); + +document.addEventListener('msfullscreenChange', function (e) +{ + if(!document.msfullscreenElement) exitFullScreen(); +}); + +/* Define drag and drop rules */ +if(!window.kanbanDropRules) +{ + window.kanbanDropRules = + { + story: + { + backlog: ['ready'], + ready: ['backlog'], + }, + bug: + { + 'unconfirmed': ['confirmed', 'fixing', 'fixed'], + 'confirmed': ['fixing', 'fixed'], + 'fixing': ['fixed'], + 'fixed': ['testing', 'tested', 'fixing'], + 'testing': ['tested', 'closed', 'fixing'], + 'tested': ['closed', 'fixing'], + 'closed': ['fixing'], + }, + task: + { + 'wait': ['developing', 'developed', 'canceled', 'closed'], + 'developing': ['developed', 'pause', 'canceled'], + 'developed': ['canceled', 'closed'], + 'pause': ['developing'], + 'canceled': ['developing'], + 'closed': ['developing'], + } + } +} + +/* + * Find drop columns + * @param {JQuery} $element Drag element + * @param {JQuery} $root Dnd root element + */ +function findDropColumns($element, $root) +{ + var $col = $element.closest('.kanban-col'); + var col = $col.data(); + var kanbanID = $root.data('id'); + var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules[kanbanID] : null; + + if(!kanbanRules) return $root.find('.kanban-lane-col:not([data-type="' + col.type + '"])'); + + var colRules = kanbanRules[col.type]; + var lane = $col.closest('.kanban-lane').data('lane'); + return $root.find('.kanban-lane-col').filter(function() + { + if(!colRules) return false; + if(colRules === true) return true; + if($.cookie('isFullScreen') == 1) return false; + + var $newCol = $(this); + var newCol = $newCol.data(); + if(newCol.id === col.id) return false; + + var $newLane = $newCol.closest('.kanban-lane'); + var newLane = $newLane.data('lane'); + var canDropHere = colRules.indexOf(newCol.type) > -1 && newLane.id === lane.id; + if(canDropHere) $newCol.addClass('can-drop-here'); + return canDropHere; + }); +} + +/** + * changeCardColType + * + * @param int $cardID + * @param int $fromColID + * @param int $toColID + * @param int $fromLaneID + * @param int $toLaneID + * @param string $cardType + * @param string $fromColType + * @param string $toColType + * @access public + * @return void + */ +function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, cardType, fromColType, toColType) +{ + var objectID = cardID; + var showIframe = false; + var moveCard = false; + + /* Task lane. */ + if(cardType == 'task') + { + if(toColType == 'developed') + { + if((fromColType == 'developing' || fromColType == 'wait') && priv.canFinishTask) + { + var link = createLink('task', 'finish', 'taskID=' + objectID, '', true); + showIframe = true; + } + } + else if(toColType == 'pause') + { + if(fromColType == 'developing' && priv.canPauseTask) + { + var link = createLink('task', 'pause', 'taskID=' + objectID, '', true); + showIframe = true; + } + } + else if(toColType == 'developing') + { + if((fromColType == 'pause' || fromColType == 'canceled' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask) + { + var link = createLink('task', 'activate', 'taskID=' + objectID, '', true); + showIframe = true; + } + if(fromColType == 'wait' && priv.canStartTask) + { + var link = createLink('task', 'start', 'taskID=' + objectID, '', true); + showIframe = true; + } + } + else if(toColType == 'canceled') + { + if((fromColType == 'developing' || fromColType == 'wait' || fromColType == 'pause') && priv.canCancelTask) + { + var link = createLink('task', 'cancel', 'taskID=' + objectID, '', true); + showIframe = true; + } + } + else if(toColType == 'closed') + { + if((fromColType == 'developed' || fromColType == 'canceled') && priv.canCloseTask) + { + var link = createLink('task', 'close', 'taskID=' + objectID, '', true); + showIframe = true; + } + } + } + + /* Bug lane. */ + if(cardType == 'bug') + { + if(toColType == 'confirmed') + { + if(fromColType == 'unconfirmed' && priv.canConfirmBug) + { + var link = createLink('bug', 'confirmBug', 'bugID=' + objectID, '', true); + showIframe = true; + } + } + else if(toColType == 'fixing') + { + if(fromColType == 'confirmed' || fromColType == 'unconfirmed') moveCard = true; + if((fromColType == 'closed' || fromColType == 'fixed' || fromColType == 'testing' || fromColType == 'tested') && priv.canActivateBug) + { + var link = createLink('bug', 'activate', 'bugID=' + objectID, '', true); + showIframe = true; + } + } + else if(toColType == 'fixed') + { + if(fromColType == 'fixing' || fromColType == 'confirmed' || fromColType == 'unconfirmed') + { + var link = createLink('bug', 'resolve', 'bugID=' + objectID, '', true); + showIframe = true; + } + } + else if(toColType == 'testing') + { + if(fromColType == 'fixed') moveCard = true; + } + else if(toColType == 'tested') + { + if(fromColType == 'fixed' || fromColType == 'testing') moveCard = true; + } + else if(toColType == 'closed') + { + if(fromColType == 'testing' || fromColType == 'tested') + { + var link = createLink('bug', 'close', 'bugID=' + objectID, '', true); + showIframe = true; + } + } + + if(moveCard) + { + var link = createLink('kanban', 'ajaxMoveCard', 'cardID=' + objectID + '&fromColID=' + fromColID + '&toColID=' + toColID + '&fromLaneID=' + fromLaneID + '&toLaneID=' + toLaneID + '&execitionID=' + executionID + '&browseType=' + browseType + '&groupBy=' + groupBy); + $.get(link, function(data) + { + if(data) + { + kanbanGroup = $.parseJSON(data); + if(groupBy == 'default') + { + updateKanban('bug', kanbanGroup.bug); + } + else + { + updateKanban(browseType, kanbanGroup[groupBy]); + } + } + }) + } + } + + /* Story lane. */ + if(cardType == 'story') + { + if(toColType == 'ready' || toColType == 'backlog') + { + var link = createLink('kanban', 'ajaxMoveCard', 'cardID=' + objectID + '&fromColID=' + fromColID + '&toColID=' + toColID + '&fromLaneID=' + fromLaneID + '&toLaneID=' + toLaneID + '&execitionID=' + executionID + '&browseType=' + browseType + '&groupBy=' + groupBy); + $.get(link, function(data) + { + if(data) + { + kanbanGroup = $.parseJSON(data); + if(groupBy == 'default') + { + updateKanban('story', kanbanGroup.story); + } + else + { + updateKanban(browseType, kanbanGroup[groupBy]); + } + } + }) + } + } + + if(showIframe) + { + var modalTrigger = new $.zui.ModalTrigger({type: 'iframe', width: '80%', url: link}); + modalTrigger.show(); + } +} + +/** + * Handle drop task. + * + * @param object $element + * @param object $event + * @param object $kanban + * @access public + * @return void + */ +function handleDropTask($element, event, kanban) +{ + if(!event.target) return; + + var $card = $element; + var $oldCol = $card.closest('.kanban-col'); + var $newCol = $(event.target).closest('.kanban-col'); + var oldCol = $oldCol.data(); + var newCol = $newCol.data(); + var oldLane = $oldCol.closest('.kanban-lane').data('lane'); + var newLane = $newCol.closest('.kanban-lane').data('lane'); + var cardType = $card.find('.kanban-card').data('type'); + + if(oldCol.id === newCol.id && newLane.id === oldLane.id) return false; + + var cardID = $card.data().id; + var fromColType = $oldCol.data('type'); + var toColType = $newCol.data('type'); + + changeCardColType(cardID, oldCol.id, newCol.id, oldLane.id, newLane.id, cardType, fromColType, toColType); +} + +var kanbanActionHandlers = +{ + dropItem: handleDropTask +}; + +/** + * Handle kanban action. + * + * @param string $action + * @param object $element + * @param object $event + * @param object $kanban + * @access public + * @return void + */ +function handleKanbanAction(action, $element, event, kanban) +{ + if(groupBy && groupBy != 'default') return false; + $('.kanban').attr('data-action-enabled', action); + var handler = kanbanActionHandlers[action]; + if(handler) handler($element, event, kanban); +} + +/** + * Handle finish drop task + * @param {Object} event Event object + * @returns {void} + */ +function handleFinishDrop(event) +{ + $('#kanbans').find('.can-drop-here').removeClass('can-drop-here'); +} + +/** Handle sort cards in column */ +function handleSortColCards() +{ + /* TODO: handle sort cards from column contextmenu */ + return false; +} + +/** + * Create column menu + * @returns {Object[]} + */ +function createColumnMenu(options) +{ + var $col = options.$trigger.closest('.kanban-col'); + var col = $col.data('col'); + var kanbanID = options.kanban; + + var items = []; + if(priv.canEditName) items.push({label: executionLang.editName, url: $.createLink('kanban', 'setColumn', 'col=' + col.id + '&executionID=' + executionID + '&from=execution'), className: 'iframe', attrs: {'data-width': '500px'}}) + if(priv.canSetWIP) items.push({label: executionLang.setWIP, url: $.createLink('kanban', 'setWIP', 'col=' + col.id + '&executionID=' + executionID + '&from=execution'), className: 'iframe', attrs: {'data-width': '500px'}}) + //if(priv.canSortCards) items.push({label: executionLang.sortColumn, items: ['按ID倒序', '按ID顺序'], className: 'iframe', onClick: handleSortColCards}) + return items; +} + +/** + * Create column create button menu + * @returns {Object[]} + */ +function createColumnCreateMenu(options) +{ + var $col = options.$trigger.closest('.kanban-col'); + var col = $col.data('col'); + var items = []; + + if(col.laneType == 'story') + { + if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID, '', true), className: 'iframe'}); + if(priv.canBatchCreateStory) items.push({label: executionLang.batchCreateStroy, url: $.createLink('story', 'batchcreate', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}}); + if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}}); + if(priv.canLinkStoryByPlane) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal'}}); + } + else if(col.laneType == 'bug') + { + if(priv.canCreateBug) items.push({label: bugLang.create, url: $.createLink('bug', 'create', 'productID=0&moduleID=0&extra=executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '80%'}}); + if(priv.canBatchCreateBug) items.push({label: bugLang.batchCreate, url: $.createLink('bug', 'batchcreate', 'productID=' + productID + '&moduleID=0&executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}}); + } + else + { + if(priv.canCreateTask) items.push({label: taskLang.create, url: $.createLink('task', 'create', 'executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '80%'}}); + if(priv.canBatchCreateTask) items.push({label: taskLang.batchCreate, url: $.createLink('task', 'batchcreate', 'executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '80%'}}); + } + return items; +} + +/** + * Create lane menu + * @returns {Object[]} + */ +function createLaneMenu(options) +{ + var $lane = options.$trigger.closest('.kanban-lane'); + var $kanban = $lane.closest('.kanban'); + var lane = $lane.data('lane'); + var kanbanID = options.kanban; + var upTargetKanban = $kanban.prev('.kanban').length ? $kanban.prev('.kanban').data('id') : ''; + var downTargetKanban = $kanban.next('.kanban').length ? $kanban.next('.kanban').data('id') : ''; + + var items = []; + if(priv.canSetLane) items.push({label: kanbanLang.setLane, icon: 'edit', url: $.createLink('kanban', 'setLane', 'lane=' + lane.laneID + '&executionID=' + executionID + '&from=execution'), className: 'iframe'}); + if(priv.canMoveLane) items.push( + {label: kanbanLang.moveUp, icon: 'arrow-up', url: $.createLink('kanban', 'laneMove', 'executionID=' + executionID + '¤tLane=' + lane.id + '&targetLane=' + upTargetKanban), className: 'iframe', disabled: !$kanban.prev('.kanban').length}, + {label: kanbanLang.moveDown, icon: 'arrow-down', url: $.createLink('kanban', 'laneMove', 'executionID=' + executionID + '¤tLane=' + lane.id + '&targetLane=' + downTargetKanban), className: 'iframe', disabled: !$kanban.next('.kanban').length} + ); + + var bounds = options.$trigger[0].getBoundingClientRect(); + items.$options = {x: bounds.right, y: bounds.top}; + return items; +} + +/** + * Create story menu + * @returns {Object[]} + */ +function createStoryMenu(options) +{ + var $card = options.$trigger.closest('.kanban-item'); + var story = $card.data('item'); + + var items = []; + $.each(story.menus, function() + { + var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}}; + if(this.size) item.attrs['data-width'] = this.size; + + if(this.icon == 'unlink') item = {label: this.label, icon: this.icon, url: this.url, attrs: {'target': 'hiddenwin'}}; + items.push(item); + }); + + return items; +} + +/** + * Create bug menu + * @returns {Object[]} + */ +function createBugMenu(options) +{ + var $card = options.$trigger.closest('.kanban-item'); + var bug = $card.data('item'); + + var items = []; + $.each(bug.menus, function() + { + var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}}; + if(this.size) item.attrs['data-width'] = this.size; + + items.push(item); + }); + + return items; +} + + /** + * Create task menu + * @returns {Object[]} + */ +function createTaskMenu(options) +{ + var $card = options.$trigger.closest('.kanban-item'); + var task = $card.data('item'); + + var items = []; + $.each(task.menus, function() + { + var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}}; + if(this.size) item.attrs['data-width'] = this.size; + + items.push(item); + }); + + return items; +} + +/** Resize kanban container size */ +function resizeKanbanContainer() +{ + var $container = $('#kanbanContainer'); + var maxHeight = window.innerHeight - 98 - 15; + if($.cookie('isFullScreen') == 1) maxHeight = window.innerHeight - 15; + $container.children('.panel-body').css('max-height', maxHeight); +} + +/* Define menu creators */ +window.menuCreators = +{ + column: createColumnMenu, + columnCreate: createColumnCreateMenu, + lane: createLaneMenu, + story: createStoryMenu, + bug: createBugMenu, + task: createTaskMenu, +}; + +/* Set kanban affix container */ +window.kanbanAffixContainer = '#kanbanContainer>.panel-body'; + +/* Overload kanban default options */ +$.extend($.fn.kanban.Constructor.DEFAULTS, +{ + onRender: function() + { + var maxWidth = 0; + $('#kanbans .kanban-board').each(function() + { + maxWidth = Math.max(maxWidth, $(this).outerWidth()); + }); + $('#kanbans').css('min-width', maxWidth); + } +}); + +/** Get card height */ +function getCardHeight() +{ + return [59, 59, 62, 62, 47][window.kanbanScaleSize]; +} + +/** Change kanban scale size */ +function changeKanbanScaleSize(newScaleSize) +{ + var newScaleSize = Math.max(1, Math.min(4, newScaleSize)); + if(newScaleSize === window.kanbanScaleSize) return; + + window.kanbanScaleSize = newScaleSize; + $.zui.store.set('executionKanbanScaleSize', newScaleSize); + $('#kanbanScaleSize').text(newScaleSize); + $('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', newScaleSize >= 4 ? 'disabled' : null); + $('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', newScaleSize <= 1 ? 'disabled' : null); + + $('#kanbans').children('.kanban').each(function() + { + var kanban = $(this).data('zui.kanban'); + if(!kanban) return; + kanban.setOptions({cardsPerRow: newScaleSize, cardHeight: getCardHeight()}); + }); + + return newScaleSize; +} + +/* Example code: */ +$(function() +{ + $.cookie('isFullScreen', 0); + + window.kanbanScaleSize = +$.zui.store.get('executionKanbanScaleSize', 1); + $('#kanbanScaleSize').text(window.kanbanScaleSize); + $('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', window.kanbanScaleSize >= 4 ? 'disabled' : null); + $('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', window.kanbanScaleSize <= 1 ? 'disabled' : null); + + /* Common options */  + var commonOptions = + { + maxColHeight: 'auto', + minColWidth: 240, + maxColWidth: 240, + cardHeight: getCardHeight(), + showCount: true, + showZeroCount: true, + fluidBoardWidth: false, + cardsPerRow: window.kanbanScaleSize, + virtualize: true, + onAction: handleKanbanAction, + virtualRenderOptions: {container: '#kanbanContainer>.panel-body'}, + droppable: + { + target: findDropColumns, + finish: handleFinishDrop, + mouseButton: 'left' + }, + onRenderHeaderCol: renderHeaderCol, + onRenderLaneName: renderLaneName, + onRenderCount: renderColumnCount + }; + + if(groupBy != 'default') commonOptions.droppable = false; + + /* Create kanban */ + if(groupBy == 'default') + { + var kanbanLane = ''; + for(var i in kanbanList) + { + if(kanbanList[i] == 'story') kanbanLane = kanbanGroup.story; + if(kanbanList[i] == 'bug') kanbanLane = kanbanGroup.bug; + if(kanbanList[i] == 'task') kanbanLane = kanbanGroup.task; + + if(browseType == kanbanList[i] || browseType == 'all') createKanban(kanbanList[i], kanbanLane, commonOptions); + } + } + else + { + /* Create kanban by group. */ + createKanban(browseType, kanbanGroup[groupBy], commonOptions); + } + + /* Init iframe modals */ + $(document).on('click', '#kanbans .iframe,.contextmenu-menu .iframe', function(event) + { + var $link = $(this); + if($link.data('zui.modaltrigger')) return; + $link.modalTrigger({show: true}); + event.preventDefault(); + }); + + /* Init contextmenu */ + $('#kanbans').on('click', '[data-contextmenu]', function(event) + { + var $trigger = $(this); + var menuType = $trigger.data('contextmenu'); + + var menuCreator = window.menuCreators[menuType]; + if(!menuCreator) return; + + var options = $.extend({event: event, $trigger: $trigger}, $trigger.data()); + var items = menuCreator(options); + if(!items || !items.length) return; + + $.zui.ContextMenu.show(items, items.$options || {event: event}); + }); + + /* Make kanbanScaleControl works */ + $('#kanbanScaleControl').on('click', '.btn', function() + { + changeKanbanScaleSize(window.kanbanScaleSize + ($(this).data('type') === '+' ? 1 : -1)); + }); + + /* Resize kanban container on window resize */ + resizeKanbanContainer(); + $(window).on('resize', resizeKanbanContainer); + + /* Hide contextmenu when page scroll */ + $(window).on('scroll', function() + { + $.zui.ContextMenu.hide(); + }); + + $('#toStoryButton').on('click', function() + { + var planID = $('#plan').val(); + if(planID) + { + location.href = createLink('execution', 'importPlanStories', 'executionID=' + executionID + '&planID=' + planID + '&productID=0&fromMethod=kanban'); + } + }); + + $('#type_chosen .chosen-single span').prepend(''); + $('#group_chosen .chosen-single span').prepend(kanbanLang.laneGroup + ': '); + + /* Ajax update kanban. */ + var lastUpdateData; + setInterval(function() + { + $.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy), function(data) + { + if(data && lastUpdateData !== data) + { + lastUpdateData = data; + kanbanGroup = $.parseJSON(data); + if(groupBy == 'default') + { + var kanbanLane = ''; + for(var i in kanbanList) + { + if(kanbanList[i] == 'story') kanbanLane = kanbanGroup.story; + if(kanbanList[i] == 'bug') kanbanLane = kanbanGroup.bug; + if(kanbanList[i] == 'task') kanbanLane = kanbanGroup.task; + + if(browseType == kanbanList[i] || browseType == 'all') updateKanban(kanbanList[i], kanbanLane); + } + } + else + { + updateKanban(browseType, kanbanGroup[groupBy]); + } + } + }); + }, 10000); +}); + +$('#type').change(function() +{ + var type = $('#type').val(); + if(type != 'all') + { + $('.c-group').show(); + $.get(createLink('execution', 'ajaxGetGroup', 'type=' + type), function(data) + { + $('#group_chosen').remove(); + $('#group').replaceWith(data); + $('#group').chosen(); + }) + } + + var link = createLink('execution', 'taskKanban', "executionID=" + executionID + '&type=' + type); + location.href = link; +}); + +$('.c-group').change(function() +{ + $('.c-group').show(); + + var type = $('#type').val(); + var group = $('#group').val(); + var link = createLink('execution', 'taskKanban', 'executionID=' + executionID + '&type=' + type + '&orderBy=order_asc' + '&groupBy=' + group); + location.href = link; +}); + +/** Calculate column height */ +function calcColHeight(col, lane, colCards, colHeight, kanban) +{ + var options = kanban.options; + if(!options.displayCards) return 0; + + var displayCards = +(options.displayCards || 2); + + if (typeof displayCards !== 'number' || displayCards < 2) displayCards = 2; + return (displayCards * (options.cardHeight + options.cardSpace) + options.cardSpace); +} diff --git a/module/execution/lang/en.php b/module/execution/lang/en.php index 6006065bfc..3f3905ff27 100644 --- a/module/execution/lang/en.php +++ b/module/execution/lang/en.php @@ -10,105 +10,106 @@ * @link http://www.zentao.net */ /* Fields. */ -$lang->execution->allExecutions = 'All ' . $lang->execution->common . 's'; -$lang->execution->allExecutionAB = 'Execution List'; -$lang->execution->id = $lang->executionCommon . ' ID'; -$lang->execution->type = $lang->executionCommon . 'Type'; -$lang->execution->name = $lang->executionCommon . 'Name'; -$lang->execution->code = $lang->executionCommon . 'Code'; -$lang->execution->projectName = 'Project'; -$lang->execution->execName = 'Execution Name'; -$lang->execution->execCode = 'Execution Code'; -$lang->execution->execType = 'Execution Type'; -$lang->execution->stage = 'Stage'; -$lang->execution->pri = 'Priority'; -$lang->execution->openedBy = 'OpenedBy'; -$lang->execution->openedDate = 'OpenedDate'; -$lang->execution->closedBy = 'ClosedBy'; -$lang->execution->closedDate = 'ClosedDate'; -$lang->execution->canceledBy = 'CanceledBy'; -$lang->execution->canceledDate = 'CanceledDate'; -$lang->execution->begin = 'Planned Begin'; -$lang->execution->end = 'Planned End'; -$lang->execution->dateRange = 'Duration'; -$lang->execution->realBeganAB = 'Actual Begin'; -$lang->execution->realEndAB = 'Actual End'; -$lang->execution->realBegan = 'Actual Begin'; -$lang->execution->realEnd = 'Actual End'; -$lang->execution->to = 'To'; -$lang->execution->days = ' Days'; -$lang->execution->day = ' Days'; -$lang->execution->workHour = ' Hours'; -$lang->execution->workHourUnit = 'H'; -$lang->execution->totalHours = ' Hours'; -$lang->execution->totalDays = ' Days'; -$lang->execution->status = $lang->executionCommon . 'Status'; -$lang->execution->execStatus = 'Status'; -$lang->execution->subStatus = 'Sub Status'; -$lang->execution->desc = $lang->executionCommon . 'Description'; -$lang->execution->execDesc = 'Description'; -$lang->execution->owner = 'Owner'; -$lang->execution->PO = "{$lang->executionCommon} Owner"; -$lang->execution->PM = "{$lang->executionCommon} Manager"; -$lang->execution->execPM = "Execution Manager"; -$lang->execution->QD = 'Test Manager'; -$lang->execution->RD = 'Release Manager'; -$lang->execution->release = 'Release'; -$lang->execution->acl = 'Access Control'; -$lang->execution->teamname = 'Team Name'; -$lang->execution->updateOrder = 'Rank'; -$lang->execution->order = "Rank {$lang->executionCommon}"; -$lang->execution->orderAB = "Rank"; -$lang->execution->products = "Link {$lang->productCommon}"; -$lang->execution->whitelist = 'Whitelist'; -$lang->execution->addWhitelist = 'Add Whitelist'; -$lang->execution->unbindWhitelist = 'Remove Whitelist'; -$lang->execution->totalEstimate = 'Estimates'; -$lang->execution->totalConsumed = 'Cost'; -$lang->execution->totalLeft = 'Left'; -$lang->execution->progress = ' Progress'; -$lang->execution->hours = 'Estimates: %s, Cost: %s, Left: %s.'; -$lang->execution->viewBug = 'Bugs'; -$lang->execution->noProduct = "No {$lang->productCommon} yet."; -$lang->execution->createStory = "Create Story"; -$lang->execution->storyTitle = "Story Name"; -$lang->execution->all = "All {$lang->executionCommon}s"; -$lang->execution->undone = 'Unfinished '; -$lang->execution->unclosed = 'Unclosed'; -$lang->execution->typeDesc = "OPS {$lang->executionCommon} has no {$lang->SRCommon}, Bug, Build, or Test features."; -$lang->execution->mine = 'Mine: '; -$lang->execution->involved = 'Mine'; -$lang->execution->other = 'Others'; -$lang->execution->deleted = 'Deleted'; -$lang->execution->delayed = 'Delayed'; -$lang->execution->product = $lang->execution->products; -$lang->execution->readjustTime = "Adjust {$lang->executionCommon} Begin and End"; -$lang->execution->readjustTask = 'Adjust Task Begin and End'; -$lang->execution->effort = 'Effort'; -$lang->execution->storyEstimate = 'Story Estimate'; -$lang->execution->newEstimate = 'New Estimate'; -$lang->execution->reestimate = 'Reestimate'; -$lang->execution->selectRound = 'Select Round'; -$lang->execution->average = 'Average'; -$lang->execution->relatedMember = 'Team'; -$lang->execution->watermark = 'Exported by ZenTao'; -$lang->execution->burnXUnit = '(Date)'; -$lang->execution->burnYUnit = '(Hours)'; -$lang->execution->waitTasks = 'Waiting Tasks'; -$lang->execution->viewByUser = 'By User'; -$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}"; -$lang->execution->noLinkProduct = "Stage not linked {$lang->productCommon}"; -$lang->execution->recent = 'Recent visits: '; -$lang->execution->copyNoExecution = 'There are no ' . $lang->executionCommon . 'available to copy.'; -$lang->execution->noTeam = 'No team members at the moment'; -$lang->execution->or = ' or '; -$lang->execution->selectProject = 'Please select project'; -$lang->execution->unfoldClosed = 'Unfold Closed'; -$lang->execution->editName = 'Edit Name'; -$lang->execution->setWIP = 'WIP Settings'; -$lang->execution->sortColumn = 'Kanban Card Sorting'; -$lang->execution->batchCreateStroy = "Batch create {$lang->SRCommon}"; -$lang->execution->batchCreateTask = 'Batch create task'; +$lang->execution->allExecutions = 'All ' . $lang->execution->common . 's'; +$lang->execution->allExecutionAB = 'Execution List'; +$lang->execution->id = $lang->executionCommon . ' ID'; +$lang->execution->type = $lang->executionCommon . 'Type'; +$lang->execution->name = $lang->executionCommon . 'Name'; +$lang->execution->code = $lang->executionCommon . 'Code'; +$lang->execution->projectName = 'Project'; +$lang->execution->execName = 'Execution Name'; +$lang->execution->execCode = 'Execution Code'; +$lang->execution->execType = 'Execution Type'; +$lang->execution->stage = 'Stage'; +$lang->execution->pri = 'Priority'; +$lang->execution->openedBy = 'OpenedBy'; +$lang->execution->openedDate = 'OpenedDate'; +$lang->execution->closedBy = 'ClosedBy'; +$lang->execution->closedDate = 'ClosedDate'; +$lang->execution->canceledBy = 'CanceledBy'; +$lang->execution->canceledDate = 'CanceledDate'; +$lang->execution->begin = 'Planned Begin'; +$lang->execution->end = 'Planned End'; +$lang->execution->dateRange = 'Duration'; +$lang->execution->realBeganAB = 'Actual Begin'; +$lang->execution->realEndAB = 'Actual End'; +$lang->execution->realBegan = 'Actual Begin'; +$lang->execution->realEnd = 'Actual End'; +$lang->execution->to = 'To'; +$lang->execution->days = ' Days'; +$lang->execution->day = ' Days'; +$lang->execution->workHour = ' Hours'; +$lang->execution->workHourUnit = 'H'; +$lang->execution->totalHours = ' Hours'; +$lang->execution->totalDays = ' Days'; +$lang->execution->status = $lang->executionCommon . 'Status'; +$lang->execution->execStatus = 'Status'; +$lang->execution->subStatus = 'Sub Status'; +$lang->execution->desc = $lang->executionCommon . 'Description'; +$lang->execution->execDesc = 'Description'; +$lang->execution->owner = 'Owner'; +$lang->execution->PO = "{$lang->executionCommon} Owner"; +$lang->execution->PM = "{$lang->executionCommon} Manager"; +$lang->execution->execPM = "Execution Manager"; +$lang->execution->QD = 'Test Manager'; +$lang->execution->RD = 'Release Manager'; +$lang->execution->release = 'Release'; +$lang->execution->acl = 'Access Control'; +$lang->execution->teamname = 'Team Name'; +$lang->execution->updateOrder = 'Rank'; +$lang->execution->order = "Rank {$lang->executionCommon}"; +$lang->execution->orderAB = "Rank"; +$lang->execution->products = "Link {$lang->productCommon}"; +$lang->execution->whitelist = 'Whitelist'; +$lang->execution->addWhitelist = 'Add Whitelist'; +$lang->execution->unbindWhitelist = 'Remove Whitelist'; +$lang->execution->totalEstimate = 'Estimates'; +$lang->execution->totalConsumed = 'Cost'; +$lang->execution->totalLeft = 'Left'; +$lang->execution->progress = ' Progress'; +$lang->execution->hours = 'Estimates: %s, Cost: %s, Left: %s.'; +$lang->execution->viewBug = 'Bugs'; +$lang->execution->noProduct = "No {$lang->productCommon} yet."; +$lang->execution->createStory = "Create Story"; +$lang->execution->storyTitle = "Story Name"; +$lang->execution->all = "All {$lang->executionCommon}s"; +$lang->execution->undone = 'Unfinished '; +$lang->execution->unclosed = 'Unclosed'; +$lang->execution->typeDesc = "OPS {$lang->executionCommon} has no {$lang->SRCommon}, Bug, Build, or Test features."; +$lang->execution->mine = 'Mine: '; +$lang->execution->involved = 'Mine'; +$lang->execution->other = 'Others'; +$lang->execution->deleted = 'Deleted'; +$lang->execution->delayed = 'Delayed'; +$lang->execution->product = $lang->execution->products; +$lang->execution->readjustTime = "Adjust {$lang->executionCommon} Begin and End"; +$lang->execution->readjustTask = 'Adjust Task Begin and End'; +$lang->execution->effort = 'Effort'; +$lang->execution->storyEstimate = 'Story Estimate'; +$lang->execution->newEstimate = 'New Estimate'; +$lang->execution->reestimate = 'Reestimate'; +$lang->execution->selectRound = 'Select Round'; +$lang->execution->average = 'Average'; +$lang->execution->relatedMember = 'Team'; +$lang->execution->watermark = 'Exported by ZenTao'; +$lang->execution->burnXUnit = '(Date)'; +$lang->execution->burnYUnit = '(Hours)'; +$lang->execution->waitTasks = 'Waiting Tasks'; +$lang->execution->viewByUser = 'By User'; +$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}"; +$lang->execution->noLinkProduct = "Stage not linked {$lang->productCommon}"; +$lang->execution->recent = 'Recent visits: '; +$lang->execution->copyNoExecution = 'There are no ' . $lang->executionCommon . 'available to copy.'; +$lang->execution->noTeam = 'No team members at the moment'; +$lang->execution->or = ' or '; +$lang->execution->selectProject = 'Please select project'; +$lang->execution->unfoldClosed = 'Unfold Closed'; +$lang->execution->editName = 'Edit Name'; +$lang->execution->setWIP = 'WIP Settings'; +$lang->execution->sortColumn = 'Kanban Card Sorting'; +$lang->execution->batchCreateStroy = "Batch create {$lang->SRCommon}"; +$lang->execution->batchCreateTask = 'Batch create task'; +$lang->execution->kanbanNoLinkProduct = "Kanban not linked {$lang->productCommon}"; /* Fields of zt_team. */ $lang->execution->root = 'Root'; @@ -163,7 +164,7 @@ global $config; if($config->systemMode == 'new') { $lang->execution->aclList['private'] = 'Private (for team members and execution stakeholders)'; - $lang->execution->aclList['open'] = 'Inherited Execution ACL (for who can access the current execution)'; + $lang->execution->aclList['open'] = 'Inherited Project ACL (for who can access the current project)'; } else { @@ -171,6 +172,9 @@ else $lang->execution->aclList['open'] = "Public (Users who can visit {$lang->executionCommon} can access it.)"; } +$lang->execution->kanbanAclList['private'] = 'Private'; +$lang->execution->kanbanAclList['open'] = 'Inherited Project'; + $lang->execution->storyPoint = 'Story Point'; $lang->execution->burnByList['left'] = 'View by remaining hours'; @@ -247,6 +251,8 @@ $lang->execution->iteration = 'Iterations'; $lang->execution->iterationInfo = '%s Iterations'; $lang->execution->viewAll = 'View All'; $lang->execution->testreport = 'Test Report'; +$lang->execution->taskKanban = 'Task Kanban'; +$lang->execution->RDKanban = 'Research & Development Kanban'; /* Group browsing. */ $lang->execution->allTasks = 'All'; @@ -422,6 +428,7 @@ $lang->printKanban->typeList['increment'] = 'Increment'; $lang->execution->typeList[''] = ''; $lang->execution->typeList['stage'] = 'Stage'; $lang->execution->typeList['sprint'] = $lang->executionCommon; +$lang->execution->typeList['kanban'] = 'Kanban'; $lang->execution->featureBar['task']['all'] = $lang->execution->allTasks; $lang->execution->featureBar['task']['unclosed'] = $lang->execution->unclosed; diff --git a/module/execution/lang/zh-cn.php b/module/execution/lang/zh-cn.php index eed1188d4c..148047ed53 100644 --- a/module/execution/lang/zh-cn.php +++ b/module/execution/lang/zh-cn.php @@ -10,105 +10,106 @@ * @link http://www.zentao.net */ /* 字段列表。*/ -$lang->execution->allExecutions = '所有' . $lang->execution->common; -$lang->execution->allExecutionAB = "{$lang->execution->common}列表"; -$lang->execution->id = $lang->executionCommon . '编号'; -$lang->execution->type = $lang->executionCommon . '类型'; -$lang->execution->name = $lang->executionCommon . '名称'; -$lang->execution->code = $lang->executionCommon . '代号'; -$lang->execution->projectName = '所属项目'; -$lang->execution->execName = "{$lang->execution->common}名称"; -$lang->execution->execCode = "{$lang->execution->common}代号"; -$lang->execution->execType = "{$lang->execution->common}类型"; -$lang->execution->stage = '阶段'; -$lang->execution->pri = '优先级'; -$lang->execution->openedBy = '由谁创建'; -$lang->execution->openedDate = '创建日期'; -$lang->execution->closedBy = '由谁关闭'; -$lang->execution->closedDate = '关闭日期'; -$lang->execution->canceledBy = '由谁取消'; -$lang->execution->canceledDate = '取消日期'; -$lang->execution->begin = '计划开始'; -$lang->execution->end = '计划完成'; -$lang->execution->dateRange = '起始日期'; -$lang->execution->realBeganAB = '实际开始'; -$lang->execution->realEndAB = '实际完成'; -$lang->execution->realBegan = '实际开始日期'; -$lang->execution->realEnd = '实际完成日期'; -$lang->execution->to = '至'; -$lang->execution->days = '可用工作日'; -$lang->execution->day = '天'; -$lang->execution->workHour = '工时'; -$lang->execution->workHourUnit = 'h'; -$lang->execution->totalHours = '可用工时'; -$lang->execution->totalDays = '可用工日'; -$lang->execution->status = $lang->executionCommon . '状态'; -$lang->execution->execStatus = "{$lang->execution->common}状态"; -$lang->execution->subStatus = '子状态'; -$lang->execution->desc = $lang->executionCommon . '描述'; -$lang->execution->execDesc = "{$lang->execution->common}描述"; -$lang->execution->owner = '负责人'; -$lang->execution->PO = $lang->productCommon . '负责人'; -$lang->execution->PM = $lang->executionCommon . '负责人'; -$lang->execution->execPM = "{$lang->execution->common}负责人"; -$lang->execution->QD = '测试负责人'; -$lang->execution->RD = '发布负责人'; -$lang->execution->release = '发布'; -$lang->execution->acl = '访问控制'; -$lang->execution->teamname = '团队名称'; -$lang->execution->updateOrder = '排序'; -$lang->execution->order = $lang->executionCommon . '排序'; -$lang->execution->orderAB = '排序'; -$lang->execution->products = '相关' . $lang->productCommon; -$lang->execution->whitelist = '白名单'; -$lang->execution->addWhitelist = '添加白名单'; -$lang->execution->unbindWhitelist = '删除白名单'; -$lang->execution->totalEstimate = '预计'; -$lang->execution->totalConsumed = '消耗'; -$lang->execution->totalLeft = '剩余'; -$lang->execution->progress = '进度'; -$lang->execution->hours = '预计 %s 消耗 %s 剩余 %s'; -$lang->execution->viewBug = '查看bug'; -$lang->execution->noProduct = "无{$lang->executionCommon}"; -$lang->execution->createStory = "提{$lang->SRCommon}"; -$lang->execution->storyTitle = "{$lang->SRCommon}名称"; -$lang->execution->all = '所有'; -$lang->execution->undone = '未完成'; -$lang->execution->unclosed = '未关闭'; -$lang->execution->typeDesc = "运维{$lang->executionCommon}没有{$lang->SRCommon}、bug、版本、测试功能。"; -$lang->execution->mine = '我负责:'; -$lang->execution->involved = '我参与'; -$lang->execution->other = '其他'; -$lang->execution->deleted = '已删除'; -$lang->execution->delayed = '已延期'; -$lang->execution->product = $lang->execution->products; -$lang->execution->readjustTime = "调整{$lang->executionCommon}起止时间"; -$lang->execution->readjustTask = '顺延任务的起止时间'; -$lang->execution->effort = '日志'; -$lang->execution->storyEstimate = '需求估算'; -$lang->execution->newEstimate = '新一轮估算'; -$lang->execution->reestimate = '重新估算'; -$lang->execution->selectRound = '选择轮次'; -$lang->execution->average = '平均值'; -$lang->execution->relatedMember = '相关成员'; -$lang->execution->watermark = '由禅道导出'; -$lang->execution->burnXUnit = '(日期)'; -$lang->execution->burnYUnit = '(工时)'; -$lang->execution->waitTasks = '待处理'; -$lang->execution->viewByUser = '按用户查看'; -$lang->execution->oneProduct = "阶段只能关联一个{$lang->productCommon}"; -$lang->execution->noLinkProduct = "阶段没有关联{$lang->productCommon}"; -$lang->execution->recent = '近期访问:'; -$lang->execution->copyNoExecution = '没有可用的' . $lang->executionCommon . '来复制'; -$lang->execution->noTeam = '暂时没有团队成员'; -$lang->execution->or = '或'; -$lang->execution->selectProject = '请选择项目'; -$lang->execution->unfoldClosed = '展开已结束'; -$lang->execution->editName = '编辑名称'; -$lang->execution->setWIP = '在制品数量设置(WIP)'; -$lang->execution->sortColumn = '看板列卡片排序'; -$lang->execution->batchCreateStroy = "批量新建{$lang->SRCommon}"; -$lang->execution->batchCreateTask = '批量建任务'; +$lang->execution->allExecutions = '所有' . $lang->execution->common; +$lang->execution->allExecutionAB = "{$lang->execution->common}列表"; +$lang->execution->id = $lang->executionCommon . '编号'; +$lang->execution->type = $lang->executionCommon . '类型'; +$lang->execution->name = $lang->executionCommon . '名称'; +$lang->execution->code = $lang->executionCommon . '代号'; +$lang->execution->projectName = '所属项目'; +$lang->execution->execName = "{$lang->execution->common}名称"; +$lang->execution->execCode = "{$lang->execution->common}代号"; +$lang->execution->execType = "{$lang->execution->common}类型"; +$lang->execution->stage = '阶段'; +$lang->execution->pri = '优先级'; +$lang->execution->openedBy = '由谁创建'; +$lang->execution->openedDate = '创建日期'; +$lang->execution->closedBy = '由谁关闭'; +$lang->execution->closedDate = '关闭日期'; +$lang->execution->canceledBy = '由谁取消'; +$lang->execution->canceledDate = '取消日期'; +$lang->execution->begin = '计划开始'; +$lang->execution->end = '计划完成'; +$lang->execution->dateRange = '起始日期'; +$lang->execution->realBeganAB = '实际开始'; +$lang->execution->realEndAB = '实际完成'; +$lang->execution->realBegan = '实际开始日期'; +$lang->execution->realEnd = '实际完成日期'; +$lang->execution->to = '至'; +$lang->execution->days = '可用工作日'; +$lang->execution->day = '天'; +$lang->execution->workHour = '工时'; +$lang->execution->workHourUnit = 'h'; +$lang->execution->totalHours = '可用工时'; +$lang->execution->totalDays = '可用工日'; +$lang->execution->status = $lang->executionCommon . '状态'; +$lang->execution->execStatus = "{$lang->execution->common}状态"; +$lang->execution->subStatus = '子状态'; +$lang->execution->desc = $lang->executionCommon . '描述'; +$lang->execution->execDesc = "{$lang->execution->common}描述"; +$lang->execution->owner = '负责人'; +$lang->execution->PO = $lang->productCommon . '负责人'; +$lang->execution->PM = $lang->executionCommon . '负责人'; +$lang->execution->execPM = "{$lang->execution->common}负责人"; +$lang->execution->QD = '测试负责人'; +$lang->execution->RD = '发布负责人'; +$lang->execution->release = '发布'; +$lang->execution->acl = '访问控制'; +$lang->execution->teamname = '团队名称'; +$lang->execution->updateOrder = '排序'; +$lang->execution->order = $lang->executionCommon . '排序'; +$lang->execution->orderAB = '排序'; +$lang->execution->products = '相关' . $lang->productCommon; +$lang->execution->whitelist = '白名单'; +$lang->execution->addWhitelist = '添加白名单'; +$lang->execution->unbindWhitelist = '删除白名单'; +$lang->execution->totalEstimate = '预计'; +$lang->execution->totalConsumed = '消耗'; +$lang->execution->totalLeft = '剩余'; +$lang->execution->progress = '进度'; +$lang->execution->hours = '预计 %s 消耗 %s 剩余 %s'; +$lang->execution->viewBug = '查看bug'; +$lang->execution->noProduct = "无{$lang->executionCommon}"; +$lang->execution->createStory = "提{$lang->SRCommon}"; +$lang->execution->storyTitle = "{$lang->SRCommon}名称"; +$lang->execution->all = '所有'; +$lang->execution->undone = '未完成'; +$lang->execution->unclosed = '未关闭'; +$lang->execution->typeDesc = "运维{$lang->executionCommon}没有{$lang->SRCommon}、bug、版本、测试功能。"; +$lang->execution->mine = '我负责:'; +$lang->execution->involved = '我参与'; +$lang->execution->other = '其他'; +$lang->execution->deleted = '已删除'; +$lang->execution->delayed = '已延期'; +$lang->execution->product = $lang->execution->products; +$lang->execution->readjustTime = "调整{$lang->executionCommon}起止时间"; +$lang->execution->readjustTask = '顺延任务的起止时间'; +$lang->execution->effort = '日志'; +$lang->execution->storyEstimate = '需求估算'; +$lang->execution->newEstimate = '新一轮估算'; +$lang->execution->reestimate = '重新估算'; +$lang->execution->selectRound = '选择轮次'; +$lang->execution->average = '平均值'; +$lang->execution->relatedMember = '相关成员'; +$lang->execution->watermark = '由禅道导出'; +$lang->execution->burnXUnit = '(日期)'; +$lang->execution->burnYUnit = '(工时)'; +$lang->execution->waitTasks = '待处理'; +$lang->execution->viewByUser = '按用户查看'; +$lang->execution->oneProduct = "阶段只能关联一个{$lang->productCommon}"; +$lang->execution->noLinkProduct = "阶段没有关联{$lang->productCommon}"; +$lang->execution->recent = '近期访问:'; +$lang->execution->copyNoExecution = '没有可用的' . $lang->executionCommon . '来复制'; +$lang->execution->noTeam = '暂时没有团队成员'; +$lang->execution->or = '或'; +$lang->execution->selectProject = '请选择项目'; +$lang->execution->unfoldClosed = '展开已结束'; +$lang->execution->editName = '编辑名称'; +$lang->execution->setWIP = '在制品数量设置(WIP)'; +$lang->execution->sortColumn = '看板列卡片排序'; +$lang->execution->batchCreateStroy = "批量新建{$lang->SRCommon}"; +$lang->execution->batchCreateTask = '批量建任务'; +$lang->execution->kanbanNoLinkProduct = "看板没有关联{$lang->productCommon}"; /* Fields of zt_team. */ $lang->execution->root = '源ID'; @@ -171,6 +172,9 @@ else $lang->execution->aclList['open'] = "公开(有{$lang->executionCommon}视图权限即可访问)"; } +$lang->execution->kanbanAclList['private'] = '私有'; +$lang->execution->kanbanAclList['open'] = '继承项目'; + $lang->execution->storyPoint = '故事点'; $lang->execution->burnByList['left'] = '按剩余工时查看'; @@ -247,6 +251,8 @@ $lang->execution->iteration = '版本迭代'; $lang->execution->iterationInfo = '迭代%s次'; $lang->execution->viewAll = '查看所有'; $lang->execution->testreport = '测试报告'; +$lang->execution->taskKanban = '任务看板'; +$lang->execution->RDKanban = '研发看板'; /* 分组浏览。*/ $lang->execution->allTasks = '所有'; @@ -422,6 +428,7 @@ $lang->printKanban->typeList['increment'] = '增量'; $lang->execution->typeList[''] = ''; $lang->execution->typeList['stage'] = '阶段'; $lang->execution->typeList['sprint'] = $lang->executionCommon; +$lang->execution->typeList['kanban'] = '看板'; $lang->execution->featureBar['task']['all'] = $lang->execution->allTasks; $lang->execution->featureBar['task']['unclosed'] = $lang->execution->unclosed; diff --git a/module/execution/model.php b/module/execution/model.php index 319c35e1a6..d584e2beec 100644 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -63,16 +63,21 @@ class executionModel extends model */ public function setMenu($executionID, $buildID = 0, $extra = '') { + $execution = $this->getByID($executionID); + if($execution and $execution->type == 'kanban') + { + $this->lang->execution->menu = new stdclass(); + $this->lang->execution->accessDenied = str_replace($this->lang->executionCommon, $this->lang->execution->kanban, $this->lang->execution->accessDenied); + } + if(!$this->app->user->admin and strpos(",{$this->app->user->view->sprints},", ",$executionID,") === false and !defined('TUTORIAL') and $executionID != 0) die(js::error($this->lang->execution->accessDenied) . js::locate('back')); - $executions = $this->loadModel('execution')->getPairs(0, 'all', 'nocode'); + $executions = $this->getPairs(0, 'all', 'nocode'); if(!$executionID and $this->session->execution) $executionID = $this->session->execution; if(!$executionID or !in_array($executionID, array_keys($executions))) $executionID = key($executions); $this->session->set('execution', $executionID); /* Unset story, bug, build and testtask if type is ops. */ - $execution = $this->getByID($executionID); - if($execution and $execution->type == 'stage' and $this->config->systemMode == 'new') { global $lang; @@ -88,15 +93,6 @@ class executionModel extends model unset($this->lang->execution->menu->build); } - /* Hide story and qa menu when execution is story or design type. */ - /* - if($execution and ($execution->attribute == 'story' or $execution->attribute == 'design')) - { - unset($this->lang->execution->menu->story); - unset($this->lang->execution->menu->qa); - } - */ - if($executions and (!isset($executions[$executionID]) or !$this->checkPriv($executionID))) $this->accessDenied(); $moduleName = $this->app->getModuleName(); @@ -312,6 +308,12 @@ class executionModel extends model return false; } + if($type == 'kanban' and empty($this->post->products[0])) + { + dao::$errors['message'][] = $this->lang->execution->kanbanNoLinkProduct; + return false; + } + $this->config->execution->create->requiredFields .= ',project'; } @@ -381,7 +383,7 @@ class executionModel extends model $creatorExists = false; $teamMembers = array(); - $this->loadModel('kanban')->createExecutionLane($executionID); + if((isset($project) and $project->model != 'kanban') or empty($project)) $this->loadModel('kanban')->createExecutionLane($executionID); /* Save order. */ $this->dao->update(TABLE_EXECUTION)->set('`order`')->eq($executionID * 5)->where('id')->eq($executionID)->exec(); @@ -474,10 +476,6 @@ class executionModel extends model return false; } - /* Get team and language item. */ - $team = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution'); - $this->lang->execution->team = $this->lang->execution->teamname; - /* Get the data from the post. */ $execution = fixer::input('post') ->setDefault('lastEditedBy', $this->app->user->account) @@ -529,6 +527,12 @@ class executionModel extends model ->limit(1) ->exec(); + if($oldExecution->type == 'kanban') $this->dao->delete()->from(TABLE_TEAM)->where('root')->eq((int)$executionID)->andWhere('type')->eq('execution')->andWhere('account')->ne($oldExecution->openedBy)->exec(); + + /* Get team and language item. */ + $this->lang->execution->team = $this->lang->execution->teamname; + $team = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution'); + $changedAccounts = array(); $teamMembers = array(); foreach($this->config->execution->ownerFields as $ownerField) @@ -1054,7 +1058,7 @@ class executionModel extends model /* Order by status's content whether or not done */ $executions = $this->dao->select('*, IF(INSTR("done,closed", status) < 2, 0, 1) AS isDone, INSTR("doing,wait,suspended,closed", status) AS sortStatus')->from(TABLE_EXECUTION) ->where('deleted')->eq(0) - ->beginIF($type == 'all')->andWhere('type')->in('stage,sprint')->fi() + ->beginIF($type == 'all')->andWhere('type')->in('stage,sprint,kanban')->fi() ->beginIF($projectID and $this->config->systemMode == 'new')->andWhere('project')->eq($projectID)->fi() ->beginIF($type != 'all' and $this->config->systemMode == 'new')->andWhere('type')->eq($type)->fi() ->beginIF(strpos($mode, 'withdelete') === false)->andWhere('deleted')->eq(0)->fi() @@ -1202,7 +1206,7 @@ class executionModel extends model public function getIdList($projectID, $status = 'all') { return $this->dao->select('id')->from(TABLE_EXECUTION) - ->where('type')->in('sprint,stage') + ->where('type')->in('sprint,stage,kanban') ->andWhere('deleted')->eq('0') ->beginIF($projectID)->andWhere('project')->eq($projectID)->fi() ->beginIF($status == 'undone')->andWhere('status')->notIN('done,closed')->fi() @@ -1228,7 +1232,7 @@ class executionModel extends model $orderBy = (isset($project->model) and $project->model == 'waterfall') ? 'begin_asc,id_asc' : 'begin_desc,id_desc'; $executions = $this->dao->select('*')->from(TABLE_EXECUTION) - ->where('type')->in('stage,sprint') + ->where('type')->in('stage,sprint,kanban') ->andWhere('deleted')->eq('0') ->beginIF($projectID)->andWhere('project')->eq((int)$projectID)->fi() ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi() @@ -1319,7 +1323,7 @@ class executionModel extends model $module = 'execution'; $method = 'task'; } - if($module == 'testcase' and ($method == 'view' || $method == 'edit' || $method == 'batchedit')) + if($module == 'testcase' and ($method == 'view' or $method == 'edit' or $method == 'batchedit')) { $module = 'execution'; $method = 'testcase'; @@ -1329,7 +1333,7 @@ class executionModel extends model $module = 'execution'; $method = 'testtask'; } - if($module == 'build' and ($method == 'edit' || $method= 'view')) + if($module == 'build' and ($method == 'edit' or $method == 'view')) { $module = 'execution'; $method = 'build'; @@ -1674,15 +1678,16 @@ class executionModel extends model /** * Get branch pairs by product id list. * - * @param array $products - * @param int $projectID - * @param string $param + * @param array $products + * @param int $projectID + * @param string $param + * @param string|array $appendBranch * @access public * @return array */ - public function getBranchByProduct($products, $projectID = 0, $param = 'noclosed') + public function getBranchByProduct($products, $projectID = 0, $param = 'noclosed', $appendBranch = '') { - $branchGroups = $this->loadModel('branch')->getByProducts($products, $param); + $branchGroups = $this->loadModel('branch')->getByProducts($products, $param, $appendBranch); if($projectID) { @@ -2267,30 +2272,39 @@ class executionModel extends model /** * Link story. * - * @param int $executionID - * @param array $stories - * @param array $products + * @param int $executionID + * @param array $stories + * @param array $products + * @param string $extra * * @access public * @return bool */ - public function linkStory($executionID, $stories = array(), $products = array()) + public function linkStory($executionID, $stories = array(), $products = array(), $extra = '') { + if(empty($executionID)) return false; if(empty($stories)) $stories = $this->post->stories; if(empty($stories)) return false; if(empty($products)) $products = $this->post->products; $this->loadModel('action'); + $this->loadModel('kanban'); $versions = $this->loadModel('story')->getVersions($stories); $linkedStories = $this->dao->select('*')->from(TABLE_PROJECTSTORY)->where('project')->eq($executionID)->orderBy('order_desc')->fetchPairs('story', 'order'); $lastOrder = reset($linkedStories); $storyList = $this->dao->select('id, status, branch')->from(TABLE_STORY)->where('id')->in(array_values($stories))->fetchAll('id'); + $execution = $this->getById($executionID); + + $extra = str_replace(array(',', ' '), array('&', ''), $extra); + parse_str($extra, $output); foreach($stories as $key => $storyID) { $notAllowedStatus = $this->app->rawMethod == 'batchcreate' ? 'closed' : 'draft,closed'; if(strpos($notAllowedStatus, $storyList[$storyID]->status) !== false) continue; if(isset($linkedStories[$storyID])) continue; + if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($executionID, $output['laneID'], $output['columnID'], 'story', $storyID); + $data = new stdclass(); $data->project = $executionID; $data->product = (int)$products[$storyID]; @@ -2306,6 +2320,8 @@ class executionModel extends model $action = $executionID == $this->session->project ? 'linked2project' : 'linked2execution'; $this->action->create('story', $storyID, $action, '', $executionID); } + + if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($executionID, 'story'); } /** @@ -2477,6 +2493,23 @@ class executionModel extends model ->fetchAll('account'); } + /** + * Get members by execution id list. + * + * @param array $executionIdList + * @access public + * @return void + */ + public function getMembersByIdList($executionIdList) + { + return $this->dao->select("t1.root, t1.account, t2.realname")->from(TABLE_TEAM)->alias('t1') + ->leftJoin(TABLE_USER)->alias('t2')->on('t1.account = t2.account') + ->where('t1.root')->in($executionIdList) + ->andWhere('t1.type')->eq('execution') + ->andWhere('t2.deleted')->eq('0') + ->fetchGroup('root'); + } + /** * Get the skip members of the team. * @@ -3020,13 +3053,16 @@ class executionModel extends model */ public static function isClickable($execution, $action) { - $action = strtolower($action); + $action = strtolower($action); + $clickable = commonModel::hasPriv('execution', $action); + if(!$clickable) return false; if($action == 'start') return $execution->status == 'wait'; if($action == 'close') return $execution->status != 'closed'; if($action == 'suspend') return $execution->status == 'wait' or $execution->status == 'doing'; if($action == 'putoff') return $execution->status == 'wait' or $execution->status == 'doing'; if($action == 'activate') return $execution->status == 'suspended' or $execution->status == 'closed'; + if($action == 'delete') return $execution->status == 'wait' or $execution->status == 'doing'; return true; } @@ -3210,13 +3246,14 @@ class executionModel extends model /** * Get Kanban tasks * - * @param int $executionID - * @param string $orderBy - * @param object $pager + * @param int $executionID + * @param string $orderBy + * @param object $pager + * @param array|string $excludeTasks * @access public * @return void */ - public function getKanbanTasks($executionID, $orderBy = 'status_asc, id_desc', $pager = null) + public function getKanbanTasks($executionID, $orderBy = 'status_asc, id_desc', $pager = null, $excludeTasks = '') { $tasks = $this->dao->select('t1.*, t2.id AS storyID, t2.title AS storyTitle, t2.version AS latestStoryVersion, t2.status AS storyStatus, t3.realname AS assignedToRealName') ->from(TABLE_TASK)->alias('t1') @@ -3225,6 +3262,7 @@ class executionModel extends model ->where('t1.execution')->eq((int)$executionID) ->andWhere('t1.deleted')->eq(0) ->andWhere('t1.parent')->ge(0) + ->beginIF($excludeTasks)->andWhere('t1.id')->notIN($excludeTasks)->fi() ->orderBy($orderBy) ->page($pager) ->fetchAll('id'); diff --git a/module/execution/view/ajaxgetdropmenu.html.php b/module/execution/view/ajaxgetdropmenu.html.php index e5ac0900c0..529e93b029 100644 --- a/module/execution/view/ajaxgetdropmenu.html.php +++ b/module/execution/view/ajaxgetdropmenu.html.php @@ -72,6 +72,11 @@ foreach($executions as $projectID => $projectExecutions) foreach($projectExecutions as $index => $execution) { + $kanbanLink = $this->createLink('execution', 'kanban', "executionID=%s"); + $taskLink = $this->createLink('execution', 'task', "executionID=%s"); + if($execution->type != 'kanban' and $link == $kanbanLink) $link = $taskLink; + if($execution->type == 'kanban' and $link != $kanbanLink) $link = $kanbanLink; + $selected = $execution->id == $executionID ? 'selected' : ''; if($execution->status != 'done' and $execution->status != 'closed' and ($execution->PM == $this->app->user->account or isset($execution->teams[$this->app->user->account]))) { diff --git a/module/execution/view/all.html.php b/module/execution/view/all.html.php index 4c46d38b2f..38e8118dfb 100644 --- a/module/execution/view/all.html.php +++ b/module/execution/view/all.html.php @@ -118,11 +118,10 @@ id);?>
flex' title='name?>'> - config->maxVersion)):?> - '>execution->typeList[$execution->type]?> - + '>execution->typeList[$execution->type]?> children) ? $execution->name : html::a($this->createLink('execution', 'task', 'execution=' . $execution->id), $execution->name, '', "class='text-ellipsis'"); + $executionLink = $execution->projectModel == 'kanban' ? html::a($this->createLink('execution', 'kanban', 'executionID=' . $execution->id), $execution->name, '', "class='text-ellipsis'") : html::a($this->createLink('execution', 'task', 'execution=' . $execution->id), $execution->name, '', "class='text-ellipsis'"); + echo !empty($execution->children) ? $execution->name : $executionLink; if(isset($execution->delay)) echo "{$lang->execution->delayed} "; ?> children)):?> diff --git a/module/execution/view/create.html.php b/module/execution/view/create.html.php index 2fb95dbfc9..d9738e31ab 100644 --- a/module/execution/view/create.html.php +++ b/module/execution/view/create.html.php @@ -42,6 +42,7 @@ project->common);?> systemMode);?> +
@@ -88,6 +89,7 @@
systemMode == 'new')) ? $lang->execution->execType : $lang->execution->type;?> @@ -104,6 +106,7 @@
execution->typeDesc;?>
stage->percent;?>
execution->teamname;?> execution->copyTeam;?> execution->copyTeamTip}'"); ?>
execution->owner;?> @@ -210,10 +215,12 @@
execution->team;?>
systemMode == 'new')) ? $lang->execution->execDesc : $lang->execution->desc;?> diff --git a/module/execution/view/edit.html.php b/module/execution/view/edit.html.php index dfd74c8305..0316288f4f 100644 --- a/module/execution/view/edit.html.php +++ b/module/execution/view/edit.html.php @@ -66,6 +66,7 @@
execution->type;?> @@ -81,6 +82,7 @@ ?>
execution->teamname;?> team, "class='form-control'");?>
- ' . $lang->kanban->create, '', 'class="btn btn-secondary iframe" data-width="75%"', '', true);?> + ' . $lang->kanban->create, '', 'class="btn btn-secondary iframe" data-width="75%"', '', true);?> ' . $lang->kanban->createSpace, '', 'class="btn btn-primary iframe" data-width="75%"', '', true);?>
@@ -50,7 +50,7 @@
status == 'closed' ? 'disabled' : '';?> - id}", ' ' . $lang->kanban->create, '', "class='iframe' data-width='75%'", '', true);?> + status != 'closed') common::printLink('kanban', 'create', "spaceID={$space->id}", ' ' . $lang->kanban->create, '', "class='iframe' data-width='75%'", '', true);?> id}", ' ' . $lang->kanban->setting, '', "class='iframe' data-width='75%'", '', true);?> id}", ' ' . $lang->close, '', "class='iframe {$class}'", '', true);?> id}", ' ' . $lang->delete, 'hiddenwin', '', '', true);?> @@ -141,9 +141,11 @@
kanban->teamSumCount, $teamCount);?>
- acl == 'open' ? 'unlock' : 'lock';?> + + acl == 'private') $icon = 'lock';?> + acl == 'extend') $icon = 'inherit-space';?> - kanban->aclGroup, $kanban->acl == 'open' ? 'open' : 'private', '');?> + kanban->aclGroup, $kanban->acl, '');?>
diff --git a/module/kanban/view/view.html.php b/module/kanban/view/view.html.php index 01a5ebb44b..2855b0104d 100644 --- a/module/kanban/view/view.html.php +++ b/module/kanban/view/view.html.php @@ -30,6 +30,7 @@ js::set('noAssigned', $lang->kanbancard->noAssigned); js::set('users', $users); js::set('colorListLang', $lang->kanbancard->colorList); js::set('colorList', $this->config->kanban->cardColorList); +js::set('displayCards', $kanban->displayCards); js::set('priv', array( diff --git a/module/kanban/view/viewarchivedcard.html.php b/module/kanban/view/viewarchivedcard.html.php index cf7acfd42c..f8183d39bd 100644 --- a/module/kanban/view/viewarchivedcard.html.php +++ b/module/kanban/view/viewarchivedcard.html.php @@ -22,6 +22,7 @@ #archivedCards .kanban-item > .info > .user {position: absolute; right: 0; top: 0} #archivedCards .card-actions {position: relative; padding: 15px 10px; padding-top: 0px;} #archivedCards .card-actions > .btn {display: block;} +[lang^='en'] #archivedCards .card-actions > .btn {width: 55px;} #archivedCards .card-actions > .btn + .btn {margin-top: 10px;} #archivedCards .info > .time {background-color: rgba(0, 0, 0, 0.15);} #archivedCards .info > .users > span {display: inline-block; color: transparent; width: 2px; height: 2px; background-color: #8990a2; position:sticky; top: 3px; margin: 0 7px; border-radius: 50%; line-height: 32px;} diff --git a/module/misc/lang/en.php b/module/misc/lang/en.php index 5c8307f44a..c6ceca2235 100644 --- a/module/misc/lang/en.php +++ b/module/misc/lang/en.php @@ -94,6 +94,8 @@ $lang->misc->feature->downloadFile = 'Download introduction'; $lang->misc->feature->tutorialDesc = '

ZenTao 15.0 has new functions, and you know how to use it through the "Tutorial".

Click your [Avatar-Theme-Young Blue] to set it.

'; $lang->misc->feature->themeDesc = '

ZenTao 15.0+ a new "Youth Blue" theme, the pages are more beautiful and the experience is more friendly.

Click your [Avatar-Theme-Young Blue] to set it.

'; +$lang->misc->releaseDate['16.2'] = '2022-01-17'; +$lang->misc->releaseDate['16.1'] = '2022-01-11'; $lang->misc->releaseDate['16.0'] = '2021-12-24'; $lang->misc->releaseDate['16.0.beta1'] = '2021-12-06'; $lang->misc->releaseDate['15.7.1'] = '2021-11-02'; @@ -164,6 +166,8 @@ $lang->misc->releaseDate['7.2.stable'] = '2015-05-22'; $lang->misc->releaseDate['7.1.stable'] = '2015-03-07'; $lang->misc->releaseDate['6.3.stable'] = '2014-11-07'; +$lang->misc->feature->all['16.2'][] = array('title' => 'Add kanban model project, fix bugs.', 'desc' => ''); +$lang->misc->feature->all['16.1'][] = array('title' => 'Plan to add status management and Kanban view, upgrade process optimization, fix bugs.', 'desc' => ''); $lang->misc->feature->all['16.0'][] = array('title' => 'New general board, improve branch management, fix bugs.', 'desc' => ''); $lang->misc->feature->all['16.0.beta1'][] = array('title' => 'Add waterfall model project, add task kanban, improved branch management, and fixed bugs.', 'desc' => ''); $lang->misc->feature->all['15.7.1'][] = array('title' => 'Fix bug.', 'desc' => ''); diff --git a/module/misc/lang/zh-cn.php b/module/misc/lang/zh-cn.php index 6d5c7de54b..ea787d429d 100644 --- a/module/misc/lang/zh-cn.php +++ b/module/misc/lang/zh-cn.php @@ -94,6 +94,8 @@ $lang->misc->feature->downloadFile = '下载新版本功能介绍文档'; $lang->misc->feature->tutorialDesc = "

禅道15系列新增了多项功能,您可以通过“新手引导教程”快速了解禅道的基本使用方法。

通过鼠标经过 [头像-新手引导],点击新手引导,即可进入新手引导教程。

"; $lang->misc->feature->themeDesc = "

禅道15系列上线了全新的“青春蓝”主题,页面呈现更加美观,体验更加友好。

通过鼠标经过 [头像-主题-青春蓝],点击青春蓝,即可设置成功。

"; +$lang->misc->releaseDate['16.2'] = '2022-01-17'; +$lang->misc->releaseDate['16.1'] = '2022-01-11'; $lang->misc->releaseDate['16.0'] = '2021-12-24'; $lang->misc->releaseDate['16.0.beta1'] = '2021-12-06'; $lang->misc->releaseDate['15.7.1'] = '2021-11-02'; @@ -164,6 +166,8 @@ $lang->misc->releaseDate['7.2.stable'] = '2015-05-22'; $lang->misc->releaseDate['7.1.stable'] = '2015-03-07'; $lang->misc->releaseDate['6.3.stable'] = '2014-11-07'; +$lang->misc->feature->all['16.2'][] = array('title' => '新增专业研发看板,可以创建看板模型项目,修复Bug。', 'desc' => ''); +$lang->misc->feature->all['16.1'][] = array('title' => '计划增加状态管理和看板视图,升级流程优化,修复Bug。', 'desc' => ''); $lang->misc->feature->all['16.0'][] = array('title' => '新增通用看板,完善分支管理,修复Bug。', 'desc' => ''); $lang->misc->feature->all['16.0.beta1'][] = array('title' => '新增瀑布模型项目,新增任务看板,完善分支管理和细节,修复Bug。', 'desc' => ''); $lang->misc->feature->all['15.7.1'][] = array('title' => '修复Bug。', 'desc' => ''); diff --git a/module/misc/view/links.html.php b/module/misc/view/links.html.php index 79801b0346..98369bae28 100755 --- a/module/misc/view/links.html.php +++ b/module/misc/view/links.html.php @@ -7,7 +7,7 @@