Merge branch 'zentaopms_256'
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `zt_story` ADD COLUMN `siblings` varchar(255) NOT NULL AFTER `linkRequirements`;
|
||||
|
||||
ALTER TABLE `zt_productplan` MODIFY COLUMN `branch` varchar(255) NOT NULL DEFAULT '0';
|
||||
+2
-1
@@ -1129,7 +1129,7 @@ CREATE TABLE IF NOT EXISTS `zt_product` (
|
||||
CREATE TABLE IF NOT EXISTS `zt_productplan` (
|
||||
`id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`product` mediumint(8) unsigned NOT NULL,
|
||||
`branch` mediumint(8) unsigned NOT NULL,
|
||||
`branch` varchar(255) NOT NULL DEFAULT '0',
|
||||
`parent` mediumint(9) NOT NULL DEFAULT '0',
|
||||
`title` varchar(90) NOT NULL,
|
||||
`status` enum('wait','doing','done','closed') NOT NULL default 'wait',
|
||||
@@ -1480,6 +1480,7 @@ CREATE TABLE IF NOT EXISTS `zt_story` (
|
||||
`childStories` varchar(255) NOT NULL,
|
||||
`linkStories` varchar(255) NOT NULL,
|
||||
`linkRequirements` varchar(255) NOT NULL,
|
||||
`siblings` varchar(255) NOT NULL,
|
||||
`duplicateStory` mediumint(8) unsigned NOT NULL,
|
||||
`version` smallint(6) NOT NULL default '1',
|
||||
`feedbackBy` varchar(100) NOT NULL,
|
||||
|
||||
@@ -225,6 +225,7 @@ $lang->action->desc->reopen = '$date, reopened by <strong>$actor</
|
||||
$lang->action->desc->merged = '$date, merged by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->submitreview = '$date, submitted for review by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->ganttmove = '$date, sort by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->relieved = '$date, relieved by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->switchtolight = '$date, Switch from ALM mode to light mode by <strong>'. $lang->admin->system .'</strong>.' . "\n";
|
||||
$lang->action->desc->unlinkproduct = '$date, the project is disassociated from the $extra, synchronization disassociates the ' . $lang->executionCommon . 's of the project from the $extra.' . "\n";
|
||||
|
||||
@@ -395,6 +396,8 @@ $lang->action->label->tolib = 'imported';
|
||||
$lang->action->label->updatetolib = 'updated';
|
||||
$lang->action->label->ganttmove = 'sorted';
|
||||
$lang->action->label->submitreview = 'submitted';
|
||||
$lang->action->label->syncsiblings = 'synchronized changes';
|
||||
$lang->action->label->relieved = 'relieved';
|
||||
$lang->action->label->switchtolight = 'switch from ALM mode to light mode';
|
||||
$lang->action->label->linkedrepo = 'Linked Code Repo';
|
||||
$lang->action->label->unlinkedrepo = 'Unlinked Code Repo';
|
||||
|
||||
@@ -225,6 +225,7 @@ $lang->action->desc->reopen = '$date, reopened by <strong>$actor</
|
||||
$lang->action->desc->merged = '$date, merged by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->submitreview = '$date, submitted for review by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->ganttmove = '$date, sort by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->relieved = '$date, relieved by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->switchtolight = '$date, Switch from ALM mode to light mode by <strong>'. $lang->admin->system .'</strong>.' . "\n";
|
||||
$lang->action->desc->unlinkproduct = '$date, the project is disassociated from the $extra, synchronization disassociates the ' . $lang->executionCommon . 's of the project from the $extra.' . "\n";
|
||||
|
||||
@@ -395,6 +396,8 @@ $lang->action->label->tolib = 'imported';
|
||||
$lang->action->label->updatetolib = 'updated';
|
||||
$lang->action->label->ganttmove = 'sorted';
|
||||
$lang->action->label->submitreview = 'submitted';
|
||||
$lang->action->label->syncsiblings = 'synchronized changes';
|
||||
$lang->action->label->relieved = 'relieved';
|
||||
$lang->action->label->switchtolight = 'switch from ALM mode to light mode';
|
||||
$lang->action->label->linkedrepo = 'Linked Code Repo';
|
||||
$lang->action->label->unlinkedrepo = 'Unlinked Code Repo';
|
||||
|
||||
@@ -225,6 +225,7 @@ $lang->action->desc->reopen = '$date, reopened by <strong>$actor</
|
||||
$lang->action->desc->merged = '$date, merged by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->submitreview = '$date, submitted for review by <strong>$actor</strong>.' . "\n";
|
||||
$lang->action->desc->ganttmove = '$date, sort by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->relieved = '$date, relieved by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->switchtolight = '$date, Switch from ALM mode to light mode by <strong>'. $lang->admin->system .'</strong>.' . "\n";
|
||||
$lang->action->desc->unlinkproduct = '$date, the project is disassociated from the $extra, synchronization disassociates the ' . $lang->executionCommon . 's of the project from the $extra.' . "\n";
|
||||
|
||||
@@ -395,6 +396,8 @@ $lang->action->label->tolib = 'Importé';
|
||||
$lang->action->label->updatetolib = 'MàJ';
|
||||
$lang->action->label->ganttmove = 'sorted';
|
||||
$lang->action->label->submitreview = 'submitted';
|
||||
$lang->action->label->syncsiblings = 'synchronized changes';
|
||||
$lang->action->label->relieved = 'relieved';
|
||||
$lang->action->label->switchtolight = 'switch from ALM mode to light mode';
|
||||
$lang->action->label->linkedrepo = 'Linked Code Repo';
|
||||
$lang->action->label->unlinkedrepo = 'Unlinked Code Repo';
|
||||
|
||||
@@ -306,6 +306,7 @@ $lang->action->label->syncproject = 'start';
|
||||
$lang->action->label->syncexecution = 'start';
|
||||
$lang->action->label->startProgram = '(The start of the project sets the status of the program as Ongoing)';
|
||||
$lang->action->label->submitreview = 'submitted';
|
||||
$lang->action->label->syncsiblings = 'synchronized changes';
|
||||
$lang->action->label->linkedrepo = 'Linked Code Repo';
|
||||
$lang->action->label->unlinkedrepo = 'Unlinked Code Repo';
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ $lang->action->desc->reopen = '$date, 由 <strong>$actor</strong>
|
||||
$lang->action->desc->merged = '$date, 由 <strong>$actor</strong> 合并。' . "\n";
|
||||
$lang->action->desc->submitreview = '$date, 由 <strong>$actor</strong> 提交评审。' . "\n";
|
||||
$lang->action->desc->ganttmove = '$date, 由 <strong>$actor</strong> 排序。' . "\n";
|
||||
$lang->action->desc->relieved = '$date, 由 <strong>$actor</strong> 解除孪生需求关系。' . "\n";
|
||||
$lang->action->desc->switchtolight = '$date, 由于 <strong>'. $lang->admin->system .'</strong> 从全生命周期管理模式切换为轻量管理模式,项目访问控制由项目集内公开调整为私有。' . "\n";
|
||||
$lang->action->desc->unlinkproduct = '$date, 系统判断由于' . $lang->executionCommon . '所属项目与$extra取消关联,同步将' . $lang->executionCommon . '与$extra取消关联。' . "\n";
|
||||
|
||||
@@ -395,6 +396,8 @@ $lang->action->label->tolib = '导入了';
|
||||
$lang->action->label->updatetolib = '更新了';
|
||||
$lang->action->label->ganttmove = '排序了';
|
||||
$lang->action->label->submitreview = '提交了评审';
|
||||
$lang->action->label->syncsiblings = '同步修改了';
|
||||
$lang->action->label->relieved = '解除了';
|
||||
$lang->action->label->switchtolight = '从全生命周期管理模式切换为轻量管理模式';
|
||||
$lang->action->label->linkedrepo = '关联代码库到';
|
||||
$lang->action->label->unlinkedrepo = '取消了项目与代码库的关联';
|
||||
|
||||
+12
-1
@@ -50,7 +50,7 @@ class actionModel extends model
|
||||
$action->extra = $extra;
|
||||
if(!defined('IN_UPGRADE')) $action->vision = $this->config->vision;
|
||||
|
||||
if($objectType == 'story' and strpos(',reviewpassed,reviewrejected,reviewclarified,reviewreverted,', ",$actionType,") !== false) $action->actor = $this->lang->action->system;
|
||||
if($objectType == 'story' and strpos(',reviewpassed,reviewrejected,reviewclarified,reviewreverted,syncsiblings,', ",$actionType,") !== false) $action->actor = $this->lang->action->system;
|
||||
|
||||
/* Use purifier to process comment. Fix bug #2683. */
|
||||
$action->comment = fixer::stripDataTags($comment);
|
||||
@@ -952,6 +952,17 @@ class actionModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
if($action->objectType == 'story' and $action->action == 'syncsiblings')
|
||||
{
|
||||
if(!empty($extra) and strpos($extra, '|') !== false)
|
||||
{
|
||||
list($operate, $storyID) = explode('|', $extra);
|
||||
$desc['operate'] = $this->lang->$objectType->{$desc['operate']};
|
||||
$link = common::hasPriv('story', 'view') ? html::a(helper::createLink('story', 'view', "storyID=$storyID"), "#$storyID ") : "#$storyID";
|
||||
$actionDesc = str_replace(array('$extra', '$operate'), array($link, $desc['operate'][$operate]), $desc['main']);
|
||||
}
|
||||
}
|
||||
|
||||
if($action->objectType == 'module' and strpos(',created,moved,', $action->action) !== false)
|
||||
{
|
||||
$moduleNames = $this->loadModel('tree')->getOptionMenu($action->objectID, 'story', 0, 'all', '');
|
||||
|
||||
@@ -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)' data-last='{$oldBranch}'"));
|
||||
}
|
||||
|
||||
|
||||
@@ -898,7 +898,7 @@ class execution extends control
|
||||
$storyBugs = $this->loadModel('bug')->getStoryBugCounts($storyIdList, $executionID);
|
||||
$storyCases = $this->loadModel('testcase')->getStoryCaseCounts($storyIdList);
|
||||
|
||||
$plans = $this->execution->getPlans($products, 'skipParent|withMainPlan');
|
||||
$plans = $this->execution->getPlans($products, 'skipParent|withMainPlan', $executionID);
|
||||
$allPlans = array('' => '');
|
||||
if(!empty($plans))
|
||||
{
|
||||
@@ -1645,7 +1645,20 @@ class execution extends control
|
||||
}
|
||||
else
|
||||
{
|
||||
return print(js::confirm($this->lang->execution->importPlanStory, inlink('create', "projectID=$projectID&executionID=$executionID©ExecutionID=&planID=$planID&confirm=yes"), inlink('create', "projectID=$projectID&executionID=$executionID")));
|
||||
$executionProductList = $this->loadModel('product')->getProducts($executionID);
|
||||
$multiBranchProduct = false;
|
||||
foreach($executionProductList as $executionProduct)
|
||||
{
|
||||
if($executionProduct->type != 'normal')
|
||||
{
|
||||
$multiBranchProduct = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$importPlanStoryTips = $multiBranchProduct ? $this->lang->execution->importBranchPlanStory : $this->lang->execution->importPlanStory;
|
||||
|
||||
return print(js::confirm($importPlanStoryTips, inlink('create', "projectID=$projectID&executionID=$executionID©ExecutionID=&planID=$planID&confirm=yes"), inlink('create', "projectID=$projectID&executionID=$executionID")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1689,10 +1702,11 @@ class execution extends control
|
||||
$linkedBranches = array();
|
||||
foreach($products as $productIndex => $product)
|
||||
{
|
||||
$productPlans[$productIndex] = array();
|
||||
foreach($branches[$productIndex] as $branchID => $branch)
|
||||
{
|
||||
$linkedBranches[$productIndex][$branchID] = $branchID;
|
||||
$productPlans[$productIndex][$branchID] = isset($plans[$productIndex][$branchID]) ? $plans[$productIndex][$branchID] : array();
|
||||
$productPlans[$productIndex] += isset($plans[$productIndex][$branchID]) ? $plans[$productIndex][$branchID] : array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1724,10 +1738,11 @@ class execution extends control
|
||||
$linkedBranches = array();
|
||||
foreach($products as $productIndex => $product)
|
||||
{
|
||||
$productPlans[$productIndex] = array();
|
||||
foreach($branches[$productIndex] as $branchID => $branch)
|
||||
{
|
||||
$linkedBranches[$productIndex][$branchID] = $branchID;
|
||||
$productPlans[$productIndex][$branchID] = isset($plans[$productIndex][$branchID]) ? $plans[$productIndex][$branchID] : array();
|
||||
$productPlans[$productIndex] += isset($plans[$productIndex][$branchID]) ? $plans[$productIndex][$branchID] : array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1776,27 +1791,15 @@ class execution extends control
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('doc', 'objectLibs', "type=execution")));
|
||||
}
|
||||
|
||||
$planID = '';
|
||||
if(isset($_POST['plans']))
|
||||
{
|
||||
foreach($_POST['plans'] as $plans)
|
||||
{
|
||||
foreach($plans as $planID)
|
||||
{
|
||||
if(!empty($planID)) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($projectID) and $project->model == 'kanban')
|
||||
{
|
||||
$execution = $this->execution->getById($executionID);
|
||||
$this->loadModel('kanban')->createRDKanban($execution);
|
||||
}
|
||||
|
||||
if(!empty($planID))
|
||||
if(!empty($_POST['plans']))
|
||||
{
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('create', "projectID=$projectID&executionID=$executionID©ExecutionID=&planID=$planID&confirm=no")));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('create', "projectID=$projectID&executionID=$executionID©ExecutionID=&planID=1&confirm=no")));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1904,7 +1907,20 @@ class execution extends control
|
||||
}
|
||||
elseif(!empty($newPlans))
|
||||
{
|
||||
return print(js::confirm($this->lang->execution->importEditPlanStory, inlink('edit', "executionID=$executionID&action=edit&extra=&newPlans=$newPlans&confirm=yes"), inlink('view', "executionID=$executionID")));
|
||||
$executionProductList = $this->loadModel('product')->getProducts($executionID);
|
||||
$multiBranchProduct = false;
|
||||
foreach($executionProductList as $executionProduct)
|
||||
{
|
||||
if($executionProduct->type != 'normal')
|
||||
{
|
||||
$multiBranchProduct = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$importEditPlanStoryTips = $multiBranchProduct ? $this->lang->execution->importBranchEditPlanStory : $this->lang->execution->importEditPlanStory;
|
||||
|
||||
return print(js::confirm($importEditPlanStoryTips, inlink('edit', "executionID=$executionID&action=edit&extra=&newPlans=$newPlans&confirm=yes"), inlink('view', "executionID=$executionID")));
|
||||
}
|
||||
|
||||
/* Set menu. */
|
||||
@@ -2003,12 +2019,15 @@ class execution extends control
|
||||
foreach($linkedProducts as $productID => $linkedProduct)
|
||||
{
|
||||
if(!isset($allProducts[$productID])) $allProducts[$productID] = $linkedProduct->name;
|
||||
$productPlans[$productID] = array();
|
||||
|
||||
foreach($branches[$productID] as $branchID => $branch)
|
||||
{
|
||||
$productPlans[$productID] += isset($plans[$productID][$branchID]) ? $plans[$productID][$branchID] : array();
|
||||
|
||||
$linkedBranchList[$branchID] = $branchID;
|
||||
$linkedBranches[$productID][$branchID] = $branchID;
|
||||
$productPlans[$productID][$branchID] = isset($plans[$productID][$branchID]) ? $plans[$productID][$branchID] : array();
|
||||
if($branchID != BRANCH_MAIN and isset($plans[$productID][BRANCH_MAIN])) $productPlans[$productID][$branchID] += $plans[$productID][BRANCH_MAIN];
|
||||
if($branchID != BRANCH_MAIN and isset($plans[$productID][BRANCH_MAIN])) $productPlans[$productID] += $plans[$productID][BRANCH_MAIN];
|
||||
if(!empty($executionStories[$productID][$branchID]))
|
||||
{
|
||||
array_push($unmodifiableProducts, $productID);
|
||||
@@ -4121,9 +4140,11 @@ class execution extends control
|
||||
$count = 0;
|
||||
if(!empty($planStory))
|
||||
{
|
||||
$projectProducts = $this->loadModel('project')->getBranchesByProject($executionID);
|
||||
foreach($planStory as $id => $story)
|
||||
{
|
||||
if($story->status == 'draft' or $story->status == 'reviewing')
|
||||
$projectBranches = zget($projectProducts, $story->product, array());
|
||||
if($story->status == 'draft' or $story->status == 'reviewing' or (!empty($story->branch) and !empty($projectBranches) and !isset($projectBranches[$story->branch])))
|
||||
{
|
||||
$count++;
|
||||
unset($planStory[$id]);
|
||||
@@ -4156,7 +4177,7 @@ class execution extends control
|
||||
}
|
||||
|
||||
$haveDraft = sprintf($this->lang->execution->haveDraft, $count);
|
||||
if(!$execution->multiple) $haveDraft = str_replace($this->lang->executionCommon, $this->lang->projectCommon, $haveDraft);
|
||||
if(!$execution->multiple or $moduleName == 'projectstory') $haveDraft = str_replace($this->lang->executionCommon, $this->lang->projectCommon, $haveDraft);
|
||||
if($count != 0) echo js::alert($haveDraft) . js::locate($this->createLink($moduleName, $fromMethod, $param));
|
||||
return print(js::locate(helper::createLink($moduleName, $fromMethod, $param)));
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
.chosen-container.chosen-highlight-selected .disabled-result.result-selected {background: none;}
|
||||
|
||||
#productsBox .row .col-sm-4 {padding-right: 13px;}
|
||||
#plansBox .row {display: inline-table; width: 102%;}
|
||||
#plansBox .row .col-sm-4 {display: inline-block; float: none; padding-right: 13px;}
|
||||
.productsBox .row .col-sm-4 {padding-right: 13px;}
|
||||
.plansBox .row {display: inline-table; width: 102%;}
|
||||
.plansBox .row .col-sm-4 {display: inline-block; float: none; padding-right: 13px;}
|
||||
|
||||
#dateRange {vertical-align: top; padding-top: 13px;}
|
||||
#dateRangeOption {vertical-align: top; padding-top: 13px;}
|
||||
@@ -32,3 +32,9 @@
|
||||
#copyProjectModal .projectSelect {display: inline-block; margin-left: 10px; width: 20%;}
|
||||
#copyProjectModal .titleBox{position: relative; bottom: 3px; display: inline-block;}
|
||||
a.chosen-single > div > b {top: -4px !important;}
|
||||
|
||||
.productsBox a {border-radius: 2px !important;}
|
||||
.productsBox .required:after {right: -9.5px !important;}
|
||||
.productsBox .input-group-addon > div {display: inline-block !important;}
|
||||
.productsBox .required + .text-danger.help-text {position: relative; left: 10px;}
|
||||
.productsBox > #productNameLabel {padding-top: 8px;}
|
||||
|
||||
@@ -113,6 +113,15 @@ function computeEndDate(delta)
|
||||
computeWorkDays();
|
||||
}
|
||||
|
||||
/* Auto compute the work days. */
|
||||
$(function()
|
||||
{
|
||||
$(".date").bind('dateSelected', function()
|
||||
{
|
||||
computeWorkDays(this.id);
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* Load branches.
|
||||
*
|
||||
@@ -122,118 +131,127 @@ function computeEndDate(delta)
|
||||
*/
|
||||
function loadBranches(product)
|
||||
{
|
||||
if($('#productsBox .input-group:last select:first').val() != 0)
|
||||
/* When selecting a product, delete a plan that is empty by default. */
|
||||
$("#planDefault").remove();
|
||||
|
||||
$(".productsBox select[name^='products']").each(function()
|
||||
{
|
||||
if(model !== 'waterfall')
|
||||
var $product = $(product);
|
||||
var productID = $(this).val();
|
||||
if($product.val() != 0 && $product.val() == $(this).val() && $product.attr('id') != $(this).attr('id') && !multiBranchProducts[$product.val()])
|
||||
{
|
||||
var length = $('#productsBox .input-group').size();
|
||||
$('#productsBox .row').append('<div class="col-sm-4">' + $('#productsBox .col-sm-4:last').html().replace('required', '') + '</div>');
|
||||
if($('#productsBox .input-group:last select').size() >= 2) $('#productsBox .input-group:last select:last').remove();
|
||||
$('#productsBox .input-group:last .chosen-container').remove();
|
||||
$('#productsBox .input-group:last select:first').attr('name', 'products[' + length + ']').attr('id', 'products' + length);
|
||||
$('#productsBox .input-group:last .chosen').chosen();
|
||||
bootbox.alert(errorSameProducts);
|
||||
$product.val(0);
|
||||
$product.trigger("chosen:updated");
|
||||
return false;
|
||||
}
|
||||
adjustProductBoxMargin();
|
||||
});
|
||||
|
||||
var $tableRow = $(product).closest('.table-row');
|
||||
var index = $tableRow.find('select:first').attr('id').replace('products' , '');
|
||||
var oldBranch = $(product).attr('data-branch') !== undefined ? $(product).attr('data-branch') : 0;
|
||||
if($(product).val() != 0)
|
||||
{
|
||||
$(product).closest('tr').find('.newProduct').addClass('hidden')
|
||||
}
|
||||
else
|
||||
{
|
||||
$(product).closest('tr').find('.newProduct').removeClass('hidden')
|
||||
}
|
||||
|
||||
var $inputgroup = $(product).closest('.input-group');
|
||||
if($inputgroup.find('select').size() >= 2) $inputgroup.removeClass('has-branch').find('select:last').remove();
|
||||
if($inputgroup.find('.chosen-container').size() >= 2) $inputgroup.find('.chosen-container:last').remove();
|
||||
if(!multiBranchProducts[$(product).val()])
|
||||
{
|
||||
$tableRow.find('.table-col:last select').val('').trigger('chosen:updated');
|
||||
$tableRow.find('.table-col:last').addClass('hidden');
|
||||
}
|
||||
|
||||
var projectID = $('#project').val();
|
||||
if(typeof(projectID) == 'undefined') projectID = 0;
|
||||
|
||||
var index = $inputgroup.find('select:first').attr('id').replace('products' , '');
|
||||
$.get(createLink('branch', 'ajaxGetBranches', "productID=" + $(product).val() + "&oldBranch=0¶m=active&projectID=" + projectID + "&withMainBranch=true"), function(data)
|
||||
$.get(createLink('branch', 'ajaxGetBranches', "productID=" + $(product).val() + "&oldBranch=" + oldBranch + "¶m=active&projectID=" + projectID + "&withMainBranch=true"), function(data)
|
||||
{
|
||||
if(data)
|
||||
{
|
||||
$inputgroup.addClass('has-branch').append(data);
|
||||
$inputgroup.find('select:last').attr('name', 'branch[' + index + ']').attr('id', 'branch' + index).attr('onchange', "loadPlans('#products" + index + "', this.value)").chosen();
|
||||
$tableRow.find("select[name^='branch']").replaceWith(data);
|
||||
$tableRow.find('.table-col:last .chosen-container').remove();
|
||||
$tableRow.find('.table-col:last').removeClass('hidden');
|
||||
$tableRow.find("select[name^='branch']").attr('multiple', '').attr('name', 'branch[' + index + '][]').attr('id', 'branch' + index).attr('onchange', "loadPlans('#products" + index + "', this)").chosen();
|
||||
|
||||
$inputgroup.find('select:last').each(disableSelectedBranch);
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
var branchID = $('#branch' + index).val();
|
||||
loadPlans(product, branchID);
|
||||
var branch = $('#branch' + index);
|
||||
loadPlans(product, branch);
|
||||
});
|
||||
|
||||
if(!multiBranchProducts[$(product).val()]) disableSelectedProduct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plans by product id.
|
||||
* Load plans.
|
||||
*
|
||||
* @param int $product
|
||||
* @param int $branchID
|
||||
* @param obj $product
|
||||
* @param obj $branchID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadPlans(product, branchID)
|
||||
function loadPlans(product, branch)
|
||||
{
|
||||
if($('#plansBox').size() == 0) return false;
|
||||
|
||||
var productID = $(product).val();
|
||||
var branchID = typeof(branchID) == 'undefined' ? 0 : branchID;
|
||||
var branchID = $(branch).val() == null ? 0 : '0,' + $(branch).val();
|
||||
var planID = $(product).attr('data-plan') !== undefined ? $(product).attr('data-plan') : 0;
|
||||
var index = $(product).attr('id').replace('products', '');
|
||||
|
||||
$.get(createLink('product', 'ajaxGetPlans', "productID=" + productID + '&branch=0,' + branchID + '&planID=0&fieldID&needCreate=&expired=noclosed,unexpired¶m=skipParent,multiple'), function(data)
|
||||
$.get(createLink('product', 'ajaxGetPlans', "productID=" + productID + '&branch=' + branchID + '&planID=' + planID + '&fieldID&needCreate=&expired=unexpired,noclosed¶m=skipParent,multiple'), function(data)
|
||||
{
|
||||
if(data)
|
||||
{
|
||||
if($("div#plan" + index).size() == 0) $("#plansBox .row").append('<div class="col-sm-4" id="plan' + index + '"></div>');
|
||||
$("div#plan" + index).html(data).find('select').attr('name', 'plans[' + productID + '][' + branchID + '][]').attr('id', 'plans' + productID).chosen();
|
||||
|
||||
adjustPlanBoxMargin();
|
||||
$("div#plan" + index).find("select[name^='plans']").replaceWith(data);
|
||||
$("div#plan" + index).find('.chosen-container').remove();
|
||||
$("div#plan" + index).find('select').attr('name', 'plans[' + productID + ']' + '[]').attr('id', 'plans' + productID).chosen();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust product box margin.
|
||||
*
|
||||
* Add new line for link product.
|
||||
*
|
||||
* @param obj $obj
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function adjustProductBoxMargin()
|
||||
function addNewLine(obj)
|
||||
{
|
||||
var productRows = Math.ceil($('#productsBox > .row > .col-sm-4').length / 3);
|
||||
if(productRows > 1)
|
||||
var newLine = $(obj).closest('tr').clone();
|
||||
var index = 0;
|
||||
$(".productsBox select[name^='products']").each(function()
|
||||
{
|
||||
for(i = 1; i <= productRows - 1; i++)
|
||||
{
|
||||
$('#productsBox .col-sm-4:lt(' + (i * 3) + ')').css('margin-bottom', '10px');
|
||||
}
|
||||
}
|
||||
}
|
||||
var id = $(this).attr('id').replace('products' , '');
|
||||
|
||||
/**
|
||||
* Adjust plan box margin.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function adjustPlanBoxMargin()
|
||||
{
|
||||
var planRows = Math.ceil($('#plansBox > .row > .col-sm-4').length / 3);
|
||||
if(planRows > 1)
|
||||
{
|
||||
for(j = 1; j <= planRows - 1; j++)
|
||||
{
|
||||
$('#plansBox .col-sm-4:lt(' + (j * 3) + ')').css('margin-bottom', '10px');
|
||||
}
|
||||
}
|
||||
}
|
||||
id = parseInt(id);
|
||||
id ++;
|
||||
|
||||
/* Auto compute the work days. */
|
||||
$(function()
|
||||
{
|
||||
$(".date").bind('dateSelected', function()
|
||||
{
|
||||
computeWorkDays(this.id);
|
||||
index = id > index ? id : index;
|
||||
})
|
||||
});
|
||||
|
||||
newLine.find('.newProduct').remove();
|
||||
newLine.find('.addProduct').remove();
|
||||
newLine.addClass('newLine');
|
||||
newLine.find('th').html('');
|
||||
newLine.find('.removeLine').css('visibility', 'visible');
|
||||
newLine.find('.chosen-container').remove();
|
||||
newLine.find('.productsBox .table-col:last').addClass('hidden');
|
||||
newLine.find("select[name^='products']").attr('name', 'products[' + index + ']').attr('id', 'products' + index).val('').chosen();
|
||||
newLine.find("select[name^='plans']").attr('name', 'plans[' + index + '][' + 0 + '][]').chosen();
|
||||
newLine.find("div[id^='plan']").attr('id', 'plan' + index);
|
||||
|
||||
$(obj).closest('tr').after(newLine);
|
||||
var product = newLine.find("select[name^='products']");
|
||||
var branch = newLine.find("select[name^='branch']");
|
||||
loadPlans(product, branch);
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
function removeLine(obj)
|
||||
{
|
||||
$(obj).closest('tr').remove();
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
$(function()
|
||||
{
|
||||
@@ -247,9 +265,6 @@ $(function()
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
adjustProductBoxMargin();
|
||||
adjustPlanBoxMargin();
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,82 +209,84 @@ $lang->execution->burnByList['estimate'] = "View by plan hours";
|
||||
$lang->execution->burnByList['storyPoint'] = 'View by story point';
|
||||
|
||||
/* Method list. */
|
||||
$lang->execution->index = "Home";
|
||||
$lang->execution->task = 'Aufgaben';
|
||||
$lang->execution->groupTask = 'Nach Gruppen';
|
||||
$lang->execution->story = 'Storys';
|
||||
$lang->execution->qa = 'QA';
|
||||
$lang->execution->bug = 'Bugs';
|
||||
$lang->execution->testcase = 'Testcase List';
|
||||
$lang->execution->dynamic = 'Verlauf';
|
||||
$lang->execution->latestDynamic = 'Letzter Verlauf';
|
||||
$lang->execution->build = 'Builds';
|
||||
$lang->execution->testtask = 'Testaufgaben';
|
||||
$lang->execution->burn = 'Burndown';
|
||||
$lang->execution->computeBurn = 'Aktualisieren';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = 'Burndown Daten';
|
||||
$lang->execution->fixFirst = 'Bearbeite Mannstunden des ersten Tags';
|
||||
$lang->execution->team = 'Teammitglieder';
|
||||
$lang->execution->doc = 'Dok';
|
||||
$lang->execution->doclib = 'Dok Bibliothek';
|
||||
$lang->execution->manageProducts = 'Verküpfe ' . $lang->productCommon;
|
||||
$lang->execution->linkStory = 'Link Stories';
|
||||
$lang->execution->linkStoryByPlan = 'Verküpfe Story aus Plan';
|
||||
$lang->execution->linkPlan = 'Verküpfe Plan';
|
||||
$lang->execution->unlinkStoryTasks = 'Verknüpfung aufheben';
|
||||
$lang->execution->linkedProducts = 'Verküpfte Produkte';
|
||||
$lang->execution->unlinkedProducts = 'Produkt verknüpfung aufheben';
|
||||
$lang->execution->view = "Übersicht";
|
||||
$lang->execution->startAction = "Start Execution";
|
||||
$lang->execution->activateAction = "Activate Execution";
|
||||
$lang->execution->delayAction = "Delay Execution";
|
||||
$lang->execution->suspendAction = "Suspend Execution";
|
||||
$lang->execution->closeAction = "Close Execution";
|
||||
$lang->execution->testtaskAction = "Execution Request";
|
||||
$lang->execution->teamAction = "Execution Members";
|
||||
$lang->execution->kanbanAction = "Execution Kanban";
|
||||
$lang->execution->printKanbanAction = "Print Kanban";
|
||||
$lang->execution->treeAction = "Execution Tree View";
|
||||
$lang->execution->exportAction = "Export Execution";
|
||||
$lang->execution->computeBurnAction = "Compute Burn";
|
||||
$lang->execution->create = "Erstelle Projekt";
|
||||
$lang->execution->createExec = "Create Execution";
|
||||
$lang->execution->createAction = "Create {$lang->execution->common}";
|
||||
$lang->execution->copyExec = "Copy Execution";
|
||||
$lang->execution->copy = "Kopiere {$lang->executionCommon}";
|
||||
$lang->execution->delete = "Lösche";
|
||||
$lang->execution->deleteAB = "Lösche Execution";
|
||||
$lang->execution->browse = "Durchsuchen";
|
||||
$lang->execution->edit = "Bearbeiten";
|
||||
$lang->execution->editAction = "Edit Execution";
|
||||
$lang->execution->batchEdit = "Mehere bearbeiten";
|
||||
$lang->execution->batchEditAction = "Batch Edit";
|
||||
$lang->execution->manageMembers = 'Teams verwalten';
|
||||
$lang->execution->unlinkMember = 'Mitgliefer entfernen';
|
||||
$lang->execution->unlinkStory = 'Story entfernen';
|
||||
$lang->execution->unlinkStoryAB = 'Unlink';
|
||||
$lang->execution->batchUnlinkStory = 'Mehere Storys entfernen';
|
||||
$lang->execution->importTask = 'Importiere Aufgaben';
|
||||
$lang->execution->importPlanStories = 'Verknüpfe Story aus Plan';
|
||||
$lang->execution->importBug = 'Importiere Bugs';
|
||||
$lang->execution->tree = 'Baum';
|
||||
$lang->execution->treeTask = 'Aufgabe anzeigen';
|
||||
$lang->execution->treeStory = 'Story anzeigen';
|
||||
$lang->execution->treeViewTask = 'Tree View Task';
|
||||
$lang->execution->treeViewStory = 'Tree View Story';
|
||||
$lang->execution->storyKanban = 'Story Kanban';
|
||||
$lang->execution->storySort = 'Story sortieren';
|
||||
$lang->execution->importPlanStory = '' . $lang->executionCommon . ' wurde erstellt!\nMöchten Sie Storys aus dem Plan importieren?';
|
||||
$lang->execution->importEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->needLinkProducts = 'The execution has not been linked with any product, and the related functions cannot be used. Please link the product first and try again.';
|
||||
$lang->execution->iteration = 'Iteration';
|
||||
$lang->execution->iterationInfo = '%s Iterationen';
|
||||
$lang->execution->viewAll = 'Alle anzeigen';
|
||||
$lang->execution->testreport = 'Test Report';
|
||||
$lang->execution->taskKanban = 'Task Kanban';
|
||||
$lang->execution->RDKanban = 'Research & Development Kanban';
|
||||
$lang->execution->index = "Home";
|
||||
$lang->execution->task = 'Aufgaben';
|
||||
$lang->execution->groupTask = 'Nach Gruppen';
|
||||
$lang->execution->story = 'Storys';
|
||||
$lang->execution->qa = 'QA';
|
||||
$lang->execution->bug = 'Bugs';
|
||||
$lang->execution->testcase = 'Testcase List';
|
||||
$lang->execution->dynamic = 'Verlauf';
|
||||
$lang->execution->latestDynamic = 'Letzter Verlauf';
|
||||
$lang->execution->build = 'Builds';
|
||||
$lang->execution->testtask = 'Testaufgaben';
|
||||
$lang->execution->burn = 'Burndown';
|
||||
$lang->execution->computeBurn = 'Aktualisieren';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = 'Burndown Daten';
|
||||
$lang->execution->fixFirst = 'Bearbeite Mannstunden des ersten Tags';
|
||||
$lang->execution->team = 'Teammitglieder';
|
||||
$lang->execution->doc = 'Dok';
|
||||
$lang->execution->doclib = 'Dok Bibliothek';
|
||||
$lang->execution->manageProducts = 'Verküpfe ' . $lang->productCommon;
|
||||
$lang->execution->linkStory = 'Link Stories';
|
||||
$lang->execution->linkStoryByPlan = 'Verküpfe Story aus Plan';
|
||||
$lang->execution->linkPlan = 'Verküpfe Plan';
|
||||
$lang->execution->unlinkStoryTasks = 'Verknüpfung aufheben';
|
||||
$lang->execution->linkedProducts = 'Verküpfte Produkte';
|
||||
$lang->execution->unlinkedProducts = 'Produkt verknüpfung aufheben';
|
||||
$lang->execution->view = "Übersicht";
|
||||
$lang->execution->startAction = "Start Execution";
|
||||
$lang->execution->activateAction = "Activate Execution";
|
||||
$lang->execution->delayAction = "Delay Execution";
|
||||
$lang->execution->suspendAction = "Suspend Execution";
|
||||
$lang->execution->closeAction = "Close Execution";
|
||||
$lang->execution->testtaskAction = "Execution Request";
|
||||
$lang->execution->teamAction = "Execution Members";
|
||||
$lang->execution->kanbanAction = "Execution Kanban";
|
||||
$lang->execution->printKanbanAction = "Print Kanban";
|
||||
$lang->execution->treeAction = "Execution Tree View";
|
||||
$lang->execution->exportAction = "Export Execution";
|
||||
$lang->execution->computeBurnAction = "Compute Burn";
|
||||
$lang->execution->create = "Erstelle Projekt";
|
||||
$lang->execution->createExec = "Create Execution";
|
||||
$lang->execution->createAction = "Create {$lang->execution->common}";
|
||||
$lang->execution->copyExec = "Copy Execution";
|
||||
$lang->execution->copy = "Kopiere {$lang->executionCommon}";
|
||||
$lang->execution->delete = "Lösche";
|
||||
$lang->execution->deleteAB = "Lösche Execution";
|
||||
$lang->execution->browse = "Durchsuchen";
|
||||
$lang->execution->edit = "Bearbeiten";
|
||||
$lang->execution->editAction = "Edit Execution";
|
||||
$lang->execution->batchEdit = "Mehere bearbeiten";
|
||||
$lang->execution->batchEditAction = "Batch Edit";
|
||||
$lang->execution->manageMembers = 'Teams verwalten';
|
||||
$lang->execution->unlinkMember = 'Mitgliefer entfernen';
|
||||
$lang->execution->unlinkStory = 'Story entfernen';
|
||||
$lang->execution->unlinkStoryAB = 'Unlink';
|
||||
$lang->execution->batchUnlinkStory = 'Mehere Storys entfernen';
|
||||
$lang->execution->importTask = 'Importiere Aufgaben';
|
||||
$lang->execution->importPlanStories = 'Verknüpfe Story aus Plan';
|
||||
$lang->execution->importBug = 'Importiere Bugs';
|
||||
$lang->execution->tree = 'Baum';
|
||||
$lang->execution->treeTask = 'Aufgabe anzeigen';
|
||||
$lang->execution->treeStory = 'Story anzeigen';
|
||||
$lang->execution->treeViewTask = 'Tree View Task';
|
||||
$lang->execution->treeViewStory = 'Tree View Story';
|
||||
$lang->execution->storyKanban = 'Story Kanban';
|
||||
$lang->execution->storySort = 'Story sortieren';
|
||||
$lang->execution->importPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->importEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->importBranchPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this ' .$lang->executionCommon. ' will be associated with the import';
|
||||
$lang->execution->importBranchEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this ' .$lang->executionCommon. ' will be associated with the import';
|
||||
$lang->execution->needLinkProducts = 'The execution has not been linked with any product, and the related functions cannot be used. Please link the product first and try again.';
|
||||
$lang->execution->iteration = 'Iteration';
|
||||
$lang->execution->iterationInfo = '%s Iterationen';
|
||||
$lang->execution->viewAll = 'Alle anzeigen';
|
||||
$lang->execution->testreport = 'Test Report';
|
||||
$lang->execution->taskKanban = 'Task Kanban';
|
||||
$lang->execution->RDKanban = 'Research & Development Kanban';
|
||||
|
||||
/* Group browsing. */
|
||||
$lang->execution->allTasks = 'Alle';
|
||||
@@ -349,7 +351,7 @@ $lang->execution->howToUpdateBurn = "<a href='https://api.zentao.pm/goto.ph
|
||||
$lang->execution->whyNoStories = "Keine Story kann verknüpft werden. Bitte prüfen Sie ob ein Story mit {$lang->executionCommon} verknüpft ist {$lang->productCommon} und stellen Sie sicher das diese geprüft ist.";
|
||||
$lang->execution->projectNoStories = "No story can be linked. Please check whether there is any story in project and make sure it has been reviewed.";
|
||||
$lang->execution->productStories = "{$lang->executionCommon} verknüpfte Story ist ein Subset von {$lang->productCommon}, welche nur nach überprüfung verknüpft werden kann. Bitte <a href='%s'> Story verknüpfen</a>。";
|
||||
$lang->execution->haveDraft = "There are %s draft stories can't be linked.";
|
||||
$lang->execution->haveDraft = "There are %s draft stories or not associated with this {$lang->executionCommon} can't be linked.";
|
||||
$lang->execution->doneExecutions = 'Erledigt';
|
||||
$lang->execution->selectDept = 'Abteilung wählen';
|
||||
$lang->execution->selectDeptTitle = 'Abteilung wählen';
|
||||
|
||||
@@ -209,82 +209,84 @@ $lang->execution->burnByList['estimate'] = "View by plan hours";
|
||||
$lang->execution->burnByList['storyPoint'] = 'View by story point';
|
||||
|
||||
/* Method list. */
|
||||
$lang->execution->index = "{$lang->executionCommon} Home";
|
||||
$lang->execution->task = 'Task List';
|
||||
$lang->execution->groupTask = 'Group View';
|
||||
$lang->execution->story = 'Story List';
|
||||
$lang->execution->qa = 'QA';
|
||||
$lang->execution->bug = 'Bug List';
|
||||
$lang->execution->testcase = 'Testcase List';
|
||||
$lang->execution->dynamic = 'Dynamics';
|
||||
$lang->execution->latestDynamic = 'Dynamics';
|
||||
$lang->execution->build = 'Build List';
|
||||
$lang->execution->testtask = 'Request';
|
||||
$lang->execution->burn = 'Burndown';
|
||||
$lang->execution->computeBurn = 'Update';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = 'Burndown Data';
|
||||
$lang->execution->fixFirst = 'Edit 1st-Day Estimates';
|
||||
$lang->execution->team = 'Members';
|
||||
$lang->execution->doc = 'Document';
|
||||
$lang->execution->doclib = 'Docoment Library';
|
||||
$lang->execution->manageProducts = 'Linked ' . $lang->productCommon . 's';
|
||||
$lang->execution->linkStory = 'Link Stories';
|
||||
$lang->execution->linkStoryByPlan = 'Link Stories By Plan';
|
||||
$lang->execution->linkPlan = 'Linked Plan';
|
||||
$lang->execution->unlinkStoryTasks = 'Unlink';
|
||||
$lang->execution->linkedProducts = "Linked {$lang->productCommon}s";
|
||||
$lang->execution->unlinkedProducts = "Unlinked {$lang->productCommon}s";
|
||||
$lang->execution->view = "Execution Detail";
|
||||
$lang->execution->startAction = "Start Execution";
|
||||
$lang->execution->activateAction = "Activate Execution";
|
||||
$lang->execution->delayAction = "Delay Execution";
|
||||
$lang->execution->suspendAction = "Suspend Execution";
|
||||
$lang->execution->closeAction = "Close Execution";
|
||||
$lang->execution->testtaskAction = "Execution Request";
|
||||
$lang->execution->teamAction = "Execution Members";
|
||||
$lang->execution->kanbanAction = "Execution Kanban";
|
||||
$lang->execution->printKanbanAction = "Print Kanban";
|
||||
$lang->execution->treeAction = "Execution Tree View";
|
||||
$lang->execution->exportAction = "Export Execution";
|
||||
$lang->execution->computeBurnAction = "Update Burndown";
|
||||
$lang->execution->create = "Create {$lang->executionCommon}";
|
||||
$lang->execution->createExec = "Create {$lang->execution->common}";
|
||||
$lang->execution->createAction = "Create {$lang->execution->common}";
|
||||
$lang->execution->copyExec = "Copy {$lang->execution->common}";
|
||||
$lang->execution->copy = "Copy {$lang->executionCommon}";
|
||||
$lang->execution->delete = "Delete {$lang->executionCommon}";
|
||||
$lang->execution->deleteAB = "Delete Execution";
|
||||
$lang->execution->browse = "{$lang->executionCommon} List";
|
||||
$lang->execution->edit = "Edit {$lang->executionCommon}";
|
||||
$lang->execution->editAction = "Edit Execution";
|
||||
$lang->execution->batchEdit = "Edit";
|
||||
$lang->execution->batchEditAction = "Batch Edit";
|
||||
$lang->execution->manageMembers = 'Manage Team';
|
||||
$lang->execution->unlinkMember = 'Remove Member';
|
||||
$lang->execution->unlinkStory = 'Unlink Story';
|
||||
$lang->execution->unlinkStoryAB = 'Unlink';
|
||||
$lang->execution->batchUnlinkStory = 'Batch Unlink Stories';
|
||||
$lang->execution->importTask = 'Transfer Task';
|
||||
$lang->execution->importPlanStories = 'Link Stories By Plan';
|
||||
$lang->execution->importBug = 'Import Bug';
|
||||
$lang->execution->tree = 'Tree';
|
||||
$lang->execution->treeTask = 'Show Task Only';
|
||||
$lang->execution->treeStory = 'Show Story Only';
|
||||
$lang->execution->treeViewTask = 'Tree View Task';
|
||||
$lang->execution->treeViewStory = 'Tree View Story';
|
||||
$lang->execution->storyKanban = 'Story Kanban';
|
||||
$lang->execution->storySort = 'Rank Story';
|
||||
$lang->execution->importPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->importEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->needLinkProducts = 'The execution has not been linked with any product, and the related functions cannot be used. Please link the product first and try again.';
|
||||
$lang->execution->iteration = 'Iterations';
|
||||
$lang->execution->iterationInfo = '%s Iterations';
|
||||
$lang->execution->viewAll = 'View All';
|
||||
$lang->execution->testreport = 'Test Report';
|
||||
$lang->execution->taskKanban = 'Task Kanban';
|
||||
$lang->execution->RDKanban = 'Research & Development Kanban';
|
||||
$lang->execution->index = "{$lang->executionCommon} Home";
|
||||
$lang->execution->task = 'Task List';
|
||||
$lang->execution->groupTask = 'Group View';
|
||||
$lang->execution->story = 'Story List';
|
||||
$lang->execution->qa = 'QA';
|
||||
$lang->execution->bug = 'Bug List';
|
||||
$lang->execution->testcase = 'Testcase List';
|
||||
$lang->execution->dynamic = 'Dynamics';
|
||||
$lang->execution->latestDynamic = 'Dynamics';
|
||||
$lang->execution->build = 'Build List';
|
||||
$lang->execution->testtask = 'Request';
|
||||
$lang->execution->burn = 'Burndown';
|
||||
$lang->execution->computeBurn = 'Update';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = 'Burndown Data';
|
||||
$lang->execution->fixFirst = 'Edit 1st-Day Estimates';
|
||||
$lang->execution->team = 'Members';
|
||||
$lang->execution->doc = 'Document';
|
||||
$lang->execution->doclib = 'Docoment Library';
|
||||
$lang->execution->manageProducts = 'Linked ' . $lang->productCommon . 's';
|
||||
$lang->execution->linkStory = 'Link Stories';
|
||||
$lang->execution->linkStoryByPlan = 'Link Stories By Plan';
|
||||
$lang->execution->linkPlan = 'Linked Plan';
|
||||
$lang->execution->unlinkStoryTasks = 'Unlink';
|
||||
$lang->execution->linkedProducts = "Linked {$lang->productCommon}s";
|
||||
$lang->execution->unlinkedProducts = "Unlinked {$lang->productCommon}s";
|
||||
$lang->execution->view = "Execution Detail";
|
||||
$lang->execution->startAction = "Start Execution";
|
||||
$lang->execution->activateAction = "Activate Execution";
|
||||
$lang->execution->delayAction = "Delay Execution";
|
||||
$lang->execution->suspendAction = "Suspend Execution";
|
||||
$lang->execution->closeAction = "Close Execution";
|
||||
$lang->execution->testtaskAction = "Execution Request";
|
||||
$lang->execution->teamAction = "Execution Members";
|
||||
$lang->execution->kanbanAction = "Execution Kanban";
|
||||
$lang->execution->printKanbanAction = "Print Kanban";
|
||||
$lang->execution->treeAction = "Execution Tree View";
|
||||
$lang->execution->exportAction = "Export Execution";
|
||||
$lang->execution->computeBurnAction = "Update Burndown";
|
||||
$lang->execution->create = "Create {$lang->executionCommon}";
|
||||
$lang->execution->createExec = "Create {$lang->execution->common}";
|
||||
$lang->execution->createAction = "Create {$lang->execution->common}";
|
||||
$lang->execution->copyExec = "Copy {$lang->execution->common}";
|
||||
$lang->execution->copy = "Copy {$lang->executionCommon}";
|
||||
$lang->execution->delete = "Delete {$lang->executionCommon}";
|
||||
$lang->execution->deleteAB = "Delete Execution";
|
||||
$lang->execution->browse = "{$lang->executionCommon} List";
|
||||
$lang->execution->edit = "Edit {$lang->executionCommon}";
|
||||
$lang->execution->editAction = "Edit Execution";
|
||||
$lang->execution->batchEdit = "Edit";
|
||||
$lang->execution->batchEditAction = "Batch Edit";
|
||||
$lang->execution->manageMembers = 'Manage Team';
|
||||
$lang->execution->unlinkMember = 'Remove Member';
|
||||
$lang->execution->unlinkStory = 'Unlink Story';
|
||||
$lang->execution->unlinkStoryAB = 'Unlink';
|
||||
$lang->execution->batchUnlinkStory = 'Batch Unlink Stories';
|
||||
$lang->execution->importTask = 'Transfer Task';
|
||||
$lang->execution->importPlanStories = 'Link Stories By Plan';
|
||||
$lang->execution->importBug = 'Import Bug';
|
||||
$lang->execution->tree = 'Tree';
|
||||
$lang->execution->treeTask = 'Show Task Only';
|
||||
$lang->execution->treeStory = 'Show Story Only';
|
||||
$lang->execution->treeViewTask = 'Tree View Task';
|
||||
$lang->execution->treeViewStory = 'Tree View Story';
|
||||
$lang->execution->storyKanban = 'Story Kanban';
|
||||
$lang->execution->storySort = 'Rank Story';
|
||||
$lang->execution->importPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->importEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->importBranchPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this ' .$lang->executionCommon. ' will be associated with the import';
|
||||
$lang->execution->importBranchEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this ' .$lang->executionCommon. ' will be associated with the import';
|
||||
$lang->execution->needLinkProducts = 'The execution has not been linked with any product, and the related functions cannot be used. Please link the product first and try again.';
|
||||
$lang->execution->iteration = 'Iterations';
|
||||
$lang->execution->iterationInfo = '%s Iterations';
|
||||
$lang->execution->viewAll = 'View All';
|
||||
$lang->execution->testreport = 'Test Report';
|
||||
$lang->execution->taskKanban = 'Task Kanban';
|
||||
$lang->execution->RDKanban = 'Research & Development Kanban';
|
||||
|
||||
/* Group browsing. */
|
||||
$lang->execution->allTasks = 'All';
|
||||
@@ -349,7 +351,7 @@ $lang->execution->howToUpdateBurn = "<a href='https://api.zentao.pm/goto.ph
|
||||
$lang->execution->whyNoStories = "No story can be linked. Please check whether there is any story in {$lang->executionCommon} which is linked to {$lang->productCommon} and make sure it has been reviewed.";
|
||||
$lang->execution->projectNoStories = "No story can be linked. Please check whether there is any story in project and make sure it has been reviewed.";
|
||||
$lang->execution->productStories = "Stories linked to {$lang->executionCommon} are the subeset of stories linked to {$lang->productCommon}. Stories can only be linked after they pass the review. <a href='%s'> Link Stories</a> now.";
|
||||
$lang->execution->haveDraft = "%s stories in draft, so they can't be linked.";
|
||||
$lang->execution->haveDraft = "There are %s draft stories or not associated with this {$lang->executionCommon} can't be linked.";
|
||||
$lang->execution->doneExecutions = 'Finished';
|
||||
$lang->execution->selectDept = 'Select Department';
|
||||
$lang->execution->selectDeptTitle = 'Select User';
|
||||
|
||||
@@ -209,82 +209,84 @@ $lang->execution->burnByList['estimate'] = "View by plan hours";
|
||||
$lang->execution->burnByList['storyPoint'] = 'View by story point';
|
||||
|
||||
/* Method list. */
|
||||
$lang->execution->index = "Accueil {$lang->executionCommon}";
|
||||
$lang->execution->task = 'Liste Tâches';
|
||||
$lang->execution->groupTask = 'Vision Groupée';
|
||||
$lang->execution->story = 'Liste Stories';
|
||||
$lang->execution->qa = 'QA';
|
||||
$lang->execution->bug = 'Liste Bugs';
|
||||
$lang->execution->testcase = 'Testcase List';
|
||||
$lang->execution->dynamic = 'Historique';
|
||||
$lang->execution->latestDynamic = 'Historique';
|
||||
$lang->execution->build = 'Liste Builds';
|
||||
$lang->execution->testtask = 'Recette';
|
||||
$lang->execution->burn = ' Atterrissage';
|
||||
$lang->execution->computeBurn = 'Calculer';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = "Données d'atterrissage";
|
||||
$lang->execution->fixFirst = 'Fixer 1er-Jour Estimation';
|
||||
$lang->execution->team = 'Membres';
|
||||
$lang->execution->doc = 'Documents';
|
||||
$lang->execution->doclib = 'Répertoire de Documents';
|
||||
$lang->execution->manageProducts = 'Liaisons du ' . $lang->executionCommon . ' avec les ' . $lang->productCommon . 's';
|
||||
$lang->execution->linkStory = 'Stories liées';
|
||||
$lang->execution->linkStoryByPlan = 'Stories liées par Plan';
|
||||
$lang->execution->linkPlan = 'Plans liés';
|
||||
$lang->execution->unlinkStoryTasks = 'Dissocier';
|
||||
$lang->execution->linkedProducts = "{$lang->productCommon}s liés à ce {$lang->executionCommon}";
|
||||
$lang->execution->unlinkedProducts = "{$lang->productCommon}s dissociés de ce {$lang->executionCommon}";
|
||||
$lang->execution->view = "Détail du Execution";
|
||||
$lang->execution->startAction = "Start Execution";
|
||||
$lang->execution->activateAction = "Activer le Execution";
|
||||
$lang->execution->delayAction = "Ajourner le Execution";
|
||||
$lang->execution->suspendAction = "Suspendre le Execution";
|
||||
$lang->execution->closeAction = "Fermer le Execution";
|
||||
$lang->execution->testtaskAction = "Recettes du Execution";
|
||||
$lang->execution->teamAction = "Membres du Execution";
|
||||
$lang->execution->kanbanAction = "Kaban Execution";
|
||||
$lang->execution->printKanbanAction = "Imprimer le Kanban";
|
||||
$lang->execution->treeAction = "Arborescence Execution";
|
||||
$lang->execution->exportAction = "Exporter Execution";
|
||||
$lang->execution->computeBurnAction = "Calculer Atterrissage";
|
||||
$lang->execution->create = "Créer {$lang->executionCommon}";
|
||||
$lang->execution->createExec = "Create Execution";
|
||||
$lang->execution->createAction = "Create {$lang->execution->common}";
|
||||
$lang->execution->copyExec = "Copy Execution";
|
||||
$lang->execution->copy = "Copier {$lang->executionCommon}";
|
||||
$lang->execution->delete = "Supprimer {$lang->executionCommon}";
|
||||
$lang->execution->deleteAB = "Delete Execution";
|
||||
$lang->execution->browse = "Liste du {$lang->executionCommon}";
|
||||
$lang->execution->edit = "Editer {$lang->executionCommon}";
|
||||
$lang->execution->editAction = "Edit Execution";
|
||||
$lang->execution->batchEdit = "Edition par lot";
|
||||
$lang->execution->batchEditAction = "Batch Edit";
|
||||
$lang->execution->manageMembers = 'Organiser Equipe';
|
||||
$lang->execution->unlinkMember = 'Retirer le membre';
|
||||
$lang->execution->unlinkStory = 'Dissocier Story';
|
||||
$lang->execution->unlinkStoryAB = 'Dissocier';
|
||||
$lang->execution->batchUnlinkStory = 'Dissocier Stories par lot';
|
||||
$lang->execution->importTask = 'Transfert Tâche';
|
||||
$lang->execution->importPlanStories = 'Lier Stories Par Plan';
|
||||
$lang->execution->importBug = 'Importer Bug';
|
||||
$lang->execution->tree = 'Arboressence';
|
||||
$lang->execution->treeTask = 'Seulement les Tâches';
|
||||
$lang->execution->treeStory = 'Seulement les Stories';
|
||||
$lang->execution->treeViewTask = 'Seulement les Tâches';
|
||||
$lang->execution->treeViewStory = 'Seulement les Stories';
|
||||
$lang->execution->storyKanban = 'Story Kanban';
|
||||
$lang->execution->storySort = 'Rang Story';
|
||||
$lang->execution->importPlanStory = $lang->executionCommon . ' est créé!\nVoulez-vous importer des stories qui ont été ajoutées au Plan ?';
|
||||
$lang->execution->importEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->needLinkProducts = 'The execution has not been linked with any product, and the related functions cannot be used. Please link the product first and try again.';
|
||||
$lang->execution->iteration = 'Itérations';
|
||||
$lang->execution->iterationInfo = '%s Itérations';
|
||||
$lang->execution->viewAll = 'Voir Tout';
|
||||
$lang->execution->testreport = 'Test Report';
|
||||
$lang->execution->taskKanban = 'Task Kanban';
|
||||
$lang->execution->RDKanban = 'Research & Development Kanban';
|
||||
$lang->execution->index = "{$lang->executionCommon} Home";
|
||||
$lang->execution->task = 'Task List';
|
||||
$lang->execution->groupTask = 'Group View';
|
||||
$lang->execution->story = 'Story List';
|
||||
$lang->execution->qa = 'QA';
|
||||
$lang->execution->bug = 'Bug List';
|
||||
$lang->execution->testcase = 'Testcase List';
|
||||
$lang->execution->dynamic = 'Dynamics';
|
||||
$lang->execution->latestDynamic = 'Dynamics';
|
||||
$lang->execution->build = 'Build List';
|
||||
$lang->execution->testtask = 'Request';
|
||||
$lang->execution->burn = 'Burndown';
|
||||
$lang->execution->computeBurn = 'Update';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = 'Burndown Data';
|
||||
$lang->execution->fixFirst = 'Edit 1st-Day Estimates';
|
||||
$lang->execution->team = 'Members';
|
||||
$lang->execution->doc = 'Document';
|
||||
$lang->execution->doclib = 'Docoment Library';
|
||||
$lang->execution->manageProducts = 'Linked ' . $lang->productCommon . 's';
|
||||
$lang->execution->linkStory = 'Link Stories';
|
||||
$lang->execution->linkStoryByPlan = 'Link Stories By Plan';
|
||||
$lang->execution->linkPlan = 'Linked Plan';
|
||||
$lang->execution->unlinkStoryTasks = 'Unlink';
|
||||
$lang->execution->linkedProducts = "Linked {$lang->productCommon}s";
|
||||
$lang->execution->unlinkedProducts = "Unlinked {$lang->productCommon}s";
|
||||
$lang->execution->view = "Execution Detail";
|
||||
$lang->execution->startAction = "Start Execution";
|
||||
$lang->execution->activateAction = "Activate Execution";
|
||||
$lang->execution->delayAction = "Delay Execution";
|
||||
$lang->execution->suspendAction = "Suspend Execution";
|
||||
$lang->execution->closeAction = "Close Execution";
|
||||
$lang->execution->testtaskAction = "Execution Request";
|
||||
$lang->execution->teamAction = "Execution Members";
|
||||
$lang->execution->kanbanAction = "Execution Kanban";
|
||||
$lang->execution->printKanbanAction = "Print Kanban";
|
||||
$lang->execution->treeAction = "Execution Tree View";
|
||||
$lang->execution->exportAction = "Export Execution";
|
||||
$lang->execution->computeBurnAction = "Update Burndown";
|
||||
$lang->execution->create = "Create {$lang->executionCommon}";
|
||||
$lang->execution->createExec = "Create {$lang->execution->common}";
|
||||
$lang->execution->createAction = "Create {$lang->execution->common}";
|
||||
$lang->execution->copyExec = "Copy {$lang->execution->common}";
|
||||
$lang->execution->copy = "Copy {$lang->executionCommon}";
|
||||
$lang->execution->delete = "Delete {$lang->executionCommon}";
|
||||
$lang->execution->deleteAB = "Delete Execution";
|
||||
$lang->execution->browse = "{$lang->executionCommon} List";
|
||||
$lang->execution->edit = "Edit {$lang->executionCommon}";
|
||||
$lang->execution->editAction = "Edit Execution";
|
||||
$lang->execution->batchEdit = "Edit";
|
||||
$lang->execution->batchEditAction = "Batch Edit";
|
||||
$lang->execution->manageMembers = 'Manage Team';
|
||||
$lang->execution->unlinkMember = 'Remove Member';
|
||||
$lang->execution->unlinkStory = 'Unlink Story';
|
||||
$lang->execution->unlinkStoryAB = 'Unlink';
|
||||
$lang->execution->batchUnlinkStory = 'Batch Unlink Stories';
|
||||
$lang->execution->importTask = 'Transfer Task';
|
||||
$lang->execution->importPlanStories = 'Link Stories By Plan';
|
||||
$lang->execution->importBug = 'Import Bug';
|
||||
$lang->execution->tree = 'Tree';
|
||||
$lang->execution->treeTask = 'Show Task Only';
|
||||
$lang->execution->treeStory = 'Show Story Only';
|
||||
$lang->execution->treeViewTask = 'Tree View Task';
|
||||
$lang->execution->treeViewStory = 'Tree View Story';
|
||||
$lang->execution->storyKanban = 'Story Kanban';
|
||||
$lang->execution->storySort = 'Rank Story';
|
||||
$lang->execution->importPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->importEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? The stories in the draft will be automatically filtered out when imported.';
|
||||
$lang->execution->importBranchPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this ' .$lang->executionCommon. ' will be associated with the import';
|
||||
$lang->execution->importBranchEditPlanStory = $lang->executionCommon . ' is edited!\nDo you want to import stories that have been linked to the plan? Only the activation stories of the branch associated with this ' .$lang->executionCommon. ' will be associated with the import';
|
||||
$lang->execution->needLinkProducts = 'The execution has not been linked with any product, and the related functions cannot be used. Please link the product first and try again.';
|
||||
$lang->execution->iteration = 'Iterations';
|
||||
$lang->execution->iterationInfo = '%s Iterations';
|
||||
$lang->execution->viewAll = 'View All';
|
||||
$lang->execution->testreport = 'Test Report';
|
||||
$lang->execution->taskKanban = 'Task Kanban';
|
||||
$lang->execution->RDKanban = 'Research & Development Kanban';
|
||||
|
||||
/* Group browsing. */
|
||||
$lang->execution->allTasks = 'Toutes';
|
||||
@@ -349,7 +351,7 @@ $lang->execution->howToUpdateBurn = "<a href='https://api.zentao.pm/goto.ph
|
||||
$lang->execution->whyNoStories = "Aucune story ne peut être associée. Vérifiez s'il existe des stories dans {$lang->executionCommon} qui sont associées à {$lang->productCommon} et vérifiez qu'elles ont bien été validées.";
|
||||
$lang->execution->projectNoStories = "No story can be linked. Please check whether there is any story in project and make sure it has been reviewed.";
|
||||
$lang->execution->productStories = "Les stories associées au {$lang->executionCommon} sont une portion des stories associées au {$lang->productCommon}. Les stories ne peuvent être associées à un {$lang->executionCommon} qu'après avoir été validées. <a href='%s'> Associer Stories</a> maintenant.";
|
||||
$lang->execution->haveDraft = "%s stories sont encore en conception, elles ne peuvent pas être associées au {$lang->executionCommon} actuellement.";
|
||||
$lang->execution->haveDraft = "There are %s draft stories or not associated with this {$lang->executionCommon} can't be linked.";
|
||||
$lang->execution->doneExecutions = 'Terminé';
|
||||
$lang->execution->selectDept = 'Sélection Compartiment';
|
||||
$lang->execution->selectDeptTitle = 'Sélection Utilisateur';
|
||||
|
||||
@@ -209,82 +209,84 @@ $lang->execution->burnByList['estimate'] = "按计划工时查看";
|
||||
$lang->execution->burnByList['storyPoint'] = '按故事点查看';
|
||||
|
||||
/* 方法列表。*/
|
||||
$lang->execution->index = "{$lang->execution->common}主页";
|
||||
$lang->execution->task = '任务列表';
|
||||
$lang->execution->groupTask = '分组浏览任务';
|
||||
$lang->execution->story = "{$lang->SRCommon}列表";
|
||||
$lang->execution->qa = '测试仪表盘';
|
||||
$lang->execution->bug = 'Bug列表';
|
||||
$lang->execution->testcase = '用例列表';
|
||||
$lang->execution->dynamic = '动态';
|
||||
$lang->execution->latestDynamic = '最新动态';
|
||||
$lang->execution->build = '所有版本';
|
||||
$lang->execution->testtask = '测试单';
|
||||
$lang->execution->burn = '燃尽图';
|
||||
$lang->execution->computeBurn = '更新燃尽图';
|
||||
$lang->execution->CFD = '累积流图';
|
||||
$lang->execution->computeCFD = '更新累积流图';
|
||||
$lang->execution->burnData = '燃尽图数据';
|
||||
$lang->execution->fixFirst = '修改首天工时';
|
||||
$lang->execution->team = '团队成员';
|
||||
$lang->execution->doc = '文档列表';
|
||||
$lang->execution->doclib = '文档库列表';
|
||||
$lang->execution->manageProducts = '关联' . $lang->productCommon;
|
||||
$lang->execution->linkStory = "关联{$lang->SRCommon}";
|
||||
$lang->execution->linkStoryByPlan = "按照计划关联";
|
||||
$lang->execution->linkPlan = "关联计划";
|
||||
$lang->execution->unlinkStoryTasks = "未关联{$lang->SRCommon}任务";
|
||||
$lang->execution->linkedProducts = '已关联';
|
||||
$lang->execution->unlinkedProducts = '未关联';
|
||||
$lang->execution->view = "{$lang->execution->common}概况";
|
||||
$lang->execution->startAction = "开始{$lang->execution->common}";
|
||||
$lang->execution->activateAction = "激活{$lang->execution->common}";
|
||||
$lang->execution->delayAction = "延期{$lang->execution->common}";
|
||||
$lang->execution->suspendAction = "挂起{$lang->execution->common}";
|
||||
$lang->execution->closeAction = "关闭{$lang->execution->common}";
|
||||
$lang->execution->testtaskAction = "{$lang->execution->common}测试单";
|
||||
$lang->execution->teamAction = "{$lang->execution->common}团队";
|
||||
$lang->execution->kanbanAction = "{$lang->execution->common}看板";
|
||||
$lang->execution->printKanbanAction = "打印看板";
|
||||
$lang->execution->treeAction = "{$lang->execution->common}树状图";
|
||||
$lang->execution->exportAction = "导出{$lang->execution->common}";
|
||||
$lang->execution->computeBurnAction = "计算燃尽图";
|
||||
$lang->execution->create = "添加{$lang->executionCommon}";
|
||||
$lang->execution->createExec = "添加{$lang->execution->common}";
|
||||
$lang->execution->createAction = "添加{$lang->execution->common}";
|
||||
$lang->execution->copyExec = "复制{$lang->execution->common}";
|
||||
$lang->execution->copy = "复制{$lang->executionCommon}";
|
||||
$lang->execution->delete = "删除{$lang->executionCommon}";
|
||||
$lang->execution->deleteAB = "删除{$lang->execution->common}";
|
||||
$lang->execution->browse = "浏览{$lang->execution->common}";
|
||||
$lang->execution->edit = "设置{$lang->executionCommon}";
|
||||
$lang->execution->editAction = "编辑{$lang->execution->common}";
|
||||
$lang->execution->batchEdit = "编辑";
|
||||
$lang->execution->batchEditAction = "批量编辑";
|
||||
$lang->execution->manageMembers = '团队管理';
|
||||
$lang->execution->unlinkMember = '移除成员';
|
||||
$lang->execution->unlinkStory = "移除{$lang->SRCommon}";
|
||||
$lang->execution->unlinkStoryAB = "移除{$lang->SRCommon}";
|
||||
$lang->execution->batchUnlinkStory = "批量移除{$lang->SRCommon}";
|
||||
$lang->execution->importTask = '转入任务';
|
||||
$lang->execution->importPlanStories = "按计划关联{$lang->SRCommon}";
|
||||
$lang->execution->importBug = '导入Bug';
|
||||
$lang->execution->tree = '树状图';
|
||||
$lang->execution->treeTask = '只看任务';
|
||||
$lang->execution->treeStory = "只看{$lang->SRCommon}";
|
||||
$lang->execution->treeViewTask = '树状图查看任务';
|
||||
$lang->execution->treeViewStory = "树状图查看{$lang->SRCommon}";
|
||||
$lang->execution->storyKanban = "{$lang->SRCommon}看板";
|
||||
$lang->execution->storySort = "{$lang->SRCommon}排序";
|
||||
$lang->execution->importPlanStory = '创建' . $lang->executionCommon . '成功!\n是否导入计划关联的相关' . $lang->SRCommon . '?导入时将自动过滤掉草稿状态的' . $lang->SRCommon . '。';
|
||||
$lang->execution->importEditPlanStory = '编辑' . $lang->executionCommon . '成功!\n是否导入计划关联的相关' . $lang->SRCommon . '?导入时将自动过滤掉草稿状态的' . $lang->SRCommon . '。';
|
||||
$lang->execution->needLinkProducts = '该执行还未关联任何产品,相关功能无法使用,请先关联产品后再试。';
|
||||
$lang->execution->iteration = '版本迭代';
|
||||
$lang->execution->iterationInfo = '迭代%s次';
|
||||
$lang->execution->viewAll = '查看所有';
|
||||
$lang->execution->testreport = '测试报告';
|
||||
$lang->execution->taskKanban = '任务看板';
|
||||
$lang->execution->RDKanban = '研发看板';
|
||||
$lang->execution->index = "{$lang->execution->common}主页";
|
||||
$lang->execution->task = '任务列表';
|
||||
$lang->execution->groupTask = '分组浏览任务';
|
||||
$lang->execution->story = "{$lang->SRCommon}列表";
|
||||
$lang->execution->qa = '测试仪表盘';
|
||||
$lang->execution->bug = 'Bug列表';
|
||||
$lang->execution->testcase = '用例列表';
|
||||
$lang->execution->dynamic = '动态';
|
||||
$lang->execution->latestDynamic = '最新动态';
|
||||
$lang->execution->build = '所有版本';
|
||||
$lang->execution->testtask = '测试单';
|
||||
$lang->execution->burn = '燃尽图';
|
||||
$lang->execution->computeBurn = '更新燃尽图';
|
||||
$lang->execution->CFD = '累积流图';
|
||||
$lang->execution->computeCFD = '更新累积流图';
|
||||
$lang->execution->burnData = '燃尽图数据';
|
||||
$lang->execution->fixFirst = '修改首天工时';
|
||||
$lang->execution->team = '团队成员';
|
||||
$lang->execution->doc = '文档列表';
|
||||
$lang->execution->doclib = '文档库列表';
|
||||
$lang->execution->manageProducts = '关联' . $lang->productCommon;
|
||||
$lang->execution->linkStory = "关联{$lang->SRCommon}";
|
||||
$lang->execution->linkStoryByPlan = "按照计划关联";
|
||||
$lang->execution->linkPlan = "关联计划";
|
||||
$lang->execution->unlinkStoryTasks = "未关联{$lang->SRCommon}任务";
|
||||
$lang->execution->linkedProducts = '已关联';
|
||||
$lang->execution->unlinkedProducts = '未关联';
|
||||
$lang->execution->view = "{$lang->execution->common}概况";
|
||||
$lang->execution->startAction = "开始{$lang->execution->common}";
|
||||
$lang->execution->activateAction = "激活{$lang->execution->common}";
|
||||
$lang->execution->delayAction = "延期{$lang->execution->common}";
|
||||
$lang->execution->suspendAction = "挂起{$lang->execution->common}";
|
||||
$lang->execution->closeAction = "关闭{$lang->execution->common}";
|
||||
$lang->execution->testtaskAction = "{$lang->execution->common}测试单";
|
||||
$lang->execution->teamAction = "{$lang->execution->common}团队";
|
||||
$lang->execution->kanbanAction = "{$lang->execution->common}看板";
|
||||
$lang->execution->printKanbanAction = "打印看板";
|
||||
$lang->execution->treeAction = "{$lang->execution->common}树状图";
|
||||
$lang->execution->exportAction = "导出{$lang->execution->common}";
|
||||
$lang->execution->computeBurnAction = "计算燃尽图";
|
||||
$lang->execution->create = "添加{$lang->executionCommon}";
|
||||
$lang->execution->createExec = "添加{$lang->execution->common}";
|
||||
$lang->execution->createAction = "添加{$lang->execution->common}";
|
||||
$lang->execution->copyExec = "复制{$lang->execution->common}";
|
||||
$lang->execution->copy = "复制{$lang->executionCommon}";
|
||||
$lang->execution->delete = "删除{$lang->executionCommon}";
|
||||
$lang->execution->deleteAB = "删除{$lang->execution->common}";
|
||||
$lang->execution->browse = "浏览{$lang->execution->common}";
|
||||
$lang->execution->edit = "设置{$lang->executionCommon}";
|
||||
$lang->execution->editAction = "编辑{$lang->execution->common}";
|
||||
$lang->execution->batchEdit = "编辑";
|
||||
$lang->execution->batchEditAction = "批量编辑";
|
||||
$lang->execution->manageMembers = '团队管理';
|
||||
$lang->execution->unlinkMember = '移除成员';
|
||||
$lang->execution->unlinkStory = "移除{$lang->SRCommon}";
|
||||
$lang->execution->unlinkStoryAB = "移除{$lang->SRCommon}";
|
||||
$lang->execution->batchUnlinkStory = "批量移除{$lang->SRCommon}";
|
||||
$lang->execution->importTask = '转入任务';
|
||||
$lang->execution->importPlanStories = "按计划关联{$lang->SRCommon}";
|
||||
$lang->execution->importBug = '导入Bug';
|
||||
$lang->execution->tree = '树状图';
|
||||
$lang->execution->treeTask = '只看任务';
|
||||
$lang->execution->treeStory = "只看{$lang->SRCommon}";
|
||||
$lang->execution->treeViewTask = '树状图查看任务';
|
||||
$lang->execution->treeViewStory = "树状图查看{$lang->SRCommon}";
|
||||
$lang->execution->storyKanban = "{$lang->SRCommon}看板";
|
||||
$lang->execution->storySort = "{$lang->SRCommon}排序";
|
||||
$lang->execution->importPlanStory = '创建' . $lang->executionCommon . '成功!\n是否导入计划关联的相关' . $lang->SRCommon . '?导入时将自动过滤掉草稿状态的' . $lang->SRCommon . '。';
|
||||
$lang->execution->importEditPlanStory = '编辑' . $lang->executionCommon . '成功!\n是否导入计划关联的相关' . $lang->SRCommon . '?导入时将自动过滤掉草稿状态的' . $lang->SRCommon . '。';
|
||||
$lang->execution->importBranchPlanStory = '创建' . $lang->executionCommon . '成功!\n是否导入计划关联的相关' . $lang->SRCommon . '?导入时将只关联本' . $lang->executionCommon . '所关联分支的激活需求。';
|
||||
$lang->execution->importBranchEditPlanStory = '编辑' . $lang->executionCommon . '成功!\n是否导入计划关联的相关' . $lang->SRCommon . '?导入时将只关联本' . $lang->executionCommon . '所关联分支的激活需求。';
|
||||
$lang->execution->needLinkProducts = '该执行还未关联任何产品,相关功能无法使用,请先关联产品后再试。';
|
||||
$lang->execution->iteration = '版本迭代';
|
||||
$lang->execution->iterationInfo = '迭代%s次';
|
||||
$lang->execution->viewAll = '查看所有';
|
||||
$lang->execution->testreport = '测试报告';
|
||||
$lang->execution->taskKanban = '任务看板';
|
||||
$lang->execution->RDKanban = '研发看板';
|
||||
|
||||
/* 分组浏览。*/
|
||||
$lang->execution->allTasks = '所有';
|
||||
@@ -349,7 +351,7 @@ $lang->execution->howToUpdateBurn = "<a href='https://api.zentao.net/goto.p
|
||||
$lang->execution->whyNoStories = "看起来没有{$lang->SRCommon}可以关联。请检查下{$lang->executionCommon}关联的{$lang->productCommon}中有没有{$lang->SRCommon},而且要确保它们已经审核通过。";
|
||||
$lang->execution->projectNoStories = "看起来没有{$lang->SRCommon}可以关联。请检查下项目中有没有{$lang->SRCommon},而且要确保它们已经审核通过。";
|
||||
$lang->execution->productStories = "{$lang->executionCommon}关联的{$lang->SRCommon}是{$lang->productCommon}{$lang->SRCommon}的子集,并且只有评审通过的{$lang->SRCommon}才能关联。请<a href='%s'>关联{$lang->SRCommon}</a>。";
|
||||
$lang->execution->haveDraft = "有%s条草稿状态的{$lang->SRCommon}无法关联到该{$lang->executionCommon}";
|
||||
$lang->execution->haveDraft = "有%s条草稿状态或非本{$lang->executionCommon}关联分支的{$lang->SRCommon}无法关联到该{$lang->executionCommon}";
|
||||
$lang->execution->doneExecutions = '已结束';
|
||||
$lang->execution->selectDept = '选择部门';
|
||||
$lang->execution->selectDeptTitle = '选择一个部门的成员';
|
||||
|
||||
+79
-24
@@ -332,6 +332,20 @@ class executionModel extends model
|
||||
$this->checkBeginAndEndDate($_POST['project'], $_POST['begin'], $_POST['end']);
|
||||
if(dao::isError()) return false;
|
||||
|
||||
if($_POST['products'])
|
||||
{
|
||||
$this->app->loadLang('project');
|
||||
$multipleProducts = $this->loadModel('product')->getMultiBranchPairs();
|
||||
foreach($_POST['products'] as $index => $productID)
|
||||
{
|
||||
if(isset($multipleProducts[$productID]) and empty($_POST['branch'][$index]))
|
||||
{
|
||||
dao::$errors[] = $this->lang->project->emptyBranch;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine whether to add a sprint or a stage according to the model of the execution. */
|
||||
$project = $this->loadModel('project')->getByID($_POST['project']);
|
||||
$type = 'sprint';
|
||||
@@ -514,6 +528,20 @@ class executionModel extends model
|
||||
return false;
|
||||
}
|
||||
|
||||
if($_POST['products'])
|
||||
{
|
||||
$this->app->loadLang('project');
|
||||
$multipleProducts = $this->loadModel('product')->getMultiBranchPairs();
|
||||
foreach($_POST['products'] as $index => $productID)
|
||||
{
|
||||
if(isset($multipleProducts[$productID]) and empty($_POST['branch'][$index]))
|
||||
{
|
||||
dao::$errors[] = $this->lang->project->emptyBranch;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the data from the post. */
|
||||
$execution = fixer::input('post')
|
||||
->add('id', $executionID)
|
||||
@@ -2370,23 +2398,28 @@ class executionModel extends model
|
||||
$oldPlan = 0;
|
||||
$branch = isset($branches[$i]) ? $branches[$i] : 0;
|
||||
|
||||
if(isset($existedProducts[$productID][$branch])) continue;
|
||||
if(!is_array($branch)) $branch = array($branch);
|
||||
|
||||
if(isset($oldProducts[$productID][$branch]))
|
||||
foreach($branch as $branchID)
|
||||
{
|
||||
$oldProduct = $oldProducts[$productID][$branch];
|
||||
if($this->app->rawMethod != 'edit') $oldPlan = $oldProduct->plan;
|
||||
}
|
||||
if(isset($existedProducts[$productID][$branchID])) continue;
|
||||
|
||||
$data = new stdclass();
|
||||
$data->project = $executionID;
|
||||
$data->product = $productID;
|
||||
$data->branch = $branch;
|
||||
$data->plan = isset($plans[$productID][$branch]) ? implode(',', $plans[$productID][$branch]) : $oldPlan;
|
||||
$data->plan = trim($data->plan, ',');
|
||||
$data->plan = empty($data->plan) ? 0 : ",$data->plan,";
|
||||
$this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec();
|
||||
$existedProducts[$productID][$branch] = true;
|
||||
if(isset($oldProducts[$productID][$branchID]))
|
||||
{
|
||||
$oldProduct = $oldProducts[$productID][$branchID];
|
||||
if($this->app->rawMethod != 'edit') $oldPlan = $oldProduct->plan;
|
||||
}
|
||||
|
||||
$data = new stdclass();
|
||||
$data->project = $executionID;
|
||||
$data->product = $productID;
|
||||
$data->branch = $branchID;
|
||||
$data->plan = isset($plans[$productID]) ? implode(',', $plans[$productID]) : $oldPlan;
|
||||
$data->plan = trim($data->plan, ',');
|
||||
$data->plan = empty($data->plan) ? 0 : ",$data->plan,";
|
||||
$this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec();
|
||||
$existedProducts[$productID][$branchID] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$oldProductKeys = array_keys($oldProducts);
|
||||
@@ -2882,19 +2915,21 @@ class executionModel extends model
|
||||
*/
|
||||
public function linkStories($executionID)
|
||||
{
|
||||
$plans = $this->dao->select('plan')->from(TABLE_PROJECTPRODUCT)
|
||||
$plans = $this->dao->select('product, plan')->from(TABLE_PROJECTPRODUCT)
|
||||
->where('project')->eq($executionID)
|
||||
->fetchPairs('plan');
|
||||
->fetchPairs('product', 'plan');
|
||||
|
||||
$planStories = array();
|
||||
$planProducts = array();
|
||||
$this->loadModel('story');
|
||||
if(!empty($plans))
|
||||
{
|
||||
foreach($plans as $planIdList)
|
||||
$executionProducts = $this->loadModel('project')->getBranchesByProject($executionID);
|
||||
foreach($plans as $productID => $planIdList)
|
||||
{
|
||||
if(empty($planIdList)) continue;
|
||||
$planIdList = explode(',', $planIdList);
|
||||
$executionBranches = zget($executionProducts, $productID, array());
|
||||
foreach($planIdList as $planID)
|
||||
{
|
||||
$planStory = $this->story->getPlanStories($planID);
|
||||
@@ -2902,7 +2937,7 @@ class executionModel extends model
|
||||
{
|
||||
foreach($planStory as $id => $story)
|
||||
{
|
||||
if($story->status == 'draft' or $story->status == 'reviewing')
|
||||
if($story->status == 'draft' or $story->status == 'reviewing' or (!empty($story->branch) and !empty($executionBranches) and !isset($executionBranches[$story->branch])))
|
||||
{
|
||||
unset($planStory[$id]);
|
||||
continue;
|
||||
@@ -4623,17 +4658,36 @@ class executionModel extends model
|
||||
$branchGroups = $this->getBranchByProduct(array_keys($products), $executionID, 'noclosed');
|
||||
foreach($branchGroups as $branches)
|
||||
{
|
||||
foreach($branches as $branchID => $branchName) $branchIdList[$branchID] = $branchID;
|
||||
foreach($branches as $branchID => $branchName) $branchIdList[] = $branchID;
|
||||
}
|
||||
|
||||
$plans = $this->dao->select('id,title,product,parent,begin,end')->from(TABLE_PRODUCTPLAN)
|
||||
->where('product')->in(array_keys($products))
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('branch')->in($branchIdList)->fi()
|
||||
->orderBy('begin desc')
|
||||
$branchQuery = '(';
|
||||
if(!empty($branchIdList))
|
||||
{
|
||||
$branchCount = count($branchIdList);
|
||||
foreach($branchIdList as $index => $branchID)
|
||||
{
|
||||
$branchQuery .= "FIND_IN_SET('$branchID', branch)";
|
||||
if($index < $branchCount - 1) $branchQuery .= ' OR ';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$branchQuery .= "FIND_IN_SET('0', branch)";
|
||||
}
|
||||
|
||||
$branchQuery .= ')';
|
||||
|
||||
$plans = $this->dao->select('t1.id,t1.title,t1.product,t1.parent,t1.begin,t1.end,t1.branch,t2.type as productType')->from(TABLE_PRODUCTPLAN)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t2.id=t1.product')
|
||||
->where('t1.product')->in(array_keys($products))
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->andWhere($branchQuery)
|
||||
->orderBy('t1.begin desc')
|
||||
->fetchAll('id');
|
||||
|
||||
$plans = $this->productplan->reorder4Children($plans);
|
||||
$plans = $this->productplan->relationBranch($plans);
|
||||
$productPlans = array();
|
||||
foreach($plans as $plan)
|
||||
{
|
||||
@@ -4641,6 +4695,7 @@ class executionModel extends model
|
||||
if($plan->parent > 0 and isset($plans[$plan->parent])) $plan->title = $plans[$plan->parent]->title . ' /' . $plan->title;
|
||||
$productPlans[$plan->product][$plan->id] = $plan->title . " [{$plan->begin} ~ {$plan->end}]";
|
||||
if($plan->begin == '2030-01-01' and $plan->end == '2030-01-01') $productPlans[$plan->product][$plan->id] = $plan->title . ' ' . $this->lang->productplan->future;
|
||||
if($plan->productType != 'normal') $productPlans[$plan->product][$plan->id] = $productPlans[$plan->product][$plan->id] . ' / ' . ($plan->branchName ? $plan->branchName : $this->lang->branch->main);
|
||||
}
|
||||
|
||||
return $productPlans;
|
||||
|
||||
@@ -130,62 +130,81 @@
|
||||
<?php $this->printExtendFields('', 'table', 'columns=3');?>
|
||||
<?php $hidden = 'hide'?>
|
||||
<?php if(!empty($project->hasProduct)) $hidden = ''?>
|
||||
<?php if($products):?>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($products as $product):?>
|
||||
<tr class="<?php echo $hidden;?>">
|
||||
<th><?php echo $lang->execution->manageProducts;?></th>
|
||||
<td class='text-left' id='productsBox' colspan="3">
|
||||
<th><?php echo $lang->project->manageProductPlan;?></th>
|
||||
<td class='text-left productsBox' colspan="3">
|
||||
<div class='row'>
|
||||
<?php $i = 0;?>
|
||||
<?php $class = $division ? '' : 'disabled';?>
|
||||
<?php foreach($products as $product):?>
|
||||
<?php $hasBranch = ($product->type != 'normal' and isset($branchGroups[$product->id]));?>
|
||||
<?php foreach($linkedBranches[$product->id] as $branchID => $branch):?>
|
||||
<div class='col-sm-4'>
|
||||
<div class="input-group<?php if($hasBranch) echo ' has-branch';?>">
|
||||
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' $class onchange='loadBranches(this)' data-last='" . $product->id . "'");?>
|
||||
<?php if($class) echo html::hidden("products[$i]", $product->id);?>
|
||||
<span class='input-group-addon fix-border'></span>
|
||||
<?php if($hasBranch) echo html::select("branch[$i]", $branchGroups[$product->id], $branchID, "class='form-control chosen' onchange=\"loadPlans('#products{$i}', this.value)\"");?>
|
||||
<div class="col-sm-6">
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php $hasBranch = $product->type != 'normal' and isset($branchGroups[$product->id]);?>
|
||||
<div class='input-group <?php if($hasBranch) echo ' has-branch';?>'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->common;?></span>
|
||||
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' onchange='loadBranches(this)' data-last='" . $product->id . "' data-type='" . $product->type . "'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col <?php if(!$hasBranch) echo 'hidden';?>'>
|
||||
<div class='input-group required'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->product->branchName['branch'];?></span>
|
||||
<?php $branchIdList = join(',', $product->branches);?>
|
||||
<?php echo html::select("branch[$i][]", isset($branchGroups[$product->id]) ? $branchGroups[$product->id] : array(), $branchIdList, "class='form-control chosen' multiple onchange=\"loadPlans('#products{$i}', this)\"");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php $i++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endforeach;?>
|
||||
<?php if((isset($project->model) and $project->model == 'scrum') or empty($products)):?>
|
||||
<div class='col-sm-4'>
|
||||
<div class="input-group">
|
||||
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
|
||||
<span class='input-group-addon fix-border'></span>
|
||||
<div class="col-sm-6">
|
||||
<div class='input-group' <?php echo "id='plan$i'";?>>
|
||||
<span class='input-group-addon'><?php echo $lang->product->plan;?></span>
|
||||
<?php echo html::select("plans[$product->id][]", isset($productPlans[$product->id]) ? $productPlans[$product->id] : array(), $product->plans, "class='form-control chosen' multiple");?>
|
||||
<div class='input-group-btn'>
|
||||
<a href='javascript:;' onclick='addNewLine(this)' class='btn btn-link addLine'><i class='icon-plus'></i></a>
|
||||
<a href='javascript:;' onclick='removeLine(this)' class='btn btn-link removeLine' <?php if($i == 0) echo "style='visibility: hidden'";?>><i class='icon-close'></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php $hidden = 'hide'?>
|
||||
<?php if(isset($project->model) and !empty($project->hasProduct) and $project->model == 'scrum') $hidden = '';?>
|
||||
<tr class="<?php echo $hidden?>">
|
||||
<th><?php echo $lang->execution->linkPlan;?></th>
|
||||
<td colspan="3" id="plansBox">
|
||||
<?php $i ++;?>
|
||||
<?php endforeach;?>
|
||||
<?php else:?>
|
||||
<tr class='<?php echo $hidden;?>'>
|
||||
<th id='productTitle'><?php echo $lang->project->manageProductPlan;?></th>
|
||||
<td class='text-left productsBox' colspan='3'>
|
||||
<div class='row'>
|
||||
<?php if(isset($plan) && !empty($plan->begin)):?>
|
||||
<div class="col-sm-4" id="plan0"><?php echo html::select("plans[{$plan->product}][{$plan->branch}][]", $productPlan, $plan->id, "class='form-control chosen' multiple");?></div>
|
||||
<?php js::set('currentPlanID', $plan->id)?>
|
||||
<?php elseif($copyExecutionID):?>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($products as $product):?>
|
||||
<?php foreach($linkedBranches[$product->id] as $branchID => $branch):?>
|
||||
<?php $plans = isset($productPlans[$product->id][$branchID]) ? $productPlans[$product->id][$branchID] : array();?>
|
||||
<div class="col-sm-4" id="plan<?php echo $i;?>"><?php echo html::select("plans[{$product->id}][$branchID][]", $plans, $branches[$product->id][$branchID]->plan, "class='form-control chosen' multiple");?></div>
|
||||
<?php $i++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endforeach;?>
|
||||
<?php else:?>
|
||||
<div class="col-sm-4" id="plan0"><?php echo html::select("plans[][][]", $productPlan, '', "class='form-control chosen' multiple");?></div>
|
||||
<?php js::set('currentPlanID', '')?>
|
||||
<?php endif;?>
|
||||
<div class="col-sm-6">
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->common;?></span>
|
||||
<?php echo html::select("products[0]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col hidden'>
|
||||
<div class='input-group required'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->product->branchName['branch'];?></span>
|
||||
<?php echo html::select("branch", '', '', "class='form-control chosen' multiple");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class='input-group' id='plan0'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->plan;?></span>
|
||||
<?php echo html::select("plans[][]", $productPlan, '', "class='form-control chosen' multiple");?>
|
||||
<div class='input-group-btn'>
|
||||
<a href='javascript:;' onclick='addNewLine(this)' class='btn btn-link addLine'><i class='icon-plus'></i></a>
|
||||
<a href='javascript:;' onclick='removeLine(this)' class='btn btn-link removeLine' style='visibility: hidden'><i class='icon-close'></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr>
|
||||
<th><?php echo $lang->execution->teamname;?></th>
|
||||
<td><?php echo html::input('team', $team, "class='form-control'");?></td>
|
||||
|
||||
@@ -141,56 +141,81 @@
|
||||
<?php if(!in_array($execution->attribute, array('request', 'design', 'review'))): ?>
|
||||
<?php $hidden = 'hide'?>
|
||||
<?php if(!empty($project->hasProduct)) $hidden = ''?>
|
||||
<?php if($linkedProducts):?>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($linkedProducts as $product):?>
|
||||
<tr class="<?php echo $hidden;?>">
|
||||
<th><?php echo $lang->execution->manageProducts;?></th>
|
||||
<td class='text-left' id='productsBox' colspan="2">
|
||||
<?php $class = ($execution->grade == 2 or $execution->type == 'stage') ? "disabled" : '';?>
|
||||
<th><?php if($i == 0) echo $lang->project->manageProductPlan;?></th>
|
||||
<td class='text-left productsBox' colspan="3">
|
||||
<div class='row'>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($linkedProducts as $product):?>
|
||||
<?php $hasBranch = $product->type != 'normal' and isset($branchGroups[$product->id]);?>
|
||||
<?php foreach($linkedBranches[$product->id] as $branchID => $branch):?>
|
||||
<div class='col-sm-4'>
|
||||
<div class="input-group<?php if($hasBranch) echo ' has-branch';?>">
|
||||
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' $class onchange='loadBranches(this)' data-last='" . $product->id . "' data-type='". $product->type ."' data-lastBranch='" . $branchID . "'");?>
|
||||
<span class='input-group-addon fix-border'></span>
|
||||
<?php if($hasBranch) echo html::select("branch[$i]", $branchGroups[$product->id], $branchID, "class='form-control chosen' $class onchange=\"loadPlans('#products{$i}', this.value)\" data-last='" . $branchID . "'");?>
|
||||
<div class="col-sm-6">
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php $hasBranch = $product->type != 'normal' and isset($branchGroups[$product->id]);?>
|
||||
<div class='input-group <?php if($hasBranch) echo ' has-branch';?>'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->common;?></span>
|
||||
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' onchange='loadBranches(this)' data-last='" . $product->id . "' data-type='" . $product->type . "'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col <?php if(!$hasBranch) echo 'hidden';?>'>
|
||||
<div class='input-group required'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->product->branchName['branch'];?></span>
|
||||
<?php $branchIdList = join(',', $product->branches);?>
|
||||
<?php echo html::select("branch[$i][]", isset($branchGroups[$product->id]) ? $branchGroups[$product->id] : array(), $branchIdList, "class='form-control chosen' multiple onchange=\"loadPlans('#products{$i}', this)\"");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php $i++; ?>
|
||||
<?php endforeach;?>
|
||||
<?php endforeach;?>
|
||||
<?php if($execution->type != 'stage'):?>
|
||||
<div class='col-sm-4'>
|
||||
<div class="input-group">
|
||||
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
|
||||
<span class='input-group-addon fix-border'></span>
|
||||
<div class="col-sm-6">
|
||||
<div class='input-group' <?php echo "id='plan$i'";?>>
|
||||
<span class='input-group-addon'><?php echo $lang->product->plan;?></span>
|
||||
<?php echo html::select("plans[$product->id][]", isset($productPlans[$product->id]) ? $productPlans[$product->id] : array(), $product->plans, "class='form-control chosen' multiple");?>
|
||||
<div class='input-group-btn'>
|
||||
<a href='javascript:;' onclick='addNewLine(this)' class='btn btn-link addLine'><i class='icon-plus'></i></a>
|
||||
<a href='javascript:;' onclick='removeLine(this)' class='btn btn-link removeLine' <?php if($i == 0) echo "style='visibility: hidden'";?>><i class='icon-close'></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if(isset($project->model) and $project->model == 'scrum') $hidden = '';?>
|
||||
<tr class="<?php echo $hidden?>">
|
||||
<th><?php echo $lang->execution->linkPlan;?></th>
|
||||
<td id="plansBox" colspan="2">
|
||||
<?php $i ++;?>
|
||||
<?php endforeach;?>
|
||||
<?php else:?>
|
||||
<tr class='<?php echo $hidden;?>'>
|
||||
<th id='productTitle'><?php echo $lang->project->manageProductPlan;?></th>
|
||||
<td class='text-left productsBox' colspan='3'>
|
||||
<div class='row'>
|
||||
<?php $i = 0;?>
|
||||
<?php if(empty($linkedProducts)):?>
|
||||
<div class="col-sm-4" id="plan0"><?php echo html::select("plans[][][]", $productPlans, '', "class='form-control chosen' multiple");?></div>
|
||||
<?php else:?>
|
||||
<?php foreach($linkedProducts as $product):?>
|
||||
<?php foreach($linkedBranches[$product->id] as $branchID => $branch):?>
|
||||
<?php $plans = isset($productPlans[$product->id][$branchID]) ? $productPlans[$product->id][$branchID] : array();?>
|
||||
<div class="col-sm-4" id="plan<?php echo $i;?>"><?php echo html::select("plans[{$product->id}][{$branchID}][]", $plans, $branches[$product->id][$branchID]->plan, "class='form-control chosen' multiple");?></div>
|
||||
<?php $i++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endforeach;?>
|
||||
<?php endif;?>
|
||||
<div class="col-sm-6">
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->common;?></span>
|
||||
<?php echo html::select("products[0]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col hidden'>
|
||||
<div class='input-group required'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->product->branchName['branch'];?></span>
|
||||
<?php echo html::select("branch", '', '', "class='form-control chosen' multiple");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class='input-group' id='plan0'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->plan;?></span>
|
||||
<?php echo html::select("plans[][]", '', '', "class='form-control chosen' multiple");?>
|
||||
<div class='input-group-btn'>
|
||||
<a href='javascript:;' onclick='addNewLine(this)' class='btn btn-link addLine'><i class='icon-plus'></i></a>
|
||||
<a href='javascript:;' onclick='removeLine(this)' class='btn btn-link removeLine' style='visibility: hidden'><i class='icon-close'></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<?php echo html::hidden("products[]", key($linkedProducts));?>
|
||||
<?php endif; ?>
|
||||
@@ -232,5 +257,6 @@
|
||||
<?php js::set('confirmSync', $lang->execution->confirmSync);?>
|
||||
<?php js::set('allProducts', $allProducts);?>
|
||||
<?php js::set('branchGroups', $branchGroups);?>
|
||||
<?php js::set('projectID', $execution->project);?>
|
||||
<?php js::set('unLinkProductTip', $lang->project->unLinkProductTip);?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
@@ -546,6 +546,7 @@ $lang->resource->story->batchChangeModule = 'batchChangeModule';
|
||||
$lang->resource->story->batchToTask = 'batchToTask';
|
||||
$lang->resource->story->processStoryChange = 'processStoryChange';
|
||||
$lang->resource->story->linkStories = 'linkStoriesAB';
|
||||
$lang->resource->story->relieved = 'relievedSiblings';
|
||||
|
||||
$lang->story->methodOrder[5] = 'create';
|
||||
$lang->story->methodOrder[10] = 'batchCreate';
|
||||
@@ -575,6 +576,7 @@ $lang->story->methodOrder[120] = 'batchChangeModule';
|
||||
$lang->story->methodOrder[125] = 'batchToTask';
|
||||
$lang->story->methodOrder[130] = 'processStoryChange';
|
||||
$lang->story->methodOrder[135] = 'linkStories';
|
||||
$lang->story->methodOrder[140] = 'relieved';
|
||||
|
||||
/* Requirement. */
|
||||
$lang->resource->requirement = new stdclass();
|
||||
|
||||
@@ -818,8 +818,11 @@ class kanbanModel extends model
|
||||
}
|
||||
elseif($branchID)
|
||||
{
|
||||
$branchName = $this->branch->getById($branchID);
|
||||
$branches = array($branchID => $branchName);
|
||||
foreach(explode(',', $branchID) as $id)
|
||||
{
|
||||
$branchName = $this->branch->getById($id);
|
||||
$branches[$id] = $branchName;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($branches as $id => $name)
|
||||
|
||||
@@ -257,7 +257,7 @@ class product extends control
|
||||
|
||||
$this->products = $this->product->getProducts($projectID, 'all', '', false);
|
||||
$projectProducts = $this->product->getProducts($projectID);
|
||||
$productPlans = $this->execution->getPlans($projectProducts, 'skipParent');
|
||||
$productPlans = $this->execution->getPlans($projectProducts, 'skipParent', $projectID);
|
||||
|
||||
if($browseType == 'bybranch') $param = $branchID;
|
||||
$stories = $this->story->getExecutionStories($projectID, $productID, $branchID, $sort, $browseType, $param, $storyType, '', $pager);
|
||||
@@ -923,6 +923,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.
|
||||
*
|
||||
@@ -1037,12 +1053,19 @@ class product extends control
|
||||
public function ajaxGetPlans($productID, $branch = 0, $planID = 0, $fieldID = '', $needCreate = false, $expired = '', $param = '')
|
||||
{
|
||||
$param = strtolower($param);
|
||||
$plans = $this->loadModel('productplan')->getPairs($productID, $branch, $expired, strpos($param, 'skipparent') !== false);
|
||||
$field = $fieldID ? "plans[$fieldID]" : 'plan';
|
||||
if(strpos($param, 'forstory') === false)
|
||||
{
|
||||
$plans = $this->loadModel('productplan')->getPairs($productID, $branch, $expired, strpos($param, 'skipparent') !== false);
|
||||
}
|
||||
else
|
||||
{
|
||||
$plans = $this->loadModel('productplan')->getPairsForStory($productID, $branch == '0' ? 'all' : $branch, $param);
|
||||
}
|
||||
$field = $fieldID !== '' ? "plans[$fieldID]" : 'plan';
|
||||
$multiple = strpos($param, 'multiple') === false ? '' : 'multiple';
|
||||
$output = html::select($field, $plans, $planID, "class='form-control chosen' $multiple");
|
||||
|
||||
if($branch == 0 and strpos($param, 'edit')) $output = html::select($field, $plans, $planID, "class='form-control chosen' multiple");
|
||||
if($branch == 0 and strpos($param, 'edit') and (strpos($param, 'forstory') === false)) $output = html::select($field, $plans, $planID, "class='form-control chosen' multiple");
|
||||
|
||||
if(count($plans) == 1 and $needCreate and $needCreate !== 'false')
|
||||
{
|
||||
|
||||
+113
-32
@@ -84,7 +84,7 @@ class productplan extends control
|
||||
}
|
||||
|
||||
$this->commonAction($productID, $branchID);
|
||||
$lastPlan = $this->productplan->getLast($productID, $branchID, $parent);
|
||||
$lastPlan = $this->productplan->getLast($productID, '', $parent);
|
||||
$product = $this->loadModel('product')->getById($productID);
|
||||
|
||||
if($lastPlan)
|
||||
@@ -117,7 +117,7 @@ class productplan extends control
|
||||
$this->view->branches = $branchPairs;
|
||||
$this->view->defaultBranch = $defaultBranch;
|
||||
$this->view->parent = $parent;
|
||||
$this->view->parentPlanPairs = $this->productplan->getTopPlanPairs($productID, $branchID, 'done,closed');
|
||||
$this->view->parentPlanPairs = $this->productplan->getTopPlanPairs($productID, '', 'done,closed');
|
||||
$this->display();
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class productplan extends control
|
||||
$changes = $this->productplan->update($planID);
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
$change[$planID] = $changes;
|
||||
$this->syncStory($change);
|
||||
$this->unlinkOldBranch($change);
|
||||
if($changes)
|
||||
{
|
||||
$actionID = $this->loadModel('action')->create('productplan', $planID, 'edited');
|
||||
@@ -178,7 +178,7 @@ class productplan extends control
|
||||
if(!empty($_POST['title']))
|
||||
{
|
||||
$changes = $this->productplan->batchUpdate($productID);
|
||||
$this->syncStory($changes);
|
||||
$this->unlinkOldBranch($changes);
|
||||
$this->loadModel('action');
|
||||
foreach($changes as $planID => $change)
|
||||
{
|
||||
@@ -194,15 +194,21 @@ class productplan extends control
|
||||
|
||||
$this->commonAction($productID, $branch);
|
||||
|
||||
$plans = $this->productplan->getByIDList($this->post->planIDList);
|
||||
$oldBranch = array();
|
||||
$plans = $this->productplan->getByIDList($this->post->planIDList);
|
||||
$oldBranch = array();
|
||||
$parentIdList = array();
|
||||
|
||||
foreach($plans as $plan) $oldBranch[$plan->id] = $plan->branch;
|
||||
foreach($plans as $plan)
|
||||
{
|
||||
$oldBranch[$plan->id] = $plan->branch;
|
||||
$parentIdList[$plan->parent] = $plan->parent;
|
||||
}
|
||||
|
||||
$this->view->title = $this->lang->productplan->batchEdit;
|
||||
$this->view->plans = $plans;
|
||||
$this->view->oldBranch = $oldBranch;
|
||||
$this->view->product = $this->loadModel('product')->getById($productID);;
|
||||
$this->view->title = $this->lang->productplan->batchEdit;
|
||||
$this->view->plans = $plans;
|
||||
$this->view->oldBranch = $oldBranch;
|
||||
$this->view->product = $this->loadModel('product')->getById($productID);;
|
||||
$this->view->parentList = $this->productplan->getByIDList($parentIdList);
|
||||
|
||||
$this->display();
|
||||
}
|
||||
@@ -447,8 +453,6 @@ class productplan extends control
|
||||
if($plan->parent > 0) $this->view->parentPlan = $this->productplan->getById($plan->parent);
|
||||
if($plan->parent == '-1') $this->view->childrenPlans = $this->productplan->getChildren($plan->id);
|
||||
|
||||
if($plan->branch > 0) $this->view->branchStatus = $this->loadModel('branch')->getById($plan->branch, $plan->product, 'status');
|
||||
|
||||
$storyIdList = array();
|
||||
$modulePairs = $this->loadModel('tree')->getOptionMenu($plan->product, 'story', 0, 'all');
|
||||
foreach($planStories as $story)
|
||||
@@ -675,7 +679,7 @@ class productplan extends control
|
||||
$this->config->product->search['style'] = 'simple';
|
||||
$this->config->product->search['params']['product']['values'] = $products + array('all' => $this->lang->product->allProductsOfProject);
|
||||
$this->config->product->search['params']['plan']['values'] = $this->productplan->getPairsForStory($plan->product, $plan->branch, 'skipParent|withMainPlan');
|
||||
$this->config->product->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($plan->product, 'story', 0, $plan->branch);
|
||||
$this->config->product->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($plan->product, 'story', 0, 'all');
|
||||
$storyStatusList = $this->lang->story->statusList;
|
||||
unset($storyStatusList['closed']);
|
||||
$this->config->product->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => $storyStatusList);
|
||||
@@ -687,8 +691,9 @@ class productplan extends control
|
||||
else
|
||||
{
|
||||
$this->config->product->search['fields']['branch'] = $this->lang->product->branch;
|
||||
$branchName = $this->loadModel('branch')->getById($plan->branch);
|
||||
$branches = array('' => '', BRANCH_MAIN => $this->lang->branch->main, $plan->branch => $branchName);
|
||||
|
||||
$branchPairs = $this->dao->select('id, name')->from(TABLE_BRANCH)->where('id')->in($plan->branch)->fetchPairs();
|
||||
$branches = array('' => '', BRANCH_MAIN => $this->lang->branch->main) + $branchPairs;
|
||||
$this->config->product->search['params']['branch']['values'] = $branches;
|
||||
}
|
||||
$this->loadModel('search')->setSearchParams($this->config->product->search);
|
||||
@@ -809,9 +814,12 @@ class productplan extends control
|
||||
$this->config->bug->search['style'] = 'simple';
|
||||
$this->config->bug->search['params']['plan']['values'] = $this->productplan->getPairsForStory($productID, $plan->branch, 'skipParent|withMainPlan');
|
||||
$this->config->bug->search['params']['execution']['values'] = $this->loadModel('product')->getExecutionPairsByProduct($plan->product, $plan->branch);
|
||||
$this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getBuildPairs($productID, $branch = 'all', $params = '');
|
||||
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->build->getBuildPairs($productID, $branch = 'all', $params = '');
|
||||
$this->config->bug->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($plan->product, 'bug', 0, 'all');
|
||||
$this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getBuildPairs($productID, $branch = 'all', $params = 'releasetag');
|
||||
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values'];
|
||||
$this->config->bug->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($plan->product, 'bug', 0, $plan->branch);
|
||||
$this->config->bug->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($plan->product, 'bug', 0, 'all');
|
||||
$this->config->bug->search['params']['project']['values'] = $this->product->getProjectPairsByProduct($productID, $plan->branch);
|
||||
|
||||
unset($this->config->bug->search['fields']['product']);
|
||||
@@ -823,8 +831,9 @@ class productplan extends control
|
||||
else
|
||||
{
|
||||
$this->config->bug->search['fields']['branch'] = $this->lang->product->branch;
|
||||
$branchName = $this->loadModel('branch')->getById($plan->branch);
|
||||
$branches = array('' => '', BRANCH_MAIN => $this->lang->branch->main, $plan->branch => $branchName);
|
||||
|
||||
$branchPairs = $this->dao->select('id, name')->from(TABLE_BRANCH)->where('id')->in($plan->branch)->fetchPairs();
|
||||
$branches = array('' => '', BRANCH_MAIN => $this->lang->branch->main) + $branchPairs;
|
||||
$this->config->bug->search['params']['branch']['values'] = $branches;
|
||||
}
|
||||
$this->loadModel('search')->setSearchParams($this->config->bug->search);
|
||||
@@ -891,13 +900,13 @@ class productplan extends control
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize story when edit plan.
|
||||
* Unlink story and bug when edit branch of plan.
|
||||
* @param int $planID
|
||||
* @param int $oldBranch
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function syncStory($changes)
|
||||
protected function unlinkOldBranch($changes)
|
||||
{
|
||||
foreach($changes as $planID => $changes)
|
||||
{
|
||||
@@ -913,38 +922,66 @@ class productplan extends control
|
||||
}
|
||||
}
|
||||
$planStories = $this->loadModel('story')->getPlanStories($planID, 'all');
|
||||
$planBugs = $this->loadModel('bug')->getPlanBugs($planID, 'all');
|
||||
if($oldBranch)
|
||||
{
|
||||
foreach($planStories as $storyID => $story)
|
||||
{
|
||||
if($story->branch and $story->branch != $newBranch) $this->productplan->unlinkStory($storyID, $planID);
|
||||
if($story->branch and strpos(",$newBranch,", ",$story->branch,") === false) $this->productplan->unlinkStory($storyID, $planID);
|
||||
}
|
||||
|
||||
foreach($planBugs as $bugID => $bug)
|
||||
{
|
||||
if($bug->branch and strpos(",$newBranch,", ",$bug->branch,") === false) $this->productplan->unlinkBug($bugID, $planID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Get conflict story.
|
||||
* AJAX: Get conflict story and bug.
|
||||
*
|
||||
* @param int $planID
|
||||
* @param int $branch
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxGetConflictStory($planID, $newBranch)
|
||||
public function ajaxGetConflict($planID, $newBranch)
|
||||
{
|
||||
$plan = $this->productplan->getByID($planID);
|
||||
$oldBranch = $plan->branch;
|
||||
$planStories = $this->loadModel('story')->getPlanStories($planID, 'all');
|
||||
$conflictStoryIdList = '';
|
||||
if($newBranch == '') return;
|
||||
|
||||
$plan = $this->productplan->getByID($planID);
|
||||
$oldBranch = $plan->branch;
|
||||
$planStories = $this->loadModel('story')->getPlanStories($planID, 'all');
|
||||
$planBugs = $this->loadModel('bug')->getPlanBugs($planID, 'all');
|
||||
|
||||
$conflictStoryCounts = 0;
|
||||
$conflictBugCounts = 0;
|
||||
if($oldBranch)
|
||||
{
|
||||
foreach($planStories as $storyID => $story)
|
||||
{
|
||||
if($story->branch and $story->branch != $newBranch) $conflictStoryIdList .= '[' . $storyID . ']';
|
||||
if($story->branch and strpos(",$newBranch,", ",$story->branch,") === false) $conflictStoryCounts ++;
|
||||
}
|
||||
|
||||
foreach($planBugs as $bugID => $bug)
|
||||
{
|
||||
if($bug->branch and strpos(",$newBranch,", ",$bug->branch,") === false) $conflictBugCounts ++;
|
||||
}
|
||||
}
|
||||
if($conflictStoryIdList != '') printf($this->lang->story->confirmChangePlan, $conflictStoryIdList);
|
||||
|
||||
if($conflictStoryCounts and $conflictBugCounts)
|
||||
{
|
||||
printf($this->lang->productplan->confirmChangePlan, $conflictStoryCounts, $conflictBugCounts);
|
||||
}
|
||||
elseif($conflictStoryCounts)
|
||||
{
|
||||
printf($this->lang->productplan->confirmRemoveStory, $conflictStoryCounts);
|
||||
}
|
||||
elseif($conflictBugCounts)
|
||||
{
|
||||
printf($this->lang->productplan->confirmRemoveBug, $conflictBugCounts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -967,12 +1004,56 @@ class productplan extends control
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $branch
|
||||
* @param int $planID
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function ajaxGetTopPlan($productID, $branch = 0)
|
||||
public function ajaxGetTopPlan($productID, $branch = 0, $planID = 0)
|
||||
{
|
||||
$parentPlanPairs = $this->productplan->getTopPlanPairs($productID, $branch);
|
||||
return print(html::select('parent', array('0' => '') + $parentPlanPairs, '', 'class="form-control"'));
|
||||
if(isset($parentPlanPairs[$planID])) unset($parentPlanPairs[$planID]);
|
||||
return print(html::select('parent', array(0 => '') + $parentPlanPairs, 0, 'class="form-control"'));
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Get diff branches tips.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $parentID
|
||||
* @param string $branches
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxGetDiffBranchesTip($productID = 0, $parentID = 0, $branches = '')
|
||||
{
|
||||
if(empty($parentID) or empty($productID)) return;
|
||||
|
||||
/* If it has children, return. */
|
||||
$parentBranch = $this->productplan->getByID($parentID);
|
||||
if($parentBranch->parent == '-1') return;
|
||||
|
||||
/* Find diff branches between parent plan and child plan. */
|
||||
$diffBranches = array();
|
||||
$diffBranchesTip = '';
|
||||
$product = $this->loadModel('product')->getByID($productID);
|
||||
$branchPairs = $this->loadModel('branch')->getPairs($productID);
|
||||
foreach(explode(',', $parentBranch->branch) as $parentBranchID)
|
||||
{
|
||||
if(empty($parentBranchID)) continue;
|
||||
if(strpos(",$branches,", ",$parentBranchID,") === false)
|
||||
{
|
||||
$diffBranches[$parentBranchID] = $parentBranchID;
|
||||
$diffBranchesTip .= "{$branchPairs[$parentBranchID]},";
|
||||
}
|
||||
}
|
||||
if(empty($diffBranchesTip)) return;
|
||||
|
||||
/* Find stories and bugs in diff branches. */
|
||||
$unlinkStories = $this->dao->select('*')->from(TABLE_STORY)->where('branch')->in($diffBranches)->andWhere("CONCAT(',', plan, ',')")->like("%,{$parentID},%")->fetchAll('id');
|
||||
$unlinkBugs = $this->dao->select('*')->from(TABLE_BUG)->where('branch')->in($diffBranches)->andWhere('plan')->eq($parentID)->fetchAll('id');
|
||||
if(empty($unlinkStories) and empty($unlinkBugs)) return;
|
||||
|
||||
$this->lang->productplan->diffBranchesTip = str_replace('@branch@', $this->lang->product->branchName[$product->type], $this->lang->productplan->diffBranchesTip);
|
||||
printf($this->lang->productplan->diffBranchesTip, trim($diffBranchesTip, ','));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.table td.content div {height: 25px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; float: left; max-width: calc(100% - 20px);}
|
||||
.table td.content .more {position: absolute; right: 16px;}
|
||||
.table td.content .more .icon {color: #838a9d;}
|
||||
td.c-branch {overflow: hidden; text-align: left !important; text-overflow: ellipsis; white-space: nowrap;}
|
||||
td.c-branch {overflow: hidden; text-align: left !important; white-space: nowrap;}
|
||||
|
||||
.table-children {border-left: 2px solid #cbd0db; border-right: 2px solid #cbd0db;}
|
||||
.table tbody > tr.table-children.table-child-top {border-top: 2px solid #cbd0db;}
|
||||
|
||||
@@ -24,17 +24,17 @@ function changeDate(planID)
|
||||
* Get conflict stories.
|
||||
*
|
||||
* @param int $planID
|
||||
* @param int $branch
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function getConflictStories(planID, branch)
|
||||
function getConflictStories(planID)
|
||||
{
|
||||
$.get(createLink('productplan', 'ajaxGetConflictStory', 'planID=' + planID + '&newBranch=' + branch), function(conflictStories)
|
||||
var newBranch = $('#branch' + planID).val() ? $('#branch' + planID).val().toString() : '';
|
||||
$.get(createLink('productplan', 'ajaxGetConflict', 'planID=' + planID + '&newBranch=' + newBranch), function(conflictStories)
|
||||
{
|
||||
if(conflictStories != '' && !confirm(conflictStories))
|
||||
{
|
||||
$('#branch' + planID).val(oldBranch[planID]);
|
||||
$('#branch' + planID).val(oldBranch[planID].split(','));
|
||||
$('#branch' + planID).trigger("chosen:updated");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -65,9 +65,14 @@ $('#future').on('change', function()
|
||||
|
||||
$('#branch').change(function()
|
||||
{
|
||||
var branchID = $(this).val();
|
||||
var lastPlanLink = createLink('productplan', 'ajaxGetLast', "productID=" + productID + "&branch=" + branchID);
|
||||
var topPlanLink = createLink('productplan', 'ajaxGetTopPlan', "productID=" + productID + "&branch=" + branchID);
|
||||
if(parentPlanID) return;
|
||||
|
||||
var branchIdList = $(this).val();
|
||||
if(!branchIdList) return;
|
||||
|
||||
var branchIdList = branchIdList.toString();
|
||||
var lastPlanLink = createLink('productplan', 'ajaxGetLast', "productID=" + productID + "&branch=" + branchIdList);
|
||||
var topPlanLink = createLink('productplan', 'ajaxGetTopPlan', "productID=" + productID + "&branch=" + branchIdList);
|
||||
|
||||
$.post(lastPlanLink, function(data)
|
||||
{
|
||||
@@ -83,3 +88,18 @@ $('#branch').change(function()
|
||||
$('#parent').chosen();
|
||||
})
|
||||
});
|
||||
|
||||
$('#submit').click(function()
|
||||
{
|
||||
var parentPlan = $('#parent').val();
|
||||
var branches = $('#branch').val();
|
||||
if(parentPlan != 0 && branches)
|
||||
{
|
||||
link = createLink('productplan', 'ajaxGetDiffBranchesTip', "produtID=" + productID + "&parentID=" + parentPlan + "&branches=" + branches.toString());
|
||||
$.post(link, function(diffBranchesTip)
|
||||
{
|
||||
if((diffBranchesTip != '' && confirm(diffBranchesTip)) || !diffBranchesTip) $('form#dataform').submit();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,34 +17,32 @@ function convertStringToDate(dateString)
|
||||
* Get conflict stories.
|
||||
*
|
||||
* @param int $planID
|
||||
* @param int $branch
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function getConflictStories(planID, branch)
|
||||
function getConflictStories(planID)
|
||||
{
|
||||
$.get(createLink('productplan', 'ajaxGetConflictStory', 'planID=' + planID + '&newBranch=' + branch), function(conflictStories)
|
||||
var newBranch = $('#branch').val() ? $('#branch').val().toString() : '';
|
||||
$.get(createLink('productplan', 'ajaxGetConflict', 'planID=' + planID + '&newBranch=' + newBranch), function(conflictStories)
|
||||
{
|
||||
if(conflictStories != '')
|
||||
{
|
||||
var result = confirm(conflictStories) ? true : false;
|
||||
if(!result)
|
||||
{
|
||||
$('#branch').val(oldBranch[planID]);
|
||||
newBranch = oldBranch[planID];
|
||||
$('#branch').val(oldBranch[planID].split(','));
|
||||
$('#branch').trigger("chosen:updated");
|
||||
}
|
||||
}
|
||||
|
||||
if(conflictStories == '' || result)
|
||||
var link = createLink('productplan', 'ajaxGetTopPlan', "productID=" + productID + "&branch=" + newBranch + "&planID=" + planID);
|
||||
$.post(link, function(data)
|
||||
{
|
||||
var link = createLink('productplan', 'ajaxGetTopPlan', "productID=" + productID + "&branch=" + branch);
|
||||
$.post(link, function(data)
|
||||
{
|
||||
$('#parent').replaceWith(data);
|
||||
$('#parent_chosen').remove();
|
||||
$('#parent').chosen();
|
||||
})
|
||||
}
|
||||
$('#parent').replaceWith(data);
|
||||
$('#parent_chosen').remove();
|
||||
$('#parent').chosen();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,5 +86,19 @@ $('#future').on('change', function()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('#future').change();
|
||||
|
||||
$('#submit').click(function()
|
||||
{
|
||||
var parentPlan = $('#parent').val();
|
||||
var branches = $('#branch').val();
|
||||
if(parentPlan != 0 && branches)
|
||||
{
|
||||
link = createLink('productplan', 'ajaxGetDiffBranchesTip', "produtID=" + productID + "&parentID=" + parentPlan + "&branches=" + branches.toString());
|
||||
$.post(link, function(diffBranchesTip)
|
||||
{
|
||||
if((diffBranchesTip != '' && confirm(diffBranchesTip)) || !diffBranchesTip) $('form#dataform').submit();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -75,6 +75,9 @@ $lang->productplan->projectNotEmpty = 'Project cannot be empty.';
|
||||
$lang->productplan->nextStep = "Next step";
|
||||
$lang->productplan->summary = "Total: <strong>%s</strong>, Parents: <strong>%s</strong>, Children: <strong>%s</strong>,Independent: <strong>%s</strong>.";
|
||||
$lang->productplan->checkedSummary = "Seleted: <strong>%total%</strong>, Parents: <strong>%parent%</strong>, Children: <strong>%child%</strong>, Independent: <strong>%independent%</strong>.";
|
||||
$lang->productplan->confirmChangePlan = "After the branch is unlinked, %s {$lang->SRCommon} and %s bugs under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
$lang->productplan->confirmRemoveStory = "After the branch is unlinked, %s {$lang->SRCommon} under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
$lang->productplan->confirmRemoveBug = "After the branch is unlinked, %s bugs under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
|
||||
$lang->productplan->id = 'ID';
|
||||
$lang->productplan->product = $lang->productCommon;
|
||||
@@ -136,6 +139,7 @@ $lang->productplan->beginGreaterChildTip = "The start date of the parent plan[%s
|
||||
$lang->productplan->endLetterChildTip = "The completion date of the parent plan[%s]: %s, cannot be less than the completion date of the child plan: %s.";
|
||||
$lang->productplan->beginLetterParentTip = "The start date of the child plan[%s]: %s, cannot be less than the start date of the parent plan: %s.";
|
||||
$lang->productplan->endGreaterParentTip = "The completion date of the child plan[%s]: %s, cannot be greater than the completion date of the parent plan: %s.";
|
||||
$lang->productplan->diffBranchesTip = "The @branch@ %s of parent plan is not linked with the child plan. @branch@'s stories and bugs whill be removed from the plan. Do you want to save?";
|
||||
|
||||
$lang->productplan->featureBar['browse']['all'] = 'Alle';
|
||||
$lang->productplan->featureBar['browse']['undone'] = 'Undone';
|
||||
|
||||
@@ -75,6 +75,9 @@ $lang->productplan->projectNotEmpty = 'Project cannot be empty.';
|
||||
$lang->productplan->nextStep = "Next step";
|
||||
$lang->productplan->summary = "Total: <strong>%s</strong>, Parents: <strong>%s</strong>, Children: <strong>%s</strong>,Independent: <strong>%s</strong>.";
|
||||
$lang->productplan->checkedSummary = "Seleted: <strong>%total%</strong>, Parents: <strong>%parent%</strong>, Children: <strong>%child%</strong>, Independent: <strong>%independent%</strong>.";
|
||||
$lang->productplan->confirmChangePlan = "After the branch is unlinked, %s {$lang->SRCommon} and %s bugs under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
$lang->productplan->confirmRemoveStory = "After the branch is unlinked, %s {$lang->SRCommon} under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
$lang->productplan->confirmRemoveBug = "After the branch is unlinked, %s bugs under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
|
||||
$lang->productplan->id = 'ID';
|
||||
$lang->productplan->product = $lang->productCommon;
|
||||
@@ -136,6 +139,7 @@ $lang->productplan->beginGreaterChildTip = "The start date of the parent plan[%s
|
||||
$lang->productplan->endLetterChildTip = "The completion date of the parent plan[%s]: %s, cannot be less than the completion date of the child plan: %s.";
|
||||
$lang->productplan->beginLetterParentTip = "The start date of the child plan[%s]: %s, cannot be less than the start date of the parent plan: %s.";
|
||||
$lang->productplan->endGreaterParentTip = "The completion date of the child plan[%s]: %s, cannot be greater than the completion date of the parent plan: %s.";
|
||||
$lang->productplan->diffBranchesTip = "The @branch@ %s of parent plan is not linked with the child plan. @branch@'s stories and bugs whill be removed from the plan. Do you want to save?";
|
||||
|
||||
$lang->productplan->featureBar['browse']['all'] = 'All';
|
||||
$lang->productplan->featureBar['browse']['undone'] = 'Undone';
|
||||
|
||||
@@ -75,6 +75,9 @@ $lang->productplan->projectNotEmpty = 'Project cannot be empty.';
|
||||
$lang->productplan->nextStep = "Next step";
|
||||
$lang->productplan->summary = "Total: <strong>%s</strong>, Parents: <strong>%s</strong>, Children: <strong>%s</strong>,Independent: <strong>%s</strong>.";
|
||||
$lang->productplan->checkedSummary = "Seleted: <strong>%total%</strong>, Parents: <strong>%parent%</strong>, Children: <strong>%child%</strong>, Independent: <strong>%independent%</strong>.";
|
||||
$lang->productplan->confirmChangePlan = "After the branch is unlinked, %s {$lang->SRCommon} and %s bugs under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
$lang->productplan->confirmRemoveStory = "After the branch is unlinked, %s {$lang->SRCommon} under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
$lang->productplan->confirmRemoveBug = "After the branch is unlinked, %s bugs under the branch will be removed from the plan at the same time, so still want to unassociate?";
|
||||
|
||||
$lang->productplan->id = 'ID';
|
||||
$lang->productplan->product = $lang->productCommon;
|
||||
@@ -136,6 +139,7 @@ $lang->productplan->beginGreaterChildTip = "The start date of the parent plan[%s
|
||||
$lang->productplan->endLetterChildTip = "The completion date of the parent plan[%s]: %s, cannot be less than the completion date of the child plan: %s.";
|
||||
$lang->productplan->beginLetterParentTip = "The start date of the child plan[%s]: %s, cannot be less than the start date of the parent plan: %s.";
|
||||
$lang->productplan->endGreaterParentTip = "The completion date of the child plan[%s]: %s, cannot be greater than the completion date of the parent plan: %s.";
|
||||
$lang->productplan->diffBranchesTip = "The @branch@ %s of parent plan is not linked with the child plan. @branch@'s stories and bugs whill be removed from the plan. Do you want to save?";
|
||||
|
||||
$lang->productplan->featureBar['browse']['all'] = 'Tous';
|
||||
$lang->productplan->featureBar['browse']['undone'] = 'Undone';
|
||||
|
||||
@@ -75,6 +75,9 @@ $lang->productplan->projectNotEmpty = '所属项目不能为空。';
|
||||
$lang->productplan->nextStep = "下一步";
|
||||
$lang->productplan->summary = "本页共 <strong>%s</strong> 个计划,父计划 <strong>%s</strong>,子计划 <strong>%s</strong>,独立计划 <strong>%s</strong>。";
|
||||
$lang->productplan->checkedSummary = "共选中 <strong>%total%</strong> 个计划,父计划 <strong>%parent%</strong>,子计划 <strong>%child%</strong>,独立计划 <strong>%independent%</strong>。";
|
||||
$lang->productplan->confirmChangePlan = "分支解除关联后,分支下的%s个{$lang->SRCommon}和%s个Bug将同步从计划中移除,是否解除?";
|
||||
$lang->productplan->confirmRemoveStory = "分支解除关联后,分支下的%s个{$lang->SRCommon}将同步从计划中移除,是否解除?";
|
||||
$lang->productplan->confirmRemoveBug = "分支解除关联后,分支下的%s个Bug将同步从计划中移除,是否解除?";
|
||||
|
||||
$lang->productplan->id = '编号';
|
||||
$lang->productplan->product = $lang->productCommon;
|
||||
@@ -136,6 +139,7 @@ $lang->productplan->beginGreaterChildTip = "父计划[%s]的开始日期:%s,
|
||||
$lang->productplan->endLetterChildTip = "父计划[%s]的完成日期:%s,不能小于子计划的完成日期: %s";
|
||||
$lang->productplan->beginLetterParentTip = "子计划[%s]的开始日期:%s,不能小于父计划的开始日期: %s";
|
||||
$lang->productplan->endGreaterParentTip = "子计划[%s]的完成日期:%s,不能大于父计划的完成日期: %s";
|
||||
$lang->productplan->diffBranchesTip = "父计划的@branch@ %s 未被子计划关联,对应@branch@的需求和bug将自动从计划中移除,是否保存?";
|
||||
|
||||
$lang->productplan->featureBar['browse']['all'] = '全部';
|
||||
$lang->productplan->featureBar['browse']['undone'] = '未完成';
|
||||
|
||||
+228
-104
@@ -52,15 +52,28 @@ class productplanModel extends model
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function getLast($productID, $branch = 0, $parent = 0)
|
||||
public function getLast($productID, $branch = '', $parent = 0)
|
||||
{
|
||||
$branchQuery = '';
|
||||
if($branch !== '')
|
||||
{
|
||||
$branchQuery .= '(';
|
||||
$branchCount = count(explode(',', $branch));
|
||||
foreach(explode(',', $branch) as $index => $branchID)
|
||||
{
|
||||
$branchQuery .= "CONCAT(',', branch, ',') LIKE '%,$branchID,%'";
|
||||
if($index < $branchCount - 1) $branchQuery .= ' AND ';
|
||||
}
|
||||
$branchQuery .= ')';
|
||||
}
|
||||
|
||||
return $this->dao->select('*')->from(TABLE_PRODUCTPLAN)
|
||||
->where('deleted')->eq(0)
|
||||
->beginIF($parent <= 0)->andWhere('parent')->le((int)$parent)->fi()
|
||||
->beginIF($parent > 0)->andWhere('parent')->eq((int)$parent)->fi()
|
||||
->andWhere('product')->eq((int)$productID)
|
||||
->andWhere('end')->ne($this->config->productplan->future)
|
||||
->andWhere('branch')->eq($branch)
|
||||
->beginIF($branch !== '' and !empty($branchQuery))->andWhere($branchQuery)->fi()
|
||||
->orderBy('end desc')
|
||||
->limit(1)
|
||||
->fetch();
|
||||
@@ -216,9 +229,22 @@ class productplanModel extends model
|
||||
*/
|
||||
public function getTopPlanPairs($productID, $branch = '', $exclude = '')
|
||||
{
|
||||
$branchQuery = '';
|
||||
if($branch !== '')
|
||||
{
|
||||
$branchQuery .= '(';
|
||||
$branchCount = count(explode(',', $branch));
|
||||
foreach(explode(',', $branch) as $index => $branchID)
|
||||
{
|
||||
$branchQuery .= "CONCAT(',', branch, ',') LIKE '%,$branchID,%'";
|
||||
if($index < $branchCount - 1) $branchQuery .= ' AND ';
|
||||
}
|
||||
$branchQuery .= ')';
|
||||
}
|
||||
|
||||
$planPairs = $this->dao->select("id,title")->from(TABLE_PRODUCTPLAN)
|
||||
->where('product')->eq($productID)
|
||||
->beginIF($branch !== '')->andWhere('branch')->eq($branch)->fi()
|
||||
->beginIF($branch !== '' and !empty($branchQuery))->andWhere($branchQuery)->fi()
|
||||
->andWhere('parent')->le(0)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF($exclude)->andWhere('status')->notin($exclude)
|
||||
@@ -240,28 +266,43 @@ class productplanModel extends model
|
||||
*/
|
||||
public function getPairs($product = 0, $branch = '', $param = '', $skipParent = false)
|
||||
{
|
||||
$date = date('Y-m-d');
|
||||
$plans = $this->dao->select('t1.id,t1.title,t1.parent,t1.begin,t1.end,t2.name as branchName,t3.type as productType')->from(TABLE_PRODUCTPLAN)->alias('t1')
|
||||
->leftJoin(TABLE_BRANCH)->alias('t2')->on('t2.id=t1.branch')
|
||||
$this->app->loadLang('branch');
|
||||
|
||||
$date = date('Y-m-d');
|
||||
|
||||
$branchQuery = '';
|
||||
if($branch !== '' and $branch != 'all')
|
||||
{
|
||||
$branchQuery .= '(';
|
||||
$branchCount = count(explode(',', $branch));
|
||||
foreach(explode(',', $branch) as $index => $branchID)
|
||||
{
|
||||
$branchQuery .= "FIND_IN_SET('$branchID', t1.branch)";
|
||||
if($index < $branchCount - 1) $branchQuery .= ' OR ';
|
||||
}
|
||||
$branchQuery .= ')';
|
||||
}
|
||||
|
||||
$plans = $this->dao->select('t1.id,t1.title,t1.parent,t1.begin,t1.end,t3.type as productType,t1.branch')->from(TABLE_PRODUCTPLAN)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t3.id=t1.product')
|
||||
->where('t1.product')->in($product)
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->beginIF($branch !== '' and $branch != 'all')->andWhere('t1.branch')->in("0,$branch")->fi()
|
||||
->beginIF(!empty($branchQuery))->andWhere($branchQuery)->fi()
|
||||
->beginIF(strpos($param, 'unexpired') !== false)->andWhere('t1.end')->ge($date)->fi()
|
||||
->beginIF(strpos($param, 'noclosed') !== false)->andWhere('t1.status')->ne('closed')->fi()
|
||||
->orderBy('t1.begin desc')
|
||||
->fetchAll('id');
|
||||
|
||||
$plans = $this->reorder4Children($plans);
|
||||
$plans = $this->relationBranch($plans);
|
||||
$planPairs = array();
|
||||
$this->app->loadLang('branch');
|
||||
foreach($plans as $plan)
|
||||
{
|
||||
if($skipParent and $plan->parent == '-1') continue;
|
||||
if($plan->parent > 0 and isset($plans[$plan->parent])) $plan->title = $plans[$plan->parent]->title . ' /' . $plan->title;
|
||||
$planPairs[$plan->id] = $plan->title . " [{$plan->begin} ~ {$plan->end}]";
|
||||
if($plan->begin == $this->config->productplan->future and $plan->end == $this->config->productplan->future) $planPairs[$plan->id] = $plan->title . ' ' . $this->lang->productplan->future;
|
||||
if($plan->productType != 'normal') $planPairs[$plan->id] = ($plan->branchName ? $plan->branchName : $this->lang->branch->main) . ' / ' . $planPairs[$plan->id];
|
||||
if($plan->productType != 'normal') $planPairs[$plan->id] = $planPairs[$plan->id] . ' / ' . ($plan->branchName ? $plan->branchName : $this->lang->branch->main);
|
||||
}
|
||||
return array('' => '') + $planPairs;
|
||||
}
|
||||
@@ -280,12 +321,26 @@ class productplanModel extends model
|
||||
$date = date('Y-m-d');
|
||||
$param = strtolower($param);
|
||||
$branch = strpos($param, 'withmainplan') !== false ? "0,$branch" : $branch;
|
||||
|
||||
$branchQuery = '';
|
||||
if($branch !== '' and $branch != 'all')
|
||||
{
|
||||
$branchQuery .= '(';
|
||||
$branchCount = count(explode(',', $branch));
|
||||
foreach(explode(',', $branch) as $index => $branchID)
|
||||
{
|
||||
$branchQuery .= "FIND_IN_SET('$branchID', branch)";
|
||||
if($index < $branchCount - 1) $branchQuery .= ' OR ';
|
||||
}
|
||||
$branchQuery .= ')';
|
||||
}
|
||||
|
||||
$plans = $this->dao->select('id,title,parent,begin,end')->from(TABLE_PRODUCTPLAN)
|
||||
->where('product')->in($product)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF(strpos($param, 'unexpired') !== false)->andWhere('end')->ge($date)->fi()
|
||||
->beginIF(strpos($param, 'noclosed') !== false)->andWhere('status')->ne('closed')->fi()
|
||||
->beginIF($branch !== 'all' and $branch !== '')->andWhere("branch")->in($branch)->fi()
|
||||
->beginIF($branch !== 'all' and $branch !== '')->andWhere($branchQuery)->fi()
|
||||
->orderBy('begin desc')
|
||||
->fetchAll('id');
|
||||
|
||||
@@ -310,7 +365,7 @@ class productplanModel extends model
|
||||
/**
|
||||
* Get plans for products
|
||||
*
|
||||
* @param int $products
|
||||
* @param array $products
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
@@ -348,32 +403,39 @@ class productplanModel extends model
|
||||
{
|
||||
$date = date('Y-m-d');
|
||||
$param = strtolower($param);
|
||||
$plans = $this->dao->select('*')->from(TABLE_PRODUCTPLAN)
|
||||
->where('deleted')->eq(0)
|
||||
->beginIF($products)->andWhere('product')->in($products)->fi()
|
||||
->beginIF(strpos($param, 'unexpired') !== false)->andWhere('end')->ge($date)->fi()
|
||||
->orderBy($orderBy)
|
||||
$plans = $this->dao->select('t1.*,t2.type as productType')->from(TABLE_PRODUCTPLAN)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t2.id=t1.product')
|
||||
->where('t1.deleted')->eq(0)
|
||||
->beginIF($products)->andWhere('t1.product')->in($products)->fi()
|
||||
->beginIF(strpos($param, 'unexpired') !== false)->andWhere('t1.end')->ge($date)->fi()
|
||||
->orderBy('t1.' . $orderBy)
|
||||
->fetchAll('id');
|
||||
|
||||
if(!empty($plans) and $field == 'name') $plans = $this->reorder4Children($plans);
|
||||
|
||||
$plans = $this->relationBranch($plans);
|
||||
|
||||
$parentTitle = array();
|
||||
$planGroup = array();
|
||||
foreach($plans as $plan)
|
||||
{
|
||||
if(!isset($planGroup[$plan->product][$plan->branch])) $planGroup[$plan->product][$plan->branch] = array('' => '');
|
||||
|
||||
if($plan->parent == '-1' and strpos($param, 'skipparent') !== false) continue;
|
||||
|
||||
if($field == 'name')
|
||||
foreach(explode(',', $plan->branch) as $branch)
|
||||
{
|
||||
if($plan->parent > 0 and isset($plans[$plan->parent])) $plan->title = $plans[$plan->parent]->title . ' /' . $plan->title;
|
||||
$planGroup[$plan->product][$plan->branch][$plan->id] = $plan->title . " [{$plan->begin} ~ {$plan->end}]";
|
||||
if($plan->begin == $this->config->productplan->future and $plan->end == $this->config->productplan->future) $planGroup[$plan->product][$plan->branch][$plan->id] = $plan->title . ' ' . $this->lang->productplan->future;
|
||||
}
|
||||
else
|
||||
{
|
||||
$planGroup[$plan->product][$plan->branch][$plan->id] = $plan;
|
||||
if(!isset($planGroup[$plan->product][$branch])) $planGroup[$plan->product][$branch] = array('' => '');
|
||||
|
||||
if($plan->parent == '-1' and strpos($param, 'skipparent') !== false) continue 2;
|
||||
|
||||
if($field == 'name')
|
||||
{
|
||||
if($plan->parent > 0 and isset($plans[$plan->parent])) $plan->title = $plans[$plan->parent]->title . ' /' . $plan->title;
|
||||
$planGroup[$plan->product][$branch][$plan->id] = $plan->title . " [{$plan->begin} ~ {$plan->end}]";
|
||||
if($plan->begin == $this->config->productplan->future and $plan->end == $this->config->productplan->future) $planGroup[$plan->product][$branch][$plan->id] = $plan->title . ' ' . $this->lang->productplan->future;
|
||||
if($plan->productType != 'normal') $planGroup[$plan->product][$branch][$plan->id] = $planGroup[$plan->product][$branch][$plan->id] . ' / ' . ($plan->branchName ? $plan->branchName : $this->lang->branch->main);
|
||||
}
|
||||
else
|
||||
{
|
||||
$planGroup[$plan->product][$branch][$plan->id] = $plan;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $planGroup;
|
||||
@@ -429,10 +491,13 @@ class productplanModel extends model
|
||||
$planPairs = array();
|
||||
foreach($plans as $planID => $plan)
|
||||
{
|
||||
if($skipParent and $plan->parent == '-1') continue;
|
||||
foreach(explode(',', $plan->branch) as $branch)
|
||||
{
|
||||
if($skipParent and $plan->parent == '-1') continue 2;
|
||||
|
||||
if($plan->parent > 0 and isset($plans[$plan->parent])) $plan->title = $plans[$plan->parent]->title . ' /' . $plan->title;
|
||||
$planPairs[$plan->branch][$planID] = $plan->title . ' [' . $plan->begin . '~' . $plan->end . ']';
|
||||
if($plan->parent > 0 and isset($plans[$plan->parent])) $plan->title = $plans[$plan->parent]->title . ' /' . $plan->title;
|
||||
$planPairs[$branch][$planID] = $plan->title . ' [' . $plan->begin . '~' . $plan->end . ']';
|
||||
}
|
||||
}
|
||||
return $planPairs;
|
||||
}
|
||||
@@ -450,9 +515,18 @@ class productplanModel extends model
|
||||
->setIF($this->post->future || empty($_POST['end']), 'end', $this->config->productplan->future)
|
||||
->setDefault('createdBy', $this->app->user->account)
|
||||
->setDefault('createdDate', helper::now())
|
||||
->setDefault('branch', 0)
|
||||
->join('branch', ',')
|
||||
->remove('delta,uid,future')
|
||||
->get();
|
||||
|
||||
$product = $this->loadModel('product')->getByID($plan->product);
|
||||
if($product->type != 'normal' and !isset($_POST['branch']))
|
||||
{
|
||||
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
|
||||
dao::$errors['branch'] = sprintf($this->lang->error->notempty, $this->lang->product->branch);
|
||||
}
|
||||
|
||||
if($plan->parent > 0)
|
||||
{
|
||||
$parentPlan = $this->getByID($plan->parent);
|
||||
@@ -477,8 +551,7 @@ class productplanModel extends model
|
||||
if(dao::isError()) return false;
|
||||
|
||||
$plan = $this->loadModel('file')->processImgURL($plan, $this->config->productplan->editor->create['id'], $this->post->uid);
|
||||
$this->dao->insert(TABLE_PRODUCTPLAN)
|
||||
->data($plan)
|
||||
$this->dao->insert(TABLE_PRODUCTPLAN)->data($plan)
|
||||
->autoCheck()
|
||||
->batchCheck($this->config->productplan->create->requiredFields, 'notempty')
|
||||
->checkIF(!$this->post->future && !empty($_POST['begin']) && !empty($_POST['end']), 'end', 'ge', $plan->begin)
|
||||
@@ -489,24 +562,10 @@ class productplanModel extends model
|
||||
$planID = $this->dao->lastInsertID();
|
||||
$this->file->updateObjectID($this->post->uid, $planID, 'plan');
|
||||
$this->loadModel('score')->create('productplan', 'create', $planID);
|
||||
if(!empty($plan->parent))
|
||||
if(!empty($plan->parent) and $parentPlan->parent == '0')
|
||||
{
|
||||
if($parentPlan->parent == '0')
|
||||
{
|
||||
$this->dao->update(TABLE_PRODUCTPLAN)->set('parent')->eq('-1')->where('id')->eq($plan->parent)->andWhere('parent')->eq('0')->exec();
|
||||
|
||||
/* Transfer stories and bugs linked with the parent plan to the child plan. */
|
||||
$this->dao->update(TABLE_PLANSTORY)->set('plan')->eq($planID)->where('plan')->eq($plan->parent)->exec();
|
||||
$this->dao->update(TABLE_BUG)->set('plan')->eq($planID)->where('plan')->eq($plan->parent)->exec();
|
||||
$stories = $this->dao->select('*')->from(TABLE_STORY)->where("CONCAT(',', plan, ',')")->like("%,{$plan->parent},%")->fetchAll('id');
|
||||
foreach($stories as $storyID => $story)
|
||||
{
|
||||
$storyPlan = str_replace(",{$plan->parent},", ",$planID,", ",$story->plan,");
|
||||
$storyPlan = trim($storyPlan, ',');
|
||||
|
||||
$this->dao->update(TABLE_STORY)->set('plan')->eq($storyPlan)->where('id')->eq($storyID)->exec();
|
||||
}
|
||||
}
|
||||
$plan->id = $planID;
|
||||
$this->transferStoriesAndBugs($plan);
|
||||
}
|
||||
return $planID;
|
||||
}
|
||||
@@ -525,10 +584,19 @@ class productplanModel extends model
|
||||
$plan = fixer::input('post')->stripTags($this->config->productplan->editor->edit['id'], $this->config->allowedTags)
|
||||
->setIF($this->post->future or empty($_POST['begin']), 'begin', $this->config->productplan->future)
|
||||
->setIF($this->post->future or empty($_POST['end']), 'end', $this->config->productplan->future)
|
||||
->setDefault('branch', 0)
|
||||
->join('branch', ',')
|
||||
->add('id', $planID)
|
||||
->remove('delta,uid,future')
|
||||
->get();
|
||||
|
||||
$product = $this->loadModel('product')->getByID($oldPlan->product);
|
||||
if($product->type != 'normal' and $oldPlan->parent != -1 and !isset($_POST['branch']))
|
||||
{
|
||||
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
|
||||
dao::$errors['branch'] = sprintf($this->lang->error->notempty, $this->lang->product->branch);
|
||||
}
|
||||
|
||||
$parentPlan = $this->getByID($plan->parent);
|
||||
$futureTime = $this->config->productplan->future;
|
||||
|
||||
@@ -545,9 +613,9 @@ class productplanModel extends model
|
||||
}
|
||||
elseif($oldPlan->parent == -1 and ($plan->begin != $futureTime or $plan->end != $futureTime))
|
||||
{
|
||||
$childPlans = $this->getChildren($planID);
|
||||
$minBegin = $plan->begin;
|
||||
$maxEnd = $plan->end;
|
||||
$childPlans = $this->getChildren($planID);
|
||||
$minBegin = $plan->begin;
|
||||
$maxEnd = $plan->end;
|
||||
foreach($childPlans as $childID => $childPlan)
|
||||
{
|
||||
$childPlan = isset($plans[$childID]) ? $plans[$childID] : $childPlan;
|
||||
@@ -560,8 +628,7 @@ class productplanModel extends model
|
||||
|
||||
if(dao::isError()) return false;
|
||||
$plan = $this->loadModel('file')->processImgURL($plan, $this->config->productplan->editor->edit['id'], $this->post->uid);
|
||||
$this->dao->update(TABLE_PRODUCTPLAN)
|
||||
->data($plan)
|
||||
$this->dao->update(TABLE_PRODUCTPLAN)->data($plan)
|
||||
->autoCheck()
|
||||
->batchCheck($this->config->productplan->edit->requiredFields, 'notempty')
|
||||
->checkIF(!$this->post->future && !empty($_POST['begin']) && !empty($_POST['end']), 'end', 'ge', $plan->begin)
|
||||
@@ -576,25 +643,7 @@ class productplanModel extends model
|
||||
if(!dao::isError())
|
||||
{
|
||||
$this->file->updateObjectID($this->post->uid, $planID, 'plan');
|
||||
if(!empty($plan->parent))
|
||||
{
|
||||
if($parentPlan->parent == '0')
|
||||
{
|
||||
$this->dao->update(TABLE_PRODUCTPLAN)->set('parent')->eq('-1')->where('id')->eq($plan->parent)->andWhere('parent')->eq('0')->exec();
|
||||
|
||||
/* Transfer stories and bugs linked with the parent plan to the child plan. */
|
||||
$this->dao->update(TABLE_PLANSTORY)->set('plan')->eq($planID)->where('plan')->eq($plan->parent)->exec();
|
||||
$this->dao->update(TABLE_BUG)->set('plan')->eq($planID)->where('plan')->eq($plan->parent)->exec();
|
||||
$stories = $this->dao->select('*')->from(TABLE_STORY)->where("CONCAT(',', plan, ',')")->like("%,{$plan->parent},%")->fetchAll('id');
|
||||
foreach($stories as $storyID => $story)
|
||||
{
|
||||
$storyPlan = str_replace(",{$plan->parent},", ",$planID,", ",$story->plan,");
|
||||
$storyPlan = trim($storyPlan, ',');
|
||||
|
||||
$this->dao->update(TABLE_STORY)->set('plan')->eq($storyPlan)->where('id')->eq($storyID)->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!empty($plan->parent) and $parentPlan->parent == '0') $this->transferStoriesAndBugs($plan);
|
||||
return common::createChanges($oldPlan, $plan);
|
||||
}
|
||||
}
|
||||
@@ -707,13 +756,20 @@ class productplanModel extends model
|
||||
|
||||
$plans = array();
|
||||
$extendFields = $this->getFlowExtendFields();
|
||||
$product = $this->loadModel('product')->getByID($productID);
|
||||
foreach($data->id as $planID)
|
||||
{
|
||||
if($product->type != 'normal' and $oldPlans[$planID]->parent != '-1' and !isset($data->branch[$planID]))
|
||||
{
|
||||
$this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
|
||||
return helper::end(js::error(sprintf($this->lang->error->notempty, $this->lang->product->branch)));
|
||||
}
|
||||
|
||||
$isFuture = isset($data->future[$planID]) ? true : false;
|
||||
if(isset($data->status[$planID]) and $data->status[$planID] != 'wait') $isFuture = false;
|
||||
|
||||
$plan = new stdclass();
|
||||
$plan->branch = isset($data->branch[$planID]) ? $data->branch[$planID] : $oldPlans[$planID]->branch;
|
||||
$plan->branch = isset($data->branch[$planID]) ? join(',', $data->branch[$planID]) : $oldPlans[$planID]->branch;
|
||||
$plan->title = $data->title[$planID];
|
||||
$plan->begin = isset($data->begin[$planID]) ? $data->begin[$planID] : '';
|
||||
$plan->end = isset($data->end[$planID]) ? $data->end[$planID] : '';
|
||||
@@ -721,8 +777,8 @@ class productplanModel extends model
|
||||
$plan->parent = $oldPlans[$planID]->parent;
|
||||
$plan->id = $planID;
|
||||
|
||||
if(empty($plan->title)) return print(js::alert(sprintf($this->lang->productplan->errorNoTitle, $planID)));
|
||||
if($plan->begin > $plan->end and !empty($plan->end)) return print(js::alert(sprintf($this->lang->productplan->beginGeEnd, $planID)));
|
||||
if(empty($plan->title)) return helper::end(js::alert(sprintf($this->lang->productplan->errorNoTitle, $planID)));
|
||||
if($plan->begin > $plan->end and !empty($plan->end)) return helper::end(js::alert(sprintf($this->lang->productplan->beginGeEnd, $planID)));
|
||||
|
||||
if($plan->begin == '') $plan->begin = $this->config->productplan->future;
|
||||
if($plan->end == '') $plan->end = $this->config->productplan->future;
|
||||
@@ -746,14 +802,14 @@ class productplanModel extends model
|
||||
/* Determine whether the begin and end dates of the parent plan and the child plan are correct. */
|
||||
if($parentID > 0)
|
||||
{
|
||||
$parent = isset($plans[$parentID]) ? $plans[$parentID] : $this->getByID($parentID);
|
||||
$parent = isset($plans[$parentID]) ? $plans[$parentID] : $this->getByID($parentID);
|
||||
if($parent->begin != $this->config->productplan->future and $plan->begin != $this->config->productplan->future and $plan->begin < $parent->begin)
|
||||
{
|
||||
return print(js::alert(sprintf($this->lang->productplan->beginLetterParentTip, $planID, $plan->begin, $parent->begin)));
|
||||
return helper::end(js::alert(sprintf($this->lang->productplan->beginLetterParentTip, $planID, $plan->begin, $parent->begin)));
|
||||
}
|
||||
elseif($parent->end != $this->config->productplan->future and $plan->end != $this->config->productplan->future and $plan->end > $parent->end)
|
||||
{
|
||||
return print(js::alert(sprintf($this->lang->productplan->endGreaterParentTip, $planID, $plan->end, $parent->end)));
|
||||
return helper::end(js::alert(sprintf($this->lang->productplan->endGreaterParentTip, $planID, $plan->end, $parent->end)));
|
||||
}
|
||||
}
|
||||
elseif($parentID == -1 and $plan->begin != $this->config->productplan->future)
|
||||
@@ -767,8 +823,8 @@ class productplanModel extends model
|
||||
if($childPlan->begin < $minBegin and $minBegin != $this->config->productplan->future) $minBegin = $childPlan->begin;
|
||||
if($childPlan->end > $maxEnd and $maxEnd != $this->config->productplan->future) $maxEnd = $childPlan->end;
|
||||
}
|
||||
if($minBegin < $plan->begin and $minBegin != $this->config->productplan->future) return print(js::alert(sprintf($this->lang->productplan->beginGreaterChildTip, $planID, $plan->begin, $minBegin)));
|
||||
if($maxEnd > $plan->end and $maxEnd != $this->config->productplan->future) return print(js::alert(sprintf($this->lang->productplan->endLetterChildTip, $planID, $plan->end, $maxEnd)));
|
||||
if($minBegin < $plan->begin and $minBegin != $this->config->productplan->future) return helper::end(js::alert(sprintf($this->lang->productplan->beginGreaterChildTip, $planID, $plan->begin, $minBegin)));
|
||||
if($maxEnd > $plan->end and $maxEnd != $this->config->productplan->future) return helper::end(js::alert(sprintf($this->lang->productplan->endLetterChildTip, $planID, $plan->end, $maxEnd)));
|
||||
}
|
||||
|
||||
$change = common::createChanges($oldPlans[$planID], $plan);
|
||||
@@ -776,7 +832,7 @@ class productplanModel extends model
|
||||
{
|
||||
if($parentID > 0 and !isset($parents[$parentID])) $parents[$parentID] = $parentID;
|
||||
$this->dao->update(TABLE_PRODUCTPLAN)->data($plan)->autoCheck()->checkFlow()->where('id')->eq($planID)->exec();
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
if(dao::isError()) return helper::end(js::error(dao::getError()));
|
||||
$changes[$planID] = $change;
|
||||
}
|
||||
}
|
||||
@@ -918,27 +974,9 @@ class productplanModel extends model
|
||||
if(strpos(",$story->plan,", ",{$planID},") !== false) continue;
|
||||
|
||||
/* Update the plan linked with the story and the order of the story in the plan. */
|
||||
if($this->session->currentProductType == 'normal' or $story->branch != 0 or empty($story->plan))
|
||||
{
|
||||
$this->dao->update(TABLE_STORY)->set("plan")->eq($planID)->where('id')->eq((int)$storyID)->exec();
|
||||
$this->dao->update(TABLE_STORY)->set("plan")->eq($planID)->where('id')->eq((int)$storyID)->exec();
|
||||
|
||||
$this->story->updateStoryOrderOfPlan($storyID, $planID, $story->plan);
|
||||
}
|
||||
else
|
||||
{
|
||||
$branchPlanPairs = $this->dao->select('branch, id as plan')->from(TABLE_PRODUCTPLAN)
|
||||
->where('product')->eq($story->product)
|
||||
->andWhere('id')->in($story->plan)
|
||||
->fetchPairs();
|
||||
if(isset($branchPlanPairs[$plan->branch])) $branchPlanPairs[$plan->branch] = $planID;
|
||||
|
||||
$plansOfStory = implode(',', $branchPlanPairs);
|
||||
$plansOfStory = trim($plansOfStory, ',');
|
||||
|
||||
$this->dao->update(TABLE_STORY)->set("plan")->eq($plansOfStory)->where('id')->eq((int)$storyID)->exec();
|
||||
|
||||
$this->story->updateStoryOrderOfPlan($storyID, $planID);
|
||||
}
|
||||
$this->story->updateStoryOrderOfPlan($storyID, $planID, $story->plan);
|
||||
|
||||
$this->action->create('story', $storyID, 'linked2plan', '', $planID);
|
||||
$this->story->setStage($storyID);
|
||||
@@ -1026,9 +1064,12 @@ class productplanModel extends model
|
||||
$planStory = $this->loadModel('story')->getPlanStories($planID);
|
||||
if(!empty($planStory))
|
||||
{
|
||||
$projectProducts = $this->loadModel('project')->getBranchesByProject($projectID);
|
||||
|
||||
foreach($planStory as $id => $story)
|
||||
{
|
||||
if($story->status == 'draft' or $story->status == 'reviewing')
|
||||
$projectBranches = zget($projectProducts, $story->product, array());
|
||||
if($story->status == 'draft' or $story->status == 'reviewing' or (!empty($story->branch) and !empty($projectBranches) and !isset($projectBranches[$story->branch])))
|
||||
{
|
||||
unset($planStory[$id]);
|
||||
continue;
|
||||
@@ -1080,6 +1121,46 @@ class productplanModel extends model
|
||||
return $plans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get relation branch for plans.
|
||||
*
|
||||
* @param array $plans
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function relationBranch($plans)
|
||||
{
|
||||
if(empty($plans)) return $plans;
|
||||
|
||||
$this->app->loadLang('branch');
|
||||
$branchMap = $this->dao->select('id, name')->from(TABLE_BRANCH)
|
||||
->where('status')->eq('active')
|
||||
->andWhere('deleted')->eq('0')
|
||||
->fetchPairs('id', 'name');
|
||||
$branchMap[BRANCH_MAIN] = $this->lang->branch->main;
|
||||
|
||||
foreach($plans as &$plan)
|
||||
{
|
||||
if($plan->branch)
|
||||
{
|
||||
$branchName = '';
|
||||
foreach(explode(',', $plan->branch) as $planBranch)
|
||||
{
|
||||
$branchName .= isset($branchMap[$planBranch]) ? $branchMap[$planBranch] : '';
|
||||
$branchName .= ',';
|
||||
}
|
||||
|
||||
$plan->branchName = trim($branchName, ',');
|
||||
}
|
||||
else
|
||||
{
|
||||
$plan->branchName = $this->lang->branch->main;
|
||||
}
|
||||
}
|
||||
|
||||
return $plans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Judge an action is clickable or not.
|
||||
*
|
||||
@@ -1246,4 +1327,47 @@ class productplanModel extends model
|
||||
if($product->type == 'normal') unset($this->config->productplan->search['fields']['branch']);
|
||||
$this->loadModel('search')->setSearchParams($this->config->productplan->search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer stories and bugs to new plan.
|
||||
*
|
||||
* @param object $plan
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function transferStoriesAndBugs($plan)
|
||||
{
|
||||
$this->dao->update(TABLE_PRODUCTPLAN)->set('parent')->eq('-1')->where('id')->eq($plan->parent)->andWhere('parent')->eq('0')->exec();
|
||||
|
||||
/* Transfer stories linked with the parent plan to the child plan. */
|
||||
$stories = $this->dao->select('*')->from(TABLE_STORY)->where("CONCAT(',', plan, ',')")->like("%,{$plan->parent},%")->fetchAll('id');
|
||||
$unlinkStories = array();
|
||||
foreach($stories as $storyID => $story)
|
||||
{
|
||||
if(!empty($story->branch) and strpos(",$plan->branch,", ",$story->branch,") === false)
|
||||
{
|
||||
$unlinkStories[$storyID] = $storyID;
|
||||
$storyPlan = str_replace(",{$plan->parent},", ',', ",$story->plan,");
|
||||
}
|
||||
else
|
||||
{
|
||||
$storyPlan = str_replace(",{$plan->parent},", ",$plan->id,", ",$story->plan,");
|
||||
}
|
||||
$storyPlan = trim($storyPlan, ',');
|
||||
|
||||
$this->dao->update(TABLE_STORY)->set('plan')->eq($storyPlan)->where('id')->eq($storyID)->exec();
|
||||
}
|
||||
if(!empty($unlinkStories)) $this->dao->delete()->from(TABLE_PLANSTORY)->where('plan')->eq($plan->parent)->andWhere('story')->in($unlinkStories)->exec();
|
||||
$this->dao->update(TABLE_PLANSTORY)->set('plan')->eq($plan->id)->where('plan')->eq($plan->parent)->exec();
|
||||
|
||||
/* Transfer bugs linked with the parent plan to the child plan. */
|
||||
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where('plan')->eq($plan->parent)->fetchAll('id');
|
||||
$unlinkBugs = array();
|
||||
foreach($bugs as $bugID => $bug)
|
||||
{
|
||||
if(!empty($bug->branch) and strpos(",$plan->branch,", ",$bug->branch,") === false) $unlinkBugs[$bugID] = $bugID;
|
||||
}
|
||||
if(!empty($unlinkBugs)) $this->dao->update(TABLE_BUG)->set('plan')->eq(0)->where('plan')->eq($plan->parent)->andWhere('id')->in($unlinkBugs)->exec();
|
||||
$this->dao->update(TABLE_BUG)->set('plan')->eq($plan->id)->where('plan')->eq($plan->parent)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<tr class='text-center'>
|
||||
<th class='c-id'><?php echo $lang->productplan->id?></th>
|
||||
<?php if($product->type != 'normal'):?>
|
||||
<th class='c-branch'><?php echo $lang->productplan->branch;?></th>
|
||||
<th class='c-branch'><?php echo $lang->product->branch;?></th>
|
||||
<?php endif;?>
|
||||
<th class='required'><?php echo $lang->productplan->title?></th>
|
||||
<th class='c-status'><?php echo $lang->productplan->status?></th>
|
||||
@@ -43,8 +43,18 @@
|
||||
<td class='text-center'><?php echo $plan->id . html::hidden("id[$plan->id]", $plan->id);?></td>
|
||||
<?php if($product->type != 'normal'):?>
|
||||
<td class='text-left'>
|
||||
<?php $disabled = $plan->parent == -1 ? "disabled='disabled'" : '';?>
|
||||
<?php echo html::select("branch[$plan->id]", $plan->parent == '-1' ? '' : $branchTagOption, $plan->branch, "onchange='getConflictStories($plan->id, this.value); 'class='form-control chosen' $disabled");?>
|
||||
<?php
|
||||
$disabled = $plan->parent == -1 ? "disabled='disabled'" : '';
|
||||
$parentBranches = array();
|
||||
if($plan->parent > 0 and isset($parentList[$plan->parent]))
|
||||
{
|
||||
foreach($branchTagOption as $branchID => $branchName)
|
||||
{
|
||||
if(strpos(",{$parentList[$plan->parent]->branch},", ",$branchID,") !== false) $parentBranches[$branchID] = $branchName;
|
||||
}
|
||||
}
|
||||
echo html::select("branch[$plan->id][]", ($plan->parent > 0 and !empty($parentBranches)) ? $parentBranches : $branchTagOption, $plan->branch, "onchange='getConflictStories($plan->id); 'class='form-control chosen' multiple $disabled");
|
||||
?>
|
||||
</td>
|
||||
<?php endif;?>
|
||||
<td title='<?php echo $plan->title?>'><?php echo html::input("title[$plan->id]", $plan->title, "class='form-control'")?></td>
|
||||
|
||||
@@ -161,7 +161,9 @@
|
||||
<td><?php echo zget($statusList, $plan->status)?></td>
|
||||
<?php endif;?>
|
||||
<?php if($this->session->currentProductType != 'normal'):?>
|
||||
<td class='c-branch' title='<?php echo $branchOption[$plan->branch];?>'><?php if($plan->parent != '-1') echo $branchOption[$plan->branch];?></td>
|
||||
<?php $planBranches = '';?>
|
||||
<?php foreach(explode(',', $plan->branch) as $branchID) $planBranches .= $branchOption[$branchID] . ',';?>
|
||||
<td class='c-branch' title='<?php echo trim($planBranches, ',');?>'><?php echo trim($planBranches, ',');?></td>
|
||||
<?php endif;?>
|
||||
<td><?php echo $plan->begin == $config->productplan->future ? $lang->productplan->future : $plan->begin;?></td>
|
||||
<td><?php echo $plan->end == $config->productplan->future ? $lang->productplan->future : $plan->end;?></td>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<?php js::set('weekend', $config->execution->weekend);?>
|
||||
<?php js::set('productID', $productID);?>
|
||||
<?php js::set('lastLang', $lang->productplan->last);?>
|
||||
<?php js::set('parentPlanID', $parent);?>
|
||||
<?php js::import($jsRoot . 'misc/date.js');?>
|
||||
<div id='mainContent'class='main-content'>
|
||||
<div class='center-block'>
|
||||
@@ -35,13 +36,22 @@
|
||||
<th><?php echo $lang->productplan->product;?></th>
|
||||
<td class='muted'><?php echo $product->name;?></td><td></td><td></td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<?php if($product->type != 'normal'):?>
|
||||
<tr>
|
||||
<th><?php echo $lang->product->branch;?></th>
|
||||
<td><?php echo html::select('branch', $branches, $branch, "class='form-control chosen'");?></td><td></td><td></td>
|
||||
<?php
|
||||
if($parent)
|
||||
{
|
||||
foreach($branches as $branchID => $branchName)
|
||||
{
|
||||
if(strpos(",$parentPlan->branch,", ",$branchID,") === false) unset($branches[$branchID]);
|
||||
}
|
||||
}
|
||||
?>
|
||||
<td><?php echo html::select('branch[]', $branches, '', "class='form-control chosen' multiple required");?></td><td></td><td></td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<?php endif;?>
|
||||
<tr>
|
||||
<th><?php echo $lang->productplan->title;?></th>
|
||||
<td><?php echo html::input('title', '', "class='form-control' required");?></td>
|
||||
@@ -50,7 +60,7 @@
|
||||
<?php if(!$parent):?>
|
||||
<tr>
|
||||
<th><?php echo $lang->productplan->parent;?></th>
|
||||
<td><?php echo html::select('parent', array('0' => '') + $parentPlanPairs, '', "class='form-control chosen'");?>
|
||||
<td><?php echo html::select('parent', array(0 => '') + $parentPlanPairs, 0, "class='form-control chosen'");?>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr>
|
||||
@@ -79,7 +89,6 @@
|
||||
<?php echo html::backButton();?>
|
||||
<?php echo html::hidden('product', $product->id);?>
|
||||
<?php if($parent):?>
|
||||
<?php echo html::hidden('branch', $parentPlan->branch);?>
|
||||
<?php echo html::hidden('parent', $parent);?>
|
||||
<?php endif;?>
|
||||
</td>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<?php if($product->type != 'normal' and $plan->parent != '-1'):?>
|
||||
<tr>
|
||||
<th><?php echo $lang->product->branch;?></th>
|
||||
<td><?php echo html::select('branch', $branchTagOption, $plan->branch, "onchange='getConflictStories($plan->id, this.value); 'class='form-control'");?></td><td></td><td></td>
|
||||
<td><?php echo html::select('branch[]', $branchTagOption, $plan->branch, "onchange='getConflictStories($plan->id);' class='form-control chosen' multiple");?></td><td></td><td></td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<?php endif;?>
|
||||
@@ -46,7 +46,7 @@
|
||||
<?php else:?>
|
||||
<tr>
|
||||
<th><?php echo $lang->productplan->parent;?></th>
|
||||
<td><?php echo html::select('parent', array('0' => '') + $parentPlanPairs, $plan->parent, "class='form-control chosen'");?>
|
||||
<td><?php echo html::select('parent', array(0 => '') + $parentPlanPairs, $plan->parent, "class='form-control chosen'");?>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr>
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
<div class='page-title'>
|
||||
<span class='label label-id'><?php echo $plan->id;?></span>
|
||||
<span title='<?php echo $plan->title;?>' class='text'><?php echo $plan->title;?></span>
|
||||
<?php if($product->type !== 'normal') echo "<span title='{$lang->product->branchName[$product->type]}' class='label label-branch label-badge'>" . $branchOption[$branch] . '</span>';?>
|
||||
<?php foreach(explode(',', $branch) as $branchID):?>
|
||||
<?php if($product->type !== 'normal') echo "<span title='{$lang->product->branchName[$product->type]}' class='label label-branch label-badge'>" . $branchOption[$branchID] . '</span>';?>
|
||||
<?php endforeach;?>
|
||||
<span class='label label-info label-badge'>
|
||||
<?php echo ($plan->begin == $config->productplan->future || $plan->end == $config->productplan->future) ? $lang->productplan->future : $plan->begin . '~' . $plan->end;?>
|
||||
</span>
|
||||
@@ -559,7 +561,11 @@
|
||||
<?php if($product->type != 'normal'):?>
|
||||
<tr>
|
||||
<th><?php echo $lang->product->branch;?></th>
|
||||
<td><?php echo $branchOption[$plan->branch];?></td>
|
||||
<?php
|
||||
$branches = '';
|
||||
foreach(explode(',', $branch) as $branchID) $branches .= "{$branchOption[$branchID]},";
|
||||
?>
|
||||
<td><?php echo trim($branches, ',');?></td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr>
|
||||
|
||||
@@ -492,12 +492,9 @@ class project extends control
|
||||
$planIdList = array();
|
||||
foreach($_POST['plans'] as $plans)
|
||||
{
|
||||
foreach($plans as $planList)
|
||||
foreach($plans as $planID)
|
||||
{
|
||||
foreach($planList as $planID)
|
||||
{
|
||||
$planIdList[$planID] = $planID;
|
||||
}
|
||||
$planIdList[$planID] = $planID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,10 +584,8 @@ class project extends control
|
||||
if(!$copyProject->hasProduct) $shadow = 1;
|
||||
foreach($products as $product)
|
||||
{
|
||||
foreach($product->branches as $branch)
|
||||
{
|
||||
$productPlans[$product->id][$branch] = $this->loadModel('productplan')->getPairs($product->id, $branch, 'noclosed', true);
|
||||
}
|
||||
$branches = implode(',', $product->branches);
|
||||
$productPlans[$product->id] = $this->loadModel('productplan')->getPairs($product->id, $branches, 'noclosed', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,9 +717,8 @@ class project extends control
|
||||
|
||||
$withProgram = $this->config->systemMode == 'ALM' ? true : false;
|
||||
|
||||
$linkedBranches = array();
|
||||
$linkedBranchList = array();
|
||||
$productPlans = array(0 => '');
|
||||
$productPlans = array();
|
||||
$branches = $this->project->getBranchesByProject($projectID);
|
||||
$linkedProductIdList = empty($branches) ? '' : array_keys($branches);
|
||||
$allProducts = $this->program->getProductPairs($project->parent, 'all', 'noclosed', '', 0, $withProgram);
|
||||
@@ -743,11 +737,10 @@ class project extends control
|
||||
if(!isset($allProducts[$productID])) $allProducts[$productID] = $linkedProduct->name;
|
||||
foreach($branches[$productID] as $branchID => $branch)
|
||||
{
|
||||
$linkedBranchList[$branchID] = $branchID;
|
||||
$linkedBranches[$productID][$branchID] = $branchID;
|
||||
$linkedBranchList[$branchID] = $branchID;
|
||||
|
||||
if($branch != BRANCH_MAIN) $productPlans[$productID][$branchID] = isset($plans[$productID][BRANCH_MAIN]) ? $plans[$productID][BRANCH_MAIN] : array();
|
||||
$productPlans[$productID][$branchID] += isset($plans[$productID][$branchID]) ? $plans[$productID][$branchID] : array();
|
||||
if($branch != BRANCH_MAIN) $productPlans[$productID] = isset($plans[$productID][BRANCH_MAIN]) ? $plans[$productID][BRANCH_MAIN] : array();
|
||||
$productPlans[$productID] += isset($plans[$productID][$branchID]) ? $plans[$productID][$branchID] : array();
|
||||
|
||||
if(!empty($projectStories[$productID][$branchID]) or !empty($projectBranches[$productID][$branchID]))
|
||||
{
|
||||
@@ -773,7 +766,6 @@ class project extends control
|
||||
$this->view->multiBranchProducts = $this->loadModel('product')->getMultiBranchPairs();
|
||||
$this->view->productPlans = array_filter($productPlans);
|
||||
$this->view->linkedProducts = $linkedProducts;
|
||||
$this->view->linkedBranches = $linkedBranches;
|
||||
$this->view->branches = $branches;
|
||||
$this->view->executions = $this->execution->getPairs($projectID);
|
||||
$this->view->unmodifiableProducts = $unmodifiableProducts;
|
||||
@@ -1286,7 +1278,6 @@ class project extends control
|
||||
$this->view->branchOption = $branchOption;
|
||||
$this->view->branchTagOption = $branchTagOption;
|
||||
$this->view->executions = $executions;
|
||||
$this->view->plans = $this->loadModel('productplan')->getPairs($productID ? $productID : array_keys($products));
|
||||
$this->view->stories = $storyList;
|
||||
$this->view->tasks = $taskList;
|
||||
$this->view->projectPairs = $this->project->getPairsByProgram();
|
||||
|
||||
@@ -15,16 +15,14 @@
|
||||
#budget {border-right: 0px;}
|
||||
#budgetUnit {border-left: 0px;}
|
||||
#projectName {display: inline-block; width: 32%;}
|
||||
#productsBox a {border-radius: 2px !important;}
|
||||
#productsBox .col-sm-4 {padding-right: 5px;}
|
||||
#productsBox .required:after {right: -9.5px !important;}
|
||||
#productsBox .addProduct {padding-left: 0px !important; padding-right: 10px;}
|
||||
#productsBox .addProduct.required:after {right: -1px !important;}
|
||||
#productsBox .input-group-addon > div {display: inline-block !important;}
|
||||
#productsBox .required + .text-danger.help-text {position: relative; left: 10px;}
|
||||
#productsBox > #productNameLabel {padding-top: 8px;}
|
||||
#plansBox .col-sm-4 {float: none; display: inline-block; padding-right: 5px;}
|
||||
.productsBox a {border-radius: 2px !important;}
|
||||
.productsBox .required:after {right: -9.5px !important;}
|
||||
.productsBox .addProduct {padding-left: 0px !important;}
|
||||
.productsBox .addProduct.required:after {right: -1px !important;}
|
||||
.productsBox .input-group-addon > div {display: inline-block !important;}
|
||||
.productsBox .required + .text-danger.help-text {position: relative; left: 10px;}
|
||||
.productsBox > #productNameLabel {padding-top: 8px;}
|
||||
.futureBox {vertical-align: top !important; padding-top: 13px !important;}
|
||||
#projectType {padding-top: 7px !important;}
|
||||
.division .icon-help {margin-left: 15px}
|
||||
.has-info {border-color: #0c64eb!important;}
|
||||
.has-info {border-color: #0c64eb!important;}
|
||||
|
||||
@@ -5,5 +5,4 @@
|
||||
#dateRange, .futureBox {vertical-align: top !important; padding-top: 13px !important;}
|
||||
#endList {vertical-align: top; padding-top: 13px;}
|
||||
#linkPlan {padding-top: 25px !important;}
|
||||
#productsBox .row .col-sm-4.required::after {right: -5px;}
|
||||
#projectType {padding-top: 7px !important;}
|
||||
|
||||
@@ -138,134 +138,6 @@ function computeEndDate(delta)
|
||||
$('#days').trigger('mousedown');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load branches.
|
||||
*
|
||||
* @param object product
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadBranches(product)
|
||||
{
|
||||
/* When selecting a product, delete a plan that is empty by default. */
|
||||
$("#planDefault").remove();
|
||||
|
||||
var chosenProducts = [];
|
||||
$("#productsBox select[name^='products']").each(function()
|
||||
{
|
||||
var $product = $(product);
|
||||
var productID = $(this).val();
|
||||
if(productID > 0 && chosenProducts.indexOf(productID) == -1) chosenProducts.push(productID);
|
||||
if($product.val() != 0 && $product.val() == $(this).val() && $product.attr('id') != $(this).attr('id') && !multiBranchProducts[$product.val()])
|
||||
{
|
||||
bootbox.alert(errorSameProducts);
|
||||
$product.val(0);
|
||||
$product.trigger("chosen:updated");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
chosenProducts.length > 1 ? $('.division').removeClass('hide') : $('.division').addClass('hide');
|
||||
|
||||
if($('#productsBox .input-group:last select:first').val() != 0)
|
||||
{
|
||||
var length = $('#productsBox .row .input-group').size();
|
||||
$('#productsBox .row').append('<div class="col-sm-4">' + $('#productsBox .col-sm-4:last').html() + '</div>');
|
||||
if($('#productsBox .input-group:last select').size() >= 2) $('#productsBox .input-group:last select:last').remove();
|
||||
$('#productsBox .input-group:last .chosen-container').remove();
|
||||
$('#productsBox .input-group:last select:first').attr('name', 'products[' + length + ']').attr('id', 'products' + length);
|
||||
$('#productsBox .input-group:last .chosen').chosen();
|
||||
|
||||
adjustProductBoxMargin();
|
||||
}
|
||||
|
||||
var $inputgroup = $(product).closest('.input-group');
|
||||
if($inputgroup.find('select').size() >= 2) $inputgroup.removeClass('has-branch').find('select:last').remove();
|
||||
if($inputgroup.find('.chosen-container').size() >= 2) $inputgroup.find('.chosen-container:last').remove();
|
||||
|
||||
var index = $inputgroup.find('select:first').attr('id').replace('products' , '');
|
||||
var oldBranch = $(product).attr('data-branch') !== undefined ? $(product).attr('data-branch') : 0;
|
||||
$.get(createLink('branch', 'ajaxGetBranches', "productID=" + $(product).val() + "&oldBranch=" + oldBranch + "¶m=active"), function(data)
|
||||
{
|
||||
if(data)
|
||||
{
|
||||
$inputgroup.addClass('has-branch').append(data);
|
||||
$inputgroup.find('select:last').attr('name', 'branch[' + index + ']').attr('id', 'branch' + index).attr('onchange', "loadPlans('#products" + index + "', this.value)").chosen();
|
||||
|
||||
$inputgroup.find('select:last').each(disableSelectedBranch);
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
var branchID = $('#branch' + index).val();
|
||||
loadPlans(product, branchID);
|
||||
});
|
||||
|
||||
if(!multiBranchProducts[$(product).val()]) disableSelectedProduct();
|
||||
|
||||
$("input[name='products[" + index + "]']").remove();
|
||||
$("input[name='branch[" + index + "]']").remove();
|
||||
}
|
||||
|
||||
function loadPlans(product, branchID)
|
||||
{
|
||||
if($('#plansBox').size() == 0) return false;
|
||||
|
||||
var productID = $(product).val();
|
||||
var branchID = typeof(branchID) == 'undefined' ? 0 : branchID;
|
||||
var planID = $(product).attr('data-plan') !== undefined ? $(product).attr('data-plan') : 0;
|
||||
var index = $(product).attr('id').replace('products', '');
|
||||
|
||||
$("input[name='products[" + index + "]']").remove();
|
||||
$("input[name='branch[" + index + "]']").remove();
|
||||
|
||||
$.get(createLink('product', 'ajaxGetPlans', "productID=" + productID + '&branch=0,' + branchID + '&planID=' + planID + '&fieldID&needCreate=&expired=unexpired,noclosed¶m=skipParent,multiple'), function(data)
|
||||
{
|
||||
if(data)
|
||||
{
|
||||
if($("div#plan" + index).size() == 0) $("#plansBox .row").append('<div class="col-sm-4" id="plan' + index + '"></div>');
|
||||
$("div#plan" + index).html(data).find('select').attr('name', 'plans[' + productID + ']' + '[' + branchID + '][]').attr('id', 'plans' + productID).chosen();
|
||||
|
||||
adjustPlanBoxMargin();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the layout of product selection.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function adjustProductBoxMargin()
|
||||
{
|
||||
var productRows = Math.ceil($('#productsBox > .row > .col-sm-4').length / 3);
|
||||
if(productRows > 1)
|
||||
{
|
||||
for(i = 1; i <= productRows - 1; i++)
|
||||
{
|
||||
$('#productsBox .col-sm-4:lt(' + (i * 3) + ')').css('margin-bottom', '10px');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the layout of the plan selection.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function adjustPlanBoxMargin()
|
||||
{
|
||||
var planRows = Math.ceil($('#plansBox > .row > .col-sm-4').length / 3);
|
||||
if(planRows > 1)
|
||||
{
|
||||
for(j = 1; j <= planRows - 1; j++)
|
||||
{
|
||||
$('#plansBox .col-sm-4:lt(' + (j * 3) + ')').css('margin-bottom', '10px');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization operation.
|
||||
*
|
||||
|
||||
+90
-94
@@ -11,7 +11,7 @@ $(function()
|
||||
if(hasProduct == 1)
|
||||
{
|
||||
var chosenProducts = 0;
|
||||
$("#productsBox select[name^='products']").each(function()
|
||||
$(".productsBox select[name^='products']").each(function()
|
||||
{
|
||||
if($(this).val() > 0) chosenProducts ++;
|
||||
});
|
||||
@@ -29,16 +29,16 @@ $(function()
|
||||
|
||||
setAclList($("#parent").val());
|
||||
|
||||
if(typeof(currentPlanID) == 'undefined')
|
||||
if(typeof(currentPlanID) == 'undefined' && copyProjectID == 0)
|
||||
{
|
||||
$('#productsBox select[id^="products"]').each(function()
|
||||
$('.productsBox select[id^="products"]').each(function()
|
||||
{
|
||||
var branchID = 0;
|
||||
if($(this).closest('.input-group').find('select[id^="branch"]').size() > 0)
|
||||
var branch = 0;
|
||||
if($(this).closest('.table-row').find('select[name^="branch"]').size() > 0)
|
||||
{
|
||||
var branchID = $(this).closest('.input-group').find('select[id^="branch"]').val();
|
||||
var branch = $(this).closest('.table-row').find('select[name^="branch"]');
|
||||
}
|
||||
loadPlans($(this), branchID);
|
||||
loadPlans($(this), branch);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ $(function()
|
||||
$('#daysLabelInfo').remove();
|
||||
|
||||
/* Determine whether the products of the same branch are linked. */
|
||||
$("#productsBox select[name^='products']").each(function()
|
||||
$(".productsBox select[name^='products']").each(function()
|
||||
{
|
||||
var productID = $(this).val();
|
||||
if(typeof(products[productID]) == 'undefined') products[productID] = new Array();
|
||||
@@ -98,7 +98,6 @@ $(function()
|
||||
}
|
||||
|
||||
/* Init for copy execution. */
|
||||
$("select[id^=branch]").each(disableSelectedBranch);
|
||||
disableSelectedProduct();
|
||||
|
||||
/* Check the all products and branches control when uncheck the product. */
|
||||
@@ -106,14 +105,10 @@ $(function()
|
||||
{
|
||||
if($(this).val() == 0)
|
||||
{
|
||||
$("select[id^='branch']").each(disableSelectedBranch);
|
||||
|
||||
disableSelectedProduct();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('change', "select[id^='branch']", disableSelectedBranch);
|
||||
|
||||
if(copyProjectID > 0 && copyType != 'previous')
|
||||
{
|
||||
$('#name').addClass('has-info')
|
||||
@@ -167,7 +162,7 @@ function setParentProgram(parentProgram)
|
||||
var productSelectHtml = data.allProducts;
|
||||
var planSelectHtml = data.plans;
|
||||
|
||||
$('#productsBox .row .col-sm-4 .input-group').each(function(index)
|
||||
$('.productsBox .row .col-sm-4 .input-group').each(function(index)
|
||||
{
|
||||
var selectedProduct = $(this).find('[name^=products]').val();
|
||||
var selectedBranch = $(this).find('[name^=branch]').val();
|
||||
@@ -177,7 +172,7 @@ function setParentProgram(parentProgram)
|
||||
$(this).find('[name^=products]').attr('name', 'products[' + index + ']').attr('id', 'products' + index).attr('data-branch', selectedBranch).attr('data-plan', selectedPlan);
|
||||
$(this).find('[name^=products]').val(selectedProduct).chosen().change();
|
||||
});
|
||||
$('#productsBox .row .col-sm-4:last').find('.input-group').append($('#productsBox .addProduct .input-group:first .input-group-addon').prop('outerHTML'));
|
||||
$('.productsBox .row .col-sm-4:last').find('.input-group').append($('.productsBox .addProduct .input-group:first .input-group-addon').prop('outerHTML'));
|
||||
}
|
||||
|
||||
if(parentProgram != 0)
|
||||
@@ -198,13 +193,13 @@ function setParentProgram(parentProgram)
|
||||
|
||||
if(parentProgram != '0')
|
||||
{
|
||||
$('#productsBox .addProduct .input-group:first').addClass('required');
|
||||
$('#productsBox .row .input-group:first').addClass('required');
|
||||
$('.productsBox .addProduct .input-group:first').addClass('required');
|
||||
$('.productsBox .row .input-group:first').addClass('required');
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#productsBox .addProduct .input-group').removeClass('required');
|
||||
$('#productsBox .row .input-group').removeClass('required');
|
||||
$('.productsBox .addProduct .input-group').removeClass('required');
|
||||
$('.productsBox .row .input-group').removeClass('required');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,35 +227,37 @@ function addNewProduct(obj)
|
||||
if($(obj).attr('checked'))
|
||||
{
|
||||
/* Hide product and plan dropdown controls. */
|
||||
$('#productsBox .row .col-sm-4').addClass('hidden');
|
||||
$('#productsBox .row .col-sm-4 .input-group').find('select').attr('disabled', true).trigger("chosen:updated");
|
||||
$('#plansBox').closest('tr').addClass('hidden');
|
||||
$('#plansBox .col-sm-4').find('select').attr('disabled', true).trigger("chosen:updated");
|
||||
$('.newLine').addClass('hidden');
|
||||
$('.productsBox .row').addClass('hidden');
|
||||
$('.productsBox .row .input-group').find('select').attr('disabled', true).trigger("chosen:updated");
|
||||
$('.division').addClass('hide');
|
||||
|
||||
/* Displays the input box for creating a product. */
|
||||
$(obj).closest('td').attr('colspan', 1);
|
||||
$("[name^='newProduct']").prop('checked', true);
|
||||
$('#productName').removeAttr('disabled', true);
|
||||
$('#productsBox .addProduct').removeClass('hidden');
|
||||
$('.productsBox .addProduct').removeClass('hidden');
|
||||
$('#productTitle').html(productName);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Show product and product dropdown controls. */
|
||||
$('#productsBox .row .col-sm-4').removeClass('hidden');
|
||||
$('#productsBox .row .col-sm-4 .input-group').find('select').removeAttr('disabled').trigger("chosen:updated");
|
||||
$('#plansBox').closest('tr').removeClass('hidden');
|
||||
$('#plansBox .col-sm-4').find('select').removeAttr('disabled', true).trigger("chosen:updated");
|
||||
$('.newLine').removeClass('hidden');
|
||||
$('.productsBox .row').removeClass('hidden');
|
||||
$('.productsBox .row .input-group').find('select').removeAttr('disabled').trigger("chosen:updated");
|
||||
if($('#plansBox .col-sm-4').find('select').length > 1) $('.division').removeClass('hide');
|
||||
|
||||
/* Hide the input box for creating a product. */
|
||||
$(obj).closest('td').attr('colspan', 3);
|
||||
$("[name^='newProduct']").prop('checked', false);
|
||||
$('#productName').attr('disabled', true);
|
||||
$('#productsBox .addProduct').addClass('hidden');
|
||||
$('.productsBox .addProduct').addClass('hidden');
|
||||
|
||||
$('#productTitle').html(manageProducts);
|
||||
$('#productTitle').html(manageProductPlan);
|
||||
}
|
||||
$('#productsBox div + .text-danger.help-text').remove();
|
||||
|
||||
$('.productsBox div + .text-danger.help-text').remove();
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,7 +292,7 @@ function loadBranches(product)
|
||||
$("#planDefault").remove();
|
||||
|
||||
var chosenProducts = [];
|
||||
$("#productsBox select[name^='products']").each(function()
|
||||
$(".productsBox select[name^='products']").each(function()
|
||||
{
|
||||
var $product = $(product);
|
||||
var productID = $(this).val();
|
||||
@@ -311,111 +308,110 @@ function loadBranches(product)
|
||||
|
||||
(chosenProducts.length > 1 && model == 'waterfall') ? $('.division').removeClass('hide') : $('.division').addClass('hide');
|
||||
|
||||
if($('#productsBox .row .input-group:last select:first').val() != 0)
|
||||
var $tableRow = $(product).closest('.table-row');
|
||||
var index = $tableRow.find('select:first').attr('id').replace('products' , '');
|
||||
var oldBranch = $(product).attr('data-branch') !== undefined ? $(product).attr('data-branch') : 0;
|
||||
if($(product).val() != 0)
|
||||
{
|
||||
var length = $('#productsBox .row .input-group').size();
|
||||
var $html = $('#productsBox .row .col-sm-4:last').html().replace('required', '');
|
||||
$('#productsBox .row .col-sm-4:last').find('.input-group-addon').remove();
|
||||
$('#productsBox .row').append('<div class="col-sm-4">' + $html + '</div>');
|
||||
if($('#productsBox .row .input-group:last select').size() >= 2) $('#productsBox .row .input-group:last select:last').remove();
|
||||
$('#productsBox .row .input-group:last .chosen-container').remove();
|
||||
$('#productsBox .row .input-group:last select:first').attr('name', 'products[' + length + ']').attr('id', 'products' + length);
|
||||
$('#productsBox .row .input-group:last .chosen').chosen();
|
||||
|
||||
$('[data-toggle="popover"]').popover();
|
||||
|
||||
adjustProductBoxMargin();
|
||||
$(product).closest('tr').find('.newProduct').addClass('hidden')
|
||||
}
|
||||
else
|
||||
{
|
||||
$(product).closest('tr').find('.newProduct').removeClass('hidden')
|
||||
}
|
||||
|
||||
var $inputgroup = $(product).closest('.input-group');
|
||||
if($inputgroup.find('select').size() >= 2) $inputgroup.removeClass('has-branch').find('select:last').remove();
|
||||
if($inputgroup.find('.chosen-container').size() >= 2) $inputgroup.find('.chosen-container:last').remove();
|
||||
if(!multiBranchProducts[$(product).val()])
|
||||
{
|
||||
$tableRow.find('.table-col:last select').val('').trigger('chosen:updated');
|
||||
$tableRow.find('.table-col:last').addClass('hidden');
|
||||
}
|
||||
|
||||
var index = $inputgroup.find('select:first').attr('id').replace('products' , '');
|
||||
var oldBranch = $(product).attr('data-branch') !== undefined ? $(product).attr('data-branch') : 0;
|
||||
$.get(createLink('branch', 'ajaxGetBranches', "productID=" + $(product).val() + "&oldBranch=" + oldBranch + "¶m=active"), function(data)
|
||||
{
|
||||
if(data)
|
||||
{
|
||||
$inputgroup.addClass('has-branch').append(data);
|
||||
$inputgroup.find('select:last').attr('name', 'branch[' + index + ']').attr('id', 'branch' + index).attr('onchange', "loadPlans('#products" + index + "', this.value)").chosen();
|
||||
$tableRow.find("select[name^='branch']").replaceWith(data);
|
||||
$tableRow.find('.table-col:last .chosen-container').remove();
|
||||
$tableRow.find('.table-col:last').removeClass('hidden');
|
||||
$tableRow.find("select[name^='branch']").attr('multiple', '').attr('name', 'branch[' + index + '][]').attr('id', 'branch' + index).attr('onchange', "loadPlans('#products" + index + "', this)").chosen();
|
||||
|
||||
$inputgroup.find('select:last').each(disableSelectedBranch);
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
var branchID = $('#branch' + index).val();
|
||||
loadPlans(product, branchID);
|
||||
var branch = $('#branch' + index);
|
||||
loadPlans(product, branch);
|
||||
});
|
||||
|
||||
if(!multiBranchProducts[$(product).val()]) disableSelectedProduct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plans.
|
||||
*
|
||||
* @param obj $product
|
||||
* @param int $branchID
|
||||
* @param obj $branchID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadPlans(product, branchID)
|
||||
function loadPlans(product, branch)
|
||||
{
|
||||
if($('#plansBox').size() == 0) return false;
|
||||
|
||||
var productID = $(product).val();
|
||||
var branchID = typeof(branchID) == 'undefined' ? 0 : branchID;
|
||||
var branchID = $(branch).val() == null ? 0 : '0,' + $(branch).val();
|
||||
var planID = $(product).attr('data-plan') !== undefined ? $(product).attr('data-plan') : 0;
|
||||
var index = $(product).attr('id').replace('products', '');
|
||||
|
||||
$.get(createLink('product', 'ajaxGetPlans', "productID=" + productID + '&branch=0,' + branchID + '&planID=' + planID + '&fieldID&needCreate=&expired=unexpired,noclosed¶m=skipParent,multiple'), function(data)
|
||||
$.get(createLink('product', 'ajaxGetPlans', "productID=" + productID + '&branch=' + branchID + '&planID=' + planID + '&fieldID&needCreate=&expired=unexpired,noclosed¶m=skipParent,multiple'), function(data)
|
||||
{
|
||||
if(data)
|
||||
{
|
||||
if($("div#plan" + index).size() == 0) $("#plansBox .row").append('<div class="col-sm-4" id="plan' + index + '"></div>');
|
||||
$("div#plan" + index).html(data).find('select').attr('name', 'plans[' + productID + '][' + branchID + '][]').attr('id', 'plans' + productID).chosen();
|
||||
|
||||
adjustPlanBoxMargin();
|
||||
$("div#plan" + index).find("select[name^='plans']").replaceWith(data);
|
||||
$("div#plan" + index).find('.chosen-container').remove();
|
||||
$("div#plan" + index).find('select').attr('name', 'plans[' + productID + ']' + '[]').attr('id', 'plans' + productID).chosen();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust product box margin.
|
||||
*
|
||||
* Add new line for link product.
|
||||
*
|
||||
* @param obj $obj
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function adjustProductBoxMargin()
|
||||
function addNewLine(obj)
|
||||
{
|
||||
var productRows = Math.ceil($('#productsBox > .row > .col-sm-4').length / 3);
|
||||
if(productRows > 1)
|
||||
var newLine = $(obj).closest('tr').clone();
|
||||
var index = 0;
|
||||
$(".productsBox select[name^='products']").each(function()
|
||||
{
|
||||
for(i = 1; i <= productRows - 1; i++)
|
||||
{
|
||||
$('#productsBox .row .col-sm-4:lt(' + (i * 3) + ')').css('margin-bottom', '10px');
|
||||
$('#productsBox .row .col-sm-4').eq(i * 3).css('padding-right', '6px');
|
||||
}
|
||||
}
|
||||
var id = $(this).attr('id').replace('products' , '');
|
||||
|
||||
id = parseInt(id);
|
||||
id ++;
|
||||
|
||||
index = id > index ? id : index;
|
||||
})
|
||||
|
||||
newLine.find('.newProduct').remove();
|
||||
newLine.find('.addProduct').remove();
|
||||
newLine.addClass('newLine');
|
||||
newLine.find('th').html('');
|
||||
newLine.find('.removeLine').css('visibility', 'visible');
|
||||
newLine.find('.chosen-container').remove();
|
||||
newLine.find('.productsBox .table-col:last').addClass('hidden');
|
||||
newLine.find("select[name^='products']").attr('name', 'products[' + index + ']').attr('id', 'products' + index).val('').chosen();
|
||||
newLine.find("select[name^='plans']").attr('name', 'plans[' + index + '][' + 0 + '][]').chosen();
|
||||
newLine.find("div[id^='plan']").attr('id', 'plan' + index);
|
||||
|
||||
$(obj).closest('tr').after(newLine);
|
||||
var product = newLine.find("select[name^='products']");
|
||||
var branch = newLine.find("select[name^='branch']");
|
||||
loadPlans(product, branch);
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust plan box margin.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function adjustPlanBoxMargin()
|
||||
function removeLine(obj)
|
||||
{
|
||||
var planRows = Math.ceil($('#plansBox > .row > .col-sm-4').length / 3);
|
||||
if(planRows > 1)
|
||||
{
|
||||
for(j = 1; j <= planRows - 1; j++)
|
||||
{
|
||||
$('#plansBox .col-sm-4:lt(' + (j * 3) + ')').css('margin-bottom', '10px');
|
||||
$('#plansBox .col-sm-4').eq(j * 3).css('padding-right', '6px');
|
||||
}
|
||||
}
|
||||
$(obj).closest('tr').remove();
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+124
-3
@@ -32,9 +32,6 @@ $(function()
|
||||
});
|
||||
});
|
||||
|
||||
adjustProductBoxMargin();
|
||||
adjustPlanBoxMargin();
|
||||
|
||||
$(document).on('change', '[name*=products]', function()
|
||||
{
|
||||
var current = $(this).val();
|
||||
@@ -173,3 +170,127 @@ function setAclList(programID)
|
||||
$('.aclBox').html($('#projectAcl').html());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load branches.
|
||||
*
|
||||
* @param int $product
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadBranches(product)
|
||||
{
|
||||
/* When selecting a product, delete a plan that is empty by default. */
|
||||
$("#planDefault").remove();
|
||||
|
||||
var chosenProducts = [];
|
||||
$(".productsBox select[name^='products']").each(function()
|
||||
{
|
||||
var $product = $(product);
|
||||
var productID = $(this).val();
|
||||
if(productID > 0 && chosenProducts.indexOf(productID) == -1) chosenProducts.push(productID);
|
||||
if($product.val() != 0 && $product.val() == $(this).val() && $product.attr('id') != $(this).attr('id') && !multiBranchProducts[$product.val()])
|
||||
{
|
||||
bootbox.alert(errorSameProducts);
|
||||
$product.val(0);
|
||||
$product.trigger("chosen:updated");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
(chosenProducts.length > 1 && model == 'waterfall') ? $('.division').removeClass('hide') : $('.division').addClass('hide');
|
||||
|
||||
var $tableRow = $(product).closest('.table-row');
|
||||
var index = $tableRow.find('select:first').attr('id').replace('products' , '');
|
||||
var oldBranch = $(product).attr('data-branch') !== undefined ? $(product).attr('data-branch') : 0;
|
||||
|
||||
if(!multiBranchProducts[$(product).val()])
|
||||
{
|
||||
$tableRow.find('.table-col:last select').val('').trigger('chosen:updated');
|
||||
$tableRow.find('.table-col:last').addClass('hidden');
|
||||
}
|
||||
|
||||
$.get(createLink('branch', 'ajaxGetBranches', "productID=" + $(product).val() + "&oldBranch=" + oldBranch + "¶m=active"), function(data)
|
||||
{
|
||||
if(data)
|
||||
{
|
||||
$tableRow.find("select[name^='branch']").replaceWith(data);
|
||||
$tableRow.find('.table-col:last .chosen-container').remove();
|
||||
$tableRow.find('.table-col:last').removeClass('hidden');
|
||||
$tableRow.find("select[name^='branch']").attr('multiple', '').attr('name', 'branch[' + index + '][]').attr('id', 'branch' + index).attr('onchange', "loadPlans('#products" + index + "', this)").chosen();
|
||||
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
var branch = $('#branch' + index);
|
||||
loadPlans(product, branch);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plans.
|
||||
*
|
||||
* @param obj $product
|
||||
* @param obj $branchID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function loadPlans(product, branch)
|
||||
{
|
||||
var productID = $(product).val();
|
||||
var branchID = $(branch).val() == null ? 0 : '0,' + $(branch).val();
|
||||
var planID = $(product).attr('data-plan') !== undefined ? $(product).attr('data-plan') : 0;
|
||||
var index = $(product).attr('id').replace('products', '');
|
||||
|
||||
$.get(createLink('product', 'ajaxGetPlans', "productID=" + productID + '&branch=' + branchID + '&planID=' + planID + '&fieldID&needCreate=&expired=unexpired,noclosed¶m=skipParent,multiple'), function(data)
|
||||
{
|
||||
if(data)
|
||||
{
|
||||
$("div#plan" + index).find("select[name^='plans']").replaceWith(data);
|
||||
$("div#plan" + index).find('.chosen-container').remove();
|
||||
$("div#plan" + index).find('select').attr('name', 'plans[' + productID + ']' + '[]').attr('id', 'plans' + productID).chosen();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new line for link product.
|
||||
*
|
||||
* @param obj $obj
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function addNewLine(obj)
|
||||
{
|
||||
var newLine = $(obj).closest('tr').clone();
|
||||
var index = 0;
|
||||
$(".productsBox select[name^='products']").each(function()
|
||||
{
|
||||
var id = $(this).attr('id').replace('products' , '');
|
||||
|
||||
id = parseInt(id);
|
||||
id ++;
|
||||
|
||||
index = id > index ? id : index;
|
||||
})
|
||||
|
||||
newLine.addClass('newLine');
|
||||
newLine.find('th').html('');
|
||||
newLine.find('.removeLine').css('visibility', 'visible');
|
||||
newLine.find('.chosen-container').remove();
|
||||
newLine.find('.productsBox .table-col:last').addClass('hidden');
|
||||
newLine.find("select[name^='products']").attr('name', 'products[' + index + ']').attr('id', 'products' + index).val('').chosen();
|
||||
newLine.find("select[name^='plans']").attr('name', 'plans[' + index + '][' + 0 + '][]').chosen();
|
||||
newLine.find("div[id^='plan']").attr('id', 'plan' + index);
|
||||
|
||||
$(obj).closest('tr').after(newLine);
|
||||
var product = newLine.find("select[name^='products']");
|
||||
var branch = newLine.find("select[name^='branch']");
|
||||
loadPlans(product, branch);
|
||||
disableSelectedProduct();
|
||||
}
|
||||
|
||||
function removeLine(obj)
|
||||
{
|
||||
$(obj).closest('tr').remove();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ $lang->project->addWhitelist = 'Project Add Whitelist';
|
||||
$lang->project->unbindWhitelist = 'Project Remove Whitelist';
|
||||
$lang->project->manageProducts = 'Manage Products';
|
||||
$lang->project->manageOtherProducts = 'Manage Other Products';
|
||||
$lang->project->manageProductPlan = 'Manage Products And Plans';
|
||||
$lang->project->copyTitle = 'Please select an project to copy';
|
||||
$lang->project->errorSameProducts = 'Project cannot be associated with multiple identical products.';
|
||||
$lang->project->errorSameBranches = 'Project cannot be associated with multiple identical branches.';
|
||||
@@ -338,6 +339,7 @@ $lang->project->scrum = 'Scrum';
|
||||
$lang->project->waterfall = 'CMMI';
|
||||
$lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.';
|
||||
$lang->project->emptyPM = 'No manager';
|
||||
$lang->project->emptyBranch = 'Branch can not be empty!';
|
||||
$lang->project->cannotChangeToCat = "It is not empty, so you cannot change it to a parent.";
|
||||
$lang->project->cannotCancelCat = "It has child projects, so you cannot unmark the parent.";
|
||||
$lang->project->parentBeginEnd = "Parent begin&end date: %s ~ %s";
|
||||
|
||||
@@ -42,6 +42,7 @@ $lang->project->addWhitelist = 'Project Add Whitelist';
|
||||
$lang->project->unbindWhitelist = 'Project Remove Whitelist';
|
||||
$lang->project->manageProducts = 'Manage Products';
|
||||
$lang->project->manageOtherProducts = 'Manage Other Products';
|
||||
$lang->project->manageProductPlan = 'Manage Products And Plans';
|
||||
$lang->project->copyTitle = 'Please select an project to copy';
|
||||
$lang->project->errorSameProducts = 'Project cannot be associated with multiple identical products.';
|
||||
$lang->project->errorSameBranches = 'Project cannot be associated with multiple identical branches.';
|
||||
@@ -338,6 +339,7 @@ $lang->project->scrum = 'Scrum';
|
||||
$lang->project->waterfall = 'Waterfall';
|
||||
$lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.';
|
||||
$lang->project->emptyPM = 'No manager';
|
||||
$lang->project->emptyBranch = 'Branch can not be empty!';
|
||||
$lang->project->cannotChangeToCat = "It is not empty, so you cannot change it to a parent.";
|
||||
$lang->project->cannotCancelCat = "It has child projects, so you cannot unmark the parent.";
|
||||
$lang->project->parentBeginEnd = "Parent begin&end date: %s ~ %s";
|
||||
|
||||
@@ -42,6 +42,7 @@ $lang->project->addWhitelist = 'Project Add Whitelist';
|
||||
$lang->project->unbindWhitelist = 'Project Remove Whitelist';
|
||||
$lang->project->manageProducts = 'Manage Products';
|
||||
$lang->project->manageOtherProducts = 'Manage Other Products';
|
||||
$lang->project->manageProductPlan = 'Manage Products And Plans';
|
||||
$lang->project->copyTitle = 'Please select an project to copy';
|
||||
$lang->project->errorSameProducts = 'Project cannot be associated with multiple identical products.';
|
||||
$lang->project->errorSameBranches = 'Project cannot be associated with multiple identical branches.';
|
||||
@@ -338,6 +339,7 @@ $lang->project->scrum = 'Scrum';
|
||||
$lang->project->waterfall = 'CMMI';
|
||||
$lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.';
|
||||
$lang->project->emptyPM = 'No manager';
|
||||
$lang->project->emptyBranch = 'Branch can not be empty!';
|
||||
$lang->project->cannotChangeToCat = "It is not empty, so you cannot change it to a parent.";
|
||||
$lang->project->cannotCancelCat = "It has child projects, so you cannot unmark the parent.";
|
||||
$lang->project->parentBeginEnd = "Parent begin&end date: %s ~ %s";
|
||||
|
||||
@@ -42,6 +42,7 @@ $lang->project->addWhitelist = 'Project Add Whitelist';
|
||||
$lang->project->unbindWhitelist = 'Project Remove Whitelist';
|
||||
$lang->project->manageProducts = 'Manage Products';
|
||||
$lang->project->manageOtherProducts = 'Manage Other Products';
|
||||
$lang->project->manageProductPlan = 'Manage Products And Plans';
|
||||
$lang->project->copyTitle = 'Please select an project to copy';
|
||||
$lang->project->errorSameProducts = 'Project cannot be associated with multiple identical products.';
|
||||
$lang->project->errorSameBranches = 'Project cannot be associated with multiple identical branches.';
|
||||
@@ -335,6 +336,7 @@ $lang->project->scrum = 'Scrum';
|
||||
$lang->project->waterfall = 'Waterfall';
|
||||
$lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.';
|
||||
$lang->project->emptyPM = 'No manager';
|
||||
$lang->project->emptyBranch = 'Branch can not be empty!';
|
||||
$lang->project->cannotChangeToCat = "It is not empty, so you cannot change it to a parent.";
|
||||
$lang->project->cannotCancelCat = "It has child projects, so you cannot unmark the parent.";
|
||||
$lang->project->parentBeginEnd = "Parent begin&end date: %s ~ %s";
|
||||
|
||||
@@ -42,6 +42,7 @@ $lang->project->addWhitelist = '项目添加白名单';
|
||||
$lang->project->unbindWhitelist = '项目删除白名单';
|
||||
$lang->project->manageProducts = '关联产品';
|
||||
$lang->project->manageOtherProducts = '关联其他产品';
|
||||
$lang->project->manageProductPlan = '关联产品和计划';
|
||||
$lang->project->copyTitle = '请选择要复制的项目';
|
||||
$lang->project->errorSameProducts = '项目不能关联多个相同的产品。';
|
||||
$lang->project->errorSameBranches = '项目不能关联多个相同的分支。';
|
||||
@@ -233,7 +234,7 @@ $lang->project->hundredMillion = '亿';
|
||||
$lang->project->unitList['CNY'] = '人民币';
|
||||
$lang->project->unitList['USD'] = '美元';
|
||||
$lang->project->unitList['HKD'] = '港元';
|
||||
$lang->project->unitList['NTD'] = '台元';
|
||||
$lang->project->unitList['NTD'] = '台币';
|
||||
$lang->project->unitList['EUR'] = '欧元';
|
||||
$lang->project->unitList['DEM'] = '马克';
|
||||
$lang->project->unitList['CHF'] = '瑞士法郎';
|
||||
@@ -338,6 +339,7 @@ $lang->project->scrum = 'Scrum';
|
||||
$lang->project->waterfall = '瀑布';
|
||||
$lang->project->cannotCreateChild = '该项目已经有实际的内容,无法直接添加子项目。您可以为当前项目创建一个父项目,然后在新的父项目下面添加子项目。';
|
||||
$lang->project->emptyPM = '暂无';
|
||||
$lang->project->emptyBranch = '分支不能为空!';
|
||||
$lang->project->cannotChangeToCat = "该项目已经有实际的内容,无法修改为父项目";
|
||||
$lang->project->cannotCancelCat = "该项目下已经有子项目,无法取消父项目标记";
|
||||
$lang->project->parentBeginEnd = "父项目起止时间:%s ~ %s";
|
||||
@@ -379,4 +381,4 @@ $lang->project->copyProject = new stdClass();
|
||||
$lang->project->copyProject->nameTips = '『项目名称』不可重复需要修改。';
|
||||
$lang->project->copyProject->codeTips = '『项目代号』不可重复需要修改。';
|
||||
$lang->project->copyProject->endTips = '『计划完成』不能为空。';
|
||||
$lang->project->copyProject->daysTips = '『可用工作日』应当是数字。';
|
||||
$lang->project->copyProject->daysTips = '『可用工作日』应当是数字。';
|
||||
|
||||
@@ -190,7 +190,7 @@ $lang->project->tenThousand = '萬';
|
||||
$lang->project->unitList['CNY'] = '人民幣';
|
||||
$lang->project->unitList['USD'] = '美元';
|
||||
$lang->project->unitList['HKD'] = '港元';
|
||||
$lang->project->unitList['NTD'] = '台元';
|
||||
$lang->project->unitList['NTD'] = '台幣';
|
||||
$lang->project->unitList['EUR'] = '歐元';
|
||||
$lang->project->unitList['DEM'] = '馬克';
|
||||
$lang->project->unitList['CHF'] = '瑞士法郎';
|
||||
|
||||
+50
-15
@@ -1243,6 +1243,20 @@ class projectModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
if($_POST['products'])
|
||||
{
|
||||
$topProgramID = $this->loadModel('program')->getTopByID($project->parent);
|
||||
$multipleProducts = $this->loadModel('product')->getMultiBranchPairs($topProgramID);
|
||||
foreach($_POST['products'] as $index => $productID)
|
||||
{
|
||||
if(isset($multipleProducts[$productID]) and empty($_POST['branch'][$index]))
|
||||
{
|
||||
dao::$errors[] = $this->lang->project->emptyBranch;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$program = new stdClass();
|
||||
if($project->parent)
|
||||
{
|
||||
@@ -1485,6 +1499,7 @@ class projectModel extends model
|
||||
->setDefault('team', $this->post->name)
|
||||
->setDefault('lastEditedBy', $this->app->user->account)
|
||||
->setDefault('lastEditedDate', helper::now())
|
||||
->setDefault('parent', 0)
|
||||
->setIF($this->post->delta == 999, 'end', LONG_TIME)
|
||||
->setIF($this->post->delta == 999, 'days', 0)
|
||||
->setIF($this->post->begin == '0000-00-00', 'begin', '')
|
||||
@@ -1508,12 +1523,27 @@ class projectModel extends model
|
||||
if(dao::isError()) return false;
|
||||
}
|
||||
|
||||
if($_POST['products'])
|
||||
{
|
||||
$topProgramID = $this->loadModel('program')->getTopByID($project->parent);
|
||||
$multipleProducts = $this->loadModel('product')->getMultiBranchPairs($topProgramID);
|
||||
foreach($_POST['products'] as $index => $productID)
|
||||
{
|
||||
if(isset($multipleProducts[$productID]) and empty($_POST['branch'][$index]))
|
||||
{
|
||||
dao::$errors[] = $this->lang->project->emptyBranch;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Judge products not empty. */
|
||||
$linkedProductsCount = 0;
|
||||
foreach($_POST['products'] as $product)
|
||||
{
|
||||
if(!empty($product)) $linkedProductsCount++;
|
||||
}
|
||||
|
||||
if(empty($linkedProductsCount))
|
||||
{
|
||||
dao::$errors[] = $this->lang->project->errorNoProducts;
|
||||
@@ -2403,24 +2433,29 @@ class projectModel extends model
|
||||
$oldPlan = 0;
|
||||
$branch = isset($branches[$i]) ? $branches[$i] : 0;
|
||||
|
||||
if(isset($existedProducts[$productID][$branch])) continue;
|
||||
if(!is_array($branch)) $branch = array($branch => $branch);
|
||||
|
||||
if(isset($oldProjectProducts[$productID][$branch]))
|
||||
foreach($branch as $branchID)
|
||||
{
|
||||
$oldProjectProduct = $oldProjectProducts[$productID][$branch];
|
||||
if($this->app->rawMethod != 'edit') $oldPlan = $oldProjectProduct->plan;
|
||||
if(isset($existedProducts[$productID][$branchID])) continue;
|
||||
|
||||
if(isset($oldProjectProducts[$productID][$branchID]))
|
||||
{
|
||||
$oldProjectProduct = $oldProjectProducts[$productID][$branchID];
|
||||
if($this->app->rawMethod != 'edit') $oldPlan = $oldProjectProduct->plan;
|
||||
}
|
||||
|
||||
$data = new stdclass();
|
||||
$data->project = $projectID;
|
||||
$data->product = $productID;
|
||||
$data->branch = $branchID;
|
||||
$data->plan = isset($plans[$productID]) ? implode(',', $plans[$productID]) : $oldPlan;
|
||||
$data->plan = trim($data->plan, ',');
|
||||
$data->plan = empty($data->plan) ? 0 : ",$data->plan,";
|
||||
|
||||
$this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec();
|
||||
$existedProducts[$productID][$branchID] = true;
|
||||
}
|
||||
|
||||
$data = new stdclass();
|
||||
$data->project = $projectID;
|
||||
$data->product = $productID;
|
||||
$data->branch = $branch;
|
||||
$data->plan = isset($plans[$productID][$branch]) ? implode(',', $plans[$productID][$branch]) : $oldPlan;
|
||||
$data->plan = trim($data->plan, ',');
|
||||
$data->plan = empty($data->plan) ? 0 : ",$data->plan,";
|
||||
|
||||
$this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec();
|
||||
$existedProducts[$productID][$branch] = true;
|
||||
}
|
||||
|
||||
/* Delete the execution linked products that is not linked with the execution. */
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<?php js::set('selectedProductID', $productID);?>
|
||||
<?php js::set('selectedBranchID', $branchID);?>
|
||||
<?php js::set('productName', $lang->product->name);?>
|
||||
<?php js::set('manageProducts', $lang->project->manageProducts);?>
|
||||
<?php js::set('manageProductPlan', $lang->project->manageProductPlan);?>
|
||||
<?php js::set('budgetOverrun', $lang->project->budgetOverrun);?>
|
||||
<?php js::set('currencySymbol', $lang->project->currencySymbol)?>
|
||||
<?php js::set('parentBudget', $lang->project->parentBudget);?>
|
||||
@@ -143,64 +143,91 @@
|
||||
</div>
|
||||
</td><td></td><td></td>
|
||||
</tr>
|
||||
<?php if($products):?>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($products as $product):?>
|
||||
<tr>
|
||||
<th id='productTitle'><?php echo $lang->project->manageProducts;?></th>
|
||||
<td class='text-left' id='productsBox' colspan="3">
|
||||
<th><?php if($i == 0) echo $lang->project->manageProductPlan;?></th>
|
||||
<td class='text-left productsBox' colspan="3">
|
||||
<div class='row'>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($products as $product):?>
|
||||
<?php foreach($product->branches as $branch):?>
|
||||
<div class="col-sm-4" style="margin-bottom: 10px; padding-right: 6px;">
|
||||
<?php $hasBranch = $product->type != 'normal' and isset($branchGroups[$product->id]);?>
|
||||
<div class="input-group<?php if($hasBranch) echo ' has-branch';?>">
|
||||
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' onchange='loadBranches(this)' data-last='" . $product->id . "'");?>
|
||||
<span class='input-group-addon fix-border'></span>
|
||||
<?php if($hasBranch) echo html::select("branch[$i]", $branchGroups[$product->id], $branch, "class='form-control chosen' onchange=\"loadPlans('#products{$i}', this.value)\"");?>
|
||||
<div class="col-sm-6">
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php $hasBranch = $product->type != 'normal' and isset($branchGroups[$product->id]);?>
|
||||
<div class='input-group <?php if($hasBranch) echo ' has-branch';?>'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->common;?></span>
|
||||
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' onchange='loadBranches(this)' data-last='" . $product->id . "' data-type='" . $product->type . "'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col <?php if(!$hasBranch) echo 'hidden';?>'>
|
||||
<div class='input-group required'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->product->branchName['branch'];?></span>
|
||||
<?php $branchIdList = join(',', $product->branches);?>
|
||||
<?php echo html::select("branch[$i][]", isset($branchGroups[$product->id]) ? $branchGroups[$product->id] : array(), $branchIdList, "class='form-control chosen' multiple onchange=\"loadPlans('#products{$i}', this)\"");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php $i++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endforeach;?>
|
||||
<div class='col-sm-4 <?php if($programID) echo 'required';?>' style="padding-right: 6px;">
|
||||
<div class='input-group'>
|
||||
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
|
||||
<?php if(common::hasPriv('product', 'create')):?>
|
||||
<span class='input-group-addon'>
|
||||
<?php echo html::checkBox('newProduct', $lang->project->addProduct, '', "onchange=addNewProduct(this);");?>
|
||||
<div><icon class='icon icon-help' data-toggle='popover' data-trigger='focus hover' data-placement='left' data-tip-class='text-muted popover-sm' data-content="<?php echo $lang->project->productTip;?>"></icon></div>
|
||||
</span>
|
||||
<?php endif;?>
|
||||
<div class="col-sm-6">
|
||||
<div class='input-group' <?php echo "id='plan$i'";?>>
|
||||
<span class='input-group-addon'><?php echo $lang->product->plan;?></span>
|
||||
<?php echo html::select("plans[$product->id][]", isset($productPlans[$product->id]) ? $productPlans[$product->id] : array(), $product->plans, "class='form-control chosen' multiple");?>
|
||||
<div class='input-group-btn'>
|
||||
<a href='javascript:;' onclick='addNewLine(this)' class='btn btn-link addLine'><i class='icon-plus'></i></a>
|
||||
<a href='javascript:;' onclick='removeLine(this)' class='btn btn-link removeLine' <?php if($i == 0) echo "style='visibility: hidden'";?>><i class='icon-close'></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 addProduct hidden <?php if($programID) echo 'required';?>">
|
||||
<div class='input-group'>
|
||||
<?php echo html::input('productName', '', "class='form-control'");?>
|
||||
<span class='input-group-addon'><?php echo html::checkBox('newProduct', $lang->project->addProduct, '', "onchange=addNewProduct(this);");?></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php $i ++;?>
|
||||
<?php endforeach;?>
|
||||
<?php else:?>
|
||||
<tr>
|
||||
<th id='linkPlan'><?php echo $lang->execution->linkPlan;?></th>
|
||||
<td colspan="3" id="plansBox">
|
||||
<th id='productTitle'><?php echo $lang->project->manageProductPlan;?></th>
|
||||
<td class='text-left productsBox' colspan='3'>
|
||||
<div class='row'>
|
||||
<?php if($copyProjectID):?>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($products as $product):?>
|
||||
<?php $productPlan = zget($productPlans, $product->id, array(0 => ''));?>
|
||||
<?php foreach($product->branches as $branch):?>
|
||||
<?php $plans = zget($productPlan, $branch, array(0 => ''));?>
|
||||
<div class="col-sm-4" id="plan<?php echo $i;?>"><?php echo html::select('plans[' . $product->id . '][' . $branch . '][]', $plans, '', "class='form-control chosen' multiple");?></div>
|
||||
<?php $i++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endforeach;?>
|
||||
<?php else:?>
|
||||
<div class="col-sm-4" id="plan0" style="padding-right: 6px;"><?php echo html::select("plans[][][]", '', '', "class='form-control chosen' multiple");?></div>
|
||||
<?php endif;?>
|
||||
<div class="col-sm-6">
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->common;?></span>
|
||||
<?php echo html::select("products[0]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
|
||||
<?php if(common::hasPriv('product', 'create')):?>
|
||||
<span class='input-group-addon newProduct'>
|
||||
<?php echo html::checkBox('newProduct', $lang->project->addProduct, '', "onchange=addNewProduct(this);");?>
|
||||
<div><icon class='icon icon-help' data-toggle='popover' data-trigger='focus hover' data-placement='left' data-tip-class='text-muted popover-sm' data-content="<?php echo $lang->project->productTip;?>"></icon></div>
|
||||
</span>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col hidden'>
|
||||
<div class='input-group required'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->product->branchName['branch'];?></span>
|
||||
<?php echo html::select("branch", '', '', "class='form-control chosen' multiple");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class='input-group' id='plan0'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->plan;?></span>
|
||||
<?php echo html::select("plans[][]", '', '', "class='form-control chosen' multiple");?>
|
||||
<div class='input-group-btn'>
|
||||
<a href='javascript:;' onclick='addNewLine(this)' class='btn btn-link addLine'><i class='icon-plus'></i></a>
|
||||
<a href='javascript:;' onclick='removeLine(this)' class='btn btn-link removeLine' style='visibility: hidden'><i class='icon-close'></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group addProduct hidden <?php if($programID) echo 'required';?>">
|
||||
<?php echo html::input('productName', '', "class='form-control'");?>
|
||||
<span class='input-group-addon'><?php echo html::checkBox('newProduct', $lang->project->addProduct, '', "onchange=addNewProduct(this);");?></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<?php if($model == 'waterfall'):?>
|
||||
<tr class='hide division'>
|
||||
<th><?php echo $lang->project->division;?></th>
|
||||
|
||||
@@ -146,49 +146,45 @@
|
||||
<td></td>
|
||||
</tr>
|
||||
<?php if($project->hasProduct and $this->config->vision != 'lite'):?>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($linkedProducts as $product):?>
|
||||
<tr>
|
||||
<th><?php echo $lang->project->manageProducts;?></th>
|
||||
<td class='text-left' id='productsBox' colspan="3">
|
||||
<th><?php if($i == 0) echo $lang->project->manageProductPlan;?></th>
|
||||
<td class='text-left productsBox' colspan="3">
|
||||
<div class='row'>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($linkedProducts as $product):?>
|
||||
<?php $hasBranch = $product->type != 'normal' and isset($branchGroups[$product->id]);?>
|
||||
<?php foreach($linkedBranches[$product->id] as $branchID => $branch):?>
|
||||
<div class='col-sm-4' style="padding-right: 6px;">
|
||||
<div class="input-group<?php if($hasBranch) echo ' has-branch';?>">
|
||||
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' onchange='loadBranches(this)' data-last='" . $product->id . "' data-type='" . $product->type . "' data-lastBranch='" . $branchID . "'");?>
|
||||
<span class='input-group-addon fix-border'></span>
|
||||
<?php if($hasBranch) echo html::select("branch[$i]", $branchGroups[$product->id], $branchID, "class='form-control chosen' onchange=\"loadPlans('#products{$i}', this.value)\" data-last='" . $branchID . "'");?>
|
||||
<div class="col-sm-6">
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php $hasBranch = $product->type != 'normal' and isset($branchGroups[$product->id]);?>
|
||||
<div class='input-group <?php if($hasBranch) echo ' has-branch';?>'>
|
||||
<span class='input-group-addon'><?php echo $lang->product->common;?></span>
|
||||
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' onchange='loadBranches(this)' data-last='" . $product->id . "' data-type='" . $product->type . "'");?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-col <?php if(!$hasBranch) echo 'hidden';?>'>
|
||||
<div class='input-group required'>
|
||||
<span class='input-group-addon fix-border'><?php echo $lang->product->branchName['branch'];?></span>
|
||||
<?php $branchIdList = join(',', $product->branches);?>
|
||||
<?php echo html::select("branch[$i][]", isset($branchGroups[$product->id]) ? $branchGroups[$product->id] : array(), $branchIdList, "class='form-control chosen' multiple onchange=\"loadPlans('#products{$i}', this)\"");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php $i++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endforeach;?>
|
||||
<div class='col-sm-4 <?php if($projectID) echo 'required';?>' style="padding-right: 6px;">
|
||||
<div class='input-group'>
|
||||
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
|
||||
<span class='input-group-addon fix-border'></span>
|
||||
<div class="col-sm-6">
|
||||
<div class='input-group' <?php echo "id='plan$i'";?>>
|
||||
<span class='input-group-addon'><?php echo $lang->product->plan;?></span>
|
||||
<?php echo html::select("plans[$product->id][]", isset($productPlans[$product->id]) ? $productPlans[$product->id] : array(), $product->plans, "class='form-control chosen' multiple");?>
|
||||
<div class='input-group-btn'>
|
||||
<a href='javascript:;' onclick='addNewLine(this)' class='btn btn-link addLine'><i class='icon-plus'></i></a>
|
||||
<a href='javascript:;' onclick='removeLine(this)' class='btn btn-link removeLine' <?php if($i == 0) echo "style='visibility: hidden'";?>><i class='icon-close'></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th id='linkPlan'><?php echo $lang->execution->linkPlan;?></th>
|
||||
<td id="plansBox" colspan="3">
|
||||
<div class='row'>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($linkedProducts as $product):?>
|
||||
<?php foreach($linkedBranches[$product->id] as $branchID => $branch):?>
|
||||
<?php $plans = isset($productPlans[$product->id][$branchID]) ? $productPlans[$product->id][$branchID] : array();?>
|
||||
<div class="col-sm-4" id="plan<?php echo $i;?>" style="padding-right: 6px;"><?php echo html::select("plans[{$product->id}][{$branchID}][]", $plans, $branches[$product->id][$branchID]->plan, "class='form-control chosen' multiple");?></div>
|
||||
<?php $i++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endforeach;?>
|
||||
<div class="col-sm-4" id="planDefault" style="padding-right: 6px;"><?php echo html::select("plans[0][0][]", array(), 0, "class='form-control chosen' multiple");?></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php $i ++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endif;?>
|
||||
<?php if($project->model == 'waterfall'):?>
|
||||
<?php $class = (!$project->division and count($linkedProducts) < 2) ? 'hide' : '';?>
|
||||
|
||||
@@ -128,6 +128,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())
|
||||
{
|
||||
@@ -164,7 +165,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);
|
||||
|
||||
/* Record submit review action. */
|
||||
$story = $this->dao->findById((int)$storyID)->from(TABLE_STORY)->fetch();
|
||||
@@ -437,8 +440,7 @@ class story extends control
|
||||
$this->view->users = $users;
|
||||
$this->view->moduleID = $moduleID;
|
||||
$this->view->moduleOptionMenu = $moduleOptionMenu;
|
||||
$this->view->plans = str_replace('2030-01-01', $this->lang->story->undetermined, $this->loadModel('productplan')->getPairsForStory($productID, $branch, 'skipParent|unexpired|noclosed'));
|
||||
$this->view->planID = $planID;
|
||||
$this->view->plans = str_replace('2030-01-01', $this->lang->story->undetermined, $this->loadModel('productplan')->getPairsForStory($productID, $branch == 0 ? '' : $branch, 'skipParent|unexpired|noclosed')); $this->view->planID = $planID;
|
||||
$this->view->source = $source;
|
||||
$this->view->sourceNote = $sourceNote;
|
||||
$this->view->color = $color;
|
||||
@@ -931,6 +933,7 @@ class story extends control
|
||||
$this->view->title = $this->lang->story->edit . "STORY" . $this->lang->colon . $this->view->story->title;
|
||||
$this->view->position[] = $this->lang->story->edit;
|
||||
$this->view->story = $story;
|
||||
$this->view->siblings = empty($story->siblings) ? array() : $this->story->getByList($story->siblings);
|
||||
$this->view->stories = $stories;
|
||||
$this->view->users = $users;
|
||||
$this->view->product = $product;
|
||||
@@ -1023,6 +1026,16 @@ class story extends control
|
||||
/* Get edited stories. */
|
||||
$stories = $this->story->getByList($storyIdList);
|
||||
|
||||
/* Filter siblings. */
|
||||
$siblings = '';
|
||||
foreach($stories as $id => $story)
|
||||
{
|
||||
if(empty($story->siblings)) continue;
|
||||
$siblings .= "#$id ";
|
||||
unset($stories[$id]);
|
||||
}
|
||||
if(!empty($siblings)) echo js::alert(sprintf($this->lang->story->batchEditTip, $siblings));
|
||||
|
||||
$this->loadModel('branch');
|
||||
if($productID and !$executionID)
|
||||
{
|
||||
@@ -1251,6 +1264,8 @@ class story extends control
|
||||
|
||||
/* Assign. */
|
||||
$this->view->title = $this->lang->story->change . "STORY" . $this->lang->colon . $this->view->story->title;
|
||||
$this->view->siblings = empty($story->siblings) ? array() : $this->story->getByList($story->siblings);
|
||||
$this->view->branches = $this->loadModel('branch')->getPairs($story->product);
|
||||
$this->view->users = $this->user->getPairs('pofirst|nodeleted|noclosed', $this->view->story->assignedTo);
|
||||
$this->view->position[] = $this->lang->story->change;
|
||||
$this->view->needReview = (($this->app->user->account == $this->view->product->PO or $this->config->story->needReview == 0 or !$this->story->checkForceReview()) and empty($reviewer)) ? "checked='checked'" : "";
|
||||
@@ -1420,6 +1435,7 @@ class story extends control
|
||||
$this->view->position = $position;
|
||||
$this->view->product = $product;
|
||||
$this->view->branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($product->id);
|
||||
$this->view->siblings = !empty($story->siblings) ? $this->story->getByList($story->siblings) : array();
|
||||
$this->view->plan = $plan;
|
||||
$this->view->bugs = $bugs;
|
||||
$this->view->fromBug = $fromBug;
|
||||
@@ -1640,7 +1656,7 @@ class story extends control
|
||||
if(!$this->post->storyIdList) return print(js::locate($this->session->storyList, 'parent'));
|
||||
$storyIdList = $this->post->storyIdList;
|
||||
$storyIdList = array_unique($storyIdList);
|
||||
$actions = $this->story->batchReview($storyIdList, $result, $reason);
|
||||
$this->story->batchReview($storyIdList, $result, $reason);
|
||||
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
if(!dao::isError()) $this->loadModel('score')->create('ajax', 'batchOther');
|
||||
@@ -1877,8 +1893,15 @@ class story extends control
|
||||
$stories = $this->story->getByList($storyIdList);
|
||||
$productStoryList = array();
|
||||
$productList = array();
|
||||
$ignoreSiblings = array();
|
||||
foreach($stories as $story)
|
||||
{
|
||||
if(!empty($ignoreSiblings) and isset($ignoreSiblings[$story->id]))
|
||||
{
|
||||
unset($stories[$story->id]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if($story->parent == -1)
|
||||
{
|
||||
$skipStory[] = $story->id;
|
||||
@@ -1893,6 +1916,11 @@ class story extends control
|
||||
$storyProduct = isset($productList[$story->product]) ? $productList[$story->product] : $this->product->getByID($story->product);
|
||||
$branch = $storyProduct->type == 'branch' ? ($story->branch > 0 ? $story->branch : '0') : 'all';
|
||||
if(!isset($productStoryList[$story->product][$story->branch])) $productStoryList[$story->product][$story->branch] = $this->story->getProductStoryPairs($story->product, $branch, 0, 'all', 'id_desc', 0, '', $story->type);
|
||||
|
||||
if(!empty($story->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($story->siblings, ',')) as $siblingID) $ignoreSiblings[$siblingID] = $siblingID;
|
||||
}
|
||||
}
|
||||
|
||||
if($this->post->comments)
|
||||
@@ -1910,6 +1938,8 @@ class story extends control
|
||||
|
||||
$actionID = $this->action->create('story', $storyID, 'Closed', htmlSpecialString($this->post->comments[$storyID]), ucfirst($this->post->closedReasons[$storyID]) . ($this->post->duplicateStoryIDList[$storyID] ? ':' . (int)$this->post->duplicateStoryIDList[$storyID] : '') . "|$preStatus");
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
|
||||
if(!empty($stories[$storyID]->siblings)) $this->story->syncSiblings($storyID, $stories[$storyID]->siblings, $changes, 'Closed');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2256,12 +2286,27 @@ class story extends control
|
||||
{
|
||||
if(!empty($_POST) && isset($_POST['storyIdList']))
|
||||
{
|
||||
$allChanges = $this->story->batchAssignTo();
|
||||
$allChanges = $this->story->batchAssignTo();
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
|
||||
$assignedSiblings = array();
|
||||
$oldStories = $this->story->getByList($this->post->storyIdList);
|
||||
foreach($allChanges as $storyID => $changes)
|
||||
{
|
||||
$actionID = $this->action->create('story', $storyID, 'Assigned', '', $this->post->assignedTo);
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStories[$storyID]->siblings))
|
||||
{
|
||||
$siblings = $oldStories[$storyID]->siblings;
|
||||
foreach(explode(',', $siblings) as $siblingID)
|
||||
{
|
||||
if(in_array($siblingID, $this->post->storyIdList) or isset($assignedSiblings[$siblingID])) $siblings = str_replace(",$siblingID,", ',', $siblings);
|
||||
}
|
||||
$this->story->syncSiblings($storyID, trim($siblings, ','), $changes, 'Assigned');
|
||||
foreach(explode(',', trim($siblings, ',')) as $assignedID) $assignedSiblings[$assignedID] = $assignedID;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!dao::isError()) $this->loadModel('score')->create('ajax', 'batchOther');
|
||||
@@ -2970,4 +3015,29 @@ class story extends control
|
||||
|
||||
return print(html::select('URS[]', $URS, $requirementList, "class='form-control chosen' multiple"));
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Deleted story sibling.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxRelieveSibling()
|
||||
{
|
||||
$siblingID = !empty($_POST['siblingID']) ? $_POST['siblingID'] : 0;
|
||||
$story = $this->story->getByID($siblingID);
|
||||
$siblings = explode(',', trim($story->siblings, ','));
|
||||
|
||||
if(empty($story->siblings)) return $this->send(array('result' => 'fail'));
|
||||
|
||||
/* batchUnset siblingID from siblings.*/
|
||||
$replaceSql = "UPDATE " . TABLE_STORY . " SET siblings = REPLACE(siblings,',$siblingID,', ',') WHERE `product` = $story->product";
|
||||
$this->dbh->exec($replaceSql);
|
||||
|
||||
/* Update siblings to empty by siblingID and if siblings eq ','.*/
|
||||
$this->dao->update(TABLE_STORY)->set('siblings')->eq('')->where('id')->eq($siblingID)->orWhere('siblings')->eq(',')->exec();
|
||||
|
||||
if(!dao::isError()) $this->loadModel('action')->create('story', $siblingID, 'relieved');
|
||||
return $this->send(array('result' => 'success', 'silbingsCount' => count($siblings)-1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
#affectedProjects .table + h6 {border-top: 1px solid #ddd;}
|
||||
.nav-tabs>li>a {height: 36px;}
|
||||
.table-form>tbody>tr>th {width: 100px !important;}
|
||||
.colorpicker {right: 7px; top: 7px; opacity: 1;}
|
||||
.colorpicker {right: 7px; top: 7px; opacity: 1;}
|
||||
#siblingList td, #siblingTitle td {padding-left: 65px;}
|
||||
#siblingTitle td {font-weight: 700;}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.icon-exclamation-sign {color: #0075ff;}
|
||||
.linkbox {height: 180px; overflow-y: auto;}
|
||||
.tab-pane h6 {margin: 0px; padding: 10px;}
|
||||
.tab-pane h6, .tab-pane table {border: 1px solid #ddd; border-top: none;}
|
||||
@@ -5,7 +6,6 @@
|
||||
|
||||
.input-group .control-product + .chosen-container-single .chosen-single {border-radius: 2px 0 0 2px; border-right-width: 1px; padding-right: 1px;}
|
||||
.input-group .control-product + .chosen-container-active .chosen-single {border-right-width: 1px; padding-right: 0;}
|
||||
.input-group .control-branch + .chosen-container-single .chosen-single {border-radius: 0 2px 2px 0; border-left-width: 0px;}
|
||||
|
||||
#batchCreateForm .input-group, #batchEditForm .input-group,
|
||||
#batchCreateForm .input-group .form-control, #batchEditForm .input-group .form-control {position: static;}
|
||||
@@ -17,3 +17,15 @@
|
||||
.c-source {width: 130px}
|
||||
.c-note {width: 100px}
|
||||
.c-category {width: 90px}
|
||||
|
||||
.icon-info {color: #ff9800; border-color: #ff9800;}
|
||||
.text-active {text-decoration: underline;}
|
||||
.text-cancel {text-decoration: underline;color:#838a9d;margin-left:10px;}
|
||||
.popover {border:unset;}
|
||||
.popover.bottom .arrow{border-bottom-color:#ffffff;}
|
||||
.popover-icon {float:left;}
|
||||
.popover .content {margin-left:25px;overflow: overlay;}
|
||||
.hide {display:none !important;}
|
||||
.sibling > a.viewlink {padding:6px;white-space: nowrap;overflow: hidden;display: inline-flex;max-width: 70%;}
|
||||
.sibling > span {max-width: 60px;white-space: nowrap;overflow: hidden;}
|
||||
.tooltip-inner {max-width: 500px;}
|
||||
|
||||
@@ -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;}
|
||||
@@ -5,3 +5,4 @@
|
||||
.needNotReviewBox {width: 130px;}
|
||||
.needNotReviewBox span.input-group-addon {border: 1px solid #dcdcdc; border-left-width: 0px; width:130px;}
|
||||
#moduleIdBox .chosen-container, #planIdBox .chosen-container {min-width: unset;}
|
||||
#legendSiblings .form-group {margin-top: 10px;}
|
||||
|
||||
@@ -11,5 +11,4 @@
|
||||
.linkStoryTitle {white-space: nowrap; text-overflow: ellipsis; overflow: hidden;}
|
||||
.linkStoryTr > th {width: 90px;}
|
||||
.linkStoryTr > td {padding-left: 0px !important;}
|
||||
.removeButton {margin-left: -10px;}
|
||||
.resolution {white-space: nowrap; overflow: hidden;}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
$(function()
|
||||
{
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('#needNotReview').on('change', function()
|
||||
{
|
||||
$('#reviewer').val($(this).is(':checked') ? '' : lastReviewer).attr('disabled', $(this).is(':checked') ? 'disabled' : null).trigger('chosen:updated');
|
||||
|
||||
@@ -94,3 +94,49 @@ function loadURS()
|
||||
$('#URS').chosen();
|
||||
});
|
||||
}
|
||||
|
||||
$('.sibling').mouseover(function() {
|
||||
$(this).parent('ul').find('a.unlink').addClass('hide');
|
||||
$(this).find('.unlink').removeClass('hide');
|
||||
});
|
||||
$('.sibling').mouseenter(function() {
|
||||
$('[data-toggle="popover"]').popover('hide');
|
||||
});
|
||||
$('.sibling').mouseout(function() {
|
||||
$(this).find('.unlink').addClass('hide');
|
||||
});
|
||||
|
||||
$('[data-toggle="popover"]').each(function(item) {
|
||||
$index = $(this).attr('data-id');
|
||||
$(this).popover({
|
||||
placement: 'bottom',
|
||||
html: true,
|
||||
content: '<div class="popover-icon"><i class="icon-info"></i></div><div class="content">' + relievedTip + '</div><div class="popover-custom text-right"><a href="javascript:relieve(' + $index + ')" class="text-active btn-info">' + relieved + '</a> <a href="javascript:popoverCancel(' + $index + ');" class="text-cancel">' + cancel + '</a></div>'
|
||||
});
|
||||
})
|
||||
|
||||
function relieve(index)
|
||||
{
|
||||
$.post(relieveURL, {siblingID:index}, function(data){
|
||||
$('[data-id="' + index + '"]').popover('hide');
|
||||
|
||||
if(data.result == 'success')
|
||||
{
|
||||
if(data.silbingsCount != 0) $('[data-id="' + index + '"]').parent('li').remove();
|
||||
if(data.silbingsCount == 0 || index == storyID)
|
||||
{
|
||||
$('[href="#legendSiblings"]').parent('li').next('li').addClass('active');;
|
||||
$('[href="#legendSiblings"]').parent('li').remove();
|
||||
$('#legendSiblings').next('div').addClass('active');
|
||||
$('#legendSiblings').remove();
|
||||
$('#siblingTitle').remove();
|
||||
$('#siblingList').remove();
|
||||
}
|
||||
}
|
||||
}, 'json');
|
||||
}
|
||||
|
||||
function popoverCancel(index)
|
||||
{
|
||||
$('[data-id="' + index + '"]').popover('hide').addClass('hide');
|
||||
}
|
||||
|
||||
@@ -60,6 +60,18 @@ $(function()
|
||||
});
|
||||
|
||||
$(document).on('change', '#module', loadURS);
|
||||
|
||||
if($(".table-form select[id^='branches']").length == $('.switchBranch #branchBox option').length)
|
||||
{
|
||||
$('.table-col .icon-plus').parent().css('pointer-events', 'none')
|
||||
$('.table-col .icon-plus').parent().addClass('disabled')
|
||||
}
|
||||
|
||||
$.get(createLink('product', 'ajaxGetProductById', "productID=" + $('#product').val()), function(data)
|
||||
{
|
||||
$.cookie('branchSourceName', data.branchSourceName)
|
||||
$.cookie('branchName', data.branchName)
|
||||
}, 'json')
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -108,3 +120,215 @@ 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)
|
||||
{
|
||||
if($(".table-form select[id^='branches']").length == $('.switchBranch #branchBox option').length) return false;
|
||||
|
||||
var selectedVal = [];
|
||||
$(".table-form select[id^='branches']").each(function()
|
||||
{
|
||||
var selectedProduct = $(this).val();
|
||||
if($.inArray(selectedProduct, selectedVal) < 0) selectedVal.push(selectedProduct);
|
||||
});
|
||||
|
||||
var branch = 0;
|
||||
$('.table-form #branches0 option').each(function(){
|
||||
if($.inArray($(this).val(), selectedVal) < 0)
|
||||
{
|
||||
branch = $(this).val();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
$.ajaxSettings.async = false;
|
||||
loadBranchForSiblings($('#product').val(), branch, itemIndex)
|
||||
loadModuleForSiblings($('#product').val(), branch, itemIndex)
|
||||
loadPlanForSiblings($('#product').val(), branch, itemIndex)
|
||||
$.ajaxSettings.async = true;
|
||||
$('.addBranchesBox' + itemIndex + ' #branchBox .input-group .input-group-addon').html($.cookie('branchSourceName'))
|
||||
|
||||
disableSelectedBranches();
|
||||
|
||||
if($(".table-form select[id^='branches']").length == $('.switchBranch #branchBox option').length)
|
||||
{
|
||||
$('.table-col .icon-plus').parent().css('pointer-events', 'none')
|
||||
$('.table-col .icon-plus').parent().addClass('disabled')
|
||||
}
|
||||
|
||||
itemIndex ++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete branch box.
|
||||
*
|
||||
* @param obj $obj
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function deleteBranchesBox(obj)
|
||||
{
|
||||
$(obj).closest('tr').remove();
|
||||
|
||||
disableSelectedBranches();
|
||||
|
||||
$('.icon-plus').parent().css('pointer-events', 'auto')
|
||||
$('.icon-plus').parent().removeClass('disabled')
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the selected branch non clickable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function disableSelectedBranches()
|
||||
{
|
||||
$(".table-form select[id^='branches'] option[disabled='disabled']").removeAttr('disabled');
|
||||
|
||||
var selectedVal = [];
|
||||
$(".table-form select[id^='branches']").each(function()
|
||||
{
|
||||
var selectedBranch = $(this).val();
|
||||
if($.inArray(selectedBranch, selectedVal) < 0) selectedVal.push(selectedBranch);
|
||||
})
|
||||
|
||||
$(".table-form select[id^='branches']").each(function()
|
||||
{
|
||||
var selectedBranch = $(this).val();
|
||||
$(this).find('option').each(function()
|
||||
{
|
||||
var optionVal = $(this).attr('value');
|
||||
if(optionVal != selectedBranch && $.inArray(optionVal, selectedVal) >= 0) $(this).attr('disabled', 'disabled');
|
||||
})
|
||||
})
|
||||
|
||||
$(".table-form select[id^=branches]").trigger('chosen:updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
$.ajaxSettings.async = false;
|
||||
loadModuleForSiblings(productID, branch, branchIndex)
|
||||
loadPlanForSiblings(productID, branch, branchIndex)
|
||||
$.ajaxSettings.async = true;
|
||||
|
||||
disableSelectedBranches()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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=" + branch + "¶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=false&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(branch == '0') branch = '';
|
||||
planLink = createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '&planID=0&fieldID=' + branchIndex + '&needCreate=false&expired=unexpired¶m=skipParent|forStory,' + 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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
$(function()
|
||||
{
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('.main-side #branch').closest('td').find('#product_chosen .chosen-single').css('width', '153px');
|
||||
|
||||
if(storyStatus == 'reviewing')
|
||||
|
||||
+9
-12
@@ -9,17 +9,14 @@ $(function()
|
||||
/* Add for task #5385. */
|
||||
if(config.onlybody == 'yes') $('.main-actions').css('width', '100%')
|
||||
|
||||
$('#unlinkStory').click(function()
|
||||
$('.legendStories').mouseover(function()
|
||||
{
|
||||
if($('.removeButton').hasClass('hide'))
|
||||
{
|
||||
$('.removeButton').removeClass('hide');
|
||||
$(this).text(cancel);
|
||||
}
|
||||
else
|
||||
{
|
||||
$('.removeButton').addClass('hide');
|
||||
$(this).text(unlink);
|
||||
}
|
||||
})
|
||||
$(this).parent('ul').find('a.removeButton').addClass('hide');
|
||||
$(this).find('.removeButton').removeClass('hide');
|
||||
});
|
||||
|
||||
$('.legendStories').mouseout(function()
|
||||
{
|
||||
$(this).find('.removeButton').addClass('hide');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
global $config;
|
||||
$lang->story->create = "Story hinzufügen";
|
||||
|
||||
$lang->story->requirement = zget($lang, 'URCommon', "Requirement");
|
||||
$lang->story->requirement = 'UR';
|
||||
$lang->story->story = zget($lang, 'SRCommon', "Story");
|
||||
$lang->story->createStory = 'Create ' . $lang->story->story;
|
||||
$lang->story->createRequirement = 'Create ' . $lang->story->requirement;
|
||||
@@ -76,6 +76,10 @@ $lang->story->undetermined = 'undetermined';
|
||||
$lang->story->order = 'Order';
|
||||
$lang->story->saveDraft = 'Save as draft';
|
||||
$lang->story->doNotSubmit = 'Do Not Submit';
|
||||
$lang->story->currentBranch = 'Current Branch';
|
||||
$lang->story->siblings = 'Siblings story';
|
||||
$lang->story->relieved = 'Relieved';
|
||||
$lang->story->relievedSiblings = 'Relieved Siblings';
|
||||
|
||||
$lang->story->editAction = "Edit {$lang->SRCommon}";
|
||||
$lang->story->changeAction = "Change Story";
|
||||
@@ -96,6 +100,15 @@ $lang->story->storyRound = '%s time estimation';
|
||||
$lang->story->float = "『%s』should be positive number, decimals included.";
|
||||
$lang->story->saveDraftSuccess = 'Save as draft succeeded.';
|
||||
|
||||
$lang->story->changeSyncTip = "The modification of this story will be synchronized to the following twin requirements";
|
||||
$lang->story->syncTip = "The twin story are synchronized except for product, branch, module, plan, and stage. After the twin relationship is dissolved, they are no longer synchronized.";
|
||||
$lang->story->relievedTip = "The twin relationship cannot be restored after dissolving, the content of the demand is no longer synchronized, whether to dissolving?";
|
||||
$lang->story->assignSyncTip = "Both twin stories modify the assignor synchronously";
|
||||
$lang->story->closeSyncTip = "Twin stories are closed synchronously";
|
||||
$lang->story->activateSyncTip = "Twin stories are activated synchronously";
|
||||
$lang->story->relievedSiblingsTip = "After product adjustment, the twin relationship of this story will be automatically removed, and the story will no longer be synchronized. Do you want to save?";
|
||||
$lang->story->batchEditTip = "{$lang->SRCommon} %sis twin stories, and this operation has been filtered.";
|
||||
|
||||
$lang->story->id = 'ID';
|
||||
$lang->story->parent = 'Parent';
|
||||
$lang->story->product = $lang->productCommon;
|
||||
@@ -308,6 +321,7 @@ $lang->story->errorDuplicateStory = $lang->SRCommon . '%s not exist';
|
||||
$lang->story->confirmRecallChange = "After undo the change, the story content will revert to the version before the change. Are you sure you want to undo?";
|
||||
$lang->story->confirmRecallReview = "Are you sure you want to withdraw the review?";
|
||||
$lang->story->noStoryToTask = "Only the activated {$lang->SRCommon} can be converted into a task!";
|
||||
$lang->story->ignoreClosedStory = "{$lang->SRCommon} %s status is closed, and the operation has been filtered.";
|
||||
|
||||
$lang->story->form = new stdclass();
|
||||
$lang->story->form->area = 'Story Bereich';
|
||||
@@ -341,6 +355,7 @@ $lang->story->action->subdividestory = array('main' => '$date, aufgeteilt
|
||||
$lang->story->action->unlinkrelatedstory = array('main' => '$date, Verknüpfung aufgelöst durch <strong>$actor</strong> von Story <strong>$extra</strong>.');
|
||||
$lang->story->action->unlinkchildstory = array('main' => '$date, Verknüpfung aufgelöst durch <strong>$actor</strong> Story <strong>$extra</strong>.');
|
||||
$lang->story->action->recalledchange = array('main' => '$date, Undo changes by <strong>\$actor</strong>.');
|
||||
$lang->story->action->syncsiblings = array('main' => "\$date, the system judges that this story is adjusted synchronously due to the \$operate of twin story <strong>\$extra</strong>.", 'operate' => 'operateList');
|
||||
|
||||
/* Statistical statement. */
|
||||
$lang->story->report = new stdclass();
|
||||
@@ -429,6 +444,7 @@ $lang->story->chosen->reviewedBy = 'Prüfer wählen';
|
||||
$lang->story->notice = new stdClass();
|
||||
$lang->story->notice->closed = 'Die ausgewählten Storys wurden bereits geschlossen!';
|
||||
$lang->story->notice->reviewerNotEmpty = 'This requirement needs to be reviewed, and the reviewedby is required.';
|
||||
$lang->story->notice->changePlan = 'The plan can be changed to only one item.';
|
||||
|
||||
$lang->story->convertToTask = new stdClass();
|
||||
$lang->story->convertToTask->fieldList = array();
|
||||
@@ -466,6 +482,17 @@ $lang->story->featureBar['browse']['unclosed'] = $lang->story->unclosed;
|
||||
$lang->story->featureBar['browse']['draft'] = $lang->story->statusList['draft'];
|
||||
$lang->story->featureBar['browse']['reviewing'] = $lang->story->statusList['reviewing'];
|
||||
|
||||
$lang->story->operateList = array();
|
||||
$lang->story->operateList['assigned'] = 'assigned';
|
||||
$lang->story->operateList['closed'] = 'closed';
|
||||
$lang->story->operateList['activated'] = 'activated';
|
||||
$lang->story->operateList['changed'] = 'changed';
|
||||
$lang->story->operateList['reviewed'] = 'reviewed';
|
||||
$lang->story->operateList['edited'] = 'edited';
|
||||
$lang->story->operateList['submitreview'] = 'submit review';
|
||||
$lang->story->operateList['recalledchange'] = 'recalled change';
|
||||
$lang->story->operateList['recalled'] = 'recalled review';
|
||||
|
||||
$lang->requirement->common = $lang->URCommon;
|
||||
$lang->requirement->create = 'Create Requirement';
|
||||
$lang->requirement->batchCreate = "Batch Create";
|
||||
@@ -490,3 +517,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';
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
global $config;
|
||||
$lang->story->create = "Create Story";
|
||||
|
||||
$lang->story->requirement = zget($lang, 'URCommon', "Requirement");
|
||||
$lang->story->requirement = 'UR';
|
||||
$lang->story->story = zget($lang, 'SRCommon', "Story");
|
||||
$lang->story->createStory = 'Create ' . $lang->story->story;
|
||||
$lang->story->createRequirement = 'Create ' . $lang->story->requirement;
|
||||
@@ -76,6 +76,10 @@ $lang->story->undetermined = 'undetermined';
|
||||
$lang->story->order = 'Order';
|
||||
$lang->story->saveDraft = 'Save as draft';
|
||||
$lang->story->doNotSubmit = 'Do Not Submit';
|
||||
$lang->story->currentBranch = 'Current Branch';
|
||||
$lang->story->siblings = 'Siblings story';
|
||||
$lang->story->relieved = 'Relieved';
|
||||
$lang->story->relievedSiblings = 'Relieved Siblings';
|
||||
|
||||
$lang->story->editAction = "Edit {$lang->SRCommon}";
|
||||
$lang->story->changeAction = "Change {$lang->SRCommon}";
|
||||
@@ -96,6 +100,15 @@ $lang->story->storyRound = '%s time estimation';
|
||||
$lang->story->float = "『%s』should have positive number, or decimals.";
|
||||
$lang->story->saveDraftSuccess = 'Save as draft succeeded.';
|
||||
|
||||
$lang->story->changeSyncTip = "The modification of this story will be synchronized to the following twin requirements";
|
||||
$lang->story->syncTip = "The twin story are synchronized except for product, branch, module, plan, and stage. After the twin relationship is dissolved, they are no longer synchronized.";
|
||||
$lang->story->relievedTip = "The twin relationship cannot be restored after dissolving, the content of the demand is no longer synchronized, whether to dissolving?";
|
||||
$lang->story->assignSyncTip = "Both twin stories modify the assignor synchronously";
|
||||
$lang->story->closeSyncTip = "Twin stories are closed synchronously";
|
||||
$lang->story->activateSyncTip = "Twin stories are activated synchronously";
|
||||
$lang->story->relievedSiblingsTip = "After product adjustment, the twin relationship of this story will be automatically removed, and the story will no longer be synchronized. Do you want to save?";
|
||||
$lang->story->batchEditTip = "{$lang->SRCommon} %sis twin stories, and this operation has been filtered.";
|
||||
|
||||
$lang->story->id = 'ID';
|
||||
$lang->story->parent = 'Parent';
|
||||
$lang->story->product = $lang->productCommon;
|
||||
@@ -308,6 +321,7 @@ $lang->story->errorDuplicateStory = $lang->SRCommon . '%s not exist';
|
||||
$lang->story->confirmRecallChange = "After undo the change, the story content will revert to the version before the change. Are you sure you want to undo?";
|
||||
$lang->story->confirmRecallReview = "Are you sure you want to withdraw the review?";
|
||||
$lang->story->noStoryToTask = "Only the activated {$lang->SRCommon} can be converted into a task!";
|
||||
$lang->story->ignoreClosedStory = "{$lang->SRCommon} %s status is closed, and the operation has been filtered.";
|
||||
|
||||
$lang->story->form = new stdclass();
|
||||
$lang->story->form->area = 'Scope';
|
||||
@@ -341,6 +355,7 @@ $lang->story->action->subdividestory = array('main' => '$date, decomposed
|
||||
$lang->story->action->unlinkrelatedstory = array('main' => '$date, unlinked by <strong>$actor</strong> from Story <strong>$extra</strong>.');
|
||||
$lang->story->action->unlinkchildstory = array('main' => '$date, unlinked by <strong>$actor</strong> Decomposed Story <strong>$extra</strong>.');
|
||||
$lang->story->action->recalledchange = array('main' => '$date, Undo changes by <strong>\$actor</strong>.');
|
||||
$lang->story->action->syncsiblings = array('main' => "\$date, the system judges that this story is adjusted synchronously due to the \$operate of twin story <strong>\$extra</strong>.", 'operate' => 'operateList');
|
||||
|
||||
/* Statistical statement. */
|
||||
$lang->story->report = new stdclass();
|
||||
@@ -429,6 +444,7 @@ $lang->story->chosen->reviewedBy = 'Choose ReviewedBy';
|
||||
$lang->story->notice = new stdClass();
|
||||
$lang->story->notice->closed = 'Story that you select is closed!';
|
||||
$lang->story->notice->reviewerNotEmpty = 'This requirement needs to be reviewed, and the reviewedby is required.';
|
||||
$lang->story->notice->changePlan = 'The plan can be changed to only one item.';
|
||||
|
||||
$lang->story->convertToTask = new stdClass();
|
||||
$lang->story->convertToTask->fieldList = array();
|
||||
@@ -466,6 +482,17 @@ $lang->story->featureBar['browse']['unclosed'] = $lang->story->unclosed;
|
||||
$lang->story->featureBar['browse']['draft'] = $lang->story->statusList['draft'];
|
||||
$lang->story->featureBar['browse']['reviewing'] = $lang->story->statusList['reviewing'];
|
||||
|
||||
$lang->story->operateList = array();
|
||||
$lang->story->operateList['assigned'] = 'assigned';
|
||||
$lang->story->operateList['closed'] = 'closed';
|
||||
$lang->story->operateList['activated'] = 'activated';
|
||||
$lang->story->operateList['changed'] = 'changed';
|
||||
$lang->story->operateList['reviewed'] = 'reviewed';
|
||||
$lang->story->operateList['edited'] = 'edited';
|
||||
$lang->story->operateList['submitreview'] = 'submit review';
|
||||
$lang->story->operateList['recalledchange'] = 'recalled change';
|
||||
$lang->story->operateList['recalled'] = 'recalled review';
|
||||
|
||||
$lang->requirement->common = $lang->URCommon;
|
||||
$lang->requirement->create = 'Create Requirement';
|
||||
$lang->requirement->batchCreate = "Batch Create";
|
||||
@@ -490,3 +517,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';
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
global $config;
|
||||
$lang->story->create = "Créer Story";
|
||||
|
||||
$lang->story->requirement = zget($lang, 'URCommon', "Requirement");
|
||||
$lang->story->requirement = 'UR';
|
||||
$lang->story->story = zget($lang, 'SRCommon', "Story");
|
||||
$lang->story->createStory = 'Create ' . $lang->story->story;
|
||||
$lang->story->createRequirement = 'Create ' . $lang->story->requirement;
|
||||
@@ -76,6 +76,10 @@ $lang->story->undetermined = 'undetermined';
|
||||
$lang->story->order = 'Order';
|
||||
$lang->story->saveDraft = 'Save as draft';
|
||||
$lang->story->doNotSubmit = 'Do Not Submit';
|
||||
$lang->story->currentBranch = 'Current Branch';
|
||||
$lang->story->siblings = 'Siblings story';
|
||||
$lang->story->relieved = 'Relieved';
|
||||
$lang->story->relievedSiblings = 'Relieved Siblings';
|
||||
|
||||
$lang->story->editAction = "Edit {$lang->SRCommon}";
|
||||
$lang->story->changeAction = "Changer Story";
|
||||
@@ -96,6 +100,15 @@ $lang->story->storyRound = '%s time estimation';
|
||||
$lang->story->float = "『 %s 』doit avoir des nombres positifs ou décimaux.";
|
||||
$lang->story->saveDraftSuccess = 'Save as draft succeeded.';
|
||||
|
||||
$lang->story->changeSyncTip = "The modification of this story will be synchronized to the following twin requirements";
|
||||
$lang->story->syncTip = "The twin story are synchronized except for product, branch, module, plan, and stage. After the twin relationship is dissolved, they are no longer synchronized.";
|
||||
$lang->story->relievedTip = "The twin relationship cannot be restored after dissolving, the content of the demand is no longer synchronized, whether to dissolving?";
|
||||
$lang->story->assignSyncTip = "Both twin stories modify the assignor synchronously";
|
||||
$lang->story->closeSyncTip = "Twin stories are closed synchronously";
|
||||
$lang->story->activateSyncTip = "Twin stories are activated synchronously";
|
||||
$lang->story->relievedSiblingsTip = "After product adjustment, the twin relationship of this story will be automatically removed, and the story will no longer be synchronized. Do you want to save?";
|
||||
$lang->story->batchEditTip = "{$lang->SRCommon} %sis twin stories, and this operation has been filtered.";
|
||||
|
||||
$lang->story->id = 'ID';
|
||||
$lang->story->parent = 'Parent';
|
||||
$lang->story->product = $lang->productCommon;
|
||||
@@ -308,6 +321,7 @@ $lang->story->errorDuplicateStory = $lang->SRCommon . '%s not exist';
|
||||
$lang->story->confirmRecallChange = "After undo the change, the story content will revert to the version before the change. Are you sure you want to undo?";
|
||||
$lang->story->confirmRecallReview = "Are you sure you want to withdraw the review?";
|
||||
$lang->story->noStoryToTask = "Only the activated {$lang->SRCommon} can be converted into a task!";
|
||||
$lang->story->ignoreClosedStory = "{$lang->SRCommon} %s status is closed, and the operation has been filtered.";
|
||||
|
||||
$lang->story->form = new stdclass();
|
||||
$lang->story->form->area = 'Périmètre';
|
||||
@@ -341,6 +355,7 @@ $lang->story->action->subdividestory = array('main' => '$date, décompos
|
||||
$lang->story->action->unlinkrelatedstory = array('main' => '$date, dissociée par <strong>$actor</strong> de la Story <strong>$extra</strong>.');
|
||||
$lang->story->action->unlinkchildstory = array('main' => '$date, dissociée par <strong>$actor</strong> de la sous-Story <strong>$extra</strong>.');
|
||||
$lang->story->action->recalledchange = array('main' => '$date, Undo changes by <strong>\$actor</strong>.');
|
||||
$lang->story->action->syncsiblings = array('main' => "\$date, the system judges that this story is adjusted synchronously due to the \$operate of twin story <strong>\$extra</strong>.", 'operate' => 'operateList');
|
||||
|
||||
/* Statistical statement. */
|
||||
$lang->story->report = new stdclass();
|
||||
@@ -429,6 +444,7 @@ $lang->story->chosen->reviewedBy = 'Choisir valideur';
|
||||
$lang->story->notice = new stdClass();
|
||||
$lang->story->notice->closed = 'La Story que vous avez sélectionnée est malheureusement fermée !';
|
||||
$lang->story->notice->reviewerNotEmpty = 'This requirement needs to be reviewed, and the reviewedby is required.';
|
||||
$lang->story->notice->changePlan = 'The plan can be changed to only one item.';
|
||||
|
||||
$lang->story->convertToTask = new stdClass();
|
||||
$lang->story->convertToTask->fieldList = array();
|
||||
@@ -466,6 +482,17 @@ $lang->story->featureBar['browse']['unclosed'] = $lang->story->unclosed;
|
||||
$lang->story->featureBar['browse']['draft'] = $lang->story->statusList['draft'];
|
||||
$lang->story->featureBar['browse']['reviewing'] = $lang->story->statusList['reviewing'];
|
||||
|
||||
$lang->story->operateList = array();
|
||||
$lang->story->operateList['assigned'] = 'assigned';
|
||||
$lang->story->operateList['closed'] = 'closed';
|
||||
$lang->story->operateList['activated'] = 'activated';
|
||||
$lang->story->operateList['changed'] = 'changed';
|
||||
$lang->story->operateList['reviewed'] = 'reviewed';
|
||||
$lang->story->operateList['edited'] = 'edited';
|
||||
$lang->story->operateList['submitreview'] = 'submit review';
|
||||
$lang->story->operateList['recalledchange'] = 'recalled change';
|
||||
$lang->story->operateList['recalled'] = 'recalled review';
|
||||
|
||||
$lang->requirement->common = $lang->URCommon;
|
||||
$lang->requirement->create = 'Create Requirement';
|
||||
$lang->requirement->batchCreate = "Batch Create";
|
||||
@@ -490,3 +517,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';
|
||||
|
||||
@@ -94,6 +94,14 @@ $lang->story->storyRound = '%s time estimation';
|
||||
$lang->story->float = "『%s』should be positive number, decimals included.";
|
||||
$lang->story->saveDraftSuccess = 'Save as draft succeeded.';
|
||||
|
||||
$lang->story->changeSyncTip = "The modification of this story will be synchronized to the following twin requirements";
|
||||
$lang->story->syncTip = "The twin story are synchronized except for product, branch, module, plan, and stage. After the twin relationship is dissolved, they are no longer synchronized.";
|
||||
$lang->story->assignSyncTip = "Both twin stories modify the assignor synchronously";
|
||||
$lang->story->closeSyncTip = "Twin stories are closed synchronously";
|
||||
$lang->story->activateSyncTip = "Twin stories are activated synchronously";
|
||||
$lang->story->relievedSiblingsTip = "After product adjustment, the twin relationship of this story will be automatically removed, and the story will no longer be synchronized. Do you want to save?";
|
||||
$lang->story->batchEditTip = "{$lang->SRCommon} %sis twin stories, and this operation has been filtered.";
|
||||
|
||||
$lang->story->id = 'ID';
|
||||
$lang->story->parent = 'Mẹ';
|
||||
$lang->story->product = $lang->productCommon;
|
||||
@@ -332,6 +340,7 @@ $lang->story->action->subdividestory = array('main' => '$date, được p
|
||||
$lang->story->action->unlinkrelatedstory = array('main' => '$date, bị hủy bởi <strong>$actor</strong> từ Câu chuyện <strong>$extra</strong>.');
|
||||
$lang->story->action->unlinkchildstory = array('main' => '$date, bị hủy bởi <strong>$actor</strong> Câu chuyện được phân rã <strong>$extra</strong>.');
|
||||
$lang->story->action->recalledchange = array('main' => '$date, Undo changes by <strong>\$actor</strong>.');
|
||||
$lang->story->action->syncsiblings = array('main' => "\$date, the system judges that this story is adjusted synchronously due to the \$operate of twin story <strong>\$extra</strong>.", 'operate' => 'operateList');
|
||||
|
||||
/* Statistical statement. */
|
||||
$lang->story->report = new stdclass();
|
||||
@@ -456,3 +465,14 @@ $lang->story->featureBar['browse']['all'] = $lang->all;
|
||||
$lang->story->featureBar['browse']['unclosed'] = $lang->story->unclosed;
|
||||
$lang->story->featureBar['browse']['draft'] = $lang->story->statusList['draft'];
|
||||
$lang->story->featureBar['browse']['reviewing'] = $lang->story->statusList['reviewing'];
|
||||
|
||||
$lang->story->operateList = array();
|
||||
$lang->story->operateList['assigned'] = 'assigned';
|
||||
$lang->story->operateList['closed'] = 'closed';
|
||||
$lang->story->operateList['activated'] = 'activated';
|
||||
$lang->story->operateList['changed'] = 'changed';
|
||||
$lang->story->operateList['reviewed'] = 'reviewed';
|
||||
$lang->story->operateList['edited'] = 'edited';
|
||||
$lang->story->operateList['submitreview'] = 'submit review';
|
||||
$lang->story->operateList['recalledchange'] = 'recalled change';
|
||||
$lang->story->operateList['recalled'] = 'recalled review';
|
||||
|
||||
@@ -76,6 +76,10 @@ $lang->story->undetermined = '待定';
|
||||
$lang->story->order = '排序';
|
||||
$lang->story->saveDraft = '存为草稿';
|
||||
$lang->story->doNotSubmit = '保存暂不提交';
|
||||
$lang->story->currentBranch = '当前%s';
|
||||
$lang->story->siblings = '孪生需求';
|
||||
$lang->story->relieved = '解除';
|
||||
$lang->story->relievedSiblings = '解除孪生需求';
|
||||
|
||||
$lang->story->editAction = "编辑{$lang->SRCommon}";
|
||||
$lang->story->changeAction = "变更{$lang->SRCommon}";
|
||||
@@ -96,6 +100,15 @@ $lang->story->storyRound = '第 %s 轮估算';
|
||||
$lang->story->float = "『%s』应当是正数,可以是小数。";
|
||||
$lang->story->saveDraftSuccess = '存为草稿成功';
|
||||
|
||||
$lang->story->changeSyncTip = "该需求的修改会同步到如下的孪生需求";
|
||||
$lang->story->syncTip = "孪生需求间除产品、分支 、模块、计划、阶段外均同步,孪生关系解除后不再同步";
|
||||
$lang->story->relievedTip = "孪生关系解除后无法恢复,需求的内容不再同步,是否解除?";
|
||||
$lang->story->assignSyncTip = "孪生需求均同步修改指派人";
|
||||
$lang->story->closeSyncTip = "孪生需求均同步关闭";
|
||||
$lang->story->activateSyncTip = "孪生需求均同步激活";
|
||||
$lang->story->relievedSiblingsTip = '产品调整后,本需求自动解除孪生关系,需求不再同步,是否保存?';
|
||||
$lang->story->batchEditTip = "{$lang->SRCommon} %s为孪生需求,本次操作已被过滤。";
|
||||
|
||||
$lang->story->id = '编号';
|
||||
$lang->story->parent = '父需求';
|
||||
$lang->story->product = "所属{$lang->productCommon}";
|
||||
@@ -308,6 +321,7 @@ $lang->story->errorDuplicateStory = $lang->SRCommon . '%s不存在';
|
||||
$lang->story->confirmRecallChange = "撤销变更后,需求内容会回退至变更前的版本,您确定要撤销吗?";
|
||||
$lang->story->confirmRecallReview = "您确定要撤回评审吗?";
|
||||
$lang->story->noStoryToTask = "只有激活的{$lang->SRCommon}才能转为任务!";
|
||||
$lang->story->ignoreClosedStory = "{$lang->SRCommon} %s 状态为已关闭,本次操作已被过滤。";
|
||||
|
||||
$lang->story->form = new stdclass();
|
||||
$lang->story->form->area = "该{$lang->SRCommon}所属范围";
|
||||
@@ -341,6 +355,7 @@ $lang->story->action->subdividestory = array('main' => "\$date, 由 <stro
|
||||
$lang->story->action->unlinkrelatedstory = array('main' => "\$date, 由 <strong>\$actor</strong> 移除相关{$lang->SRCommon} <strong>\$extra</strong>。");
|
||||
$lang->story->action->unlinkchildstory = array('main' => "\$date, 由 <strong>\$actor</strong> 移除细分{$lang->SRCommon} <strong>\$extra</strong>。");
|
||||
$lang->story->action->recalledchange = array('main' => "\$date, 由 <strong>\$actor</strong> 撤销变更。");
|
||||
$lang->story->action->syncsiblings = array('main' => "\$date, 系统判断由于孪生需求 <strong>\$extra</strong> \$operate,本需求同步调整。", 'operate' => 'operateList');
|
||||
|
||||
/* 统计报表。*/
|
||||
$lang->story->report = new stdclass();
|
||||
@@ -429,6 +444,7 @@ $lang->story->chosen->reviewedBy = '选择评审人...';
|
||||
$lang->story->notice = new stdClass();
|
||||
$lang->story->notice->closed = "您选择的{$lang->SRCommon}已经被关闭了!";
|
||||
$lang->story->notice->reviewerNotEmpty = '该需求需要评审,评审人员不能为空。';
|
||||
$lang->story->notice->changePlan = '所属计划只能改为一条,修改后才能保存成功。';
|
||||
|
||||
$lang->story->convertToTask = new stdClass();
|
||||
$lang->story->convertToTask->fieldList = array();
|
||||
@@ -466,6 +482,17 @@ $lang->story->featureBar['browse']['unclosed'] = $lang->story->unclosed;
|
||||
$lang->story->featureBar['browse']['draft'] = $lang->story->statusList['draft'];
|
||||
$lang->story->featureBar['browse']['reviewing'] = $lang->story->statusList['reviewing'];
|
||||
|
||||
$lang->story->operateList = array();
|
||||
$lang->story->operateList['assigned'] = '指派';
|
||||
$lang->story->operateList['closed'] = '关闭';
|
||||
$lang->story->operateList['activated'] = '激活';
|
||||
$lang->story->operateList['changed'] = '变更';
|
||||
$lang->story->operateList['reviewed'] = '评审';
|
||||
$lang->story->operateList['edited'] = '编辑';
|
||||
$lang->story->operateList['submitreview'] = '提交评审';
|
||||
$lang->story->operateList['recalledchange'] = '撤销变更';
|
||||
$lang->story->operateList['recalled'] = '撤销评审';
|
||||
|
||||
$lang->requirement->common = $lang->URCommon;
|
||||
$lang->requirement->create = "提{$lang->URCommon}";
|
||||
$lang->requirement->batchCreate = "批量创建";
|
||||
@@ -490,3 +517,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 = '每个分支会建立一个需求,需求间互为孪生关系,孪生需求间除产品、分支、模块、计划、阶段字段外均同步,后期您可以手动解除孪生关系';
|
||||
|
||||
+544
-168
@@ -49,11 +49,18 @@ class storyModel extends model
|
||||
|
||||
$story->executions = $this->dao->select('t1.project, t2.name, t2.status, t2.type, t2.multiple')->from(TABLE_PROJECTSTORY)->alias('t1')
|
||||
->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.project = t2.id')
|
||||
->where('t1.story')->eq($storyID)
|
||||
->andWhere('t2.type')->in('sprint,stage,kanban')
|
||||
->where('t2.type')->in('sprint,stage,kanban')
|
||||
->beginIF($story->siblings)->andWhere('t1.story')->in(ltrim($story->siblings, ',') . $story->id)
|
||||
->beginIF(!$story->siblings)->andWhere('t1.story')->in($story->id)
|
||||
->orderBy('t1.`order` DESC')
|
||||
->fetchAll('project');
|
||||
$story->tasks = $this->dao->select('id, name, assignedTo, execution, project, status, consumed, `left`,type')->from(TABLE_TASK)->where('story')->eq($storyID)->andWhere('deleted')->eq(0)->orderBy('id DESC')->fetchGroup('execution');
|
||||
$story->tasks = $this->dao->select('id, name, assignedTo, execution, project, status, consumed, `left`,type')->from(TABLE_TASK)
|
||||
->where('deleted')->eq(0)
|
||||
->beginIF($story->siblings)->andWhere('story')->in(ltrim($story->siblings, ',') . $story->id)
|
||||
->beginIF(!$story->siblings)->andWhere('story')->in($story->id)
|
||||
->orderBy('id DESC')
|
||||
->fetchGroup('execution');
|
||||
|
||||
$story->stages = $this->dao->select('*')->from(TABLE_STORYSTAGE)->where('story')->eq($storyID)->fetchPairs('branch', 'stage');
|
||||
//$story->bugCount = $this->dao->select('COUNT(*)')->alias('count')->from(TABLE_BUG)->where('story')->eq($storyID)->fetch('count');
|
||||
//$story->caseCount = $this->dao->select('COUNT(*)')->alias('count')->from(TABLE_CASE)->where('story')->eq($storyID)->fetch('count');
|
||||
@@ -159,13 +166,20 @@ class storyModel extends model
|
||||
}
|
||||
|
||||
/* Get affected bugs. */
|
||||
$story->bugs = $this->dao->findByStory($story->id)->from(TABLE_BUG)
|
||||
$story->bugs = $this->dao->select('*')->from(TABLE_BUG)
|
||||
->where('status')->ne('closed')
|
||||
->beginIF($story->siblings)->andWhere('story')->in(ltrim($story->siblings, ',') . $story->id)
|
||||
->beginIF(!$story->siblings)->andWhere('story')->in($story->id)
|
||||
->andWhere('status')->ne('closed')
|
||||
->andWhere('deleted')->eq(0)
|
||||
->orderBy('id desc')->fetchAll();
|
||||
|
||||
/* Get affected cases. */
|
||||
$story->cases = $this->dao->findByStory($story->id)->from(TABLE_CASE)->andWhere('deleted')->eq(0)->fetchAll();
|
||||
$story->cases = $this->dao->select('*')->from(TABLE_CASE)
|
||||
->where('deleted')->eq(0)
|
||||
->beginIF($story->siblings)->andWhere('story')->in(ltrim($story->siblings, ',') . $story->id)
|
||||
->beginIF(!$story->siblings)->andWhere('story')->in($story->id)
|
||||
->fetchAll();
|
||||
|
||||
return $story;
|
||||
}
|
||||
@@ -228,173 +242,223 @@ 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())
|
||||
/* check module */
|
||||
$requiredFields = "," . $this->config->story->create->requiredFields . ",";
|
||||
if(strpos($requiredFields, ',module,') !== false)
|
||||
{
|
||||
$storyID = $this->dao->lastInsertID();
|
||||
|
||||
/* Fix bug #21992, user story have no parent story. */
|
||||
if(isset($story->parent) and $story->parent)
|
||||
foreach($this->post->modules as $module)
|
||||
{
|
||||
$stories = array($storyID);
|
||||
$this->subdivide($story->parent, $stories);
|
||||
}
|
||||
|
||||
if(!empty($story->plan))
|
||||
{
|
||||
$this->updateStoryOrderOfPlan($storyID, $story->plan); // Set story order in this plan.
|
||||
$this->loadModel('action')->create('productplan', $story->plan, 'linkstory', '', $storyID);
|
||||
}
|
||||
|
||||
$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)
|
||||
if(empty($module))
|
||||
{
|
||||
if(empty($reviewer)) continue;
|
||||
|
||||
$reviewData = new stdclass();
|
||||
$reviewData->story = $storyID;
|
||||
$reviewData->version = 1;
|
||||
$reviewData->reviewer = $reviewer;
|
||||
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
|
||||
dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->story->module);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Project or execution linked story. */
|
||||
if($executionID != 0)
|
||||
$storyIds = array();
|
||||
$storyFile = array();
|
||||
$mainStoryID = 0;
|
||||
foreach($this->post->branches as $key => $branch)
|
||||
{
|
||||
$story->branch = $branch;
|
||||
$story->module = $this->post->modules[$key];
|
||||
$story->plan = $this->post->plans[$key];
|
||||
|
||||
if(strpos('draft,reviewing', $story->status) !== false) $story->stage = $this->post->plan > 0 ? 'planned' : 'wait';
|
||||
if($story->type == 'requirement') $requiredFields = str_replace(',plan,', ',', $requiredFields);
|
||||
if(strpos($requiredFields, ',estimate,') !== false)
|
||||
{
|
||||
$this->linkStory($executionID, $this->post->product, $storyID);
|
||||
if($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(strlen(trim($story->estimate)) == 0) dao::$errors['estimate'] = sprintf($this->lang->error->notempty, $this->lang->story->estimate);
|
||||
$requiredFields = str_replace(',estimate,', ',', $requiredFields);
|
||||
}
|
||||
|
||||
if(is_array($this->post->URS))
|
||||
$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())
|
||||
{
|
||||
foreach($this->post->URS as $URID)
|
||||
$storyID = $this->dao->lastInsertID();
|
||||
|
||||
/* Fix bug #21992, user story have no parent story. */
|
||||
if(isset($story->parent) and $story->parent)
|
||||
{
|
||||
$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();
|
||||
$stories = array($storyID);
|
||||
$this->subdivide($story->parent, $stories);
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
if(!empty($story->plan))
|
||||
{
|
||||
foreach($files as $file)
|
||||
$this->updateStoryOrderOfPlan($storyID, $story->plan); // Set story order in this plan.
|
||||
$this->loadModel('action')->create('productplan', $story->plan, 'linkstory', '', $storyID);
|
||||
}
|
||||
|
||||
$this->file->updateObjectID($this->post->uid, $storyID, $story->type);
|
||||
$files = $this->file->saveUpload($story->type, $storyID, 1);
|
||||
/* Multi branch sync files */
|
||||
!empty($files) ? $storyFile = $files : $files = $storyFile;
|
||||
|
||||
$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)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -739,6 +803,16 @@ class storyModel extends model
|
||||
$data->files = $story->files = $addedFiles . trim($storyFiles, ',');
|
||||
$this->dao->insert(TABLE_STORYSPEC)->data($data)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$data->story = $siblingID;
|
||||
$this->dao->insert(TABLE_STORYSPEC)->data($data)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
/* IF is story and has changed, update its relation version to new. */
|
||||
if($oldStory->type == 'story')
|
||||
{
|
||||
@@ -767,6 +841,16 @@ class storyModel extends model
|
||||
$reviewData->version = $story->version;
|
||||
$reviewData->reviewer = $reviewer;
|
||||
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$reviewData->story = $siblingID;
|
||||
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($reviewerHasChanged)
|
||||
@@ -776,7 +860,9 @@ class storyModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
return common::createChanges($oldStory, $story);
|
||||
$changes = common::createChanges($oldStory, $story);
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($oldStory->id, $oldStory->siblings, $changes, 'Changed');
|
||||
return $changes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -803,6 +889,17 @@ class storyModel extends model
|
||||
return false;
|
||||
}
|
||||
|
||||
$storyPlan = !empty($_POST['plan']) ? array_filter($_POST['plan']) : array();
|
||||
if(count($storyPlan) > 1)
|
||||
{
|
||||
$oldStoryPlan = !empty($oldStory->planTitle) ? array_keys($oldStory->planTitle) : array();
|
||||
if(!empty(array_diff($storyPlan,$oldStoryPlan)) or !empty(array_diff($oldStoryPlan,$storyPlan)))
|
||||
{
|
||||
dao::$errors[] = $this->lang->story->notice->changePlan;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$story = fixer::input('post')
|
||||
->cleanInt('product,module,pri,duplicateStory')
|
||||
->cleanFloat('estimate')
|
||||
@@ -836,6 +933,14 @@ class storyModel extends model
|
||||
->remove('files,labels,comment,contactListMenu,stages,reviewer,needNotReview')
|
||||
->get();
|
||||
|
||||
/* Relieve siblings when change product. */
|
||||
if(!empty($oldStory->siblings) and $story->product != $oldStory->product)
|
||||
{
|
||||
$this->dbh->exec("UPDATE " . TABLE_STORY . " SET siblings = REPLACE(siblings, ',$storyID,', ',') WHERE `product` = $oldStory->product");
|
||||
$this->dao->update(TABLE_STORY)->set('siblings')->eq('')->where('id')->eq($storyID)->orWhere('siblings')->eq(',')->exec();
|
||||
$oldStory->siblings = '';
|
||||
}
|
||||
|
||||
if($oldStory->type == 'story' and !isset($story->linkStories)) $story->linkStories = '';
|
||||
if($oldStory->type == 'requirement' and !isset($story->linkRequirements)) $story->linkRequirements = '';
|
||||
if($oldStory->status == 'changing' and $story->status == 'draft') $story->status = 'changing';
|
||||
@@ -887,6 +992,19 @@ class storyModel extends model
|
||||
->beginIF($oldStory->status == 'reviewing')->andWhere('reviewer')->notin(implode(',', $_POST['reviewer']))
|
||||
->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)
|
||||
->where('story')->eq($siblingID)
|
||||
->andWhere('version')->eq($oldStory->version)
|
||||
->beginIF($oldStory->status == 'reviewing')->andWhere('reviewer')->notin(implode(',', $_POST['reviewer']))
|
||||
->exec();
|
||||
}
|
||||
}
|
||||
|
||||
foreach($_POST['reviewer'] as $reviewer)
|
||||
{
|
||||
if($oldStory->status == 'reviewing' and in_array($reviewer, array_keys($oldReviewer))) continue;
|
||||
@@ -896,6 +1014,16 @@ class storyModel extends model
|
||||
$reviewData->version = $oldStory->version;
|
||||
$reviewData->reviewer = $reviewer;
|
||||
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$reviewData->story = $siblingID;
|
||||
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($oldStory->status == 'reviewing') $story = $this->updateStoryByReview($storyID, $oldStory, $story);
|
||||
@@ -933,6 +1061,18 @@ class storyModel extends model
|
||||
$data->verify = $story->verify;
|
||||
$data->files = $story->files = $addedFiles . trim($storyFiles, ',');
|
||||
$this->dao->update(TABLE_STORYSPEC)->data($data)->where('story')->eq((int)$storyID)->andWhere('version')->eq($oldStory->version)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->update(TABLE_STORYSPEC)->data($data)
|
||||
->where('story')->eq((int)$siblingID)
|
||||
->andWhere('version')->eq($oldStory->version)
|
||||
->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($story->product != $oldStory->product)
|
||||
@@ -1062,6 +1202,8 @@ class storyModel extends model
|
||||
if(isset($story->finalResult)) $this->recordReviewAction($story);
|
||||
}
|
||||
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($oldStory->id, $oldStory->siblings, $changes, 'Edited');
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1475,7 +1617,28 @@ class storyModel extends model
|
||||
/* Fix bug #671. */
|
||||
$this->lang->story->closedReason = $this->lang->story->rejectedReason;
|
||||
|
||||
$this->dao->update(TABLE_STORYREVIEW)->set('result')->eq($this->post->result)->set('reviewDate')->eq($now)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->eq($this->app->user->account)->exec();
|
||||
$this->dao->update(TABLE_STORYREVIEW)
|
||||
->set('result')->eq($this->post->result)
|
||||
->set('reviewDate')->eq($now)
|
||||
->where('story')->eq($storyID)
|
||||
->andWhere('version')->eq($oldStory->version)
|
||||
->andWhere('reviewer')->eq($this->app->user->account)
|
||||
->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->update(TABLE_STORYREVIEW)
|
||||
->set('result')->eq($this->post->result)
|
||||
->set('reviewDate')->eq($now)
|
||||
->where('story')->eq($siblingID)
|
||||
->andWhere('version')->eq($oldStory->version)
|
||||
->andWhere('reviewer')->eq($this->app->user->account)
|
||||
->exec();
|
||||
}
|
||||
}
|
||||
|
||||
$story = $this->updateStoryByReview($storyID, $oldStory, $story);
|
||||
|
||||
@@ -1507,6 +1670,8 @@ class storyModel extends model
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($oldStory->id, $oldStory->siblings, $changes, 'Reviewed');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1525,17 +1690,23 @@ class storyModel extends model
|
||||
$actions = array();
|
||||
$this->loadModel('action');
|
||||
|
||||
$oldStories = $this->getByList($storyIdList);
|
||||
$hasResult = $this->dao->select('story,version,result')->from(TABLE_STORYREVIEW)->where('story')->in($storyIdList)->andWhere('reviewer')->eq($this->app->user->account)->andWhere('result')->ne('')->orderBy('version')->fetchAll('story');
|
||||
$reviewerList = $this->dao->select('story,reviewer,result,version')->from(TABLE_STORYREVIEW)->where('story')->in($storyIdList)->orderBy('version')->fetchGroup('story', 'reviewer');
|
||||
$reviewedSiblings = array();
|
||||
$oldStories = $this->getByList($storyIdList);
|
||||
$hasResult = $this->dao->select('story,version,result')->from(TABLE_STORYREVIEW)->where('story')->in($storyIdList)->andWhere('reviewer')->eq($this->app->user->account)->andWhere('result')->ne('')->orderBy('version')->fetchAll('story');
|
||||
$reviewerList = $this->dao->select('story,reviewer,result,version')->from(TABLE_STORYREVIEW)->where('story')->in($storyIdList)->orderBy('version')->fetchGroup('story', 'reviewer');
|
||||
foreach($storyIdList as $storyID)
|
||||
{
|
||||
if(!$storyID) continue;
|
||||
|
||||
$isSuperReviewer = strpos(',' . trim(zget($this->config->story, 'superReviewers', ''), ',') . ',', ',' . $this->app->user->account . ',');
|
||||
$oldStory = $oldStories[$storyID];
|
||||
|
||||
if($oldStory->status != 'reviewing') continue;
|
||||
|
||||
foreach($reviewerList[$storyID] as $reviewer => $reviewerInfo)
|
||||
{
|
||||
if($reviewerInfo->version != $oldStory->version) unset($reviewerList[$storyID][$reviewer]);
|
||||
}
|
||||
|
||||
if(!in_array($this->app->user->account, array_keys($reviewerList[$storyID])) and $isSuperReviewer === false) continue;
|
||||
if(isset($hasResult[$storyID]) and $hasResult[$storyID]->version == $oldStories[$storyID]->version) continue;
|
||||
if($oldStory->version > 1 and $result == 'reject') continue;
|
||||
@@ -1549,6 +1720,21 @@ class storyModel extends model
|
||||
|
||||
$this->dao->update(TABLE_STORYREVIEW)->set('result')->eq($result)->set('reviewDate')->eq($now)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->eq($this->app->user->account)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->update(TABLE_STORYREVIEW)
|
||||
->set('result')->eq($result)
|
||||
->set('reviewDate')->eq($now)
|
||||
->where('story')->eq($siblingID)
|
||||
->andWhere('version')->eq($oldStory->version)
|
||||
->andWhere('reviewer')->eq($this->app->user->account)
|
||||
->exec();
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the story status by review rules. */
|
||||
$reviewedBy = explode(',', trim($story->reviewedBy, ','));
|
||||
if($isSuperReviewer !== false)
|
||||
@@ -1571,6 +1757,19 @@ class storyModel extends model
|
||||
$story->id = $storyID;
|
||||
$story->version = $oldStory->version;
|
||||
$actions[$storyID] = $this->recordReviewAction($story, $result, $reason);
|
||||
|
||||
/* Sync siblings. */
|
||||
$changes = common::createChanges($oldStory, $story);
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
$siblings = $oldStory->siblings;
|
||||
foreach(explode(',', $siblings) as $siblingID)
|
||||
{
|
||||
if(in_array($siblingID, $storyIdList) or isset($reviewedSiblings[$siblingID])) $siblings = str_replace(",$siblingID,", ',', $siblings);
|
||||
}
|
||||
$this->syncSiblings($storyID, trim($siblings, ','), $changes, 'Reviewed');
|
||||
foreach(explode(',', trim($siblings, ',')) as $reviewedID) $reviewedSiblings[$reviewedID] = $reviewedID;
|
||||
}
|
||||
}
|
||||
|
||||
return $actions;
|
||||
@@ -1585,11 +1784,26 @@ class storyModel extends model
|
||||
*/
|
||||
public function recallReview($storyID)
|
||||
{
|
||||
$story = $this->getById($storyID);
|
||||
$isChanged = $story->changedBy ? true : false;
|
||||
$status = $isChanged ? 'changing' : 'draft';
|
||||
$this->dao->update(TABLE_STORY)->set('status')->eq($status)->where('id')->eq($storyID)->exec();
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($story->version)->exec();
|
||||
$oldStory = $this->getById($storyID);
|
||||
$isChanged = $oldStory->changedBy ? true : false;
|
||||
|
||||
$story = clone $oldStory;
|
||||
$story->status = $isChanged ? 'changing' : 'draft';
|
||||
$this->dao->update(TABLE_STORY)->set('status')->eq($story->status)->where('id')->eq($storyID)->exec();
|
||||
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($siblingID)->andWhere('version')->eq($oldStory->version)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
$changes = common::createChanges($oldStory, $story);
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($storyID, $oldStory->siblings, $changes, 'recalled');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1601,16 +1815,31 @@ class storyModel extends model
|
||||
*/
|
||||
public function recallChange($storyID)
|
||||
{
|
||||
$story = $this->getById($storyID);
|
||||
$oldStory = $this->getById($storyID);
|
||||
|
||||
/* Update story title and version and status. */
|
||||
$preVersion = $story->version - 1;
|
||||
$preTitle = $this->dao->select('title')->from(TABLE_STORYSPEC)->where('story')->eq($storyID)->andWHere('version')->eq($preVersion)->fetch('title');
|
||||
$this->dao->update(TABLE_STORY)->set('title')->eq($preTitle)->set('version')->eq($preVersion)->set('status')->eq('active')->where('id')->eq($storyID)->exec();
|
||||
$story = clone $oldStory;
|
||||
$story->version = $oldStory->version - 1;
|
||||
$story->title = $this->dao->select('title')->from(TABLE_STORYSPEC)->where('story')->eq($storyID)->andWHere('version')->eq($story->version)->fetch('title');
|
||||
$story->status = 'active';
|
||||
$this->dao->update(TABLE_STORY)->set('title')->eq($story->title)->set('version')->eq($story->version)->set('status')->eq($story->status)->where('id')->eq($storyID)->exec();
|
||||
|
||||
/* Delete versions that is after this version. */
|
||||
$this->dao->delete()->from(TABLE_STORYSPEC)->where('story')->eq($storyID)->andWHere('version')->eq($story->version)->exec();
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($story->version)->exec();
|
||||
$this->dao->delete()->from(TABLE_STORYSPEC)->where('story')->eq($storyID)->andWHere('version')->eq($oldStory->version)->exec();
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_STORYSPEC)->where('story')->eq($siblingID)->andWHere('version')->eq($oldStory->version)->exec();
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($siblingID)->andWhere('version')->eq($oldStory->version)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
$changes = common::createChanges($oldStory, $story);
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($storyID, $oldStory->siblings, $changes, 'recalledChange');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1643,6 +1872,15 @@ class storyModel extends model
|
||||
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($siblingID)->andWhere('version')->eq($oldStory->version)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_POST['reviewer']))
|
||||
{
|
||||
foreach($this->post->reviewer as $reviewer)
|
||||
@@ -1654,12 +1892,25 @@ class storyModel extends model
|
||||
$reviewData->version = $oldStory->version;
|
||||
$reviewData->reviewer = $reviewer;
|
||||
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$reviewData->story = $siblingID;
|
||||
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
$story->status = 'reviewing';
|
||||
}
|
||||
|
||||
$this->dao->update(TABLE_STORY)->data($story, 'reviewer')->where('id')->eq($storyID)->exec();
|
||||
if(!dao::isError()) return common::createChanges($oldStory, $story);
|
||||
|
||||
$changes = common::createChanges($oldStory, $story);
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($storyID, $oldStory->siblings, $changes, 'submitReview');
|
||||
if(!dao::isError()) return $changes;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1797,7 +2048,10 @@ class storyModel extends model
|
||||
|
||||
if(($this->config->edition == 'biz' || $this->config->edition == 'max') && $oldStory->feedback) $this->loadModel('feedback')->updateStatus('story', $oldStory->feedback, $story->status, $oldStory->status);
|
||||
}
|
||||
return common::createChanges($oldStory, $story);
|
||||
|
||||
$changes = common::createChanges($oldStory, $story);
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($storyID, $oldStory->siblings, $changes, 'Closed');
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2312,7 +2566,10 @@ class storyModel extends model
|
||||
|
||||
$story = $this->loadModel('file')->processImgURL($story, $this->config->story->editor->assignto['id'], $this->post->uid);
|
||||
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->checkFlow()->where('id')->eq((int)$storyID)->exec();
|
||||
if(!dao::isError()) return common::createChanges($oldStory, $story);
|
||||
|
||||
$changes = common::createChanges($oldStory, $story);
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($storyID, $oldStory->siblings, $changes, 'Assigned');
|
||||
if(!dao::isError()) return $changes;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2329,9 +2586,15 @@ class storyModel extends model
|
||||
$storyIdList = $this->post->storyIdList;
|
||||
$assignedTo = $this->post->assignedTo;
|
||||
$oldStories = $this->getByList($storyIdList);
|
||||
$ignoreStories = '';
|
||||
foreach($storyIdList as $storyID)
|
||||
{
|
||||
$oldStory = $oldStories[$storyID];
|
||||
if($oldStory->status == 'closed')
|
||||
{
|
||||
$ignoreStories .= "#{$storyID},";
|
||||
continue;
|
||||
}
|
||||
if($assignedTo == $oldStory->assignedTo) continue;
|
||||
|
||||
$story = new stdclass();
|
||||
@@ -2344,6 +2607,13 @@ class storyModel extends model
|
||||
|
||||
$allChanges[$storyID] = common::createChanges($oldStory, $story);
|
||||
}
|
||||
|
||||
if($ignoreStories)
|
||||
{
|
||||
$ignoreStories = trim($ignoreStories, ',');
|
||||
echo js::alert(sprintf($this->lang->story->ignoreClosedStory, $ignoreStories));
|
||||
}
|
||||
|
||||
return $allChanges;
|
||||
}
|
||||
|
||||
@@ -2381,14 +2651,28 @@ class storyModel extends model
|
||||
$story = $this->loadModel('file')->processImgURL($story, $this->config->story->editor->activate['id'], $this->post->uid);
|
||||
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->checkFlow()->where('id')->eq($storyID)->exec();
|
||||
|
||||
if($story->status == 'active') $this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->exec();
|
||||
if($story->status == 'active')
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($siblingID)->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->setStage($storyID);
|
||||
|
||||
/* Update parent story status. */
|
||||
if($oldStory->parent > 0) $this->updateParentStatus($storyID, $oldStory->parent);
|
||||
|
||||
return common::createChanges($oldStory, $story);
|
||||
$changes = common::createChanges($oldStory, $story);
|
||||
if(!empty($oldStory->siblings)) $this->syncSiblings($storyID, $oldStory->siblings, $changes, 'Activated');
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3481,6 +3765,7 @@ class storyModel extends model
|
||||
->andWhere('status')->notin('closed,draft')
|
||||
->andWhere('product')->eq($productID)
|
||||
->andWhere('plan')->in('0,')
|
||||
->andWhere('siblings')->eq('')
|
||||
->beginIF($append)->orWhere('id')->in($append)->fi()
|
||||
->fetchPairs();
|
||||
return array(0 => '') + $stories ;
|
||||
@@ -5748,6 +6033,16 @@ class storyModel extends model
|
||||
/* Delete versions that is after this version. */
|
||||
$this->dao->delete()->from(TABLE_STORYSPEC)->where('story')->eq($story->id)->andWHere('version')->in($oldStory->version)->exec();
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($story->id)->andWhere('version')->in($oldStory->version)->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_STORYSPEC)->where('story')->eq($siblingID)->andWHere('version')->in($oldStory->version)->exec();
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($siblingID)->andWhere('version')->in($oldStory->version)->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($result == 'reject')
|
||||
@@ -5853,6 +6148,19 @@ class storyModel extends model
|
||||
->andWhere('result')->eq('')
|
||||
->exec();
|
||||
|
||||
/* Sync siblings. */
|
||||
if(!empty($oldStory->siblings))
|
||||
{
|
||||
foreach(explode(',', trim($oldStory->siblings, ',')) as $siblingID)
|
||||
{
|
||||
$this->dao->delete()->from(TABLE_STORYREVIEW)
|
||||
->where('story')->eq($siblingID)
|
||||
->andWhere('version')->eq($oldStory->version)
|
||||
->andWhere('result')->eq('')
|
||||
->exec();
|
||||
}
|
||||
}
|
||||
|
||||
return $story;
|
||||
}
|
||||
|
||||
@@ -5980,7 +6288,16 @@ class storyModel extends model
|
||||
|
||||
/* Get related objects title or names. */
|
||||
$relatedSpecs = $this->dao->select('*')->from(TABLE_STORYSPEC)->where('`story`')->in($storyIdList)->orderBy('version desc')->fetchGroup('story');
|
||||
|
||||
$fileIdList = array();
|
||||
foreach($relatedSpecs as $relatedSpec)
|
||||
{
|
||||
if(!empty($relatedSpec[0]->files)) $fileIdList[] = $relatedSpec[0]->files;
|
||||
}
|
||||
$fileIdList = array_unique($fileIdList);
|
||||
$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');
|
||||
$filesInfo = $this->dao->select('id, objectID, pathname, title')->from(TABLE_FILE)->where('id')->in($fileIdList)->andWhere('extra')->ne('editor')->fetchAll('id');
|
||||
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$story->spec = '';
|
||||
@@ -5991,6 +6308,11 @@ class storyModel extends model
|
||||
$story->title = $storySpec->title;
|
||||
$story->spec = $storySpec->spec;
|
||||
$story->verify = $storySpec->verify;
|
||||
|
||||
if(!empty($storySpec->files) and empty($relatedFiles[$story->id]) and !empty($filesInfo[$storySpec->files]))
|
||||
{
|
||||
$relatedFiles[$story->id][0] = $filesInfo[$storySpec->files];
|
||||
}
|
||||
}
|
||||
|
||||
if($this->post->fileType == 'csv')
|
||||
@@ -6079,22 +6401,33 @@ class storyModel extends model
|
||||
* Get the story status after activation.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param bool $hasSiblings
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getActivateStatus($storyID)
|
||||
public function getActivateStatus($storyID, $hasSiblings = true)
|
||||
{
|
||||
$status = 'active';
|
||||
$action = $hasSiblings ? 'closed,reviewrejected,syncsiblings' : 'closed,reviewrejected';
|
||||
$lastRecord = $this->dao->select('action,extra')->from(TABLE_ACTION)
|
||||
->where('objectType')->eq('story')
|
||||
->andWhere('objectID')->eq($storyID)
|
||||
->andWhere('action')->in('closed,reviewrejected')
|
||||
->andWhere('action')->in($action)
|
||||
->orderBy('id_desc')
|
||||
->fetch();
|
||||
|
||||
$lastAction = $lastRecord->action;
|
||||
if($lastAction == 'reviewrejected') $status = 'draft';
|
||||
if($lastAction == 'closed') $status = strpos($lastRecord->extra, '|') !== false ? substr($lastRecord->extra, strpos($lastRecord->extra, '|') + 1) : 'active';
|
||||
if(strpos('closed,reviewrejected', $lastAction) !== false)
|
||||
{
|
||||
$status = strpos($lastRecord->extra, '|') !== false ? substr($lastRecord->extra, strpos($lastRecord->extra, '|') + 1) : 'active';
|
||||
}
|
||||
|
||||
/* When activating twin story, you need to check the status of the twin story selected when closing. */
|
||||
if($lastAction == 'syncsiblings')
|
||||
{
|
||||
$syncStoryID = strpos($lastRecord->extra, '|') !== false ? substr($lastRecord->extra, strpos($lastRecord->extra, '|') + 1) : 0;
|
||||
$status = $this->getActivateStatus($syncStoryID, false);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
@@ -6133,4 +6466,47 @@ class storyModel extends model
|
||||
|
||||
return $lastReviewer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync siblings.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $siblings
|
||||
* @param array $changes
|
||||
* @param string $operate
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function syncSiblings($storyID, $siblings, $changes, $operate)
|
||||
{
|
||||
if(empty($siblings) or empty($changes)) return;
|
||||
|
||||
/* Get the fields and values to be synchronized. */
|
||||
$syncFieldList = array();
|
||||
foreach($changes as $changeInfo)
|
||||
{
|
||||
$fieldName = $changeInfo['field'];
|
||||
$fieldValue = $changeInfo['new'];
|
||||
|
||||
if(strpos('product,branch,module,plan,spec,verify,files,reviewers', $fieldName) !== false) continue;
|
||||
$syncFieldList[$fieldName] = $fieldValue;
|
||||
}
|
||||
|
||||
if(empty($syncFieldList)) return;
|
||||
|
||||
/* Synchronize and record dynamics. */
|
||||
$this->loadModel('action');
|
||||
$siblings = explode(',', trim($siblings, ','));
|
||||
foreach($siblings as $siblingID)
|
||||
{
|
||||
$this->dao->update(TABLE_STORY)->data($syncFieldList)->where('id')->eq((int)$siblingID)->exec();
|
||||
if(!dao::isError())
|
||||
{
|
||||
$this->setStage($siblingID);
|
||||
|
||||
$actionID = $this->action->create('story', $siblingID, 'syncsiblings', '', "$operate|$storyID");
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
<th class='w-80px'><?php echo $lang->story->assignedTo;?></th>
|
||||
<td class='w-p45'><?php echo html::select('assignedTo', $users, $story->closedBy, 'class="form-control chosen"');?></td><td></td>
|
||||
</tr>
|
||||
<?php if(!empty($story->siblings)):?>
|
||||
<tr>
|
||||
<th></th>
|
||||
<td colspan='2'><i class='icon-exclamation-sign'></i> <?php echo $lang->story->activateSyncTip;?></td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<?php $this->printExtendFields($story, 'table');?>
|
||||
<tr>
|
||||
<th><?php echo $lang->story->comment;?></th>
|
||||
|
||||
@@ -28,7 +28,13 @@
|
||||
<tr>
|
||||
<th class='w-80px'><?php echo $lang->story->assign;?></th>
|
||||
<td class='w-p25-f'><?php echo html::select('assignedTo', $users, $story->assignedTo, "class='form-control chosen'");?></td><td></td>
|
||||
</tr>
|
||||
</tr>
|
||||
<?php if(!empty($story->siblings)):?>
|
||||
<tr>
|
||||
<th></th>
|
||||
<td colspan='2'><i class='icon-exclamation-sign'></i> <?php echo $lang->story->assignSyncTip;?></td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr class='hide'>
|
||||
<th><?php echo $lang->story->status;?></th>
|
||||
<td><?php echo html::hidden('status', $story->status);?></td>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
$lang->story->currentBranch = sprintf($this->lang->story->currentBranch, $this->lang->product->branchName[$product->type]);
|
||||
js::set('relieveURL', inlink('ajaxRelieveSibling'));
|
||||
js::set('storyID', $story->id);
|
||||
js::set('relieved', $lang->story->relieved);
|
||||
js::set('relievedTip', $lang->story->relievedTip);
|
||||
js::set('cancel', $lang->cancel);
|
||||
$canViewLinkStory = common::hasPriv('story', 'view');
|
||||
$canRelieved = common::hasPriv('story', 'relieved');
|
||||
array_unshift($siblings, $story);
|
||||
$class = isonlybody() ? 'showinonlybody' : 'iframe';
|
||||
|
||||
foreach($siblings as $sibling)
|
||||
{
|
||||
$id = $sibling->id;
|
||||
$title = '#' . $id . ' '. $sibling->title;
|
||||
$branch = $branches[$sibling->branch];
|
||||
$stage = $lang->story->stageList[$sibling->stage];
|
||||
$labelClass = $story->branch == $sibling->branch ? 'label-primary' : '';
|
||||
|
||||
echo "<li title='$title' class='sibling'>";
|
||||
echo "<span class='label {$labelClass} label-outline label-badge' title='$branch'>{$branch}</span> ";
|
||||
echo "<span class='label label-outline' title='{$stage}'>{$stage}</span> ";
|
||||
echo ($canViewLinkStory ? html::a($this->createLink('story', 'view', "id=$id", '', true), "$title", '', "class='$class viewlink' data-width='80%'") : "$title");
|
||||
if($canRelieved) echo "<a class='unlink hide' data-id='$id' data-toggle='popover'><i class='icon icon-unlink btn-info'></i></a>";
|
||||
echo "</li>";
|
||||
}
|
||||
?>
|
||||
@@ -73,6 +73,21 @@
|
||||
<?php echo $this->fetch('file', 'buildform');?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if(!empty($siblings)):?>
|
||||
<tr id='siblingTitle'>
|
||||
<td colspan='2'>
|
||||
<?php echo $lang->story->changeSyncTip;?>
|
||||
<span data-toggle='tooltip' data-placement='right' title='<?php echo $lang->story->syncTip;?>'><i class='icon-help'></i></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id='siblingList'>
|
||||
<td colspan='2'>
|
||||
<ul class='list-unstyled'>
|
||||
<?php include './blocksibling.html.php';?>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr>
|
||||
<th><?php echo $lang->story->checkAffection;?></th>
|
||||
<td><?php include './affected.html.php';?></td>
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
<th class='thWidth'><?php echo $lang->story->closedReason;?></th>
|
||||
<td class='w-p25-f'><?php echo html::select('closedReason', $reasonList, '', 'class="form-control" onchange="setStory(this.value)"');?></td><td></td>
|
||||
</tr>
|
||||
<?php if(!empty($story->siblings)):?>
|
||||
<tr>
|
||||
<th></th>
|
||||
<td colspan='2'><i class='icon-exclamation-sign'></i> <?php echo $lang->story->closeSyncTip;?></td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr id='duplicateStoryBox' style='display:none'>
|
||||
<th><?php echo $lang->story->duplicateStory;?></th>
|
||||
<td class='required'><?php echo html::select('duplicateStory', array('' => '') + $productStories, '', "class='form-control' placeholder='{$lang->bug->duplicateTip}'"); ?></td><td></td>
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
<?php js::set('feedbackSource', $config->story->feedbackSource); ?>
|
||||
<?php js::set('storyType', $type);?>
|
||||
<?php js::set('requiredFields', $config->story->create->requiredFields);?>
|
||||
<?php if($type == 'requirement'): ?>
|
||||
<style>.input-group .control-branch + .chosen-container-single .chosen-single {border-radius: 0 2px 2px 0; border-left-width: 0px;}</style>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
foreach(explode(',', $config->story->create->requiredFields) as $field)
|
||||
{
|
||||
@@ -51,35 +54,76 @@ foreach(explode(',', $config->story->create->requiredFields) as $field)
|
||||
<th><?php echo $hiddenProduct ? $lang->story->module : $lang->story->product;?></th>
|
||||
<td colspan="2" class="<?php if($hiddenProduct) echo 'hidden';?>">
|
||||
<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'>
|
||||
<?php if(!$hiddenProduct):?>
|
||||
<div class="input-group-addon"><?php echo $lang->story->module;?></div>
|
||||
<?php endif;?>
|
||||
<?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>
|
||||
<th class="planTh"><?php echo $hiddenPlan ? $lang->story->assignedTo : $lang->story->planAB;?></th>
|
||||
<td colspan="2" class="<?php if($hiddenPlan) echo 'hidden';?>">
|
||||
<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'><?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'>
|
||||
<?php
|
||||
echo html::select('plan', $plans, $planID, "class='form-control chosen'");
|
||||
@@ -311,10 +355,53 @@ 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'><?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);?>
|
||||
<?php js::set('originProductType', !empty($branches) ? 'normal' : 'branch');?>
|
||||
<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';?>
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
<?php js::set('feedbackSource', $config->story->feedbackSource); ?>
|
||||
<?php js::set('storyStatus', $story->status);?>
|
||||
<?php js::set('lastReviewer', explode(',', $lastReviewer))?>
|
||||
<?php js::set('siblings', $story->siblings)?>
|
||||
<?php js::Set('relievedSiblingsTip', $lang->story->relievedSiblingsTip)?>
|
||||
<div class='main-content' id='mainContent'>
|
||||
<form method='post' enctype='multipart/form-data' target='hiddenwin' id='dataform'>
|
||||
<div class='main-header'>
|
||||
@@ -98,6 +100,22 @@
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<?php $this->printExtendFields($story, 'div', 'position=left');?>
|
||||
<?php if(!empty($siblings)):?>
|
||||
<div class='detail' id='legendSiblings'>
|
||||
<div class='detail-title'>
|
||||
<?php echo $lang->story->changeSyncTip;?>
|
||||
<span data-toggle='tooltip' data-placement='right' title='<?php echo $lang->story->syncTip;?>'><i class='icon-help'></i></span>
|
||||
</div>
|
||||
<div class='form-group'>
|
||||
<div>
|
||||
<ul class='list-unstyled'>
|
||||
<?php $branches = $branchTagOption;?>
|
||||
<?php include './blocksibling.html.php';?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<div class='detail'>
|
||||
<div class='detail-title'><?php echo $lang->story->comment;?></div>
|
||||
<div class='form-group'>
|
||||
@@ -174,8 +192,9 @@
|
||||
<th><?php echo $lang->story->plan;?></th>
|
||||
<td>
|
||||
<div class='input-group' id='planIdBox'>
|
||||
<?php $multiple = ($this->session->currentProductType != 'normal' and empty($story->branch)) ? true : false;?>
|
||||
<?php echo html::select($multiple ? 'plan[]' : 'plan', $plans, $story->plan, "class='form-control chosen'" . ($multiple ? ' multiple' : ''));
|
||||
<?php $planCount = !empty($story->planTitle) ? count($story->planTitle) : 0?>
|
||||
<?php $multiple = ($this->session->currentProductType != 'normal' and empty($story->branch) and $planCount > 1) ? 'multiple' : '';?>
|
||||
<?php echo html::select(!empty($multiple) ? 'plan[]' : 'plan', $plans, $story->plan, "class='form-control chosen' " . $multiple);
|
||||
if(count($plans) == 1)
|
||||
{
|
||||
echo "<span class='input-group-addon'>";
|
||||
|
||||
@@ -11,6 +11,17 @@
|
||||
*/
|
||||
function loadProduct(productID)
|
||||
{
|
||||
if(page == 'edit' && siblings && productID != oldProductID)
|
||||
{
|
||||
confirmRelievedSiblings = confirm(relievedSiblingsTip);
|
||||
if(!confirmRelievedSiblings)
|
||||
{
|
||||
$('#product').val(oldProductID);
|
||||
$('#product').trigger("chosen:updated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(typeof parentStory != 'undefined' && parentStory)
|
||||
{
|
||||
confirmLoadProduct = confirm(moveChildrenTips);
|
||||
@@ -81,20 +92,81 @@ 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' && page == 'create' ? 'yes' : 'no';
|
||||
$.get(createLink('branch', 'ajaxGetBranches', "productID=" + productID + "&oldBranch=0¶m=" + param + "&projectID=" + executionID + "&withMainBranch=1&isSiblings=" + isSiblings), function(data)
|
||||
{
|
||||
if(storyType == 'story' && page == 'create')
|
||||
{
|
||||
var newProductType = data ? 'normal' : 'branch';
|
||||
if(originProductType != newProductType)
|
||||
{
|
||||
$('.switchBranch').toggleClass('hidden');
|
||||
$('.switchBranch').toggleClass('disable');
|
||||
}
|
||||
originProductType = newProductType;
|
||||
|
||||
$('tr[class^="addBranchesBox"]').remove();
|
||||
|
||||
if(data)
|
||||
{
|
||||
$.ajaxSettings.async = false;
|
||||
$.get(createLink('product', 'ajaxGetProductById', "productID=" + productID), function(data)
|
||||
{
|
||||
$.cookie('branchSourceName', data.branchSourceName)
|
||||
$.cookie('branchName', data.branchName)
|
||||
}, 'json')
|
||||
$.ajaxSettings.async = true;
|
||||
|
||||
gap = $('#product').closest('td').next().width();
|
||||
$('#planIdBox').css('flex', '0 0 ' + gap + 'px')
|
||||
|
||||
$('.switchBranch #branchBox .input-group .input-group-addon').html($.cookie('branchSourceName'))
|
||||
$('.switchBranch #branchBox').closest('td').prev().html($.cookie('branchName'))
|
||||
|
||||
/* reload branch */
|
||||
$('#branches0').replaceWith(data);
|
||||
$('#branches0' + "_chosen").remove();
|
||||
$('#branches0').next('.picker').remove();
|
||||
$('#branches0').chosen();
|
||||
|
||||
loadModuleForSiblings(productID, 0, 0)
|
||||
loadPlanForSiblings(productID, 0, 0)
|
||||
|
||||
/* Init multi branch icon-plus. */
|
||||
if($(".table-form select[id^='branches']").length == $('.switchBranch #branchBox option').length)
|
||||
{
|
||||
$('.table-col .icon-plus').parent().css('pointer-events', 'none')
|
||||
$('.table-col .icon-plus').parent().addClass('disabled')
|
||||
}
|
||||
else
|
||||
{
|
||||
$('.table-col .icon-plus').parent().css('pointer-events', 'auto')
|
||||
$('.table-col .icon-plus').parent().removeClass('disabled')
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
loadProductModules(productID, 0);
|
||||
loadProductPlans(productID, 0);
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -139,9 +211,12 @@ function loadProductPlans(productID, branch)
|
||||
{
|
||||
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=&needCreate=true&expired='+ expired +'¶m=skipParent,' + config.currentMethod);
|
||||
var $planIdBox = $('#planIdBox');
|
||||
|
||||
var param = rawMethod == 'edit' ? 'skipParent|forStory' : 'skipParent';
|
||||
var expired = config.currentMethod == 'create' ? 'unexpired' : '';
|
||||
var planLink = createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '&planID=' + $('#plan').val() + '&fieldID=&needCreate=true&expired='+ expired +'¶m=skipParent|forStory|' + config.currentMethod);
|
||||
var $planIdBox = rawMethod == 'create' ? $('.switchBranch #planIdBox') : $('#planIdBox');
|
||||
|
||||
$planIdBox.load(planLink, function()
|
||||
{
|
||||
$planIdBox.find('#plan').chosen();
|
||||
|
||||
@@ -419,17 +419,27 @@
|
||||
<div class="cell">
|
||||
<div class='tabs'>
|
||||
<ul class='nav nav-tabs'>
|
||||
<?php if(!empty($siblings)):?>
|
||||
<li class='active'><a href='#legendSiblings' data-toggle='tab'><?php echo $lang->story->siblings;?></a></li>
|
||||
<?php endif;?>
|
||||
<?php if($this->config->URAndSR and !$hiddenURS):?>
|
||||
<li class='active'><a href='#legendStories' data-toggle='tab'><?php echo $story->type == 'story' ? $lang->story->requirement : $lang->story->story;?></a></li>
|
||||
<li class='<?php if(empty($siblings)) echo 'active';?>'><a href='#legendStories' data-toggle='tab'><?php echo $story->type == 'story' ? $lang->story->requirement : $lang->story->story;?></a></li>
|
||||
<?php endif;?>
|
||||
<?php if($story->type == 'story'):?>
|
||||
<li class="<?php if(!$this->config->URAndSR || $hiddenURS) echo 'active';?>"><a href='#legendProjectAndTask' data-toggle='tab'><?php echo $lang->story->legendProjectAndTask;?></a></li>
|
||||
<li class="<?php if((!$this->config->URAndSR || $hiddenURS) and empty($siblings)) echo 'active';?>"><a href='#legendProjectAndTask' data-toggle='tab'><?php echo $lang->story->legendProjectAndTask;?></a></li>
|
||||
<?php endif;?>
|
||||
<li><a href='#legendRelated' data-toggle='tab'><?php echo $lang->story->legendRelated;?></a></li>
|
||||
</ul>
|
||||
<div class='tab-content'>
|
||||
<?php if(!empty($siblings)):?>
|
||||
<div class='tab-pane active' id='legendSiblings'>
|
||||
<ul class="list-unstyled">
|
||||
<?php include './blocksibling.html.php';?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<?php if($this->config->URAndSR and !$hiddenURS):?>
|
||||
<div class='tab-pane active' id='legendStories'>
|
||||
<div class='tab-pane <?php if(empty($siblings)) echo 'active';?>' id='legendStories'>
|
||||
<ul class="list-unstyled">
|
||||
<?php
|
||||
$relation = array();
|
||||
@@ -438,19 +448,18 @@
|
||||
foreach($relations as $item) $relation[$item->id] = $item->title;
|
||||
foreach($relation as $id => $title)
|
||||
{
|
||||
echo "<li title='$title'>" . ($canViewLinkStory ? html::a($this->createLink('story', 'view', "id=$id&version=0¶m=0&storyType=$relationType", '', true), "#$id $title", '', "class='iframe' data-width='80%'") : "#$id $title");
|
||||
echo html::a($this->createLink('story', 'linkStory', "storyID=$story->id&type=remove&linkedID=$id&browseType=&queryID=0&storyType=$story->type"), '<i class="icon icon-close"></i>', 'hiddenwin', "class='hide removeButton'");
|
||||
echo "<li title='$title' class='legendStories'>" . ($canViewLinkStory ? html::a($this->createLink('story', 'view', "id=$id&version=0¶m=0&storyType=$relationType", '', true), "#$id $title", '', "class='iframe' data-width='80%'") : "#$id $title");
|
||||
echo html::a($this->createLink('story', 'linkStory', "storyID=$story->id&type=remove&linkedID=$id&browseType=&queryID=0&storyType=$story->type"), '<i class="icon icon-unlink btn-info"></i>', 'hiddenwin', "class='hide removeButton'");
|
||||
}
|
||||
?>
|
||||
<?php $linkLang = ($story->type == 'story') ? $lang->story->requirement : $lang->story->story;?>
|
||||
<li><?php if(common::hasPriv($story->type, 'linkStory')) echo html::a($this->createLink('story', 'linkStory', "storyID=$story->id&type=linkStories&linkedID=0&browseType=&queryID=0&storyType=$story->type", '', true), $lang->story->link . $linkLang, '', "class='btn btn-info iframe' data-width='95%' id='linkButton'");?>
|
||||
<?php if(!empty($relations)) echo html::a('javascript:void(0)', $lang->story->unlink, '', "class='btn btn-info' id='unlinkStory'");?></li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
|
||||
<?php if($story->type == 'story'):?>
|
||||
<div class="tab-pane <?php if(!$this->config->URAndSR || $hiddenURS) echo 'active';?>" id='legendProjectAndTask'>
|
||||
<div class="tab-pane <?php if((!$this->config->URAndSR || $hiddenURS) and empty($siblings)) echo 'active';?>" id='legendProjectAndTask'>
|
||||
<ul class="list-unstyled">
|
||||
<?php
|
||||
foreach($story->tasks as $executionTasks)
|
||||
@@ -662,7 +671,6 @@ js::set('unlink', $lang->story->unlink);
|
||||
js::set('cancel', $lang->cancel);
|
||||
js::set('rawModule', $this->app->rawModule);
|
||||
?>
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<?php include '../../common/view/syntaxhighlighter.html.php';?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
foreach($modulePath as $key => $module)
|
||||
{
|
||||
if($tab == 'qa' || $tab == 'ops')
|
||||
if($tab == 'qa' || $tab == 'ops' || $tab == 'product')
|
||||
{
|
||||
if($isLibCase)
|
||||
{
|
||||
|
||||
@@ -552,13 +552,13 @@ 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;
|
||||
|
||||
$output = html::select("$field", $optionMenu, $currentModuleID, "class='form-control' $changeFunc");
|
||||
if(count($optionMenu) == 1 and $needManage)
|
||||
if(count($optionMenu) == 1 and $needManage !== 'false')
|
||||
{
|
||||
$output .= "<span class='input-group-addon'>";
|
||||
$output .= html::a($this->createLink('tree', 'browse', "rootID=$rootID&view=$viewType¤tModuleID=0&branch=$branch", '', true), $this->lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'");
|
||||
|
||||
+1
-6
@@ -1044,12 +1044,7 @@ function disableSelectedProduct()
|
||||
$("select[id^='products']").each(function()
|
||||
{
|
||||
var selectedProduct = $(this).val();
|
||||
if(selectedProduct != 0 && $.inArray(selectedProduct, selectedVal) < 0 && !multiBranchProducts[selectedProduct]) selectedVal.push(selectedProduct);
|
||||
if(multiBranchProducts[selectedProduct])
|
||||
{
|
||||
var isDisabled = checkMultiProducts(this);
|
||||
if(isDisabled) selectedVal.push(selectedProduct);
|
||||
}
|
||||
if(selectedProduct != 0 && $.inArray(selectedProduct, selectedVal) < 0) selectedVal.push(selectedProduct);
|
||||
})
|
||||
|
||||
$("select[id^='products']").each(function()
|
||||
|
||||
Reference in New Issue
Block a user