diff --git a/VERSION b/VERSION index 48f8d64e0c..546fe2354d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.6.2 +11.6.3 diff --git a/config/config.php b/config/config.php index ee8f73de3b..e2d63a3b71 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 = '11.6.2'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it. +$config->version = '11.6.3'; // 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/db/standard/zentao11.6.3.sql b/db/standard/zentao11.6.3.sql new file mode 100644 index 0000000000..436985846a --- /dev/null +++ b/db/standard/zentao11.6.3.sql @@ -0,0 +1,937 @@ +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(9) NOT NULL, + `actor` varchar(30) 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 `objectID` (`objectID`) +) 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, + `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 `accountModuleOrder` (`account`,`module`,`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, + `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, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `project` 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, + `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, + `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 `project` (`project`), + KEY `status` (`status`), + KEY `plan` (`plan`), + KEY `story` (`story`), + KEY `case` (`case`), + KEY `assignedTo` (`assignedTo`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_build` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `branch` mediumint(8) unsigned NOT NULL DEFAULT '0', + `project` 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 `project` (`project`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_burn` ( + `project` 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, + PRIMARY KEY (`project`,`date`,`task`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_case` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT '0', + `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', + `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, + `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 `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_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` text 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` tinyint(3) 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_doc` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL, + `project` 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, + `collector` text NOT NULL, + `addedBy` varchar(30) NOT NULL, + `addedDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `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 `project` (`project`), + 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` text 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, + `name` varchar(60) NOT NULL, + `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, + `order` tinyint(5) unsigned NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `project` (`project`) +) 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 IF NOT EXISTS `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` (`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, + `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_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_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`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_product` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(90) NOT NULL, + `code` varchar(45) NOT NULL, + `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, + `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 `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, + `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, + `isCat` enum('1','0') NOT NULL DEFAULT '0', + `catID` mediumint(8) unsigned NOT NULL, + `type` varchar(20) NOT NULL DEFAULT 'sprint', + `parent` mediumint(8) unsigned NOT NULL DEFAULT '0', + `name` varchar(90) NOT NULL, + `code` varchar(45) NOT NULL, + `begin` date NOT NULL, + `end` date NOT NULL, + `days` smallint(5) unsigned NOT NULL, + `status` varchar(10) NOT NULL, + `subStatus` varchar(30) NOT NULL DEFAULT '', + `statge` enum('1','2','3','4','5') NOT NULL DEFAULT '1', + `pri` enum('1','2','3','4') NOT NULL DEFAULT '1', + `desc` text NOT NULL, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime NOT NULL, + `openedVersion` varchar(20) NOT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime NOT NULL, + `canceledBy` varchar(30) NOT NULL DEFAULT '', + `canceledDate` datetime 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` enum('open','private','custom') 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 `order` (`order`) +) 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`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectstory` ( + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `product` 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_release` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `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, + `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_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 IF NOT EXISTS `zt_story` ( + `id` mediumint(8) unsigned NOT NULL auto_increment, + `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 '', + `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` date NOT NULL, + `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', + `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_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 IF NOT EXISTS `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, + `parent` mediumint(8) NOT NULL DEFAULT '0', + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `module` mediumint(8) unsigned NOT NULL DEFAULT '0', + `story` mediumint(8) unsigned NOT NULL DEFAULT '0', + `storyVersion` smallint(6) NOT NULL DEFAULT '1', + `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, + `openedBy` varchar(30) NOT NULL, + `openedDate` datetime NOT NULL, + `assignedTo` varchar(30) NOT NULL, + `assignedDate` datetime NOT NULL, + `estStarted` date NOT NULL, + `realStarted` date 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, + `closedReason` varchar(30) NOT NULL, + `lastEditedBy` varchar(30) NOT NULL, + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `project` (`project`), + KEY `story` (`story`), + 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_team` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `root` mediumint(8) unsigned NOT NULL DEFAULT '0', + `type` enum('project','task') 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(2,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, + `product` mediumint(8) unsigned NOT NULL, + `project` 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, + `caseResult` char(30) NOT NULL, + `stepResults` text NOT NULL, + `lastRunner` varchar(30) NOT NULL, + `date` datetime 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, + `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, + `name` char(90) NOT NULL, + `product` mediumint(8) unsigned NOT NULL, + `project` mediumint(8) unsigned NOT NULL DEFAULT '0', + `build` char(30) NOT NULL, + `owner` varchar(30) NOT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT '0', + `begin` date NOT NULL, + `end` date NOT NULL, + `mailto` text, + `desc` text NOT NULL, + `report` text NOT NULL, + `status` enum('blocked','doing','wait','done') NOT NULL DEFAULT 'wait', + `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_translation` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `lang` varchar(30) NOT NULL, + `module` varchar(30) NOT NULL, + `key` varchar(100) NOT NULL, + `value` text NOT NULL, + `referer` text NOT NULL, + `status` varchar(30) NOT NULL, + `translator` char(30) NOT NULL, + `translatedTime` datetime NOT NULL, + `reviewer` char(30) NOT NULL, + `reviewedTime` datetime NOT NULL, + `reason` text NOT NULL, + `version` varchar(20) NOT NULL, + `mode` varchar(20) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `lang_module_key_mode` (`lang`,`module`,`key`,`mode`) +) 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, + 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, + `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 '', + `nickname` char(60) NOT NULL DEFAULT '', + `commiter` varchar(100) NOT NULL, + `avatar` char(30) NOT NULL DEFAULT '', + `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 '', + `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', + PRIMARY KEY (`id`), + UNIQUE KEY `account` (`account`), + KEY `dept` (`dept`), + KEY `email` (`email`), + KEY `commiter` (`commiter`) +) 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', + 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, + `products` mediumtext NOT NULL, + `projects` 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(10) NOT NULL DEFAULT 'default', + `name` varchar(50) NOT NULL, + `url` varchar(255) NOT NULL, + `domain` varchar(255) NOT NULL, + `contentType` varchar(30) NOT NULL DEFAULT 'application/json', + `sendType` enum('sync','async') NOT NULL DEFAULT 'sync', + `products` text NOT NULL, + `projects` 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; diff --git a/db/zentao.sql b/db/zentao.sql index 5b0927f08c..eeb638d840 100644 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -1026,7 +1026,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (1, 'admin', 'index'), (1, 'admin', 'safe'), (1, 'api', 'debug'), -(1, 'api', 'getModel'), (1, 'api', 'sql'), (1, 'backup', 'backup'), (1, 'backup', 'change'), @@ -1433,7 +1432,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (1, 'user', 'view'), (1, 'user', 'unbind'), (2, 'action', 'editComment'), -(2, 'api', 'getModel'), (2, 'bug', 'activate'), (2, 'bug', 'assignTo'), (2, 'bug', 'batchAssignTo'), @@ -1620,7 +1618,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (2, 'user', 'todo'), (2, 'user', 'view'), (3, 'action', 'editComment'), -(3, 'api', 'getModel'), (3, 'bug', 'activate'), (3, 'bug', 'assignTo'), (3, 'bug', 'batchClose'), @@ -1847,7 +1844,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (4, 'action', 'trash'), (4, 'action', 'undelete'), (4, 'admin', 'index'), -(4, 'api', 'getModel'), (4, 'bug', 'activate'), (4, 'bug', 'assignTo'), (4, 'bug', 'batchAssignTo'), @@ -2086,7 +2082,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (5, 'action', 'trash'), (5, 'action', 'undelete'), (5, 'admin', 'index'), -(5, 'api', 'getModel'), (5, 'bug', 'activate'), (5, 'bug', 'assignTo'), (5, 'bug', 'batchAssignTo'), @@ -2351,7 +2346,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (6, 'action', 'trash'), (6, 'action', 'undelete'), (6, 'admin', 'index'), -(6, 'api', 'getModel'), (6, 'bug', 'activate'), (6, 'bug', 'assignTo'), (6, 'bug', 'batchAssignTo'), @@ -2588,7 +2582,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (7, 'action', 'trash'), (7, 'action', 'undelete'), (7, 'admin', 'index'), -(7, 'api', 'getModel'), (7, 'bug', 'activate'), (7, 'bug', 'assignTo'), (7, 'bug', 'batchClose'), @@ -2838,7 +2831,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (8, 'action', 'trash'), (8, 'action', 'undelete'), (8, 'admin', 'index'), -(8, 'api', 'getModel'), (8, 'bug', 'activate'), (8, 'bug', 'assignTo'), (8, 'bug', 'batchAssignTo'), @@ -3098,7 +3090,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (9, 'action', 'trash'), (9, 'action', 'undelete'), (9, 'admin', 'index'), -(9, 'api', 'getModel'), (9, 'bug', 'browse'), (9, 'bug', 'export'), (9, 'bug', 'index'), @@ -3266,7 +3257,6 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (9, 'user', 'view'), (9, 'user', 'unbind'), (10, 'action', 'editComment'), -(10, 'api', 'getModel'), (10, 'bug', 'activate'), (10, 'bug', 'browse'), (10, 'bug', 'close'), diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 2808d98881..091185b9cb 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -1,3 +1,9 @@ +2019-09-24 11.6.3 +修复的Bug: +2722 多人任务轮流完成之后状态异常 +2755 项目下需求页面搜索异常 +2766 导入用例时解析了js代码 + 2019-09-06 11.6.2 完成的需求: 4346 执行 init.sh生成的脚本和后台计划任务保持一致 diff --git a/framework/helper.class.php b/framework/helper.class.php index b8bfde4127..1ef5f1ac61 100644 --- a/framework/helper.class.php +++ b/framework/helper.class.php @@ -96,7 +96,8 @@ class helper extends baseHelper if($position !== false) $toEncoding = substr($toEncoding, 0, $position); /* Check string encoding. */ - $encoding = strtolower(mb_detect_encoding($string, array('ASCII','UTF-8','GB2312','GBK','BIG5'))); + $encodings = array_merge(array('GB2312','GBK','BIG5'), mb_list_encodings()); + $encoding = strtolower(mb_detect_encoding($string, $encodings)); if($encoding == $toEncoding) return $string; return mb_convert_encoding($string, $toEncoding, $encoding); } @@ -178,4 +179,4 @@ function autoloader($class) } } -spl_autoload_register('autoloader'); \ No newline at end of file +spl_autoload_register('autoloader'); diff --git a/lib/api/api.class.php b/lib/api/api.class.php index 99ec4e2880..f5e1b00550 100755 --- a/lib/api/api.class.php +++ b/lib/api/api.class.php @@ -99,7 +99,7 @@ class ztclient $controlAPI = $this->zentao->root . $module . $this->zentao->requestFix . $method . $this->zentao->requestFix; if($vars) { - $vars = parse_str($vars); + parse_str($vars, $vars); foreach($vars as $var) $controlAPI .= $var . $this->zentao->requestFix; } $controlAPI = rtrim($controlAPI, $this->zentao->requestFix) . '.json'; diff --git a/module/action/lang/en.php b/module/action/lang/en.php index b971bdd570..e76cc268d3 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -137,6 +137,7 @@ $lang->action->desc->linkchildtask = '$date, $actor linked $lang->action->desc->unlinkchildrentask = '$date, $actor unlinked a child task $extra。' . "\n"; $lang->action->desc->linkparenttask = '$date, $actor linked to a parent task $extra。' . "\n"; $lang->action->desc->unlinkparenttask = '$date, $actor unlinked a parent task $extra。' . "\n"; +$lang->action->desc->deletechildrentask = '$date, $actor deleted a child task $extra。' . "\n"; /* Historical record of actions when associating and removing cases. */ $lang->action->desc->linkrelatedcase = '$date, $actor linked a case $extra.' . "\n"; @@ -209,6 +210,7 @@ $lang->action->label->unlinkparenttask = "unlink from parent task"; $lang->action->label->batchcreate = "batch created tasks"; $lang->action->label->createchildren = "create child tasks"; $lang->action->label->managed = "managed"; +$lang->action->label->deletechildrentask = "delete children task"; /* Dynamic information is grouped by object. */ $lang->action->dynamicAction = new stdclass; @@ -281,6 +283,7 @@ $lang->action->dynamicAction->task['canceled'] = 'Cancel Task'; $lang->action->dynamicAction->task['activated'] = 'Activate Task'; $lang->action->dynamicAction->task['createchildren'] = 'Create Child Task'; $lang->action->dynamicAction->task['unlinkparenttask'] = 'Unlink Parent Task'; +$lang->action->dynamicAction->task['deletechildrentask'] = 'Delete children task'; $lang->action->dynamicAction->task['linkparenttask'] = 'Link Parent Task'; $lang->action->dynamicAction->task['linkchildtask'] = 'Link Child Task'; $lang->action->dynamicAction->task['undeleted'] = 'Restore Task'; diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index e6d26c50d3..c5315de6ef 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -137,6 +137,7 @@ $lang->action->desc->linkchildtask = '$date, 由 $actor $lang->action->desc->unlinkchildrentask = '$date, 由 $actor 移除子任务 $extra。' . "\n"; $lang->action->desc->linkparenttask = '$date, 由 $actor 关联到父任务 $extra。' . "\n"; $lang->action->desc->unlinkparenttask = '$date, 由 $actor 从父任务$extra取消关联。' . "\n"; +$lang->action->desc->deletechildrentask = '$date, 由 $actor 删除子任务$extra。' . "\n"; /* 关联用例和移除用例时的历史操作记录。*/ $lang->action->desc->linkrelatedcase = '$date, 由 $actor 关联相关用例 $extra。' . "\n"; @@ -209,6 +210,7 @@ $lang->action->label->unlinkparenttask = "从父任务取消关联"; $lang->action->label->batchcreate = "批量创建任务"; $lang->action->label->createchildren = "创建子任务"; $lang->action->label->managed = "维护"; +$lang->action->label->deletechildrentask = "删除子任务"; /* 动态信息按照对象分组 */ $lang->action->dynamicAction = new stdclass(); @@ -281,6 +283,7 @@ $lang->action->dynamicAction->task['canceled'] = '取消任务'; $lang->action->dynamicAction->task['activated'] = '激活任务'; $lang->action->dynamicAction->task['createchildren'] = '创建子任务'; $lang->action->dynamicAction->task['unlinkparenttask'] = '从父任务取消关联'; +$lang->action->dynamicAction->task['deletechildrentask'] = '删除子任务'; $lang->action->dynamicAction->task['linkparenttask'] = '关联到父任务'; $lang->action->dynamicAction->task['linkchildtask'] = '关联子任务'; $lang->action->dynamicAction->task['undeleted'] = '还原任务'; diff --git a/module/action/model.php b/module/action/model.php index 304f139054..3eac992695 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -303,7 +303,7 @@ class actionModel extends model } $action->extra = trim(trim($action->extra), ','); } - elseif($actionName == 'totask' or $actionName == 'linkchildtask' or $actionName == 'unlinkchildrentask' or $actionName == 'linkparenttask' or $actionName == 'unlinkparenttask') + elseif($actionName == 'totask' or $actionName == 'linkchildtask' or $actionName == 'unlinkchildrentask' or $actionName == 'linkparenttask' or $actionName == 'unlinkparenttask' or $actionName == 'deletechildrentask') { $name = $this->dao->select('name')->from(TABLE_TASK)->where('id')->eq($action->extra)->fetch('name'); if($name) $action->extra = common::hasPriv('task', 'view') ? html::a(helper::createLink('task', 'view', "taskID=$action->extra"), "#$action->extra " . $name) : "#$action->extra " . $name; @@ -838,7 +838,7 @@ class actionModel extends model } $action->objectLink = helper::createLink($moduleName, $methodName, sprintf($vars, $action->objectID)); - if($action->objectType == 'user') $action->objectLink = helper::createLink($moduleName, $methodName, sprintf($vars, $action->actor)); + if($action->objectType == 'user') $action->objectLink = helper::createLink($moduleName, $methodName, sprintf($vars, $action->objectName)); $action->objectLabel = $objectLabel; } else diff --git a/module/api/control.php b/module/api/control.php index 31a5822d87..741d8f87b3 100644 --- a/module/api/control.php +++ b/module/api/control.php @@ -105,8 +105,9 @@ class api extends control */ public function sql($keyField = '') { - $sql = isset($_POST['sql']) ? $this->post->sql : ''; - $this->view->results = $this->api->sql($sql, $keyField); - die($this->display()); + die(); + //$sql = isset($_POST['sql']) ? $this->post->sql : ''; + //$this->view->results = $this->api->sql($sql, $keyField); + //die($this->display()); } } diff --git a/module/block/control.php b/module/block/control.php index e19c413f9b..a16ca26ac6 100644 --- a/module/block/control.php +++ b/module/block/control.php @@ -346,7 +346,7 @@ class block extends control } else { - $html = "
" . htmlspecialchars_decode($block->params->html) .'
'; + $html = "
" . $block->params->html . '
'; } } elseif($block->source != '') @@ -713,8 +713,20 @@ class block extends control $pager = pager::init(0, $num , 1); $productStats = $this->loadModel('product')->getStats('order_desc', $this->viewType != 'json' ? $pager : '', $type); + $projects = $this->loadModel('project')->getPairs(); $productIdList = array(); - foreach($productStats as $product) $productIdList[] = $product->id; + $this->loadModel('action'); + foreach($productStats as $product) + { + $currentProjectID = $this->dao->select('objectID')->from(TABLE_ACTION) + ->where('objectType')->eq('project') + ->andWhere('action')->eq('managed') + ->andWhere("CONCAT(extra, ',,')")->like("%$product->id,,") + ->orderBy('id_desc') + ->fetch('objectID'); + $product->currentProject = zget($projects, $currentProjectID, ''); + $productIdList[] = $product->id; + } $this->view->projects = $this->dao->select('t1.product,t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id') diff --git a/module/block/view/productblock.html.php b/module/block/view/productblock.html.php index 7f2b3cab12..d6d00ede50 100644 --- a/module/block/view/productblock.html.php +++ b/module/block/view/productblock.html.php @@ -39,8 +39,8 @@ > name?> - id, '');?> - id, '');?> + currentProject ? $product->currentProject : zget($projects, $product->id, '');?> + plans?> releases?> diff --git a/module/bug/control.php b/module/bug/control.php index 919d0495be..5e22a3f736 100644 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -258,7 +258,7 @@ class bug extends control if(!empty($_POST)) { $response['result'] = 'success'; - $response['message'] = ''; + $response['message'] = $this->lang->saveSuccess; /* Set from param if there is a object to transfer bug. */ $bugResult = $this->bug->create($from = isset($fromObjectIDKey) ? $fromObjectIDKey : ''); @@ -408,7 +408,7 @@ class bug extends control $this->view->position[] = html::a($this->createLink('bug', 'browse', "productID=$productID"), $this->products[$productID]); $this->view->position[] = $this->lang->bug->create; - $this->view->products = $products; + $this->view->products = $products; $this->view->productID = $productID; $this->view->productName = $this->products[$productID]; $this->view->moduleOptionMenu = $moduleOptionMenu; diff --git a/module/bug/css/create.css b/module/bug/css/create.css index f006a70179..2aa61ebd72 100644 --- a/module/bug/css/create.css +++ b/module/bug/css/create.css @@ -41,4 +41,6 @@ html[lang='en'] #deadlineTd .input-group-addon{padding: 5px 18px} #mainContent .center-block{padding-bottom:40px;} #typeBox {width:180px;} +#typeBox .required:after {right: 1px;} +#osBox .required:after {right: 1px;} #osBox {width:190px;} diff --git a/module/bug/model.php b/module/bug/model.php index 009e72fb0b..841acec8be 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -632,6 +632,7 @@ class bugModel extends model ->setIF($this->post->resolution == '' and $this->post->resolvedDate =='', 'status', 'active') ->setIF($this->post->resolution != '', 'confirmed', 1) ->setIF($this->post->story != false and $this->post->story != $oldBug->story, 'storyVersion', $this->loadModel('story')->getVersion($this->post->story)) + ->setIF(!$this->post->linkBug, 'linkBug', '') ->remove('comment,files,labels,uid,contactListMenu') ->get(); diff --git a/module/bug/view/create.html.php b/module/bug/view/create.html.php index f6bab1886b..d57f1ebdcf 100644 --- a/module/bug/view/create.html.php +++ b/module/bug/view/create.html.php @@ -38,7 +38,7 @@ js::set('flow', $config->global->flow);
- session->currentProductType != 'normal'):?> + session->currentProductType != 'normal' and isset($products[$productID])):?>
diff --git a/module/caselib/config.php b/module/caselib/config.php new file mode 100644 index 0000000000..994c2381b4 --- /dev/null +++ b/module/caselib/config.php @@ -0,0 +1,16 @@ +caselib = new stdclass(); +$config->caselib->create = new stdclass(); +$config->caselib->createcase = new stdclass(); +$config->caselib->create->requiredFields = 'name'; +$config->caselib->createcase->requiredFields = 'title,type'; + +$config->caselib->editor = new stdclass(); +$config->caselib->editor->create = array('id' => 'desc', 'tools' => 'simpleTools'); + +$config->caselib->datatable = new stdclass(); +$config->caselib->datatable->defaultField = array('id', 'pri', 'title', 'type', 'assignedTo', 'lastRunner', 'lastRunDate', 'lastRunResult', 'status', 'bugs', 'results', 'actions'); + +$config->caselib->custom = new stdclass(); +$config->caselib->custom->createFields = 'stage,pri,keywords'; +$config->caselib->customCreateFields = 'stage,pri,keywords'; diff --git a/module/caselib/control.php b/module/caselib/control.php new file mode 100644 index 0000000000..90a3b807dd --- /dev/null +++ b/module/caselib/control.php @@ -0,0 +1,737 @@ + + * @package caselib + * @version $Id: control.php 5114 2013-07-12 06:02:59Z chencongzhi520@gmail.com $ + * @link http://www.zentao.net + */ +class caselib extends control +{ + public $products = array(); + + /** + * Index page, header to browse. + * + * @access public + * @return void + */ + public function index() + { + $this->locate($this->createLink('caselib', 'browse')); + } + + /** + * Create lib + * + * @access public + * @return void + */ + public function create() + { + if(!empty($_POST)) + { + $response['result'] = 'success'; + $response['message'] = $this->lang->saveSuccess; + $libID = $this->caselib->create(); + if(dao::isError()) + { + $response['result'] = 'fail'; + $response['message'] = dao::getError(); + $this->send($response); + } + $this->loadModel('action')->create('caselib', $libID, 'opened'); + $response['locate'] = $this->createLink('caselib', 'browse', "libID=$libID"); + $this->send($response); + } + + /* Set menu. */ + $libraries = $this->caselib->getLibraries(); + $libID = $this->caselib->saveLibState(0, $libraries); + $this->caselib->setLibMenu($libraries, $libID); + + $this->view->title = $this->lang->caselib->common . $this->lang->colon . $this->lang->caselib->create; + $this->view->position[] = $this->lang->caselib->common; + $this->view->position[] = $this->lang->caselib->create; + $this->display(); + } + + /** + * Edit a case lib. + * + * @param int $lib + * @access public + * @return void + */ + public function edit($libID) + { + $lib = $this->caselib->getById($libID); + if(!empty($_POST)) + { + $response['result'] = 'success'; + $response['message'] = $this->lang->saveSuccess; + $changes = $this->caselib->update($libID); + if(dao::isError()) + { + $response['result'] = 'fail'; + $response['message'] = dao::getError(); + $this->send($response); + } + if($changes) + { + $actionID = $this->loadModel('action')->create('caselib', $libID, 'edited'); + $this->action->logHistory($actionID, $changes); + } + + $this->executeHooks($libID); + + $response['locate'] = inlink('view', "libID=$libID"); + $this->send($response); + } + + /* Set lib menu. */ + $libraries = $this->caselib->getLibraries(); + $libID = $this->caselib->saveLibState($libID, $libraries); + $this->caselib->setLibMenu($libraries, $libID); + + $this->view->title = $libraries[$libID] . $this->lang->colon . $this->lang->caselib->edit; + $this->view->position[] = html::a($this->createLink('caselib', 'browse', "libID=$libID"), $libraries[$libID]); + $this->view->position[] = $this->lang->caselib->common; + $this->view->position[] = $this->lang->caselib->edit; + + $this->view->lib = $lib; + $this->display(); + } + + /** + * Delete a case lib. + * + * @param int $libID + * @param string $confirm yes|no + * @access public + * @return void + */ + public function delete($libID, $confirm = 'no') + { + if($confirm == 'no') + { + die(js::confirm($this->lang->caselib->libraryDelete, inlink('delete', "libID=$libID&confirm=yes"))); + } + else + { + $this->caselib->delete($libID); + + $this->executeHooks($libID); + + /* if ajax request, send result. */ + if($this->server->ajax) + { + if(dao::isError()) + { + $response['result'] = 'fail'; + $response['message'] = dao::getError(); + } + else + { + $response['result'] = 'success'; + $response['message'] = ''; + } + $this->send($response); + } + die(js::reload('parent')); + } + } + + /** + * Show library case. + * + * @param int $libID + * @param string $browseType + * @param int $param + * @param string $orderBy + * @param int $recTotal + * @param int $recPerPage + * @param int $pageID + * @access public + * @return void + */ + public function browse($libID = 0, $browseType = 'all', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1) + { + /* Set browse type. */ + $browseType = strtolower($browseType); + + $libraries = $this->caselib->getLibraries(); + if(empty($libraries)) $this->locate(inlink('create')); + + /* Save session. */ + $this->session->set('caseList', $this->app->getURI(true)); + + /* Set menu. */ + $libID = $this->caselib->saveLibState($libID, $libraries); + setcookie('preCaseLibID', $libID, $this->config->cookieLife, $this->config->webRoot); + if($this->cookie->preCaseLibID != $libID) + { + $_COOKIE['libCaseModule'] = 0; + setcookie('libCaseModule', 0, 0, $this->config->webRoot); + } + + if($browseType == 'bymodule') setcookie('libCaseModule', (int)$param, 0, $this->config->webRoot); + if($browseType != 'bymodule') $this->session->set('libBrowseType', $browseType); + $moduleID = ($browseType == 'bymodule') ? (int)$param : ($browseType == 'bysearch' ? 0 : ($this->cookie->libCaseModule ? $this->cookie->libCaseModule : 0)); + $queryID = ($browseType == 'bysearch') ? (int)$param : 0; + + /* Set lib menu. */ + $this->caselib->setLibMenu($libraries, $libID, $moduleID); + + /* Load pager. */ + $this->app->loadClass('pager', $static = true); + $pager = pager::init($recTotal, $recPerPage, $pageID); + + /* Build the search form. */ + $this->loadModel('testcase'); + $actionURL = $this->createLink('caselib', 'browse', "libID=$libID&browseType=bySearch&queryID=myQueryID"); + $this->caselib->buildSearchForm($libID, $libraries, $queryID, $actionURL); + + /* Append id for secend sort. */ + $sort = $this->loadModel('common')->appendOrder($orderBy); + + /* save session .*/ + $cases = $this->caselib->getLibCases($libID, $browseType, $queryID, $moduleID, $sort, $pager); + $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', true); + + $this->loadModel('datatable'); + $this->loadModel('tree'); + $showModule = !empty($this->config->datatable->caselibLibrary->showModule) ? $this->config->datatable->caselibLibrary->showModule : ''; + $this->view->modulePairs = $showModule ? $this->tree->getModulePairs($libID, 'caselib', $showModule) : array(); + + $this->view->title = $this->lang->caselib->common . $this->lang->colon . $libraries[$libID]; + $this->view->position[] = html::a($this->createLink('caselib', 'browse', "libID=$libID"), $libraries[$libID]); + + $this->view->libID = $libID; + $this->view->libName = $libraries[$libID]; + $this->view->cases = $cases; + $this->view->orderBy = $orderBy; + $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter'); + $this->view->modules = $this->tree->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); + $this->view->moduleTree = $this->tree->getTreeMenu($libID, $viewType = 'caselib', $startModuleID = 0, array('treeModel', 'createCaseLibLink')); + $this->view->pager = $pager; + $this->view->browseType = $browseType; + $this->view->moduleID = $moduleID; + $this->view->moduleName = $moduleID ? $this->tree->getById($moduleID)->name : $this->lang->tree->all; + $this->view->param = $param; + $this->view->setShowModule = true; + + $this->display(); + } + + /** + * Create case for library. + * + * @param int $libID + * @param int $moduleID + * @access public + * @return void + */ + public function createCase($libID, $moduleID = 0, $param = 0) + { + if(!empty($_POST)) + { + $this->loadModel('testcase'); + $this->config->testcase->create->requiredFields = $this->config->caselib->createcase->requiredFields; + $caseResult = $this->testcase->create($bugID = 0); + if(!$caseResult or dao::isError()) die(js::error(dao::getError())); + + $caseID = $caseResult['id']; + if($caseResult['status'] == 'exists') + { + echo js::alert(sprintf($this->lang->duplicate, $this->lang->testcase->common)); + die(js::locate($this->createLink('testcase', 'view', "caseID=$caseID"), 'parent')); + } + + $this->loadModel('action')->create('case', $caseID, 'Opened'); + + /* If link from no head then reload. */ + if(isonlybody()) die(js::reload('parent')); + die(js::locate($this->createLink('caselib', 'browse', "libID={$libID}&browseType=byModule¶m={$_POST['module']}"), 'parent')); + } + /* Set lib menu. */ + $libraries = $this->caselib->getLibraries(); + $libID = $this->caselib->saveLibState($libID, $libraries); + $this->caselib->setLibMenu($libraries, $libID); + + $type = 'feature'; + $stage = ''; + $pri = 3; + $caseTitle = ''; + $precondition = ''; + $keywords = ''; + $steps = array(); + + $this->loadModel('testcase'); + if($param) + { + $testcase = $this->testcase->getById((int)$param); + $type = $testcase->type ? $testcase->type : 'feature'; + $stage = $testcase->stage; + $pri = $testcase->pri; + $storyID = $testcase->story; + $caseTitle = $testcase->title; + $precondition = $testcase->precondition; + $keywords = $testcase->keywords; + $steps = $testcase->steps; + } + + if(count($steps) < $this->config->testcase->defaultSteps) + { + $paddingCount = $this->config->testcase->defaultSteps - count($steps); + $step = new stdclass(); + $step->type = 'item'; + $step->desc = ''; + $step->expect = ''; + for($i = 1; $i <= $paddingCount; $i ++) $steps[] = $step; + } + + $this->view->title = $libraries[$libID] . $this->lang->colon . $this->lang->testcase->create; + $this->view->position[] = html::a($this->createLink('caselib', 'browse', "libID=$libID"), $libraries[$libID]); + $this->view->position[] = $this->lang->caselib->common; + $this->view->position[] = $this->lang->testcase->create; + + foreach(explode(',', $this->config->caselib->customCreateFields) as $field) $customFields[$field] = $this->lang->testcase->$field; + $this->view->showFields = $this->config->caselib->custom->createFields; + $this->view->customFields = $customFields; + $this->view->libraries = $libraries; + $this->view->libID = $libID; + $this->view->currentModuleID = (int)$moduleID; + $this->view->caseTitle = $caseTitle; + $this->view->type = $type; + $this->view->stage = $stage; + $this->view->pri = $pri; + $this->view->precondition = $precondition; + $this->view->keywords = $keywords; + $this->view->steps = $steps; + $this->view->moduleOptionMenu = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); + $this->display(); + } + + /** + * Batch create case. + * + * @param int $libID + * @param int $moduleID + * @access public + * @return void + */ + public function batchCreateCase($libID, $moduleID = 0) + { + $this->loadModel('testcase'); + if(!empty($_POST)) + { + $caseID = $this->caselib->batchCreateCase($libID); + if(dao::isError()) die(js::error(dao::getError())); + if(isonlybody()) die(js::closeModal('parent.parent', 'this')); + die(js::locate($this->createLink('caselib', 'browse', "libID=$libID&browseType=byModule¶m=$moduleID"), 'parent')); + } + + $libraries = $this->caselib->getLibraries(); + if(empty($libraries)) $this->locate(inlink('create')); + + /* Set lib menu. */ + $this->caselib->setLibMenu($libraries, $libID); + + $currentModuleID = (int)$moduleID; + + /* Set module option menu. */ + $moduleOptionMenu = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); + $moduleOptionMenu['ditto'] = $this->lang->testcase->ditto; + + $this->view->title = $libraries[$libID] . $this->lang->colon . $this->lang->testcase->batchCreate; + $this->view->position[] = html::a($this->createLink('caselib', 'browse', "libID=$libID"), $libraries[$libID]); + $this->view->position[] = $this->lang->testcase->batchCreate; + $this->view->libID = $libID; + $this->view->moduleOptionMenu = $moduleOptionMenu; + $this->view->currentModuleID = $currentModuleID; + + $this->display(); + } + + /** + * View library + * + * @param int $libID + * @access public + * @return void + */ + public function view($libID) + { + $lib = $this->caselib->getById($libID, true); + + /* Set lib menu. */ + $libraries = $this->caselib->getLibraries(); + $this->caselib->setLibMenu($libraries, $libID); + + $this->loadModel('testcase'); + $this->view->title = $lib->name . $this->lang->colon . $this->lang->caselib->view; + $this->view->position[] = html::a($this->createLink('caselib', 'browse', "libID=$libID"), $lib->name); + $this->view->position[] = $this->lang->caselib->common; + $this->view->position[] = $this->lang->caselib->view; + + $this->view->lib = $lib; + $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter'); + $this->view->actions = $this->loadModel('action')->getList('caselib', $libID); + $this->display(); + } + + /** + * Ajax get drop menu. + * + * @param int $libID + * @param string $module + * @param string $method + * @param string $extra + * @access public + * @return void + */ + public function ajaxGetDropMenu($libID, $module, $method, $extra = '') + { + $this->view->link = $this->caselib->getLibLink($module, $method, $extra); + $this->view->libID = $libID; + $this->view->module = $module; + $this->view->method = $method; + $this->view->extra = $extra; + + $libraries = $this->caselib->getLibraries(); + + $this->view->libraries = $libraries; + $this->view->librariesPinyin = common::convert2Pinyin($libraries); + $this->display(); + } + + /** + * The results page of search. + * + * @param string $keywords + * @param string $module + * @param string $method + * @param mix $extra + * @access public + * @return void + */ + public function ajaxGetMatchedItems($keywords, $module, $method, $extra) + { + $libraries = $this->dao->select('*')->from(TABLE_TESTSUITE)->where('deleted')->eq(0)->andWhere('name')->like("%$keywords%")->andWhere('type')->eq('library')->orderBy('`id` desc')->fetchAll(); + $this->view->link = $this->caselib->getLibLink($module, $method, $extra); + $this->view->libraries = $libraries; + $this->view->keywords = $keywords; + $this->display(); + } + + /** + * Export templet. + * + * @param int $libID + * @access public + * @return void + */ + public function exportTemplet($libID) + { + $this->loadModel('testcase'); + if($_POST) + { + $fields['module'] = $this->lang->testcase->module; + $fields['title'] = $this->lang->testcase->title; + $fields['precondition'] = $this->lang->testcase->precondition; + $fields['stepDesc'] = $this->lang->testcase->stepDesc; + $fields['stepExpect'] = $this->lang->testcase->stepExpect; + $fields['keywords'] = $this->lang->testcase->keywords; + $fields['pri'] = $this->lang->testcase->pri; + $fields['type'] = $this->lang->testcase->type; + $fields['stage'] = $this->lang->testcase->stage; + + $fields[''] = ''; + $fields['typeValue'] = $this->lang->testcase->lblTypeValue; + $fields['stageValue'] = $this->lang->testcase->lblStageValue; + + $modules = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); + $rows = array(); + $num = (int)$this->post->num; + for($i = 0; $i < $num; $i++) + { + foreach($modules as $moduleID => $module) + { + $row = new stdclass(); + $row->module = $module . "(#$moduleID)"; + $row->stepDesc = "1. \n2. \n3."; + $row->stepExpect = "1. \n2. \n3."; + + if(empty($rows)) + { + $row->typeValue = join("\n", $this->lang->testcase->typeList); + $row->stageValue = join("\n", $this->lang->testcase->stageList); + } + $rows[] = $row; + } + } + + $this->post->set('fields', $fields); + $this->post->set('kind', 'testcase'); + $this->post->set('rows', $rows); + $this->post->set('extraNum', $num); + $this->post->set('fileName', 'templet'); + $this->fetch('file', 'export2csv', $_POST); + } + + $this->display(); + } + + /** + * Import case csv file. + * + * @param int $libID + * @access public + * @return void + */ + public function import($libID) + { + $this->loadModel('testcase'); + if($_FILES) + { + $file = $this->loadModel('file')->getUpload('file'); + $file = $file[0]; + + $fileName = $this->file->savePath . $this->file->getSaveName($file['pathname']); + move_uploaded_file($file['tmpname'], $fileName); + + $rows = $this->file->parseCSV($fileName); + $fields = $this->testcase->getImportFields($productID); + $fields = array_flip($fields); + $header = array(); + foreach($rows[0] as $i => $rowValue) + { + if(empty($rowValue)) break; + $header[$i] = $rowValue; + } + unset($rows[0]); + + $columnKey = array(); + foreach($header as $title) + { + if(!isset($fields[$title])) continue; + $columnKey[] = $fields[$title]; + } + + if(count($columnKey) != count($header) or $this->post->encode != 'utf-8') + { + $fc = file_get_contents($fileName); + $encode = $this->post->encode != "utf-8" ? $this->post->encode : 'gbk'; + $fc = helper::convertEncoding($fc, $encode, 'utf-8'); + file_put_contents($fileName, $fc); + + $rows = $this->file->parseCSV($fileName); + $columnKey = array(); + $header = array(); + foreach($rows[0] as $i => $rowValue) + { + if(empty($rowValue)) break; + $header[$i] = $rowValue; + } + unset($rows[0]); + foreach($header as $title) + { + if(!isset($fields[$title])) continue; + $columnKey[] = $fields[$title]; + } + if(count($columnKey) != count($header)) die(js::alert($this->lang->testcase->errorEncode)); + } + + $this->session->set('importFile', $fileName); + + die(js::locate(inlink('showImport', "libID=$libID"), 'parent.parent')); + } + $this->display(); + } + + /** + * Show import case. + * + * @param int $libID + * @access public + * @return void + */ + public function showImport($libID) + { + $this->loadModel('testcase'); + if($_POST) + { + $this->caselib->createFromImport($libID); + die(js::locate(inlink('browse', "libID=$libID"), 'parent')); + } + + $libraries = $this->caselib->getLibraries(); + if(empty($libraries)) $this->locate(inlink('create')); + + $this->caselib->setLibMenu($libraries, $libID); + + $file = $this->session->importFile; + $caseLang = $this->lang->testcase; + $caseConfig = $this->config->testcase; + $modules = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); + + $fields = explode(',', $caseConfig->exportFields); + foreach($fields as $key => $fieldName) + { + $fieldName = trim($fieldName); + $fields[$fieldName] = isset($caseLang->$fieldName) ? $caseLang->$fieldName : $fieldName; + unset($fields[$key]); + } + + $fields = array_flip($fields); + $rows = $this->loadModel('file')->parseCSV($file); + $header = array(); + foreach($rows[0] as $i => $rowValue) + { + if(empty($rowValue)) break; + $header[$i] = $rowValue; + } + unset($rows[0]); + + foreach($header as $title) + { + if(!isset($fields[$title])) continue; + $columnKey[] = $fields[$title]; + } + + $endField = end($fields); + $caseData = array(); + $stepData = array(); + $stepVars = 0; + foreach($rows as $row => $data) + { + $case = new stdclass(); + foreach($columnKey as $key => $field) + { + if(!isset($data[$key])) continue; + $cellValue = $data[$key]; + if($field == 'module') + { + $case->$field = 0; + if(strrpos($cellValue, '(#') !== false) + { + $id = trim(substr($cellValue, strrpos($cellValue,'(#') + 2), ')'); + $case->$field = $id; + } + } + elseif(in_array($field, $caseConfig->export->listFields)) + { + if($field == 'stage') + { + $stages = explode("\n", $cellValue); + foreach($stages as $stage) $case->stage[] = array_search($stage, $caseLang->{$field . 'List'}); + $case->stage = join(',', $case->stage); + } + else + { + $case->$field = array_search($cellValue, $caseLang->{$field . 'List'}); + } + } + elseif($field != 'stepDesc' and $field != 'stepExpect') + { + $case->$field = $cellValue; + } + else + { + $steps = (array)$cellValue; + if(strpos($cellValue, "\n")) + { + $steps = explode("\n", $cellValue); + } + elseif(strpos($cellValue, "\r")) + { + $steps = explode("\r", $cellValue); + } + + $stepKey = str_replace('step', '', strtolower($field)); + $caseStep = array(); + + foreach($steps as $step) + { + $step = trim($step); + if(empty($step)) continue; + if(preg_match('/^(([0-9]+)\.[0-9]+)([.、]{1})/U', $step, $out)) + { + $num = $out[1]; + $parent = $out[2]; + $sign = $out[3]; + $signbit = $sign == '.' ? 1 : 3; + $step = trim(substr($step, strlen($num) + $signbit)); + if(!empty($step)) $caseStep[$num]['content'] = $step; + $caseStep[$num]['type'] = 'item'; + $caseStep[$parent]['type'] = 'group'; + } + elseif(preg_match('/^([0-9]+)([.、]{1})/U', $step, $out)) + { + $num = $out[1]; + $sign = $out[2]; + $signbit = $sign == '.' ? 1 : 3; + $step = trim(substr($step, strlen($num) + $signbit)); + if(!empty($step)) $caseStep[$num]['content'] = $step; + $caseStep[$num]['type'] = 'step'; + } + elseif(isset($num)) + { + if(!isset($caseStep[$num]['content'])) $caseStep[$num]['content'] = ''; + $caseStep[$num]['content'] .= "\n" . $step; + } + else + { + if($field == 'stepDesc') + { + $num = 1; + $caseStep[$num]['content'] = $step; + $caseStep[$num]['type'] = 'step'; + } + if($field == 'stepExpect' and isset($stepData[$row]['desc'])) + { + end($stepData[$row]['desc']); + $num = key($stepData[$row]['desc']); + $caseStep[$num]['content'] = $step; + } + } + } + unset($num); + unset($sign); + $stepVars += count($caseStep, COUNT_RECURSIVE) - count($caseStep); + $stepData[$row][$stepKey] = $caseStep; + } + } + + $caseData[$row] = $case; + unset($case); + } + + if(empty($caseData)) + { + unlink($this->session->importFile); + unset($_SESSION['importFile']); + echo js::alert($this->lang->error->noData); + die(js::locate($this->createLink('caselib', 'browse', "libID=$libID"))); + } + + /* Judge whether the editedTasks is too large and set session. */ + $countInputVars = count($caseData) * 9 + $stepVars; + $showSuhosinInfo = common::judgeSuhosinSetting($countInputVars); + if($showSuhosinInfo) $this->view->suhosinInfo = extension_loaded('suhosin') ? sprintf($this->lang->suhosinInfo, $countInputVars) : sprintf($this->lang->maxVarsInfo, $countInputVars); + + $this->view->title = $this->lang->caselib->common . $this->lang->colon . $this->lang->testcase->showImport; + $this->view->position[] = $this->lang->testcase->showImport; + + $this->view->modules = $modules; + $this->view->cases = $this->dao->select('id,module,stage,status,pri,type')->from(TABLE_CASE)->where('lib')->eq($libID)->andWhere('deleted')->eq(0)->andWhere('product')->eq(0)->fetchAll('id'); + $this->view->caseData = $caseData; + $this->view->stepData = $stepData; + $this->view->libID = $libID; + $this->display(); + } +} diff --git a/module/testsuite/css/batchcreatecase.css b/module/caselib/css/batchcreatecase.css similarity index 100% rename from module/testsuite/css/batchcreatecase.css rename to module/caselib/css/batchcreatecase.css diff --git a/module/testsuite/css/library.css b/module/caselib/css/browse.css similarity index 100% rename from module/testsuite/css/library.css rename to module/caselib/css/browse.css diff --git a/module/testsuite/css/createcase.css b/module/caselib/css/createcase.css similarity index 100% rename from module/testsuite/css/createcase.css rename to module/caselib/css/createcase.css diff --git a/module/testsuite/css/showimport.css b/module/caselib/css/showimport.css similarity index 100% rename from module/testsuite/css/showimport.css rename to module/caselib/css/showimport.css diff --git a/module/testsuite/js/library.js b/module/caselib/js/browse.js similarity index 100% rename from module/testsuite/js/library.js rename to module/caselib/js/browse.js diff --git a/module/testsuite/js/createcase.js b/module/caselib/js/createcase.js similarity index 100% rename from module/testsuite/js/createcase.js rename to module/caselib/js/createcase.js diff --git a/module/caselib/lang/en.php b/module/caselib/lang/en.php new file mode 100644 index 0000000000..429aa86b8b --- /dev/null +++ b/module/caselib/lang/en.php @@ -0,0 +1,37 @@ + + * @package caselib + * @version $Id: zh-cn.php 4490 2013-02-27 03:27:05Z wyd621@gmail.com $ + * @link http://www.zentao.net + */ +$lang->caselib->common = 'Case Library'; +$lang->caselib->all = 'All Case Libraries'; + +$lang->caselib->index = "Library Home"; +$lang->caselib->create = "Create Case Library"; +$lang->caselib->edit = 'Edit Case Library'; +$lang->caselib->browse = 'View Cases in Library'; +$lang->caselib->view = 'Library Detail'; +$lang->caselib->createCase = 'Create Case'; +$lang->caselib->delete = "Delete Suite"; +$lang->caselib->linkVersion = "Version"; +$lang->caselib->deleted = 'Deleted'; +$lang->caselib->exportTemplet = 'Export Template'; +$lang->caselib->batchCreateCase = 'Batch Create'; +$lang->caselib->import = 'Import'; +$lang->caselib->importAction = 'Import Case'; +$lang->caselib->showImport = 'Imported Data'; + +$lang->caselib->id = 'ID'; +$lang->caselib->name = 'Name'; +$lang->caselib->desc = 'Description'; + +$lang->caselib->legendDesc = 'Description'; + +$lang->caselib->libraryDelete = 'Do you want to delete this library?'; +$lang->caselib->noModule = '
You have no modules.
Manage it now.
'; diff --git a/module/caselib/lang/zh-cn.php b/module/caselib/lang/zh-cn.php new file mode 100644 index 0000000000..e22369385b --- /dev/null +++ b/module/caselib/lang/zh-cn.php @@ -0,0 +1,37 @@ + + * @package caselib + * @version $Id: zh-cn.php 4490 2013-02-27 03:27:05Z wyd621@gmail.com $ + * @link http://www.zentao.net + */ +$lang->caselib->common = '用例库'; +$lang->caselib->all = '所有用例库'; + +$lang->caselib->index = "用例库首页"; +$lang->caselib->create = "创建用例库"; +$lang->caselib->edit = '编辑用例库'; +$lang->caselib->browse = '浏览库用例'; +$lang->caselib->view = '查看库概况'; +$lang->caselib->createCase = '创建用例'; +$lang->caselib->delete = "删除"; +$lang->caselib->linkVersion = "版本"; +$lang->caselib->deleted = '已删除'; +$lang->caselib->exportTemplet = '导出模板'; +$lang->caselib->batchCreateCase = '批量创建用例'; +$lang->caselib->import = '导入'; +$lang->caselib->importAction = '导入用例'; +$lang->caselib->showImport = '显示导入数据'; + +$lang->caselib->id = '编号'; +$lang->caselib->name = '名称'; +$lang->caselib->desc = '描述'; + +$lang->caselib->legendDesc = '描述'; + +$lang->caselib->libraryDelete = '您确认要删除该用例库吗?'; +$lang->caselib->noModule = '
您现在还没有模块信息
请维护用例库模块
'; diff --git a/module/caselib/model.php b/module/caselib/model.php new file mode 100644 index 0000000000..ce16fc972c --- /dev/null +++ b/module/caselib/model.php @@ -0,0 +1,640 @@ + + * @package caselib + * @version $Id: model.php 5114 2013-07-12 06:02:59Z chencongzhi520@gmail.com $ + * @link http://www.zentao.net + */ +?> +app->viewType == 'mhtml'; + $selectHtml = ''; + if(!empty($libraries)) + { + if($isMobile) + { + $selectHtml = "{$currentLibName} "; + } + else + { + $dropMenuLink = helper::createLink('caselib', 'ajaxGetDropMenu', "objectID=$libID&module=caselib&method=browse"); + $selectHtml = "
"; + } + } + + if($this->config->global->flow == 'onlyTest') + { + $modules = $this->loadModel('tree')->getModulePairs($libID, 'caselib'); + $moduleName = ($moduleID && isset($modules[$moduleID])) ? $modules[$moduleID] : $this->lang->tree->all; + + if(!$isMobile) + { + $dropMenuLink = helper::createLink('tree', 'ajaxGetDropMenu', "objectID=$libID&module=caselib&method=browse"); + $selectHtml .= "
"; + } + else + { + $selectHtml .= "{$moduleName} "; + } + } + + setCookie("lastCaseLib", $libID, $this->config->cookieLife, $this->config->webRoot); + + $pageNav = ''; + $pageActions = ''; + $isMobile = $this->app->viewType == 'mhtml'; + if($isMobile) + { + $this->app->loadLang('qa'); + $pageNav = html::a(helper::createLink('qa', 'index'), $this->lang->qa->index) . $this->lang->colon; + } + else + { + if($this->config->global->flow == 'full') + { + $this->app->loadLang('qa'); + $pageNav .= '
'; + $pageNav .= '
'; + } + else + { + $this->app->loadLang('testcase'); + $pageActions .= "
"; + if(common::hasPriv('caselib', 'exportTemplet')) + { + $link = helper::createLink('caselib', 'exportTemplet', "libID=$libID"); + $pageActions .= html::a($link, " " . $this->lang->caselib->exportTemplet, '', "class='btn btn-link export'"); + } + if(common::hasPriv('caselib', 'import')) + { + $link = helper::createLink('caselib', 'import', "libID=$libID"); + $pageActions .= html::a($link, " " . $this->lang->testcase->importFile, '', "class='btn btn-link export'"); + } + $pageActions .= '
'; + $params = "libID=$libID&moduleID=" . (isset($moduleID) ? $moduleID : 0); + if(common::hasPriv('caselib', 'batchCreateCase')) + { + $link = helper::createLink('caselib', 'batchCreateCase', $params); + $pageActions .= html::a($link, "" . $this->lang->testcase->batchCreate, '', "class='btn btn-secondary'"); + } + if(common::hasPriv('caselib', 'createCase')) + { + $link = helper::createLink('caselib', 'createCase', $params); + $pageActions .= html::a($link, "" . $this->lang->testcase->create, '', "class='btn btn-primary'"); + } + if(common::hasPriv('caselib', 'create')) + { + $link = helper::createLink('caselib', 'create'); + $pageActions .= html::a($link, "" . $this->lang->caselib->create, '', "class='btn btn-primary'"); + } + } + } + $pageNav .= $selectHtml; + + $this->lang->modulePageNav = $pageNav; + $this->lang->modulePageActions = $pageActions; + foreach($this->lang->caselib->menu as $key => $value) + { + $replace = ''; + if($this->config->global->flow == 'onlyTest') $replace = $libID; + common::setMenuVars($this->lang->caselib->menu, $key, $replace); + } + if($this->config->global->flow != 'full' && $this->app->getMethodName() != 'view') $this->lang->caselib->menu->bysearch = " {$this->lang->testcase->bySearch}"; + } + + /** + * Save lib state. + * + * @param int $libID + * @param array $libraries + * @access public + * @return int + */ + public function saveLibState($libID = 0, $libraries = array()) + { + if($libID > 0) $this->session->set('caseLib', (int)$libID); + if($libID == 0 and $this->cookie->lastCaseLib) $this->session->set('caseLib', $this->cookie->lastCaseLib); + if($libID == 0 and $this->session->caseLib == '') $this->session->set('caseLib', key($libraries)); + if(!isset($libraries[$this->session->caseLib])) + { + $this->session->set('caseLib', key($libraries)); + $libID = $this->session->caseLib; + } + return $this->session->caseLib; + } + + /** + * Get caselib info by id. + * + * @param int $libID + * @param bool $setImgSize + * @access public + * @return object + */ + public function getById($libID, $setImgSize = false) + { + $lib = $this->dao->select('*')->from(TABLE_TESTSUITE)->where('id')->eq((int)$libID)->fetch(); + $lib = $this->loadModel('file')->replaceImgURL($lib, 'desc'); + if($setImgSize) $lib->desc = $this->file->setImgSize($lib->desc); + return $lib; + } + + /** + * Update a caselib. + * + * @param int $libID + * @access public + * @return bool|array + */ + public function update($libID) + { + $oldLib = $this->dao->select("*")->from(TABLE_TESTSUITE)->where('id')->eq((int)$libID)->fetch(); + $lib = fixer::input('post') + ->stripTags($this->config->caselib->editor->edit['id'], $this->config->allowedTags) + ->add('lastEditedBy', $this->app->user->account) + ->add('lastEditedDate', helper::now()) + ->remove('uid') + ->get(); + $lib = $this->loadModel('file')->processImgURL($lib, $this->config->caselib->editor->edit['id'], $this->post->uid); + $this->dao->update(TABLE_TESTSUITE)->data($lib) + ->autoCheck() + ->batchcheck($this->config->caselib->edit->requiredFields, 'notempty') + ->where('id')->eq($libID) + ->exec(); + if(!dao::isError()) + { + $this->file->updateObjectID($this->post->uid, $libID, 'caselib'); + return common::createChanges($oldLib, $lib); + } + return false; + } + + /** + * Delete library. + * + * @param int $libID + * @param string $table + * @access public + * @return bool + */ + public function delete($libID, $table = '') + { + $this->dao->update(TABLE_TESTSUITE)->set('deleted')->eq(1)->where('id')->eq($libID)->exec(); + + $this->loadModel('action'); + $this->action->create('caselib', $libID, 'deleted', '', ACTIONMODEL::CAN_UNDELETED); + } + + /** + * Get libraries. + * + * @access public + * @return array + */ + public function getLibraries() + { + return $this->dao->select("id,name")->from(TABLE_TESTSUITE) + ->where('product')->eq(0) + ->andWhere('deleted')->eq(0) + ->andWhere('type')->eq('library') + ->orderBy('id_desc') + ->fetchPairs('id', 'name'); + } + + /** + * Create lib. + * + * @access public + * @return int + */ + public function create() + { + $lib = fixer::input('post') + ->stripTags($this->config->caselib->editor->create['id'], $this->config->allowedTags) + ->setForce('type', 'library') + ->add('addedBy', $this->app->user->account) + ->add('addedDate', helper::now()) + ->remove('uid') + ->get(); + $lib = $this->loadModel('file')->processImgURL($lib, $this->config->caselib->editor->create['id'], $this->post->uid); + $this->dao->insert(TABLE_TESTSUITE)->data($lib) + ->batchcheck($this->config->caselib->create->requiredFields, 'notempty') + ->check('name', 'unique', "deleted = '0'") + ->exec(); + if(!dao::isError()) + { + $libID = $this->dao->lastInsertID(); + $this->file->updateObjectID($this->post->uid, $libID, 'caselib'); + return $libID; + } + return false; + } + + /** + * Get lib cases. + * + * @param int $libID + * @param string $browseType + * @param int $queryID + * @param int $moduleID + * @param string $sort + * @param object $pager + * @access public + * @return array + */ + public function getLibCases($libID, $browseType, $queryID = 0, $moduleID = 0, $sort = 'id_desc', $pager = null) + { + $moduleIdList = $moduleID ? $this->loadModel('tree')->getAllChildId($moduleID) : '0'; + $browseType = ($browseType == 'bymodule' and $this->session->libBrowseType and $this->session->libBrowseType != 'bysearch') ? $this->session->libBrowseType : $browseType; + + $cases = array(); + if($browseType == 'bymodule' or $browseType == 'all' or $browseType == 'wait') + { + $cases = $this->dao->select('*')->from(TABLE_CASE) + ->where('lib')->eq((int)$libID) + ->andWhere('product')->eq(0) + ->beginIF($moduleIdList)->andWhere('module')->in($moduleIdList)->fi() + ->beginIF($browseType == 'wait')->andWhere('status')->eq($browseType)->fi() + ->andWhere('deleted')->eq('0') + ->orderBy($sort)->page($pager)->fetchAll('id'); + } + /* By search. */ + elseif($browseType == 'bysearch') + { + if($queryID) + { + $query = $this->loadModel('search')->getQuery($queryID); + $this->session->set('caselibQuery', ' 1 = 1'); + if($query) + { + $this->session->set('caselibQuery', $query->sql); + $this->session->set('caselibForm', $query->form); + } + } + else + { + if($this->session->caselibQuery == false) $this->session->set('caselibQuery', ' 1 = 1'); + } + + $queryLibID = $libID; + $allLib = "`lib` = 'all'"; + $caseQuery = '(' . $this->session->caselibQuery; + if(strpos($this->session->caselibQuery, $allLib) !== false) + { + $caseQuery = str_replace($allLib, '1', $caseQuery); + $queryLibID = 'all'; + } + $caseQuery .= ')'; + + $cases = $this->dao->select('*')->from(TABLE_CASE)->where($caseQuery) + ->beginIF($queryLibID != 'all')->andWhere('lib')->eq((int)$libID)->fi() + ->andWhere('product')->eq(0) + ->andWhere('deleted')->eq(0) + ->orderBy($sort)->page($pager)->fetchAll(); + + } + return $cases; + } + + /** + * Build search form. + * + * @param int $libID + * @param array $libraries + * @param int $queryID + * @param string $actionURL + * @access public + * @return void + */ + public function buildSearchForm($libID, $libraries, $queryID, $actionURL) + { + $this->config->testcase->search['fields']['lib'] = $this->lang->testcase->lib; + $this->config->testcase->search['params']['lib']['values'] = array('' => '', $libID => $libraries[$libID], 'all' => $this->lang->caselib->all); + $this->config->testcase->search['params']['lib']['operator'] = '='; + $this->config->testcase->search['params']['lib']['control'] = 'select'; + $this->config->testcase->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib'); + if(!$this->config->testcase->needReview) unset($this->config->testcase->search['params']['status']['values']['wait']); + unset($this->config->testcase->search['fields']['product']); + unset($this->config->testcase->search['params']['product']); + unset($this->config->testcase->search['fields']['branch']); + unset($this->config->testcase->search['params']['branch']); + unset($this->config->testcase->search['fields']['lastRunner']); + unset($this->config->testcase->search['params']['lastRunner']); + unset($this->config->testcase->search['fields']['lastRunResult']); + unset($this->config->testcase->search['params']['lastRunResult']); + unset($this->config->testcase->search['fields']['lastRunDate']); + unset($this->config->testcase->search['params']['lastRunDate']); + + $this->config->testcase->search['module'] = 'caselib'; + $this->config->testcase->search['actionURL'] = $actionURL; + $this->config->testcase->search['queryID'] = $queryID; + + $this->loadModel('search')->setSearchParams($this->config->testcase->search); + } + + /** + * Get lib link. + * + * @param string $module + * @param string $method + * @param string $extra + * @access public + * @return string + */ + public function getLibLink($module, $method, $extra) + { + $link = ''; + if($module == 'caselib') + { + if($module == 'caselib' && ($method == 'create')) + { + $link = helper::createLink($module, 'browse', "libID=%s"); + } + else + { + $link = helper::createLink($module, $method, "libID=%s"); + } + } + else if($module == 'tree') + { + $link = helper::createLink($module, $method, "libID=%s&type=caselib¤tModuleID=0"); + } + else + { + $link = helper::createLink('caselib', 'browse', "libID=%s"); + } + return $link; + } + + /** + * Create from import. + * + * @param int $libID + * @access public + * @return void + */ + public function createFromImport($libID) + { + $this->loadModel('action'); + $this->loadModel('testcase'); + $this->loadModel('file'); + $now = helper::now(); + $data = fixer::input('post')->get(); + + if(!empty($_POST['id'])) + { + $oldSteps = $this->dao->select('t2.*')->from(TABLE_CASE)->alias('t1') + ->leftJoin(TABLE_CASESTEP)->alias('t2')->on('t1.id = t2.case') + ->where('t1.id')->in(($_POST['id'])) + ->andWhere('t1.version=t2.version') + ->orderBy('t2.id') + ->fetchGroup('case'); + $oldCases = $this->dao->select('*')->from(TABLE_CASE)->where('id')->in($_POST['id'])->fetchAll('id'); + } + + $cases = array(); + foreach($data->lib as $key => $lib) + { + $caseData = new stdclass(); + + $caseData->lib = $lib; + $caseData->module = $data->module[$key]; + $caseData->title = $data->title[$key]; + $caseData->pri = (int)$data->pri[$key]; + $caseData->type = $data->type[$key]; + $caseData->status = $data->status[$key]; + $caseData->stage = join(',', $data->stage[$key]); + $caseData->keywords = $data->keywords[$key]; + $caseData->frequency = 1; + $caseData->precondition = $data->precondition[$key]; + + if(isset($this->config->testcase->create->requiredFields)) + { + $requiredFields = explode(',', $this->config->testcase->create->requiredFields); + foreach($requiredFields as $requiredField) + { + $requiredField = trim($requiredField); + if(empty($caseData->$requiredField)) dao::$errors[] = sprintf($this->lang->testcase->noRequire, $key, $this->lang->testcase->$requiredField); + } + } + + $cases[$key] = $caseData; + } + if(dao::isError()) die(js::error(dao::getError())); + + $forceNotReview = $this->testcase->forceNotReview(); + foreach($cases as $key => $caseData) + { + if(!empty($_POST['id'][$key]) and empty($_POST['insert'])) + { + $caseID = $data->id[$key]; + $stepChanged = false; + $steps = array(); + $oldStep = isset($oldSteps[$caseID]) ? $oldSteps[$caseID] : array(); + $oldCase = $oldCases[$caseID]; + + /* Remove the empty setps in post. */ + $steps = array(); + if(isset($_POST['desc'][$key])) + { + foreach($data->desc[$key] as $id => $desc) + { + $desc = trim($desc); + if(empty($desc)) continue; + $step = new stdclass(); + $step->type = $data->stepType[$key][$id]; + $step->desc = $desc; + $step->expect = trim($data->expect[$key][$id]); + + $steps[] = $step; + } + } + + /* If step count changed, case changed. */ + if((!$oldStep != !$steps) or (count($oldStep) != count($steps))) + { + $stepChanged = true; + } + else + { + /* Compare every step. */ + foreach($oldStep as $id => $oldStep) + { + if(trim($oldStep->desc) != trim($steps[$id]->desc) or trim($oldStep->expect) != $steps[$id]->expect) + { + $stepChanged = true; + break; + } + } + } + + $version = $stepChanged ? $oldCase->version + 1 : $oldCase->version; + $caseData->version = $version; + $changes = common::createChanges($oldCase, $caseData); + if(!$changes and !$stepChanged) continue; + + if($changes or $stepChanged) + { + $caseData->lastEditedBy = $this->app->user->account; + $caseData->lastEditedDate = $now; + if($stepChanged and !$forceNotReview) $caseData->status = 'wait'; + $this->dao->update(TABLE_CASE)->data($caseData)->where('id')->eq($caseID)->autoCheck()->exec(); + if($stepChanged) + { + $parentStepID = 0; + foreach($steps as $id => $step) + { + $step = (array)$step; + if(empty($step['desc'])) continue; + $stepData = new stdclass(); + $stepData->type = ($step['type'] == 'item' and $parentStepID == 0) ? 'step' : $step['type']; + $stepData->parent = ($stepData->type == 'item') ? $parentStepID : 0; + $stepData->case = $caseID; + $stepData->version = $version; + $stepData->desc = $step['desc']; + $stepData->expect = $step['expect']; + $this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec(); + if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID(); + if($stepData->type == 'step') $parentStepID = 0; + } + } + $oldCase->steps = $this->testcase->joinStep($oldStep); + $caseData->steps = $this->testcase->joinStep($steps); + $changes = common::createChanges($oldCase, $caseData); + $actionID = $this->action->create('case', $caseID, 'Edited'); + $this->action->logHistory($actionID, $changes); + } + } + else + { + $caseData->version = 1; + $caseData->openedBy = $this->app->user->account; + $caseData->openedDate = $now; + $caseData->status = $forceNotReview ? 'normal' : 'wait'; + $this->dao->insert(TABLE_CASE)->data($caseData)->autoCheck()->exec(); + + if(!dao::isError()) + { + $caseID = $this->dao->lastInsertID(); + $parentStepID = 0; + foreach($data->desc[$key] as $id => $desc) + { + $desc = trim($desc); + if(empty($desc)) continue; + $stepData = new stdclass(); + $stepData->type = ($data->stepType[$key][$id] == 'item' and $parentStepID == 0) ? 'step' : $data->stepType[$key][$id]; + $stepData->parent = ($stepData->type == 'item') ? $parentStepID : 0; + $stepData->case = $caseID; + $stepData->version = 1; + $stepData->desc = $desc; + $stepData->expect = $data->expect[$key][$id]; + $this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec(); + if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID(); + if($stepData->type == 'step') $parentStepID = 0; + } + $this->action->create('case', $caseID, 'Opened'); + } + } + } + + unlink($this->session->importFile); + unset($_SESSION['importFile']); + } + + /** + * Batch create case for lib. + * + * @param int $libID + * @access public + * @return void + */ + public function batchCreateCase($libID) + { + $this->loadModel('testcase'); + $this->loadModel('action'); + + $now = helper::now(); + $cases = fixer::input('post')->get(); + $batchNum = count(reset($cases)); + + $result = $this->loadModel('common')->removeDuplicate('case', $cases, "lib={$libID}"); + $cases = $result['data']; + + for($i = 0; $i < $batchNum; $i++) + { + if(!empty($cases->title[$i]) and empty($cases->type[$i])) die(js::alert(sprintf($this->lang->error->notempty, $this->lang->testcase->type))); + } + + $module = 0; + $type = ''; + $pri = 3; + for($i = 0; $i < $batchNum; $i++) + { + $module = $cases->module[$i] == 'ditto' ? $module : $cases->module[$i]; + $type = $cases->type[$i] == 'ditto' ? $type : $cases->type[$i]; + $pri = $cases->pri[$i] == 'ditto' ? $pri : $cases->pri[$i]; + $cases->module[$i] = (int)$module; + $cases->type[$i] = $type; + $cases->pri[$i] = $pri; + } + + $forceNotReview = $this->testcase->forceNotReview(); + for($i = 0; $i < $batchNum; $i++) + { + if($cases->type[$i] != '' and $cases->title[$i] != '') + { + $data[$i] = new stdclass(); + $data[$i]->lib = $libID; + $data[$i]->module = $cases->module[$i]; + $data[$i]->type = $cases->type[$i]; + $data[$i]->pri = $cases->pri[$i]; + $data[$i]->stage = empty($cases->stage[$i]) ? '' : implode(',', $cases->stage[$i]); + $data[$i]->color = $cases->color[$i]; + $data[$i]->title = $cases->title[$i]; + $data[$i]->precondition = $cases->precondition[$i]; + $data[$i]->keywords = $cases->keywords[$i]; + $data[$i]->openedBy = $this->app->user->account; + $data[$i]->openedDate = $now; + $data[$i]->status = $forceNotReview ? 'normal' : 'wait'; + $data[$i]->version = 1; + + $this->dao->insert(TABLE_CASE)->data($data[$i]) + ->autoCheck() + ->batchCheck($this->config->testcase->create->requiredFields, 'notempty') + ->exec(); + + if(dao::isError()) + { + echo js::error(dao::getError()); + die(js::reload('parent')); + } + + $caseID = $this->dao->lastInsertID(); + $actionID = $this->action->create('case', $caseID, 'Opened'); + } + } + } +} diff --git a/module/testsuite/view/ajaxgetdropmenu.html.php b/module/caselib/view/ajaxgetdropmenu.html.php similarity index 100% rename from module/testsuite/view/ajaxgetdropmenu.html.php rename to module/caselib/view/ajaxgetdropmenu.html.php diff --git a/module/testsuite/view/batchcreatecase.html.php b/module/caselib/view/batchcreatecase.html.php similarity index 98% rename from module/testsuite/view/batchcreatecase.html.php rename to module/caselib/view/batchcreatecase.html.php index 1c211b319d..86c08b124f 100644 --- a/module/testsuite/view/batchcreatecase.html.php +++ b/module/caselib/view/batchcreatecase.html.php @@ -1,11 +1,11 @@ - * @package testsuite + * @package caselib * @version $Id$ * @link http://www.zentao.net */ diff --git a/module/testsuite/view/library.html.php b/module/caselib/view/browse.html.php similarity index 87% rename from module/testsuite/view/library.html.php rename to module/caselib/view/browse.html.php index 1f1cee3ef4..c989800fac 100644 --- a/module/testsuite/view/library.html.php +++ b/module/caselib/view/browse.html.php @@ -1,11 +1,11 @@ - * @package testsuite + * @package caselib * @version $Id: library.html.php 5108 2013-07-12 01:59:04Z chencongzhi520@gmail.com $ * @link http://www.zentao.net */ @@ -15,7 +15,7 @@ include '../../common/view/header.html.php'; include '../../common/view/datatable.fix.html.php'; js::set('browseType', $browseType); js::set('moduleID', $moduleID); -js::set('confirmDelete', $lang->testsuite->confirmDelete); +js::set('confirmDelete', $lang->testcase->confirmDelete); js::set('batchDelete', $lang->testcase->confirmBatchDelete); js::set('flow', $config->global->flow); ?> @@ -36,29 +36,29 @@ js::set('flow', $config->global->flow);
inlink('library', "libID=$libID&browseType=all"), "{$lang->testcase->allCases}", '', "id='allTab' class='btn btn-link'"); + echo html::a($this->inlink('browse', "libID=$libID&browseType=all"), "{$lang->testcase->allCases}", '', "id='allTab' class='btn btn-link'"); if($config->testcase->needReview or !empty($config->testcase->forceReview)) echo html::a($this->inlink('library', "libID=$libID&browseType=wait"), "" . $lang->testcase->statusList['wait'] . "", '', "id='waitTab' class='btn btn-link'"); } ?> testcase->bySearch;?> " . $this->lang->testsuite->libView, '', "class='btn btn-link'"); + $link = helper::createLink('caselib', 'view', "libID=$libID"); + echo html::a($link, " " . $this->lang->caselib->view, '', "class='btn btn-link'"); } ?>
- " . $lang->testsuite->exportTemplet, '', "class='btn btn-link export' data-width='35%'");?> - " . $lang->testcase->importFile, '', "class='btn btn-link export'");?> + " . $lang->caselib->exportTemplet, '', "class='btn btn-link export' data-width='35%'");?> + " . $lang->testcase->importFile, '', "class='btn btn-link export'");?>
- " . $lang->testcase->batchCreate, '', "class='btn btn-secondary'");?> - " . $lang->testcase->create, '', "class='btn btn-primary'");?> + " . $lang->testcase->batchCreate, '', "class='btn btn-secondary'");?> + " . $lang->testcase->create, '', "class='btn btn-primary'");?>
@@ -69,7 +69,7 @@ js::set('flow', $config->global->flow);
- testsuite->noModule;?> + caselib->noModule;?>

