Merge branch release/22.0.beta of zentao/zentaopms (#13251)
This commit is contained in:
+2
-2
@@ -7,13 +7,13 @@ Z PUBLIC LICENSE(ZPL) 1.2
|
||||
|
||||
许可
|
||||
|
||||
Z PUBLIC LICENSE 由禅道软件(青岛)有限公司(www.easycorp.cn)起草,简称ZPL协议。
|
||||
Z PUBLIC LICENSE 由禅道软件(青岛)有限公司(www.chandao.com)起草,简称ZPL协议。
|
||||
任何人均可使用该协议来发布开源软件,并可对下面协议正文中以下划线标注的空白部分做相应修改,
|
||||
除此之外的任何内容不得做任何修改。禅道软件(青岛)有限公司拥有对该协议条款的最终解释权。
|
||||
|
||||
前言:
|
||||
|
||||
禅道项目管理软件(以下简称该软件)由 禅道软件(青岛)有限公司(www.easycorp.cn)开发(以下简称我)。我依法拥有该软件的所有版权。
|
||||
禅道项目管理软件(以下简称该软件)由 禅道软件(青岛)有限公司(www.chandao.com)开发(以下简称我)。我依法拥有该软件的所有版权。
|
||||
本着共享开放的角度,我以开放源代码的形式发布该软件。您可以在遵守该协议的前提下使用该软件。
|
||||
自您安装该软件开始,您和我之间的合同关系自动成立。除非您停止使用该软件或与我有签署额外合同,
|
||||
您须认真遵循该授权协议约定的每一条款。
|
||||
|
||||
+120
-116
@@ -1,116 +1,120 @@
|
||||
<?php
|
||||
$routes = array();
|
||||
|
||||
$routes['/programs'] = array('response' => 'programs(array),pager');
|
||||
$routes['/programs/:programID'] = array('redirect' => '/programs/:programID/edit', 'response' => 'program');
|
||||
$routes['/programs/:programID/projects'] = array('redirect' => '/programs/:programID/project', 'response' => 'projectStats(array)|projects,pager');
|
||||
$routes['/programs/:programID/products'] = array('redirect' => '/programs/:programID/product', 'response' => 'products(array),pager');
|
||||
|
||||
$routes['/products'] = array('redirect' => '/products/all');
|
||||
$routes['/products/all'] = array('response' => 'productStats|products,pager');
|
||||
$routes['/products/browse'] = array('response' => 'stories,pager', 'response' => 'stories(array),pager'); // stories
|
||||
$routes['/products/:productID'] = array('response' => 'product,dynamics,members,branches,reviewers');
|
||||
|
||||
$routes['/products/:productID/stories'] = array('redirect' => '/products/browse?productID=:productID');
|
||||
$routes['/projects/:projectID/stories'] = array('redirect' => '/projectstories/story?projectID=:projectID', 'response' => 'stories(array),pager');
|
||||
$routes['/executions/:executionID/stories'] = array('redirect' => '/executions/story?executionID=:executionID');
|
||||
$routes['/stories/:storyID'] = array('response' => 'story,actions(array)');
|
||||
|
||||
$routes['/products/:productID/epics'] = array('redirect' => '/products/browse?productID=:productID&storyType=epic', 'response' => 'stories(array)|epics,pager');
|
||||
$routes['/epics/:storyID'] = array('response' => 'story|epic,actions(array)');
|
||||
|
||||
$routes['/products/:productID/requirements'] = array('redirect' => '/products/browse?productID=:productID&storyType=requirement', 'response' => 'stories(array)|requirements,pager');
|
||||
$routes['/requirements/:storyID'] = array('response' => 'story|requirement,actions(array)');
|
||||
|
||||
$routes['/products/:productID/productplans'] = array('redirect' => '/productplans?productID=:productID', 'response' => 'plans(array)|productplans,pager');
|
||||
$routes['/productplans/:planID'] = array('response' => 'plan|productplan,actions(array)');
|
||||
|
||||
$routes['/products/:productID/releases'] = array('redirect' => '/releases?productID=:productID', 'response' => 'releases,pager');
|
||||
$routes['/projects/:projectID/releases'] = array('redirect' => '/projectreleases?projectID=:projectID', 'response' => 'releases,pager');
|
||||
$routes['/releases/:releaseID'] = array('response' => 'release,actions(array)');
|
||||
|
||||
$routes['/projects'] = array('response' => 'projectStats|projects,pager');
|
||||
$routes['/projects/list/:browseType'] = array('redirect' => '/projects?browseType=:browseType');
|
||||
$routes['/projects/execution'] = array('response' => 'executionStats|executions,pager');
|
||||
$routes['/projects/build'] = array('response' => 'builds,pager');
|
||||
$routes['/projects/bug'] = array('response' => 'bugs,pager', 'response' => 'bugs(array),pager');
|
||||
$routes['/projects/testcase'] = array('response' => 'cases(array)|testcases,pager');
|
||||
$routes['/projects/testtask'] = array('response' => 'tasks(array)|testtasks,pager');
|
||||
$routes['/projects/testreport'] = array('response' => 'reports(array)|testreports,pager');
|
||||
$routes['/projects/:projectID'] = array('response' => 'project');
|
||||
|
||||
$routes['/executions'] = array('method' => 'all', 'response' => 'executionStats|executions,pager');
|
||||
$routes['/projects/:projectID/executions'] = array('redirect' => '/projects/execution?projectID=:projectID');
|
||||
$routes['/executions/task'] = array('response' => 'tasks(array),pager');
|
||||
$routes['/executions/story'] = array('response' => 'stories(array),pager');
|
||||
$routes['/executions/build'] = array('response' => 'builds,pager');
|
||||
$routes['/executions/bug'] = array('response' => 'bugs(array),pager');
|
||||
$routes['/executions/testcase'] = array('response' => 'cases(array)|testcases,pager');
|
||||
$routes['/executions/testtask'] = array('response' => 'tasks(array)|testtasks,pager');
|
||||
$routes['/executions/testreport'] = array('response' => 'reports(array)|testreports,pager');
|
||||
$routes['/executions/:executionID'] = array('response' => 'execution');
|
||||
|
||||
$routes['/executions/:executionID/tasks'] = array('redirect' => '/executions/task?executionID=:executionID');
|
||||
$routes['/tasks/:taskID'] = array('response' => 'task,actions(array)');
|
||||
|
||||
$routes['/projects/:projectID/builds'] = array('redirect' => '/projects/build?projectID=:projectID');
|
||||
$routes['/executions/:executionID/builds'] = array('redirect' => '/executions/build?executionID=:executionID');
|
||||
$routes['/builds/:buildID'] = array('response' => 'build,actions(array)');
|
||||
|
||||
$routes['/products/:productID/bugs'] = array('redirect' => '/bugs?productID=:productID', 'response' => 'bugs(array),pager');
|
||||
$routes['/projects/:projectID/bugs'] = array('redirect' => '/projects/bug?projectID=:projectID');
|
||||
$routes['/executions/:executionID/bugs'] = array('redirect' => '/executions/bug?executionID=:executionID');
|
||||
$routes['/bugs/:bugID'] = array('response' => 'bug,actions(array)');
|
||||
|
||||
$routes['/products/:productID/testcases'] = array('redirect' => '/testcases?productID=:productID', 'response' => 'cases(array)|testcases,pager');
|
||||
$routes['/projects/:projectID/testcases'] = array('redirect' => '/projects/testcase?projectID=:projectID');
|
||||
$routes['/executions/:executionID/testcases'] = array('redirect' => '/executions/testcase?executionID=:executionID');
|
||||
$routes['/testcases/:caseID'] = array('response' => 'testcase,actions(array)');
|
||||
|
||||
$routes['/products/:productID/testtasks'] = array('redirect' => '/testtasks?productID=:productID', 'response' => 'tasks(array)|testtasks,pager');
|
||||
$routes['/projects/:projectID/testtasks'] = array('redirect' => '/projects/testtask?projectID=:projectID');
|
||||
$routes['/executions/:executionID/testtasks'] = array('redirect' => '/executions/testtask?executionID=:executionID');
|
||||
$routes['/testtasks/:testtaskID'] = array('response' => 'task|testtask,actions(array)');
|
||||
|
||||
$routes['/products/:productID/testreports'] = array('redirect' => '/testreports?objectID=:productID', 'response' => 'reports(array)|testreports,pager');
|
||||
$routes['/projects/:projectID/testreports'] = array('redirect' => '/projects/testreport?projectID=:projectID');
|
||||
$routes['/executions/:executionID/testreports'] = array('redirect' => '/executions/testreport?executionID=:executionID');
|
||||
$routes['/testreports/:reportID'] = array('response' => 'report|testreport,actions(array)');
|
||||
|
||||
$routes['/projects/:projectID/builds'] = array('redirect' => '/projects/build?projectID=:projectID');
|
||||
$routes['/executions/:executionID/builds'] = array('redirect' => '/executions/build?executionID=:executionID');
|
||||
$routes['/builds/:buildID'] = array('response' => 'build,actions(array)');
|
||||
|
||||
$routes['/issues'] = array('response' => 'issueList(array)|issues,pager');
|
||||
$routes['/projects/:projectID/issues'] = array('redirect' => '/issues?objectID=:projectID');
|
||||
$routes['/executions/:executionID/issues'] = array('redirect' => '/issues?objectID=:executionID&from=execution');
|
||||
$routes['/issues/:issueID'] = array('response' => 'issue,actions(array)');
|
||||
|
||||
$routes['/risks'] = array('response' => 'risks(array),pager');
|
||||
$routes['/projects/:projectID/risks'] = array('redirect' => '/risks?projectID=:projectID');
|
||||
$routes['/executions/:executionID/risks'] = array('redirect' => '/risks?executionID=:executionID&from=execution');
|
||||
$routes['/risks/:riskID'] = array('response' => 'risk,actions(array)');
|
||||
|
||||
$routes['/opportunities'] = array('response' => 'opportunities(array),pager');
|
||||
$routes['/projects/:projectID/opportunities'] = array('redirect' => '/opportunities?projectID=:projectID');
|
||||
$routes['/executions/:executionID/opportunities'] = array('redirect' => '/opportunities?executionID=:executionID&from=execution');
|
||||
$routes['/opportunities/:opportunityID'] = array('response' => 'opportunity,actions(array)');
|
||||
|
||||
$routes['/auditplans'] = array('response' => 'auditplans(array),pager');
|
||||
$routes['/projects/:projectID/auditplans'] = array('redirect' => '/auditplans?projectID=:projectID');
|
||||
$routes['/executions/:executionID/auditplans'] = array('redirect' => '/auditplans?executionID=:executionID&from=execution');
|
||||
|
||||
$routes['/feedbacks'] = array('method' => 'admin', 'response' => 'feedbacks(array),pager');
|
||||
$routes['/products/:productID/feedbacks'] = array('redirect' => '/feedbacks?param=:productID');
|
||||
$routes['/feedbacks/:feedbackID'] = array('response' => 'feedback,actions(array)');
|
||||
|
||||
$routes['/tickets'] = array('response' => 'tickets(array),pager');
|
||||
$routes['/products/:productID/tickets'] = array('redirect' => '/tickets?param=:productID');
|
||||
$routes['/tickets/:ticketID'] = array('response' => 'ticket,actions(array)');
|
||||
|
||||
$routes['/depts'] = array('response' => 'sons|depts');
|
||||
$routes['/depts/browse'] = array();
|
||||
$routes['/depts/:deptID'] = array('redirect' => '/depts/browse?deptID=:deptID', 'response' => 'sons');
|
||||
|
||||
$routes['/users'] = array('redirect' => '/companies/browse', 'response' => 'users,pager');
|
||||
$routes['/users/:userID'] = array('redirect' => '/users/:userID/profile', 'response' => 'user');
|
||||
<?php
|
||||
$routes = array();
|
||||
|
||||
$routes['/programs'] = array('response' => 'programs(array),pager');
|
||||
$routes['/programs/:programID'] = array('redirect' => '/programs/:programID/edit', 'response' => 'program');
|
||||
$routes['/programs/:programID/projects'] = array('redirect' => '/programs/:programID/project', 'response' => 'projectStats(array)|projects,pager');
|
||||
$routes['/programs/:programID/products'] = array('redirect' => '/programs/:programID/product', 'response' => 'products(array),pager');
|
||||
|
||||
$routes['/products'] = array('redirect' => '/products/all');
|
||||
$routes['/products/all'] = array('response' => 'productStats|products,pager');
|
||||
$routes['/products/browse'] = array('response' => 'stories,pager', 'response' => 'stories(array),pager'); // stories
|
||||
$routes['/products/:productID'] = array('response' => 'product,dynamics,members,branches,reviewers');
|
||||
|
||||
$routes['/products/:productID/stories'] = array('redirect' => '/products/browse?productID=:productID');
|
||||
$routes['/projects/:projectID/stories'] = array('redirect' => '/projectstories/story?projectID=:projectID', 'response' => 'stories(array),pager');
|
||||
$routes['/executions/:executionID/stories'] = array('redirect' => '/executions/story?executionID=:executionID');
|
||||
$routes['/stories/:storyID'] = array('response' => 'story,actions(array)');
|
||||
|
||||
$routes['/products/:productID/epics'] = array('redirect' => '/products/browse?productID=:productID&storyType=epic', 'response' => 'stories(array)|epics,pager');
|
||||
$routes['/epics/:storyID'] = array('response' => 'story|epic,actions(array)');
|
||||
|
||||
$routes['/products/:productID/requirements'] = array('redirect' => '/products/browse?productID=:productID&storyType=requirement', 'response' => 'stories(array)|requirements,pager');
|
||||
$routes['/requirements/:storyID'] = array('response' => 'story|requirement,actions(array)');
|
||||
|
||||
$routes['/products/:productID/productplans'] = array('redirect' => '/productplans?productID=:productID', 'response' => 'plans(array)|productplans,pager');
|
||||
$routes['/productplans/:planID'] = array('response' => 'plan|productplan,actions(array)');
|
||||
|
||||
$routes['/products/:productID/releases'] = array('redirect' => '/releases?productID=:productID', 'response' => 'releases,pager');
|
||||
$routes['/projects/:projectID/releases'] = array('redirect' => '/projectreleases?projectID=:projectID', 'response' => 'releases,pager');
|
||||
$routes['/releases/:releaseID'] = array('response' => 'release,actions(array)');
|
||||
|
||||
$routes['/projects'] = array('response' => 'projectStats|projects,pager');
|
||||
$routes['/projects/list/:browseType'] = array('redirect' => '/projects?browseType=:browseType');
|
||||
$routes['/projects/execution'] = array('response' => 'executionStats|executions,pager');
|
||||
$routes['/projects/build'] = array('response' => 'builds,pager');
|
||||
$routes['/projects/bug'] = array('response' => 'bugs,pager', 'response' => 'bugs(array),pager');
|
||||
$routes['/projects/testcase'] = array('response' => 'cases(array)|testcases,pager');
|
||||
$routes['/projects/testtask'] = array('response' => 'tasks(array)|testtasks,pager');
|
||||
$routes['/projects/testreport'] = array('response' => 'reports(array)|testreports,pager');
|
||||
$routes['/projects/:projectID'] = array('response' => 'project');
|
||||
|
||||
$routes['/executions'] = array('method' => 'all', 'response' => 'executionStats|executions,pager');
|
||||
$routes['/projects/:projectID/executions'] = array('redirect' => '/projects/execution?projectID=:projectID');
|
||||
$routes['/executions/task'] = array('response' => 'tasks(array),pager');
|
||||
$routes['/executions/story'] = array('response' => 'stories(array),pager');
|
||||
$routes['/executions/build'] = array('response' => 'builds,pager');
|
||||
$routes['/executions/bug'] = array('response' => 'bugs(array),pager');
|
||||
$routes['/executions/testcase'] = array('response' => 'cases(array)|testcases,pager');
|
||||
$routes['/executions/testtask'] = array('response' => 'tasks(array)|testtasks,pager');
|
||||
$routes['/executions/testreport'] = array('response' => 'reports(array)|testreports,pager');
|
||||
$routes['/executions/:executionID'] = array('response' => 'execution');
|
||||
|
||||
$routes['/executions/:executionID/tasks'] = array('redirect' => '/executions/task?executionID=:executionID');
|
||||
$routes['/tasks/:taskID'] = array('response' => 'task,actions(array)');
|
||||
|
||||
$routes['/projects/:projectID/builds'] = array('redirect' => '/projects/build?projectID=:projectID');
|
||||
$routes['/executions/:executionID/builds'] = array('redirect' => '/executions/build?executionID=:executionID');
|
||||
$routes['/builds/:buildID'] = array('response' => 'build,actions(array)');
|
||||
|
||||
$routes['/products/:productID/bugs'] = array('redirect' => '/bugs?productID=:productID', 'response' => 'bugs(array),pager');
|
||||
$routes['/projects/:projectID/bugs'] = array('redirect' => '/projects/bug?projectID=:projectID');
|
||||
$routes['/executions/:executionID/bugs'] = array('redirect' => '/executions/bug?executionID=:executionID');
|
||||
$routes['/bugs/:bugID'] = array('response' => 'bug,actions(array)');
|
||||
|
||||
$routes['/products/:productID/testcases'] = array('redirect' => '/testcases?productID=:productID', 'response' => 'cases(array)|testcases,pager');
|
||||
$routes['/projects/:projectID/testcases'] = array('redirect' => '/projects/testcase?projectID=:projectID');
|
||||
$routes['/executions/:executionID/testcases'] = array('redirect' => '/executions/testcase?executionID=:executionID');
|
||||
$routes['/testcases/:caseID'] = array('response' => 'testcase,actions(array)');
|
||||
|
||||
$routes['/products/:productID/testtasks'] = array('redirect' => '/testtasks?productID=:productID', 'response' => 'tasks(array)|testtasks,pager');
|
||||
$routes['/projects/:projectID/testtasks'] = array('redirect' => '/projects/testtask?projectID=:projectID');
|
||||
$routes['/executions/:executionID/testtasks'] = array('redirect' => '/executions/testtask?executionID=:executionID');
|
||||
$routes['/testtasks/:testtaskID'] = array('response' => 'task|testtask,actions(array)');
|
||||
|
||||
$routes['/products/:productID/testreports'] = array('redirect' => '/testreports?objectID=:productID', 'response' => 'reports(array)|testreports,pager');
|
||||
$routes['/projects/:projectID/testreports'] = array('redirect' => '/projects/testreport?projectID=:projectID');
|
||||
$routes['/executions/:executionID/testreports'] = array('redirect' => '/executions/testreport?executionID=:executionID');
|
||||
$routes['/testreports/:reportID'] = array('response' => 'report|testreport,actions(array)');
|
||||
|
||||
$routes['/projects/:projectID/builds'] = array('redirect' => '/projects/build?projectID=:projectID');
|
||||
$routes['/executions/:executionID/builds'] = array('redirect' => '/executions/build?executionID=:executionID');
|
||||
$routes['/builds/:buildID'] = array('response' => 'build,actions(array)');
|
||||
|
||||
$routes['/issues'] = array('response' => 'issueList(array)|issues,pager');
|
||||
$routes['/projects/:projectID/issues'] = array('redirect' => '/issues?objectID=:projectID');
|
||||
$routes['/executions/:executionID/issues'] = array('redirect' => '/issues?objectID=:executionID&from=execution');
|
||||
$routes['/issues/:issueID'] = array('response' => 'issue,actions(array)');
|
||||
|
||||
$routes['/risks'] = array('response' => 'risks(array),pager');
|
||||
$routes['/projects/:projectID/risks'] = array('redirect' => '/risks?projectID=:projectID');
|
||||
$routes['/executions/:executionID/risks'] = array('redirect' => '/risks?executionID=:executionID&from=execution');
|
||||
$routes['/risks/:riskID'] = array('response' => 'risk,actions(array)');
|
||||
|
||||
$routes['/opportunities'] = array('response' => 'opportunities(array),pager');
|
||||
$routes['/projects/:projectID/opportunities'] = array('redirect' => '/opportunities?projectID=:projectID');
|
||||
$routes['/executions/:executionID/opportunities'] = array('redirect' => '/opportunities?executionID=:executionID&from=execution');
|
||||
$routes['/opportunities/:opportunityID'] = array('response' => 'opportunity,actions(array)');
|
||||
|
||||
$routes['/auditplans'] = array('response' => 'auditplans(array),pager');
|
||||
$routes['/projects/:projectID/auditplans'] = array('redirect' => '/auditplans?projectID=:projectID');
|
||||
$routes['/executions/:executionID/auditplans'] = array('redirect' => '/auditplans?executionID=:executionID&from=execution');
|
||||
|
||||
$routes['/feedbacks'] = array('method' => 'admin', 'response' => 'feedbacks(array),pager');
|
||||
$routes['/products/:productID/feedbacks'] = array('redirect' => '/feedbacks?param=:productID');
|
||||
$routes['/feedbacks/:feedbackID'] = array('response' => 'feedback,actions(array)');
|
||||
|
||||
$routes['/tickets'] = array('response' => 'tickets(array),pager');
|
||||
$routes['/products/:productID/tickets'] = array('redirect' => '/tickets?param=:productID');
|
||||
$routes['/tickets/:ticketID'] = array('response' => 'ticket,actions(array)');
|
||||
|
||||
$routes['/systems'] = array('response' => 'appList(array)|systems,pager');
|
||||
$routes['/products/:productID/systems'] = array('redirect' => '/systems?productID=:productID');
|
||||
$routes['/systems/:systemID'] = array('response' => 'system,actions(array)');
|
||||
|
||||
$routes['/depts'] = array('response' => 'sons|depts');
|
||||
$routes['/depts/browse'] = array();
|
||||
$routes['/depts/:deptID'] = array('redirect' => '/depts/browse?deptID=:deptID', 'response' => 'sons');
|
||||
|
||||
$routes['/users'] = array('redirect' => '/companies/browse', 'response' => 'users,pager');
|
||||
$routes['/users/:userID'] = array('redirect' => '/users/:userID/profile', 'response' => 'user');
|
||||
|
||||
@@ -220,6 +220,7 @@ $config->openMethods[] = 'sso.gettodolist';
|
||||
$config->openMethods[] = 'sso.login';
|
||||
$config->openMethods[] = 'sso.logout';
|
||||
$config->openMethods[] = 'upgrade.ajaxupdatefile';
|
||||
$config->openMethods[] = 'upgrade.safedelete';
|
||||
$config->openMethods[] = 'user.forgetpassword';
|
||||
$config->openMethods[] = 'user.login';
|
||||
$config->openMethods[] = 'user.refreshrandom';
|
||||
|
||||
@@ -1080,7 +1080,7 @@ CREATE TABLE `zt_demand` (
|
||||
`pool` int unsigned NOT NULL DEFAULT 0,
|
||||
`module` int unsigned NOT NULL DEFAULT 0,
|
||||
`product` varchar(255) NOT NULL DEFAULT '',
|
||||
`parent` int unsigned NOT NULL DEFAULT 0,
|
||||
`parent` int NOT NULL DEFAULT 0,
|
||||
`pri` tinyint unsigned NOT NULL DEFAULT 3,
|
||||
`category` varchar(30) NOT NULL DEFAULT '',
|
||||
`source` varchar(30) NOT NULL DEFAULT '',
|
||||
@@ -2721,7 +2721,7 @@ CREATE TABLE `zt_project` (
|
||||
`milestone` tinyint unsigned NOT NULL DEFAULT 0,
|
||||
`output` text DEFAULT NULL,
|
||||
`auth` varchar(30) NOT NULL DEFAULT '',
|
||||
`storyType` varchar(30) NOT NULL DEFAULT '',
|
||||
`storyType` varchar(30) NOT NULL DEFAULT 'story',
|
||||
`parent` int unsigned NOT NULL DEFAULT 0,
|
||||
`path` varchar(255) NOT NULL DEFAULT '',
|
||||
`grade` tinyint unsigned NOT NULL DEFAULT 0,
|
||||
|
||||
@@ -2921,13 +2921,6 @@ CHANGE `reviewedDate` `reviewedDate` datetime NULL,
|
||||
CHANGE `level` `level` tinyint(3) NOT NULL DEFAULT '0',
|
||||
CHANGE `assignedTo` `assignedTo` varchar(30) NOT NULL DEFAULT '';
|
||||
|
||||
ALTER TABLE `zt_holiday`
|
||||
CHANGE `name` `name` varchar(30) NOT NULL DEFAULT '',
|
||||
CHANGE `type` `type` enum('holiday', 'working') NOT NULL DEFAULT 'holiday',
|
||||
CHANGE `year` `year` char(4) NOT NULL DEFAULT '',
|
||||
CHANGE `begin` `begin` date NULL,
|
||||
CHANGE `end` `end` date NULL;
|
||||
|
||||
ALTER TABLE `zt_leave`
|
||||
CHANGE `year` `year` char(4) NOT NULL DEFAULT '',
|
||||
CHANGE `begin` `begin` date NULL,
|
||||
|
||||
@@ -12,7 +12,7 @@ DROP INDEX account_vision_module_type_order ON `zt_block`;
|
||||
UPDATE `zt_block` SET `dashboard` = CONCAT(`module`, `type`);
|
||||
UPDATE `zt_block` SET `module` = IF(`source` != '', `source`, `code`);
|
||||
UPDATE `zt_block` SET `width` = IF(`grid` > 4, '2', '1');
|
||||
UPDATE `zt_block` SET `left` = '2' WHERE `module` = 'dynamic' AND `code` = 'dynamic';
|
||||
UPDATE `zt_block` SET `left` = '2' WHERE `width` = '1';
|
||||
UPDATE `zt_block` SET `params` = '{"count":"20"}' WHERE `module` = 'assigntome' AND `code` = 'assigntome';
|
||||
|
||||
ALTER TABLE `zt_block` DROP COLUMN `source`;
|
||||
|
||||
+1
-2
@@ -604,7 +604,6 @@ ALTER TABLE `zt_demand`
|
||||
MODIFY COLUMN `id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
MODIFY COLUMN `pool` int unsigned NOT NULL DEFAULT 0,
|
||||
MODIFY COLUMN `module` int unsigned NOT NULL DEFAULT 0,
|
||||
MODIFY COLUMN `parent` int unsigned NOT NULL DEFAULT 0,
|
||||
MODIFY COLUMN `pri` tinyint unsigned NOT NULL DEFAULT 3,
|
||||
MODIFY COLUMN `category` varchar(30) NOT NULL DEFAULT '',
|
||||
MODIFY COLUMN `source` varchar(30) NOT NULL DEFAULT '',
|
||||
@@ -1385,7 +1384,7 @@ ALTER TABLE `zt_project`
|
||||
MODIFY COLUMN `percent` decimal(5,2) unsigned NOT NULL DEFAULT 0.00,
|
||||
MODIFY COLUMN `milestone` char(1) NOT NULL DEFAULT '0',
|
||||
MODIFY COLUMN `auth` varchar(30) NOT NULL DEFAULT '',
|
||||
MODIFY COLUMN `storyType` varchar(30) NOT NULL DEFAULT '',
|
||||
MODIFY COLUMN `storyType` varchar(30) NOT NULL DEFAULT 'story',
|
||||
MODIFY COLUMN `parent` int unsigned NOT NULL DEFAULT 0,
|
||||
MODIFY COLUMN `workflowGroup` int unsigned NOT NULL DEFAULT 0,
|
||||
MODIFY COLUMN `pri` char(1) NOT NULL DEFAULT '1',
|
||||
|
||||
+2
-2
@@ -1569,7 +1569,7 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
|
||||
`milestone` tinyint unsigned NOT NULL DEFAULT 0,
|
||||
`output` text DEFAULT NULL,
|
||||
`auth` varchar(30) NOT NULL DEFAULT '',
|
||||
`storyType` varchar(30) NOT NULL DEFAULT '',
|
||||
`storyType` varchar(30) NOT NULL DEFAULT 'story',
|
||||
`parent` int unsigned NOT NULL DEFAULT 0,
|
||||
`path` varchar(255) NOT NULL DEFAULT '',
|
||||
`grade` tinyint unsigned NOT NULL DEFAULT 0,
|
||||
@@ -15109,7 +15109,7 @@ CREATE TABLE IF NOT EXISTS `zt_demand` (
|
||||
`pool` int unsigned NOT NULL DEFAULT 0,
|
||||
`module` int unsigned NOT NULL DEFAULT 0,
|
||||
`product` varchar(255) NOT NULL DEFAULT '',
|
||||
`parent` int unsigned NOT NULL DEFAULT 0,
|
||||
`parent` int NOT NULL DEFAULT 0,
|
||||
`pri` tinyint unsigned NOT NULL DEFAULT 3,
|
||||
`category` varchar(30) NOT NULL DEFAULT '',
|
||||
`source` varchar(30) NOT NULL DEFAULT '',
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
2025-12-15 21.7.8
|
||||
2026-01-08 21.7.8
|
||||
完成的需求
|
||||
企业版:
|
||||
79153 调整大桌面二级导航为固定展示
|
||||
|
||||
+2
-2
@@ -2,13 +2,13 @@ Z PUBLIC LICENSE 1.2
|
||||
|
||||
许可
|
||||
|
||||
Z PUBLIC LICENSE 由禅道软件(青岛)有限公司(www.easycorp.cn)起草,简称ZPL协议。
|
||||
Z PUBLIC LICENSE 由禅道软件(青岛)有限公司(www.chandao.com)起草,简称ZPL协议。
|
||||
任何人均可使用该协议来发布开源软件,并可对下面协议正文中以下划线标注的空白部分做相应修改,
|
||||
除此之外的任何内容不得做任何修改。禅道软件(青岛)有限公司拥有对该协议条款的最终解释权。
|
||||
|
||||
前言:
|
||||
|
||||
禅道迅捷版软件(以下简称该软件)由 禅道软件(青岛)有限公司(www.easycorp.cn)开发(以下简称我)。我依法拥有该软件的所有版权。
|
||||
禅道迅捷版软件(以下简称该软件)由 禅道软件(青岛)有限公司(www.chandao.com)开发(以下简称我)。我依法拥有该软件的所有版权。
|
||||
本着共享开放的角度,我以开放源代码的形式发布该软件。您可以在遵守该协议的前提下使用该软件。
|
||||
自您安装该软件开始,您和我之间的合同关系自动成立。除非您停止使用该软件或与我有签署额外合同,
|
||||
您须认真遵循该授权协议约定的每一条款。
|
||||
|
||||
@@ -130,6 +130,8 @@ class control extends baseControl
|
||||
*/
|
||||
public function display($moduleName = '', $methodName = '')
|
||||
{
|
||||
if($this->viewType == 'html') return parent::display($moduleName, $methodName);
|
||||
|
||||
$this->render($moduleName, $methodName);
|
||||
}
|
||||
|
||||
@@ -144,6 +146,8 @@ class control extends baseControl
|
||||
*/
|
||||
public function render($moduleName = '', $methodName = '')
|
||||
{
|
||||
if($this->viewType == 'html') return parent::render($moduleName, $methodName);
|
||||
|
||||
if($this->app->apiVersion != 'v2')
|
||||
{
|
||||
$this->parseJSON($moduleName, $methodName);
|
||||
@@ -663,7 +667,17 @@ class control extends baseControl
|
||||
{
|
||||
if($moduleName != $this->moduleName) $this->app->fetchModule = $moduleName;
|
||||
|
||||
return parent::fetch($moduleName, $methodName, $params, $appName);
|
||||
$content = parent::fetch($moduleName, $methodName, $params, $appName);
|
||||
|
||||
if($this->getFormData)
|
||||
{
|
||||
$this->app->loadClass('formdom');
|
||||
$parser = new formdom(array('include_disabled' => true));
|
||||
|
||||
$this->formData = $parser->parse($content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -325,9 +325,6 @@ class api extends router
|
||||
else
|
||||
{
|
||||
$_GET[$moduleName.'ID'] = $pathItems[1];
|
||||
|
||||
/* Set default params and post data to delete.*/
|
||||
if($this->action == 'delete') $_GET['confirm'] = 'yes';
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -342,6 +339,13 @@ class api extends router
|
||||
$this->setModuleName($moduleName);
|
||||
$this->setMethodName($methodName);
|
||||
$this->setControlFile();
|
||||
|
||||
/* Set default params and post data to delete.*/
|
||||
if($this->action == 'delete')
|
||||
{
|
||||
$defaultParams = $this->getDefaultParams();
|
||||
if(isset($defaultParams['confirm'])) $_GET['confirm'] = 'yes';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -398,7 +402,7 @@ class api extends router
|
||||
*/
|
||||
public function checkObjectPriv(object $object, string $table): bool
|
||||
{
|
||||
if($this->user->admin) true;
|
||||
if($this->user->admin) return true;
|
||||
|
||||
$userView = $this->user->view;
|
||||
switch($table)
|
||||
|
||||
@@ -57,6 +57,9 @@ class detail extends wg
|
||||
/* 标题,如果不指定则尝试使用当前页面上的 `${$objectType}->title` 或 `${$objectType}->name` 的值,例如 `$story->title`、`$task->name` 。 */
|
||||
'title' => '?string',
|
||||
|
||||
/* 标题提示,如果不指定则使用title属性。 */
|
||||
'titleProps' => '?string',
|
||||
|
||||
/* 底部固定操作按钮的定义,不包括返回按钮,可以通过 `-` 来指定分割线,如果没有指定 actions 属性,则从 actionList 和 operateList 生成。 */
|
||||
'actions' => '?array',
|
||||
|
||||
@@ -125,6 +128,7 @@ class detail extends wg
|
||||
.no-width {padding: 0!important; width: 0!important; overflow: hidden!important;}
|
||||
.detail-toggle {margin: 0!important;}
|
||||
.detail-toggle:hover {background-color: rgba(var(--color-border-rgb), var(--tw-bg-opacity)); transition-duration: .15s; transition-property: background-color; transition-timing-function: cubic-bezier(.4,0,.2,1);}
|
||||
.article > .files-list > li.file{white-space:normal;}
|
||||
CSS;
|
||||
}
|
||||
|
||||
@@ -202,7 +206,7 @@ CSS;
|
||||
|
||||
protected function buildTitle()
|
||||
{
|
||||
list($object, $objectID, $title, $color, $objectType, $parent, $parentID, $parentUrl, $parentTitle, $parentType, $parentTitleProps) = $this->prop(array('object', 'objectID', 'title', 'color', 'objectType', 'parent', 'parentID', 'parentUrl', 'parentTitle', 'parentType', 'parentTitleProps'));
|
||||
list($object, $objectID, $title, $titleProps, $color, $objectType, $parent, $parentID, $parentUrl, $parentTitle, $parentType, $parentTitleProps) = $this->prop(array('object', 'objectID', 'title', 'titleProps', 'color', 'objectType', 'parent', 'parentID', 'parentUrl', 'parentTitle', 'parentType', 'parentTitleProps'));
|
||||
$titleBlock = $this->block('title');
|
||||
$titleLeading = $this->block('titleLeading');
|
||||
|
||||
@@ -214,7 +218,7 @@ CSS;
|
||||
set::object($object),
|
||||
set::title($title),
|
||||
set::titleClass('text-lg text-clip font-bold'),
|
||||
set::titleProps(array('title' => $title)),
|
||||
set::titleProps(array('title' => empty($titleProps) ? $title : $titleProps)),
|
||||
set::type($objectType),
|
||||
set::color($color),
|
||||
set::parentTitleClass('text-lg text-clip font-bold'),
|
||||
|
||||
@@ -24,7 +24,7 @@ class editor extends wg
|
||||
'locale?: string', // 语言,可选值 'zh', 'en',默认跟随浏览器,也可以是自定义的语言项 JSON,详见 ZenEditor 文档
|
||||
'markdown?: bool=false', // 是否启用 Markdown 模式,若启用,为了兼容性将会隐藏一些功能
|
||||
'neglectDefaultTextStyle?: bool=false', // 是否不赋予默认的文本样式
|
||||
'preferHardBreak?: bool=true', // 是否优先使用硬回车而不是新段落
|
||||
'preferHardBreak?: bool=false', // 是否优先使用硬回车而不是新段落
|
||||
'value?: string', // 初始内容
|
||||
'templateType?: string', // 模板类型
|
||||
'uid?: string' // 图片上传 uid
|
||||
|
||||
@@ -7,75 +7,27 @@ title=测试 adminModel::checkInternet();
|
||||
timeout=0
|
||||
cid=0
|
||||
|
||||
|
||||
- 默认参数:可能因网络环境而失败 @1
|
||||
- 有效URL:在测试环境中可能无网络 @1
|
||||
- 无效域名:期望失败 @0
|
||||
- 超时时间为0:期望快速超时失败 @1
|
||||
- 较长超时:在无网络环境中仍会失败 @1
|
||||
- HTTPS协议:测试SSL连接 @1
|
||||
- 本地连接:测试本地网络检查 @0
|
||||
|
||||
*/
|
||||
|
||||
// 创建一个简化的测试环境,不依赖完整的ZenTao初始化
|
||||
class MockConfig {
|
||||
public $admin;
|
||||
|
||||
public function __construct() {
|
||||
$this->admin = new stdClass();
|
||||
$this->admin->apiSite = 'https://api.zentao.net/';
|
||||
}
|
||||
}
|
||||
|
||||
class MockAdminModel {
|
||||
private $config;
|
||||
|
||||
public function __construct($config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function checkInternet(string $url = '', int $timeout = 1): bool
|
||||
{
|
||||
if(empty($url)) $url = $this->config->admin->apiSite;
|
||||
|
||||
// 在测试环境中模拟网络连接检查
|
||||
// 为了测试稳定性,始终返回false(模拟无网络环境)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class AdminTestWrapper {
|
||||
private $model;
|
||||
|
||||
public function __construct() {
|
||||
$config = new MockConfig();
|
||||
$this->model = new MockAdminModel($config);
|
||||
}
|
||||
|
||||
public function checkInternetTest(string $url = '', int $timeout = 1): string {
|
||||
$result = $this->model->checkInternet($url, $timeout);
|
||||
return $result ? '1' : '0';
|
||||
}
|
||||
}
|
||||
|
||||
// 简化的测试框架函数
|
||||
function r($result) {
|
||||
global $testResult;
|
||||
$testResult = $result;
|
||||
return $result;
|
||||
}
|
||||
|
||||
function p($field = '') {
|
||||
return true; // 简化实现
|
||||
}
|
||||
|
||||
function e($expected) {
|
||||
global $testResult;
|
||||
return $testResult === $expected;
|
||||
}
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/admin.unittest.class.php';
|
||||
|
||||
// 创建测试实例
|
||||
$adminTest = new AdminTestWrapper();
|
||||
global $tester;
|
||||
$tester->loadModel('admin');
|
||||
|
||||
// 执行测试步骤 - 每个都期望返回0(false),模拟无网络环境
|
||||
r($adminTest->checkInternetTest()) && p() && e('0'); // 默认参数:可能因网络环境而失败
|
||||
r($adminTest->checkInternetTest('https://www.zentao.net')) && p() && e('0'); // 有效URL:在测试环境中可能无网络
|
||||
r($adminTest->checkInternetTest('http://invalid-domain-test-12345.com')) && p() && e('0'); // 无效域名:期望失败
|
||||
r($adminTest->checkInternetTest('', 0)) && p() && e('0'); // 超时时间为0:期望快速超时失败
|
||||
r($adminTest->checkInternetTest('', 5)) && p() && e('0'); // 较长超时:在无网络环境中仍会失败
|
||||
r($adminTest->checkInternetTest('https://api.zentao.net/')) && p() && e('0'); // HTTPS协议:测试SSL连接
|
||||
r($adminTest->checkInternetTest('http://127.0.0.1:80', 1)) && p() && e('0'); // 本地连接:测试本地网络检查
|
||||
r($tester->admin->checkInternet()) && p() && e('1'); // 默认参数:可能因网络环境而失败
|
||||
r($tester->admin->checkInternet('https://www.zentao.net')) && p() && e('1'); // 有效URL:在测试环境中可能无网络
|
||||
r($tester->admin->checkInternet('http://invalid-domain-test-12345.com')) && p() && e('0'); // 无效域名:期望失败
|
||||
r($tester->admin->checkInternet('', 0)) && p() && e('1'); // 超时时间为0:期望快速超时失败
|
||||
r($tester->admin->checkInternet('', 5)) && p() && e('1'); // 较长超时:在无网络环境中仍会失败
|
||||
r($tester->admin->checkInternet('https://api.zentao.net/')) && p() && e('1'); // HTTPS协议:测试SSL连接
|
||||
r($tester->admin->checkInternet('http://127.0.0.1:80', 1)) && p() && e('0'); // 本地连接:测试本地网络检查
|
||||
|
||||
@@ -6,8 +6,8 @@ $config->ai->initAIPrompts[] = (object)['id' => 2, 'name' => '绘制需求原型
|
||||
2. 整体结构框架(如导航栏、侧边栏、主要内容区)
|
||||
3. 各功能模块的简要设计(如按钮、表单、图表等)
|
||||
4. 交互设计提示(如如何进行用户操作)
|
||||
5. 请生成完整的 HTML5 页面代码,包括开始的 `<!DOCTYPE html>` 和 结束的 `</html>`,使用 markdown html 代码块表示,如果有多个页面请使用多个代码块表示。
|
||||
6. 如果页面有 CSS,应该直接在 HTML 页面内使用内联样式`<style>`表示,而不是链接单独的 CSS 文件。',
|
||||
5. 请生成完整的 HTML5 页面代码,包括开始的 `<!DOCTYPE html>` 和 结束的 `</html>`,使用 markdown html 代码块表示,如果有多个页面请使用多个代码块表示。
|
||||
6. 如果页面有 CSS,应该直接在 HTML 页面内使用内联样式`<style>`表示,而不是链接单独的 CSS 文件。',
|
||||
'elaboration' => '', 'role' => '请你扮演一名资深的产品经理。', 'characterization' => '负责绘制需求原型图的过程。您将需求转化为可视化的原型图,同时确保其符合需求描述和需求验收标准。', 'status' => 'active', 'createdBy' => 'system'];
|
||||
|
||||
$config->ai->initAIPromptFields = array();
|
||||
|
||||
@@ -831,6 +831,7 @@ class bug extends control
|
||||
{
|
||||
if(!empty($bug->assignedTo)) $bug->assignedDate = helper::now();
|
||||
$bug->id = $this->bug->create($bug);
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
/* 批量创建后的一些其他操作。*/
|
||||
/* Processing other operations after batch creation. */
|
||||
|
||||
@@ -2147,7 +2147,7 @@ class bugModel extends model
|
||||
{
|
||||
$bugQuery = $this->processSearchQuery($object, $queryID, $productIdList, (string)$branch);
|
||||
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) AS priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) AS severityOrder")->from(TABLE_BUG)
|
||||
return $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) AS priOrder, IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) AS severityOrder, INSTR('active,resolved,closed,', status) as statusOrder")->from(TABLE_BUG)
|
||||
->where($bugQuery)
|
||||
->andWhere('deleted')->eq('0')
|
||||
->beginIF($excludeBugs)->andWhere('id')->notIN($excludeBugs)->fi()
|
||||
|
||||
@@ -14,9 +14,9 @@ cid=15361
|
||||
- 获取产品1,2 下的全部 bug 列表,查看数量是否正确 @10
|
||||
- 获取不存在的产品下的全部 bug 列表,查看数量是否正确 @0
|
||||
- 获取产品1 分支0 下的全部 bug 列表,查看数量是否正确 @5
|
||||
- 获取产品1 分支0 下的 bug 列表,查看数量是否正确 @5
|
||||
- 获取产品1 不存在的分支下的全部 bug 列表,查看数量是否正确 @5
|
||||
- 获取产品1 不存在的分支下的全部 bug 列表,查看数量是否正确 @5
|
||||
- 获取产品1 分支0 下的 bug 列表,查看数量是否正确 @0
|
||||
- 获取产品1 不存在的分支下的全部 bug 列表,查看数量是否正确 @0
|
||||
- 获取产品1 不存在的分支下的全部 bug 列表,查看数量是否正确 @0
|
||||
- 获取产品1 项目2 下的全部 bug 列表,查看数量是否正确 @5
|
||||
- 获取产品1 不存在项目下的全部 bug 列表,查看数量是否正确 @5
|
||||
- 获取产品1 下的不包含 bug1 的 bug 列表,查看数量是否正确 @4
|
||||
@@ -65,9 +65,9 @@ r(count($bug->getBySearch('bug', $productIdList[1], $branch[0], $projectID[0], 0
|
||||
r(count($bug->getBySearch('bug', $productIdList[2], $branch[0], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('10'); //获取产品1,2 下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[3], $branch[0], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('0'); //获取不存在的产品下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[1], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('5'); //获取产品1 分支0 下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[2], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('5'); //获取产品1 分支0 下的 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[3], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('5'); //获取产品1 不存在的分支下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[3], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('5'); //获取产品1 不存在的分支下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[2], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('0'); //获取产品1 分支0 下的 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[3], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('0'); //获取产品1 不存在的分支下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[3], $projectID[0], 0, 0, $excludeBugs[0]))) && p('') && e('0'); //获取产品1 不存在的分支下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[0], $projectID[1], 0, 0, $excludeBugs[0]))) && p('') && e('5'); //获取产品1 项目2 下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[0], $projectID[2], 0, 0, $excludeBugs[0]))) && p('') && e('5'); //获取产品1 不存在项目下的全部 bug 列表,查看数量是否正确
|
||||
r(count($bug->getBySearch('bug', $productIdList[0], $branch[0], $projectID[0], 0, 0, $excludeBugs[1]))) && p('') && e('4'); //获取产品1 下的不包含 bug1 的 bug 列表,查看数量是否正确
|
||||
@@ -75,4 +75,4 @@ r(count($bug->getBySearch('bug', $productIdList[0], $branch[0], $projectID[0], 0
|
||||
|
||||
r($bug->getBySearch('bug', $productIdList[0], $branch[0], $projectID[0], 0, 0, $excludeBugs[0])) && p('1:title') && e('BUG1'); //获取产品1 下的 bug 列表,查看第一个 bug 的名称是否正确
|
||||
r($bug->getBySearch('bug', $productIdList[1], $branch[0], $projectID[0], 0, 0, $excludeBugs[0])) && p('2:title') && e('BUG2'); //获取产品2 下的 bug 列表,查看第一个 bug 的名称是否正确
|
||||
r($bug->getBySearch('bug', $productIdList[2], $branch[0], $projectID[0], 0, 0, $excludeBugs[0])) && p('1:title;2:title', ';') && e('BUG1;BUG2'); //获取产品1,2 下的 bug 列表,查看第一个和第二个 bug 的名称是否正确
|
||||
r($bug->getBySearch('bug', $productIdList[2], $branch[0], $projectID[0], 0, 0, $excludeBugs[0])) && p('1:title;2:title', ';') && e('BUG1;BUG2'); //获取产品1,2 下的 bug 列表,查看第一个和第二个 bug 的名称是否正确
|
||||
+2
-27
@@ -1115,37 +1115,12 @@ class commonModel extends model
|
||||
|
||||
if($this->app->getModuleName() == 'upgrade' and $this->session->upgrading) return false;
|
||||
|
||||
if($this->app->getModuleName() == 'upgrade' && $this->app->getMethodName() == 'safedelete') return false;
|
||||
|
||||
$statusFile = $this->app->getAppRoot() . 'www' . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'ok.txt';
|
||||
return (!is_file($statusFile) or (time() - filemtime($statusFile)) > $this->config->safeFileTimeout) ? $statusFile : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查升级验证文件是否创建,给出升级的提示。
|
||||
* Check upgrade's status file is ok or not.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function checkUpgradeStatus()
|
||||
{
|
||||
$statusFile = $this->checkSafeFile();
|
||||
if($statusFile)
|
||||
{
|
||||
$this->app->loadLang('upgrade');
|
||||
$cmd = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? $this->lang->upgrade->createFileWinCMD : $this->lang->upgrade->createFileLinuxCMD;
|
||||
$cmd = sprintf($cmd, $statusFile);
|
||||
|
||||
echo "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head><body>";
|
||||
echo "<table align='center' style='margin-top:100px; border:1px solid gray; font-size:14px;padding:8px;'><tr><td>";
|
||||
printf($this->lang->upgrade->setStatusFile, $cmd, $statusFile);
|
||||
echo '</td></tr></table></body></html>';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查系统是否处于维护状态,如果处于维护状态则给非管理员用户输出提示。
|
||||
* Check whether system in maintenance status and show message for non-admin user.
|
||||
|
||||
@@ -129,11 +129,12 @@ window.redirectParentWindow = function(link, from, spaceID, libID, moduleID)
|
||||
{
|
||||
const aiResult = JSON.parse(localStorage.getItem('aiResult'));
|
||||
zui.DocApp.storeNextCreatingDoc({
|
||||
content: aiResult.content || '',
|
||||
title : aiResult.title || '',
|
||||
content : aiResult.content || '',
|
||||
contentType: 'markdown',
|
||||
space: Number(spaceID),
|
||||
lib: Number(libID),
|
||||
module: Number(moduleID),
|
||||
space : +spaceID,
|
||||
lib : +libID,
|
||||
module : +moduleID,
|
||||
});
|
||||
localStorage.removeItem('aiResult');
|
||||
}
|
||||
|
||||
@@ -939,7 +939,7 @@ class docModel extends model
|
||||
{
|
||||
if(!isset($modules[$doc->lib])) $modules[$doc->lib] = $this->tree->getOptionMenu((int)$doc->lib, 'doc', 0, 'all', 'nodeleted', 'all', ' > ');
|
||||
$doc->moduleName = zget($modules[$doc->lib], $doc->module);
|
||||
$doc->moduleName = ltrim($doc->moduleName, '/');
|
||||
$doc->moduleName = ltrim((string)$doc->moduleName, '/');
|
||||
|
||||
$doc->objectID = zget($doc, $doc->objectType, 0);
|
||||
$doc->objectName = '';
|
||||
|
||||
@@ -1511,6 +1511,7 @@ class executionModel extends model
|
||||
*/
|
||||
public function fetchExecutionList(int $projectID = 0, string $browseType = 'undone', int $productID = 0, int $param = 0, string $orderBy = 'id_asc', ?object $pager = null): array
|
||||
{
|
||||
if(strpos($orderBy, 'nameCol') !== false) $orderBy = str_replace('nameCol', 'name', $orderBy);
|
||||
/* Construct the query SQL at search executions. */
|
||||
$executionQuery = $browseType == 'bySearch' ? $this->getExecutionQuery($param) : '';
|
||||
$projectModel = $this->dao->select('model')->from(TABLE_PROJECT)->where('id')->eq($projectID)->fetch('model');
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=测试 executionZen::processPrintKanbanData();
|
||||
timeout=0
|
||||
cid=16438
|
||||
|
||||
- 步骤1:有历史数据时过滤重复项目
|
||||
- 属性wait @3
|
||||
- 属性doing @2
|
||||
- 步骤2:无历史数据时返回原始数据
|
||||
- 属性wait @2
|
||||
- 属性doing @1
|
||||
- 步骤3:空数据列表返回0 @0
|
||||
- 步骤4:历史数据存在但无重复时返回原数据
|
||||
- 属性wait @2
|
||||
- 属性doing @1
|
||||
- 步骤5:多类型数据重复被全部移除
|
||||
- 属性story @2
|
||||
- 属性bug @2
|
||||
|
||||
*/
|
||||
|
||||
// 1. 导入依赖(路径固定,不可修改)
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/executionzen.unittest.class.php';
|
||||
|
||||
// 2. 手动数据准备(设置历史看板数据)
|
||||
global $tester;
|
||||
$settingModel = $tester->loadModel('setting');
|
||||
$settingModel->setItem('owner=null&module=execution§ion=kanban&key=execution1', '{"wait":["1","2"],"doing":["3"],"done":["4","5"]}');
|
||||
$settingModel->setItem('owner=null&module=execution§ion=kanban&key=execution2', '{"story":["101","102"],"bug":["201","202"]}');
|
||||
$settingModel->setItem('owner=null&module=execution§ion=kanban&key=execution3', '{"task":["301","302","303"],"story":["104"]}');
|
||||
$settingModel->setItem('owner=null&module=execution§ion=kanban&key=execution4', '{"wait":["6","7","8"],"doing":["9"]}');
|
||||
|
||||
// 3. 用户登录(选择合适角色)
|
||||
su('admin');
|
||||
|
||||
// 4. 创建测试实例(变量名与模块名一致)
|
||||
$executionTest = new executionZenTest();
|
||||
|
||||
// 5. 🔴 强制要求:必须包含至少5个测试步骤
|
||||
r($executionTest->processPrintKanbanDataTest(1, array('wait' => array('1' => 'task1', '2' => 'task2', '10' => 'task10'), 'doing' => array('3' => 'task3', '11' => 'task11')))) && p('wait,doing') && e('3,2'); // 步骤1:有历史数据时过滤重复项目
|
||||
r($executionTest->processPrintKanbanDataTest(999, array('wait' => array('1' => 'task1', '2' => 'task2'), 'doing' => array('3' => 'task3')))) && p('wait,doing') && e('2,1'); // 步骤2:无历史数据时返回原始数据
|
||||
r($executionTest->processPrintKanbanDataTest(1, array())) && p() && e('0'); // 步骤3:空数据列表返回0
|
||||
r($executionTest->processPrintKanbanDataTest(1, array('wait' => array('20' => 'task20', '21' => 'task21'), 'doing' => array('22' => 'task22')))) && p('wait,doing') && e('2,1'); // 步骤4:历史数据存在但无重复时返回原数据
|
||||
r($executionTest->processPrintKanbanDataTest(2, array('story' => array('101' => 'story101', '103' => 'story103'), 'bug' => array('201' => 'bug201', '203' => 'bug203')))) && p('story,bug') && e('2,2'); // 步骤5:多类型数据重复被全部移除
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=测试 executionZen::setUserMoreLink();
|
||||
timeout=0
|
||||
cid=16445
|
||||
|
||||
- 应返回4个用户列表数组 @4
|
||||
- 应返回4个用户列表数组 @4
|
||||
- 应返回4个数组(但内容为空) @4
|
||||
- 应返回4个数组(但内容为空) @4
|
||||
- 应返回4个用户列表数组,重复用户会被处理 @4
|
||||
|
||||
*/
|
||||
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/executionzen.unittest.class.php';
|
||||
|
||||
// 准备测试数据
|
||||
zenData('user')->gen(10);
|
||||
zenData('project')->gen(5);
|
||||
|
||||
// 用户登录
|
||||
su('admin');
|
||||
|
||||
// 创建测试实例
|
||||
$executionZenTest = new executionZenTest();
|
||||
|
||||
// 测试步骤1:传入单个执行对象,获取用户列表
|
||||
$execution1 = new stdClass();
|
||||
$execution1->PO = 'admin';
|
||||
$execution1->PM = 'user1';
|
||||
$execution1->QD = 'user2';
|
||||
$execution1->RD = 'user3';
|
||||
|
||||
$result1 = $executionZenTest->setUserMoreLinkTest($execution1);
|
||||
r(count($result1)) && p() && e(4); // 应返回4个用户列表数组
|
||||
|
||||
// 测试步骤2:传入执行对象数组,获取用户列表
|
||||
$execution2 = new stdClass();
|
||||
$execution2->PO = 'user4';
|
||||
$execution2->PM = 'user5';
|
||||
$execution2->QD = 'user6';
|
||||
$execution2->RD = 'user7';
|
||||
|
||||
$executionArray = array($execution1, $execution2);
|
||||
$result2 = $executionZenTest->setUserMoreLinkTest($executionArray);
|
||||
r(count($result2)) && p() && e(4); // 应返回4个用户列表数组
|
||||
|
||||
// 测试步骤3:传入null参数,获取空用户列表
|
||||
$result3 = $executionZenTest->setUserMoreLinkTest(null);
|
||||
r(count($result3)) && p() && e(4); // 应返回4个数组(但内容为空)
|
||||
|
||||
// 测试步骤4:传入空数组,获取空用户列表
|
||||
$result4 = $executionZenTest->setUserMoreLinkTest(array());
|
||||
r(count($result4)) && p() && e(4); // 应返回4个数组(但内容为空)
|
||||
|
||||
// 测试步骤5:传入包含重复用户的执行对象数组
|
||||
$execution3 = new stdClass();
|
||||
$execution3->PO = 'admin'; // 重复用户
|
||||
$execution3->PM = 'user1'; // 重复用户
|
||||
$execution3->QD = 'user8';
|
||||
$execution3->RD = 'user9';
|
||||
|
||||
$executionArrayDup = array($execution1, $execution3);
|
||||
$result5 = $executionZenTest->setUserMoreLinkTest($executionArrayDup);
|
||||
r(count($result5)) && p() && e(4); // 应返回4个用户列表数组,重复用户会被处理
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=测试 executionZen::setUserMoreLink();
|
||||
timeout=0
|
||||
cid=16447
|
||||
|
||||
- 步骤1:传入execution对象数组第0条的pm1属性 @P:项目经理1
|
||||
- 步骤2:传入单个execution对象第0条的pm1属性 @P:项目经理1
|
||||
- 步骤3:传入null参数,验证返回数组长度 @4
|
||||
- 步骤4:传入空数组,验证返回数组长度 @4
|
||||
- 步骤5:验证PO用户获取第1条的po1属性 @P:产品经理1
|
||||
|
||||
*/
|
||||
|
||||
// 1. 导入依赖(路径固定,不可修改)
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/executionzen.unittest.class.php';
|
||||
|
||||
// 2. zendata数据准备(根据需要配置)
|
||||
$user = zenData('user');
|
||||
$user->account->range('admin,pm1,pm2,po1,po2,qd1,qd2,rd1,rd2,dev1');
|
||||
$user->realname->range('管理员,项目经理1,项目经理2,产品经理1,产品经理2,测试主管1,测试主管2,研发主管1,研发主管2,开发1');
|
||||
$user->role->range('admin{1},pm{2},po{2},qd{2},rd{2},dev{1}');
|
||||
$user->deleted->range('0');
|
||||
$user->gen(10);
|
||||
|
||||
$project = zenData('project');
|
||||
$project->id->range('1-5');
|
||||
$project->name->range('项目1,项目2,项目3,项目4,项目5');
|
||||
$project->type->range('sprint');
|
||||
$project->parent->range('0');
|
||||
$project->status->range('doing');
|
||||
$project->deleted->range('0');
|
||||
$project->gen(5);
|
||||
|
||||
// 3. 用户登录(选择合适角色)
|
||||
su('admin');
|
||||
|
||||
// 4. 创建测试实例(变量名与模块名一致)
|
||||
$executionTest = new executionZenTest();
|
||||
|
||||
// 5. 强制要求:必须包含至少5个测试步骤
|
||||
r($executionTest->setUserMoreLinkTest(array((object)array('PO' => 'po1', 'PM' => 'pm1', 'QD' => 'qd1', 'RD' => 'rd1'), (object)array('PO' => 'po2', 'PM' => 'pm2', 'QD' => 'qd2', 'RD' => 'rd2')))) && p('0:pm1') && e('P:项目经理1'); // 步骤1:传入execution对象数组
|
||||
r($executionTest->setUserMoreLinkTest((object)array('PO' => 'po1', 'PM' => 'pm1', 'QD' => 'qd1', 'RD' => 'rd1'))) && p('0:pm1') && e('P:项目经理1'); // 步骤2:传入单个execution对象
|
||||
r(count($executionTest->setUserMoreLinkTest(null))) && p() && e('4'); // 步骤3:传入null参数,验证返回数组长度
|
||||
r(count($executionTest->setUserMoreLinkTest(array()))) && p() && e('4'); // 步骤4:传入空数组,验证返回数组长度
|
||||
r($executionTest->setUserMoreLinkTest((object)array('PO' => 'po1', 'PM' => 'pm1', 'QD' => 'qd1', 'RD' => 'rd1'))) && p('1:po1') && e('P:产品经理1'); // 步骤5:验证PO用户获取
|
||||
@@ -1,129 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=测试 kanbanTao::getERURCardMenu();
|
||||
timeout=0
|
||||
cid=16977
|
||||
|
||||
- 步骤1:正常执行ID和空对象数组 @0
|
||||
- 步骤2:正常执行ID和包含单个需求对象的数组 @1
|
||||
- 步骤3:正常执行ID和包含多个需求对象的数组 @3
|
||||
- 步骤4:不存在的执行ID和对象数组 @1
|
||||
- 步骤5:包含不同状态和类型需求的对象数组 @2
|
||||
|
||||
*/
|
||||
|
||||
// 1. 导入依赖(路径固定,不可修改)
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/kanban.unittest.class.php';
|
||||
|
||||
// 2. zendata数据准备(根据需要配置)
|
||||
$story = zenData('story');
|
||||
$story->id->range('1-10');
|
||||
$story->product->range('1-3');
|
||||
$story->title->range('用需求1,业需求1,父需求1,需求标题4,需求标题5,需求标题6,需求标题7,需求标题8,需求标题9,需求标题10');
|
||||
$story->type->range('epic{2},requirement{3},story{5}');
|
||||
$story->status->range('active{7},draft{2},closed{1}');
|
||||
$story->stage->range('wait{3},projected{2},developing{3},testing{2}');
|
||||
$story->pri->range('1-4');
|
||||
$story->estimate->range('1-10');
|
||||
$story->openedBy->range('admin,user1,user2,user3');
|
||||
$story->assignedTo->range('admin,user1,user2,user3,closed');
|
||||
$story->version->range('1');
|
||||
$story->deleted->range('0');
|
||||
$story->isParent->range('0{8},1{2}');
|
||||
$story->gen(10);
|
||||
|
||||
$execution = zenData('project');
|
||||
$execution->id->range('101-103');
|
||||
$execution->name->range('执行1,执行2,执行3');
|
||||
$execution->type->range('execution');
|
||||
$execution->status->range('doing');
|
||||
$execution->hasProduct->range('1');
|
||||
$execution->deleted->range('0');
|
||||
$execution->gen(3);
|
||||
|
||||
$projectStory = zenData('projectstory');
|
||||
$projectStory->project->range('101,102,103');
|
||||
$projectStory->story->range('1-3,4-6,7-9');
|
||||
$projectStory->version->range('1');
|
||||
$projectStory->gen(9);
|
||||
|
||||
$user = zenData('user');
|
||||
$user->id->range('1-5');
|
||||
$user->account->range('admin,user1,user2,user3,closed');
|
||||
$user->realname->range('管理员,用户1,用户2,用户3,已关闭');
|
||||
$user->password->range('123456');
|
||||
$user->role->range('admin,dev,qa,pm,td');
|
||||
$user->deleted->range('0{4},1{1}');
|
||||
$user->gen(5);
|
||||
|
||||
// 3. 用户登录(选择合适角色)
|
||||
su('admin');
|
||||
|
||||
// 4. 创建测试实例(变量名与模块名一致)
|
||||
$kanbanTest = new kanbanTest();
|
||||
|
||||
// 5. 强制要求:必须包含至少5个测试步骤
|
||||
r($kanbanTest->getERURCardMenuTest(101, array())) && p() && e('0'); // 步骤1:正常执行ID和空对象数组
|
||||
|
||||
$story1 = new stdclass();
|
||||
$story1->id = 1;
|
||||
$story1->type = 'epic';
|
||||
$story1->status = 'active';
|
||||
$story1->title = '用需求1';
|
||||
$story1->stage = 'wait';
|
||||
$story1->assignedTo = 'admin';
|
||||
$story1->version = 1;
|
||||
$story1->deleted = '0';
|
||||
$story1->story = 1;
|
||||
|
||||
r($kanbanTest->getERURCardMenuTest(101, array($story1))) && p() && e('1'); // 步骤2:正常执行ID和包含单个需求对象的数组
|
||||
|
||||
$story2 = new stdclass();
|
||||
$story2->id = 2;
|
||||
$story2->type = 'requirement';
|
||||
$story2->status = 'active';
|
||||
$story2->title = '业需求1';
|
||||
$story2->stage = 'projected';
|
||||
$story2->assignedTo = 'user1';
|
||||
$story2->version = 1;
|
||||
$story2->deleted = '0';
|
||||
$story2->story = 2;
|
||||
|
||||
$story3 = new stdclass();
|
||||
$story3->id = 3;
|
||||
$story3->type = 'story';
|
||||
$story3->status = 'active';
|
||||
$story3->title = '父需求1';
|
||||
$story3->stage = 'developing';
|
||||
$story3->assignedTo = 'user2';
|
||||
$story3->version = 1;
|
||||
$story3->deleted = '0';
|
||||
$story3->story = 3;
|
||||
|
||||
r($kanbanTest->getERURCardMenuTest(101, array($story1, $story2, $story3))) && p() && e('3'); // 步骤3:正常执行ID和包含多个需求对象的数组
|
||||
|
||||
r($kanbanTest->getERURCardMenuTest(999, array($story1))) && p() && e('1'); // 步骤4:不存在的执行ID和对象数组
|
||||
|
||||
$storyDraft = new stdclass();
|
||||
$storyDraft->id = 4;
|
||||
$storyDraft->type = 'story';
|
||||
$storyDraft->status = 'draft';
|
||||
$storyDraft->title = 'draft状态的需求';
|
||||
$storyDraft->stage = 'wait';
|
||||
$storyDraft->assignedTo = 'admin';
|
||||
$storyDraft->story = 4;
|
||||
|
||||
$storyClosed = new stdclass();
|
||||
$storyClosed->id = 5;
|
||||
$storyClosed->type = 'requirement';
|
||||
$storyClosed->status = 'closed';
|
||||
$storyClosed->title = 'closed状态的需求';
|
||||
$storyClosed->stage = 'closed';
|
||||
$storyClosed->assignedTo = 'closed';
|
||||
$storyClosed->story = 5;
|
||||
|
||||
r($kanbanTest->getERURCardMenuTest(101, array($storyDraft, $storyClosed))) && p() && e('2'); // 步骤5:包含不同状态和类型需求的对象数组
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
|
||||
title=测试 kanbanTao::getERURCardMenu();
|
||||
timeout=0
|
||||
cid=16981
|
||||
|
||||
- 步骤1:正常执行ID和空对象数组 @0
|
||||
- 步骤2:正常执行ID和包含需求对象的数组 @2
|
||||
- 步骤3:不存在的执行ID和对象数组 @1
|
||||
- 步骤4:执行ID为0和对象数组 @1
|
||||
- 步骤5:draft状态的需求 @1
|
||||
|
||||
*/
|
||||
|
||||
// 1. 导入依赖(路径固定,不可修改)
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
include dirname(__FILE__, 2) . '/lib/kanban.unittest.class.php';
|
||||
|
||||
// 2. zendata数据准备(根据需要配置)
|
||||
$story = zenData('story');
|
||||
$story->id->range('1-10');
|
||||
$story->product->range('1-3');
|
||||
$story->title->range('用需求1,业需求1,父需求1,需求标题4,需求标题5,需求标题6,需求标题7,需求标题8,需求标题9,需求标题10');
|
||||
$story->type->range('epic{2},requirement{3},story{5}');
|
||||
$story->status->range('active{5},draft{2},reviewing{2},closed{1}');
|
||||
$story->stage->range('wait{3},projected{2},developing{3},testing{2}');
|
||||
$story->pri->range('1-4');
|
||||
$story->estimate->range('1-10');
|
||||
$story->openedBy->range('admin,user1,user2,user3');
|
||||
$story->assignedTo->range('admin,user1,user2,user3,closed');
|
||||
$story->version->range('1');
|
||||
$story->deleted->range('0');
|
||||
$story->gen(10);
|
||||
|
||||
$execution = zenData('project');
|
||||
$execution->id->range('101-103');
|
||||
$execution->name->range('执行1,执行2,执行3');
|
||||
$execution->type->range('execution');
|
||||
$execution->status->range('doing');
|
||||
$execution->hasProduct->range('1');
|
||||
$execution->deleted->range('0');
|
||||
$execution->gen(3);
|
||||
|
||||
$projectStory = zenData('projectstory');
|
||||
$projectStory->project->range('101,102,103');
|
||||
$projectStory->story->range('1-3,4-6,7-9');
|
||||
$projectStory->version->range('1');
|
||||
$projectStory->gen(9);
|
||||
|
||||
// 3. 用户登录(选择合适角色)
|
||||
su('admin');
|
||||
|
||||
// 4. 创建测试实例(变量名与模块名一致)
|
||||
$kanbanTest = new kanbanTest();
|
||||
|
||||
// 5. 强制要求:必须包含至少5个测试步骤
|
||||
r($kanbanTest->getERURCardMenuTest(101, array())) && p() && e('0'); // 步骤1:正常执行ID和空对象数组
|
||||
|
||||
$story1 = new stdclass();
|
||||
$story1->id = 1;
|
||||
$story1->type = 'epic';
|
||||
$story1->status = 'active';
|
||||
$story1->title = '用需求1';
|
||||
$story1->stage = 'wait';
|
||||
$story1->assignedTo = 'admin';
|
||||
$story1->version = 1;
|
||||
$story1->deleted = '0';
|
||||
$story1->story = 1;
|
||||
|
||||
$story2 = new stdclass();
|
||||
$story2->id = 2;
|
||||
$story2->type = 'requirement';
|
||||
$story2->status = 'active';
|
||||
$story2->title = '业需求1';
|
||||
$story2->stage = 'projected';
|
||||
$story2->assignedTo = 'user1';
|
||||
$story2->version = 1;
|
||||
$story2->deleted = '0';
|
||||
$story2->story = 2;
|
||||
|
||||
r($kanbanTest->getERURCardMenuTest(101, array($story1, $story2))) && p() && e('2'); // 步骤2:正常执行ID和包含需求对象的数组
|
||||
|
||||
r($kanbanTest->getERURCardMenuTest(999, array($story1))) && p() && e('1'); // 步骤3:不存在的执行ID和对象数组
|
||||
|
||||
r($kanbanTest->getERURCardMenuTest(0, array($story1))) && p() && e('1'); // 步骤4:执行ID为0和对象数组
|
||||
|
||||
$storyDraft = new stdclass();
|
||||
$storyDraft->id = 3;
|
||||
$storyDraft->type = 'story';
|
||||
$storyDraft->status = 'draft';
|
||||
$storyDraft->title = 'draft状态的需求';
|
||||
$storyDraft->stage = 'wait';
|
||||
$storyDraft->assignedTo = 'admin';
|
||||
$storyDraft->story = 3;
|
||||
|
||||
r($kanbanTest->getERURCardMenuTest(101, array($storyDraft))) && p() && e('1'); // 步骤5:draft状态的需求
|
||||
@@ -59,7 +59,7 @@ $lang->misc->zentao->others['meshiot' ] = "<img src='{$config->webRoot}theme/def
|
||||
|
||||
$lang->misc->mobile = "手机访问";
|
||||
$lang->misc->noGDLib = "请用手机浏览器访问:<strong>%s</strong>";
|
||||
$lang->misc->copyright = "© 2009 - " . date('Y') . " <a href='https://www.easycorp.cn' target='_blank'>禅道软件(青岛)有限公司</a> 电话:4006-8899-23 Email:<a href='mailto:co@zentao.net'>co@zentao.net</a> QQ:1492153927";
|
||||
$lang->misc->copyright = "© 2009 - " . date('Y') . " <a href='https://www.chandao.com' target='_blank'>禅道软件(青岛)有限公司</a> 电话:4006-8899-23 Email:<a href='mailto:co@zentao.net'>co@zentao.net</a> QQ:1492153927";
|
||||
$lang->misc->checkTable = "检查修复数据表";
|
||||
$lang->misc->needRepair = "修复表";
|
||||
$lang->misc->repairTable = "数据库表可能因为断电原因损坏,需要检查修复!!";
|
||||
|
||||
@@ -59,7 +59,7 @@ $lang->misc->zentao->others['meshiot' ] = "<img src='{$config->webRoot}theme/def
|
||||
|
||||
$lang->misc->mobile = "手機訪問";
|
||||
$lang->misc->noGDLib = "請用手機瀏覽器訪問:<strong>%s</strong>";
|
||||
$lang->misc->copyright = "© 2009 - " . date('Y') . " <a href='https://www.easycorp.cn' target='_blank'>青島易軟天創網絡科技有限公司</a> 電話:4006-8899-23 Email:<a href='mailto:co@zentao.net'>co@zentao.net</a> QQ:1492153927";
|
||||
$lang->misc->copyright = "© 2009 - " . date('Y') . " <a href='https://www.chandao.com' target='_blank'>青島易軟天創網絡科技有限公司</a> 電話:4006-8899-23 Email:<a href='mailto:co@zentao.net'>co@zentao.net</a> QQ:1492153927";
|
||||
$lang->misc->checkTable = "檢查修復數據表";
|
||||
$lang->misc->needRepair = "修復表";
|
||||
$lang->misc->repairTable = "資料庫表可能因為斷電原因損壞,需要檢查修復!!";
|
||||
|
||||
@@ -113,6 +113,7 @@ dtable
|
||||
set::data($data),
|
||||
set::userMap($users),
|
||||
set::fixedLeftWidth('44%'),
|
||||
set::noNestedCheck(true),
|
||||
set::onRenderCell(jsRaw('window.renderCell')),
|
||||
set::checkable(true),
|
||||
set::defaultSummary(array('html' => $summary)),
|
||||
|
||||
@@ -24,7 +24,7 @@ $cols['assignedTo']['type'] = 'user';
|
||||
$cols['module']['type'] = 'text';
|
||||
$cols['module']['map'] = $modules;
|
||||
|
||||
foreach($allStories as $story) $story->estimate = $story->estimate . $config->hourUnit;
|
||||
foreach($allStories as $story) $story->estimate = helper::formatHours($story->estimate) . $config->hourUnit;
|
||||
|
||||
$config->product->search['fields']['title'] = $lang->productplan->storyTitle;
|
||||
searchForm
|
||||
|
||||
@@ -104,7 +104,7 @@ formPanel
|
||||
)
|
||||
);
|
||||
|
||||
foreach($planStories as $story) $story->estimate = $story->estimate . $config->hourUnit;
|
||||
foreach($planStories as $story) $story->estimate = helper::formatHours($story->estimate) . $config->hourUnit;
|
||||
dtable
|
||||
(
|
||||
setID('planStories'),
|
||||
|
||||
@@ -154,7 +154,7 @@ if($canBatchActionBug)
|
||||
|
||||
$planStories = initTableData($planStories, $storyCols, $this->productplan);
|
||||
$planBugs = initTableData($planBugs, $bugCols, $this->productplan);
|
||||
foreach($planStories as $story) $story->estimate = $story->estimate . $config->hourUnit;
|
||||
foreach($planStories as $story) $story->estimate = helper::formatHours($story->estimate) . $config->hourUnit;
|
||||
|
||||
$createStoryLink = common::hasPriv('story', 'create') ? $this->createLink('story', 'create', "productID=$plan->product&branch=$plan->branch&moduleID=0&storyID=0&projectID=$projectID&bugID=0&planID=$plan->id") : null;
|
||||
$batchCreateStoryLink = common::hasPriv('story', 'batchCreate') ? $this->createLink('story', 'batchCreate', "productID=$plan->product&branch=$plan->branch&moduleID=0&story=0&project=$projectID&plan={$plan->id}") : null;
|
||||
|
||||
@@ -2884,7 +2884,7 @@ class storyModel extends model
|
||||
->fetchPairs();
|
||||
|
||||
$review = $this->storyTao->getRevertStoryIdList((int)$productID);
|
||||
$sql = str_replace(array('`product`', '`version`', '`branch`'), array('t1.`product`', 't1.`version`', 't1.`branch`'), $sql);
|
||||
$sql = str_replace(array('`product`', '`version`', '`branch`', '`id`'), array('t1.`product`', 't1.`version`', 't1.`branch`', 't1.`id`'), $sql);
|
||||
if(strpos($sql, 'result') !== false)
|
||||
{
|
||||
if(strpos($sql, 'revert') !== false)
|
||||
|
||||
@@ -158,7 +158,7 @@ if($isFromDoc || $isFromAI)
|
||||
}
|
||||
|
||||
$linkParams = $rawMethod == 'groupcase' ? "productID=$productID&branch=$branch&groupBy=$groupBy&objectID=0&caseType=$caseType&browseType={key}" : "productID=$productID&branch=$branch&browseType={key}¶m=0" . $suffixParam;
|
||||
if($app->tab == 'project' && $from != 'doc' && $from != 'ai') $linkParams = "projectID={$projectID}&$linkParams";
|
||||
if($app->tab == 'project' && $from != 'doc' && $from != 'ai' && $rawMethod != 'groupcase') $linkParams = "projectID={$projectID}&$linkParams";
|
||||
$browseLink = createLink('testcase', 'browse', $linkParams);
|
||||
if($app->tab == 'project') $browseLink = createLink('project', 'testcase', $linkParams);
|
||||
if($app->tab == 'execution' && $from != 'doc' && $from != 'ai') $browseLink = createLink('execution', 'testcase', "executionID={$executionID}&productID=$productID&branch=$branch&browseType={key}");
|
||||
|
||||
@@ -1669,7 +1669,7 @@ class testcaseZen extends testcase
|
||||
foreach($fromSteps[$caseID] as $step)
|
||||
{
|
||||
$stepID = $step->id;
|
||||
$step->version = zget($libCase, 'version', $step->version);
|
||||
$step->version = zget($libCase, 'version', 1);
|
||||
unset($step->id);
|
||||
$steps[$caseID][$stepID] = $step;
|
||||
}
|
||||
|
||||
+40
-119
@@ -11,6 +11,23 @@
|
||||
*/
|
||||
class upgrade extends control
|
||||
{
|
||||
public function __construct(string $moduleName = '', string $methodName = '', string $appName = '')
|
||||
{
|
||||
parent::__construct($moduleName, $methodName, $appName);
|
||||
|
||||
$statusFile = $this->loadModel('common')->checkSafeFile();
|
||||
if($statusFile)
|
||||
{
|
||||
$this->view->title = $this->lang->upgrade->common;
|
||||
$this->view->statusFile = $statusFile;
|
||||
$this->display('upgrade', 'setStatusFile');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->session->set('upgrading', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The index page.
|
||||
*
|
||||
@@ -24,7 +41,8 @@ class upgrade extends control
|
||||
$upgradeFile = $this->app->wwwRoot . 'upgrade.php';
|
||||
if(!file_exists($upgradeFile)) $this->locate($this->createLink('my', 'index'));
|
||||
|
||||
if(version_compare($this->config->installedVersion, '6.4', '<=')) $this->locate(inlink('license'));
|
||||
$openVersion = $this->upgrade->getOpenVersion(str_replace('.', '_', $this->config->installedVersion));
|
||||
if(version_compare($openVersion, '6.4', '<=')) $this->locate(inlink('license'));
|
||||
$this->locate(inlink('backup'));
|
||||
}
|
||||
|
||||
@@ -54,8 +72,6 @@ class upgrade extends control
|
||||
*/
|
||||
public function backup()
|
||||
{
|
||||
$this->session->set('upgrading', true);
|
||||
|
||||
$this->view->title = $this->lang->upgrade->common;
|
||||
$this->display();
|
||||
}
|
||||
@@ -96,7 +112,8 @@ class upgrade extends control
|
||||
if(empty($this->config->upgrade->fromVersion)) $this->setting->setItem('system.upgrade.fromVersion', $this->post->fromVersion);
|
||||
|
||||
$fromVersion = $this->config->upgrade->fromVersion ?? $this->post->fromVersion;
|
||||
$this->locate(inlink('confirm', "fromVersion={$fromVersion}"));
|
||||
if(strpos($fromVersion, 'lite') !== false) $fromVersion = $this->config->upgrade->liteVersion[$fromVersion];
|
||||
$this->locate(inlink('execute', "fromVersion={$fromVersion}"));
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->upgrade->common . $this->lang->hyphen . $this->lang->upgrade->selectVersion;
|
||||
@@ -114,8 +131,6 @@ class upgrade extends control
|
||||
*/
|
||||
public function confirm(string $fromVersion = '')
|
||||
{
|
||||
if(file_exists($this->app->getTmpRoot() . 'upgradeSqlLines')) @unlink($this->app->getTmpRoot() . 'upgradeSqlLines');
|
||||
|
||||
$this->view->fromVersion = $fromVersion;
|
||||
|
||||
if(strpos($fromVersion, 'lite') !== false) $fromVersion = $this->config->upgrade->liteVersion[$fromVersion];
|
||||
@@ -127,11 +142,8 @@ class upgrade extends control
|
||||
/* When sql is empty then skip it. */
|
||||
if(empty($confirmSql)) $this->locate(inlink('execute', "fromVersion={$fromVersion}"));
|
||||
|
||||
$this->session->set('step', '');
|
||||
$this->view->title = $this->lang->upgrade->confirm;
|
||||
$this->view->confirm = $confirmSql;
|
||||
$this->view->writable = is_writable($this->app->getTmpRoot()) ? true : false;
|
||||
|
||||
$this->display();
|
||||
}
|
||||
|
||||
@@ -175,8 +187,8 @@ class upgrade extends control
|
||||
$sessionChanges = [];
|
||||
foreach($upgradeChanges as $change)
|
||||
{
|
||||
if($change['type'] == 'sql') $sessionChanges[] = ['executed' => false, 'type' => 'sql', 'sqlFile' => $change['sqlFile'], 'sqlMd5' => md5($change['sql'])];
|
||||
if($change['type'] == 'method') $sessionChanges[] = ['executed' => false, 'type' => 'method', 'method' => $change['method']];
|
||||
if($change['type'] == 'sql') $sessionChanges[] = ['version' => $change['version'], 'executed' => false, 'type' => 'sql', 'fileMd5' => $change['fileMd5'], 'sqlMd5' => $change['sqlMd5']];
|
||||
if($change['type'] == 'method') $sessionChanges[] = ['version' => $change['version'], 'executed' => false, 'type' => 'method', 'method' => $change['method']];
|
||||
}
|
||||
$this->loadModel('setting')->setItem('system.upgrade.upgradeChanges', json_encode($sessionChanges));
|
||||
|
||||
@@ -232,8 +244,8 @@ class upgrade extends control
|
||||
|
||||
foreach($upgradeChanges as $key => $change)
|
||||
{
|
||||
if($change['type'] == 'sql' && isset($executedChanges['sqls'][$change['sqlFile']][$change['sqlMd5']])) $upgradeChanges[$key]['executed'] = true;
|
||||
if($change['type'] == 'method' && isset($executedChanges['methods'][$change['method']])) $upgradeChanges[$key]['executed'] = true;
|
||||
if($change['type'] == 'sql' && isset($executedChanges[$change['version']]['sqls'][$change['fileMd5']][$change['sqlMd5']])) $upgradeChanges[$key]['executed'] = true;
|
||||
if($change['type'] == 'method' && isset($executedChanges[$change['version']]['methods'][$change['method']])) $upgradeChanges[$key]['executed'] = true;
|
||||
}
|
||||
|
||||
$executedKeys = array_keys(array_filter($upgradeChanges, function($change)
|
||||
@@ -253,7 +265,7 @@ class upgrade extends control
|
||||
$this->setting->deleteItems('owner=system&module=upgrade&key=executedChanges');
|
||||
}
|
||||
|
||||
return print(json_encode(['executedKeys' => $executedKeys, 'allChangesExecuted' => $allChangesExecuted]));
|
||||
return print(json_encode(['version' => $this->config->installedVersion, 'executedKeys' => $executedKeys, 'allChangesExecuted' => $allChangesExecuted]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,59 +491,6 @@ class upgrade extends control
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取执行sql的进度。
|
||||
* Ajax get progress.
|
||||
*
|
||||
* @param int $offset
|
||||
* @access public
|
||||
* @return 1
|
||||
*/
|
||||
public function ajaxGetProgress(int $offset = 0)
|
||||
{
|
||||
$tmpProgressFile = $this->app->getTmpRoot() . 'upgradeSqlLines';
|
||||
$upgradeLogFile = $this->upgrade->getLogFile();
|
||||
|
||||
/* 计算执行的进度。*/
|
||||
/* Compute progress for executiong sql. */
|
||||
$progress = 1;
|
||||
if(file_exists($tmpProgressFile) && $offset != 0)
|
||||
{
|
||||
$sqlLines = file_get_contents($tmpProgressFile);
|
||||
if(empty($sqlLines)) $progress = $this->session->upgradeProgress ? $this->session->upgradeProgress : 1;
|
||||
if($sqlLines == 'completed') $progress = 100;
|
||||
|
||||
if(strpos($sqlLines, '-') !== false)
|
||||
{
|
||||
$sqlLines = explode('-', $sqlLines);
|
||||
$progress = round((int)$sqlLines[1] / (int)$sqlLines[0] * 100);
|
||||
}
|
||||
if($progress > 95) $progress = 100;
|
||||
|
||||
/* Fix progress 1 to 99. */
|
||||
$progress = empty($progress) ? 1 : $progress;
|
||||
if($progress >= 100) $progress = 99;
|
||||
|
||||
$this->session->set('upgradeProgress', $progress);
|
||||
}
|
||||
|
||||
/* 显示执行 sql 的日志。*/
|
||||
/* Display the log of execution sql. */
|
||||
$log = !file_exists($upgradeLogFile) ? '' : file_get_contents($upgradeLogFile, false, null, $offset);
|
||||
$size = 10 * 1024;
|
||||
if(!empty($log) && mb_strlen($log) > $size)
|
||||
{
|
||||
$left = mb_substr($log, $size);
|
||||
$log = mb_substr($log, 0, $size);
|
||||
$position = strpos($left, "\n");
|
||||
if($position !== false) $log .= substr($left, 0, $position + 1);
|
||||
}
|
||||
|
||||
$offset += strlen($log);
|
||||
$log = trim($log);
|
||||
return print(json_encode(array('log' => str_replace("\n", "<br />", htmlspecialchars($log)) . ($log ? '<br />' : ''), 'progress' => $progress, 'offset' => $offset)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取修复冲突的记录。
|
||||
* Ajax get fix consistency logs.
|
||||
@@ -853,67 +812,29 @@ class upgrade extends control
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理历史指标。
|
||||
* Process old metrics in order to easy of test.
|
||||
* 删除安装和升级文件。
|
||||
* Safe delete install and upgrade files.
|
||||
*
|
||||
* @param bool $isDelete
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function processOldMetrics(bool $isDelete = false)
|
||||
public function safeDelete()
|
||||
{
|
||||
if($isDelete)
|
||||
$files = [];
|
||||
$wwwRoot = $this->app->getWwwRoot();
|
||||
foreach(['install', 'upgrade'] as $file)
|
||||
{
|
||||
$this->upgrade->deleteMetrics();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->upgrade->processOldMetrics();
|
||||
if(is_file($wwwRoot . $file . '.php')) $files[] = $wwwRoot . $file . '.php';
|
||||
}
|
||||
|
||||
if(dao::isError()) echo 'fail';
|
||||
if($files)
|
||||
{
|
||||
$command = 'rm -f ' . implode(' ', $files);
|
||||
$tips = $this->lang->upgrade->safeDeleteFile . ' ' . $this->lang->upgrade->execCommand;
|
||||
return $this->displayCommand($command, $tips);
|
||||
}
|
||||
|
||||
echo 'ok';
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理历史指标数据。
|
||||
* Process history metric data.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function processHistoryDataForMetric()
|
||||
{
|
||||
$this->upgrade->processHistoryDataForMetric();
|
||||
if(dao::isError()) echo 'fail';
|
||||
echo 'ok';
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级BI内置数据。
|
||||
* Upgrade BI built-in data.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxUpgradeBIData()
|
||||
{
|
||||
$this->upgrade->upgradeBIData();
|
||||
echo 'ok';
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级大屏和度量项内置数据。
|
||||
* Upgrade screen and metric built-in data.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxUpgradeScreenAndMetricData()
|
||||
{
|
||||
$this->upgrade->upgradeScreenAndMetricData();
|
||||
echo 'ok';
|
||||
$this->locate($this->config->webRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
let netConnct = 0;
|
||||
$.ajax(
|
||||
{
|
||||
url: $.createLink('misc', 'checkNetConnect'),
|
||||
timeout: 1000,
|
||||
success: function(data)
|
||||
{
|
||||
$('#upgrade').attr('href', $.createLink('upgrade', 'consistency', 'netConnect=' + +(data == 'success'))).removeClass('disabled');
|
||||
netConnect = +(data == 'success');
|
||||
},
|
||||
error: function(XMLHttpRequest, textStatus)
|
||||
{
|
||||
$('#upgrade').attr('href', $.createLink('upgrade', 'consistency', 'netConnect=0')).removeClass('disabled');
|
||||
netConnect = 0;
|
||||
}
|
||||
});
|
||||
|
||||
function confirmBackup(event)
|
||||
{
|
||||
if($(event.target).prop('checked'))
|
||||
{
|
||||
$('#upgrade').removeClass('disabled').attr('href', $.createLink('upgrade', 'consistency', 'netConnect=' + netConnect));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#upgrade').addClass('disabled').removeAttr('href');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ function updateProgressInterval()
|
||||
if(result.finished)
|
||||
{
|
||||
clearInterval(interval);
|
||||
window.location.reload();
|
||||
$('#continueBtn').removeClass('disabled').attr('onclick', 'loadCurrentPage()');
|
||||
}
|
||||
})
|
||||
}, 500);
|
||||
|
||||
@@ -32,8 +32,6 @@ $(function()
|
||||
{
|
||||
currentPollVersion = currentVersion;
|
||||
|
||||
let executedCount = 0;
|
||||
|
||||
const poll = () =>
|
||||
{
|
||||
if(shouldAbortAll) return;
|
||||
@@ -45,19 +43,37 @@ $(function()
|
||||
if(shouldAbortAll) return;
|
||||
if(currentPollVersion !== currentVersion) return;
|
||||
|
||||
const executedKeys = response.executedKeys;
|
||||
const executedCount = executedKeys.length;
|
||||
$('#changeBox .change-item').removeClass('executed');
|
||||
executedKeys.forEach(function(key)
|
||||
const executedKeys = response.executedKeys;
|
||||
$('#changesBox .change-item').not('.executed').each(function()
|
||||
{
|
||||
$('#changeBox .change-item[data-key="' + key + '"]').addClass('executed');
|
||||
const $item = $(this);
|
||||
const key = $item.data('key');
|
||||
if(executedKeys.includes(key))
|
||||
{
|
||||
$item.addClass('executed');
|
||||
const $label = $item.find('.label');
|
||||
$label.text($label.data('text'));
|
||||
$label.removeClass('gray-pale text-gray-400').addClass('primary-pale text-primary');
|
||||
}
|
||||
});
|
||||
$('#executedCount').text(executedCount);
|
||||
|
||||
const lastExecuted = $('#changesBox .change-item.executed').last();
|
||||
if(lastExecuted.length)
|
||||
const $executedItems = $('#changesBox .change-item.executed');
|
||||
const $lastExecuted = $executedItems.last();
|
||||
if($lastExecuted.length)
|
||||
{
|
||||
lastExecuted[0].scrollIntoView({behavior: 'smooth', block: 'nearest'});
|
||||
const $nextItem = $lastExecuted.next('.change-item');
|
||||
const scrollItem = $nextItem.length ? $nextItem[0] : $lastExecuted[0];
|
||||
scrollItem.scrollIntoView({behavior: 'smooth', block: 'nearest'});
|
||||
}
|
||||
$('#executedCount').text($executedItems.length);
|
||||
|
||||
const keyLength = executedKeys.length;
|
||||
if(keyLength && executedKeys[keyLength - 1] != keyLength - 1)
|
||||
{
|
||||
console.log(response);
|
||||
console.warn('Detected missing executed change keys. Aborting upgrade to prevent inconsistencies.');
|
||||
abortUpgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
if(response.allChangesExecuted)
|
||||
@@ -76,6 +92,7 @@ $(function()
|
||||
})
|
||||
.fail(function()
|
||||
{
|
||||
console.warn('Failed to fetch upgrade progress. Retrying...');
|
||||
stopTimer();
|
||||
if(currentPollVersion === currentVersion) timer = setTimeout(poll, 500);
|
||||
});
|
||||
@@ -111,8 +128,8 @@ $(function()
|
||||
|
||||
/* 更新版本列表状态为正在升级 */
|
||||
const $versionItem = $('#versionsBox .version-item[data-version="' + currentVersion + '"]');
|
||||
$versionItem.scrollIntoView({behavior: 'smooth', block: 'nearest'});
|
||||
$versionItem.find('.icon').replaceWith('<i class="icon icon-spinner-indicator text-xl text-gray-400"></i>');
|
||||
$versionItem[0].scrollIntoView({behavior: 'smooth', block: 'nearest'});
|
||||
$versionItem.find('.icon-clock').replaceWith('<i class="icon icon-spinner-indicator text-gray-400 w-4 h-4"></i>');
|
||||
|
||||
/* 启动轮询获取升级进度 */
|
||||
const pollPromise = fetchProgress(currentVersion);
|
||||
@@ -146,7 +163,7 @@ $(function()
|
||||
await pollPromise;
|
||||
|
||||
/* 更新版本列表状态为升级完成 */
|
||||
$versionItem.find('.icon').replaceWith('<i class="icon icon-check-circle text-xl text-success"></i>');
|
||||
$versionItem.find('.icon-spinner-indicator').replaceWith('<span class="bg-success rounded-full w-4 h-4"></span>');
|
||||
|
||||
/* 更新版本升级进度条 */
|
||||
const executedVersions = versionIndex + 1;
|
||||
@@ -158,7 +175,7 @@ $(function()
|
||||
if(isLastVersion && upgradeResult.load)
|
||||
{
|
||||
abortUpgrade();
|
||||
window.location.href = upgradeResult.load;
|
||||
$('#continueBtn').removeClass('disabled').attr('href', upgradeResult.load);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -171,7 +188,15 @@ $(function()
|
||||
loadCurrentPage(
|
||||
{
|
||||
selector: '#changesBlock',
|
||||
complete: () => resolve()
|
||||
complete: () =>
|
||||
{
|
||||
const firstChange = $('#changesBox .change-item').first();
|
||||
if(firstChange.length)
|
||||
{
|
||||
firstChange[0].scrollIntoView({behavior: 'smooth', block: 'nearest'});
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
function confirmStatusFile(event)
|
||||
{
|
||||
if($(event.target).prop('checked'))
|
||||
{
|
||||
$('#confirm').removeClass('disabled').attr('onclick', 'loadCurrentPage()');
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#confirm').addClass('disabled').removeAttr('onclick');
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,13 @@ $lang->upgrade->tohome = 'Home';
|
||||
$lang->upgrade->notice = 'Hinweis';
|
||||
$lang->upgrade->checkExtension = 'Erweiterung prüfen';
|
||||
$lang->upgrade->consistency = 'Check Consistency';
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>Database upgrade requires high privileges. Please use the root user.</p>
|
||||
<p>Upgrade carries risks. Please back up your database first, just in case.</p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. You can back up using phpMyAdmin..
|
||||
2. Use the mysql command-line tool:
|
||||
$> mysqldump -u <span class='text-danger'>username</span> -p <span class='text-danger'>dbname</span> > <span class='text-danger'>filename</span>
|
||||
$> mysqldump -u <span class='font-bold text-danger'>username</span> -p <span class='font-bold text-danger'>dbname</span> > <span class='font-bold text-danger'>filename</span>
|
||||
Replace the red parts above with your actual username and database name.
|
||||
<em>Example</em>: mysqldump -u root -p zentao > zentao.bak
|
||||
</pre>
|
||||
@@ -36,20 +36,20 @@ EOT;
|
||||
|
||||
if($config->db->driver == 'dm')
|
||||
{
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>The upgrade requires high database privileges, please use the root user.</p>
|
||||
<p>Please backup your database before updating ZenTao!</p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. It can be backed up by graphical client tools.
|
||||
2. Use DIsql tool to back up data.
|
||||
$> BACKUP DATABASE BACKUPSET <span class='text-danger'>'filename'</span>;
|
||||
$> BACKUP DATABASE BACKUPSET <span class='font-bold text-danger'>'filename'</span>;
|
||||
After the statement is executed, a backup set directory named "filename" is generated in the default backup path.
|
||||
The default backup path is the path configured with BAK_PATH in dm.ini. If BAK_PATH is not configured, bak in SYSTEM_PATH is used by default.
|
||||
This is the simplest database backup statement,To set additional backup options, you need to understand the syntax of the online backup database.
|
||||
</pre>
|
||||
EOT;
|
||||
}
|
||||
|
||||
$lang->upgrade->confirmBackup = 'Ich habe die Datenbank gesichert.';
|
||||
$lang->upgrade->createFileWinCMD = 'Öffnen Sie ein Konsolenfenster und rufen Sie <strong style="color:#ed980f">echo > %s</strong> auf';
|
||||
$lang->upgrade->createFileLinuxCMD = 'Ausführen in der Shell: <strong style="color:#ed980f">touch %s</strong>';
|
||||
$lang->upgrade->setStatusFile = '<h4>Please complete the following actions</h4>
|
||||
|
||||
+13
-14
@@ -26,13 +26,13 @@ $lang->upgrade->tohome = 'Visit ZenTao';
|
||||
$lang->upgrade->notice = 'Notice';
|
||||
$lang->upgrade->checkExtension = 'Check Extensions';
|
||||
$lang->upgrade->consistency = 'Check Consistency';
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>Database upgrade requires high privileges. Please use the root user.</p>
|
||||
<p>Upgrade carries risks. Please back up your database first, just in case.</p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. You can back up using phpMyAdmin..
|
||||
2. Use the mysql command-line tool:
|
||||
$> mysqldump -u <span class='text-danger'>username</span> -p <span class='text-danger'>dbname</span> > <span class='text-danger'>filename</span>
|
||||
$> mysqldump -u <span class='font-bold text-danger'>username</span> -p <span class='font-bold text-danger'>dbname</span> > <span class='font-bold text-danger'>filename</span>
|
||||
Replace the red parts above with your actual username and database name.
|
||||
<em>Example</em>: mysqldump -u root -p zentao > zentao.bak
|
||||
</pre>
|
||||
@@ -40,13 +40,13 @@ EOT;
|
||||
|
||||
if($config->db->driver == 'dm')
|
||||
{
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>The upgrade requires high database privileges, please use the root user.</p>
|
||||
<p>Please backup your database before updating ZenTao!</p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. It can be backed up by graphical client tools.
|
||||
2. Use DIsql tool to back up data.
|
||||
$> BACKUP DATABASE BACKUPSET <span class='text-danger'>'filename'</span>;
|
||||
$> BACKUP DATABASE BACKUPSET <span class='font-bold text-danger'>'filename'</span>;
|
||||
After the statement is executed, a backup set directory named "filename" is generated in the default backup path.
|
||||
The default backup path is the path configured with BAK_PATH in dm.ini. If BAK_PATH is not configured, bak in SYSTEM_PATH is used by default.
|
||||
This is the simplest database backup statement,To set additional backup options, you need to understand the syntax of the online backup database.
|
||||
@@ -54,14 +54,13 @@ if($config->db->driver == 'dm')
|
||||
EOT;
|
||||
}
|
||||
|
||||
$lang->upgrade->createFileWinCMD = 'Open command line and execute <strong style="color:#ed980f">echo > %s</strong>';
|
||||
$lang->upgrade->createFileLinuxCMD = 'Execute command line: <strong style="color:#ed980f">touch %s</strong>';
|
||||
$lang->upgrade->setStatusFile = '<h4>Please complete the following actions</h4>
|
||||
<ul style="line-height:1.5;font-size:13px;">
|
||||
<li>%s</li>
|
||||
<li>Or delete "<strong style="color:#ed980f">%s</strong>" and create <strong style="color:#ed980f">ok.txt</strong> and leave it blank.</li>
|
||||
</ul>
|
||||
<p><strong style="color:red">I have read and done as instructed above. <a href="upgrade.php">Continue upgrading.</a></strong></p>';
|
||||
$lang->upgrade->confirmBackup = 'I have backed up the database';
|
||||
$lang->upgrade->setStatusFileTitle = 'Please complete the following actions';
|
||||
$lang->upgrade->createWinFile = 'Open command line and execute <span id="command" class="font-bold text-danger">echo > %s</span>';
|
||||
$lang->upgrade->createLinuxFile = 'Execute <span id="command" class="font-bold text-danger">touch %s</span> in the command line';
|
||||
$lang->upgrade->deleteStatusFile = 'Or delete <span class="font-bold text-danger">%s</span> and create <span class="font-bold text-danger">ok.txt</span> and leave it blank.';
|
||||
$lang->upgrade->confirmStatusFile = 'I have read and done as instructed above.';
|
||||
$lang->upgrade->safeDeleteFile = 'For system security, the files need to be deleted.';
|
||||
|
||||
$lang->upgrade->selectVersion = 'Version';
|
||||
$lang->upgrade->copyCommand = 'Copy';
|
||||
|
||||
@@ -22,13 +22,13 @@ $lang->upgrade->tohome = 'Visitez ZenTao';
|
||||
$lang->upgrade->notice = 'Avis';
|
||||
$lang->upgrade->checkExtension = 'Vérifiez Extensions';
|
||||
$lang->upgrade->consistency = 'Vérifiez Consistence';
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>Database upgrade requires high privileges. Please use the root user.</p>
|
||||
<p>Upgrade carries risks. Please back up your database first, just in case.</p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. You can back up using phpMyAdmin..
|
||||
2. Use the mysql command-line tool:
|
||||
$> mysqldump -u <span class='text-danger'>username</span> -p <span class='text-danger'>dbname</span> > <span class='text-danger'>filename</span>
|
||||
$> mysqldump -u <span class='font-bold text-danger'>username</span> -p <span class='font-bold text-danger'>dbname</span> > <span class='font-bold text-danger'>filename</span>
|
||||
Replace the red parts above with your actual username and database name.
|
||||
<em>Example</em>: mysqldump -u root -p zentao > zentao.bak
|
||||
</pre>
|
||||
@@ -36,13 +36,13 @@ EOT;
|
||||
|
||||
if($config->db->driver == 'dm')
|
||||
{
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>The upgrade requires high database privileges, please use the root user.</p>
|
||||
<p>Please backup your database before updating ZenTao!</p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. It can be backed up by graphical client tools.
|
||||
2. Use DIsql tool to back up data.
|
||||
$> BACKUP DATABASE BACKUPSET <span class='text-danger'>'filename'</span>;
|
||||
$> BACKUP DATABASE BACKUPSET <span class='font-bold text-danger'>'filename'</span>;
|
||||
After the statement is executed, a backup set directory named "filename" is generated in the default backup path.
|
||||
The default backup path is the path configured with BAK_PATH in dm.ini. If BAK_PATH is not configured, bak in SYSTEM_PATH is used by default.
|
||||
This is the simplest database backup statement,To set additional backup options, you need to understand the syntax of the online backup database.
|
||||
@@ -50,6 +50,7 @@ if($config->db->driver == 'dm')
|
||||
EOT;
|
||||
}
|
||||
|
||||
$lang->upgrade->confirmBackup = "J'ai sauvegardé la base de données";
|
||||
$lang->upgrade->createFileWinCMD = 'Ouvrez la fenêtre Ligne de commandes de windows et exécutez <strong style="color:#ed980f">echo > %s</strong>';
|
||||
$lang->upgrade->createFileLinuxCMD = 'Executez la ligne de commande suivante: <strong style="color:#ed980f">touch %s</strong>';
|
||||
$lang->upgrade->setStatusFile = '<h4>Please complete the following actions</h4>
|
||||
|
||||
@@ -26,13 +26,13 @@ $lang->upgrade->tohome = '访问禅道';
|
||||
$lang->upgrade->notice = '提示';
|
||||
$lang->upgrade->checkExtension = '检查插件';
|
||||
$lang->upgrade->consistency = '一致性检查';
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<div>升级对数据库权限要求较高,请使用 root 用户。</div>
|
||||
<div>升级有危险,请先备份数据库,以防万一。</div>
|
||||
<pre class='bg-gray-200 leading-6 px-3 py-2'>
|
||||
<pre class='leading-6 px-3 py-2'>
|
||||
1. 可以通过phpMyAdmin进行备份。
|
||||
2. 使用mysql命令行的工具:
|
||||
$> mysqldump -u <span class='text-danger'>username</span> -p <span class='text-danger'>dbname</span> > <span class='text-danger'>filename</span>
|
||||
$> mysqldump -u <span class='font-bold text-danger'>username</span> -p <span class='font-bold text-danger'>dbname</span> > <span class='font-bold text-danger'>filename</span>
|
||||
将上面红色的部分分别替换成真实的用户名和禅道系统的数据库名。
|
||||
<em>比如</em>: mysqldump -u root -p zentao > zentao.bak
|
||||
</pre>
|
||||
@@ -40,13 +40,13 @@ EOT;
|
||||
|
||||
if($config->db->driver == 'dm')
|
||||
{
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>升级对数据库权限要求较高,请使用管理员用户。</p>
|
||||
<p>升级有危险,请先备份数据库,以防万一。</p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. 可以通过图形化客户端工具进行备份。
|
||||
2. 使用DIsql工具进行备份。
|
||||
$> BACKUP DATABASE BACKUPSET <span class='text-danger'>'filename'</span>;
|
||||
$> BACKUP DATABASE BACKUPSET <span class='font-bold text-danger'>'filename'</span>;
|
||||
语句执行完后会在默认的备份路径下生成名为“filename”的备份集目录。
|
||||
默认的备份路径为 dm.ini 中 BAK_PATH 配置的路径,若未配置 BAK_PATH,则默认使用 SYSTEM_PATH 下的 bak 目录。
|
||||
这是最简单的数据库备份语句,如果要设置其他的备份选项需了解联机备份数据库的语法。
|
||||
@@ -54,20 +54,19 @@ if($config->db->driver == 'dm')
|
||||
EOT;
|
||||
}
|
||||
|
||||
$lang->upgrade->createFileWinCMD = '打开命令行,执行<strong style="color:#ed980f">echo > %s</strong>';
|
||||
$lang->upgrade->createFileLinuxCMD = '在命令行执行: <strong style="color:#ed980f">touch %s</strong>';
|
||||
$lang->upgrade->setStatusFile = '<h4>升级之前请先完成下面的操作:</h4>
|
||||
<ul style="line-height:1.5;font-size:13px;">
|
||||
<li>%s</li>
|
||||
<li>或者删掉"<strong style="color:#ed980f">%s</strong>" 这个文件 ,重新创建一个<strong style="color:#ed980f">ok.txt</strong>文件,不需要内容。</li>
|
||||
</ul>
|
||||
<p><strong style="color:red">我已经仔细阅读上面提示且完成上述工作,<a href="#" onclick="location.reload()">继续更新</a></strong></p>';
|
||||
$lang->upgrade->confirmBackup = '我已经备份了数据库';
|
||||
$lang->upgrade->setStatusFileTitle = '升级之前请先完成下面的操作';
|
||||
$lang->upgrade->createWinFile = '打开命令行,执行 <span id="command" class="font-bold text-danger">echo > %s</span>';
|
||||
$lang->upgrade->createLinuxFile = '在命令行执行 <span id="command" class="font-bold text-danger">touch %s</span>';
|
||||
$lang->upgrade->deleteStatusFile = '或者删除 <span class="font-bold text-danger">%s</span> 这个文件,重新创建一个 <span class="font-bold text-danger">ok.txt</span> 文件,不需要内容。';
|
||||
$lang->upgrade->confirmStatusFile = '我已经仔细阅读上面提示且完成上述工作';
|
||||
$lang->upgrade->safeDeleteFile = '为了系统安全,需要删除文件。';
|
||||
|
||||
$lang->upgrade->selectVersion = '选择版本';
|
||||
$lang->upgrade->copyCommand = '复制命令';
|
||||
$lang->upgrade->copySuccess = '复制成功';
|
||||
$lang->upgrade->copyFail = '浏览器不支持复制功能,请手动复制';
|
||||
$lang->upgrade->continue = '继续';
|
||||
$lang->upgrade->continue = '继续升级';
|
||||
$lang->upgrade->noteVersion = "务必选择正确的版本,否则会造成数据丢失。";
|
||||
$lang->upgrade->fromVersion = '原来的版本';
|
||||
$lang->upgrade->toVersion = '升级到';
|
||||
|
||||
@@ -21,13 +21,13 @@ $lang->upgrade->tohome = '訪問禪道';
|
||||
$lang->upgrade->notice = '提示';
|
||||
$lang->upgrade->checkExtension = '檢查插件';
|
||||
$lang->upgrade->consistency = '一致性檢查';
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>升級對資料庫權限要求較高,請使用 root 使用者。 </p>
|
||||
<p>升級有危險,請先備份資料庫,以防萬一。 </p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. 可以透過phpMyAdmin進行備份。
|
||||
2. 使用mysql命令列的工具:
|
||||
$> mysqldump -u <span class='text-danger'>username</span> -p <span class='text-danger'>dbname</span> > <span class='text-danger'>filename</span>
|
||||
$> mysqldump -u <span class='font-bold text-danger'>username</span> -p <span class='font-bold text-danger'>dbname</span> > <span class='font-bold text-danger'>filename</span>
|
||||
將上面紅色的部分分別替換成真實的使用者名稱和禪道系統的資料庫名稱。
|
||||
<em>如</em>: mysqldump -u root -p zentao > zentao.bak
|
||||
</pre>
|
||||
@@ -35,13 +35,13 @@ EOT;
|
||||
|
||||
if($config->db->driver == 'dm')
|
||||
{
|
||||
$lang->upgrade->noticeContent = <<<EOT
|
||||
$lang->upgrade->backupNotice = <<<EOT
|
||||
<p>升級對資料庫權限要求較高,請使用管理員使用者。</p>
|
||||
<p>升級有危險,請先備份資料庫,以防萬一。</p>
|
||||
<pre class='bg-gray-200 leading-6 mt-1 p-3'>
|
||||
<pre class='leading-6 mt-1 p-3'>
|
||||
1. 可以透過圖形化客戶端工具進行備份。
|
||||
2. 使用DIsql工具進行備份。
|
||||
$> BACKUP DATABASE BACKUPSET <span class='text-danger'>'filename'</span>;
|
||||
$> BACKUP DATABASE BACKUPSET <span class='font-bold text-danger'>'filename'</span>;
|
||||
語句執行完後會在預設的備份路徑下產生名為「 filename」 的備份集目錄。
|
||||
預設的備份路徑為 dm.ini 中 BAK_PATH 配置的路徑,若未配置 BAK_PATH,則預設使用 SYSTEM_PATH 下的 bak 目錄。
|
||||
這是最簡單的資料庫備份語句,如果要設定其他的備份選項需了解線上備份資料庫的語法。
|
||||
@@ -49,6 +49,7 @@ if($config->db->driver == 'dm')
|
||||
EOT;
|
||||
}
|
||||
|
||||
$lang->upgrade->confirmBackup = '我已經備份了資料庫';
|
||||
$lang->upgrade->createFileWinCMD = '打開命令行,執行<strong style="color:#ed980f">echo > %s</strong>';
|
||||
$lang->upgrade->createFileLinuxCMD = '在命令行執行: <strong style="color:#ed980f">touch %s</strong>';
|
||||
$lang->upgrade->setStatusFile = '<h4>升級之前請先完成下面的操作:</h4>
|
||||
@@ -59,7 +60,7 @@ $lang->upgrade->setStatusFile = '<h4>升級之前請先完成下面的操
|
||||
<p><strong style="color:red">我已經仔細閲讀上面提示且完成上述工作,<a href="#" onclick="location.reload()">繼續更新</a></strong></p>';
|
||||
|
||||
$lang->upgrade->selectVersion = '選擇版本';
|
||||
$lang->upgrade->continue = '繼續';
|
||||
$lang->upgrade->continue = '繼續升級';
|
||||
$lang->upgrade->noteVersion = "務必選擇正確的版本,否則會造成數據丟失。";
|
||||
$lang->upgrade->fromVersion = '原來的版本';
|
||||
$lang->upgrade->toVersion = '升級到';
|
||||
|
||||
+105
-152
@@ -123,18 +123,18 @@ class upgradeModel extends model
|
||||
/* Means open source/pro upgrade to biz or max. */
|
||||
if($this->config->edition != 'open' && ($fromEdition == 'open' || $fromEdition == 'pro'))
|
||||
{
|
||||
$methods['importBuildinModules'] = [];
|
||||
$methods['importLiteModules'] = [];
|
||||
$methods['addSubStatus'] = [];
|
||||
$methods['importBuildinModules'] = [];
|
||||
$methods['importLiteModules'] = [];
|
||||
$methods['addSubStatus'] = [];
|
||||
$methods['workflowgroup-initAllWorkflowGroup'] = [];
|
||||
$methods['processDataset'] = [];
|
||||
$methods['upgradeScreenAndMetricData'] = [];
|
||||
$methods['upgradeBIData'] = [];
|
||||
}
|
||||
$methods['convertCharset'] = [];
|
||||
$methods['program-refreshStats'] = [true];
|
||||
$methods['product-refreshStats'] = [true];
|
||||
$methods['deletePatch'] = [];
|
||||
$methods['processDataset'] = [];
|
||||
$methods['upgradeScreenAndMetricData'] = [];
|
||||
$methods['upgradeBIData'] = [];
|
||||
|
||||
return $methods;
|
||||
}
|
||||
@@ -211,7 +211,7 @@ class upgradeModel extends model
|
||||
|
||||
/* Execute open edition. */
|
||||
$this->saveLogs("Execute $openVersion");
|
||||
$result = $this->execSQL($this->getUpgradeFile(str_replace('_', '.', $openVersion)));
|
||||
$result = $this->execSQL($this->getUpgradeFile(str_replace('_', '.', $openVersion)), $openVersion);
|
||||
if(!$result) return $this->recordExecutedChanges();
|
||||
|
||||
$result = $this->executeByConfig($openVersion);
|
||||
@@ -222,7 +222,7 @@ class upgradeModel extends model
|
||||
foreach($chargedVersion as $version)
|
||||
{
|
||||
$this->saveLogs("Execute $version");
|
||||
$result = $this->execSQL($this->getUpgradeFile(str_replace('_', '.', $version)));
|
||||
$result = $this->execSQL($this->getUpgradeFile(str_replace('_', '.', $version)),$version);
|
||||
if(!$result) return $this->recordExecutedChanges();
|
||||
|
||||
$result = $this->executeByConfig($version);
|
||||
@@ -259,11 +259,11 @@ class upgradeModel extends model
|
||||
*/
|
||||
public function executeOthers(string $fromEdition): bool
|
||||
{
|
||||
$version = str_replace('.', '_', $this->config->installedVersion);
|
||||
$methods = $this->getOtherMethods($fromEdition);
|
||||
|
||||
foreach($methods as $method => $params)
|
||||
{
|
||||
$result = $this->executeUpgradeMethod($method, $params);
|
||||
$result = $this->executeUpgradeMethod($version, $method, $params);
|
||||
if(!$result) return false;
|
||||
}
|
||||
return true;
|
||||
@@ -287,24 +287,24 @@ class upgradeModel extends model
|
||||
|
||||
foreach(array_filter(explode(',', $functions)) as $function)
|
||||
{
|
||||
$result = $this->executeUpgradeMethod($function, zget($params, $function, array()));
|
||||
$result = $this->executeUpgradeMethod($version, $function, zget($params, $function, array()));
|
||||
if(!$result) return false;
|
||||
}
|
||||
if($version == 'pro1_1_1')
|
||||
{
|
||||
$result = $this->execSQL($this->getUpgradeFile('pro1.1'));
|
||||
$result = $this->execSQL($this->getUpgradeFile('pro1.1'), $version);
|
||||
if(!$result) return false;
|
||||
}
|
||||
if($version == 'pro8_3')
|
||||
{
|
||||
$result = $this->execSQL($this->getUpgradeFile('pro8.2'));
|
||||
$result = $this->execSQL($this->getUpgradeFile('pro8.2'), $version);
|
||||
if(!$result) return false;
|
||||
}
|
||||
if(!empty($xxsqls))
|
||||
{
|
||||
foreach(array_filter(explode(',', $xxsqls)) as $sqlFile)
|
||||
{
|
||||
$result = $this->execSQL($sqlFile);
|
||||
$result = $this->execSQL($sqlFile, $version);
|
||||
if(!$result) return false;
|
||||
}
|
||||
}
|
||||
@@ -312,7 +312,7 @@ class upgradeModel extends model
|
||||
{
|
||||
foreach(array_filter(explode(',', $xxfunctions)) as $function)
|
||||
{
|
||||
$result = $this->executeUpgradeMethod($function, zget($params, $function, array()));
|
||||
$result = $this->executeUpgradeMethod($version, $function, zget($params, $function, array()));
|
||||
if(!$result) return false;
|
||||
}
|
||||
}
|
||||
@@ -323,14 +323,15 @@ class upgradeModel extends model
|
||||
* 执行单个升级方法。
|
||||
* Execute single upgrade method.
|
||||
*
|
||||
* @param string $version
|
||||
* @param string $rawMethod
|
||||
* @param array $params
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function executeUpgradeMethod(string $rawMethod, array $params = []): bool
|
||||
public function executeUpgradeMethod(string $version, string $rawMethod, array $params = []): bool
|
||||
{
|
||||
if(isset($this->executedChanges['methods'][$rawMethod])) return true;
|
||||
if(isset($this->executedChanges[$version]['methods'][$rawMethod])) return true;
|
||||
|
||||
$this->saveLogs("Run Method {$rawMethod}");
|
||||
|
||||
@@ -354,7 +355,7 @@ class upgradeModel extends model
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->executedChanges['methods'][$rawMethod] = true;
|
||||
$this->executedChanges[$version]['methods'][$rawMethod] = true;
|
||||
$this->recordExecutedChanges();
|
||||
return true;
|
||||
}
|
||||
@@ -397,12 +398,11 @@ class upgradeModel extends model
|
||||
if(file_exists($sqlFile)) $confirmContent .= file_get_contents($sqlFile);
|
||||
|
||||
/* Get xuanxuan contents. */
|
||||
if(isset($this->config->upgrade->execFlow[$version]['xxsqls']))
|
||||
if(!isset($this->config->upgrade->execFlow[$version]['xxsqls'])) continue;
|
||||
|
||||
foreach(array_filter(explode(',', $this->config->upgrade->execFlow[$version]['xxsqls'])) as $sqlFile)
|
||||
{
|
||||
foreach(array_filter(explode(',', $this->config->upgrade->execFlow[$version]['xxsqls'])) as $sqlFile)
|
||||
{
|
||||
if(file_exists($sqlFile)) $confirmContent .= file_get_contents($sqlFile);
|
||||
}
|
||||
if(file_exists($sqlFile)) $confirmContent .= file_get_contents($sqlFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,7 +481,6 @@ class upgradeModel extends model
|
||||
|
||||
$lines = explode("\n", $sql);
|
||||
$createHead = array_shift($lines);
|
||||
$createFoot = array_pop($lines);
|
||||
|
||||
preg_match_all('/CREATE TABLE [^`]*`([^`]*)`/', $createHead, $out);
|
||||
if(!isset($out[1][0])) return $changes;
|
||||
@@ -495,8 +494,6 @@ class upgradeModel extends model
|
||||
{
|
||||
$dbCreateSQL = $this->dbh->query("SHOW CREATE TABLE `$table`")->fetch(PDO::FETCH_ASSOC);
|
||||
$dbSQLLines = explode("\n", $dbCreateSQL['Create Table']);
|
||||
$dbSQLHead = array_shift($dbSQLLines);
|
||||
$dbSQLFoot = array_pop($dbSQLLines);
|
||||
|
||||
foreach($dbSQLLines as $dbSQLLine)
|
||||
{
|
||||
@@ -663,14 +660,11 @@ class upgradeModel extends model
|
||||
if(!empty($dbOutput[1][0])) $dbType = $dbOutput[1][0];
|
||||
if(!empty($dbOutput[3][0])) $dbLength = $dbOutput[3][0];
|
||||
|
||||
$stdIsInt = stripos($stdType, 'int') !== false;
|
||||
$stdIsVarchar = stripos($stdType, 'varchar') !== false;
|
||||
$stdIsText = stripos($stdType, 'text') !== false;
|
||||
$stdIsFloat = preg_match('/float|decimal|double/i', $stdType);
|
||||
$dbIsInt = stripos($dbType, 'int') !== false;
|
||||
$dbIsVarchar = stripos($dbType, 'varchar') !== false;
|
||||
$dbIsText = stripos($dbType, 'text') !== false;
|
||||
$dbIsFloat = preg_match('/int|float|decimal|double/i', $dbType);
|
||||
|
||||
if($dbIsInt)
|
||||
{
|
||||
@@ -1055,53 +1049,45 @@ class upgradeModel extends model
|
||||
*/
|
||||
public function updateEstimatePriv()
|
||||
{
|
||||
$methods = ['recordWorkhour', 'editEffort', 'deleteWorkhour'];
|
||||
$privTable = $this->config->db->prefix . 'groupPriv';
|
||||
$groups = $this->dao->select('*')->from($privTable)
|
||||
$groups = $this->dao->select('*')->from($privTable)
|
||||
->where('module')->eq('task')
|
||||
->andWhere('method')->eq('edit')
|
||||
->fetchAll();
|
||||
|
||||
$this->dao->begin();
|
||||
|
||||
foreach($groups as $group)
|
||||
{
|
||||
$this->dao->delete()->from($privTable)
|
||||
->where('`group`')->eq($group->group)
|
||||
->andWhere('module')->eq('task')
|
||||
->andWhere('method')->eq('recordWorkhour')
|
||||
->andWhere('method')->in($methods)
|
||||
->exec();
|
||||
if(dao::isError())
|
||||
{
|
||||
$this->dao->rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->dao->insert($privTable)
|
||||
->set('company')->eq($group->company)
|
||||
->set('`group`')->eq($group->group)
|
||||
->set('module')->eq('task')
|
||||
->set('method')->eq('recordWorkhour')
|
||||
->exec();
|
||||
|
||||
$this->dao->delete()->from($privTable)
|
||||
->where('`group`')->eq($group->group)
|
||||
->andWhere('module')->eq('task')
|
||||
->andWhere('method')->eq('editEffort')
|
||||
->exec();
|
||||
|
||||
$this->dao->insert($privTable)
|
||||
->set('company')->eq($group->company)
|
||||
->set('`group`')->eq($group->group)
|
||||
->set('module')->eq('task')
|
||||
->set('method')->eq('editEffort')
|
||||
->exec();
|
||||
|
||||
$this->dao->delete()->from($privTable)
|
||||
->where('`group`')->eq($group->group)
|
||||
->andWhere('module')->eq('task')
|
||||
->andWhere('method')->eq('deleteWorkhour')
|
||||
->exec();
|
||||
|
||||
$this->dao->insert($privTable)
|
||||
->set('company')->eq($group->company)
|
||||
->set('`group`')->eq($group->group)
|
||||
->set('module')->eq('task')
|
||||
->set('method')->eq('deleteWorkhour')
|
||||
->exec();
|
||||
foreach($methods as $method)
|
||||
{
|
||||
$this->dao->insert($privTable)
|
||||
->set('company')->eq($group->company)
|
||||
->set('`group`')->eq($group->group)
|
||||
->set('module')->eq('task')
|
||||
->set('method')->eq($method)
|
||||
->exec();
|
||||
if(dao::isError())
|
||||
{
|
||||
$this->dao->rollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
return $this->dao->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1126,7 +1112,7 @@ class upgradeModel extends model
|
||||
/* Process project actions. */
|
||||
foreach($projects as $projectID)
|
||||
{
|
||||
$productList = isset($projectProducts[$projectID]) ? join(',', array_keys($projectProducts[$projectID])) : '';
|
||||
$productList = isset($projectProducts[$projectID]) ? implode(',', array_keys($projectProducts[$projectID])) : '';
|
||||
$this->dao->update(TABLE_ACTION)->set('product')->eq($productList)->where('objectType')->eq('project')->andWhere('objectID')->eq($projectID)->exec();
|
||||
}
|
||||
|
||||
@@ -1140,7 +1126,7 @@ class upgradeModel extends model
|
||||
}
|
||||
else
|
||||
{
|
||||
$productList = isset($projectProducts[$projectID]) ? join(',', array_keys($projectProducts[$projectID])) : '';
|
||||
$productList = isset($projectProducts[$projectID]) ? implode(',', array_keys($projectProducts[$projectID])) : '';
|
||||
}
|
||||
$this->dao->update(TABLE_ACTION)->set('product')->eq($productList)->where('objectType')->eq('task')->andWhere('objectID')->eq($taskID)->andWhere('project')->eq($projectID)->exec();
|
||||
}
|
||||
@@ -1230,10 +1216,11 @@ class upgradeModel extends model
|
||||
* Execute a sql.
|
||||
*
|
||||
* @param string $sqlFile
|
||||
* @param string $version
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function execSQL(string $sqlFile): bool
|
||||
public function execSQL(string $sqlFile, string $version = ''): bool
|
||||
{
|
||||
$this->saveLogs('Run Method ' . __FUNCTION__);
|
||||
|
||||
@@ -1243,15 +1230,16 @@ class upgradeModel extends model
|
||||
{
|
||||
if(empty($sql)) continue;
|
||||
|
||||
$sqlMd5 = md5($sql);
|
||||
if(isset($this->executedChanges['sqls'][$sqlFile][$sqlMd5])) continue; // Skip the sql that has been executed.
|
||||
$sqlMd5 = md5($sql);
|
||||
$fileMd5 = md5($sqlFile);
|
||||
if(isset($this->executedChanges[$version]['sqls'][$fileMd5][$sqlMd5])) continue; // Skip the sql that has been executed.
|
||||
|
||||
try
|
||||
{
|
||||
$this->saveLogs($sql);
|
||||
$this->dbh->exec($sql);
|
||||
|
||||
$this->executedChanges['sqls'][$sqlFile][$sqlMd5] = true; // Mark the sql has been executed.
|
||||
$this->executedChanges[$version]['sqls'][$fileMd5][$sqlMd5] = true; // Mark the sql has been executed.
|
||||
$this->recordExecutedChanges();
|
||||
}
|
||||
catch(PDOException $e)
|
||||
@@ -1260,7 +1248,7 @@ class upgradeModel extends model
|
||||
$errorCode = !empty($errorInfo) ? $errorInfo[1] : '';
|
||||
if(strpos($ignoreCode, "|$errorCode|") !== false)
|
||||
{
|
||||
$this->executedChanges['sqls'][$sqlFile][$sqlMd5] = true; // Mark the sql has been executed.
|
||||
$this->executedChanges[$version]['sqls'][$fileMd5][$sqlMd5] = true; // Mark the sql has been executed.
|
||||
$this->recordExecutedChanges();
|
||||
}
|
||||
else
|
||||
@@ -1387,66 +1375,42 @@ class upgradeModel extends model
|
||||
*/
|
||||
public function addPriv8_1()
|
||||
{
|
||||
$methods = [
|
||||
'bug' => ['linkBugs', 'unlinkBug'],
|
||||
'story' => ['linkStory', 'unlinkStory'],
|
||||
'testcase' => ['linkCases', 'unlinkCase']
|
||||
];
|
||||
|
||||
$privTable = $this->config->db->prefix . 'grouppriv';
|
||||
|
||||
$oldPriv = $this->dao->select('*')->from($privTable)
|
||||
->where('module')->eq('bug')
|
||||
$oldPrivs = $this->dao->select('*')->from($privTable)
|
||||
->where('module')->in(array_keys($methods))
|
||||
->andWhere('method')->eq('edit')
|
||||
->fetchAll();
|
||||
foreach($oldPriv as $item)
|
||||
{
|
||||
$this->dao->replace($privTable)
|
||||
->set('module')->eq('bug')
|
||||
->set('method')->eq('linkBugs')
|
||||
->set('`group`')->eq($item->group)
|
||||
->exec();
|
||||
->fetchGroup('module');
|
||||
|
||||
$this->dao->replace($privTable)
|
||||
->set('module')->eq('bug')
|
||||
->set('method')->eq('unlinkBug')
|
||||
->set('`group`')->eq($item->group)
|
||||
->exec();
|
||||
$this->dao->begin();
|
||||
|
||||
foreach($oldPrivs as $module => $privs)
|
||||
{
|
||||
foreach($privs as $item)
|
||||
{
|
||||
foreach($methods[$module] as $method)
|
||||
{
|
||||
$this->dao->replace($privTable)
|
||||
->set('module')->eq($module)
|
||||
->set('method')->eq($method)
|
||||
->set('`group`')->eq($item->group)
|
||||
->exec();
|
||||
|
||||
if(dao::isError())
|
||||
{
|
||||
$this->dao->rollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oldPriv = $this->dao->select('*')->from($privTable)
|
||||
->where('module')->eq('story')
|
||||
->andWhere('method')->eq('edit')
|
||||
->fetchAll();
|
||||
foreach($oldPriv as $item)
|
||||
{
|
||||
$this->dao->replace($privTable)
|
||||
->set('module')->eq('story')
|
||||
->set('method')->eq('linkStory')
|
||||
->set('`group`')->eq($item->group)
|
||||
->exec();
|
||||
|
||||
$this->dao->replace($privTable)
|
||||
->set('module')->eq('story')
|
||||
->set('method')->eq('unlinkStory')
|
||||
->set('`group`')->eq($item->group)
|
||||
->exec();
|
||||
}
|
||||
|
||||
$oldPriv = $this->dao->select('*')->from($privTable)
|
||||
->where('module')->eq('testcase')
|
||||
->andWhere('method')->eq('edit')
|
||||
->fetchAll();
|
||||
foreach($oldPriv as $item)
|
||||
{
|
||||
$this->dao->replace($privTable)
|
||||
->set('module')->eq('testcase')
|
||||
->set('method')->eq('linkCases')
|
||||
->set('`group`')->eq($item->group)
|
||||
->exec();
|
||||
|
||||
$this->dao->replace($privTable)
|
||||
->set('module')->eq('testcase')
|
||||
->set('method')->eq('unlinkCase')
|
||||
->set('`group`')->eq($item->group)
|
||||
->exec();
|
||||
}
|
||||
|
||||
return true;
|
||||
return $this->dao->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1908,7 +1872,7 @@ class upgradeModel extends model
|
||||
if(isset($relation[$path])) $newPaths[] = $relation[$path];
|
||||
}
|
||||
|
||||
$newPaths = join(',', $newPaths);
|
||||
$newPaths = implode(',', $newPaths);
|
||||
$this->dao->update(TABLE_MODULE)->set('path')->eq($newPaths)->set('parent')->eq($relation[$module->parent])->where('id')->eq($newModuleID)->exec();
|
||||
$this->dao->update(TABLE_DOC)->set('module')->eq($newModuleID)->where('product')->eq($productID)->andWhere('module')->eq($moduleID)->andWhere('lib')->eq('product')->exec();
|
||||
}
|
||||
@@ -1929,7 +1893,7 @@ class upgradeModel extends model
|
||||
$lib->acl = $project->acl == 'open' ? 'open' : 'custom';
|
||||
|
||||
$teams = $this->dao->select('project, account')->from(TABLE_TEAM)->where('project')->eq($projectID)->fetchPairs('account', 'account');
|
||||
$lib->users = join(',', $teams);
|
||||
$lib->users = implode(',', $teams);
|
||||
if($project->acl == 'custom') $lib->groups = $project->whitelist;
|
||||
$this->dao->insert(TABLE_DOCLIB)->data($lib)->exec();
|
||||
$libID = $this->dao->lastInsertID();
|
||||
@@ -1943,7 +1907,7 @@ class upgradeModel extends model
|
||||
{
|
||||
$docUsers = $teams + explode(',', $lib->users);
|
||||
$docUsers = array_unique($docUsers);
|
||||
$this->dao->update(TABLE_DOCLIB)->set('users')->eq(join(',', $docUsers))->where('id')->eq($lib->id)->exec();
|
||||
$this->dao->update(TABLE_DOCLIB)->set('users')->eq(implode(',', $docUsers))->where('id')->eq($lib->id)->exec();
|
||||
}
|
||||
|
||||
$relation = array();
|
||||
@@ -1962,7 +1926,7 @@ class upgradeModel extends model
|
||||
if(isset($relation[$path])) $newPaths[] = $relation[$path];
|
||||
}
|
||||
|
||||
$newPaths = join(',', $newPaths);
|
||||
$newPaths = implode(',', $newPaths);
|
||||
$newPaths = ",$newPaths,";
|
||||
$this->dao->update(TABLE_MODULE)->set('path')->eq($newPaths)->where('id')->eq($newModuleID)->exec();
|
||||
$this->dao->update(TABLE_DOC)->set('module')->eq($newModuleID)->where('project')->eq($projectID)->andWhere('module')->eq($moduleID)->exec();
|
||||
@@ -2054,7 +2018,7 @@ class upgradeModel extends model
|
||||
$docContent->content .= empty($url) ? '' : $url;
|
||||
$docContent->version = 1;
|
||||
$docContent->type = 'html';
|
||||
if(isset($fileGroups[$doc->id])) $docContent->files = join(',', array_keys($fileGroups[$doc->id]));
|
||||
if(isset($fileGroups[$doc->id])) $docContent->files = implode(',', array_keys($fileGroups[$doc->id]));
|
||||
$this->dao->insert(TABLE_DOCCONTENT)->data($docContent)->exec();
|
||||
}
|
||||
$this->dao->exec('ALTER TABLE ' . TABLE_DOC . ' DROP `digest`');
|
||||
@@ -3764,7 +3728,7 @@ class upgradeModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
$this->dao->update(TABLE_USERVIEW)->set('sprints')->eq(join(',', $executions))->set('projects')->eq(join(',', $projects))->where('account')->eq($account)->exec();
|
||||
$this->dao->update(TABLE_USERVIEW)->set('sprints')->eq(implode(',', $executions))->set('projects')->eq(implode(',', $projects))->where('account')->eq($account)->exec();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -4046,6 +4010,7 @@ class upgradeModel extends model
|
||||
$project->lastEditedBy = $account;
|
||||
$project->lastEditedDate = $now;
|
||||
$project->acl = isset($data->projectAcl) ? $data->projectAcl : 'open';
|
||||
$project->storyType = 'story';
|
||||
$this->dao->insert(TABLE_PROJECT)->data($project)
|
||||
->batchcheck('name', 'notempty')
|
||||
->check('name', 'unique', "type='project' AND parent='{$programID}' AND deleted='0'")
|
||||
@@ -5883,7 +5848,7 @@ class upgradeModel extends model
|
||||
$queryData = array();
|
||||
foreach($idIndices as $id => $index) $queryData[] = "WHEN $id THEN $index";
|
||||
|
||||
$query = "UPDATE `$table` SET `index` = (CASE `id` " . join(' ', $queryData) . " END) WHERE `id` IN(" . join(',', $ids) . ");";
|
||||
$query = "UPDATE `$table` SET `index` = (CASE `id` " . implode(' ', $queryData) . " END) WHERE `id` IN(" . implode(',', $ids) . ");";
|
||||
$this->dao->query($query);
|
||||
|
||||
$messageIndex = max(array_values($idIndices));
|
||||
@@ -5913,7 +5878,7 @@ class upgradeModel extends model
|
||||
$queryData = array();
|
||||
foreach($messages as $message) $queryData[] = "WHEN {$message->id} THEN {$message->index}";
|
||||
|
||||
$query = "UPDATE " . TABLE_IM_CHATUSER . " SET `lastReadMessageIndex` = (CASE `lastReadMessage` " . join(' ', $queryData) . " END) WHERE `id` IN(" . join(',', $lastReadMessages) . ");";
|
||||
$query = "UPDATE " . TABLE_IM_CHATUSER . " SET `lastReadMessageIndex` = (CASE `lastReadMessage` " . implode(' ', $queryData) . " END) WHERE `id` IN(" . implode(',', $lastReadMessages) . ");";
|
||||
$this->dao->query($query);
|
||||
|
||||
return !dao::isError();
|
||||
@@ -5941,7 +5906,7 @@ class upgradeModel extends model
|
||||
$queryData = array();
|
||||
foreach($lastMessages as $cgid => $lastMessage) $queryData[] = "WHEN '{$cgid}' THEN {$lastMessage->$maxIndex}";
|
||||
|
||||
$query = "UPDATE " . TABLE_IM_CHATUSER . " SET `lastReadMessageIndex` = (CASE `cgid` " . join(' ', $queryData) . " END) WHERE `cgid` IN('" . join("','", array_keys($lastMessages)) . "');";
|
||||
$query = "UPDATE " . TABLE_IM_CHATUSER . " SET `lastReadMessageIndex` = (CASE `cgid` " . implode(' ', $queryData) . " END) WHERE `cgid` IN('" . implode("','", array_keys($lastMessages)) . "');";
|
||||
$this->dao->query($query);
|
||||
|
||||
return !dao::isError();
|
||||
@@ -6549,7 +6514,7 @@ class upgradeModel extends model
|
||||
|
||||
if(empty($queryData)) return true;
|
||||
|
||||
$query = "UPDATE " . TABLE_IM_CHAT . " SET `createdDate` = (CASE `id` " . join(' ', $queryData) . " END) WHERE `id` IN(" . join(",", array_values($chats)) . ");";
|
||||
$query = "UPDATE " . TABLE_IM_CHAT . " SET `createdDate` = (CASE `id` " . implode(' ', $queryData) . " END) WHERE `id` IN(" . implode(",", array_values($chats)) . ");";
|
||||
$this->dao->query($query);
|
||||
|
||||
return !dao::isError();
|
||||
@@ -6603,12 +6568,12 @@ class upgradeModel extends model
|
||||
{
|
||||
$queryData = array();
|
||||
foreach($tableRange->start as $id) $queryData[] = "WHEN $id THEN {$tableRange->indexPairs[$id]}";
|
||||
$query = "UPDATE " . TABLE_IM_CHAT_MESSAGE_INDEX . " SET `startIndex` = (CASE `start` " . join(' ', $queryData) . " END) WHERE `start` IN(" . join(',', $tableRange->start) . ");";
|
||||
$query = "UPDATE " . TABLE_IM_CHAT_MESSAGE_INDEX . " SET `startIndex` = (CASE `start` " . implode(' ', $queryData) . " END) WHERE `start` IN(" . implode(',', $tableRange->start) . ");";
|
||||
$this->dao->query($query);
|
||||
|
||||
$queryData = array();
|
||||
foreach($tableRange->end as $id) $queryData[] = "WHEN $id THEN {$tableRange->indexPairs[$id]}";
|
||||
$query = "UPDATE " . TABLE_IM_CHAT_MESSAGE_INDEX . " SET `endIndex` = (CASE `end` " . join(' ', $queryData) . " END) WHERE `end` IN(" . join(',', $tableRange->end) . ");";
|
||||
$query = "UPDATE " . TABLE_IM_CHAT_MESSAGE_INDEX . " SET `endIndex` = (CASE `end` " . implode(' ', $queryData) . " END) WHERE `end` IN(" . implode(',', $tableRange->end) . ");";
|
||||
$this->dao->query($query);
|
||||
}
|
||||
}
|
||||
@@ -6748,6 +6713,7 @@ class upgradeModel extends model
|
||||
$project->lastEditedDate = $now;
|
||||
$project->grade = 2;
|
||||
$project->acl = 'open';
|
||||
$project->storyType = 'story';
|
||||
|
||||
$projectStatus = 'closed';
|
||||
foreach($sprints as $sprint)
|
||||
@@ -6879,7 +6845,7 @@ class upgradeModel extends model
|
||||
{
|
||||
if(isset($relation[$path])) $newPaths[] = $relation[$path];
|
||||
}
|
||||
$newPaths = join(',', $newPaths);
|
||||
$newPaths = implode(',', $newPaths);
|
||||
$parent = !empty($module->parent) ? $relation[$module->parent] : 0;
|
||||
$this->dao->update(TABLE_MODULE)->set('path')->eq($newPaths)->set('parent')->eq($parent)->where('id')->eq($newModuleID)->exec();
|
||||
}
|
||||
@@ -8584,19 +8550,6 @@ class upgradeModel extends model
|
||||
return (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_PROJECT)->where('vision')->eq('rnd')->andWhere('project')->eq(0)->andWhere('type')->eq('sprint')->andWhere('deleted')->eq('0')->fetch('count');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指标。
|
||||
* Delete Metrics.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteMetrics(): bool
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_METRIC)->where('fromID')->ne(0)->exec();
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add default traincourse priv to each group.
|
||||
*
|
||||
|
||||
@@ -827,6 +827,8 @@ class upgradeTao extends upgradeModel
|
||||
$project->lastEditedDate = $now;
|
||||
$project->grade = 2;
|
||||
$project->acl = $sprint->acl == 'open' ? 'open' : 'private';
|
||||
$project->storyType = 'story';
|
||||
|
||||
if($fromMode == 'classic')
|
||||
{
|
||||
$project->multiple = '0';
|
||||
|
||||
@@ -14,7 +14,7 @@ set::zui(true);
|
||||
|
||||
div
|
||||
(
|
||||
setStyle(['padding' => '3rem 4rem', 'height' => '100vh', 'overflow' => 'hidden']),
|
||||
setStyle(['padding' => '3rem 4rem', 'height' => '100vh']),
|
||||
col
|
||||
(
|
||||
setClass('rounded-md bg-white gap-5 m-auto'),
|
||||
@@ -35,8 +35,13 @@ div
|
||||
),
|
||||
col
|
||||
(
|
||||
setClass('rounded-md gap-2.5 bg-gray-100 bg-opacity-70 p-4'),
|
||||
html($lang->upgrade->noticeContent)
|
||||
setClass('rounded-md gap-2.5 bg-gray-100 p-4'),
|
||||
html($lang->upgrade->backupNotice)
|
||||
),
|
||||
checkbox
|
||||
(
|
||||
on::change('confirmBackup'),
|
||||
$lang->upgrade->confirmBackup
|
||||
),
|
||||
div
|
||||
(
|
||||
@@ -45,8 +50,7 @@ div
|
||||
(
|
||||
setID('upgrade'),
|
||||
setClass('btn primary disabled w-24'),
|
||||
set::href(inlink('consistency')),
|
||||
$lang->upgrade->common
|
||||
$lang->upgrade->continue
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -41,14 +41,14 @@ div
|
||||
div
|
||||
(
|
||||
setID('command'),
|
||||
setClass('pre rounded-md bg-gray-100 px-8 py-6'),
|
||||
setClass('pre-wrap break-all break-words rounded-md bg-gray-100 px-8 py-6'),
|
||||
setStyle('height', 'calc(100% - 8rem)'),
|
||||
$command
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('text-warning'),
|
||||
$lang->upgrade->execCommand
|
||||
$tips
|
||||
),
|
||||
row
|
||||
(
|
||||
|
||||
@@ -20,8 +20,8 @@ div
|
||||
setStyle(['padding' => '3rem 4rem', 'height' => '100vh', 'overflow' => 'hidden']),
|
||||
col
|
||||
(
|
||||
setClass('container rounded-md bg-white gap-5 h-full'),
|
||||
setStyle(['padding' => '1.5rem 2rem']),
|
||||
setClass('container rounded-md bg-white gap-5'),
|
||||
setStyle(['padding' => '1.5rem 2rem', 'max-height' => '100%']),
|
||||
div
|
||||
(
|
||||
setClass('text-xl font-medium'),
|
||||
@@ -39,17 +39,17 @@ div
|
||||
setStyle(['max-height' => 'calc(100% - 5rem)']),
|
||||
$hasError ? html($alterSQL . ';') : null
|
||||
),
|
||||
$hasError ? div
|
||||
div
|
||||
(
|
||||
setClass('text-center'),
|
||||
btn
|
||||
(
|
||||
setID('continueBtn'),
|
||||
setClass('px-10'),
|
||||
set::type('primary'),
|
||||
on::click('loadCurrentPage()'),
|
||||
$lang->refresh
|
||||
set::type('primary disabled'),
|
||||
$hasError ? $lang->refresh : $lang->upgrade->continue
|
||||
)
|
||||
) : null
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -18,11 +18,13 @@ jsVar('upgradeVersions', array_keys($upgradeVersions));
|
||||
$editionNames = [];
|
||||
foreach(['open', 'biz', 'max', 'ipd'] as $edition)
|
||||
{
|
||||
$editionNames[$edition] = $edition == 'open' ? $lang->pmsName : $lang->{$edition . 'Name'};
|
||||
$editionName = $lang->{$edition . 'Name'} ?? $edition;
|
||||
$editionNames[$edition] = $edition == 'open' ? $lang->pmsName : $editionName;
|
||||
}
|
||||
|
||||
$toVersionEdition = is_numeric($toVersion[0]) ? 'open' : substr($toVersion, 0, 3);
|
||||
$toVersionName = $editionNames[$toVersionEdition] . str_ireplace($toVersionEdition, '', $toVersion);
|
||||
$versionCount = count($upgradeVersions);
|
||||
|
||||
$buildVersions = function() use ($upgradeVersions, $editionNames)
|
||||
{
|
||||
@@ -30,43 +32,40 @@ $buildVersions = function() use ($upgradeVersions, $editionNames)
|
||||
foreach($upgradeVersions as $version => $label)
|
||||
{
|
||||
$edition = is_numeric($version[0]) ? 'open' : substr($version, 0, 3);
|
||||
$versions[] = div
|
||||
$versions[] = row
|
||||
(
|
||||
row
|
||||
setClass('version-item items-center gap-2'),
|
||||
setData(['version' => $version]),
|
||||
icon
|
||||
(
|
||||
setClass('version-item items-center gap-2'),
|
||||
setData(['version' => $version]),
|
||||
icon
|
||||
(
|
||||
setClass('text-xl text-gray-400'),
|
||||
'clock'
|
||||
),
|
||||
span
|
||||
(
|
||||
$editionNames[$edition] . str_ireplace($edition, '', $label)
|
||||
)
|
||||
setClass('text-gray-400 w-4 h-4'),
|
||||
'clock'
|
||||
),
|
||||
span
|
||||
(
|
||||
$editionNames[$edition] . str_ireplace($edition, '', $label)
|
||||
)
|
||||
);
|
||||
}
|
||||
return $versions;
|
||||
};
|
||||
|
||||
$buildChanges = function() use ($lang, $upgradeChanges)
|
||||
$buildChanges = function() use ($app, $lang, $upgradeChanges)
|
||||
{
|
||||
$changes = [];
|
||||
$bgColors = ['create' => 'success-pale', 'update' => 'primary-pale', 'delete' => 'danger-pale'];
|
||||
$textColors = ['create' => 'text-success', 'update' => 'text-primary', 'delete' => 'text-danger'];
|
||||
$changes = [];
|
||||
$width = $app->getClientLang() == 'en' ? 'w-14' : 'w-11';
|
||||
foreach($upgradeChanges as $key => $change)
|
||||
{
|
||||
$sql = json_encode($change['sql'] ?? []);
|
||||
$changes[] = row
|
||||
(
|
||||
setClass('change-item items-center gap-3'),
|
||||
setData(['key' => $key, 'change' => $change]),
|
||||
setData(['key' => $key]),
|
||||
span
|
||||
(
|
||||
setClass("label {$bgColors[$change['mode']]} {$textColors[$change['mode']]} px-2.5 py-1"),
|
||||
$lang->upgrade->changeModes[$change['mode']]
|
||||
setClass("label gray-pale text-gray-400 px-2.5 py-1 {$width}"),
|
||||
setData(['text' => $lang->upgrade->changeModes[$change['mode']]]),
|
||||
icon('spinner-indicator')
|
||||
),
|
||||
span
|
||||
(
|
||||
@@ -77,7 +76,7 @@ $buildChanges = function() use ($lang, $upgradeChanges)
|
||||
set::href("javascript:showSQL({$sql})"),
|
||||
icon
|
||||
(
|
||||
setClass('text-gray-400 text-lg'),
|
||||
setClass('text-lg text-gray-400'),
|
||||
'fields'
|
||||
),
|
||||
) : null
|
||||
@@ -105,7 +104,7 @@ div
|
||||
row
|
||||
(
|
||||
setClass('bg-gray-100 gap-2 p-2'),
|
||||
setStyle(['max-height' => 'calc(100% - 4rem)']),
|
||||
setStyle(['max-height' => 'calc(100% - 6rem)']),
|
||||
col
|
||||
(
|
||||
setID('versionsBlock'),
|
||||
@@ -118,7 +117,7 @@ div
|
||||
col
|
||||
(
|
||||
setID('versionsBox'),
|
||||
setClass('gap-4 overflow-x-hidden overflow-y-auto h-full'),
|
||||
setClass('gap-2 overflow-x-hidden overflow-y-auto h-full'),
|
||||
$buildVersions
|
||||
),
|
||||
col
|
||||
@@ -130,23 +129,25 @@ div
|
||||
),
|
||||
row
|
||||
(
|
||||
setClass('items-center gap-2'),
|
||||
setClass('justify-between items-center gap-2'),
|
||||
progressbar
|
||||
(
|
||||
setID('versionsProgressBar'),
|
||||
setClass('rounded-full'),
|
||||
setStyle(['height' => '.75rem', 'width' => 'calc(100% - 3rem)']),
|
||||
setStyle(['height' => '.75rem', 'width' => '100%']),
|
||||
set::color('rgba(var(--color-success-500-rgb), var(--tw-bg-opacity));'),
|
||||
set::percent(0)
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('text-right'),
|
||||
setStyle(['min-width' => (strlen((string)$versionCount) + 1) . 'rem']),
|
||||
span
|
||||
(
|
||||
setID('versionsProgressText'),
|
||||
'0'
|
||||
0
|
||||
),
|
||||
' / ' . count($upgradeVersions)
|
||||
' / ' . $versionCount
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -171,10 +172,20 @@ div
|
||||
col
|
||||
(
|
||||
setID('changesBox'),
|
||||
setClass('gap-4 overflow-x-hidden overflow-y-auto h-full'),
|
||||
setClass('gap-2 overflow-x-hidden overflow-y-auto h-full'),
|
||||
$buildChanges
|
||||
)
|
||||
)
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('center'),
|
||||
a
|
||||
(
|
||||
setID('continueBtn'),
|
||||
setClass('btn primary w-24 disabled'),
|
||||
$lang->upgrade->continue
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The license view file of upgrade 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 Mengyi Liu <liumengyi@easycorp.ltd>
|
||||
* @package upgrade
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
set::zui(true);
|
||||
|
||||
jsVar('copySuccess', $lang->upgrade->copySuccess);
|
||||
jsVar('copyFail', $lang->upgrade->copyFail);
|
||||
|
||||
$cmd = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? $lang->upgrade->createWinFile : $lang->upgrade->createLinuxFile;
|
||||
|
||||
div
|
||||
(
|
||||
setStyle(['padding' => '3rem 4rem', 'height' => '100vh']),
|
||||
col
|
||||
(
|
||||
setClass('rounded-md bg-white gap-5 m-auto'),
|
||||
setStyle(['padding' => '1.5rem 2rem', 'width' => '50rem']),
|
||||
div
|
||||
(
|
||||
setClass('text-xl font-medium'),
|
||||
$lang->upgrade->setStatusFileTitle
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('pre-wrap break-all break-words rounded-md bg-gray-100 px-8 py-6'),
|
||||
ul
|
||||
(
|
||||
li(html(sprintf($cmd, $statusFile))),
|
||||
li(html(sprintf($lang->upgrade->deleteStatusFile, $statusFile)))
|
||||
)
|
||||
),
|
||||
checkbox
|
||||
(
|
||||
on::change('confirmStatusFile'),
|
||||
$lang->upgrade->confirmStatusFile
|
||||
),
|
||||
row
|
||||
(
|
||||
setClass('justify-center gap-4'),
|
||||
a
|
||||
(
|
||||
setClass('btn success w-24'),
|
||||
set::href('javascript:copyCommand("#command");'),
|
||||
$lang->upgrade->copyCommand
|
||||
),
|
||||
a
|
||||
(
|
||||
setID('confirm'),
|
||||
setClass('btn primary w-24 disabled'),
|
||||
$lang->upgrade->continue
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
render('pagebase');
|
||||
+63
-15
@@ -2,6 +2,15 @@
|
||||
declare(strict_types=1);
|
||||
class upgradeZen extends upgrade
|
||||
{
|
||||
/**
|
||||
* 升级变更内容缓存。
|
||||
* Upgrade changes cache.
|
||||
*
|
||||
* @var array
|
||||
* @access private
|
||||
*/
|
||||
private $upgradeChanges = [];
|
||||
|
||||
/**
|
||||
* 获取要升级到的版本列表。
|
||||
* Get upgrade versions.
|
||||
@@ -158,8 +167,11 @@ class upgradeZen extends upgrade
|
||||
*/
|
||||
protected function getUpgradeChanges(string $fromVersion, string $toVersion): array
|
||||
{
|
||||
$this->upgradeChanges = [];
|
||||
|
||||
$currentVersion = $this->config->installedVersion;
|
||||
$fromEdition = $this->upgrade->getEditionByVersion($fromVersion);
|
||||
$fromOpenVersion = $this->upgrade->getOpenVersion(str_replace('.', '_', $this->config->installedVersion));
|
||||
$fromOpenVersion = $this->upgrade->getOpenVersion(str_replace('.', '_', $currentVersion));
|
||||
$toOpenVersion = $this->upgrade->getOpenVersion($toVersion);
|
||||
$upgradeVersions = $this->upgrade->getVersionsToUpdate($fromOpenVersion, $fromEdition);
|
||||
|
||||
@@ -170,7 +182,7 @@ class upgradeZen extends upgrade
|
||||
if(version_compare($openVersion, $toOpenVersion, '>=')) continue;
|
||||
|
||||
$sqlFile = $this->upgrade->getUpgradeFile(str_replace('_', '.', $openVersion));
|
||||
$changes = array_merge($changes, $this->getChangesBySql($sqlFile));
|
||||
$changes = array_merge($changes, $this->getChangesBySql($openVersion, $sqlFile));
|
||||
$changes = array_merge($changes, $this->getChangesByConfig($openVersion));
|
||||
|
||||
foreach($chargedVersions as $chargedVersion)
|
||||
@@ -178,7 +190,7 @@ class upgradeZen extends upgrade
|
||||
foreach($chargedVersion as $version)
|
||||
{
|
||||
$sqlFile = $this->upgrade->getUpgradeFile(str_replace('_', '.', $version));
|
||||
$changes = array_merge($changes, $this->getChangesBySql($sqlFile));
|
||||
$changes = array_merge($changes, $this->getChangesBySql($version, $sqlFile));
|
||||
$changes = array_merge($changes, $this->getChangesByConfig($version));
|
||||
}
|
||||
}
|
||||
@@ -191,7 +203,7 @@ class upgradeZen extends upgrade
|
||||
$methods = $this->upgrade->getOtherMethods($edition);
|
||||
foreach(array_keys($methods) as $method)
|
||||
{
|
||||
$changes[] = $this->getChangesByMethod($method);
|
||||
$changes[] = $this->getChangesByMethod($currentVersion, $method);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,26 +227,26 @@ class upgradeZen extends upgrade
|
||||
|
||||
foreach(array_filter(explode(',', $functions)) as $function)
|
||||
{
|
||||
$changes[] = $this->getChangesByMethod($function);
|
||||
$changes[] = $this->getChangesByMethod($version, $function);
|
||||
}
|
||||
|
||||
if($version == 'pro1_1_1')
|
||||
{
|
||||
$sqlFile = $this->ugprade->getUpgradeFile('pro1.1');
|
||||
$sqlChanges = $this->getChangesBySql($sqlFile);
|
||||
$sqlChanges = $this->getChangesBySql($version, $sqlFile);
|
||||
$changes = array_merge($changes, $sqlChanges);
|
||||
}
|
||||
if($version == 'pro8_3')
|
||||
{
|
||||
$sqlFile = $this->ugprade->getUpgradeFile('pro8.2');
|
||||
$sqlChanges = $this->getChangesBySql($sqlFile);
|
||||
$sqlChanges = $this->getChangesBySql($version, $sqlFile);
|
||||
$changes = array_merge($changes, $sqlChanges);
|
||||
}
|
||||
if(!empty($xxsqls))
|
||||
{
|
||||
foreach(array_filter(explode(',', $xxsqls)) as $sqlFile)
|
||||
{
|
||||
$sqlChanges = $this->getChangesBySql($sqlFile);
|
||||
$sqlChanges = $this->getChangesBySql($version, $sqlFile);
|
||||
$changes = array_merge($changes, $sqlChanges);
|
||||
}
|
||||
}
|
||||
@@ -242,7 +254,7 @@ class upgradeZen extends upgrade
|
||||
{
|
||||
foreach(array_filter(explode(',', $xxfunctions)) as $function)
|
||||
{
|
||||
$changes[] = $this->getChangesByMethod($function);
|
||||
$changes[] = $this->getChangesByMethod($version, $function);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,11 +265,12 @@ class upgradeZen extends upgrade
|
||||
* 获取 SQL 文件中的变更内容列表。
|
||||
* Get changes by sql file.
|
||||
*
|
||||
* @param string $version
|
||||
* @param string $sqlFile
|
||||
* @access protected
|
||||
* @return array[]
|
||||
*/
|
||||
protected function getChangesBySql(string $sqlFile): array
|
||||
protected function getChangesBySql(string $version, string $sqlFile): array
|
||||
{
|
||||
if(!is_file($sqlFile)) return [];
|
||||
|
||||
@@ -265,6 +278,12 @@ class upgradeZen extends upgrade
|
||||
$sqls = $this->upgrade->parseToSqls($sqlFile);
|
||||
foreach($sqls as $sql)
|
||||
{
|
||||
$sqlMd5 = md5($sql);
|
||||
$fileMd5 = md5($sqlFile);
|
||||
if(isset($this->upgradeChanges[$version]['sqls'][$fileMd5][$sqlMd5])) continue;
|
||||
|
||||
$this->upgradeChanges[$version]['sqls'][$fileMd5][$sqlMd5] = true;
|
||||
|
||||
$items = $this->parseSqlToSemantic($sql);
|
||||
foreach($items as $item)
|
||||
{
|
||||
@@ -272,7 +291,7 @@ class upgradeZen extends upgrade
|
||||
$replace = [$item['table'] ?? '', $item['field'] ?? '', $item['index'] ?? '', $item['view'] ?? '', $item['old'] ?? '', $item['new'] ?? ''];
|
||||
$subject = $this->lang->upgrade->changeActions[$item['action']] ?? $this->lang->upgrade->changeActions['other'];
|
||||
$content = str_replace($search, $replace, $subject);
|
||||
$changes[] = ['type' => 'sql', 'mode' => $item['mode'], 'content' => $content, 'sql' => $sql, 'sqlFile' => $sqlFile];
|
||||
$changes[] = ['version' => $version, 'type' => 'sql', 'mode' => $item['mode'], 'content' => $content, 'sql' => $sql, 'fileMd5' => $fileMd5, 'sqlMd5' => $sqlMd5];
|
||||
}
|
||||
}
|
||||
return $changes;
|
||||
@@ -282,19 +301,25 @@ class upgradeZen extends upgrade
|
||||
* 获取方法变更内容。
|
||||
* Get changes by method.
|
||||
*
|
||||
* @param string $version
|
||||
* @param string $rawMethod
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getChangesByMethod(string $rawMethod): array
|
||||
protected function getChangesByMethod(string $version, string $rawMethod): array
|
||||
{
|
||||
if(isset($this->upgradeChanges[$version]['methods'][$rawMethod])) return [];
|
||||
|
||||
$this->upgradeChanges[$version]['methods'][$rawMethod] = true;
|
||||
|
||||
$module = 'upgrade';
|
||||
$method = $rawMethod;
|
||||
if(strpos($rawMethod, '-') !== false)
|
||||
{
|
||||
list($module, $method) = explode('-', $rawMethod);
|
||||
}
|
||||
return ['type' => 'method', 'mode' => 'update', 'content' => str_replace(['%MODULE%', '%METHOD%'], [$module, $method], $this->lang->upgrade->changeActions['method']), 'method' => $rawMethod];
|
||||
$content = str_replace(['%MODULE%', '%METHOD%'], [$module, $method], $this->lang->upgrade->changeActions['method']);
|
||||
return ['version' => $version, 'type' => 'method', 'mode' => 'update', 'content' => $content, 'method' => $rawMethod];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,6 +474,28 @@ class upgradeZen extends upgrade
|
||||
{
|
||||
$full = trim($full); // 去除首尾空白
|
||||
|
||||
// 截断第一个未被反引号包围的左括号之前的内容
|
||||
$trimmed = '';
|
||||
$inBacktick = false;
|
||||
for($i = 0; $i < strlen($full); $i++)
|
||||
{
|
||||
$c = $full[$i];
|
||||
if($c === '`')
|
||||
{
|
||||
$inBacktick = !$inBacktick;
|
||||
$trimmed .= $c;
|
||||
}
|
||||
elseif($c === '(' && !$inBacktick)
|
||||
{
|
||||
break; // 遇到未在反引号内的 '(',截断
|
||||
}
|
||||
else
|
||||
{
|
||||
$trimmed .= $c;
|
||||
}
|
||||
}
|
||||
$full = trim($trimmed);
|
||||
|
||||
/* 按未被反引号包围的点分割 */
|
||||
$parts = [];
|
||||
$current = '';
|
||||
@@ -1101,15 +1148,16 @@ class upgradeZen extends upgrade
|
||||
* Display command.
|
||||
*
|
||||
* @param string $command
|
||||
* @param string $tips
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function displayCommand(string $command): void
|
||||
protected function displayCommand(string $command, string $tips = ''): void
|
||||
{
|
||||
$this->view->title = $this->lang->upgrade->common;
|
||||
$this->view->result = 'fail';
|
||||
$this->view->command = $command;
|
||||
|
||||
$this->view->tips = $tips ?: $this->lang->upgrade->execCommand;
|
||||
$this->display('upgrade', 'command');
|
||||
}
|
||||
|
||||
|
||||
+19
-25
@@ -49,31 +49,6 @@ $handler = static function ()
|
||||
/* Check the request is getconfig or not. */
|
||||
if(isset($_GET['mode']) and $_GET['mode'] == 'getconfig') die(helper::removeUTF8Bom($app->exportConfig()));
|
||||
|
||||
/* Remove install.php and upgrade.php. */
|
||||
if(file_exists('install.php') or file_exists('upgrade.php'))
|
||||
{
|
||||
if(!empty($config->inContainer) && !isset($_SESSION['installing']))
|
||||
{
|
||||
if(file_exists('install.php')) unlink('install.php');
|
||||
if(file_exists('upgrade.php')) unlink('upgrade.php');
|
||||
}
|
||||
else
|
||||
{
|
||||
$undeletedFiles = array();
|
||||
if(file_exists('install.php')) $undeletedFiles[] = '<strong style="color:#ed980f">install.php</strong>';
|
||||
if(file_exists('upgrade.php')) $undeletedFiles[] = '<strong style="color:#ed980f">upgrade.php</strong>';
|
||||
$wwwDir = dirname(__FILE__);
|
||||
if($undeletedFiles)
|
||||
{
|
||||
echo "<html><head><meta charset='utf-8'></head>
|
||||
<body><table align='center' style='width:700px; margin-top:100px; border:1px solid gray; font-size:14px;'><tr><td style='padding:8px'>";
|
||||
echo "<div style='margin-bottom:8px;'>安全起见,请删除 <strong style='color:#ed980f'>{$wwwDir}</strong> 目录下的 " . join(' 和 ', $undeletedFiles) . " 文件。</div>";
|
||||
echo "<div>Please remove " . join(' and ', $undeletedFiles) . " under <strong style='color:#ed980f'>$wwwDir</strong> dir for security reason.</div>";
|
||||
die("</td></tr></table></body></html>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($_GET['display']) && $_GET['display'] == 'card') $config->default->view = 'xhtml';
|
||||
|
||||
try
|
||||
@@ -87,6 +62,25 @@ $handler = static function ()
|
||||
$isClient = strpos($_SERVER['HTTP_USER_AGENT'], 'xuanxuan') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'uni-app') !== false;
|
||||
if($app->getViewType() != 'json' && session_id() != $app->sessionID && !$isClient) helper::restartSession($app->sessionID);
|
||||
|
||||
/* Remove install.php and upgrade.php. */
|
||||
$wwwDir = dirname(__FILE__);
|
||||
if(file_exists("{$wwwDir}/install.php") || file_exists("{$wwwDir}/upgrade.php"))
|
||||
{
|
||||
if(!empty($config->inContainer) && !isset($_SESSION['installing']))
|
||||
{
|
||||
if(file_exists('install.php')) unlink('install.php');
|
||||
if(file_exists('upgrade.php')) unlink('upgrade.php');
|
||||
}
|
||||
else
|
||||
{
|
||||
if($app->getModuleName() != 'upgrade' && $app->getMethodName() != 'safedelete')
|
||||
{
|
||||
$url = helper::createLink('upgrade', 'safeDelete');
|
||||
die(header("location: $url"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$app->loadModule();
|
||||
}
|
||||
catch (EndResponseException $endResponseException)
|
||||
|
||||
@@ -1 +1 @@
|
||||
export{M as MonacoEditor,Z as ZenEditor}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
export{M as MonacoEditor,Z as ZenEditor}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
@@ -1,4 +1,4 @@
|
||||
import{t,m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{t,m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{conf as e,language as t}from"./p-115adbf1.js";import"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{conf as e,language as t}from"./p-831ce65f.js";import"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as t}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as t}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as t}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as t}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as e}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as e}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
@@ -1,4 +1,4 @@
|
||||
import{m as t}from"./p-4196c3f1.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
import{m as t}from"./p-c01502dc.js";import"./p-7900c24a.js";import"./p-986e5fe7.js";
|
||||
/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Version: 0.45.0(5e5af013f8d295555a7210df0d5f2cea0bf5dd56)
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{p as e,b as o}from"./p-7900c24a.js";export{s as setNonce}from"./p-7900c24a.js";import{g as r}from"./p-c53e41a7.js";(()=>{const o=import.meta.url,r={};return""!==o&&(r.resourcesUrl=new URL(".",o).href),e(r)})().then((async e=>(await r(),o([["p-1d846237",[[65,"zen-editor",{name:[1],readonly:[1028],uploadUrl:[1,"upload-url"],placeholder:[1],fullscreenable:[4],resizable:[4],exposeEditor:[4,"expose-editor"],size:[1],hideUI:[4,"hide-ui"],hideMenubar:[4,"hide-menubar"],menubarMode:[1,"menubar-mode"],slashMenu:[4,"slash-menu"],bubbleMenu:[4,"bubble-menu"],preferHardBreak:[4,"prefer-hard-break"],neglectDefaultTextStyle:[4,"neglect-default-text-style"],markdown:[4],initialContent:[1,"initial-content"],locale:[1],css:[1],collaborative:[4],hocuspocus:[1],docName:[1,"doc-name"],username:[1],userColor:[1,"user-color"],value:[1025],cssSrc:[1,"css-src"],coreClass:[1,"core-class"],contentClass:[1,"content-class"],proseClass:[1,"prose-class"],editor:[32],isFullscreen:[32],rendered:[32],setHTML:[64],insertHTML:[64],getHTML:[64],getText:[64],focusEditor:[64],blurEditor:[64],setReadonly:[64]},null,{cssSrc:["onCssSrcChange"],css:["onCSSChange"]}],[1,"zen-editor-core",{name:[1],readonly:[4],uploadUrl:[1,"upload-url"],placeholder:[1],initialContent:[1,"initial-content"],resizable:[4],exposeEditor:[4,"expose-editor"],size:[1],hideUI:[4,"hide-ui"],hideMenubar:[4,"hide-menubar"],menubarMode:[1,"menubar-mode"],extraMenubarItems:[1,"extra-menubar-items"],slashMenu:[4,"slash-menu"],bubbleMenu:[4,"bubble-menu"],preferHardBreak:[4,"prefer-hard-break"],neglectDefaultTextStyle:[4,"neglect-default-text-style"],markdown:[4],locale:[1],styles:[16],cssSrc:[1,"css-src"],contentClass:[1,"content-class"],proseClass:[1,"prose-class"],collaborative:[4],hocuspocus:[1],docName:[1,"doc-name"],username:[1],userColor:[1,"user-color"],updateInputValue:[16],fullscreenable:[4],toggleFullscreen:[16],isFullscreen:[4,"is-fullscreen"],value:[1],editor:[32],forceUpdateCounter:[32],isMonaco:[32]},null,{locale:["onLocaleChanage"],cssSrc:["onCssSrcChange"]}],[0,"zen-editor-content",{editor:[16],styles:[16],bubbleMenu:[4,"bubble-menu"],proseClass:[1,"prose-class"]},[[2,"click","handleClick"]]],[0,"zen-editor-menubar",{editor:[16],menubarMode:[2,"menubar-mode"],uploadDisabled:[4,"upload-disabled"],extraMenubarItems:[1,"extra-menubar-items"],forceUpdateCounter:[2,"force-update-counter"],states:[8],toggleMonaco:[16],toggleFullscreen:[16],styles:[16]}],[0,"monaco-editor",{options:[8],initialValue:[1,"initial-value"],tiptapEditor:[16],updateInputValue:[16],setFocus:[64],getValue:[64]},null,{options:["onOptionsChange"]}],[2,"zen-editor-bubble-menu",{menuProps:[16],editor:[16],disabled:[4],element:[32]}],[0,"zen-editor-menu-item",{itemProps:[8,"item-props"],menubarMode:[2,"menubar-mode"],styles:[16]}]]],["p-d22ddfa9",[[0,"zen-editor-floating-menu",{menuProps:[16],editor:[16],element:[32]}]]],["p-ad0a1235",[[1,"zen-editor-slash-menu",{editor:[16],items:[1040],props:[16],currentSelectionIndex:[32]},null,{editor:["onEditorChange"],items:["onItemsChange"],props:["onPropsChange"]}]]],["p-795d6f9f",[[0,"zen-editor-footer",{editor:[16],forceUpdateCounter:[2,"force-update-counter"],toggleFullscreen:[16],showCharCount:[4,"show-char-count"]}]]]],e))));
|
||||
import{p as e,b as o}from"./p-7900c24a.js";export{s as setNonce}from"./p-7900c24a.js";import{g as r}from"./p-c53e41a7.js";(()=>{const o=import.meta.url,r={};return""!==o&&(r.resourcesUrl=new URL(".",o).href),e(r)})().then((async e=>(await r(),o([["p-f18a426a",[[65,"zen-editor",{name:[1],readonly:[1028],uploadUrl:[1,"upload-url"],placeholder:[1],fullscreenable:[4],resizable:[4],exposeEditor:[4,"expose-editor"],size:[1],hideUI:[4,"hide-ui"],hideMenubar:[4,"hide-menubar"],menubarMode:[1,"menubar-mode"],slashMenu:[4,"slash-menu"],bubbleMenu:[4,"bubble-menu"],preferHardBreak:[4,"prefer-hard-break"],neglectDefaultTextStyle:[4,"neglect-default-text-style"],markdown:[4],initialContent:[1,"initial-content"],locale:[1],css:[1],collaborative:[4],hocuspocus:[1],docName:[1,"doc-name"],username:[1],userColor:[1,"user-color"],value:[1025],cssSrc:[1,"css-src"],coreClass:[1,"core-class"],contentClass:[1,"content-class"],proseClass:[1,"prose-class"],editor:[32],isFullscreen:[32],rendered:[32],setHTML:[64],insertHTML:[64],getHTML:[64],getText:[64],focusEditor:[64],blurEditor:[64],setReadonly:[64]},null,{cssSrc:["onCssSrcChange"],css:["onCSSChange"]}],[1,"zen-editor-core",{name:[1],readonly:[4],uploadUrl:[1,"upload-url"],placeholder:[1],initialContent:[1,"initial-content"],resizable:[4],exposeEditor:[4,"expose-editor"],size:[1],hideUI:[4,"hide-ui"],hideMenubar:[4,"hide-menubar"],menubarMode:[1,"menubar-mode"],extraMenubarItems:[1,"extra-menubar-items"],slashMenu:[4,"slash-menu"],bubbleMenu:[4,"bubble-menu"],preferHardBreak:[4,"prefer-hard-break"],neglectDefaultTextStyle:[4,"neglect-default-text-style"],markdown:[4],locale:[1],styles:[16],cssSrc:[1,"css-src"],contentClass:[1,"content-class"],proseClass:[1,"prose-class"],collaborative:[4],hocuspocus:[1],docName:[1,"doc-name"],username:[1],userColor:[1,"user-color"],updateInputValue:[16],fullscreenable:[4],toggleFullscreen:[16],isFullscreen:[4,"is-fullscreen"],value:[1],editor:[32],forceUpdateCounter:[32],isMonaco:[32]},null,{locale:["onLocaleChanage"],cssSrc:["onCssSrcChange"]}],[0,"zen-editor-content",{editor:[16],styles:[16],bubbleMenu:[4,"bubble-menu"],proseClass:[1,"prose-class"]},[[2,"click","handleClick"]]],[0,"zen-editor-menubar",{editor:[16],menubarMode:[2,"menubar-mode"],uploadDisabled:[4,"upload-disabled"],extraMenubarItems:[1,"extra-menubar-items"],forceUpdateCounter:[2,"force-update-counter"],states:[8],toggleMonaco:[16],toggleFullscreen:[16],styles:[16]}],[0,"monaco-editor",{options:[8],initialValue:[1,"initial-value"],tiptapEditor:[16],updateInputValue:[16],setFocus:[64],getValue:[64]},null,{options:["onOptionsChange"]}],[2,"zen-editor-bubble-menu",{menuProps:[16],editor:[16],disabled:[4],element:[32]}],[0,"zen-editor-menu-item",{itemProps:[8,"item-props"],menubarMode:[2,"menubar-mode"],styles:[16]}]]],["p-d22ddfa9",[[0,"zen-editor-floating-menu",{menuProps:[16],editor:[16],element:[32]}]]],["p-ad0a1235",[[1,"zen-editor-slash-menu",{editor:[16],items:[1040],props:[16],currentSelectionIndex:[32]},null,{editor:["onEditorChange"],items:["onItemsChange"],props:["onPropsChange"]}]]],["p-795d6f9f",[[0,"zen-editor-footer",{editor:[16],forceUpdateCounter:[2,"force-update-counter"],toggleFullscreen:[16],showCharCount:[4,"show-char-count"]}]]]],e))));
|
||||
+24
-24
File diff suppressed because one or more lines are too long
+1
-1
@@ -93,7 +93,7 @@ try
|
||||
{
|
||||
/* Run it. */
|
||||
$app->parseRequest();
|
||||
if($common->checkUpgradeStatus()) $app->loadModule();
|
||||
$app->loadModule();
|
||||
}
|
||||
catch (EndResponseException $endResponseException)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user