diff --git a/Makefile b/Makefile index 46a0f9054b..0051cf639a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ VERSION = $(shell head -n 1 VERSION) XUANVERSION = $(shell jq -r .pkg.xuanxuan.gitVersion < ci.json) XVERSION = $(shell jq -r .pkg.xuanxuan.version < ci.json) +SUITEVERSION = $(shell jq -r .pkg.blocksuite.version < ci.json) XHPROF_VERSION = 2.3.9 XUANPATH := $(XUANXUAN_SRC_PATH) @@ -28,6 +29,8 @@ clean: rm -f *.sh rm -f *.deb *.rpm common: + curl https://$(GITFOX_HOST)/_artifacts/zentao/raw/zui3/static/blocksuite/$(SUITEVERSION)/blocksuite-$(SUITEVERSION).tar.gz | tar zxf - -C www/js/zui3/editor/ + mkdir zentaopms cp -fr api zentaopms/ cp -fr bin zentaopms/ diff --git a/ci.json b/ci.json index a70fb1b4c4..255a6f904b 100644 --- a/ci.json +++ b/ci.json @@ -15,6 +15,9 @@ }, "downgradeExt": { "gitRepo": "ci/zentaoext-downgrade" + }, + "blocksuite": { + "version": "0.17.10" } }, "image": { diff --git a/db/standard/zentao21.0.sql b/db/standard/zentao21.0.sql index c9aa02e52b..8dca1c4e85 100644 --- a/db/standard/zentao21.0.sql +++ b/db/standard/zentao21.0.sql @@ -1690,7 +1690,9 @@ CREATE TABLE `zt_history` ( `action` mediumint(8) unsigned NOT NULL DEFAULT 0, `field` varchar(30) NOT NULL DEFAULT '', `old` text DEFAULT NULL, + `oldValue` text DEFAULT NULL, `new` text DEFAULT NULL, + `newValue` text DEFAULT NULL, `diff` mediumtext DEFAULT NULL, PRIMARY KEY (`id`), KEY `action` (`action`) @@ -4292,7 +4294,7 @@ CREATE TABLE `zt_workflowgroup` ( `name` varchar(30) NOT NULL DEFAULT '', `code` varchar(30) NOT NULL DEFAULT '', `desc` text DEFAULT NULL, - `disabledModules` text DEFAULT '', + `disabledModules` varchar(255) NOT NULL DEFAULT '', `status` varchar(10) NOT NULL DEFAULT 'wait', `vision` varchar(10) NOT NULL DEFAULT 'rnd', `main` enum('0','1') NOT NULL DEFAULT '0', diff --git a/db/update20.8.sql b/db/update20.8.sql index 336c6fd8ab..e9edfaae1b 100644 --- a/db/update20.8.sql +++ b/db/update20.8.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowgroup` ( `projectType` varchar(10) NOT NULL DEFAULT '', `name` varchar(30) NOT NULL DEFAULT '', `desc` text NULL, - `disabledModules` text NULL DEFAULT '', + `disabledModules` varchar(255) NOT NULL DEFAULT '', `status` varchar(10) NOT NULL DEFAULT 'wait', `vision` varchar(10) NOT NULL DEFAULT 'rnd', `createdBy` varchar(30) NOT NULL DEFAULT '', @@ -30,13 +30,13 @@ ALTER TABLE `zt_workflowui` ADD `group` mediumint(8) unsigned NOT NULL DEFAULT ' UPDATE `zt_workflow` SET `role` = 'custom' WHERE `buildin` = '0'; -ALTER TABLE `zt_workflow` DROP INDEX `unique`; +DROP INDEX `unique` ON `zt_workflow`; CREATE UNIQUE INDEX `unique` ON `zt_workflow`(`group`,`app`,`module`,`vision`); -ALTER TABLE `zt_workflowfield` DROP INDEX `unique`; +DROP INDEX `unique` ON `zt_workflowfield`; CREATE UNIQUE INDEX `unique` ON `zt_workflowfield`(`group`,`module`,`field`); -ALTER TABLE `zt_workflowaction` DROP INDEX `unique`; +DROP INDEX `unique` ON `zt_workflowaction`; CREATE UNIQUE INDEX `unique` ON `zt_workflowaction`(`group`,`module`,`action`,`vision`); -ALTER TABLE `zt_workflowlayout` DROP INDEX `unique`; +DROP INDEX `unique` ON `zt_workflowlayout`; CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`group`,`module`,`action`,`ui`,`field`,`vision`); ALTER TABLE `zt_product` ADD `workflowGroup` int(8) NOT NULL DEFAULT '0' AFTER `ticket`; @@ -46,11 +46,11 @@ ALTER TABLE `zt_workflowgroup` ADD `code` varchar(30) NOT NULL DEFAULT '' AFTER DELETE FROM `zt_workflowgroup` WHERE `main` = '1'; INSERT INTO `zt_workflowgroup` (`type`, `projectModel`, `projectType`, `name`, `code`, `status`, `vision`, `main`) VALUES -('product', '', 'project', '默认流程', 'productproject', 'normal', 'rnd', '1'), -('project', 'scrum', 'product', '敏捷-产品型默认流程', 'scrumproduct', 'normal', 'rnd', '1'), -('project', 'scrum', 'project', '敏捷-项目型默认流程', 'scrumproject', 'normal', 'rnd', '1'), -('project', 'waterfall', 'product', '瀑布-产品型默认流程', 'waterfallproduct', 'normal', 'rnd', '1'), -('project', 'waterfall', 'project', '瀑布-项目型默认流程', 'waterfallproject', 'normal', 'rnd', '1'); +('product', '', 'project', '默认流程', 'productproject', 'normal', 'rnd', '1'), +('project', 'scrum', 'product', '产品型敏捷项目流程', 'scrumproduct', 'normal', 'rnd', '1'), +('project', 'scrum', 'project', '项目型敏捷项目流程', 'scrumproject', 'normal', 'rnd', '1'), +('project', 'waterfall', 'product', '产品型瀑布项目流程', 'waterfallproduct','normal', 'rnd', '1'), +('project', 'waterfall', 'project', '项目型瀑布项目流程', 'waterfallproject','normal', 'rnd', '1'); ALTER TABLE `zt_doclib` ADD `orderBy` varchar(30) NOT NULL DEFAULT 'id_asc' AFTER `deleted`; diff --git a/db/zentao.sql b/db/zentao.sql index 43f0260443..88440fbc65 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -13220,7 +13220,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowgroup` ( `name` varchar(30) NOT NULL DEFAULT '', `code` varchar(30) NOT NULL DEFAULT '', `desc` text NULL, - `disabledModules` text NULL DEFAULT '', + `disabledModules` varchar(255) NOT NULL DEFAULT '', `status` varchar(10) NOT NULL DEFAULT 'wait', `vision` varchar(10) NOT NULL DEFAULT 'rnd', `main` enum('0','1') NOT NULL DEFAULT '0', @@ -13506,11 +13506,11 @@ REPLACE INTO `zt_workflowrule`(`type`, `name`, `rule`, `createdBy`, `createdDate ('system','IP','ip','admin','2020-10-14 14:06:14'); INSERT INTO `zt_workflowgroup` (`type`, `projectModel`, `projectType`, `name`, `code`, `status`, `vision`, `main`) VALUES -('product', '', 'project', '默认流程', 'productproject', 'normal', 'rnd', '1'), -('project', 'scrum', 'product', '敏捷-产品型默认流程', 'scrumproduct', 'normal', 'rnd', '1'), -('project', 'scrum', 'project', '敏捷-项目型默认流程', 'scrumproject', 'normal', 'rnd', '1'), -('project', 'waterfall', 'product', '瀑布-产品型默认流程', 'waterfallproduct', 'normal', 'rnd', '1'), -('project', 'waterfall', 'project', '瀑布-项目型默认流程', 'waterfallproject', 'normal', 'rnd', '1'); +('product', '', 'project', '默认流程', 'productproject', 'normal', 'rnd', '1'), +('project', 'scrum', 'product', '产品型敏捷项目流程', 'scrumproduct', 'normal', 'rnd', '1'), +('project', 'scrum', 'project', '项目型敏捷项目流程', 'scrumproject', 'normal', 'rnd', '1'), +('project', 'waterfall', 'product', '产品型瀑布项目流程', 'waterfallproduct','normal', 'rnd', '1'), +('project', 'waterfall', 'project', '项目型瀑布项目流程', 'waterfallproject','normal', 'rnd', '1'); INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`) VALUES ('system', '产品', 'products', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"product\",\"method\":\"getPairs\",\"methodDesc\":\"Get product pairs.\",\"params\":[{\"name\":\"mode\",\"type\":\"string\",\"desc\":\"\",\"value\":\"all\"}]}', '', '', ''), diff --git a/extension/lite/common/ext/lang/de/lite.php b/extension/lite/common/ext/lang/de/lite.php index 54940392ec..affe62e813 100644 --- a/extension/lite/common/ext/lang/de/lite.php +++ b/extension/lite/common/ext/lang/de/lite.php @@ -130,6 +130,7 @@ $lang->SRCommon = 'Story'; /* Doc menu. */ $lang->doc->menu = new stdclass(); $lang->doc->menu->dashboard = array('link' => "{$lang->dashboard}|doc|index"); +$lang->doc->menu->quick = array('link' => "{$lang->doc->quick}|doc|quick"); $lang->doc->menu->my = array('link' => "{$lang->doc->mySpace}|doc|mySpace|", 'alias' => 'myspace'); $lang->doc->menu->project = array('link' => "{$lang->doc->projectSpace}|doc|projectSpace", 'alias' => 'showfiles,project'); $lang->doc->menu->custom = array('link' => "{$lang->doc->teamSpace}|doc|teamSpace|", 'alias' => 'teamspace'); @@ -139,9 +140,10 @@ $lang->doc->dividerMenu = ',project,'; /* Doc menu order. */ $lang->doc->menuOrder = array(); $lang->doc->menuOrder[5] = 'dashboard'; -$lang->doc->menuOrder[10] = 'my'; -$lang->doc->menuOrder[15] = 'custom'; -$lang->doc->menuOrder[20] = 'project'; +$lang->doc->menuOrder[10] = 'quick'; +$lang->doc->menuOrder[15] = 'my'; +$lang->doc->menuOrder[20] = 'custom'; +$lang->doc->menuOrder[25] = 'project'; /* Admin menu. */ $lang->admin->menu = new stdclass(); diff --git a/extension/lite/common/ext/lang/en/lite.php b/extension/lite/common/ext/lang/en/lite.php index 54940392ec..affe62e813 100644 --- a/extension/lite/common/ext/lang/en/lite.php +++ b/extension/lite/common/ext/lang/en/lite.php @@ -130,6 +130,7 @@ $lang->SRCommon = 'Story'; /* Doc menu. */ $lang->doc->menu = new stdclass(); $lang->doc->menu->dashboard = array('link' => "{$lang->dashboard}|doc|index"); +$lang->doc->menu->quick = array('link' => "{$lang->doc->quick}|doc|quick"); $lang->doc->menu->my = array('link' => "{$lang->doc->mySpace}|doc|mySpace|", 'alias' => 'myspace'); $lang->doc->menu->project = array('link' => "{$lang->doc->projectSpace}|doc|projectSpace", 'alias' => 'showfiles,project'); $lang->doc->menu->custom = array('link' => "{$lang->doc->teamSpace}|doc|teamSpace|", 'alias' => 'teamspace'); @@ -139,9 +140,10 @@ $lang->doc->dividerMenu = ',project,'; /* Doc menu order. */ $lang->doc->menuOrder = array(); $lang->doc->menuOrder[5] = 'dashboard'; -$lang->doc->menuOrder[10] = 'my'; -$lang->doc->menuOrder[15] = 'custom'; -$lang->doc->menuOrder[20] = 'project'; +$lang->doc->menuOrder[10] = 'quick'; +$lang->doc->menuOrder[15] = 'my'; +$lang->doc->menuOrder[20] = 'custom'; +$lang->doc->menuOrder[25] = 'project'; /* Admin menu. */ $lang->admin->menu = new stdclass(); diff --git a/extension/lite/common/ext/lang/fr/lite.php b/extension/lite/common/ext/lang/fr/lite.php index 54940392ec..affe62e813 100644 --- a/extension/lite/common/ext/lang/fr/lite.php +++ b/extension/lite/common/ext/lang/fr/lite.php @@ -130,6 +130,7 @@ $lang->SRCommon = 'Story'; /* Doc menu. */ $lang->doc->menu = new stdclass(); $lang->doc->menu->dashboard = array('link' => "{$lang->dashboard}|doc|index"); +$lang->doc->menu->quick = array('link' => "{$lang->doc->quick}|doc|quick"); $lang->doc->menu->my = array('link' => "{$lang->doc->mySpace}|doc|mySpace|", 'alias' => 'myspace'); $lang->doc->menu->project = array('link' => "{$lang->doc->projectSpace}|doc|projectSpace", 'alias' => 'showfiles,project'); $lang->doc->menu->custom = array('link' => "{$lang->doc->teamSpace}|doc|teamSpace|", 'alias' => 'teamspace'); @@ -139,9 +140,10 @@ $lang->doc->dividerMenu = ',project,'; /* Doc menu order. */ $lang->doc->menuOrder = array(); $lang->doc->menuOrder[5] = 'dashboard'; -$lang->doc->menuOrder[10] = 'my'; -$lang->doc->menuOrder[15] = 'custom'; -$lang->doc->menuOrder[20] = 'project'; +$lang->doc->menuOrder[10] = 'quick'; +$lang->doc->menuOrder[15] = 'my'; +$lang->doc->menuOrder[20] = 'custom'; +$lang->doc->menuOrder[25] = 'project'; /* Admin menu. */ $lang->admin->menu = new stdclass(); diff --git a/extension/lite/common/ext/lang/zh-cn/lite.php b/extension/lite/common/ext/lang/zh-cn/lite.php index 6441205785..d69e3356b8 100644 --- a/extension/lite/common/ext/lang/zh-cn/lite.php +++ b/extension/lite/common/ext/lang/zh-cn/lite.php @@ -130,6 +130,7 @@ $lang->SRCommon = '目标'; /* Doc menu. */ $lang->doc->menu = new stdclass(); $lang->doc->menu->dashboard = array('link' => "{$lang->dashboard}|doc|index"); +$lang->doc->menu->quick = array('link' => "{$lang->doc->quick}|doc|quick"); $lang->doc->menu->my = array('link' => "{$lang->doc->mySpace}|doc|mySpace|", 'alias' => 'myspace'); $lang->doc->menu->project = array('link' => "{$lang->doc->projectSpace}|doc|projectSpace", 'alias' => 'showfiles,project'); $lang->doc->menu->custom = array('link' => "{$lang->doc->teamSpace}|doc|teamSpace|", 'alias' => 'teamspace'); @@ -139,9 +140,10 @@ $lang->doc->dividerMenu = ',project,'; /* Doc menu order. */ $lang->doc->menuOrder = array(); $lang->doc->menuOrder[5] = 'dashboard'; -$lang->doc->menuOrder[10] = 'my'; -$lang->doc->menuOrder[15] = 'custom'; -$lang->doc->menuOrder[20] = 'project'; +$lang->doc->menuOrder[10] = 'quick'; +$lang->doc->menuOrder[15] = 'my'; +$lang->doc->menuOrder[20] = 'custom'; +$lang->doc->menuOrder[25] = 'project'; /* Admin menu. */ $lang->admin->menu = new stdclass(); diff --git a/extension/lite/group/ext/lang/resource.php b/extension/lite/group/ext/lang/resource.php index 918d48302d..04cbe8bae5 100644 --- a/extension/lite/group/ext/lang/resource.php +++ b/extension/lite/group/ext/lang/resource.php @@ -572,9 +572,7 @@ $lang->task->methodOrder[125] = 'import'; $lang->resource->doc = new stdclass(); $lang->resource->doc->index = 'index'; $lang->resource->doc->mySpace = 'mySpace'; -$lang->resource->doc->myView = 'myView'; -$lang->resource->doc->myCollection = 'myCollection'; -$lang->resource->doc->myCreation = 'myCreation'; +$lang->resource->doc->quick = 'quick'; $lang->resource->doc->createSpace = 'createSpace'; $lang->resource->doc->createLib = 'createLibAction'; $lang->resource->doc->editLib = 'editLibAction'; diff --git a/framework/helper.class.php b/framework/helper.class.php index f19a12656f..06cb6c422b 100644 --- a/framework/helper.class.php +++ b/framework/helper.class.php @@ -442,11 +442,11 @@ class helper extends baseHelper * @param string $characters * @static * @access public - * @return string + * @return float */ - public static function formatHours($hours, $characters = '.0'): string + public static function formatHours($hours, $characters = '.0'): float { - return rtrim(number_format((float)$hours, 2, '.', ''), $characters); + return (float)rtrim(number_format((float)$hours, 2, '.', ''), $characters); } } diff --git a/lib/dbh/dbh.class.php b/lib/dbh/dbh.class.php index 7e49aca8b5..da86c7a205 100644 --- a/lib/dbh/dbh.class.php +++ b/lib/dbh/dbh.class.php @@ -376,12 +376,8 @@ class dbh { $sql = trim($sql); $sql = $this->formatFunction($sql); - - if(defined('IN_UPGRADE')) - { - $sql = $this->processDmChangeColumn($sql); - $sql = $this->processDmTableIndex($sql); - } + $sql = $this->processDmChangeColumn($sql); + $sql = $this->processDmTableIndex($sql); $actionPos = strpos($sql, ' '); $action = strtoupper(substr($sql, 0, $actionPos)); diff --git a/lib/zin/wg/backbtn/v1.php b/lib/zin/wg/backbtn/v1.php index 62075ccfea..7501e25303 100644 --- a/lib/zin/wg/backbtn/v1.php +++ b/lib/zin/wg/backbtn/v1.php @@ -78,8 +78,8 @@ class backBtn extends btn 'ai' => 'ai-models', 'api' => 'api-index', 'demand' => 'demand-browse,demand-view,my-work,my-contribute,feedback-adminview,feedback-admin,my-index,demandpool-track,product-dynamic', - 'issue' => 'issue-browse,issue-view,my-index,project-dynamic,execution-dynamic,project-view,execution-view', - 'risk' => 'risk-browse,risk-view,my-index,project-dynamic,execution-dynamic,project-view,execution-view', + 'issue' => 'issue-browse,issue-view,my-index,project-index,project-dynamic,execution-dynamic,project-view,execution-view', + 'risk' => 'risk-browse,risk-view,my-index,project-index,project-dynamic,execution-dynamic,project-view,execution-view', 'meeting' => 'meeting-browse,meeting-view,my-index,project-dynamic,execution-dynamic,project-view,execution-view', 'todo' => 'my-todo,my-effort', 'feedback' => 'my-effort,feedback-admin,feedback-browse,my-work,my-contribute', diff --git a/lib/zin/wg/docapp/js/v1.js b/lib/zin/wg/docapp/js/v1.js index fddd285d46..9f4bf593e1 100644 --- a/lib/zin/wg/docapp/js/v1.js +++ b/lib/zin/wg/docapp/js/v1.js @@ -55,9 +55,10 @@ window.getLang = function(key, args) * Check if the specified privilege exists, privileges are defined in $privs in module/doc/ui/app.html.php. * @param {string} priv */ -function hasPriv(priv) +function hasPriv(priv, value) { - return !!userPrivs[priv]; + const setting = userPrivs[priv]; + return value !== undefined ? setting === value : !!setting; } /** @@ -105,7 +106,7 @@ function processDocAppAction(action, docApp) function canModifySpace(space) { const docApp = getDocApp(); - if(!docApp) return false; + if(!docApp && !space) return false; space = typeof space !== 'object' ? docApp.getSpace(space) : space; return space && space.canModify !== false; } @@ -124,6 +125,7 @@ zui.AjaxForm.DEFAULT.onResult = function(res) delete res.docApp; delete res.load; + delete res.locate; delete res.callback; } }; @@ -230,9 +232,22 @@ function submitNewDoc(doc, spaceID, libID, moduleID, formData) keywords : doc.keywords, acl : 'private', space : spaceType, - uid : doc.contentType === 'doc' ? '' : (doc.uid || `doc${doc.id}`), + uid : (doc.uid || `doc${doc.id}`), }; if(formData) mergeDocFormData(docData, formData); + const getErrorMessage = (res) => { + if(typeof res.message === 'string') return res.message; + if(typeof res.message === 'object' && res.message && Object.values(res.message).length) + { + for(const x of Object.values(res.message)) + { + if(typeof x === 'string') return x; + if(Array.isArray(x) && x.length) return x[0]; + } + } + if(typeof data.error === 'string') return data.error; + return getLang('errorOccurred'); + }; return new Promise((resolve, reject) => { $.post(url, docData, (res) => @@ -242,7 +257,7 @@ function submitNewDoc(doc, spaceID, libID, moduleID, formData) const data = JSON.parse(res); if(typeof data !== 'object' || data.result === 'fail') { - throw new Error(data.message || data.error || getLang('errorOccurred')); + throw new Error(getErrorMessage(data)); } resolve($.extend(doc, {id: data.id}, docData, data.doc, {status: doc.status || data.status})); } @@ -288,9 +303,12 @@ function handleSaveDoc(doc) keywords : doc.keywords, acl : doc.acl, space : spaceType, - uid : doc.contentType === 'doc' ? '' : (doc.uid || `doc${doc.id}`), + uid : (doc.uid || `doc${doc.id}`), }; + const docAppData = docApp.doc.data; + if (doc.id === docAppData.id) docData.mailto = docAppData.mailto; + if(savingDocData[doc.id]) { mergeDocFormData(docData, savingDocData[doc.id]); @@ -477,7 +495,7 @@ const actionsMap = if(hasPriv('exportDoc')) moreItems.push({icon: 'export', text: lang.export, command: 'exportDoc'}); return [ - {icon: doc.isCollector ? 'star text-warning' : 'star-empty', text: doc.collects || null, hint: doc.isCollector ? lang.cancelCollection : lang.collect, rounded: 'lg', textClass: 'text-gray', type: 'ghost', command: hasPriv('collect') ? `collectDoc/${doc.id}` : null}, + hasPriv('collect', 'no') ? null : {icon: doc.isCollector ? 'star text-warning' : 'star-empty', text: doc.collects || null, hint: doc.isCollector ? lang.cancelCollection : lang.collect, rounded: 'lg', textClass: 'text-gray', type: 'ghost', command: hasPriv('collect') ? `collectDoc/${doc.id}` : null}, canEditDoc ? {icon: 'edit', type: 'ghost text-primary', hint: lang.edit, rounded: 'lg', command: 'startEditDoc'} : null, moreItems.length ? {icon: 'icon-ellipsis-v', type: 'dropdown', rounded: 'lg', placement: 'bottom-end', caret: false, items: moreItems} : null, ]; @@ -498,10 +516,10 @@ const actionsMap = /* 获取侧边栏没有模块时的操作按钮。 Get actions when sidebar no module. */ if(canAddModule && info.ui === 'sidebar-no-module') { - return [{text: lang.actions.addModule, command: `addModule/${lib.id}/0/${lib.id}/child`}]; + return [{text: lang.actions.addModule, command: `addModule/${lib.id}/0/${lib.id}/child`, icon: 'plus', type: 'primary-pale'}]; } - if(canAddModule && info.ui !== 'space-card' && info.ui !== 'sidebar' && info.ui !== 'sidebar-toolbar') items.push({text: lang.actions.addModule, command: `addModule/${lib.id}/0/${lib.id}/child`}); + if(canAddModule && info.ui !== 'space-card' && info.ui !== 'sidebar') items.push({text: lang.actions.addModule, command: `addModule/${lib.id}/0/${lib.id}/child`}); if(canModify && hasPriv('editLib')) items.push({text: lang.actions.editLib, command: `editLib/${lib.id}`}); if(canModify && hasPriv('moveLib') && info.ui !== 'space-card' && info.ui !== 'sidebar-toolbar') items.push({text: lang.moveTo, command: `moveLib/${lib.id}`}); if(canModify && hasPriv('deleteLib')) items.push({text: lang.actions.deleteLib, command: `deleteLib/${lib.id}`}); @@ -551,6 +569,7 @@ const actionsMap = */ 'doc-table': function(info) { + const docApp = this; return [hasPriv('batchMoveDoc') ? { text: getLang('batchMove'), onClick: function() @@ -560,10 +579,10 @@ const actionsMap = if(!selections.length) return; const docIdList = window.btoa(JSON.stringify(selections)); - const spaceType = getDocApp().spaceType; - const spaceID = getDocApp().spaceID; - const libID = getDocApp().libID; - const moduleID = getDocApp().moduleID; + const spaceType = docApp.spaceType; + const spaceID = docApp.spaceID; + const libID = docApp.libID; + const moduleID = docApp.moduleID; const url = $.createLink('doc', 'batchMoveDoc', 'type=' + spaceType + '&docIdList=' + docIdList + '&spaceID=' + spaceID + '&libID=' + libID + '&moduleID=' + moduleID); zui.Modal.open({size: 'sm', url: url}); @@ -584,6 +603,7 @@ const actionsMap = const canCreateLib = canModify && hasPriv('createLib'); const canCreateSpace = canModify && hasPriv('createSpace'); const canCreateOffice = canModify && hasPriv('createOffice'); + const canUploadDoc = canModify && hasPriv('uploadFile'); const canExportDoc = hasPriv('exportDoc'); /* 文档列表没有文档时的按钮。Actions for empty doc list. */ @@ -596,17 +616,17 @@ const actionsMap = [ canCreateDoc ? {icon: 'plus', text: lang.createDoc, command: 'startCreateDoc'} : null, canCreateDoc ? {type: 'divider'} : null, - canCreateOffice ? {icon: 'file-word', text: lang.createList.word, command: 'startCreateOffice/word'} : null, - canCreateOffice ? {icon: 'file-powerpoint', text: lang.createList.ppt, command: 'startCreateOffice/ppt'} : null, - canCreateOffice ? {icon: 'file-excel', text: lang.createList.excel, command: 'startCreateOffice/excel'} : null, - (canCreateLib || canCreateSpace) ? {type: 'divider'} : null, + canCreateOffice ? {icon: 'file-word', text: lang.createList.word, command: 'startCreateOffice/word'} : null, + canCreateOffice ? {icon: 'file-powerpoint', text: lang.createList.ppt, command: 'startCreateOffice/ppt'} : null, + canCreateOffice ? {icon: 'file-excel', text: lang.createList.excel, command: 'startCreateOffice/excel'} : null, + (canCreateOffice && (canCreateLib || canCreateSpace)) ? {type: 'divider'} : null, canCreateSpace ? {icon: 'cube', text: lang.createSpace, command: 'createSpace'} : null, canCreateLib ? {icon: 'wiki-lib', text: lang.createLib, command: 'createLib'} : null, ].filter(Boolean) : []; return [ ((canExportDoc && docApp.libID > 0 ) || canCreateDoc) ? {type: 'divider', style: {margin: '6px 0'}} : null, canExportDoc && docApp.libID > 0 ? {icon: 'export', text: lang.export, command: 'exportDoc'} : null, // 不在库中,无法导出 - canCreateDoc ? {icon: 'import', text: lang.uploadDoc, command: 'uploadDoc'} : null, + canUploadDoc ? {icon: 'import', text: lang.uploadDoc, command: 'uploadDoc'} : null, items.length ? {icon: 'plus', type: 'dropdown', btnType: 'primary', size: 'md', text: lang.create, items: items} : null, ]; }, @@ -988,7 +1008,7 @@ function getTableOptions(options, info) if(info.type === 'doc-list') { const tableCols = lang.tableCols; - const canModify = canModifySpace(); + const canModify = canModifySpace(this.space); options.cols.forEach(col => { if(typeof tableCols[col.name] === 'string') col.title = tableCols[col.name]; diff --git a/lib/zin/wg/docapp/v1.php b/lib/zin/wg/docapp/v1.php index 4baa311e18..01d805b460 100644 --- a/lib/zin/wg/docapp/v1.php +++ b/lib/zin/wg/docapp/v1.php @@ -117,6 +117,7 @@ class docApp extends wg $langData->fileDelete = $lang->file->delete; $langData->fileRename = $lang->file->edit; $langData->fileConfirmDelete = $lang->file->confirmDelete; + $langData->deleted = $lang->file->deleted; /** * 通过语言项定义文档表格列显示名称。 diff --git a/lib/zin/wg/filter/v1.php b/lib/zin/wg/filter/v1.php index ebc22f3710..9c35ddb10e 100644 --- a/lib/zin/wg/filter/v1.php +++ b/lib/zin/wg/filter/v1.php @@ -38,7 +38,23 @@ class filter extends wg set::items($items), set::menu($menu), set::multiple($multiple), - on::change("$onChange(e, '$name')") + on::change("$onChange(e, '$name')"), + on::inited()->do(<< \$this.zui().\$.state.items; + const hasUrl = () => \$this.zui().\$.state.items.hasOwnProperty('url'); + const waitItems = () => { + if(!hasUrl()) + { + const picker = \$this.zui().\$; + const itemValues = picker.state.items.map(item => item.value); + const values = picker.valueList.filter(value => itemValues.includes(value)); + picker.setValue(values); + return; + } + setTimeout(waitItems, 100); + } + if(hasUrl()) waitItems(); + JS) ); } diff --git a/lib/zin/wg/productsbox/js/v1.js b/lib/zin/wg/productsbox/js/v1.js index 43db3d3543..eaf4aee9fb 100644 --- a/lib/zin/wg/productsbox/js/v1.js +++ b/lib/zin/wg/productsbox/js/v1.js @@ -49,7 +49,7 @@ window.loadBranches = function(e) { let branch = $(e.target).closest('.productBox').find("[name^='branch']"); - if($('[name="charter"]').length && $('[name="charter"]').val()) + if($('[name="charter"]').length && $('[name="charter"]').val() > 0) { loadRoadmaps(e.target, branch.length > 0 ? branch[0] : branch); } @@ -63,7 +63,7 @@ window.loadBranches = function(e) { let branch = $(e.target).closest('.productBox').find("[name^='branch']"); - if($('[name="charter"]').length && $('[name="charter"]').val()) + if($('[name="charter"]').length && $('[name="charter"]').val() > 0) { loadRoadmaps(e.target, branch.length > 0 ? branch[0] : branch); } diff --git a/misc/ci/Jenkinsfile b/misc/ci/Jenkinsfile index c0d8f36854..537129ddb1 100644 --- a/misc/ci/Jenkinsfile +++ b/misc/ci/Jenkinsfile @@ -38,6 +38,7 @@ pipeline { environment { TZ="Asia/Shanghai" + GIT_HTTP_USER_AGENT="gitfox/pipeline" ZENTAO_RELEASE_PATH = "${WORKSPACE}/release" XUANXUAN_SRC_PATH = "${WORKSPACE}/xuansrc" diff --git a/misc/ci/Jenkinsfile.downgrade b/misc/ci/Jenkinsfile.downgrade index e634a34b1c..b603b2a9a8 100755 --- a/misc/ci/Jenkinsfile.downgrade +++ b/misc/ci/Jenkinsfile.downgrade @@ -1,5 +1,5 @@ library 'z-common@master' -library 'z-zentaopms@feat-240509' +library 'z-zentaopms' pipeline { agent { @@ -52,11 +52,11 @@ pipeline { checkout scm script { def triggeredBy = currentBuild.getBuildCauses() - println "Build triggered by: ${triggeredBy}" + println "Build triggered by: ${triggeredBy}" env.GIT_URL = sh(returnStdout: true, script: 'git config --get remote.origin.url').trim() env.GIT_COMMIT = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() - + def j = job.newJob() env.GIT_BRANCH = j.getBranchById(env.GIT_COMMIT) @@ -73,7 +73,7 @@ pipeline { [name: "downRepoExt", subDir: true, url: "https://${env.GITFOX_HOST}/git/${env.DOWNGRADE_EXT_GIT_REPO}.git", credentialsId: "gitfox-zcorp-cc-ci-robot", branchs: ["downgrade/blank"] ] ]) } - + } } @@ -86,11 +86,11 @@ pipeline { sh 'git config --global --add safe.directory $PWD/zentaoext' sh 'git config --global --add safe.directory $PWD/downRepo' sh 'git config --global --add safe.directory $PWD/downRepoExt' - + sh 'echo ${GIT_BRANCH}' sh 'mkdir -pv ${ZENTAO_RELEASE_PATH} && chown 1000:1000 ${ZENTAO_RELEASE_PATH}' sh 'make ciCommon' - + withCredentials([gitUsernamePassword(credentialsId: 'gitfox-zcorp-cc-ci-robot', gitToolName: 'Default')]) { sh '${ZENTAO_BUILD_PATH}/script/downgrade/fullDowngrade_v2.sh -c $PWD/misc -r $PWD/zentaopms -b ${GIT_BRANCH} -d downRepo -i -s -g $GIT_COMMIT -o "$ZENTAO_RELEASE_PATH" -p "$DOWNGRADE_VERSIONS" api extension framework lib module/*' } @@ -136,7 +136,7 @@ pipeline { sh 'git config --global --add safe.directory $SRC_ZENTAOEXT_PATH/zentaomax' sh 'git config --global --add safe.directory $SRC_ZENTAOEXT_PATH/zentaoipd' sh 'cd $SRC_ZENTAOEXT_PATH;make build' - + withCredentials([gitUsernamePassword(credentialsId: 'gitfox-zcorp-cc-ci-robot', gitToolName: 'Default')]) { sh '${ZENTAO_BUILD_PATH}/script/downgrade/fullDowngrade_v2.sh -c $PWD/misc -r $ZENTAO_BUILD_PATH/zentaoext -b ${GIT_BRANCH} -d downRepoExt -i -s -g $GIT_COMMIT -o "$ZENTAO_RELEASE_PATH" -p "$DOWNGRADE_VERSIONS" bizext biz max ipd' } diff --git a/module/action/config.php b/module/action/config.php index fdc331ceae..5eedf8abf1 100755 --- a/module/action/config.php +++ b/module/action/config.php @@ -121,8 +121,9 @@ $config->action->multipleUserFields = 'mailto,whitelist,reviewer,users,assignee, $config->action->objectFields['task']['closedReason'] = 'reasonList'; -$config->action->multipleObjectFields['bug']['os'] = 'osList'; -$config->action->multipleObjectFields['bug']['browser'] = 'browserList'; +$config->action->multipleObjectFields['bug']['os'] = 'osList'; +$config->action->multipleObjectFields['bug']['browser'] = 'browserList'; +$config->action->multipleObjectFields['testtask']['type'] = 'typeList'; $config->action->approvalFields['reviewStatus'] = 'reviewStatusList'; $config->action->approvalFields['reviewResult'] = 'reviewResultList'; diff --git a/module/action/model.php b/module/action/model.php index 5dd974cf05..6a2c452608 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -246,7 +246,8 @@ class actionModel extends model { if(empty($history)) return $history; $users = $this->loadModel('user')->getPairs('noletter'); - $objectType = $this->dao->select('objectType')->from(TABLE_ACTION)->where('id')->eq($history->action)->fetch('objectType'); + $action = $this->dao->select('objectType,objectID')->from(TABLE_ACTION)->where('id')->eq($history->action)->fetch(); + $objectType = $action->objectType == 'story' ? $this->dao->select('type')->from(TABLE_STORY)->where('id')->eq($action->objectID)->fetch('type') : $action->objectType; $objectTypeList = array(); if(!isset($objectTypeList[$objectType])) $this->app->loadLang($objectType); @@ -268,7 +269,7 @@ class actionModel extends model { $history->oldValue = ''; $oldValues = explode(',', $history->old); - foreach($oldValues as $key => $value) $history->oldValue .= zget($fieldList, $value) . ','; + foreach($oldValues as $value) $history->oldValue .= zget($fieldList, $value) . ','; $history->oldValue = trim($history->oldValue, ','); } @@ -276,7 +277,7 @@ class actionModel extends model { $history->newValue = ''; $newValues = explode(',', $history->new); - foreach($newValues as $key => $value) $history->newValue .= zget($fieldList, $value) . ','; + foreach($newValues as $value) $history->newValue .= zget($fieldList, $value) . ','; $history->newValue = trim($history->newValue, ','); } } @@ -291,7 +292,7 @@ class actionModel extends model { $history->oldValue = ''; $oldValues = explode(',', $history->old); - foreach($oldValues as $key => $value) $history->oldValue .= zget($users, $value) . ','; + foreach($oldValues as $value) $history->oldValue .= zget($users, $value) . ','; $history->oldValue = trim($history->oldValue, ','); } @@ -299,7 +300,7 @@ class actionModel extends model { $history->newValue = ''; $newValues = explode(',', $history->new); - foreach($newValues as $key => $value) $history->newValue .= zget($users, $value) . ','; + foreach($newValues as $value) $history->newValue .= zget($users, $value) . ','; $history->newValue = trim($history->newValue, ','); } } @@ -770,13 +771,11 @@ class actionModel extends model } } - if($action->objectType == 'story' && $action->action == 'closed') + $isCloseStory = $action->objectType == 'story' && $action->action == 'closed'; + if($isCloseStory && !empty($extra) && strpos($extra, '|') !== false) { - if(!empty($extra) && strpos($extra, '|') !== false) - { - list($extra, $status) = explode('|', $extra); - if(!empty($desc['extra'][$extra])) $actionDesc = str_replace('$extra', $desc['extra'][$extra], $desc['main']); - } + list($extra) = explode('|', $extra); + if(!empty($desc['extra'][$extra])) $actionDesc = str_replace('$extra', $desc['extra'][$extra], $desc['main']); } if($action->objectType == 'module' && strpos(',created,moved,', $action->action) !== false) @@ -1295,12 +1294,14 @@ class actionModel extends model */ public function computeBeginAndEnd(string $period, string $date, string $direction): array { + $period = strtolower($period); + /* 1. 确切的日期。 */ /* 1. The exact date. */ if($date) { - if($direction == 'next') return array('begin' => $date, 'end' => FUTURE_DATE); - if($direction == 'pre') return array('begin' => EPOCH_DATE, 'end' => $date); + if($direction == 'pre') return array('begin' => $date, 'end' => FUTURE_DATE); + if($direction == 'next') return array('begin' => EPOCH_DATE, 'end' => $date); return array('begin' => $date, 'end' => $date); } @@ -1318,11 +1319,6 @@ class actionModel extends model return array('begin' => $beginDate, 'end' => FUTURE_DATE); } - /* 3. 时间段。 */ - /* 3. Period. */ - $period = strtolower($period); - if($period == 'all') return array('begin' => EPOCH_DATE, 'end' => FUTURE_DATE); - $this->app->loadClass('date'); $today = date('Y-m-d'); @@ -1450,8 +1446,9 @@ class actionModel extends model $action = $this->getById($actionID); if(!$action || $action->action != 'deleted') return false; - list($table, $orderby, $field) = $this->actionTao->getUndeleteParamsByObjectType($action->objectType); - $object = $this->actionTao->getObjectBaseInfo($table, array('id' => $action->objectID), $field, $orderby); + list($table, $orderby, $field, $queryKey) = $this->actionTao->getUndeleteParamsByObjectType($action->objectType); + if(empty($queryKey)) $queryKey = 'id'; + $object = $this->actionTao->getObjectBaseInfo($table, array($queryKey => $action->objectID), $field, $orderby); if(empty($object)) return false; $result = $this->checkActionCanUndelete($action, $object); @@ -1634,7 +1631,7 @@ class actionModel extends model /* 移除搜索中的时间筛选条件。 */ /* Remove time filter from search. */ - $condition = preg_replace("/AND +date[\<\>]'\d{4}\-\d{2}\-\d{2}'/", '', $condition); + $condition = preg_replace("/AND +`?date`? +(<|>|<=|>=) +'\d{4}\-\d{2}\-\d{2}'/", '', $condition); $count = $this->dao->select('COUNT(1) AS count') ->from(TABLE_ACTION) ->where($condition) @@ -1661,7 +1658,10 @@ class actionModel extends model $actionType = strtolower($actionType); if(!isset($this->config->search->fields->{$objectType})) return false; - if((strpos($this->config->search->buildAction, ",{$actionType},") === false && $actionType != 'commented' && empty($_POST['comment'])) || ($actionType == 'commented' && empty($_POST['actioncomment']))) return false; + + $isCommentedAction = $actionType == 'commented'; + if(strpos($this->config->search->buildAction, ",{$actionType},") === false && !$isCommentedAction && empty($_POST['comment'])) return false; + if($isCommentedAction && empty($_POST['actioncomment'])) return false; if($actionType == 'deleted' || $actionType == 'erased') return $this->search->deleteIndex($objectType, $objectID); $field = $this->config->search->fields->{$objectType}; diff --git a/module/action/tao.php b/module/action/tao.php index 2b5824fa40..3a02ab9a83 100644 --- a/module/action/tao.php +++ b/module/action/tao.php @@ -369,7 +369,12 @@ class actionTao extends actionModel $name = $execution->name; if($execution->type == 'kanban') $method = 'kanban'; - if($name) $action->extra = !common::hasPriv('execution', $method) || ($method == 'kanban' && isonlybody()) || $this->config->vision == 'or' ? $name : html::a(helper::createLink('execution', $method, "executionID={$action->execution}"), $name, '', "data-app='execution'"); + if($name) + { + $isModalKanban = $method == 'kanban' && isonlybody(); + $canShowLink = common::hasPriv('execution', $method) && !$isModalKanban && $this->config->vision != 'or'; + $action->extra = $canShowLink ? html::a(helper::createLink('execution', $method, "executionID={$action->execution}"), $name, '', "data-app='execution'") : $name; + } } elseif($type == 'project') { @@ -790,7 +795,7 @@ class actionTao extends actionModel { $action->objectName = $this->dao->select('name')->from(TABLE_AI_ASSISTANT)->where('id')->eq($action->objectID)->fetch('name'); } - if(empty($action->objectName) && (substr($objectType, 0, 6) == 'gitlab' || substr($objectType, 0, 5) == 'gitea' || substr($objectType, 0, 4) == 'gogs' || substr($objectType, 0, 2) == 'mr')) $action->objectName = $action->extra; + if (empty($action->objectName) && preg_match('/^(gitlab|gitea|gogs|mr)/', $objectType)) $action->objectName = $action->extra; } /** @@ -983,9 +988,10 @@ class actionTao extends actionModel */ public function getUndeleteParamsByObjectType(string $objectType): array { - $table = $this->config->objectTables[$objectType]; - $orderby = ''; - $field = '*'; + $table = $this->config->objectTables[$objectType]; + $orderby = ''; + $field = '*'; + $queryKey = 'id'; switch($objectType) { case 'product': @@ -996,11 +1002,12 @@ class actionTao extends actionModel $field = 'id, acl, name, hasProduct'; break; case 'doc': - $table = TABLE_DOCCONTENT; - $orderby = 'version desc'; + $table = TABLE_DOCCONTENT; + $orderby = 'version desc'; + $queryKey = 'doc'; default: break; - } - return array($table, $orderby, $field); + } + return array($table, $orderby, $field, $queryKey); } } diff --git a/module/action/zen.php b/module/action/zen.php index 3c65ab131f..c1f43b644c 100644 --- a/module/action/zen.php +++ b/module/action/zen.php @@ -111,6 +111,10 @@ class actionZen extends action $params = "libID=0&moduelID=0&apiID={$trash->objectID}"; $methodName = 'index'; } + if($trash->objectType == 'doc') + { + $params = "docID={$trash->objectID}"; + } if(in_array($module, array('traincourse','traincontents'))) { $methodName = $module == 'traincourse' ? 'viewcourse' : 'viewchapter'; diff --git a/module/bi/config/metrics.php b/module/bi/config/metrics.php index 6568979a05..a8c3281d03 100644 --- a/module/bi/config/metrics.php +++ b/module/bi/config/metrics.php @@ -2714,7 +2714,7 @@ $config->bi->builtin->metrics[] = array 'unit' => 'count', 'dateType' => 'month', 'desc' => '按产品统计的月度解决Bug数是指每天在产品开发过程中被解决并关闭的Bug的数量。该度量项可以帮助我们了解开发团队解决Bug的速度和效率。', - 'definition' => "产品中创建时间在某年某月的Bug个数求和\n过滤已删除的Bug\n过滤已删除的产品" + 'definition' => "产品中Bug个数求和\n解决日期为某年某月\n过滤已删除的Bug\n过滤已删除的产品" ); $config->bi->builtin->metrics[] = array @@ -2728,7 +2728,7 @@ $config->bi->builtin->metrics[] = array 'unit' => 'count', 'dateType' => 'month', 'desc' => '按产品统计的月度关闭Bug数是指在某月度关闭的Bug数量。这个度量项反映了产品开发过程中每月被确认并关闭的Bug的数量。该度量项可以帮助我们了解开发团队对Bug进行确认与关闭的速度和效率。', - 'definition' => "产品中创建时间在某年某月的Bug个数求和\n过滤已删除的Bug\n过滤已删除的产品" + 'definition' => "产品中关闭时间在某年某月的Bug个数求和\n过滤已删除的Bug\n过滤已删除的产品" ); $config->bi->builtin->metrics[] = array @@ -4646,5 +4646,61 @@ $config->bi->builtin->metrics[] = array 'unit' => 'count', 'dateType' => 'month', 'desc' => '按项目统计的月度新增Bug数是指在某年度新发现的Bug数量。这个度量项反映了系统或项目在某月度出现的新问题数量。月度新增Bug数的增加可能意味着质量控制存在问题,需要及时进行处理和改进。', - 'definition' => "项目中创建时间在某年某月的Bug个数求和,过滤已删除的Bug,过滤已删除的项目。" + 'definition' => "项目中创建时间在某年某月的Bug个数求和\n过滤已删除的Bug\n过滤已删除的项目\n" +); + +$config->bi->builtin->metrics[] = array +( + 'name' => '按项目统计的月度关闭Bug数', + 'alias' => '关闭Bug数', + 'code' => 'count_of_monthly_closed_bug_in_project', + 'purpose' => 'scale', + 'scope' => 'project', + 'object' => 'bug', + 'unit' => 'count', + 'dateType' => 'month', + 'desc' => '按项目统计的月度关闭Bug数是指在某月度关闭的Bug数量。这个度量项反映了产品开发过程中每月被确认并关闭的Bug的数量。该度量项可以帮助我们了解开发团队对Bug进行确认与关闭的速度和效率。', + 'definition' => "项目中关闭时间在某年某月的Bug个数求和,过滤已删除的Bug,过滤已删除的项目。", +); + +$config->bi->builtin->metrics[] = array +( + 'name' => '按项目统计的月度修复Bug数', + 'alias' => '解决Bug数', + 'code' => 'count_of_monthly_fixed_bug_in_project', + 'purpose' => 'scale', + 'scope' => 'project', + 'object' => 'bug', + 'unit' => 'count', + 'dateType' => 'month', + 'desc' => '按项目统计的月度修复Bug数是指每月在项目开发过程中被解决并关闭的Bug的数量。该度量项可以帮助我们了解开发团队解决Bug的速度和效率。', + 'definition' => "项目中Bug的个数求和\n关闭时间为某年某月\n解决方案为已解决\n过滤已删除的Bug\n过滤已删除的项目\n", +); + +$config->bi->builtin->metrics[] = array +( + 'name' => '按项目统计的年度修复Bug数', + 'alias' => '修复Bug数', + 'code' => 'count_of_annual_fixed_bug_in_project', + 'purpose' => 'scale', + 'scope' => 'project', + 'object' => 'bug', + 'unit' => 'count', + 'dateType' => 'year', + 'desc' => '按项目统计的年度修复Bug数是指在某年度解决并关闭的Bug数量。这个度量项反映了项目在某年度解决的问题数量。年度修复Bug数越多可能说明开发团队在Bug解决方面的工作效率较高。', + 'definition' => "项目中Bug的个数求和\n关闭时间为某年\n解决方案为已解决\n过滤已删除的Bug\n过滤已删除的项目\n", +); + +$config->bi->builtin->metrics[] = array +( + 'name' => '按项目统计的年度新增有效Bug数', + 'alias' => '新增有效Bug数', + 'code' => 'count_of_annual_created_effective_bug_in_project', + 'purpose' => 'scale', + 'scope' => 'project', + 'object' => 'bug', + 'unit' => 'count', + 'dateType' => 'year', + 'desc' => '按项目统计的年度新增有效Bug数是指项目在某年度新发现的真正具有影响和价值的Bug数量。有效Bug通常是指导致项目不正常运行或影响用户体验的Bug。统计有效Bug数可以帮助评估项目的稳定性和质量也可以评估测试人员之前的协作或对项目的了解程度。', + 'definition' => "项目中Bug个数求和\n创建时间为某年\n解决方案为已解决和延期处理或者状态为激活\n过滤已删除的Bug\n过滤已删除的项目\n", ); diff --git a/module/bug/control.php b/module/bug/control.php index c6f0915e65..9667d9508a 100755 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -172,14 +172,14 @@ class bug extends control $product = $this->product->getByID($bug->product); $branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($bug->product); $projects = $this->product->getProjectPairsByProduct($bug->product, (string)$bug->branch); - $projectID = key($projects); + $projectID = isset($projects[$bug->project]) ? $bug->project : key($projects); if(in_array($this->app->tab, array('project', 'execution'))) { $objectType = $this->app->tab == 'project' ? 'projectID' : 'executionID'; $this->view->{$objectType} = $this->session->{$this->app->tab}; } - $this->session->set("project", $projectID, 'project'); + $this->session->set('project', $projectID, 'project'); $this->session->set('storyList', '', 'product'); $this->session->set('projectList', $this->app->getURI(true) . "#app={$this->app->tab}", 'project'); diff --git a/module/bug/zen.php b/module/bug/zen.php index 594c8afe8f..c1f5a1f12a 100644 --- a/module/bug/zen.php +++ b/module/bug/zen.php @@ -2142,14 +2142,7 @@ class bugZen extends bug if($this->app->tab == 'execution') { - if(!preg_match("/(m=|\/)execution(&f=|-)bug(&|-|\.)?/", $this->session->bugList)) - { - $location = $this->session->bugList; - } - else - { - $location = $this->createLink('execution', 'bug', "executionID=$executionID"); - } + $location = $this->createLink('execution', 'bug', "executionID=$executionID"); } elseif($this->app->tab == 'project') { diff --git a/module/chart/model.php b/module/chart/model.php index eec958eef8..c896d71487 100644 --- a/module/chart/model.php +++ b/module/chart/model.php @@ -23,7 +23,6 @@ class chartModel extends model parent::__construct($appName); $this->loadBIDAO(); $this->loadModel('bi'); - $this->viewableObjects = $this->bi->getViewableObject('chart'); } /** @@ -36,7 +35,8 @@ class chartModel extends model */ public function checkAccess($chartID, $method = 'preview') { - if(!in_array($chartID, $this->viewableObjects)) + $viewableObjects = $this->bi->getViewableObject('chart'); + if(!in_array($chartID, $viewableObjects)) { return $this->app->control->sendError($this->lang->chart->accessDenied, helper::createLink('chart', $method)); } @@ -80,6 +80,7 @@ class chartModel extends model $this->app->loadModuleConfig('screen'); + $viewableObjects = $this->bi->getViewableObject('chart'); /* 获取分组下的第一个图表。*/ /* Get the first chart under the group. */ foreach($groups as $groupID) @@ -91,7 +92,7 @@ class chartModel extends model ->markRight(1) ->andWhere("FIND_IN_SET({$groupID}, `group`)") ->andWhere('stage')->eq('published') - ->andWhere('id')->in($this->viewableObjects) + ->andWhere('id')->in($viewableObjects) ->orderBy('id_desc') ->limit(1) ->fetch(); @@ -174,6 +175,7 @@ class chartModel extends model $this->app->loadModuleConfig('screen'); + $viewableObjects = $this->bi->getViewableObject('chart'); /* 获取每个分组下的图表以供生成菜单树。*/ /* Get the charts under each group for generating the menu tree. */ $chartGroups = array(); @@ -186,7 +188,7 @@ class chartModel extends model ->markRight(1) ->andWhere("FIND_IN_SET({$group->id}, `group`)") ->andWhere('stage')->eq('published') - ->andWhere('id')->in($this->viewableObjects) + ->andWhere('id')->in($viewableObjects) ->orderBy($orderBy) ->fetchAll(); } diff --git a/module/common/model.php b/module/common/model.php index e0bee84e49..c29c200b4b 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -907,10 +907,10 @@ class commonModel extends model if($value != stripslashes((string)$old->$key)) { - $diff = ''; - if(substr_count((string)$value, "\n") > 1 or - substr_count((string)$old->$key, "\n") > 1 or - strpos('name,title,desc,spec,steps,content,digest,verify,report,definition,analysis,summary,prevention,resolution,outline,schedule,minutes,sql,interface,ui,langs,performance,privileges,search,actions,deploy,bi,safe,other', strtolower($key)) !== false) + $diff = ''; + $showDiff = (substr_count((string)$value, "\n") > 1 || substr_count((string)$old->$key, "\n") > 1 || strpos(',name,title,desc,spec,steps,content,digest,verify,report,definition,analysis,summary,prevention,resolution,outline,schedule,minutes,sql,interface,ui,langs,performance,privileges,search,actions,deploy,bi,safe,other,', ',' . strtolower($key) . ',') !== false); + $hiddenDiff = array('bug' => 'resolution'); + if($showDiff && (!isset($hiddenDiff[$moduleName]) || (isset($hiddenDiff[$moduleName]) && strpos(",$hiddenDiff[$moduleName],", ",$key,") === false))) { $diff = commonModel::diff((string)$old->$key, (string)$value); } @@ -1567,14 +1567,39 @@ eof; if(empty($object)) return true; $account = $app->user->account; - if(!empty($object->openedBy) && $object->openedBy == $account) return true; - if(!empty($object->addedBy) && $object->addedBy == $account) return true; - if(!empty($object->account) && $object->account == $account) return true; - if(!empty($object->assignedTo) && $object->assignedTo == $account) return true; - if(!empty($object->finishedBy) && $object->finishedBy == $account) return true; - if(!empty($object->canceledBy) && $object->canceledBy == $account) return true; - if(!empty($object->closedBy) && $object->closedBy == $account) return true; - if(!empty($object->lastEditedBy) && $object->lastEditedBy == $account) return true; + if($module == 'task' && !empty($object->team)) + { + $taskModel = $app->control->loadModel('task'); + if($object->mode == 'linear') + { + if($method == 'assignto' && !in_array($object->status, array('done', 'cancel', 'closed'))) return false; + if($method == 'start' && in_array($object->status, array('wait', 'doing'))) + { + if($object->assignedTo != $account) return false; + + $currentTeam = $taskModel->getTeamByAccount($object->team, $account); + if($currentTeam && $currentTeam->status == 'wait') return true; + } + if($method == 'finish' && $object->assignedTo != $account) return false; + } + elseif($object->mode == 'multi') + { + $currentTeam = $taskModel->getTeamByAccount($object->team, $account); + if($method == 'start' && in_array($object->status, array('wait', 'doing')) && $currentTeam && $currentTeam->status == 'wait') return true; + if($method == 'finish' && (empty($currentTeam) || $currentTeam->status == 'done')) return false; + } + } + else + { + if(!empty($object->openedBy) && $object->openedBy == $account) return true; + if(!empty($object->addedBy) && $object->addedBy == $account) return true; + if(!empty($object->account) && $object->account == $account) return true; + if(!empty($object->assignedTo) && $object->assignedTo == $account) return true; + if(!empty($object->finishedBy) && $object->finishedBy == $account) return true; + if(!empty($object->canceledBy) && $object->canceledBy == $account) return true; + if(!empty($object->closedBy) && $object->closedBy == $account) return true; + if(!empty($object->lastEditedBy) && $object->lastEditedBy == $account) return true; + } return false; } @@ -1916,18 +1941,21 @@ eof; { if(!isset($executionsStatus[$object->execution])) { - $execution = $commonModel->loadModel('execution')->getByID($object->execution); + $execution = $commonModel->loadModel('execution')->getByID((int)$object->execution); $executionsStatus[$object->execution] = $execution ? $execution->status : ''; } if($executionsStatus[$object->execution] == 'closed' || !empty($config->CRProject)) return false; /* Check the execution's project is closed. */ - if(isset($object->project) && !isset($projectsStatus[$object->project])) + if(isset($object->project)) { - $project = $commonModel->loadModel('project')->getByID((int)$object->project); - $projectsStatus[$object->project] = $project ? $project->status : ''; + if(!isset($projectsStatus[$object->project])) + { + $project = $commonModel->loadModel('project')->getByID((int)$object->project); + $projectsStatus[$object->project] = $project ? $project->status : ''; + } + if($projectsStatus[$object->project] == 'closed') return false; } - if($projectsStatus[$object->project] == 'closed') return false; } return true; diff --git a/module/design/js/linkcommit.ui.js b/module/design/js/linkcommit.ui.js index 286fa4a64e..0f94bcb51e 100644 --- a/module/design/js/linkcommit.ui.js +++ b/module/design/js/linkcommit.ui.js @@ -40,3 +40,8 @@ $(document).off('click','.batch-btn').on('click', '.batch-btn', function() $.ajaxSubmit({url, data: form}); }); + +window.calcHeight = function() +{ + return $('#viewCommitModal').height() - $('#viewCommitModal .modal-header').height() - $('#viewCommitModal .modal-body > .flex').height() - 200; +} diff --git a/module/design/ui/linkcommit.html.php b/module/design/ui/linkcommit.html.php index a3d3f7f9a7..30c7f303ad 100644 --- a/module/design/ui/linkcommit.html.php +++ b/module/design/ui/linkcommit.html.php @@ -33,8 +33,9 @@ if(empty($repoID)) } else { - featureBar + div ( + setClass('flex'), div ( setClass('select-repo-box'), @@ -95,6 +96,7 @@ else set::footToolbar($footToolbar), set::checkInfo(jsRaw('function(checkedIDList){return \'\';}')), set::rowKey('revision'), + set::height(jsRaw('calcHeight')), set::footPager(usePager('pager', 'noTotalCount')) ); } diff --git a/module/dimension/model.php b/module/dimension/model.php index 265d800cc8..b5b35d7e89 100644 --- a/module/dimension/model.php +++ b/module/dimension/model.php @@ -14,7 +14,6 @@ class dimensionModel extends model public function __construct() { parent::__construct(); - $this->viewableObjects = $this->loadModel('bi')->getViewableObject('dimension'); } /** @@ -39,7 +38,8 @@ class dimensionModel extends model */ public function getFirst(): object|false { - $firstID = current($this->viewableObjects); + $viewableObjects = $this->loadModel('bi')->getViewableObject('dimension'); + $firstID = current($viewableObjects); return $this->dao->select('*')->from(TABLE_DIMENSION)->where('id')->eq($firstID)->fetch(); } @@ -52,7 +52,8 @@ class dimensionModel extends model */ public function getList(): array { - return $this->dao->select('*')->from(TABLE_DIMENSION)->where('id')->in($this->viewableObjects)->fetchAll('id'); + $viewableObjects = $this->loadModel('bi')->getViewableObject('dimension'); + return $this->dao->select('*')->from(TABLE_DIMENSION)->where('id')->in($viewableObjects)->fetchAll('id'); } /** @@ -90,7 +91,8 @@ class dimensionModel extends model if(!$dimensionID && $this->session->dimension) $dimensionID = $this->session->dimension; /* 验证维度是否可见 */ - if($dimensionID && $this->viewableObjects && !in_array($dimensionID, $this->viewableObjects)) $dimensionID = current($this->viewableObjects); + $viewableObjects = $this->loadModel('bi')->getViewableObject('dimension'); + if($dimensionID && $viewableObjects && !in_array($dimensionID, $viewableObjects)) $dimensionID = current($viewableObjects); /* 如果维度 ID 不为空,检查对应的对象是否存在。*/ /* If dimension ID is not empty, check if the object exists. */ diff --git a/module/doc/control.php b/module/doc/control.php index 959c025288..9eef090671 100755 --- a/module/doc/control.php +++ b/module/doc/control.php @@ -1238,7 +1238,7 @@ class doc extends control $this->doc->moveLib($libID, $data); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - return $this->docZen->responseAfterMove($this->post->space, $spaceType, $libID); + return $this->docZen->responseAfterMove($this->post->space, $libID); } $this->docZen->setAclForCreateLib($spaceType); @@ -1294,7 +1294,7 @@ class doc extends control $this->action->logHistory($actionID, $changes); } - return $this->docZen->responseAfterMove($this->post->space, $spaceType, $data->lib, $locate, $docID); + return $this->docZen->responseAfterMove($this->post->space, $data->lib, $docID); } $projects = $this->loadModel('project')->getPairsByProgram(0, 'all', false, 'order_asc'); @@ -1576,7 +1576,7 @@ class doc extends control if($noPicks || strpos($picks, ',space,') !== false) $data['spaces'] = $spaces; if($noPicks || strpos($picks, ',lib,') !== false) $data['libs'] = array_values($libs); if($noPicks || strpos($picks, ',module,') !== false) $data['modules'] = array_values($this->doc->getModulesOfLibs($libIds)); - if($noPicks || strpos($picks, ',doc,') !== false) $data['docs'] = array_values($this->doc->getDocsOfLibs($libIds + array($spaceID))); + if($noPicks || strpos($picks, ',doc,') !== false) $data['docs'] = array_values($this->doc->getDocsOfLibs($libIds + array($spaceID), $type)); $this->send($data); } diff --git a/module/doc/js/quick.ui.js b/module/doc/js/quick.ui.js index ce3a3430c9..0e09a39067 100644 --- a/module/doc/js/quick.ui.js +++ b/module/doc/js/quick.ui.js @@ -56,6 +56,7 @@ window.docAppActions.doc = function(info) const object = doc.object; const lib = doc.libInfo; const objectUrl = object ? getObjectBrowseUrl(object, doc.objectType) : null; + const libUrl = object ? getObjectBrowseUrl(object, doc.objectType, lib.id) : null; actions.unshift( { type: 'custom', @@ -63,7 +64,7 @@ window.docAppActions.doc = function(info) className: 'order-first mr-2', html: `${getDocAppLang('position')}${getDocAppLang('colon')} ` + [ objectUrl ? `${object.name || object.title}` : null, - lib ? `${lib.name} ` : null, + lib ? `${lib.name} ` : null, ].filter(Boolean).join(' / '), }); } diff --git a/module/doc/model.php b/module/doc/model.php index a3b8e7b043..23c043d69c 100644 --- a/module/doc/model.php +++ b/module/doc/model.php @@ -726,18 +726,16 @@ class docModel extends model * 获取文档列表数据。 * Get doc list. * - * @param array $docIdList - * @param string $orderBy - * @param object $pager + * @param array $libs + * @param string $spaceType * @access public * @return array */ - public function getDocsOfLibs(array $libs): array + public function getDocsOfLibs(array $libs, string $spaceType): array { $docs = $this->dao->select('t1.*')->from(TABLE_DOC)->alias('t1') ->leftJoin(TABLE_MODULE)->alias('t2')->on('t1.module=t2.id') - ->where('t1.deleted')->eq(0) - ->andWhere('t1.lib')->in($libs) + ->where('t1.lib')->in($libs) ->andWhere('t1.vision')->eq($this->config->vision) ->andWhere('t1.templateType')->eq('') ->andWhere("(t1.status = 'normal' or (t1.status = 'draft' and t1.addedBy='{$this->app->user->account}'))") @@ -747,8 +745,7 @@ class docModel extends model ->fetchAll('id'); $rootDocs = $this->dao->select('*')->from(TABLE_DOC) - ->where('deleted')->eq(0) - ->andWhere('lib')->in($libs) + ->where('lib')->in($libs) ->andWhere('vision')->eq($this->config->vision) ->andWhere('templateType')->eq('') ->andWhere("(status = 'normal' or (status = 'draft' and addedBy='{$this->app->user->account}'))") @@ -758,7 +755,7 @@ class docModel extends model $docs = $docs + $rootDocs; $docs = $this->processCollector($docs); - $docs = $this->filterPrivDocs($docs); + $docs = $this->filterPrivDocs($docs, $spaceType); foreach($docs as &$doc) { @@ -778,20 +775,24 @@ class docModel extends model * Filter docs which has privilege. * * @param array $docs + * @param string $spaceType * @access public * @return array */ - public function filterPrivDocs(array $docs): array + public function filterPrivDocs(array $docs, string $spaceType): array { $currentAccount = $this->app->user->account; $userGroups = $this->app->user->groups; $privDocs = array(); - foreach($docs as $index => $doc) + foreach($docs as $doc) { - if($doc->acl == 'open' || ($doc->acl == 'private' && $doc->addedBy == $currentAccount) || strpos(",$doc->users,", ",$currentAccount,") !== false) + $isOpen = $doc->acl == 'open'; + $isAuthorOrAdmin = $doc->acl == 'private' && ($doc->addedBy == $currentAccount || ($this->app->user->admin && $spaceType !== 'mine')); + $isInUsers = strpos(",$doc->users,", ",$currentAccount,") !== false; + if($isOpen || $isAuthorOrAdmin || $isInUsers) { - $privDocs[$index] = $doc; + $privDocs[] = $doc; } elseif(!empty($doc->groups)) { @@ -799,7 +800,7 @@ class docModel extends model { if(strpos(",$doc->groups,", ",$groupID,") !== false) { - $privDocs[$index] = $doc; + $privDocs[] = $doc; break; } } @@ -1297,7 +1298,6 @@ class docModel extends model } if($type == 'project') { - $account = $this->app->user->account; $projects = $this->loadModel('project')->getListByCurrentUser(); $involvedProjects = $this->project->getInvolvedListByCurrentUser(); $spaceID = $this->project->checkAccess($spaceID, $projects); @@ -2156,7 +2156,7 @@ class docModel extends model { if(!$this->checkPrivDoc($doc)) unset($docs[$id]); } - $docIdList = empty($docs) ? 0 : $this->dao->select('id')->from(TABLE_DOC)->where($type)->eq($objectID)->andWhere('id')->in(array_keys($docs))->get(); + $docIdList = empty($docs) ? 0 : $this->dao->select('id')->from(TABLE_DOC)->where($type)->eq($objectID)->andWhere('vision')->eq($this->config->vision)->andWhere('id')->in(array_keys($docs))->get(); if($type == 'product') { diff --git a/module/doc/ui/app.html.php b/module/doc/ui/app.html.php index 42b276f264..0599d5763e 100644 --- a/module/doc/ui/app.html.php +++ b/module/doc/ui/app.html.php @@ -39,6 +39,7 @@ $privs['editLib'] = hasPriv('doc', 'editLib'); $privs['moveLib'] = hasPriv('doc', 'moveLib'); $privs['deleteLib'] = hasPriv('doc', 'deleteLib'); $privs['sortDocLib'] = hasPriv('doc', 'sortDocLib'); +$privs['uploadFile'] = hasPriv('doc', 'create'); $privs['exportFiles'] = hasPriv('doc', 'exportFiles'); $privs['createSpace'] = $hasCustomSpace && hasPriv('doc', 'createSpace'); $privs['deleteSpace'] = $hasCustomSpace && hasPriv('doc', 'deleteSpace'); diff --git a/module/doc/ui/quick.html.php b/module/doc/ui/quick.html.php index 979a4c0a31..35361a5123 100644 --- a/module/doc/ui/quick.html.php +++ b/module/doc/ui/quick.html.php @@ -11,10 +11,11 @@ declare(strict_types=1); namespace zin; $privs = array(); -$privs['edit'] = hasPriv('doc', 'edit'); -$privs['delete'] = hasPriv('doc', 'delete'); -$privs['moveDoc'] = hasPriv('doc', 'moveDoc'); -$privs['collect'] = hasPriv('doc', 'collect'); +$privs['edit'] = hasPriv('doc', 'edit'); +$privs['delete'] = hasPriv('doc', 'delete'); +$privs['moveDoc'] = hasPriv('doc', 'moveDoc'); +$privs['collect'] = hasPriv('doc', 'collect'); +$privs['batchMoveDoc'] = hasPriv('doc', 'batchMoveDoc'); $spaceID = 1; $libID = $menu['id']; diff --git a/module/doc/zen.php b/module/doc/zen.php index 68a81f4843..ebf3f1355f 100644 --- a/module/doc/zen.php +++ b/module/doc/zen.php @@ -284,9 +284,7 @@ class docZen extends doc $docAppActions = array(); $docAppActions[] = array('update', 'lib', $lib); - $docAppActions[] = array('list:selectLib', $libID); - $docAppActions[] = array('home:selectSpace', $objectID, $libID); - $docAppActions[] = array('load', null, null, null, array('picks' => 'lib')); + $docAppActions[] = array('selectSpace', $objectID, $libID); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => array('name' => 'locateNewLib', 'params' => array($type, $objectID, $libID, $libName)), 'docApp' => $docAppActions)); } @@ -394,24 +392,18 @@ class docZen extends doc * Return after move lib or doc. * * @param string $space - * @param string $spaceType * @param int $libID - * @param string $locateLink * @param int $docID * * @access protected - * @return bool|int + * @return void */ - protected function responseAfterMove(string $space, string $spaceType = 'mine', int $libID = 0, string $locateLink = '', int $docID = 0): bool|int + protected function responseAfterMove(string $space, int $libID = 0, int $docID = 0) { - if(empty($locateLink)) - { - if($spaceType == 'mine') $locateLink = $this->createLink('doc', 'mySpace', "type=mine&libID={$libID}"); - elseif($spaceType == 'custom') $locateLink = $this->createLink('doc', 'teamSpace', "objectID={$space}&libID={$libID}"); - else $locateLink = true; - } - if($locateLink === 'true') $locateLink = true; - return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'docApp' => $docID ? array('executeCommand', 'handleMovedDoc', array($docID, $space, $libID)) : array('load', null, null, null, array('noLoading' => true, 'picks' => 'lib')))); + $spaceID = (int)(explode('.', $space)[1]); + if($docID) $docAppAction = array('executeCommand', 'handleMovedDoc', array($docID, $space, $libID)); + else $docAppAction = array('selectSpace', $spaceID, $libID); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'docApp' => $docAppAction)); } /** diff --git a/module/execution/model.php b/module/execution/model.php index 6a5b0702b3..ae8e08efe2 100755 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -3580,7 +3580,7 @@ class executionModel extends model $onSQL = ''; foreach($matches[1] as $matchIndex => $match) $onSQL .= "t2.account {$match} or "; $onSQL = trim($onSQL, ' or '); - $sql = $sql->leftJoin(TABLE_TASKTEAM)->alias('t2')->on("t2.task = t1.id and ({$onSQL})"); + if($onSQL) $sql = $sql->leftJoin(TABLE_TASKTEAM)->alias('t2')->on("t2.task = t1.id and ({$onSQL})"); } $orderBy = array_map(function($value){return 't1.' . $value;}, explode(',', $orderBy)); @@ -4613,6 +4613,8 @@ class executionModel extends model $rows = array(); $this->app->loadConfig('project'); + $this->getLimitedExecution(); + $executionList = array(); foreach($executions as $execution) $executionList[$execution->id] = $execution; foreach($executionList as $execution) @@ -4687,6 +4689,8 @@ class executionModel extends model { $rawAction = str_replace('Task', '', $action); if(!commonModel::hasPriv('task', $rawAction)) continue; + if(!common::hasDBPriv($task, 'task', $rawAction)) continue; + $clickable = $this->task->isClickable($task, $rawAction); $action = array('name' => $action); if(!$clickable) $action['disabled'] = true; diff --git a/module/execution/test/lib/tree.ui.class.php b/module/execution/test/lib/tree.ui.class.php new file mode 100644 index 0000000000..dbda4483d7 --- /dev/null +++ b/module/execution/test/lib/tree.ui.class.php @@ -0,0 +1,39 @@ +initForm('execution', 'tree', array('execution' => '2'), 'appIframe-execution'); + if($click) $form->dom->onlyStoryBtn->click(); + $form->wait(1); + if(count($form->dom->getElementList($form->dom->xpath[$level])->element) != $num) return $this->failed('数据错误'); + return $this->success('数据正确'); + } + + /** + * 检查树状图中的任务、需求详情显示。 + * Check detail data. + * + * @access public + * @return object + */ + public function checkDetail() + { + $form = $this->initForm('execution', 'tree', array('execution' => '2'), 'appIframe-execution'); + $form->dom->detail->click(); + $form->wait(1); + if($form->dom->detail->getText() == $form->dom->title->getText()) return $this->success('详情数据正确'); + return $this->failed('详情数据错误'); + } +} diff --git a/module/execution/test/ui/page/tree.php b/module/execution/test/ui/page/tree.php new file mode 100644 index 0000000000..fed30a7e2e --- /dev/null +++ b/module/execution/test/ui/page/tree.php @@ -0,0 +1,19 @@ + "//*[@id='mainContent']/div/div[1]/div/menu/li[@z-type='item']", + 'secondLevelA' => "//*[@id='mainContent']/div/div[1]/div/menu/li[1]/menu/li[@z-type='item']", + 'secondLevelB' => "//*[@id='mainContent']/div/div[1]/div/menu/li[2]/menu/li[@z-type='item']", + 'thirdLevelB' => "//*[@id='mainContent']/div/div[1]/div/menu/li[2]/menu/li[1]/menu/li[@z-type='item']", + 'fourthLevelB' => "//*[@id='mainContent']/div/div[1]/div/menu/li[2]/menu/li[1]/menu/li[1]/menu/li[@z-type='item']", + 'onlyStoryBtn' => "//*[@id='featureBar']/menu/div/label", + 'detail' => "//*[@id='mainContent']/div/div[1]/div/menu/li[2]/menu/li/menu/li[1]/div/div/div/div/span[3]", + 'title' => "//*[@id='detailBlock']/div[1]/div[1]/span[2]" + ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/execution/test/ui/tree.php b/module/execution/test/ui/tree.php new file mode 100644 index 0000000000..df5c49367e --- /dev/null +++ b/module/execution/test/ui/tree.php @@ -0,0 +1,116 @@ +#!/usr/bin/env php +id->range('1-100'); +$product->name->range('产品1, 产品2'); +$product->type->range('normal'); +$product->gen(2); + +$project = zenData('project'); +$project->id->range('1-100'); +$project->project->range('0, 1'); +$project->model->range('scrum, []'); +$project->type->range('project, sprint'); +$project->auth->range('extend, []'); +$project->storyType->range('story, []'); +$project->parent->range('0, 1'); +$project->path->range('`,1,`, `,1,2,`'); +$project->grade->range('1'); +$project->name->range('项目, 执行'); +$project->begin->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD'); +$project->end->range('(+2M)-(+3M):1D')->type('timestamp')->format('YY/MM/DD'); +$project->openedBy->range('user1'); +$project->acl->range('open'); +$project->status->range('doing'); +$project->gen(2); + +$projectproduct = zenData('projectproduct'); +$projectproduct->project->range('1{2}, 2{2}'); +$projectproduct->product->range('1, 2'); +$projectproduct->gen(4); + +$story = zenData('story'); +$story->id->range('1-100'); +$story->parent->range('0'); +$story->isParent->range('0'); +$story->root->range('1-100'); +$story->path->range('`,1,`, `,2,`, `,3,`'); +$story->grade->range('1'); +$story->product->range('1{2}, 2'); +$story->module->range('1{2}, 2'); +$story->plan->range('0'); +$story->title->range('1-100'); +$story->type->range('story'); +$story->estimate->range('0'); +$story->status->range('active'); +$story->stage->range('projected'); +$story->assignedTo->range('[]'); +$story->version->range('1'); +$story->gen(3); + +$storySpec = zenData('storyspec'); +$storySpec->story->range('1-100'); +$storySpec->version->range('1'); +$storySpec->title->range('1-100'); +$storySpec->gen(3); + +$projectStory = zenData('projectstory'); +$projectStory->project->range('1{3}, 2{3}'); +$projectStory->product->range('1{2}, 2'); +$projectStory->branch->range('0'); +$projectStory->story->range('1, 2, 3'); +$projectStory->version->range('1'); +$projectStory->order->range('1, 2, 3'); +$projectStory->gen(6); + +$module = zenData('module'); +$module->id->range('1-100'); +$module->root->range('1, 2, 2'); +$module->branch->range('0'); +$module->name->range('产品1模块1, 产品2模块1, 执行模块1'); +$module->parent->range('0'); +$module->path->range('`,1,`, `,2,`, `,3,`'); +$module->grade->range('1'); +$module->type->range('story{2}, task'); +$module->short->range('0'); +$module->gen(3); + +$task = zenData('task'); +$task->id->range('1-100'); +$task->project->range('1'); +$task->execution->range('2'); +$task->module->range('1{3}, 2, 3, 0{99}'); +$task->story->range('1, 1, 2, 3, 0{99}'); +$task->storyVersion->range('1'); +$task->name->range('1-100'); +$task->gen(12); + +$taskSpec = zenData('taskspec'); +$taskSpec->task->range('1-100'); +$taskSpec->version->range('0'); +$taskSpec->name->range('1-100'); +$taskSpec->gen(12); + +$tester = new treeTester(); +$tester->login(); + +r($tester->checkTreeData('firstLevel', '4')) && p('status,message') && e('SUCCESS,数据正确'); +r($tester->checkTreeData('secondLevelA', '7')) && p('status,message') && e('SUCCESS,数据正确'); +r($tester->checkTreeData('secondLevelB', '1')) && p('status,message') && e('SUCCESS,数据正确'); +r($tester->checkTreeData('thirdLevelB', '2')) && p('status,message') && e('SUCCESS,数据正确'); +r($tester->checkTreeData('fourthLevelB', '1')) && p('status,message') && e('SUCCESS,数据正确'); +r($tester->checkTreeData('firstLevel', '2', true)) && p('status,message') && e('SUCCESS,数据正确'); +r($tester->checkTreeData('secondLevelA', '1') ) && p('status,message') && e('SUCCESS,数据正确'); +r($tester->checkTreeData('thirdLevelB', '1')) && p('status,message') && e('SUCCESS,数据正确'); +r($tester->checkDetail()) && p('status,message') && e('SUCCESS,详情数据正确'); +$tester->closeBrowser(); diff --git a/module/file/model.php b/module/file/model.php index 108d01a361..6c37d6ca7c 100755 --- a/module/file/model.php +++ b/module/file/model.php @@ -463,7 +463,8 @@ class fileModel extends model */ public function setSavePath(): void { - $savePath = $this->app->getAppRoot() . "www/data/upload/{$this->app->company->id}/" . date('Ym/', $this->now); + $companyID = isset($this->app->company->id) ? $this->app->company->id : 1; + $savePath = $this->app->getAppRoot() . "www/data/upload/{$companyID}/" . date('Ym/', $this->now); if(!file_exists($savePath)) { mkdir($savePath, 0777, true); @@ -480,7 +481,8 @@ class fileModel extends model */ public function setWebPath(): void { - $this->webPath = $this->app->getWebRoot() . "data/upload/{$this->app->company->id}/"; + $companyID = isset($this->app->company->id) ? $this->app->company->id : 1; + $this->webPath = $this->app->getWebRoot() . "data/upload/{$companyID}/"; } /** diff --git a/module/group/packagemanager.php b/module/group/packagemanager.php index d056c51c0f..7d7a4863ef 100644 --- a/module/group/packagemanager.php +++ b/module/group/packagemanager.php @@ -3608,13 +3608,13 @@ $config->group->package->browseDoc->subset = 'doc'; $config->group->package->browseDoc->privs = array(); $config->group->package->browseDoc->privs['doc-index'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 0, 'depend' => array(), 'recommend' => array('doc-mySpace', 'doc-productSpace', 'doc-projectSpace', 'doc-teamSpace', 'doc-view')); $config->group->package->browseDoc->privs['doc-mySpace'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 1, 'depend' => array('doc-index'), 'recommend' => array('doc-index', 'doc-productSpace', 'doc-projectSpace', 'doc-teamSpace', 'doc-view')); -$config->group->package->browseDoc->privs['doc-quick'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 6, 'depend' => array(), 'recommend' => array('doc-index', 'doc-view')); -$config->group->package->browseDoc->privs['doc-view'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('doc-index'), 'recommend' => array('doc-diff', 'doc-displaySetting', 'doc-index', 'doc-myCollection', 'doc-myCreation', 'doc-myEdited', 'doc-mySpace', 'doc-myView', 'doc-productSpace', 'doc-projectSpace', 'doc-teamSpace')); +$config->group->package->browseDoc->privs['doc-quick'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 6, 'depend' => array(), 'recommend' => array('doc-index', 'doc-view')); +$config->group->package->browseDoc->privs['doc-view'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('doc-index'), 'recommend' => array('doc-displaySetting', 'doc-index', 'doc-myCollection', 'doc-myCreation', 'doc-myEdited', 'doc-mySpace', 'doc-myView', 'doc-productSpace', 'doc-projectSpace', 'doc-teamSpace')); $config->group->package->browseDoc->privs['doc-productSpace'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,or', 'order' => 2, 'depend' => array('doc-index'), 'recommend' => array('doc-index', 'doc-mySpace', 'doc-projectSpace', 'doc-teamSpace', 'doc-view')); $config->group->package->browseDoc->privs['doc-projectSpace'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 3, 'depend' => array('doc-index'), 'recommend' => array('doc-index', 'doc-mySpace', 'doc-productSpace', 'doc-teamSpace', 'doc-view')); $config->group->package->browseDoc->privs['doc-teamSpace'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 4, 'depend' => array('doc-index'), 'recommend' => array('doc-index', 'doc-mySpace', 'doc-productSpace', 'doc-projectSpace', 'doc-view')); $config->group->package->browseDoc->privs['doc-displaySetting'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 10, 'depend' => array('doc-view'), 'recommend' => array()); -$config->group->package->browseDoc->privs['doc-diff'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 11, 'depend' => array('doc-view'), 'recommend' => array()); +//$config->group->package->browseDoc->privs['doc-diff'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 11, 'depend' => array('doc-view'), 'recommend' => array()); $config->group->package->manageDoc = new stdclass(); $config->group->package->manageDoc->order = 15; diff --git a/module/holiday/model.php b/module/holiday/model.php index 07e91f4290..558951c4e8 100644 --- a/module/holiday/model.php +++ b/module/holiday/model.php @@ -199,7 +199,7 @@ class holidayModel extends model /* Get holidays, working days and weekend days .*/ $holidays = $this->getHolidays($begin, $end); $workingDays = $this->getWorkingDays($begin, $end); - $weekend = isset($this->config->project->weekend) ? $this->config->project->weekend : 2; + $weekend = isset($this->config->execution->weekend) ? $this->config->execution->weekend : 2; /* When the start date and end date are the same. */ $actualDays = array(); diff --git a/module/metric/calc/execution/scale/count_of_daily_finished_task_in_execution.php b/module/metric/calc/execution/scale/count_of_daily_finished_task_in_execution.php index aba0c73179..932d8e1847 100755 --- a/module/metric/calc/execution/scale/count_of_daily_finished_task_in_execution.php +++ b/module/metric/calc/execution/scale/count_of_daily_finished_task_in_execution.php @@ -32,12 +32,13 @@ class count_of_daily_finished_task_in_execution extends baseCalc $finishedDate = $row->finishedDate; $execution = $row->execution; - if($status != 'done' || empty($finishedDate)) return false; + if($status != 'done') return false; + + $year = $this->getYear($finishedDate); + if(!$year) return false; - if(empty($finishedDate)) return false; $date = substr($finishedDate, 0, 10); list($year, $month, $day) = explode('-', $date); - if($year == '0000') return false; if(!isset($this->result[$execution])) $this->result[$execution] = array(); if(!isset($this->result[$execution][$year])) $this->result[$execution][$year] = array(); diff --git a/module/metric/calc/product/scale/count_of_annual_closed_feedback_in_product.php b/module/metric/calc/product/scale/count_of_annual_closed_feedback_in_product.php index 089db52ad6..ce2fd10c11 100755 --- a/module/metric/calc/product/scale/count_of_annual_closed_feedback_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_closed_feedback_in_product.php @@ -31,11 +31,8 @@ class count_of_annual_closed_feedback_in_product extends baseCalc $product = $data->product; $closedDate = $data->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_closed_story_in_product.php b/module/metric/calc/product/scale/count_of_annual_closed_story_in_product.php index 1f9a27c97c..e990d008a0 100755 --- a/module/metric/calc/product/scale/count_of_annual_closed_story_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_closed_story_in_product.php @@ -34,9 +34,8 @@ class count_of_annual_closed_story_in_product extends baseCalc $product = $row->product; $closedDate = $row->closedDate; - if(empty($closedDate)) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_created_bug_in_product.php b/module/metric/calc/product/scale/count_of_annual_created_bug_in_product.php index f36d68b0bd..52de620958 100755 --- a/module/metric/calc/product/scale/count_of_annual_created_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_created_bug_in_product.php @@ -30,10 +30,9 @@ class count_of_annual_created_bug_in_product extends baseCalc { $product = $data->product; $openedDate = $data->openedDate; - if(empty($openedDate)) return false; - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_created_case_in_product.php b/module/metric/calc/product/scale/count_of_annual_created_case_in_product.php index 7b4e7b30a8..bcfde7d0b0 100755 --- a/module/metric/calc/product/scale/count_of_annual_created_case_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_created_case_in_product.php @@ -31,11 +31,8 @@ class count_of_annual_created_case_in_product extends baseCalc $product = $row->product; $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_created_effective_bug_in_product.php b/module/metric/calc/product/scale/count_of_annual_created_effective_bug_in_product.php index 1c4a08b0ef..b119c14a02 100755 --- a/module/metric/calc/product/scale/count_of_annual_created_effective_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_created_effective_bug_in_product.php @@ -30,9 +30,8 @@ class count_of_annual_created_effective_bug_in_product extends baseCalc { $openedDate = $data->openedDate; - if(empty($openedDate)) return false; - $year = substr($openedDate, 0 ,4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; $product = $data->product; if(!isset($this->result[$product])) $this->result[$product] = array(); diff --git a/module/metric/calc/product/scale/count_of_annual_created_feedback_in_product.php b/module/metric/calc/product/scale/count_of_annual_created_feedback_in_product.php index 205dc0de1d..5202b06e8c 100755 --- a/module/metric/calc/product/scale/count_of_annual_created_feedback_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_created_feedback_in_product.php @@ -31,11 +31,8 @@ class count_of_annual_created_feedback_in_product extends baseCalc $product = $data->product; $openedDate = $data->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_created_productplan_in_product.php b/module/metric/calc/product/scale/count_of_annual_created_productplan_in_product.php index 9e894cc5a5..e020a73c17 100755 --- a/module/metric/calc/product/scale/count_of_annual_created_productplan_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_created_productplan_in_product.php @@ -29,9 +29,9 @@ class count_of_annual_created_productplan_in_product extends baseCalc public function calculate($row) { if(empty(trim($row->createdDate))) return false; - $row->year = substr($row->createdDate, 0, 4); - if(empty(trim($row->year))) return false; - if($row->year == '0000') return false; + + $row->year = $this->getYear($row->createdDate); + if(!$row->year) return false; if(!isset($this->result[$row->year])) $this->result[$row->year] = array(); if(!isset($this->result[$row->year][$row->product])) $this->result[$row->year][$row->product] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_created_release_in_product.php b/module/metric/calc/product/scale/count_of_annual_created_release_in_product.php index c47668c170..5d7a6fa474 100755 --- a/module/metric/calc/product/scale/count_of_annual_created_release_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_created_release_in_product.php @@ -28,10 +28,8 @@ class count_of_annual_created_release_in_product extends baseCalc public function calculate($row) { - if(empty($row->date)) return null; - - $year = substr($row->date, 0, 4); - if($year == '0000') return null; + $year = $this->getYear($row->date); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$row->product])) $this->result[$year][$row->product] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_created_requirement_in_product.php b/module/metric/calc/product/scale/count_of_annual_created_requirement_in_product.php index 73776f5898..7988dfbf76 100755 --- a/module/metric/calc/product/scale/count_of_annual_created_requirement_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_created_requirement_in_product.php @@ -32,10 +32,10 @@ class count_of_annual_created_requirement_in_product extends baseCalc $type = $data->type; $openedDate = $data->openedDate; - if(empty($openedDate) || $type != 'requirement') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + if($type != 'requirement') return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_created_story_in_product.php b/module/metric/calc/product/scale/count_of_annual_created_story_in_product.php index bf8b8009ac..00f88e5f30 100755 --- a/module/metric/calc/product/scale/count_of_annual_created_story_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_created_story_in_product.php @@ -31,11 +31,8 @@ class count_of_annual_created_story_in_product extends baseCalc $product = $row->product; $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - - if(empty($year) || $year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_delivered_story_in_product.php b/module/metric/calc/product/scale/count_of_annual_delivered_story_in_product.php index 715660893b..3e0265abb7 100755 --- a/module/metric/calc/product/scale/count_of_annual_delivered_story_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_delivered_story_in_product.php @@ -37,16 +37,14 @@ class count_of_annual_delivered_story_in_product extends baseCalc $year = null; if($stage == 'released') { - if(empty($releasedDate)) return false; - $year = substr($releasedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($releasedDate); + if(!$year) return false; } if($closedReason == 'done') { - if(empty($closedDate)) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; } if(empty($year)) return false; diff --git a/module/metric/calc/product/scale/count_of_annual_finished_story_in_product.php b/module/metric/calc/product/scale/count_of_annual_finished_story_in_product.php index 04a8881cae..601203da39 100755 --- a/module/metric/calc/product/scale/count_of_annual_finished_story_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_finished_story_in_product.php @@ -33,11 +33,8 @@ class count_of_annual_finished_story_in_product extends baseCalc $product = $row->product; $closedDate = $row->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - - if(empty($year) || $year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_annual_fixed_bug_in_product.php b/module/metric/calc/product/scale/count_of_annual_fixed_bug_in_product.php index 67e03fabe9..d839b06531 100755 --- a/module/metric/calc/product/scale/count_of_annual_fixed_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_annual_fixed_bug_in_product.php @@ -32,10 +32,10 @@ class count_of_annual_fixed_bug_in_product extends baseCalc $resolution = $data->resolution; $closedDate = $data->closedDate; - if(empty($closedDate)) return false; + $year = $this->getYear($closedDate); + if(!$year) return false; - $year = substr($closedDate, 0, 4); - if($resolution != 'fixed' || $year == '0000') return false; + if($resolution != 'fixed') return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/count_of_daily_closed_bug_in_product.php b/module/metric/calc/product/scale/count_of_daily_closed_bug_in_product.php index ea367a4e6d..f0be003fa9 100755 --- a/module/metric/calc/product/scale/count_of_daily_closed_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_daily_closed_bug_in_product.php @@ -28,7 +28,10 @@ class count_of_daily_closed_bug_in_product extends baseCalc public function calculate($row) { - if($row->status != 'closed' || empty($row->closedDate)) return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; + + if($row->status != 'closed') return false; $date = substr($row->closedDate, 0, 10); list($year, $month, $day) = explode('-', $date); diff --git a/module/metric/calc/product/scale/count_of_daily_created_bug_in_product.php b/module/metric/calc/product/scale/count_of_daily_created_bug_in_product.php index c9c693045d..89e5dc78e0 100755 --- a/module/metric/calc/product/scale/count_of_daily_created_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_daily_created_bug_in_product.php @@ -28,11 +28,11 @@ class count_of_daily_created_bug_in_product extends baseCalc public function calculate($row) { - if(empty($row->openedDate)) return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; $date = substr($row->openedDate, 0, 10); list($year, $month, $day) = explode('-', $date); - if($year == '0000') return false; if(!isset($this->result[$row->product])) $this->result[$row->product] = array(); if(!isset($this->result[$row->product][$year])) $this->result[$row->product][$year] = array(); diff --git a/module/metric/calc/product/scale/count_of_daily_resolved_bug_in_product.php b/module/metric/calc/product/scale/count_of_daily_resolved_bug_in_product.php index 9acb9e62ac..41387624c4 100755 --- a/module/metric/calc/product/scale/count_of_daily_resolved_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_daily_resolved_bug_in_product.php @@ -28,11 +28,13 @@ class count_of_daily_resolved_bug_in_product extends baseCalc public function calculate($row) { - if($row->status == 'active' || empty($row->resolvedDate)) return false; + $year = $this->getYear($row->resolvedDate); + if(!$year) return false; + + if($row->status == 'active') return false; $date = substr($row->resolvedDate, 0, 10); list($year, $month, $day) = explode('-', $date); - if($year == '0000') return false; if(!isset($this->result[$row->product])) $this->result[$row->product] = array(); if(!isset($this->result[$row->product][$year])) $this->result[$row->product][$year] = array(); diff --git a/module/metric/calc/product/scale/count_of_monthly_closed_bug_in_product.php b/module/metric/calc/product/scale/count_of_monthly_closed_bug_in_product.php index 14b2031469..dbf09a0487 100755 --- a/module/metric/calc/product/scale/count_of_monthly_closed_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_monthly_closed_bug_in_product.php @@ -31,10 +31,8 @@ class count_of_monthly_closed_bug_in_product extends baseCalc $product = $row->product; $closedDate = $row->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0 ,4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; $month = substr($closedDate, 5, 2); diff --git a/module/metric/calc/product/scale/count_of_monthly_closed_story_in_product.php b/module/metric/calc/product/scale/count_of_monthly_closed_story_in_product.php index b07071e10c..57ae78c17c 100755 --- a/module/metric/calc/product/scale/count_of_monthly_closed_story_in_product.php +++ b/module/metric/calc/product/scale/count_of_monthly_closed_story_in_product.php @@ -31,13 +31,10 @@ class count_of_monthly_closed_story_in_product extends baseCalc $product = $data->product; $closedDate = $data->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); + $year = $this->getYear($closedDate); + if(!$year) return false; $month = substr($closedDate, 5, 2); - if($year == '0000') return false; - if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = array(); if(!isset($this->result[$product][$year][$month])) $this->result[$product][$year][$month] = 0; diff --git a/module/metric/calc/product/scale/count_of_monthly_created_bug_in_product.php b/module/metric/calc/product/scale/count_of_monthly_created_bug_in_product.php index 4bd16afaa2..d4ad3e4354 100755 --- a/module/metric/calc/product/scale/count_of_monthly_created_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_monthly_created_bug_in_product.php @@ -31,10 +31,8 @@ class count_of_monthly_created_bug_in_product extends baseCalc $product = $row->product; $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0 ,4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; $month = substr($openedDate, 5, 2); diff --git a/module/metric/calc/product/scale/count_of_monthly_created_release_in_product.php b/module/metric/calc/product/scale/count_of_monthly_created_release_in_product.php index fa6845c104..7e83435802 100755 --- a/module/metric/calc/product/scale/count_of_monthly_created_release_in_product.php +++ b/module/metric/calc/product/scale/count_of_monthly_created_release_in_product.php @@ -31,10 +31,8 @@ class count_of_monthly_created_release_in_product extends baseCalc $product = $row->product; $createdDate = $row->date; - if(empty($createdDate)) return false; - - $year = substr($createdDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($createdDate); + if(!$year) return false; $month = substr($createdDate, 5, 2); diff --git a/module/metric/calc/product/scale/count_of_monthly_created_story_in_product.php b/module/metric/calc/product/scale/count_of_monthly_created_story_in_product.php index 52bdd7bd21..77db93c090 100755 --- a/module/metric/calc/product/scale/count_of_monthly_created_story_in_product.php +++ b/module/metric/calc/product/scale/count_of_monthly_created_story_in_product.php @@ -31,13 +31,12 @@ class count_of_monthly_created_story_in_product extends baseCalc $product = $data->product; $openedDate = $data->openedDate; - if(empty($openedDate)) return false; + $year = $this->getYear($openedDate); + if(!$year) return false; $year = substr($openedDate, 0, 4); $month = substr($openedDate, 5, 2); - if($year == '0000') return false; - if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = array(); if(!isset($this->result[$product][$year][$month])) $this->result[$product][$year][$month] = 0; diff --git a/module/metric/calc/product/scale/count_of_monthly_delivered_story_in_product.php b/module/metric/calc/product/scale/count_of_monthly_delivered_story_in_product.php index a3310ec537..0b08061bef 100755 --- a/module/metric/calc/product/scale/count_of_monthly_delivered_story_in_product.php +++ b/module/metric/calc/product/scale/count_of_monthly_delivered_story_in_product.php @@ -38,22 +38,20 @@ class count_of_monthly_delivered_story_in_product extends baseCalc $month = null; if($stage == 'released') { - if(empty($releasedDate)) return false; - $year = substr($releasedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($releasedDate); + if(!$year) return false; + $month = substr($releasedDate, 5, 2); } if($closedReason == 'done') { - if(empty($closedDate)) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; + $month = substr($closedDate, 5, 2); } - if(empty($year)) return false; - if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = array(); if(!isset($this->result[$product][$year][$month])) $this->result[$product][$year][$month] = 0; diff --git a/module/metric/calc/product/scale/count_of_monthly_finished_story_in_product.php b/module/metric/calc/product/scale/count_of_monthly_finished_story_in_product.php index 3fe62cfa7b..949dc02a00 100755 --- a/module/metric/calc/product/scale/count_of_monthly_finished_story_in_product.php +++ b/module/metric/calc/product/scale/count_of_monthly_finished_story_in_product.php @@ -32,14 +32,10 @@ class count_of_monthly_finished_story_in_product extends baseCalc $closedDate = $row->closedDate; $closedReason = $row->closedReason; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); + $year = $this->getYear($closedDate); + if(!$year) return false; $month = substr($closedDate, 5, 2); - if(empty($year) || empty($month)) return false; - if($year == '0000') return false; - if($closedReason != 'done') return false; if(!isset($this->result[$product])) $this->result[$product] = array(); diff --git a/module/metric/calc/product/scale/count_of_monthly_fixed_bug_in_product.php b/module/metric/calc/product/scale/count_of_monthly_fixed_bug_in_product.php index f940357640..8612fc8b6f 100755 --- a/module/metric/calc/product/scale/count_of_monthly_fixed_bug_in_product.php +++ b/module/metric/calc/product/scale/count_of_monthly_fixed_bug_in_product.php @@ -32,11 +32,10 @@ class count_of_monthly_fixed_bug_in_product extends baseCalc $resolution = $data->resolution; $closedDate = $data->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - if($resolution != 'fixed' || $year == '0000') return false; + if($resolution != 'fixed') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; $month = substr($closedDate, 5, 2); if(!isset($this->result[$product])) $this->result[$product] = array(); diff --git a/module/metric/calc/product/scale/scale_of_annual_closed_story_in_product.php b/module/metric/calc/product/scale/scale_of_annual_closed_story_in_product.php index c092faa9a4..3e2f6942df 100755 --- a/module/metric/calc/product/scale/scale_of_annual_closed_story_in_product.php +++ b/module/metric/calc/product/scale/scale_of_annual_closed_story_in_product.php @@ -35,11 +35,8 @@ class scale_of_annual_closed_story_in_product extends baseCalc if($parent == '-1') return false; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/scale_of_annual_delivered_story_in_product.php b/module/metric/calc/product/scale/scale_of_annual_delivered_story_in_product.php index b7c875c2e9..5a810b7fce 100755 --- a/module/metric/calc/product/scale/scale_of_annual_delivered_story_in_product.php +++ b/module/metric/calc/product/scale/scale_of_annual_delivered_story_in_product.php @@ -41,16 +41,14 @@ class scale_of_annual_delivered_story_in_product extends baseCalc $year = null; if($stage == 'released') { - if(empty($releasedDate)) return false; - $year = substr($releasedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($releasedDate); + if(!$year) return false; } if($closedReason == 'done') { - if(empty($closedDate)) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; } if(empty($year)) return false; diff --git a/module/metric/calc/product/scale/scale_of_annual_finished_story_in_product.php b/module/metric/calc/product/scale/scale_of_annual_finished_story_in_product.php index f4115ebe7d..43c054ad7e 100755 --- a/module/metric/calc/product/scale/scale_of_annual_finished_story_in_product.php +++ b/module/metric/calc/product/scale/scale_of_annual_finished_story_in_product.php @@ -36,11 +36,8 @@ class scale_of_annual_finished_story_in_product extends baseCalc if($parent == '-1') return false; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$product])) $this->result[$product] = array(); if(!isset($this->result[$product][$year])) $this->result[$product][$year] = 0; diff --git a/module/metric/calc/product/scale/scale_of_monthly_finished_story_in_product.php b/module/metric/calc/product/scale/scale_of_monthly_finished_story_in_product.php index bcadbae6f8..bc44416ea8 100755 --- a/module/metric/calc/product/scale/scale_of_monthly_finished_story_in_product.php +++ b/module/metric/calc/product/scale/scale_of_monthly_finished_story_in_product.php @@ -38,11 +38,8 @@ class scale_of_monthly_finished_story_in_product extends baseCalc if($closedReason != 'done') return false; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; $month = substr($closedDate, 5, 2); diff --git a/module/metric/calc/project/scale/count_annual_closed_execution_in_project.php b/module/metric/calc/project/scale/count_annual_closed_execution_in_project.php index 73b7730ff2..101d87128f 100755 --- a/module/metric/calc/project/scale/count_annual_closed_execution_in_project.php +++ b/module/metric/calc/project/scale/count_annual_closed_execution_in_project.php @@ -31,9 +31,8 @@ class count_annual_closed_execution_in_project extends baseCalc $project = $row->project; $closedDate = $row->closedDate; - if(empty($closedDate)) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$project])) $this->result[$project] = array(); if(!isset($this->result[$project][$year])) $this->result[$project][$year] = 0; diff --git a/module/metric/calc/project/scale/count_of_annual_created_bug_in_project.php b/module/metric/calc/project/scale/count_of_annual_created_bug_in_project.php index 74fc8086ef..c7868a4fb5 100644 --- a/module/metric/calc/project/scale/count_of_annual_created_bug_in_project.php +++ b/module/metric/calc/project/scale/count_of_annual_created_bug_in_project.php @@ -30,10 +30,9 @@ class count_of_annual_created_bug_in_project extends baseCalc { $project = $data->project; $openedDate = $data->openedDate; - if(empty($openedDate)) return false; - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; if(!isset($this->result[$project])) $this->result[$project] = array(); if(!isset($this->result[$project][$year])) $this->result[$project][$year] = 0; diff --git a/module/metric/calc/project/scale/count_of_annual_created_effective_bug_in_project.php b/module/metric/calc/project/scale/count_of_annual_created_effective_bug_in_project.php new file mode 100644 index 0000000000..448ee4a6f2 --- /dev/null +++ b/module/metric/calc/project/scale/count_of_annual_created_effective_bug_in_project.php @@ -0,0 +1,59 @@ + + * @package + * @uses func + * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html) + * @Link https://www.zentao.net + */ +class count_of_annual_created_effective_bug_in_project extends baseCalc +{ + public $dataset = 'getProjectBugs'; + + public $fieldList = array('t1.project', 't1.status', 't1.resolution', 't1.openedDate'); + + public $result = array(); + + public function calculate($data) + { + $openedDate = $data->openedDate; + + $year = $this->getYear($openedDate); + if(!$year) return false; + + $project = $data->project; + if(!isset($this->result[$project])) $this->result[$project] = array(); + if(!isset($this->result[$project][$year])) $this->result[$project][$year] = 0; + + $resolution = $data->resolution; + $status = $data->status; + + if($status == 'active' or $resolution == 'fixed' or $resolution == 'postponed') $this->result[$project][$year] += 1; + } + + public function getResult($options = array()) + { + $records = array(); + foreach($this->result as $project => $years) + { + foreach($years as $year => $value) + { + $records[] = array('project' => $project, 'year' => $year, 'value' => $value); + } + } + + return $this->filterByOptions($records, $options); + } +} diff --git a/module/metric/calc/project/scale/count_of_annual_finished_execution_in_project.php b/module/metric/calc/project/scale/count_of_annual_finished_execution_in_project.php index 7fc3bcbaf8..648af6b02c 100644 --- a/module/metric/calc/project/scale/count_of_annual_finished_execution_in_project.php +++ b/module/metric/calc/project/scale/count_of_annual_finished_execution_in_project.php @@ -31,9 +31,8 @@ class count_of_annual_finished_execution_in_project extends baseCalc $project = $row->project; $realEnd = $row->realEnd; - if(empty($realEnd)) return false; - $year = substr($realEnd, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($realEnd); + if(!$year) return false; if(!isset($this->result[$project])) $this->result[$project] = array(); if(!isset($this->result[$project][$year])) $this->result[$project][$year] = 0; diff --git a/module/metric/calc/project/scale/count_of_annual_finished_story_in_project.php b/module/metric/calc/project/scale/count_of_annual_finished_story_in_project.php index 1f286f27db..f9400ffa6e 100755 --- a/module/metric/calc/project/scale/count_of_annual_finished_story_in_project.php +++ b/module/metric/calc/project/scale/count_of_annual_finished_story_in_project.php @@ -31,9 +31,8 @@ class count_of_annual_finished_story_in_project extends baseCalc $project = $row->project; $closedDate = $row->closedDate; - if(empty($closedDate)) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$project])) $this->result[$project] = array(); if(!isset($this->result[$project][$year])) $this->result[$project][$year] = 0; diff --git a/module/metric/calc/project/scale/count_of_annual_fixed_bug_in_project.php b/module/metric/calc/project/scale/count_of_annual_fixed_bug_in_project.php new file mode 100644 index 0000000000..78d06cac12 --- /dev/null +++ b/module/metric/calc/project/scale/count_of_annual_fixed_bug_in_project.php @@ -0,0 +1,51 @@ + + * @package + * @uses func + * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html) + * @Link https://www.zentao.net + */ +class count_of_annual_fixed_bug_in_project extends baseCalc +{ + public $dataset = 'getProjectBugs'; + + public $fieldList = array('t1.project', 't1.resolution', 't1.closedDate'); + + public $result = array(); + + public function calculate($data) + { + $project = $data->project; + $resolution = $data->resolution; + $closedDate = $data->closedDate; + + $year = $this->getYear($closedDate); + if(!$year) return false; + + if($resolution != 'fixed') return false; + + if(!isset($this->result[$project])) $this->result[$project] = array(); + if(!isset($this->result[$project][$year])) $this->result[$project][$year] = 0; + + $this->result[$project][$year] += 1; + } + + public function getResult($options = array()) + { + $records = $this->getRecords(array('project', 'year', 'value')); + return $this->filterByOptions($records, $options); + } +} diff --git a/module/metric/calc/project/scale/count_of_daily_closed_bug_in_project.php b/module/metric/calc/project/scale/count_of_daily_closed_bug_in_project.php index 9ceea8235c..bee0f9d93c 100644 --- a/module/metric/calc/project/scale/count_of_daily_closed_bug_in_project.php +++ b/module/metric/calc/project/scale/count_of_daily_closed_bug_in_project.php @@ -28,11 +28,13 @@ class count_of_daily_closed_bug_in_project extends baseCalc public function calculate($row) { - if($row->status != 'closed' || empty($row->closedDate)) return false; + if($row->status != 'closed') return false; + + $year = $this->getYear($row->closedDate); + if(!$year) return false; $date = substr($row->closedDate, 0, 10); list($year, $month, $day) = explode('-', $date); - if($year == '0000') return false; if(!isset($this->result[$row->project])) $this->result[$row->project] = array(); if(!isset($this->result[$row->project][$year])) $this->result[$row->project][$year] = array(); diff --git a/module/metric/calc/project/scale/count_of_daily_created_bug_in_project.php b/module/metric/calc/project/scale/count_of_daily_created_bug_in_project.php index ff8fdaed00..311edb715b 100644 --- a/module/metric/calc/project/scale/count_of_daily_created_bug_in_project.php +++ b/module/metric/calc/project/scale/count_of_daily_created_bug_in_project.php @@ -28,11 +28,11 @@ class count_of_daily_created_bug_in_project extends baseCalc public function calculate($row) { - if(empty($row->openedDate)) return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; $date = substr($row->openedDate, 0, 10); list($year, $month, $day) = explode('-', $date); - if($year == '0000') return false; if(!isset($this->result[$row->project])) $this->result[$row->project] = array(); if(!isset($this->result[$row->project][$year])) $this->result[$row->project][$year] = array(); diff --git a/module/metric/calc/project/scale/count_of_daily_resolved_bug_in_project.php b/module/metric/calc/project/scale/count_of_daily_resolved_bug_in_project.php index cb5fe403cc..7999f28129 100644 --- a/module/metric/calc/project/scale/count_of_daily_resolved_bug_in_project.php +++ b/module/metric/calc/project/scale/count_of_daily_resolved_bug_in_project.php @@ -28,11 +28,13 @@ class count_of_daily_resolved_bug_in_project extends baseCalc public function calculate($row) { - if($row->status == 'active' || empty($row->resolvedDate)) return false; + if($row->status == 'active') return false; + + $year = $this->getYear($row->resolvedDate); + if(!$year) return false; $date = substr($row->resolvedDate, 0, 10); list($year, $month, $day) = explode('-', $date); - if($year == '0000') return false; if(!isset($this->result[$row->project])) $this->result[$row->project] = array(); if(!isset($this->result[$row->project][$year])) $this->result[$row->project][$year] = array(); diff --git a/module/metric/calc/project/scale/count_of_monthly_closed_bug_in_project.php b/module/metric/calc/project/scale/count_of_monthly_closed_bug_in_project.php new file mode 100644 index 0000000000..61edcf6577 --- /dev/null +++ b/module/metric/calc/project/scale/count_of_monthly_closed_bug_in_project.php @@ -0,0 +1,51 @@ + + * @package + * @uses func + * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html) + * @Link https://www.zentao.net + */ +class count_of_monthly_closed_bug_in_project extends baseCalc +{ + public $dataset = 'getBugs'; + + public $fieldList = array('t1.project', 't1.closedDate'); + + public $result = array(); + + public function calculate($row) + { + $project = $row->project; + $closedDate = $row->closedDate; + + $year = $this->getYear($closedDate); + if(!$year) return false; + + $month = substr($closedDate, 5, 2); + + if(!isset($this->result[$project])) $this->result[$project] = array(); + if(!isset($this->result[$project][$year])) $this->result[$project][$year] = array(); + if(!isset($this->result[$project][$year][$month])) $this->result[$project][$year][$month] = 0; + + $this->result[$project][$year][$month] += 1; + } + + public function getResult($options = array()) + { + $records = $this->getRecords(array('project', 'year', 'month', 'value')); + return $this->filterByOptions($records, $options); + } +} diff --git a/module/metric/calc/project/scale/count_of_monthly_created_bug_in_project.php b/module/metric/calc/project/scale/count_of_monthly_created_bug_in_project.php index 1be09c18b9..f8add1cc1c 100644 --- a/module/metric/calc/project/scale/count_of_monthly_created_bug_in_project.php +++ b/module/metric/calc/project/scale/count_of_monthly_created_bug_in_project.php @@ -31,10 +31,8 @@ class count_of_monthly_created_bug_in_project extends baseCalc $project = $row->project; $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0 ,4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; $month = substr($openedDate, 5, 2); diff --git a/module/metric/calc/project/scale/count_of_monthly_fixed_bug_in_project.php b/module/metric/calc/project/scale/count_of_monthly_fixed_bug_in_project.php new file mode 100644 index 0000000000..c7df829075 --- /dev/null +++ b/module/metric/calc/project/scale/count_of_monthly_fixed_bug_in_project.php @@ -0,0 +1,54 @@ + + * @package + * @uses func + * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html) + * @Link https://www.zentao.net + */ +class count_of_monthly_fixed_bug_in_project extends baseCalc +{ + public $dataset = 'getProjectBugs'; + + public $fieldList = array('t1.id', 't1.project', 't1.resolution', 't1.closedDate'); + + public $result = array(); + + public function calculate($data) + { + $project = $data->project; + $resolution = $data->resolution; + $closedDate = $data->closedDate; + + $year = $this->getYear($closedDate); + if(!$year) return false; + + if($resolution != 'fixed') return false; + + $month = substr($closedDate, 5, 2); + + if(!isset($this->result[$project])) $this->result[$project] = array(); + if(!isset($this->result[$project][$year])) $this->result[$project][$year] = array(); + if(!isset($this->result[$project][$year][$month])) $this->result[$project][$year][$month] = 0; + + $this->result[$project][$year][$month] += 1; + } + + public function getResult($options = array()) + { + $records = $this->getRecords(array('project', 'year', 'month', 'value')); + return $this->filterByOptions($records, $options); + } +} diff --git a/module/metric/calc/project/scale/scale_of_annual_finished_story_in_project.php b/module/metric/calc/project/scale/scale_of_annual_finished_story_in_project.php index 87423a6b10..db3cc0ac5b 100755 --- a/module/metric/calc/project/scale/scale_of_annual_finished_story_in_project.php +++ b/module/metric/calc/project/scale/scale_of_annual_finished_story_in_project.php @@ -34,9 +34,8 @@ class scale_of_annual_finished_story_in_project extends baseCalc if($parent == '-1') return false; - if(empty($closedDate)) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$project])) $this->result[$project] = array(); if(!isset($this->result[$project][$year])) $this->result[$project][$year] = 0; diff --git a/module/metric/calc/system/hour/consume_of_annual_closed_project.php b/module/metric/calc/system/hour/consume_of_annual_closed_project.php index ab2b641466..90c35b2517 100755 --- a/module/metric/calc/system/hour/consume_of_annual_closed_project.php +++ b/module/metric/calc/system/hour/consume_of_annual_closed_project.php @@ -29,12 +29,12 @@ class consume_of_annual_closed_project extends baseCalc public function calculate($data) { $project = $data->project; - $year = substr($data->closedDate, 0, 4); $consumed = $data->consumed; $status = $data->status; if($status != 'closed') return false; - if(empty($year) || $year == '0000') return false; + $year = $this->getYear($data->closedDate); + if(!$year) return false; if(!is_numeric($consumed) || empty($consumed)) return false; diff --git a/module/metric/calc/system/hour/consume_of_monthly_closed_project.php b/module/metric/calc/system/hour/consume_of_monthly_closed_project.php index e716d9a172..cb7399d214 100755 --- a/module/metric/calc/system/hour/consume_of_monthly_closed_project.php +++ b/module/metric/calc/system/hour/consume_of_monthly_closed_project.php @@ -29,12 +29,12 @@ class consume_of_monthly_closed_project extends baseCalc public function calculate($data) { $project = $data->project; - $year = substr($data->closedDate, 0, 4); $consumed = $data->consumed; $status = $data->status; if($status != 'closed') return false; - if(empty($year) || $year == '0000') return false; + $year = $this->getYear($data->closedDate); + if(!$year) return false; $month = substr($data->closedDate, 5, 2); diff --git a/module/metric/calc/system/hour/day_of_annual_closed_project.php b/module/metric/calc/system/hour/day_of_annual_closed_project.php index c5e87a5443..ac51495ecd 100755 --- a/module/metric/calc/system/hour/day_of_annual_closed_project.php +++ b/module/metric/calc/system/hour/day_of_annual_closed_project.php @@ -29,13 +29,13 @@ class day_of_annual_closed_project extends baseCalc public function calculate($data) { $project = $data->project; - $year = substr($data->closedDate, 0, 4); $consumed = (float)$data->consumed; $status = $data->status; $defaultHours = (float)$data->defaultHours; if($status != 'closed') return false; - if(empty($year) || $year == '0000') return false; + $year = $this->getYear($data->closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$project])) $this->result[$year][$project] = 0; diff --git a/module/metric/calc/system/hour/day_of_annual_effort.php b/module/metric/calc/system/hour/day_of_annual_effort.php index 9ecd5e6947..c5cfcb6094 100755 --- a/module/metric/calc/system/hour/day_of_annual_effort.php +++ b/module/metric/calc/system/hour/day_of_annual_effort.php @@ -28,10 +28,8 @@ class day_of_annual_effort extends baseCalc public function calculate($row) { - if(empty($row->date)) return false; - - $year = substr($row->date, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->date); + if(!$year) return false; $consumed = $row->consumed; $defaultHours = $row->defaultHours; diff --git a/module/metric/calc/system/hour/day_of_daily_effort.php b/module/metric/calc/system/hour/day_of_daily_effort.php index 56387fde9d..f60c98c001 100755 --- a/module/metric/calc/system/hour/day_of_daily_effort.php +++ b/module/metric/calc/system/hour/day_of_daily_effort.php @@ -29,10 +29,8 @@ class day_of_daily_effort extends baseCalc public function calculate($row) { $date = $row->date; - if(empty($date)) return false; - - $year = substr($date, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($date); + if(!$year) return false; $month = substr($date, 5, 2); $day = substr($date, 8, 2); diff --git a/module/metric/calc/system/hour/hour_of_annual_effort.php b/module/metric/calc/system/hour/hour_of_annual_effort.php index 123c8e56ba..0b692b18b2 100755 --- a/module/metric/calc/system/hour/hour_of_annual_effort.php +++ b/module/metric/calc/system/hour/hour_of_annual_effort.php @@ -28,10 +28,8 @@ class hour_of_annual_effort extends baseCalc public function calculate($row) { - if(empty($row->date)) return false; - - $year = substr($row->date, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->date); + if(!$year) return false; $consumed = $row->consumed; diff --git a/module/metric/calc/system/hour/hour_of_daily_effort.php b/module/metric/calc/system/hour/hour_of_daily_effort.php index 1701b220fd..0f75bb4bf2 100755 --- a/module/metric/calc/system/hour/hour_of_daily_effort.php +++ b/module/metric/calc/system/hour/hour_of_daily_effort.php @@ -29,12 +29,8 @@ class hour_of_daily_effort extends baseCalc public function calculate($row) { $date = $row->date; - - if(empty($date)) return false; - - $year = substr($date, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($date); + if(!$year) return false; $month = substr($date, 5, 2); $day = substr($date, 8, 2); diff --git a/module/metric/calc/system/rate/rate_of_annual_delivered_story.php b/module/metric/calc/system/rate/rate_of_annual_delivered_story.php index dd863d3146..0dd1919704 100755 --- a/module/metric/calc/system/rate/rate_of_annual_delivered_story.php +++ b/module/metric/calc/system/rate/rate_of_annual_delivered_story.php @@ -38,9 +38,8 @@ class rate_of_annual_delivered_story extends baseCalc if($stage == 'released' && !empty($closedDate)) $date = $releasedDate; if(empty($date)) return false; - - $year = substr($date, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($date); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = array('finished' => 0, 'valid' => 0); if($stage == 'released' || $closedReason == 'done') $this->result[$year]['finished'] += 1; diff --git a/module/metric/calc/system/rate/rate_of_annual_finished_story.php b/module/metric/calc/system/rate/rate_of_annual_finished_story.php index 7f0c2098cd..c8e85f7151 100755 --- a/module/metric/calc/system/rate/rate_of_annual_finished_story.php +++ b/module/metric/calc/system/rate/rate_of_annual_finished_story.php @@ -31,11 +31,8 @@ class rate_of_annual_finished_story extends baseCalc $closedDate = $data->closedDate; $closedReason = $data->closedReason; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = array(); diff --git a/module/metric/calc/system/rate/rate_of_delayed_closed_execution_which_annual_finished.php b/module/metric/calc/system/rate/rate_of_delayed_closed_execution_which_annual_finished.php index e03f06843a..1fae015b16 100755 --- a/module/metric/calc/system/rate/rate_of_delayed_closed_execution_which_annual_finished.php +++ b/module/metric/calc/system/rate/rate_of_delayed_closed_execution_which_annual_finished.php @@ -28,10 +28,8 @@ class rate_of_delayed_closed_execution_which_annual_finished extends baseCalc public function calculate($row) { - if(empty($row->closedDate)) return false; - - $year = substr($row->closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = array('closed' => 0, 'delayed' => 0); if($row->status == 'closed') $this->result[$year]['closed'] ++; diff --git a/module/metric/calc/system/rate/rate_of_delayed_finished_project_which_annual_finished.php b/module/metric/calc/system/rate/rate_of_delayed_finished_project_which_annual_finished.php index 13a390db03..615cd4fe7f 100755 --- a/module/metric/calc/system/rate/rate_of_delayed_finished_project_which_annual_finished.php +++ b/module/metric/calc/system/rate/rate_of_delayed_finished_project_which_annual_finished.php @@ -28,9 +28,8 @@ class rate_of_delayed_finished_project_which_annual_finished extends baseCalc public function calculate($row) { - if(empty($row->closedDate)) return false; - - $closedYear = substr($row->closedDate, 0, 4); + $closedYear = $this->getYear($row->closedDate); + if(!$closedYear) return false; if(!isset($this->result[$closedYear])) $this->result[$closedYear] = array('closed' => 0, 'delayed' => 0); if($row->status == 'closed') $this->result[$closedYear]['closed'] ++; diff --git a/module/metric/calc/system/rate/rate_of_undelayed_closed_execution_which_annual_finished.php b/module/metric/calc/system/rate/rate_of_undelayed_closed_execution_which_annual_finished.php index 23e970239b..e52f57b99b 100755 --- a/module/metric/calc/system/rate/rate_of_undelayed_closed_execution_which_annual_finished.php +++ b/module/metric/calc/system/rate/rate_of_undelayed_closed_execution_which_annual_finished.php @@ -28,10 +28,8 @@ class rate_of_undelayed_closed_execution_which_annual_finished extends baseCalc public function calculate($row) { - if(empty($row->closedDate)) return false; - - $year = substr($row->closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = array('closed' => 0, 'undelayed' => 0); if($row->status == 'closed') $this->result[$year]['closed'] ++; diff --git a/module/metric/calc/system/rate/rate_of_undelayed_finished_project_which_annual_finished.php b/module/metric/calc/system/rate/rate_of_undelayed_finished_project_which_annual_finished.php index 609ec092df..2752fa61a2 100755 --- a/module/metric/calc/system/rate/rate_of_undelayed_finished_project_which_annual_finished.php +++ b/module/metric/calc/system/rate/rate_of_undelayed_finished_project_which_annual_finished.php @@ -28,9 +28,8 @@ class rate_of_undelayed_finished_project_which_annual_finished extends baseCalc public function calculate($row) { - if(empty($row->closedDate)) return false; - - $closedYear = substr($row->closedDate, 0, 4); + $closedYear = $this->getYear($row->closedDate); + if(!$closedYear) return false; if(!isset($this->result[$closedYear])) $this->result[$closedYear] = array('closed' => 0, 'delayed' => 0); if($row->status == 'closed') $this->result[$closedYear]['closed'] ++; diff --git a/module/metric/calc/system/scale/count_of_annual_closed_execution.php b/module/metric/calc/system/scale/count_of_annual_closed_execution.php index d4edc9af64..868923d0aa 100755 --- a/module/metric/calc/system/scale/count_of_annual_closed_execution.php +++ b/module/metric/calc/system/scale/count_of_annual_closed_execution.php @@ -29,10 +29,8 @@ class count_of_annual_closed_execution extends baseCalc public function calculate($row) { $closedDate = $row->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_closed_feedback.php b/module/metric/calc/system/scale/count_of_annual_closed_feedback.php index b0a2a4006a..f338a82e71 100755 --- a/module/metric/calc/system/scale/count_of_annual_closed_feedback.php +++ b/module/metric/calc/system/scale/count_of_annual_closed_feedback.php @@ -28,10 +28,10 @@ class count_of_annual_closed_feedback extends baseCalc public function calculate($row) { - if(empty($row->closedDate) || $row->status != 'closed') return false; + if($row->status != 'closed') return false; - $year = substr($row->closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_closed_product.php b/module/metric/calc/system/scale/count_of_annual_closed_product.php index b635458d4d..0a50961ea8 100755 --- a/module/metric/calc/system/scale/count_of_annual_closed_product.php +++ b/module/metric/calc/system/scale/count_of_annual_closed_product.php @@ -29,10 +29,8 @@ class count_of_annual_closed_product extends baseCalc public function calculate($data) { $closedDate = $data->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_closed_productplan.php b/module/metric/calc/system/scale/count_of_annual_closed_productplan.php index 85002c6f0d..4162b21e8d 100755 --- a/module/metric/calc/system/scale/count_of_annual_closed_productplan.php +++ b/module/metric/calc/system/scale/count_of_annual_closed_productplan.php @@ -29,10 +29,8 @@ class count_of_annual_closed_productplan extends baseCalc public function calculate($data) { $closedDate = $data->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_closed_project.php b/module/metric/calc/system/scale/count_of_annual_closed_project.php index 8dd406214b..3b9da8e490 100755 --- a/module/metric/calc/system/scale/count_of_annual_closed_project.php +++ b/module/metric/calc/system/scale/count_of_annual_closed_project.php @@ -28,10 +28,10 @@ class count_of_annual_closed_project extends baseCalc public function calculate($row) { - if(empty($row->closedDate) || $row->status != 'closed') return false; + if($row->status != 'closed') return false; - $year = substr($row->closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_closed_top_program.php b/module/metric/calc/system/scale/count_of_annual_closed_top_program.php index 3a76e66225..60e0fcd3c7 100755 --- a/module/metric/calc/system/scale/count_of_annual_closed_top_program.php +++ b/module/metric/calc/system/scale/count_of_annual_closed_top_program.php @@ -28,10 +28,10 @@ class count_of_annual_closed_top_program extends baseCalc public function calculate($row) { - if(empty($row->closedDate) or $row->status != 'closed') return false; + if($row->status != 'closed') return false; - $year = substr($row->closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_created_bug.php b/module/metric/calc/system/scale/count_of_annual_created_bug.php index 4d4543e2fb..81c9392581 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_bug.php +++ b/module/metric/calc/system/scale/count_of_annual_created_bug.php @@ -28,11 +28,8 @@ class count_of_annual_created_bug extends baseCalc public function calculate($row) { - $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_created_case.php b/module/metric/calc/system/scale/count_of_annual_created_case.php index 80ec83d097..a2273536f0 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_case.php +++ b/module/metric/calc/system/scale/count_of_annual_created_case.php @@ -28,11 +28,8 @@ class count_of_annual_created_case extends baseCalc public function calculate($row) { - $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_created_doc.php b/module/metric/calc/system/scale/count_of_annual_created_doc.php index 3700c1cba9..da34e3be35 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_doc.php +++ b/module/metric/calc/system/scale/count_of_annual_created_doc.php @@ -28,11 +28,8 @@ class count_of_annual_created_doc extends baseCalc public function calculate($row) { - $addedDate = $row->addedDate; - if(empty($addedDate)) return false; - - $year = substr($addedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->addedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_created_execution.php b/module/metric/calc/system/scale/count_of_annual_created_execution.php index f69a81b931..6a29483315 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_execution.php +++ b/module/metric/calc/system/scale/count_of_annual_created_execution.php @@ -28,11 +28,8 @@ class count_of_annual_created_execution extends baseCalc public function calculate($row) { - $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_created_feedback.php b/module/metric/calc/system/scale/count_of_annual_created_feedback.php index 48257f4cce..2c0aa43d18 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_feedback.php +++ b/module/metric/calc/system/scale/count_of_annual_created_feedback.php @@ -28,10 +28,8 @@ class count_of_annual_created_feedback extends baseCalc public function calculate($row) { - if(empty($row->openedDate)) return false; - - $year = substr($row->openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_created_product.php b/module/metric/calc/system/scale/count_of_annual_created_product.php index 7f2f913a62..858839a924 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_product.php +++ b/module/metric/calc/system/scale/count_of_annual_created_product.php @@ -28,11 +28,8 @@ class count_of_annual_created_product extends baseCalc public function calculate($data) { - $createdDate = $data->createdDate; - if(empty($createdDate)) return false; - - $year = substr($createdDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($data->createdDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_created_productplan.php b/module/metric/calc/system/scale/count_of_annual_created_productplan.php index f1757ce929..7116db4e03 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_productplan.php +++ b/module/metric/calc/system/scale/count_of_annual_created_productplan.php @@ -28,11 +28,8 @@ class count_of_annual_created_productplan extends baseCalc public function calculate($data) { - $createdDate = $data->createdDate; - if(empty($createdDate)) return false; - - $year = substr($createdDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($data->createdDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_created_project.php b/module/metric/calc/system/scale/count_of_annual_created_project.php index 401fb98b05..83675f80eb 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_project.php +++ b/module/metric/calc/system/scale/count_of_annual_created_project.php @@ -28,10 +28,8 @@ class count_of_annual_created_project extends baseCalc public function calculate($row) { - if(empty($row->openedDate)) return false; - - $year = substr($row->openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_created_release.php b/module/metric/calc/system/scale/count_of_annual_created_release.php index a052e1a1b3..dd004de029 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_release.php +++ b/module/metric/calc/system/scale/count_of_annual_created_release.php @@ -28,13 +28,9 @@ class count_of_annual_created_release extends baseCalc public function calculate($data) { - if(empty($data->date)) return false; - $date = $data->date; - - $year = substr($date, 0, 4); - - if($year == '0000') return false; + $year = $this->getYear($date); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_created_story.php b/module/metric/calc/system/scale/count_of_annual_created_story.php index 3bdf646889..ba589cfae0 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_story.php +++ b/module/metric/calc/system/scale/count_of_annual_created_story.php @@ -30,10 +30,8 @@ class count_of_annual_created_story extends baseCalc { $openedDate = $data->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_created_task.php b/module/metric/calc/system/scale/count_of_annual_created_task.php index 5a2e04874e..b8f0d21810 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_task.php +++ b/module/metric/calc/system/scale/count_of_annual_created_task.php @@ -30,10 +30,8 @@ class count_of_annual_created_task extends baseCalc { $openedDate = $data->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_created_top_program.php b/module/metric/calc/system/scale/count_of_annual_created_top_program.php index b5ae5a8991..0628751f0d 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_top_program.php +++ b/module/metric/calc/system/scale/count_of_annual_created_top_program.php @@ -28,10 +28,8 @@ class count_of_annual_created_top_program extends baseCalc public function calculate($row) { - if(empty($row->openedDate)) return false; - - $year = substr($row->openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_created_user.php b/module/metric/calc/system/scale/count_of_annual_created_user.php index 85b39a6ba3..a661db13dd 100755 --- a/module/metric/calc/system/scale/count_of_annual_created_user.php +++ b/module/metric/calc/system/scale/count_of_annual_created_user.php @@ -29,10 +29,9 @@ class count_of_annual_created_user extends baseCalc public function calculate($row) { $join = $row->join; - if(empty($join)) return false; - $year = substr($join, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($join); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_delivered_story.php b/module/metric/calc/system/scale/count_of_annual_delivered_story.php index cb507b17a6..01ce04ea3d 100755 --- a/module/metric/calc/system/scale/count_of_annual_delivered_story.php +++ b/module/metric/calc/system/scale/count_of_annual_delivered_story.php @@ -39,8 +39,8 @@ class count_of_annual_delivered_story extends baseCalc if($date === null) return false; - $year = substr($date, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($date); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_finished_execution.php b/module/metric/calc/system/scale/count_of_annual_finished_execution.php index d4fe8b503f..d21db196ca 100644 --- a/module/metric/calc/system/scale/count_of_annual_finished_execution.php +++ b/module/metric/calc/system/scale/count_of_annual_finished_execution.php @@ -28,11 +28,8 @@ class count_of_annual_finished_execution extends baseCalc public function calculate($row) { - $realEnd = $row->realEnd; - if(empty($realEnd)) return false; - - $year = substr($realEnd, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->realEnd); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_annual_finished_productplan.php b/module/metric/calc/system/scale/count_of_annual_finished_productplan.php index 75c0439b6f..3689b79280 100755 --- a/module/metric/calc/system/scale/count_of_annual_finished_productplan.php +++ b/module/metric/calc/system/scale/count_of_annual_finished_productplan.php @@ -29,10 +29,8 @@ class count_of_annual_finished_productplan extends baseCalc public function calculate($data) { $finishedDate = $data->finishedDate; - if(empty($finishedDate)) return false; - - $year = substr($finishedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($finishedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_finished_project.php b/module/metric/calc/system/scale/count_of_annual_finished_project.php index ab7b108fdf..4978ff6fd6 100755 --- a/module/metric/calc/system/scale/count_of_annual_finished_project.php +++ b/module/metric/calc/system/scale/count_of_annual_finished_project.php @@ -28,12 +28,10 @@ class count_of_annual_finished_project extends baseCalc public function calculate($row) { - if(empty($row->realEnd)) return false; + $year = $this->getYear($row->realEnd); + if(!$year) return false; - if(empty($row->realEnd)) return false; - $year = substr($row->realEnd, 0, 4); - - if($year == '0000' || $row->status != 'closed') return false; + if($row->status != 'closed') return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_finished_story.php b/module/metric/calc/system/scale/count_of_annual_finished_story.php index 3a7d9f97d4..037bbec5bb 100755 --- a/module/metric/calc/system/scale/count_of_annual_finished_story.php +++ b/module/metric/calc/system/scale/count_of_annual_finished_story.php @@ -31,11 +31,10 @@ class count_of_annual_finished_story extends baseCalc $closedDate = $data->closedDate; $closedReason = $data->closedReason; - if(empty($closedDate)) return false; + $year = $this->getYear($closedDate); + if(!$year) return false; - $year = substr($closedDate, 0, 4); - - if($year == '0000' || $closedReason != 'done') return false; + if($closedReason != 'done') return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_finished_task.php b/module/metric/calc/system/scale/count_of_annual_finished_task.php index 65ab80f95a..38132ceafe 100755 --- a/module/metric/calc/system/scale/count_of_annual_finished_task.php +++ b/module/metric/calc/system/scale/count_of_annual_finished_task.php @@ -28,13 +28,10 @@ class count_of_annual_finished_task extends baseCalc public function calculate($data) { - if(empty($data->finishedDate) or $data->status != 'done') return false; + $year = $this->getYear($data->finishedDate); + if(!$year) return false; - $finishedDate = $data->finishedDate; - - if(empty($finishedDate)) return false; - $year = substr($finishedDate, 0, 4); - if($year == '0000') return false; + if($data->status != 'done') return false; if(!isset($this->result[$year])) $this->result[$year] = 0; diff --git a/module/metric/calc/system/scale/count_of_annual_fixed_bug.php b/module/metric/calc/system/scale/count_of_annual_fixed_bug.php index 1ad26b09fd..58e2307021 100755 --- a/module/metric/calc/system/scale/count_of_annual_fixed_bug.php +++ b/module/metric/calc/system/scale/count_of_annual_fixed_bug.php @@ -28,11 +28,8 @@ class count_of_annual_fixed_bug extends baseCalc public function calculate($row) { - $closedDate = $row->closedDate; - if(empty($closedDate)) return false; - - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if(!isset($this->result[$year])) $this->result[$year] = 0; if($row->status == 'closed' and $row->resolution == 'fixed') $this->result[$year] += 1; diff --git a/module/metric/calc/system/scale/count_of_daily_closed_bug.php b/module/metric/calc/system/scale/count_of_daily_closed_bug.php index 94e4845465..b080e42427 100755 --- a/module/metric/calc/system/scale/count_of_daily_closed_bug.php +++ b/module/metric/calc/system/scale/count_of_daily_closed_bug.php @@ -29,10 +29,9 @@ class count_of_daily_closed_bug extends baseCalc public function calculate($row) { $closedDate = $row->closedDate; - if(empty($closedDate)) return false; + $year = $this->getYear($closedDate); + if(!$year) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; $month = substr($closedDate, 5, 2); $day = substr($closedDate, 8, 2); diff --git a/module/metric/calc/system/scale/count_of_daily_created_story.php b/module/metric/calc/system/scale/count_of_daily_created_story.php index 5d69d9b323..adb7ccfd71 100755 --- a/module/metric/calc/system/scale/count_of_daily_created_story.php +++ b/module/metric/calc/system/scale/count_of_daily_created_story.php @@ -30,10 +30,8 @@ class count_of_daily_created_story extends baseCalc { $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; list($year, $month, $day) = explode('-', $openedDate); diff --git a/module/metric/calc/system/scale/count_of_daily_finished_task.php b/module/metric/calc/system/scale/count_of_daily_finished_task.php index 59cd369625..b2888badb2 100755 --- a/module/metric/calc/system/scale/count_of_daily_finished_task.php +++ b/module/metric/calc/system/scale/count_of_daily_finished_task.php @@ -29,10 +29,10 @@ class count_of_daily_finished_task extends baseCalc public function calculate($row) { $finishedDate = $row->finishedDate; - if(empty($finishedDate)) return false; - $year = substr($finishedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($finishedDate); + if(!$year) return false; + $month = substr($finishedDate, 5, 2); $day = substr($finishedDate, 8, 2); diff --git a/module/metric/calc/system/scale/count_of_daily_run_case.php b/module/metric/calc/system/scale/count_of_daily_run_case.php index 6662b362c8..4efca05f6b 100755 --- a/module/metric/calc/system/scale/count_of_daily_run_case.php +++ b/module/metric/calc/system/scale/count_of_daily_run_case.php @@ -29,10 +29,9 @@ class count_of_daily_run_case extends baseCalc public function calculate($row) { $date = $row->date; - if(empty($date)) return false; - $year = substr($date, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($date); + if(!$year) return false; $month = substr($date, 5, 2); $day = substr($date, 8, 2); diff --git a/module/metric/calc/system/scale/count_of_delayed_finished_execution_which_annual_finished.php b/module/metric/calc/system/scale/count_of_delayed_finished_execution_which_annual_finished.php index 1da389e196..f528149419 100755 --- a/module/metric/calc/system/scale/count_of_delayed_finished_execution_which_annual_finished.php +++ b/module/metric/calc/system/scale/count_of_delayed_finished_execution_which_annual_finished.php @@ -28,10 +28,10 @@ class count_of_delayed_finished_execution_which_annual_finished extends baseCalc public function calculate($row) { - if(empty($row->closedDate) || empty($row->firstEnd)) return false; + if(empty($row->firstEnd)) return false; - $year = substr($row->closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if($row->status == 'closed' and $row->closedDate > $row->firstEnd) { diff --git a/module/metric/calc/system/scale/count_of_delayed_finished_project_which_annual_finished.php b/module/metric/calc/system/scale/count_of_delayed_finished_project_which_annual_finished.php index 6e26c8c542..be743c3284 100755 --- a/module/metric/calc/system/scale/count_of_delayed_finished_project_which_annual_finished.php +++ b/module/metric/calc/system/scale/count_of_delayed_finished_project_which_annual_finished.php @@ -30,9 +30,11 @@ class count_of_delayed_finished_project_which_annual_finished extends baseCalc { if(empty($row->closedDate) || empty($row->firstEnd) || empty($row->realEnd)) return false; - $finishedYear = substr($row->realEnd, 0, 4); - $firstEndYear = substr($row->firstEnd, 0, 4); - if($finishedYear == '0000' || $firstEndYear == '0000') return false; + $finishedYear = $this->getYear($row->realEnd); + if(!$finishedYear) return false; + + $firstEndYear = $this->getYear($row->firstEnd); + if(!$firstEndYear) return false; if($row->status == 'closed' && $row->realEnd > $row->firstEnd) { diff --git a/module/metric/calc/system/scale/count_of_monthly_closed_execution.php b/module/metric/calc/system/scale/count_of_monthly_closed_execution.php index 31f169ffe5..6726c6c58e 100755 --- a/module/metric/calc/system/scale/count_of_monthly_closed_execution.php +++ b/module/metric/calc/system/scale/count_of_monthly_closed_execution.php @@ -29,10 +29,9 @@ class count_of_monthly_closed_execution extends baseCalc public function calculate($row) { $closedDate = $row->closedDate; - if(empty($closedDate)) return false; + $year = $this->getYear($closedDate); + if(!$year) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; $month = substr($closedDate, 5, 2); if(!isset($this->result[$year])) $this->result[$year] = array(); diff --git a/module/metric/calc/system/scale/count_of_monthly_closed_project.php b/module/metric/calc/system/scale/count_of_monthly_closed_project.php index 17276f9d8f..1ea23491ec 100755 --- a/module/metric/calc/system/scale/count_of_monthly_closed_project.php +++ b/module/metric/calc/system/scale/count_of_monthly_closed_project.php @@ -28,13 +28,12 @@ class count_of_monthly_closed_project extends baseCalc public function calculate($row) { - if(empty($row->closedDate) || $row->status != 'closed') return false; + if($row->status != 'closed') return false; - $year = substr($row->closedDate, 0, 4); + $year = $this->getYear($row->closedDate); + if(!$year) return false; $month = substr($row->closedDate, 5, 2); - if($year == '0000') return false; - if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$month])) $this->result[$year][$month] = 0; diff --git a/module/metric/calc/system/scale/count_of_monthly_created_bug.php b/module/metric/calc/system/scale/count_of_monthly_created_bug.php index 1eaf0e5a1d..d77f93e5b1 100755 --- a/module/metric/calc/system/scale/count_of_monthly_created_bug.php +++ b/module/metric/calc/system/scale/count_of_monthly_created_bug.php @@ -29,10 +29,9 @@ class count_of_monthly_created_bug extends baseCalc public function calculate($row) { $openedDate = $row->openedDate; - if(empty($openedDate)) return false; + $year = $this->getYear($openedDate); + if(!$year) return false; - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; $month = substr($openedDate, 5, 2); if(!isset($this->result[$year])) $this->result[$year] = array(); diff --git a/module/metric/calc/system/scale/count_of_monthly_created_execution.php b/module/metric/calc/system/scale/count_of_monthly_created_execution.php index d241e62fa0..8eb05dc70b 100755 --- a/module/metric/calc/system/scale/count_of_monthly_created_execution.php +++ b/module/metric/calc/system/scale/count_of_monthly_created_execution.php @@ -29,10 +29,9 @@ class count_of_monthly_created_execution extends baseCalc public function calculate($row) { $openedDate = $row->openedDate; - if(empty($openedDate)) return false; - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($openedDate); + if(!$year) return false; $month = substr($openedDate, 5, 2); if(!isset($this->result[$year])) $this->result[$year] = array(); diff --git a/module/metric/calc/system/scale/count_of_monthly_created_project.php b/module/metric/calc/system/scale/count_of_monthly_created_project.php index f70c196793..56176ca370 100755 --- a/module/metric/calc/system/scale/count_of_monthly_created_project.php +++ b/module/metric/calc/system/scale/count_of_monthly_created_project.php @@ -28,13 +28,11 @@ class count_of_monthly_created_project extends baseCalc public function calculate($row) { - if(empty($row->openedDate)) return false; + $year = $this->getYear($row->openedDate); + if(!$year) return false; - $year = substr($row->openedDate, 0, 4); $month = substr($row->openedDate, 5, 2); - if($year == '0000') return false; - if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$month])) $this->result[$year][$month] = 0; diff --git a/module/metric/calc/system/scale/count_of_monthly_created_release.php b/module/metric/calc/system/scale/count_of_monthly_created_release.php index 0fa9d5c87b..953f970198 100755 --- a/module/metric/calc/system/scale/count_of_monthly_created_release.php +++ b/module/metric/calc/system/scale/count_of_monthly_created_release.php @@ -28,15 +28,12 @@ class count_of_monthly_created_release extends baseCalc public function calculate($data) { - if(empty($data->date)) return false; - $date = $data->date; + $year = $this->getYear($date); + if(!$year) return false; - $year = substr($date, 0, 4); $month = substr($date, 5, 2); - if($year == '0000') return false; - if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$month])) $this->result[$year][$month] = 0; diff --git a/module/metric/calc/system/scale/count_of_monthly_created_story.php b/module/metric/calc/system/scale/count_of_monthly_created_story.php index 71d47db042..6b6aa50531 100755 --- a/module/metric/calc/system/scale/count_of_monthly_created_story.php +++ b/module/metric/calc/system/scale/count_of_monthly_created_story.php @@ -30,13 +30,10 @@ class count_of_monthly_created_story extends baseCalc { $openedDate = $data->openedDate; - if(empty($openedDate)) return false; - - $year = substr($openedDate, 0, 4); + $year = $this->getYear($openedDate); + if(!$year) return false; $month = substr($openedDate, 5, 2); - if($year == '0000') return false; - if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$month])) $this->result[$year][$month] = 0; diff --git a/module/metric/calc/system/scale/count_of_monthly_created_task.php b/module/metric/calc/system/scale/count_of_monthly_created_task.php index 01917c955d..00f35924ad 100755 --- a/module/metric/calc/system/scale/count_of_monthly_created_task.php +++ b/module/metric/calc/system/scale/count_of_monthly_created_task.php @@ -29,10 +29,9 @@ class count_of_monthly_created_task extends baseCalc public function calculate($row) { $openedDate = $row->openedDate; - if(empty($openedDate)) return false; + $year = $this->getYear($openedDate); + if(!$year) return false; - $year = substr($openedDate, 0, 4); - if($year == '0000') return false; $month = substr($openedDate, 5, 2); if(!isset($this->result[$year])) $this->result[$year] = array(); diff --git a/module/metric/calc/system/scale/count_of_monthly_finished_story.php b/module/metric/calc/system/scale/count_of_monthly_finished_story.php index fc32f68f51..bc17fe13cd 100755 --- a/module/metric/calc/system/scale/count_of_monthly_finished_story.php +++ b/module/metric/calc/system/scale/count_of_monthly_finished_story.php @@ -31,12 +31,12 @@ class count_of_monthly_finished_story extends baseCalc $closedDate = $data->closedDate; $closedReason = $data->closedReason; - if(empty($closedDate)) return false; + $year = $this->getYear($closedDate); + if(!$year) return false; - $year = substr($closedDate, 0, 4); $month = substr($closedDate, 5, 2); - if($year == '0000' || $closedReason != 'done') return false; + if($closedReason != 'done') return false; if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$month])) $this->result[$year][$month] = 0; diff --git a/module/metric/calc/system/scale/count_of_monthly_finished_task.php b/module/metric/calc/system/scale/count_of_monthly_finished_task.php index 882f31a304..05a3f36ccd 100755 --- a/module/metric/calc/system/scale/count_of_monthly_finished_task.php +++ b/module/metric/calc/system/scale/count_of_monthly_finished_task.php @@ -29,10 +29,10 @@ class count_of_monthly_finished_task extends baseCalc public function calculate($row) { $closedDate = $row->closedDate; - if(empty($closedDate)) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($closedDate); + if(!$year) return false; + $month = substr($closedDate, 5, 2); if(!isset($this->result[$year])) $this->result[$year] = array(); diff --git a/module/metric/calc/system/scale/count_of_monthly_fixed_bug.php b/module/metric/calc/system/scale/count_of_monthly_fixed_bug.php index e65557dace..1811c60c6b 100755 --- a/module/metric/calc/system/scale/count_of_monthly_fixed_bug.php +++ b/module/metric/calc/system/scale/count_of_monthly_fixed_bug.php @@ -29,10 +29,9 @@ class count_of_monthly_fixed_bug extends baseCalc public function calculate($row) { $closedDate = $row->closedDate; - if(empty($closedDate)) return false; + $year = $this->getYear($closedDate); + if(!$year) return false; - $year = substr($closedDate, 0, 4); - if($year == '0000') return false; $month = substr($closedDate, 5, 2); if(!isset($this->result[$year])) $this->result[$year] = array(); diff --git a/module/metric/calc/system/scale/count_of_undelayed_finished_execution_which_annual_finished.php b/module/metric/calc/system/scale/count_of_undelayed_finished_execution_which_annual_finished.php index 43aff76444..2979bce1ec 100755 --- a/module/metric/calc/system/scale/count_of_undelayed_finished_execution_which_annual_finished.php +++ b/module/metric/calc/system/scale/count_of_undelayed_finished_execution_which_annual_finished.php @@ -30,8 +30,8 @@ class count_of_undelayed_finished_execution_which_annual_finished extends baseCa { if(empty($row->closedDate) || empty($row->firstEnd)) return false; - $year = substr($row->closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if($row->status == 'closed' && $row->closedDate <= $row->firstEnd) { diff --git a/module/metric/calc/system/scale/count_of_undelayed_finished_project_which_annual_finished.php b/module/metric/calc/system/scale/count_of_undelayed_finished_project_which_annual_finished.php index 5d16077421..8d7d9e2883 100755 --- a/module/metric/calc/system/scale/count_of_undelayed_finished_project_which_annual_finished.php +++ b/module/metric/calc/system/scale/count_of_undelayed_finished_project_which_annual_finished.php @@ -30,9 +30,11 @@ class count_of_undelayed_finished_project_which_annual_finished extends baseCalc { if(empty($row->closedDate) || empty($row->firstEnd) || empty($row->realEnd)) return false; - $finishedYear = substr($row->realEnd, 0, 4); - $firstEndYear = substr($row->firstEnd, 0, 4); - if($finishedYear == '0000' || $firstEndYear == '0000') return false; + $finishedYear = $this->getYear($row->realEnd); + if(!$finishedYear) return false; + + $firstEndYear = $this->getYear($row->firstEnd); + if(!$firstEndYear) return false; if($row->status == 'closed' && $row->realEnd <= $row->firstEnd) { diff --git a/module/metric/calc/system/scale/count_of_undelayed_finished_project_which_annual_started.php b/module/metric/calc/system/scale/count_of_undelayed_finished_project_which_annual_started.php index 9f5bf8de9f..b60293300b 100755 --- a/module/metric/calc/system/scale/count_of_undelayed_finished_project_which_annual_started.php +++ b/module/metric/calc/system/scale/count_of_undelayed_finished_project_which_annual_started.php @@ -30,8 +30,8 @@ class count_of_undelayed_finished_project_which_annual_started extends baseCalc { if(empty($row->realBegan) or empty($row->realEnd) or empty($row->firstEnd)) return false; - $beginYear = substr($row->realBegan, 0, 4); - if($beginYear == '0000') return false; + $beginYear = $this->getYear($row->realBegan); + if(!$beginYear) return false; if(!isset($this->result[$beginYear])) $this->result[$beginYear] = 0; diff --git a/module/metric/calc/system/scale/scale_of_annual_closed_story.php b/module/metric/calc/system/scale/scale_of_annual_closed_story.php index b2dceab878..76e9a132f7 100755 --- a/module/metric/calc/system/scale/scale_of_annual_closed_story.php +++ b/module/metric/calc/system/scale/scale_of_annual_closed_story.php @@ -34,7 +34,7 @@ class scale_of_annual_closed_story extends baseCalc if(empty($row->closedDate) or !isset($row->estimate)) return false; $year = $this->getYear($row->closedDate); - if($year == '0000') return false; + if(!$year) return false; if($row->status == 'closed') { diff --git a/module/metric/calc/system/scale/scale_of_annual_finished_story.php b/module/metric/calc/system/scale/scale_of_annual_finished_story.php index 9146b424b2..3aabf6d10e 100755 --- a/module/metric/calc/system/scale/scale_of_annual_finished_story.php +++ b/module/metric/calc/system/scale/scale_of_annual_finished_story.php @@ -33,8 +33,8 @@ class scale_of_annual_finished_story extends baseCalc if(empty($row->closedDate) or !isset($row->estimate)) return false; - $year = substr($row->closedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($row->closedDate); + if(!$year) return false; if($row->status == 'closed' and $row->closedReason == 'done') { diff --git a/module/metric/calc/system/scale/scale_of_monthly_closed_story.php b/module/metric/calc/system/scale/scale_of_monthly_closed_story.php index 6539a3600b..25b8f02694 100755 --- a/module/metric/calc/system/scale/scale_of_monthly_closed_story.php +++ b/module/metric/calc/system/scale/scale_of_monthly_closed_story.php @@ -33,11 +33,10 @@ class scale_of_monthly_closed_story extends baseCalc if(empty($row->closedDate) || $row->status != 'closed') return false; - $year = substr($row->closedDate, 0, 4); + $year = $this->getYear($row->closedDate); + if(!$year) return false; $month = substr($row->closedDate, 5, 2); - if($year == '0000') return false; - if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$month])) $this->result[$year][$month] = 0; diff --git a/module/metric/calc/system/scale/scale_of_monthly_delivered_story.php b/module/metric/calc/system/scale/scale_of_monthly_delivered_story.php index 569ca1ea9e..3ec383d447 100755 --- a/module/metric/calc/system/scale/scale_of_monthly_delivered_story.php +++ b/module/metric/calc/system/scale/scale_of_monthly_delivered_story.php @@ -37,9 +37,9 @@ class scale_of_monthly_delivered_story extends baseCalc if($date === null) return false; - $year = substr($date, 0, 4); + $year = $this->getYear($date); + if(!$year) return false; $month = substr($date, 5, 2); - if($year == '0000') return false; if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$month])) $this->result[$year][$month] = 0; diff --git a/module/metric/calc/system/scale/scale_of_monthly_finished_story.php b/module/metric/calc/system/scale/scale_of_monthly_finished_story.php index 687ab81b5b..93f8c1ac9f 100755 --- a/module/metric/calc/system/scale/scale_of_monthly_finished_story.php +++ b/module/metric/calc/system/scale/scale_of_monthly_finished_story.php @@ -33,11 +33,10 @@ class scale_of_monthly_finished_story extends baseCalc if(empty($row->closedDate) || $row->closedReason != 'done') return false; - $year = substr($row->closedDate, 0, 4); + $year = $this->getYear($row->closedDate); + if(!$year) return false; $month = substr($row->closedDate, 5, 2); - if($year == '0000') return false; - if(!isset($this->result[$year])) $this->result[$year] = array(); if(!isset($this->result[$year][$month])) $this->result[$year][$month] = 0; diff --git a/module/metric/calc/user/scale/count_of_daily_finished_task_in_user.php b/module/metric/calc/user/scale/count_of_daily_finished_task_in_user.php index b8fa9f016a..bab7625fab 100755 --- a/module/metric/calc/user/scale/count_of_daily_finished_task_in_user.php +++ b/module/metric/calc/user/scale/count_of_daily_finished_task_in_user.php @@ -50,8 +50,8 @@ class count_of_daily_finished_task_in_user extends baseCalc if(empty($finishedDate) || empty($finishedBy)) return false; if($projectStatus == 'suspended' || $executionStatus == 'suspended') return false; - $year = substr($finishedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($finishedDate); + if(!$year) return false; $date = date("Y-m-d", strtotime($finishedDate)); list($year, $month, $day) = explode('-', $date); diff --git a/module/metric/calc/user/scale/count_of_daily_fixed_bug_in_user.php b/module/metric/calc/user/scale/count_of_daily_fixed_bug_in_user.php index ebbe13903f..b1fc84dfad 100755 --- a/module/metric/calc/user/scale/count_of_daily_fixed_bug_in_user.php +++ b/module/metric/calc/user/scale/count_of_daily_fixed_bug_in_user.php @@ -45,8 +45,8 @@ class count_of_daily_fixed_bug_in_user extends baseCalc if(empty($resolvedDate) || empty($resolvedBy)) return false; - $year = substr($resolvedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($resolvedDate); + if(!$year) return false; $month = substr($resolvedDate, 5, 2); $day = substr($resolvedDate, 8, 2); diff --git a/module/metric/calc/user/scale/count_of_daily_review_feedback_in_user.php b/module/metric/calc/user/scale/count_of_daily_review_feedback_in_user.php index fd7f443d30..9135af7be6 100755 --- a/module/metric/calc/user/scale/count_of_daily_review_feedback_in_user.php +++ b/module/metric/calc/user/scale/count_of_daily_review_feedback_in_user.php @@ -36,8 +36,8 @@ class count_of_daily_review_feedback_in_user extends baseCalc /* input ,admin output admin */ $reviewedBy = current(array_filter(explode(',', $reviewedBy))); - $year = substr($reviewedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($reviewedDate); + if(!$year) return false; $date = date("Y-m-d", strtotime($reviewedDate)); list($year, $month, $day) = explode('-', $date); diff --git a/module/metric/calc/user/scale/count_of_daily_review_story_in_user.php b/module/metric/calc/user/scale/count_of_daily_review_story_in_user.php index 09a177cf34..9ea703666d 100755 --- a/module/metric/calc/user/scale/count_of_daily_review_story_in_user.php +++ b/module/metric/calc/user/scale/count_of_daily_review_story_in_user.php @@ -36,8 +36,8 @@ class count_of_daily_review_story_in_user extends baseCalc /* input ,admin output admin */ $reviewedBy = current(array_filter(explode(',', $reviewedBy))); - $year = substr($reviewedDate, 0, 4); - if($year == '0000') return false; + $year = $this->getYear($reviewedDate); + if(!$year) return false; $date = date("Y-m-d", strtotime($reviewedDate)); list($year, $month, $day) = explode('-', $date); diff --git a/module/metric/calc/user/scale/count_of_dealyed_task_in_user.php b/module/metric/calc/user/scale/count_of_dealyed_task_in_user.php index 2ca3bad453..5523818cbe 100755 --- a/module/metric/calc/user/scale/count_of_dealyed_task_in_user.php +++ b/module/metric/calc/user/scale/count_of_dealyed_task_in_user.php @@ -34,7 +34,9 @@ class count_of_dealyed_task_in_user extends baseCalc $deadline = $row->deadline; $assignedTo = $row->assignedTo; - if(empty($deadline) || substr($deadline, 0 ,4) == '0000') return false; + $year = $this->getYear($deadline); + if(!$year) return false; + if(strtotime($nowDate) <= strtotime($deadline)) return false; if(!isset($this->result[$assignedTo])) $this->result[$assignedTo] = 0; diff --git a/module/metric/calc/user/scale/count_of_delayed_task_in_user.php b/module/metric/calc/user/scale/count_of_delayed_task_in_user.php index 54762cecbe..4d281385d3 100755 --- a/module/metric/calc/user/scale/count_of_delayed_task_in_user.php +++ b/module/metric/calc/user/scale/count_of_delayed_task_in_user.php @@ -34,7 +34,9 @@ class count_of_delayed_task_in_user extends baseCalc $deadline = $row->deadline; $assignedTo = $row->assignedTo; - if(empty($deadline) || substr($deadline, 0 ,4) == '0000') return false; + $year = $this->getYear($deadline); + if(!$year) return false; + if(strtotime($nowDate) <= strtotime($deadline)) return false; if(!isset($this->result[$assignedTo])) $this->result[$assignedTo] = 0; diff --git a/module/metric/control.php b/module/metric/control.php index 8050748e51..315711dbb5 100755 --- a/module/metric/control.php +++ b/module/metric/control.php @@ -73,6 +73,9 @@ class metric extends control $result = $this->metric->getResultByCode($current->code, array(), 'cron', $pager); $allResult = $this->metric->getResultByCode($current->code, array(), 'cron'); + $result = $this->completeMissingRecords($result, $current); + $allResult = $this->completeMissingRecords($allResult, $current); + $resultHeader = $this->metric->getViewTableHeader($current); $resultData = $this->metric->getViewTableData($current, $result); $allResultData = $this->metric->getViewTableData($current, $allResult); diff --git a/module/metric/model.php b/module/metric/model.php index a6edf6cb34..1105cf4468 100755 --- a/module/metric/model.php +++ b/module/metric/model.php @@ -490,7 +490,7 @@ class metricModel extends model { static $metricsWithCode; if(is_array($fieldList)) $fieldList = implode(',', $fieldList); - if(!isset($metricsWithCode[$code . $fieldList])) $metricsWithCode[$code . $fieldList] = $this->metricTao->fetchMetricByCode($code, $fieldList); + if(!isset($metricsWithCode[$code . $fieldList])) $metricsWithCode[$code . $fieldList] = $this->fetchMetricByCode($code, $fieldList); return $metricsWithCode[$code . $fieldList]; } diff --git a/module/metric/tao.php b/module/metric/tao.php index abb4ed47dc..4837f18540 100644 --- a/module/metric/tao.php +++ b/module/metric/tao.php @@ -510,7 +510,8 @@ class metricTao extends metricModel { $this->dao->delete()->from(TABLE_METRICLIB) ->where('metricCode')->eq($code) - ->andWhere('deleted')->eq('1') + ->andWhere('deleted', true)->eq('1') + ->orWhere('value')->eq(0)->markRight(1) ->exec(); } diff --git a/module/metric/zen.php b/module/metric/zen.php index b29c828dde..492249e72f 100644 --- a/module/metric/zen.php +++ b/module/metric/zen.php @@ -245,37 +245,25 @@ class metricZen extends metric $records = array(); foreach($calcList as $code => $calc) { - $metric = $this->metric->getByCode($code); - $dateType = $this->metric->getDateTypeByCode($code); - $recordCommon = $this->buildRecordCommonFields($metric->id, $code, $now, $dateValues->$dateType); - $initRecords = !$calc->initRecord ? array() : $this->initMetricRecords($recordCommon, $metric->scope); - + $metric = $this->metric->getByCode($code); if($calc->reuse) $this->prepareReuseMetricResult($calc, $options); $results = $calc->getResult($options); + $records[$code] = array(); if(is_array($results)) { foreach($results as $record) { $record = (object)$record; - if(!is_numeric($record->value)) continue; + if(!is_numeric($record->value) || empty($record->value)) continue; $record->metricID = $calc->id; $record->metricCode = $code; $record->date = $now; $record->system = $metric->scope == 'system' ? 1 : 0; - $uniqueKey = $this->getUniqueKeyByRecord($record); - if(!isset($initRecords[$uniqueKey])) - { - $initRecords[$uniqueKey] = $record; - continue; - } - - $initRecords[$uniqueKey]->value = $record->value; + $records[$code][] = $record; } } - - $records[$code] = array_values($initRecords); } return $records; @@ -318,11 +306,10 @@ class metricZen extends metric if($dateType == 'nodate') return array(); if($type == 'all' && $this->metric->checkHasInferenceOfDate($code, $dateType, $date)) return array(); - $dateConfig = $this->metric->parseDateStr($date, $dateType); - $recordCommon = $this->buildRecordCommonFields($metric->id, $code, $now, $dateConfig); - $initRecords = !$calc->initRecord ? array() : $this->initMetricRecords($recordCommon, $metric->scope, "{$date} 23:59:59"); + $records = array(); + $dateConfig = $this->metric->parseDateStr($date, $dateType); + $results = $calc->getResult($dateConfig); - $results = $calc->getResult($dateConfig); if(is_array($results)) { foreach($results as $record) @@ -335,18 +322,11 @@ class metricZen extends metric $record->date = $now; $record->system = $metric->scope == 'system' ? 1 : 0; - $uniqueKey = $this->getUniqueKeyByRecord($record); - if(!isset($initRecords[$uniqueKey])) - { - $initRecords[$uniqueKey] = $record; - continue; - } - - $initRecords[$uniqueKey]->value = $record->value; + $records[] = $record; } } - return array_values($initRecords); + return $records; } /** @@ -403,16 +383,48 @@ class metricZen extends metric protected function buildRecordCommonFields($metricID, $code, $date, $dateValues) { $record = new stdclass(); - $record->value = 0; - $record->metricID = $metricID; - $record->metricCode = $code; - $record->date = $date; + $record->value = 0; + $record->date = $date; + $record->calcType = 'cron'; + $record->calculatedBy = 'system'; $record = (object)array_merge((array)$record, $dateValues); return $record; } + /** + * 补全缺失的度量数据。 + * Complete missing metric data. + * + * @param array $records + * @param array $metric + * @access protected + * @return array + */ + protected function completeMissingRecords($records, $metric) + { + $now = helper::now(); + $dateValues = $this->metric->parseDateStr($now); + $dateType = $metric->dateType; + $recordCommon = $this->buildRecordCommonFields($metric->id, $metric->code, $now, $dateValues->$dateType); + $initRecords = $this->initMetricRecords($recordCommon, $metric->scope); + + foreach($records as $record) + { + $uniqueKey = $this->getUniqueKeyByRecord($record); + if(!isset($initRecords[$uniqueKey])) + { + $initRecords[$uniqueKey] = $record; + continue; + } + + $initRecords[$uniqueKey]->value = $record->value; + } + + return array_values($initRecords); + } + /** * 根据度量数据,获取度量数据的唯一键。 * Get the unique key of metric data based on metric data. diff --git a/module/pivot/model.php b/module/pivot/model.php index d4763579b0..c059274384 100644 --- a/module/pivot/model.php +++ b/module/pivot/model.php @@ -23,7 +23,6 @@ class pivotModel extends model parent::__construct(); $this->loadBIDAO(); $this->loadModel('bi'); - $this->viewableObjects = $this->bi->getViewableObject('pivot'); } /** @@ -36,7 +35,8 @@ class pivotModel extends model */ public function checkAccess($pivotID, $method = 'preview') { - if(!in_array($pivotID, $this->viewableObjects)) + $viewableObjects = $this->bi->getViewableObject('pivot'); + if(!in_array($pivotID, $viewableObjects)) { return $this->app->control->sendError($this->lang->pivot->accessDenied, helper::createLink('pivot', $method)); } @@ -52,9 +52,10 @@ class pivotModel extends model */ public function filterInvisiblePivot($pivots) { + $viewableObjects = $this->bi->getViewableObject('pivot'); foreach($pivots as $index => $pivot) { - if(!in_array($pivot->id, $this->viewableObjects)) unset($pivots[$index]); + if(!in_array($pivot->id, $viewableObjects)) unset($pivots[$index]); } return array_values($pivots); @@ -69,7 +70,7 @@ class pivotModel extends model * @access public * @return object|bool */ - public function getByID(int $pivotID, bool $processDateVar = false, string $filterStatus = 'published'): object|bool + public function getByID(int $pivotID, bool $processDateVar = false, string $filterStatus = 'published', bool $addDrills = true): object|bool { $pivot = $this->pivotTao->fetchPivot($pivotID); if(!$pivot) return false; @@ -91,7 +92,9 @@ class pivotModel extends model $pivot->filters = array(); } - $this->processPivot($pivot); + $this->completePivot($pivot); + if($addDrills) $this->addDrills($pivot); + // if(isset($pivot->stage) && $pivot->stage == 'published' && $this->app->methodName == 'preview') $this->processFieldSettings($pivot); return $pivot; diff --git a/module/pivot/tao.php b/module/pivot/tao.php index 6839fa9056..eda310c83d 100644 --- a/module/pivot/tao.php +++ b/module/pivot/tao.php @@ -421,11 +421,12 @@ EOT)->from(TABLE_TASK)->alias('t1') */ protected function getPivotID(int $groupID): int { + $viewableObjects = $this->bi->getViewableObject('pivot'); return (int)$this->dao->select('id')->from(TABLE_PIVOT) ->where("FIND_IN_SET({$groupID}, `group`)") ->andWhere('stage')->ne('draft') ->andWhere('deleted')->eq('0') - ->andWhere('id')->in($this->viewableObjects) + ->andWhere('id')->in($viewableObjects) ->orderBy('id_desc') ->limit(1) ->fetch('id'); diff --git a/module/pivot/zen.php b/module/pivot/zen.php index 5ed3b19328..5ede438925 100644 --- a/module/pivot/zen.php +++ b/module/pivot/zen.php @@ -408,7 +408,11 @@ class pivotZen extends pivot */ public function getDrill(int $pivotID, string $colName, string $status = 'published'): object { - if($status == 'published') return $this->pivot->fetchPivotDrill($pivotID, $colName); + if($status == 'published') + { + $drills = $this->pivot->fetchPivotDrills($pivotID, $colName); + return reset($drills); + } $cache = $this->getCache($pivotID); $drills = json_decode(json_encode($cache->drills), true); diff --git a/module/product/ui/batchedit.html.php b/module/product/ui/batchedit.html.php index 1c8483bb7a..7cb3dd312d 100644 --- a/module/product/ui/batchedit.html.php +++ b/module/product/ui/batchedit.html.php @@ -24,25 +24,8 @@ foreach($fields as $fieldName => $field) } $items['acl']['control'] = array('control' => $items['acl']['control'], 'inline' => true); -$extendFields = $this->product->getFlowExtendFields(); -foreach($extendFields as $extendField) -{ - $items[$extendField->field] = array('name' => $extendField->field, 'label' => $extendField->name, 'required' => strpos(",$extendField->rules,", ',1,') !== false, 'control' => $extendField->control, 'items' => zget($extendField, 'options', array())); - if($extendField->control == 'select') $items[$extendField->field]['control'] = 'picker'; - if($extendField->control == 'multi-select') - { - $items[$extendField->field]['control'] = 'picker'; - $items[$extendField->field]['multiple'] = true; - } -} - /* Build form field value for batch edit. */ $fieldNameList = array_keys($items); -$productData = array(); -foreach($products as $product) -{ - foreach($fieldNameList as $fieldName) $productData[$product->id][$fieldName] = zget($product, $fieldName, $fieldName == 'productIdList' ? $product->id : ''); -} formBatchPanel ( @@ -51,7 +34,7 @@ formBatchPanel set::mode('edit'), set::customFields(array('list' => $customFields, 'show' => explode(',', $showFields), 'key' => 'batchEditFields')), set::items($items), - set::data(array_values($productData)), + set::data(array_values($products)), set::onRenderRow(jsRaw('renderRowData')) ); diff --git a/module/product/zen.php b/module/product/zen.php index 764b7da85a..57d4987020 100644 --- a/module/product/zen.php +++ b/module/product/zen.php @@ -255,7 +255,8 @@ class productZen extends product if($this->config->edition != 'open' && isset($fields['workflowGroup'])) { - $fields['workflowGroup']['options'] = $this->loadModel('workflowGroup')->getPairs(); + $groupPairs = $this->loadModel('workflowGroup')->getPairs(); + $fields['workflowGroup']['options'] = $this->workflowGroup->appendBuildinLabel($groupPairs); } return $fields; diff --git a/module/program/config/form.php b/module/program/config/form.php index fd75a8d7f3..556ca7991c 100644 --- a/module/program/config/form.php +++ b/module/program/config/form.php @@ -2,7 +2,6 @@ $now = helper::now(); $today = helper::today(); -global $app; $config->program->form = new stdclass(); $config->program->form->create['parent'] = array('type' => 'int', 'control' => 'picker', 'required' => false, 'default' => 0); @@ -31,16 +30,16 @@ $config->program->form->edit['whitelist'] = array('type' => 'array', 'contro $config->program->form->close['realEnd'] = array('type' => 'date', 'required' => true, 'default' => $now, 'filter' => 'trim'); $config->program->form->close['status'] = array('type' => 'string', 'required' => false, 'default' => 'closed'); $config->program->form->close['closedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); -$config->program->form->close['closedBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account); +$config->program->form->close['closedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->program->form->close['lastEditedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); -$config->program->form->close['lastEditedBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account); +$config->program->form->close['lastEditedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->program->form->activate['begin'] = array('type' => 'date', 'required' => false, 'default' => null, 'filter' => 'trim'); $config->program->form->activate['end'] = array('type' => 'date', 'required' => false, 'default' => null, 'filter' => 'trim'); $config->program->form->activate['status'] = array('type' => 'string', 'required' => false, 'default' => 'doing'); $config->program->form->activate['realEnd'] = array('type' => 'date', 'required' => false, 'default' => null); $config->program->form->activate['lastEditedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); -$config->program->form->activate['lastEditedBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account); +$config->program->form->activate['lastEditedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->program->form->start['realBegan'] = array('type' => 'date', 'required' => true, 'default' => $today, 'filter' => 'trim'); $config->program->form->start['comment'] = array('type' => 'string', 'required' => false, 'default' => '', 'control' => 'editor'); diff --git a/module/program/control.php b/module/program/control.php index d613259dba..f1df7ef3ae 100644 --- a/module/program/control.php +++ b/module/program/control.php @@ -241,7 +241,11 @@ class program extends control $hasUnfinished = $this->program->hasUnfinishedChildren($program); if($hasUnfinished) return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.alert('{$this->lang->program->closeErrorMessage}');")); - $programData = form::data(null, $programID)->get(); + $programData = form::data(null, $programID) + ->add('closedBy', $this->app->user->account) + ->add('lastEditedBy', $this->app->user->account) + ->get(); + $changes = $this->program->close($programData, $program); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -316,7 +320,7 @@ class program extends control if(!empty($_POST)) { - $programData = form::data(null, $programID)->get(); + $programData = form::data(null, $programID)->add('lastEditedBy', $this->app->user->account)->get(); $changes = $this->program->activate($programData, $program); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); diff --git a/module/program/ui/kanban.html.php b/module/program/ui/kanban.html.php index b1f05306b9..ca305e0f30 100644 --- a/module/program/ui/kanban.html.php +++ b/module/program/ui/kanban.html.php @@ -87,6 +87,7 @@ else { zui::kanbanList ( + set('$replace', false), set::key('kanban'), set::items($kanbanList), set::height('calc(100vh - 120px)') diff --git a/module/project/control.php b/module/project/control.php index ce88e1df09..3124fc4e95 100755 --- a/module/project/control.php +++ b/module/project/control.php @@ -1732,9 +1732,7 @@ class project extends control $workflowGroups = $this->loadModel('workflowgroup')->getPairs('project', $model, $hasProduct); - $items = array(); - foreach($workflowGroups as $id => $name) $items[] = array('text' => $name, 'value' => $id); - + $items = $this->workflowgroup->appendBuildinLabel($workflowGroups); return $this->send(array('items' => array_values($items), 'defaultValue' => key($workflowGroups))); } } diff --git a/module/project/model.php b/module/project/model.php index c936f35b59..cbd12ca75e 100755 --- a/module/project/model.php +++ b/module/project/model.php @@ -571,7 +571,7 @@ class projectModel extends model if($this->config->edition != 'open') { $flow = $this->loadModel('workflow')->getByModule($module); - if(in_array($flow->app, array('scrum', 'waterfall'))) $flow->app = 'project'; + if(!empty($flow->app) && in_array($flow->app, array('scrum', 'waterfall'))) $flow->app = 'project'; if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . "#app=$flow->app"; } diff --git a/module/project/test/lib/bug.ui.class.php b/module/project/test/lib/bug.ui.class.php index 35ddf8ba35..2c3276a565 100644 --- a/module/project/test/lib/bug.ui.class.php +++ b/module/project/test/lib/bug.ui.class.php @@ -57,4 +57,42 @@ class bugTester extends tester if($form->dom->firstAssign->getText() == $user) return $this->success('单个指派Bug成功'); return $this->failed('单个指派Bug失败'); } + + /** + * 批量指派bug。 + * Batch assign bug. + * + * @param string $user + * @access public + * @return object + */ + public function batchAssignBug($user) + { + $form = $this->initForm('project', 'bug', array('project' => 1), 'appIframe-project'); + $form->dom->secondCheckbox->click(); + $form->dom->batchAssignTo->click(); + $form->dom->assignToAdmin->click(); + $form->wait(1); + if($form->dom->firstAssign->getText() == $user) return $this->success('批量指派Bug成功'); + return $this->failed('批量指派Bug失败'); + } + + /** + * 确认bug。 + * Confirm bug. + * + * @param string $user + * @access public + * @return object + */ + public function confirmBug($user) + { + $form = $this->initForm('project', 'bug', array('project' => 1), 'appIframe-project'); + $form->dom->confirmBtn->click(); + $form->dom->confirmAssignTo->picker($user); + $form->dom->confirm->click(); + $form->wait(1); + if($form->dom->firstConfirm->getText() == '已确认') return $this->success('确认Bug成功'); + return $this->failed('确认Bug失败'); + } } diff --git a/module/project/test/ui/bug.php b/module/project/test/ui/bug.php index 270ac9482f..bbe0083b34 100644 --- a/module/project/test/ui/bug.php +++ b/module/project/test/ui/bug.php @@ -22,6 +22,12 @@ cid=1 - 执行tester模块的assignBug方法,参数是'admin' - 最终测试状态 @SUCCESS - 测试结果 @单个指派Bug成功 + - 执行tester模块的batchAssignBug方法,参数是'admin' + - 最终测试状态 @SUCCESS + - 测试结果 @批量指派Bug成功 +- 执行tester模块的confirmBug方法,参数是'admin' + - 最终测试状态 @SUCCESS + - 测试结果 @确认Bug成功 */ @@ -83,5 +89,9 @@ r($tester->switchProduct('firstProduct', '5')) && p('status,message') && e('SUCC r($tester->switchProduct('secondProduct', '5')) && p('status,message') && e('SUCCESS,切换secondProduct查看数据成功'); /* 单个指派Bug */ r($tester->assignBug('admin')) && p('status,message') && e('SUCCESS,单个指派Bug成功'); +/* 批量指派Bug */ +r($tester->batchAssignBug('admin')) && p('status,message') && e('SUCCESS,批量指派Bug成功'); +#/* 确认Bug */ +r($tester->confirmBug('admin')) && p('status,message') && e('SUCCESS,确认Bug成功'); $tester->closeBrowser(); diff --git a/module/project/zen.php b/module/project/zen.php index 6fb07986e6..08c341feaf 100755 --- a/module/project/zen.php +++ b/module/project/zen.php @@ -328,7 +328,11 @@ class projectZen extends project } $hasProduct = isset($copyProject->hasProduct) ? $copyProject->hasProduct : 1; - if($this->config->edition != 'open') $this->view->workflowGroups = $this->loadModel('workflowgroup')->getPairs('project', $model, $hasProduct); + if($this->config->edition != 'open') + { + $workflowGroups = $this->loadModel('workflowgroup')->getPairs('project', $model, $hasProduct); + $this->view->workflowGroups = $this->workflowgroup->appendBuildinLabel($workflowGroups); + } /* Get copy projects. */ $copyProjects = $this->project->getPairsByModel($model, '', 0, false); diff --git a/module/projectbuild/test/lib/browse.ui.class.php b/module/projectbuild/test/lib/browse.ui.class.php new file mode 100644 index 0000000000..22b8037f64 --- /dev/null +++ b/module/projectbuild/test/lib/browse.ui.class.php @@ -0,0 +1,43 @@ +initForm('projectbuild', 'browse', array('projectID' => '1' ), 'appIframe-project'); + $form->wait(2); + if($product != '') $form->dom->product->picker($product); + $form->wait(1); + $string = $form->dom->num->getText(); + $num = preg_replace('/\D/', '', $string); + if($num == $expectNum) return $this->success('版本显示正确'); + return $this->failed('版本显示错误'); + } + + /** + * 搜索项目版本。 + * Search projectbuild. + * + * @param array $build + * @access public + */ + public function searchBuild($build) + { + $form = $this->initForm('projectbuild', 'browse', array('projectID' => '1' ), 'appIframe-project'); + $form->dom->search($searchList = array("名称,=, {$build}")); + $form->wait(2); + + //断言检查搜索是否成功 + if($form->dom->buildNameBrowse->getText() != $build) return $this->failed('项目版本搜索失败'); + return $this->success('项目版本搜索成功'); + } +} diff --git a/module/projectbuild/test/lib/view.ui.class.php b/module/projectbuild/test/lib/view.ui.class.php new file mode 100644 index 0000000000..cbb6d3e63e --- /dev/null +++ b/module/projectbuild/test/lib/view.ui.class.php @@ -0,0 +1,34 @@ +initForm('projectbuild', 'browse', array('projectID' => 1), 'appIframe-project'); + $buildNameBrowse = $browsePage->dom->buildNameBrowse->getText(); + $productBrowse = $browsePage->dom->productBrowse->getText(); + $executionBrowse = $browsePage->dom->executionBrowse->getText(); + $browsePage->dom->buildNameBrowse->click(); + + //在详情页面,获取项目版本字段信息 + $viewPage = $this->loadPage('projectbuild', 'view'); + $viewPage->wait(2); + $viewPage->dom->basic->click(); + $viewPage->wait(2); + $basicbuildName = $viewPage->dom->basicBuildName->getText(); + $basicProduct = $viewPage->dom->basicProduct->getText(); + $basicExecution = $viewPage->dom->basicExecution->getText(); + + //断言检查版本详情页显示是否正确 + if($buildNameBrowse != $basicbuildName) return $this->failed('项目版本详情页名称显示不正确'); + if($productBrowse != $basicProduct) return $this->failed('项目版本详情页所属产品显示不正确'); + if($executionBrowse != $basicExecution) return $this->failed('项目版本详情页所属执行显示不正确'); + return $this->success('项目版本详情查看成功'); + } +} diff --git a/module/projectbuild/test/ui/browse.php b/module/projectbuild/test/ui/browse.php new file mode 100755 index 0000000000..d583eadbdc --- /dev/null +++ b/module/projectbuild/test/ui/browse.php @@ -0,0 +1,91 @@ +#!/usr/bin/env php +id->range('1,2'); +$product->name->range('产品1,产品2'); +$product->type->range('normal'); +$product->gen(2); + +$project = zenData('project'); +$project->id->range('1'); +$project->project->range('0'); +$project->model->range('scrum'); +$project->type->range('project'); +$project->attribute->range('[]'); +$project->auth->range('[]'); +$project->parent->range('0'); +$project->grade->range('1'); +$project->name->range('敏捷项目1'); +$project->path->range('`,1,`'); +$project->begin->range('(-3w)-(-2w):1D')->type('timestamp')->format('YY/MM/DD'); +$project->end->range('(+5w)-(+6w):1D')->type('timestamp')->format('YY/MM/DD'); +$project->acl->range('open'); +$project->status->range('wait'); +$project->gen(1); + +$execution = zenData('project'); +$execution->id->range('2-3'); +$execution->project->range('1'); +$execution->type->range('sprint'); +$execution->attribute->range('[]'); +$execution->auth->range('[]'); +$execution->parent->range('1'); +$execution->grade->range('1'); +$execution->name->range('项目1迭代1, 项目1迭代2'); +$execution->path->range('`,1,2,`, `,1,3,`'); +$execution->begin->range('(-3w)-(-2w):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->end->range('(+5w)-(+6w):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->acl->range('open'); +$execution->status->range('wait'); +$execution->gen(2, false); + +$projectProduct = zenData('projectproduct'); +$projectProduct->project->range('1{2},2{2}'); +$projectProduct->product->range('1,2'); +$projectProduct->gen(4); + +$build = zenData('build'); +$build->id->range('1-6'); +$build->project->range('1'); +$build->product->range('1{1},2{5}'); +$build->branch->range('0'); +$build->execution->range('2'); +$build->name->range('版本1,版本2,版本3,版本4,版本5,版本6'); +$build->stories->range('[]'); +$build->bugs->range('[]'); +$build->scmPath->range('[]'); +$build->filePath->range('[]'); +$build->desc->range('描述111'); +$build->builder->range('admin'); +$build->deleted->range('0'); +$build->gen(6); + +$tester = new browseTester(); +$tester->login(); + +r($tester->switchProduct('产品1', '1')) && p('status,message') && e('SUCCESS,版本显示正确'); //查看产品1下的版本 +r($tester->switchProduct('产品2', '5')) && p('status,message') && e('SUCCESS,版本显示正确'); //切换产品,查看产品2下版本 +r($tester->searchBuild('版本2')) && p('status,message') && e('SUCCESS,项目版本搜索成功'); //项目版本列表搜索版本 + +$tester->closeBrowser(); diff --git a/module/projectbuild/test/ui/page/browse.php b/module/projectbuild/test/ui/page/browse.php new file mode 100755 index 0000000000..23dbf27138 --- /dev/null +++ b/module/projectbuild/test/ui/page/browse.php @@ -0,0 +1,26 @@ + "//*[@id='table-projectbuild-browse']/div[2]/div[3]/div/div/div/nav/a[4]/i", + 'waitTab' => "//*[@id='featureBar']/menu/li[2]/a/span[1]", + 'builddTab' => "//*[@id='featureBar']/menu/li[3]/a/span[1]", + 'terminatedTab' => "//*[@id='featureBar']/menu/li[4]/a/span[1]", + 'buildBtn' => "//*[@id='table-projectbuild-browse']/div[2]/div[3]/div/div/div/nav/a[3]/i", + 'buildSubmit' => "//*[@id='zin_projectbuild_publish_form']/div[4]/div/button/span", + 'terminateBtn' => "//*[@class='dtable-cell-content']/nav/a[3]/i", + 'terminateConfirm' => "//*[@class='modal-footer']/nav/button/span", + 'activeBtn' => "//*[@class='toolbar']/a[3]/i", + 'activeConfirm' => "//*[@class='modal-footer']/nav/button/span", + 'buildName' => "//*[@class='dtable-cells-container']/div[2]/div/a", + 'buildNameBrowse' => "//*[@id='mainContent']/div/div/div[2]/div[1]/div/div[2]/div/a", + 'productBrowse' => "//*[@id='table-projectbuild-browse']/div[2]/div[2]/div/div[1]/div", + 'executionBrowse' => "//*[@id='table-projectbuild-browse']/div[2]/div[2]/div/div[2]/div", + 'num' => "//*[@id='table-projectbuild-browse']/div[3]/nav/div[1]", + ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/projectbuild/test/ui/page/view.php b/module/projectbuild/test/ui/page/view.php index 4b023e8f17..f641596859 100644 --- a/module/projectbuild/test/ui/page/view.php +++ b/module/projectbuild/test/ui/page/view.php @@ -8,6 +8,7 @@ class viewPage extends page $xpath = array( 'basic' => "//*[@id='mainContent']/div[2]/div/div/div/div/ul/li[4]/a/span", 'basicBuildName' => "//*[@class='section']/table/tbody[1]/tr[2]/td", + 'basicProduct' => "//*[@class='section']/table/tbody[1]/tr[1]/td", 'basicExecution' => "//*[@class='section']/table/tbody[1]/tr[3]/td", /* 版本关联研发需求的元素 */ 'linkStoryBtn' => "//*[@id='mainContent']/div[2]/div/div/div/div[2]/div/div/button/span", diff --git a/module/projectbuild/test/ui/view.php b/module/projectbuild/test/ui/view.php new file mode 100644 index 0000000000..e5e5e6c8a7 --- /dev/null +++ b/module/projectbuild/test/ui/view.php @@ -0,0 +1,83 @@ +#!/usr/bin/env php +id->range('1'); +$product->name->range('产品1'); +$product->type->range('normal'); +$product->gen(1); + +$project = zenData('project'); +$project->id->range('1'); +$project->project->range('0'); +$project->model->range('scrum'); +$project->type->range('project'); +$project->attribute->range('[]'); +$project->auth->range('[]'); +$project->parent->range('0'); +$project->grade->range('1'); +$project->name->range('敏捷项目1'); +$project->path->range('`,1,`'); +$project->begin->range('(-3w)-(-2w):1D')->type('timestamp')->format('YY/MM/DD'); +$project->end->range('(+5w)-(+6w):1D')->type('timestamp')->format('YY/MM/DD'); +$project->acl->range('open'); +$project->status->range('wait'); +$project->gen(1); + +$execution = zenData('project'); +$execution->id->range('2-3'); +$execution->project->range('1'); +$execution->type->range('sprint'); +$execution->attribute->range('[]'); +$execution->auth->range('[]'); +$execution->parent->range('1'); +$execution->grade->range('1'); +$execution->name->range('项目1迭代1, 项目1迭代2'); +$execution->path->range('`,1,2,`, `,1,3,`'); +$execution->begin->range('(-3w)-(-2w):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->end->range('(+5w)-(+6w):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->acl->range('open'); +$execution->status->range('wait'); +$execution->gen(2, false); + +$projectProduct = zenData('projectproduct'); +$projectProduct->project->range('1,2,3'); +$projectProduct->product->range('1'); +$projectProduct->gen(3); + +$build = zenData('build'); +$build->id->range('1'); +$build->project->range('1'); +$build->product->range('1'); +$build->branch->range('0'); +$build->execution->range('2'); +$build->name->range('版本1'); +$build->stories->range('[]'); +$build->bugs->range('[]'); +$build->scmPath->range('[]'); +$build->filePath->range('[]'); +$build->desc->range('描述111'); +$build->deleted->range('0'); +$build->gen(1); + + +$tester = new buildViewTester(); +$tester->login(); + +r($tester->checkBuildView()) && p('message,status') && e('项目版本详情查看成功,SUCCESS'); //项目版本详情检查 + +$tester->closeBrowser(); diff --git a/module/repo/config/dtable.php b/module/repo/config/dtable.php index e65520b3e5..50ffa52a48 100644 --- a/module/repo/config/dtable.php +++ b/module/repo/config/dtable.php @@ -263,8 +263,8 @@ $config->repo->reviewDtable->fieldList['revisionA']['link'] = array('module' => $app->loadLang('bug'); $config->repo->reviewDtable->fieldList['type']['title'] = $lang->repo->type; $config->repo->reviewDtable->fieldList['type']['name'] = 'type'; -$config->repo->reviewDtable->fieldList['type']['map'] = $lang->bug->typeList; -$config->repo->reviewDtable->fieldList['type']['map'] += $lang->repo->typeList; +$config->repo->reviewDtable->fieldList['type']['map'] = $lang->repo->typeList; +$config->repo->reviewDtable->fieldList['type']['map'] += $lang->bug->typeList; $config->repo->reviewDtable->fieldList['status']['map'] = $lang->bug->statusList; diff --git a/module/screen/model.php b/module/screen/model.php index e377975847..4a96d2c6c2 100644 --- a/module/screen/model.php +++ b/module/screen/model.php @@ -41,7 +41,6 @@ class screenModel extends model $this->filter->dept = ''; $this->filter->account = ''; $this->filter->charts = array(); - $this->viewableObjects = $this->bi->getViewableObject('screen'); } /** @@ -54,7 +53,8 @@ class screenModel extends model */ public function checkAccess($screenID) { - if(!in_array($screenID, $this->viewableObjects)) + $viewableObjects = $this->bi->getViewableObject('screen'); + if(!in_array($screenID, $viewableObjects)) { return $this->app->control->sendError($this->lang->screen->accessDenied, helper::createLink('screen', 'browse')); } diff --git a/module/task/js/batchcreate.ui.js b/module/task/js/batchcreate.ui.js index c6ed61090a..61fb30a70b 100644 --- a/module/task/js/batchcreate.ui.js +++ b/module/task/js/batchcreate.ui.js @@ -170,7 +170,7 @@ function checkBatchEstStartedAndDeadline(event) const estStarted = $currentRow.find('[name^=estStarted]').val(); const deadline = $currentRow.find('[name^=deadline]').val(); - if(field == 'estStarted' && estStarted.length > 0 && estStarted < parentEstStarted) + if(field == 'estStarted' && estStarted.length > 0 && parentEstStarted.length > 0 && estStarted < parentEstStarted) { const $estStartedTd = $currentRow.find('td[data-name=estStarted]'); if($estStartedTd.find('.date-tip').length == 0 || $estStartedTd.find('.date-tip .form-tip').length > 0) @@ -184,7 +184,7 @@ function checkBatchEstStartedAndDeadline(event) } } - if(field == 'deadline' && deadline.length > 0 && deadline > parentDeadline) + if(field == 'deadline' && deadline.length > 0 && parentDeadline.length > 0 && deadline > parentDeadline) { const $deadlineTd = $currentRow.find('td[data-name=deadline]'); if($deadlineTd.find('.date-tip').length == 0 || $deadlineTd.find('.date-tip .form-tip').length > 0) diff --git a/module/task/js/batchedit.ui.js b/module/task/js/batchedit.ui.js index 0f7cf55646..237667a7ed 100644 --- a/module/task/js/batchedit.ui.js +++ b/module/task/js/batchedit.ui.js @@ -75,7 +75,6 @@ window.clickSubmit = async function(e) const $taskBatchFormTrs = $taskBatchForm.find('tbody tr'); var confirmID = ''; - var tipAll = true; for(let i = 0; i < $taskBatchFormTrs.length; i++) { const $currentTr = $($taskBatchFormTrs[i]); @@ -94,7 +93,6 @@ window.clickSubmit = async function(e) const nonStoryChildTaskIdList = Object.keys(nonStoryChildTasks[taskID]); if(nonStoryChildTaskIdList.length == 0) continue; - if(tipAll) tipAll = Object.keys(childTasks[taskID]).length == nonStoryChildTaskIdList.length; for(let j = 0; j < nonStoryChildTaskIdList.length; j++) confirmID += 'ID' + nonStoryChildTaskIdList[j].toString() + ', '; } @@ -102,8 +100,7 @@ window.clickSubmit = async function(e) if(confirmID.endsWith(', ')) confirmID = confirmID.slice(0, -2); - let confirmTip = tipAll ? syncStoryToAllChildrenTip : syncStoryToChildrenTip; - confirmTip = confirmTip.replace('%s', confirmID); + let confirmTip = syncStoryToChildrenTip.replace('%s', confirmID); zui.Modal.confirm(confirmTip).then((res) => { $taskBatchForm.find('[name=syncChildren]').remove(); @@ -166,7 +163,7 @@ function checkBatchEstStartedAndDeadline(event) if(field == 'estStarted') { let parentEstStarted = typeof tasks[parentID] == 'undefined' || $(event.target).closest('tbody').find('[name="estStarted[' + parentID + ']"]').length == 0 ? parentTask.estStarted : $(event.target).closest('tbody').find('[name="estStarted[' + parentID + ']"]').val(); - if(estStarted.length > 0 && estStarted < parentEstStarted) + if(estStarted.length > 0 && parentEstStarted.length > 0 && estStarted < parentEstStarted) { const $estStartedTd = $currentRow.find('td[data-name=estStarted]'); if($estStartedTd.find('.date-tip').length == 0 || $estStartedTd.find('.date-tip .form-tip').length > 0) @@ -184,7 +181,7 @@ function checkBatchEstStartedAndDeadline(event) if(field == 'deadline') { let parentDeadline = typeof tasks[parentID] == 'undefined' || $(event.target).closest('tbody').find('[name="deadline[' + parentID + ']"]').length == 0 ? parentTask.deadline : $(event.target).closest('tbody').find('[name="deadline[' + parentID + ']"]').val(); - if(deadline.length > 0 && deadline > parentDeadline) + if(deadline.length > 0 && parentDeadline.length > 0 && deadline > parentDeadline) { const $deadlineTd = $currentRow.find('td[data-name=deadline]'); if($deadlineTd.find('.date-tip').length == 0 || $deadlineTd.find('.date-tip .form-tip').length > 0) diff --git a/module/task/js/edit.ui.js b/module/task/js/edit.ui.js index fac713b184..1cf0e5686c 100644 --- a/module/task/js/edit.ui.js +++ b/module/task/js/edit.ui.js @@ -408,7 +408,7 @@ function checkEstStartedAndDeadline(event) const $deadline = $form.find('[name=deadline]'); const deadline = $deadline.val(); - if(field == 'estStarted' && estStarted.length > 0 && estStarted < parentEstStarted) + if(field == 'estStarted' && estStarted.length > 0 && parentEstStarted.length > 0&& estStarted < parentEstStarted) { const $estStartedDiv = $estStarted.closest('.form-group'); if($estStartedDiv.find('.date-tip').length == 0 || $estStartedDiv.find('.date-tip .form-tip').length > 0) @@ -422,7 +422,7 @@ function checkEstStartedAndDeadline(event) } } - if(field == 'deadline' && deadline.length > 0 && deadline > parentDeadline) + if(field == 'deadline' && deadline.length > 0 && parentDeadline.length > 0 && deadline > parentDeadline) { const $deadlineDiv = $deadline.closest('.form-group'); if($deadlineDiv.find('.date-tip').length == 0 || $deadlineDiv.find('.date-tip .form-tip').length > 0) diff --git a/module/task/lang/de.php b/module/task/lang/de.php index 4cf016c3b4..1c52bd6077 100644 --- a/module/task/lang/de.php +++ b/module/task/lang/de.php @@ -286,7 +286,6 @@ $lang->task->cannotDeleteParent = 'Cannot delete parent task'; $lang->task->addChildTask = 'Because the task has already consumed consumption, to ensure data consistency, we will help you create a subtask with the same name to record the consumption.'; $lang->task->selectTestStoryTip = "The following {$lang->SRCommon} will be subtasks of this task"; $lang->task->effortOperateTips = 'Only the project manager, the executive supervisor, and the department head have the authority to %s logs belonging to others.'; -$lang->task->syncStoryToAllChildrenTip = "Will be {$lang->SRCommon} synchronised with all child tasks?"; $lang->task->syncStoryToChildrenTip = "Child tasks of %s do not have {$lang->SRCommon}, will {$lang->SRCommon} be synchronised with these child tasks?"; $lang->task->error = new stdclass(); diff --git a/module/task/lang/en.php b/module/task/lang/en.php index a59de06763..7e4e827692 100755 --- a/module/task/lang/en.php +++ b/module/task/lang/en.php @@ -286,7 +286,6 @@ $lang->task->cannotDeleteParent = 'Cannot delete parent task'; $lang->task->addChildTask = 'Because the task has cost hours, ZenTao will create a child task with the same name to record the cost housrs to ensure data consistency.'; $lang->task->selectTestStoryTip = "The following {$lang->SRCommon} will be subtasks of this task"; $lang->task->effortOperateTips = 'Only the project manager, the executive supervisor, and the department head have the authority to %s logs belonging to others.'; -$lang->task->syncStoryToAllChildrenTip = "Will be {$lang->SRCommon} synchronised with all child tasks?"; $lang->task->syncStoryToChildrenTip = "Child tasks of %s do not have {$lang->SRCommon}, will {$lang->SRCommon} be synchronised with these child tasks?"; $lang->task->error = new stdclass(); diff --git a/module/task/lang/fr.php b/module/task/lang/fr.php index 5f51abe0ca..d7c78079d1 100644 --- a/module/task/lang/fr.php +++ b/module/task/lang/fr.php @@ -286,7 +286,6 @@ $lang->task->cannotDeleteParent = 'Impossible de supprimer la tâche pare $lang->task->addChildTask = 'Because the task has already consumed consumption, to ensure data consistency, we will help you create a subtask with the same name to record the consumption.'; $lang->task->selectTestStoryTip = "The following {$lang->SRCommon} will be subtasks of this task"; $lang->task->effortOperateTips = 'Only the project manager, the executive supervisor, and the department head have the authority to %s logs belonging to others.'; -$lang->task->syncStoryToAllChildrenTip = "Will be {$lang->SRCommon} synchronised with all child tasks?"; $lang->task->syncStoryToChildrenTip = "Child tasks of %s do not have {$lang->SRCommon}, will {$lang->SRCommon} be synchronised with these child tasks?"; $lang->task->error = new stdclass(); diff --git a/module/task/lang/zh-cn.php b/module/task/lang/zh-cn.php index dd3e61d807..b43ff1b987 100755 --- a/module/task/lang/zh-cn.php +++ b/module/task/lang/zh-cn.php @@ -286,7 +286,6 @@ $lang->task->cannotDeleteParent = '不能删除父任务。'; $lang->task->addChildTask = '因该任务已经产生消耗,为保证数据一致性,我们会帮您创建一条同名子任务记录该消耗。'; $lang->task->selectTestStoryTip = "以下{$lang->SRCommon}将作为此任务的子任务"; $lang->task->effortOperateTips = '只有项目负责人、执行负责人、所属部门负责人才能%s他人日志。'; -$lang->task->syncStoryToAllChildrenTip = "是否同步更新所有子任务的相关{$lang->SRCommon}?"; $lang->task->syncStoryToChildrenTip = "子任务%s没有关联{$lang->SRCommon},是否同步更新这些子任务的相关{$lang->SRCommon}?"; $lang->task->error = new stdclass(); @@ -416,5 +415,5 @@ $lang->taskestimate->consumed = '工时'; $lang->task->overEsStartDate = '已超出%s计划开始时间,请先修改%s计划开始时间'; $lang->task->overEsEndDate = '已超出%s计划结束时间,请先修改%s计划结束时间'; -$lang->task->overParentEsStarted = '任务预计开始日期小于父任务预计开始日期:%s'; -$lang->task->overParentDeadline = '任务截止日期大于父任务截止日期:%s'; +$lang->task->overParentEsStarted = '任务的预计开始日期小于了父任务的预计开始日期:%s'; +$lang->task->overParentDeadline = '任务的截止日期大于了父任务的截止日期:%s'; diff --git a/module/task/ui/batchedit.html.php b/module/task/ui/batchedit.html.php index 71a3d9e89a..5eeebd922f 100644 --- a/module/task/ui/batchedit.html.php +++ b/module/task/ui/batchedit.html.php @@ -22,7 +22,6 @@ jsVar('childTasks', $childTasks); jsVar('nonStoryChildTasks', $nonStoryChildTasks); jsVar('tasks', $tasks); jsVar('stories', $stories); -jsVar('syncStoryToAllChildrenTip', $lang->task->syncStoryToAllChildrenTip); jsVar('syncStoryToChildrenTip', $lang->task->syncStoryToChildrenTip); jsVar('parentTasks', $parentTasks); jsVar('ignoreLang', $lang->project->ignore); @@ -81,7 +80,7 @@ formBatchPanel set::label($lang->task->story), set::control('picker'), set::items(array()), - set::width('200px'), + set::width('300px'), set::ditto(true), set::defaultDitto('off') ), diff --git a/module/task/ui/edit.html.php b/module/task/ui/edit.html.php index ad66a5358a..3377f0eddd 100644 --- a/module/task/ui/edit.html.php +++ b/module/task/ui/edit.html.php @@ -34,14 +34,11 @@ jsVar('requiredFields', $config->task->edit->requiredFields); jsVar('parentEstStarted', !empty($parentTask) ? $parentTask->estStarted : ''); jsVar('parentDeadline', !empty($parentTask) ? $parentTask->deadline : ''); jsVar('ignoreLang', $lang->project->ignore); -jsVar('overParentEstStartedLang', isset($parentTask) ? sprintf($lang->task->overParentEsStarted, $parentTask->estStarted) : ''); -jsVar('overParentDeadlineLang', isset($parentTask) ? sprintf($lang->task->overParentDeadline, $parentTask->deadline) : ''); +jsVar('overParentEstStartedLang', !empty($parentTask) ? sprintf($lang->task->overParentEsStarted, $parentTask->estStarted) : ''); +jsVar('overParentDeadlineLang', !empty($parentTask) ? sprintf($lang->task->overParentDeadline, $parentTask->deadline) : ''); $confirmSyncTip = ''; -if(!empty($syncChildren) && !empty($task->children)) -{ - $confirmSyncTip = count($syncChildren) == count($task->children) ? $lang->task->syncStoryToAllChildrenTip : sprintf($lang->task->syncStoryToChildrenTip, 'ID' . implode(', ID', $syncChildren)); -} +if(!empty($syncChildren) && !empty($task->children)) $confirmSyncTip = sprintf($lang->task->syncStoryToChildrenTip, 'ID' . implode(', ID', $syncChildren)); jsVar('confirmSyncTip', $confirmSyncTip); jsVar('taskID', $task->id); jsVar('taskStory', $task->story); diff --git a/module/testcase/test/lib/testcase.ui.class.php b/module/testcase/test/lib/testcase.ui.class.php index 603d3aa457..2990faabb6 100644 --- a/module/testcase/test/lib/testcase.ui.class.php +++ b/module/testcase/test/lib/testcase.ui.class.php @@ -4,84 +4,25 @@ use Facebook\WebDriver\WebDriverBy; include dirname(__FILE__, 5) . '/test/lib/ui.php'; class testcase extends tester { - public function createTestCase($project = array(), $testcase = array()) + /** + * 添加测试用例 + * create testcase + * + * @param array $product + * @param array $testcase + * @access public + * @return object + */ + public function createTestCase($product = array(), $testcase = array()) { $this->login(); - $form = $this->initForm('testcase', 'create', $project, 'appIframe-qa'); + $form = $this->initForm('testcase', 'create', $product, 'appIframe-qa'); if(isset($testcase['caseName'])) $form->dom->title->setValue($testcase['caseName']); if(isset($testcase['type'])) $form->dom->type->picker($testcase['type']); if(isset($testcase['stage'])) $form->dom->{'stage[]'}->multiPicker($testcase['stage']); if(isset($testcase['pri'])) $form->dom->pri->picker($testcase['pri']); if(isset($testcase['prediction'])) $form->dom->prediction->setValue($testcase['prediction']); - if(isset($testcase['steps'])) - { - $parentGroup = 0; - /* 遍历$testcase['steps']数组,将该数组内的键值对,作为用例的步骤和预期 */ - foreach($testcase['steps'] as $parentSteps => $parentExpects) - { - $parentGroup++; - /* 判断如果遍历次数>=3时,点击一下创建步骤按钮 */ - if($parentGroup >= 3) - { - $parentSibButton = "//textarea[@name = 'steps[$parentGroup]']/../..//button[@data-action='sib']/i"; - $parentSibButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($parentSibButton)); - $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView();", [$parentSibButtonXpath]); - $this->page->webdriver->driver->executeScript("arguments[0].click();", [$parentSibButtonXpath]); - } - /* 如果$parentExpects不是数组,则视将该键值对视为一级步骤预期 */ - if(!is_array($parentExpects)) - { - $form->dom->{"steps[$parentGroup]"}->setValue($parentSteps); - $form->dom->{"expects[$parentGroup]"}->setValue($parentExpects); - } - else - { - $group = 0; - $form->dom->{"steps[$parentGroup]"}->setValue($parentSteps); - $subButton = "//textarea[@name = 'steps[$parentGroup]']/../..//button[@data-action='sub']/i"; - $subButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($subButton)); - $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView();", [$subButtonXpath]); - $this->page->webdriver->driver->executeScript("arguments[0].click();", [$subButtonXpath]); - /* 如果$parentExpexts是数组,则继续遍历该数组,并将遍历的键值对作为二级预期步骤 */ - foreach($parentExpects as $steps => $expects) - { - $group++; - $this->webdriver->wait(1); - $sibButton = "//textarea[@name = 'steps[$parentGroup.$group]']/../..//button[@data-action='sib']/i"; - $sibButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($sibButton)); - $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView();", [$sibButtonXpath]); - $this->page->webdriver->driver->executeScript("arguments[0].click();", [$sibButtonXpath]); - /* 如果$expects不是数组,则视将该键值对视为二级步骤预期 */ - if(!is_array($expects)) - { - $form->dom->{"steps[$parentGroup.$group]"}->setValue($steps); - $form->dom->{"expects[$parentGroup.$group]"}->setValue($expects); - } - else - { - $sonGroup = 0; - $form->dom->{"steps[$parentGroup.$group]"}->setValue($steps); - $subButton = "//textarea[@name = 'steps[$parentGroup.$group]']/../..//button[@data-action='sub']/i"; - $subButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($subButton)); - $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView();", [$subButtonXpath]); - $this->page->webdriver->driver->executeScript("arguments[0].click();", [$subButtonXpath]); - /* 如果$expexts是数组,则继续遍历该数组,并将遍历的键值对作为三级预期步骤 */ - foreach($expects as $sonSteps => $sonExpects) - { - $sonGroup++; - $sonSibButton = "//textarea[@name = 'steps[$parentGroup.$group.$sonGroup]']/../..//button[@data-action='sib']/i"; - $sonSibButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($sonSibButton)); - $form->dom->{"steps[$parentGroup.$group.$sonGroup]"}->setValue($sonSteps); - $form->dom->{"expects[$parentGroup.$group.$sonGroup]"}->setValue($sonExpects); - $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView()", [$sonSibButtonXpath]); - $this->page->webdriver->driver->executeScript("arguments[0].click()", [$sonSibButtonXpath]); - $this->webdriver->wait(1); - } - } - } - } - } - } + if(isset($testcase['steps'])) $this->fillInCaseSteps($form, $testcase); $form->dom->saveButton->click(); $this->webdriver->wait(2); @@ -95,6 +36,201 @@ class testcase extends tester * 测试用例列表。 * check testcase list. * + * @param array $product + * @access public + * @return object + */ + public function testcaseBrowse($product) + { + $this->login(); + $form = $this->initForm('testcase', 'browse', $product, 'appIframe-qa'); + if($form->dom->caseListID->getText()) return $this->success('测试用例列表验证成功'); + return $this->failed('测试用例列表验证失败'); + } + + /** + * 编辑测试用例。 + * edit testcase. + * + * @param array $url + * @param array $testcase + * @access public + * @return object + */ + public function editTestCase($url, $testcase) + { + $this->login(); + $form = $this->initForm('testcase', 'edit', $url, 'appIframe-qa'); + if(isset($testcase['caseName'])) $form->dom->title->setValue($testcase['caseName']); + if(isset($testcase['type'])) $form->dom->type->picker($testcase['type']); + if(isset($testcase['stage'])) $form->dom->{'stage[]'}->multiPicker($testcase['stage']); + if(isset($testcase['pri'])) $form->dom->pri->picker($testcase['pri']); + if(isset($testcase['prediction'])) $form->dom->prediction->setValue($testcase['prediction']); + if(isset($testcase['steps'])) $this->fillInCaseSteps($form, $testcase); + $form->dom->saveButton->click(); + $this->webdriver->wait(3); + if($form->dom->caseName->getText() == $testcase['caseName']) return $this->success('编辑测试用例成功'); + return $this->failed('编辑测试用例失败'); + } + + /** + * 测试用例详情。 + * testcase view. + * + * @param array $product + * @access public + * @return object + */ + public function testcaseView($product) + { + $this->login(); + $form = $this->initForm('testcase', 'browse', $product, 'appIframe-qa'); + $caseName = $form->dom->caseName->getText(); + $form->dom->caseName->click(); + $this->webdriver->wait(1); + if($this->response('method') == 'view' && $form->dom->caseNameView->getText() == $caseName) return $this->success('测试用例详情页验证成功'); + return $this->failed('测试用例详情页验证失败'); + } + + /** + * 批量创建测试用例。 + * batch create testcase. + * + * @param array $product + * @access public + * @return object + */ + public function batchCreate($product, $testcase) + { + $this->login(); + $form = $this->initForm('testcase', 'batchCreate', $product, 'appIframe-qa'); + if(isset($testcase['caseName'])) + { + $count = 0; + foreach($testcase['caseName'] as $caseName) + { + $count++; + $form->dom->{"title[$count]"}->setValue($caseName); + } + } + $form->dom->saveButton->click(); + $this->webdriver->wait(2); + + $caseList = array_map(function($element){return $element->getText();}, $form->dom->getElementList($form->dom->xpath['caseNameList'])->element); + if(!in_array($testcase['caseName'], $caseList)) return $this->success('批量创建测试用例成功'); + return $this->failed('批量创建测试用例成功'); + } + + /** + * 批量创建测试用例。 + * batch create testcase. + * + * @param array $product + * @param array $testcase + * @access public + * @return object + */ + public function testcaseReview($product, $testcase) + { + $this->login(); + $form = $this->initForm('testcase', 'browse', $product, 'appIframe-qa'); + $form->dom->review->click(); + $this->webdriver->wait(1); + + if(isset($testcase['reviewedDate'])) $form->dom->reviewedDate->datePicker($testcase['reviewedDate']); + if(isset($testcase['result'])) $form->dom->result->picker($testcase['result']); + if(isset($testcase['reviewedBy'])) $form->dom->{'reviewedBy[]'}->multiPicker($testcase['reviewedBy']); + if(isset($testcase['comment'])) $form->dom->comment->setValueInZenEditor($testcase['comment']); + $form->dom->btn($this->lang->projectreview->common)->click(); + $this->webdriver->wait(1); + + if($form->dom->review->attr('href')) return $this->success('测试用例评审通过'); + return $this->failed('测试用例评审失败'); + } + + /** + * 添加测试用例步骤 + * fill in case steps + * + * @param object $form + * @param array $testcase + * @access public + * @return void + */ + public function fillInCaseSteps($form, $testcase) + { + $parentGroup = 0; + /* 遍历$testcase['steps']数组,将该数组内的键值对,作为用例的步骤和预期 */ + foreach($testcase['steps'] as $parentSteps => $parentExpects) + { + $parentGroup++; + /* 判断如果遍历次数>=3时,点击一下创建步骤按钮 */ + if($parentGroup >= 3) + { + $parentSibButton = "//textarea[@name = 'steps[$parentGroup]']/../..//button[@data-action='sib']/i"; + $parentSibButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($parentSibButton)); + $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView();", [$parentSibButtonXpath]); + $this->page->webdriver->driver->executeScript("arguments[0].click();", [$parentSibButtonXpath]); + } + /* 如果$parentExpects不是数组,则视将该键值对视为一级步骤预期 */ + if(!is_array($parentExpects)) + { + $form->dom->{"steps[$parentGroup]"}->setValue($parentSteps); + $form->dom->{"expects[$parentGroup]"}->setValue($parentExpects); + } + else + { + $group = 0; + $form->dom->{"steps[$parentGroup]"}->setValue($parentSteps); + $subButton = "//textarea[@name = 'steps[$parentGroup]']/../..//button[@data-action='sub']/i"; + $subButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($subButton)); + $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView();", [$subButtonXpath]); + $this->page->webdriver->driver->executeScript("arguments[0].click();", [$subButtonXpath]); + /* 如果$parentExpexts是数组,则继续遍历该数组,并将遍历的键值对作为二级预期步骤 */ + foreach($parentExpects as $steps => $expects) + { + $group++; + $this->webdriver->wait(1); + $sibButton = "//textarea[@name = 'steps[$parentGroup.$group]']/../..//button[@data-action='sib']/i"; + $sibButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($sibButton)); + $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView();", [$sibButtonXpath]); + $this->page->webdriver->driver->executeScript("arguments[0].click();", [$sibButtonXpath]); + /* 如果$expects不是数组,则视将该键值对视为二级步骤预期 */ + if(!is_array($expects)) + { + $form->dom->{"steps[$parentGroup.$group]"}->setValue($steps); + $form->dom->{"expects[$parentGroup.$group]"}->setValue($expects); + } + else + { + $sonGroup = 0; + $form->dom->{"steps[$parentGroup.$group]"}->setValue($steps); + $subButton = "//textarea[@name = 'steps[$parentGroup.$group]']/../..//button[@data-action='sub']/i"; + $subButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($subButton)); + $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView();", [$subButtonXpath]); + $this->page->webdriver->driver->executeScript("arguments[0].click();", [$subButtonXpath]); + /* 如果$expexts是数组,则继续遍历该数组,并将遍历的键值对作为三级预期步骤 */ + foreach($expects as $sonSteps => $sonExpects) + { + $sonGroup++; + $sonSibButton = "//textarea[@name = 'steps[$parentGroup.$group.$sonGroup]']/../..//button[@data-action='sib']/i"; + $sonSibButtonXpath = $this->page->webdriver->driver->findElement(WebDriverBy::xpath($sonSibButton)); + $form->dom->{"steps[$parentGroup.$group.$sonGroup]"}->setValue($sonSteps); + $form->dom->{"expects[$parentGroup.$group.$sonGroup]"}->setValue($sonExpects); + $this->page->webdriver->driver->executeScript("arguments[0].scrollIntoView()", [$sonSibButtonXpath]); + $this->page->webdriver->driver->executeScript("arguments[0].click()", [$sonSibButtonXpath]); + $this->webdriver->wait(1); + } + } + } + } + } + } + + /** + * 测试用例列表。 + * check testcase list. + * * @param array $project * @access public * @return object diff --git a/module/testcase/test/ui/batchcreatetestcase.php b/module/testcase/test/ui/batchcreatetestcase.php new file mode 100755 index 0000000000..3d0ddd8f09 --- /dev/null +++ b/module/testcase/test/ui/batchcreatetestcase.php @@ -0,0 +1,34 @@ +#!/usr/bin/env php +loadYaml('product')->gen(1); +$tester = new testcase(); + +$testcase = array( + 'caseName' => array('testcase1' . time(), 'testcase2' . time(), 'testcase3' . time()), + 'type' => '安装部署', + 'stage' => array('单元测试阶段', '功能测试阶段', '集成测试阶段', '系统测试阶段', '冒烟测试阶段', '版本验证阶段'), + 'pri' => '2', + 'precondition' => '前置条件测试', +); + +$product = array( + 'productID' => 1, + 'branch' => 0, + 'moduleID' => 0 +); + +r($tester->batchCreate($product, $testcase)) && p('message,status') && e('批量创建测试用例成功 ,SUCCESS'); //验证批量创建测试用例成功 diff --git a/module/testcase/test/ui/createtestcase.php b/module/testcase/test/ui/createtestcase.php index 226c9b2ab1..4f6b68536d 100755 --- a/module/testcase/test/ui/createtestcase.php +++ b/module/testcase/test/ui/createtestcase.php @@ -39,10 +39,10 @@ $testcase = array( ) ); -$project = array( +$product = array( 'productID' => 1, 'branch' => 0, 'moduleID' => 0 ); -r($tester->createTestCase($project, $testcase)) && p('message,status') && e('创建多层级测试用例成功,SUCCESS'); //验证创建多层级测试用例 +r($tester->createTestCase($product, $testcase)) && p('message,status') && e('创建多层级测试用例成功,SUCCESS'); //验证创建多层级测试用例 diff --git a/module/testcase/test/ui/edittestcase.php b/module/testcase/test/ui/edittestcase.php new file mode 100755 index 0000000000..74c06bc86a --- /dev/null +++ b/module/testcase/test/ui/edittestcase.php @@ -0,0 +1,77 @@ +#!/usr/bin/env php +id->range('1'); +$product->program->range('0'); +$product->name->range('产品1'); +$product->shadow->range('0'); +$product->bind->range('0'); +$product->acl->range('open'); +$product->createdBy->range('admin'); +$product->vision->range('rnd'); +$product->gen(1); + +$story = zenData('story'); +$story->id->range('1-2'); +$story->root->range('1-2'); +$story->path->range('`,1,`, `,2,`'); +$story->grade->range('1'); +$story->product->range('1'); +$story->module->range('0'); +$story->title->range('激活研发需求,激活用户需求,激活业务需求'); +$story->type->range('story,requirement,epic'); +$story->stage->range('wait'); +$story->status->range('active'); +$story->openedBy->range('admin'); +$story->version->range('1'); +$story->assignedTo->range('[]'); +$story->reviewedBy->range('[]'); +$story->reviewedDate->range('`NULL`'); +$story->closedBy->range('[]'); +$story->closedReason->range('[]'); +$story->gen(2); + +zenData('case')->loadYaml('case')->gen(1); + +$tester = new testcase(); + +$url = array( + 'caseID' => 1, + 'comment' => 'false', + 'excecutionID' => 0 +); + +$testcase = array( + 'caseName' => 'testcase' . time(), + 'type' => '安装部署', + 'stage' => array('单元测试阶段', '功能测试阶段', '集成测试阶段', '系统测试阶段', '冒烟测试阶段', '版本验证阶段'), + 'pri' => '2', + 'precondition' => '前置条件测试', + 'steps' => array( + 'step1' => 'expect1', + 'step2' => 'expect2', + 'step3' => 'expect3', + 'step4' => 'expect4', + 'step5' => 'expect5', + 'step6' => 'expect6', + 'group7' => array('step7.1' => 'expect7.1', 'step7.2' => 'expect7.2', 'step7.3' => 'expect7.3', 'step7.4' => 'expect7.4', 'step7.5' => 'expect7.5'), + 'group8' => array('step8.1' => 'expect8.1', 'step8.2' => 'expect8.2', 'step8.3' => 'expect8.3', 'step8.4' => 'expect8.4', 'step8.5' => 'expect8.5'), + 'group9' => array('step9.1' => 'expect9.1', 'step9.2' => 'expect9.2', 'step9.3' => 'expect9.3', 'step9.4' => 'expect9.4', 'step9.5' => 'expect9.5'), + 'group10' => array('group10.1' => array('step10.1.1' => 'expect10.1.1', 'step10.1.2' => 'expect10.1.2', 'step10.1.3' => 'expect10.1.3', 'step10.1.4' => 'expect10.1.4'), 'step10.2' => 'expect10.2'), + 'step11' => 'expect11', + 'group12' => array('group12.1' => array('step12.1.1' => 'expect12.1.1', 'step12.1.2' => 'expect12.1.2', 'step12.1.3' => 'expect12.1.3', 'step12.1.4' => 'expect12.1.4'), 'step12.2' => 'expect12.2'),)); +r($tester->editTestCase($url, $testcase)) && p('message,status') && e('编辑测试用例成功,SUCCESS'); //验证编辑测试用例 \ No newline at end of file diff --git a/module/testcase/test/ui/page/batchcreate.php b/module/testcase/test/ui/page/batchcreate.php new file mode 100644 index 0000000000..bcc86e87cf --- /dev/null +++ b/module/testcase/test/ui/page/batchcreate.php @@ -0,0 +1,14 @@ + "//div[@id='table-testcase-browse_table']//div[@data-col='title']//a[@data-app='qa']", + 'saveButton' => "//button[@type = 'submit']" + ); + + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/testcase/test/ui/page/browse.php b/module/testcase/test/ui/page/browse.php index b049a5bd09..8acd07f579 100755 --- a/module/testcase/test/ui/page/browse.php +++ b/module/testcase/test/ui/page/browse.php @@ -5,7 +5,11 @@ class browsePage extends page { parent::__construct($webdriver); $xpath = array( - 'caseListID' => "//a[text()='ID']" + 'caseListID' => "//a[text()='ID']", + 'caseName' => "(//div[@data-col='title']//a[@data-app='qa'])[1]", + 'caseNameView' => "(//div[@id='mainContent']//span[text()])[3]", + 'review' => "(//div[@data-row='case_1']//a[@data-toggle='modal'])[1]", + 'comment' => "//zen-editor[@menubar-mode='compact']" ); $this->dom->xpath = array_merge($this->dom->xpath, $xpath); diff --git a/module/testcase/test/ui/page/edit.php b/module/testcase/test/ui/page/edit.php new file mode 100644 index 0000000000..5ddd7513c5 --- /dev/null +++ b/module/testcase/test/ui/page/edit.php @@ -0,0 +1,14 @@ + "(//div[@id='mainContent']//span[text()])[3]", + 'saveButton' => "//button[@type = 'submit']" + ); + + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/testcase/test/ui/page/view.php b/module/testcase/test/ui/page/view.php new file mode 100755 index 0000000000..7f904bed61 --- /dev/null +++ b/module/testcase/test/ui/page/view.php @@ -0,0 +1,13 @@ + "(//div[@id='mainContent']//span[text()])[3]" + ); + + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/testcase/test/ui/testcasebrowse.php b/module/testcase/test/ui/testcasebrowse.php index f8044750f9..39d2d6ffbc 100755 --- a/module/testcase/test/ui/testcasebrowse.php +++ b/module/testcase/test/ui/testcasebrowse.php @@ -18,8 +18,8 @@ $data1 = zenData('product')->loadYaml('product')->gen(1); $data2 = zenData('case')->loadYaml('case')->gen(1); $tester = new testcase(); -$project = array( +$product = array( 'productID' => 1, ); -r($tester->testcaseBrowse($project)) && p('message,status') && e('测试用例列表验证成功,SUCCESS'); //测试用例列表验证成功 \ No newline at end of file +r($tester->testcaseBrowse($product)) && p('message,status') && e('测试用例列表验证成功,SUCCESS'); //测试用例列表验证成功 diff --git a/module/testcase/test/ui/testcasereview.php b/module/testcase/test/ui/testcasereview.php new file mode 100755 index 0000000000..8705f0329b --- /dev/null +++ b/module/testcase/test/ui/testcasereview.php @@ -0,0 +1,38 @@ +#!/usr/bin/env php +loadYaml('product')->gen(1); +$story = zenData('story'); +$story->id->setFields(array(array('range' => '2'))); +$story->version->setFields(array(array('range' => '1'))); +$story->gen(1); +/* 修改zt_config表中控制用例评审的开关。value值为'1'则开启,'0'则关闭。 */ +zenData('setting')->dao->update(TABLE_CONFIG)->set('value')->eq('1')->where('module')->eq('testcase')->andWhere('`key`')->eq('needReview')->exec(); +zenData('case')->loadYaml('case')->gen(1); + +$tester = new testcase(); + +$product = array( + 'productID' => 1, +); +$testcase = array( + 'reviewedDate' => '2025-09-09', + 'result' => '确认通过', + 'reviewedBy' => array('admin', '研发'), + 'comment' => '备注一下' +); + +r($tester->testcaseReview($product, $testcase)) && p('message,status') && e('测试用例评审通过,SUCCESS'); //测试用例评审通过。 \ No newline at end of file diff --git a/module/testcase/test/ui/testcaseview.php b/module/testcase/test/ui/testcaseview.php new file mode 100755 index 0000000000..5bc56a495a --- /dev/null +++ b/module/testcase/test/ui/testcaseview.php @@ -0,0 +1,25 @@ +#!/usr/bin/env php +loadYaml('product')->gen(1); +zenData('case')->loadYaml('case')->gen(1); +$tester = new testcase(); + +$product = array( + 'productID' => 1, +); + +r($tester->testcaseView($product)) && p('message,status') && e('测试用例详情页验证成功,SUCCESS'); //测试用例详情验证 \ No newline at end of file diff --git a/module/testtask/model.php b/module/testtask/model.php index 102c159263..12443ba3cd 100755 --- a/module/testtask/model.php +++ b/module/testtask/model.php @@ -107,7 +107,7 @@ class testtaskModel extends model { $begin = ''; $end = ''; - $beginAndEnd = $this->loadModel('action')->computeBeginAndEnd($browseType); + $beginAndEnd = $this->loadModel('action')->computeBeginAndEnd($browseType, '', 'next'); if($browseType != 'all' and $browseType != 'newest' and !empty($beginAndEnd)) { $begin = $beginAndEnd['begin']; diff --git a/module/testtask/ui/runcase.html.php b/module/testtask/ui/runcase.html.php index 5f11fae4a9..eb74178347 100644 --- a/module/testtask/ui/runcase.html.php +++ b/module/testtask/ui/runcase.html.php @@ -235,7 +235,7 @@ if($confirm != 'yes') ), h::td ( - nl2br(zget($run->case, 'precondition', '')), + html(nl2br(zget($run->case, 'precondition', ''))), ) ) ) : ''; diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 71edd759a9..d0a5226507 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -7132,9 +7132,11 @@ class upgradeModel extends model public function processDashboard() { $dashboards = $this->dao->select('*')->from(TABLE_DASHBOARD)->fetchAll(); + $id = 1002; foreach($dashboards as $dashboard) { $screen = new stdclass(); + $screen->id = $id; $screen->name = $dashboard->name; $screen->dimension = 1; $screen->desc = $dashboard->desc; @@ -7144,6 +7146,7 @@ class upgradeModel extends model $screen->createdBy = $dashboard->createdBy; $screen->createdDate = $dashboard->createdDate; $this->dao->insert(TABLE_SCREEN)->data($screen)->exec(); + $id++; } $this->dao->exec("ALTER TABLE " . TABLE_CHART . " DROP `dataset`"); @@ -7179,7 +7182,7 @@ class upgradeModel extends model $component->attr->h = round(54 * $option->h); $type = !empty($option->i->type) ? $option->i->type : 'chart'; - $chart = $this->loadModel($type)->getByID($option->i->id); + $chart = $this->loadModel($type)->getByID($option->i->id, false, 'published', false); if($chart) { @@ -7210,7 +7213,6 @@ class upgradeModel extends model $chart->langs = json_encode($chart->langs); $chartFilters = !empty($chart->filters) ? json_decode($chart->filters, true) : array(); - $component = $this->screen->getChartOption($chart, $component, $chartFilters); } $componentList[] = $component; diff --git a/module/weekly/model.php b/module/weekly/model.php index e3c9bde249..b3fb215bad 100755 --- a/module/weekly/model.php +++ b/module/weekly/model.php @@ -566,9 +566,9 @@ class weeklyModel extends model $data = new stdclass(); $PVEV = $this->getPVEV($projectID, $date); - $data->pv = helper::formatHours($PVEV['PV']); - $data->ev = helper::formatHours($PVEV['EV']); - $data->ac = helper::formatHours($this->getAC($projectID, $date)); + $data->pv = (float) helper::formatHours($PVEV['PV']); + $data->ev = (float) helper::formatHours($PVEV['EV']); + $data->ac = (float) helper::formatHours($this->getAC($projectID, $date)); $data->sv = $this->getSV($data->ev, $data->pv); $data->cv = $this->getCV($data->ev, $data->ac); diff --git a/www/dev.php b/www/dev.php index 370aced5f2..7cc20f86f3 100644 --- a/www/dev.php +++ b/www/dev.php @@ -14,7 +14,7 @@
-