@@ -86,8 +86,8 @@ js::set('flow', $config->global->flow);

testcase->noCase;?> - - createLink('testsuite', 'createCase', "libID=$libID&moduleID=" . (isset($moduleID) ? $moduleID : 0)), " " . $lang->testcase->create, '', "class='btn btn-info'");?> + + createLink('caselib', 'createCase', "libID=$libID&moduleID=" . (isset($moduleID) ? $moduleID : 0)), " " . $lang->testcase->create, '', "class='btn btn-info'");?>

diff --git a/module/testsuite/view/createlib.html.php b/module/caselib/view/create.html.php similarity index 83% rename from module/testsuite/view/createlib.html.php rename to module/caselib/view/create.html.php index f1b96e6ffe..21d55a3eac 100644 --- a/module/testsuite/view/createlib.html.php +++ b/module/caselib/view/create.html.php @@ -1,11 +1,11 @@ - * @package testsuite + * @package caselib * @version $Id: create.html.php 4728 2013-05-03 06:14:34Z chencongzhi520@gmail.com $ * @link http://www.zentao.net */ @@ -15,16 +15,16 @@
-

testsuite->createLib;?>

+

caselib->create;?>

- + - + diff --git a/module/testsuite/view/createcase.html.php b/module/caselib/view/createcase.html.php similarity index 98% rename from module/testsuite/view/createcase.html.php rename to module/caselib/view/createcase.html.php index 2ab7777d28..396f973840 100644 --- a/module/testsuite/view/createcase.html.php +++ b/module/caselib/view/createcase.html.php @@ -1,11 +1,11 @@ - * @package testsuite + * @package caselib * @version $Id: createcase.html.php 4904 2013-06-26 05:37:45Z wyd621@gmail.com $ * @link http://www.zentao.net */ @@ -19,7 +19,7 @@

