diff --git a/db/update17.5.sql b/db/update17.5.sql
new file mode 100644
index 0000000000..bd9371892f
--- /dev/null
+++ b/db/update17.5.sql
@@ -0,0 +1,249 @@
+ALTER TABLE `zt_bug` MODIFY COLUMN `os` varchar(255) NOT NULL default '';
+ALTER TABLE `zt_bug` MODIFY COLUMN `browser` varchar(255) NOT NULL default '';
+
+INSERT IGNORE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) VALUES
+('zh-cn', 'bug', 'browserList', 'all', '全部', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'chrome', 'Chrome', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'edge', 'Edge', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'ie', 'IE系列', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'ie11', 'IE11', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'ie10', 'IE10', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'ie9', 'IE9', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'ie8', 'IE8', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'ie7', 'IE7', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'ie6', 'IE6', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'firefox', 'firefox系列', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'firefox4', 'firefox4', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'firefox3', 'firefox3', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'firefox2', 'firefox2', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'opera', 'Opera系列', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'opera11', 'opera11', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'opera10', 'opera10', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'opera9', 'opera9', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'safari', 'safari', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', '360', '360浏览器', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'qq', 'QQ浏览器', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'maxthon', '遨游', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'uc', 'UC', '1', 'rnd'),
+('zh-cn', 'bug', 'browserList', 'other', '其他', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'all', '全部', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'chrome', 'Chrome', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'edge', 'Edge', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'ie', 'IE系列', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'ie11', 'IE11', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'ie10', 'IE10', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'ie9', 'IE9', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'ie8', 'IE8', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'ie7', 'IE7', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'ie6', 'IE6', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'firefox', 'firefox系列', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'firefox4', 'firefox4', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'firefox3', 'firefox3', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'firefox2', 'firefox2', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'opera', 'Opera系列', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'opera11', 'opera11', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'opera10', 'opera10', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'opera9', 'opera9', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'safari', 'safari', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', '360', '360瀏覽器', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'qq', 'QQ瀏覽器', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'maxthon', '遨游', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'uc', 'UC', '1', 'rnd'),
+('zh-tw', 'bug', 'browserList', 'other', '其他', '1', 'rnd'),
+('en', 'bug', 'browserList', 'all', 'All', '1', 'rnd'),
+('en', 'bug', 'browserList', 'chrome', 'Chrome', '1', 'rnd'),
+('en', 'bug', 'browserList', 'edge', 'Edge', '1', 'rnd'),
+('en', 'bug', 'browserList', 'ie', 'IE series', '1', 'rnd'),
+('en', 'bug', 'browserList', 'ie11', 'IE11', '1', 'rnd'),
+('en', 'bug', 'browserList', 'ie10', 'IE10', '1', 'rnd'),
+('en', 'bug', 'browserList', 'ie9', 'IE9', '1', 'rnd'),
+('en', 'bug', 'browserList', 'ie8', 'IE8', '1', 'rnd'),
+('en', 'bug', 'browserList', 'ie7', 'IE7', '1', 'rnd'),
+('en', 'bug', 'browserList', 'ie6', 'IE6', '1', 'rnd'),
+('en', 'bug', 'browserList', 'firefox', 'Firefox series', '1', 'rnd'),
+('en', 'bug', 'browserList', 'firefox4', 'Firefox4', '1', 'rnd'),
+('en', 'bug', 'browserList', 'firefox3', 'Firefox3', '1', 'rnd'),
+('en', 'bug', 'browserList', 'firefox2', 'Firefox2', '1', 'rnd'),
+('en', 'bug', 'browserList', 'opera', 'Opera series', '1', 'rnd'),
+('en', 'bug', 'browserList', 'opera11', 'Opera11', '1', 'rnd'),
+('en', 'bug', 'browserList', 'opera10', 'Opera10', '1', 'rnd'),
+('en', 'bug', 'browserList', 'opera9', 'Opera9', '1', 'rnd'),
+('en', 'bug', 'browserList', 'safari', 'Safari', '1', 'rnd'),
+('en', 'bug', 'browserList', '360', '360', '1', 'rnd'),
+('en', 'bug', 'browserList', 'qq', 'QQ', '1', 'rnd'),
+('en', 'bug', 'browserList', 'maxthon', 'Maxthon', '1', 'rnd'),
+('en', 'bug', 'browserList', 'uc', 'UC', '1', 'rnd'),
+('en', 'bug', 'browserList', 'other', 'Others', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'all', 'Tous', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'chrome', 'Chrome', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'edge', 'Edge', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'ie', 'IE series', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'ie11', 'IE11', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'ie10', 'IE10', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'ie9', 'IE9', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'ie8', 'IE8', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'ie7', 'IE7', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'ie6', 'IE6', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'firefox', 'Firefox series', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'firefox4', 'Firefox4', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'firefox3', 'Firefox3', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'firefox2', 'Firefox2', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'opera', 'Opera series', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'opera11', 'Opera11', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'opera10', 'Opera10', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'opera9', 'Opera9', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'safari', 'Safari', '1', 'rnd'),
+('fr', 'bug', 'browserList', '360', '360', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'qq', 'QQ', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'maxthon', 'Maxthon', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'uc', 'UC', '1', 'rnd'),
+('fr', 'bug', 'browserList', 'other', 'Autres', '1', 'rnd'),
+('de', 'bug', 'browserList', 'all', 'Alle', '1', 'rnd'),
+('de', 'bug', 'browserList', 'chrome', 'Chrome', '1', 'rnd'),
+('de', 'bug', 'browserList', 'edge', 'Edge', '1', 'rnd'),
+('de', 'bug', 'browserList', 'ie', 'IE series', '1', 'rnd'),
+('de', 'bug', 'browserList', 'ie11', 'IE11', '1', 'rnd'),
+('de', 'bug', 'browserList', 'ie10', 'IE10', '1', 'rnd'),
+('de', 'bug', 'browserList', 'ie9', 'IE9', '1', 'rnd'),
+('de', 'bug', 'browserList', 'ie8', 'IE8', '1', 'rnd'),
+('de', 'bug', 'browserList', 'ie7', 'IE7', '1', 'rnd'),
+('de', 'bug', 'browserList', 'ie6', 'IE6', '1', 'rnd'),
+('de', 'bug', 'browserList', 'firefox', 'Firefox series', '1', 'rnd'),
+('de', 'bug', 'browserList', 'firefox4', 'Firefox4', '1', 'rnd'),
+('de', 'bug', 'browserList', 'firefox3', 'Firefox3', '1', 'rnd'),
+('de', 'bug', 'browserList', 'firefox2', 'Firefox2', '1', 'rnd'),
+('de', 'bug', 'browserList', 'opera', 'Opera series', '1', 'rnd'),
+('de', 'bug', 'browserList', 'opera11', 'Opera11', '1', 'rnd'),
+('de', 'bug', 'browserList', 'opera10', 'Opera10', '1', 'rnd'),
+('de', 'bug', 'browserList', 'opera9', 'Opera9', '1', 'rnd'),
+('de', 'bug', 'browserList', 'safari', 'Safari', '1', 'rnd'),
+('de', 'bug', 'browserList', '360', '360', '1', 'rnd'),
+('de', 'bug', 'browserList', 'qq', 'QQ', '1', 'rnd'),
+('de', 'bug', 'browserList', 'maxthon', 'Maxthon', '1', 'rnd'),
+('de', 'bug', 'browserList', 'uc', 'UC', '1', 'rnd'),
+('de', 'bug', 'browserList', 'other', 'Andere', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'all', '全部', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'windows', 'Windows', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'win11', 'Windows 11', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'win10', 'Windows 10', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'win8', 'Windows 8', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'win7', 'Windows 7', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'winxp', 'Windows XP', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'osx', 'Mac OS', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'android', 'Android', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'ios', 'IOS', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'linux', 'Linux', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'ubuntu', 'Ubuntu', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'chromeos', 'Chrome OS', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'fedora', 'Fedora', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'vista', 'Windows Vista', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'win2012', 'Windows 2012', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'win2008', 'Windows 2008', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'win2003', 'Windows 2003', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'win2000', 'Windows 2000', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'wp8', 'WP8', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'wp7', 'WP7', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'symbian', 'Symbian', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'freebsd', 'FreeBSD', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'unix', 'Unix', '1', 'rnd'),
+('zh-cn', 'bug', 'osList', 'others', '其他', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'all', '全部', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'windows', 'Windows', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'win11', 'Windows 11', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'win10', 'Windows 10', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'win8', 'Windows 8', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'win7', 'Windows 7', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'winxp', 'Windows XP', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'osx', 'Mac OS', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'android', 'Android', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'ios', 'IOS', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'linux', 'Linux', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'ubuntu', 'Ubuntu', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'chromeos', 'Chrome OS', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'fedora', 'Fedora', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'vista', 'Windows Vista', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'win2012', 'Windows 2012', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'win2008', 'Windows 2008', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'win2003', 'Windows 2003', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'win2000', 'Windows 2000', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'wp8', 'WP8', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'wp7', 'WP7', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'symbian', 'Symbian', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'freebsd', 'FreeBSD', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'unix', 'Unix', '1', 'rnd'),
+('zh-tw', 'bug', 'osList', 'others', '其他', '1', 'rnd'),
+('en', 'bug', 'osList', 'all', 'All', '1', 'rnd'),
+('en', 'bug', 'osList', 'windows', 'Windows', '1', 'rnd'),
+('en', 'bug', 'osList', 'win11', 'Windows 11', '1', 'rnd'),
+('en', 'bug', 'osList', 'win10', 'Windows 10', '1', 'rnd'),
+('en', 'bug', 'osList', 'win8', 'Windows 8', '1', 'rnd'),
+('en', 'bug', 'osList', 'win7', 'Windows 7', '1', 'rnd'),
+('en', 'bug', 'osList', 'winxp', 'Windows XP', '1', 'rnd'),
+('en', 'bug', 'osList', 'osx', 'Mac OS', '1', 'rnd'),
+('en', 'bug', 'osList', 'android', 'Android', '1', 'rnd'),
+('en', 'bug', 'osList', 'ios', 'IOS', '1', 'rnd'),
+('en', 'bug', 'osList', 'linux', 'Linux', '1', 'rnd'),
+('en', 'bug', 'osList', 'ubuntu', 'Ubuntu', '1', 'rnd'),
+('en', 'bug', 'osList', 'chromeos', 'Chrome OS', '1', 'rnd'),
+('en', 'bug', 'osList', 'fedora', 'Fedora', '1', 'rnd'),
+('en', 'bug', 'osList', 'vista', 'Windows Vista', '1', 'rnd'),
+('en', 'bug', 'osList', 'win2012', 'Windows 2012', '1', 'rnd'),
+('en', 'bug', 'osList', 'win2008', 'Windows 2008', '1', 'rnd'),
+('en', 'bug', 'osList', 'win2003', 'Windows 2003', '1', 'rnd'),
+('en', 'bug', 'osList', 'win2000', 'Windows 2000', '1', 'rnd'),
+('en', 'bug', 'osList', 'wp8', 'WP8', '1', 'rnd'),
+('en', 'bug', 'osList', 'wp7', 'WP7', '1', 'rnd'),
+('en', 'bug', 'osList', 'symbian', 'Symbian', '1', 'rnd'),
+('en', 'bug', 'osList', 'freebsd', 'FreeBSD', '1', 'rnd'),
+('en', 'bug', 'osList', 'unix', 'Unix', '1', 'rnd'),
+('en', 'bug', 'osList', 'others', 'Others', '1', 'rnd'),
+('fr', 'bug', 'osList', 'all', 'Tous', '1', 'rnd'),
+('fr', 'bug', 'osList', 'windows', 'Windows', '1', 'rnd'),
+('fr', 'bug', 'osList', 'win11', 'Windows 11', '1', 'rnd'),
+('fr', 'bug', 'osList', 'win10', 'Windows 10', '1', 'rnd'),
+('fr', 'bug', 'osList', 'win8', 'Windows 8', '1', 'rnd'),
+('fr', 'bug', 'osList', 'win7', 'Windows 7', '1', 'rnd'),
+('fr', 'bug', 'osList', 'winxp', 'Windows XP', '1', 'rnd'),
+('fr', 'bug', 'osList', 'osx', 'Mac OS', '1', 'rnd'),
+('fr', 'bug', 'osList', 'android', 'Android', '1', 'rnd'),
+('fr', 'bug', 'osList', 'ios', 'IOS', '1', 'rnd'),
+('fr', 'bug', 'osList', 'linux', 'Linux', '1', 'rnd'),
+('fr', 'bug', 'osList', 'ubuntu', 'Ubuntu', '1', 'rnd'),
+('fr', 'bug', 'osList', 'chromeos', 'Chrome OS', '1', 'rnd'),
+('fr', 'bug', 'osList', 'fedora', 'Fedora', '1', 'rnd'),
+('fr', 'bug', 'osList', 'vista', 'Windows Vista', '1', 'rnd'),
+('fr', 'bug', 'osList', 'win2012', 'Windows 2012', '1', 'rnd'),
+('fr', 'bug', 'osList', 'win2008', 'Windows 2008', '1', 'rnd'),
+('fr', 'bug', 'osList', 'win2003', 'Windows 2003', '1', 'rnd'),
+('fr', 'bug', 'osList', 'win2000', 'Windows 2000', '1', 'rnd'),
+('fr', 'bug', 'osList', 'wp8', 'WP8', '1', 'rnd'),
+('fr', 'bug', 'osList', 'wp7', 'WP7', '1', 'rnd'),
+('fr', 'bug', 'osList', 'symbian', 'Symbian', '1', 'rnd'),
+('fr', 'bug', 'osList', 'freebsd', 'FreeBSD', '1', 'rnd'),
+('fr', 'bug', 'osList', 'unix', 'Unix', '1', 'rnd'),
+('fr', 'bug', 'osList', 'others', 'Autres', '1', 'rnd'),
+('de', 'bug', 'osList', 'all', 'Alle', '1', 'rnd'),
+('de', 'bug', 'osList', 'windows', 'Windows', '1', 'rnd'),
+('de', 'bug', 'osList', 'win11', 'Windows 11', '1', 'rnd'),
+('de', 'bug', 'osList', 'win10', 'Windows 10', '1', 'rnd'),
+('de', 'bug', 'osList', 'win8', 'Windows 8', '1', 'rnd'),
+('de', 'bug', 'osList', 'win7', 'Windows 7', '1', 'rnd'),
+('de', 'bug', 'osList', 'winxp', 'Windows XP', '1', 'rnd'),
+('de', 'bug', 'osList', 'osx', 'Mac OS', '1', 'rnd'),
+('de', 'bug', 'osList', 'android', 'Android', '1', 'rnd'),
+('de', 'bug', 'osList', 'ios', 'IOS', '1', 'rnd'),
+('de', 'bug', 'osList', 'linux', 'Linux', '1', 'rnd'),
+('de', 'bug', 'osList', 'ubuntu', 'Ubuntu', '1', 'rnd'),
+('de', 'bug', 'osList', 'chromeos', 'Chrome OS', '1', 'rnd'),
+('de', 'bug', 'osList', 'fedora', 'Fedora', '1', 'rnd'),
+('de', 'bug', 'osList', 'vista', 'Windows Vista', '1', 'rnd'),
+('de', 'bug', 'osList', 'win2012', 'Windows 2012', '1', 'rnd'),
+('de', 'bug', 'osList', 'win2008', 'Windows 2008', '1', 'rnd'),
+('de', 'bug', 'osList', 'win2003', 'Windows 2003', '1', 'rnd'),
+('de', 'bug', 'osList', 'win2000', 'Windows 2000', '1', 'rnd'),
+('de', 'bug', 'osList', 'wp8', 'WP8', '1', 'rnd'),
+('de', 'bug', 'osList', 'wp7', 'WP7', '1', 'rnd'),
+('de', 'bug', 'osList', 'symbian', 'Symbian', '1', 'rnd'),
+('de', 'bug', 'osList', 'freebsd', 'FreeBSD', '1', 'rnd'),
+('de', 'bug', 'osList', 'unix', 'Unix', '1', 'rnd'),
+('de', 'bug', 'osList', 'others', 'Andere', '1', 'rnd');
diff --git a/db/zentao.sql b/db/zentao.sql
index 7bee5678c7..d586ddba19 100755
--- a/db/zentao.sql
+++ b/db/zentao.sql
@@ -262,8 +262,8 @@ CREATE TABLE IF NOT EXISTS `zt_bug` (
`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 '',
+ `os` varchar(255) NOT NULL default '',
+ `browser` varchar(255) NOT NULL default '',
`hardware` varchar(30) NOT NULL,
`found` varchar(30) NOT NULL default '',
`steps` mediumtext NOT NULL,
diff --git a/module/action/model.php b/module/action/model.php
index 4debe68768..97ee08747b 100755
--- a/module/action/model.php
+++ b/module/action/model.php
@@ -1765,6 +1765,12 @@ class actionModel extends model
{
$products = $this->product->getProducts($project->id, 'all', '', false);
if(!empty($products)) $this->loadModel('user')->updateUserView(array_keys($products), 'product');
+
+ if($action->objectType == 'execution')
+ {
+ $execution = $this->dao->select('id, project, grade, parent, status, deleted')->from(TABLE_EXECUTION)->where('id')->eq($action->objectID)->fetch();
+ $this->loadModel('common')->syncExecutionByChild($execution);
+ }
}
/* Revert doclib when undelete product or project. */
diff --git a/module/admin/lang/de.php b/module/admin/lang/de.php
index 4911a17729..35be329946 100644
--- a/module/admin/lang/de.php
+++ b/module/admin/lang/de.php
@@ -75,6 +75,7 @@ $lang->admin->safe->checkWeak = 'Schwache Passwörter prüfen';
$lang->admin->safe->changeWeak = 'Ihr Passwort ist schwach. Bitte ändern.';
$lang->admin->safe->loginCaptcha = 'Login using CAPTCHA';
$lang->admin->safe->modifyPasswordFirstLogin = 'Passwort nach der ersten Anmeldung ändern';
+$lang->admin->safe->passwordStrengthWeak = 'The password strength is weaker than the system settings.';
$lang->admin->safe->modeList[0] = 'N/A';
$lang->admin->safe->modeList[1] = 'Medium';
diff --git a/module/admin/lang/en.php b/module/admin/lang/en.php
index 1c8d55ebc2..1657fb2e33 100644
--- a/module/admin/lang/en.php
+++ b/module/admin/lang/en.php
@@ -75,6 +75,7 @@ $lang->admin->safe->checkWeak = 'Weak Password Scan';
$lang->admin->safe->changeWeak = 'Force to change weak password';
$lang->admin->safe->loginCaptcha = 'Login using CAPTCHA';
$lang->admin->safe->modifyPasswordFirstLogin = 'Force to change password after first login';
+$lang->admin->safe->passwordStrengthWeak = 'The password strength is weaker than the system settings.';
$lang->admin->safe->modeList[0] = 'I don\'t care.';
$lang->admin->safe->modeList[1] = 'Medium';
diff --git a/module/admin/lang/fr.php b/module/admin/lang/fr.php
index 4bb1433680..f8370e4fee 100644
--- a/module/admin/lang/fr.php
+++ b/module/admin/lang/fr.php
@@ -75,6 +75,7 @@ $lang->admin->safe->checkWeak = 'Balayage des mots de passe faibl
$lang->admin->safe->changeWeak = 'Forcer le changement des mots de passe faibles';
$lang->admin->safe->loginCaptcha = 'Login using CAPTCHA';
$lang->admin->safe->modifyPasswordFirstLogin = 'Forcer le changement du mot de passe après la première connexion';
+$lang->admin->safe->passwordStrengthWeak = 'Le mot de passe est moins fort que les paramètres du système.';
$lang->admin->safe->modeList[0] = "Contrôle Modéré";
$lang->admin->safe->modeList[1] = 'Contrôle Moyen';
diff --git a/module/admin/lang/zh-cn.php b/module/admin/lang/zh-cn.php
index 51b9e2c510..2d5d8048c7 100644
--- a/module/admin/lang/zh-cn.php
+++ b/module/admin/lang/zh-cn.php
@@ -75,6 +75,7 @@ $lang->admin->safe->checkWeak = '弱口令扫描';
$lang->admin->safe->changeWeak = '修改弱口令密码';
$lang->admin->safe->loginCaptcha = '登录使用验证码';
$lang->admin->safe->modifyPasswordFirstLogin = '首次登录修改密码';
+$lang->admin->safe->passwordStrengthWeak = '密码强度小于系统设置';
$lang->admin->safe->modeList[0] = '不检查';
$lang->admin->safe->modeList[1] = '中';
diff --git a/module/admin/model.php b/module/admin/model.php
index 1a6062e7f0..c6ec4bed40 100644
--- a/module/admin/model.php
+++ b/module/admin/model.php
@@ -234,13 +234,6 @@ class adminModel extends model
{
$weaks = array();
- /* Check weak password when login. */
- if($this->app->moduleName == 'user' and $this->app->methodName == 'login')
- {
- if(!isset($_POST['passwordStrength'])) return false;
- if(isset($this->config->safe->mode) and $this->post->passwordStrength < $this->config->safe->mode) return true;
- }
-
foreach(explode(',', $this->config->safe->weak) as $weak)
{
$weak = md5(trim($weak));
diff --git a/module/block/control.php b/module/block/control.php
index 6b53ff3a5b..224ca9b8b4 100644
--- a/module/block/control.php
+++ b/module/block/control.php
@@ -888,6 +888,7 @@ class block extends control
/* Load models and langs. */
$this->loadModel('project');
$this->loadModel('weekly');
+ $this->loadModel('execution');
$this->app->loadLang('task');
$this->app->loadLang('story');
$this->app->loadLang('bug');
@@ -898,7 +899,7 @@ class block extends control
/* Get projects. */
$excludedModel = $this->config->edition == 'max' ? '' : 'waterfall';
- $projects = $this->loadModel('project')->getOverviewList('byStatus', $status, 'order_asc', $count, $excludedModel);
+ $projects = $this->project->getOverviewList('byStatus', $status, 'order_asc', $count, $excludedModel);
if(empty($projects))
{
$this->view->projects = $projects;
@@ -924,7 +925,7 @@ class block extends control
$this->app->loadClass('pager', $static = true);
$pager = pager::init(0, 3, 1);
$project->progress = $project->allStories == 0 ? 0 : round($project->doneStories / $project->allStories, 3) * 100;
- $project->executions = $this->project->getStats($projectID, 'all', 0, 0, 30, 'id_desc', $pager);
+ $project->executions = $this->execution->getStatData($projectID, 'all', 0, 0, false, '', 'id_desc', $pager);
}
elseif($project->model == 'waterfall')
{
@@ -1356,8 +1357,8 @@ class block extends control
$this->app->loadClass('pager', $static = true);
$pager = pager::init(0, $count, 1);
- $this->app->loadLang('execution');
- $this->view->executionStats = !defined('TUTORIAL') ? $this->loadModel('project')->getStats($this->session->project, $type, 0, 0, 30, 'id_desc', $pager) : array($this->loadModel('tutorial')->getExecution());
+ $this->loadModel('execution');
+ $this->view->executionStats = !defined('TUTORIAL') ? $this->execution->getStatData($this->session->project, $type, 0, 0, false, '', 'id_desc', $pager) : array($this->loadModel('tutorial')->getExecution());
}
/**
@@ -1692,7 +1693,7 @@ class block extends control
$count = isset($this->params->count) ? (int)$this->params->count : 0;
$status = isset($this->params->type) ? $this->params->type : 'all';
- $this->app->loadLang('execution');
+ $this->loadModel('execution');
$this->app->loadClass('pager', true);
$pager = pager::init(0, $count, 1);
@@ -1700,18 +1701,8 @@ class block extends control
if($this->config->systemMode == 'new') $projectPairs = $this->dao->select('id,name')->from(TABLE_PROJECT)->where('type')->eq('project')->fetchPairs('id', 'name');
$projectID = $this->view->block->module == 'my' ? 0 : (int)$this->session->project;
- $executions = $this->loadModel('project')->getStats($projectID, $status, 0, 0, 30, 'id_asc', $pager);
- foreach($executions as $index => $execution)
- {
- if(empty($execution->children)) continue;
-
- foreach($execution->children as $child) $executions[] = $child;
- unset($executions[$index]);
- }
-
- $this->view->executionStats = $executions;
- $this->view->projectPairs = $projectPairs;
+ $this->view->executionStats = $this->execution->getStatData($projectID, $status, 0, 0, false, 'skipParent', 'id_asc', $pager);
}
/**
diff --git a/module/block/view/executionblock.html.php b/module/block/view/executionblock.html.php
index 1f5d597033..d3d3804164 100644
--- a/module/block/view/executionblock.html.php
+++ b/module/block/view/executionblock.html.php
@@ -43,7 +43,6 @@
get->entry}'" : "class='text-center'";
$viewLink = $this->createLink('execution', 'task', 'executionID=' . $execution->id);
- if($config->systemMode == 'new' and isset($projectPairs[$execution->project])) $execution->name = zget($projectPairs, $execution->project, '') . ' / ' . $execution->name;
?>
>
| name, '', "title='$execution->name'");?> |
diff --git a/module/bug/config.php b/module/bug/config.php
index c0dd78d8ef..0ba621fbc7 100644
--- a/module/bug/config.php
+++ b/module/bug/config.php
@@ -133,35 +133,28 @@ $config->bug->search['params']['branch'] = array('operator' => '=',
$config->bug->search['params']['plan'] = array('operator' => '=', 'control' => 'select', 'values' => '');
$config->bug->search['params']['module'] = array('operator' => 'belong', 'control' => 'select', 'values' => 'modules');
$config->bug->search['params']['execution'] = array('operator' => '=', 'control' => 'select', 'values' => 'executions');
-
$config->bug->search['params']['severity'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->bug->severityList);
$config->bug->search['params']['pri'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->bug->priList);
$config->bug->search['params']['type'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->bug->typeList);
-$config->bug->search['params']['os'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->bug->osList);
-$config->bug->search['params']['browser'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->bug->browserList);
+$config->bug->search['params']['os'] = array('operator' => 'include', 'control' => 'select', 'values' => $lang->bug->osList);
+$config->bug->search['params']['browser'] = array('operator' => 'include', 'control' => 'select', 'values' => $lang->bug->browserList);
$config->bug->search['params']['resolution'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->bug->resolutionList);
-
$config->bug->search['params']['activatedCount']= array('operator' => '>=', 'control' => 'input', 'values' => '');
-
$config->bug->search['params']['toTask'] = array('operator' => '=', 'control' => 'input', 'values' => '');
$config->bug->search['params']['toStory'] = array('operator' => '=', 'control' => 'input', 'values' => '');
-
$config->bug->search['params']['openedBy'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->bug->search['params']['closedBy'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->bug->search['params']['lastEditedBy'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
-
-$config->bug->search['params']['mailto'] = array('operator' => 'include', 'control' => 'select', 'values' => 'users');
-
+$config->bug->search['params']['mailto'] = array('operator' => 'include', 'control' => 'select', 'values' => 'users');
$config->bug->search['params']['openedBuild'] = array('operator' => 'include', 'control' => 'select', 'values' => 'builds');
$config->bug->search['params']['resolvedBuild'] = array('operator' => '=', 'control' => 'select', 'values' => 'builds');
-
-$config->bug->search['params']['openedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
-$config->bug->search['params']['assignedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
-$config->bug->search['params']['resolvedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
-$config->bug->search['params']['closedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
-$config->bug->search['params']['lastEditedDate']= array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
-$config->bug->search['params']['deadline'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
-$config->bug->search['params']['activatedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+$config->bug->search['params']['openedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+$config->bug->search['params']['assignedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+$config->bug->search['params']['resolvedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+$config->bug->search['params']['closedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+$config->bug->search['params']['lastEditedDate']= array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+$config->bug->search['params']['deadline'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+$config->bug->search['params']['activatedDate'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->bug->datatable = new stdclass();
$config->bug->datatable->defaultField = array('id', 'severity', 'pri', 'confirmed', 'title', 'status', 'openedBy', 'openedDate', 'assignedTo', 'resolution', 'actions');
diff --git a/module/bug/control.php b/module/bug/control.php
index 7dcae1aa83..bb3f353043 100755
--- a/module/bug/control.php
+++ b/module/bug/control.php
@@ -859,6 +859,7 @@ class bug extends control
$this->view->moduleID = $moduleID;
$this->view->branch = $branch;
$this->view->branches = $branches;
+
$this->display();
}
@@ -1132,6 +1133,8 @@ class bug extends control
$moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, $bug->branch);
if(!isset($moduleOptionMenu[$bug->module])) $moduleOptionMenu += $this->tree->getModulesName($bug->module);
+ $cases = $this->loadmodel('testcase')->getPairsByProduct($bug->product, array(0, $bug->branch));
+
/* Get assigned to member. */
if($bug->execution)
{
@@ -1165,6 +1168,7 @@ class bug extends control
$this->view->testtasks = $this->loadModel('testtask')->getPairs($bug->product, $bug->execution, $bug->testtask);
$this->view->users = $this->user->getPairs('', "$bug->assignedTo,$bug->resolvedBy,$bug->closedBy,$bug->openedBy");
$this->view->assignedToList = $assignedToList;
+ $this->view->cases = array('' => '') + $cases;
$this->view->openedBuilds = $openedBuilds;
$this->view->resolvedBuilds = array('' => '') + $openedBuilds + $oldResolvedBuild;
$this->view->actions = $this->action->getList('bug', $bugID);
@@ -1379,8 +1383,6 @@ class bug extends control
$this->view->priList = array('0' => '', 'ditto' => $this->lang->bug->ditto) + $this->lang->bug->priList;
$this->view->resolutionList = array('' => '', 'ditto' => $this->lang->bug->ditto) + $this->lang->bug->resolutionList;
$this->view->statusList = array('' => '', 'ditto' => $this->lang->bug->ditto) + $this->lang->bug->statusList;
- $this->view->osList = array('' => '', 'ditto' => $this->lang->bug->ditto) + $this->lang->bug->osList;
- $this->view->browserList = array('' => '', 'ditto' => $this->lang->bug->ditto) + $this->lang->bug->browserList;
$this->view->bugs = $bugs;
$this->view->branch = $branch;
$this->view->users = $users;
diff --git a/module/bug/css/create.css b/module/bug/css/create.css
index 470dd51d77..557bf69e84 100644
--- a/module/bug/css/create.css
+++ b/module/bug/css/create.css
@@ -55,3 +55,4 @@ html[lang='de'] #keywordsAddonLabel, .task {width: 73px;}
#executionLabel {display: inline; position: relative; width: 50%; left: 37%;}
#projectLabel {display: inline;}
#openedBuildLabel {display: inline; position: relative; width: 50%; left: 14%;}
+#bugTypeInputGroup > .table-col {width: 40% !important;}
diff --git a/module/bug/css/view.css b/module/bug/css/view.css
index 452146b88a..7583e152e0 100644
--- a/module/bug/css/view.css
+++ b/module/bug/css/view.css
@@ -5,3 +5,4 @@
.tab-pane table {border: 1px solid #ddd; border-top: none;}
.btn-edit-comment {z-index: 100;}
.select-project-modal .modal-body {right: 15px;}
+.osContent, .browserContent {margin: 0 0 5px;}
diff --git a/module/bug/js/browse.js b/module/bug/js/browse.js
index 6d469700d9..0cc3ce246d 100644
--- a/module/bug/js/browse.js
+++ b/module/bug/js/browse.js
@@ -8,4 +8,27 @@ $(function()
$("#sidebarHeader").toggle("fast");
});
if($("main").is(".hide-sidebar")) $("#sidebarHeader").hide();
+
+ $('#bugList').on('change', "[name='bugIDList[]']", checkClosed);
});
+
+/**
+ * Closed bugs are not assignable.
+ *
+ * @access public
+ * @return void
+ */
+function checkClosed()
+{
+ var disabledAssigned = $('#bugList tr.checked .c-assignedTo a').length > 0 ? true : false;
+ $('#bugList tr.checked .c-assignedTo a').each(function()
+ {
+ if(!$(this).hasClass('disabled'))
+ {
+ disabledAssigned = false;
+ return false;
+ }
+ });
+
+ $('#mulAssigned').prop('disabled', disabledAssigned);
+}
diff --git a/module/bug/lang/de.php b/module/bug/lang/de.php
index c9e6875e7b..4a4999204f 100644
--- a/module/bug/lang/de.php
+++ b/module/bug/lang/de.php
@@ -232,51 +232,39 @@ $lang->bug->priList[2] = '2';
$lang->bug->priList[3] = '3';
$lang->bug->priList[4] = '4';
-$lang->bug->osList[''] = '';
-$lang->bug->osList['all'] = 'Alle';
-$lang->bug->osList['windows'] = 'Windows';
-$lang->bug->osList['win10'] = 'Windows 10';
-$lang->bug->osList['win8'] = 'Windows 8';
-$lang->bug->osList['win7'] = 'Windows 7';
-$lang->bug->osList['vista'] = 'Windows Vista';
-$lang->bug->osList['winxp'] = 'Windows XP';
-$lang->bug->osList['win2012'] = 'Windows 2012';
-$lang->bug->osList['win2008'] = 'Windows 2008';
-$lang->bug->osList['win2003'] = 'Windows 2003';
-$lang->bug->osList['win2000'] = 'Windows 2000';
-$lang->bug->osList['android'] = 'Android';
-$lang->bug->osList['ios'] = 'IOS';
-$lang->bug->osList['wp8'] = 'WP8';
-$lang->bug->osList['wp7'] = 'WP7';
-$lang->bug->osList['symbian'] = 'Symbian';
-$lang->bug->osList['linux'] = 'Linux';
-$lang->bug->osList['freebsd'] = 'FreeBSD';
-$lang->bug->osList['osx'] = 'OS X';
-$lang->bug->osList['unix'] = 'Unix';
-$lang->bug->osList['others'] = 'Andere';
+$lang->bug->osList[''] = '';
+$lang->bug->osList['all'] = 'Alle';
+$lang->bug->osList['windows'] = 'Windows';
+$lang->bug->osList['win11'] = 'Windows 11';
+$lang->bug->osList['win10'] = 'Windows 10';
+$lang->bug->osList['win8'] = 'Windows 8';
+$lang->bug->osList['win7'] = 'Windows 7';
+$lang->bug->osList['winxp'] = 'Windows XP';
+$lang->bug->osList['osx'] = 'Mac OS';
+$lang->bug->osList['android'] = 'Android';
+$lang->bug->osList['ios'] = 'IOS';
+$lang->bug->osList['linux'] = 'Linux';
+$lang->bug->osList['ubuntu'] = 'Ubuntu';
+$lang->bug->osList['chromeos'] = 'Chrome OS';
+$lang->bug->osList['fedora'] = 'Fedora';
+$lang->bug->osList['unix'] = 'Unix';
+$lang->bug->osList['others'] = 'Andere';
-$lang->bug->browserList[''] = '';
-$lang->bug->browserList['all'] = 'Alle';
-$lang->bug->browserList['ie'] = 'IE series';
-$lang->bug->browserList['ie11'] = 'IE11';
-$lang->bug->browserList['ie10'] = 'IE10';
-$lang->bug->browserList['ie9'] = 'IE9';
-$lang->bug->browserList['ie8'] = 'IE8';
-$lang->bug->browserList['ie7'] = 'IE7';
-$lang->bug->browserList['ie6'] = 'IE6';
-$lang->bug->browserList['chrome'] = 'chrome';
-$lang->bug->browserList['firefox'] = 'Firefox series';
-$lang->bug->browserList['firefox4'] = 'Firefox4';
-$lang->bug->browserList['firefox3'] = 'Firefox3';
-$lang->bug->browserList['firefox2'] = 'Firefox2';
-$lang->bug->browserList['opera'] = 'Opera series';
-$lang->bug->browserList['oprea11'] = 'Opera11';
-$lang->bug->browserList['oprea10'] = 'Opera10';
-$lang->bug->browserList['opera9'] = 'Opera9';
-$lang->bug->browserList['safari'] = 'Safari';
-$lang->bug->browserList['maxthon'] = 'Maxthon';
-$lang->bug->browserList['uc'] = 'UC';
-$lang->bug->browserList['other'] = 'Andere';
+$lang->bug->browserList[''] = '';
+$lang->bug->browserList['all'] = 'Alle';
+$lang->bug->browserList['chrome'] = 'Chrome';
+$lang->bug->browserList['edge'] = 'Edge';
+$lang->bug->browserList['ie'] = 'IE series';
+$lang->bug->browserList['ie11'] = 'IE11';
+$lang->bug->browserList['ie10'] = 'IE10';
+$lang->bug->browserList['ie9'] = 'IE9';
+$lang->bug->browserList['ie8'] = 'IE8';
+$lang->bug->browserList['firefox'] = 'Firefox series';
+$lang->bug->browserList['opera'] = 'Opera series';
+$lang->bug->browserList['safari'] = 'Safari';
+$lang->bug->browserList['360'] = '360 series';
+$lang->bug->browserList['qq'] = 'QQ series';
+$lang->bug->browserList['other'] = 'Andere';
$lang->bug->typeList[''] = '';
$lang->bug->typeList['codeerror'] = 'Code Fehler';
@@ -428,7 +416,6 @@ $lang->bug->featureBar['browse']['unclosed'] = $lang->bug->unclosed;
$lang->bug->featureBar['browse']['openedbyme'] = $lang->bug->openedByMe;
$lang->bug->featureBar['browse']['assigntome'] = $lang->bug->assignToMe;
$lang->bug->featureBar['browse']['resolvedbyme'] = $lang->bug->resolvedByMe;
-
$lang->bug->featureBar['browse']['unresolved'] = $lang->bug->unResolved;
$lang->bug->featureBar['browse']['more'] = $lang->more;
diff --git a/module/bug/lang/en.php b/module/bug/lang/en.php
index fc62a14d4f..9a67e54d8c 100644
--- a/module/bug/lang/en.php
+++ b/module/bug/lang/en.php
@@ -232,51 +232,39 @@ $lang->bug->priList[2] = '2';
$lang->bug->priList[3] = '3';
$lang->bug->priList[4] = '4';
-$lang->bug->osList[''] = '';
-$lang->bug->osList['all'] = 'All';
-$lang->bug->osList['windows'] = 'Windows';
-$lang->bug->osList['win10'] = 'Windows 10';
-$lang->bug->osList['win8'] = 'Windows 8';
-$lang->bug->osList['win7'] = 'Windows 7';
-$lang->bug->osList['vista'] = 'Windows Vista';
-$lang->bug->osList['winxp'] = 'Windows XP';
-$lang->bug->osList['win2012'] = 'Windows 2012';
-$lang->bug->osList['win2008'] = 'Windows 2008';
-$lang->bug->osList['win2003'] = 'Windows 2003';
-$lang->bug->osList['win2000'] = 'Windows 2000';
-$lang->bug->osList['android'] = 'Android';
-$lang->bug->osList['ios'] = 'IOS';
-$lang->bug->osList['wp8'] = 'WP8';
-$lang->bug->osList['wp7'] = 'WP7';
-$lang->bug->osList['symbian'] = 'Symbian';
-$lang->bug->osList['linux'] = 'Linux';
-$lang->bug->osList['freebsd'] = 'FreeBSD';
-$lang->bug->osList['osx'] = 'OS X';
-$lang->bug->osList['unix'] = 'Unix';
-$lang->bug->osList['others'] = 'Others';
+$lang->bug->osList[''] = '';
+$lang->bug->osList['all'] = 'All';
+$lang->bug->osList['windows'] = 'Windows';
+$lang->bug->osList['win11'] = 'Windows 11';
+$lang->bug->osList['win10'] = 'Windows 10';
+$lang->bug->osList['win8'] = 'Windows 8';
+$lang->bug->osList['win7'] = 'Windows 7';
+$lang->bug->osList['winxp'] = 'Windows XP';
+$lang->bug->osList['osx'] = 'Mac OS';
+$lang->bug->osList['android'] = 'Android';
+$lang->bug->osList['ios'] = 'IOS';
+$lang->bug->osList['linux'] = 'Linux';
+$lang->bug->osList['ubuntu'] = 'Ubuntu';
+$lang->bug->osList['chromeos'] = 'Chrome OS';
+$lang->bug->osList['fedora'] = 'Fedora';
+$lang->bug->osList['unix'] = 'Unix';
+$lang->bug->osList['others'] = 'Others';
-$lang->bug->browserList[''] = '';
-$lang->bug->browserList['all'] = 'All';
-$lang->bug->browserList['ie'] = 'IE series';
-$lang->bug->browserList['ie11'] = 'IE11';
-$lang->bug->browserList['ie10'] = 'IE10';
-$lang->bug->browserList['ie9'] = 'IE9';
-$lang->bug->browserList['ie8'] = 'IE8';
-$lang->bug->browserList['ie7'] = 'IE7';
-$lang->bug->browserList['ie6'] = 'IE6';
-$lang->bug->browserList['chrome'] = 'Chrome';
-$lang->bug->browserList['firefox'] = 'Firefox series';
-$lang->bug->browserList['firefox4'] = 'Firefox4';
-$lang->bug->browserList['firefox3'] = 'Firefox3';
-$lang->bug->browserList['firefox2'] = 'Firefox2';
-$lang->bug->browserList['opera'] = 'Opera series';
-$lang->bug->browserList['oprea11'] = 'Opera11';
-$lang->bug->browserList['oprea10'] = 'Opera10';
-$lang->bug->browserList['opera9'] = 'Opera9';
-$lang->bug->browserList['safari'] = 'Safari';
-$lang->bug->browserList['maxthon'] = 'Maxthon';
-$lang->bug->browserList['uc'] = 'UC';
-$lang->bug->browserList['other'] = 'Others';
+$lang->bug->browserList[''] = '';
+$lang->bug->browserList['all'] = 'All';
+$lang->bug->browserList['chrome'] = 'Chrome';
+$lang->bug->browserList['edge'] = 'Edge';
+$lang->bug->browserList['ie'] = 'IE series';
+$lang->bug->browserList['ie11'] = 'IE11';
+$lang->bug->browserList['ie10'] = 'IE10';
+$lang->bug->browserList['ie9'] = 'IE9';
+$lang->bug->browserList['ie8'] = 'IE8';
+$lang->bug->browserList['firefox'] = 'Firefox series';
+$lang->bug->browserList['opera'] = 'Opera series';
+$lang->bug->browserList['safari'] = 'Safari';
+$lang->bug->browserList['360'] = '360 series';
+$lang->bug->browserList['qq'] = 'QQ series';
+$lang->bug->browserList['other'] = 'Others';
$lang->bug->typeList[''] = '';
$lang->bug->typeList['codeerror'] = 'CodeError';
@@ -428,7 +416,6 @@ $lang->bug->featureBar['browse']['unclosed'] = $lang->bug->unclosed;
$lang->bug->featureBar['browse']['openedbyme'] = $lang->bug->openedByMe;
$lang->bug->featureBar['browse']['assigntome'] = $lang->bug->assignToMe;
$lang->bug->featureBar['browse']['resolvedbyme'] = $lang->bug->resolvedByMe;
-
$lang->bug->featureBar['browse']['unresolved'] = $lang->bug->unResolved;
$lang->bug->featureBar['browse']['more'] = $lang->more;
diff --git a/module/bug/lang/fr.php b/module/bug/lang/fr.php
index ff95166866..6ea57d780c 100644
--- a/module/bug/lang/fr.php
+++ b/module/bug/lang/fr.php
@@ -232,51 +232,39 @@ $lang->bug->priList[2] = '2';
$lang->bug->priList[3] = '3';
$lang->bug->priList[4] = '4';
-$lang->bug->osList[''] = '';
-$lang->bug->osList['all'] = 'Tous';
-$lang->bug->osList['windows'] = 'Windows';
-$lang->bug->osList['win10'] = 'Windows 10';
-$lang->bug->osList['win8'] = 'Windows 8';
-$lang->bug->osList['win7'] = 'Windows 7';
-$lang->bug->osList['vista'] = 'Windows Vista';
-$lang->bug->osList['winxp'] = 'Windows XP';
-$lang->bug->osList['win2012'] = 'Windows 2012';
-$lang->bug->osList['win2008'] = 'Windows 2008';
-$lang->bug->osList['win2003'] = 'Windows 2003';
-$lang->bug->osList['win2000'] = 'Windows 2000';
-$lang->bug->osList['android'] = 'Android';
-$lang->bug->osList['ios'] = 'IOS';
-$lang->bug->osList['wp8'] = 'WP8';
-$lang->bug->osList['wp7'] = 'WP7';
-$lang->bug->osList['symbian'] = 'Symbian';
-$lang->bug->osList['linux'] = 'Linux';
-$lang->bug->osList['freebsd'] = 'FreeBSD';
-$lang->bug->osList['osx'] = 'OS X';
-$lang->bug->osList['unix'] = 'Unix';
-$lang->bug->osList['others'] = 'Autres';
+$lang->bug->osList[''] = '';
+$lang->bug->osList['all'] = 'Tous';
+$lang->bug->osList['windows'] = 'Windows';
+$lang->bug->osList['win11'] = 'Windows 11';
+$lang->bug->osList['win10'] = 'Windows 10';
+$lang->bug->osList['win8'] = 'Windows 8';
+$lang->bug->osList['win7'] = 'Windows 7';
+$lang->bug->osList['winxp'] = 'Windows XP';
+$lang->bug->osList['osx'] = 'Mac OS';
+$lang->bug->osList['android'] = 'Android';
+$lang->bug->osList['ios'] = 'IOS';
+$lang->bug->osList['linux'] = 'Linux';
+$lang->bug->osList['ubuntu'] = 'Ubuntu';
+$lang->bug->osList['chromeos'] = 'Chrome OS';
+$lang->bug->osList['fedora'] = 'Fedora';
+$lang->bug->osList['unix'] = 'Unix';
+$lang->bug->osList['others'] = 'Autres';
-$lang->bug->browserList[''] = '';
-$lang->bug->browserList['all'] = 'Tous';
-$lang->bug->browserList['ie'] = 'IE series';
-$lang->bug->browserList['ie11'] = 'IE11';
-$lang->bug->browserList['ie10'] = 'IE10';
-$lang->bug->browserList['ie9'] = 'IE9';
-$lang->bug->browserList['ie8'] = 'IE8';
-$lang->bug->browserList['ie7'] = 'IE7';
-$lang->bug->browserList['ie6'] = 'IE6';
-$lang->bug->browserList['chrome'] = 'Chrome';
-$lang->bug->browserList['firefox'] = 'Firefox series';
-$lang->bug->browserList['firefox4'] = 'Firefox4';
-$lang->bug->browserList['firefox3'] = 'Firefox3';
-$lang->bug->browserList['firefox2'] = 'Firefox2';
-$lang->bug->browserList['opera'] = 'Opera series';
-$lang->bug->browserList['oprea11'] = 'Opera11';
-$lang->bug->browserList['oprea10'] = 'Opera10';
-$lang->bug->browserList['opera9'] = 'Opera9';
-$lang->bug->browserList['safari'] = 'Safari';
-$lang->bug->browserList['maxthon'] = 'Maxthon';
-$lang->bug->browserList['uc'] = 'UC';
-$lang->bug->browserList['other'] = 'Autres';
+$lang->bug->browserList[''] = '';
+$lang->bug->browserList['all'] = 'Tous';
+$lang->bug->browserList['chrome'] = 'Chrome';
+$lang->bug->browserList['edge'] = 'Edge';
+$lang->bug->browserList['ie'] = 'IE series';
+$lang->bug->browserList['ie11'] = 'IE11';
+$lang->bug->browserList['ie10'] = 'IE10';
+$lang->bug->browserList['ie9'] = 'IE9';
+$lang->bug->browserList['ie8'] = 'IE8';
+$lang->bug->browserList['firefox'] = 'Firefox series';
+$lang->bug->browserList['opera'] = 'Opera series';
+$lang->bug->browserList['safari'] = 'Safari';
+$lang->bug->browserList['360'] = '360 series';
+$lang->bug->browserList['qq'] = 'QQ series';
+$lang->bug->browserList['other'] = 'Autres';
$lang->bug->typeList[''] = '';
$lang->bug->typeList['codeerror'] = 'Code Erreur';
@@ -428,7 +416,6 @@ $lang->bug->featureBar['browse']['unclosed'] = $lang->bug->unclosed;
$lang->bug->featureBar['browse']['openedbyme'] = $lang->bug->openedByMe;
$lang->bug->featureBar['browse']['assigntome'] = $lang->bug->assignToMe;
$lang->bug->featureBar['browse']['resolvedbyme'] = $lang->bug->resolvedByMe;
-
$lang->bug->featureBar['browse']['unresolved'] = $lang->bug->unResolved;
$lang->bug->featureBar['browse']['more'] = $lang->more;
diff --git a/module/bug/lang/zh-cn.php b/module/bug/lang/zh-cn.php
index 73c41b12e0..e4ea8310b7 100644
--- a/module/bug/lang/zh-cn.php
+++ b/module/bug/lang/zh-cn.php
@@ -232,51 +232,39 @@ $lang->bug->priList[2] = '2';
$lang->bug->priList[3] = '3';
$lang->bug->priList[4] = '4';
-$lang->bug->osList[''] = '';
-$lang->bug->osList['all'] = '全部';
-$lang->bug->osList['windows'] = 'Windows';
-$lang->bug->osList['win10'] = 'Windows 10';
-$lang->bug->osList['win8'] = 'Windows 8';
-$lang->bug->osList['win7'] = 'Windows 7';
-$lang->bug->osList['vista'] = 'Windows Vista';
-$lang->bug->osList['winxp'] = 'Windows XP';
-$lang->bug->osList['win2012'] = 'Windows 2012';
-$lang->bug->osList['win2008'] = 'Windows 2008';
-$lang->bug->osList['win2003'] = 'Windows 2003';
-$lang->bug->osList['win2000'] = 'Windows 2000';
-$lang->bug->osList['android'] = 'Android';
-$lang->bug->osList['ios'] = 'IOS';
-$lang->bug->osList['wp8'] = 'WP8';
-$lang->bug->osList['wp7'] = 'WP7';
-$lang->bug->osList['symbian'] = 'Symbian';
-$lang->bug->osList['linux'] = 'Linux';
-$lang->bug->osList['freebsd'] = 'FreeBSD';
-$lang->bug->osList['osx'] = 'OS X';
-$lang->bug->osList['unix'] = 'Unix';
-$lang->bug->osList['others'] = '其他';
+$lang->bug->osList[''] = '';
+$lang->bug->osList['all'] = '全部';
+$lang->bug->osList['windows'] = 'Windows';
+$lang->bug->osList['win11'] = 'Windows 11';
+$lang->bug->osList['win10'] = 'Windows 10';
+$lang->bug->osList['win8'] = 'Windows 8';
+$lang->bug->osList['win7'] = 'Windows 7';
+$lang->bug->osList['winxp'] = 'Windows XP';
+$lang->bug->osList['osx'] = 'Mac OS';
+$lang->bug->osList['android'] = 'Android';
+$lang->bug->osList['ios'] = 'IOS';
+$lang->bug->osList['linux'] = 'Linux';
+$lang->bug->osList['ubuntu'] = 'Ubuntu';
+$lang->bug->osList['chromeos'] = 'Chrome OS';
+$lang->bug->osList['fedora'] = 'Fedora';
+$lang->bug->osList['unix'] = 'Unix';
+$lang->bug->osList['others'] = '其他';
-$lang->bug->browserList[''] = '';
-$lang->bug->browserList['all'] = '全部';
-$lang->bug->browserList['ie'] = 'IE系列';
-$lang->bug->browserList['ie11'] = 'IE11';
-$lang->bug->browserList['ie10'] = 'IE10';
-$lang->bug->browserList['ie9'] = 'IE9';
-$lang->bug->browserList['ie8'] = 'IE8';
-$lang->bug->browserList['ie7'] = 'IE7';
-$lang->bug->browserList['ie6'] = 'IE6';
-$lang->bug->browserList['chrome'] = 'chrome';
-$lang->bug->browserList['firefox'] = 'firefox系列';
-$lang->bug->browserList['firefox4'] = 'firefox4';
-$lang->bug->browserList['firefox3'] = 'firefox3';
-$lang->bug->browserList['firefox2'] = 'firefox2';
-$lang->bug->browserList['opera'] = 'opera系列';
-$lang->bug->browserList['oprea11'] = 'opera11';
-$lang->bug->browserList['oprea10'] = 'opera10';
-$lang->bug->browserList['opera9'] = 'opera9';
-$lang->bug->browserList['safari'] = 'safari';
-$lang->bug->browserList['maxthon'] = '傲游';
-$lang->bug->browserList['uc'] = 'UC';
-$lang->bug->browserList['other'] = '其他';
+$lang->bug->browserList[''] = '';
+$lang->bug->browserList['all'] = '全部';
+$lang->bug->browserList['chrome'] = 'Chrome';
+$lang->bug->browserList['edge'] = 'Edge';
+$lang->bug->browserList['ie'] = 'IE系列';
+$lang->bug->browserList['ie11'] = 'IE11';
+$lang->bug->browserList['ie10'] = 'IE10';
+$lang->bug->browserList['ie9'] = 'IE9';
+$lang->bug->browserList['ie8'] = 'IE8';
+$lang->bug->browserList['firefox'] = 'firefox系列';
+$lang->bug->browserList['opera'] = 'Opera系列';
+$lang->bug->browserList['safari'] = 'safari';
+$lang->bug->browserList['360'] = '360浏览器';
+$lang->bug->browserList['qq'] = 'QQ浏览器';
+$lang->bug->browserList['other'] = '其他';
$lang->bug->typeList[''] = '';
$lang->bug->typeList['codeerror'] = '代码错误';
@@ -432,7 +420,6 @@ $lang->bug->featureBar['browse']['assignedbyme'] = $lang->bug->assignedByMe;
$lang->bug->featureBar['browse']['unresolved'] = $lang->bug->unResolved;
$lang->bug->featureBar['browse']['more'] = $lang->more;
-
$lang->bug->moreSelects['unconfirmed'] = $lang->bug->unconfirmed;
$lang->bug->moreSelects['assigntonull'] = $lang->bug->assignToNull;
$lang->bug->moreSelects['longlifebugs'] = $lang->bug->longLifeBugs;
diff --git a/module/bug/model.php b/module/bug/model.php
index 34c06d1800..803fba1cf8 100644
--- a/module/bug/model.php
+++ b/module/bug/model.php
@@ -76,6 +76,8 @@ class bugModel extends model
->trim('title')
->join('openedBuild', ',')
->join('mailto', ',')
+ ->join('os', ',')
+ ->join('browser', ',')
->remove('files,labels,uid,oldTaskID,contactListMenu,region,lane')
->get();
@@ -163,25 +165,24 @@ class bugModel extends model
$execution = 0;
$type = '';
$pri = 0;
- $os = '';
- $browser = '';
foreach($data->title as $i => $title)
{
+ $oses = array_filter($data->oses[$i]);
+ $browsers = array_filter($data->browsers[$i]);
+
if($data->modules[$i] != 'ditto') $module = (int)$data->modules[$i];
if($data->projects[$i] != 'ditto') $project = (int)$data->projects[$i];
if($data->executions[$i] != 'ditto') $execution = (int)$data->executions[$i];
if($data->types[$i] != 'ditto') $type = $data->types[$i];
if($data->pris[$i] != 'ditto') $pri = $data->pris[$i];
- if($data->oses[$i] != 'ditto') $os = $data->oses[$i];
- if($data->browsers[$i] != 'ditto') $browser = $data->browsers[$i];
$data->modules[$i] = (int)$module;
$data->projects[$i] = (int)$project;
$data->executions[$i] = (int)$execution;
$data->types[$i] = $type;
$data->pris[$i] = $pri;
- $data->oses[$i] = $os;
- $data->browsers[$i] = $browser;
+ $data->oses[$i] = implode(',', $oses);
+ $data->browsers[$i] = implode(',', $browsers);
}
/* Get bug data. */
@@ -689,6 +690,8 @@ class bugModel extends model
->stripTags($this->config->bug->editor->edit['id'], $this->config->allowedTags)
->setDefault('product,module,execution,story,task,duplicateBug,branch', 0)
->setDefault('openedBuild', '')
+ ->setDefault('os', '')
+ ->setDefault('browser', '')
->setDefault('plan', 0)
->setDefault('deadline', '0000-00-00')
->setDefault('resolvedDate', '0000-00-00 00:00:00')
@@ -699,6 +702,8 @@ class bugModel extends model
->join('openedBuild', ',')
->join('mailto', ',')
->join('linkBug', ',')
+ ->join('os', ',')
+ ->join('browser', ',')
->setIF($this->post->assignedTo != $oldBug->assignedTo, 'assignedDate', $now)
->setIF($this->post->resolvedBy != '' and $this->post->resolvedDate == '', 'resolvedDate', $now)
->setIF($this->post->resolution != '' and $this->post->resolvedDate == '', 'resolvedDate', $now)
@@ -716,6 +721,7 @@ class bugModel extends model
->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', '')
+ ->setIF($this->post->case === '', 'case', 0)
->remove('comment,files,labels,uid,contactListMenu')
->get();
@@ -805,8 +811,6 @@ class bugModel extends model
if($data->assignedTos[$bugID] == 'ditto') $data->assignedTos[$bugID] = isset($prev['assignedTo']) ? $prev['assignedTo'] : '';
if($data->resolvedBys[$bugID] == 'ditto') $data->resolvedBys[$bugID] = isset($prev['resolvedBy']) ? $prev['resolvedBy'] : '';
if($data->resolutions[$bugID] == 'ditto') $data->resolutions[$bugID] = isset($prev['resolution']) ? $prev['resolution'] : '';
- if($data->os[$bugID] == 'ditto') $data->os[$bugID] = isset($prev['os']) ? $prev['os'] : '';
- if($data->browsers[$bugID] == 'ditto') $data->browsers[$bugID] = isset($prev['browser']) ? $prev['browser'] : '';
if(isset($data->branches[$bugID]) and $data->branches[$bugID] == 'ditto') $data->branches[$bugID] = isset($prev['branch']) ? $prev['branch'] : 0;
$prev['type'] = $data->types[$bugID];
@@ -817,8 +821,6 @@ class bugModel extends model
$prev['assignedTo'] = $data->assignedTos[$bugID];
$prev['resolvedBy'] = $data->resolvedBys[$bugID];
$prev['resolution'] = $data->resolutions[$bugID];
- $prev['os'] = $data->os[$bugID];
- $prev['browser'] = $data->browsers[$bugID];
}
/* Initialize bugs from the post data.*/
@@ -828,6 +830,9 @@ class bugModel extends model
{
$oldBug = $oldBugs[$bugID];
+ $os = array_filter($data->os[$bugID]);
+ $browsers = array_filter($data->browsers[$bugID]);
+
$bug = new stdclass();
$bug->id = $bugID;
$bug->lastEditedBy = $this->app->user->account;
@@ -840,12 +845,12 @@ class bugModel extends model
$bug->plan = empty($data->plans[$bugID]) ? 0 : $data->plans[$bugID];
$bug->branch = empty($data->branches[$bugID]) ? 0 : $data->branches[$bugID];
$bug->module = $data->modules[$bugID];
- $bug->assignedTo = $data->assignedTos[$bugID];
+ $bug->assignedTo = $bug->status == 'closed' ? $oldBug->assignedTo : $data->assignedTos[$bugID];
$bug->deadline = $data->deadlines[$bugID];
$bug->resolvedBy = $data->resolvedBys[$bugID];
$bug->keywords = $data->keywords[$bugID];
- $bug->os = $data->os[$bugID];
- $bug->browser = $data->browsers[$bugID];
+ $bug->os = implode(',', $os);
+ $bug->browser = implode(',', $browsers);
$bug->resolution = $data->resolutions[$bugID];
$bug->duplicateBug = $data->duplicateBugs[$bugID] ? $data->duplicateBugs[$bugID] : $oldBug->duplicateBug;
@@ -1004,6 +1009,8 @@ class bugModel extends model
{
$now = helper::now();
$oldBug = $this->getById($bugID);
+ if($oldBug->status == 'closed') return false;
+
$bug = fixer::input('post')
->add('id', $bugID)
->setDefault('lastEditedBy', $this->app->user->account)
@@ -3170,9 +3177,25 @@ class bugModel extends model
}
}
- $bugLink = helper::createLink('bug', 'view', "bugID=$bug->id");
- $account = $this->app->user->account;
- $id = $col->id;
+ $bugLink = helper::createLink('bug', 'view', "bugID=$bug->id");
+ $account = $this->app->user->account;
+ $id = $col->id;
+ $os = '';
+ $browser = '';
+ $osList = explode(',', $bug->os);
+ $browserList = explode(',', $bug->browser);
+ foreach($osList as $value)
+ {
+ if(empty($value)) continue;
+ $os .= $this->lang->bug->osList[$value] . ',';
+ }
+ foreach($browserList as $value)
+ {
+ if(empty($value)) continue;
+ $browser .= $this->lang->bug->browserList[$value] . ',';
+ }
+ $os = trim($os, ',');
+ $browser = trim($browser, ',');
if($col->show)
{
$class = "c-$id";
@@ -3221,6 +3244,14 @@ class bugModel extends model
$class .= ' text-ellipsis';
$title = "title='" . $bug->resolvedBuild . "'";
break;
+ case 'os':
+ $class .= ' text-ellipsis';
+ $title = "title='" . $os . "'";
+ break;
+ case 'browser':
+ $class .= ' text-ellipsis';
+ $title = "title='" . $browser . "'";
+ break;
}
if($id == 'deadline' && isset($bug->delay) && $bug->status == 'active') $class .= ' delayed';
@@ -3319,10 +3350,10 @@ class bugModel extends model
echo $bug->keywords;
break;
case 'os':
- echo zget($this->lang->bug->osList, $bug->os);
+ echo $os;
break;
case 'browser':
- echo zget($this->lang->bug->browserList, $bug->browser);
+ echo $browser;
break;
case 'mailto':
$mailto = explode(',', $bug->mailto);
@@ -3528,7 +3559,7 @@ class bugModel extends model
$toStoryParams = "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id";
$menu .= $this->buildMenu('bug', 'confirmBug', $params, $bug, $type, 'ok', '', "iframe", true);
- if($type == 'view') $menu .= $this->buildMenu('bug', 'assignTo', $params, $bug, $type, '', '', "iframe", true);
+ if($type == 'view' and $bug->status != 'closed') $menu .= $this->buildMenu('bug', 'assignTo', $params, $bug, $type, '', '', "iframe", true);
$menu .= $this->buildMenu('bug', 'resolve', $params, $bug, $type, 'checked', '', "iframe showinonlybody", true);
$menu .= $this->buildMenu('bug', 'close', $params, $bug, $type, '', '', "text-danger iframe showinonlybody", true);
if($type == 'view') $menu .= $this->buildMenu('bug', 'activate', $params, $bug, $type, '', '', "text-success iframe showinonlybody", true);
diff --git a/module/bug/view/batchcreate.html.php b/module/bug/view/batchcreate.html.php
index 762b5634ed..5e167e4891 100755
--- a/module/bug/view/batchcreate.html.php
+++ b/module/bug/view/batchcreate.html.php
@@ -92,8 +92,6 @@ foreach(explode(',', $config->bug->create->requiredFields) as $field)
$executions += array('ditto' => $lang->bug->ditto);
$lang->bug->typeList += array('ditto' => $lang->bug->ditto);
$lang->bug->priList += array('ditto' => $lang->bug->ditto);
- $lang->bug->osList += array('ditto' => $lang->bug->ditto);
- $lang->bug->browserList += array('ditto' => $lang->bug->ditto);
?>
@@ -104,8 +102,6 @@ foreach(explode(',', $config->bug->create->requiredFields) as $field)
$executionID = $i == 1 ? $executionID : 'ditto';
$type = $i == 1 ? '' : 'ditto';
$pri = $i == 1 ? 0 : 'ditto';
- $os = $i == 1 ? '' : 'ditto';
- $browser = $i == 1 ? '' : 'ditto';
?>
|
@@ -139,8 +135,8 @@ foreach(explode(',', $config->bug->create->requiredFields) as $field)
typeBox' style='overflow:visible'> bug->typeList, $type, "class='form-control chosen'");?> |
priBox' style='overflow:visible'> bug->priList, $pri, "class='form-control'");?> |
severityBox' style='overflow:visible'>bug->severityList, '3', "class='form-control'");?> |
- osBox' style='overflow:visible'> bug->osList, $os, "class='form-control chosen'");?> |
- browserBox' style='overflow:visible'> bug->browserList, $browser, "class='form-control chosen'");?> |
+ osBox' style='overflow:visible'> bug->osList, '', "class='form-control chosen' multiple");?> |
+ browserBox' style='overflow:visible'> bug->browserList, '', "class='form-control chosen' multiple");?> |
keywordsBox'> |
loadModel('flow');
@@ -164,8 +160,6 @@ foreach(explode(',', $config->bug->create->requiredFields) as $field)
$executionID = $i - $nextStart == 0 ? $executionID : 'ditto';
$type = $i - $nextStart == 0 ? '' : 'ditto';
$pri = $i - $nextStart == 0 ? 0 : 'ditto';
- $os = $i - $nextStart == 0 ? '' : 'ditto';
- $browser = $i - $nextStart == 0 ? '' : 'ditto';
?>
|
@@ -199,8 +193,8 @@ foreach(explode(',', $config->bug->create->requiredFields) as $field)
typeBox' style='overflow:visible'> bug->typeList, $type, "class='form-control chosen'");?> |
priBox' style='overflow:visible'> bug->priList, $pri, "class='form-control'");?> |
severityBox' style='overflow:visible'>bug->severityList, '3', "class='form-control'");?> |
- osBox' style='overflow:visible'> bug->osList, $os, "class='form-control chosen'");?> |
- browserBox' style='overflow:visible'> bug->browserList, $browser, "class='form-control chosen'");?> |
+ osBox' style='overflow:visible'> bug->osList, '', "class='form-control chosen' multiple");?> |
+ browserBox' style='overflow:visible'> bug->browserList, '', "class='form-control chosen' multiple");?> |
keywordsBox'> |
loadModel('flow');
@@ -258,8 +252,8 @@ foreach(explode(',', $config->bug->create->requiredFields) as $field)
typeBox' style='overflow:visible'> bug->typeList, $type, "class='form-control chosen'");?> |
priBox' style='overflow:visible'> bug->priList, $pri, "class='form-control'");?> |
severityBox' style='overflow:visible'>bug->severityList, '3', "class='form-control'");?> |
- osBox' style='overflow:visible'> bug->osList, $os, "class='form-control chosen'");?> |
- browserBox' style='overflow:visible'> bug->browserList, $browser, "class='form-control chosen'");?> |
+ osBox' style='overflow:visible'> bug->osList, '', "class='form-control chosen' multiple");?> |
+ browserBox' style='overflow:visible'> bug->browserList, '', "class='form-control chosen' multiple");?> |
keywordsBox'> |
loadModel('flow');
@@ -307,8 +301,8 @@ foreach(explode(',', $config->bug->create->requiredFields) as $field)
typeBox' style='overflow:visible'> bug->typeList, $type, "class='form-control chosen'");?> |
priBox' style='overflow:visible'> bug->priList, $pri, "class='form-control'");?> |
severityBox' style='overflow:visible'>bug->severityList, '3', "class='form-control'");?> |
- osBox' style='overflow:visible'> bug->osList, $os, "class='form-control chosen'");?> |
- browserBox' style='overflow:visible'> bug->browserList, $browser, "class='form-control chosen'");?> |
+ osBox' style='overflow:visible'> bug->osList, '', "class='form-control chosen' multiple");?> |
+ browserBox' style='overflow:visible'> bug->browserList, '', "class='form-control chosen' multiple");?> |
keywordsBox'> |
loadModel('flow');
diff --git a/module/bug/view/batchedit.html.php b/module/bug/view/batchedit.html.php
index 68d5f4bd91..20ecce70db 100644
--- a/module/bug/view/batchedit.html.php
+++ b/module/bug/view/batchedit.html.php
@@ -132,10 +132,10 @@
unset($assignedToList['closed']);
}
?>
- ' style='overflow:visible'>assignedTo, "class='form-control picker-select' data-drop-width='135px'");?> |
+ ' style='overflow:visible'>status == 'closed' ? ucfirst($bug->assignedTo) : html::select("assignedTos[$bugID]", $assignedToList, $bug->assignedTo, "class='form-control picker-select' data-drop-width='135px'");?> |
' style='overflow:visible'>deadline) ? '' : $bug->deadline, "class='form-control form-date'");?> |
- >os, 'class=form-control');?> |
- >browser, 'class=form-control');?> |
+ >bug->osList, $bug->os, 'class="form-control chosen" multiple');?> |
+ >bug->browserList, $bug->browser, 'class="form-control chosen" multiple');?> |
>keywords, 'class=form-control');?> |
' style='overflow:visible'>resolvedBy, "class='form-control picker-select' data-drop-width='auto'");?> |
>
diff --git a/module/bug/view/browse.html.php b/module/bug/view/browse.html.php
index b3e3e81c65..248a4d7f4a 100644
--- a/module/bug/view/browse.html.php
+++ b/module/bug/view/browse.html.php
@@ -403,7 +403,7 @@ $currentBrowseType = isset($lang->bug->mySelects[$browseType]) && in_array($brow
-
+
6;?>
|
@@ -186,7 +194,7 @@ if($this->app->tab == 'project') js::set('objectID', $projectID);
bug->os?>
- bug->osList, $os, "class='form-control chosen'");?>
+ bug->osList, $os, "class='form-control chosen' multiple");?>
@@ -194,7 +202,7 @@ if($this->app->tab == 'project') js::set('objectID', $projectID);
bug->browser?>
- bug->browserList, $browser, "class='form-control chosen'");?>
+ bug->browserList, $browser, "class='form-control chosen' multiple");?>
diff --git a/module/bug/view/edit.html.php b/module/bug/view/edit.html.php
index 94596414f9..6e41532080 100644
--- a/module/bug/view/edit.html.php
+++ b/module/bug/view/edit.html.php
@@ -153,10 +153,14 @@ if($this->app->tab == 'project') js::set('objectID', $bug->project);
| bug->assignedTo;?> |
+ status == 'closed'):?>
+ assignedTo);?>
+
assignedTo, "class='form-control chosen'");?>
bug->allUsers, "class='btn btn-default' onclick='loadAllUsers()' data-toggle='tooltip'");?>
+
|
@@ -173,11 +177,11 @@ if($this->app->tab == 'project') js::set('objectID', $bug->project);
| bug->os;?> |
- bug->osList, $bug->os, "class='form-control chosen'");?> |
+ bug->osList, $bug->os, "class='form-control chosen' multiple");?> |
| bug->browser;?> |
- bug->browserList, $bug->browser, "class='form-control chosen'");?> |
+ bug->browserList, $bug->browser, "class='form-control chosen' multiple");?> |
| bug->keywords;?> |
@@ -282,7 +286,7 @@ if($this->app->tab == 'project') js::set('objectID', $bug->project);
bug->linkBug;?> |
createLink('bug', 'linkBugs', "bugID=$bug->id", '', true), $lang->bug->linkBugs, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'");?> |
-
+
linkBugTitles)) echo 'class="hidden"';?>>
|
@@ -306,12 +310,10 @@ if($this->app->tab == 'project') js::set('objectID', $bug->project);
| bug->testtask;?> |
testtask, 'class="form-control chosen"');?> |
|
- case):?>
| bug->fromCase;?> |
- case, 'class="form-control"');?> |
+ case, 'class="form-control picker-select"');?> |
-
diff --git a/module/bug/view/linkbugs.html.php b/module/bug/view/linkbugs.html.php
index 6f883c20ae..ba9f34c460 100644
--- a/module/bug/view/linkbugs.html.php
+++ b/module/bug/view/linkbugs.html.php
@@ -90,7 +90,7 @@ $(function()
output += checkbox;
});
$.closeModal();
- parent.$('#linkBugsBox').html(output);
+ parent.$('#linkBugsBox').html(output).closest('tr').removeClass('hidden');
return false;
});
});
diff --git a/module/bug/view/view.html.php b/module/bug/view/view.html.php
index 46013377fa..2b78e900a7 100644
--- a/module/bug/view/view.html.php
+++ b/module/bug/view/view.html.php
@@ -232,11 +232,29 @@
| bug->os;?> |
- bug->osList[$bug->os];?> |
+
+ os);?>
+
+
+
+ " . zget($lang->bug->osList, $os) . "";?>
+
+
+
+ |
| bug->browser;?> |
- bug->browserList[$bug->browser];?> |
+
+ browser);?>
+
+
+
+ " . zget($lang->bug->browserList, $browser) . "";?>
+
+
+
+ |
| bug->keywords;?> |
diff --git a/module/common/model.php b/module/common/model.php
index 98e5f4df58..24e444425c 100644
--- a/module/common/model.php
+++ b/module/common/model.php
@@ -66,7 +66,7 @@ class commonModel extends model
if($rawModule == 'execution')
{
$executionID = $objectID;
- $execution = $this->dao->select('id, project, grade, parent')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch();
+ $execution = $this->dao->select('id, project, grade, parent, status, deleted')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch();
$this->syncExecutionByChild($execution);
$project = $this->syncProjectStatus($execution);
$this->syncProgramStatus($project);
@@ -153,7 +153,7 @@ class commonModel extends model
$today = helper::today();
$parentExecution = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->eq($parentExecutionID)->fetch();
- if($parentExecution->status == 'wait')
+ if($execution->deleted == '0' and $execution->status == 'doing' and in_array($parentExecution->status, array('wait', 'closed')))
{
$this->dao->update(TABLE_EXECUTION)
->set('status')->eq('doing')
@@ -163,6 +163,32 @@ class commonModel extends model
$this->loadModel('action')->create('execution', $parentExecutionID, 'syncexecutionbychild');
}
+ if($parentExecution->type == 'stage')
+ {
+ $childExecutions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('parent')->eq($parentExecutionID)->andWhere('deleted')->eq('0')->fetchAll('id');
+ if($execution->deleted == '1' and count($childExecutions) > 0)
+ {
+ $childWait = true;
+ $childClosed = true;
+ foreach($childExecutions as $childExecution)
+ {
+ if($childExecution->status != 'wait') $childWait = false;
+ if($childExecution->status != 'closed') $childClosed = false;
+ }
+
+ if($childWait and $parentExecution->status != 'wait')
+ {
+ $this->dao->update(TABLE_EXECUTION)->set('status')->eq('wait')->where('id')->eq($parentExecutionID)->exec();
+ $this->loadModel('action')->create('execution', $parentExecutionID, 'waitbychilddelete');
+ }
+ if($childClosed and $parentExecution->status != 'closed')
+ {
+ $this->dao->update(TABLE_EXECUTION)->set('status')->eq('closed')->where('id')->eq($parentExecutionID)->exec();
+ $this->loadModel('action')->create('execution', $parentExecutionID, 'closebychilddelete');
+ }
+ }
+ }
+
return $parentExecution;
}
@@ -185,6 +211,11 @@ class commonModel extends model
{
$this->dao->update(TABLE_EXECUTION)->set('status')->eq('doing')->set('realBegan')->eq($today)->where('id')->eq($execution->id)->exec();
$this->loadModel('action')->create('execution', $execution->id, 'syncexecution');
+ if($execution->parent)
+ {
+ $execution = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->eq($execution->id)->fetch(); // Get updated execution.
+ $this->syncExecutionByChild($execution);
+ }
}
return $execution;
}
diff --git a/module/execution/config.php b/module/execution/config.php
index 57c97990ff..10694f9e4f 100644
--- a/module/execution/config.php
+++ b/module/execution/config.php
@@ -1,10 +1,11 @@
execution = new stdclass();
-$config->execution->defaultWorkhours = '7.0';
-$config->execution->orderBy = 'isDone,status,order_desc';
-$config->execution->maxBurnDay = '31';
-$config->execution->weekend = '2';
-$config->execution->ownerFields = array('PO', 'PM', 'QD', 'RD');
+$config->execution->defaultWorkhours = '7.0';
+$config->execution->orderBy = 'isDone,status,order_desc';
+$config->execution->maxBurnDay = '31';
+$config->execution->weekend = '2';
+$config->execution->ownerFields = array('PO', 'PM', 'QD', 'RD');
+$config->execution->defaultBurnPeriod = 30;
$config->execution->list = new stdclass();
$config->execution->list->exportFields = 'id,name,projectName,code,PM,begin,end,status,totalEstimate,totalConsumed,totalLeft,progress';
diff --git a/module/execution/control.php b/module/execution/control.php
index 282e2bedee..c8b7475689 100644
--- a/module/execution/control.php
+++ b/module/execution/control.php
@@ -469,10 +469,13 @@ class execution extends control
*
* @param int $executionID
* @param int $fromExecution
+ * @param int $recTotal
+ * @param int $recPerPage
+ * @param int $pageID
* @access public
* @return void
*/
- public function importTask($toExecution, $fromExecution = 0)
+ public function importTask($toExecution, $fromExecution = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
if(!empty($_POST))
{
@@ -509,13 +512,20 @@ class execution extends control
$tasks2Imported = zget($tasks, $fromExecution, array());
}
+ /* Pager. */
+ $this->app->loadClass('pager', $static = true);
+ $recTotal = count($tasks2Imported);
+ $pager = new pager($recTotal, $recPerPage, $pageID);
+ $tasks2ImportedList = array_chunk($tasks2Imported, $pager->recPerPage, true);
+
/* Save session. */
$this->app->session->set('taskList', $this->app->getURI(true), 'execution');
$this->view->title = $execution->name . $this->lang->colon . $this->lang->execution->importTask;
+ $this->view->pager = $pager;
$this->view->position[] = html::a(inlink('browse', "executionID=$toExecution"), $execution->name);
$this->view->position[] = $this->lang->execution->importTask;
- $this->view->tasks2Imported = $tasks2Imported;
+ $this->view->tasks2Imported = empty($tasks2ImportedList) ? $tasks2ImportedList : $tasks2ImportedList[$pageID - 1];
$this->view->executions = $executions;
$this->view->executionID = $execution->id;
$this->view->fromExecution = $fromExecution;
@@ -632,7 +642,7 @@ class execution extends control
$this->config->bug->search['params']['product']['values'] = array(''=>'');
}
$this->config->bug->search['params']['execution']['values'] = array(''=>'') + $executions + array('all'=>$this->lang->execution->aboveAllExecution);
- $this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs(array_keys($products));
+ $this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs(array_keys($products));
$this->config->bug->search['module'] = 'importBug';
$this->config->bug->search['params']['confirmed']['values'] = array('' => '') + $this->lang->bug->confirmedList;
@@ -2179,15 +2189,9 @@ class execution extends control
if(!empty($_POST))
{
- $this->loadModel('action');
- $changes = $this->execution->activate($executionID);
+ $this->execution->activate($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
- if($this->post->comment != '' or !empty($changes))
- {
- $actionID = $this->action->create($this->objectType, $executionID, 'Activated', $this->post->comment);
- $this->action->logHistory($actionID, $changes);
- }
$this->executeHooks($executionID);
if(isonlybody() and $from == 'kanban')
{
@@ -2230,16 +2234,10 @@ class execution extends control
if(!empty($_POST))
{
- $this->loadModel('action');
$this->execution->computeBurn($executionID);
- $changes = $this->execution->close($executionID);
+ $this->execution->close($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
- if($this->post->comment != '' or !empty($changes))
- {
- $actionID = $this->action->create($this->objectType, $executionID, 'Closed', $this->post->comment);
- $this->action->logHistory($actionID, $changes);
- }
$this->executeHooks($executionID);
if(isonlybody() and $from == 'kanban')
{
@@ -2552,7 +2550,7 @@ class execution extends control
{
$this->loadModel('project');
$projects = $this->project->getPairsByProgram('', 'noclosed');
- $executions = $this->project->getStats(0, 'all', 0, 0, 30, 'id_desc');
+ $executions = $this->execution->getStatData(0, 'all', 0, 0, false, '', 'id_desc');
$teams = $this->dao->select('root,account')->from(TABLE_TEAM)
->where('root')->in($this->app->user->view->sprints)
@@ -2890,6 +2888,7 @@ class execution extends control
$this->dao->update(TABLE_EXECUTION)->set('deleted')->eq(1)->where('id')->eq($executionID)->exec();
$this->loadModel('action')->create('execution', $executionID, 'deleted', '', ACTIONMODEL::CAN_UNDELETED);
$this->execution->updateUserView($executionID);
+ $this->loadModel('common')->syncPPEStatus($executionID);
$this->session->set('execution', '');
$message = $this->executeHooks($executionID);
@@ -3731,7 +3730,7 @@ class execution extends control
$this->view->title = $this->lang->execution->allExecutions;
$this->view->position[] = $this->lang->execution->allExecutions;
- $executionStats = $this->project->getStats(0, $status, $productID, 0, 30, $orderBy, $pager, false, $queryID);
+ $executionStats = $this->execution->getStatData(0, $status, $productID, 0, false, $queryID, $orderBy, $pager);
$parentIdList = array();
foreach($executionStats as $execution)
@@ -3742,7 +3741,7 @@ class execution extends control
$parents = array();
if($parentIdList) $parents = $this->execution->getByIdList($parentIdList);
- $allExecutionsNum = $this->project->getStats(0, 'all');
+ $allExecutionsNum = $this->execution->getStatData(0, 'all');
$this->view->allExecutionsNum = count($allExecutionsNum);
$this->view->executionStats = $executionStats;
@@ -3853,7 +3852,7 @@ class execution extends control
}
$project = $this->project->getByID($projectID);
- $executionStats = $this->project->getStats($projectID, $status == 'byproduct' ? 'all' : $status, $productID, 0, 30, 'id_asc');
+ $executionStats = $this->execution->getStatData($projectID, $status == 'byproduct' ? 'all' : $status, $productID, 0, false, '', 'id_asc');
if(isset($project->model) and $project->model == 'waterfall')
{
$stageList = array();
diff --git a/module/execution/js/bug.js b/module/execution/js/bug.js
index ce07804c40..7a73ac2808 100644
--- a/module/execution/js/bug.js
+++ b/module/execution/js/bug.js
@@ -2,4 +2,27 @@ $(function()
{
if(browseType != 'bysearch') $('#module' + param).closest('li').addClass('active');
if($('#bugList thead th.c-title').width() < 150) $('#bugList thead th.c-title').width(150);
+
+ $('#bugList').on('change', "[name='bugIDList[]']", checkClosed);
});
+
+/**
+ * Closed bugs are not assignable.
+ *
+ * @access public
+ * @return void
+ */
+function checkClosed()
+{
+ var disabledAssigned = $('#bugList tr.checked .c-assignedTo a').length > 0 ? true : false;
+ $('#bugList tr.checked .c-assignedTo a').each(function()
+ {
+ if(!$(this).hasClass('disabled'))
+ {
+ disabledAssigned = false;
+ return false;
+ }
+ });
+
+ $('#mulAssigned').prop('disabled', disabledAssigned);
+}
diff --git a/module/execution/lang/de.php b/module/execution/lang/de.php
index 7ab7927d18..1cc9cb51bd 100644
--- a/module/execution/lang/de.php
+++ b/module/execution/lang/de.php
@@ -536,10 +536,19 @@ $lang->execution->treeLevel['task'] = 'Aufgabe anzeigen';
$lang->execution->treeLevel['story'] = 'Story anzeigen';
$lang->execution->action = new stdclass();
-$lang->execution->action->opened = '$date, created by $actor. $extra' . "\n";
-$lang->execution->action->managed = '$date, managed by $actor. $extra' . "\n";
-$lang->execution->action->edited = '$date, edited by $actor. $extra' . "\n";
-$lang->execution->action->extra = 'Linked products is %s.';
+$lang->execution->action->opened = '$date, created by $actor. $extra' . "\n";
+$lang->execution->action->managed = '$date, managed by $actor. $extra' . "\n";
+$lang->execution->action->edited = '$date, edited by $actor. $extra' . "\n";
+$lang->execution->action->extra = 'Linked products is %s.';
+$lang->execution->action->startbychildactivate = '$date, activating the sub stage sets the execution status as Ongoing.' . "\n";
+$lang->execution->action->waitbychilddelete = '$date, deleting the sub stage sets the execution status as waitting.' . "\n";
+$lang->execution->action->closebychilddelete = '$date, deleting the sub stage sets the execution status as closing.' . "\n";
+$lang->execution->action->closebychildclose = '$date, closing the sub stage sets the execution status as closing.' . "\n";
+
+$lang->execution->startbychildactivate = 'activated';
+$lang->execution->waitbychilddelete = 'stop';
+$lang->execution->closebychilddelete = 'closed';
+$lang->execution->closebychildclose = 'closed';
$lang->execution->statusColorList = array();
$lang->execution->statusColorList['wait'] = '#0991FF';
diff --git a/module/execution/lang/en.php b/module/execution/lang/en.php
index cf11af6af7..4c34595681 100644
--- a/module/execution/lang/en.php
+++ b/module/execution/lang/en.php
@@ -536,10 +536,19 @@ $lang->execution->treeLevel['task'] = 'Stories&Tasks';
$lang->execution->treeLevel['story'] = 'Only Stories';
$lang->execution->action = new stdclass();
-$lang->execution->action->opened = '$date, created by $actor. $extra' . "\n";
-$lang->execution->action->managed = '$date, managed by $actor. $extra' . "\n";
-$lang->execution->action->edited = '$date, edited by $actor. $extra' . "\n";
-$lang->execution->action->extra = 'Linked products is %s.';
+$lang->execution->action->opened = '$date, created by $actor. $extra' . "\n";
+$lang->execution->action->managed = '$date, managed by $actor. $extra' . "\n";
+$lang->execution->action->edited = '$date, edited by $actor. $extra' . "\n";
+$lang->execution->action->extra = 'Linked products is %s.';
+$lang->execution->action->startbychildactivate = '$date, activating the sub stage sets the execution status as Ongoing.' . "\n";
+$lang->execution->action->waitbychilddelete = '$date, deleting the sub stage sets the execution status as waitting.' . "\n";
+$lang->execution->action->closebychilddelete = '$date, deleting the sub stage sets the execution status as closing.' . "\n";
+$lang->execution->action->closebychildclose = '$date, closing the sub stage sets the execution status as closing.' . "\n";
+
+$lang->execution->startbychildactivate = 'activated';
+$lang->execution->waitbychilddelete = 'stop';
+$lang->execution->closebychilddelete = 'closed';
+$lang->execution->closebychildclose = 'closed';
$lang->execution->statusColorList = array();
$lang->execution->statusColorList['wait'] = '#0991FF';
diff --git a/module/execution/lang/fr.php b/module/execution/lang/fr.php
index 6dea9a32aa..3e71a37d6b 100644
--- a/module/execution/lang/fr.php
+++ b/module/execution/lang/fr.php
@@ -536,10 +536,19 @@ $lang->execution->treeLevel['task'] = 'Stories&Tâches';
$lang->execution->treeLevel['story'] = 'Seulement Stories';
$lang->execution->action = new stdclass();
-$lang->execution->action->opened = '$date, created by $actor. $extra' . "\n";
-$lang->execution->action->managed = '$date, managed by $actor. $extra' . "\n";
-$lang->execution->action->edited = '$date, edited by $actor. $extra' . "\n";
-$lang->execution->action->extra = 'Linked products is %s.';
+$lang->execution->action->opened = '$date, created by $actor. $extra' . "\n";
+$lang->execution->action->managed = '$date, managed by $actor. $extra' . "\n";
+$lang->execution->action->edited = '$date, edited by $actor. $extra' . "\n";
+$lang->execution->action->extra = 'Linked products is %s.';
+$lang->execution->action->startbychildactivate = '$date, activating the sub stage sets the execution status as Ongoing.' . "\n";
+$lang->execution->action->waitbychilddelete = '$date, deleting the sub stage sets the execution status as waitting.' . "\n";
+$lang->execution->action->closebychilddelete = '$date, deleting the sub stage sets the execution status as closing.' . "\n";
+$lang->execution->action->closebychildclose = '$date, closing the sub stage sets the execution status as closing.' . "\n";
+
+$lang->execution->startbychildactivate = 'activated';
+$lang->execution->waitbychilddelete = 'stop';
+$lang->execution->closebychilddelete = 'closed';
+$lang->execution->closebychildclose = 'closed';
$lang->execution->statusColorList = array();
$lang->execution->statusColorList['wait'] = '#0991FF';
diff --git a/module/execution/lang/zh-cn.php b/module/execution/lang/zh-cn.php
index a064ccd15c..046e868a9d 100644
--- a/module/execution/lang/zh-cn.php
+++ b/module/execution/lang/zh-cn.php
@@ -536,10 +536,19 @@ $lang->execution->treeLevel['task'] = '全部显示';
$lang->execution->treeLevel['story'] = "只看{$lang->SRCommon}";
$lang->execution->action = new stdclass();
-$lang->execution->action->opened = '$date, 由 $actor 创建。$extra' . "\n";
-$lang->execution->action->managed = '$date, 由 $actor 维护。$extra' . "\n";
-$lang->execution->action->edited = '$date, 由 $actor 编辑。$extra' . "\n";
-$lang->execution->action->extra = '相关产品为 %s。';
+$lang->execution->action->opened = '$date, 由 $actor 创建。$extra' . "\n";
+$lang->execution->action->managed = '$date, 由 $actor 维护。$extra' . "\n";
+$lang->execution->action->edited = '$date, 由 $actor 编辑。$extra' . "\n";
+$lang->execution->action->extra = '相关产品为 %s。';
+$lang->execution->action->startbychildactivate = '$date, 系统判断由于子阶段激活,将' . $lang->executionCommon . '状态置为进行中。' . "\n";
+$lang->execution->action->waitbychilddelete = '$date, 系统判断由于子阶段删除,将' . $lang->executionCommon . '状态置为未开始。' . "\n";
+$lang->execution->action->closebychilddelete = '$date, 系统判断由于子阶段删除,将' . $lang->executionCommon . '状态置为已关闭。' . "\n";
+$lang->execution->action->closebychildclose = '$date, 系统判断由于子阶段关闭,将' . $lang->executionCommon . '状态置为已关闭。' . "\n";
+
+$lang->execution->startbychildactivate = '激活了';
+$lang->execution->waitbychilddelete = '停止了';
+$lang->execution->closebychilddelete = '关闭了';
+$lang->execution->closebychildclose = '关闭了';
$lang->execution->statusColorList = array();
$lang->execution->statusColorList['wait'] = '#0991FF';
diff --git a/module/execution/model.php b/module/execution/model.php
index fdfc3479c8..39756b26b9 100644
--- a/module/execution/model.php
+++ b/module/execution/model.php
@@ -1005,17 +1005,24 @@ class executionModel extends model
}
}
+ $changes = common::createChanges($oldExecution, $execution);
+ if($this->post->comment != '' or !empty($changes))
+ {
+ $this->loadModel('action');
+ $actionID = $this->action->create('execution', $executionID, 'Activated', $this->post->comment);
+ $this->action->logHistory($actionID, $changes);
+ }
+
/* Update the status of the parent stage. */
if($oldExecution->type == 'stage')
{
$parent = $this->getByID($oldExecution->parent);
- if($parent->type == 'stage' and $parent->status == 'closed')
+ if($parent->type == 'stage' and in_array($parent->status, array('closed', 'wait')))
{
$this->dao->update(TABLE_EXECUTION)->set('status')->eq('doing')->where('id')->eq($parent->id)->exec();
+ $this->loadModel('action')->create('execution', $parent->id, 'startbychildactivate');
}
}
-
- if(!dao::isError()) return common::createChanges($oldExecution, $execution);
}
/**
@@ -1058,6 +1065,15 @@ class executionModel extends model
if(!dao::isError())
{
+
+ $changes = common::createChanges($oldExecution, $execution);
+ if($this->post->comment != '' or !empty($changes))
+ {
+ $this->loadModel('action');
+ $actionID = $this->action->create('execution', $executionID, 'Closed', $this->post->comment);
+ $this->action->logHistory($actionID, $changes);
+ }
+
/* Update the status of the parent stage. */
if($oldExecution->type == 'stage')
{
@@ -1071,12 +1087,15 @@ class executionModel extends model
if($childExecution->status != 'closed') $isClosed = false;
}
- if($isClosed) $this->dao->update(TABLE_EXECUTION)->set('status')->eq('closed')->where('id')->eq($parent->id)->exec();
+ if($isClosed)
+ {
+ $this->dao->update(TABLE_EXECUTION)->set('status')->eq('closed')->where('id')->eq($parent->id)->exec();
+ $this->loadModel('action')->create('execution', $parent->id, 'closebychildclose');
+ }
}
}
$this->loadModel('score')->create('execution', 'close', $oldExecution);
- return common::createChanges($oldExecution, $execution);
}
}
@@ -1409,6 +1428,129 @@ class executionModel extends model
->fetchPairs('id', 'id');
}
+ /**
+ * Get execution stat data.
+
+ * @param int $projectID
+ * @param string $browseType all|undone|wait|doing|suspended|closed|involved|bySearch|review
+ * @param int $productID
+ * @param int $branch
+ * @param bool $withTasks
+ * @param string|int $param skipParent
+ * @param string $orderBy
+ * @param object $pager
+ * @access public
+ * @return array
+ */
+ public function getStatData($projectID = 0, $browseType = 'undone', $productID = 0, $branch = 0, $withTasks = false, $param = '', $orderBy = 'id_asc', $pager = null)
+ {
+ /* Construct the query SQL at search executions. */
+ $executionQuery = '';
+ if($browseType == 'bySearch')
+ {
+ $queryID = (int)$param;
+ if($queryID)
+ {
+ $query = $this->loadModel('search')->getQuery($queryID);
+ if($query)
+ {
+ $this->session->set('executionQuery', $query->sql);
+ $this->session->set('executionForm', $query->form);
+ }
+ }
+ if($this->session->executionQuery == false) $this->session->set('executionQuery', ' 1 = 1');
+
+ $executionQuery = $this->session->executionQuery;
+ $allProject = "`project` = 'all'";
+
+ if(strpos($executionQuery, $allProject) !== false) $executionQuery = str_replace($allProject, '1', $executionQuery);
+ $executionQuery = preg_replace('/(`\w*`)/', 't1.$1',$executionQuery);
+ }
+
+ /* Get involved executions. */
+ $myExecutionIDList = array();
+ if($browseType == 'involved')
+ {
+ $myExecutionIDList = $this->dao->select('root')->from(TABLE_TEAM)
+ ->where('account')->eq($this->app->user->account)
+ ->andWhere('type')->eq('execution')
+ ->fetchPairs();
+ }
+
+ $executions = $this->dao->select('t1.*,t2.name projectName, t2.model as projectModel')->from(TABLE_EXECUTION)->alias('t1')
+ ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
+ ->beginIF($productID)->leftJoin(TABLE_PROJECTPRODUCT)->alias('t3')->on('t1.id=t3.project')->fi()
+ ->where('t1.type')->in('sprint,stage,kanban')
+ ->andWhere('t1.deleted')->eq('0')
+ ->andWhere('t1.vision')->eq($this->config->vision)
+ ->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->sprints)->fi()
+ ->beginIF(!empty($executionQuery))->andWhere($executionQuery)->fi()
+ ->beginIF($productID)->andWhere('t3.product')->eq($productID)->fi()
+ ->beginIF($projectID)->andWhere('t1.project')->eq($projectID)->fi()
+ ->beginIF(!in_array($browseType, array('all', 'undone', 'involved', 'review', 'bySearch')))->andWhere('t1.status')->eq($browseType)->fi()
+ ->beginIF($browseType == 'undone')->andWhere('t1.status')->notIN('done,closed')->fi()
+ ->beginIF($browseType == 'review')
+ ->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.reviewers)")
+ ->andWhere('t1.reviewStatus')->eq('doing')
+ ->fi()
+ ->orderBy($orderBy)
+ ->page($pager)
+ ->fetchAll('id');
+
+ $hours = $this->loadModel('project')->computerProgress($executions);
+ $burns = $this->getBurnData($executions);
+
+ if($withTasks) $executionTasks = $this->getTaskGroupByExecution(array_keys($executions));
+
+ /* Process executions. */
+ $this->app->loadConfig('task');
+
+ $emptyHour = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'progress' => 0);
+ $today = helper::today();
+ foreach($executions as $key => $execution)
+ {
+ /* Process the end time. */
+ $execution->end = date(DT_DATE1, strtotime($execution->end));
+
+ /* Judge whether the execution is delayed. */
+ if($execution->status != 'done' and $execution->status != 'closed' and $execution->status != 'suspended')
+ {
+ $delay = helper::diffDate($today, $execution->end);
+ if($delay > 0) $execution->delay = $delay;
+ }
+
+ /* Process the burns. */
+ $execution->burns = array();
+ $burnData = isset($burns[$execution->id]) ? $burns[$execution->id] : array();
+ foreach($burnData as $data) $execution->burns[] = $data->value;
+
+ /* Process the hours. */
+ $execution->hours = isset($hours[$execution->id]) ? $hours[$execution->id] : (object)$emptyHour;
+
+ if(isset($executionTasks) and isset($executionTasks[$execution->id]))
+ {
+ $tasks = array_chunk($executionTasks[$execution->id], $this->config->task->defaultLoadCount, true);
+ $execution->tasks = $tasks[0];
+ }
+
+ /* In the case of the waterfall model, calculate the sub-stage. */
+ if($param == 'skipParent')
+ {
+ if($execution->parent < 0 and $execution->type == 'stage') unset($executions[$key]);
+ if($this->config->systemMode == 'new' and $execution->projectName) $execution->name = $execution->projectName . ' / ' . $execution->name;
+ }
+ else
+ {
+ if(isset($executions[$execution->parent]) and $execution->type == 'stage')
+ {
+ $executions[$execution->parent]->children[$key] = $execution;
+ unset($executions[$key]);
+ }
+ }
+ }
+ return array_values($executions);
+ }
+
/**
* Get executions by project.
*
@@ -1800,6 +1942,38 @@ class executionModel extends model
return $tasks;
}
+ /**
+ * Get the task data group by execution id list.
+ *
+ * @param array $executionIdList
+ * @access public
+ * @return array
+ */
+ public function getTaskGroupByExecution($executionIdList = array())
+ {
+ if(empty($executionIdList)) return array();
+ $executionTasks = $this->dao->select('*')->from(TABLE_TASK)
+ ->where('deleted')->eq(0)
+ ->andWhere('status')->notin('closed,cancel')
+ ->andWhere('execution')->in($executionIdList)
+ ->orderBy('id_asc')
+ ->fetchGroup('execution', 'id');
+
+ foreach($executionTasks as $executionID => $tasks)
+ {
+ foreach($tasks as $task)
+ {
+ if($task->parent > 0 and isset($executionTasks[$executionID][$task->parent]))
+ {
+ $executionTasks[$executionID][$task->parent]->children[$task->id] = $task;
+ unset($executionTasks[$executionID][$task->id]);
+ }
+ }
+ }
+
+ return $executionTasks;
+ }
+
/**
* Get the execution by ID.
*
@@ -3236,6 +3410,47 @@ class executionModel extends model
return $burnData;
}
+ /**
+ * Get execution burn data.
+ *
+ * @param array $executions
+ * @access public
+ * @return array
+ */
+ public function getBurnData($executions)
+ {
+ if(empty($executions)) return array();
+
+ /* Get burndown charts datas. */
+ $burns = $this->dao->select('execution, date AS name, `left` AS value')
+ ->from(TABLE_BURN)
+ ->where('execution')->in(array_keys($executions))
+ ->andWhere('task')->eq(0)
+ ->orderBy('date desc')
+ ->fetchGroup('execution', 'name');
+
+ foreach($burns as $executionID => $executionBurns)
+ {
+ /* If executionBurns > $itemCounts, split it, else call processBurnData() to pad burns. */
+ $begin = $executions[$executionID]->begin;
+ $end = $executions[$executionID]->end;
+ if(helper::isZeroDate($begin)) $begin = $executions[$executionID]->openedDate;
+ $executionBurns = $this->processBurnData($executionBurns, $this->config->execution->defaultBurnPeriod, $begin, $end);
+
+ /* Shorter names. */
+ foreach($executionBurns as $executionBurn)
+ {
+ $executionBurn->name = substr($executionBurn->name, 5);
+ unset($executionBurn->execution);
+ }
+
+ ksort($executionBurns);
+ $burns[$executionID] = $executionBurns;
+ }
+
+ return $burns;
+ }
+
/**
* Process burndown datas when the sets is smaller than the itemCounts.
*
diff --git a/module/execution/view/bug.html.php b/module/execution/view/bug.html.php
index 0dcf920a7a..606c7aee68 100644
--- a/module/execution/view/bug.html.php
+++ b/module/execution/view/bug.html.php
@@ -162,7 +162,7 @@
diff --git a/module/install/lang/de.php b/module/install/lang/de.php
index 5c7d739bf6..ac375fa988 100644
--- a/module/install/lang/de.php
+++ b/module/install/lang/de.php
@@ -162,7 +162,7 @@ $lang->install->account = 'Admin Konto';
$lang->install->password = 'Admin Passwort';
$lang->install->placeholder = new stdclass();
-$lang->install->placeholder->password = 'Combination of letters, numbers and special symbols, at least 6 characters';
+$lang->install->placeholder->password = 'The Password should be ≥ 6 characters, combination of uppercase, lowercase letters and numbers.';
$lang->install->errorEmpty['company'] = "{$lang->install->company} should not be blank.";
$lang->install->errorEmpty['account'] = "{$lang->install->account} should not be blank.";
diff --git a/module/install/lang/en.php b/module/install/lang/en.php
index cfba4afa3c..ae67f6612d 100644
--- a/module/install/lang/en.php
+++ b/module/install/lang/en.php
@@ -162,7 +162,7 @@ $lang->install->account = 'Admin Account';
$lang->install->password = 'Admin Password';
$lang->install->placeholder = new stdclass();
-$lang->install->placeholder->password = 'Combination of letters, numbers and special symbols, at least 6 characters';
+$lang->install->placeholder->password = 'The Password should be ≥ 6 characters, combination of uppercase, lowercase letters and numbers.';
$lang->install->errorEmpty['company'] = "{$lang->install->company} should not be blank.";
$lang->install->errorEmpty['account'] = "{$lang->install->account} should not be blank.";
diff --git a/module/install/lang/fr.php b/module/install/lang/fr.php
index c5d7bfad74..ffa59af709 100644
--- a/module/install/lang/fr.php
+++ b/module/install/lang/fr.php
@@ -162,7 +162,7 @@ $lang->install->account = 'Compte Admin';
$lang->install->password = 'Mot de Passe Admin';
$lang->install->placeholder = new stdclass();
-$lang->install->placeholder->password = 'Combination of letters, numbers and special symbols, at least 6 characters';
+$lang->install->placeholder->password = 'Le mot de passe doit ≥ 6 caractères, combinaison de lettres majuscules, minuscules et de chiffres.';
$lang->install->errorEmpty['company'] = "{$lang->install->company} should not be blank.";
$lang->install->errorEmpty['account'] = "{$lang->install->account} should not be blank.";
diff --git a/module/install/lang/zh-cn.php b/module/install/lang/zh-cn.php
index 0b4042c8b7..14dd84cb04 100644
--- a/module/install/lang/zh-cn.php
+++ b/module/install/lang/zh-cn.php
@@ -162,7 +162,7 @@ $lang->install->account = '管理员帐号';
$lang->install->password = '管理员密码';
$lang->install->placeholder = new stdclass();
-$lang->install->placeholder->password = '六位及以上,包含数字、字母和特殊字符';
+$lang->install->placeholder->password = '6位及以上,包含大小写字母,数字。';
$lang->install->errorEmpty['company'] = "{$lang->install->company}不能为空";
$lang->install->errorEmpty['account'] = "{$lang->install->account}不能为空";
diff --git a/module/install/model.php b/module/install/model.php
index 8d4b8d65c3..4d4d302c96 100644
--- a/module/install/model.php
+++ b/module/install/model.php
@@ -516,14 +516,13 @@ class installModel extends model
}
}
+ $this->loadModel('user');
+ $this->app->loadConfig('admin');
/* Check password. */
if(!validater::checkReg($this->post->password, '|(.){6,}|')) dao::$errors['password'][] = $this->lang->error->passwordrule;
- if(isset($this->config->safe->mode) and (strlen($this->post->password) < $this->config->safe->mode)) dao::$errors['password'][] = $this->lang->user->weakPassword;
- if(!empty($this->config->safe->changeWeak))
- {
- if(!isset($this->config->safe->weak)) $this->app->loadConfig('admin');
- if(strpos(",{$this->config->safe->weak},", ",{$this->post->password},") !== false) dao::$errors['password'][] = sprintf($this->lang->user->errorWeak, $this->config->safe->weak);
- }
+ if($this->user->computePasswordStrength($this->post->password) < 1) dao::$errors['password'][] = $this->lang->user->placeholder->passwordStrengthCheck[1];
+ if(!isset($this->config->safe->weak)) $this->app->loadConfig('admin');
+ if(strpos(",{$this->config->safe->weak},", ",{$this->post->password},") !== false) dao::$errors['password'] = sprintf($this->lang->user->errorWeak, $this->config->safe->weak);
if(dao::isError()) return false;
/* Insert a company. */
diff --git a/module/kanban/control.php b/module/kanban/control.php
index 3f3b9f82b0..e58bca853b 100644
--- a/module/kanban/control.php
+++ b/module/kanban/control.php
@@ -1353,7 +1353,7 @@ class kanban extends control
$this->view->projects = array($this->lang->kanban->allProjects) + $this->project->getPairsByProgram();
$this->view->selectedProjectID = $selectedProjectID;
$this->view->lanePairs = $this->kanban->getLanePairsByGroup($groupID);
- $this->view->executions2Imported = $this->project->getStats($selectedProjectID, 'undone', 0, 0, 30, 'id_asc', $pager);
+ $this->view->executions2Imported = $this->execution->getStatData($selectedProjectID, 'undone', 0, 0, false, '', 'id_asc', $pager);
$this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$this->view->pager = $pager;
$this->view->kanbanID = $kanbanID;
diff --git a/module/kanban/model.php b/module/kanban/model.php
index 020e6ab7d9..a0e0f05747 100755
--- a/module/kanban/model.php
+++ b/module/kanban/model.php
@@ -1664,6 +1664,7 @@ class kanbanModel extends model
if(in_array($groupBy, array('type', 'category', 'source')) and $object->$groupBy !== $laneID) continue;
if($groupBy == 'assignedTo')
{
+ $laneID = (string)$laneID;
if(empty($object->team) and $object->$groupBy !== $laneID) continue;
if(!empty($object->team) and !in_array($laneID, array_keys($object->team), true)) continue;
diff --git a/module/my/view/changepassword.html.php b/module/my/view/changepassword.html.php
index 42cd5db2e3..4fc172707a 100755
--- a/module/my/view/changepassword.html.php
+++ b/module/my/view/changepassword.html.php
@@ -54,8 +54,9 @@
app->user->modifyPasswordReason)):?>
app->loadLang('admin');?>
+ app->user->modifyPasswordReason;?>
admin->safe->common . ' : ';?>
- app->user->modifyPasswordReason == 'weak' ? $lang->admin->safe->changeWeak : $lang->admin->safe->modifyPasswordFirstLogin;?>
+ admin->safe->changeWeak : $lang->admin->safe->$reason;?>
diff --git a/module/project/control.php b/module/project/control.php
index fd90e7168d..fe97348180 100644
--- a/module/project/control.php
+++ b/module/project/control.php
@@ -1024,13 +1024,13 @@ class project extends control
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
- $allExecution = $this->project->getStats($projectID, 'all');
+ $allExecution = $this->execution->getStatData($projectID, 'all');
$this->view->allExecutionNum = empty($allExecution);
$this->view->title = $this->lang->execution->allExecutions;
$this->view->position[] = $this->lang->execution->allExecutions;
- $this->view->executionStats = $this->project->getStats($projectID, $status, $productID, 0, 30, $orderBy, $pager, $this->cookie->showTask);
+ $this->view->executionStats = $this->execution->getStatData($projectID, $status, $productID, 0, $this->cookie->showTask, '', $orderBy, $pager);
$this->view->productList = $this->loadModel('product')->getProductPairsByProject($projectID);
$this->view->productID = $productID;
$this->view->projectID = $projectID;
diff --git a/module/project/model.php b/module/project/model.php
index 57e1968acd..1bd12fd0cb 100644
--- a/module/project/model.php
+++ b/module/project/model.php
@@ -240,7 +240,7 @@ class projectModel extends model
{
$orderBy = $project->model == 'waterfall' ? 'id_asc' : 'id_desc';
$pager = $project->model == 'waterfall' ? null : new pager(0, 1, 1);
- $project->executions = $this->getStats($projectID, 'undone', 0, 0, 30, $orderBy, $pager);
+ $project->executions = $this->loadModel('execution')->getStatData($projectID, 'undone', 0, 0, false, '', $orderBy, $pager);
$project->teamCount = isset($teams[$projectID]) ? $teams[$projectID]->count : 0;
$project->estimate = isset($estimates[$projectID]) ? round($estimates[$projectID]->estimate, 2) : 0;
$project->parentName = $this->getParentProgram($project);
@@ -2263,215 +2263,6 @@ class projectModel extends model
->fetchAll('account');
}
- /**
- * Get project stats.
- *
- * @param int $projectID
- * @param string $status
- * @param int $productID
- * @param int $branch
- * @param int $itemCounts
- * @param string $orderBy
- * @param object $pager
- * @param bool $withTasks
- * @param int $queryID
- * @access public
- * @return array
- */
- public function getStats($projectID = 0, $status = 'undone', $productID = 0, $branch = 0, $itemCounts = 30, $orderBy = 'id_asc', $pager = null, $withTasks = false, $queryID = 0)
- {
- if(empty($productID))
- {
- $myExecutionIDList = array();
- $query = '';
- if($status == 'bySearch')
- {
- $status = 'all';
- if($queryID)
- {
- $query = $this->loadModel('search')->getQuery($queryID);
- if($query)
- {
- $this->session->set('executionQuery', $query->sql);
- $this->session->set('executionForm', $query->form);
- }
- else
- {
- $this->session->set('executionQuery', ' 1 = 1');
- }
- }
- else
- {
- if($this->session->executionQuery == false) $this->session->set('executionQuery', ' 1 = 1');
- }
- $query = $this->session->executionQuery;
- $allProject = "`project` = 'all'";
-
- if(strpos($query, $allProject) !== false) $query = str_replace($allProject, '1', $query);
- $query = preg_replace('/(`\w*`)/', 't1.$1',$query);
- }
-
- if($status == 'involved')
- {
- $myExecutionIDList = $this->dao->select('root')->from(TABLE_TEAM)
- ->where('account')->eq($this->app->user->account)
- ->andWhere('type')->eq('execution')
- ->fetchPairs();
- }
-
- $executions = $this->dao->select('t1.*,t2.name projectName, t2.model as projectModel')->from(TABLE_EXECUTION)->alias('t1')
- ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
- ->where('t1.type')->in('sprint,stage,kanban')
- ->beginIF(!empty($query))->andWhere($query)->fi()
- ->beginIF($projectID != 0)->andWhere('t1.project')->eq($projectID)->fi()
- ->beginIF($projectID == 0 and $this->config->vision)->andWhere('t1.vision')->eq($this->config->vision)->fi()
- ->beginIF(!empty($myExecutionIDList))->andWhere('t1.id')->in(array_keys($myExecutionIDList))->fi()
- ->beginIF($status == 'undone')->andWhere('t1.status')->notIN('done,closed')->fi()
- ->beginIF(!in_array($status, array('all', 'undone', 'involved', 'review'), true))->andWhere('t1.status')->eq($status)->fi()
- ->beginIF($status == 'review')
- ->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.reviewers)")
- ->andWhere('t1.reviewStatus')->eq('doing')
- ->fi()
- ->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->sprints)->fi()
- ->andWhere('t1.vision')->eq($this->config->vision)
- ->andWhere('t1.deleted')->eq('0')
- ->orderBy($orderBy)
- ->page($pager)
- ->fetchAll('id');
- }
- else
- {
- $executions = $this->dao->select('t2.*,t3.name projectName, t3.model as projectModel')->from(TABLE_PROJECTPRODUCT)->alias('t1')
- ->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.project=t2.id')
- ->leftJoin(TABLE_PROJECT)->alias('t3')->on('t2.project=t3.id')
- ->where('t1.product')->eq($productID)
- ->beginIF($projectID)->andWhere('t2.project')->eq($projectID)->fi()
- ->beginIF($status == 'undone')->andWhere('t2.status')->notIN('done,closed')->fi()
- ->beginIF($status != 'all' and $status != 'undone')->andWhere('t2.status')->eq($status)->fi()
- ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->sprints)->fi()
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere('t2.vision')->eq($this->config->vision)
- ->orderBy($orderBy)
- ->page($pager)
- ->fetchAll('id');
- }
-
- $hours = $this->computerProgress($executions);
- $emptyHour = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'progress' => 0);
-
- /* Get burndown charts datas. */
- $burns = $this->dao->select('execution, date AS name, `left` AS value')
- ->from(TABLE_BURN)
- ->where('execution')->in(array_keys($executions))
- ->andWhere('task')->eq(0)
- ->orderBy('date desc')
- ->fetchGroup('execution', 'name');
-
- $this->loadModel('execution');
- foreach($burns as $executionID => $executionBurns)
- {
- /* If executionBurns > $itemCounts, split it, else call processBurnData() to pad burns. */
- $begin = $executions[$executionID]->begin;
- $end = $executions[$executionID]->end;
- if(helper::isZeroDate($begin)) $begin = $executions[$executionID]->openedDate;
- $executionBurns = $this->execution->processBurnData($executionBurns, $itemCounts, $begin, $end);
-
- /* Shorter names. */
- foreach($executionBurns as $executionBurn)
- {
- $executionBurn->name = substr($executionBurn->name, 5);
- unset($executionBurn->execution);
- }
-
- ksort($executionBurns);
- $burns[$executionID] = $executionBurns;
- }
-
- if($withTasks)
- {
- $executionTasks = $this->dao->select('*')->from(TABLE_TASK)
- ->where('deleted')->eq(0)
- ->andWhere('status')->notin('closed,cancel')
- ->andWhere('execution')->in(array_keys($executions))
- ->orderBy('id_asc')
- ->fetchGroup('execution', 'id');
-
- foreach($executionTasks as $executionID => $tasks)
- {
- foreach($tasks as $task)
- {
- if($task->parent > 0)
- {
- if(isset($executionTasks[$executionID][$task->parent]))
- {
- $executionTasks[$executionID][$task->parent]->children[$task->id] = $task;
- unset($executionTasks[$executionID][$task->id]);
- }
- }
- }
- }
- }
-
- /* Process executions. */
- $parents = array();
- $children = array();
- foreach($executions as $key => $execution)
- {
- /* Process the end time. */
- $execution->end = date(DT_DATE1, strtotime($execution->end));
-
- /* Judge whether the execution is delayed. */
- if($execution->status != 'done' and $execution->status != 'closed' and $execution->status != 'suspended')
- {
- $delay = helper::diffDate(helper::today(), $execution->end);
- if($delay > 0) $execution->delay = $delay;
- }
-
- /* Process the burns. */
- $execution->burns = array();
- $burnData = isset($burns[$execution->id]) ? $burns[$execution->id] : array();
- foreach($burnData as $data) $execution->burns[] = $data->value;
-
- /* Process the hours. */
- $execution->hours = isset($hours[$execution->id]) ? $hours[$execution->id] : (object)$emptyHour;
-
- $execution->children = array();
- $execution->grade == 1 ? $parents[$execution->id] = $execution : $children[$execution->parent][] = $execution;
- if(isset($executionTasks) and isset($executionTasks[$execution->id]))
- {
- $tasks = array_chunk($executionTasks[$execution->id], 50, true);
- $execution->tasks = $tasks[0];
- }
- }
-
- /* In the case of the waterfall model, calculate the sub-stage. */
- if($projectID == 0)
- {
- foreach($executions as $key => $execution)
- {
- if($execution->type == 'stage')
- {
- $execution->children = isset($children[$execution->id]) ? $children[$execution->id] : array();
- unset($children[$execution->id]);
- }
- }
- }
- $project = $this->getByID($projectID);
- if($project and $project->model == 'waterfall')
- {
- foreach($parents as $id => $execution)
- {
- $execution->children = isset($children[$id]) ? $children[$id] : array();
- unset($children[$id]);
- }
- }
-
- $orphan = array();
- foreach($children as $child) $orphan = array_merge($child, $orphan);
-
- return array_merge($parents, $orphan);
- }
-
/**
* Get stats for project kanban.
*
@@ -2483,7 +2274,7 @@ class projectModel extends model
$this->loadModel('program');
$projects = $this->program->getProjectStats(0, 'all', 0, 'order_asc');
- $executions = $this->getStats(0, 'doing');
+ $executions = $this->loadModel('execution')->getStatData(0, 'doing');
$doingExecutions = array();
$latestExecutions = array();
diff --git a/module/search/model.php b/module/search/model.php
index 0c17675817..742cf697ea 100644
--- a/module/search/model.php
+++ b/module/search/model.php
@@ -215,10 +215,19 @@ class searchModel extends model
$condition = '`' . $this->post->$fieldName . "` >= '$value' AND `" . $this->post->$fieldName . "` <= '$value 23:59:59'";
$where .= " $andOr ($condition)";
}
+ elseif($operator == '!=' and preg_match('/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/', $value))
+ {
+ $condition = '`' . $this->post->$fieldName . "` < '$value' OR `" . $this->post->$fieldName . "` > '$value 23:59:59'";
+ $where .= " $andOr ($condition)";
+ }
elseif($operator == '<=' and preg_match('/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/', $value))
{
$where .= " $andOr " . '`' . $this->post->$fieldName . "` <= '$value 23:59:59'";
}
+ elseif($operator == '>' and preg_match('/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/', $value))
+ {
+ $where .= " $andOr " . '`' . $this->post->$fieldName . "` > '$value 23:59:59'";
+ }
elseif($condition)
{
$where .= " $andOr " . '`' . $this->post->$fieldName . '` ' . $condition;
diff --git a/module/task/config.php b/module/task/config.php
index 9068d4a030..b14fdcb456 100644
--- a/module/task/config.php
+++ b/module/task/config.php
@@ -41,6 +41,7 @@ $config->task->exportFields = '
$config->task->customCreateFields = 'story,estStarted,deadline,mailto,pri,estimate';
$config->task->customBatchCreateFields = 'module,story,assignedTo,estimate,estStarted,deadline,desc,pri';
$config->task->customBatchEditFields = 'module,assignedTo,status,pri,estimate,record,left,estStarted,deadline,finishedBy,canceledBy,closedBy,closedReason';
+$config->task->defaultLoadCount = 50;
$config->task->custom = new stdclass();
$config->task->custom->createFields = $config->task->customCreateFields;
diff --git a/module/testcase/model.php b/module/testcase/model.php
index fd02631f3b..b626241bc0 100644
--- a/module/testcase/model.php
+++ b/module/testcase/model.php
@@ -702,6 +702,24 @@ class testcaseModel extends model
->fetchAll('id');
}
+ /**
+ * Get case pairs by product id and branch.
+ *
+ * @param int $productID
+ * @param int|string $branch
+ * @access public
+ * @return void
+ */
+ public function getPairsByProduct($productID, $branch = 0)
+ {
+ return $this->dao->select('id, concat_ws(":", id, title) as title')->from(TABLE_CASE)
+ ->where('deleted')->eq(0)
+ ->andWhere('product')->eq($productID)
+ ->beginIF($branch)->andWhere('branch')->in($branch)->fi()
+ ->orderBy('id_desc')
+ ->fetchPairs();
+ }
+
/**
* Get cases of a story.
*
diff --git a/module/upgrade/model.php b/module/upgrade/model.php
index 793c336335..d62ac53853 100644
--- a/module/upgrade/model.php
+++ b/module/upgrade/model.php
@@ -543,6 +543,9 @@ class upgradeModel extends model
}
}
break;
+ case '17_5':
+ $this->updateOSAndBrowserOfBug();
+ break;
}
$this->deletePatch();
@@ -7089,4 +7092,31 @@ class upgradeModel extends model
return $result->rowCount() > 0;
}
+
+ /**
+ * Update OS and browser of bug.
+ *
+ * @access public
+ * @return bool
+ */
+ public function updateOSAndBrowserOfBug()
+ {
+ $existOSList = $this->dao->select('distinct os')->from(TABLE_BUG)->where('os')->ne('')->fetchPairs();
+ $existBrowserList = $this->dao->select('distinct browser')->from(TABLE_BUG)->where('os')->ne('')->fetchPairs();
+ $deletedOSList = array('vista', 'win2012', 'win2008', 'win2003', 'win2000', 'wp8', 'wp7', 'symbian', 'freebsd');
+ $deletedBrowserList = array('ie7', 'ie6', 'firefox4', 'firefox3', 'firefox2', 'oprea11', 'oprea10', 'oprea9', 'maxthon', 'uc');
+ $existList = array_merge($existOSList, $existBrowserList);
+ $deletedList = array_merge($deletedOSList, $deletedBrowserList);
+
+ foreach($deletedList as $deletedLang)
+ {
+ if(in_array($deletedLang, $existList)) continue;
+ $this->dao->delete()->from(TABLE_LANG)->where('module')->eq('bug')->andWhere('`key`')->eq($deletedLang)->andWhere('`system`')->eq(1)->andWhere('vision')->eq('rnd')->exec();
+ }
+
+ $this->dao->update(TABLE_LANG)->set('value')->eq('Mac OS')->where('module')->eq('bug')->andWhere('`key`')->eq('osx')->andWhere('value')->eq('OS X')->exec();
+ $this->dao->update(TABLE_LANG)->set('value')->eq('Opera 系列')->where('module')->eq('bug')->andWhere('`key`')->eq('operea')->andWhere('value')->eq('opera 系列')->exec();
+
+ return true;
+ }
}
diff --git a/module/user/model.php b/module/user/model.php
index 7fec646510..f0d90532c9 100644
--- a/module/user/model.php
+++ b/module/user/model.php
@@ -920,6 +920,12 @@ class userModel extends model
$user->modifyPassword = $this->loadModel('admin')->checkWeak($user);
if($user->modifyPassword) $user->modifyPasswordReason = 'weak';
}
+ /* Check weak password when login. */
+ if(!$user->modifyPassword and $this->app->moduleName == 'user' and $this->app->methodName == 'login' and isset($_POST['passwordStrength']))
+ {
+ $user->modifyPassword = (isset($this->config->safe->mode) and $this->post->passwordStrength < $this->config->safe->mode);
+ if($user->modifyPassword) $user->modifyPasswordReason = 'passwordStrengthWeak';
+ }
/* code for bug #2729. */
if(defined('IN_USE')) $this->dao->update(TABLE_USER)->set('visits = visits + 1')->set('ip')->eq($ip)->set('last')->eq($last)->where('account')->eq($account)->exec();
diff --git a/test/class/execution.class.php b/test/class/execution.class.php
index 12e317336a..9600ac750a 100644
--- a/test/class/execution.class.php
+++ b/test/class/execution.class.php
@@ -166,10 +166,11 @@ class executionTest
*
* @param string $executionID
* @param array $param
+ * @param bool $testParent
* @access public
* @return array
*/
- public function startTest($executionID,$param = array())
+ public function startTest($executionID,$param = array(), $testParent = false)
{
$data = date('Y-m-d');
@@ -196,6 +197,12 @@ class executionTest
}
else
{
+ if($testParent)
+ {
+ $execution = $this->objectModel->getByID($executionID);
+ $executionParent = $this->objectModel->getByID($execution->parent);
+ return $executionParent;
+ }
return $obj;
}
}
@@ -268,10 +275,11 @@ class executionTest
*
* @param string $executionID
* @param array $param
+ * @param bool $testParent
* @access public
* @return array
*/
- public function activateTest($executionID, $param = array())
+ public function activateTest($executionID, $param = array(), $testParent = false)
{
self::suspendTest($executionID);
@@ -294,6 +302,12 @@ class executionTest
}
else
{
+ if($testParent)
+ {
+ $execution = $this->objectModel->getByID($executionID);
+ $executionParent = $this->objectModel->getByID($execution->parent);
+ return $executionParent;
+ }
return $obj;
}
}
@@ -303,10 +317,11 @@ class executionTest
*
* @param string $executionID
* @param array $param
+ * @param bool $testParent
* @access public
* @return array
*/
- public function closeTest($executionID, $param = array())
+ public function closeTest($executionID, $param = array(), $testParent = false)
{
$todate = date('Y-m-d');
@@ -326,6 +341,12 @@ class executionTest
}
else
{
+ if($testParent)
+ {
+ $execution = $this->objectModel->getByID($executionID);
+ $executionParent = $this->objectModel->getByID($execution->parent);
+ return $executionParent;
+ }
return $obj;
}
}
diff --git a/test/class/testcase.class.php b/test/class/testcase.class.php
index 5642b59f97..077080ef01 100644
--- a/test/class/testcase.class.php
+++ b/test/class/testcase.class.php
@@ -306,6 +306,24 @@ class testcaseTest
return $objects;
}
+ /**
+ * Test get case pairs by product id and branch.
+ *
+ * @param int $productID
+ * @param int $branch
+ * @access public
+ * @return array
+ */
+ public function getPairsByProductTest($productID = 0, $branch = 0)
+ {
+ $objects = $this->objectModel->getPairsByProduct($productID, $branch);
+
+ if(dao::isError()) return dao::getError();
+
+ if(empty($objects)) return 'empty';
+ return $objects;
+ }
+
/**
* Test get counts of some stories' cases.
*
diff --git a/test/model/execution/activate.php b/test/model/execution/activate.php
index c026149452..8730b9d6c5 100755
--- a/test/model/execution/activate.php
+++ b/test/model/execution/activate.php
@@ -16,18 +16,19 @@ pid=1
看板执行激活 >> status,suspended,doing
修改激活时间 >> begin
修改顺延 >> begin
+子瀑布激活获取父瀑布状态 >> doing
*/
-$executionIDList = array('105', '136', '165', '106', '107');
+$executionIDList = array('105', '136', '165', '106', '107', '708');
$readjustTask = array('readjustTime' => '1', 'readjustTask' => '1');
$readjustTime = array('readjustTime' => '1');
$execution = new executionTest();
-//var_dump($execution->activateTest($executionIDList[4],$readjustTask ));die;
-r($execution->activateTest($executionIDList[0])) && p('0:field,old,new') && e('status,suspended,doing'); // 敏捷执行激活
-r($execution->activateTest($executionIDList[1])) && p('0:field,old,new') && e('status,suspended,doing'); // 瀑布执行激活
-r($execution->activateTest($executionIDList[2])) && p('0:field,old,new') && e('status,suspended,doing'); // 看板执行激活
-r($execution->activateTest($executionIDList[3],$readjustTime)) && p('1:field') && e('begin'); // 修改激活时间
-r($execution->activateTest($executionIDList[4],$readjustTask)) && p('1:field') && e('begin'); // 修改顺延
-$db->restoreDB();
\ No newline at end of file
+r($execution->activateTest($executionIDList[0])) && p('0:field,old,new') && e('status,suspended,doing'); // 敏捷执行激活
+r($execution->activateTest($executionIDList[1])) && p('0:field,old,new') && e('status,suspended,doing'); // 瀑布执行激活
+r($execution->activateTest($executionIDList[2])) && p('0:field,old,new') && e('status,suspended,doing'); // 看板执行激活
+r($execution->activateTest($executionIDList[3],$readjustTime)) && p('1:field') && e('begin'); // 修改激活时间
+r($execution->activateTest($executionIDList[4],$readjustTask)) && p('1:field') && e('begin'); // 修改顺延
+r($execution->activateTest($executionIDList[5], array(), true)) && p('status') && e('doing'); // 子瀑布激活获取父瀑布状态
+$db->restoreDB();
diff --git a/test/model/execution/close.php b/test/model/execution/close.php
index cd43bbabf6..6f0752d869 100755
--- a/test/model/execution/close.php
+++ b/test/model/execution/close.php
@@ -15,15 +15,17 @@ pid=1
瀑布执行关闭 >> status,doing,closed
看板执行关闭 >> status,doing,closed
不输入实际完成时间校验 >> 『realEnd』不能为空。
+子瀑布关闭获取父瀑布状态 >> closed
*/
-$executionIDList = array('109', '138', '168');
+$executionIDList = array('109', '138', '168', '708');
$noRealEnd = array('realEnd' => '');
$execution = new executionTest();
-r($execution->closeTest($executionIDList[0])) && p('0:field,old,new') && e('status,wait,closed'); // 敏捷执行关闭
-r($execution->closeTest($executionIDList[1])) && p('0:field,old,new') && e('status,doing,closed'); // 瀑布执行关闭
-r($execution->closeTest($executionIDList[2])) && p('0:field,old,new') && e('status,doing,closed'); // 看板执行关闭
-r($execution->closeTest($executionIDList[1],$noRealEnd)) && p('realEnd:0') && e('『realEnd』不能为空。'); // 不输入实际完成时间校验
-$db->restoreDB();
\ No newline at end of file
+r($execution->closeTest($executionIDList[0])) && p('0:field,old,new') && e('status,wait,closed'); // 敏捷执行关闭
+r($execution->closeTest($executionIDList[1])) && p('0:field,old,new') && e('status,doing,closed'); // 瀑布执行关闭
+r($execution->closeTest($executionIDList[2])) && p('0:field,old,new') && e('status,doing,closed'); // 看板执行关闭
+r($execution->closeTest($executionIDList[1],$noRealEnd)) && p('realEnd:0') && e('『realEnd』不能为空。'); // 不输入实际完成时间校验
+r($execution->closeTest($executionIDList[3], array(), true)) && p('status') && e('closed'); // 子瀑布关闭获取父瀑布状态
+$db->restoreDB();
diff --git a/test/model/execution/start.php b/test/model/execution/start.php
index 22f92a08d1..1f52ea7577 100755
--- a/test/model/execution/start.php
+++ b/test/model/execution/start.php
@@ -15,16 +15,18 @@ pid=1
瀑布阶段开始 >> status,wait,doing
看板执行开始 >> status,wait,doing
重复执行开始 >> 此任务已被启动,不能重复启动!
+子瀑布开启获取父瀑布状态 >> doing
*/
-$executionIDList = array('101', '131', '161', '103');
+$executionIDList = array('101', '131', '161', '103', '701');
$noRealBegan = array('realBegan' => '');
$execution = new executionTest();
-r($execution->startTest($executionIDList[0])) && p('1:field,old,new') && e('status,wait,doing'); // 敏捷执行开始
-r($execution->startTest($executionIDList[1])) && p('1:field,old,new') && e('status,wait,doing'); // 瀑布阶段开始
-r($execution->startTest($executionIDList[2])) && p('1:field,old,new') && e('status,wait,doing'); // 看板执行开始
-r($execution->startTest($executionIDList[0],$noRealBegan)) && p() && e('此任务已被启动,不能重复启动!'); // 重复执行开始
-$db->restoreDB();
\ No newline at end of file
+r($execution->startTest($executionIDList[0])) && p('1:field,old,new') && e('status,wait,doing'); // 敏捷执行开始
+r($execution->startTest($executionIDList[1])) && p('1:field,old,new') && e('status,wait,doing'); // 瀑布阶段开始
+r($execution->startTest($executionIDList[2])) && p('1:field,old,new') && e('status,wait,doing'); // 看板执行开始
+r($execution->startTest($executionIDList[0],$noRealBegan)) && p() && e('此任务已被启动,不能重复启动!'); // 重复执行开始
+r($execution->startTest($executionIDList[3], array(), true)) && p('status') && e('doing'); // 子瀑布开启获取父瀑布状态
+$db->restoreDB();
diff --git a/test/model/testcase/getpairsbyproduct.php b/test/model/testcase/getpairsbyproduct.php
new file mode 100644
index 0000000000..1eda797975
--- /dev/null
+++ b/test/model/testcase/getpairsbyproduct.php
@@ -0,0 +1,22 @@
+#!/usr/bin/env php
+getPairsByProduct();
+cid=1
+pid=1
+
+获取产品1的case >> 1:这个是测试用例1
+获取产品41分支1的case >> empty
+
+*/
+$productIDList = array(1, 41);
+$branch = 1;
+
+$testcase = new testcaseTest();
+r($testcase->getPairsByProductTest($productIDList[0])) && p('1') && e('1:这个是测试用例1'); // 获取产品1的case
+r($testcase->getPairsByProductTest($productIDList[1], $branch)) && p() && e('empty'); // 获取产品41分支1的case
diff --git a/test/ztest b/test/ztest
index 6fc88a6b7c..8454508f16 100755
--- a/test/ztest
+++ b/test/ztest
@@ -58,6 +58,9 @@ switch($argv[1])
case 'cron':
ztfRun('model/cron');
break;
+ case 'custom':
+ ztfRun('model/custom');
+ break;
case 'datatable':
ztfRun('model/datatable');
break;