diff --git a/db/zentao.sql b/db/zentao.sql index 0501dc8774..d9ba2e42a1 100644 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -837,7 +837,7 @@ CREATE TABLE IF NOT EXISTS `zt_projectproduct` ( `product` mediumint(8) unsigned NOT NULL, `branch` mediumint(8) unsigned NOT NULL, `plan` mediumint(8) unsigned NOT NULL, - PRIMARY KEY (`project`,`product`) + PRIMARY KEY (`project`,`product`, `branch`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- DROP TABLE IF EXISTS `zt_projectspec`; CREATE TABLE IF NOT EXISTS `zt_projectspec` ( diff --git a/module/branch/lang/de.php b/module/branch/lang/de.php index e4e5855de3..d3bbb31a21 100644 --- a/module/branch/lang/de.php +++ b/module/branch/lang/de.php @@ -7,6 +7,7 @@ $lang->branch->add = 'Hinzufügen'; $lang->branch->manageTitle = '%s Management'; $lang->branch->all = 'Alle '; +$lang->branch->main = 'Trunk'; $lang->branch->id = 'ID'; $lang->branch->product = 'Produkt'; diff --git a/module/branch/lang/en.php b/module/branch/lang/en.php index 6768fd3460..39c0e684ad 100644 --- a/module/branch/lang/en.php +++ b/module/branch/lang/en.php @@ -7,6 +7,7 @@ $lang->branch->add = 'Add'; $lang->branch->manageTitle = '%s Management'; $lang->branch->all = 'All '; +$lang->branch->main = 'Trunk'; $lang->branch->id = 'ID'; $lang->branch->product = 'Product'; diff --git a/module/branch/lang/fr.php b/module/branch/lang/fr.php index b06a97b3df..77af33535d 100644 --- a/module/branch/lang/fr.php +++ b/module/branch/lang/fr.php @@ -7,6 +7,7 @@ $lang->branch->add = 'Ajout'; $lang->branch->manageTitle = 'Gestion %s'; $lang->branch->all = 'Tous'; +$lang->branch->main = 'Trunk'; $lang->branch->id = 'ID'; $lang->branch->product = 'Product'; diff --git a/module/branch/lang/vi.php b/module/branch/lang/vi.php index 8b1ef13d6a..249089736c 100644 --- a/module/branch/lang/vi.php +++ b/module/branch/lang/vi.php @@ -7,6 +7,7 @@ $lang->branch->add = 'Thêm'; $lang->branch->manageTitle = 'Quản lý %s'; $lang->branch->all = 'Tất cả '; +$lang->branch->main = 'Trunk'; $lang->branch->id = 'ID'; $lang->branch->product = 'Sản phẩm'; diff --git a/module/branch/lang/zh-cn.php b/module/branch/lang/zh-cn.php index f60eceeeb3..690f79c532 100644 --- a/module/branch/lang/zh-cn.php +++ b/module/branch/lang/zh-cn.php @@ -7,6 +7,7 @@ $lang->branch->add = '添加'; $lang->branch->manageTitle = '%s管理'; $lang->branch->all = '所有'; +$lang->branch->main = '主干'; $lang->branch->id = '编号'; $lang->branch->product = '所属产品'; diff --git a/module/branch/model.php b/module/branch/model.php index 9e91f48fb7..ab0e5691f7 100644 --- a/module/branch/model.php +++ b/module/branch/model.php @@ -25,7 +25,7 @@ class branchModel extends model if(empty($productID)) $productID = $this->session->product; $product = $this->loadModel('product')->getById($productID); if(empty($product) or !isset($this->lang->product->branchName[$product->type])) return false; - return $this->lang->branch->all . $this->lang->product->branchName[$product->type]; + return $this->lang->branch->main; } return htmlspecialchars_decode($this->dao->select('*')->from(TABLE_BRANCH)->where('id')->eq($branchID)->fetch('name')); } @@ -52,7 +52,7 @@ class branchModel extends model $product = $this->loadModel('product')->getById($productID); if(!$product or $product->type == 'normal') return array(); - $branches = array('0' => $this->lang->branch->all . $this->lang->product->branchName[$product->type]) + $branches; + $branches = array('0' => $this->lang->branch->main) + $branches; } return $branches; } @@ -81,10 +81,7 @@ class branchModel extends model } } - if(strpos($params, 'noempty') === false) - { - $branchPairs = array('0' => $this->lang->branch->all . $this->lang->product->branchName['branch']) + $branchPairs; - } + if(strpos($params, 'noempty') === false) $branchPairs = array('0' => $this->lang->branch->main) + $branchPairs; return $branchPairs; } @@ -154,7 +151,7 @@ class branchModel extends model if($product->type == 'normal') continue; if(!isset($branchGroups[$product->id])) $branchGroups[$product->id] = array(); - if(strpos($params, 'noempty') === false) $branchGroups[$product->id] = array('0' => $this->lang->branch->all . $this->lang->product->branchName[$product->type]) + $branchGroups[$product->id]; + if(strpos($params, 'noempty') === false) $branchGroups[$product->id] = array('0' => $this->lang->branch->main) + $branchGroups[$product->id]; } return $branchGroups; diff --git a/module/bug/control.php b/module/bug/control.php index ffd5a18765..63a3446018 100644 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -1776,7 +1776,7 @@ class bug extends control $relatedTasks = $this->dao->select('id, name')->from(TABLE_TASK)->where('id')->in($relatedTaskIdList)->fetchPairs(); $relatedBugs = $this->dao->select('id, title')->from(TABLE_BUG)->where('id')->in($relatedBugIdList)->fetchPairs(); $relatedCases = $this->dao->select('id, title')->from(TABLE_CASE)->where('id')->in($relatedCaseIdList)->fetchPairs(); - $relatedBranch = array('0' => $this->lang->branch->all) + $this->dao->select('id, name')->from(TABLE_BRANCH)->where('id')->in($relatedBranchIdList)->fetchPairs(); + $relatedBranch = array('0' => $this->lang->branch->main) + $this->dao->select('id, name')->from(TABLE_BRANCH)->where('id')->in($relatedBranchIdList)->fetchPairs(); $relatedBuilds = array('trunk' => $this->lang->trunk) + $this->dao->select('id, name')->from(TABLE_BUILD)->where('id')->in($relatedBuildIdList)->fetchPairs(); $relatedFiles = $this->dao->select('id, objectID, pathname, title')->from(TABLE_FILE)->where('objectType')->eq('bug')->andWhere('objectID')->in(@array_keys($bugs))->andWhere('extra')->ne('editor')->fetchGroup('objectID'); $relatedModules = $this->loadModel('tree')->getAllModulePairs('bug'); diff --git a/module/bug/model.php b/module/bug/model.php index cfe3c1200e..17318e9d63 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -1320,7 +1320,7 @@ class bugModel extends model else { $this->config->bug->search['fields']['branch'] = $this->lang->product->branch; - $this->config->bug->search['params']['branch']['values'] = array('' => '') + $this->loadModel('branch')->getPairs($productID, 'noempty') + array('all' => $this->lang->branch->all); + $this->config->bug->search['params']['branch']['values'] = array('' => '', 0 => $this->lang->branch->main) + $this->loadModel('branch')->getPairs($productID, 'noempty') + array('all' => $this->lang->branch->all); } $this->loadModel('search')->setSearchParams($this->config->bug->search); diff --git a/module/common/lang/en.php b/module/common/lang/en.php index 089c0c4521..b26a53028d 100644 --- a/module/common/lang/en.php +++ b/module/common/lang/en.php @@ -402,18 +402,19 @@ $lang->pasteImgFail = "Failed to paste images. Try again later."; $lang->pasteImgUploading = "Uploading..."; /* Time formats settings. */ -if(!defined('DT_DATETIME1')) define('DT_DATETIME1', 'Y-m-d H:i:s'); -if(!defined('DT_DATETIME2')) define('DT_DATETIME2', 'y-m-d H:i'); -if(!defined('DT_MONTHTIME1'))define('DT_MONTHTIME1', 'n/d H:i'); -if(!defined('DT_MONTHTIME2'))define('DT_MONTHTIME2', 'n/d H:i'); -if(!defined('DT_DATE1')) define('DT_DATE1', 'Y-m-d'); -if(!defined('DT_DATE2')) define('DT_DATE2', 'Ymd'); -if(!defined('DT_DATE3')) define('DT_DATE3', 'Y/m/d'); -if(!defined('DT_DATE4')) define('DT_DATE4', 'M d'); -if(!defined('DT_DATE5')) define('DT_DATE5', 'j/n'); -if(!defined('DT_TIME1')) define('DT_TIME1', 'H:i:s'); -if(!defined('DT_TIME2')) define('DT_TIME2', 'H:i'); -if(!defined('LONG_TIME')) define('LONG_TIME', '2059-12-31'); +if(!defined('DT_DATETIME1')) define('DT_DATETIME1', 'Y-m-d H:i:s'); +if(!defined('DT_DATETIME2')) define('DT_DATETIME2', 'y-m-d H:i'); +if(!defined('DT_MONTHTIME1')) define('DT_MONTHTIME1', 'n/d H:i'); +if(!defined('DT_MONTHTIME2')) define('DT_MONTHTIME2', 'n/d H:i'); +if(!defined('DT_DATE1')) define('DT_DATE1', 'Y-m-d'); +if(!defined('DT_DATE2')) define('DT_DATE2', 'Ymd'); +if(!defined('DT_DATE3')) define('DT_DATE3', 'Y/m/d'); +if(!defined('DT_DATE4')) define('DT_DATE4', 'M d'); +if(!defined('DT_DATE5')) define('DT_DATE5', 'j/n'); +if(!defined('DT_TIME1')) define('DT_TIME1', 'H:i:s'); +if(!defined('DT_TIME2')) define('DT_TIME2', 'H:i'); +if(!defined('LONG_TIME')) define('LONG_TIME', '2059-12-31'); +if(!defined('MAIN')) define('MAIN', '0'); /* Datepicker. */ $lang->datepicker = new stdclass(); diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index 59b4a7c9c2..30aed1d98d 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -402,18 +402,19 @@ $lang->pasteImgFail = "贴图失败,请稍后重试。"; $lang->pasteImgUploading = "正在上传图片,请稍后..."; /* 时间格式设置。*/ -if(!defined('DT_DATETIME1')) define('DT_DATETIME1', 'Y-m-d H:i:s'); -if(!defined('DT_DATETIME2')) define('DT_DATETIME2', 'y-m-d H:i'); +if(!defined('DT_DATETIME1')) define('DT_DATETIME1', 'Y-m-d H:i:s'); +if(!defined('DT_DATETIME2')) define('DT_DATETIME2', 'y-m-d H:i'); if(!defined('DT_MONTHTIME1')) define('DT_MONTHTIME1', 'n/d H:i'); if(!defined('DT_MONTHTIME2')) define('DT_MONTHTIME2', 'n月d日 H:i'); -if(!defined('DT_DATE1')) define('DT_DATE1', 'Y-m-d'); -if(!defined('DT_DATE2')) define('DT_DATE2', 'Ymd'); -if(!defined('DT_DATE3')) define('DT_DATE3', 'Y年m月d日'); -if(!defined('DT_DATE4')) define('DT_DATE4', 'n月j日'); -if(!defined('DT_DATE5')) define('DT_DATE5', 'j/n'); -if(!defined('DT_TIME1')) define('DT_TIME1', 'H:i:s'); -if(!defined('DT_TIME2')) define('DT_TIME2', 'H:i'); -if(!defined('LONG_TIME')) define('LONG_TIME', '2059-12-31'); +if(!defined('DT_DATE1')) define('DT_DATE1', 'Y-m-d'); +if(!defined('DT_DATE2')) define('DT_DATE2', 'Ymd'); +if(!defined('DT_DATE3')) define('DT_DATE3', 'Y年m月d日'); +if(!defined('DT_DATE4')) define('DT_DATE4', 'n月j日'); +if(!defined('DT_DATE5')) define('DT_DATE5', 'j/n'); +if(!defined('DT_TIME1')) define('DT_TIME1', 'H:i:s'); +if(!defined('DT_TIME2')) define('DT_TIME2', 'H:i'); +if(!defined('LONG_TIME')) define('LONG_TIME', '2059-12-31'); +if(!defined('MAIN')) define('MAIN', '0'); /* datepicker 时间*/ $lang->datepicker = new stdclass(); diff --git a/module/product/model.php b/module/product/model.php index c711eac417..96739eacc5 100644 --- a/module/product/model.php +++ b/module/product/model.php @@ -858,7 +858,7 @@ class productModel extends model else { $this->config->product->search['fields']['branch'] = $this->lang->product->branch; - $this->config->product->search['params']['branch']['values'] = array('' => '') + $this->loadModel('branch')->getPairs($productID, 'noempty') + array('all' => $this->lang->branch->all); + $this->config->product->search['params']['branch']['values'] = array('' => '', '0' => $this->lang->branch->main) + $this->loadModel('branch')->getPairs($productID, 'noempty') + array('all' => $this->lang->branch->all); } $this->loadModel('search')->setSearchParams($this->config->product->search); diff --git a/module/productplan/model.php b/module/productplan/model.php index 76cabb9a9f..ff97b370d4 100644 --- a/module/productplan/model.php +++ b/module/productplan/model.php @@ -275,6 +275,24 @@ class productplanModel extends model return array('' => '') + $planPairs; } + /** + * Get plan list by story id list. + * + * @param string|array $storyIdList + * @access public + * @return array + */ + public function getListByStories($storyIdList) + { + if(empty($storyIdList)) return array(); + return $this->dao->select('t3.*, t1.id as storyID')->from(TABLE_STORY)->alias('t1') + ->leftJoin(TABLE_PLANSTORY)->alias('t2')->on('t1.id=t2.story') + ->leftJoin(TABLE_PRODUCTPLAN)->alias('t3')->on('t2.plan=t3.id') + ->where('t1.id')->in($storyIdList) + ->andWhere('t3.deleted')->eq(0) + ->fetchGroup('storyID', 'id'); + } + /** * Get plans for products * diff --git a/module/story/control.php b/module/story/control.php index 2723bd9b80..c86ebcea53 100644 --- a/module/story/control.php +++ b/module/story/control.php @@ -1381,7 +1381,55 @@ class story extends control */ public function batchChangeBranch($branchID) { - $storyIdList = !empty($_POST['storyIdList']) ? $this->post->storyIdList : die(js::locate($this->session->storyList, 'parent')); + /* Get the storyIdList value from the post. */ + if(!empty($_POST['storyIdList'])) $storyIdList = $_POST['storyIdList']; + $storyIdList = $storyIdList ? $storyIdList : die(js::locate($this->session->storyList, 'parent')); + $plans = $this->loadModel('productplan')->getListByStories($storyIdList); + if(empty($confirm)) + { + $stories = $this->story->getByList($storyIdList); + $conflictStotyIdList = ''; + $normalStotyIdList = ''; + /* Determine whether there are conflicting stories. */ + foreach($storyIdList as $storyID) + { + /* If the story of a non-trunk branch needs to be changed to a non-trunk branch, judge whether there is a conflict in the plan. */ + if($branchID and $stories[$storyID]->branch and $stories[$storyID]->branch != $branchID and isset($plans[$storyID])) + { + if(!empty($plan->branch) and $plan->branch != $branchID) + { + $conflictStotyIdList .= '[' . $storyID . ']'; + break; + } + } + /* If a trunk story needs to be switched to a non-trunk branch to determine whether there is a conflicting plan. */ + elseif($branchID and empty($stories[$storyID]->branch) and $plans[$storyID]) + { + foreach($plans[$storyID] as $plan) + { + if(!empty($plan->branch) and $plan->branch != $branchID) + { + $conflictStotyIdList .= '[' . $storyID . ']'; + break; + } + } + } + else + { + $normalStotyIdList .= $storyID . ','; + } + } + + if($conflictStotyIdList) + { + $storyIdList = implode(',', $storyIdList); + $confirmURL = $this->createLink('story', 'batchChangeBranch', "branchID=$branchID&confirm=yes&storyIdList=$storyIdList"); + $cancelURL = $this->createLink('story', 'batchChangeBranch', "branchID=$branchID&confirm=no&storyIdList=$normalStotyIdList"); + die(js::confirm(sprintf($this->lang->story->confirmChangeBranch, $conflictStotyIdList), $confirmURL, $cancelURL)); + } + } + + if(!is_array($storyIdList)) $storyIdList = explode(',', $storyIdList); $storyIdList = array_unique($storyIdList); $allChanges = $this->story->batchChangeBranch($storyIdList, $branchID); if(dao::isError()) die(js::error(dao::getError())); @@ -2039,7 +2087,7 @@ class story extends control $relatedStories = $this->dao->select('id,title')->from(TABLE_STORY) ->where('id')->in($relatedStoryIdList)->fetchPairs(); $relatedFiles = $this->dao->select('id, objectID, pathname, title')->from(TABLE_FILE)->where('objectType')->eq('story')->andWhere('objectID')->in($storyIdList)->andWhere('extra')->ne('editor')->fetchGroup('objectID'); $relatedSpecs = $this->dao->select('*')->from(TABLE_STORYSPEC)->where('`story`')->in($storyIdList)->orderBy('version desc')->fetchGroup('story'); - $relatedBranch = array('0' => $this->lang->branch->all) + $this->dao->select('id, name')->from(TABLE_BRANCH)->where('id')->in($relatedBranchIdList)->fetchPairs(); + $relatedBranch = array('0' => $this->lang->branch->main) + $this->dao->select('id, name')->from(TABLE_BRANCH)->where('id')->in($relatedBranchIdList)->fetchPairs(); $relatedModules = $this->loadModel('tree')->getAllModulePairs(); foreach($stories as $story) diff --git a/module/story/model.php b/module/story/model.php index 25a49f5c07..5136d1dff6 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -1692,7 +1692,44 @@ class storyModel extends model $story->branch = $branchID; $this->dao->update(TABLE_STORY)->data($story)->autoCheck()->where('id')->eq((int)$storyID)->exec(); - if(!dao::isError()) $allChanges[$storyID] = common::createChanges($oldStory, $story); + if(!dao::isError()) + { + /* If a trunk story to be switched to a non-trunk branch to cancel the association with other non-trunk branch plans. */ + $removeUnlinkPlan = ''; + $continueLinkPlan = ''; + if(empty($oldStory->branch) and $branchID and isset($plans[$storyID])) + { + foreach($plans[$storyID] as $planID => $plan) + { + if(!empty($plan->branch) and $plan->branch != $branchID) + { + $removeUnlinkPlan = $planID . ','; + continue; + } + $continueLinkPlan = $planID . ','; + } + } + /* If a non-trunk story wants to switch to a non-trunk branch, cancel the association with other non-trunk branch plans. */ + elseif($oldStory->branch and $branchID and isset($plans[$storyID])) + { + foreach($plans[$storyID] as $planID => $plan) + { + if(!empty($plan->branch) and $plan->branch != $branchID) + { + $removeUnlinkPlan = $planID . ','; + continue; + } + $continueLinkPlan = $planID . ','; + } + } + if($removeUnlinkPlan) $this->dao->delete()->from(TABLE_PLANSTORY)->where('story')->eq($storyID)->andWhere('plan')->in($removeUnlinkPlan)->exec(); + if($continueLinkPlan) + { + $story->plan = $continueLinkPlan; + $this->dao->update(TABLE_STORY)->set('plan')->eq($continueLinkPlan)->where('id')->eq($storyID)->exec(); + } + $allChanges[$storyID] = common::createChanges($oldStory, $story); + } } return $allChanges; } @@ -2413,6 +2450,7 @@ class storyModel extends model $storyQuery = $storyQuery . ' AND `product` ' . helper::dbIN(array_keys($products)); if($excludeStories) $storyQuery = $storyQuery . ' AND `id` NOT ' . helper::dbIN($excludeStories); if($this->app->moduleName == 'productplan') $storyQuery .= " AND `status` NOT IN ('closed') AND `parent` >= 0 "; + $allBranch = "`branch` = 'all'"; if($executionID != '') { foreach($products as $product) $branches[$product->branch] = $product->branch; @@ -2430,9 +2468,11 @@ class storyModel extends model } elseif($branch) { - $allBranch = "`branch` = 'all'"; if($branch and strpos($storyQuery, '`branch` =') === false) $storyQuery .= " AND `branch` in('0','$branch')"; - if(strpos($storyQuery, $allBranch) !== false) $storyQuery = str_replace($allBranch, '1', $storyQuery); + } + elseif(strpos($storyQuery, $allBranch) !== false) + { + $storyQuery = str_replace($allBranch, '1', $storyQuery); } $storyQuery = preg_replace("/`plan` +LIKE +'%([0-9]+)%'/i", "CONCAT(',', `plan`, ',') LIKE '%,$1,%'", $storyQuery); diff --git a/module/testcase/model.php b/module/testcase/model.php index 6da824b03d..12a3fdd97c 100644 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -1439,7 +1439,7 @@ class testcaseModel extends model else { $this->config->testcase->search['fields']['branch'] = $this->lang->product->branch; - $this->config->testcase->search['params']['branch']['values'] = array('' => '') + $this->loadModel('branch')->getPairs($productID, 'noempty') + array('all' => $this->lang->branch->all); + $this->config->testcase->search['params']['branch']['values'] = array('' => '', '0' => $this->lang->branch->main) + $this->loadModel('branch')->getPairs($productID, 'noempty') + array('all' => $this->lang->branch->all); } if(!$this->config->testcase->needReview) unset($this->config->testcase->search['params']['status']['values']['wait']); $this->config->testcase->search['actionURL'] = $actionURL; diff --git a/module/testsuite/model.php b/module/testsuite/model.php index c610a4d56a..360314084a 100644 --- a/module/testsuite/model.php +++ b/module/testsuite/model.php @@ -42,7 +42,7 @@ class testsuiteModel extends model if($currentProduct->type != 'normal') { $this->app->loadLang('branch'); - $branchName = $this->lang->branch->all . $this->lang->product->branchName[$currentProduct->type]; + $branchName = $this->lang->branch->main; $output .= "
"; } $output .= '';