testcase->create;?>

- createLink('custom', 'ajaxSaveCustomFields', 'module=testsuite§ion=custom&key=createFields');?> + createLink('custom', 'ajaxSaveCustomFields', 'module=caselib§ion=custom&key=createFields');?>
diff --git a/module/caselib/view/edit.html.php b/module/caselib/view/edit.html.php new file mode 100644 index 0000000000..2b21f4aad9 --- /dev/null +++ b/module/caselib/view/edit.html.php @@ -0,0 +1,41 @@ + + * @package caselib + * @version $Id: edit.html.php 4728 2013-05-03 06:14:34Z chencongzhi520@gmail.com $ + * @link http://www.zentao.net + */ +?> + + +
+
+
+

caselib->edit;?>

+
+ +
testsuite->name;?>caselib->name;?>
testsuite->desc;?>caselib->desc;?>
+ + + + + + + + + printExtendFields($lib, 'table');?> + + + +
caselib->name;?>name, "class='form-control'");?>
caselib->desc;?>desc), "rows=10 class='form-control'");?>
+ + +
+
+
+
+ diff --git a/module/testsuite/view/exporttemplet.html.php b/module/caselib/view/exporttemplet.html.php similarity index 100% rename from module/testsuite/view/exporttemplet.html.php rename to module/caselib/view/exporttemplet.html.php diff --git a/module/testsuite/view/import.html.php b/module/caselib/view/import.html.php similarity index 100% rename from module/testsuite/view/import.html.php rename to module/caselib/view/import.html.php diff --git a/module/testsuite/view/showimport.html.php b/module/caselib/view/showimport.html.php similarity index 99% rename from module/testsuite/view/showimport.html.php rename to module/caselib/view/showimport.html.php index 2e34529c00..202e99a182 100644 --- a/module/testsuite/view/showimport.html.php +++ b/module/caselib/view/showimport.html.php @@ -7,7 +7,7 @@
-

testsuite->import;?>

+

caselib->import;?>

