Merge branch 'sprint/zentaopms256_lanzongjun' into 'zentaopms_256'
* task #74142 See merge request easycorp/zentaopms!5995
This commit is contained in:
@@ -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)'"));
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;}
|
||||
@@ -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('<tr class="addBranchesBox' + itemIndex + '">' + item + '</tr>');
|
||||
$('#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("<span class='input-group-addon fix-border'>" + storyModule + "</span>" );
|
||||
|
||||
$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("<span class='input-group-addon fix-border'>" + storyPlan + "</span>");
|
||||
$planIdBox.fixInputGroup();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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 = '每个分支会建立一个需求,需求间互为孪生关系,孪生需求间除产品、分支、模块、计划、阶段字段外均同步,后期您可以手动解除孪生关系';
|
||||
+171
-135
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,31 +51,74 @@ foreach(explode(',', $config->story->create->requiredFields) as $field)
|
||||
<th><?php echo $lang->story->product;?></th>
|
||||
<td colspan="2">
|
||||
<div class='input-group'>
|
||||
<?php echo html::select('product', $products, $productID, "onchange='loadProduct(this.value);' class='form-control chosen control-product'");?>
|
||||
<?php echo html::select('product', $products, $productID, "onchange='loadProduct(this.value);' class='form-control chosen control-product' required");?>
|
||||
<span class='input-group-addon fix-border fix-padding'></span>
|
||||
<?php if($branches) echo html::select('branch', $branches, $branch, "onchange='loadBranch();' class='form-control chosen control-branch'");?>
|
||||
<?php if($branches and $type != 'story') echo html::select('branch', $branches, $branch, "onchange='loadBranch();' class='form-control chosen control-branch'");?>
|
||||
</div>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<div class='input-group' id='moduleIdBox'>
|
||||
<div class="input-group-addon"><?php echo $lang->story->module;?></div>
|
||||
<?php
|
||||
echo html::select('module', $moduleOptionMenu, $moduleID, "class='form-control chosen'");
|
||||
if(count($moduleOptionMenu) == 1)
|
||||
{
|
||||
echo "<div class='input-group-addon'>";
|
||||
echo html::a($this->createLink('tree', 'browse', "rootID=$productID&view=story¤tModuleID=0&branch=$branch", '', true), $lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='90%'");
|
||||
echo ' ';
|
||||
echo html::a("javascript:void(0)", $lang->refreshIcon, '', "class='refresh' title='refresh' onclick='loadProductModules($productID)'");
|
||||
echo '</div>';
|
||||
}
|
||||
?>
|
||||
<td colspan='2' id='assignedToBox' class='<?php if((!$branches and $type == 'story') or $type == 'requirement') echo "hidden"; ?> switchBranch'>
|
||||
<div class='input-group'>
|
||||
<div class="input-group-addon assignedTo"><?php echo $lang->story->assignedTo;?></div>
|
||||
<?php echo html::select('assignedTo', $users, '', "class='form-control picker-select'");?>
|
||||
</div>
|
||||
</td>
|
||||
<td class='w-60px <?php if((!$branches and $type == 'story') or $type == 'requirement') echo "hidden"; ?> switchBranch'></td>
|
||||
<td colspan="2" class='<?php if($branches and $type == 'story') echo "hidden"; ?> switchBranch'>
|
||||
<div class='input-group' id='moduleIdBox'>
|
||||
<div class="input-group-addon"><?php echo $lang->story->module;?></div>
|
||||
<?php
|
||||
echo html::select('module', $moduleOptionMenu, $moduleID, "class='form-control chosen'");
|
||||
if(count($moduleOptionMenu) == 1)
|
||||
{
|
||||
echo "<div class='input-group-addon'>";
|
||||
echo html::a($this->createLink('tree', 'browse', "rootID=$productID&view=story¤tModuleID=0&branch=$branch", '', true), $lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='90%'");
|
||||
echo ' ';
|
||||
echo html::a("javascript:void(0)", $lang->refreshIcon, '', "class='refresh' title='refresh' onclick='loadProductModules($productID)'");
|
||||
echo '</div>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php $hiddenSource = strpos(",$showFields,", ',source,') !== false ? '' : 'hidden';?>
|
||||
<?php if($type == 'story'):?>
|
||||
<tr>
|
||||
<tr class='<?php if(!$branches) echo 'hidden'; ?> switchBranch' >
|
||||
<th><?php echo $lang->product->branchName[$product->type];?></th>
|
||||
<td colspan="4">
|
||||
<div class='input-group' style='display: flex;'>
|
||||
<div class='table-col' id='branchBox'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon fix-border'><?php echo sprintf($lang->product->branch, $lang->product->branchName[$product->type])?></span>
|
||||
<?php echo html::select('branches[0]', $branches, $branch, "onchange='loadBranchRelation(this.value, 0);' class='form-control chosen control-branch'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col' id='moduleIdBox'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->story->module?></span>
|
||||
<?php echo html::select('modules[0]', $moduleOptionMenu, $moduleID, "class='form-control chosen'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col' id='planIdBox'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->story->plan;?></span>
|
||||
<?php echo html::select('plans[0]', $plans, $planID, "class='form-control chosen'");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="table-col c-actions text-left w-60px">
|
||||
<div>
|
||||
<a href='javascript:;' onclick='addBranchesBox(this)' class='btn btn-link' title='<?php echo sprintf($lang->story->addBranch, $lang->product->branchName[$product->type])?>'><i class='icon-plus'></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class='<?php if(!$branches) echo "hidden"; ?> switchBranch' >
|
||||
<th></th>
|
||||
<td colspan="4" id="storyNoticeBranch">
|
||||
<i class="icon-exclamation-sign"></i> <?php echo $lang->story->notice->branch;?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class='<?php if($branches) echo "hidden"; ?> switchBranch'>
|
||||
<th class='planTh'><?php echo $lang->story->planAB;?></th>
|
||||
<td colspan="2">
|
||||
<div class='input-group' id='planIdBox'>
|
||||
@@ -301,10 +344,52 @@ foreach(explode(',', $config->story->create->requiredFields) as $field)
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php js::set('itemIndex', 1);?>
|
||||
<?php $i = '%i%';?>
|
||||
<table class="hidden">
|
||||
<tr id="addBranchesBox" class="hidden">
|
||||
<th></th>
|
||||
<td colspan="4">
|
||||
<div class='input-group' style='display: flex;'>
|
||||
<div class='table-col' id='branchBox' style='flex: 1 0 160px'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon fix-border'><?php echo sprintf($lang->product->branch, $lang->product->branchName[$product->type])?></span>
|
||||
<?php echo html::select("branches[$i]", $branches, $branch, "onchange='loadBranchRelation(this.value, $i);' class='form-control chosen control-branch'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col' id='moduleIdBox' style='flex: 1 0 160px'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->story->module?></span>
|
||||
<?php echo html::select("modules[$i]", $moduleOptionMenu, $moduleID, "class='form-control chosen'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col' id='planIdBox'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->story->plan;?></span>
|
||||
<?php echo html::select("plans[$i]", $plans, $planID, "class='form-control chosen'");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="table-col text-left w-60px c-actions">
|
||||
<div>
|
||||
<a href='javascript:;' onclick='addBranchesBox(this)' class='btn btn-link' title='<?php echo sprintf($lang->story->addBranch, $lang->product->branchName[$product->type])?>'><i class='icon-plus'></i></a>
|
||||
<a href='javascript:;' onclick='deleteBranchesBox(this)' class='btn btn-link' title='<?php echo sprintf($lang->story->deleteBranch, $lang->product->branchName[$product->type])?>'><i class='icon icon-close'></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php js::set('executionID', $objectID);?>
|
||||
<?php js::set('storyModule', $lang->story->module);?>
|
||||
<?php js::set('storyPlan', $lang->story->plan);?>
|
||||
<?php js::set('storyType', $type);?>
|
||||
<script>
|
||||
$(function(){parent.$('body.hide-modal-close').removeClass('hide-modal-close');})
|
||||
|
||||
var gap = $('#assignedTo').parent().parent().width();
|
||||
$('#planIdBox').css('flex', '0 0 ' + gap + 'px')
|
||||
$("#branches0").parent().parent().css('flex', '1 0 160px')
|
||||
$("#modules0").parent().parent().css('flex', '1 0 160px')
|
||||
|
||||
</script>
|
||||
<?php include '../../common/view/footer.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)
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user