diff --git a/module/branch/control.php b/module/branch/control.php
index 6f497f7abb..f10bfb8cbd 100644
--- a/module/branch/control.php
+++ b/module/branch/control.php
@@ -260,10 +260,12 @@ class branch extends control
* @param string $param
* @param int $projectID
* @param bool $withMainBranch
+ * @param string $isSiblings
+ * @param string $fieldID
* @access public
* @return void
*/
- public function ajaxGetBranches($productID, $oldBranch = 0, $param = 'all', $projectID = 0, $withMainBranch = true)
+ public function ajaxGetBranches($productID, $oldBranch = 0, $param = 'all', $projectID = 0, $withMainBranch = true, $isSiblings = 'no', $fieldID = '0')
{
$product = $this->loadModel('product')->getById($productID);
if(empty($product) or $product->type == 'normal') return;
@@ -280,6 +282,7 @@ class branch extends control
$branchTagOption[$oldBranch] = $oldBranch == BRANCH_MAIN ? $branch : ($branch->name . ($branch->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : ''));
}
+ if($isSiblings == 'yes') return print(html::select("branches[$fieldID]", $branchTagOption, $oldBranch, "onchange='loadBranchRelation(this.value, $fieldID);' class='form-control chosen control-branch'"));
return print(html::select('branch', $branchTagOption, $oldBranch, "class='form-control' onchange='loadBranch(this)'"));
}
diff --git a/module/product/control.php b/module/product/control.php
index e9261dddfc..76a023a307 100644
--- a/module/product/control.php
+++ b/module/product/control.php
@@ -959,6 +959,22 @@ class product extends control
}
}
+ /**
+ * Ajax get product by id.
+ *
+ * @param int $productID
+ * @access public
+ * @return void
+ */
+ public function ajaxGetProductById($productID)
+ {
+ $product = $this->product->getById($productID);
+
+ $product->branchSourceName = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
+ $product->branchName = $this->lang->product->branchName[$product->type];
+ echo json_encode($product);
+ }
+
/**
* AJAX: get projects of a product in html select.
*
@@ -1063,7 +1079,7 @@ class product extends control
{
$param = strtolower($param);
$plans = $this->loadModel('productplan')->getPairs($productID, $branch, $expired, strpos($param, 'skipparent') !== false);
- $field = $fieldID ? "plans[$fieldID]" : 'plan';
+ $field = $fieldID !== '' ? "plans[$fieldID]" : 'plan';
$multiple = strpos($param, 'multiple') === false ? '' : 'multiple';
$output = html::select($field, $plans, $planID, "class='form-control chosen' $multiple");
diff --git a/module/story/control.php b/module/story/control.php
index eb9b306212..5bbe651826 100644
--- a/module/story/control.php
+++ b/module/story/control.php
@@ -121,6 +121,7 @@ class story extends control
$response['result'] = 'success';
setcookie('lastStoryModule', (int)$this->post->module, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false);
+
$storyResult = $this->story->create($objectID, $bugID, $from = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $extra);
if(!$storyResult or dao::isError())
{
@@ -157,7 +158,9 @@ class story extends control
$action = $fromObjectAction;
$extra = $fromObjectID;
}
- $actionID = $this->action->create('story', $storyID, $action, '', $extra);
+ /* Create actions. */
+ $storyIds = $storyResult['ids'];
+ foreach($storyIds as $idItem) $actionID = $this->action->create('story', $idItem, $action, '', $extra);
if($objectID != 0)
{
diff --git a/module/story/css/create.css b/module/story/css/create.css
index c9854e8d17..ad21e95319 100644
--- a/module/story/css/create.css
+++ b/module/story/css/create.css
@@ -16,3 +16,7 @@
.source .input-group-addon, .sourceNote .input-group-addon {padding-top: 9px;}
#sourceNoteBox {width: 80px;}
#moduleIdBox .input-group-addon {min-width: 77px;}
+
+.btn.btn-action, .c-actions .btn {color: grey}
+#storyNoticeBranch {font-size: 13px; color: #5e626d;}
+#storyNoticeBranch i {font-size: 14px; color: #0075ff; margin-right: 3px;}
\ No newline at end of file
diff --git a/module/story/js/create.js b/module/story/js/create.js
index 868dfd6a20..23b7231072 100644
--- a/module/story/js/create.js
+++ b/module/story/js/create.js
@@ -108,3 +108,151 @@ function setLane(regionID)
$(window).unload(function(){
if(blockID) window.parent.refreshBlock($('#block' + blockID));
});
+
+/**
+ * Add branch box.
+ *
+ * @param obj $obj
+ * @access public
+ * @return void
+ */
+ function addBranchesBox(obj)
+ {
+ var item = $('#addBranchesBox').html().replace(/%i%/g, itemIndex);
+ $(obj).closest('tr').after('
' + item + '
');
+ $('#branches_i__chosen').remove();
+ $('#branches' + itemIndex).chosen();
+ $('#modules_i__chosen').remove();
+ $('#modules' + itemIndex).chosen();
+ $('#plans_i__chosen').remove();
+ $('#plans' + itemIndex).chosen();
+ $('.addBranchesBox' + itemIndex + ' #planIdBox').css('flex', '0 0 ' + gap + 'px');
+
+ loadBranchForSiblings($('#product').val(), 0, itemIndex)
+ loadModuleForSiblings($('#product').val(), 0, itemIndex)
+ loadPlanForSiblings($('#product').val(), 0, itemIndex)
+ $('.addBranchesBox' + itemIndex + ' #branchBox .input-group .input-group-addon').html($.cookie('branchSourceName'))
+
+ itemIndex ++;
+ }
+
+ /**
+ * Delete branch box.
+ *
+ * @param obj $obj
+ * @access public
+ * @return void
+ */
+ function deleteBranchesBox(obj)
+ {
+ $(obj).closest('tr').remove();
+ }
+
+ /**
+ * Load branch for multi branch or multi platform.
+ *
+ * @param int $branch
+ * @param int $branchIndex
+ * @access public
+ * @return void
+ */
+function loadBranchRelation(branch, branchIndex)
+{
+ var productID = $('#product').val();
+ if(typeof(branch) == 'undefined') branch = 0;
+
+ loadModuleForSiblings(productID, branch, branchIndex)
+ loadPlanForSiblings(productID, branch, branchIndex)
+}
+
+/**
+ * Load branch for siblings.
+ *
+ * @paran int $procutID
+ * @param int $branch
+ * @param int $branchIndex
+ * @access public
+ * @return void
+ */
+function loadBranchForSiblings(productID, branch, branchIndex)
+{
+ var isSiblings = storyType == 'story' ? 'yes' : 'no';
+ $.get(createLink('branch', 'ajaxGetBranches', "productID=" + productID + "&oldBranch=0¶m=active&projectID=" + executionID + "&withMainBranch=1&isSiblings=" + isSiblings + "&fieldID=" + branchIndex), function(data)
+ {
+ if(data)
+ {
+ /* reload branch */
+ $('#branches' + branchIndex).replaceWith(data);
+ $('#branches' + branchIndex + "_chosen").remove();
+ $('#branches' + branchIndex).next('.picker').remove();
+ $('#branches' + branchIndex).chosen();
+ }
+ })
+}
+
+/**
+ * Load module for siblings.
+ *
+ * @paran int $procutID
+ * @param int $branch
+ * @param int $branchIndex
+ * @access public
+ * @return void
+ */
+function loadModuleForSiblings(productID, branch, branchIndex)
+{
+ /* Load module */
+ var currentModule = 0;
+ var moduleLink = createLink('tree', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=story&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=' + branchIndex + '&needManage=true&extra=¤tModuleID=' + currentModule);
+ if(branchIndex > 0)
+ {
+ var $moduleIDBox = $('.addBranchesBox'+ branchIndex +' #moduleIdBox');
+ }
+ else
+ {
+ var $moduleIDBox = $('.switchBranch #moduleIdBox');
+ }
+ $moduleIDBox.load(moduleLink, function()
+ {
+ $moduleIDBox.find('#modules' + branchIndex).chosen();
+ if(branchIndex == 0)
+ {
+ $('.switchBranch #moduleIdBox span:first-child').remove()
+ }
+ $moduleIDBox.prepend("" + storyModule + "" );
+
+ $moduleIDBox.fixInputGroup();
+ });
+}
+
+/**
+ * Load plan for siblings.
+ *
+ * @paran int $procutID
+ * @param int $branch
+ * @param int $branchIndex
+ * @access public
+ * @return void
+ */
+function loadPlanForSiblings(productID, branch, branchIndex)
+{
+ /* Load plan */
+ if(typeof(branch) == 'undefined') branch = 0;
+ if(!branch) branch = 0;
+ var expired = config.currentMethod == 'create' ? 'unexpired' : '';
+ planLink = createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '&planID=' + $('#plan').val() + '&fieldID=' + branchIndex + '&needCreate=true&expired='+ expired +'¶m=skipParent,' + config.currentMethod);
+ if(branchIndex > 0)
+ {
+ var $planIdBox = $('.addBranchesBox'+ branchIndex +' #planIdBox');
+ }
+ else
+ {
+ var $planIdBox = $('#planIdBox');
+ }
+ $planIdBox.load(planLink, function()
+ {
+ $planIdBox.find('#plans' + branchIndex).chosen();
+ $planIdBox.prepend("" + storyPlan + "");
+ $planIdBox.fixInputGroup();
+ });
+}
diff --git a/module/story/lang/de.php b/module/story/lang/de.php
index f1369fc64d..14c4e80eb5 100644
--- a/module/story/lang/de.php
+++ b/module/story/lang/de.php
@@ -508,3 +508,7 @@ $lang->requirement->batchAssignTo = "Batch Assign";
$lang->requirement->batchChangeModule = "Batch Change Modules";
$lang->requirement->submitReview = $lang->story->submitReview;
$lang->requirement->linkStory = 'Link Story';
+
+$lang->story->addBranch = 'Add %s';
+$lang->story->deleteBranch = 'Delete %s';
+$lang->story->notice->branch = 'Each branch will establish a requirement, and the requirements are twins. The twins requirements are synchronized except for the product, branch, module, plan, and stage fields. You can manually remove the twins relationship later';
\ No newline at end of file
diff --git a/module/story/lang/en.php b/module/story/lang/en.php
index 21f3941765..4de2c96313 100644
--- a/module/story/lang/en.php
+++ b/module/story/lang/en.php
@@ -508,3 +508,7 @@ $lang->requirement->batchAssignTo = "Batch Assign";
$lang->requirement->batchChangeModule = "Batch Change Modules";
$lang->requirement->submitReview = $lang->story->submitReview;
$lang->requirement->linkStory = 'Link Story';
+
+$lang->story->addBranch = 'Add %s';
+$lang->story->deleteBranch = 'Delete %s';
+$lang->story->notice->branch = 'Each branch will establish a requirement, and the requirements are twins. The twins requirements are synchronized except for the product, branch, module, plan, and stage fields. You can manually remove the twins relationship later';
\ No newline at end of file
diff --git a/module/story/lang/fr.php b/module/story/lang/fr.php
index d1532afc18..df1111501f 100644
--- a/module/story/lang/fr.php
+++ b/module/story/lang/fr.php
@@ -508,3 +508,7 @@ $lang->requirement->batchAssignTo = "Batch Assign";
$lang->requirement->batchChangeModule = "Batch Change Modules";
$lang->requirement->submitReview = $lang->story->submitReview;
$lang->requirement->linkStory = 'Lier Story';
+
+$lang->story->addBranch = 'Add %s';
+$lang->story->deleteBranch = 'Delete %s';
+$lang->story->notice->branch = 'Each branch will establish a requirement, and the requirements are twins. The twins requirements are synchronized except for the product, branch, module, plan, and stage fields. You can manually remove the twins relationship later';
\ No newline at end of file
diff --git a/module/story/lang/zh-cn.php b/module/story/lang/zh-cn.php
index ac70cc2d84..4d522d9645 100644
--- a/module/story/lang/zh-cn.php
+++ b/module/story/lang/zh-cn.php
@@ -508,3 +508,7 @@ $lang->requirement->batchAssignTo = "批量指派";
$lang->requirement->batchChangeModule = "批量修改模块";
$lang->requirement->submitReview = $lang->story->submitReview;
$lang->requirement->linkStory = "关联{$lang->SRCommon}";
+
+$lang->story->addBranch = '添加%s';
+$lang->story->deleteBranch = '删除%s';
+$lang->story->notice->branch = '每个分支会建立一个需求,需求间互为孪生关系,孪生需求间除产品、分支、模块、计划、阶段字段外均同步,后期您可以手动解除孪生关系';
\ No newline at end of file
diff --git a/module/story/model.php b/module/story/model.php
index 8a4a822fb9..464a76a15a 100644
--- a/module/story/model.php
+++ b/module/story/model.php
@@ -234,173 +234,209 @@ class storyModel extends model
->join('assignedTo', '')
->join('mailto', ',')
->stripTags($this->config->story->editor->create['id'], $this->config->allowedTags)
- ->remove('files,labels,reviewer,needNotReview,newStory,uid,contactListMenu,URS,region,lane,ticket')
+ ->remove('files,labels,reviewer,needNotReview,newStory,uid,contactListMenu,URS,region,lane,ticket,branches,modules,plans')
->get();
/* Check repeat story. */
$result = $this->loadModel('common')->removeDuplicate('story', $story, "product={$story->product}");
if(isset($result['stop']) and $result['stop']) return array('status' => 'exists', 'id' => $result['duplicate']);
-
if($story->status != 'draft' and $this->checkForceReview()) $story->status = 'reviewing';
- if(strpos('draft,reviewing', $story->status) !== false) $story->stage = $this->post->plan > 0 ? 'planned' : 'wait';
$story = $this->loadModel('file')->processImgURL($story, $this->config->story->editor->create['id'], $this->post->uid);
- $requiredFields = "," . $this->config->story->create->requiredFields . ",";
-
- if($story->type == 'requirement') $requiredFields = str_replace(',plan,', ',', $requiredFields);
- if(strpos($requiredFields, ',estimate,') !== false)
+ $product = $this->loadModel('product')->getById($story->product);
+ if($product->type == 'normal' or $story->type == 'requirement')
{
- if(strlen(trim($story->estimate)) == 0) dao::$errors['estimate'] = sprintf($this->lang->error->notempty, $this->lang->story->estimate);
- $requiredFields = str_replace(',estimate,', ',', $requiredFields);
+ $this->post->branches = isset($story->branch) ? array($story->branch) : array(0 => 0);
+ $this->post->modules = isset($story->module) ? array($story->module) : array(0 => 0);
+ $this->post->plans = isset($story->plan) ? array($story->plan) : array(0 => 0);
}
- $requiredFields = trim($requiredFields, ',');
-
- $this->dao->insert(TABLE_STORY)->data($story, 'spec,verify')
- ->autoCheck()
- ->checkIF($story->notifyEmail, 'notifyEmail', 'email')
- ->batchCheck($requiredFields, 'notempty')
- ->checkFlow()
- ->exec();
-
- if(!dao::isError())
+ $storyIds = array();
+ $mainStoryID = 0;
+ foreach($this->post->branches as $key => $branch)
{
- $storyID = $this->dao->lastInsertID();
+ $story->branch = $branch;
+ $story->module = $this->post->modules[$key];
+ $story->plan = $this->post->plans[$key];
- /* Fix bug #21992, user story have no parent story. */
- if(isset($story->parent) and $story->parent)
+ if(strpos('draft,reviewing', $story->status) !== false) $story->stage = $this->post->plan > 0 ? 'planned' : 'wait';
+
+ $requiredFields = "," . $this->config->story->create->requiredFields . ",";
+
+ if($story->type == 'requirement') $requiredFields = str_replace(',plan,', ',', $requiredFields);
+ if(strpos($requiredFields, ',estimate,') !== false)
{
- $stories = array($storyID);
- $this->subdivide($story->parent, $stories);
+ if(strlen(trim($story->estimate)) == 0) dao::$errors['estimate'] = sprintf($this->lang->error->notempty, $this->lang->story->estimate);
+ $requiredFields = str_replace(',estimate,', ',', $requiredFields);
}
- if(!empty($story->plan))
+ $requiredFields = trim($requiredFields, ',');
+
+ $this->dao->insert(TABLE_STORY)->data($story, 'spec,verify')
+ ->autoCheck()
+ ->checkIF($story->notifyEmail, 'notifyEmail', 'email')
+ ->batchCheck($requiredFields, 'notempty')
+ ->checkFlow()
+ ->exec();
+
+ if(dao::isError()) return false;
+
+ if(!dao::isError())
{
- $this->updateStoryOrderOfPlan($storyID, $story->plan); // Set story order in this plan.
- $this->loadModel('action')->create('productplan', $story->plan, 'linkstory', '', $storyID);
- }
+ $storyID = $this->dao->lastInsertID();
- $this->file->updateObjectID($this->post->uid, $storyID, $story->type);
- $files = $this->file->saveUpload($story->type, $storyID, 1);
-
- $data = new stdclass();
- $data->story = $storyID;
- $data->version = 1;
- $data->title = $story->title;
- $data->spec = $story->spec;
- $data->verify = $story->verify;
- $data->files = join(',', array_keys($files));
- $this->dao->insert(TABLE_STORYSPEC)->data($data)->exec();
-
- /* Save the story reviewer to storyreview table. */
- if(isset($_POST['reviewer']))
- {
- foreach($this->post->reviewer as $reviewer)
+ /* Fix bug #21992, user story have no parent story. */
+ if(isset($story->parent) and $story->parent)
{
- if(empty($reviewer)) continue;
-
- $reviewData = new stdclass();
- $reviewData->story = $storyID;
- $reviewData->version = 1;
- $reviewData->reviewer = $reviewer;
- $this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
+ $stories = array($storyID);
+ $this->subdivide($story->parent, $stories);
}
- }
- /* Project or execution linked story. */
- if($executionID != 0)
- {
- $this->linkStory($executionID, $this->post->product, $storyID);
- if($this->config->systemMode == 'new' and $executionID != $this->session->project) $this->linkStory($this->session->project, $this->post->product, $storyID);
-
- $this->loadModel('kanban');
-
- $laneID = isset($output['laneID']) ? $output['laneID'] : 0;
- if(isset($_POST['lane'])) $laneID = $_POST['lane'];
-
- $columnID = $this->kanban->getColumnIDByLaneID($laneID, 'backlog');
- if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
-
- if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($executionID, $laneID, $columnID, 'story', $storyID);
- if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($executionID, 'story');
- }
-
- if(is_array($this->post->URS))
- {
- foreach($this->post->URS as $URID)
+ if(!empty($story->plan))
{
- $requirement = $this->getByID($URID);
- $data = new stdclass();
- $data->product = $story->product;
- $data->AType = 'requirement';
- $data->relation = 'subdivideinto';
- $data->BType = 'story';
- $data->AID = $URID;
- $data->BID = $storyID;
- $data->AVersion = $requirement->version;
- $data->BVersion = 1;
- $data->extra = 1;
-
- $this->dao->insert(TABLE_RELATION)->data($data)->autoCheck()->exec();
-
- $data->AType = 'story';
- $data->relation = 'subdividedfrom';
- $data->BType = 'requirement';
- $data->AID = $storyID;
- $data->BID = $URID;
- $data->AVersion = 1;
- $data->BVersion = $requirement->version;
-
- $this->dao->insert(TABLE_RELATION)->data($data)->autoCheck()->exec();
+ $this->updateStoryOrderOfPlan($storyID, $story->plan); // Set story order in this plan.
+ $this->loadModel('action')->create('productplan', $story->plan, 'linkstory', '', $storyID);
}
- }
- if($bugID > 0)
- {
- if(($this->config->edition == 'biz' || $this->config->edition == 'max')) $oldBug = $this->dao->select('feedback, status')->from(TABLE_BUG)->where('id')->eq($bugID)->fetch();
+ $this->file->updateObjectID($this->post->uid, $storyID, $story->type);
+ $files = $this->file->saveUpload($story->type, $storyID, 1);
- $bug = new stdclass();
- $bug->toStory = $storyID;
- $bug->status = 'closed';
- $bug->resolution = 'tostory';
- $bug->resolvedBy = $this->app->user->account;
- $bug->resolvedDate = $now;
- $bug->closedBy = $this->app->user->account;
- $bug->closedDate = $now;
- $bug->assignedTo = 'closed';
- $bug->assignedDate = $now;
- $this->dao->update(TABLE_BUG)->data($bug)->where('id')->eq($bugID)->exec();
+ $data = new stdclass();
+ $data->story = $storyID;
+ $data->version = 1;
+ $data->title = $story->title;
+ $data->spec = $story->spec;
+ $data->verify = $story->verify;
+ $data->files = join(',', array_keys($files));
+ $this->dao->insert(TABLE_STORYSPEC)->data($data)->exec();
- $this->loadModel('action')->create('bug', $bugID, 'ToStory', '', $storyID);
- $this->action->create('bug', $bugID, 'Closed');
-
- if(($this->config->edition == 'biz' || $this->config->edition == 'max') && !dao::isError() && $oldBug->feedback) $this->loadModel('feedback')->updateStatus('bug', $oldBug->feedback, 'closed', $oldBug->status);
-
- /* add files to story from bug. */
- $files = $this->dao->select('*')->from(TABLE_FILE)
- ->where('objectType')->eq('bug')
- ->andWhere('objectID')->eq($bugID)
- ->fetchAll();
- if(!empty($files))
+ /* Save the story reviewer to storyreview table. */
+ if(isset($_POST['reviewer']))
{
- foreach($files as $file)
+ foreach($this->post->reviewer as $reviewer)
{
- $file->objectType = 'story';
- $file->objectID = $storyID;
- unset($file->id);
- $this->dao->insert(TABLE_FILE)->data($file)->exec();
+ if(empty($reviewer)) continue;
+
+ $reviewData = new stdclass();
+ $reviewData->story = $storyID;
+ $reviewData->version = 1;
+ $reviewData->reviewer = $reviewer;
+ $this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
}
}
+
+ /* Project or execution linked story. */
+ if($executionID != 0)
+ {
+ $this->linkStory($executionID, $this->post->product, $storyID);
+ if($this->config->systemMode == 'new' and $executionID != $this->session->project) $this->linkStory($this->session->project, $this->post->product, $storyID);
+
+ $this->loadModel('kanban');
+
+ $laneID = isset($output['laneID']) ? $output['laneID'] : 0;
+ if(isset($_POST['lane'])) $laneID = $_POST['lane'];
+
+ $columnID = $this->kanban->getColumnIDByLaneID($laneID, 'backlog');
+ if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
+
+ if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($executionID, $laneID, $columnID, 'story', $storyID);
+ if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($executionID, 'story');
+ }
+
+ if(is_array($this->post->URS))
+ {
+ foreach($this->post->URS as $URID)
+ {
+ $requirement = $this->getByID($URID);
+ $data = new stdclass();
+ $data->product = $story->product;
+ $data->AType = 'requirement';
+ $data->relation = 'subdivideinto';
+ $data->BType = 'story';
+ $data->AID = $URID;
+ $data->BID = $storyID;
+ $data->AVersion = $requirement->version;
+ $data->BVersion = 1;
+ $data->extra = 1;
+
+ $this->dao->insert(TABLE_RELATION)->data($data)->autoCheck()->exec();
+
+ $data->AType = 'story';
+ $data->relation = 'subdividedfrom';
+ $data->BType = 'requirement';
+ $data->AID = $storyID;
+ $data->BID = $URID;
+ $data->AVersion = 1;
+ $data->BVersion = $requirement->version;
+
+ $this->dao->insert(TABLE_RELATION)->data($data)->autoCheck()->exec();
+ }
+ }
+
+ if($bugID > 0)
+ {
+ if(($this->config->edition == 'biz' || $this->config->edition == 'max')) $oldBug = $this->dao->select('feedback, status')->from(TABLE_BUG)->where('id')->eq($bugID)->fetch();
+
+ $bug = new stdclass();
+ $bug->toStory = $storyID;
+ $bug->status = 'closed';
+ $bug->resolution = 'tostory';
+ $bug->resolvedBy = $this->app->user->account;
+ $bug->resolvedDate = $now;
+ $bug->closedBy = $this->app->user->account;
+ $bug->closedDate = $now;
+ $bug->assignedTo = 'closed';
+ $bug->assignedDate = $now;
+ $this->dao->update(TABLE_BUG)->data($bug)->where('id')->eq($bugID)->exec();
+
+ $this->loadModel('action')->create('bug', $bugID, 'ToStory', '', $storyID);
+ $this->action->create('bug', $bugID, 'Closed');
+
+ if(($this->config->edition == 'biz' || $this->config->edition == 'max') && !dao::isError() && $oldBug->feedback) $this->loadModel('feedback')->updateStatus('bug', $oldBug->feedback, 'closed', $oldBug->status);
+
+ /* add files to story from bug. */
+ $files = $this->dao->select('*')->from(TABLE_FILE)
+ ->where('objectType')->eq('bug')
+ ->andWhere('objectID')->eq($bugID)
+ ->fetchAll();
+ if(!empty($files))
+ {
+ foreach($files as $file)
+ {
+ $file->objectType = 'story';
+ $file->objectID = $storyID;
+ unset($file->id);
+ $this->dao->insert(TABLE_FILE)->data($file)->exec();
+ }
+ }
+ }
+ if(!defined('TUTORIAL')) $this->setStage($storyID);
+ if(!dao::isError()) $this->loadModel('score')->create('story', 'create',$storyID);
+
+ /* Callback the callable method to process the related data for object that is transfered to story. */
+ if($from && is_callable(array($this, $this->config->story->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->story->fromObjects[$from]['callback']), $storyID);
+
+ $storyIds[] = $storyID;
+ if(empty($mainStoryID)) $mainStoryID = $storyID;
}
- if(!defined('TUTORIAL')) $this->setStage($storyID);
- if(!dao::isError()) $this->loadModel('score')->create('story', 'create',$storyID);
-
- /* Callback the callable method to process the related data for object that is transfered to story. */
- if($from && is_callable(array($this, $this->config->story->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->story->fromObjects[$from]['callback']), $storyID);
-
- return array('status' => 'created', 'id' => $storyID);
}
- return false;
+
+ /* bind siblings story id */
+ if(count($storyIds) > 1)
+ {
+ foreach($storyIds as $siblingsStoryID)
+ {
+ $siblingsArr = array();
+ foreach($storyIds as $idItem)
+ {
+ if($idItem != $siblingsStoryID) $siblingsArr[] = $idItem;
+ $siblings = ',' . implode(',', $siblingsArr) . ',';
+ $this->dao->update(TABLE_STORY)->set('siblings')->eq($siblings)->where('id')->eq($siblingsStoryID)->exec();
+ }
+ }
+ }
+
+ return array('status' => 'created', 'id' => $mainStoryID, 'ids' => $storyIds);
}
/**
diff --git a/module/story/view/create.html.php b/module/story/view/create.html.php
index 856bc23ffe..b04c50041e 100644
--- a/module/story/view/create.html.php
+++ b/module/story/view/create.html.php
@@ -51,31 +51,74 @@ foreach(explode(',', $config->story->create->requiredFields) as $field)
story->product;?> |
-
+
-
+
|
-
- ';
- }
- ?>
+ | switchBranch'>
+
+ |
+ switchBranch'> |
+ switchBranch'>
+ ';
+ }
+ ?>
|
-
+
switchBranch' >
+ | product->branchName[$product->type];?> |
+
+
+ |
+
+
+ |
+
+
+ |
+
+ story->notice->branch;?>
+ |
+
+
| story->planAB;?> |
@@ -301,10 +344,52 @@ foreach(explode(',', $config->story->create->requiredFields) as $field)
+
+
+
story->module);?>
+story->plan);?>
diff --git a/module/story/view/header.html.php b/module/story/view/header.html.php
index 5d84b1a6b8..31b5bd2cfd 100644
--- a/module/story/view/header.html.php
+++ b/module/story/view/header.html.php
@@ -81,20 +81,71 @@ function loadProductBranches(productID)
if(page == 'create') param = 'active';
$('#branch').remove();
$('#branch_chosen').remove();
- $.get(createLink('branch', 'ajaxGetBranches', "productID=" + productID + "&oldBranch=0¶m=" + param + "&projectID=" + executionID), function(data)
- {
- var $product = $('#product');
- var $inputGroup = $product.closest('.input-group');
- $inputGroup.find('.input-group-addon').toggleClass('hidden', !data);
- if(data)
- {
- $inputGroup.append(data);
- $('#branch').css('width', config.currentMethod == 'create' ? '120px' : '65px').chosen();
- }
- $inputGroup.fixInputGroup();
- loadProductModules(productID, $('#branch').val());
- loadProductPlans(productID, $('#branch').val());
+ var isSiblings = storyType == 'story' ? 'yes' : 'no';
+ $.get(createLink('branch', 'ajaxGetBranches', "productID=" + productID + "&oldBranch=0¶m=" + param + "&projectID=" + executionID + "&withMainBranch=1&isSiblings=" + isSiblings), function(data)
+ {
+ if(storyType == 'story')
+ {
+ $('.switchBranch').toggleClass('hidden');
+ $('.switchBranch').toggleClass('disable');
+ $('tr[class^="addBranchesBox"]').remove();
+
+ if(data)
+ {
+ $.get(createLink('product', 'ajaxGetProductById', "productID=" + productID), function(data)
+ {
+ $('.switchBranch #branchBox .input-group .input-group-addon').html(data.branchSourceName)
+ $('.switchBranch #branchBox').closest('td').prev().html(data.branchName)
+ $.cookie('branchSourceName', data.branchSourceName)
+ }, 'json')
+
+ /* reload branch */
+ $('#branches0').replaceWith(data);
+ $('#branches0' + "_chosen").remove();
+ $('#branches0').next('.picker').remove();
+ $('#branches0').chosen();
+
+ $.get(createLink('tree', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=story&branch=0' + '&rootModuleID=0&returnType=html&fieldID=0' + '&needManage=true&extra=¤tModuleID=0'), function(moduleData)
+ {
+ if(moduleData)
+ {
+ $('#modules0').replaceWith(moduleData);
+ $('#modules0' + "_chosen").remove();
+ $('#modules0').next('.picker').remove();
+ $('#modules0').chosen();
+ }
+ });
+
+ var expired = config.currentMethod == 'create' ? 'unexpired' : '';
+ $.get(createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=0' + '&planID=' + $('#plan').val() + '&fieldID=0' + '&needCreate=true&expired='+ expired +'¶m=skipParent,' + config.currentMethod), function(planData)
+ {
+ if(planData)
+ {
+ $('#plans0').replaceWith(planData);
+ $('#plans0' + "_chosen").remove();
+ $('#plans0').next('.picker').remove();
+ $('#plans0').chosen();
+ }
+ });
+ }
+ }
+ else
+ {
+ var $product = $('#product');
+ var $inputGroup = $product.closest('.input-group');
+ $inputGroup.find('.input-group-addon').toggleClass('hidden', !data);
+ if(data)
+ {
+ $inputGroup.append(data);
+ $('#branch').css('width', config.currentMethod == 'create' ? '120px' : '65px').chosen();
+ }
+ $inputGroup.fixInputGroup();
+
+ loadProductModules(productID, $('#branch').val());
+ loadProductPlans(productID, $('#branch').val());
+ }
+
})
}
@@ -184,4 +235,4 @@ function loadProductReviewers(productID)
}
});
}
-
+
\ No newline at end of file
diff --git a/module/tree/control.php b/module/tree/control.php
index 49ac28269b..f245d371b6 100644
--- a/module/tree/control.php
+++ b/module/tree/control.php
@@ -538,7 +538,7 @@ class tree extends control
$changeFunc = '';
if($viewType == 'bug' or $viewType == 'case') $changeFunc = "onchange='loadModuleRelated()'";
if($viewType == 'task') $changeFunc = "onchange='setStories(this.value, $rootID)'";
- $field = $fieldID ? "modules[$fieldID]" : 'module';
+ $field = $fieldID !== '' ? "modules[$fieldID]" : 'module';
$currentModule = $this->tree->getById($currentModuleID);
$currentModuleID = (isset($currentModule->branch) and $currentModule->branch == 0) ? $currentModuleID : 0;
|