Merge branch release/21.7.3 of zentao/zentaopms (#10163)
This commit is contained in:
@@ -6,13 +6,13 @@
|
||||
},
|
||||
"zentaoext": {
|
||||
"gitRepo": "zentao/zentaoext",
|
||||
"gitVersion": "release/12.1"
|
||||
"gitVersion": "release/12.2"
|
||||
},
|
||||
"zentaomax": {
|
||||
"gitVersion": "release/7.1"
|
||||
"gitVersion": "release/7.2"
|
||||
},
|
||||
"zentaoipd": {
|
||||
"gitVersion": "release/4.1"
|
||||
"gitVersion": "release/4.2"
|
||||
},
|
||||
"devops": {
|
||||
"gitRepo": "zentao/devops"
|
||||
|
||||
@@ -363,6 +363,7 @@ $config->logonMethods[] = 'user.deny';
|
||||
$config->logonMethods[] = 'user.logout';
|
||||
$config->logonMethods[] = 'zanode.nodelist';
|
||||
$config->logonMethods[] = 'screen.viewold';
|
||||
$config->logonMethods[] = 'system.backupview';
|
||||
|
||||
$config->openModules = array();
|
||||
$config->openModules[] = 'install';
|
||||
|
||||
@@ -2338,11 +2338,11 @@ CREATE TABLE `zt_metriclib` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`metricID` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`metricCode` varchar(100) NOT NULL DEFAULT '',
|
||||
`system` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`program` int(11) NOT NULL DEFAULT '0',
|
||||
`project` int(11) NOT NULL DEFAULT '0',
|
||||
`product` int(11) NOT NULL DEFAULT '0',
|
||||
`execution` int(11) NOT NULL DEFAULT '0',
|
||||
`system` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
||||
`program` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`project` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`product` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`execution` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`code` char(30) NOT NULL DEFAULT '',
|
||||
`pipeline` char(30) NOT NULL DEFAULT '',
|
||||
`repo` char(30) NOT NULL DEFAULT '',
|
||||
@@ -2358,10 +2358,12 @@ CREATE TABLE `zt_metriclib` (
|
||||
`date` datetime DEFAULT NULL,
|
||||
`deleted` enum('0','1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `metricID` (`metricID`),
|
||||
KEY `metricCode` (`metricCode`),
|
||||
KEY `date` (`date`),
|
||||
KEY `deleted` (`deleted`)
|
||||
KEY `metricCode_system_date` (`metricCode`, `system`, `date`),
|
||||
KEY `metricCode_program_date` (`metricCode`, `program`, `date`),
|
||||
KEY `metricCode_project_date` (`metricCode`, `project`, `date`),
|
||||
KEY `metricCode_product_date` (`metricCode`, `product`, `date`),
|
||||
KEY `metricCode_execution_date` (`metricCode`, `execution`, `date`),
|
||||
KEY `metricCode_user_date` (`metricCode`, `user`, `date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE TABLE `zt_module` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
|
||||
+4
-1
@@ -48,4 +48,7 @@ UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasour
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewStatus';
|
||||
|
||||
CREATE OR REPLACE VIEW `ztv_executionsummary` AS SELECT `zt_task`.`execution` AS `execution`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`execution`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
|
||||
CREATE OR REPLACE VIEW `ztv_projectsummary` AS SELECT `zt_task`.`project` AS `project`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`estimate`,0)) AS `estimate`,SUM(IF((`zt_task`.`isParent` > '0'),`zt_task`.`consumed`,0)) AS `consumed`,SUM(IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `left`,COUNT(0) AS `number`,SUM(IF(((`zt_task`.`status` <> 'done') AND (`zt_task`.`status` <> 'closed')),1,0)) AS `undone`,SUM(IF(`zt_task`.`isParent` > '0',`zt_task`.`consumed`,0) + IF(((`zt_task`.`status` <> 'cancel') AND (`zt_task`.`status` <> 'closed') AND (`zt_task`.`isParent` > '0')),`zt_task`.`left`,0)) AS `totalReal` FROM `zt_task` WHERE (`zt_task`.`deleted` = '0') GROUP BY `zt_task`.`project`;
|
||||
|
||||
ALTER TABLE `zt_history` MODIFY `new` longtext NULL;
|
||||
ALTER TABLE `zt_history` MODIFY `old` longtext NULL;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
ALTER TABLE `zt_project` ADD `tplAcl` char(30) NOT NULL DEFAULT 'open' AFTER `whitelist`;
|
||||
ALTER TABLE `zt_project` ADD `tplWhiteList` text NULL AFTER `tplAcl`;
|
||||
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项级别', 'charterLevel', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterLevel', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterLevel');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项类型', 'charterCategory', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCategory', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterCategory');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项适用市场', 'charterMarket', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterMarket', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterMarket');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项状态', 'charterStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterStatus', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterStatus');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项关闭原因', 'charterCloseReason', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCloseReason', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterCloseReason');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项审批结果', 'charterReviewResult', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewResult', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterReviewResult');
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`)
|
||||
SELECT 'lang', '立项审批状态', 'charterReviewStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewStatus', '', '', '' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM `zt_workflowdatasource` WHERE `type` = 'lang' AND `code` = 'charterReviewStatus');
|
||||
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterLevel' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'level';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterCategory' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'category';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterMarket' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'market';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'status';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterCloseReason' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'closedReason';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewResult' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewedResult';
|
||||
UPDATE `zt_workflowfield` SET `options` = (SELECT `id` FROM `zt_workflowdatasource` WHERE `code` = 'charterReviewStatus' LIMIT 1) WHERE `module` = 'charter' AND `field` = 'reviewStatus';
|
||||
+15
-13
@@ -1058,9 +1058,9 @@ CREATE TABLE IF NOT EXISTS `zt_history` (
|
||||
`id` int(11) unsigned NOT NULL auto_increment,
|
||||
`action` mediumint(8) unsigned NOT NULL default '0',
|
||||
`field` varchar(30) NOT NULL default '',
|
||||
`old` text NULL,
|
||||
`old` longtext NULL,
|
||||
`oldValue` text NULL,
|
||||
`new` text NULL,
|
||||
`new` longtext NULL,
|
||||
`newValue` text NULL,
|
||||
`diff` mediumtext NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@@ -1615,6 +1615,8 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
|
||||
`team` varchar(90) NOT NULL DEFAULT '',
|
||||
`acl` char(30) NOT NULL DEFAULT 'open',
|
||||
`whitelist` text NULL,
|
||||
`tplAcl` char(30) NOT NULL DEFAULT 'open',
|
||||
`tplWhiteList` text NULL,
|
||||
`order` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
|
||||
`stageBy` enum('project','product') NOT NULL DEFAULT 'product',
|
||||
@@ -16174,11 +16176,11 @@ CREATE TABLE IF NOT EXISTS `zt_metriclib` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`metricID` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`metricCode` varchar(100) NOT NULL DEFAULT '',
|
||||
`system` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`program` int(11) NOT NULL DEFAULT '0',
|
||||
`project` int(11) NOT NULL DEFAULT '0',
|
||||
`product` int(11) NOT NULL DEFAULT '0',
|
||||
`execution` int(11) NOT NULL DEFAULT '0',
|
||||
`system` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
||||
`program` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`project` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`product` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`execution` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`code` char(30) NOT NULL DEFAULT '',
|
||||
`pipeline` char(30) NOT NULL DEFAULT '',
|
||||
`repo` char(30) NOT NULL DEFAULT '',
|
||||
@@ -16195,12 +16197,12 @@ CREATE TABLE IF NOT EXISTS `zt_metriclib` (
|
||||
`deleted` ENUM('0', '1') NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
CREATE INDEX `metricCode_system_date` ON `zt_metriclib`(`metricCode`, `system`, `date`);
|
||||
CREATE INDEX `metricCode_program_date` ON `zt_metriclib`(`metricCode`, `program`, `date`);
|
||||
CREATE INDEX `metricCode_project_date` ON `zt_metriclib`(`metricCode`, `project`, `date`);
|
||||
CREATE INDEX `metricCode_product_date` ON `zt_metriclib`(`metricCode`, `product`, `date`);
|
||||
CREATE INDEX `metricCode_execution_date` ON `zt_metriclib`(`metricCode`, `execution`, `date`);
|
||||
CREATE INDEX `metricCode_user_date` ON `zt_metriclib`(`metricCode`, `user`, `date`);
|
||||
CREATE INDEX `metricCode_system_date` ON `zt_metriclib` (`metricCode`, `system`, `date`);
|
||||
CREATE INDEX `metricCode_program_date` ON `zt_metriclib` (`metricCode`, `program`, `date`);
|
||||
CREATE INDEX `metricCode_project_date` ON `zt_metriclib` (`metricCode`, `project`, `date`);
|
||||
CREATE INDEX `metricCode_product_date` ON `zt_metriclib` (`metricCode`, `product`, `date`);
|
||||
CREATE INDEX `metricCode_execution_date` ON `zt_metriclib` (`metricCode`, `execution`, `date`);
|
||||
CREATE INDEX `metricCode_user_date` ON `zt_metriclib` (`metricCode`, `user`, `date`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_duckdbqueue`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_duckdbqueue` (
|
||||
|
||||
@@ -3548,31 +3548,45 @@ class baseRouter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* config类。
|
||||
* The config class.
|
||||
*
|
||||
* @package framework
|
||||
*/
|
||||
class config
|
||||
if(version_compare(PHP_VERSION, '8.2.0', '>='))
|
||||
{
|
||||
#[\AllowDynamicProperties] // https://www.php.net/manual/zh/class.allowdynamicproperties.php
|
||||
class config
|
||||
{
|
||||
public function set(string $key, $value)
|
||||
{
|
||||
helper::setMember('config', $key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* 设置成员变量,成员可以是'db.user'类似的格式。
|
||||
* Set the value of a member. the member can be the format like db.user.
|
||||
* config类。
|
||||
* The config class.
|
||||
*
|
||||
* <code>
|
||||
* <?php
|
||||
* $config->set('db.user', 'wwccss');
|
||||
* ?>
|
||||
* </code>
|
||||
* @param string $key the key of the member
|
||||
* @param mixed $value the value
|
||||
* @access public
|
||||
* @return void
|
||||
* @package framework
|
||||
*/
|
||||
public function set(string $key, $value)
|
||||
class config
|
||||
{
|
||||
helper::setMember('config', $key, $value);
|
||||
/**
|
||||
* 设置成员变量,成员可以是'db.user'类似的格式。
|
||||
* Set the value of a member. the member can be the format like db.user.
|
||||
*
|
||||
* <code>
|
||||
* <?php
|
||||
* $config->set('db.user', 'wwccss');
|
||||
* ?>
|
||||
* </code>
|
||||
* @param string $key the key of the member
|
||||
* @param mixed $value the value
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function set(string $key, $value)
|
||||
{
|
||||
helper::setMember('config', $key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -534,6 +534,16 @@ class control extends baseControl
|
||||
|
||||
$uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object);
|
||||
$fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID);
|
||||
|
||||
/* 复制项目时显示被复制项目的工作流字段值。*/
|
||||
if($moduleName == 'project' && $methodName == 'create')
|
||||
{
|
||||
foreach($fieldList as $field)
|
||||
{
|
||||
$objectValue = zget($object, $field->field, '');
|
||||
if($objectValue) $field->default = $objectValue;
|
||||
}
|
||||
}
|
||||
return $this->loadModel('flow')->buildFormFields($fields, $fieldList, array(), $object);
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ class dao extends baseDAO
|
||||
$method = $this->app->getMethodName();
|
||||
if($module == 'story' && $this->app->rawModule == 'requirement') $module = 'requirement';
|
||||
if($module == 'story' && $this->app->rawModule == 'epic') $module = 'epic';
|
||||
if($module == 'project' && $method == 'createtemplate') $method = 'create';
|
||||
|
||||
$flowAction = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '{$module}' AND `action` = '{$method}' AND `buildin` = '1' AND `extensionType` = 'extend' AND `vision` = '{$this->config->vision}'")->fetch(PDO::FETCH_OBJ);
|
||||
if(!$flowAction) return $this;
|
||||
|
||||
@@ -119,6 +119,10 @@ class form extends fixer
|
||||
if($moduleName == 'projectrelease') $moduleName = 'release';
|
||||
if($moduleName == 'projectbuild') $moduleName = 'build';
|
||||
|
||||
/* 项目复制用项目创建的工作流。 */
|
||||
if($moduleName == 'project' && $methodName == 'copyconfirm') $methodName = 'create';
|
||||
if($moduleName == 'project' && $methodName == 'edittemplate') $methodName = 'edit';
|
||||
|
||||
/* 用户需求和业务需求用自己的工作流。*/
|
||||
if($moduleName == 'story' && $app->rawModule == 'requirement') $moduleName = 'requirement';
|
||||
if($moduleName == 'story' && $app->rawModule == 'epic') $moduleName = 'epic';
|
||||
|
||||
@@ -61,7 +61,7 @@ class backBtn extends btn
|
||||
'build' => 'execution-build,build-view,project-index,kanban-view,testtask-browse,projectbuild-browse,my-effort,company-effort,company-calendar,my-index,project-dynamic,execution-dynamic,project-view,execution-view',
|
||||
'projectbuild' => 'projectbuild-browse,projectbuild-view,project-index',
|
||||
'repo' => 'repo-maintain,repo-log,repo-browse,repo-view,repo-review,mr-view,repo-browsewebhooks,repo-browserule',
|
||||
'mr' => 'pullreq-browse,mr-browse',
|
||||
'mr' => 'pullreq-browse,mr-browse,my-audit',
|
||||
'pullreq' => 'pullreq-browse',
|
||||
'compile' => 'compile-browse',
|
||||
'store' => 'store-browse',
|
||||
|
||||
@@ -70,7 +70,7 @@ class docList extends wg
|
||||
set::multiple(true),
|
||||
set::maxItemsCount(50),
|
||||
set::menu(array('checkbox' => true)),
|
||||
!empty($items) ? set::toolbar(true) : null
|
||||
!empty($docs) ? set::toolbar(true) : null
|
||||
) : null,
|
||||
div(setClass('mt-2'), $docBox)
|
||||
);
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
#pick-pop-admin-menu .dropmenu-item + .dropmenu-item {margin-top: 4px}
|
||||
#pick-pop-admin-menu .dropmenu-item.active {background-color: unset;}
|
||||
#pick-pop-admin-menu .dropmenu-item:hover {color: rgba(var(--color-primary-500-rgb),var(--tw-text-opacity)); background-color: rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));}
|
||||
.dropmenu-item .item-content .label {flex-shrink: 0;}
|
||||
@@ -77,12 +77,13 @@ class formPanel extends panel
|
||||
{
|
||||
global $app;
|
||||
$moduleName = $app->getModuleName();
|
||||
$methodName = $app->getMethodName();
|
||||
if($moduleName == 'caselib') return data('lib');
|
||||
if($moduleName == 'flow') return data('data');
|
||||
if($moduleName == 'productplan') return data('plan');
|
||||
if($moduleName == 'projectrelease') return data('release');
|
||||
if($moduleName == 'projectbuild') return data('build');
|
||||
if($moduleName == 'project' && $app->getMethodName() == 'create') return data('copyProject');
|
||||
if($moduleName == 'project' && ($methodName == 'create' or $methodName == 'createtemplate')) return data('copyProject') ? data('copyProject') : null;
|
||||
return data($moduleName);
|
||||
}
|
||||
|
||||
@@ -105,6 +106,9 @@ class formPanel extends panel
|
||||
}
|
||||
}
|
||||
|
||||
if($moduleName == 'project' && $methodName == 'createtemplate') $methodName = 'create';
|
||||
if($moduleName == 'project' && $methodName == 'edittemplate') $methodName = 'edit';
|
||||
|
||||
/* 反馈转化。 */
|
||||
if($moduleName == 'feedback')
|
||||
{
|
||||
|
||||
@@ -23,12 +23,15 @@ class remotePicker extends wg
|
||||
if(!$items)
|
||||
{
|
||||
$type = $this->prop('type');
|
||||
$params = $this->prop('params') ? $this->prop('params') : 'noclosed|nodeleted';
|
||||
$params = $this->prop('params') ? $this->prop('params') : 'noclosed,nodeleted';
|
||||
switch($type)
|
||||
{
|
||||
case 'user':
|
||||
$items = createLink('user', 'ajaxGetItems', 'params=' . $params);
|
||||
break;
|
||||
default:
|
||||
$items = createLink('user', 'ajaxGetItems', 'params=' . $params);
|
||||
break;
|
||||
}
|
||||
$this->setProp('items', $items);
|
||||
}
|
||||
|
||||
@@ -218,7 +218,6 @@ class actionModel extends model
|
||||
$this->loadModel('workflow');
|
||||
foreach($actions as $actionID => $action)
|
||||
{
|
||||
$extra = $action->extra;
|
||||
$actionName = strtolower($action->action);
|
||||
if($this->config->edition != 'open' && !isset($flowList[$action->objectType])) $flowList[$action->objectType] = $this->workflow->getByModule($action->objectType);
|
||||
|
||||
@@ -1293,7 +1292,7 @@ class actionModel extends model
|
||||
|
||||
/* If idList include ',*,' Format ',*,' to '*'. */
|
||||
$projectIdList = array();
|
||||
foreach($projectGroups as $key => $idList)
|
||||
foreach($projectGroups as $idList)
|
||||
{
|
||||
$idList = explode(',', (string)$idList);
|
||||
foreach($idList as $id) $projectIdList[] = $id;
|
||||
|
||||
@@ -113,17 +113,12 @@ class actionTest
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getListTest(string $objectType, int $objectID, string $edition = 'open'): string
|
||||
public function getListTest(string $objectType, int $objectID): string
|
||||
{
|
||||
global $tester;
|
||||
$oldEdition = $tester->config->edition;
|
||||
$tester->config->edition = $edition;
|
||||
|
||||
$objects = $this->objectModel->getList($objectType, $objectID);
|
||||
|
||||
$tester->config->edition = $oldEdition;
|
||||
|
||||
global $tester;
|
||||
$modules = $objectType == 'module' ? $tester->dao->select('id')->from(TABLE_MODULE)->where('root')->in($objectID)->fetchPairs('id') : array();
|
||||
$actions = $this->objectModel->getActionListByTypeAndID($objectType, $objectID, $modules);
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ cid=1
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @0
|
||||
- 查找昨天的动态 @1
|
||||
- 查找上周的动态 @3
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @2
|
||||
- 查找今天的动态 @0
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
@@ -50,9 +50,9 @@ r($action->getDynamicByAccountTest($accountList[3])) && p() && e('11'); // 查
|
||||
|
||||
zenData('action')->loadYaml('action_week')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_week')->gen(35, true, false);
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[1])) && p() && e('0'); // 查找今天的动态
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[2])) && p() && e('1'); // 查找昨天的动态
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[3])) && p() && e('3'); // 查找上周的动态
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($action->getDynamicByAccountTest($accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_year')->gen(35, true, false);
|
||||
|
||||
@@ -27,9 +27,9 @@ cid=1
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @0
|
||||
- 查找昨天的动态 @1
|
||||
- 查找上周的动态 @3
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @2
|
||||
- 查找今天的动态 @5
|
||||
|
||||
*/
|
||||
@@ -53,9 +53,9 @@ $action->gen(35, true, false);
|
||||
$actionrecent = zenData('actionrecent')->loadYaml('action_week');
|
||||
$actionrecent->execution->range('101');
|
||||
$actionrecent->gen(35, true, false);
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[1])) && p() && e('0'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[2])) && p() && e('1'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[3])) && p() && e('3'); // 查找上周的动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByExecutionTest($executionID, $accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
|
||||
|
||||
$action = zenData('action')->loadYaml('action_year');
|
||||
$action->execution->range('101');
|
||||
|
||||
@@ -23,8 +23,8 @@ cid=1
|
||||
- 查找用户admin动态 @3
|
||||
- 查找用户dev17动态 @2
|
||||
- 查找用户test18动态 @2
|
||||
- 查找今天的动态 @0
|
||||
- 查找昨天的动态 @1
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @0
|
||||
- 查找今天的动态 @1
|
||||
|
||||
@@ -45,8 +45,8 @@ r($actionTest->getDynamicByProductTest($productID, $accountList[3])) && p() && e
|
||||
|
||||
zenData('action')->loadYaml('action_week')->gen(35, true, false);
|
||||
zenData('actionrecent')->loadYaml('action_week')->gen(35, true, false);
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[1])) && p() && e('0'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[2])) && p() && e('1'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByProductTest($productID, $accountList[1], $typeList[3])) && p() && e('0'); // 查找上周的动态
|
||||
|
||||
zenData('action')->loadYaml('action_year')->gen(35, true, false);
|
||||
|
||||
@@ -27,9 +27,9 @@ cid=1
|
||||
- 查找用户admin动态 @12
|
||||
- 查找用户dev17动态 @12
|
||||
- 查找用户test18动态 @11
|
||||
- 查找今天的动态 @0
|
||||
- 查找昨天的动态 @1
|
||||
- 查找上周的动态 @3
|
||||
- 查找今天的动态 @1
|
||||
- 查找昨天的动态 @0
|
||||
- 查找上周的动态 @2
|
||||
- 查找今天的动态 @5
|
||||
|
||||
*/
|
||||
@@ -53,9 +53,9 @@ $action->gen(35, true, false);
|
||||
$actionrecent = zenData('actionrecent')->loadYaml('action_week');
|
||||
$actionrecent->project->range('101');
|
||||
$actionrecent->gen(35, true, false);
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[1])) && p() && e('0'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[2])) && p() && e('1'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[3])) && p() && e('3'); // 查找上周的动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[1])) && p() && e('1'); // 查找今天的动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[2])) && p() && e('0'); // 查找昨天的动态
|
||||
r($actionTest->getDynamicByProjectTest($projectID, $accountList[1], $typeList[3])) && p() && e('2'); // 查找上周的动态
|
||||
|
||||
$action = zenData('action')->loadYaml('action_year');
|
||||
$action->project->range('101');
|
||||
|
||||
@@ -19,19 +19,25 @@ timeout=0
|
||||
cid=1
|
||||
|
||||
- 获取排序为date倒序的所有动态
|
||||
- 第46条的id属性 @46
|
||||
- 第46条的objectType属性 @review
|
||||
- 第78条的id属性 @78
|
||||
- 第78条的objectType属性 @testcase
|
||||
- 第0条的id属性 @62
|
||||
- 第0条的objectType属性 @branch
|
||||
- 第1条的id属性 @31
|
||||
- 第1条的objectType属性 @bug
|
||||
- 获取排序为date正序的所有动态
|
||||
- 第1条的id属性 @1
|
||||
- 第1条的objectType属性 @product
|
||||
- 第0条的id属性 @63
|
||||
- 第0条的objectType属性 @module
|
||||
- 第1条的id属性 @32
|
||||
- 第1条的objectType属性 @testcase
|
||||
- 获取排序为date倒序的今年之后的动态
|
||||
- 第46条的id属性 @46
|
||||
- 第46条的objectType属性 @review
|
||||
- 第0条的id属性 @62
|
||||
- 第0条的objectType属性 @branch
|
||||
- 第1条的id属性 @31
|
||||
- 第1条的objectType属性 @bug
|
||||
- 获取排序为date正序的所有动态
|
||||
- 第2条的id属性 @2
|
||||
- 第2条的objectType属性 @story
|
||||
- 第0条的id属性 @61
|
||||
- 第0条的objectType属性 @todo
|
||||
- 第1条的id属性 @30
|
||||
- 第1条的objectType属性 @build
|
||||
- 获取排序为date倒序的今年之后的动态
|
||||
- 第0条的id属性 @31
|
||||
- 第0条的objectType属性 @bug
|
||||
@@ -53,8 +59,8 @@ $directionList = array('next', 'pre');
|
||||
|
||||
$action = new actionTest();
|
||||
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('64,testsuite;32,testcase'); // 获取排序为date倒序的所有动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[1], $limit, $dateList[0], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('65,caselib;33,case'); // 获取排序为date正序的所有动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType;1:id,objectType') && e('64,testsuite;32,testcase'); // 获取排序为date倒序的今年之后的动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[1], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('63,module;31,bug'); // 获取排序为date倒序的今天之前的动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[1], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType') && e('31,bug'); // 获取排序为date倒序的今年之后的动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('62,branch;31,bug'); // 获取排序为date倒序的所有动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[1], $limit, $dateList[0], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('63,module;32,testcase'); // 获取排序为date正序的所有动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType;1:id,objectType') && e('62,branch;31,bug'); // 获取排序为date倒序的今年之后的动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[0], $orderByList[0], $limit, $dateList[1], $directionList[0]))) && p('0:id,objectType;1:id,objectType') && e('61,todo;30,build'); // 获取排序为date倒序的今天之前的动态
|
||||
r(array_values($action->getDynamicBySearchTest($queryID[1], $orderByList[0], $limit, $dateList[0], $directionList[1]))) && p('0:id,objectType') && e('31,bug'); // 获取排序为date倒序的今年之后的动态
|
||||
|
||||
@@ -107,10 +107,10 @@ cid=1
|
||||
- 测试获取对象类型 testtask 对象ID 1 的动态信息 @link
|
||||
- 测试获取对象类型 testtask 对象ID 2 的动态信息 @link
|
||||
- 测试获取对象类型 testtask 对象ID 3 的动态信息 @link
|
||||
- 测试获取对象类型 task 对象ID 12 的动态信息 @nochanged
|
||||
- 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息 @nochanged
|
||||
- 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息 @nochanged
|
||||
- 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息 @nochanged
|
||||
- 测试获取对象类型 task 对象ID 12 的动态信息 @link
|
||||
- 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息 @link
|
||||
- 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息 @link
|
||||
- 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息 @link
|
||||
- 测试获取对象类型 execution 对象ID 1 的动态信息 @link
|
||||
- 测试获取对象类型 execution 对象ID 2 的动态信息 @link
|
||||
- 测试获取对象类型 execution 对象ID 3 的动态信息 @link
|
||||
@@ -325,16 +325,16 @@ r($action->getListTest($objectType[9], $testtaskID[2])) && p() && e('link'); //
|
||||
|
||||
// 开源版 导入资产库相关
|
||||
// 操作 importfromstorylib
|
||||
r($action->getListTest($objectType[1], $taskID[11])) && p() && e('nochanged'); // 测试获取对象类型 task 对象ID 12 的动态信息
|
||||
r($action->getListTest($objectType[1], $taskID[11])) && p() && e('link'); // 测试获取对象类型 task 对象ID 12 的动态信息
|
||||
|
||||
// 操作 importfromrisklib
|
||||
r($action->getListTest($objectType[10], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息
|
||||
r($action->getListTest($objectType[10], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 risk 对象ID 1 的动态信息
|
||||
|
||||
// 操作 importfromissuelib
|
||||
r($action->getListTest($objectType[11], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息
|
||||
r($action->getListTest($objectType[11], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 isue 对象ID 1 的动态信息
|
||||
|
||||
// 操作 importfromopportunitylib
|
||||
r($action->getListTest($objectType[12], $hasOneID[0])) && p() && e('nochanged'); // 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息
|
||||
r($action->getListTest($objectType[12], $hasOneID[0])) && p() && e('link'); // 测试 开源版 获取对象类型 opportunity 对象ID 1 的动态信息
|
||||
|
||||
// 操作 opened 对象类型 execution
|
||||
r($action->getListTest($objectType[13], $executionID[0])) && p() && e('link'); // 测试获取对象类型 execution 对象ID 1 的动态信息
|
||||
|
||||
@@ -5,7 +5,7 @@ $config->admin->log->saveDays = 30;
|
||||
if(!isset($config->safe)) $config->safe = new stdclass();
|
||||
if(!isset($config->safe->weak)) $config->safe->weak = '123456,password,12345,12345678,qwerty,123456789,1234,1234567,abc123,111111,123123';
|
||||
|
||||
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup');
|
||||
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'admin|metriclib', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup');
|
||||
$config->admin->menuGroup['company'] = array('dept', 'company', 'user', 'group', 'tutorial');
|
||||
$config->admin->menuGroup['switch'] = array('admin|setmodule');
|
||||
$config->admin->menuGroup['feature'] = array('auditcl', 'stage', 'baseline', 'deliverable', 'design', 'cmcl', 'reviewcl', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting', 'custom|project', 'custom|set', 'custom|product', 'custom|execution', 'custom|required', 'custom|kanban', 'measurement', 'meetingroom', 'custom|browsestoryconcept', 'custom|kanban', 'sqlbuilder', 'report', 'custom|limittaskdate', 'measurement');
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
$config->admin->metricLib = new stdClass();
|
||||
$config->admin->metricLib->updateSQLs[1] = 'CREATE INDEX IF NOT EXISTS `metricCode_system_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `system`, `date`)';
|
||||
$config->admin->metricLib->updateSQLs[2] = 'CREATE INDEX IF NOT EXISTS `metricCode_program_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `program`, `date`)';
|
||||
$config->admin->metricLib->updateSQLs[3] = 'CREATE INDEX IF NOT EXISTS `metricCode_project_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `project`, `date`)';
|
||||
$config->admin->metricLib->updateSQLs[4] = 'CREATE INDEX IF NOT EXISTS `metricCode_product_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `product`, `date`)';
|
||||
$config->admin->metricLib->updateSQLs[5] = 'CREATE INDEX IF NOT EXISTS `metricCode_execution_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `execution`, `date`)';
|
||||
$config->admin->metricLib->updateSQLs[6] = 'CREATE INDEX IF NOT EXISTS `metricCode_user_date` ON ' . TABLE_METRICLIB . ' (`metricCode`, `user`(30), `date`)';
|
||||
$config->admin->metricLib->updateSQLs[7] = 'ALTER TABLE ' . TABLE_METRICLIB . ' DROP INDEX IF EXISTS `metricID`';
|
||||
$config->admin->metricLib->updateSQLs[8] = 'ALTER TABLE ' . TABLE_METRICLIB . ' DROP INDEX IF EXISTS `metricCode`';
|
||||
$config->admin->metricLib->updateSQLs[9] = 'ALTER TABLE ' . TABLE_METRICLIB . ' DROP INDEX IF EXISTS `date`';
|
||||
$config->admin->metricLib->updateSQLs[10] = 'ALTER TABLE ' . TABLE_METRICLIB . ' DROP INDEX IF EXISTS `deleted`';
|
||||
@@ -354,6 +354,39 @@ class admin extends control
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新度量库表的索引。
|
||||
* Upgrade metriclib table index.
|
||||
*
|
||||
* @param int $key
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function metriclib(int $key = 0)
|
||||
{
|
||||
$sql = $this->config->admin->metricLib->updateSQLs[$key] ?? '';
|
||||
if($sql)
|
||||
{
|
||||
set_time_limit(0);
|
||||
session_write_close();
|
||||
|
||||
try
|
||||
{
|
||||
$this->dbh->exec($sql);
|
||||
|
||||
if(isset($this->config->admin->metricLib->updateSQLs[++$key])) return $this->send(['result' => 'success', 'key' => $key]);
|
||||
return $this->send(['result' => 'success']);
|
||||
}
|
||||
catch(PDOException $e)
|
||||
{
|
||||
return $this->sendError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->metriclib->common;
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更换表引擎为InnoDB。
|
||||
* Ajax change table engine.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Update metriclib table structure.
|
||||
*/
|
||||
function updateMetriclib(key)
|
||||
{
|
||||
$('#updateSQLs > #sql' + key).removeClass('hidden');
|
||||
$('#startUpdate').remove();
|
||||
|
||||
$.getJSON($.createLink('admin', 'metriclib', 'key=' + key), function(resp)
|
||||
{
|
||||
if(resp.result == 'success')
|
||||
{
|
||||
$('#sql' + key).addClass('text-success').find('.icon').toggleClass('animate-spin icon-spinner-indicator icon-check');
|
||||
if(resp.key)
|
||||
{
|
||||
updateMetriclib(resp.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#updated').removeClass('hidden');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
zui.Modal.alert(resp.message).then(() => {loadCurrentPage()});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -26,10 +26,10 @@ $lang->admin->setModuleIndex = 'Set Module';
|
||||
|
||||
$lang->admin->mon = 'month';
|
||||
$lang->admin->day = 'day';
|
||||
$lang->admin->updateDynamics = 'updateDynamics';
|
||||
$lang->admin->updatePatch = 'updatePatch';
|
||||
$lang->admin->upgradeRecommend = 'upgradeRecommend';
|
||||
$lang->admin->zentaoUsed = '';
|
||||
$lang->admin->updateDynamics = 'Update Dynamics';
|
||||
$lang->admin->updatePatch = 'Patch Update';
|
||||
$lang->admin->upgradeRecommend = 'Upgrade Recommend';
|
||||
$lang->admin->zentaoUsed = 'You have been using ZenTao';
|
||||
|
||||
$lang->admin->api = 'API';
|
||||
$lang->admin->log = 'Log';
|
||||
@@ -301,4 +301,19 @@ $lang->admin->community->uxPlan->cancel = 'Canceled';
|
||||
$lang->admin->community->unBind = new stdclass();
|
||||
$lang->admin->community->unBind->success = 'Disconnect';
|
||||
|
||||
$lang->admin->nickname = 'Nickname';
|
||||
$lang->admin->position = 'Position';
|
||||
$lang->admin->company = 'Company Name';
|
||||
$lang->admin->solvedProblems = 'Project Management Problems';
|
||||
|
||||
$lang->admin->mobile = 'Mobile Phone';
|
||||
$lang->admin->code = 'SMS Verification Code';
|
||||
$lang->admin->agreeUX = 'User Experience Program';
|
||||
|
||||
$lang->admin->metricLib = new stdclass();
|
||||
$lang->admin->metricLib->startUpdate = 'Start Update';
|
||||
$lang->admin->metricLib->updating = 'Updating';
|
||||
$lang->admin->metricLib->updated = 'Update Completed';
|
||||
$lang->admin->metricLib->tips = "Due to the large amount of data in the metric library table, updating indexes may take a long time. Please operate during low business hours. During this time, you can perform other operations, but please do not close the current page or browser.";
|
||||
|
||||
include dirname(__FILE__) . '/menu.php';
|
||||
|
||||
@@ -44,6 +44,7 @@ $lang->admin->menuList->system['subMenu']['cron'] = array('link' => "{$la
|
||||
$lang->admin->menuList->system['subMenu']['timezone'] = array('link' => "{$lang->timezone}|custom|timezone|");
|
||||
$lang->admin->menuList->system['subMenu']['buildindex'] = array('link' => "{$lang->admin->buildIndex}|search|buildindex|");
|
||||
$lang->admin->menuList->system['subMenu']['tableengine'] = array('link' => "{$lang->admin->tableEngine}|admin|tableengine|");
|
||||
if($config->edition != 'open') $lang->admin->menuList->system['subMenu']['metriclib'] = array('link' => "{$lang->metriclib->common}|admin|metriclib|");
|
||||
if(in_array($this->config->db->driver, $this->config->mysqlDriverList)) $lang->admin->menuList->system['subMenu']['backup'] = array('link' => "{$lang->backup->common}|backup|index|");
|
||||
|
||||
$lang->admin->menuList->system['menuOrder']['5'] = 'mode';
|
||||
|
||||
@@ -308,4 +308,10 @@ $lang->admin->mobile = '手机号';
|
||||
$lang->admin->code = '短信验证码';
|
||||
$lang->admin->agreeUX = '用户体验计划';
|
||||
|
||||
$lang->admin->metricLib = new stdclass();
|
||||
$lang->admin->metricLib->startUpdate = '开始更新';
|
||||
$lang->admin->metricLib->updating = '正在更新';
|
||||
$lang->admin->metricLib->updated = '更新完成';
|
||||
$lang->admin->metricLib->tips = "由于度量库表数据量较大,更新索引可能需要较长时间,请在业务低峰期操作。在此期间您可以进行其他操作,但请勿关闭当前页面或浏览器。";
|
||||
|
||||
include dirname(__FILE__) . '/menu.php';
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The metric library view file of admin module of ZenTaoPMS.
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Gang Liu <liugang@easycorp.ltd>
|
||||
* @package admin
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
$sqls = [];
|
||||
foreach($config->admin->metricLib->updateSQLs as $key => $sql)
|
||||
{
|
||||
$sqls[] = div
|
||||
(
|
||||
setID('sql' . $key),
|
||||
setClass('hidden'),
|
||||
icon(setClass('icon icon-spinner-indicator mr-2 animate-spin')),
|
||||
$sql
|
||||
);
|
||||
}
|
||||
|
||||
panel
|
||||
(
|
||||
setClass('m-auto w-2/3'),
|
||||
set::title($lang->metriclib->common),
|
||||
set::headingClass('justify-start border-b'),
|
||||
div
|
||||
(
|
||||
setClass('mb-4'),
|
||||
$lang->admin->metricLib->tips
|
||||
),
|
||||
div
|
||||
(
|
||||
setID('updateSQLs'),
|
||||
setClass('mb-4'),
|
||||
$sqls,
|
||||
div
|
||||
(
|
||||
setID('updated'),
|
||||
setClass('text-success hidden'),
|
||||
icon(setClass('icon icon-check mr-2')),
|
||||
$lang->admin->metricLib->updated
|
||||
)
|
||||
),
|
||||
a
|
||||
(
|
||||
setID('startUpdate'),
|
||||
setClass('btn primary'),
|
||||
on::click('updateMetriclib(1)'),
|
||||
$lang->admin->metricLib->startUpdate
|
||||
)
|
||||
);
|
||||
@@ -138,6 +138,8 @@ if($config->inQuickon)
|
||||
if($this->config->inQuickon)
|
||||
{
|
||||
$data = array();
|
||||
$config->system->dtable->backup->fieldList['name']['link'] = array('module' => 'system', 'method' => 'backupView', 'params' => 'name={id}');
|
||||
$config->system->dtable->backup->fieldList['name']['data-toggle'] = 'modal';
|
||||
if(!empty($backups)) $data = initTableData($backups, $config->system->dtable->backup->fieldList, $this->system);
|
||||
|
||||
foreach($data as &$backup)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
$(function()
|
||||
{
|
||||
if($('#expiredModal').length) zui.Modal.open({id: 'expiredModal'});
|
||||
if($('#metriclibModal').length) zui.Modal.open({id: 'metriclibModal'});
|
||||
if($('#annualModal').length) zui.Modal.open({id: 'annualModal'});
|
||||
if($('#upgradeModal').length) zui.Modal.open({id: 'upgradeModal'});
|
||||
});
|
||||
|
||||
@@ -50,6 +50,13 @@ $remind ? modal
|
||||
html($remind)
|
||||
) : null;
|
||||
|
||||
$metriclibRemind = $this->misc->getMetriclibRemind();
|
||||
$metriclibRemind ? modal
|
||||
(
|
||||
setID('metriclibModal'),
|
||||
html($metriclibRemind)
|
||||
) : null;
|
||||
|
||||
$upgradeRemind = $this->misc->getUpgradeRemind();
|
||||
if($upgradeRemind)
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ class bugModel extends model
|
||||
if(!empty($bug->assignedTo)) $this->action->create('bug', $bugID, 'Assigned', '', $bug->assignedTo);
|
||||
|
||||
/* Add score for create. */
|
||||
$files = $this->loadModel('file')->saveUpload('bug', $bugID);
|
||||
$this->loadModel('file')->saveUpload('bug', $bugID);
|
||||
if(!empty($bug->case))
|
||||
{
|
||||
$this->loadModel('score')->create('bug', 'createFormCase', $bug->case);
|
||||
|
||||
@@ -39,6 +39,7 @@ detailHeader
|
||||
),
|
||||
);
|
||||
|
||||
$editorUid = uniqid();
|
||||
detailBody
|
||||
(
|
||||
on::change('[name="product"]', 'changeProduct'),
|
||||
@@ -89,6 +90,7 @@ detailBody
|
||||
editor
|
||||
(
|
||||
set::name('steps'),
|
||||
set::uid($editorUid),
|
||||
html($bug->steps)
|
||||
)
|
||||
),
|
||||
@@ -100,7 +102,7 @@ detailBody
|
||||
section
|
||||
(
|
||||
set::title($lang->bug->legendComment),
|
||||
editor(set::name('comment'))
|
||||
editor(set::uid($editorUid), set::name('comment'))
|
||||
)
|
||||
),
|
||||
history(),
|
||||
|
||||
+3
-2
@@ -1163,7 +1163,7 @@ class bugZen extends bug
|
||||
if($product->type != 'normal') $branchTagOption = $this->getBranchOptions($product->id);
|
||||
if($this->config->edition == 'max') $this->view->injectionList = $this->view->identifyList = $this->loadModel('review')->getPairs($bug->project, $bug->product, true);
|
||||
|
||||
$this->assignVarsForEdit($bug);
|
||||
$this->assignVarsForEdit($bug, $product);
|
||||
|
||||
$this->view->title = $this->lang->bug->edit . "BUG #$bug->id $bug->title - " . $this->products[$bug->product];
|
||||
$this->view->bug = $bug;
|
||||
@@ -1181,10 +1181,11 @@ class bugZen extends bug
|
||||
* Assign variables for editing bug.
|
||||
*
|
||||
* @param object $bug
|
||||
* @param object $product
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function assignVarsForEdit(object $bug): void
|
||||
protected function assignVarsForEdit(object $bug, object $product): void
|
||||
{
|
||||
/* Add product related to the bug when it is not in the products. */
|
||||
$product = $this->loadModel('product')->fetchByID($bug->product);
|
||||
|
||||
@@ -245,8 +245,8 @@ class caselibZen extends caselib
|
||||
|
||||
if(empty($rows))
|
||||
{
|
||||
$row->typeValue = join("\n", $this->lang->testcase->typeList);
|
||||
$row->stageValue = join("\n", $this->lang->testcase->stageList);
|
||||
$row->typeValue = implode("\n", $this->lang->testcase->typeList);
|
||||
$row->stageValue = implode("\n", $this->lang->testcase->stageList);
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
@@ -444,7 +444,7 @@ class caselibZen extends caselib
|
||||
|
||||
$case->stage = array();
|
||||
foreach($stages as $stage) $case->stage[] = array_search($stage, $this->lang->testcase->stageList);
|
||||
$case->stage = join(',', $case->stage);
|
||||
$case->stage = implode(',', $case->stage);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -558,7 +558,6 @@ class caselibZen extends caselib
|
||||
{
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
$relatedModules = $this->loadModel('tree')->getModulePairs($libID, 'caselib');
|
||||
$relatedCases = $this->loadModel('testcase')->getRelatedCases($cases);
|
||||
$relatedSteps = $this->testcase->getRelatedSteps(array_keys($cases));
|
||||
$relatedFiles = $this->testcase->getRelatedFiles(array_keys($cases));
|
||||
|
||||
@@ -655,7 +654,7 @@ class caselibZen extends caselib
|
||||
{
|
||||
$case->stage = explode(',', $case->stage);
|
||||
foreach($case->stage as $key => $stage) $case->stage[$key] = isset($this->lang->testcase->stageList[$stage]) ? $this->lang->testcase->stageList[$stage] : $stage;
|
||||
$case->stage = join("\n", $case->stage);
|
||||
$case->stage = implode("\n", $case->stage);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -697,6 +696,6 @@ class caselibZen extends caselib
|
||||
$linkCaseID = trim($linkCaseID);
|
||||
$tmpLinkCases[] = isset($relatedCases[$linkCaseID]) ? $relatedCases[$linkCaseID] . "(#$linkCaseID)" : $linkCaseID;
|
||||
}
|
||||
$case->linkCase = join("; \n", $tmpLinkCases);
|
||||
$case->linkCase = implode("; \n", $tmpLinkCases);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1136,4 +1136,26 @@ class cneModel extends model
|
||||
$apiUrl = "/api/cne/system/update";
|
||||
return $this->apiPost($apiUrl, $apiParams, $this->config->CNE->api->headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取备份的详细信息。
|
||||
* Get backup detail.
|
||||
*
|
||||
* @param object $instance
|
||||
* @param string $backupName
|
||||
* @access public
|
||||
* @return object|false
|
||||
*/
|
||||
public function backupDetail(object $instance, string $backupName): object|false
|
||||
{
|
||||
if(empty($instance->k8name) || empty($instance->spaceData)) return false;
|
||||
|
||||
$apiUrl = "/api/cne/app/backup/detail";
|
||||
$apiParams = array('name' => $instance->k8name, 'namespace' => $instance->spaceData->k8space, 'backup_name' => $backupName);
|
||||
|
||||
$result = $this->apiGet($apiUrl, $apiParams, $this->config->CNE->api->headers);
|
||||
if(empty($result) || $result->code != 200 || empty($result->data)) return false;
|
||||
|
||||
return $result->data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,4 +382,27 @@ class cneTest
|
||||
{
|
||||
return $this->objectModel->tryAllocate($resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test backupDetail method.
|
||||
*
|
||||
* @param object $instance
|
||||
* @param int $count
|
||||
* @access public
|
||||
* @return object|bool|string
|
||||
*/
|
||||
public function backupDetailTest(object $instance, int $count): object|bool|string
|
||||
{
|
||||
$name = '';
|
||||
if($count > 0)
|
||||
{
|
||||
$backupList = $this->objectModel->getBackupList($instance);
|
||||
if(empty($backupList->data)) return false;
|
||||
if(!empty($backupList->data[$count - 1]->name)) $name = $backupList->data[$count - 1]->name;
|
||||
}
|
||||
$result = $this->objectModel->backupDetail($instance, $name);
|
||||
if(!$result) return $result;
|
||||
if(!empty($result->message)) return $result->message;
|
||||
return $result->backup_details;
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=测试 cneModel->backupDetail();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 空的数据 @0
|
||||
- 错误的空间 @0
|
||||
- 备份的数据库
|
||||
- 第0条的db_type属性 @mysql
|
||||
- 第0条的status属性 @completed
|
||||
- 备份的数据
|
||||
- 第0条的volume属性 @data
|
||||
- 第0条的status属性 @completed
|
||||
|
||||
*/
|
||||
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/cne.unittest.class.php';
|
||||
|
||||
$cneModel = new cneTest();
|
||||
|
||||
$instance = new stdClass();
|
||||
r($cneModel->backupDetailTest($instance, 0)) && p() && e('0'); // 空的数据
|
||||
|
||||
$instance->spaceData = new stdClass();
|
||||
$instance->spaceData->k8space = '';
|
||||
r($cneModel->backupDetailTest($instance, 0)) && p() && e('0'); // 错误的空间
|
||||
|
||||
$instance->spaceData->k8space = 'quickon-system';
|
||||
$instance->k8name = 'zentaopaas';
|
||||
|
||||
r($cneModel->backupDetailTest($instance, 1)->db) && p('0:db_type,status') && e('mysql,completed'); // 备份的数据库
|
||||
r($cneModel->backupDetailTest($instance, 2)->volume) && p('0:volume,status') && e('data,completed'); // 备份的数据
|
||||
@@ -25,4 +25,4 @@ $certName = 'fail-name';
|
||||
r($cneModel->certInfoTest($certName)) && p() && e('0'); // 证书名称错误
|
||||
|
||||
$certName = 'tls-haogs-cn';
|
||||
r($cneModel->certInfoTest($certName)) && p('sans:0') && e('*.devops.corp.cc'); // 证书名称正确
|
||||
r($cneModel->certInfoTest($certName)) && p('sans:0') && e('devops.corp.cc'); // 证书名称正确
|
||||
|
||||
@@ -12,10 +12,10 @@ timeout=0
|
||||
cid=1
|
||||
|
||||
- 执行 @1
|
||||
- 执行common模块的getPreAndNextObject方法,参数是'', 3
|
||||
- 执行common模块的getPreAndNextObject方法,参数是'', 3
|
||||
- 属性pre @~~
|
||||
- 属性next @~~
|
||||
- 执行common模块的getPreAndNextObject方法,参数是'story', 3
|
||||
- 执行common模块的getPreAndNextObject方法,参数是'story', 3
|
||||
- 属性pre @~~
|
||||
- 属性next @~~
|
||||
- 执行$preAndNextObject->pre属性id @2
|
||||
|
||||
@@ -596,7 +596,7 @@ class custom extends control
|
||||
$menus = json_decode($items);
|
||||
|
||||
$menuNames = array();
|
||||
foreach($menus as $key => $item) $menuNames[] = $item->name;
|
||||
foreach($menus as $item) $menuNames[] = $item->name;
|
||||
|
||||
foreach($oldMenus as $key => $item)
|
||||
{
|
||||
|
||||
@@ -211,6 +211,12 @@ class datatable extends control
|
||||
if(!$execution->hasProduct) unset($cols['branch']);
|
||||
}
|
||||
|
||||
if($module == 'project' && $method == 'execution')
|
||||
{
|
||||
$project = $this->datatable->fetchByID($this->session->project, 'project');
|
||||
if(!empty($project->isTpl)) unset($cols['deliverable']);
|
||||
}
|
||||
|
||||
if($extra == 'unsetStory' && isset($cols['story'])) unset($cols['story']);
|
||||
|
||||
if($this->config->edition == 'ipd' && $module == 'product' && $method == 'browse' && $extra == 'story') unset($cols['roadmap']);
|
||||
|
||||
+11
-5
@@ -367,6 +367,7 @@ class doc extends control
|
||||
public function createLib(string $type = '', int $objectID = 0, int $libID = 0)
|
||||
{
|
||||
$this->app->loadLang('api');
|
||||
$this->doc->setMenuByType($type, (int)$objectID, (int)$libID);
|
||||
|
||||
if(!empty($_POST))
|
||||
{
|
||||
@@ -386,10 +387,10 @@ class doc extends control
|
||||
}
|
||||
|
||||
$objects = array();
|
||||
if($type == 'project' && $this->app->tab == 'doc')
|
||||
if($type == 'project')
|
||||
{
|
||||
$this->view->executionPairs = $this->execution->getPairs($objectID, 'all', 'multiple,leaf,noprefix');
|
||||
$this->view->project = $this->project->getById($objectID);
|
||||
$this->view->project = $this->project->getById($objectID);
|
||||
if($this->app->tab == 'doc') $this->view->executionPairs = $this->execution->getPairs($objectID, 'all', 'multiple,leaf,noprefix');
|
||||
}
|
||||
|
||||
if($type == 'execution')
|
||||
@@ -397,6 +398,7 @@ class doc extends control
|
||||
$objects = $this->execution->getPairs(0, 'all', 'multiple,leaf,noprefix,withobject');
|
||||
$execution = $this->execution->getByID($objectID);
|
||||
if($execution->type == 'stage') $this->lang->doc->execution = str_replace($this->lang->executionCommon, $this->lang->project->stage, $this->lang->doc->execution);
|
||||
$this->view->execution = $this->doc->fetchByID($objectID, 'execution');
|
||||
}
|
||||
|
||||
if($type == 'custom' || $type == 'mine' || $type == 'doctemplate')
|
||||
@@ -456,6 +458,11 @@ class doc extends control
|
||||
{
|
||||
if(!common::hasPriv('doc', 'editSpace') && !common::hasPriv('doc', 'editLib')) return;
|
||||
$lib = $this->doc->getLibByID($libID);
|
||||
|
||||
if($lib->type != 'project') $this->docZen->setAclForEditLib($lib);
|
||||
$this->doc->setMenuByType($lib->type, (int)zget($lib, $lib->type, 0), (int)$libID);
|
||||
if($lib->type == 'project') $this->docZen->setAclForEditLib($lib);
|
||||
|
||||
if(!empty($_POST))
|
||||
{
|
||||
$this->lang->doc->name = $this->lang->nameAB;
|
||||
@@ -499,8 +506,6 @@ class doc extends control
|
||||
$this->view->object = $execution;
|
||||
}
|
||||
|
||||
$this->docZen->setAclForEditLib($lib);
|
||||
|
||||
$this->view->lib = $lib;
|
||||
$this->view->groups = $this->loadModel('group')->getPairs();
|
||||
$this->view->users = $this->user->getPairs('noletter|noclosed', $lib->users);
|
||||
@@ -2208,6 +2213,7 @@ class doc extends control
|
||||
$this->doc->setMenuByType($type, $spaceID, $libID);
|
||||
$objectKey = $type . 'ID';
|
||||
$this->view->$objectKey = $spaceID;
|
||||
$this->view->$type = $this->doc->fetchByID($spaceID, $type);
|
||||
}
|
||||
|
||||
if($type == 'mine') $menuType = 'my';
|
||||
|
||||
@@ -188,21 +188,22 @@ function submitNewDoc(doc, spaceID, libID, moduleID, formData, afterCreate)
|
||||
const url = $.createLink('doc', 'createTemplate', `libID=${libID}&moduleID=${moduleID}`);
|
||||
const docData =
|
||||
{
|
||||
rawContent : doc.content,
|
||||
content : doc.html,
|
||||
status : doc.status || 'normal',
|
||||
contentType : doc.contentType,
|
||||
type : doc.type || 'text',
|
||||
lib : libID,
|
||||
module : moduleID,
|
||||
title : doc.title,
|
||||
keywords : doc.keywords,
|
||||
acl : 'private',
|
||||
space : spaceType,
|
||||
project : 0,
|
||||
templateType: module && module.data ? module.data.short : '',
|
||||
uid : (doc.uid || `doc${doc.id}`),
|
||||
parent : doc.parent
|
||||
rawContent : doc.content,
|
||||
content : doc.html,
|
||||
status : doc.status || 'normal',
|
||||
contentType : doc.contentType,
|
||||
type : doc.type || 'text',
|
||||
lib : libID,
|
||||
module : moduleID,
|
||||
title : doc.title,
|
||||
keywords : doc.keywords,
|
||||
templateDesc : doc.templateDesc,
|
||||
acl : 'private',
|
||||
space : spaceType,
|
||||
project : 0,
|
||||
templateType : module && module.data ? module.data.short : '',
|
||||
uid : (doc.uid || `doc${doc.id}`),
|
||||
parent : doc.parent
|
||||
};
|
||||
if(formData) mergeDocFormData(docData, formData);
|
||||
docApp.props.fetcher = $.createLink('doc', 'ajaxGetSpaceData', `type=template&spaceID=${spaceID}&picks={picks}&libID=${libID}`);
|
||||
@@ -327,18 +328,19 @@ function handleSaveDoc(doc)
|
||||
const moduleID = docApp.signals.moduleID.value;
|
||||
const url = $.createLink('doc', 'editTemplate', `docID=${doc.id}`);
|
||||
const docData = {
|
||||
rawContent : doc.content,
|
||||
status : doc.status || 'normal',
|
||||
contentType: doc.contentType,
|
||||
type : 'text',
|
||||
lib : libID,
|
||||
module : moduleID,
|
||||
title : doc.title,
|
||||
keywords : doc.keywords,
|
||||
acl : doc.acl,
|
||||
content : doc.html,
|
||||
space : spaceType,
|
||||
uid : (doc.uid || `doc${doc.id}`),
|
||||
rawContent : doc.content,
|
||||
status : doc.status || 'normal',
|
||||
contentType : doc.contentType,
|
||||
type : 'text',
|
||||
lib : libID,
|
||||
module : moduleID,
|
||||
title : doc.title,
|
||||
keywords : doc.keywords,
|
||||
templateDesc : doc.templateDesc,
|
||||
acl : doc.acl,
|
||||
content : doc.html,
|
||||
space : spaceType,
|
||||
uid : (doc.uid || `doc${doc.id}`),
|
||||
};
|
||||
|
||||
const docAppData = docApp.doc.data;
|
||||
|
||||
+26
-11
@@ -15,17 +15,7 @@ window.setDocAppOptions = function(_, options)
|
||||
onSwitchView: function(mode, location, info)
|
||||
{
|
||||
onSwitchView.call(this, mode, location, info);
|
||||
if(location.libID !== this.props.libID)
|
||||
{
|
||||
const lib = this.lib;
|
||||
const quickType = lib ? lib.data.quickType : 'view';
|
||||
const url = $.createLink('doc', 'quick', `type=${quickType}`);
|
||||
this.signals.loading.value = true;
|
||||
loadPartial(url, '#mainContent', {complete: () =>
|
||||
{
|
||||
this.signals.loading.value = false;
|
||||
}});
|
||||
}
|
||||
trySwitchView(this, location.libID);
|
||||
},
|
||||
formatDataItem: function(type, item)
|
||||
{
|
||||
@@ -35,6 +25,31 @@ window.setDocAppOptions = function(_, options)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 尝试切换视图。
|
||||
* Try to switch view.
|
||||
* @param {Object} docApp - 文档应用实例。
|
||||
* @param {number} libID - 库ID。
|
||||
*/
|
||||
function trySwitchView(docApp, libID)
|
||||
{
|
||||
if(window._trySwitchViewTimer) clearTimeout(window._trySwitchViewTimer);
|
||||
|
||||
window._trySwitchViewTimer = setTimeout(() => {
|
||||
window._trySwitchViewTimer = 0;
|
||||
if(libID === docApp.props.libID) return;
|
||||
|
||||
const lib = docApp.lib;
|
||||
const quickType = lib ? lib.data.quickType : 'view';
|
||||
const url = $.createLink('doc', 'quick', `type=${quickType}`);
|
||||
docApp.signals.loading.value = true;
|
||||
loadPartial(url, '#mainContent', {complete: () =>
|
||||
{
|
||||
docApp.signals.loading.value = false;
|
||||
}});
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function getObjectBrowseUrl(object, objectType, libID)
|
||||
{
|
||||
const browsePath =
|
||||
|
||||
@@ -25,26 +25,27 @@ if($type === 'project')
|
||||
* 定义文档界面上的权限。
|
||||
* Define the privs of doc app.
|
||||
*/
|
||||
$hasCustomSpace = $type == 'mine' || $type == 'custom';
|
||||
$hasCustomSpace = $type == 'mine' || $type == 'custom';
|
||||
$projectTemplate = ($type === 'project' && !empty($project->isTpl)) || ($type == 'execution' && !empty($execution->isTpl));
|
||||
$privs = array();
|
||||
$privs['create'] = hasPriv('doc', 'create');
|
||||
$privs['edit'] = hasPriv('doc', 'edit');
|
||||
$privs['delete'] = hasPriv('doc', 'delete');
|
||||
$privs['create'] = hasPriv('doc', 'create') && !$projectTemplate;
|
||||
$privs['edit'] = hasPriv('doc', 'edit') && !$projectTemplate;
|
||||
$privs['delete'] = hasPriv('doc', 'delete') && !$projectTemplate;
|
||||
$privs['view'] = hasPriv('doc', 'view');
|
||||
$privs['effort'] = $this->config->edition != 'open' && hasPriv('effort', 'createForObject');
|
||||
$privs['exportDoc'] = $this->config->edition != 'open' && hasPriv('doc', $type . '2export');
|
||||
$privs['exportDoc'] = $this->config->edition != 'open' && hasPriv('doc', $type . '2export') && !$projectTemplate;
|
||||
$privs['exportApi'] = $this->config->edition != 'open' && hasPriv('api', 'export');
|
||||
$privs['moveDoc'] = hasPriv('doc', 'moveDoc');
|
||||
$privs['moveDoc'] = hasPriv('doc', 'moveDoc') && !$projectTemplate;
|
||||
$privs['collect'] = hasPriv('doc', 'collect');
|
||||
$privs['createLib'] = hasPriv('doc', 'createLib');
|
||||
$privs['editLib'] = hasPriv('doc', 'editLib');
|
||||
$privs['moveLib'] = hasPriv('doc', 'moveLib');
|
||||
$privs['moveLib'] = hasPriv('doc', 'moveLib') && !$projectTemplate;
|
||||
$privs['deleteLib'] = hasPriv('doc', 'deleteLib');
|
||||
$privs['sortDocLib'] = hasPriv('doc', 'sortDocLib');
|
||||
$privs['uploadFile'] = hasPriv('doc', 'create');
|
||||
$privs['uploadFile'] = hasPriv('doc', 'create') && !$projectTemplate;
|
||||
$privs['editFile'] = hasPriv('file', 'edit');
|
||||
$privs['deleteFile'] = hasPriv('doc', 'deleteFile');
|
||||
$privs['exportFiles'] = hasPriv('doc', 'exportFiles');
|
||||
$privs['exportFiles'] = hasPriv('doc', 'exportFiles') && !$projectTemplate;
|
||||
$privs['createSpace'] = $hasCustomSpace && hasPriv('doc', 'createSpace');
|
||||
$privs['deleteSpace'] = $hasCustomSpace && hasPriv('doc', 'deleteSpace');
|
||||
$privs['editSpace'] = $hasCustomSpace && hasPriv('doc', 'editSpace');
|
||||
|
||||
@@ -26,6 +26,7 @@ $renderObjectBox = function(string $type) use ($lang, $objects, $objectID)
|
||||
return formRow(
|
||||
setClass('objectBox'),
|
||||
formGroup(
|
||||
set::id('object'),
|
||||
set::width('5/6'),
|
||||
set::label($lang->doc->{$type}),
|
||||
set::name($type),
|
||||
|
||||
@@ -2738,7 +2738,7 @@ class execution extends control
|
||||
$executionGroups = $this->dao->select('*')->from(TABLE_EXECUTION) /* 按照项目分组,获取有权限访问的执行列表。*/
|
||||
->where('deleted')->eq('0')
|
||||
->andWhere('multiple')->eq('1')
|
||||
->andWhere('type')->in('sprint,stage,kanban')
|
||||
->andWhere('type')->in(!empty($execution->isTpl) ? 'sprint,stage' : 'sprint,stage,kanban')
|
||||
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi()
|
||||
->beginIF(!empty($execution->isTpl))->andWhere('isTpl')->eq('1')->fi()
|
||||
->andWhere('project')->in(array_keys($projects))
|
||||
|
||||
@@ -153,7 +153,6 @@ class executionModel extends model
|
||||
|
||||
unset($this->lang->execution->menu->burn);
|
||||
unset($this->lang->execution->menu->kanban);
|
||||
unset($this->lang->execution->menu->view);
|
||||
unset($this->lang->execution->menu->story);
|
||||
unset($this->lang->execution->menu->qa);
|
||||
unset($this->lang->execution->menu->devops);
|
||||
@@ -162,6 +161,22 @@ class executionModel extends model
|
||||
unset($this->lang->execution->menu->effort);
|
||||
unset($this->lang->execution->menu->more);
|
||||
|
||||
if(!empty($this->lang->execution->menu->view['subMenu']->gantt))
|
||||
{
|
||||
$this->lang->execution->menu->gantt = $this->lang->execution->menu->view['subMenu']->gantt;
|
||||
|
||||
$taskOrder = 0;
|
||||
foreach($this->lang->execution->menuOrder as $order => $menu) if($menu == 'task') $taskOrder = $order;
|
||||
$this->lang->execution->menuOrder[$taskOrder + 1] = 'gantt';
|
||||
|
||||
if(!empty($this->lang->project->menuOrder))
|
||||
{
|
||||
$taskOrder = 0;
|
||||
foreach($this->lang->project->menuOrder as $order => $menu) if($menu == 'task') $taskOrder = $order;
|
||||
$this->lang->project->menuOrder[$taskOrder + 1] = 'gantt';
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($this->lang->execution->menu->other['dropMenu']->pssp))
|
||||
{
|
||||
$this->lang->execution->menu->pssp = $this->lang->execution->menu->other['dropMenu']->pssp;
|
||||
@@ -181,6 +196,7 @@ class executionModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
unset($this->lang->execution->menu->view);
|
||||
unset($this->lang->execution->menu->other);
|
||||
if(isset($this->lang->execution->menu->settings['subMenu']->products)) unset($this->lang->execution->menu->settings['subMenu']->products); // 模板下隐藏产品
|
||||
if(isset($this->lang->execution->menu->settings['subMenu']->whitelist)) unset($this->lang->execution->menu->settings['subMenu']->whitelist); // 模板下隐藏白名单
|
||||
|
||||
@@ -11,6 +11,7 @@ class createExecutionTester extends tester
|
||||
*/
|
||||
public function inputFields($execution)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$form = $this->initForm('execution', 'create', '', 'appIframe-project');
|
||||
$form->wait(1);
|
||||
if(isset($execution['project'])) $form->dom->project->picker($execution['project']);
|
||||
@@ -88,10 +89,11 @@ class createExecutionTester extends tester
|
||||
*/
|
||||
public function create($execution, $module = 'execution')
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($execution);
|
||||
|
||||
/* 创建成功会跳转至看板列表全部标签下,从url中获取status字段内容 */
|
||||
$form = $this->loadPage();
|
||||
$form->wait(1);
|
||||
$url = explode('status=', $this->response('url'));
|
||||
/* 根据url中是否包含status,判断是否创建成功 */
|
||||
if(!isset($url[1]))
|
||||
@@ -112,7 +114,6 @@ class createExecutionTester extends tester
|
||||
*/
|
||||
public function createWithEmptyName($execution, $field)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($execution);
|
||||
$form = $this->loadPage();
|
||||
$text = $form->dom->{$field . 'Tip'}->getText();
|
||||
@@ -134,7 +135,6 @@ class createExecutionTester extends tester
|
||||
*/
|
||||
public function createWithRepeatName($execution, $field)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($execution);
|
||||
if($this->checkRepeatInfo($field)) return $this->success('创建看板表单页提示信息正确');
|
||||
return $this->failed('创建看板表单页提示信息不正确');
|
||||
@@ -151,7 +151,6 @@ class createExecutionTester extends tester
|
||||
*/
|
||||
public function createWithDateError($execution, $dateType = 'end')
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($execution);
|
||||
if($this->checkDateInfo($dateType)) return $this->success('创建看板表单页提示信息正确');
|
||||
return $this->failed('创建看板表单页提示信息不正确');
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/ui.php';
|
||||
class editExecutionTester extends tester
|
||||
{
|
||||
/**
|
||||
* 输入表单字段内容。
|
||||
* Input fields
|
||||
*
|
||||
* @param array $execution
|
||||
* @access public
|
||||
*/
|
||||
public function inputFields($execution)
|
||||
{
|
||||
$form = $this->initForm('execution', 'kanban', array('kanbanID' => '2'), 'appIframe-project');
|
||||
$form->wait(1);
|
||||
$form->dom->kanbanSettingInLite->click();
|
||||
$form->wait();
|
||||
$form = $this->loadPage();
|
||||
$form->wait(1);
|
||||
$form->dom->btn($this->lang->execution->setKanban)->click();
|
||||
$form->wait(1);
|
||||
if(isset($execution['name'])) $form->dom->name->setValue($execution['name']);
|
||||
if(isset($execution['begin'])) $form->dom->begin->datePicker($execution['begin']);
|
||||
if(isset($execution['end'])) $form->dom->end->datePicker($execution['end']);
|
||||
$form->dom->editSubmitInLite->click();
|
||||
$form->wait(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 看板名称已存在时获取提示信息。
|
||||
* Get error info of repeat name.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function checkRepeatInfo()
|
||||
{
|
||||
$form = $this->loadPage();
|
||||
$form->wait(1);
|
||||
if(!is_object($form->dom->nameTip)) return $this->failed('看板名称重复没有提示信息');
|
||||
$text = $form->dom->nameTip->getText();
|
||||
$info = sprintf($this->lang->error->repeat, $this->lang->kanban->name, $form->dom->name->getValue());
|
||||
if($text == $info) return $this->success('编辑看板表单页提示信息正确');
|
||||
return $this->failed('编辑看板表单页提示信息不正确');
|
||||
}
|
||||
|
||||
/**
|
||||
* 计划起止日期错误时获取提示信息。
|
||||
* Get error info of begin and end date.
|
||||
*
|
||||
* @param string $dateType begin|end
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function checkDateInfo($dateType = 'end')
|
||||
{
|
||||
$form = $this->loadPage();
|
||||
$form->wait(1);
|
||||
if($dateType == 'begin')
|
||||
{
|
||||
$text = $form->dom->beginTip->getText();
|
||||
if($this->config->default->lang == 'zh-cn') $info = '看板开始日期应大于等于项目的开始日期:。';
|
||||
if($this->config->default->lang == 'en') $info = 'The start date of Kanban should be ≥ the start date of Project : .';
|
||||
}
|
||||
else
|
||||
{
|
||||
$text = $form->dom->endTip->getText();
|
||||
if($this->config->default->lang == 'zh-cn') $info = '看板截止日期应小于等于项目的截止日期:。';
|
||||
if($this->config->default->lang == 'en') $info = 'The deadline of Kanban should be ≤ the deadline of Project : .';
|
||||
}
|
||||
|
||||
/* 获取页面返回信息中除日期外的内容 */
|
||||
preg_match_all('/(\d{4}-\d{2}-\d{2})/', $text, $matches);
|
||||
$date = $matches[0];
|
||||
$params = str_replace($date, '', $text);
|
||||
$params = trim($params);
|
||||
|
||||
return $params == $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑看板。
|
||||
* Edit kanban.
|
||||
*
|
||||
* @param array $execution
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function edit($execution)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($execution);
|
||||
if($this->checkFormTips('execution')) return $this->success('编辑看板表单页提示信息正确');
|
||||
|
||||
/* 访问view方法,方便获取信息 */
|
||||
$form = $this->initForm('execution', 'view', array('kanbanID' => '2' ), 'appIframe-project');
|
||||
if(isset($execution['name']) && $form->dom->executionName->getText() != $execution['name']) return $this->failed('编辑看板名称失败');
|
||||
if(isset($execution['begin']) && $form->dom->plannedBeginInLite->getText() != $execution['begin']) return $this->failed('编辑预计开始日期失败');
|
||||
if(isset($execution['end']) && $form->dom->plannedEndInLite->getText() != $execution['end']) return $this->failed('编辑预计结束日期失败');
|
||||
return $this->success('编辑看板成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 看板名称为空编辑执行。
|
||||
* Edit kanban with no name.
|
||||
*
|
||||
* @param array $execution
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function editWithEmptyName($execution)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($execution);
|
||||
$form = $this->loadPage();
|
||||
$form->wait(1);
|
||||
$text = $form->dom->nameTip->getText();
|
||||
$info = sprintf($this->lang->error->notempty, $this->lang->kanban->name);
|
||||
if($text == $info) return $this->success('编辑看板表单页提示信息正确');
|
||||
return $this->failed('编辑看板表单页提示信息不正确');
|
||||
}
|
||||
|
||||
/**
|
||||
* 看板名称已存在编辑执行。
|
||||
* Edit kanban with repeat name.
|
||||
*
|
||||
* @param array $execution
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function editWithRepeatName($execution)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($execution);
|
||||
return $this->checkRepeatInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 看板起止日期错误编辑看板。
|
||||
* Edit kanban with date error.
|
||||
*
|
||||
* @param array $execution
|
||||
* @param string $dateType begin|end
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function editWithDateError($execution, $dateType = 'end')
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($execution);
|
||||
if($this->checkDateInfo($dateType)) return $this->success('编辑看板表单页提示信息正确');
|
||||
return $this->failed('编辑看板表单页提示信息不正确');
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ class kanbanTester extends tester
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$form = $this->initForm('execution', 'kanban', array('kanbanID' => '2'), 'appIframe-project');
|
||||
$form->wait(1);
|
||||
|
||||
/* 选择泳道分组 */
|
||||
if(!empty($groupId))
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/ui.php';
|
||||
class putoffExecutionTester extends tester
|
||||
{
|
||||
/**
|
||||
* 输入延期执行表单字段。
|
||||
* Input fields.
|
||||
*
|
||||
* @param array $execution
|
||||
* @param string $executionId
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function inputFields($execution, $executionId)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$form = $this->initForm('execution', 'kanban', array('kanbanID' => $executionId ), 'appIframe-project');
|
||||
$form->wait(1);
|
||||
$form->dom->kanbanSettingInLite->click();
|
||||
$form->wait(1);
|
||||
$form->dom->btn($this->lang->execution->putoff)->click();
|
||||
if(isset($execution['begin'])) $form->dom->begin->datePicker($execution['begin']);
|
||||
if(isset($execution['end'])) $form->dom->end->datePicker($execution['end']);
|
||||
if(isset($execution['days'])) $form->dom->days->setValue($execution['days']);
|
||||
$form->dom->putoffSubmitInLite->click();
|
||||
$form->wait(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 延期执行。
|
||||
* Putoff execution.
|
||||
*
|
||||
* @param array $execution
|
||||
* @param string $executionId
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function putoff($execution, $executionId)
|
||||
{
|
||||
$this->inputFields($execution, $executionId);
|
||||
$form = $this->loadPage();
|
||||
$form->wait(1);
|
||||
if(is_object($form->dom->putoffSubmitInLite)) return $this->failed('延期失败');
|
||||
|
||||
$form = $this->initForm('execution', 'view', array('kanbanID' => $executionId ), 'appIframe-project');
|
||||
if(isset($execution['begin']) && $form->dom->plannedBeginInLite->getText() != $execution['begin']) return $this->failed('计划开始时间错误');
|
||||
if(isset($execution['end']) && $form->dom->plannedEndInLite->getText() != $execution['end']) return $this->failed('计划完成时间错误');
|
||||
return $this->success('延期执行成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 延期执行的计划起止日期不正确。
|
||||
* Putoff execution with wrong date.
|
||||
*
|
||||
* @param array $execution
|
||||
* @param string $executionId
|
||||
* @param string $dateType begin|end
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function putoffWithWrongDate($execution, $executionId, $dateType = 'end')
|
||||
{
|
||||
$this->inputFields($execution, $executionId);
|
||||
$form = $this->loadPage();
|
||||
$form->wait(1);
|
||||
|
||||
if(!is_object($form->dom->putoffSubmitInLite)) return $this->failed('错误的延期成功');
|
||||
|
||||
if($dateType == 'end' && $execution['end'] != '')
|
||||
{
|
||||
$text = $form->dom->endTip->getText();
|
||||
if($this->config->default->lang == 'zh-cn') $info = '看板截止日期应小于等于项目的截止日期:。';
|
||||
if($this->config->default->lang == 'en') $info = 'The deadline of Kanban should be ≤ the deadline of Project : .';
|
||||
}
|
||||
if($dateType == 'begin')
|
||||
{
|
||||
$text = $form->dom->beginTip->getText();
|
||||
if($this->config->default->lang == 'zh-cn') $info = '看板开始日期应大于等于项目的开始日期:。';
|
||||
if($this->config->default->lang == 'en') $info = 'The start date of Kanban should be ≥ the start date of Project : .';
|
||||
}
|
||||
/* 计划结束日期为空时,错误提示中没有日期 */
|
||||
if($dateType == 'end' && $execution['end'] == '')
|
||||
{
|
||||
$info = sprintf($this->lang->error->notempty, $this->lang->execution->end);
|
||||
$params = $form->dom->endTip->getText();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 获取页面返回信息中除日期外的内容 */
|
||||
preg_match_all('/(\d{4}-\d{2}-\d{2})/', $text, $matches); ~
|
||||
$date = $matches[0][0]; ~
|
||||
$params = str_replace($date, '', $text); ~
|
||||
$params = trim($params);
|
||||
}
|
||||
|
||||
if($params == $info) return $this->success('延期执行表单页提示信息正确');
|
||||
return $this->failed('延期执行表单页提示信息不正确');
|
||||
}
|
||||
|
||||
/**
|
||||
* 延期执行的可用工日不正确。
|
||||
* Putoff execution with wrong days.
|
||||
*
|
||||
* @param array $execution
|
||||
* @param string $executionId
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function putoffWithWrongDays($execution, $executionId)
|
||||
{
|
||||
$this->inputFields($execution, $executionId);
|
||||
$form = $this->loadPage();
|
||||
$form->wait(1);
|
||||
if(!is_object($form->dom->putoffSubmitInLite)) return $this->failed('错误的延期成功');
|
||||
if($form->dom->daysTip->getText() == $this->lang->project->copyProject->daysTips) return $this->success('延期执行表单页提示信息正确');
|
||||
return $this->failed('延期执行表单页提示信息不正确');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/ui.php';
|
||||
class startExecutionTester extends tester
|
||||
{
|
||||
/**
|
||||
* 开始看板弹窗中输入实际开始日期。
|
||||
* Input fields.
|
||||
*
|
||||
* @param string $realBegan
|
||||
* @param string $kanbanId
|
||||
* @access public
|
||||
*/
|
||||
public function inputFields($realBegan, $kanbanId)
|
||||
{
|
||||
|
||||
$form = $this->initForm('execution', 'kanban', array('kanbanID' => $kanbanId ), 'appIframe-project');
|
||||
$form->wait(1);
|
||||
$form->dom->kanbanSettingInLite->click();
|
||||
$form->wait(1);
|
||||
$form->dom->btn($this->lang->execution->start)->click();
|
||||
$form->wait(1);
|
||||
if(isset($realBegan)) $form->dom->realBegan->datePicker($realBegan);
|
||||
$form->wait(1);
|
||||
$form->dom->startSubmitInLite->click();
|
||||
$form->wait(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 正常开始看板。
|
||||
* Start kanban.
|
||||
*
|
||||
* @param string $realBegan
|
||||
* @param string $kanbanId
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function start($realBegan, $kanbanId)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($realBegan, $kanbanId);
|
||||
$form = $this->initForm('execution', 'view', array('kanbanID' => $kanbanId ), 'appIframe-project');
|
||||
if($form->dom->status->getText() != $this->lang->execution->statusList->doing) return $this->failed('看板状态错误');
|
||||
if($form->dom->realBeganView->getText() != $realBegan) return $this->failed('看板实际开始日期错误');
|
||||
return $this->success('开始看板成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际开始日期大于当前日期。
|
||||
* The real start date is greater than the current date.
|
||||
*
|
||||
* @param string $realBegan
|
||||
* @param string $kanbanId
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function startWithGreaterDate($realBegan, $kanbanId)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$this->inputFields($realBegan, $kanbanId);
|
||||
$form = $this->loadPage();
|
||||
$field = $form->dom->realBegan->getValue();
|
||||
$text = $form->dom->realBeganTip->getText();
|
||||
$info = sprintf($this->lang->error->le, $field, $realBegan);
|
||||
if($text != $info) return $this->success('开始看板表单页提示信息正确');
|
||||
return $this->failed('开始看板表单页提示信息不正确');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/ui.php';
|
||||
class suspendExecutionTester extends tester
|
||||
{
|
||||
/**
|
||||
* 运营界面挂起看板。
|
||||
* Suspend execution.
|
||||
*
|
||||
* @param string $executionId
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function suspend($executionId)
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$form = $this->initForm('execution', 'kanban', array('execution' => $executionId ), 'appIframe-project');
|
||||
$form->wait(1);
|
||||
$form->dom->kanbanSettingInLite->click();
|
||||
$form->wait(1);
|
||||
$form->dom->btn($this->lang->execution->suspend)->click();
|
||||
$form->dom->suspendSubmitInLite->click();
|
||||
$form->wait(3);
|
||||
|
||||
/* 调用view方法,方便获取数据 */
|
||||
$viewForm = $this->initForm('execution', 'view', array('execution' => $executionId ), 'appIframe-project');
|
||||
if($viewForm->dom->status->getText() != $this->lang->execution->statusList->suspended) return $this->failed('看板状态错误');
|
||||
return $this->success('挂起看板成功');
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,15 @@ class taskkanbanTester extends tester
|
||||
* 检查看板数据。
|
||||
* Check kanban.
|
||||
*
|
||||
* @param string $col
|
||||
* @param string $num
|
||||
* @param string $nameId
|
||||
* @param string $groupId
|
||||
* @param string $lane 页面上显示的第几个泳道
|
||||
* @param string $col 泳道中的第几个列
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function checkKanban($col, $num, $nameId = '', $groupId = '')
|
||||
public function checkKanban($lane, $col, $num, $nameId = '', $groupId = '')
|
||||
{
|
||||
$form = $this->initForm('execution', 'taskkanban', array('execution' => '2'), 'appIframe-execution');
|
||||
|
||||
@@ -30,12 +31,13 @@ class taskkanbanTester extends tester
|
||||
{
|
||||
$form->dom->xpath['kanbanGroup'] = "(//menu/menu/li)[{$groupId}]";
|
||||
$form->dom->groupPicker->click();
|
||||
$form->wait(1);
|
||||
$form->dom->kanbanGroup->click();
|
||||
$form->wait(1);
|
||||
}
|
||||
|
||||
/* 检查对应泳道下的卡片数量 */
|
||||
$form->dom->xpath['col'] = "//div[@z-col='{$col}']//div[@class='card']";
|
||||
$form->dom->xpath['col'] = "//div[@class='kanban-body']/div[{$lane}]/div[2]/div[{$col}]//div[@class='card']";
|
||||
if(count($form->dom->getElementList($form->dom->xpath['col'])->element) != $num) return $this->failed("数据错误");
|
||||
return $this->success("数据正确");
|
||||
}
|
||||
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=运营界面编辑看板
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 编辑看板成功
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @编辑看板成功
|
||||
- 看板名称为空,编辑失败
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @编辑看板表单页提示信息正确
|
||||
- 计划开始时间为空,编辑失败
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @编辑看板表单页提示信息正确
|
||||
- 计划结束时间为空,编辑失败
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @编辑看板表单页提示信息正确
|
||||
- 看板名称重复,编辑失败
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @编辑看板表单页提示信息正确
|
||||
- 看板的计划开始时间早于项目的计划开始时间,编辑失败
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @编辑看板表单页提示信息正确
|
||||
- 看板的计划结束时间晚于项目的计划结束时间,编辑失败
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @编辑看板表单页提示信息正确
|
||||
|
||||
*/
|
||||
|
||||
chdir(__DIR__);
|
||||
include '../lib/editinlite.ui.class.php';
|
||||
|
||||
$project = zenData('project');
|
||||
$project->id->range('1-100');
|
||||
$project->project->range('0, 1{2}');
|
||||
$project->model->range('kanban, []{2}');
|
||||
$project->type->range('project, kanban{2}');
|
||||
$project->auth->range('[]');
|
||||
$project->storyType->range('[]');
|
||||
$project->parent->range('0, 1{2}');
|
||||
$project->path->range('`,1,`, `,1,2,`, `,1,3,`');
|
||||
$project->grade->range('1');
|
||||
$project->name->range('项目, 看板1, 看板2');
|
||||
$project->hasProduct->range('0');
|
||||
$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('wait');
|
||||
$project->vision->range('lite');
|
||||
$project->gen(3);
|
||||
|
||||
$product = zenData('product');
|
||||
$product->id->range('1-100');
|
||||
$product->name->range('项目');
|
||||
$product->shadow->range('1');
|
||||
$product->bind->range('1');
|
||||
$product->type->range('normal');
|
||||
$product->vision->range('lite');
|
||||
$product->gen(1);
|
||||
|
||||
$projectProduct = zenData('projectproduct');
|
||||
$projectProduct->project->range('1-3');
|
||||
$projectProduct->product->range('1');
|
||||
$projectProduct->plan->range('0');
|
||||
$projectProduct->gen(3);
|
||||
|
||||
$tester = new editExecutionTester();
|
||||
$tester->login();
|
||||
|
||||
$execution = array(
|
||||
'0' => array(
|
||||
'name' => '编辑看板1',
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => date('Y-m-d', strtotime('+2 days')),
|
||||
),
|
||||
'1' => array(
|
||||
'name' => '',
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => date('Y-m-d', strtotime('+2 days')),
|
||||
),
|
||||
'2' => array(
|
||||
'name' => '编辑看板2',
|
||||
'begin' => '',
|
||||
'end' => date('Y-m-d', strtotime('+2 days')),
|
||||
),
|
||||
'3' => array(
|
||||
'name' => '编辑看板3',
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => '',
|
||||
),
|
||||
'4' => array(
|
||||
'name' => '看板2',
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => date('Y-m-d', strtotime('+2 days')),
|
||||
),
|
||||
'5' => array(
|
||||
'name' => '编辑看板5',
|
||||
'begin' => date('Y-m-d', strtotime('-2 years')),
|
||||
'end' => date('Y-m-d', strtotime('+2 days')),
|
||||
),
|
||||
'6' => array(
|
||||
'name' => '测试看板9',
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => date('Y-m-d', strtotime('+2 years')),
|
||||
),
|
||||
);
|
||||
|
||||
r($tester->edit($execution['0'])) && p('status,message') && e('SUCCESS,编辑看板成功'); //编辑看板成功
|
||||
r($tester->editWithEmptyName($execution['1'])) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //看板名称为空,编辑失败
|
||||
r($tester->edit($execution['2'])) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //计划开始时间为空,编辑失败
|
||||
r($tester->edit($execution['3'])) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //计划结束时间为空,编辑失败
|
||||
r($tester->editWithRepeatName($execution['4'])) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //看板名称重复,编辑失败
|
||||
r($tester->editWithDateError($execution['5'], 'begin')) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //看板的计划开始时间早于项目的计划开始时间,编辑失败
|
||||
r($tester->editWithDateError($execution['6'], 'end')) && p('status,message') && e('SUCCESS,编辑看板表单页提示信息正确'); //看板的计划结束时间晚于项目的计划结束时间,编辑失败
|
||||
$tester->closeBrowser();
|
||||
@@ -5,8 +5,17 @@ class kanbanPage extends page
|
||||
{
|
||||
parent::__construct($webdriver);
|
||||
$xpath = array(
|
||||
'settings' => "//*[@id='navbar']//a[@data-id='settings']/span",
|
||||
'groupPickerInLite' => "//*[@id='actionBar']/button[1]"
|
||||
'settings' => "//*[@id='navbar']//a[@data-id='settings']/span",
|
||||
'groupPickerInLite' => "//*[@id='actionBar']/button[1]",
|
||||
'kanbanSettingInLite' => "//*[@id='actionBar']/button[2]/span",
|
||||
/* 编辑看板弹窗中的提交按钮 */
|
||||
'editSubmitInLite' => "//form[contains(@id,'zin_execution_edit')]/div[last()]/button",
|
||||
/* 开始看板弹窗中的提交按钮 */
|
||||
'startSubmitInLite' => "//*[@data-name='realBegan']/..//button",
|
||||
/* 延期看板弹窗中的提交按钮 */
|
||||
'putoffSubmitInLite' => "//form[contains(@id,'zin_execution_putoff')]//button",
|
||||
/* 挂起看板弹窗中的提交按钮 */
|
||||
'suspendSubmitInLite' => "//form[contains(@id,'zin_execution_suspend')]//button",
|
||||
);
|
||||
$this->dom->xpath = array_merge($this->dom->xpath, $xpath);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ class viewPage extends page
|
||||
'putoffSubmit' => "//*[@class='modal-actions']/../div[3]/div/div/form/div[4]/div/button",
|
||||
/* 激活执行弹窗中的元素 */
|
||||
'activateSubmit' => "//*[@class='modal-actions']/../div[3]/div/div/form/div[3]/div/button",
|
||||
/* 运营管理界面下元素 */
|
||||
'plannedBeginInLite' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[1]/span[2]",
|
||||
'plannedEndInLite' => "//*[@id='mainContent']/div[2]/div[1]/div/table[2]/tbody/tr/td/div/div[2]/span[2]",
|
||||
);
|
||||
$this->dom->xpath = array_merge($this->dom->xpath, $xpath);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=运营管理界面延期看板
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 未开始的执行,修改可用工作日,保存成功
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行成功
|
||||
- 进行中的执行,修改可用工作日,保存成功
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行成功
|
||||
- 未开始的执行,延期弹窗中修改正确的起止日期后点击保存
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行成功
|
||||
- 进行中的执行,延期弹窗中修改正确的起止日期后点击保存
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行成功
|
||||
- 计划结束日期为空,可用工日不为空
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行表单页提示信息正确
|
||||
- 计划开始日期为空
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行表单页提示信息正确
|
||||
- 计划开始日期小于项目计划开始日期
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行表单页提示信息正确
|
||||
- 计划结束日期大于项目计划完成日期
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行表单页提示信息正确
|
||||
- 可用工日为空
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行表单页提示信息正确
|
||||
- 可用工日为非数字
|
||||
- 最终测试状态 @SUCCESS
|
||||
- 测试结果 @延期执行表单页提示信息正确
|
||||
|
||||
*/
|
||||
|
||||
chdir(__DIR__);
|
||||
include '../lib/putoffinlite.ui.class.php';
|
||||
|
||||
$project = zenData('project');
|
||||
$project->id->range('1-100');
|
||||
$project->project->range('0');
|
||||
$project->model->range('kanban');
|
||||
$project->type->range('project');
|
||||
$project->auth->range('[]');
|
||||
$project->storyType->range('[]');
|
||||
$project->parent->range('0');
|
||||
$project->path->range('`,1,`');
|
||||
$project->grade->range('1');
|
||||
$project->name->range('项目');
|
||||
$project->hasProduct->range('0');
|
||||
$project->begin->range('(-6M)-(-5M):1D')->type('timestamp')->format('YY/MM/DD');
|
||||
$project->end->range('(+5M)-(+6M):1D')->type('timestamp')->format('YY/MM/DD');
|
||||
$project->openedBy->range('user1');
|
||||
$project->acl->range('open');
|
||||
$project->status->range('doing');
|
||||
$project->vision->range('lite');
|
||||
$project->gen(1);
|
||||
|
||||
$execution = zenData('project');
|
||||
$execution->id->range('2, 3');
|
||||
$execution->project->range('1{2}');
|
||||
$execution->model->range('[]{2}');
|
||||
$execution->type->range('kanban{2}');
|
||||
$execution->auth->range('[]');
|
||||
$execution->storyType->range('[]');
|
||||
$execution->parent->range('1{2}');
|
||||
$execution->path->range('`,1,2,`, `,1,3,`');
|
||||
$execution->grade->range('1');
|
||||
$execution->name->range('看板1, 看板2');
|
||||
$execution->hasProduct->range('0');
|
||||
$execution->begin->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD');
|
||||
$execution->end->range('(+2M)-(+3M):1D')->type('timestamp')->format('YY/MM/DD');
|
||||
$execution->openedBy->range('user1');
|
||||
$execution->acl->range('open');
|
||||
$execution->status->range('wait, doing');
|
||||
$execution->vision->range('lite');
|
||||
$execution->gen(2, false);
|
||||
|
||||
$product = zenData('product');
|
||||
$product->id->range('1-100');
|
||||
$product->name->range('项目');
|
||||
$product->shadow->range('1');
|
||||
$product->bind->range('1');
|
||||
$product->type->range('normal');
|
||||
$product->vision->range('lite');
|
||||
$product->gen(1);
|
||||
|
||||
$projectProduct = zenData('projectproduct');
|
||||
$projectProduct->project->range('1-3');
|
||||
$projectProduct->product->range('1');
|
||||
$projectProduct->plan->range('0');
|
||||
$projectProduct->gen(3);
|
||||
$tester = new putoffexecutionTester();
|
||||
$tester->login();
|
||||
|
||||
$execution = array(
|
||||
'0' => array(
|
||||
'days' => '10',
|
||||
),
|
||||
'1' => array(
|
||||
'begin' => date('Y-m-d', strtotime('-1 days')),
|
||||
'end' => date('Y-m-d', strtotime('+1 days')),
|
||||
'days' => '10',
|
||||
),
|
||||
'2' => array(
|
||||
'begin' => '',
|
||||
'end' => date('Y-m-d'),
|
||||
'days' => '10',
|
||||
),
|
||||
'3' =>array(
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => '',
|
||||
'days' => '10',
|
||||
),
|
||||
'4' => array(
|
||||
'begin' => date('Y-m-d', strtotime('-10 months')),
|
||||
'end' => date('Y-m-d'),
|
||||
'days' => '10',
|
||||
),
|
||||
'5' => array(
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => date('Y-m-d', strtotime('+10 months')),
|
||||
'days' => '10',
|
||||
),
|
||||
'6' => array(
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => date('Y-m-d'),
|
||||
'days' => '',
|
||||
),
|
||||
'7' => array(
|
||||
'begin' => date('Y-m-d'),
|
||||
'end' => date('Y-m-d'),
|
||||
'days' => 'aaa',
|
||||
),
|
||||
);
|
||||
|
||||
r($tester->putoff($execution['0'], '2')) && p('status,message') && e('SUCCESS,延期执行成功'); //未开始的执行,修改可用工作日,保存成功
|
||||
r($tester->putoff($execution['0'], '3')) && p('status,message') && e('SUCCESS,延期执行成功'); //进行中的执行,修改可用工作日,保存成功
|
||||
r($tester->putoff($execution['1'], '2')) && p('status,message') && e('SUCCESS,延期执行成功'); //未开始的执行,延期弹窗中修改正确的起止日期后点击保存
|
||||
r($tester->putoff($execution['1'], '3')) && p('status,message') && e('SUCCESS,延期执行成功'); //进行中的执行,延期弹窗中修改正确的起止日期后点击保存
|
||||
r($tester->putoffWithWrongDate($execution['3'], '3', 'end')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //计划结束日期为空,可用工日不为空
|
||||
r($tester->putoffWithWrongDate($execution['2'], '2', 'begin')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //计划开始日期为空
|
||||
r($tester->putoffWithWrongDate($execution['4'], '3', 'begin')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //计划开始日期小于项目计划开始日期
|
||||
r($tester->putoffWithWrongDate($execution['5'], '2', 'end')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //计划结束日期大于项目计划完成日期
|
||||
r($tester->putoffWithWrongDays($execution['6'], '2')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //可用工日为空
|
||||
r($tester->putoffWithWrongDays($execution['7'], '2')) && p('status,message') && e('SUCCESS,延期执行表单页提示信息正确'); //可用工日为非数字
|
||||
$tester->closeBrowser();
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
title=运营管理界面开始看板
|
||||
timeout=0
|
||||
cid=1
|
||||
*/
|
||||
|
||||
chdir(__DIR__);
|
||||
include '../lib/startinlite.ui.class.php';
|
||||
|
||||
$project = zenData('project');
|
||||
$project->id->range('1-100');
|
||||
$project->project->range('0, 1{2}');
|
||||
$project->model->range('kanban, []{2}');
|
||||
$project->type->range('project, kanban{2}');
|
||||
$project->auth->range('[]');
|
||||
$project->storyType->range('[]');
|
||||
$project->parent->range('0, 1{2}');
|
||||
$project->path->range('`,1,`, `,1,2,`, `,1,3,`');
|
||||
$project->grade->range('1');
|
||||
$project->name->range('项目, 看板1, 看板2');
|
||||
$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('wait');
|
||||
$project->vision->range('lite');
|
||||
$project->gen(3);
|
||||
|
||||
$tester = new startExecutionTester();
|
||||
$tester->login();
|
||||
|
||||
$realBegan = array(date('Y-m-d', strtotime('+20 days')), date('Y-m-d'));
|
||||
|
||||
r($tester->startWithGreaterDate($realBegan[0], 2)) && p('status,message') && e('SUCCESS,开始看板表单页提示信息正确');
|
||||
r($tester->start($realBegan[1], 2)) && p('status,message') && e('SUCCESS,开始看板成功');
|
||||
$tester->closeBrowser();
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
title=运营界面挂起看板
|
||||
timeout=0
|
||||
cid=1
|
||||
*/
|
||||
|
||||
chdir(__DIR__);
|
||||
include '../lib/suspendinlite.ui.class.php';
|
||||
|
||||
$project = zenData('project');
|
||||
$project->id->range('1-100');
|
||||
$project->project->range('0, 1{2}');
|
||||
$project->model->range('kanban, []{2}');
|
||||
$project->type->range('project, kanban{2}');
|
||||
$project->auth->range('[]');
|
||||
$project->storyType->range('[]{2}');
|
||||
$project->parent->range('0, 1{2}');
|
||||
$project->path->range('`,1,`, `,1,2,`, `,1,3,`');
|
||||
$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, wait, doing');
|
||||
$project->vision->range('lite');
|
||||
$project->gen(3);
|
||||
|
||||
$tester = new suspendExecutionTester();
|
||||
$tester->login();
|
||||
|
||||
r($tester->suspend('2')) && p('status,message') && e('SUCCESS,挂起看板成功'); //挂起未开始的看板
|
||||
r($tester->suspend('3')) && p('status,message') && e('SUCCESS,挂起看板成功'); //挂起进行中的看板
|
||||
$tester->closeBrowser();
|
||||
@@ -99,10 +99,7 @@ $bug->gen(8);
|
||||
$tester = new taskkanbanTester();
|
||||
$tester->login();
|
||||
|
||||
r($tester->checkKanban('1', 1)) && p('status,message') && e('SUCCESS,数据正确');
|
||||
r($tester->checkKanban('4', 4)) && p('status,message') && e('SUCCESS,数据正确');
|
||||
r($tester->checkKanban('10', 1)) && p('status,message') && e('SUCCESS,数据正确');
|
||||
r($tester->checkKanban('20', 0)) && p('status,message') && e('SUCCESS,数据正确');
|
||||
r($tester->checkKanban('30', 1)) && p('status,message') && e('SUCCESS,数据正确');
|
||||
r($tester->checkKanban('1', 1, '4', '3')) && p('status,message') && e('SUCCESS,数据正确');
|
||||
r($tester->checkKanban('1', '1', '2', '3', '1')) && p('status,message') && e('SUCCESS,数据正确');
|
||||
r($tester->checkKanban('1', '4', '4', '4', '5')) && p('status,message') && e('SUCCESS,数据正确');
|
||||
r($tester->checkKanban('1', '1', '1', '4', '3')) && p('status,message') && e('SUCCESS,数据正确');
|
||||
$tester->closeBrowser();
|
||||
|
||||
@@ -31,5 +31,4 @@ r($gitlab->addPushWebhookTest(1, $token, 2)) && p() && e('1'); //使用repoID
|
||||
r($gitlab->addPushWebhookTest(1, $token, -1)) && p() && e('0'); //使用repoID为1,异常的项目id推送webhook
|
||||
|
||||
r($gitlab->addPushWebhookTest(2, $token)) && p() && e('0'); //使用repoID为2,不存在的项目id推送webhook
|
||||
r($gitlab->addPushWebhookTest(2, $token, 2)) && p() && e('1'); //使用repoID为2,存在的项目id推送webhook
|
||||
|
||||
r($gitlab->addPushWebhookTest(2, $token, 2)) && p() && e('1'); //使用repoID为2,存在的项目id推送webhook
|
||||
@@ -1074,9 +1074,13 @@ $config->group->package->projectTemplate = new stdclass();
|
||||
$config->group->package->projectTemplate->order = 5;
|
||||
$config->group->package->projectTemplate->subset = 'projectTemplate';
|
||||
$config->group->package->projectTemplate->privs = array();
|
||||
$config->group->package->projectTemplate->privs['project-template'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('project-index'));
|
||||
$config->group->package->projectTemplate->privs['project-createTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('project-index', 'project-template'));
|
||||
$config->group->package->projectTemplate->privs['project-deleteTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('project-index', 'project-template'));
|
||||
$config->group->package->projectTemplate->privs['project-template'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('project-index'));
|
||||
$config->group->package->projectTemplate->privs['project-createTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('project-index', 'project-template'));
|
||||
$config->group->package->projectTemplate->privs['project-editTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('project-index', 'project-template'));
|
||||
$config->group->package->projectTemplate->privs['project-publishTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('project-index', 'project-template'));
|
||||
$config->group->package->projectTemplate->privs['project-disableTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('project-index', 'project-template'));
|
||||
$config->group->package->projectTemplate->privs['project-templatePriv'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('project-index', 'project-template'));
|
||||
$config->group->package->projectTemplate->privs['project-deleteTemplate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('project-index', 'project-template'));
|
||||
|
||||
$config->group->package->browseExecution = new stdclass();
|
||||
$config->group->package->browseExecution->order = 5;
|
||||
|
||||
@@ -36,11 +36,11 @@ zenData('usergroup')->loadYaml('usergroup')->gen(10);
|
||||
|
||||
$groupID = 2;
|
||||
|
||||
$normal1Group = array('name' => '复制的分组1');
|
||||
$normal2Group = array('name' => '复制的分组2');
|
||||
$normal3Group = array('name' => '复制的分组3');
|
||||
$normal4Group = array('name' => '复制的分组4');
|
||||
$existGroup = array('name' => '这是一个新的用户分组5');
|
||||
$normal1Group = array('name' => '复制的分组1', 'project' => '0');
|
||||
$normal2Group = array('name' => '复制的分组2', 'project' => '0');
|
||||
$normal3Group = array('name' => '复制的分组3', 'project' => '0');
|
||||
$normal4Group = array('name' => '复制的分组4', 'project' => '0');
|
||||
$existGroup = array('name' => '这是一个新的用户分组5', 'project' => '0');
|
||||
|
||||
$group = new groupTest();
|
||||
r($group->copyTest($groupID, $normal1Group)) && p('name') && e('复制的分组1'); // 正常复制
|
||||
|
||||
@@ -21,11 +21,11 @@ include dirname(__FILE__, 2) . '/lib/group.unittest.class.php';
|
||||
zenData('group')->gen(5);
|
||||
su('admin');
|
||||
|
||||
$normalGroup = array('name' => '我是一个分组', 'desc' => '');
|
||||
$repeatGroup = array('name' => '这是一个新的用户分组5', 'desc' => '');
|
||||
$visionGroup = array('name' => '我是当前管理界面的分组', 'desc' => '');
|
||||
$limitedGroup1 = array('name' => '我是一个受限分组', 'desc' => '', 'role' => 'limited');
|
||||
$limitedGroup2 = array('name' => '我是另一个受限分组', 'desc' => '', 'limited' => 1);
|
||||
$normalGroup = array('name' => '我是一个分组', 'desc' => '', 'project' => '0');
|
||||
$repeatGroup = array('name' => '这是一个新的用户分组5', 'desc' => '', 'project' => '0');
|
||||
$visionGroup = array('name' => '我是当前管理界面的分组', 'desc' => '', 'project' => '0');
|
||||
$limitedGroup1 = array('name' => '我是一个受限分组', 'desc' => '', 'role' => 'limited', 'project' => '0');
|
||||
$limitedGroup2 = array('name' => '我是另一个受限分组', 'desc' => '', 'limited' => 1, 'project' => '0');
|
||||
|
||||
$group = new groupTest();
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ title=测试 messageModel->saveNotice();
|
||||
cid=1
|
||||
|
||||
- 发送 需求 2 动态 2 用户 admin 的消息
|
||||
- 属性id @1
|
||||
- 属性objectType @message
|
||||
- 属性action @2
|
||||
- 属性id @1
|
||||
- 属性objectType @message
|
||||
- 属性action @2
|
||||
- 属性createdBy @admin
|
||||
- 发送 任务 6 动态 6 用户 admin 的消息
|
||||
- 属性id @2
|
||||
@@ -39,15 +39,15 @@ cid=1
|
||||
- 属性createdBy @user1
|
||||
- 发送 任务 6 动态 6 用户 user1 的消息
|
||||
- 属性id @4
|
||||
- 属性objectType @message
|
||||
- 属性action @6
|
||||
- 属性createdBy @user1
|
||||
- 发送 反馈 0 动态 0 用户 user1 的消息
|
||||
- 属性id @0
|
||||
- 属性objectType @0
|
||||
- 属性action @0
|
||||
- 属性objectType @message
|
||||
- 属性action @6
|
||||
- 属性createdBy @user1
|
||||
- 发送 反馈 0 动态 0 用户 user1 的消息
|
||||
- 属性id @0
|
||||
- 属性objectType @0
|
||||
- 属性action @0
|
||||
- 属性createdBy @0
|
||||
- 发送 需求 2 动态 2 不传 actor 的消息
|
||||
- 发送 需求 2 动态 2 不传 actor 的消息
|
||||
- 属性id @5
|
||||
- 属性objectType @message
|
||||
- 属性action @2
|
||||
@@ -89,4 +89,4 @@ r($message->saveNoticeTest($objectType[2], $objectID[2], $actionType[2], $action
|
||||
|
||||
r($message->saveNoticeTest($objectType[0], $objectID[0], $actionType[0], $actionID[0], $actor[3])) && p('id') && e('0'); // 发送 需求 2 动态 2 未登录 的消息
|
||||
r($message->saveNoticeTest($objectType[1], $objectID[1], $actionType[1], $actionID[1], $actor[3])) && p('id') && e('0'); // 发送 任务 6 动态 6 未登录 的消息
|
||||
r($message->saveNoticeTest($objectType[2], $objectID[2], $actionType[2], $actionID[2], $actor[3])) && p('id') && e('0'); // 发送 反馈 0 动态 0 未登录 的消息
|
||||
r($message->saveNoticeTest($objectType[2], $objectID[2], $actionType[2], $actionID[2], $actor[3])) && p('id') && e('0'); // 发送 反馈 0 动态 0 未登录 的消息
|
||||
@@ -170,10 +170,12 @@ class metric extends control
|
||||
*/
|
||||
public function updateMetricLib()
|
||||
{
|
||||
// 保存当前的错误报告级别和显示错误的设置
|
||||
/* 保存当前的错误报告级别和显示错误的设置。*/
|
||||
/* Save the current error reporting level and display error settings. */
|
||||
$originalDebug = $this->config->debug;
|
||||
|
||||
// 开启调试模式
|
||||
/* 开启调试模式。*/
|
||||
/* Enable debug mode. */
|
||||
$this->config->debug = 2;
|
||||
|
||||
$this->metric->saveLogs('----------------------------------');
|
||||
@@ -184,7 +186,8 @@ class metric extends control
|
||||
$classifiedCalcGroup = $this->metric->classifyCalc($calcList);
|
||||
$this->metricZen->calculateMetric($classifiedCalcGroup);
|
||||
|
||||
// 恢复之前的调试状态
|
||||
/* 恢复之前的调试状态。*/
|
||||
/* Restore the previous debug state. */
|
||||
$this->config->debug = $originalDebug;
|
||||
|
||||
if(dao::isError())
|
||||
@@ -197,10 +200,12 @@ class metric extends control
|
||||
|
||||
public function updateDashboardMetricLib()
|
||||
{
|
||||
// 保存当前的错误报告级别和显示错误的设置
|
||||
/* 保存当前的错误报告级别和显示错误的设置。*/
|
||||
/* Save the current error reporting level and display error settings. */
|
||||
$originalDebug = $this->config->debug;
|
||||
|
||||
// 开启调试模式
|
||||
/* 开启调试模式。*/
|
||||
/* Enable debug mode. */
|
||||
$this->config->debug = 2;
|
||||
|
||||
$this->metric->saveLogs('-------------------------------------------');
|
||||
@@ -213,7 +218,8 @@ class metric extends control
|
||||
$classifiedCalcGroup = $this->metric->classifyCalc($calcList);
|
||||
$this->metricZen->calculateMetric($classifiedCalcGroup);
|
||||
|
||||
// 恢复之前的调试状态
|
||||
/* 恢复之前的调试状态。*/
|
||||
/* Restore the previous debug state. */
|
||||
$this->config->debug = $originalDebug;
|
||||
|
||||
if(dao::isError())
|
||||
|
||||
+77
-5
@@ -11,6 +11,8 @@ declare(strict_types=1);
|
||||
*/
|
||||
class metricZen extends metric
|
||||
{
|
||||
private $validObjects;
|
||||
|
||||
/**
|
||||
* 构建创建度量的数据。
|
||||
* Build metric data for create.
|
||||
@@ -213,6 +215,40 @@ class metricZen extends metric
|
||||
return number_format((float)$time, 5, '.', '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效的对象。
|
||||
* Get valid objects.
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getValidObjects()
|
||||
{
|
||||
if($this->validObjects !== null) return $this->validObjects;
|
||||
|
||||
/* 保证逻辑集中,这里直接使用sql查询获取数据,保证查询性能。*/
|
||||
/* To ensure logical concentration, here we directly use sql to get data to ensure query performance. */
|
||||
$productList = $this->dao->select('id')->from(TABLE_PRODUCT)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('status')->ne('closed')
|
||||
->andWhere('shadow')->eq(0)
|
||||
->fetchPairs('id');
|
||||
|
||||
$projectList = $this->dao->select('id')->from(TABLE_PROJECT)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('status')->notIN('closed,done')
|
||||
->andWhere('type')->eq('project')
|
||||
->fetchPairs('id');
|
||||
|
||||
$executionList = $this->dao->select('id')->from(TABLE_EXECUTION)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('status')->notIN('closed,done')
|
||||
->andWhere('type')->in('sprint,stage,kanban')
|
||||
->fetchPairs('id');
|
||||
|
||||
$this->validObjects = array('product' => $productList, 'project' => $projectList, 'execution' => $executionList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算度量数据。
|
||||
* Calculate metric data.
|
||||
@@ -225,23 +261,35 @@ class metricZen extends metric
|
||||
{
|
||||
set_time_limit(0);
|
||||
$calcBeginTime = $this->startTime();
|
||||
|
||||
foreach($classifiedCalcGroup as $calcGroup)
|
||||
{
|
||||
try
|
||||
{
|
||||
/* 开始计算度量数据。*/
|
||||
/* Start calculating metric data. */
|
||||
$beginTime = $this->startTime();
|
||||
|
||||
$statement = $this->prepareDataset($calcGroup);
|
||||
$sql = $statement ? $statement->get() : '';
|
||||
$this->calcMetric($statement, $calcGroup->calcList);
|
||||
|
||||
$recordWithCode = $this->prepareMetricRecord($calcGroup->calcList);
|
||||
|
||||
$calcTime = $this->endTime($beginTime);
|
||||
|
||||
/* 开始插入度量数据。*/
|
||||
/* Start inserting metric data. */
|
||||
$beginTime = $this->startTime();
|
||||
|
||||
$this->metric->insertMetricLib($recordWithCode);
|
||||
|
||||
$executeTime = $this->endTime($beginTime);
|
||||
|
||||
/* 记录度量数据计算和插入的时间以及sql语句。*/
|
||||
/* Record the time of metric data calculation and insertion and sql statement. */
|
||||
$total = 0;
|
||||
$codes = '';
|
||||
$sql = $statement ? $statement->get() : '';
|
||||
foreach($recordWithCode as $code => $records)
|
||||
{
|
||||
$count = count($records);
|
||||
@@ -260,9 +308,13 @@ class metricZen extends metric
|
||||
}
|
||||
}
|
||||
|
||||
/* 记录度量数据计算的总时间。*/
|
||||
/* Record the total time of metric data calculation. */
|
||||
$executeTime = $this->endTime($calcBeginTime);
|
||||
$this->metric->saveLogs("Calculate all consumed time: {$executeTime} seconds");
|
||||
|
||||
/* 开始去重。*/
|
||||
/* Start deduplication. */
|
||||
$beginTime = $this->startTime();
|
||||
$metrics = $this->metric->getExecutableMetric();
|
||||
foreach($metrics as $code)
|
||||
@@ -272,7 +324,13 @@ class metricZen extends metric
|
||||
$executeTime = $this->endTime($deduplicationBeginTime);
|
||||
$this->metric->saveLogs("Deduplication consumed time: {$executeTime} seconds, the code: $code");
|
||||
}
|
||||
|
||||
/* 重建主键。*/
|
||||
/* Rebuild the primary key. */
|
||||
$this->metric->rebuildPrimaryKey();
|
||||
|
||||
/* 记录去重的时间。*/
|
||||
/* Record the time of deduplication. */
|
||||
$executeTime = $this->endTime($beginTime);
|
||||
$this->metric->saveLogs("Deduplicate all consumed time: {$executeTime} seconds");
|
||||
}
|
||||
@@ -287,30 +345,44 @@ class metricZen extends metric
|
||||
*/
|
||||
protected function prepareMetricRecord($calcList)
|
||||
{
|
||||
/* 获取今天和昨天的日期。*/
|
||||
/* Get the date of today and yesterday. */
|
||||
$yesterday = date('j', strtotime('-1 day', strtotime('today')));
|
||||
$today = date('j');
|
||||
$options = array('year' => date('Y'), 'month' => date('n'), 'week' => substr(date('oW'), -2), 'day' => "$today,$yesterday");
|
||||
|
||||
$now = helper::now();
|
||||
$dateValues = $this->metric->parseDateStr($now);
|
||||
|
||||
/* 获取未关闭的对象id。*/
|
||||
/* Get the id of the unclosed objects. */
|
||||
$this->getValidObjects();
|
||||
$validObjects = $this->validObjects;
|
||||
$records = array();
|
||||
foreach($calcList as $code => $calc)
|
||||
{
|
||||
$metric = $this->metric->getByCode($code);
|
||||
/* 如果度量项是复用的,则计算复用的度量数据。*/
|
||||
/* If the metric is reused, calculate the reused metric data. */
|
||||
if($calc->reuse) $this->prepareReuseMetricResult($calc, $options);
|
||||
|
||||
/* 判断是否统计关闭时的对象的度量数据。*/
|
||||
$endWithClosing = preg_match('/_when_closing$/', $code);
|
||||
|
||||
$results = $calc->getResult($options);
|
||||
$records[$code] = array();
|
||||
if(is_array($results))
|
||||
{
|
||||
$scope = $metric->scope;
|
||||
foreach($results as $record)
|
||||
{
|
||||
$record = (object)$record;
|
||||
if(!is_numeric($record->value) || empty($record->value)) continue;
|
||||
|
||||
/* 如果度量项是产品、项目、执行且不统计关闭时的对象的度量数据,则过滤掉已关闭的数据。*/
|
||||
/* If the metric is product, project, execution, filter out the closed data. */
|
||||
if(!$endWithClosing && isset($validObjects[$scope]) && !isset($validObjects[$scope][$record->$scope])) continue;
|
||||
|
||||
$record->metricID = $calc->id;
|
||||
$record->metricCode = $code;
|
||||
$record->date = $now;
|
||||
$record->date = helper::now();
|
||||
$record->system = $metric->scope == 'system' ? 1 : 0;
|
||||
|
||||
$records[$code][] = $record;
|
||||
|
||||
@@ -78,6 +78,7 @@ $lang->misc->expiredCountTips = 'There are <span class="expired-tips text-blu
|
||||
$lang->misc->expiredPluginTips = 'Expired plugins are: %s. ';
|
||||
$lang->misc->expiringPluginTips = 'The plug-ins that will expire are: %s.';
|
||||
$lang->misc->expiredTipsForAdmin = 'There are %s plug-ins in the current system that will expire soon. To avoid affecting the regular use of the function, please renew or uninstall them in the system background plug-in management as soon as possible.';
|
||||
$lang->misc->metriclibTips = 'Added a new metric library indexing function, which can significantly improve the query speed of related metrics after updating the index. You can update it on the "Admin->System->Metric Library" page.';
|
||||
|
||||
$lang->misc->noticeRepair = "<h5>If you are not Administrator, contact your ZenTao Administrator to repair tables.</h5>
|
||||
<h5>If you are the Administrator, login your ZenTao host and create a file named <span>%s</span>.</h5>
|
||||
|
||||
@@ -78,6 +78,7 @@ $lang->misc->expiredCountTips = '系统中有<span class="expired-tips text-b
|
||||
$lang->misc->expiredPluginTips = '已到期的插件为:%s。';
|
||||
$lang->misc->expiringPluginTips = '即将到期的插件为:%s。';
|
||||
$lang->misc->expiredTipsForAdmin = '当前系统中有%s个插件即将到期,为避免影响功能的正常使用,请尽快到系统后台插件管理中进行续费或卸载处理。';
|
||||
$lang->misc->metriclibTips = '新增更新度量库索引功能,更新索引后可大幅度提升相关度量项的查询速度,可以到『后台->系统设置->度量库』页面更新。';
|
||||
|
||||
$lang->misc->noticeRepair = "<h5>普通用户请联系管理员进行修复</h5>
|
||||
<h5>管理员请登录禅道所在的服务器,创建<span>%s</span>文件。</h5>
|
||||
|
||||
@@ -84,6 +84,28 @@ class miscModel extends model
|
||||
return $remind;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取更新度量库表索引的通知。
|
||||
* Get the notification information about updating metriclib table index.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getMetriclibRemind(): string
|
||||
{
|
||||
if(!$this->app->user->admin) return '';
|
||||
if($this->config->edition == 'open') return '';
|
||||
|
||||
$remind = '';
|
||||
if(empty($this->config->global->metriclibShowed))
|
||||
{
|
||||
$remind .= '<p>' . $this->lang->misc->metriclibTips . '</p>';
|
||||
$remind .= "<p class='text-center mt-4'>" . html::a(helper::createLink('admin', 'metriclib'), $this->lang->misc->view, '', "id='metriclibButton' class='btn primary wide' data-app='admin'", false) . '</p>';
|
||||
$this->loadModel('setting')->setItem("{$this->app->user->account}.common.global.metriclibShowed", 1);
|
||||
}
|
||||
return $remind;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查一键安装包的安全性。
|
||||
* Check one click package.
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
|
||||
zenData('company')->gen(1);
|
||||
zenData('user')->gen(2);
|
||||
zenData('config')->gen(0);
|
||||
|
||||
su('user1');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 miscModel->getMetriclibRemind();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 非管理员用户返回空字符串。 @0
|
||||
- 管理员用户开源版返回空字符串。 @0
|
||||
- 管理员用户企业版返回正常内容。 @1
|
||||
- 管理员用户企业版返回正常内容后数据库中记录已显示过。 @1
|
||||
- 管理员用户企业版返回正常内容一次后第二次返回空字符串。 @0
|
||||
|
||||
*/
|
||||
|
||||
global $tester, $config;
|
||||
$config->edition = 'open';
|
||||
|
||||
$misc = $tester->loadModel('misc');
|
||||
$remind = "<p>新增更新度量库索引功能,更新索引后可大幅度提升相关度量项的查询速度";
|
||||
|
||||
r($misc->getMetriclibRemind()) && p() && e(0); //非管理员用户返回空字符串。
|
||||
|
||||
su('admin');
|
||||
|
||||
r($misc->getMetriclibRemind()) && p() && e(0); //管理员用户开源版返回空字符串。
|
||||
|
||||
$config->edition = 'biz';
|
||||
r(mb_substr($misc->getMetriclibRemind(), 0, 36) == $remind) && p() && e(1); //管理员用户企业版返回正常内容。
|
||||
|
||||
$showed = $tester->loadModel('setting')->getItem('owner=admin&module=common§ion=global&key=metriclibShowed');
|
||||
|
||||
r($showed) && p() && e(1); //管理员用户企业版返回正常内容后数据库中记录已显示过。
|
||||
|
||||
$config->global->metriclibShowed = $showed;
|
||||
|
||||
r($misc->getMetriclibRemind()) && p() && e(0); //管理员用户企业版返回正常内容一次后第二次返回空字符串。
|
||||
@@ -8,11 +8,23 @@ title=测试 miscModel->getPluginRemind();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 调用方法返回值 @0
|
||||
- 调用方法返回值 @0
|
||||
- 调用方法返回值 @0
|
||||
- 调用方法返回值 @0
|
||||
- 调用方法返回值 @0
|
||||
|
||||
*/
|
||||
|
||||
zenData('extension')->gen(10);
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('misc');
|
||||
|
||||
/* 由于没办法上传过期授权文件,这个单测无法实际测试。 */
|
||||
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
|
||||
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
|
||||
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
|
||||
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
|
||||
r($tester->misc->getPluginRemind()) && p() && e('0'); //调用方法返回值
|
||||
?>
|
||||
|
||||
@@ -16,6 +16,12 @@ window.onRenderCell = function(result, {row, col})
|
||||
link = oaReviewLink.replace('{module}', row.data.module);
|
||||
delete result[0].props.items[0]['data-toggle'];
|
||||
}
|
||||
else if(['mr', 'pullreq'].includes(row.data.module))
|
||||
{
|
||||
link = $.createLink(row.data.module, 'view', 'id=' + row.data.id);
|
||||
result[0].props.items[0]['disabled'] = viewPrivs[row.data.module];
|
||||
delete result[0].props.items[0]['data-toggle'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!noFlowAuditModules.includes(row.data.module) && row.data.module != 'charter') link = flowReviewLink;
|
||||
|
||||
@@ -165,6 +165,7 @@ $lang->my->featureBar['audit']['story'] = $lang->SRCommon;
|
||||
$lang->my->featureBar['audit']['requirement'] = $lang->URCommon;
|
||||
$lang->my->featureBar['audit']['epic'] = $lang->ERCommon;
|
||||
$lang->my->featureBar['audit']['testcase'] = 'Test case';
|
||||
$lang->my->featureBar['audit']['mr'] = 'Merge request';
|
||||
if(in_array($config->edition, array('max', 'ipd')) and (helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus'))) $lang->my->featureBar['audit']['project'] = $lang->projectCommon;
|
||||
if($config->edition != 'open') $lang->my->featureBar['audit']['feedback'] = 'Feedback';
|
||||
if($config->edition != 'open' and helper::hasFeature('OA')) $lang->my->featureBar['audit']['oa'] = 'OA';
|
||||
|
||||
@@ -165,6 +165,7 @@ $lang->my->featureBar['audit']['story'] = $lang->SRCommon;
|
||||
$lang->my->featureBar['audit']['requirement'] = $lang->URCommon;
|
||||
$lang->my->featureBar['audit']['epic'] = $lang->ERCommon;
|
||||
$lang->my->featureBar['audit']['testcase'] = 'Test case';
|
||||
$lang->my->featureBar['audit']['mr'] = 'Merge request';
|
||||
if(in_array($config->edition, array('max', 'ipd')) and (helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus'))) $lang->my->featureBar['audit']['project'] = $lang->projectCommon;
|
||||
if($config->edition != 'open') $lang->my->featureBar['audit']['feedback'] = 'Feedback';
|
||||
if($config->edition != 'open' and helper::hasFeature('OA')) $lang->my->featureBar['audit']['oa'] = 'OA';
|
||||
|
||||
@@ -165,6 +165,7 @@ $lang->my->featureBar['audit']['story'] = $lang->SRCommon;
|
||||
$lang->my->featureBar['audit']['requirement'] = $lang->URCommon;
|
||||
$lang->my->featureBar['audit']['epic'] = $lang->ERCommon;
|
||||
$lang->my->featureBar['audit']['testcase'] = 'Test case';
|
||||
$lang->my->featureBar['audit']['mr'] = 'Merge request';
|
||||
if(in_array($config->edition, array('max', 'ipd')) and (helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus'))) $lang->my->featureBar['audit']['project'] = $lang->projectCommon;
|
||||
if($config->edition != 'open') $lang->my->featureBar['audit']['feedback'] = 'Feedback';
|
||||
if($config->edition != 'open' and helper::hasFeature('OA')) $lang->my->featureBar['audit']['oa'] = 'OA';
|
||||
|
||||
@@ -165,6 +165,7 @@ $lang->my->featureBar['audit']['story'] = $lang->SRCommon;
|
||||
$lang->my->featureBar['audit']['requirement'] = $lang->URCommon;
|
||||
$lang->my->featureBar['audit']['epic'] = $lang->ERCommon;
|
||||
$lang->my->featureBar['audit']['testcase'] = '用例';
|
||||
$lang->my->featureBar['audit']['mr'] = '合并请求';
|
||||
if(in_array($config->edition, array('max', 'ipd')) and (helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus'))) $lang->my->featureBar['audit']['project'] = $lang->projectCommon;
|
||||
if($config->edition != 'open') $lang->my->featureBar['audit']['feedback'] = '反馈';
|
||||
if($config->edition != 'open' and helper::hasFeature('OA')) $lang->my->featureBar['audit']['oa'] = '办公';
|
||||
|
||||
@@ -956,6 +956,7 @@ class myModel extends model
|
||||
if($this->config->vision != 'or' && $this->getReviewingApprovals('id_desc', true)) $typeList[] = 'project';
|
||||
if($this->getReviewingFeedbacks('id_desc', true)) $typeList[] = 'feedback';
|
||||
if($this->config->vision != 'or' && $this->getReviewingOA('status', true)) $typeList[] = 'oa';
|
||||
if($this->config->vision != 'or' && $this->getReviewingMRs('id_desc')) $typeList[] = 'mr';
|
||||
$typeList = array_merge($typeList, $this->getReviewingFlows('all', 'id_desc', true));
|
||||
|
||||
$flows = $this->config->edition == 'open' ? array() : $this->dao->select('module,name')->from(TABLE_WORKFLOW)->where('module')->in($typeList)->andWhere('buildin')->eq(0)->fetchPairs('module', 'name');
|
||||
@@ -991,6 +992,7 @@ class myModel extends model
|
||||
if($vision != 'or' && ($browseType == 'all' || $browseType == 'testcase') && common::hasPriv('testcase', 'review')) $reviewList = array_merge($reviewList, $this->getReviewingCases());
|
||||
if($vision != 'or' && ($browseType == 'all' || $browseType == 'project')) $reviewList = array_merge($reviewList, $this->getReviewingApprovals());
|
||||
if($vision != 'or' && ($browseType == 'all' || $browseType == 'oa')) $reviewList = array_merge($reviewList, $this->getReviewingOA());
|
||||
if($vision != 'or' && ($browseType == 'all' || $browseType == 'mr')) $reviewList = array_merge($reviewList, $this->getReviewingMRs());
|
||||
if($browseType == 'all' || !in_array($browseType, $this->config->my->noFlowAuditModules)) $reviewList = array_merge($reviewList, $this->getReviewingFlows($browseType));
|
||||
if(($browseType == 'all' || $browseType == 'feedback') && common::hasPriv('feedback', 'review')) $reviewList = array_merge($reviewList, $this->getReviewingFeedbacks());
|
||||
if(empty($reviewList)) return array();
|
||||
@@ -1151,6 +1153,25 @@ class myModel extends model
|
||||
return array_values($cases);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待评审的MR。
|
||||
* Get reviewing mrs.
|
||||
*
|
||||
* @param string $orderBy
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getReviewingMRs(string $orderBy = 'id_desc'): array
|
||||
{
|
||||
return $this->dao->select("`id`, `title`, IF(`isFlow`='1', 'pullreq', 'mr') AS type, `createdDate` AS time, `approvalStatus` AS status, 0 AS product, 0 AS project")->from(TABLE_MR)
|
||||
->where('deleted')->eq('0')
|
||||
->andWhere('approvalStatus')->notIn(array('approved', 'rejected'))
|
||||
->andWhere('status')->ne('closed')
|
||||
->andWhere('assignee')->eq($this->app->user->account)
|
||||
->orderBy($orderBy)
|
||||
->fetchAll('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待评审的审批。
|
||||
* Get reviewing approvals.
|
||||
|
||||
@@ -6,6 +6,7 @@ class myTest
|
||||
{
|
||||
global $tester;
|
||||
$this->objectModel = $tester->loadModel('my');
|
||||
$tester->dao->delete()->from(TABLE_ACTIONRECENT)->exec();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,6 +441,25 @@ class myTest
|
||||
return !empty($return) ? implode(',', array_column($return, 'id')) : 'empty';
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试获取审批中的合并请求。
|
||||
* Test get reviewing mrs.
|
||||
*
|
||||
* @param string $account
|
||||
* @param string $orderBy
|
||||
* @param bool $checkExist
|
||||
* @access public
|
||||
* @return string|array
|
||||
*/
|
||||
public function getReviewingMRsTest(string $account, string $orderBy): string|array
|
||||
{
|
||||
su($account);
|
||||
$result = $this->objectModel->getReviewingMRs($orderBy);
|
||||
if(dao::isError()) return dao::getError();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试获取审批中的用例。
|
||||
* Test get reviewing cases.
|
||||
|
||||
@@ -37,8 +37,8 @@ $app->loadLang('action');
|
||||
$my = new myTest();
|
||||
|
||||
$actions = $my->getActionsTest();
|
||||
r($actions) && p('0') && e('96'); // 正常查询action
|
||||
r($actions) && p('1') && e('64'); // 正常查询action
|
||||
r($actions) && p('2') && e('32'); // 正常查询action
|
||||
r($actions) && p('3') && e('95'); // 正常查询action
|
||||
r($actions) && p('4') && e('63'); // 正常查询action
|
||||
r($actions) && p('0') && e('93'); // 正常查询action
|
||||
r($actions) && p('1') && e('62'); // 正常查询action
|
||||
r($actions) && p('2') && e('31'); // 正常查询action
|
||||
r($actions) && p('3') && e('92'); // 正常查询action
|
||||
r($actions) && p('4') && e('61'); // 正常查询action
|
||||
|
||||
@@ -18,6 +18,7 @@ cid=1
|
||||
- 测试获取排序 id_desc 的审批是否存在。 @empty
|
||||
- 测试获取排序 id_asc 的审批id。 @empty
|
||||
- 测试获取排序 id_asc 的审批是否存在。 @empty
|
||||
- 测试获取排序 id_asc 的审批是否存在。 @empty
|
||||
|
||||
*/
|
||||
|
||||
@@ -29,4 +30,5 @@ $checkExist = array(false, true);
|
||||
r($my->getReviewingApprovalsTest($orderBy[0], $checkExist[0])) && p() && e('empty'); // 测试获取排序 id_desc 的审批id。
|
||||
r($my->getReviewingApprovalsTest($orderBy[0], $checkExist[1])) && p() && e('empty'); // 测试获取排序 id_desc 的审批是否存在。
|
||||
r($my->getReviewingApprovalsTest($orderBy[1], $checkExist[0])) && p() && e('empty'); // 测试获取排序 id_asc 的审批id。
|
||||
r($my->getReviewingApprovalsTest($orderBy[1], $checkExist[1])) && p() && e('empty'); // 测试获取排序 id_asc 的审批是否存在。
|
||||
r($my->getReviewingApprovalsTest($orderBy[1], $checkExist[1])) && p() && e('empty'); // 测试获取排序 id_asc 的审批是否存在。
|
||||
r($my->getReviewingApprovalsTest($orderBy[1], $checkExist[1])) && p() && e('empty'); // 测试获取排序 id_asc 的审批是否存在。
|
||||
|
||||
@@ -5,6 +5,7 @@ include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/my.unittest.class.php';
|
||||
|
||||
zenData('case')->gen('20');
|
||||
zenData('product')->gen('20');
|
||||
zenData('user')->gen('2');
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ zenData('demand')->loadYaml('demand_reviewing')->gen('20');
|
||||
zenData('demandreview')->gen('20');
|
||||
zenData('user')->gen('10');
|
||||
zenData('action')->gen('0');
|
||||
zenData('mr')->gen('1');
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,14 +19,10 @@ title=测试 myModel->getReviewingList();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试获取用户 admin 类型 all 排序 id_desc 不分页 的待评审类型。 @testcase,17;testcase,13;story,10;testcase,9;story,8;story,6;testcase,5;story,4;story,2;testcase,1;
|
||||
|
||||
- 测试获取用户 admin 类型 all 排序 id_asc 不分页 的待评审类型。 @testcase,1;story,2;story,4;testcase,5;story,6;story,8;testcase,9;story,10;testcase,13;testcase,17;
|
||||
|
||||
- 测试获取用户 admin 类型 all 排序 id_desc 不分页 的待评审类型。 @testcase,17;testcase,13;story,10;testcase,9;story,8;story,6;testcase,5;story,4;story,2;testcase,1;mr,1;
|
||||
- 测试获取用户 admin 类型 all 排序 id_asc 不分页 的待评审类型。 @testcase,1;mr,1;story,2;story,4;testcase,5;story,6;story,8;testcase,9;story,10;testcase,13;testcase,17;
|
||||
- 测试获取用户 admin 类型 all 排序 id_desc 获取前两个 的待评审类型。 @testcase,17;testcase,13;
|
||||
|
||||
- 测试获取用户 admin 类型 all 排序 id_asc 获取前两个 的待评审类型。 @testcase,1;story,2;
|
||||
|
||||
- 测试获取用户 admin 类型 all 排序 id_asc 获取前两个 的待评审类型。 @testcase,1;mr,1;
|
||||
- 测试获取用户 admin 类型 createdbyme 排序 id_desc 不分页 的待评审类型。 @0
|
||||
- 测试获取用户 admin 类型 createdbyme 排序 id_asc 不分页 的待评审类型。 @0
|
||||
- 测试获取用户 admin 类型 createdbyme 排序 id_desc 获取前两个 的待评审类型。 @0
|
||||
@@ -33,7 +30,7 @@ cid=1
|
||||
|
||||
*/
|
||||
|
||||
global $teser, $config, $app;
|
||||
global $tester, $config, $app;
|
||||
$app->rawModule = 'my';
|
||||
$app->rawMethod = 'getReviewingList';
|
||||
$config->edition = 'open';
|
||||
@@ -50,10 +47,10 @@ $browseType = array('all', 'createdbyme');
|
||||
$orderBy = array('id_desc', 'id_asc');
|
||||
$pageList = array(null, $pager);
|
||||
|
||||
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[0], $pageList[0])) && p() && e('testcase,17;testcase,13;story,10;testcase,9;story,8;story,6;testcase,5;story,4;story,2;testcase,1;'); // 测试获取用户 admin 类型 all 排序 id_desc 不分页 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[1], $pageList[0])) && p() && e('testcase,1;story,2;story,4;testcase,5;story,6;story,8;testcase,9;story,10;testcase,13;testcase,17;'); // 测试获取用户 admin 类型 all 排序 id_asc 不分页 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[0], $pageList[0])) && p() && e('testcase,17;testcase,13;story,10;testcase,9;story,8;story,6;testcase,5;story,4;story,2;testcase,1;mr,1;'); // 测试获取用户 admin 类型 all 排序 id_desc 不分页 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[1], $pageList[0])) && p() && e('testcase,1;mr,1;story,2;story,4;testcase,5;story,6;story,8;testcase,9;story,10;testcase,13;testcase,17;'); // 测试获取用户 admin 类型 all 排序 id_asc 不分页 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[0], $pageList[1])) && p() && e('testcase,17;testcase,13;'); // 测试获取用户 admin 类型 all 排序 id_desc 获取前两个 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[1], $pageList[1])) && p() && e('testcase,1;story,2;'); // 测试获取用户 admin 类型 all 排序 id_asc 获取前两个 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[0], $orderBy[1], $pageList[1])) && p() && e('testcase,1;mr,1;'); // 测试获取用户 admin 类型 all 排序 id_asc 获取前两个 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[1], $orderBy[0], $pageList[0])) && p() && e('0'); // 测试获取用户 admin 类型 createdbyme 排序 id_desc 不分页 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[1], $orderBy[1], $pageList[0])) && p() && e('0'); // 测试获取用户 admin 类型 createdbyme 排序 id_asc 不分页 的待评审类型。
|
||||
r($my->getReviewingListTest($account[0], $browseType[1], $orderBy[0], $pageList[1])) && p() && e('0'); // 测试获取用户 admin 类型 createdbyme 排序 id_desc 获取前两个 的待评审类型。
|
||||
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/my.unittest.class.php';
|
||||
|
||||
zenData('mr')->loadYaml('mrreview')->gen('10');
|
||||
zenData('user')->gen('5');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 myModel->getReviewingStories();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试获取用户 account 排序 id_desc 的合并请求。
|
||||
- 第0条的title属性 @Test MR9
|
||||
- 第0条的id属性 @9
|
||||
- 测试获取用户 account 排序 id_asc 的合并请求。
|
||||
- 第0条的title属性 @Test MR3
|
||||
- 第0条的id属性 @3
|
||||
- 测试获取没有审批用户的数据。 @0
|
||||
*/
|
||||
|
||||
$account = array('admin', 'user1');
|
||||
$orderBy = array('id_desc', 'id_asc');
|
||||
$checkExist = array(false, true);
|
||||
|
||||
$my = new myTest();
|
||||
r($my->getReviewingMRsTest($account[0], $orderBy[0])) && p('9:title,id') && e('Test MR9,9'); // 测试获取用户 account 排序 id_desc 的合并请求。
|
||||
r($my->getReviewingMRsTest($account[0], $orderBy[1])) && p('3:title,id') && e('Test MR3,3'); // 测试获取用户 account 排序 id_asc 的合并请求。
|
||||
r($my->getReviewingMRsTest($account[1], $orderBy[0])) && p() && e('0'); // 测试获取没有审批用户的数据。
|
||||
@@ -10,6 +10,13 @@ zenData('case')->gen('20');
|
||||
zenData('demand')->loadYaml('demand_reviewing')->gen('20');
|
||||
zenData('demandreview')->gen('20');
|
||||
zenData('user')->gen('10');
|
||||
zenData('userview')->gen('0');
|
||||
zenData('product')->gen('0');
|
||||
zenData('testcase')->gen('0');
|
||||
zenData('mr')->gen('0');
|
||||
zenData('requirement')->gen('0');
|
||||
zenData('workflow')->gen('0');
|
||||
zenData('action')->gen('0');
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,14 +25,10 @@ timeout=0
|
||||
cid=1
|
||||
|
||||
- 测试获取用户 admin 的待评审类型。 @all,story,requirement,testcase
|
||||
|
||||
- 测试获取用户 user1 的待评审类型。 @all,testcase
|
||||
|
||||
- 测试获取用户 user2 的待评审类型。 @all,testcase
|
||||
|
||||
- 测试获取用户 user3 的待评审类型。 @all,testcase
|
||||
|
||||
- 测试获取用户 user4 的待评审类型。 @all,testcase
|
||||
- 测试获取用户 user1 的待评审类型。 @all
|
||||
- 测试获取用户 user2 的待评审类型。 @all
|
||||
- 测试获取用户 user3 的待评审类型。 @all
|
||||
- 测试获取用户 user4 的待评审类型。 @all
|
||||
|
||||
*/
|
||||
|
||||
@@ -34,10 +37,11 @@ $config->edition = 'open';
|
||||
|
||||
$my = new myTest();
|
||||
|
||||
su('admin');
|
||||
$account = array('admin', 'user1', 'user2', 'user3', 'user4');
|
||||
|
||||
r($my->getReviewingTypeListTest($account[0])) && p() && e('all,story,requirement,testcase'); // 测试获取用户 admin 的待评审类型。
|
||||
r($my->getReviewingTypeListTest($account[1])) && p() && e('all,testcase'); // 测试获取用户 user1 的待评审类型。
|
||||
r($my->getReviewingTypeListTest($account[2])) && p() && e('all,testcase'); // 测试获取用户 user2 的待评审类型。
|
||||
r($my->getReviewingTypeListTest($account[3])) && p() && e('all,testcase'); // 测试获取用户 user3 的待评审类型。
|
||||
r($my->getReviewingTypeListTest($account[4])) && p() && e('all,testcase'); // 测试获取用户 user4 的待评审类型。
|
||||
r($my->getReviewingTypeListTest($account[1])) && p() && e('all'); // 测试获取用户 user1 的待评审类型。
|
||||
r($my->getReviewingTypeListTest($account[2])) && p() && e('all'); // 测试获取用户 user2 的待评审类型。
|
||||
r($my->getReviewingTypeListTest($account[3])) && p() && e('all'); // 测试获取用户 user3 的待评审类型。
|
||||
r($my->getReviewingTypeListTest($account[4])) && p() && e('all'); // 测试获取用户 user4 的待评审类型。
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
title: zt_mr
|
||||
author: Yang LI
|
||||
version: "1.0"
|
||||
fields:
|
||||
- field: id
|
||||
range: 1-10
|
||||
- field: hostID
|
||||
range: 1
|
||||
- field: sourceProject
|
||||
range: 42
|
||||
- field: sourceBranch
|
||||
prefix: "branch-"
|
||||
range: 1-10
|
||||
- field: targetProject
|
||||
range: 42
|
||||
- field: targetBranch
|
||||
range: master
|
||||
- field: mriid
|
||||
range: 1-10
|
||||
- field: title
|
||||
prefix: "Test MR"
|
||||
range: 1-10
|
||||
- field: status
|
||||
range: opened,closed
|
||||
- field: mergeStatus
|
||||
range: can_be_merged
|
||||
- field: approvalStatus
|
||||
range: approved,rejected,[]
|
||||
- field: repoID
|
||||
range: 1
|
||||
- field: jobID
|
||||
range: 0
|
||||
- field: compileID
|
||||
range: 0
|
||||
- field: createdBy
|
||||
range: admin
|
||||
- field: assignee
|
||||
range: admin,user1
|
||||
- field: deleted
|
||||
range: 0
|
||||
@@ -28,9 +28,14 @@ foreach(array_keys($lang->my->featureBar['audit']) as $type)
|
||||
$viewPrivs[$type] = $viewPriv;
|
||||
}
|
||||
}
|
||||
$reviewPrivs['review'] = hasPriv('review', 'access');
|
||||
$viewPrivs['review'] = hasPriv('review', 'view');
|
||||
$viewPrivs['task'] = hasPriv('task', 'view');
|
||||
$reviewPrivs['review'] = hasPriv('review', 'access');
|
||||
$reviewPrivs['mr'] = hasPriv('mr', 'view');
|
||||
$reviewPrivs['pullreq'] = hasPriv('pullreq', 'view');
|
||||
|
||||
$viewPrivs['review'] = hasPriv('review', 'view');
|
||||
$viewPrivs['task'] = hasPriv('task', 'view');
|
||||
$viewPrivs['mr'] = hasPriv('mr', 'view');
|
||||
$viewPrivs['pullreq'] = hasPriv('pullreq', 'view');
|
||||
|
||||
jsVar('reviewLink', createLink('{module}', 'review', 'id={id}'));
|
||||
jsVar('flowReviewLink', createLink('{module}', 'approvalreview', 'id={id}'));
|
||||
@@ -103,6 +108,12 @@ foreach($reviewList as $review)
|
||||
if(isset($flows[$review->type]) && $rawMethod != 'audit') $statusList = $lang->approval->statusList;
|
||||
}
|
||||
|
||||
if($type == 'mr')
|
||||
{
|
||||
$this->app->loadLang('mr');
|
||||
$statusList = $lang->mr->approvalStatusList;
|
||||
}
|
||||
|
||||
$review->type = $typeName;
|
||||
$review->status = zget($statusList, $review->status, '');
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class editProductTester extends tester
|
||||
$form->dom->editBtn->click();
|
||||
if(isset($product->name)) $form->dom->name->setValue($product->name);
|
||||
$form->dom->btn($this->lang->save)->click();
|
||||
$form->wait(1);
|
||||
$form->wait(2);
|
||||
if($this->response('method') != 'view')
|
||||
{
|
||||
if($this->checkFormTips('product')) return $this->success('编辑产品表单提示信息正确');
|
||||
|
||||
@@ -74,7 +74,7 @@ $release->gen(5);
|
||||
$tester = new kanbanTester();
|
||||
$tester->login();
|
||||
|
||||
r($tester->checkKanbanData('plan', '3')) && p('message,status') && e('未过期计划数正确,SUCCESS');//检查未过期计划数
|
||||
r($tester->checkKanbanData('plan', '4')) && p('message,status') && e('未过期计划数正确,SUCCESS');//检查未过期计划数
|
||||
r($tester->checkKanbanData('project', '2')) && p('message,status') && e('进行中的项目数正确,SUCCESS');//检查进行中的项目数
|
||||
r($tester->checkKanbanData('execution', '1')) && p('message,status') && e('进行中的执行数正确,SUCCESS');//检查进行中的执行数
|
||||
r($tester->checkKanbanData('release', '4')) && p('message,status') && e('正常发布数正确,SUCCESS');//检查正常发布数
|
||||
|
||||
@@ -27,7 +27,7 @@ $fields->field('type')
|
||||
$fields->field('code')->control('input');
|
||||
|
||||
$fields->field('PO')
|
||||
->control(array('control' => 'remotepicker', 'params' => 'nodeleted|pofirst|noclosed'))
|
||||
->control(array('control' => 'remotepicker', 'params' => 'nodeleted,pofirst,noclosed'))
|
||||
->value(data('fields.PO.default'));
|
||||
|
||||
$fields->field('reviewer')
|
||||
@@ -37,11 +37,11 @@ $fields->field('reviewer')
|
||||
|
||||
$fields->field('QD')
|
||||
->wrapBefore()
|
||||
->control(array('control' => 'remotepicker', 'params' => 'nodeleted|qdfirst|noclosed'))
|
||||
->control(array('control' => 'remotepicker', 'params' => 'nodeleted,qdfirst,noclosed'))
|
||||
->value(data('fields.QD.default'));
|
||||
|
||||
$fields->field('RD')
|
||||
->control(array('control' => 'remotepicker', 'params' => 'nodeleted|devfirst|noclosed'))
|
||||
->control(array('control' => 'remotepicker', 'params' => 'nodeleted,devfirst,noclosed'))
|
||||
->value(data('fields.RD.default'));
|
||||
|
||||
$fields->field('desc')
|
||||
|
||||
@@ -69,5 +69,4 @@ $task = $tester->programplan->loadModel('task')->getById(4);
|
||||
r((array)$tester->programplan->buildTaskDataForGantt($task, $dateLimit, 0)) && p('id,type,text,owner_id,start_date,parent') && e("4,task,<span class='pri-4 align-middle' title='4'>4</span> <span class='gantt_title'>开发任务14</span>,``,28-09-2023,0"); //检查构建分组Gantt数据。
|
||||
|
||||
$task = $tester->programplan->loadModel('task')->getById(5);
|
||||
r((array)$tester->programplan->buildTaskDataForGantt($task, $dateLimit, 0)) && p('id,type,text,owner_id,start_date,parent') && e("5,task,<span class='pri-1 align-middle' title='1'>1</span> <span class='gantt_title'>开发任务15</span>,``,28-09-2023,0"); //检查构建分组Gantt数据。
|
||||
|
||||
r((array)$tester->programplan->buildTaskDataForGantt($task, $dateLimit, 0)) && p('id,type,text,owner_id,start_date,parent') && e("5,task,<span class='pri-1 align-middle' title='1'>1</span> <span class='gantt_title'>开发任务15</span>,``,28-09-2023,0"); //检查构建分组Gantt数据。
|
||||
@@ -189,6 +189,9 @@ class projectModel extends model
|
||||
|
||||
$action = strtolower($action);
|
||||
|
||||
if($action == 'publishtemplate') return $project->status == 'wait' || $project->status == 'closed';
|
||||
if($action == 'disabletemplate') return $project->status == 'doing';
|
||||
|
||||
if($action == 'close') return $project->status != 'closed';
|
||||
if($action == 'group') return $project->model != 'kanban';
|
||||
if($action == 'start') return $project->status == 'wait' || $project->status == 'suspended';
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/ui.php';
|
||||
class editProjectLiteTester extends tester
|
||||
{
|
||||
/**
|
||||
* 编辑项目时检查页面输入。
|
||||
* Check the page input when editing the project.
|
||||
*
|
||||
* @param array $project
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function checkInput($project = array())
|
||||
{
|
||||
$form = $this->loadPage('project', 'browse');
|
||||
$this->switchVision('lite');
|
||||
$form = $this->initForm('project', 'edit', array('projecID' => 1), 'appIframe-project');
|
||||
if(isset($project['name'])) $form->dom->name->setValue($project['name']);
|
||||
if(isset($project['begin'])) $form->dom->begin->datePicker($project['begin']);
|
||||
if(isset($project['end'])) $form->dom->end->datePicker($project['end']);
|
||||
if(isset($project['PM'])) $form->dom->PM->picker($project['PM']);
|
||||
|
||||
$form->dom->btn($this->lang->save)->click();
|
||||
$form->wait(2);
|
||||
return $this->checkResult($project);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑项目后结果检查。
|
||||
* Check the result after editing the project.
|
||||
*
|
||||
* @param array $project
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function checkResult($project = array())
|
||||
{
|
||||
/* 检查编辑页面提示信息 */
|
||||
$form = $this->loadPage('project', 'edit');
|
||||
if($this->response('method') != 'view')
|
||||
{
|
||||
/* 检查项目名称不能为空 */
|
||||
if($form->dom->nameTip && $project['name'] == '')
|
||||
{
|
||||
$nameTipText = $form->dom->nameTip->getText();
|
||||
$nameTip = sprintf($this->lang->error->notempty, $this->lang->project->name);
|
||||
return ($nameTipText == $nameTip) ? $this->success('编辑项目表单页提示信息正确') : $this->failed('编辑项目表单页提示信息不正确');
|
||||
}
|
||||
/* 检查项目名称唯一 */
|
||||
if($form->dom->nameTip && $project['name'] != '')
|
||||
{
|
||||
$existName = '运营界面项目2';
|
||||
$nameTipText = $form->dom->nameTip->getText();
|
||||
$nameTip = sprintf($this->lang->error->unique, $this->lang->project->name, $existName);
|
||||
return ($nameTipText == $nameTip) ? $this->success('编辑项目表单页提示信息正确') : $this->failed('编辑项目表单页提示信息不正确');
|
||||
}
|
||||
if($form->dom->endTip)
|
||||
{
|
||||
/* 检查结束日期不能为空 */
|
||||
if($project['end'] == '')
|
||||
{
|
||||
$endTipText = $form->dom->endTip->getText();
|
||||
$endTip = sprintf($this->lang->copyProject->endTip, '');
|
||||
return ($endTipText == $endTip) ? $this->success('编辑项目表单页提示信息正确') : $this->failed('编辑项目表单页提示信息不正确');
|
||||
}
|
||||
/* 检查结束日期不能小于开始日期 */
|
||||
if($project['begin'] > $project['end'])
|
||||
{
|
||||
$endTipText = $form->dom->endTip->getText();
|
||||
$endTip = sprintf($this->lang->error->gt, $this->lang->project->end, $project['begin']);
|
||||
return ($endTipText == $endTip) ? $this->success('编辑项目表单页提示信息正确') : $this->failed('编辑项目表单页提示信息不正确');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 跳转到项目列表页面,按照项目名称进行搜索 */
|
||||
$browsePage = $this->initForm('project', 'browse');
|
||||
$browsePage->dom->search($searchList = array("项目名称,包含,{$project['name']}"));
|
||||
$browsePage->wait(2);
|
||||
$browsePage->dom->projectNameLite->click();
|
||||
/* 进入项目概况页面 */
|
||||
$browsePage->dom->settings->click();
|
||||
$viewPage = $this->loadPage('project', 'view');
|
||||
$viewPage->wait(2);
|
||||
|
||||
/* 断言检查字段信息是否正确 */
|
||||
if($viewPage->dom->projectNameLite->getText() != $project['name']) return $this->failed('名称错误');
|
||||
if($viewPage->dom->aclLite->getText() != $this->lang->project->shortAclList->open) return $this->failed('权限错误');
|
||||
return $this->success('编辑项目成功');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/ui.php';
|
||||
class teamTesterForLite extends tester
|
||||
{
|
||||
/**
|
||||
* Check remove members for lite.
|
||||
* 运营界面移除项目团队成员
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function removeMembers()
|
||||
{
|
||||
$this->switchVision('lite');
|
||||
$form = $this->initForm('project', 'team', array('projectID' => '1'), 'appIframe-project');
|
||||
$browseFirAccount1 = $form->dom->browseFirAccount->getText();
|
||||
$form->dom->unlinkBtn->click();
|
||||
$form->dom->confirmBtn->click();
|
||||
$form->wait(2);
|
||||
|
||||
//添加断言,判断是否移除团队成员成功
|
||||
if($form->dom->browseFirAccount->getText() == $browseFirAccount1) return $this->failed('项目团队成员移除失败');
|
||||
return $this->success('项目团队成员移除成功');
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user