diff --git a/module/testsuite/view/libview.html.php b/module/caselib/view/view.html.php similarity index 75% rename from module/testsuite/view/libview.html.php rename to module/caselib/view/view.html.php index 9cf3b019be..f9eb466230 100644 --- a/module/testsuite/view/libview.html.php +++ b/module/caselib/view/view.html.php @@ -1,11 +1,11 @@ - * @package testsuite + * @package caselib * @version $Id: view.html.php 4141 2013-01-18 06:15:13Z zhujinyonging@gmail.com $ * @link http://www.zentao.net */ @@ -13,14 +13,14 @@ + parent != '-1'):?> + diff --git a/module/testcase/control.php b/module/testcase/control.php index 62438778e3..c90a495de1 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -217,7 +217,7 @@ class testcase extends control if(!empty($_POST)) { $response['result'] = 'success'; - $response['message'] = ''; + $response['message'] = $this->lang->saveSuccess; $caseResult = $this->testcase->create($bugID); if(!$caseResult or dao::isError()) @@ -320,6 +320,7 @@ class testcase extends control /* Get the status of stories are not closed. */ $storyStatus = $this->lang->story->statusList; + unset($storyStatus['closed']); $modules = array(); if($currentModuleID) { @@ -482,7 +483,15 @@ class testcase extends control { $case = $this->testcase->getById($caseID, $version); if(!$case) die(js::error($this->lang->notFound) . js::locate('back')); - if($from == 'testtask') $run = $this->loadModel('testtask')->getRunByCase($taskID, $caseID); + if($from == 'testtask') + { + $run = $this->loadModel('testtask')->getRunByCase($taskID, $caseID); + $case->assignedTo = $run->assignedTo; + $case->lastRunner = $run->lastRunner; + $case->lastRunDate = $run->lastRunDate; + $case->lastRunResult = $run->lastRunResult; + $case->status = $run->status; + } $branches = $this->session->currentProductType == 'normal' ? array() : $this->loadModel('branch')->getPairs($case->product); $isLibCase = ($case->lib and empty($case->product)); @@ -1077,6 +1086,7 @@ class testcase extends control if($product->type != 'normal') $this->lang->testcase->branch = $this->lang->product->branchName[$product->type]; if($_POST) { + $this->app->loadLang('testtask'); $caseLang = $this->lang->testcase; $caseConfig = $this->config->testcase; @@ -1122,6 +1132,7 @@ class testcase extends control $row->id = $caseID; } } + if($taskID) $caseLang->statusList = $this->lang->testtask->statusList; $stmt = $this->dao->select('t1.*')->from(TABLE_TESTRESULT)->alias('t1') ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.run=t2.id') @@ -1173,7 +1184,7 @@ class testcase extends control $case->stepExpect = ''; $case->real = ''; $result = isset($results[$case->id]) ? $results[$case->id] : array(); - $case->real = $result[0]['real']; + $case->real = empty($result) ? '' : $result[0]['real']; if(isset($relatedSteps[$case->id])) { $i = $childId = 0; @@ -1681,8 +1692,9 @@ class testcase extends control */ public function ajaxGetStatus($methodName, $caseID = 0) { - $status = $this->testcase->getStatus($methodName, $caseID); - + $case = $this->testcase->getByID($caseID); + $status = $this->testcase->getStatus($methodName, $case); + if($methodName == 'update') $status = zget($status, 1, ''); die($status); } } diff --git a/module/testcase/js/batchcreate.js b/module/testcase/js/batchcreate.js index b3b13f4504..52aca5f003 100644 --- a/module/testcase/js/batchcreate.js +++ b/module/testcase/js/batchcreate.js @@ -11,7 +11,7 @@ $(document).ready(function() { var index = id.substring(5); var moduleID = $('#module' + index).val(); - var branch = $('#branch' + index).val(); + var branch = $('#branch' + index).length > 0 ? $('#branch' + index).val() : 0; if(moduleID == 'ditto') { for(var i = index - 1; i >=0; i--) diff --git a/module/testcase/js/showimport.js b/module/testcase/js/showimport.js index 3ca378f148..aede447892 100644 --- a/module/testcase/js/showimport.js +++ b/module/testcase/js/showimport.js @@ -1,24 +1,25 @@ -$(document).on('change', '.moduleChange', function() +$(document).on('mouseup', '[id^=story].chosen-with-drop', function() { - var moduleID = $(this).val(); - if(typeof(moduleID) == 'undefined') moduleID = 0; - var id = $(this).attr('id'); - var index = id.substring(6); - link = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&storyID='+ '' + '&onlyOption=true&status=noclosed'); - $('#story' + index).load(link, function(){$(this).trigger("chosen:updated");}); -}) - -$(document).on('change', '.storyChange', function() -{ - var storyID = $(this).val(); - if(typeof(storyID) == 'undefined') storyID = 0; - var link = createLink('testcase', 'ajaxGetStoryModule', 'storyID=' + storyID); - var id = $(this).attr('id'); - var index = id.substring(5); - $.get(link, function(json) - { - var obj = JSON.parse(json); - $("#module" + index).val(obj.moduleID); - $('#module' + index).trigger("chosen:updated"); - }) -}) + var select = $(this).prev('select'); + var id = $(select).attr('id'); + var index = id.substring(5); + var moduleID = $('#module' + index).val(); + var branch = $('#branch' + index).length > 0 ? $('#branch' + index).val() : 0; + if(moduleID == 'ditto') + { + for(var i = index - 1; i >=0; i--) + { + if($('#module' + i).val() != 'ditto') + { + moduleID = $('#module' + i).val(); + break; + } + } + } + var link = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&storyID='+ ($(select).val() || '0') + '&onlyOption=true&status=noclosed&limit=0&type=null'); + var $story = $('#story' + index); + if($story.data('loadLink') !== link) + { + $story.load(link, function(){$story.data('loadLink', link).trigger("chosen:updated");}); + } +}); diff --git a/module/testcase/model.php b/module/testcase/model.php index 06e0ec8116..62ba6d8121 100644 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -1103,8 +1103,8 @@ class testcaseModel extends model if(empty($desc)) continue; $step = new stdclass(); $step->type = $data->stepType[$key][$id]; - $step->desc = $desc; - $step->expect = trim($this->post->expect[$key][$id]); + $step->desc = htmlspecialchars($desc); + $step->expect = htmlspecialchars(trim($this->post->expect[$key][$id])); $steps[] = $step; } @@ -1152,7 +1152,7 @@ class testcaseModel extends model $stepData->parent = ($stepData->type == 'item') ? $parentStepID : 0; $stepData->case = $caseID; $stepData->version = $version; - $stepData->desc = $step['desc']; + $stepData->desc = htmlspecialchars($step['desc']); $stepData->expect = htmlspecialchars($step['expect']); $this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec(); if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID(); @@ -1189,7 +1189,7 @@ class testcaseModel extends model $stepData->parent = ($stepData->type == 'item') ? $parentStepID : 0; $stepData->case = $caseID; $stepData->version = 1; - $stepData->desc = $desc; + $stepData->desc = htmlspecialchars($desc); $stepData->expect = htmlspecialchars($this->post->expect[$key][$id]); $this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec(); if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID(); @@ -1592,7 +1592,7 @@ class testcaseModel extends model return false; } - $status = $this->post->status; + $status = $this->post->status ? $this->post->status : $case->status; $stepChanged = false; $steps = array(); diff --git a/module/testcase/view/browse.html.php b/module/testcase/view/browse.html.php index 8944b1959c..8c51fe241b 100644 --- a/module/testcase/view/browse.html.php +++ b/module/testcase/view/browse.html.php @@ -132,7 +132,7 @@ js::set('suiteID', $suiteID); $actionLink = $this->createLink('testcase', 'batchDelete', "productID=$productID"); $misc = common::hasPriv('testcase', 'batchDelete') ? "onclick=\"confirmBatchDelete('$actionLink')\"" : $class; - echo "
  • " . html::a('#', $lang->delete, '', $misc) . "
  • "; + if(common::hasPriv('testcase', 'batchDelete')) echo "
  • " . html::a('#', $lang->delete, '', $misc) . "
  • "; if(common::hasPriv('testcase', 'batchCaseTypeChange')) { diff --git a/module/testcase/view/view.html.php b/module/testcase/view/view.html.php index cba1543a51..9addab92ad 100644 --- a/module/testcase/view/view.html.php +++ b/module/testcase/view/view.html.php @@ -66,7 +66,7 @@ - + @@ -85,7 +85,7 @@ echo ""; echo ""; + echo nl2br(str_replace(' ', ' ', $step->desc)) . ""; echo ""; echo ""; $childId ++; diff --git a/module/testsuite/config.php b/module/testsuite/config.php index 4e325d3cf8..4a93268ced 100644 --- a/module/testsuite/config.php +++ b/module/testsuite/config.php @@ -2,17 +2,12 @@ $config->testsuite = new stdclass(); $config->testsuite->create = new stdclass(); $config->testsuite->edit = new stdclass(); -$config->testsuite->createlib = new stdclass(); -$config->testsuite->createcase = new stdclass(); -$config->testsuite->create->requiredFields = 'name'; -$config->testsuite->edit->requiredFields = 'name'; -$config->testsuite->createlib->requiredFields = 'name'; -$config->testsuite->createcase->requiredFields = 'title,type'; +$config->testsuite->create->requiredFields = 'name'; +$config->testsuite->edit->requiredFields = 'name'; $config->testsuite->editor = new stdclass(); $config->testsuite->editor->create = array('id' => 'desc', 'tools' => 'simpleTools'); $config->testsuite->editor->edit = array('id' => 'desc', 'tools' => 'simpleTools'); -$config->testsuite->editor->createlib = array('id' => 'desc', 'tools' => 'simpleTools'); $config->testsuite->datatable = new stdclass(); $config->testsuite->datatable->defaultField = array('id', 'pri', 'title', 'type', 'assignedTo', 'lastRunner', 'lastRunDate', 'lastRunResult', 'status', 'bugs', 'results', 'actions'); diff --git a/module/testsuite/control.php b/module/testsuite/control.php index 19239badf1..89d4839d80 100644 --- a/module/testsuite/control.php +++ b/module/testsuite/control.php @@ -79,7 +79,7 @@ class testsuite extends control if(!empty($_POST)) { $response['result'] = 'success'; - $response['message'] = ''; + $response['message'] = $this->lang->testsuite->successSaved; $suiteID = $this->testsuite->create($productID); if(dao::isError()) { @@ -92,7 +92,6 @@ class testsuite extends control $this->executeHooks($suiteID); $response['locate'] = $this->createLink('testsuite', 'browse', "productID=$productID"); - $response['message'] = $this->lang->testsuite->successSaved; $this->send($response); } @@ -176,7 +175,7 @@ class testsuite extends control if(!empty($_POST)) { $response['result'] = 'success'; - $response['message'] = ''; + $response['message'] = $this->lang->testsuite->successSaved; $changes = $this->testsuite->update($suiteID); if(dao::isError()) { @@ -186,47 +185,28 @@ class testsuite extends control } if($changes) { - $objectType = $suite->type == 'library' ? 'caselib' : 'testsuite'; - $actionID = $this->loadModel('action')->create($objectType, $suiteID, 'edited'); + $actionID = $this->loadModel('action')->create('testsuite', $suiteID, 'edited'); $this->action->logHistory($actionID, $changes); } $this->executeHooks($suiteID); - $method = $suite->type == 'library' ? 'libView' : 'view'; - $response['locate'] = inlink($method, "suiteID=$suiteID"); - $response['message'] = $this->lang->testsuite->successSaved; + $response['locate'] = inlink('view', "suiteID=$suiteID"); $this->send($response); } - if($suite->type == 'library') - { - /* Set lib menu. */ - if($this->config->global->flow == 'onlyTest') $this->lang->menugroup->testsuite = 'caselib'; - $libraries = $this->testsuite->getLibraries(); - $suiteID = $this->testsuite->saveLibState($suiteID, $libraries); - $this->testsuite->setLibMenu($libraries, $suiteID); + if($suite->type == 'private' and $suite->addedBy != $this->app->user->account and !$this->app->user->admin) die(js::error($this->lang->error->accessDenied) . js::locate('back')); - $this->view->title = $libraries[$suiteID] . $this->lang->colon . $this->lang->testsuite->edit; - $this->view->position[] = html::a($this->createLink('testsuite', 'library', "libID=$suiteID"), $libraries[$suiteID]); - $this->view->position[] = $this->lang->caselib->common; - } - else - { - if($suite->type == 'private' and $suite->addedBy != $this->app->user->account and !$this->app->user->admin) die(js::error($this->lang->error->accessDenied) . js::locate('back')); + /* Get suite info. */ + $this->view->products = $this->products = $this->loadModel('product')->getPairs('nocode'); + $productID = $this->product->saveState($suite->product, $this->products); - /* Get suite info. */ - $this->view->products = $this->products = $this->loadModel('product')->getPairs('nocode'); - $productID = $this->product->saveState($suite->product, $this->products); - - /* Set menu. */ - $this->testsuite->setMenu($this->products, $productID); - - $this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->testsuite->edit; - $this->view->position[] = html::a($this->createLink('testsuite', 'browse', "productID=$productID"), $this->products[$productID]); - $this->view->position[] = $this->lang->testsuite->common; - } + /* Set menu. */ + $this->testsuite->setMenu($this->products, $productID); + $this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->testsuite->edit; + $this->view->position[] = html::a($this->createLink('testsuite', 'browse', "productID=$productID"), $this->products[$productID]); + $this->view->position[] = $this->lang->testsuite->common; $this->view->position[] = $this->lang->testsuite->edit; $this->view->suite = $suite; @@ -245,7 +225,7 @@ class testsuite extends control { if($confirm == 'no') { - die(js::confirm($this->lang->testsuite->libraryDelete, inlink('delete', "suiteID=$suiteID&confirm=yes"))); + die(js::confirm($this->lang->testsuite->confirmDelete, inlink('delete', "suiteID=$suiteID&confirm=yes"))); } else { @@ -384,635 +364,4 @@ class testsuite extends control die(js::locate($this->createLink('testsuite', 'view', "suiteID=$suiteID"))); } - - /** - * Show library case. - * - * @param int $libID - * @param string $browseType - * @param int $param - * @param string $orderBy - * @param int $recTotal - * @param int $recPerPage - * @param int $pageID - * @access public - * @return void - */ - public function library($libID = 0, $browseType = 'all', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1) - { - /* Set browse type. */ - $browseType = strtolower($browseType); - - $libraries = $this->testsuite->getLibraries(); - if(empty($libraries)) $this->locate(inlink('createLib')); - - /* Save session. */ - $this->session->set('caseList', $this->app->getURI(true)); - - /* Set menu. */ - $libID = $this->testsuite->saveLibState($libID, $libraries); - setcookie('preCaseLibID', $libID, $this->config->cookieLife, $this->config->webRoot); - if($this->cookie->preCaseLibID != $libID) - { - $_COOKIE['libCaseModule'] = 0; - setcookie('libCaseModule', 0, 0, $this->config->webRoot); - } - - if($browseType == 'bymodule') setcookie('libCaseModule', (int)$param, 0, $this->config->webRoot); - if($browseType != 'bymodule') $this->session->set('libBrowseType', $browseType); - $moduleID = ($browseType == 'bymodule') ? (int)$param : ($browseType == 'bysearch' ? 0 : ($this->cookie->libCaseModule ? $this->cookie->libCaseModule : 0)); - $queryID = ($browseType == 'bysearch') ? (int)$param : 0; - - /* Set lib menu. */ - $this->testsuite->setLibMenu($libraries, $libID, $moduleID); - if($this->config->global->flow == 'onlyTest') $this->lang->menugroup->testsuite = 'caselib'; - - /* Load pager. */ - $this->app->loadClass('pager', $static = true); - $pager = pager::init($recTotal, $recPerPage, $pageID); - - /* Build the search form. */ - $this->loadModel('testcase'); - $actionURL = $this->createLink('testsuite', 'library', "libID=$libID&browseType=bySearch&queryID=myQueryID"); - $this->testsuite->buildSearchForm($libID, $libraries, $queryID, $actionURL); - - /* Append id for secend sort. */ - $sort = $this->loadModel('common')->appendOrder($orderBy); - - /* save session .*/ - $cases = $this->testsuite->getLibCases($libID, $browseType, $queryID, $moduleID, $sort, $pager); - $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', true); - - $this->loadModel('datatable'); - $this->loadModel('tree'); - $showModule = !empty($this->config->datatable->testsuiteLibrary->showModule) ? $this->config->datatable->testsuiteLibrary->showModule : ''; - $this->view->modulePairs = $showModule ? $this->tree->getModulePairs($libID, 'caselib', $showModule) : array(); - - $this->view->title = $this->lang->caselib->common . $this->lang->colon . $libraries[$libID]; - $this->view->position[] = html::a($this->createLink('testsuite', 'library', "libID=$libID"), $libraries[$libID]); - - $this->view->libID = $libID; - $this->view->libName = $libraries[$libID]; - $this->view->cases = $cases; - $this->view->orderBy = $orderBy; - $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter'); - $this->view->modules = $this->tree->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); - $this->view->moduleTree = $this->tree->getTreeMenu($libID, $viewType = 'caselib', $startModuleID = 0, array('treeModel', 'createCaseLibLink')); - $this->view->pager = $pager; - $this->view->browseType = $browseType; - $this->view->moduleID = $moduleID; - $this->view->moduleName = $moduleID ? $this->tree->getById($moduleID)->name : $this->lang->tree->all; - $this->view->param = $param; - $this->view->setShowModule = true; - - $this->display(); - } - - /** - * Create lib - * - * @access public - * @return void - */ - public function createLib() - { - if(!empty($_POST)) - { - $response['result'] = 'success'; - $response['message'] = ''; - $libID = $this->testsuite->createLib(); - if(dao::isError()) - { - $response['result'] = 'fail'; - $response['message'] = dao::getError(); - $this->send($response); - } - $this->loadModel('action')->create('caselib', $libID, 'opened'); - $response['locate'] = $this->createLink('testsuite', 'library', "libID=$libID"); - $response['message'] = $this->lang->testsuite->successSaved; - $this->send($response); - } - - /* Set menu. */ - $libraries = $this->testsuite->getLibraries(); - $libID = $this->testsuite->saveLibState(0, $libraries); - $this->testsuite->setLibMenu($libraries, $libID); - if($this->config->global->flow == 'onlyTest') $this->lang->menugroup->testsuite = 'caselib'; - - $this->view->title = $this->lang->caselib->common . $this->lang->colon . $this->lang->testsuite->createLib; - $this->view->position[] = $this->lang->caselib->common; - $this->view->position[] = $this->lang->testsuite->createLib; - $this->display(); - } - - /** - * Create case for library. - * - * @param int $libID - * @param int $moduleID - * @access public - * @return void - */ - public function createCase($libID, $moduleID = 0, $param = 0) - { - if(!empty($_POST)) - { - $this->loadModel('testcase'); - $this->config->testcase->create->requiredFields = $this->config->testsuite->createcase->requiredFields; - $caseResult = $this->testcase->create($bugID = 0); - if(!$caseResult or dao::isError()) die(js::error(dao::getError())); - - $caseID = $caseResult['id']; - if($caseResult['status'] == 'exists') - { - echo js::alert(sprintf($this->lang->duplicate, $this->lang->testcase->common)); - die(js::locate($this->createLink('testcase', 'view', "caseID=$caseID"), 'parent')); - } - - $this->loadModel('action')->create('case', $caseID, 'Opened'); - - /* If link from no head then reload. */ - if(isonlybody()) die(js::reload('parent')); - die(js::locate($this->createLink('testsuite', 'library', "libID={$libID}&browseType=byModule¶m={$_POST['module']}"), 'parent')); - } - /* Set lib menu. */ - $libraries = $this->testsuite->getLibraries(); - $libID = $this->testsuite->saveLibState($libID, $libraries); - $this->testsuite->setLibMenu($libraries, $libID); - if($this->config->global->flow == 'onlyTest') $this->lang->menugroup->testsuite = 'caselib'; - - $type = 'feature'; - $stage = ''; - $pri = 3; - $caseTitle = ''; - $precondition = ''; - $keywords = ''; - $steps = array(); - - $this->loadModel('testcase'); - if($param) - { - $testcase = $this->testcase->getById((int)$param); - $type = $testcase->type ? $testcase->type : 'feature'; - $stage = $testcase->stage; - $pri = $testcase->pri; - $storyID = $testcase->story; - $caseTitle = $testcase->title; - $precondition = $testcase->precondition; - $keywords = $testcase->keywords; - $steps = $testcase->steps; - } - - if(count($steps) < $this->config->testcase->defaultSteps) - { - $paddingCount = $this->config->testcase->defaultSteps - count($steps); - $step = new stdclass(); - $step->type = 'item'; - $step->desc = ''; - $step->expect = ''; - for($i = 1; $i <= $paddingCount; $i ++) $steps[] = $step; - } - - $this->view->title = $libraries[$libID] . $this->lang->colon . $this->lang->testcase->create; - $this->view->position[] = html::a($this->createLink('testsuite', 'library', "libID=$libID"), $libraries[$libID]); - $this->view->position[] = $this->lang->testsuite->common; - $this->view->position[] = $this->lang->testcase->create; - - foreach(explode(',', $this->config->testsuite->customCreateFields) as $field) $customFields[$field] = $this->lang->testcase->$field; - $this->view->showFields = $this->config->testsuite->custom->createFields; - $this->view->customFields = $customFields; - $this->view->libraries = $libraries; - $this->view->libID = $libID; - $this->view->currentModuleID = (int)$moduleID; - $this->view->caseTitle = $caseTitle; - $this->view->type = $type; - $this->view->stage = $stage; - $this->view->pri = $pri; - $this->view->precondition = $precondition; - $this->view->keywords = $keywords; - $this->view->steps = $steps; - $this->view->moduleOptionMenu = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); - $this->display(); - } - - /** - * Batch create case. - * - * @param int $libID - * @param int $moduleID - * @access public - * @return void - */ - public function batchCreateCase($libID, $moduleID = 0) - { - $this->loadModel('testcase'); - if(!empty($_POST)) - { - $caseID = $this->testsuite->batchCreateCase($libID); - if(dao::isError()) die(js::error(dao::getError())); - if(isonlybody()) die(js::closeModal('parent.parent', 'this')); - die(js::locate($this->createLink('testsuite', 'library', "libID=$libID&browseType=byModule¶m=$moduleID"), 'parent')); - } - - $libraries = $this->testsuite->getLibraries(); - if(empty($libraries)) $this->locate(inlink('createLib')); - - /* Set lib menu. */ - $this->testsuite->setLibMenu($libraries, $libID); - if($this->config->global->flow == 'onlyTest') $this->lang->menugroup->testsuite = 'caselib'; - - $currentModuleID = (int)$moduleID; - - /* Set module option menu. */ - $moduleOptionMenu = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); - $moduleOptionMenu['ditto'] = $this->lang->testcase->ditto; - - $this->view->title = $libraries[$libID] . $this->lang->colon . $this->lang->testcase->batchCreate; - $this->view->position[] = html::a($this->createLink('testsuite', 'library', "libID=$libID"), $libraries[$libID]); - $this->view->position[] = $this->lang->testcase->batchCreate; - $this->view->libID = $libID; - $this->view->moduleOptionMenu = $moduleOptionMenu; - $this->view->currentModuleID = $currentModuleID; - - $this->display(); - } - - /** - * View library - * - * @param int $libID - * @access public - * @return void - */ - public function libView($libID) - { - $lib = $this->testsuite->getById($libID, true); - - /* Set lib menu. */ - $libraries = $this->testsuite->getLibraries(); - $this->testsuite->setLibMenu($libraries, $libID); - if($this->config->global->flow == 'onlyTest') $this->lang->menugroup->testsuite = 'caselib'; - - $this->loadModel('testcase'); - $this->view->title = $lib->name . $this->lang->colon . $this->lang->testsuite->view; - $this->view->position[] = html::a($this->createLink('testsuite', 'library', "libID=$libID"), $lib->name); - $this->view->position[] = $this->lang->testsuite->common; - $this->view->position[] = $this->lang->testsuite->view; - - $this->view->lib = $lib; - $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter'); - $this->view->actions = $this->loadModel('action')->getList('caselib', $libID); - $this->display(); - } - - /** - * Ajax get drop menu. - * - * @param int $libID - * @param string $module - * @param string $method - * @param string $extra - * @access public - * @return void - */ - public function ajaxGetDropMenu($libID, $module, $method, $extra = '') - { - $this->view->link = $this->testsuite->getLibLink($module, $method, $extra); - $this->view->libID = $libID; - $this->view->module = $module; - $this->view->method = $method; - $this->view->extra = $extra; - - $libraries = $this->testsuite->getLibraries(); - - $this->view->libraries = $libraries; - $this->view->librariesPinyin = common::convert2Pinyin($libraries); - $this->display(); - } - - /** - * The results page of search. - * - * @param string $keywords - * @param string $module - * @param string $method - * @param mix $extra - * @access public - * @return void - */ - public function ajaxGetMatchedItems($keywords, $module, $method, $extra) - { - $libraries = $this->dao->select('*')->from(TABLE_TESTSUITE)->where('deleted')->eq(0)->andWhere('name')->like("%$keywords%")->andWhere('type')->eq('library')->orderBy('`id` desc')->fetchAll(); - $this->view->link = $this->testsuite->getLibLink($module, $method, $extra); - $this->view->libraries = $libraries; - $this->view->keywords = $keywords; - $this->display(); - } - - /** - * Export templet. - * - * @param int $libID - * @access public - * @return void - */ - public function exportTemplet($libID) - { - $this->loadModel('testcase'); - if($_POST) - { - $fields['module'] = $this->lang->testcase->module; - $fields['title'] = $this->lang->testcase->title; - $fields['precondition'] = $this->lang->testcase->precondition; - $fields['stepDesc'] = $this->lang->testcase->stepDesc; - $fields['stepExpect'] = $this->lang->testcase->stepExpect; - $fields['keywords'] = $this->lang->testcase->keywords; - $fields['pri'] = $this->lang->testcase->pri; - $fields['type'] = $this->lang->testcase->type; - $fields['stage'] = $this->lang->testcase->stage; - - $fields[''] = ''; - $fields['typeValue'] = $this->lang->testcase->lblTypeValue; - $fields['stageValue'] = $this->lang->testcase->lblStageValue; - - $modules = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); - $rows = array(); - $num = (int)$this->post->num; - for($i = 0; $i < $num; $i++) - { - foreach($modules as $moduleID => $module) - { - $row = new stdclass(); - $row->module = $module . "(#$moduleID)"; - $row->stepDesc = "1. \n2. \n3."; - $row->stepExpect = "1. \n2. \n3."; - - if(empty($rows)) - { - $row->typeValue = join("\n", $this->lang->testcase->typeList); - $row->stageValue = join("\n", $this->lang->testcase->stageList); - } - $rows[] = $row; - } - } - - $this->post->set('fields', $fields); - $this->post->set('kind', 'testcase'); - $this->post->set('rows', $rows); - $this->post->set('extraNum', $num); - $this->post->set('fileName', 'templet'); - $this->fetch('file', 'export2csv', $_POST); - } - - $this->display(); - } - - /** - * Import case csv file. - * - * @param int $libID - * @access public - * @return void - */ - public function import($libID) - { - $this->loadModel('testcase'); - if($_FILES) - { - $file = $this->loadModel('file')->getUpload('file'); - $file = $file[0]; - - $fileName = $this->file->savePath . $this->file->getSaveName($file['pathname']); - move_uploaded_file($file['tmpname'], $fileName); - - $rows = $this->file->parseCSV($fileName); - $fields = $this->testcase->getImportFields($productID); - $fields = array_flip($fields); - $header = array(); - foreach($rows[0] as $i => $rowValue) - { - if(empty($rowValue)) break; - $header[$i] = $rowValue; - } - unset($rows[0]); - - $columnKey = array(); - foreach($header as $title) - { - if(!isset($fields[$title])) continue; - $columnKey[] = $fields[$title]; - } - - if(count($columnKey) != count($header) or $this->post->encode != 'utf-8') - { - $fc = file_get_contents($fileName); - $encode = $this->post->encode != "utf-8" ? $this->post->encode : 'gbk'; - $fc = helper::convertEncoding($fc, $encode, 'utf-8'); - file_put_contents($fileName, $fc); - - $rows = $this->file->parseCSV($fileName); - $columnKey = array(); - $header = array(); - foreach($rows[0] as $i => $rowValue) - { - if(empty($rowValue)) break; - $header[$i] = $rowValue; - } - unset($rows[0]); - foreach($header as $title) - { - if(!isset($fields[$title])) continue; - $columnKey[] = $fields[$title]; - } - if(count($columnKey) != count($header)) die(js::alert($this->lang->testcase->errorEncode)); - } - - $this->session->set('importFile', $fileName); - - die(js::locate(inlink('showImport', "libID=$libID"), 'parent.parent')); - } - $this->display(); - } - - /** - * Show import case. - * - * @param int $libID - * @access public - * @return void - */ - public function showImport($libID) - { - $this->loadModel('testcase'); - if($_POST) - { - $this->testsuite->createFromImport($libID); - die(js::locate(inlink('library', "libID=$libID"), 'parent')); - } - - $libraries = $this->testsuite->getLibraries(); - if(empty($libraries)) $this->locate(inlink('createLib')); - - $this->testsuite->setLibMenu($libraries, $libID); - - $file = $this->session->importFile; - $caseLang = $this->lang->testcase; - $caseConfig = $this->config->testcase; - $modules = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); - - $fields = explode(',', $caseConfig->exportFields); - foreach($fields as $key => $fieldName) - { - $fieldName = trim($fieldName); - $fields[$fieldName] = isset($caseLang->$fieldName) ? $caseLang->$fieldName : $fieldName; - unset($fields[$key]); - } - - $fields = array_flip($fields); - $rows = $this->loadModel('file')->parseCSV($file); - $header = array(); - foreach($rows[0] as $i => $rowValue) - { - if(empty($rowValue)) break; - $header[$i] = $rowValue; - } - unset($rows[0]); - - foreach($header as $title) - { - if(!isset($fields[$title])) continue; - $columnKey[] = $fields[$title]; - } - - $endField = end($fields); - $caseData = array(); - $stepData = array(); - $stepVars = 0; - foreach($rows as $row => $data) - { - $case = new stdclass(); - foreach($columnKey as $key => $field) - { - if(!isset($data[$key])) continue; - $cellValue = $data[$key]; - if($field == 'module') - { - $case->$field = 0; - if(strrpos($cellValue, '(#') !== false) - { - $id = trim(substr($cellValue, strrpos($cellValue,'(#') + 2), ')'); - $case->$field = $id; - } - } - elseif(in_array($field, $caseConfig->export->listFields)) - { - if($field == 'stage') - { - $stages = explode("\n", $cellValue); - foreach($stages as $stage) $case->stage[] = array_search($stage, $caseLang->{$field . 'List'}); - $case->stage = join(',', $case->stage); - } - else - { - $case->$field = array_search($cellValue, $caseLang->{$field . 'List'}); - } - } - elseif($field != 'stepDesc' and $field != 'stepExpect') - { - $case->$field = $cellValue; - } - else - { - $steps = (array)$cellValue; - if(strpos($cellValue, "\n")) - { - $steps = explode("\n", $cellValue); - } - elseif(strpos($cellValue, "\r")) - { - $steps = explode("\r", $cellValue); - } - - $stepKey = str_replace('step', '', strtolower($field)); - $caseStep = array(); - - foreach($steps as $step) - { - $step = trim($step); - if(empty($step)) continue; - if(preg_match('/^(([0-9]+)\.[0-9]+)([.、]{1})/U', $step, $out)) - { - $num = $out[1]; - $parent = $out[2]; - $sign = $out[3]; - $signbit = $sign == '.' ? 1 : 3; - $step = trim(substr($step, strlen($num) + $signbit)); - if(!empty($step)) $caseStep[$num]['content'] = $step; - $caseStep[$num]['type'] = 'item'; - $caseStep[$parent]['type'] = 'group'; - } - elseif(preg_match('/^([0-9]+)([.、]{1})/U', $step, $out)) - { - $num = $out[1]; - $sign = $out[2]; - $signbit = $sign == '.' ? 1 : 3; - $step = trim(substr($step, strlen($num) + $signbit)); - if(!empty($step)) $caseStep[$num]['content'] = $step; - $caseStep[$num]['type'] = 'step'; - } - elseif(isset($num)) - { - if(!isset($caseStep[$num]['content'])) $caseStep[$num]['content'] = ''; - $caseStep[$num]['content'] .= "\n" . $step; - } - else - { - if($field == 'stepDesc') - { - $num = 1; - $caseStep[$num]['content'] = $step; - $caseStep[$num]['type'] = 'step'; - } - if($field == 'stepExpect' and isset($stepData[$row]['desc'])) - { - end($stepData[$row]['desc']); - $num = key($stepData[$row]['desc']); - $caseStep[$num]['content'] = $step; - } - } - } - unset($num); - unset($sign); - $stepVars += count($caseStep, COUNT_RECURSIVE) - count($caseStep); - $stepData[$row][$stepKey] = $caseStep; - } - } - - $caseData[$row] = $case; - unset($case); - } - - if(empty($caseData)) - { - unlink($this->session->importFile); - unset($_SESSION['importFile']); - echo js::alert($this->lang->error->noData); - die(js::locate($this->createLink('testsuite', 'library', "libID=$libID"))); - } - - /* Judge whether the editedTasks is too large and set session. */ - $countInputVars = count($caseData) * 9 + $stepVars; - $showSuhosinInfo = common::judgeSuhosinSetting($countInputVars); - if($showSuhosinInfo) $this->view->suhosinInfo = extension_loaded('suhosin') ? sprintf($this->lang->suhosinInfo, $countInputVars) : sprintf($this->lang->maxVarsInfo, $countInputVars); - - $this->view->title = $this->lang->caselib->common . $this->lang->colon . $this->lang->testcase->showImport; - $this->view->position[] = $this->lang->testcase->showImport; - - $this->view->modules = $modules; - $this->view->cases = $this->dao->select('id,module,stage,status,pri,type')->from(TABLE_CASE)->where('lib')->eq($libID)->andWhere('deleted')->eq(0)->andWhere('product')->eq(0)->fetchAll('id'); - $this->view->caseData = $caseData; - $this->view->stepData = $stepData; - $this->view->libID = $libID; - $this->display(); - } } diff --git a/module/testsuite/lang/en.php b/module/testsuite/lang/en.php index 12b8e00358..5a69a3ac69 100644 --- a/module/testsuite/lang/en.php +++ b/module/testsuite/lang/en.php @@ -21,11 +21,6 @@ $lang->testsuite->unlinkCase = "Unlink"; $lang->testsuite->unlinkCaseAction = "Unlink Case"; $lang->testsuite->batchUnlinkCases = "Batch Unlink Cases"; $lang->testsuite->deleted = 'Deleted'; -$lang->testsuite->exportTemplet = 'Export Template'; -$lang->testsuite->batchCreateCase = 'Batch Create'; -$lang->testsuite->import = 'Import'; -$lang->testsuite->importAction = 'Import Case'; -$lang->testsuite->showImport = 'Imported Data'; $lang->testsuite->successSaved = 'Saved'; $lang->testsuite->id = 'ID'; @@ -46,7 +41,6 @@ $lang->testsuite->legendBasicInfo = 'Basic Info'; $lang->testsuite->unlinkedCases = 'Unlinked Cases'; $lang->testsuite->confirmDelete = 'Do you want to delete this test suite?'; -$lang->testsuite->libraryDelete = 'Do you want to delete this library?'; $lang->testsuite->confirmUnlinkCase = 'Do you want to unlink this Case?'; $lang->testsuite->noticeNone = 'You have not created any suite yet.'; $lang->testsuite->noModule = '
    You have no modules.
    Manage it now.
    '; @@ -57,12 +51,3 @@ $lang->testsuite->lblUnlinkCase = 'Unlink Case'; $lang->testsuite->authorList['private'] = 'Private'; $lang->testsuite->authorList['public'] = 'Public'; - -$lang->caselib->common = 'Case Library'; -$lang->caselib->all = 'All Case Libraries'; - -$lang->testsuite->createLib = 'Create Case Library'; -$lang->testsuite->editLib = 'Edit Case Library'; -$lang->testsuite->library = 'View Cases in Library'; -$lang->testsuite->createCase = 'Create Case'; -$lang->testsuite->libView = 'Library Detail'; diff --git a/module/testsuite/lang/zh-cn.php b/module/testsuite/lang/zh-cn.php index 097bdc9db0..05a4bcfb24 100644 --- a/module/testsuite/lang/zh-cn.php +++ b/module/testsuite/lang/zh-cn.php @@ -21,11 +21,6 @@ $lang->testsuite->unlinkCase = "移除"; $lang->testsuite->unlinkCaseAction = "移除用例"; $lang->testsuite->batchUnlinkCases = "批量移除用例"; $lang->testsuite->deleted = '已删除'; -$lang->testsuite->exportTemplet = '导出模板'; -$lang->testsuite->batchCreateCase = '批量创建用例'; -$lang->testsuite->import = '导入'; -$lang->testsuite->importAction = '导入用例'; -$lang->testsuite->showImport = '显示导入数据'; $lang->testsuite->successSaved = '保存成功'; $lang->testsuite->id = '编号'; @@ -46,7 +41,6 @@ $lang->testsuite->legendBasicInfo = '基本信息'; $lang->testsuite->unlinkedCases = '未关联'; $lang->testsuite->confirmDelete = '您确认要删除该套件吗?'; -$lang->testsuite->libraryDelete = '您确认要删除该用例库吗?'; $lang->testsuite->confirmUnlinkCase = '您确认要移除该用例吗?'; $lang->testsuite->noticeNone = '您还没有创建套件'; $lang->testsuite->noModule = '
    您现在还没有模块信息
    请维护用例库模块
    '; @@ -57,12 +51,3 @@ $lang->testsuite->lblUnlinkCase = '移除用例'; $lang->testsuite->authorList['private'] = '私有'; $lang->testsuite->authorList['public'] = '公开'; - -$lang->caselib->common = '公共用例库'; -$lang->caselib->all = '所有用例库'; - -$lang->testsuite->createLib = '创建用例库'; -$lang->testsuite->editLib = '编辑用例库'; -$lang->testsuite->library = '浏览库用例'; -$lang->testsuite->createCase = '创建用例'; -$lang->testsuite->libView = '查看库概况'; diff --git a/module/testsuite/model.php b/module/testsuite/model.php index 9bada58ab6..1c17664957 100644 --- a/module/testsuite/model.php +++ b/module/testsuite/model.php @@ -99,140 +99,6 @@ class testsuiteModel extends model return $output; } - /** - * Set library menu. - * - * @param array $libraries - * @param int $libID - * @access public - * @return void - */ - public function setLibMenu($libraries, $libID, $moduleID = 0) - { - $currentLibName = zget($libraries, $libID, ''); - $isMobile = $this->app->viewType == 'mhtml'; - $selectHtml = ''; - if(!empty($libraries)) - { - if($isMobile) - { - $selectHtml = "{$currentLibName} "; - } - else - { - $dropMenuLink = helper::createLink('testsuite', 'ajaxGetDropMenu', "objectID=$libID&module=testsuite&method=library"); - $selectHtml = "
    "; - } - } - - if($this->config->global->flow == 'onlyTest') - { - $modules = $this->loadModel('tree')->getModulePairs($libID, 'caselib'); - $moduleName = ($moduleID && isset($modules[$moduleID])) ? $modules[$moduleID] : $this->lang->tree->all; - - if(!$isMobile) - { - $dropMenuLink = helper::createLink('tree', 'ajaxGetDropMenu', "objectID=$libID&module=testsuite&method=library"); - $selectHtml .= "
    "; - } - else - { - $selectHtml .= "{$moduleName} "; - } - } - - setCookie("lastCaseLib", $libID, $this->config->cookieLife, $this->config->webRoot); - - $pageNav = ''; - $pageActions = ''; - $isMobile = $this->app->viewType == 'mhtml'; - if($isMobile) - { - $this->app->loadLang('qa'); - $pageNav = html::a(helper::createLink('qa', 'index'), $this->lang->qa->index) . $this->lang->colon; - } - else - { - if($this->config->global->flow == 'full') - { - $this->app->loadLang('qa'); - $pageNav .= '
    '; - $pageNav .= '
    '; - } - else - { - $this->app->loadLang('testcase'); - $pageActions .= "
    "; - if(common::hasPriv('testsuite', 'exportTemplet')) - { - $link = helper::createLink('testsuite', 'exportTemplet', "libID=$libID"); - $pageActions .= html::a($link, " " . $this->lang->testsuite->exportTemplet, '', "class='btn btn-link export'"); - } - if(common::hasPriv('testsuite', 'import')) - { - $link = helper::createLink('testsuite', 'import', "libID=$libID"); - $pageActions .= html::a($link, " " . $this->lang->testcase->importFile, '', "class='btn btn-link export'"); - } - $pageActions .= '
    '; - $params = "libID=$libID&moduleID=" . (isset($moduleID) ? $moduleID : 0); - if(common::hasPriv('testsuite', 'batchCreateCase')) - { - $link = helper::createLink('testsuite', 'batchCreateCase', $params); - $pageActions .= html::a($link, "" . $this->lang->testcase->batchCreate, '', "class='btn btn-secondary'"); - } - if(common::hasPriv('testsuite', 'createCase')) - { - $link = helper::createLink('testsuite', 'createCase', $params); - $pageActions .= html::a($link, "" . $this->lang->testcase->create, '', "class='btn btn-primary'"); - } - if(common::hasPriv('testsuite', 'createLib')) - { - $link = helper::createLink('testsuite', 'createLib'); - $pageActions .= html::a($link, "" . $this->lang->testsuite->createLib, '', "class='btn btn-primary'"); - } - } - } - $pageNav .= $selectHtml; - - $this->lang->modulePageNav = $pageNav; - $this->lang->modulePageActions = $pageActions; - foreach($this->lang->caselib->menu as $key => $value) - { - $replace = ''; - if($this->config->global->flow == 'onlyTest') $replace = $libID; - common::setMenuVars($this->lang->caselib->menu, $key, $replace); - } - $this->lang->testsuite->menu = $this->lang->caselib->menu; - if($this->config->global->flow != 'full' && $this->app->getMethodName() != 'libview') $this->lang->testsuite->menu->bysearch = " {$this->lang->testcase->bySearch}"; - } - - /** - * Save lib state. - * - * @param int $libID - * @param array $libraries - * @access public - * @return int - */ - public function saveLibState($libID = 0, $libraries = array()) - { - if($libID > 0) $this->session->set('caseLib', (int)$libID); - if($libID == 0 and $this->cookie->lastCaseLib) $this->session->set('caseLib', $this->cookie->lastCaseLib); - if($libID == 0 and $this->session->caseLib == '') $this->session->set('caseLib', key($libraries)); - if(!isset($libraries[$this->session->caseLib])) - { - $this->session->set('caseLib', key($libraries)); - $libID = $this->session->caseLib; - } - return $this->session->caseLib; - } - /** * Create a test suite. * @@ -424,130 +290,11 @@ class testsuiteModel extends model */ public function delete($suiteID, $table = '') { - $suite = $this->getById($suiteID); parent::delete(TABLE_TESTSUITE, $suiteID); - if($suite->type == 'library') - { - $this->dao->update(TABLE_ACTION)->set('objectType')->eq('caselib')->where('objectID')->eq($suiteID)->andWhere('objectType')->eq('testsuite')->exec(); - } - else - { - $this->dao->delete()->from(TABLE_SUITECASE)->where('suite')->eq($suiteID)->exec(); - } + $this->dao->delete()->from(TABLE_SUITECASE)->where('suite')->eq($suiteID)->exec(); return !dao::isError(); } - /** - * Get libraries. - * - * @access public - * @return array - */ - public function getLibraries() - { - return $this->dao->select("id,name")->from(TABLE_TESTSUITE) - ->where('product')->eq(0) - ->andWhere('deleted')->eq(0) - ->andWhere('type')->eq('library') - ->orderBy('id_desc') - ->fetchPairs('id', 'name'); - } - - /** - * Create lib. - * - * @access public - * @return int - */ - public function createLib() - { - $lib = fixer::input('post') - ->stripTags($this->config->testsuite->editor->create['id'], $this->config->allowedTags) - ->setForce('type', 'library') - ->add('addedBy', $this->app->user->account) - ->add('addedDate', helper::now()) - ->remove('uid') - ->get(); - $lib = $this->loadModel('file')->processImgURL($lib, $this->config->testsuite->editor->create['id'], $this->post->uid); - $this->dao->insert(TABLE_TESTSUITE)->data($lib) - ->batchcheck($this->config->testsuite->createlib->requiredFields, 'notempty') - ->check('name', 'unique', "deleted = '0'") - ->exec(); - if(!dao::isError()) - { - $libID = $this->dao->lastInsertID(); - $this->file->updateObjectID($this->post->uid, $libID, 'caselib'); - return $libID; - } - return false; - } - - /** - * Get lib cases. - * - * @param int $libID - * @param string $browseType - * @param int $queryID - * @param int $moduleID - * @param string $sort - * @param object $pager - * @access public - * @return array - */ - public function getLibCases($libID, $browseType, $queryID = 0, $moduleID = 0, $sort = 'id_desc', $pager = null) - { - $moduleIdList = $moduleID ? $this->loadModel('tree')->getAllChildId($moduleID) : '0'; - $browseType = ($browseType == 'bymodule' and $this->session->libBrowseType and $this->session->libBrowseType != 'bysearch') ? $this->session->libBrowseType : $browseType; - - $cases = array(); - if($browseType == 'bymodule' or $browseType == 'all' or $browseType == 'wait') - { - $cases = $this->dao->select('*')->from(TABLE_CASE) - ->where('lib')->eq((int)$libID) - ->andWhere('product')->eq(0) - ->beginIF($moduleIdList)->andWhere('module')->in($moduleIdList)->fi() - ->beginIF($browseType == 'wait')->andWhere('status')->eq($browseType)->fi() - ->andWhere('deleted')->eq('0') - ->orderBy($sort)->page($pager)->fetchAll('id'); - } - /* By search. */ - elseif($browseType == 'bysearch') - { - if($queryID) - { - $query = $this->loadModel('search')->getQuery($queryID); - $this->session->set('caselibQuery', ' 1 = 1'); - if($query) - { - $this->session->set('caselibQuery', $query->sql); - $this->session->set('caselibForm', $query->form); - } - } - else - { - if($this->session->caselibQuery == false) $this->session->set('caselibQuery', ' 1 = 1'); - } - - $queryLibID = $libID; - $allLib = "`lib` = 'all'"; - $caseQuery = '(' . $this->session->caselibQuery; - if(strpos($this->session->caselibQuery, $allLib) !== false) - { - $caseQuery = str_replace($allLib, '1', $caseQuery); - $queryLibID = 'all'; - } - $caseQuery .= ')'; - - $cases = $this->dao->select('*')->from(TABLE_CASE)->where($caseQuery) - ->beginIF($queryLibID != 'all')->andWhere('lib')->eq((int)$libID)->fi() - ->andWhere('product')->eq(0) - ->andWhere('deleted')->eq(0) - ->orderBy($sort)->page($pager)->fetchAll(); - - } - return $cases; - } - /** * Get not imported cases. * @@ -601,323 +348,4 @@ class testsuiteModel extends model ->page($pager) ->fetchAll('id'); } - - /** - * Build search form. - * - * @param int $libID - * @param array $libraries - * @param int $queryID - * @param string $actionURL - * @access public - * @return void - */ - public function buildSearchForm($libID, $libraries, $queryID, $actionURL) - { - $this->config->testcase->search['fields']['lib'] = $this->lang->testcase->lib; - $this->config->testcase->search['params']['lib']['values'] = array('' => '', $libID => $libraries[$libID], 'all' => $this->lang->caselib->all); - $this->config->testcase->search['params']['lib']['operator'] = '='; - $this->config->testcase->search['params']['lib']['control'] = 'select'; - $this->config->testcase->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib'); - if(!$this->config->testcase->needReview) unset($this->config->testcase->search['params']['status']['values']['wait']); - unset($this->config->testcase->search['fields']['product']); - unset($this->config->testcase->search['params']['product']); - unset($this->config->testcase->search['fields']['branch']); - unset($this->config->testcase->search['params']['branch']); - unset($this->config->testcase->search['fields']['lastRunner']); - unset($this->config->testcase->search['params']['lastRunner']); - unset($this->config->testcase->search['fields']['lastRunResult']); - unset($this->config->testcase->search['params']['lastRunResult']); - unset($this->config->testcase->search['fields']['lastRunDate']); - unset($this->config->testcase->search['params']['lastRunDate']); - - $this->config->testcase->search['module'] = 'caselib'; - $this->config->testcase->search['actionURL'] = $actionURL; - $this->config->testcase->search['queryID'] = $queryID; - - $this->loadModel('search')->setSearchParams($this->config->testcase->search); - } - - /** - * Get lib link. - * - * @param string $module - * @param string $method - * @param string $extra - * @access public - * @return string - */ - public function getLibLink($module, $method, $extra) - { - $link = ''; - if($module == 'testsuite') - { - if($module == 'testsuite' && ($method == 'createlib')) - { - $link = helper::createLink($module, 'library', "libID=%s"); - } - else - { - $link = helper::createLink($module, $method, "libID=%s"); - } - } - else if($module == 'tree') - { - $link = helper::createLink($module, $method, "libID=%s&type=caselib¤tModuleID=0"); - } - else - { - $link = helper::createLink('testsuite', 'library', "libID=%s"); - } - return $link; - } - - /** - * Create from import. - * - * @param int $libID - * @access public - * @return void - */ - public function createFromImport($libID) - { - $this->loadModel('action'); - $this->loadModel('testcase'); - $this->loadModel('file'); - $now = helper::now(); - $data = fixer::input('post')->get(); - - if(!empty($_POST['id'])) - { - $oldSteps = $this->dao->select('t2.*')->from(TABLE_CASE)->alias('t1') - ->leftJoin(TABLE_CASESTEP)->alias('t2')->on('t1.id = t2.case') - ->where('t1.id')->in(($_POST['id'])) - ->andWhere('t1.version=t2.version') - ->orderBy('t2.id') - ->fetchGroup('case'); - $oldCases = $this->dao->select('*')->from(TABLE_CASE)->where('id')->in($_POST['id'])->fetchAll('id'); - } - - $cases = array(); - foreach($data->lib as $key => $lib) - { - $caseData = new stdclass(); - - $caseData->lib = $lib; - $caseData->module = $data->module[$key]; - $caseData->title = $data->title[$key]; - $caseData->pri = (int)$data->pri[$key]; - $caseData->type = $data->type[$key]; - $caseData->status = $data->status[$key]; - $caseData->stage = join(',', $data->stage[$key]); - $caseData->keywords = $data->keywords[$key]; - $caseData->frequency = 1; - $caseData->precondition = $data->precondition[$key]; - - if(isset($this->config->testcase->create->requiredFields)) - { - $requiredFields = explode(',', $this->config->testcase->create->requiredFields); - foreach($requiredFields as $requiredField) - { - $requiredField = trim($requiredField); - if(empty($caseData->$requiredField)) dao::$errors[] = sprintf($this->lang->testcase->noRequire, $key, $this->lang->testcase->$requiredField); - } - } - - $cases[$key] = $caseData; - } - if(dao::isError()) die(js::error(dao::getError())); - - $forceNotReview = $this->testcase->forceNotReview(); - foreach($cases as $key => $caseData) - { - if(!empty($_POST['id'][$key]) and empty($_POST['insert'])) - { - $caseID = $data->id[$key]; - $stepChanged = false; - $steps = array(); - $oldStep = isset($oldSteps[$caseID]) ? $oldSteps[$caseID] : array(); - $oldCase = $oldCases[$caseID]; - - /* Remove the empty setps in post. */ - $steps = array(); - if(isset($_POST['desc'][$key])) - { - foreach($data->desc[$key] as $id => $desc) - { - $desc = trim($desc); - if(empty($desc)) continue; - $step = new stdclass(); - $step->type = $data->stepType[$key][$id]; - $step->desc = $desc; - $step->expect = trim($data->expect[$key][$id]); - - $steps[] = $step; - } - } - - /* If step count changed, case changed. */ - if((!$oldStep != !$steps) or (count($oldStep) != count($steps))) - { - $stepChanged = true; - } - else - { - /* Compare every step. */ - foreach($oldStep as $id => $oldStep) - { - if(trim($oldStep->desc) != trim($steps[$id]->desc) or trim($oldStep->expect) != $steps[$id]->expect) - { - $stepChanged = true; - break; - } - } - } - - $version = $stepChanged ? $oldCase->version + 1 : $oldCase->version; - $caseData->version = $version; - $changes = common::createChanges($oldCase, $caseData); - if(!$changes and !$stepChanged) continue; - - if($changes or $stepChanged) - { - $caseData->lastEditedBy = $this->app->user->account; - $caseData->lastEditedDate = $now; - if($stepChanged and !$forceNotReview) $caseData->status = 'wait'; - $this->dao->update(TABLE_CASE)->data($caseData)->where('id')->eq($caseID)->autoCheck()->exec(); - if($stepChanged) - { - $parentStepID = 0; - foreach($steps as $id => $step) - { - $step = (array)$step; - if(empty($step['desc'])) continue; - $stepData = new stdclass(); - $stepData->type = ($step['type'] == 'item' and $parentStepID == 0) ? 'step' : $step['type']; - $stepData->parent = ($stepData->type == 'item') ? $parentStepID : 0; - $stepData->case = $caseID; - $stepData->version = $version; - $stepData->desc = $step['desc']; - $stepData->expect = $step['expect']; - $this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec(); - if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID(); - if($stepData->type == 'step') $parentStepID = 0; - } - } - $oldCase->steps = $this->testcase->joinStep($oldStep); - $caseData->steps = $this->testcase->joinStep($steps); - $changes = common::createChanges($oldCase, $caseData); - $actionID = $this->action->create('case', $caseID, 'Edited'); - $this->action->logHistory($actionID, $changes); - } - } - else - { - $caseData->version = 1; - $caseData->openedBy = $this->app->user->account; - $caseData->openedDate = $now; - $caseData->status = $forceNotReview ? 'normal' : 'wait'; - $this->dao->insert(TABLE_CASE)->data($caseData)->autoCheck()->exec(); - - if(!dao::isError()) - { - $caseID = $this->dao->lastInsertID(); - $parentStepID = 0; - foreach($data->desc[$key] as $id => $desc) - { - $desc = trim($desc); - if(empty($desc)) continue; - $stepData = new stdclass(); - $stepData->type = ($data->stepType[$key][$id] == 'item' and $parentStepID == 0) ? 'step' : $data->stepType[$key][$id]; - $stepData->parent = ($stepData->type == 'item') ? $parentStepID : 0; - $stepData->case = $caseID; - $stepData->version = 1; - $stepData->desc = $desc; - $stepData->expect = $data->expect[$key][$id]; - $this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec(); - if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID(); - if($stepData->type == 'step') $parentStepID = 0; - } - $this->action->create('case', $caseID, 'Opened'); - } - } - } - - unlink($this->session->importFile); - unset($_SESSION['importFile']); - } - - /** - * Batch create case for lib. - * - * @param int $libID - * @access public - * @return void - */ - public function batchCreateCase($libID) - { - $this->loadModel('testcase'); - $this->loadModel('action'); - - $now = helper::now(); - $cases = fixer::input('post')->get(); - $batchNum = count(reset($cases)); - - $result = $this->loadModel('common')->removeDuplicate('case', $cases, "lib={$libID}"); - $cases = $result['data']; - - for($i = 0; $i < $batchNum; $i++) - { - if(!empty($cases->title[$i]) and empty($cases->type[$i])) die(js::alert(sprintf($this->lang->error->notempty, $this->lang->testcase->type))); - } - - $module = 0; - $type = ''; - $pri = 3; - for($i = 0; $i < $batchNum; $i++) - { - $module = $cases->module[$i] == 'ditto' ? $module : $cases->module[$i]; - $type = $cases->type[$i] == 'ditto' ? $type : $cases->type[$i]; - $pri = $cases->pri[$i] == 'ditto' ? $pri : $cases->pri[$i]; - $cases->module[$i] = (int)$module; - $cases->type[$i] = $type; - $cases->pri[$i] = $pri; - } - - $forceNotReview = $this->testcase->forceNotReview(); - for($i = 0; $i < $batchNum; $i++) - { - if($cases->type[$i] != '' and $cases->title[$i] != '') - { - $data[$i] = new stdclass(); - $data[$i]->lib = $libID; - $data[$i]->module = $cases->module[$i]; - $data[$i]->type = $cases->type[$i]; - $data[$i]->pri = $cases->pri[$i]; - $data[$i]->stage = empty($cases->stage[$i]) ? '' : implode(',', $cases->stage[$i]); - $data[$i]->color = $cases->color[$i]; - $data[$i]->title = $cases->title[$i]; - $data[$i]->precondition = $cases->precondition[$i]; - $data[$i]->keywords = $cases->keywords[$i]; - $data[$i]->openedBy = $this->app->user->account; - $data[$i]->openedDate = $now; - $data[$i]->status = $forceNotReview ? 'normal' : 'wait'; - $data[$i]->version = 1; - - $this->dao->insert(TABLE_CASE)->data($data[$i]) - ->autoCheck() - ->batchCheck($this->config->testcase->create->requiredFields, 'notempty') - ->exec(); - - if(dao::isError()) - { - echo js::error(dao::getError()); - die(js::reload('parent')); - } - - $caseID = $this->dao->lastInsertID(); - $actionID = $this->action->create('case', $caseID, 'Opened'); - } - } - } } diff --git a/module/testtask/control.php b/module/testtask/control.php index c156c43c4d..e025ea3a79 100644 --- a/module/testtask/control.php +++ b/module/testtask/control.php @@ -289,6 +289,7 @@ class testtask extends control $this->config->testcase->search['module'] = 'testtask'; $this->config->testcase->search['params']['product']['values'] = array($productID => $this->products[$productID], 'all' => $this->lang->testcase->allProduct); $this->config->testcase->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'case'); + $this->config->testcase->search['params']['status']['values'] = array('' => '') + $this->lang->testtask->statusList; $this->config->testcase->search['queryID'] = $queryID; $this->config->testcase->search['fields']['assignedTo'] = $this->lang->testtask->assignedTo; @@ -505,7 +506,7 @@ class testtask extends control if($this->post->comment != '' or !empty($changes)) { - $actionID = $this->action->create('testtask', $taskID, 'Started', $this->post->comment); + $actionID = $this->loadModel('action')->create('testtask', $taskID, 'Started', $this->post->comment); $this->action->logHistory($actionID, $changes); } @@ -547,7 +548,7 @@ class testtask extends control if($this->post->comment != '' or !empty($changes)) { - $actionID = $this->action->create('testtask', $taskID, 'Activated', $this->post->comment); + $actionID = $this->loadModel('action')->create('testtask', $taskID, 'Activated', $this->post->comment); $this->action->logHistory($actionID, $changes); } @@ -589,7 +590,7 @@ class testtask extends control if($this->post->comment != '' or !empty($changes)) { - $actionID = $this->action->create('testtask', $taskID, 'Closed', $this->post->comment); + $actionID = $this->loadModel('action')->create('testtask', $taskID, 'Closed', $this->post->comment); $this->action->logHistory($actionID, $changes); } @@ -632,7 +633,7 @@ class testtask extends control if($this->post->comment != '' or !empty($changes)) { - $actionID = $this->action->create('testtask', $taskID, 'Blocked', $this->post->comment); + $actionID = $this->loadModel('action')->create('testtask', $taskID, 'Blocked', $this->post->comment); $this->action->logHistory($actionID, $changes); } diff --git a/module/testtask/model.php b/module/testtask/model.php index 046f65a44a..c198c5911f 100644 --- a/module/testtask/model.php +++ b/module/testtask/model.php @@ -574,8 +574,8 @@ class testtaskModel extends model { $datas = $this->dao->select('lastRunner AS name, COUNT(*) AS value')->from(TABLE_TESTRUN)->where('task')->eq($taskID)->groupBy('name')->orderBy('value DESC')->fetchAll('name'); if(!$datas) return array(); - - foreach($datas as $result => $data) $data->name = $result ? $result : $this->lang->testtask->unexecuted; + $users = $this->loadModel('user')->getPairs('noclosed|noletter'); + foreach($datas as $result => $data) $data->name = $result ? zget($users, $result, $result) : $this->lang->testtask->unexecuted; return $datas; } @@ -764,7 +764,7 @@ class testtaskModel extends model { $orderBy = (strpos($orderBy, 'assignedTo') !== false or strpos($orderBy, 'lastRunResult') !== false) ? ('t1.' . $orderBy) : ('t2.' . $orderBy); - return $this->dao->select('t2.*,t1.*,t2.version as caseVersion,t3.title as storyTitle')->from(TABLE_TESTRUN)->alias('t1') + return $this->dao->select('t2.*,t1.*,t2.version as caseVersion,t3.title as storyTitle,t2.status as caseStatus')->from(TABLE_TESTRUN)->alias('t1') ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id') ->leftJoin(TABLE_STORY)->alias('t3')->on('t2.story = t3.id') ->where('t1.task')->eq((int)$taskID) @@ -788,7 +788,7 @@ class testtaskModel extends model { $orderBy = strpos($orderBy, 'assignedTo') !== false ? ('t1.' . $orderBy) : ('t2.' . $orderBy); - return $this->dao->select('t2.*,t1.*,t2.version as caseVersion,t3.title as storyTitle')->from(TABLE_TESTRUN)->alias('t1') + return $this->dao->select('t2.*,t1.*,t2.version as caseVersion,t3.title as storyTitle,t2.status as caseStatus')->from(TABLE_TESTRUN)->alias('t1') ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id') ->leftJoin(TABLE_STORY)->alias('t3')->on('t2.story = t3.id') ->where('t1.task')->eq((int)$taskID) @@ -852,9 +852,10 @@ class testtaskModel extends model } $caseQuery = preg_replace('/`(\w+)`/', 't2.`$1`', $caseQuery); - $caseQuery = str_replace(array('t2.`assignedTo`', 't2.`lastRunner`', 't2.`lastRunDate`', 't2.`lastRunResult`'), array('t1.`assignedTo`', 't1.`lastRunner`', 't1.`lastRunDate`', 't1.`lastRunResult`'), $caseQuery); - $runs = $this->dao->select('t2.*,t1.*, t2.version as caseVersion')->from(TABLE_TESTRUN)->alias('t1') + $caseQuery = str_replace(array('t2.`assignedTo`', 't2.`lastRunner`', 't2.`lastRunDate`', 't2.`lastRunResult`', 't2.`status`'), array('t1.`assignedTo`', 't1.`lastRunner`', 't1.`lastRunDate`', 't1.`lastRunResult`', 't1.`status`'), $caseQuery); + $runs = $this->dao->select('t2.*,t1.*, t2.version as caseVersion,t3.title as storyTitle,t2.status as caseStatus')->from(TABLE_TESTRUN)->alias('t1') ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id') + ->leftJoin(TABLE_STORY)->alias('t3')->on('t2.story = t3.id') ->where($caseQuery) ->andWhere('t1.task')->eq($task->id) ->andWhere('t2.deleted')->eq(0) @@ -1159,7 +1160,7 @@ class testtaskModel extends model if($action == 'block') return ($testtask->status == 'doing' || $testtask->status == 'wait'); if($action == 'activate') return ($testtask->status == 'blocked' || $testtask->status == 'done'); if($action == 'close') return $testtask->status != 'done'; - if($action == 'runcase') return $testtask->status != 'wait'; + if($action == 'runcase') return isset($testtask->caseStatus) ? $testtask->caseStatus != 'wait' : $testtask->status != 'wait'; return true; } diff --git a/module/todo/control.php b/module/todo/control.php index 1f2638062d..7e7764b3f5 100644 --- a/module/todo/control.php +++ b/module/todo/control.php @@ -142,7 +142,7 @@ class todo extends control $actionID = $this->loadModel('action')->create('todo', $todoID, 'edited'); $this->action->logHistory($actionID, $changes); } - die(js::locate(inlink('view', "todoID=$todoID"), 'parent')); + die(js::locate($this->session->todoList, 'parent')); } /* Judge a private todo or not, If private, die. */ diff --git a/module/todo/view/edit.html.php b/module/todo/view/edit.html.php index 1b0fc626f4..e851e4ea77 100644 --- a/module/todo/view/edit.html.php +++ b/module/todo/view/edit.html.php @@ -20,7 +20,7 @@ - +
    diff --git a/module/tree/control.php b/module/tree/control.php index 8fef7d1d4f..059b5e3aee 100644 --- a/module/tree/control.php +++ b/module/tree/control.php @@ -54,8 +54,8 @@ class tree extends control } elseif(strpos($viewType, 'caselib') !== false) { - $this->loadModel('testsuite'); - $lib = $this->testsuite->getById($rootID); + $this->loadModel('caselib'); + $lib = $this->caselib->getById($rootID); $this->view->root = $lib; } @@ -116,13 +116,13 @@ class tree extends control } elseif($viewType == 'caselib') { - $this->testsuite->setLibMenu($this->testsuite->getLibraries(), $rootID); - $this->lang->tree->menu = $this->lang->testsuite->menu; - $this->lang->tree->menuOrder = $this->lang->testsuite->menuOrder; + $this->caselib->setLibMenu($this->caselib->getLibraries(), $rootID); + $this->lang->tree->menu = $this->lang->caselib->menu; + $this->lang->tree->menuOrder = $this->lang->caselib->menuOrder; $this->lang->set('menugroup.tree', 'qa'); $title = $this->lang->tree->manageCaseLib; - $position[] = html::a($this->createLink('testsuite', 'library', "libID=$rootID"), $lib->name); + $position[] = html::a($this->createLink('caselib', 'browse', "libID=$rootID"), $lib->name); $position[] = $this->lang->tree->manageCaseLib; } elseif(strpos($viewType, 'doc') !== false) @@ -438,9 +438,9 @@ class tree extends control $this->view->method = $method; $this->view->extra = $extra; + $viewType = $module; if($module == 'bug') $viewType = 'bug'; if($module == 'testcase') $viewType = 'case'; - if($module == 'testsuite') $viewType = 'caselib'; $modules = $this->tree->getOptionMenu($rootID, $viewType); $modulesPinyin = common::convert2Pinyin($modules); diff --git a/module/tree/css/edit.css b/module/tree/css/edit.css deleted file mode 100644 index 0037a851da..0000000000 --- a/module/tree/css/edit.css +++ /dev/null @@ -1 +0,0 @@ -body{background:white; overflow:none} diff --git a/module/tree/css/edit.zh-cn.css b/module/tree/css/edit.zh-cn.css index 585844a6f4..6e60376bc6 100644 --- a/module/tree/css/edit.zh-cn.css +++ b/module/tree/css/edit.zh-cn.css @@ -1 +1 @@ -.thWidth{width:80px !important;} +.thWidth{width:90px !important;} diff --git a/module/tree/css/edit.zh-tw.css b/module/tree/css/edit.zh-tw.css index 585844a6f4..6e60376bc6 100644 --- a/module/tree/css/edit.zh-tw.css +++ b/module/tree/css/edit.zh-tw.css @@ -1 +1 @@ -.thWidth{width:80px !important;} +.thWidth{width:90px !important;} diff --git a/module/tree/lang/zh-cn.php b/module/tree/lang/zh-cn.php index fcd2db6f9a..36f820d7ab 100644 --- a/module/tree/lang/zh-cn.php +++ b/module/tree/lang/zh-cn.php @@ -39,6 +39,7 @@ $lang->tree->addChild = "增加子模块"; $lang->tree->confirmDelete = '该模块及其子模块都会被删除,您确定删除吗?'; $lang->tree->confirmDeleteLine = "您确定删除该{$lang->productCommon}线吗?"; $lang->tree->confirmRoot = "模块的所属{$lang->productCommon}修改,会关联修改该模块下的需求、Bug、用例的所属{$lang->productCommon},以及{$lang->projectCommon}和{$lang->productCommon}的关联关系。该操作比较危险,请谨慎操作。是否确认修改?"; +$lang->tree->confirmRoot4Doc = "模块的所属文档库修改,会关联修改该模块下的文档的关联关系。该操作比较危险,请谨慎操作。是否确认修改?"; $lang->tree->successSave = '成功保存'; $lang->tree->successFixed = '成功修正数据!'; $lang->tree->repeatName = '模块名“%s”已经存在!'; diff --git a/module/tree/model.php b/module/tree/model.php index 17baad9782..bd5241c751 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -248,6 +248,8 @@ class treeModel extends model { $parentModules = explode(',', trim($module->path, ',')); if($type == 'product' and isset($noProductModules[$parentModules[0]])) continue; + /* Fix bug #2007. */ + if($type == 'product' and $module->type == 'task' and !isset($modules[$parentModules[0]])) continue; $rootName = ($productNum > 1 and $type == 'product') ? "/$rootModule/" : '/'; if($type == 'product' and $module->branch and isset($branchGroups[$id][$module->branch])) $rootName .= $branchGroups[$id][$module->branch] . '/'; $this->buildTreeArray($treeMenu, $modules, $module, $rootName); diff --git a/module/tree/view/browse.html.php b/module/tree/view/browse.html.php index b3ae64b327..9b849c2c0d 100644 --- a/module/tree/view/browse.html.php +++ b/module/tree/view/browse.html.php @@ -14,7 +14,6 @@ diff --git a/module/tree/view/edit.html.php b/module/tree/view/edit.html.php index e52c7b790f..511cfdfa0e 100644 --- a/module/tree/view/edit.html.php +++ b/module/tree/view/edit.html.php @@ -34,7 +34,7 @@ if(isset($pageCSS)) css::internal($pageCSS);
    - + type != 'line'):?> @@ -81,20 +81,7 @@ function getProductModules(productID) $('#parent').trigger('chosen:updated') }, 'json'); } -$(function() -{ - if(type == 'doc') return; - $('#root').change(function() - { - if($(this).val() == currentRoot) return true; - if(!confirm('tree->confirmRoot?>')) - { - $('#root').val(currentRoot); - $('#root').trigger('chosen:updated'); - } - getProductModules($(this).val()); - }) -}) + function loadDocModule(libID) { var link = createLink('doc', 'ajaxGetChild', 'libID=' + libID + '&type=parent'); @@ -105,6 +92,22 @@ function loadDocModule(libID) } $(function() { + $('#root').change(function() + { + if($(this).val() == currentRoot) return true; + var confirmRoot = tree->confirmRoot4Doc : $lang->tree->confirmRoot);?>; + if(!confirm(confirmRoot)) + { + $('#root').val(currentRoot); + $('#root').trigger('chosen:updated'); + } + else + { + if(type != 'doc') getProductModules($(this).val()); + if(type == 'doc') loadDocModule($(this).val()); + } + }) + $('#dataform .chosen').chosen(); // hide #parent chosen dropdown on root dropdown show diff --git a/module/upgrade/lang/version.php b/module/upgrade/lang/version.php index 10a01c522f..d88508c273 100644 --- a/module/upgrade/lang/version.php +++ b/module/upgrade/lang/version.php @@ -111,3 +111,4 @@ $lang->upgrade->fromVersions['11_5_1'] = '11.5.1'; $lang->upgrade->fromVersions['11_5_2'] = '11.5.2'; $lang->upgrade->fromVersions['11_6'] = '11.6'; $lang->upgrade->fromVersions['11_6_1'] = '11.6.1'; +$lang->upgrade->fromVersions['11_6_2'] = '11.6.2'; diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 53cb581dab..a13f1ce053 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -110,235 +110,338 @@ class upgradeModel extends model case '3_1': $this->saveLogs('Execute 3_1'); $this->execSQL($this->getUpgradeFile('3.1')); + $this->appendExec('3_1'); case '3_2': $this->saveLogs('Execute 3_2'); $this->execSQL($this->getUpgradeFile('3.2')); + $this->appendExec('3_2'); case '3_2_1': $this->saveLogs('Execute 3_2_1'); $this->execSQL($this->getUpgradeFile('3.2.1')); + $this->appendExec('3_2_1'); case '3_3': $this->saveLogs('Execute 3_3'); $this->execSQL($this->getUpgradeFile('3.3')); $this->updateTaskAssignedTo(); + $this->appendExec('3_3'); case '4_0_beta1': $this->saveLogs('Execute 4_0_beta1'); $this->execSQL($this->getUpgradeFile('4.0.beta1')); + $this->appendExec('4_0_beta1'); case '4_0_beta2': $this->saveLogs('Execute 4_0_beta2'); $this->execSQL($this->getUpgradeFile('4.0.beta2')); $this->updateProjectType(); $this->updateEstimatePriv(); + $this->appendExec('4_0_beta2'); case '4_0': $this->saveLogs('Execute 4_0'); $this->execSQL($this->getUpgradeFile('4.0')); + $this->appendExec('4_0'); case '4_0_1': $this->saveLogs('Execute 4_0_1'); $this->execSQL($this->getUpgradeFile('4.0.1')); $this->addPriv4_0_1(); + $this->appendExec('4_0_1'); case '4_1': $this->saveLogs('Execute 4_1'); $this->execSQL($this->getUpgradeFile('4.1')); $this->addPriv4_1(); $this->processTaskFinish(); $this->deleteCompany(); + $this->appendExec('4_1'); case '4_2_beta': $this->saveLogs('Execute 4_2_beta'); $this->execSQL($this->getUpgradeFile('4.2')); + $this->appendExec('4_2_beta'); case '4_3_beta': $this->saveLogs('Execute 4_3_beta'); $this->execSQL($this->getUpgradeFile('4.3')); - case '5_0_beta1': $this->saveLogs('Execute 5_0_beta1'); - case '5_0_beta2': $this->saveLogs('Execute 5_0_beta2'); - case '5_0': $this->saveLogs('Execute 5_0'); - case '5_1': $this->saveLogs('Execute 5_1'); - case '5_2': $this->saveLogs('Execute 5_2'); + $this->appendExec('4_3_beta'); + case '5_0_beta1': + $this->saveLogs('Execute 5_0_beta1'); + $this->appendExec('5_0_beta1'); + case '5_0_beta2': + $this->saveLogs('Execute 5_0_beta2'); + $this->appendExec('5_0_beta2'); + case '5_0': + $this->saveLogs('Execute 5_0'); + $this->appendExec('5_0'); + case '5_1': + $this->saveLogs('Execute 5_1'); + $this->appendExec('5_1'); + case '5_2': + $this->saveLogs('Execute 5_2'); + $this->appendExec('5_2'); case '5_2_1': $this->saveLogs('Execute 5_2_1'); $this->mergeProjectGoalAndDesc(); $this->execSQL($this->getUpgradeFile('5.2.1')); - case '5_3': $this->saveLogs('Execute 5_3'); + $this->appendExec('5_2_1'); + case '5_3': + $this->saveLogs('Execute 5_3'); + $this->appendExec('5_3'); case '6_0_beta1': $this->saveLogs('Execute 6_0_beta'); $this->execSQL($this->getUpgradeFile('6.0.beta1')); $this->toLowerTable(); $this->fixBugOSInfo(); $this->fixTaskFinishedBy(); + $this->appendExec('6_0_beta1'); case '6_0': $this->saveLogs('Execute 6_0'); $this->execSQL($this->getUpgradeFile('6.0')); $this->fixDataIndex(); + $this->appendExec('6_0'); case '6_1': $this->saveLogs('Execute 6_1'); $this->execSQL($this->getUpgradeFile('6.1')); - case '6_2': $this->saveLogs('Execute 6_2'); - case '6_3': $this->saveLogs('Execute 6_3'); - case '6_4': $this->saveLogs('Execute 6_4'); + $this->appendExec('6_1'); + case '6_2': + $this->saveLogs('Execute 6_2'); + $this->appendExec('6_2'); + case '6_3': + $this->saveLogs('Execute 6_3'); + $this->appendExec('6_3'); + case '6_4': + $this->saveLogs('Execute 6_4'); + $this->appendExec('6_4'); case '7_0': $this->saveLogs('Execute 7_0'); $this->execSQL($this->getUpgradeFile('7.0')); + $this->appendExec('7_0'); case '7_1': $this->saveLogs('Execute 7_1'); $this->execSQL($this->getUpgradeFile('7.1')); $this->initOrder(); - case '7_2': $this->saveLogs('Execute 7_2'); + $this->appendExec('7_1'); + case '7_2': + $this->saveLogs('Execute 7_2'); + $this->appendExec('7_2'); case '7_2_4': $this->saveLogs('Execute 7_2_4'); $this->execSQL($this->getUpgradeFile('7.2.4')); + $this->appendExec('7_2_4'); case '7_2_5': $this->saveLogs('Execute 7_2_5'); $this->adjustOrder7_3(); + $this->appendExec('7_2_5'); case '7_3': $this->saveLogs('Execute 7_3'); $this->execSQL($this->getUpgradeFile('7.3')); $this->adjustPriv7_4_beta(); + $this->appendExec('7_3'); case '7_4_beta': $this->saveLogs('Execute 7_4_beta'); $this->execSQL($this->getUpgradeFile('7.4.beta')); - case '8_0': $this->saveLogs('Execute 8_0'); + $this->appendExec('7_4_beta'); + case '8_0': + $this->saveLogs('Execute 8_0'); + $this->appendExec('8_0'); case '8_0_1': $this->saveLogs('Execute 8_0_1'); $this->execSQL($this->getUpgradeFile('8.0.1')); $this->addPriv8_1(); + $this->appendExec('8_0_1'); case '8_1': $this->saveLogs('Execute 8_1'); $this->execSQL($this->getUpgradeFile('8.1')); + $this->appendExec('8_1'); case '8_1_3': $this->saveLogs('Execute 8_1_3'); $this->execSQL($this->getUpgradeFile('8.1.3')); $this->addPriv8_2_beta(); $this->adjustConfigSectionAndKey(); - case '8_2_beta': $this->saveLogs('Execute 8_2_beta'); - case '8_2': $this->saveLogs('Execute 8_2'); + $this->appendExec('8_1_3'); + case '8_2_beta': + $this->saveLogs('Execute 8_2_beta'); + $this->appendExec('8_2_beta'); + case '8_2': + $this->saveLogs('Execute 8_2'); + $this->appendExec('8_2'); case '8_2_1': $this->saveLogs('Execute 8_2_1'); $this->execSQL($this->getUpgradeFile('8.2.1')); - case '8_2_2': $this->saveLogs('Execute 8_2_2'); - case '8_2_3': $this->saveLogs('Execute 8_2_3'); - case '8_2_4': $this->saveLogs('Execute 8_2_4'); - case '8_2_5': $this->saveLogs('Execute 8_2_5'); + $this->appendExec('8_2_1'); + case '8_2_2': + $this->saveLogs('Execute 8_2_2'); + $this->appendExec('8_2_2'); + case '8_2_3': + $this->saveLogs('Execute 8_2_3'); + $this->appendExec('8_2_3'); + case '8_2_4': + $this->saveLogs('Execute 8_2_4'); + $this->appendExec('8_2_4'); + case '8_2_5': + $this->saveLogs('Execute 8_2_5'); + $this->appendExec('8_2_5'); case '8_2_6': $this->saveLogs('Execute 8_2_6'); $this->execSQL($this->getUpgradeFile('8.2.6')); $this->adjustDocModule(); $this->moveDocContent(); $this->adjustPriv8_3(); - case '8_3': $this->saveLogs('Execute 8_3'); + $this->appendExec('8_2_6'); + case '8_3': + $this->saveLogs('Execute 8_3'); + $this->appendExec('8_3'); case '8_3_1': $this->saveLogs('Execute 8_3_1'); $this->execSQL($this->getUpgradeFile('8.3.1')); $this->renameMainLib(); $this->adjustPriv8_4(); - case '8_4': $this->saveLogs('Execute 8_4'); + $this->appendExec('8_3_1'); + case '8_4': + $this->saveLogs('Execute 8_4'); + $this->appendExec('8_4'); case '8_4_1': $this->saveLogs('Execute 8_4_1'); $this->execSQL($this->getUpgradeFile('8.4.1')); + $this->appendExec('8_4_1'); case '9_0_beta': $this->saveLogs('Execute 9_0_beta'); $this->execSQL($this->getUpgradeFile('9.0.beta')); $this->adjustPriv9_0(); + $this->appendExec('9_0_beta'); case '9_0': $this->saveLogs('Execute 9_0'); $this->fixProjectProductData(); + $this->appendExec('9_0'); case '9_0_1': $this->saveLogs('Execute 9_0_1'); $this->execSQL($this->getUpgradeFile('9.0.1')); $this->addBugDeadlineToCustomFields(); $this->adjustPriv9_0_1(); + $this->appendExec('9_0_1'); case '9_1': $this->saveLogs('Execute 9_1'); $this->execSQL($this->getUpgradeFile('9.1')); + $this->appendExec('9_1'); case '9_1_1': $this->saveLogs('Execute 9_1_1'); $this->execSQL($this->getUpgradeFile('9.1.1')); + $this->appendExec('9_1_1'); case '9_1_2': $this->saveLogs('Execute 9_1_2'); $this->execSQL($this->getUpgradeFile('9.1.2')); $this->processCustomMenus(); $this->adjustPriv9_2(); - case '9_2': $this->saveLogs('Execute 9_2'); - case '9_2_1': $this->saveLogs('Execute 9_2_1'); + $this->appendExec('9_1_2'); + case '9_2': + $this->saveLogs('Execute 9_2'); + $this->appendExec('9_2'); + case '9_2_1': + $this->saveLogs('Execute 9_2_1'); + $this->appendExec('9_2_1'); case '9_3_beta': $this->saveLogs('Execute 9_3_beta'); $this->execSQL($this->getUpgradeFile('9.3.beta')); + $this->appendExec('9_3_beta'); case '9_4': $this->saveLogs('Execute 9_4'); $this->execSQL($this->getUpgradeFile('9.4')); $this->adjustPriv9_4(); + $this->appendExec('9_4'); case '9_5': $this->saveLogs('Execute 9_5'); $this->execSQL($this->getUpgradeFile('9.5')); + $this->appendExec('9_5'); case '9_5_1': $this->saveLogs('Execute 9_5_1'); $this->execSQL($this->getUpgradeFile('9.5.1')); $this->initProjectStoryOrder(); + $this->appendExec('9_5_1'); case '9_6': $this->saveLogs('Execute 9_6'); $this->execSQL($this->getUpgradeFile('9.6')); $this->fixDatatableColsConfig(); + $this->appendExec('9_6'); case '9_6_1': $this->saveLogs('Execute 9_6_1'); $this->addLimitedGroup(); - case '9_6_2': $this->saveLogs('Execute 9_6_2'); + $this->appendExec('9_6_1'); + case '9_6_2': + $this->saveLogs('Execute 9_6_2'); + $this->appendExec('9_6_2'); case '9_6_3': $this->saveLogs('Execute 9_6_3'); $this->execSQL($this->getUpgradeFile('9.6.3')); $this->changeLimitedName(); $this->adjustPriv9_7(); $this->changeStoryWidth(); + $this->appendExec('9_6_3'); case '9_7': $this->saveLogs('Execute 9_7'); $this->execSQL($this->getUpgradeFile('9.7')); $this->changeTeamFields(); $this->moveData2Notify(); + $this->appendExec('9_7'); case '9_8': $this->saveLogs('Execute 9_8'); $this->fixTaskFinishedInfo(); + $this->appendExec('9_8'); case '9_8_1': $this->saveLogs('Execute 9_8_1'); $this->execSQL($this->getUpgradeFile('9.8.1')); $this->fixTaskAssignedTo(); $this->fixProjectClosedInfo(); $this->resetProductLine(); + $this->appendExec('9_8_1'); case '9_8_2': $this->saveLogs('Execute 9_8_2'); $this->execSQL($this->getUpgradeFile('9.8.2')); $this->addUniqueKeyToTeam(); + $this->appendExec('9_8_2'); case '9_8_3': $this->saveLogs('Execute 9_8_3'); $this->execSQL($this->getUpgradeFile('9.8.3')); $this->adjustPriv10_0_alpha(); + $this->appendExec('9_8_3'); case '10_0_alpha': $this->saveLogs('Execute 10_0_alpha'); $this->execSQL($this->getUpgradeFile('10.0.alpha')); $this->fixProjectStatisticBlock(); + $this->appendExec('10_0_alpha'); case '10_0_beta': $this->saveLogs('Execute 10_0_beta'); $this->execSQL($this->getUpgradeFile('10.0.beta')); + $this->appendExec('10_0_beta'); case '10_0': $this->saveLogs('Execute 10_0'); $this->execSQL($this->getUpgradeFile('10.0')); $this->fixStorySpecTitle(); $this->removeUnlinkPriv();//Remove unlink privilege for story, bug and testcase module. + $this->appendExec('10_0'); case '10_1': $this->saveLogs('Execute 10_1'); $xuanxuanSql = $this->app->getAppRoot() . 'db' . DS . 'xuanxuan.sql'; $this->execSQL($xuanxuanSql); $executeXuanxuan = true; - case '10_2': $this->saveLogs('Execute 10_2'); - case '10_3': $this->saveLogs('Execute 10_3'); + $this->appendExec('10_1'); + case '10_2': + $this->saveLogs('Execute 10_2'); + $this->appendExec('10_2'); + case '10_3': + $this->saveLogs('Execute 10_3'); + $this->appendExec('10_3'); case '10_3_1': $this->saveLogs('Execute 10_3_1'); $this->execSQL($this->getUpgradeFile('10.3.1')); $this->removeCustomMenu(); $this->initUserView(); + $this->appendExec('10_3_1'); case '10_4': $this->saveLogs('Execute 10_4'); $this->execSQL($this->getUpgradeFile('10.4')); $this->changeTaskParentValue(); - case '10_5': $this->saveLogs('Execute 10_5'); + $this->appendExec('10_4'); + case '10_5': + $this->saveLogs('Execute 10_5'); + $this->appendExec('10_5'); case '10_5_1': $this->saveLogs('Execute 10_5_1'); $this->execSQL($this->getUpgradeFile('10.5.1')); + $this->appendExec('10_5_1'); case '10_6': $this->saveLogs('Execute 10_6'); if(!$executeXuanxuan) @@ -349,7 +452,10 @@ class upgradeModel extends model $this->execSQL($xuanxuanSql); } $this->initXuanxuan(); - case '11_0': $this->saveLogs('Execute 11_0'); + $this->appendExec('10_6'); + case '11_0': + $this->saveLogs('Execute 11_0'); + $this->appendExec('11_0'); case '11_1': $this->saveLogs('Execute 11_1'); $this->execSQL($this->getUpgradeFile('11.1')); @@ -363,17 +469,21 @@ class upgradeModel extends model $this->dao->update(TABLE_CONFIG)->set('value')->eq('off')->where('`key`')->eq('isHttps')->andWhere('`section`')->eq('xuanxuan')->andWhere('`value`')->eq('0')->exec(); $this->dao->update(TABLE_CONFIG)->set('value')->eq('on')->where('`key`')->eq('isHttps')->andWhere('`section`')->eq('xuanxuan')->andWhere('`value`')->eq('1')->exec(); } + $this->appendExec('11_1'); case '11_2': $this->saveLogs('Execute 11_2'); $this->execSQL($this->getUpgradeFile('11.2')); $this->processDocLibAcl(); + $this->appendExec('11_2'); case '11_3': $this->saveLogs('Execute 11_3'); $this->execSQL($this->getUpgradeFile('11.3')); $this->addPriv11_4(); + $this->appendExec('11_3'); case '11_4': $this->saveLogs('Execute 11_4'); $this->execSQL($this->getUpgradeFile('11.4')); + $this->appendExec('11_4'); case '11_4_1': $this->saveLogs('Execute 11_4_1'); $this->execSQL($this->getUpgradeFile('11.4.1')); @@ -389,20 +499,30 @@ class upgradeModel extends model } $this->updateXX_11_5(); } + $this->appendExec('11_4_1'); case '11_5': $this->saveLogs('Execute 11_5'); $this->execSQL($this->getUpgradeFile('11.5')); + $this->appendExec('11_5'); case '11_5_1': + $this->saveLogs('Execute 11_5_1'); + $this->appendExec('11_5_1'); case '11_5_2': $this->saveLogs('Execute 11_5_2'); $this->execSQL($this->getUpgradeFile('11.5.2')); + $this->appendExec('11_5_2'); case '11_6': $this->saveLogs('Execute 11_6'); $this->execSQL($this->getUpgradeFile('11.6')); + $this->appendExec('11_6'); case '11_6_1': $this->saveLogs('Execute 11_6_1'); $this->adjustWebhookType(); $this->adjustPriv11_6_2(); + $this->appendExec('11_6_1'); + case '11_6_2': + $this->saveLogs('Execute 11_6_2'); + $this->appendExec('11_6_2'); } $this->deletePatch(); @@ -547,7 +667,9 @@ class upgradeModel extends model case '11_5' : $confirmContent .= file_get_contents($this->getUpgradeFile('11.5')); case '11_5_1' : case '11_5_2' : $confirmContent .= file_get_contents($this->getUpgradeFile('11.5.2')); - case '11_6' : $confirmContent .= file_get_contents($this->getUpgradeFile('11.6')); + case '11_6' : $confirmContent .= file_get_contents($this->getUpgradeFile('11.6')); + case '11_6_1' : + case '11_6_2' : } return str_replace('zt_', $this->config->db->prefix, $confirmContent); } @@ -3199,6 +3321,45 @@ class upgradeModel extends model return true; } + /** + * Adjust priv for 11.6.4. + * + * @access public + * @return void + */ + public function adjustPriv11_6_4() + { + $this->saveLogs('Run Method ' . __FUNCTION__); + + $this->dao->update(TABLE_GROUPPRIV)->set('module')->eq('caselib')->set('method')->eq('browse')->where('module')->eq('testsuite')->andWhere('method')->eq('library')->exec(); + $this->dao->update(TABLE_GROUPPRIV)->set('module')->eq('caselib')->set('method')->eq('create')->where('module')->eq('testsuite')->andWhere('method')->eq('createLib')->exec(); + $this->dao->update(TABLE_GROUPPRIV)->set('module')->eq('caselib')->set('method')->eq('view')->where('module')->eq('testsuite')->andWhere('method')->eq('libView')->exec(); + $this->dao->update(TABLE_GROUPPRIV)->set('module')->eq('caselib')->where('module')->eq('testsuite')->andWhere('method')->in('exportTemplet,import,showImport,batchCreateCase,createCase')->exec(); + + $groups = $this->dao->select('*')->from(TABLE_GROUPPRIV)->where('module')->eq('testsuite')->andWhere('method')->eq('edit')->fetchPairs('group', 'group'); + foreach($groups as $groupID) + { + $groupPriv = new stdclass(); + $groupPriv->group = $groupID; + $groupPriv->module = 'caselib'; + $groupPriv->method = 'edit'; + $this->dao->replace(TABLE_GROUPPRIV)->data($groupPriv)->exec(); + $this->saveLogs($this->dao->get()); + } + + $groups = $this->dao->select('*')->from(TABLE_GROUPPRIV)->where('module')->eq('testsuite')->andWhere('method')->eq('delete')->fetchPairs('group', 'group'); + foreach($groups as $groupID) + { + $groupPriv = new stdclass(); + $groupPriv->group = $groupID; + $groupPriv->module = 'caselib'; + $groupPriv->method = 'delete'; + $this->dao->replace(TABLE_GROUPPRIV)->data($groupPriv)->exec(); + $this->saveLogs($this->dao->get()); + } + return true; + } + /** * Save Logs. * @@ -3216,4 +3377,15 @@ class upgradeModel extends model if(empty($fh)) $fh = fopen($logFile, 'a'); fwrite($fh, $log); } + + /** + * Append execute for pro and biz. + * + * @param string $fromVersion + * @access public + * @return void + */ + public function appendExec($zentaoVersion) + { + } } diff --git a/module/user/control.php b/module/user/control.php index 0ae554abab..5fea443c22 100644 --- a/module/user/control.php +++ b/module/user/control.php @@ -846,8 +846,25 @@ class user extends control $this->view->refererBeforeDeny = $refererBeforeDeny; // The referer of the denied page. $this->app->loadLang($module); $this->app->loadLang('my'); - $this->display(); - exit; + + /* Check deny type. */ + $rights = $this->app->user->rights['rights']; + $acls = $this->app->user->rights['acls']; + $module = strtolower($module); + $method = strtolower($method); + + $denyType = 'nopriv'; + if(isset($rights[$module][$method])) + { + $menu = isset($this->lang->menugroup->$module) ? $this->lang->menugroup->$module : $module; + $menu = strtolower($menu); + + if(!isset($acls['views'][$menu])) $denyType = 'noview'; + $this->view->menu = $menu; + } + $this->view->denyType = $denyType; + + die($this->display()); } /** @@ -1052,7 +1069,9 @@ class user extends control */ public function ajaxDeleteTemplate($templateID) { - $this->dao->delete()->from(TABLE_USERTPL)->where('id')->eq($templateID)->andWhere('account')->eq($this->app->user->account)->exec(); + $this->dao->delete()->from(TABLE_USERTPL)->where('id')->eq($templateID) + ->beginIF(!$this->app->user->admin)->andWhere('account')->eq($this->app->user->account)->fi() + ->exec(); die(); } } diff --git a/module/user/lang/zh-cn.php b/module/user/lang/zh-cn.php index de145c76ea..626d90d757 100644 --- a/module/user/lang/zh-cn.php +++ b/module/user/lang/zh-cn.php @@ -109,6 +109,7 @@ $lang->user->case2Him = '给%s的用例'; $lang->user->caseByHim = '%s建的用例'; $lang->user->errorDeny = "抱歉,您无权访问『%s』模块的『%s』功能。请联系管理员获取权限。点击后退返回上页。"; +$lang->user->errorView = "抱歉,您无权访问『%s』视图。请联系管理员获取权限。点击后退返回上页。"; $lang->user->loginFailed = "登录失败,请检查您的用户名或密码是否填写正确。"; $lang->user->lockWarning = "您还有%s次尝试机会。"; $lang->user->loginLocked = "密码尝试次数太多,请联系管理员解锁,或%s分钟后重试。"; diff --git a/module/user/model.php b/module/user/model.php index 6c7532e5cc..8672b54beb 100644 --- a/module/user/model.php +++ b/module/user/model.php @@ -277,7 +277,7 @@ class userModel extends model if(!validater::checkAccount($users->account[$i])) die(js::error(sprintf($this->lang->user->error->account, $i + 1))); if($users->realname[$i] == '') die(js::error(sprintf($this->lang->user->error->realname, $i + 1))); if($users->email[$i] and !validater::checkEmail($users->email[$i])) die(js::error(sprintf($this->lang->user->error->mail, $i + 1))); - $users->password[$i] = (isset($prev['password']) and $users->ditto[$i] == 'on' and empty($users->password[$i])) ? $prev['password'] : $users->password[$i]; + $users->password[$i] = (isset($prev['password']) and $users->ditto[$i] == 'on' and !$this->post->password[$i]) ? $prev['password'] : $this->post->password[$i]; if(!validater::checkReg($users->password[$i], '|(.){6,}|')) die(js::error(sprintf($this->lang->user->error->password, $i + 1))); $role = $users->role[$i] == 'ditto' ? (isset($prev['role']) ? $prev['role'] : '') : $users->role[$i]; @@ -328,7 +328,7 @@ class userModel extends model $prev['dept'] = $data[$i]->dept; $prev['role'] = $data[$i]->role; $prev['group'] = $data[$i]->group; - $prev['password'] = $users->password[$i]; + $prev['password'] = $this->post->password[$i]; } } diff --git a/module/user/view/ajaxprinttemplates.html.php b/module/user/view/ajaxprinttemplates.html.php index c779460e82..6680b5418e 100644 --- a/module/user/view/ajaxprinttemplates.html.php +++ b/module/user/view/ajaxprinttemplates.html.php @@ -6,7 +6,7 @@ foreach($templates as $key => $template) echo ""; if($template->public) echo "{$lang->public} "; echo $template->title . ""; - if(empty($template->public) or $template->account == $app->user->account)echo ""; + if(empty($template->public) or $template->account == $app->user->account or $app->user->admin) echo ""; echo ""; echo ''; } @@ -35,7 +35,7 @@ foreach($templates as $key => $template) echo ""; if($template->public) echo "{$lang->public} "; echo $template->title . ""; - if(empty($template->public) or $template->account == $app->user->account)echo ""; + if(empty($template->public) or $template->account == $app->user->account or $app->user->admin) echo ""; echo ""; echo ''; } diff --git a/module/user/view/deny.html.php b/module/user/view/deny.html.php index 9154c56a6b..188833e01f 100644 --- a/module/user/view/deny.html.php +++ b/module/user/view/deny.html.php @@ -18,21 +18,28 @@ include '../../common/view/header.lite.html.php';
    $module->common) ? $lang->$module->common: $module; - $methodName = isset($lang->$module->$method) ? $lang->$module->$method: $method; - - /* find method name if method is lowercase letter. */ - if(!isset($lang->$module->$method)) + if($denyType == 'nopriv') { - $tmpLang = array(); - foreach($lang->$module as $key => $value) + $moduleName = isset($lang->$module->common) ? $lang->$module->common : $module; + $methodName = isset($lang->$module->$method) ? $lang->$module->$method : $method; + + /* find method name if method is lowercase letter. */ + if(!isset($lang->$module->$method)) { - $tmpLang[strtolower($key)] = $value; - } - $methodName = isset($tmpLang[$method]) ? $tmpLang[$method] : $method; + $tmpLang = array(); + foreach($lang->$module as $key => $value) $tmpLang[strtolower($key)] = $value; + $methodName = isset($tmpLang[$method]) ? $tmpLang[$method] : $method; + } + + printf($lang->user->errorDeny, $moduleName, $methodName); } - printf($lang->user->errorDeny, $moduleName, $methodName); + if($denyType == 'noview') + { + $menuName = $menu; + if(isset($lang->menu->$menu))list($menuName) = explode('|', $lang->menu->$menu); + printf($lang->user->errorView, $menuName); + } ?>
    diff --git a/www/js/my.full.js b/www/js/my.full.js index 048d8d6634..3896a95619 100644 --- a/www/js/my.full.js +++ b/www/js/my.full.js @@ -322,6 +322,7 @@ function ajaxDelete(url, replaceID, notice) if(typeof sortTable == 'function') sortTable(); $('#' + replaceID).find('[data-toggle=modal], a.iframe').modalTrigger(); if($('#' + replaceID).find('table.datatable').length) $('#' + replaceID).find('table.datatable').datatable(); + $('.table-footer [data-ride=pager]').pager(); }); } else if(data.result == 'fail' && typeof(data.message) == 'string')
    - $actionLabel):?> $moduleName->menus) and $action == 'browse') continue;;?> diff --git a/module/misc/lang/de.php b/module/misc/lang/de.php index 090ce3dab2..20ab27477b 100644 --- a/module/misc/lang/de.php +++ b/module/misc/lang/de.php @@ -46,7 +46,7 @@ $lang->misc->zentao->cowin['reportbug'] = "Bug melden "; $lang->misc->zentao->cowin['feedback'] = "Feedback"; $lang->misc->zentao->cowin['recommend'] = "More"; -$lang->misc->zentao->service['zentaotrain']= 'Zentao Train'; +$lang->misc->zentao->service['zentaotrain']= 'Zentao Training'; $lang->misc->zentao->service['idc'] = 'Zentao Cloud'; $lang->misc->zentao->service['custom'] = 'Zentao Custom'; $lang->misc->zentao->service['servicemore']= 'Mehr'; @@ -82,6 +82,7 @@ $lang->misc->feature = new stdclass(); $lang->misc->feature->lastest = 'Letzte Version'; $lang->misc->feature->detailed = 'Details'; +$lang->misc->releaseDate['11.6.3'] = '2019-09-24'; $lang->misc->releaseDate['11.6.2'] = '2019-09-06'; $lang->misc->releaseDate['11.6.1'] = '2019-08-23'; $lang->misc->releaseDate['11.6.stable'] = '2019-07-12'; @@ -114,16 +115,15 @@ $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['11.6.2'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); - -$lang->misc->feature->all['11.6.1'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); +$lang->misc->feature->all['11.6.3'][] = array('title'=>'Fix bug.', 'desc' => ''); +$lang->misc->feature->all['11.6.2'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); +$lang->misc->feature->all['11.6.1'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'Improving the International Edition Interface', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'Add translate function', 'desc' => ''); -$lang->misc->feature->all['11.5.2'][] = array('title'=>'Increase the security of ZenTao and increase the login password for weak password check', 'desc' => ''); - -$lang->misc->feature->all['11.5.1'][] = array('title'=>'Add a third-party authentication and fix bugs.', 'desc' => ''); +$lang->misc->feature->all['11.5.2'][] = array('title'=>'Increase the security of ZenTao and increase the login password for weak password check', 'desc' => ''); +$lang->misc->feature->all['11.5.1'][] = array('title'=>'Add a third-party authentication and fix bugs.', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'Added filters to Dynamics', 'desc' => ''); diff --git a/module/misc/lang/en.php b/module/misc/lang/en.php index 11b87c6702..cef9eadc8a 100644 --- a/module/misc/lang/en.php +++ b/module/misc/lang/en.php @@ -46,7 +46,7 @@ $lang->misc->zentao->cowin['reportbug'] = "Report Bug "; $lang->misc->zentao->cowin['feedback'] = "Feedback"; $lang->misc->zentao->cowin['recommend'] = "More"; -$lang->misc->zentao->service['zentaotrain']= 'Zentao Train'; +$lang->misc->zentao->service['zentaotrain']= 'Zentao Training'; $lang->misc->zentao->service['idc'] = 'Zentao Cloud'; $lang->misc->zentao->service['custom'] = 'Zentao Custom'; $lang->misc->zentao->service['servicemore']= 'More'; @@ -82,6 +82,7 @@ $lang->misc->feature = new stdclass(); $lang->misc->feature->lastest = 'Latest Version'; $lang->misc->feature->detailed = 'Detail'; +$lang->misc->releaseDate['11.6.3'] = '2019-09-24'; $lang->misc->releaseDate['11.6.2'] = '2019-09-06'; $lang->misc->releaseDate['11.6.1'] = '2019-08-23'; $lang->misc->releaseDate['11.6.stable'] = '2019-07-12'; @@ -114,16 +115,15 @@ $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['11.6.2'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); - -$lang->misc->feature->all['11.6.1'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); +$lang->misc->feature->all['11.6.3'][] = array('title'=>'Fix bug.', 'desc' => ''); +$lang->misc->feature->all['11.6.2'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); +$lang->misc->feature->all['11.6.1'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'Improving the International Edition Interface', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'Add translate function', 'desc' => ''); -$lang->misc->feature->all['11.5.2'][] = array('title'=>'Increase the security of ZenTao and increase the login password for weak password check', 'desc' => ''); - -$lang->misc->feature->all['11.5.1'][] = array('title'=>'Add a third-party authentication and fix bugs.', 'desc' => ''); +$lang->misc->feature->all['11.5.2'][] = array('title'=>'Increase the security of ZenTao and increase the login password for weak password check', 'desc' => ''); +$lang->misc->feature->all['11.5.1'][] = array('title'=>'Add a third-party authentication and fix bugs.', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'Added filters to Dynamics', 'desc' => ''); diff --git a/module/misc/lang/zh-cn.php b/module/misc/lang/zh-cn.php index 1e7b739d15..97d1bbf48b 100644 --- a/module/misc/lang/zh-cn.php +++ b/module/misc/lang/zh-cn.php @@ -82,6 +82,7 @@ $lang->misc->feature = new stdclass(); $lang->misc->feature->lastest = '最新版本'; $lang->misc->feature->detailed = '详情'; +$lang->misc->releaseDate['11.6.3'] = '2019-09-24'; $lang->misc->releaseDate['11.6.2'] = '2019-09-06'; $lang->misc->releaseDate['11.6.1'] = '2019-08-23'; $lang->misc->releaseDate['11.6.stable'] = '2019-07-12'; @@ -114,16 +115,15 @@ $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['11.6.2'][] = array('title'=>'完善细节,修复Bug', 'desc' => ''); - -$lang->misc->feature->all['11.6.1'][] = array('title'=>'完善细节,修复Bug', 'desc' => ''); +$lang->misc->feature->all['11.6.3'][] = array('title'=>'修复Bug', 'desc' => ''); +$lang->misc->feature->all['11.6.2'][] = array('title'=>'完善细节,修复Bug', 'desc' => ''); +$lang->misc->feature->all['11.6.1'][] = array('title'=>'完善细节,修复Bug', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'改善国际版界面', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'添加翻译功能', 'desc' => ''); -$lang->misc->feature->all['11.5.2'][] = array('title'=>'增加禅道安全性,增加登录禅道弱口令检查', 'desc' => ''); - -$lang->misc->feature->all['11.5.1'][] = array('title'=>'新增第三方应用免密登录禅道,修复Bug', 'desc' => ''); +$lang->misc->feature->all['11.5.2'][] = array('title'=>'增加禅道安全性,增加登录禅道弱口令检查', 'desc' => ''); +$lang->misc->feature->all['11.5.1'][] = array('title'=>'新增第三方应用免密登录禅道,修复Bug', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'完善细节,修复Bug', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'新增动态过滤机制', 'desc' => ''); diff --git a/module/misc/lang/zh-tw.php b/module/misc/lang/zh-tw.php index b13488a8e6..b4fa438a82 100644 --- a/module/misc/lang/zh-tw.php +++ b/module/misc/lang/zh-tw.php @@ -82,6 +82,7 @@ $lang->misc->feature = new stdclass(); $lang->misc->feature->lastest = '最新版本'; $lang->misc->feature->detailed = '詳情'; +$lang->misc->releaseDate['11.6.3'] = '2019-09-24'; $lang->misc->releaseDate['11.6.2'] = '2019-09-06'; $lang->misc->releaseDate['11.6.1'] = '2019-08-23'; $lang->misc->releaseDate['11.6.stable'] = '2019-07-12'; @@ -114,16 +115,15 @@ $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['11.6.2'][] = array('title'=>'完善細節,修復Bug', 'desc' => ''); - -$lang->misc->feature->all['11.6.1'][] = array('title'=>'完善細節,修復Bug', 'desc' => ''); +$lang->misc->feature->all['11.6.3'][] = array('title'=>'修復Bug', 'desc' => ''); +$lang->misc->feature->all['11.6.2'][] = array('title'=>'完善細節,修復Bug', 'desc' => ''); +$lang->misc->feature->all['11.6.1'][] = array('title'=>'完善細節,修復Bug', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'改善國際版界面', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'添加翻譯功能', 'desc' => ''); -$lang->misc->feature->all['11.5.2'][] = array('title'=>'增加禪道安全性,增加登錄禪道弱口令檢查', 'desc' => ''); - -$lang->misc->feature->all['11.5.1'][] = array('title'=>'新增第三方應用免密登錄禪道,修復Bug', 'desc' => ''); +$lang->misc->feature->all['11.5.2'][] = array('title'=>'增加禪道安全性,增加登錄禪道弱口令檢查', 'desc' => ''); +$lang->misc->feature->all['11.5.1'][] = array('title'=>'新增第三方應用免密登錄禪道,修復Bug', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'完善細節,修復Bug', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'新增動態過濾機制', 'desc' => ''); diff --git a/module/product/css/roadmap.css b/module/product/css/roadmap.css index fbea14a1a2..c2c833d057 100644 --- a/module/product/css/roadmap.css +++ b/module/product/css/roadmap.css @@ -7,3 +7,4 @@ .release-line > li > a:before {border-color: #00da88;} .release-line > li:nth-child(even) > a {padding-bottom: 38px;} .release-link-line {pointer-events: none; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+YAAADiCAMAAAD0xy2eAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABOUExURUdwTOXl5eXl5ebm5ufn5+rq6u7u7unp6eXl5f///+Xl5eXl5efn5+Xl5eXl5eXl5ebm5uXl5eXl5ebm5ubm5ubm5uXl5ebm5uXl5eXl5Uh6G7kAAAAZdFJOUwDuuVQtFQwg1gXH+Dpzoq1CmIV7aEuOX+MTWsN0AAAGrUlEQVQYGe3ACbaYOHRF0SuQkETfw5v/RPNdLpMZZKWez5buVQBcu8zmJACO1WOwcAqAZ7kx66MAeNa+ZncnAI51t1kvAK7F/hEAAAAAAACA/xP93gmAZ6tZOKsAODb2ZuUSAM/aYtZEAXCsTsFs7gTAse4eigD4lqMAAAAAAMD/D09/CYBrvVl/CYBjdXrN+lUAHOumYNavAuBYNwWzTQA866ZwCYBvnQAAAAD8V7RHEgDParHhzgLg2NqYDXMUAMfiPJg1qwA4lu/BbBMAz7rjvQTAtyoAAOBG1wmAb1O4swB4NptZcwmAY3EbzN4pCYBfaSpmwywAnl2NNQLgW44CAABuZAHwLQ1lSgLg2BjMhuUSAL9q25jZu2cB8CsfxcweAfBsnYcsAL51AgAAboxL2wmAZ5vZMF9VANxKZ29mYRsFwK98FDN7swA4Fvc3VAHwLQsAALgxlSMKgGeNmZU9CoBb9dqCmb33KABu1XV7zSwJgGfjPQsAALixHlEAXJvN3u3qBMCtdXvNbGjOLABuxaO3H1EAHEvPUgQAANwYlycLgGe7mZX76gTAq3wuwcyGZsoC4FUd997MLgHwLLVbJwB/j7UTANeyDf2+dgLgVuwHMxv6fa0C4FR37b39WATAse66+0kA/h45CYBvjZX5yQLg1xLsR1imsQqATzVOy2s/kgA4lp+tEYC/R9rOsRMAx1r7UebjygLgU57mMtgvswC4VeNzN++hT0oC4FDVZ7ehLPe55ioAPu3BfhseAXCqi+2xNa9d+rTnFTsB8KLqH7Xq09iPoTTzngXApXNrSrBfRn1i7ATAlS5e5570KWbD2y/b8XQC4NJSgv2W9RljrgLgR5fH9tznqk8ws1D6ZTs6AXCp6d/B/lH1Oc52jakKgBcpj+156FPtt1B6AXCp27elKcHMgj5d6Zdtn55rzALgRcpRn2x/vPrUMaYqAP9hVZ8a1/Y87rnpG32y/Qhv38y7ALiU+xIG+6Xok5r5Ps52jbkTABe6PF5Pq89ofxR9upg7AXAiXc+0b0tT3kaf0cyGtzTLdgqAS2N5B/tHo09ctn162jGmKgAedDmu7Xnp09ofjT5dTFUAnEjtedzz0pd30+cxs/D2zXy3AuBSW4L9tumzzvdxtmvMnQC40OXxes5Vn8n+2PTpkgC4EZ9p35amvMOuz2QWSr/M9zQKgEtTsH/t+qzH2a4xVQFwoaY4Xs+0r/rs9lsopwC4tO7b0pcwmJ367KVftn16rjEJgBddTvrM9sejT46pCoATKY7tedxz06/6zGYWSr9sexQAl+4S7LdLn3Z6rpirAHiR4tieR9ansX+E0o8C4NJzz015BzMb9bmX7TjbMXcC4EaXx06fYv8aogC4NLbnvi19CZb06Usz38dzjVkA/Kj6X8H+SPpUAfAjxfWZ9rnp36pPCKWZ7+m5ogC4VAf7o+rTCYAjKa7PdM9Nr0+1UJr5ntoxC4BLebB/BQFwKsX1me6lb/RJ89HGKgB+jfZLae5zFACX0nk3xX5pBMCvGttjnvRJWQB8myw0e5sFwK0p2C+h2aMAOJXbvQlm1gqAZ/k6sgD8PfpmXzsB8KuzX8rWZgFwKrV3P9iPIgB+1fFoQi8AziV9qgA4d4bljALg2GY/3vnJAuBVfubXfryrAPgVzyVYFgDfogD8Per8JAHwbDWzfh+rAHiVpmYws7CcWQC8qtddzOwWAM/yuawCAABu9HPbCYBj2cyG5swC4FacevtR9lEA3ErPEswWAfCsrncrAADgxraPAuBZN5i921oFwK31fs0szG0nAG7FvZhZLwCe5ak/BAAA3FizAPj2WjmyAPjVzcHMyhEFwK16zcHMyiQAftVrCzYLgGt1jQIAAG7MRxYAz7KZ9VMWALfqNQcz66ckAG7VdhnM7BYAx2q7DKcA+NZ1AgAAXtSyjQLg2WVm7x4FwK94v2ZWpiwAfo1bMLNJAByr1zyMAuBbJwAA4EYsUxYAzw4za55OANyq1zKYDctVBcCt7mnMLIwC4Fg+ytAJgG9ZAADAjWuPAuBaY9afSQD8GrdgNixXFQC3atuYWbirAPiVpmJFAHyLqwAAgBtjEgDX6jssVxUAv9IymIU7CoBfaSpm1p9JAPwat2A2C4BntW1WAQAAAPiPyGVKAuDZYTYslwD4VdvGzN49C4Bf6Shm1kQBcGydhyEJgGvdJQAA4EZOAuDbMiyrAHi2mNl7ZAHwKx2vmTVXFQC/1mUw6wXAszSVQwCcqwIAAG4cSQBcW4ckAK7lU/8D/a9fSZZlwxkAAAAASUVORK5CYII=); height: 196px; position: absolute; left: 10px; right: 100px; top: 97px; background-size: 100% 100%; background-repeat: no-repeat;} +.release-line li .title{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap} diff --git a/module/product/view/roadmap.html.php b/module/product/view/roadmap.html.php index e1f6e4be90..e3e5892c6d 100644 --- a/module/product/view/roadmap.html.php +++ b/module/product/view/roadmap.html.php @@ -66,7 +66,8 @@
    - build) ? $roadmap->name : $roadmap->title;?> + build) ? $roadmap->name : $roadmap->title;?> + build) ? $roadmap->date : ($roadmap->end == '2030-01-01' ? $lang->productplan->future : $roadmap->begin . '~' . $roadmap->end);?>
    diff --git a/module/project/control.php b/module/project/control.php index e31ef6e1fd..384ae24f71 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -1101,7 +1101,11 @@ class project extends control $this->executeHooks($projectID); - $planID = reset($_POST['plans']); + $planID = ''; + foreach($_POST['plans'] as $planID) + { + if(!empty($planID)) break; + } if(!empty($planID)) { $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('create', "projectID=$projectID©ProjectID=&planID=$planID&confirm=no"))); diff --git a/module/project/model.php b/module/project/model.php index 1acb35bddf..f3981e23ff 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -1566,6 +1566,8 @@ class projectModel extends model $lastOrder = reset($linkedStories); foreach($stories as $key => $storyID) { + $status = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch('status'); + if($status == 'draft') continue; if(isset($linkedStories[$storyID])) continue; $productID = (int)$products[$storyID]; @@ -1599,6 +1601,7 @@ class projectModel extends model { foreach($plans as $planID => $productID) { + if(empty($planID)) continue; $planStory = $this->loadModel('story')->getPlanStories($planID); if(!empty($planStory)) { @@ -1716,6 +1719,7 @@ class projectModel extends model ->where('t1.account')->eq($account) ->andWhere('t1.root')->ne($currentProject) ->andWhere('t1.type')->eq('project') + ->andWhere('t2.deleted')->eq('0') ->groupBy('t1.root') ->orderBy('t1.root DESC') ->fetchPairs(); diff --git a/module/project/view/story.html.php b/module/project/view/story.html.php index f3c380952d..c606778c18 100644 --- a/module/project/view/story.html.php +++ b/module/project/view/story.html.php @@ -90,7 +90,7 @@
    -
    '>
    +
    '>

    diff --git a/module/search/view/buildform.html.php b/module/search/view/buildform.html.php index c75c71fa43..4c6568b406 100644 --- a/module/search/view/buildform.html.php +++ b/module/search/view/buildform.html.php @@ -339,31 +339,31 @@ $(function() var setDateField = function(query, fieldNO) { var $period = $('#selectPeriod'); - if(!$period.length) - { - $period = $("

    ").appendTo('body'); - $period.find('li > a').click(function(event) - { - var target = $(query).closest('form').find('#' + $period.data('target')); - if(target.length) - { - if(target.next('input[type=hidden]').length) - { - target.next('input[type=hidden]').val($(this).attr('href').replace('#', '$')); - target.attr('placeholder', $(this).attr('href').replace('#', '$')); - } - else - { - target.val($(this).attr('href').replace('#', '$')); - } + if($period.length) $period.remove(); - $(query).closest('form').find('#operator' + $period.data('fieldNO')).val('between'); - $period.hide(); + $period = $("").appendTo('body'); + $period.find('li > a').click(function(event) + { + var target = $(query).closest('form').find('#' + $period.data('target')); + if(target.length) + { + if(target.next('input[type=hidden]').length) + { + target.next('input[type=hidden]').val($(this).attr('href').replace('#', '$')); + target.attr('placeholder', $(this).attr('href').replace('#', '$')); } - event.stopPropagation(); - return false; - }); - } + else + { + target.val($(this).attr('href').replace('#', '$')); + } + + $(query).closest('form').find('#operator' + $period.data('fieldNO')).val('between'); + $period.hide(); + } + event.stopPropagation(); + return false; + }); + $(query).datetimepicker('remove').datepicker(dtOptions).on('show', function(e) { var $e = $(e.target); @@ -412,6 +412,7 @@ $(function() if(typeof(params[fieldName]['class']) != undefined && params[fieldName]['class'] == 'date') { + console.log($searchForm.find("#value" + fieldNO)); setDateField($searchForm.find("#value" + fieldNO), fieldNO); $searchForm.find("#value" + fieldNO).addClass('date'); // Shortcut the width of the datepicker to make sure align with others. var maxNO = 2 * groupItems; diff --git a/module/story/model.php b/module/story/model.php index 6935c586c5..574794dcf1 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -869,7 +869,6 @@ class storyModel extends model ->setDefault('assignedDate', $now) ->removeIF($this->post->closedReason != 'duplicate', 'duplicateStory') ->removeIF($this->post->closedReason != 'subdivided', 'childStories') - ->setIF($this->post->closedReason != 'done', 'plan', 0) ->remove('comment') ->get(); @@ -1709,7 +1708,8 @@ class storyModel extends model { if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getProjectStories(); - if($type == 'bySearch') + $type = strtolower($type); + if($type == 'bysearch') { $queryID = (int)$param; $products = $this->loadModel('project')->getProducts($projectID); diff --git a/module/task/control.php b/module/task/control.php index 603b8fe9e2..c6c47eabff 100644 --- a/module/task/control.php +++ b/module/task/control.php @@ -90,7 +90,7 @@ class task extends control if(!empty($_POST)) { $response['result'] = 'success'; - $response['message'] = ''; + $response['message'] = $this->lang->saveSuccess; if($this->post->project) $projectID = $this->post->project; $tasksID = $this->task->create($projectID); @@ -173,11 +173,18 @@ class task extends control } $users = $this->loadModel('user')->getPairs('noclosed|nodeleted'); - $moduleIdList = $this->tree->getAllChildID($moduleID); - $stories = $this->story->getProjectStoryPairs($projectID, 0, 0, $moduleIdList); $members = $this->project->getTeamMemberPairs($projectID, 'nodeleted'); $moduleOptionMenu = $this->tree->getTaskOptionMenu($projectID); + /* Fix bug #2737. When moduleID is not story module. */ + $moduleIdList = array(); + if($moduleID) + { + $moduleID = $this->tree->getStoryModule($moduleID); + $moduleIdList = $this->tree->getAllChildID($moduleID); + } + $stories = $this->story->getProjectStoryPairs($projectID, 0, 0, $moduleIdList); + $title = $project->name . $this->lang->colon . $this->lang->task->create; $position[] = html::a($taskLink, $project->name); $position[] = $this->lang->task->common; @@ -1168,7 +1175,11 @@ class task extends control else { $this->task->delete(TABLE_TASK, $taskID); - if($task->parent > 0) $this->task->updateParentStatus($task->id); + if($task->parent > 0) + { + $this->task->updateParentStatus($task->id); + $this->loadModel('action')->create('task', $task->parent, 'deleteChildrenTask', '', $taskID); + } if($task->fromBug != 0) $this->dao->update(TABLE_BUG)->set('toTask')->eq(0)->where('id')->eq($task->fromBug)->exec(); if($task->story) $this->loadModel('story')->setStage($task->story); diff --git a/module/task/lang/en.php b/module/task/lang/en.php index 6d175a2b54..5e447ebbfd 100644 --- a/module/task/lang/en.php +++ b/module/task/lang/en.php @@ -199,6 +199,7 @@ $lang->task->cannotDeleteParent = 'Cannot delete parent task'; $lang->task->error = new stdclass(); $lang->task->error->consumedNumber = '"Current Cost" must be numbers.'; $lang->task->error->estimateNumber = '"Estimates" must be numbers.'; +$lang->task->error->recordMinus = 'Work hours should not be negative number.'; $lang->task->error->consumedSmall = '"Hours Cost" must be > the last number.'; $lang->task->error->consumedThisTime = 'Please enter "Hours Cost"'; $lang->task->error->left = 'Please enter "Hours Left"'; @@ -207,6 +208,7 @@ $lang->task->error->skipClose = 'Task: %s is not "Finished” or “Cance $lang->task->error->consumed = 'Task: %s hour must be < 0. Ignore changes to this task.'; $lang->task->error->assignedTo = 'Multi-user task in the current status cannot be assigned to a member who is not in the task team.'; $lang->task->error->consumedEmpty = '"Current Cost" should not be empty.'; +$lang->task->error->deadlineSmall = '"Deadline" must be greater than "StartDate".'; /* Report. */ $lang->task->report = new stdclass(); diff --git a/module/task/lang/zh-cn.php b/module/task/lang/zh-cn.php index 5b9fd3ca27..d244282fe3 100644 --- a/module/task/lang/zh-cn.php +++ b/module/task/lang/zh-cn.php @@ -199,6 +199,7 @@ $lang->task->cannotDeleteParent = '不能删除父任务。'; $lang->task->error = new stdclass(); $lang->task->error->consumedNumber = '"本次消耗"必须为数字'; $lang->task->error->estimateNumber = '"预计剩余"必须为数字'; +$lang->task->error->recordMinus = '工时不能为负数'; $lang->task->error->consumedSmall = '"已经消耗"必须大于之前消耗'; $lang->task->error->consumedThisTime = '请填写"工时"'; $lang->task->error->left = '请填写"剩余"'; @@ -207,6 +208,7 @@ $lang->task->error->skipClose = '任务:%s 不是“已完成”或“ $lang->task->error->consumed = '任务:%s工时不能小于0,忽略该任务工时的改动'; $lang->task->error->assignedTo = '当前状态的多人任务不能指派给任务团队外的成员。'; $lang->task->error->consumedEmpty = '"本次消耗"不能为空'; +$lang->task->error->deadlineSmall = '"截止日期"必须大于"预计开始"'; /* Report. */ $lang->task->report = new stdclass(); diff --git a/module/task/model.php b/module/task/model.php index d6082781f4..9a3b068a66 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -22,6 +22,11 @@ class taskModel extends model */ public function create($projectID) { + if($this->post->estimate < 0) + { + dao::$errors[] = $this->lang->task->error->recordMinus; + return false; + } $taskIdList = array(); $taskFiles = array(); $this->loadModel('file'); @@ -272,6 +277,12 @@ class taskModel extends model /* check data. */ foreach($data as $i => $task) { + + if($task->deadline != '0000-00-00' and $task->deadline <= $task->estStarted) + { + dao::$errors['message'][] = $this->lang->task->error->deadlineSmall; + return false; + } if($task->estimate and !preg_match("/^[0-9]+(.[0-9]{1,3})?$/", $task->estimate)) { dao::$errors['message'][] = $this->lang->task->error->estimateNumber; @@ -413,6 +424,7 @@ class taskModel extends model if($parentID <= 0) return true; $oldParentTask = $this->dao->select('*')->from(TABLE_TASK)->where('id')->eq($parentID)->fetch(); + if($oldParentTask->parent != '-1') $this->dao->update(TABLE_TASK)->set('parent')->eq('-1')->where('id')->eq($parentID)->exec(); $this->computeWorkingHours($parentID); $childrenStatus = $this->dao->select('id,status')->from(TABLE_TASK)->where('parent')->eq($parentID)->andWhere('deleted')->eq(0)->fetchPairs('status', 'status'); @@ -547,10 +559,11 @@ class taskModel extends model * * @param object $oldTask * @param object $task + * @param bool $autoStatus * @access public * @return object|bool */ - public function computeHours4Multiple($oldTask, $task = null, $team = array()) + public function computeHours4Multiple($oldTask, $task = null, $team = array(), $autoStatus = true) { if(!$oldTask) return false; @@ -603,7 +616,7 @@ class taskModel extends model if(!empty($task)) { - if($this->post->status) return $currentTask; + if(!$autoStatus) return $currentTask; if($currentTask->consumed == 0) { @@ -662,6 +675,11 @@ class taskModel extends model public function update($taskID) { $oldTask = $this->dao->select('*')->from(TABLE_TASK)->where('id')->eq((int)$taskID)->fetch(); + if($this->post->estimate < 0 or $this->post->left < 0 or $this->post->consumed < 0) + { + dao::$errors[] = $this->lang->task->error->recordMinus; + return false; + } if(!empty($_POST['lastEditedDate']) and $oldTask->lastEditedDate != $this->post->lastEditedDate) { dao::$errors[] = $this->lang->error->editedByOther; @@ -749,7 +767,7 @@ class taskModel extends model if(!empty($teams)) { foreach($teams as $member) $this->dao->insert(TABLE_TEAM)->data($member)->autoCheck()->exec(); - $task = $this->computeHours4Multiple($oldTask, $task); + $task = $this->computeHours4Multiple($oldTask, $task, array(), $autoStatus = false); if($task->status == 'wait') { reset($teams); @@ -1472,6 +1490,7 @@ class taskModel extends model $this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('id')->eq((int)$taskID)->exec(); if($oldTask->parent > 0) $this->updateParentStatus($taskID); + if($oldTask->parent == '-1') $this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('parent')->eq((int)$taskID)->exec(); if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story); if(!dao::isError()) return common::createChanges($oldTask, $task); } @@ -1493,6 +1512,7 @@ class taskModel extends model } $oldTask = $this->getById($taskID); + if($oldTask->parent == '-1') $this->config->task->activate->requiredFields = ''; $task = fixer::input('post') ->setIF(is_numeric($this->post->left), 'left', (float)$this->post->left) ->setDefault('left', 0) @@ -1529,6 +1549,12 @@ class taskModel extends model ->exec(); if($oldTask->parent > 0) $this->updateParentStatus($taskID); + if($oldTask->parent == '-1') + { + unset($task->left); + $this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('parent')->eq((int)$taskID)->exec(); + $this->computeWorkingHours($taskID); + } if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story); if(!dao::isError()) return common::createChanges($oldTask, $task); } @@ -1647,7 +1673,6 @@ class taskModel extends model ->leftJoin(TABLE_TEAM)->alias('t4')->on('t4.root = t1.id') ->leftJoin(TABLE_MODULE)->alias('t5')->on('t1.module = t5.id') ->where('t1.project')->eq((int)$projectID) - ->beginIF(($type == 'all' || is_array($type)) && $modules == 0)->andWhere('t1.parent')->lt(1)->fi() ->beginIF($type == 'myinvolved') ->andWhere("((t4.`account` = '{$this->app->user->account}' AND t4.`type` = 'task') OR t1.`assignedTo` = '{$this->app->user->account}' OR t1.`finishedby` = '{$this->app->user->account}')") ->fi() @@ -2542,9 +2567,7 @@ class taskModel extends model if($action == 'start' and !empty($task->children)) return false; if($action == 'finish' and !empty($task->children)) return false; - if($action == 'cancel' and !empty($task->children)) return false; if($action == 'pause' and !empty($task->children)) return false; - if($action == 'activate' and !empty($task->children)) return false; if($action == 'assignto' and !empty($task->children)) return false; if($action == 'close' and !empty($task->children)) return false; if($action == 'batchcreate' and !empty($task->team)) return false; @@ -2760,7 +2783,7 @@ class taskModel extends model case 'actions': if($storyChanged) { - common::printIcon('task', 'confirmStoryChange', "taskid=$task->id", '', 'list', '', 'hiddenwin', 'btn-wide'); + common::printIcon('task', 'confirmStoryChange', "taskid=$task->id", '', 'list', '', 'hiddenwin'); break; } diff --git a/module/task/view/activate.html.php b/module/task/view/activate.html.php index 05c7c97fd2..922d3b23c9 100644 --- a/module/task/view/activate.html.php +++ b/module/task/view/activate.html.php @@ -31,6 +31,7 @@
    finishedBy, "class='form-control chosen'");?>
    task->left;?> @@ -40,6 +41,7 @@
    task->status;?>
    testcase->stepID;?>testcase->stepDesc;?>testcase->stepDesc;?> testcase->stepExpect;?>
    $stepId
    "; if($step->type == 'item') echo "{$stepId}.{$childId}"; - echo nl2br(str_replace(' ', ' ', htmlspecialchars($step->desc))) . "
    " . nl2br(str_replace(' ', ' ', $step->expect)) . "
    doc->lib;?>root, "class='form-control chosen' onchange=loadDocModule(this.value)");?>root, "class='form-control chosen'");?>