Perf guard data

+

Zentao Profiles

@@ -88,6 +88,7 @@ let autoRefreshTimer = 0; let autoRefreshInterval = 10000; const $table = $('#table'); const metricsLevelNames = ['', 'BLOCK', 'WARN', 'PASS']; +const metricsStats = {warning: 0, danger: 0}; function getQueryUrl() { @@ -129,31 +130,31 @@ function initTable(data) {name: 'metricsLevel', title: 'LEVEL', hint: true, width: 60, fixed: 'left', cellClass: 'font-mono text-sm select-all', map: metricsLevelNames, hint: info => info.row.data.requestId, flex: 0, align: 'center', sort: 'number', border: 'right'}, {name: 'identifier', type: Object.keys(window.userMap || {}).length ? 'avatarName' : '', title: 'User', fixed: 'left', flex: 0, width: 80, sort: true, avatarKey: 'identifier_avatar', avatarCodeKey: 'identifier_avatar_code', avatarNameKey: 'identifier_name'}, {name: 'path', type: 'title', title: 'Page', hint: (info) => info.row.data.request.url, link: (info) => ({url: info.row.data.request.url, target: '_blank'}), flex: 1, fixed: 'left', maxWidth: 1000, sort: true}, - {name: 'metrics.backend.totalTime', title: 'Back Time', sort: 'number', type: 'number', digits: 2, format: '{0}ms', digits: 3, headerGroup: 'Backend', hint: info => + {name: 'metrics.backend.totalTime', title: 'Back Time', sort: 'number', digits: 1, format: '{0}ms', headerGroup: 'Backend', align: 'right', hint: info => { const timeClass = info.row.data['metrics.backend.totalTimeClass']; if(timeClass === 'danger') return `BLOCK: > 500ms`; if(timeClass === 'warning') return `WARN: > 300ms`; }}, - {name: 'metrics.backend.sqlTime', title: 'SQL Time', sort: 'number', format: '{0}ms', digits: 3, headerGroup: 'Backend', hint: info => + {name: 'metrics.backend.sqlTime', title: 'SQL Time', sort: 'number', format: '{0}ms', digits: 1, headerGroup: 'Backend', align: 'right', hint: info => { const timeClass = info.row.data['metrics.backend.sqlTimeClass']; if(timeClass === 'danger') return `BLOCK: > 300ms`; if(timeClass === 'warning') return `WARN: > 200ms`; }}, - {name: 'metrics.backend.sqlCount', title: 'SQLs', sort: 'number', width: 48, link: '#', hint: 'Click to check details', headerGroup: 'Backend'}, - {name: 'metrics.backend.requestMemory', title: 'Memory', sort: 'number', format: (value) => value ? zui.formatBytes(value) : '', headerGroup: 'Backend'}, + {name: 'metrics.backend.sqlCount', title: 'SQLs', sort: 'number', width: 48, link: '#', hint: 'Click to check details', headerGroup: 'Backend', align: 'center'}, + {name: 'metrics.backend.requestMemory', title: 'Memory', sort: 'number', format: (value) => value ? zui.formatBytes(value) : '', headerGroup: 'Backend', align: 'right'}, {name: 'metrics.backend.phpFileLoaded', title: 'PHP Files', width: 50, sort: 'number', align: 'center', headerGroup: 'Backend'}, - {name: 'metrics.frontend.downloadSize', title: 'Transfer Size', sort: 'number', format: (value) => value ? zui.formatBytes(value) : '', headerGroup: 'Frontend'}, - {name: 'metrics.frontend.renderTime', title: 'Front Time', sort: 'number', format: '{0}ms', headerGroup: 'Frontend', border: 'left', hint: info => + {name: 'metrics.frontend.downloadSize', title: 'Transfer Size', sort: 'number', format: (value) => value ? zui.formatBytes(value) : '', headerGroup: 'Frontend', align: 'right'}, + {name: 'metrics.frontend.renderTime', title: 'Front Time', sort: 'number', format: '{0}ms', headerGroup: 'Frontend', border: 'left', align: 'right', hint: info => { const timeClass = info.row.data['metrics.frontend.renderTimeClass']; if(timeClass === 'danger') return `BLOCK: > 100ms`; if(timeClass === 'warning') return `WARN: > 60ms`; }}, - {name: 'userEnv.browser', title: 'Client Browser', sort: true, align: 'left', headerGroup: 'Frontend'}, - {name: 'userEnv.system', title: 'Client OS', sort: true, align: 'center', headerGroup: 'Frontend'}, - {name: 'request.php', title: 'PHP', hint: true, width: 40}, + {name: 'userEnv.browser', title: 'Client Browser', sort: true, align: 'left', headerGroup: 'Frontend', hidden: true}, + {name: 'userEnv.system', title: 'Client OS', sort: true, align: 'center', headerGroup: 'Frontend', hidden: true}, + {name: 'request.php', title: 'PHP', hint: true, width: 40, align: 'center'}, {name: 'request.xhprof', title: 'Xhprof', link: (info) => ({url: info.row.data.request.xhprof || '#', target: '_blank'}), format: (value) => (value ? 'Open' : ''), hint: true, width: 40}, {name: 'requestId', title: 'RID', hint: true, width: 80, cellClass: 'font-mono text-sm select-all', hint: info => info.row.data.requestId, flex: 0, align: 'left', fixed: 'right'}, {name: 'timestamp', title: 'Date', type: 'datetime', hint: (info) => zui.formatDate(info.row.data.timestamp, 'yyyy-MM-dd hh:mm:ss'), width: 120, sort: 'number', fixed: 'right'}, @@ -173,56 +174,12 @@ function initTable(data) colResize: true, rowKey: 'requestId', footer: [function(result, layout) { - const metricsStats = this.metricsStats || {}; return [ {html: `Total ${layout.allRows.length}`, className: 'text-gray mr-4'}, metricsStats.danger ? {html: `
BLOCK ${metricsStats.danger}
`, className: 'text-danger mr-4'} : null, metricsStats.warning ? {html: `
WARN ${metricsStats.warning}
`, className: 'text-warning mr-4'} : null, ]; }], - rowConverter: function(row) - { - row.path = `${row.request.module}-${row.request.method}`; - row['metrics.backend.totalTime'] = row.metrics.backend.totalTime; - row['metrics.backend.sqlCount'] = row.metrics.backend.sqlCount; - row['metrics.backend.sqlTime'] = row.metrics.backend.sqlTime * ((!row.dataVer || row.dataVer < 2) ? 1000 : 1); - row['metrics.backend.requestMemory'] = row.metrics.backend.requestMemory; - row['metrics.backend.phpFileLoaded'] = row.metrics.backend.phpFileLoaded; - row['metrics.frontend.renderTime'] = row.metrics.frontend.renderTime; - row['metrics.frontend.downloadSize'] = row.metrics.frontend.downloadSize; - row['userEnv.browser'] = row.userEnv.browser; - row['userEnv.system'] = row.userEnv.system; - row['request.xhprof'] = row.request.xhprof; - row['request.php'] = row.request.php; - - const totalTimeClass = getTimeClass(row.metrics.backend.totalTime || 0, 500, 300); - const sqlTimeClass = getTimeClass(row.metrics.backend.sqlTime || 0, 300, 200); - const renderTimeClass = getTimeClass(row.metrics.frontend.renderTime || 0, 100, 60); - const classList = [totalTimeClass, sqlTimeClass, renderTimeClass]; - row['metrics.backend.totalTimeClass'] = totalTimeClass; - row['metrics.backend.sqlTimeClass'] = sqlTimeClass; - row['metrics.frontend.renderTimeClass'] = renderTimeClass; - row.metricsClass = classList.includes('danger') ? 'danger' : (classList.includes('warning') ? 'warning' : ''); - row.metricsLevel = classList.includes('danger') ? 1 : (classList.includes('warning') ? 2 : 3); - - if(window.userMap) - { - const user = window.userMap[row.identifier]; - if(user) - { - row.identifier_avatar = user.avatar; - row.identifier_avatar_code = user.id; - row.identifier_name = user.realname || user.account; - } - } - - if(row.metricsClass) - { - if(!this.metricsStats) this.metricsStats = {warning: 0, danger: 0}; - this.metricsStats[row.metricsClass]++; - } - return row; - }, cols: cols, data: data, onRenderCell: function(result, info) @@ -277,6 +234,52 @@ function initTable(data) console.log('> table', table); } +function initData(data) +{ + metricsStats.danger = 0; + metricsStats.warning = 0; + data.forEach(row => + { + row.path = `${row.request.module}-${row.request.method}`; + row['metrics.backend.totalTime'] = row.metrics.backend.totalTime; + row['metrics.backend.sqlCount'] = row.metrics.backend.sqlCount; + row['metrics.backend.sqlTime'] = row.metrics.backend.sqlTime * ((!row.dataVer || row.dataVer < 2) ? 1000 : 1); + row['metrics.backend.requestMemory'] = row.metrics.backend.requestMemory; + row['metrics.backend.phpFileLoaded'] = row.metrics.backend.phpFileLoaded; + row['metrics.frontend.renderTime'] = row.metrics.frontend.renderTime; + row['metrics.frontend.downloadSize'] = row.metrics.frontend.downloadSize; + row['userEnv.browser'] = row.userEnv.browser; + row['userEnv.system'] = row.userEnv.system; + row['request.xhprof'] = row.request.xhprof; + row['request.php'] = row.request.php; + + const totalTimeClass = getTimeClass(row.metrics.backend.totalTime || 0, 500, 300); + const sqlTimeClass = getTimeClass(row.metrics.backend.sqlTime || 0, 300, 200); + const renderTimeClass = getTimeClass(row.metrics.frontend.renderTime || 0, 100, 60); + const classList = [totalTimeClass, sqlTimeClass, renderTimeClass]; + row['metrics.backend.totalTimeClass'] = totalTimeClass; + row['metrics.backend.sqlTimeClass'] = sqlTimeClass; + row['metrics.frontend.renderTimeClass'] = renderTimeClass; + row.metricsClass = classList.includes('danger') ? 'danger' : (classList.includes('warning') ? 'warning' : ''); + row.metricsLevel = classList.includes('danger') ? 1 : (classList.includes('warning') ? 2 : 3); + + if(window.userMap) + { + const user = window.userMap[row.identifier.toLowerCase()]; + if(user) + { + row.identifier_avatar = user.avatar; + row.identifier_avatar_code = user.id; + row.identifier_name = user.realname || user.account; + } + } + + if(row.metricsClass) metricsStats[row.metricsClass]++; + return row; + }); + return data; +} + function queryData(options) { options = options || {}; @@ -284,11 +287,8 @@ function queryData(options) loadData().then(data => { $table.removeClass('loading'); - if(table) - { - table.$.metricsStats = {warning: 0, danger: 0}; - return table.render({data}); - } + data = initData(data); + if(table) return table.render({data}); initTable(data); }).catch(error => { $table.removeClass('loading'); diff --git a/www/js/zui/min.js b/www/js/zui/min.js index 5e7eec0855..090e9afb07 100644 --- a/www/js/zui/min.js +++ b/www/js/zui/min.js @@ -1,5 +1,5 @@ /*! - * ZUI: ZUI for Zentao - v1.10.0 - 2024-06-26 + * ZUI: ZUI for Zentao - v1.10.0 - 2024-10-31 * http://openzui.com * GitHub: https://github.com/easysoft/zui.git * Copyright (c) 2024 cnezsoft.com; Licensed MIT @@ -76,5 +76,5 @@ function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"= */ function(t){function e(e){if("string"==typeof e.data){var i=e.handler,n=e.data.toLowerCase().split(" ");e.handler=function(e){if(this===e.target||!/textarea|select/i.test(e.target.nodeName)&&"text"!==e.target.type){var o="keypress"!==e.type&&t.hotkeys.specialKeys[e.which],a=String.fromCharCode(e.which).toLowerCase(),s="",r={};e.altKey&&"alt"!==o&&(s+="alt+"),e.ctrlKey&&"ctrl"!==o&&(s+="ctrl+"),e.metaKey&&!e.ctrlKey&&"meta"!==o&&(s+="meta+"),e.shiftKey&&"shift"!==o&&(s+="shift+"),o?r[s+o]=!0:(r[s+a]=!0,r[s+t.hotkeys.shiftNums[a]]=!0,"shift+"===s&&(r[t.hotkeys.shiftNums[a]]=!0));for(var l=0,c=n.length;l","/":"?","\\":"|"}},t.each(["keydown","keyup","keypress"],function(){t.event.special[this]={add:e}})}(jQuery),function(t,e,i){"use strict";var n="zui.picker",o={},a={width:null,lang:null,remote:null,remoteConverter:null,remoteOnly:!1,onRemoteError:null,disableEmptySearch:!1,textKey:"text",valueKey:"value",keysKey:"keys",multi:"auto",formItem:"auto",list:null,allowSingleDeselect:null,autoSelectFirst:!1,maxSelectedCount:0,maxListCount:100,hideEmptyTextOption:!0,searchValueKey:!0,emptyResultHint:null,hideOnScroll:!0,inheritFormItemClasses:!1,emptySearchResultHint:null,accurateSearchHint:null,remoteErrorHint:null,deleteByBackspace:!0,disableScrollOnShow:!0,maxDropHeight:250,dropDirection:"auto",dropWidth:"100%",maxAutoDropWidth:450,minAutoDropWidth:100,multiValueSplitter:",",multiSelectActions:5,searchDelay:200,autoClearDrop:6e4,fixLabelFor:!0,hotkey:!0,onSelect:null,onDeselect:null,onBeforeChange:null,onChange:null,onReady:null,onNoResults:null,onShowingDrop:null,onHidingDrop:null,onShowedDrop:null,onHiddenDrop:null,valueMustInList:!0},s={zh_cn:{emptyResultHint:"没有可选项",emptySearchResultHint:"没有找到 “{0}”",accurateSearchHint:"请提供更多关键词缩小匹配范围",remoteErrorHint:"无法从服务器获取结果 - {0}",selectAll:"全选",deselectAll:"取消选择"},zh_tw:{emptyResultHint:"沒有可選項",emptySearchResultHint:"沒有找到 “{0}”",accurateSearchHint:"請提供更多關鍵詞縮小匹配範圍",remoteErrorHint:"無法從服務器獲取結果 - {0}",selectAll:"全選",deselectAll:"取消選擇"},en:{emptyResultHint:"No options",emptySearchResultHint:'Cannot found "{0}"',accurateSearchHint:"Suggest to provide more keywords",remoteErrorHint:"Unable to get result from server: {0}",selectAll:"Select all",deselectAll:"Deselect all"}},r=function(o,a){var l=this;l.name=n,l.$=t(o),l.id="pk_"+(l.$.attr("id")||t.zui.uuid()),a=l.options=t.extend({},r.DEFAULTS,this.$.data(),a),void 0!==a.hideOnWindowScroll&&(a.hideOnScroll=a.hideOnWindowScroll);var c=t.zui.clientLang?t.zui.clientLang():"en",h=a.lang||c;l.lang=t.zui.getLangData?t.zui.getLangData(n,h,s):s[h]||s[c];var d,u,p=a.formItem,f='.form-item,input[type="hidden"],select,input[type="text"]';if(d="self"===p?l.$:"auto"!==p&&p?l.$.find(p):l.$.is(f)?l.$:l.$.find(f).first(),!d.length)return console.error&&console.error("Cannot found form item for picker.");if(d.is('input[type="hidden"]'))u="hidden";else if(d.is("select"))u="select";else{if(!d.is('input[type="text"]'))return console.error&&console.error("Unknown form type for picker.");u="text"}a.inheritFormItemClasses&&v.addClass(d.attr("class")),l.formType=u,l.$formItem=d.removeClass("picker").hide(),l.selfFormItem=d.is(l.$);var g=a.multi;g&&"auto"!==g||(g="select"===u&&"multiple"===d.attr("multiple")),g=!!g,l.multi=g,g||(l.options.checkable=!1);var m=a.list;m?l.setList("function"==typeof m?m({search:l.search,limit:a.maxListCount}):m,!0):"select"===u?l.updateFromSelect():l.setList([],!0);var v;v=!l.selfFormItem&&l.$.hasClass("picker")?l.$:t('
').insertAfter(l.$),v.addClass("picker").toggleClass("picker-multi",g).toggleClass("picker-single",!g),a.width&&v.width(a.width);var y=v.children(".picker-selections");y.length?y.empty():y=t('
');var b=l.id+"-search",w=t('').appendTo(y);if(!g){var x=t('
');a.allowSingleDeselect&&x.append(''),x.appendTo(y),l.$singleSelection=x}v.toggleClass("picker-input-empty",!w.val().length).append(y),l.$container=v,l.$selections=y,l.$search=w,l.search="";var C=a.placeholder;if(void 0===C&&(C=d.attr("placeholder")),"string"==typeof C&&C.length&&y.append(t('
').text(C)),a.placeholder=C,a.fixLabelFor){var _=d.attr("id");_&&t('label[for="'+_+'"]').attr("for",b)}var k=void 0!==a.defaultValue?a.defaultValue:d.val();if(null===k&&(k=""),l.setValue(k,!0),l.setDisabled(),w.on("focus",function(){l.disabled||(l._blurTimer&&(clearTimeout(l._blurTimer),l._blurTimer=0),v.addClass("picker-focus"),l.options.disableEmptySearch&&"string"==typeof l.search&&!l.search.length||l.showDropList())}).on("blur",function(){l.disabled||(l._blurTimer&&clearTimeout(l._blurTimer),l._blurTimer=setTimeout(function(){l._blurTimer=0,w.is(":focus")||v.removeClass("picker-focus")},100))}).on("input change",function(){if(!l.disabled){var t=w.val();if(g&&w.width(14*t.length),v.toggleClass("picker-input-empty",!t.length),l.tryUpdateList(t),a.disableEmptySearch){const e="string"!=typeof t||t.length;!l.dropListShowed&&e?l.showDropList():l.dropListShowed&&!e&&l.hideDropList()}}}),a.hotkey&&w.on("keydown",function(t){if(!l.disabled){var e=t.key||t.which;if(l.dropListShowed){var i=l.activeValue,n="string"==typeof i;if("Enter"===e||13===e)n&&(l.select(i,g),g?(l.$search.val(""),l.tryUpdateList("")):w.blur(),t.preventDefault(),t.stopPropagation());else if("ArrowDown"===e||40===e){var o,s=l.$activeOption;if(s&&(o=s.next(".picker-option"),g))for(;o.length&&o.hasClass("picker-option-selected");)o=o.next(".picker-option");o&&o.length||(o=l.$optionsList.children(g?".picker-option:not(.picker-option-selected)":".picker-option").first()),o.length&&l.activeOption(o),t.preventDefault(),t.stopPropagation()}else if("ArrowUp"===e||30===e){var r,s=l.$activeOption;if(s&&(r=s.prev(".picker-option"),g))for(;r.length&&r.hasClass("picker-option-selected");)r=r.prev(".picker-option");r&&r.length||(r=l.$optionsList.children(g?".picker-option:not(.picker-option-selected)":".picker-option").last()),r.length&&l.activeOption(r),t.preventDefault(),t.stopPropagation()}else"Escape"===e||27===e?l.hideDropList(!0):a.deleteByBackspace&&g&&("Backspace"===e||8===e)&&l.value&&l.value.length&&!w.val().length&&l.deselect(l.value[l.value.length-1])}}}),g){y.on("mousedown",function(t){if(!l.disabled)return l.dropListShowed&&!a.checkable?(t.preventDefault(),void t.stopPropagation()):void 0}).on("mouseup",function(e){l.disabled||y.hasClass("sortable-sorting")||t(e.target).closest(".picker-selection-remove").length||l.dropListShowed&&!a.checkable||l.focus()});var T=a.sortValuesByDnd;if(T&&t.fn.sortable){v.addClass("picker-sortable");var S={selector:".picker-selection",stopPropagation:!0,start:function(){l.hideDropList(!0)},finish:function(e){var i=[];t.each(e.list,function(t,e){i.push(e.item.data("value"))}),l.setValue(i.slice(),!1,!0)}};"object"==typeof T&&t.extend(S,T),y.sortable(S)}}if(y.on("click",".picker-selection-remove",function(e){if(!l.disabled){if(l.multi){var i=t(this).closest(".picker-selection");l.deselect(i.data("value"))}else l.deselect();e.stopPropagation()}}),d.on("chosen:updated",function(){l.updateFromSelect(!1),l.setValue(d.val(),!0),l.setDisabled(),l.updateList()}).on("chosen:activate",l.focus).on("chosen:open",l.showDropList).on("chosen:close",l.hideDropList),v.addClass("picker-ready"),t.zui.asap(function(){l.triggerEvent("ready",{picker:l},"","chosen:ready")}),!a.disableScrollOnShow){var D=a.hideOnScroll;D&&![e,i,!0].includes(D)&&t(D).on("scroll",this.handleParentScroll.bind(this))}};r.prototype.destroy=function(){var e=this,i=e.options;e.hideDropList(!0);var o=e.$search;o.off("focus blur input change"),i.hotkey&&o.off("keydown"),o.remove();var a=e.$selections;a.off("click"),e.multi&&a.off("mousedown mouseup"),a.remove();var s=e.$formItem;e.selectOptionsBackup&&(s.empty(),t.each(e.selectOptionsBackup,function(e,n){var o={value:n[i.valueKey]},a=n[i.keysKey];void 0!==a&&(o["data-"+i.keysKey]=a),s.append(t("