diff --git a/VERSION b/VERSION index 4b99d26192..343f3a761a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -15.0.beta1 +15.0.rc1 diff --git a/db/update15.0.sql b/db/update15.0.sql index 6bdefddd2b..e5c22331ef 100644 --- a/db/update15.0.sql +++ b/db/update15.0.sql @@ -68,7 +68,7 @@ ALTER TABLE `zt_user` ADD `analysis` text NOT NULL AFTER `nature`; ALTER TABLE `zt_user` ADD `strategy` text NOT NULL AFTER `analysis`; ALTER TABLE `zt_user` CHANGE `avatar` `avatar` text NOT NULL AFTER `commiter`; -REPLACE INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'URSR', '6'); +REPLACE INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'URSR', '2'); REPLACE INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'project', '', 'unitList', 'CNY,USD'); REPLACE INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'project', '', 'defaultCurrency', 'CNY'); diff --git a/db/zentao.sql b/db/zentao.sql index ef21a26c0f..3ca79c4803 100644 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -811,6 +811,7 @@ CREATE TABLE IF NOT EXISTS `zt_story` ( `id` mediumint(8) unsigned NOT NULL auto_increment, `parent` mediumint(9) NOT NULL default '0', `product` mediumint(8) unsigned NOT NULL default '0', + `project` mediumint(8) unsigned NOT NULL default '0', `branch` mediumint(8) unsigned NOT NULL default '0', `module` mediumint(8) unsigned NOT NULL default '0', `plan` text, @@ -4146,6 +4147,7 @@ REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`) V INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'hourPoint', '1'); INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'common', '', 'CRProduct', '1'); INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'common', '', 'CRExecution', '1'); -INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'URSR', '6'); +INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'URSR', '2'); +INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'common', 'global', 'mode', 'new'); INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'project', '', 'unitList', 'CNY,USD'); INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'project', '', 'defaultCurrency', 'CNY'); diff --git a/framework/router.class.php b/framework/router.class.php index e665fb722d..569859956e 100755 --- a/framework/router.class.php +++ b/framework/router.class.php @@ -154,7 +154,6 @@ class router extends baseRouter { if(!defined('ITERATION_KEY')) define('ITERATION_KEY', 0); if(!defined('SPRINT_KEY')) define('SPRINT_KEY', 1); - if(!defined('STAGE_KEY')) define('STAGE_KEY', 2); if(!defined('PRODUCT_KEY')) define('PRODUCT_KEY', 0); if(!defined('STORYPOINT_KEY')) define('STORYPOINT_KEY', 1); if(!defined('FUNCTIONPOINT_KEY')) define('FUNCTIONPOINT_KEY', 2); @@ -206,12 +205,7 @@ class router extends baseRouter if($hourKey == STORYPOINT_KEY) $config->hourUnit = 'sp'; if($hourKey == FUNCTIONPOINT_KEY) $config->hourUnit = 'fp'; - $model = new stdclass(); - $model->model = 'scrum'; - //if($this->session->PRJ) $model = $this->dbh->query('SELECT model FROM' . TABLE_PROJECT . "WHERE id = {$this->session->PRJ}")->fetch(); - $iterationKey = $projectKey; - if(isset($model->model) && $model->model == 'waterfall') $projectKey = STAGE_KEY; /* Set productCommon, projectCommon and hourCommon. Default english lang. */ $lang->productCommon = $this->config->productCommonList[$this->clientLang][PRODUCT_KEY]; @@ -244,7 +238,7 @@ class router extends baseRouter if($this->dbh and !empty($this->config->db->name) and !defined('IN_UPGRADE')) { /* Get story concept in project and product. */ - $URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and section = 'URSRList' and `key` = \"{$this->clientLang}\"")->fetchAll(); + $URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and section = 'URSRList' and `lang` = \"{$this->clientLang}\"")->fetchAll(); if(empty($URSRList)) $URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and section = 'URSRList' and `key` = \"{$config->URSR}\"")->fetchAll(); /* Get UR pairs and SR pairs. */ diff --git a/module/block/view/contribute.html.php b/module/block/view/contribute.html.php index 97858ccb15..9e2260c17a 100644 --- a/module/block/view/contribute.html.php +++ b/module/block/view/contribute.html.php @@ -6,10 +6,12 @@
block->createdTodos;?>
createLink('my', 'todo', 'type=all'), (int)$data['createdTodos']);?>
+ URAndSR):?>
block->createdRequirements;?>
+
block->createdStories;?>
diff --git a/module/block/view/projectstatisticblock.html.php b/module/block/view/projectstatisticblock.html.php index 37532e6a0d..23cbdc39f4 100644 --- a/module/block/view/projectstatisticblock.html.php +++ b/module/block/view/projectstatisticblock.html.php @@ -62,10 +62,10 @@ html[lang="en"] .product-info .type-info {color: #A6AAB8; text-align: center; po .block-statistic .data {width: 40%; text-align: left; padding: 10px 0px; font-size: 14px; font-weight: 700;} .block-statistic .dataTitle {width: 60%; text-align: right; padding: 10px 0px; font-size: 14px;} -.block-statistic .executionName {padding-top: 2px; font-size: 14px;} +.block-statistic .executionName {padding: 2px 10px; font-size: 14px;} .block-statistic .lastIteration {padding-top: 6px;} .block-statistic .progress-text-left {margin-right: 90px} -.block-statistic .progress-text-left .progress-text {padding-top: 2px; font-size: 14px;} +.block-statistic .progress-text-left .progress-text {padding-top: 2px; font-size: 14px; padding-right:5px; left: -45px;} .status-count {margin: auto;} .status-count tr:first-child td:last-child {color: #000; font-weight: bold;} @@ -134,68 +134,70 @@ $(function()
- model == 'scrum'):?> -
-
-

block->story;?>

-
-
project->allStories . ":";?>
-
allStories;?>
+ model == 'scrum'):?> +
+
+

block->story;?>

+
+
project->allStories . ":";?>
+
allStories;?>
+
+
+
project->doneStories . ":";?>
+
doneStories;?>
+
+
+
block->left . ":";?>
+
leftStories;?>
+
-
-
project->doneStories . ":";?>
-
doneStories;?>
+
+

block->investment;?>

+
+
block->totalPeople . ":";?>
+
teamCount;?>
+
+
+
block->estimate . ":";?>
+
estimate;?>
+
+
+
block->consumedHours . ":";?>
+
consumed;?>
+
-
-
block->left . ":";?>
-
leftStories;?>
+
+

block->bug;?>

+
+
block->totalBug . ":";?>
+
allBugs;?>
+
+
+
block->doneBugs . ":";?>
+
doneBugs;?>
+
+
+
block->leftBugs . ":";?>
+
leftBugs;?>
+
-
-

block->investment;?>

-
-
block->totalPeople . ":";?>
-
teamCount;?>
-
-
-
block->estimate . ":";?>
-
estimate;?>
-
-
-
block->consumedHours . ":";?>
-
consumed;?>
-
-
-
-

block->bug;?>

-
-
block->totalBug . ":";?>
-
allBugs;?>
-
-
-
block->doneBugs . ":";?>
-
doneBugs;?>
-
-
-
block->leftBugs . ":";?>
-
leftBugs;?>
-
-
-
-
-

block->last;?>

-
-
executions[0]->name;?>
-
-
-
- executions[0]->hours->progress . '%';?> + executions)):?> +
+

block->last;?>

+
+
createLink('execution', 'task', "executionID={$project->executions[0]->id}"), $project->executions[0]->name);?>
+
+
+
+ executions[0]->hours->progress . '%';?> +
-
- + +
project->weekly;?> diff --git a/module/block/view/recentprojectblock.html.php b/module/block/view/recentprojectblock.html.php index 5646c60e8a..1b08ff76b4 100644 --- a/module/block/view/recentprojectblock.html.php +++ b/module/block/view/recentprojectblock.html.php @@ -22,7 +22,7 @@ #cards .project-detail .progress {height: 4px;} #cards .project-detail .progress-text-left .progress-text {width: 50px; left: -50px;} #cards .panel-heading {cursor: pointer;} -#cards .project-stages-container {margin: 0 -16px -16px -16px; padding: 0 4px; height: 46px; overflow-x: auto; position: relative;} +#cards .project-stages-container {margin: 0 0 -16px 0; padding: 0 4px; height: 46px; overflow-x: auto; position: relative;} #cards .project-stages:after {content: ' '; width: 30px; display: block; right: -16px; top: 16px; bottom: -6px; z-index: 1; background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%); position: absolute;} #cards .project-stages-row {position: relative; height: 30px; z-index: 0;} #cards .project-stage-item {white-space: nowrap; position: absolute; top: 0; min-width: 48px; padding-top: 13px; color: #838A9D;} diff --git a/module/block/view/todoblock.html.php b/module/block/view/todoblock.html.php index b49987904e..55cb05f1c3 100644 --- a/module/block/view/todoblock.html.php +++ b/module/block/view/todoblock.html.php @@ -96,7 +96,7 @@ if(!$selfCall) die(include('./todolist.html.php')); ?>
  • - > + > date == '2030-01-01') :?> todo->periods['future'] ?> diff --git a/module/bug/control.php b/module/bug/control.php index 90e202f660..8f1d5148fd 100644 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -1098,11 +1098,12 @@ class bug extends control /** * Batch update assign of bug. * - * @param int $executionID + * @param int $objectID projectID|executionID + * @param string $type execution|project|product|my * @access public * @return void */ - public function batchAssignTo($executionID, $type = 'execution') + public function batchAssignTo($objectID, $type = 'execution') { if(!empty($_POST) && isset($_POST['bugIDList'])) { @@ -1119,8 +1120,10 @@ class bug extends control } $this->loadModel('score')->create('ajax', 'batchOther'); } + if($type == 'product' || $type == 'my') die(js::locate($this->session->bugList, 'parent')); - die(js::locate($this->createLink('execution', 'bug', "executionID=$executionID"))); + if($type == 'execution') die(js::locate($this->createLink('execution', 'bug', "executionID=$objectID"))); + if($type == 'project') die(js::locate($this->createLink('project', 'bug', "projectID=$objectID"))); } /** diff --git a/module/common/lang/menu.php b/module/common/lang/menu.php index a01621e869..848b1a7125 100644 --- a/module/common/lang/menu.php +++ b/module/common/lang/menu.php @@ -158,6 +158,8 @@ $lang->product->menuOrder[55] = 'setting'; $lang->product->menuOrder[60] = 'create'; $lang->product->menuOrder[65] = 'all'; +$lang->product->menu->doc['subMenu'] = new stdclass(); + $lang->product->menu->settings['subMenu'] = new stdclass(); $lang->product->menu->settings['subMenu']->view = array('link' => "{$lang->overview}|product|view|productID=%s", 'alias' => 'edit'); $lang->product->menu->settings['subMenu']->module = array('link' => "{$lang->module}|tree|browse|product=%s&view=story", 'subModule' => 'tree'); @@ -197,6 +199,8 @@ $lang->scrum->menuOrder[40] = 'release'; $lang->scrum->menuOrder[48] = 'dynamic'; $lang->scrum->menuOrder[50] = 'settings'; +$lang->scrum->menu->doc['subMenu'] = new stdclass(); + $lang->scrum->menu->qa['subMenu'] = new stdclass(); //$lang->scrum->menu->qa['subMenu']->index = array('link' => "$lang->dashboard|project|qa|projectID=%s"); $lang->scrum->menu->qa['subMenu']->bug = array('link' => "{$lang->bug->common}|project|bug|projectID=%s", 'subModule' => 'bug'); @@ -245,6 +249,8 @@ $lang->execution->menuOrder[55] = 'release'; $lang->execution->menuOrder[60] = 'action'; $lang->execution->menuOrder[65] = 'setting'; +$lang->execution->menu->doc['subMenu'] = new stdclass(); + $lang->execution->menu->view['subMenu'] = new stdclass(); $lang->execution->menu->view['subMenu']->groupTask = "$lang->groupView|execution|grouptask|executionID=%s"; $lang->execution->menu->view['subMenu']->tree = "$lang->treeView|execution|tree|executionID=%s"; @@ -252,7 +258,7 @@ $lang->execution->menu->view['subMenu']->tree = "$lang->treeView|execution| $lang->execution->menu->qa['subMenu'] = new stdclass(); //$lang->execution->menu->qa['subMenu']->qa = array('link' => "$lang->dashboard|execution|qa|executionID=%s"); $lang->execution->menu->qa['subMenu']->bug = array('link' => "{$lang->bug->common}|execution|bug|executionID=%s", 'subModule' => 'bug'); -$lang->execution->menu->qa['subMenu']->testcase = array('link' => "{$lang->testcase->shortCommon}|execution|testcase|executionID=%s", 'alias' => 'create'); +$lang->execution->menu->qa['subMenu']->testcase = array('link' => "{$lang->testcase->shortCommon}|execution|testcase|executionID=%s", 'subModule' => 'testcase'); $lang->execution->menu->qa['subMenu']->testtask = array('link' => "{$lang->testtask->common}|execution|testtask|executionID=%s", 'subModule' => 'testtask'); $lang->execution->menu->qa['subMenu']->testreport = array('link' => "{$lang->testreport->common}|execution|testreport|exeutionID=%s", 'subModule' => 'testreport'); @@ -320,8 +326,8 @@ $lang->doc->menu->dashboard = array('link' => "{$lang->dashboard}|doc|index"); $lang->doc->menu->recent = array('link' => "{$lang->doc->recent}|doc|browse|libID=0&browseTyp=byediteddate", 'alias' => 'recent'); $lang->doc->menu->my = array('link' => "{$lang->doc->my}|doc|browse|libID=0&browseTyp=openedbyme", 'alias' => 'my'); $lang->doc->menu->collect = array('link' => "{$lang->doc->favorite}|doc|browse|libID=0&browseTyp=collectedbyme", 'alias' => 'collect'); -$lang->doc->menu->product = array('link' => "{$lang->doc->product}|doc|objectLibs|type=product"); -$lang->doc->menu->project = array('link' => "{$lang->doc->project}|doc|objectLibs|type=project"); +$lang->doc->menu->product = array('link' => "{$lang->doc->product}|doc|objectLibs|type=product", 'alias' => 'product'); +$lang->doc->menu->project = array('link' => "{$lang->doc->project}|doc|objectLibs|type=project", 'alias' => 'project'); $lang->doc->menu->custom = array('link' => "{$lang->doc->custom}|doc|objectLibs|libID=0"); $lang->doc->dividerMenu = ',product,'; @@ -335,6 +341,9 @@ $lang->doc->menuOrder[25] = 'product'; $lang->doc->menuOrder[30] = 'project'; $lang->doc->menuOrder[35] = 'custom'; +$lang->doc->menu->product['subMenu'] = new stdclass(); +$lang->doc->menu->project['subMenu'] = new stdclass(); + /* Report menu.*/ $lang->report->menu = new stdclass(); $lang->report->menu->annual = array('link' => "{$lang->report->annual}|report|annualData|year=&dept=&userID=" . (isset($_SESSION['user']) ? zget($_SESSION['user'], 'id', 0) : 0), 'target' => '_blank'); @@ -373,7 +382,7 @@ $lang->admin->menu->custom = array('link' => "{$lang->custom->common}|custom| $lang->admin->menu->extension = array('link' => "{$lang->extension->common}|extension|browse", 'subModule' => 'extension'); $lang->admin->menu->dev = array('link' => "$lang->redev|dev|api", 'alias' => 'db', 'subModule' => 'dev,editor,entry'); $lang->admin->menu->message = array('link' => "{$lang->message->common}|message|index", 'subModule' => 'message,mail,webhook'); -$lang->admin->menu->system = array('link' => "{$lang->admin->system}|backup|index", 'subModule' => 'cron,backup,action,admin,search', 'exclude' => 'admin-index'); +$lang->admin->menu->system = array('link' => "{$lang->admin->system}|backup|index", 'subModule' => 'cron,backup,action,admin,search', 'exclude' => 'admin-index,admin-xuanxuan'); /* Admin menu order. */ $lang->admin->menuOrder[5] = 'index'; @@ -529,7 +538,8 @@ $lang->navGroup->entry = 'admin'; $lang->navGroup->extension = 'admin'; $lang->navGroup->action = 'admin'; -$lang->navGroup->search = 'search'; -$lang->navGroup->index = 'index'; -$lang->navGroup->tree = 'tree'; -$lang->navGroup->misc = 'misc'; +$lang->navGroup->search = 'search'; +$lang->navGroup->index = 'index'; +$lang->navGroup->tree = 'tree'; +$lang->navGroup->misc = 'misc'; +$lang->navGroup->upgrade = 'upgrade'; diff --git a/module/custom/config.php b/module/custom/config.php index d8c98dc875..e5837ffb1e 100644 --- a/module/custom/config.php +++ b/module/custom/config.php @@ -62,7 +62,7 @@ $config->custom->fieldList['testsuite'] = 'desc'; $config->custom->fieldList['caselib'] = 'desc'; $config->custom->fieldList['testcase']['createcase'] = 'lib,stage,pri,precondition,keywords'; $config->custom->fieldList['testreport'] = 'begin,end,members,report'; -$config->custom->fieldList['testtask'] = 'owner,pri,status,desc'; +$config->custom->fieldList['testtask'] = 'owner,pri,desc'; $config->custom->fieldList['doc'] = 'keywords,content'; $config->custom->fieldList['user']['create'] = 'dept,role,email,commiter'; $config->custom->fieldList['user']['edit'] = 'dept,role,email,commiter,skype,qq,mobile,phone,address,zipcode,dingding,slack,whatsapp,weixin'; diff --git a/module/custom/control.php b/module/custom/control.php index 00b5a84737..ce5e543de9 100644 --- a/module/custom/control.php +++ b/module/custom/control.php @@ -540,7 +540,7 @@ class custom extends control $mode = zget($this->config->global, 'mode', 'classic'); if($mode == 'new') { - if(isset($this->config->global->upgradeStep) and $this->config->global->upgradeStep == 'mergeProgram') $this->locate($this->createLink('upgrade', 'mergeProgram')); + if(isset($this->config->global->upgradeStep) and $this->config->global->upgradeStep == 'mergeProgram') $this->locate($this->createLink('upgrade', 'mergeProgram'), 'parent'); unset($_SESSION['upgrading']); $this->locate(inlink('index')); diff --git a/module/doc/control.php b/module/doc/control.php index b9ffb0c8a5..d1deb4854e 100644 --- a/module/doc/control.php +++ b/module/doc/control.php @@ -47,10 +47,6 @@ class doc extends control $this->app->loadClass('pager', $static = true); $pager = new pager(0, 5, 1); - $this->lang->TRActions = $this->doc->setFastMenu($this->lang->doc->fast); - $this->lang->TRActions .= common::hasPriv('doc', 'createLib') ? html::a(helper::createLink('doc', 'createLib'), " " . $this->lang->doc->createlib, '', "class='btn btn-secondary iframe' data-width='70%'") : ''; - $this->lang->TRActions .= common::hasPriv('doc', 'create') ? $this->doc->buildCreateButton4Doc() : ''; - $actionURL = $this->createLink('doc', 'browse', "lib=0&browseType=bySearch&queryID=myQueryID"); $this->doc->buildSearchForm(0, array(), 0, $actionURL, 'index'); @@ -218,7 +214,7 @@ class doc extends control if(!dao::isError()) { $this->action->create('docLib', $libID, 'Created'); - die(js::locate($this->createLink($this->moduleName, 'browse', "libID=$libID"), 'parent.parent')); + die(js::locate($this->createLink('doc', 'objectLibs', "type=$type&objectID=$objectID&libID=$libID"), 'parent.parent')); } else { @@ -226,9 +222,9 @@ class doc extends control } } - $projectID = $this->session->project; - $products = $this->product->getProductPairsByProject($projectID, 'noclosed'); - $executions = $this->execution->getByProject($projectID, 'all', 0, true); + $products = $this->product->getPairs(); + $projects = $this->project->getPairsByProgram(); + $executions = $this->execution->getPairs(); $libTypeList = $this->lang->doc->libTypeList; if(empty($products)) unset($libTypeList['product']); @@ -237,6 +233,7 @@ class doc extends control $this->view->groups = $this->loadModel('group')->getPairs(); $this->view->users = $this->user->getPairs('nocode'); $this->view->products = $products; + $this->view->projects = $projects; $this->view->executions = $executions; $this->view->type = $type; $this->view->libTypeList = $libTypeList; @@ -310,7 +307,7 @@ class doc extends control * @access public * @return void */ - public function create($libID, $moduleID = 0, $docType = '') + public function create($type, $objectID, $libID, $moduleID = 0, $docType = '') { if(!empty($_POST)) { @@ -336,6 +333,22 @@ class doc extends control $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link)); } + if($this->app->openApp == 'product') + { + $this->product->setMenu($objectID); + unset($this->lang->product->menu->doc['subMenu']); + } + else if($this->app->openApp == 'project') + { + $this->project->setMenu($objectID); + unset($this->lang->project->menu->doc['subMenu']); + } + else if($this->app->openApp == 'execution') + { + $this->execution->setMenu($objectID); + unset($this->lang->exectuion->menu->doc['subMenu']); + } + $lib = $this->doc->getLibByID($libID); $type = $lib->type; @@ -859,12 +872,34 @@ class doc extends control * @access public * @return void */ - public function objectLibs($type, $objectID = 0) + public function objectLibs($type, $objectID = 0, $libID = 0) { // setcookie('from', $from, $this->config->cookieLife, $this->config->webRoot, '', false, true); $this->session->set('docList', $this->app->getURI(true), 'doc'); - $table = $type == 'product' ? TABLE_PRODUCT : TABLE_PROJECT; + $objects = $this->doc->getOrderedObjects($type); + if($type == 'product') + { + $objectID = $this->product->saveState($objectID, $objects); + $table = TABLE_PRODUCT; + + $libs = $this->doc->getLibsByObject('product', $objectID); + $this->lang->modulePageNav = $this->doc->select($type, $objects, $objectID, $libs, $libID == 0 ? key($libs) : $libID); + + $this->app->rawMethod = 'product'; + } + else + { + $objectID = $this->project->saveState($objectID, $objects); + $table = TABLE_PROJECT; + + $libs = $this->doc->getLibsByObject('project', $objectID); + $this->lang->modulePageNav = $this->doc->select($type, $objects, $objectID, $libs, $libID == 0 ? key($libs) : $libID); + + $this->app->rawMethod = 'project'; + } + + if(!$libID) $libID = key($libs); $object = $this->dao->select('id,name,status')->from($table)->where('id')->eq($objectID)->fetch(); if(empty($object)) $this->locate($this->createLink($type, 'create', '', '', '', $this->session->project)); @@ -877,6 +912,9 @@ class doc extends control $actionURL = $this->createLink('doc', 'browse', "lib=0&browseType=bySearch&queryID=myQueryID"); $this->doc->buildSearchForm(0, array(), 0, $actionURL, 'objectLibs'); + $this->lang->TRActions = common::hasPriv('doc', 'createLib') ? html::a(helper::createLink('doc', 'createLib', "type=$type&objectID=$objectID"), " " . $this->lang->doc->createlib, '', "class='btn btn-secondary iframe' data-width='70%'") : ''; + $this->lang->TRActions .= common::hasPriv('doc', 'create') ? $this->doc->buildCreateButton4Doc($type, $objectID, $libID) : ''; + $this->view->customObjectLibs = $customObjectLibs; $this->view->showLibs = $this->config->doc->custom->objectLibs; @@ -886,6 +924,7 @@ class doc extends control $this->view->type = $type; $this->view->object = $object; $this->view->libs = $this->doc->getLibsByObject($type, $objectID); + $this->view->moduleTree = $this->doc->getTreeMenu($type, $objectID, 0); $this->view->canBeChanged = common::canModify($type, $object); // Determines whether an object is editable. $this->display(); } diff --git a/module/doc/css/objectlibs.css b/module/doc/css/objectlibs.css index 42e741fb34..5c92ebe539 100644 --- a/module/doc/css/objectlibs.css +++ b/module/doc/css/objectlibs.css @@ -3,5 +3,7 @@ .addbtn .icon-plus {font-size: 18px; display: block; opacity: 0.5; transition: opacity .2s; text-shadow: 1px 1px 3px rgba(0,0,0,.2);} .addbtn:hover .icon-plus {opacity: .9; animation: flash-icon 1s linear alternate infinite;} #dropMenu {min-width: 250px; box-sizing: inhert;} +#dropMenu .table-col .list-group {padding-top: 10px;} .main-col .block-files .panel-heading {padding-right: 20px;} .main-col .block-files .panel-heading .panel-title {height: 35px; line-height: 30px;} +.side-col .action a {margin-bottom: 3px;} diff --git a/module/doc/js/createlib.js b/module/doc/js/createlib.js index 94a784e005..8e756e199d 100644 --- a/module/doc/js/createlib.js +++ b/module/doc/js/createlib.js @@ -14,18 +14,28 @@ function changeByLibType(libType) if(libType == 'product') { $('table tr.product').removeClass('hidden'); + $('table tr.project').addClass('hidden'); + $('table tr.execution').addClass('hidden'); + changeDoclibAcl(libType); + } + else if(libType == 'project') + { + $('table tr.product').addClass('hidden'); + $('table tr.project').removeClass('hidden'); $('table tr.execution').addClass('hidden'); changeDoclibAcl(libType); } else if(libType == 'execution') { $('table tr.product').addClass('hidden'); + $('table tr.project').addClass('hidden'); $('table tr.execution').removeClass('hidden'); changeDoclibAcl(libType); } else { $('table tr.product').addClass('hidden'); + $('table tr.project').addClass('hidden'); $('table tr.execution').addClass('hidden'); changeDoclibAcl(libType); } diff --git a/module/doc/lang/en.php b/module/doc/lang/en.php index cbd766b9ce..8351758fad 100644 --- a/module/doc/lang/en.php +++ b/module/doc/lang/en.php @@ -13,6 +13,7 @@ $lang->doc->common = 'Document'; $lang->doc->id = 'ID'; $lang->doc->product = $lang->productCommon; +$lang->doc->project = 'Project'; $lang->doc->execution = $lang->executionCommon; $lang->doc->lib = 'Library'; $lang->doc->module = 'Category'; @@ -48,6 +49,7 @@ $lang->doc->item = ' Items'; $lang->doc->num = 'Documents'; $lang->doc->searchResult = 'Search Result'; $lang->doc->mailto = 'Mailto'; +$lang->doc->noModule = 'No document in this lib, please create it'; $lang->doc->moduleDoc = 'By Module'; $lang->doc->searchDoc = 'Search'; @@ -82,6 +84,8 @@ $lang->doc->collect = 'Add Favorite'; $lang->doc->cancelCollection = 'Remove Favorite'; $lang->doc->deleteFile = 'Delete File'; +$lang->doc->collectAction = 'Add Favorite'; + $lang->doc->libName = 'Document Library'; $lang->doc->libType = 'Category'; $lang->doc->custom = 'Custom Document Library'; @@ -90,7 +94,7 @@ $lang->doc->createlib = 'Create Document Library'; $lang->doc->allLibs = 'Library List'; $lang->doc->objectLibs = "{$lang->productCommon}/{$lang->executionCommon} Libraries"; $lang->doc->showFiles = 'Attachments'; -$lang->doc->editlib = 'Edit Document Library'; +$lang->doc->editLib = 'Edit Document Library'; $lang->doc->deleteLib = 'Delete Document Library'; $lang->doc->fixedMenu = 'Fix to Menu'; $lang->doc->removeMenu = 'Remove from Menu'; @@ -101,6 +105,7 @@ $lang->doc->allProduct = 'All' . $lang->productCommon . 's'; $lang->doc->allExecutions = 'All' . $lang->executionCommon . 's'; $lang->doc->libTypeList['product'] = $lang->productCommon . ' Library'; +$lang->doc->libTypeList['project'] = 'Project Library'; $lang->doc->libTypeList['execution'] = $lang->executionCommon . ' Library'; $lang->doc->libTypeList['custom'] = 'Custom Library'; @@ -212,6 +217,7 @@ $lang->doclib->create['execution'] = 'Create ' . $lang->executionCommon . ' Libr $lang->doclib->create['custom'] = 'Create Custom Library'; $lang->doclib->main['product'] = 'Primary Library'; +$lang->doclib->main['project'] = 'Primary Library'; $lang->doclib->main['execution'] = 'Primary Library'; $lang->doclib->tabList['product'] = $lang->productCommon; diff --git a/module/doc/lang/zh-cn.php b/module/doc/lang/zh-cn.php index bfc7ace429..4214833e83 100644 --- a/module/doc/lang/zh-cn.php +++ b/module/doc/lang/zh-cn.php @@ -13,6 +13,7 @@ $lang->doc->common = '文档'; $lang->doc->id = '文档编号'; $lang->doc->product = '所属' . $lang->productCommon; +$lang->doc->project = '所属项目'; $lang->doc->execution = '所属' . $lang->executionCommon; $lang->doc->lib = '所属文档库'; $lang->doc->module = '所属分类'; @@ -48,6 +49,7 @@ $lang->doc->item = '项'; $lang->doc->num = '文档数量'; $lang->doc->searchResult = '搜索结果'; $lang->doc->mailto = '抄送给'; +$lang->doc->noModule = '文档库下没有目录和文档,请维护目录或者创建文档'; $lang->doc->moduleDoc = '按模块浏览'; $lang->doc->searchDoc = '搜索'; @@ -73,10 +75,10 @@ $lang->doc->view = '文档详情'; $lang->doc->diff = '对比'; $lang->doc->diffAction = '对比文档'; $lang->doc->sort = '文档排序'; -$lang->doc->manageType = '维护分类'; -$lang->doc->editType = '编辑分类'; -$lang->doc->deleteType = '删除分类'; -$lang->doc->addType = '增加分类'; +$lang->doc->manageType = '维护目录'; +$lang->doc->editType = '编辑目录'; +$lang->doc->deleteType = '删除目录'; +$lang->doc->addType = '增加目录'; $lang->doc->childType = '子分类'; $lang->doc->collect = '收藏'; $lang->doc->cancelCollection = '取消收藏'; @@ -92,7 +94,7 @@ $lang->doc->createlib = '创建文档库'; $lang->doc->allLibs = '文档库列表'; $lang->doc->objectLibs = "{$lang->productCommon}/{$lang->executionCommon}库列表"; $lang->doc->showFiles = '附件库'; -$lang->doc->editlib = '编辑文档库'; +$lang->doc->editLib = '编辑文档库'; $lang->doc->deleteLib = '删除文档库'; $lang->doc->fixedMenu = '固定到菜单栏'; $lang->doc->removeMenu = '从菜单栏移除'; @@ -103,6 +105,7 @@ $lang->doc->allProduct = '所有' . $lang->productCommon; $lang->doc->allExecutions = '所有' . $lang->executionCommon; $lang->doc->libTypeList['product'] = $lang->productCommon . '文档库'; +$lang->doc->libTypeList['project'] = '项目文档库'; $lang->doc->libTypeList['execution'] = $lang->executionCommon . '文档库'; $lang->doc->libTypeList['custom'] = '自定义文档库'; diff --git a/module/doc/model.php b/module/doc/model.php index fd5b8a535b..9be74f367b 100644 --- a/module/doc/model.php +++ b/module/doc/model.php @@ -1117,6 +1117,7 @@ class docModel extends model public function getLibsByObject($type, $objectID, $mode = '') { if($type != 'product' and $type != 'project' and $type != 'execution') return false; + $objectLibs = $this->dao->select('*')->from(TABLE_DOCLIB)->where('deleted')->eq(0)->andWhere($type)->eq($objectID)->orderBy('`order`, id')->fetchAll('id'); if($type == 'product') @@ -1139,16 +1140,88 @@ class docModel extends model $itemCounts = $this->statLibCounts(array_keys($libs)); foreach($libs as $libID => $lib) $libs[$libID]->allCount = $itemCounts[$libID]; + /* if(common::hasPriv('doc', 'showFiles')) { $libs['files'] = new stdclass(); $libs['files']->name = $this->lang->doclib->files; $libs['files']->allCount = count($this->getLibFiles($type, $objectID, 'id_desc')); } + */ return $libs; } + /** + * Get ordered objects for dic. + * + * @param string $objectType + * @access public + * @return array + */ + public function getOrderedObjects($objectType = 'product') + { + $myObjects = $normalObjects = $closedObjects = array(); + if($objectType == 'product') + { + $products = $this->loadModel('product')->getList(); + foreach($products as $id => $product) + { + if($product->status == 'normal' and $product->PO == $this->app->user->account) + { + $myObjects[$id] = $product->name; + } + elseif($product->status == 'normal' and !($product->PO == $this->app->user->account)) + { + $normalObjects[$id] = $product->name; + } + elseif($product->status == 'closed') + { + $closedObjects[$id] = $product->name; + } + } + } + elseif($objectType == 'project') + { + $programs = $this->dao->select('id, name')->from(TABLE_PROGRAM)->where('type')->eq('program')->andWhere('deleted')->eq(0)->orderBy('order_asc')->fetchPairs(); + $projects = $this->dao->select('*')->from(TABLE_PROJECT) + ->where('deleted')->eq(0) + ->andWhere('type')->eq('project') + ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi() + ->orderBy('order_asc') + ->fetchAll('id'); + + $orderedProjects = array(); + foreach($programs as $programID => $programName) + { + foreach($projects as $id => $project) + { + if($project->parent and $project->parent != $programID) continue; + $orderedProjects[$id] = $project; + unset($projects[$project->id]); + } + } + + foreach($orderedProjects as $id => $project) + { + if($project->status != 'done' and $project->status != 'closed' and $project->PM == $this->app->user->account) + { + $myObjects[$id] = $project->name; + } + else if($project->status != 'done' and $project->status != 'closed' and !($project->PM == $this->app->user->account)) + { + $normalObjects[$id] = $project->name; + } + else if($project->status == 'done' or $project->status == 'closed') + { + $closedObjects[$id] = $project->name; + } + } + } + + return $myObjects + $normalObjects + $closedObjects; + } + /** * Stat module and document counts of lib. * @@ -1203,6 +1276,7 @@ class docModel extends model public function getLibFiles($type, $objectID, $orderBy, $pager = null) { if($type != 'execution' and $type != 'project' and $type != 'product') return true; + $this->loadModel('file'); $docs = $this->dao->select('*')->from(TABLE_DOC)->where($type)->eq($objectID)->fetchAll('id'); foreach($docs as $id => $doc) @@ -1289,7 +1363,8 @@ class docModel extends model $executionIdList = join(',', $executionIdList); $files = $this->dao->select('*')->from(TABLE_FILE)->alias('t1') ->where('size')->gt('0') - ->andWhere("(objectType = 'execution' and objectID in ($executionIdList))", true) + ->andWhere("(objectType = 'project' and objectID = $objectID)", true) + ->orWhere("(objectType = 'execution' and objectID in ($executionIdList))") ->orWhere("(objectType = 'doc' and objectID in ($docIdList))") ->orWhere("(objectType = 'task' and objectID in ($taskIdList))") ->orWhere("(objectType = 'build' and objectID in ($buildIdList))") @@ -1591,28 +1666,26 @@ class docModel extends model /** * Build document module index page create document button. * + * @param string $type + * @param int $objectID + * @param int $libID * @access public * @return string */ - public function buildCreateButton4Doc() + public function buildCreateButton4Doc($type, $objectID, $libID) { - $libs = $this->getLibs('all', strpos($this->config->doc->custom->showLibs, 'unclosed') !== false ? 'unclosedProject' : ''); - $html = ""; - if($libs) + $html = ""; + return $html; } @@ -1767,4 +1840,123 @@ EOF; } return $navCSS; } + + /** + * Create the select code of doc. + * + * @param string $type + * @param array $objects + * @param int $objectID + * @param array $libs + * @param int $libID + * @access public + * @return string + */ + public function select($type, $objects, $objectID, $libs, $libID = 0) + { + if(empty($objects)) return ''; + + $output = ''; + $dropMenuLink = helper::createLink('repo', 'ajaxGetDropMenu'); + + $output = "
    "; + + $dropMenuLink = helper::createLink('repo', 'ajaxGetBranchDropMenu'); + $output .= "
    "; + + return $output; + } + + /** + * Get doc tree menu. + * + * @param string $rootID + * @param int $rootID + * @param int $startModule + * @access public + * @return string + */ + public function getTreeMenu($type, $rootID, $startModule = 0) + { + return ''; + + $extra['projectID'] = $rootID; + $menu = "
      "; + $startModulePath = ''; + if($startModule > 0) + { + $startModule = $this->getById($startModule); + if($startModule) $startModulePath = $startModule->path . '%'; + } + + $executionModules = $this->getTaskTreeModules($rootID, true); + $executionBranches = $this->dao->select('DISTINCT t2.branch')->from(TABLE_PROJECTSTORY)->alias('t1') + ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id') + ->where('t1.project')->eq($rootID) + ->andWhere('t2.deleted')->eq(0) + ->fetchPairs(); + + /* Get module according to product. */ + $products = $this->loadModel('product')->getProductPairsByProject($rootID); + $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products)); + $productNum = count($products); + foreach($products as $id => $product) + { + $projectProductLink = helper::createLink('projectstory', 'story', "projectID=$rootID&productID=$id"); + $executionProductLink = helper::createLink('execution', 'story', "executionID=$rootID&ordery=&status=byProduct&praram=$id"); + $link = $this->app->rawModule == 'projectstory' ? $projectProductLink : $executionProductLink; + if($productNum > 1) $menu .= "
    • " . html::a($link, $product, '_self', "id='product$id'"); + + /* tree menu. */ + $tree = ''; + if(empty($branchGroups[$id])) $branchGroups[$id]['0'] = ''; + foreach($branchGroups[$id] as $branch => $branchName) + { + $treeMenu = array(); + $query = $this->dao->select('*')->from(TABLE_MODULE) + ->where('root')->eq((int)$id) + ->andWhere('type')->eq('story') + ->beginIF(count($branchGroups[$id]) > 1)->andWhere('branch')->eq($branch)->fi() + ->beginIF($startModulePath)->andWhere('path')->like($startModulePath)->fi() + ->andWhere('deleted')->eq(0) + ->orderBy('grade desc, branch, `order`, type') + ->get(); + $stmt = $this->dbh->query($query); + while($module = $stmt->fetch()) + { + /* If not manage, ignore unused modules. */ + if(isset($executionModules[$module->id]) and $this->app->rawModule == 'execution') $this->buildTree($treeMenu, $module, 'task', $userFunc, $extra); + if($this->app->rawModule == 'projectstory') $this->buildTree($treeMenu, $module, 'task', $userFunc, $extra); + } + if((isset($treeMenu[0]) and $branch) or isset($executionBranches[$branch])) + { + $childMenu = isset($treeMenu[0]) ? "
        {$treeMenu[0]}
      " : ''; + $projectBranchLink = helper::createLink('projectstory', 'story', "projectID=$rootID&productID=$id&branch=" . (empty($branch) ? 0 : $branch) . "&browseType=byBranch"); + $executionBranchLink = helper::createLink('execution', 'story', "executionID=$rootID&ordery=&status=byBranch&praram=" . (empty($branch) ? "{$id},0" : $branch)); + $link = $this->app->rawModule == 'projectstory' ? $projectBranchLink : $executionBranchLink; + if($branchName) $treeMenu[0] = "
    • " . html::a($link, $branchName, '_self', "id='branch" . (empty($branch) ? "{$id}_0" : $branch) . "'") . "{$childMenu}
    • "; + } + $tree .= isset($treeMenu[0]) ? $treeMenu[0] : ''; + } + if($productNum > 1) $tree = "
        " . $tree . "
      \n"; + $menu .= $tree; + } + + $menu .= '
    '; + return $menu; + } } diff --git a/module/doc/view/browse.html.php b/module/doc/view/browse.html.php index f2ef2957e8..96457575b2 100644 --- a/module/doc/view/browse.html.php +++ b/module/doc/view/browse.html.php @@ -20,7 +20,7 @@ app->user->feedback) && !$this->cookie->feedbackView && $this->from == 'doc') ? true : false;?>
    -
    +
    diff --git a/module/doc/view/createlib.html.php b/module/doc/view/createlib.html.php index b62f0bd177..5ce73f96ae 100644 --- a/module/doc/view/createlib.html.php +++ b/module/doc/view/createlib.html.php @@ -29,6 +29,10 @@ doc->product?> + + doc->project?> + + doc->execution?> diff --git a/module/doc/view/index.html.php b/module/doc/view/index.html.php index 8b1a313b1e..336898342a 100644 --- a/module/doc/view/index.html.php +++ b/module/doc/view/index.html.php @@ -13,7 +13,7 @@ doc->appendNavCSS();?>
    -
    +
    diff --git a/module/doc/view/objectlibs.html.php b/module/doc/view/objectlibs.html.php index 9788f087ea..609c2c1117 100644 --- a/module/doc/view/objectlibs.html.php +++ b/module/doc/view/objectlibs.html.php @@ -10,7 +10,6 @@ * @link http://www.zentao.net */ ?> -doc->appendNavCSS();?> openApp == 'execution'):;?> diff --git a/module/doc/view/side.html.php b/module/doc/view/side.html.php index 4d9f54a3c5..816751b59d 100644 --- a/module/doc/view/side.html.php +++ b/module/doc/view/side.html.php @@ -15,193 +15,22 @@ if(empty($type)) $type = 'product'; $sideWidth = common::checkNotCN() ? '270' : '238'; ?>
    -
    -
    - -
    - doclib->tabList as $tabValue => $tabName):?> - -
    "> -
      - - -
    • - navGroup->doc == 'doc' and $tabValue == 'execution') - { - echo $lang->noData; - } - else - { - $text = zget($lang->doclib->create, $tabValue, ''); - if($text and common::hasPriv($tabValue, 'create')) echo html::a($this->createLink($tabValue, 'create', ''), $text, '', "class='text-ellipsis'"); - } - ?> -
    • - - $tabMenuName):?> - $subLibName) - { - if(is_numeric($subLibID) and !empty($mainLibID)) $customLibCount += 1; - if(is_numeric($subLibID) and empty($mainLibID)) $mainLibID = $subLibID; - } - } - - $icon = $tabValue == 'product' ? " " : " "; - $activeClass = ($this->methodName == 'objectlibs' && $type == $tabValue && $object->id == $tabMenuID) ? 'active' : ''; - $activeClass = ($this->methodName == 'browse' && isset($currentLib->id) && $currentLib->id == $mainLibID) ? 'active' : $activeClass; - ?> -
    • > - 0):?> - createLink('doc', 'objectLibs', "type=$tabValue&objectID=$tabMenuID"), $icon . $tabMenuName, '', "class='text-ellipsis' title='{$tabMenuName}'");?> - - createLink('doc', 'browse', "libID=$mainLibID"), $icon . $tabMenuName, '', "class='text-ellipsis' title='{$tabMenuName}'");?> - - -
        - $subLibName):?> - methodName == 'alllibs' && $type == 'execution' && $$tabValue == $tabMenuID) ? "class='active'" : ''; - $icon = 'icon-stack'; - } - elseif($subLibID == 'files') - { - $subLibLink = inlink('showFiles', "type=$tabValue&objectID=$tabMenuID"); - $activeClass = ($this->methodName == 'showfiles' && $type == $tabValue && $object->id == $tabMenuID) ? "class='active'" : ''; - $icon = 'icon-paper-clip'; - } - else - { - $subLibLink = inlink('browse', "libID=$subLibID"); - $activeClass = ($this->methodName == 'browse' && $browseType != 'bymodule' && $subLibID == $libID) ? "class='active'" : ''; - $icon = 'icon-folder-outline'; - } - ?> - 0):?> -
      • > - " . $subLibName, '', "class='text-ellipsis' title='{$subLibName}'");?> - - - 0):?> -
          - - - parent != 0) continue;?> -
        • methodName == 'browse' && $browseType == 'bymodule' && $moduleID == $module->id) echo "class='active'";?>> - createLink('doc', 'browse', "libID=$subLibID&browseType=byModule¶m={$module->id}"), " " . $module->name, '', "class='text-ellipsis' title='{$module->name}'");?> - doc->printChildModule($module, $subLibID, $this->methodName, $browseType, $moduleID);?> -
        • - - 0):?> -
        - - - 0):?> -
      • - - -
      • > - " . $subLibName, '', "class='text-ellipsis' title='{$subLibName}'");?> -
      • - - -
      - -
    • - - - - - -
    • - doclib->create, $tabValue, ''); - if($text and common::hasPriv('doc', 'createLib')) echo html::a($this->createLink('doc', 'createLib', "type={$tabValue}"), $text, '', "class='iframe' data-width='70%'"); - ?> -
    • - - $sideLibName):?> - - - - methodName == 'objectlibs' && $type == $tabValue && $object->id == $sideLibID) ? 'active' : ''; - $activeClass = ($this->methodName == 'browse' && isset($currentLib->id) && $currentLib->id == $sideLibID) ? 'active' : $activeClass; - ?> -
    • > - createLink('doc', 'browse', "libID=$sideLibID"), " " . $sideLibName, '', "class='text-ellipsis' title='{$sideLibName}'");?> - -
        - - parent != 0) continue;?> -
      • methodName == 'browse' && $browseType == 'bymodule' && $moduleID == $module->id) echo "class='active'";?>> - createLink('doc', 'browse', "libID=$sideLibID&browseType=byModule¶m={$module->id}"), " " . $module->name, '', "class='text-ellipsis' title='{$module->name}'");?> - doc->printChildModule($module, $sideLibID, $this->methodName, $browseType, $moduleID);?> -
      • - -
      - -
    • - - - - - -
    -
    - +
    + +
    +
    + doc->noModule;?> +
    +
    + + +
    + doc->manageType, '', "class='btn btn-info btn-wide'");?> + doc->editLib, '', "class='btn btn-info btn-wide'");?> + doc->deleteLib, '', "class='btn btn-info btn-wide'");?> +
    - -
    -