Merge branch 'pailei2'

This commit is contained in:
王怡栋
2022-03-22 09:01:03 +08:00
142 changed files with 7504 additions and 6950 deletions
+19 -17
View File
@@ -113,6 +113,7 @@ $filter->project->browse = new stdclass();
$filter->project->story = new stdclass();
$filter->project->export = new stdclass();
$filter->project->task = new stdclass();
$filter->project->execution = new stdclass();
$filter->projectstory->story = new stdclass();
$filter->qa->default = new stdclass();
$filter->story->create = new stdclass();
@@ -244,23 +245,24 @@ $filter->program->browse->cookie['showClosed'] = 'code';
$filter->program->export->cookie['checkedItem'] = 'reg::checked';
$filter->program->ajaxgetdropmenu->cookie['showClosed'] = 'code';
$filter->project->default->cookie['lastProject'] = 'int';
$filter->project->default->cookie['lastPRJ'] = 'int';
$filter->project->default->cookie['projectMode'] = 'code';
$filter->project->default->cookie['kanbanview'] = 'code';
$filter->project->browse->cookie['involved'] = 'code';
$filter->project->browse->cookie['projectType'] = 'code';
$filter->project->story->cookie['storyModuleParam'] = 'int';
$filter->project->story->cookie['storyPreProjectID'] = 'int';
$filter->project->story->cookie['storyProductParam'] = 'int';
$filter->project->story->cookie['storyBranchParam'] = 'reg::checked';
$filter->project->story->cookie['projectStoryOrder'] = 'reg::orderBy';
$filter->project->task->cookie['moduleBrowseParam'] = 'int';
$filter->project->task->cookie['preProjectID'] = 'int';
$filter->project->task->cookie['productBrowseParam'] = 'int';
$filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy';
$filter->project->task->cookie['windowWidth'] = 'int';
$filter->project->export->cookie['checkedItem'] = 'reg::checked';
$filter->project->default->cookie['lastProject'] = 'int';
$filter->project->default->cookie['lastPRJ'] = 'int';
$filter->project->default->cookie['projectMode'] = 'code';
$filter->project->default->cookie['kanbanview'] = 'code';
$filter->project->browse->cookie['involved'] = 'code';
$filter->project->browse->cookie['projectType'] = 'code';
$filter->project->story->cookie['storyModuleParam'] = 'int';
$filter->project->story->cookie['storyPreProjectID'] = 'int';
$filter->project->story->cookie['storyProductParam'] = 'int';
$filter->project->story->cookie['storyBranchParam'] = 'reg::checked';
$filter->project->story->cookie['projectStoryOrder'] = 'reg::orderBy';
$filter->project->task->cookie['moduleBrowseParam'] = 'int';
$filter->project->task->cookie['preProjectID'] = 'int';
$filter->project->task->cookie['productBrowseParam'] = 'int';
$filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy';
$filter->project->task->cookie['windowWidth'] = 'int';
$filter->project->export->cookie['checkedItem'] = 'reg::checked';
$filter->project->execution->cookie['pagerExecutionAll'] = 'int';
$filter->projectstory->story->cookie['storyModuleParam'] = 'int';
$filter->projectstory->story->cookie['pagerProductBrowse'] = 'int';
+3 -1
View File
@@ -216,6 +216,7 @@ $config->openMethods[] = 'kanban.importexecution';
$config->openMethods[] = 'kanban.importbuild';
$config->openMethods[] = 'kanban.activatecard';
$config->openMethods[] = 'kanban.finishcard';
$config->openMethods[] = 'kanban.deleteobjectcard';
/* Define the tables. */
define('TABLE_COMPANY', '`' . $config->db->prefix . 'company`');
@@ -366,10 +367,11 @@ $config->objectTables['kanbancard'] = TABLE_KANBANCARD;
$config->objectTables['sonarqube'] = TABLE_PIPELINE;
$config->objectTables['gitlab'] = TABLE_PIPELINE;
$config->objectTables['jebkins'] = TABLE_PIPELINE;
$config->objectTables['stage'] = TABLE_STAGE;
$config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions');
/* Program privs.*/
$config->programPriv = new stdclass();
$config->programPriv->scrum = array('story', 'projectstory', 'projectrelease', 'project', 'build', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask');
$config->programPriv->scrum = array('story', 'projectstory', 'projectrelease', 'project', 'execution', 'build', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask');
$config->programPriv->waterfall = array_merge($config->programPriv->scrum, array('workestimation', 'durationestimation', 'budget', 'programplan', 'review', 'reviewissue', 'weekly', 'cm', 'milestone', 'design', 'issue', 'risk', 'opportunity', 'measrecord', 'auditplan', 'trainplan', 'gapanalysis', 'pssp', 'researchplan', 'researchreport'));
+2 -2
View File
@@ -244,8 +244,8 @@ INSERT INTO `zt_dept` (`id`, `name`, `parent`, `path`, `grade`, `order`, `positi
INSERT INTO `zt_doclib` (`id`, `type`, `product`, `project`, `execution`, `name`, `acl`, `groups`, `users`, `main`, `collector`, `order`, `deleted`) VALUES
(1, 'product', 1, 0, 0, '产品库', 'default', '', '', '1', '', 0, '0'),
(2, 'product', 2, 0, 0, '产品库', 'default', '', '', '1', '', 0, '0'),
(3, 'project', 0, 0, 1, '项目库', 'default', '', '', '1', '', 0, '0'),
(4, 'project', 0, 0, 2, '项目库', 'default', '', '', '1', '', 0, '0'),
(3, 'execution', 0, 0, 1, '项目库', 'default', '', '', '1', '', 0, '0'),
(4, 'execution', 0, 0, 2, '项目库', 'default', '', '', '1', '', 0, '0'),
(5, 'project', 0, 7, 0, '项目主库', 'default', '', '', '1', '', 0, '0');
INSERT INTO `zt_history` (`id`, `action`, `field`, `old`, `new`, `diff`) VALUES
(1, 14, 'version', '1', '2', ''),
+1
View File
@@ -42,6 +42,7 @@ $config->action->objectNameFields['kanban'] = 'name';
$config->action->objectNameFields['kanbancard'] = 'name';
$config->action->objectNameFields['sonarqube'] = 'name';
$config->action->objectNameFields['gitlab'] = 'name';
$config->action->objectNameFields['stage'] = 'name';
$config->action->commonImgSize = 870;
+1
View File
@@ -645,6 +645,7 @@ $lang->action->label->kanbanlane = '看板泳道|execution|kanban|execution=%s
$lang->action->label->kanbancard = '看板卡片|kanban|view|kanbanID=%s';
$lang->action->label->mr = '合并请求|mr|view|id=%s';
$lang->action->label->gitlab = 'GitLab服务器|gitlab|view|id=%s';
$lang->action->label->stage = '瀑布模型的阶段|stage|browse|';
/* Object type. */
$lang->action->search = new stdclass();
@@ -44,10 +44,8 @@
<div class='col'>
<div class='panel-content'>
<div class='panel-heading not-move-handler'>
<?php if($config->systemMode == 'new'):?>
<?php $projectLabel = $project->model == 'waterfall' ? 'label-warning' : 'label-info';?>
<span class="project-type-label label <?php echo $projectLabel;?> label-outline"><?php echo $lang->project->{$project->model};?></span>
<?php endif;?>
<?php $labelClass = $project->model == 'waterfall' ? 'label-warning' : 'label-info';?>
<span class='project-type-label label <?php echo $labelClass;?> label-outline'><?php echo $lang->project->{$project->model};?></span>
<strong class='project-name' title='<?php echo $project->name;?>'> <?php echo html::a($viewLink, $project->name);?> </strong>
<nav class='panel-actions nav nav-default'>
<li class='dropdown'>
+42 -6
View File
@@ -322,6 +322,21 @@ class bug extends control
if($this->app->tab == 'execution')
{
if(isset($output['executionID'])) $this->loadModel('execution')->setMenu($output['executionID']);
$execution = $this->dao->findById((int)$output['executionID'])->from(TABLE_EXECUTION)->fetch();
if($execution->type == 'kanban')
{
$this->loadModel('kanban');
$regionPairs = $this->kanban->getRegionPairs($execution->id, 0, 'execution');
$regionID = isset($output['regionID']) ? $output['regionID'] : key($regionPairs);
$lanePairs = $this->kanban->getLanePairsByRegion($regionID, 'bug');
$laneID = isset($output['laneID']) ? $output['laneID'] : key($lanePairs);
$this->view->executionType = $execution->type;
$this->view->regionID = $regionID;
$this->view->laneID = $laneID;
$this->view->regionPairs = $regionPairs;
$this->view->lanePairs = $lanePairs;
}
}
else if($this->app->tab == 'project')
{
@@ -662,6 +677,9 @@ class bug extends control
*/
public function batchCreate($productID, $branch = '', $executionID = 0, $moduleID = 0, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
if(!empty($_POST))
{
$actions = $this->bug->batchCreate($productID, $branch, $extra);
@@ -675,9 +693,6 @@ class bug extends control
setcookie('bugModule', 0, 0, $this->config->webRoot, '', $this->config->cookieSecure, false);
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
/* If link from no head then reload. */
if(isonlybody() and $executionID)
{
@@ -709,6 +724,20 @@ class bug extends control
$builds = $this->loadModel('build')->getBuildPairs($productID, $branch, 'noempty', $executionID, 'execution');
$stories = $this->story->getExecutionStoryPairs($executionID);
$execution = $this->loadModel('execution')->getById($executionID);
if($execution->type == 'kanban')
{
$this->loadModel('kanban');
$regionPairs = $this->kanban->getRegionPairs($executionID, 0, 'execution');
$regionID = isset($output['regionID']) ? $output['regionID'] : key($regionPairs);
$lanePairs = $this->kanban->getLanePairsByRegion($regionID, 'bug');
$laneID = isset($output['laneID']) ? $output['laneID'] : key($lanePairs);
$this->view->executionType = $execution->type;
$this->view->regionID = $regionID;
$this->view->laneID = $laneID;
$this->view->regionPairs = $regionPairs;
$this->view->lanePairs = $lanePairs;
}
}
else
{
@@ -1672,7 +1701,7 @@ class bug extends control
}
else
{
return print(js::closeModal('parent.parent'));
return print(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
}
}
if(defined('RUN_MODE') && RUN_MODE == 'api')
@@ -1835,15 +1864,16 @@ class bug extends control
*
* @param int $bugID
* @param string $confirm yes|no
* @param string $from taskkanban
* @access public
* @return void
*/
public function delete($bugID, $confirm = 'no')
public function delete($bugID, $confirm = 'no', $from = '')
{
$bug = $this->bug->getById($bugID);
if($confirm == 'no')
{
return print(js::confirm($this->lang->bug->confirmDelete, inlink('delete', "bugID=$bugID&confirm=yes")));
return print(js::confirm($this->lang->bug->confirmDelete, inlink('delete', "bugID=$bugID&confirm=yes&from=$from")));
}
else
{
@@ -1864,6 +1894,9 @@ class bug extends control
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
if(isonlybody()) return print(js::reload('parent.parent'));
if($from == 'taskkanban') return print(js::reload('parent'));
$locateLink = $this->session->bugList ? $this->session->bugList : inlink('browse', "productID={$bug->product}");
return print(js::locate($locateLink, 'parent'));
}
@@ -1922,6 +1955,9 @@ class bug extends control
{
$executionMembers = $this->user->getTeamMemberPairs($executionID, 'execution', '', $selectedUser);
$execution = $this->loadModel('execution')->getByID($executionID);
if(empty($selectedUser)) $selectedUser = $execution->QD;
return print(html::select('assignedTo', $executionMembers, $selectedUser, 'class="form-control"'));
}
+1
View File
@@ -4,6 +4,7 @@
.confirm0 {color: gray;}
.confirm1 {color: green;}
td.delayed {color: #fff; background: #e84e0f !important;}
#bugForm .setting {height: 25px;}
.datatable-wrapper .table-datatable .datatable-row td {height: 37px;}
-1
View File
@@ -18,7 +18,6 @@ html[lang='en'] #deadlineTd .input-group-addon {padding: 5px 18px;}
.dropdown-pris > .btn {background-color: #fff; text-shadow: none;}
#contactListGroup .input-group-btn > .btn {margin-left: -1px !important;}
#contactListGroup .chosen-container-single a {border-left-width: 0px;}
.minw-80px {min-width: 80px;}
+21
View File
@@ -38,6 +38,27 @@ function setOpenedBuilds(link, index)
});
}
/**
* Set lane.
*
* @param int $regionID
* @param int $num
* @access public
* @return void
*/
function setLane(regionID, num)
{
laneLink = createLink('kanban', 'ajaxGetLanes', 'regionID=' + regionID + '&type=bug&field=lanes&i=' + num);
$.get(laneLink, function(lanes)
{
if(!lanes) lanes = '<select id="lanes' + num + '" name="lanes[' + num + ']" class="form-control"></select>';
$('#lanes' + num).replaceWith(lanes);
$("#lanes" + num + "_chosen").remove();
$("#lanes" + num).next('.picker').remove();
$("#lanes" + num).chosen();
});
}
/**
* Load execution builds
*
+21 -28
View File
@@ -29,8 +29,6 @@ $(function()
*/
function loadAll(productID)
{
if(page == 'create') loadExecutionTeamMembers(productID, true);
if(typeof(changeProductConfirmed) != 'undefined' && !changeProductConfirmed)
{
firstChoice = confirm(confirmChangeProduct);
@@ -50,9 +48,7 @@ function loadAll(productID)
{
$('#taskIdBox').innerHTML = '<select id="task"></select>'; // Reset the task.
$('#task').chosen();
var param = '';
if(page == 'create') param = 'active';
loadProductBranches(productID, param)
loadProductBranches(productID)
}
}
@@ -266,7 +262,7 @@ function loadProductProjects(productID)
function loadProductExecutions(productID, projectID = 0)
{
required = $('#execution_chosen').hasClass('required');
branch = $('#branch').val();
branch = $('#branch').val();
if(typeof(branch) == 'undefined') branch = 0;
link = createLink('product', 'ajaxGetExecutions', 'productID=' + productID + '&projectID=' + projectID + '&branch=' + branch);
@@ -275,9 +271,10 @@ function loadProductExecutions(productID, projectID = 0)
$(this).find('select').chosen();
if(typeof(bugExecution) == 'string' && systemMode != 'classic') $('#executionIdBox').prepend("<span class='input-group-addon' id='executionBox' style='border-left-width: 0px;'>" + bugExecution + "</span>");
if(required) $(this).addClass('required');
changeExecutionName(projectID);
changeAssignedTo(projectID);
});
loadProjectBuilds(projectID);
projectID != 0 ? loadProjectBuilds(projectID) : loadProductBuilds(productID);
}
/**
@@ -349,13 +346,16 @@ function loadExecutionRelated(executionID)
}
else
{
var currentProjectID = $('#project').val();
var currentProductID = $('#product').val();
$('#taskIdBox').innerHTML = '<select id="task"></select>'; // Reset the task.
loadProductStories($('#product').val());
loadProductBuilds($('#product').val());
loadTestTasks($('#product').val());
loadProjectTeamMembers($('#project').val());
loadProductStories(currentProductID);
loadTestTasks(currentProductID);
loadProjectTeamMembers(currentProjectID);
currentProjectID != 0 ? loadProjectBuilds(currentProjectID) : loadProductBuilds(currentProductID);
}
notice();
}
/**
@@ -528,7 +528,8 @@ function loadProductBranches(productID, param)
$('#branch_chosen').remove();
$('#branch').next('.picker').remove();
var param = "productID=" + productID + "&oldBranch=0&param=" + param;
var branchStatus = page == 'create' ? 'active' : 'all';
var param = "productID=" + productID + "&oldBranch=0&param=" + branchStatus;
if(typeof(tab) != 'undefined' && (tab == 'execution' || tab == 'project')) param += "&projectID=" + objectID;
$.get(createLink('branch', 'ajaxGetBranches', param), function(data)
{
@@ -541,16 +542,11 @@ function loadProductBranches(productID, param)
loadProductModules(productID);
loadProductProjects(productID);
loadProductBuilds(productID);
loadProductplans(productID);
loadProductStories(productID);
})
}
var oldAssignedToTitle = $("#assignedTo").find("option:selected").text();
var oldAssignedTo = $("#assignedTo").find("option:selected").val();
/**
* Load team members of the execution as assignedTo list.
*
@@ -564,18 +560,9 @@ function loadAssignedTo(executionID, selectedUser)
link = createLink('bug', 'ajaxLoadAssignedTo', 'executionID=' + executionID + '&selectedUser=' + selectedUser);
$.get(link, function(data)
{
var defaultOption = '<option title="' + oldAssignedToTitle + '" value="' + oldAssignedTo + '" selected="selected">' + oldAssignedToTitle + '</option>';
var defaultAssignedTo = $('#assignedTo').val();
$('#assignedTo_chosen').remove();
$('#assignedTo').next('.picker').remove();
$('#assignedTo').replaceWith(data);
if(defaultAssignedTo !== oldAssignedTo && selectedUser == '')
{
if($('#assignedTo option[value="' + oldAssignedTo + '"]').length > 0) $('#assignedTo option[value="' + oldAssignedTo + '"]').remove();
$('#assignedTo').append(defaultOption);
}
$('#assignedTo').chosen();
});
}
@@ -593,6 +580,8 @@ var oldTestTask = $("#testtask").find("option:selected").val();
*/
function loadTestTasks(productID, executionID)
{
if(!$('#testtaskBox').length) return;
if(typeof(executionID) == 'undefined') executionID = 0;
link = createLink('testtask', 'ajaxGetTestTasks', 'productID=' + productID + '&executionID=' + executionID);
$.get(link, function(data)
@@ -612,6 +601,9 @@ function loadTestTasks(productID, executionID)
*/
function notice()
{
if(page == 'edit') return;
$('#buildBoxActions').empty().hide();
if($('#openedBuild').find('option').length <= 1)
{
var html = '';
@@ -638,6 +630,7 @@ function notice()
if($bba.length)
{
$bba.html(html);
$bba.show();
}
else
{
+33 -4
View File
@@ -56,6 +56,27 @@ function loadModuleRelated()
setStories(moduleID, productID, storyID);
}
/**
* Set lane.
*
* @param int $regionID
* @access public
* @return void
*/
function setLane(regionID)
{
console.log(regionID);
laneLink = createLink('kanban', 'ajaxGetLanes', 'regionID=' + regionID + '&type=bug&field=lane');
$.get(laneLink, function(lane)
{
if(!lane) lane = "<select id='lane' name='lane' class='form-control'></select>";
$('#lane').replaceWith(lane);
$("#lane" + "_chosen").remove();
$("#lane").next('.picker').remove();
$("#lane").chosen();
});
}
/**
* Set the assignedTo field.
*
@@ -109,12 +130,16 @@ $(function()
{
loadExecutionRelated($('#execution').val());
}
else if(parseInt($('#project').val()))
{
loadProjectBuilds($('#project').val());
loadProjectTeamMembers($('#project').val());
}
else
{
if(parseInt($('#project').val())) loadProjectTeamMembers($('#project').val());
if(!assignedto) setTimeout(function(){setAssignedTo(moduleID, productID)}, 500);
}
if(!assignedto) setTimeout(function(){setAssignedTo(moduleID, productID)}, 500);
notice();
$('[data-toggle=tooltip]').tooltip();
@@ -168,7 +193,7 @@ $(function()
* @access public
* @return void
*/
function changeExecutionName(projectID)
function changeAssignedTo(projectID)
{
if(parseInt(projectID))
{
@@ -179,9 +204,13 @@ function changeExecutionName(projectID)
$('#executionBox').html(executionLang);
})
}
else if($('#execution').val() != 0)
{
loadAssignedTo($('#execution').val());
}
else
{
loadExecutionTeamMembers($('#product').val());
setAssignedTo();
}
}
-19
View File
@@ -21,25 +21,6 @@ $(function()
}
});
/**
* Ajax change execution name.
*
* @param int projectID
* @access public
* @return void
*/
function changeExecutionName(projectID)
{
if(parseInt(projectID))
{
var link = createLink('bug', 'ajaxGetExecutionLang', 'projectID=' + projectID);
$.post(link, function(executionLang)
{
$('#executionBox').html(executionLang);
})
}
}
/**
* Set duplicate field.
*
+26 -7
View File
@@ -76,7 +76,7 @@ class bugModel extends model
->trim('title')
->join('openedBuild', ',')
->join('mailto', ',')
->remove('files, labels,uid,oldTaskID,contactListMenu')
->remove('files,labels,uid,oldTaskID,contactListMenu,region,lane')
->get();
if($bug->execution != 0) $bug->project = $this->dao->select('parent')->from(TABLE_EXECUTION)->where('id')->eq($bug->execution)->fetch('parent');
@@ -107,8 +107,15 @@ class bugModel extends model
if($bug->execution)
{
$this->loadModel('kanban');
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($bug->execution, $output['laneID'], $output['columnID'], 'bug', $bugID);
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($bug->execution, 'bug');
$laneID = isset($output['laneID']) ? $output['laneID'] : 0;
if(!empty($_POST['lane'])) $laneID = $_POST['lane'];
$columnID = $this->kanban->getColumnIDByLaneID($laneID, 'unconfirmed');
if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($bug->execution, $laneID, $columnID, 'bug', $bugID);
if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($bug->execution, 'bug');
}
/* Callback the callable method to process the related data for object that is transfered to bug. */
@@ -201,6 +208,8 @@ class bugModel extends model
$bug->browser = $data->browsers[$i];
$bug->keywords = $data->keywords[$i];
if(isset($data->lanes[$i])) $bug->laneID = $data->lanes[$i];
if($bug->execution != 0) $bug->project = $this->dao->select('parent')->from(TABLE_EXECUTION)->where('id')->eq($bug->execution)->fetch('parent');
/* Assign the bug to the person in charge of the module. */
@@ -236,6 +245,13 @@ class bugModel extends model
/* When the bug is created by uploading an image, add the image to the step of the bug. */
foreach($bugs as $i => $bug)
{
$laneID = isset($output['laneID']) ? $output['laneID'] : 0;
if(isset($bug->laneID))
{
$laneID = $bug->laneID;
unset($bug->laneID);
}
if(!empty($data->uploadImage[$i]))
{
$fileName = $data->uploadImage[$i];
@@ -274,8 +290,11 @@ class bugModel extends model
if($bug->execution)
{
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($bug->execution, $output['laneID'], $output['columnID'], 'bug', $bugID);
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($bug->execution, 'bug');
$columnID = $this->kanban->getColumnIDByLaneID($laneID, 'unconfirmed');
if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($bug->execution, $laneID, $columnID, 'bug', $bugID);
if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($bug->execution, 'bug');
}
/* When the bug is created by uploading the image, add the image to the file of the bug. */
@@ -290,7 +309,7 @@ class bugModel extends model
unset($file);
}
if(dao::isError()) return print(js::error('bug#' . ($i+1) . dao::getError(true)));
if(dao::isError()) return print(js::error('bug#' . ($i) . dao::getError(true)));
$actions[$bugID] = $this->action->create('bug', $bugID, 'Opened');
}
@@ -2925,7 +2944,7 @@ class bugModel extends model
echo $bug->activatedCount;
break;
case 'activatedDate':
echo substr($bug->activatedDate, 5, 11);
echo helper::isZeroDate($bug->activatedDate) ? '' : substr($bug->activatedDate, 5, 11);
break;
case 'keywords':
echo $bug->keywords;
+22 -10
View File
@@ -51,6 +51,10 @@
<th class='c-execution<?php echo zget($visibleFields, 'execution', ' hidden') . zget($requiredFields, 'execution', '', ' required');?>'><?php echo (isset($project->model) and $project->model == 'kanban') ? $lang->bug->kanban : $lang->bug->execution;?></th>
<th class='c-build required'><?php echo $lang->bug->openedBuild;?></th>
<th class='c-title required'><?php echo $lang->bug->title;?></th>
<?php if(isset($executionType) and $executionType == 'kanban'):?>
<th class='c-execution'><?php echo $lang->kanbancard->region;?></th>
<th class='c-execution'><?php echo $lang->kanbancard->lane;?></th>
<?php endif;?>
<th class='c-date<?php echo zget($visibleFields, 'deadline', ' hidden') . zget($requiredFields, 'deadline', '', ' required');?>'><?php echo $lang->bug->deadline;?></th>
<th class='c-steps<?php echo zget($visibleFields, 'steps', ' hidden') . zget($requiredFields, 'steps', '', ' required');?>'><?php echo $lang->bug->steps;?></th>
<th class='c-type<?php echo zget($visibleFields, 'type', ' hidden') . zget($requiredFields, 'type', '', ' required');?>'><?php echo $lang->typeAB;?></th>
@@ -74,19 +78,19 @@
$lang->bug->osList += array('ditto' => $lang->bug->ditto);
$lang->bug->browserList += array('ditto' => $lang->bug->ditto);
?>
<?php $i = 0; ?>
<?php $i = 1; ?>
<?php if(!empty($titles)):?>
<?php foreach($titles as $bugTitle => $fileName):?>
<?php
$moduleID = $i == 0 ? $moduleID : 'ditto';
$executionID = $i == 0 ? $executionID : 'ditto';
$type = $i == 0 ? '' : 'ditto';
$pri = $i == 0 ? 0 : 'ditto';
$os = $i == 0 ? '' : 'ditto';
$browser = $i == 0 ? '' : 'ditto';
$moduleID = $i == 1 ? $moduleID : 'ditto';
$executionID = $i == 1 ? $executionID : 'ditto';
$type = $i == 1 ? '' : 'ditto';
$pri = $i == 1 ? 0 : 'ditto';
$os = $i == 1 ? '' : 'ditto';
$browser = $i == 1 ? '' : 'ditto';
?>
<tr>
<td class='text-center'><?php echo $i+1;?></td>
<td class='text-center'><?php echo $i;?></td>
<td class='<?php echo zget($visibleFields, $product->type, ' hidden')?>' style='overflow:visible'><?php echo html::select("branches[$i]", $branches, $branch, "class='form-control chosen' onchange='setBranchRelated(this.value, $productID, $i)'");?></td>
<td><?php echo html::select("modules[$i]", $moduleOptionMenu, $moduleID, "class='form-control chosen'");?></td>
<td class='<?php echo zget($visibleFields, 'execution', ' hidden')?>' style='overflow:visible'><?php echo html::select("executions[$i]", $executions, $executionID, "class='form-control chosen' onchange='loadExecutionBuilds($productID, this.value, $i)'");?></td>
@@ -105,6 +109,10 @@
</div>
</div>
</td>
<?php if(isset($executionType) and $executionType == 'kanban'):?>
<td><?php echo html::select("regions[$i]", $regionPairs, $regionID, "class='form-control chosen'");?></td>
<td><?php echo html::select("lanes[$i]", $lanePairs, $laneID, "class='form-control chosen'");?></td>
<?php endif;?>
<td class='<?php echo zget($visibleFields, 'deadline', 'hidden')?>'><?php echo html::input("deadlines[$i]", '', "class='form-control form-date'");?></td>
<td class='<?php echo zget($visibleFields, 'steps', 'hidden')?>'><?php echo html::textarea("stepses[$i]", '', "rows='1' class='form-control autosize'");?></td>
<td class='<?php echo zget($visibleFields, 'type', 'hidden')?>' style='overflow:visible'> <?php echo html::select("types[$i]", $lang->bug->typeList, $type, "class='form-control chosen'");?></td>
@@ -119,7 +127,7 @@
<?php endforeach;?>
<?php endif;?>
<?php $nextStart = $i;?>
<?php for($i = $nextStart; $i < $config->bug->batchCreate; $i++):?>
<?php for($i = $nextStart; $i <= $config->bug->batchCreate; $i++):?>
<?php
$moduleID = $i - $nextStart == 0 ? $moduleID : 'ditto';
$executionID = $i - $nextStart == 0 ? $executionID : 'ditto';
@@ -129,7 +137,7 @@
$browser = $i - $nextStart == 0 ? '' : 'ditto';
?>
<tr>
<td><?php echo $i+1;?></td>
<td><?php echo $i;?></td>
<td class='<?php echo zget($visibleFields, $product->type, ' hidden')?>' style='overflow:visible'><?php echo html::select("branches[$i]", $branches, $branch, "class='form-control chosen' onchange='setBranchRelated(this.value, $productID, $i)'");?></td>
<td><?php echo html::select("modules[$i]", $moduleOptionMenu, $moduleID, "class='form-control chosen'");?></td>
<td class='<?php echo zget($visibleFields, 'execution', ' hidden')?>' style='overflow:visible'><?php echo html::select("executions[$i]", $executions, $executionID, "class='form-control chosen' onchange='loadExecutionBuilds($productID, this.value, $i)'");?></td>
@@ -148,6 +156,10 @@
</div>
</div>
</td>
<?php if(isset($executionType) and $executionType == 'kanban'):?>
<td><?php echo html::select("regions[$i]", $regionPairs, $regionID, "class='form-control chosen' onchange='setLane(this.value, $i)'");?></td>
<td><?php echo html::select("lanes[$i]", $lanePairs, $laneID, "class='form-control chosen'");?></td>
<?php endif;?>
<td class='<?php echo zget($visibleFields, 'deadline', 'hidden')?>'><?php echo html::input("deadlines[$i]", '', "class='form-control form-date'");?></td>
<td class='<?php echo zget($visibleFields, 'steps', 'hidden')?>'><?php echo html::textarea("stepses[$i]", '', "rows='1' class='form-control autosize'");?></td>
<td class='<?php echo zget($visibleFields, 'type', 'hidden')?>' style='overflow:visible'> <?php echo html::select("types[$i]", $lang->bug->typeList, $type, "class='form-control chosen'");?></td>
+1 -1
View File
@@ -237,7 +237,7 @@ $currentBrowseType = isset($lang->bug->mySelects[$browseType]) && in_array($brow
<form class='main-table table-bug' method='post' id='bugForm' <?php if(!$useDatatable) echo "data-ride='table'";?>>
<?php endif;?>
<div class="table-header fixed-right">
<nav class="btn-toolbar pull-right"></nav>
<nav class="btn-toolbar pull-right setting"></nav>
</div>
<?php
$vars = "productID=$productID&branch=$branch&browseType=$browseType&param=$param&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}";
+12
View File
@@ -199,6 +199,18 @@ if($this->app->tab == 'project') js::set('objectID', $projectID);
</td>
</tr>
<?php endif;?>
<?php if(isset($executionType) and $executionType == 'kanban'):?>
<tr>
<th><?php echo $lang->kanbancard->region;?></th>
<td><?php echo html::select('region', $regionPairs, $regionID, "onchange='setLane(this.value)' class='form-control chosen'");?></td>
<td>
<div class='input-group'>
<div class="input-group-addon"><?php echo $lang->kanbancard->lane;?></div>
<?php echo html::select('lane', $lanePairs, $laneID, "class='form-control chosen'");?>
</div>
</td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->bug->title;?></th>
<td colspan='2'>
+1 -1
View File
@@ -18,7 +18,7 @@
<tr>
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
<?php $color = empty($object->color) ? '#333' : $object->color;?>
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('bug', 'view', "bugID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
<?php echo html::a($domain . helper::createLink('bug', 'view', "bugID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
</td>
</tr>
</table>
+1 -1
View File
@@ -117,7 +117,7 @@
{
common::printIcon('bug', 'create', $copyParams, $bug, 'button', 'copy');
}
common::printIcon('bug', 'delete', $params, $bug, 'button', 'trash', 'hiddenwin');
common::printIcon('bug', 'delete', $params, $bug, 'button', 'trash', 'hiddenwin', 'showinonlybody');
?>
<?php endif;?>
</div>
+1 -1
View File
@@ -323,7 +323,7 @@ class buildModel extends model
$buildID = (int)$buildID;
$oldBuild = $this->dao->select('*')->from(TABLE_BUILD)->where('id')->eq($buildID)->fetch();
$build = fixer::input('post')->stripTags($this->config->build->editor->edit['id'], $this->config->allowedTags)
->setIF(!isset($this->post->branch), 'branch', $oldBuild->branch)
->setIF(!isset($_POST['branch']), 'branch', $oldBuild->branch)
->setDefault('product', $oldBuild->product)
->cleanInt('product,branch,execution')
->remove('allchecker,resolvedBy,files,labels,uid')
+1
View File
@@ -662,6 +662,7 @@ $lang->navGroup->entry = 'admin';
$lang->navGroup->extension = 'admin';
$lang->navGroup->action = 'admin';
$lang->navGroup->convert = 'admin';
$lang->navGroup->stage = 'admin';
$lang->navGroup->search = 'search';
$lang->navGroup->index = 'index';
+2 -2
View File
@@ -2708,12 +2708,12 @@ EOD;
$response->errcode = $this->config->entry->errcode[$code];
$response->errmsg = urlencode($this->lang->entry->errmsg[$code]);
return print(urldecode(json_encode($response)));
die(urldecode(json_encode($response)));
}
else
{
$response->error = $code;
return print(urldecode(json_encode($response)));
die(urldecode(json_encode($response)));
}
}
+1 -1
View File
@@ -34,7 +34,7 @@ $uid = uniqid('');
'emoticons', 'image', 'insertfile', 'hr', '|', 'link', 'unlink', '/',
'undo', 'redo', '|', 'selectall', 'cut', 'copy', 'paste', '|', 'plainpaste', 'wordpaste', '|', 'removeformat', 'clearhtml','quickformat', '|',
'indent', 'outdent', 'subscript', 'superscript', '|',
'table', 'code', '|', 'pagebreak', 'anchor', '|',
'table', 'code', 'pagebreak',
'fullscreen', 'source', 'preview', 'about'];
var editorToolsMap = {fullTools: fullTools, simpleTools: simpleTools, bugTools: bugTools};
-2
View File
@@ -1,4 +1,2 @@
#whiteListBox .input-group:last-child {margin-top: 10px;}
#mailtoGroup .input-group-btn > .btn {margin-left: -1px !important;}
#mailtoGroup .chosen-container-single a {border-left-width: 0px;}
.fullscreen-save, .markdown-fullscreen-save {position: fixed; top: 10px; right: 30px; z-index: 999999;}
+2
View File
@@ -588,6 +588,7 @@ class docModel extends model
{
$now = helper::now();
$doc = fixer::input('post')
->callFunc('title', 'trim')
->add('addedBy', $this->app->user->account)
->add('addedDate', $now)
->add('editedBy', $this->app->user->account)
@@ -684,6 +685,7 @@ class docModel extends model
$now = helper::now();
$doc = fixer::input('post')->setDefault('module', 0)
->callFunc('title', 'trim')
->stripTags($this->config->doc->editor->edit['id'], $this->config->allowedTags)
->setDefault('users', '')
->setDefault('groups', '')
+1 -1
View File
@@ -18,7 +18,7 @@
<tr>
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
<?php $color = empty($object->color) ? '#333' : $object->color;?>
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('doc', 'view', "docID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
<?php echo html::a($domain . helper::createLink('doc', 'view', "docID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
</td>
</tr>
</table>
+56 -33
View File
@@ -773,12 +773,20 @@ class execution extends control
{
foreach($products as $product) $productModules += $this->tree->getOptionMenu($product->id, 'story', 0, $product->branches);
}
foreach($productModules as $branchID => $moduleList)
if(defined('TUTORIAL'))
{
foreach($moduleList as $moduleID => $moduleName)
$modules = $this->loadModel('tutorial')->getModulePairs();
}
else
{
foreach($productModules as $branchID => $moduleList)
{
if($moduleID and !isset($executionModules[$moduleID])) continue;
$modules[$moduleID] = ((count($products) >= 2 and $moduleID) ? $product->name : '') . $moduleName;
foreach($moduleList as $moduleID => $moduleName)
{
if($moduleID and !isset($executionModules[$moduleID])) continue;
$modules[$moduleID] = ((count($products) >= 2 and $moduleID) ? $product->name : '') . $moduleName;
}
}
}
$actionURL = $this->createLink('execution', 'story', "executionID=$executionID&orderBy=$orderBy&type=bySearch&queryID=myQueryID");
@@ -1304,11 +1312,12 @@ class execution extends control
}
}
if(!empty($projectID) and $project->model == 'kanban' and $this->app->tab == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'index', "projectID=$projectID")));
if(!empty($projectID) and $project->model == 'kanban') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('kanban', "executionID=$executionID")));
if(!empty($projectID) and $project->model == 'kanban' and $this->app->tab == 'project') return $this->send(array('result' => 'success', 'locate' => $this->createLink('project', 'index', "projectID=$projectID")));
if(!empty($projectID) and $project->model == 'kanban') return $this->send(array('result' => 'success', 'locate' => inlink('kanban', "executionID=$executionID")));
$this->view->title = $this->lang->execution->tips;
$this->view->tips = $this->fetch('execution', 'tips', "executionID=$executionID");
$this->view->defaultURL = isset($this->session->closeTipsList) ? $this->session->closeTipsList : $this->createLink('execution', 'task', 'executionID=' . $executionID);
$this->view->executionID = $executionID;
$this->view->projectID = $projectID;
$this->view->project = $project;
@@ -1402,13 +1411,6 @@ class execution extends control
{
$execution = $this->execution->getById($executionID);
$this->loadModel('kanban')->createRDKanban($execution);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$link= $this->config->vision != 'lite' ? $this->createLink('project', 'index', "projectID=$projectID") : $this->createLink('project', 'execution', "status=all&projectID=$projectID");
if($this->app->tab == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('kanban', "executionID=$executionID")));
}
if(!empty($planID))
@@ -1417,8 +1419,15 @@ class execution extends control
}
else
{
if(!empty($projectID) and $project->model == 'kanban' and $this->app->tab == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'index', "projectID=$projectID")));
if(!empty($projectID) and $project->model == 'kanban') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('kanban', "executionID=$executionID")));
if(!empty($projectID) and $project->model == 'kanban')
{
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$link = $this->config->vision != 'lite' ? $this->createLink('project', 'index', "projectID=$projectID") : $this->createLink('project', 'execution', "status=all&projectID=$projectID");
if($this->app->tab == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('kanban', "executionID=$executionID")));
}
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('create', "projectID=$projectID&executionID=$executionID")));
}
}
@@ -1565,7 +1574,8 @@ class execution extends control
$position[] = html::a($browseExecutionLink, $execution->name);
$position[] = $this->lang->execution->edit;
$allProducts = array(0 => '') + $this->product->getProducts($execution->project, 'noclosed', '', false);
$allProducts = $this->config->systemMode == 'classic' ? $this->product->getPairs('noclosed') : $this->product->getProducts($execution->project, 'noclosed', '', false);
$allProducts = array(0 => '') + $allProducts;
$this->loadModel('productplan');
$productPlans = array(0 => '');
@@ -1609,6 +1619,8 @@ class execution extends control
$rdUsers = $this->user->getPairs('noclosed|nodeleted|devfirst', $execution->RD, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["RD"] = $this->config->user->moreLink;
$project = $this->project->getById($execution->project);
$this->view->title = $title;
$this->view->position = $position;
$this->view->executions = $executions;
@@ -1618,8 +1630,7 @@ class execution extends control
$this->view->qdUsers = $qdUsers;
$this->view->rdUsers = $rdUsers;
$this->view->users = $this->user->getPairs('nodeleted|noclosed');
$this->view->allProjects = $this->project->getPairsByModel('all', 0, 'noclosed');
$this->view->project = $this->project->getById($execution->project);
$this->view->project = $project;
$this->view->groups = $this->loadModel('group')->getPairs();
$this->view->allProducts = $allProducts;
$this->view->linkedProducts = $linkedProducts;
@@ -1631,6 +1642,7 @@ class execution extends control
$this->view->productPlans = $productPlans;
$this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($linkedProducts), $this->config->systemMode == 'new' ? $execution->project : 0, 'noclosed', $linkedBranchList);
$this->view->teamMembers = $this->execution->getTeamMembers($executionID);
if($this->config->systemMode == 'new') $this->view->allProjects = $this->project->getPairsByModel($project->model, 0, 'noclosed');
$this->display();
}
@@ -1648,6 +1660,8 @@ class execution extends control
if($this->post->names)
{
$allChanges = $this->execution->batchUpdate();
if(dao::isError()) return print(js::error(dao::getError()));
if(!empty($allChanges))
{
foreach($allChanges as $executionID => $changes)
@@ -2718,6 +2732,8 @@ class execution extends control
{
$this->loadModel('story');
$this->loadModel('product');
$this->loadModel('tree');
$this->loadModel('branch');
/* Init objectID */
$originObjectID = $objectID;
@@ -2733,6 +2749,7 @@ class execution extends control
$object = $this->project->getByID($objectID, $this->app->tab == 'project' ? 'project' : 'sprint,stage,kanban');
$products = $this->product->getProducts($objectID);
$browseLink = $this->createLink($this->app->tab == 'project' ? 'projectstory' : 'execution', 'story', "objectID=$objectID");
$queryID = ($browseType == 'bySearch') ? (int)$param : 0;
$this->session->set('storyList', $this->app->getURI(true), $this->app->tab); // Save session.
@@ -2770,33 +2787,37 @@ class execution extends control
{
$this->project->setMenu($object->id);
}
else if($object->type == 'sprint' or $object->type == 'stage')
elseif($object->type == 'sprint' or $object->type == 'stage')
{
$this->execution->setMenu($object->id);
}
$queryID = ($browseType == 'bySearch') ? (int)$param : 0;
/* Set modules and branches. */
$modules = array();
$branchIDList = array(BRANCH_MAIN);
$branches = $this->project->getBranchesByProject($objectID);
$productType = 'normal';
$this->loadModel('tree');
$this->loadModel('branch');
foreach($products as $product)
if(defined('TUTORIAL'))
{
$productModules = $this->tree->getOptionMenu($product->id, 'story', 0, defined('TUTORIAL') ? array(0 => 0) : array_keys($branches[$product->id]));
foreach($productModules as $branch => $branchModules)
$modules = $this->loadModel('tutorial')->getModulePairs();
}
else
{
foreach($products as $product)
{
foreach($branchModules as $moduleID => $moduleName) $modules[$moduleID] = ((count($products) >= 2 and $moduleID != 0) ? $product->name : '') . $moduleName;
}
if($product->type != 'normal')
{
$productType = $product->type;
if(isset($branches[$product->id]))
$productModules = $this->tree->getOptionMenu($product->id, 'story', 0, array_keys($branches[$product->id]));
foreach($productModules as $branch => $branchModules)
{
foreach($branches[$product->id] as $branchID => $branch) $branchIDList[$branchID] = $branchID;
foreach($branchModules as $moduleID => $moduleName) $modules[$moduleID] = ((count($products) >= 2 and $moduleID != 0) ? $product->name : '') . $moduleName;
}
if($product->type != 'normal')
{
$productType = $product->type;
if(isset($branches[$product->id]))
{
foreach($branches[$product->id] as $branchID => $branch) $branchIDList[$branchID] = $branchID;
}
}
}
}
@@ -3246,6 +3267,8 @@ class execution extends control
*/
public function all($status = 'all', $projectID = 0, $orderBy = 'order_asc', $productID = 0, $recTotal = 0, $recPerPage = 10, $pageID = 1)
{
$this->session->set('closeTipsList', $this->app->getURI(true));
$this->app->loadLang('my');
$this->app->loadLang('product');
$this->app->loadLang('stage');
+1
View File
@@ -0,0 +1 @@
#importBugForm {min-height: 250px;}
+3 -2
View File
@@ -34,12 +34,12 @@
.region .kanban-header {border-bottom: none!important; min-width: max-content; min-width: -moz-max-content;}
.region .kanban-header-col > .title {max-width: 80% !important;}
.region .kanban-header-col > .title {margin: 0}
.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial; max-width: 140px !important;}
.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial; max-width: 85px !important;}
.region .kanban-header-col > .title > .text-grey {opacity: .5; font-weight: bold; color: #8b91a2;}
.region .kanban-header-col > .title > .count {opacity: .5; font-weight: bold; color: #8b91a2;}
.region .kanban-header-col > .title > .error {color: #333333; padding-left: 2px; font-size: 10px; padding-top: 1px; padding-right: 2px;}
.kanban-affixed .kanban-header-col > .title > .text {color: #fff!important}
.region .kanban-header-col > .actions {position: relative; right: -30px;}
.region .kanban-header-col > .actions {position: relative; right: 5px;}
.region .kanban-header-parent-col > .actions {position: absolute; right: 0px;}
.region .kanban-header-sub-cols .actions {position: absolute; right: 0px;}
@@ -66,6 +66,7 @@
.kanban-col[data-type=ADD] {display: none;}
.kanban-col[data-type=EMPTY] {display: none;}
.kanban-col {padding-right: 0px !important;}
.kanban-item:hover .title {padding-right: 10px;}
.gray .actions {display:none;}
+1
View File
@@ -28,6 +28,7 @@
.kanban-affixed .kanban-header-col > .title > .text,
.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;}
.kanban-header-col > .title > .text {max-width: 110px !important;}
#kanbanContainer {margin: 0;}
#kanbanContainer > .panel-body {overflow: auto;}
+51 -12
View File
@@ -265,31 +265,32 @@ function createColumnMenu(options)
*/
function createColumnCreateMenu(options)
{
var $col = options.$trigger.closest('.kanban-col');
var col = $col.data('col');
var items = [];
var laneID = col.$kanbanData.lanes[0].id ? col.$kanbanData.lanes[0].id : 0;
var $col = options.$trigger.closest('.kanban-col');
var col = $col.data('col');
var items = [];
var laneID = col.$kanbanData.lanes[0].id ? col.$kanbanData.lanes[0].id : 0;
var regionID = col.$kanbanData.region;
if(col.type == 'backlog')
{
if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&objectID=' + executionID + '&bugID=0&planID=0&todoID=0&extra=laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canBatchCreateStory) items.push({label: executionLang.batchCreateStory, url: productCount > 1 ? '#batchCreateStory' : $.createLink('story', 'batchcreate', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&executionID=' + executionID + '&plan=0&type=story&extra=laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe',attrs: {'data-toggle': 'modal', 'data-width': '90%'}});
if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&objectID=' + executionID + '&bugID=0&planID=0&todoID=0&extra=regionID=' + regionID + ',laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canBatchCreateStory) items.push({label: executionLang.batchCreateStory, url: productCount > 1 ? '#batchCreateStory' : $.createLink('story', 'batchcreate', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&executionID=' + executionID + '&plan=0&type=story&extra=regionID=' + regionID + ',laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe',attrs: {'data-toggle': 'modal', 'data-width': '90%'}});
if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID + '&browseType=&param=0&recTotal=0&recPerPage=50,&pageID=1&extra=laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '90%'}});
if(priv.canLinkStoryByPlan) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal', 'data-target': '#linkStoryByPlan','data-col' : col.id, 'data-lane' : laneID, 'class' : 'linkStoryByPlanButton'}});
}
else if(col.type == 'unconfirmed')
{
if(priv.canCreateBug) items.push({label: bugLang.create, url: $.createLink('bug', 'create', 'productID=0&moduleID=0&extra=laneID=' + laneID + ',columnID=' + col.id + ',executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canCreateBug) items.push({label: bugLang.create, url: $.createLink('bug', 'create', 'productID=' + productID + '&moduleID=0&extra=regionID=' + regionID + ',laneID=' + laneID + ',columnID=' + col.id + ',executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canBatchCreateBug)
{
if(productNum > 1) items.push({label: bugLang.batchCreate, url: '#batchCreateBug', 'attrs' : {'data-toggle': 'modal'}});
else items.push({label: bugLang.batchCreate, url: $.createLink('bug', 'batchcreate', 'productID=' + productID + '&branch=&executionID=' + executionID + '&module=0&extra=laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '90%'}});
else items.push({label: bugLang.batchCreate, url: $.createLink('bug', 'batchcreate', 'productID=' + productID + '&branch=&executionID=' + executionID + '&module=0&extra=regionID=' + regionID + ',laneID=' + laneID + ',columnID=' + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '90%'}});
}
}
else if(col.type == 'wait')
{
if(priv.canCreateTask) items.push({label: taskLang.create, url: $.createLink('task', 'create', 'executionID=' + executionID + "&storyID=0&moduleID=0&taskID=0&todoID=0&extra=laneID=" + laneID + ",columnID=" + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canBatchCreateTask) items.push({label: taskLang.batchCreate, url: $.createLink('task', 'batchcreate', 'executionID=' + executionID + "&storyID=0&moduleID=0&taskID=0&iframe=0&extra=laneID=" + laneID + ",columnID=" + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canCreateTask) items.push({label: taskLang.create, url: $.createLink('task', 'create', 'executionID=' + executionID + "&storyID=0&moduleID=0&taskID=0&todoID=0&extra=regionID=" + regionID + ",laneID=" + laneID + ",columnID=" + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canBatchCreateTask) items.push({label: taskLang.batchCreate, url: $.createLink('task', 'batchcreate', 'executionID=' + executionID + "&storyID=0&moduleID=0&taskID=0&iframe=0&extra=regionID=" + regionID + ",laneID=" + laneID + ",columnID=" + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canImportBug && vision == 'rnd') items.push({label: executionLang.importBug, url: $.createLink('execution', 'importBug', 'executionID=' + executionID + "&storyID=0&moduleID=0&taskID=0&todoID=0&extra=laneID=" + laneID + ",columnID=" + col.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
}
return items;
@@ -548,7 +549,7 @@ function renderBugItem(item, $item, col)
if(scaleSize <= 1)
{
var $actions = $item.find('.actions');
if(!$actions.length && (priv.canEditBug || priv.canResolveBug || priv.canConfirmBug || priv.canCopyBug || priv.canToStoryBug))
if(!$actions.length && (priv.canEditBug || priv.canResolveBug || priv.canConfirmBug || priv.canCopyBug || priv.canToStoryBug || priv.canDeleteBug))
{
$actions = $([
'<div class="actions">',
@@ -862,11 +863,16 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
}
else if(toColType == 'developing')
{
if((fromColType == 'pause' || fromColType == 'canceled' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask)
if((fromColType == 'canceled' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask)
{
var link = createLink('task', 'activate', 'taskID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID + ',regionID=' + regionID, '', true);
showIframe = true;
}
if(fromColType == 'pause' && priv.canActivateTask)
{
var link = createLink('task', 'restart', 'taskID=' + objectID, '', true);
showIframe = true;
}
if(fromColType == 'wait' && priv.canStartTask)
{
var link = createLink('task', 'start', 'taskID=' + objectID + '&extra=fromColID=' + fromColID + ',toColID=' + toColID + ',fromLaneID=' + fromLaneID + ',toLaneID=' + toLaneID + ',regionID=' + regionID, '', true);
@@ -971,6 +977,36 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
}
}
/**
* Delete a card.
*
* @param string objectType
* @param int objectID
* @param int regionID
* @access public
* @return void
*/
function deleteCard(objectType, objectID, regionID)
{
var objectLang = objectType + 'Lang';
var result = confirm(window[objectLang].confirmDelete) ? true : false;
if(!result) return false;
if(!objectID) return false;
var url = createLink('kanban', 'deleteObjectCard', 'objectType=' + objectType + '&objectID=' + objectID + '&regionID=' + regionID);
return $.ajax(
{
method: 'post',
dataType: 'json',
url: url,
success: function(data)
{
updateRegion(regionID, data[regionID]);
}
});
}
/**
* Close modal and update kanban data.
*
@@ -1071,6 +1107,7 @@ function createStoryMenu(options)
if(priv.canBatchCreateTask && showAction) items.push({label: executionLang.batchWBS, icon: 'pluses', url: createLink('task', 'batchCreate', 'executionID=' + execution.id + '&storyID=' + story.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canActivateStory && story.$col.type == 'closed') items.push({label: executionLang.activate, icon: 'magic', url: createLink('story', 'activate', 'storyID=' + story.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canUnlinkStory) items.push({label: executionLang.unlinkStory, icon: 'unlink', url: createLink('execution', 'unlinkStory', 'executionID=' + execution.id + '&storyID=' + story.id, '', 'false'), attrs: {target: 'hiddenwin'}});
if(priv.canDeleteStory) items.push({label: storyLang.delete, icon: 'trash', onClick: function(){deleteCard('story', story.id, story.$lane.region)}});
return items;
}
@@ -1090,6 +1127,7 @@ function createTaskMenu(options)
if(priv.canActivateTask && (task.$col.type == 'developed' || task.$col.type == 'canceled' || task.$col.type == 'closed')) items.push({label: executionLang.activate, icon: 'magic', url: createLink('task', 'activate', 'taskID=' + task.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canCreateTask) items.push({label: taskLang.copy, icon: 'copy', url: createLink('task', 'create', 'executionID=' + executionID + '&storyID=' + '0' + '&moduleID=' + '0' + '&taskID=' + task.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canCancelTask && (task.$col.type == 'wait' || task.$col.type == 'developing' || task.$col.type == 'pause')) items.push({label: taskLang.cancel, icon: 'cancel', url: createLink('task', 'cancel', 'taskID=' + task.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canDeleteTask) items.push({label: taskLang.delete, icon: 'trash', onClick: function(){deleteCard('task', task.id, task.$lane.region)}});
return items;
}
@@ -1109,6 +1147,7 @@ function createBugMenu(options)
if(priv.canCopyBug) items.push({label: bugLang.copy, icon: 'copy', url: createLink('bug', 'create', 'productID=' + productID + '&branch=' + '' + '&extras=bugID=' + bug.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canToStoryBug && (bug.$col.type != 'closed')) items.push({label: bugLang.toStory, icon: 'lightbulb', url: createLink('story', 'create', 'product=' + productID + '&branch=' + '0' + '&module=' + '0' + '&story=' + '0' + '&execution=' + '0' + '&bugID=' + bug.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canActivateBug && (bug.$col.type == 'fixed') || bug.$col.type == 'testing' || bug.$col.type == 'tested' || bug.$col.type == 'closed') items.push({label: bugLang.activate, icon: 'magic', url: createLink('bug', 'activate', 'bugID=' + bug.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canDeleteBug) items.push({label: bugLang.delete, icon: 'trash', onClick: function(){deleteCard('bug', bug.id, bug.$lane.region)}});
return items;
}
+4 -4
View File
@@ -23,9 +23,9 @@ function setRole(account, roleID)
function addItem(obj)
{
var item = $('#addItem').html().replace(/%i%/g, i);
$(obj).closest('tr').after('<tr class="addedItem">' + item + '</tr>');
var $accounts = $('#hours' + i).closest('tr').find('select:first')
var item = $('#addItem').html().replace(/%i%/g, itemIndex);
$('<tr class="addedItem">' + item + '</tr>').insertAfter($(obj).closest('tr'));
var $accounts = $('#hours' + itemIndex).closest('tr').find('select:first')
if($accounts.attr('data-pickertype') != 'remote')
{
@@ -37,7 +37,7 @@ function addItem(obj)
var pickerremote = $accounts.attr('data-pickerremote');
$accounts.picker({chosenMode: true, remote: pickerremote});
}
i ++;
itemIndex ++;
}
function deleteItem(obj)
+18 -7
View File
@@ -315,11 +315,15 @@ function renderHeaderCol($col, col, $header, kanban)
].join(''));
}
$actions.append([
'<a data-contextmenu="column" title="' + kanbanLang.moreAction + '" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>'
].join(''));
if(priv.canSetWIP || priv.canEditName)
{
$actions.append([
'<a data-contextmenu="column" title="' + kanbanLang.moreAction + '" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>'
].join(''));
}
$actions.appendTo($col);
}
@@ -571,11 +575,16 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
}
else if(toColType == 'developing')
{
if((fromColType == 'pause' || fromColType == 'canceled' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask)
if((fromColType == 'canceled' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask)
{
var link = createLink('task', 'activate', 'taskID=' + objectID, '', true);
showIframe = true;
}
if(fromColType == 'pause' && priv.canActivateTask)
{
var link = createLink('task', 'restart', 'taskID=' + objectID, '', true);
showIframe = true;
}
if(fromColType == 'wait' && priv.canStartTask)
{
var link = createLink('task', 'start', 'taskID=' + objectID, '', true);
@@ -861,7 +870,7 @@ function createStoryMenu(options)
var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}};
if(this.size) item.attrs['data-width'] = this.size;
if(this.icon == 'unlink') item = {label: this.label, icon: this.icon, url: this.url, attrs: {'target': 'hiddenwin'}};
if(this.icon == 'unlink' || this.icon == 'trash') item = {label: this.label, icon: this.icon, url: this.url, attrs: {'target': 'hiddenwin'}};
items.push(item);
});
@@ -883,6 +892,7 @@ function createBugMenu(options)
var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}};
if(this.size) item.attrs['data-width'] = this.size;
if(this.icon == 'trash') item = {label: this.label, icon: this.icon, url: this.url, attrs: {'target': 'hiddenwin'}};
items.push(item);
});
@@ -904,6 +914,7 @@ function createTaskMenu(options)
var item = {label: this.label, icon: this.icon, url: this.url, attrs: {'data-toggle': 'modal', 'data-type': 'iframe'}};
if(this.size) item.attrs['data-width'] = this.size;
if(this.icon == 'trash') item = {label: this.label, icon: this.icon, url: this.url, attrs: {'target': 'hiddenwin'}};
items.push(item);
});
+9 -7
View File
@@ -14,11 +14,11 @@ $lang->execution->allExecutions = 'All ' . $lang->execution->common . 's';
$lang->execution->allExecutionAB = 'Execution List';
$lang->execution->id = $lang->executionCommon . ' ID';
$lang->execution->type = $lang->executionCommon . 'Type';
$lang->execution->name = $lang->executionCommon . 'Name';
$lang->execution->code = $lang->executionCommon . 'Code';
$lang->execution->name = "{$lang->executionCommon} Name";
$lang->execution->code = "{$lang->executionCommon} Code";
$lang->execution->projectName = 'Project';
$lang->execution->execName = 'Execution Name';
$lang->execution->execCode = 'Execution Code';
$lang->execution->execName = "{$lang->execution->common} Name";
$lang->execution->execCode = "{$lang->execution->common} Code";
$lang->execution->execType = 'Execution Type';
$lang->execution->stage = 'Stage';
$lang->execution->pri = 'Priority';
@@ -45,7 +45,7 @@ $lang->execution->totalDays = ' Days';
$lang->execution->status = $lang->executionCommon . 'Status';
$lang->execution->execStatus = 'Status';
$lang->execution->subStatus = 'Sub Status';
$lang->execution->desc = $lang->executionCommon . 'Description';
$lang->execution->desc = "{$lang->executionCommon} Description";
$lang->execution->execDesc = 'Description';
$lang->execution->owner = 'Owner';
$lang->execution->PO = "{$lang->executionCommon} Owner";
@@ -221,8 +221,8 @@ $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 Execution";
$lang->execution->copyExec = "Copy Execution";
$lang->execution->createExec = "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";
@@ -347,6 +347,8 @@ $lang->execution->errorSameProducts = "{$lang->executionCommon} cannot
$lang->execution->errorSameBranches = "{$lang->executionCommon} cannot be linked to the same branch twice";
$lang->execution->errorBegin = "The start time of {$lang->executionCommon} cannot be less than the start time of the project %s.";
$lang->execution->errorEnd = "The end time of {$lang->executionCommon} cannot be greater than the end time %s of the project.";
$lang->execution->errorLetterProject = "The start time of stage cannot be less than the start time of the project %s.";
$lang->execution->errorGreaterProject = "The end time of stage cannot be greater than the end time %s of the project.";
$lang->execution->accessDenied = "Your access to {$lang->executionCommon} is denied!";
$lang->execution->tips = 'Note';
$lang->execution->afterInfo = "{$lang->executionCommon} is created. Next you can ";
+2
View File
@@ -347,6 +347,8 @@ $lang->execution->errorSameProducts = "{$lang->executionCommon}不能
$lang->execution->errorSameBranches = "{$lang->executionCommon}不能关联多个相同的分支。";
$lang->execution->errorBegin = "{$lang->executionCommon}的开始时间不能小于所属项目的开始时间%s。";
$lang->execution->errorEnd = "{$lang->executionCommon}的截止时间不能大于所属项目的结束时间%s。";
$lang->execution->errorLetterProject = "阶段的计划开始时间不能小于所属项目的计划开始时间%s。";
$lang->execution->errorGreaterProject = "阶段的计划完成时间不能大于所属项目的计划完成时间%s。";
$lang->execution->accessDenied = "您无权访问该{$lang->executionCommon}!";
$lang->execution->tips = '提示';
$lang->execution->afterInfo = "{$lang->executionCommon}添加成功,您现在可以进行以下操作:";
+44 -21
View File
@@ -252,6 +252,10 @@ class executionModel extends model
if(!isset($executions[$executionID]))
{
$this->session->set('execution', key($executions), $this->app->tab);
$execution = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->andWhere('type')->in('sprint,stage,kanban')->fetch();
if(empty($execution)) return js::error($this->lang->notFound);
if($executionID && strpos(",{$this->app->user->view->sprints},", ",{$executionID},") === false) $this->accessDenied();
}
@@ -448,18 +452,12 @@ class executionModel extends model
}
$products = array_filter($this->post->products);
if($oldExecution->type == 'stage' and empty($products))
$noLinkTip = $oldExecution->type != 'kanban' ? $this->lang->execution->noLinkProduct : $this->lang->execution->kanbanNoLinkProduct;
if(empty($products))
{
dao::$errors['message'][] = $this->lang->execution->noLinkProduct;
dao::$errors['message'][] = $noLinkTip;
return false;
}
if($oldExecution->type == 'kanban' and empty($products))
{
dao::$errors['message'][] = $this->lang->execution->kanbanNoLinkProduct;
return false;
}
/* Get the data from the post. */
$execution = fixer::input('post')
->setDefault('lastEditedBy', $this->app->user->account)
@@ -547,11 +545,8 @@ class executionModel extends model
->exec();
if(isset($execution->project) and $execution->project) $this->addProjectMembers($execution->project, $teamMembers);
if(!empty($execution->whitelist))
{
$whitelist = explode(',', $execution->whitelist);
$this->loadModel('personnel')->updateWhitelist($whitelist, 'sprint', $executionID);
}
$whitelist = explode(',', $execution->whitelist);
$this->loadModel('personnel')->updateWhitelist($whitelist, 'sprint', $executionID);
/* Fix bug#3074, Update views for team members. */
if($execution->acl != 'open') $this->updateUserView($executionID, 'sprint', $changedAccounts);
@@ -579,6 +574,7 @@ class executionModel extends model
public function batchUpdate()
{
$this->loadModel('user');
$this->loadModel('project');
$executions = array();
$allChanges = array();
@@ -634,11 +630,16 @@ class executionModel extends model
if($projectModel == 'scrum' and empty($executionCode))
{
dao::$errors['code'][] = 'execution#' . $executionID . sprintf($this->lang->error->notempty, $this->lang->project->code);
return false;
}
elseif(!empty($executionCode))
{
/* Check unique code for edited executions. */
if(isset($codeList[$executionCode])) dao::$errors['code'][] = 'execution#' . $executionID . sprintf($this->lang->error->unique, $this->lang->project->code, $executionCode);
if(isset($codeList[$executionCode]))
{
dao::$errors['code'][] = 'execution#' . $executionID . sprintf($this->lang->error->unique, $this->lang->project->code, $executionCode);
return false;
}
$codeList[$executionCode] = $executionCode;
}
@@ -649,29 +650,44 @@ class executionModel extends model
$executions[$executionID]->{$extendField->field} = htmlSpecialString($executions[$executionID]->{$extendField->field});
$message = $this->checkFlowRule($extendField, $executions[$executionID]->{$extendField->field});
if($message) return print(js::alert($message));
if($message)
{
dao::$errors['message'][] = $message;
return false;
}
}
}
if(dao::isError()) return print(js::error(dao::getError()));
foreach($executions as $executionID => $execution)
{
$oldExecution = $oldExecutions[$executionID];
$team = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution');
$projectID = isset($execution->project) ? $execution->project : $oldExecution->project;
$project = $this->project->getByID($projectID);
if($execution->begin < $project->begin)
{
dao::$errors['begin'] = sprintf($this->lang->execution->errorLetterProject, $project->begin);
return false;
}
if($execution->end > $project->end)
{
dao::$errors['end'] = sprintf($this->lang->execution->errorGreaterProject, $project->end);
return false;
}
$this->dao->update(TABLE_EXECUTION)->data($execution)
->autoCheck($skipFields = 'begin,end')
->batchcheck($this->config->execution->edit->requiredFields, 'notempty')
->checkIF($execution->begin != '', 'begin', 'date')
->checkIF($execution->end != '', 'end', 'date')
->checkIF($execution->end != '', 'end', 'gt', $execution->begin)
->checkIF($execution->end != '', 'end', 'ge', $execution->begin)
->checkIF((!empty($execution->name) and $this->config->systemMode == 'new'), 'name', 'unique', "id != $executionID and type in ('sprint','stage') and `project` = $projectID")
->checkIF(!empty($execution->code), 'code', 'unique', "id != $executionID and type in ('sprint','stage')")
->where('id')->eq($executionID)
->limit(1)
->exec();
if(dao::isError()) return print(js::error('execution#' . $executionID . dao::getError(true)));
if(!empty($execution->project) and $oldExecution->project != $execution->project)
{
@@ -2269,11 +2285,12 @@ class executionModel extends model
* @param array $stories
* @param array $products
* @param string $extra
* @param array $lanes
*
* @access public
* @return bool
*/
public function linkStory($executionID, $stories = array(), $products = array(), $extra = '')
public function linkStory($executionID, $stories = array(), $products = array(), $extra = '', $lanes = array())
{
if(empty($executionID)) return false;
if(empty($stories)) $stories = $this->post->stories;
@@ -2296,7 +2313,13 @@ class executionModel extends model
if(strpos($notAllowedStatus, $storyList[$storyID]->status) !== false) continue;
if(isset($linkedStories[$storyID])) continue;
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($executionID, $output['laneID'], $output['columnID'], 'story', $storyID);
$laneID = isset($output['laneID']) ? $output['laneID'] : 0;
if(!empty($lanes[$storyID])) $laneID = $lanes[$storyID];
$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);
$data = new stdclass();
$data->project = $executionID;
+1 -2
View File
@@ -11,7 +11,6 @@
*/
?>
<?php if(isset($tips)):?>
<?php $defaultURL = $this->createLink('execution', 'task', 'executionID=' . $executionID);?>
<?php include '../../common/view/header.html.php';?>
<body>
<div class='modal-dialog mw-500px' id='tipsModal'>
@@ -145,7 +144,7 @@
<?php endforeach;?>
<div class='col-sm-4'>
<div class="input-group <?php if(in_array($project->model, array('kanban', 'waterfall'))) echo 'required';?>">
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' required onchange='loadBranches(this)'");?>
<span class='input-group-addon fix-border'></span>
</div>
</div>
+1 -1
View File
@@ -159,7 +159,7 @@
<?php endforeach;?>
<div class='col-sm-4'>
<div class="input-group">
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' required onchange='loadBranches(this)'");?>
<span class='input-group-addon fix-border'></span>
</div>
</div>
+1 -1
View File
@@ -75,7 +75,7 @@
</div>
<div class='btn-toolbar'>
<?php if(isonlybody()):?>
<?php echo html::submitButton('<i class="icon icon-back icon-sm"></i> ' . $lang->goback, 'onclick="goback()"', 'btn');?>
<?php echo html::commonButton('<i class="icon icon-sm"></i> ' . $lang->goback, 'onclick="goback()"', 'btn');?>
<?php else:?>
<?php echo html::backButton('','','btn');?>
<?php endif;?>
+3
View File
@@ -85,6 +85,7 @@ js::set('priv',
'canResolveBug' => common::hasPriv('bug', 'resolve'),
'canCopyBug' => common::hasPriv('bug', 'create'),
'canEditBug' => common::hasPriv('bug', 'edit'),
'canDeleteBug' => common::hasPriv('bug', 'delete'),
'canActivateBug' => common::hasPriv('bug', 'activate'),
'canAssignTask' => common::hasPriv('task', 'assignto'),
'canFinishTask' => common::hasPriv('task', 'finish'),
@@ -96,10 +97,12 @@ js::set('priv',
'canStartTask' => common::hasPriv('task', 'start'),
'canRestartTask' => common::hasPriv('task', 'restart'),
'canEditTask' => common::hasPriv('task', 'edit'),
'canDeleteTask' => common::hasPriv('task', 'delete'),
'canRecordEstimateTask' => common::hasPriv('task', 'recordEstimate'),
'canToStoryBug' => common::hasPriv('story', 'create'),
'canAssignStory' => common::hasPriv('story', 'assignto'),
'canEditStory' => common::hasPriv('story', 'edit'),
'canDeleteStory' => common::hasPriv('story', 'delete'),
'canChangeStory' => common::hasPriv('story', 'change'),
'canUnlinkStory' => common::hasPriv('execution', 'unlinkStory'),
)
+1 -1
View File
@@ -122,7 +122,7 @@
</tbody>
<tfoot><tr><td colspan='6' class='text-center form-actions'><?php echo html::submitButton() . ' ' . html::backButton(); ?></td></tr></tfoot>
</table>
<?php js::set('i', $i);?>
<?php js::set('itemIndex', $i);?>
</form>
</div>
<div>
+3
View File
@@ -372,6 +372,9 @@ class groupModel extends model
if(isset($_POST['allchecker']))$actions['views'] = array();
if(!isset($actions['actions']))$actions['actions'] = array();
if(isset($actions['actions']['project']['started'])) $actions['actions']['project']['syncproject'] = 'syncproject';
if(isset($actions['actions']['execution']['started'])) $actions['actions']['execution']['syncexecution'] = 'syncexecution';
$dynamic = $actions['actions'];
if(!isset($_POST['allchecker']))
{
+30 -9
View File
@@ -156,7 +156,7 @@ class kanban extends control
{
if($confirm == 'no')
{
return print(js::confirm($this->lang->kanban->confirmDelete, $this->createLink('kanban', 'deleteSpace', "spaceID=$spaceID&confirm=yes")));
return print(js::confirm($this->lang->kanban->confirmDeleteSpace, $this->createLink('kanban', 'deleteSpace', "spaceID=$spaceID&confirm=yes")));
}
else
{
@@ -324,7 +324,7 @@ class kanban extends control
{
if($confirm == 'no')
{
return print(js::confirm($this->lang->kanban->confirmDelete, $this->createLink('kanban', 'delete', "kanbanID=$kanbanID&confirm=yes")));
return print(js::confirm($this->lang->kanban->confirmDeleteKanban, $this->createLink('kanban', 'delete', "kanbanID=$kanbanID&confirm=yes")));
}
else
{
@@ -830,13 +830,9 @@ class kanban extends control
$kanbanUsers = $kanbanID == 0 ? ',' : trim($kanban->owner) . ',' . trim($kanban->team);
$users = $this->loadModel('user')->getPairs('noclosed|nodeleted', '', 0, $kanbanUsers);
$lanePairs = $this->kanban->getLanePairsByGroup($groupID);
$lanePairs['ditto'] = $this->lang->kanbancard->ditto;
$this->view->title = $this->lang->kanban->batchCreateCard;
$this->view->users = $users;
$this->view->lanePairs = $lanePairs;
$this->view->lanePairs = $this->kanban->getLanePairsByGroup($groupID);
$this->display();
}
@@ -1341,8 +1337,11 @@ class kanban extends control
{
if($confirm == 'no')
{
$card = $this->kanban->getCardByID($cardID);
$column = $this->dao->select('*')->from(TABLE_KANBANCOLUMN)->where('id')->eq($card->column)->fetch();
$column = $this->dao->select('t2.*')->from(TABLE_KANBANCELL)->alias('t1')
->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.column=t2.id')
->where('t1.cards')->like("%,$cardID,%")
->andWhere('t1.type')->eq('common')
->fetch();
if($column->archived or $column->deleted) return print(js::alert(sprintf($this->lang->kanbancard->confirmRestoreTip, $column->name)));
return print(js::confirm(sprintf($this->lang->kanbancard->confirmRestore, $column->name), $this->createLink('kanban', 'restoreCard', "cardID=$cardID&confirm=yes"), ''));
@@ -1384,6 +1383,28 @@ class kanban extends control
}
}
/**
* Delete a card.
*
* @param string $objectType story|task|bug
* @param int $objectID
* @param int $regionID
* @access public
* @return void
*/
public function deleteObjectCard($objectType, $objectID, $regionID)
{
if(!($objectType == 'task' or $objectType == 'story' or $objectType == 'bug')) return false;
$table = 'TABLE_' . strtoupper($objectType);
$this->loadModel($objectType)->delete(constant($table), $objectID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$kanbanID = $this->kanban->getKanbanIDByregion($regionID);
$kanbanGroup = $this->kanban->getRDKanban($kanbanID);
return print(json_encode($kanbanGroup));
}
/**
* Set WIP.
*
+1 -1
View File
@@ -1 +1 @@
#c-name {width: 110px}
#c-name {width: 90px}
+1
View File
@@ -2,6 +2,7 @@
.c-WIPTitle {width: 80px; padding-left: 15px !important; padding-right: 0px !important;}
.c-color {width: 320px;}
.c-WIPCount {width: 240px;}
.c-WIPTitle {width: 85px;}
td.required:after {right: -1px;}
.c-action {width: 80px;}
.c-action > a {padding-top: 10px;}
+4 -3
View File
@@ -23,7 +23,7 @@
.region .kanban-header-sub-cols .kanban-header-col > .title {max-width: 100% !important;min-width:240px;}
.region .sort .region-header {cursor: move;}
.region .kanban {padding: 0px 10px 10px 10px; min-height: unset; overflow:auto; width: 100%}
.region .kanban {padding: 0px 5px 10px 10px; min-height: unset; overflow:auto; width: 100%}
.region .kanban .form-actions {margin: 0;}
.region .kanban .form-actions .btn {margin-right: 10px; min-width: 50px;}
@@ -35,12 +35,12 @@
.region .kanban-header {border-bottom: none!important; min-width: max-content; min-width: -moz-max-content;}
.region .kanban-header-col > .title {max-width: 80% !important;}
.region .kanban-header-col > .title {margin: 0}
.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial; max-width: 140px !important;}
.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial; max-width:95px !important;}
.region .kanban-header-col > .title > .text-grey {opacity: .5; font-weight: bold; color: #8b91a2;}
.region .kanban-header-col > .title > .count {opacity: .5; font-weight: bold; color: #8b91a2;}
.region .kanban-header-col > .title > .error {color: #333333; padding-left: 2px; font-size: 10px; padding-top: 1px; padding-right: 2px;}
.kanban-affixed .kanban-header-col > .title > .text {color: #fff!important}
.region .kanban-header-col > .actions {position: relative; right: -30px;}
.region .kanban-header-col > .actions {position: relative; right: -5px;}
.region .kanban-header-parent-col > .actions {position: absolute; right: 0px;}
.region .kanban-header-sub-cols .actions {position: absolute; right: 0px;}
@@ -98,6 +98,7 @@
.kanban-col[data-type=ADD] {display: none;}
.kanban-col[data-type=EMPTY] {display: none;}
.kanban-col {padding-right: 0px !important;}
.kanban-item .title {padding-right: 10px;}
.gray .actions {display:none;}
+23
View File
@@ -85,3 +85,26 @@ function locateView(module, objectID)
if(module == 'build') dataApp = 'project';
parent.$.apps.open(createLink(module, 'view', 'objectID=' + objectID), dataApp);
}
/**
* When type change.
*
* @param string type
* @access public
* @return void
*/
function changeType(type)
{
if(type == 'private')
{
$('#ownerBox').addClass('hidden');
$('#teamBox').addClass('hidden');
$('#whitelistBox').removeClass('hidden');
}
else
{
$('#ownerBox').removeClass('hidden');
$('#teamBox').removeClass('hidden');
$('#whitelistBox').addClass('hidden');
}
}
-12
View File
@@ -1,12 +0,0 @@
/**
* When type change.
*
* @param string type
* @access public
* @return void
*/
function changeType(type)
{
location.href = createLink('kanban', 'createSpace', 'type=' + type);
}
-12
View File
@@ -1,12 +0,0 @@
/**
* When type change.
*
* @param int spaceID
* @param string type
* @access public
* @return void
*/
function changeType(spaceID, type)
{
location.href = createLink('kanban', 'editSpace', 'spaceID=' + spaceID + '&type=' + type);
}
-13
View File
@@ -27,16 +27,3 @@ function setWIPLimit()
$('#limit').val(count);
}
/**
* When WIPCount value change.
*
* @param int value
* @access public
* @return void
*/
function wipValueChange(value)
{
if(value == '') $('#submit').addClass('disabled');
if(value) $('#submit').removeClass('disabled');
}
+1 -1
View File
@@ -1490,7 +1490,7 @@ function initSortable()
var groupID = e.element.closest('.kanban-board').data('id');
e.list.each(function(index, data)
{
if(data.item.hasClass('kanban-col') && data.item.hasClass('sort') && groupID == data.item.data().col.group) orders.push(data.item.data('id'));
if(data.item.hasClass('kanban-col') && (data.item.hasClass('sort') || data.item.children().hasClass('sort')) && groupID == data.item.data().col.group) orders.push(data.item.data('id'));
});
regionID = e.element.closest('.region').data('id');
+6 -3
View File
@@ -101,9 +101,10 @@ $lang->kanban->cardCount = 'Card Count';
$lang->kanban->createColumnOnLeft = 'Create Column On Left';
$lang->kanban->createColumnOnRight = 'Create Column On Right';
$lang->kanban->accessDenied = "You have no access to the kanban.";
$lang->kanban->confirmDelete = 'Do you want to delete this?';
$lang->kanban->cardCountTip = 'Please enter the number of cards';
$lang->kanban->accessDenied = "You have no access to the kanban.";
$lang->kanban->confirmDeleteSpace = 'Do you want to delete this Space?';
$lang->kanban->confirmDeleteKanban = 'Do you want to delete this Kanban?';
$lang->kanban->cardCountTip = 'Please enter the number of cards';
$lang->kanban->selectedKanban = 'Please select Kanban';
$lang->kanban->selectedProduct = 'Please select Product';
@@ -188,6 +189,7 @@ $lang->kanban->error->mustBeInt = 'The WIPs must be positive integer.';
$lang->kanban->error->parentLimitNote = 'The WIPs in the parent column cannot be < the sum of the WIPs in the child column.';
$lang->kanban->error->childLimitNote = 'The sum of products in the child column cannot be > the number of products in the parent column.';
$lang->kanban->error->importObjNotEmpty = 'Please select at least one import object.';
$lang->kanban->error->childLimitEmpty = 'Please set the WIP quantity of the sub column before operation.';
$lang->kanban->importList = array();
$lang->kanban->importList['off'] = 'Import is not enabled';
@@ -332,6 +334,7 @@ $lang->kanbancard->name = 'Card Name';
$lang->kanbancard->legendBasicInfo = 'Basic Info';
$lang->kanbancard->legendLifeTime = 'Card Life';
$lang->kanbancard->space = 'Space';
$lang->kanbancard->region = 'Region';
$lang->kanbancard->kanban = 'Kanban';
$lang->kanbancard->lane = 'Lane';
$lang->kanbancard->column = 'Column';
+6 -3
View File
@@ -101,9 +101,10 @@ $lang->kanban->cardCount = '卡片数量';
$lang->kanban->createColumnOnLeft = '在左侧添加看板列';
$lang->kanban->createColumnOnRight = '在右侧添加看板列';
$lang->kanban->accessDenied = '您无权访问该看板';
$lang->kanban->confirmDelete = '您确认删除吗?';
$lang->kanban->cardCountTip = '请输入卡片数量';
$lang->kanban->accessDenied = '您无权访问该看板';
$lang->kanban->confirmDeleteSpace = '您确认要删除该空间吗?';
$lang->kanban->confirmDeleteKanban = '您确认要删除该看板吗?';
$lang->kanban->cardCountTip = '请输入卡片数量';
$lang->kanban->selectedKanban = '请选择看板';
$lang->kanban->selectedProduct = '请选择产品';
@@ -188,6 +189,7 @@ $lang->kanban->error->mustBeInt = '在制品数量必须是正整数。'
$lang->kanban->error->parentLimitNote = '父列的在制品数量不能小于子列在制品数量之和';
$lang->kanban->error->childLimitNote = '子列在制品数量之和不能大于父列的在制品数量';
$lang->kanban->error->importObjNotEmpty = '请至少选择一个导入对象';
$lang->kanban->error->childLimitEmpty = '请先设置子列的在制品数量后再操作';
$lang->kanban->importList = array();
$lang->kanban->importList['off'] = '不启用导入功能';
@@ -332,6 +334,7 @@ $lang->kanbancard->name = '卡片名称';
$lang->kanbancard->legendBasicInfo = '基本信息';
$lang->kanbancard->legendLifeTime = '卡片的一生';
$lang->kanbancard->space = '所属空间';
$lang->kanbancard->region = '所属区域';
$lang->kanbancard->kanban = '所属看板';
$lang->kanbancard->lane = '所属泳道';
$lang->kanbancard->column = '所属看板列';
+4 -2
View File
@@ -77,8 +77,9 @@ $lang->kanban->teamSumCount = '共%s人';
$lang->kanban->createColumnOnLeft = '在左側添加看板列';
$lang->kanban->createColumnOnRight = '在右側添加看板列';
$lang->kanban->accessDenied = '您無權訪問該看板';
$lang->kanban->confirmDelete = '您確認刪除嗎?';
$lang->kanban->accessDenied = '您無權訪問該看板';
$lang->kanban->confirmDeleteSpace = '您確認要删除該空間嗎?';
$lang->kanban->confirmDeleteKanban = '您確認要删除該看板嗎?';
$lang->kanban->aclGroup['open'] = '公開';
$lang->kanban->aclGroup['private'] = '私有';
@@ -266,6 +267,7 @@ $lang->kanbancard->name = '卡片名稱';
$lang->kanbancard->legendBasicInfo = '基本信息';
$lang->kanbancard->legendLifeTime = '卡片的一生';
$lang->kanbancard->space = '所屬空間';
$lang->kanbancard->region = '所屬區域';
$lang->kanbancard->kanban = '所屬看板';
$lang->kanbancard->lane = '所屬泳道';
$lang->kanbancard->column = '所屬看板列';
+66 -26
View File
@@ -412,27 +412,15 @@ class kanbanModel extends model
/**
* Create a kanban card.
*
* @param int $kanbanID
* @param int $regionID
* @param int $groupID
* @param int $columnID
* @param int $kanbanID
* @param int $regionID
* @param int $groupID
* @param int $columnID
* @access public
* @return void
* @return bool|int
*/
public function createCard($kanbanID, $regionID, $groupID, $columnID)
{
if($this->post->estimate < 0)
{
dao::$errors[] = $this->lang->kanbancard->error->recordMinus;
return false;
}
if($this->post->end && $this->post->begin > $this->post->end)
{
dao::$errors[] = $this->lang->kanbancard->error->endSmall;
return false;
}
$now = helper::now();
$card = fixer::input('post')
->add('kanban', $kanbanID)
@@ -442,7 +430,7 @@ class kanbanModel extends model
->add('createdDate', $now)
->add('assignedDate', $now)
->add('color', '#fff')
->trim('name')
->trim('name,estimate')
->setDefault('estimate', 0)
->stripTags($this->config->kanban->editor->createcard['id'], $this->config->allowedTags)
->join('assignedTo', ',')
@@ -450,6 +438,18 @@ class kanbanModel extends model
->remove('uid,lane')
->get();
if($card->estimate < 0)
{
dao::$errors['estimate'] = $this->lang->kanbancard->error->recordMinus;
return false;
}
if($card->end && $card->begin > $card->end)
{
dao::$errors['end'] = $this->lang->kanbancard->error->endSmall;
return false;
}
$card = $this->loadModel('file')->processImgURL($card, $this->config->kanban->editor->createcard['id'], $this->post->uid);
$this->dao->insert(TABLE_KANBANCARD)->data($card)->autoCheck()
@@ -553,10 +553,15 @@ class kanbanModel extends model
return false;
}
/*
/**
* Batch create kanban cards.
*
* @param int $kanbanID
* @param int $regionID
* @param int $groupID
* @param int $columnID
* @access public
* @return void
* @return bool
*/
public function batchCreateCard($kanbanID, $regionID, $groupID, $columnID)
{
@@ -564,6 +569,9 @@ class kanbanModel extends model
{
if($_POST['name'][$index] and isset($_POST['assignedTo'][$index])) $_POST['assignedTo'][$index] = implode(',', $_POST['assignedTo'][$index]);
if(!isset($_POST['assignedTo'][$index])) $_POST['assignedTo'][$index] = '';
$_POST['estimate'][$index] = trim($_POST['estimate'][$index]);
if(empty($_POST['estimate'][$index])) $_POST['estimate'][$index] = 0;
}
$cards = fixer::input('post')->get();
@@ -888,6 +896,18 @@ class kanbanModel extends model
->fetchPairs();
}
/**
* Get kanban id by region id.
*
* @param int $regionID
* @access public
* @return int
*/
public function getKanbanIDByRegion($regionID)
{
return $this->dao->select('kanban')->from(TABLE_KANBANREGION)->where('id')->eq($regionID)->fetch('kanban');
}
/**
* Get kanban group by regions.
*
@@ -1423,7 +1443,7 @@ class kanbanModel extends model
->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.`column` = t2.id')
->where('t1.kanban')->eq($executionID)
->andWhere('t1.`type`')->eq($browseType)
->fetchAll();
->fetchAll('columnType');
$cardGroup = array();
foreach($columns as $column)
@@ -1671,11 +1691,10 @@ class kanbanModel extends model
->where('deleted')->eq(0)
->beginIF(strpos($param, 'noclosed') !== false)->andWhere('status')->ne('closed')->fi()
->fetchAll('id');
if($this->app->user->admin) return array_keys($objects);
$spaceList = $objectType == 'kanban' ? $this->dao->select('id,owner,type')->from(TABLE_KANBANSPACE)->fetchAll('id') : array();
if($this->app->user->admin and $param != 'involved') return array_keys($objects);
if($this->app->user->admin and strpos('private,involved', $param) === false) return array_keys($objects);
$account = $this->app->user->account;
foreach($objects as $objectID => $object)
@@ -1825,8 +1844,8 @@ class kanbanModel extends model
/**
* Get lane pairs by region id.
*
* @param array $regionID
* @param string $type all|story|task|bug|common
* @param array|int $regionID
* @param string $type all|story|task|bug|common
* @access public
* @return array
*/
@@ -2769,7 +2788,7 @@ class kanbanModel extends model
{
if($childColumn->limit == -1)
{
dao::$errors['limit'] = $this->lang->kanban->error->parentLimitNote;
dao::$errors['limit'] = $this->lang->kanban->error->childLimitEmpty;
return false;
}
@@ -2898,6 +2917,7 @@ class kanbanModel extends model
->where('t1.kanban')->eq($kanban->id)
->andWhere('t1.deleted')->eq(0)
->andWhere('t2.deleted')->eq(0)
->andWhere('t2.type')->eq('common')
->fetch('count');
if($laneCount > 1) $printSetHeightBtn = true;
@@ -3322,6 +3342,23 @@ class kanbanModel extends model
->fetchAll('id');
}
/**
* Get column ID by lane ID.
*
* @param int $laneID
* @param string $columnType
* @access public
* @return int
*/
public function getColumnIDByLaneID($laneID, $columnType)
{
return $this->dao->select('t1.column')->from(TABLE_KANBANCELL)->alias('t1')
->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.column = t2.id')
->where('t1.lane')->eq($laneID)
->andWhere('t2.type')->eq($columnType)
->fetch('column');
}
/**
* Get lane by id.
*
@@ -3474,6 +3511,7 @@ class kanbanModel extends model
if(common::hasPriv('task', 'batchCreate') and $toTaskPriv) $menu[] = array('label' => $this->lang->execution->batchWBS, 'icon' => 'pluses', 'url' => helper::createLink('task', 'batchCreate', "executionID=$executionID&storyID=$story->id&moduleID=0&taskID=0&iframe=true", '', true), 'size' => '95%');
if(common::hasPriv('story', 'activate') and $this->story->isClickable($story, 'activate')) $menu[] = array('label' => $this->lang->story->activate, 'icon' => 'magic', 'url' => helper::createLink('story', 'activate', "storyID=$story->id", '', true));
if(common::hasPriv('execution', 'unlinkStory')) $menu[] = array('label' => $this->lang->execution->unlinkStory, 'icon' => 'unlink', 'url' => helper::createLink('execution', 'unlinkStory', "executionID=$executionID&storyID=$story->story&confirm=no", '', true));
if(common::hasPriv('story', 'delete')) $menu[] = array('label' => $this->lang->story->delete, 'icon' => 'trash', 'url' => helper::createLink('story', 'delete', "storyID=$story->id&confirm=no&from=taskkanban"));
$menus[$story->id] = $menu;
}
@@ -3492,6 +3530,7 @@ class kanbanModel extends model
if(common::hasPriv('bug', 'create') and $this->bug->isClickable($bug, 'create')) $menu[] = array('label' => $this->lang->bug->copy, 'icon' => 'copy', 'url' => helper::createLink('bug', 'create', "productID=$bug->product&branch=$bug->branch&extras=bugID=$bug->id", '', true), 'size' => '95%');
if(common::hasPriv('bug', 'activate') and $this->bug->isClickable($bug, 'activate')) $menu[] = array('label' => $this->lang->bug->activate, 'icon' => 'magic', 'url' => helper::createLink('bug', 'activate', "bugID=$bug->id", '', true));
if(common::hasPriv('story', 'create') and $bug->status != 'closed') $menu[] = array('label' => $this->lang->bug->toStory, 'icon' => 'lightbulb', 'url' => helper::createLink('story', 'create', "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id", '', true), 'size' => '95%');
if(common::hasPriv('bug', 'delete')) $menu[] = array('label' => $this->lang->bug->delete, 'icon' => 'trash', 'url' => helper::createLink('bug', 'delete', "bugID=$bug->id&confirm=no&from=taskkanban"));
$menus[$bug->id] = $menu;
}
@@ -3511,6 +3550,7 @@ class kanbanModel extends model
if(common::hasPriv('task', 'batchCreate') and $this->task->isClickable($task, 'batchCreate')) $menu[] = array('label' => $this->lang->task->children, 'icon' => 'split', 'url' => helper::createLink('task', 'batchCreate', "execution=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id", '', true), 'size' => '95%');
if(common::hasPriv('task', 'create') and $this->task->isClickable($task, 'create')) $menu[] = array('label' => $this->lang->task->copy, 'icon' => 'copy', 'url' => helper::createLink('task', 'create', "projctID=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id", '', true), 'size' => '95%');
if(common::hasPriv('task', 'cancel') and $this->task->isClickable($task, 'cancel')) $menu[] = array('label' => $this->lang->task->cancel, 'icon' => 'ban-circle', 'url' => helper::createLink('task', 'cancel', "taskID=$task->id", '', true));
if(common::hasPriv('task', 'delete')) $menu[] = array('label' => $this->lang->task->delete, 'icon' => 'trash', 'url' => helper::createLink('task', 'delete', "executionID=$task->execution&taskID=$task->id&confirm=no&from=taskkanban"));
$menus[$task->id] = $menu;
}
+2 -1
View File
@@ -39,7 +39,8 @@
<tr>
<td class='text-center'><?php echo $i + 1;?></td>
<td class='text-center'><?php echo html::input("name[$i]", '', "class='form-control title-import'");?></td>
<td style='overflow:visible'><?php echo html::select("lane[$i]", $lanePairs, key($lanePairs), "class='form-control chosen'")?></td>
<?php if($i > 0) $lanePairs['ditto'] = $this->lang->kanbancard->ditto;?>
<td style='overflow:visible'><?php echo html::select("lane[$i]", $lanePairs, $i > 0 ? 'ditto' : key($lanePairs), "class='form-control chosen'")?></td>
<td style='overflow:visible'><?php echo html::select("assignedTo[$i][]", $users, $app->user->account, "class='form-control chosen' multiple");?></td>
<td><?php echo html::input("estimate[$i]", '', "class='form-control text-center'");?></td>
<td>
+3 -7
View File
@@ -27,12 +27,11 @@
<th><?php echo $lang->kanbanspace->name;?></th>
<td><?php echo html::input('name', '', "class='form-control'");?></td>
</tr>
<?php if($type != 'private'):?>
<tr>
<tr id='ownerBox' class="<?php echo $type == 'private' ? 'hidden' : '';?>">
<th><?php echo $lang->kanbanspace->owner;?></th>
<td><?php echo html::select('owner', $users, '', "class='form-control chosen'");?></td>
</tr>
<tr>
<tr id='teamBox' class="<?php echo $type == 'private' ? 'hidden' : '';?>">
<th><?php echo $lang->kanbanspace->team;?></th>
<td colspan='2'>
<div class="input-group">
@@ -41,7 +40,6 @@
</div>
</td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->kanbanspace->desc;?></th>
<td colspan='2'>
@@ -49,12 +47,10 @@
<?php echo html::textarea('desc', '', "rows='10' class='form-control'");?>
</td>
</tr>
<?php if($type == 'private'):?>
<tr id="whitelistBox">
<tr id="whitelistBox" class="<?php echo $type == 'private' ? '' : 'hidden';?>">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, '', 'class="form-control chosen" multiple');?></td>
</tr>
<?php endif;?>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton();?>
+4 -8
View File
@@ -20,7 +20,7 @@
<table class='table table-form'>
<tr>
<th><?php echo $lang->kanbanspace->type;?></th>
<td><?php echo html::select('type', $typeList, $defaultType, "onchange='changeType($spaceID, this.value)' class='form-control chosen'");?></td>
<td><?php echo html::select('type', $typeList, $defaultType, "onchange='changeType(this.value)' class='form-control chosen'");?></td>
<td></td>
</tr>
<tr>
@@ -28,12 +28,11 @@
<td><?php echo html::input('name', $space->name, "class='form-control'");?></td>
<td></td>
</tr>
<?php if($space->type != 'private' or $type == 'cooperation' or $type == 'public'):?>
<tr>
<tr id='ownerBox' class="<?php echo $space->type == 'private' ? 'hidden' : '';?>">
<th><?php echo $lang->kanbanspace->owner;?></th>
<td><?php echo html::select('owner', $users, $space->owner, "class='form-control chosen'");?></td>
</tr>
<tr>
<tr id='teamBox' class="<?php echo $space->type == 'private' ? 'hidden' : '';?>">
<th><?php echo $lang->kanbanspace->team;?></th>
<td colspan='2'>
<div class="input-group">
@@ -42,7 +41,6 @@
</div>
</td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->kanbanspace->desc;?></th>
<td colspan='2'>
@@ -50,12 +48,10 @@
<?php echo html::textarea('desc', $space->desc, "rows='10' class='form-control'");?>
</td>
</tr>
<?php if($space->type == 'private' and $type != 'cooperation' and $type != 'public'):?>
<tr id="whitelistBox">
<tr id="whitelistBox" class="<?php echo $space->type == 'private' ? '' : 'hidden';?>">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, $space->whitelist, 'class="form-control chosen" multiple');?></td>
</tr>
<?php endif;?>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton();?>
+1 -1
View File
@@ -44,7 +44,7 @@
<td colspan='2'>
<div class="table-col">
<?php $attr = $column->limit == -1 ? 'disabled' : '';?>
<?php echo html::input('WIPCount', $column->limit != -1 ? $column->limit : '', "class='form-control' $attr onchange='wipValueChange(this.value)'");?>
<?php echo html::input('WIPCount', $column->limit != -1 ? $column->limit : '', "class='form-control' $attr");?>
</div>
<div class="table-col w-50px">
<?php echo html::hidden('limit', $column->limit, "class='form-control'");?>
+1
View File
@@ -691,6 +691,7 @@ class mailModel extends model
$nameFields = $this->config->action->objectNameFields[$objectType];
$title = zget($object, $nameFields, '');
$subject = $this->getSubject($objectType, $object, $title, $action->action);
$domain = (defined('RUN_MODE') and RUN_MODE == 'api') ? '' : zget($this->config->mail, 'domain', common::getSysURL());
if($objectType == 'review' and empty($object->auditedBy)) return;
+7
View File
@@ -72,8 +72,15 @@ class messageModel extends model
$actions = $messageSetting['mail']['setting'];
if(isset($actions[$objectType]) and in_array($actionType, $actions[$objectType]))
{
/* If it is an api call, get the request method set by the user. */
global $config;
$requestType = $config->requestType;
if(defined('RUN_MODE') and RUN_MODE == 'api') include $this->app->getConfigRoot() . 'config.php';
$moduleName = $objectType == 'case' ? 'testcase' : $objectType;
$this->loadModel('mail')->sendmail($objectID, $actionID);
if(defined('RUN_MODE') and RUN_MODE == 'api') $config->requestType = $requestType;
}
}
+2 -2
View File
@@ -170,7 +170,7 @@ class my extends control
$riskCount = $pager->recTotal;
/* Get the number of reviews assigned to me. */
$reviewList = $this->review->getUserReviews('wait', 'id_desc', $pager);
$reviewList = $this->review->getUserReviews('needreview', 'id_desc', $pager);
$reviewCount = $pager->recTotal;
/* Get the number of nc assigned to me. */
@@ -803,7 +803,7 @@ EOF;
* @access public
* @return void
*/
public function audit($browseType = 'wait', $orderBy = 't1.id_desc', $recTotal = 0, $recPerPage = 15, $pageID = 1)
public function audit($browseType = 'needreview', $orderBy = 't1.id_desc', $recTotal = 0, $recPerPage = 15, $pageID = 1)
{
$this->loadModel('datatable');
$this->loadModel('baseline');
+4 -4
View File
@@ -634,13 +634,13 @@ class product extends control
$this->lang->program->switcherMenu = $this->loadModel('program')->getSwitcher($programID, true);
$this->loadModel('user');
$poUsers = $this->user->getPairs('nodeleted|pofirst', $appendPoUsers);
$poUsers = $this->user->getPairs('nodeleted|noclosed|pofirst', $appendPoUsers);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PO"] = $this->config->user->moreLink;
$qdUsers = $this->user->getPairs('nodeleted|qdfirst', $appendQdUsers);
$qdUsers = $this->user->getPairs('nodeleted|noclosed|qdfirst', $appendQdUsers);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["QD"] = $this->config->user->moreLink;
$rdUsers = $this->user->getPairs('nodeleted|devfirst', $appendRdUsers);
$rdUsers = $this->user->getPairs('nodeleted|noclosed|devfirst', $appendRdUsers);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["RD"] = $this->config->user->moreLink;
$programs = array();
@@ -994,7 +994,7 @@ class product extends control
if($number === '')
{
return print(html::select('execution', array('' => '') + $executions, empty($executionID) ? key(array_filter($executions)) : $executionID, "class='form-control' onchange='loadExecutionRelated(this.value)'"));
return print(html::select('execution', array('' => '') + $executions, $executionID, "class='form-control' onchange='loadExecutionRelated(this.value)'"));
}
else
{
+1
View File
@@ -9,6 +9,7 @@
.dropdown-list > li > a {display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.53846154; color: #141414; white-space: nowrap;}
.dropdown-list > li > a:hover,
.dropdown-list > li > a:focus {color: #1a4f85; text-decoration: none; background-color: #ddd;}
#productStoryForm .setting {height: 25px;}
.pl-5px {padding-left: 5px;}
.popoverStage {position: relative;}
+5 -1
View File
@@ -572,6 +572,7 @@ class productModel extends model
public function create()
{
$product = fixer::input('post')
->callFunc('name', 'trim')
->setDefault('status', 'normal')
->setDefault('line', 0)
->setDefault('createdBy', $this->app->user->account)
@@ -653,6 +654,7 @@ class productModel extends model
if($oldProduct->bind) $this->config->product->edit->requiredFields = 'name';
$product = fixer::input('post')
->callFunc('name', 'trim')
->setDefault('line', 0)
->join('whitelist', ',')
->join('reviewer', ',')
@@ -1005,12 +1007,14 @@ class productModel extends model
*/
public function getProjectPairsByProduct($productID, $branch = 0, $appendProject = 0)
{
$product = $this->getById($productID);
$projects = $this->dao->select('t2.id,t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->where('t1.product')->eq($productID)
->andWhere('t2.type')->eq('project')
->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi()
->beginIF($branch !== '')->andWhere('t1.branch')->in($branch)->fi()
->beginIF($product->type != 'normal' and $branch !== '')->andWhere('t1.branch')->in($branch)->fi()
->andWhere('t2.deleted')->eq('0')
->orderBy('order_asc')
->fetchPairs('id', 'name');
+3 -3
View File
@@ -269,14 +269,14 @@ $projectIDParam = $isProjectStory ? "projectID=$projectID&" : '';
<p>
<span class="text-muted"><?php echo $storyType == 'story' ? $lang->story->noStory : $lang->story->noRequirement;?></span>
<?php if(common::canModify('product', $product) and common::hasPriv('story', 'create')):?>
<?php echo html::a($this->createLink('story', 'create', "productID={$productID}&branch={$branch}&moduleID={$moduleID}&storyID=0&projectID=$projectID&bugID=0&planID=0&todoID=0&extra=&type=$storyType"), "<i class='icon icon-plus'></i> " . $lang->story->createCommon, '', "class='btn btn-info' data-app='$from'");?>
<?php echo html::a($this->createLink('story', 'create', "productID={$productID}&branch={$branch}&moduleID={$moduleID}&storyID=0&projectID=$projectID&bugID=0&planID=0&todoID=0&extra=&type=$storyType"), "<i class='icon icon-plus'></i> " . $lang->story->create, '', "class='btn btn-info' data-app='$from'");?>
<?php endif;?>
</p>
</div>
<?php else:?>
<form class="main-table table-story skip-iframe-modal" method="post" id='productStoryForm'>
<div class="table-header fixed-right">
<nav class="btn-toolbar pull-right"></nav>
<nav class="btn-toolbar pull-right setting"></nav>
</div>
<?php
$datatableId = $this->moduleName . ucfirst($this->methodName);
@@ -510,7 +510,7 @@ $projectIDParam = $isProjectStory ? "projectID=$projectID&" : '';
{
$searchKey = $withSearch ? ('data-key="' . zget($modulesPinYin, $module, '') . '"') : '';
$actionLink = $this->createLink('story', 'batchChangeModule', "moduleID=$moduleId");
echo html::a('#', empty($module) ? '/' : $module, '', "$searchKey onclick=\"setFormAction('$actionLink', 'hiddenwin', '#productStoryForm')\"");
echo html::a('#', empty($module) ? '/' : $module, '', "$searchKey title='{$module}' onclick=\"setFormAction('$actionLink', 'hiddenwin', '#productStoryForm')\"");
}
?>
</div>
+6 -6
View File
@@ -444,14 +444,14 @@ class productplan extends control
{
if($confirm == 'no')
{
die(js::confirm($this->lang->productplan->confirmStart, $this->createLink('productplan', 'start', "planID=$planID&confirm=yes"), 'parent'));
return print(js::confirm($this->lang->productplan->confirmStart, $this->createLink('productplan', 'start', "planID=$planID&confirm=yes")));
}
else
{
$this->productplan->updateStatus($planID, 'doing', 'started');
if(dao::isError()) die(js::error(dao::getError()));
die(js::reload('parent'));
if(dao::isError()) return print(js::error(dao::getError()));
return print(js::reload('parent'));
}
}
@@ -467,14 +467,14 @@ class productplan extends control
{
if($confirm == 'no')
{
die(js::confirm($this->lang->productplan->confirmFinish, $this->createLink('productplan', 'finish', "planID=$planID&confirm=yes"), 'parent'));
return print(js::confirm($this->lang->productplan->confirmFinish, $this->createLink('productplan', 'finish', "planID=$planID&confirm=yes")));
}
else
{
$this->productplan->updateStatus($planID, 'done', 'finished');
if(dao::isError()) die(js::error(dao::getError()));
die(js::reload('parent'));
if(dao::isError()) return print(js::error(dao::getError()));
return print(js::reload('parent'));
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ $(function()
{
var $content = $(this).find('td.content');
var content = $content.find('div').html();
if(content.indexOf('<br') >= 0)
if(content.indexOf('<br') >= 0 || content.indexOf('<img') >= 0)
{
$content.append("<a href='###' class='more'><i class='icon icon-chevron-double-down'></i></a>");
}
+4 -11
View File
@@ -45,17 +45,10 @@
if($plan->parent >= 0)
{
$attr = "target='hiddenwin'";
$isOnlyBody = false;
$class = '';
if($plan->begin == $config->productplan->future or $plan->end == $config->productplan->future)
{
$class = 'iframe';
$attr = "data-toggle='modal' data-id='{$plan->id}' data-width='550px'";
$isOnlyBody = true;
}
$class = $plan->status == 'wait' ? $class : 'disabled';
common::printLink('productplan', 'start', "planID=$plan->id", "<i class='icon-play'></i>{$lang->productplan->startAB}", '', "class='btn btn-link {$class}'{$attr} title='{$lang->productplan->start}'", '', $isOnlyBody, $plan);
$attr = "target='hiddenwin'";
$class = $plan->status == 'wait' ? '' : 'disabled';
common::printLink('productplan', 'start', "planID=$plan->id", "<i class='icon-play'></i>{$lang->productplan->startAB}", '', "class='btn btn-link {$class}'{$attr} title='{$lang->productplan->start}'", '', false, $plan);
$class = $plan->status == 'doing' ? '' : 'disabled';
common::printLink('productplan', 'finish', "planID=$plan->id", "<i class='icon-checked'></i>{$lang->productplan->finishAB}", '', "class='btn btn-link {$class}' target='hiddenwin' title='{$lang->productplan->finish}'", '', false, $plan);
$class = $plan->status !== 'closed' ? 'iframe' : 'disabled';
+1 -1
View File
@@ -679,7 +679,7 @@ class programModel extends model
->setIF($this->post->delta == 999, 'end', LONG_TIME)
->setIF($this->post->future, 'budget', 0)
->setIF($this->post->budget != 0, 'budget', round($this->post->budget, 2))
->setIF(!isset($this->post->budgetUnit), 'budgetUnit', $oldProgram->budgetUnit)
->setIF(!isset($_POST['budgetUnit']), 'budgetUnit', $oldProgram->budgetUnit)
->join('whitelist', ',')
->stripTags($this->config->program->editor->edit['id'], $this->config->allowedTags)
->remove('uid,delta,future,syncPRJUnit,exchangeRate,contactListMenu')
+5 -5
View File
@@ -7,15 +7,15 @@
*/
function addItem(obj)
{
var item = $('#addItem').html().replace(/%i%/g, i);
var item = $('#addItem').html().replace(/%i%/g, itemIndex);
$(obj).closest('tr').after('<tr class="addedItem">' + item + '</tr>');
var newItem = $('#names' + i).closest('tr');
var newItem = $('#names' + itemIndex).closest('tr');
newItem.find('.form-date').datepicker();
$("#output" + i).chosen();
$("#output" + itemIndex).chosen();
$("#PM_i__chosen").remove();
$("#PM" + i).chosen();
$("#PM" + itemIndex).chosen();
$("#output_i__chosen").remove();
i ++;
itemIndex ++;
}
/**
+1 -1
View File
@@ -598,7 +598,7 @@ class programplanModel extends model
$member->account = $account;
$member->role = $this->lang->user->roleList[$this->app->user->role];
$member->join = $now;
$member->type = $data->type;
$member->type = 'execution';
$member->days = $data->days;
$member->hours = $this->config->execution->defaultWorkhours;
$this->dao->insert(TABLE_TEAM)->data($member)->exec();
+1 -1
View File
@@ -170,7 +170,7 @@
</tfoot>
</table>
</div>
<?php js::set('i', $i);?>
<?php js::set('itemIndex', $i);?>
</form>
</div>
<div>
+1
View File
@@ -133,6 +133,7 @@ $config->project->maxCheckList->scrum = array('bug', 'execution', 'build', '
$config->project->maxCheckList->waterfall = array('execution', 'design', 'doc', 'bug', 'case', 'build', 'release', 'testtask', 'review', 'build', 'researchplan', 'issue', 'risk', 'opportunity', 'auditplan', 'gapanalysis', 'meeting');
$config->project->excludedPriv['project'] = array('index', 'browse', 'kanban', 'create', 'batchEdit', 'qa', 'updateOrder', 'createGuide', 'programTitle', 'export');
$config->project->excludedPriv['execution'] = array('view', 'browse', 'edit', 'batchedit', 'start', 'activate', 'putoff', 'suspend', 'close', 'delete', 'task', 'grouptask', 'importtask', 'importplanstories', 'importBug', 'story', 'build', 'testtask', 'testcase', 'bug', 'testreport', 'burn', 'computeBurn', 'fixFirst', 'burnData', 'team', 'doc', 'dynamic', 'manageProducts', 'manageMembers', 'unlinkMember', 'linkStory', 'unlinkStory', 'batchUnlinkStory', 'updateOrder', 'taskKanban', 'printKanban', 'tree', 'treeTask', 'treeStory', 'all', 'kanbanHideCols', 'kanbanColsColor', 'export', 'storyKanban', 'storySort', 'whitelist', 'addWhitelist', 'unbindWhitelist', 'storyEstimate', 'executionkanban', 'kanban');
$config->project->excludedPriv['story'] = array('report', 'linkStory', 'batchChangeBranch', 'batchChangeModule', 'batchToTask', 'processStoryChange', 'track', 'tasks', 'bugs', 'cases');
$config->project->excludedPriv['bug'] = array('browse', 'batchChangePlan', 'batchCreate', 'batchEdit', 'batchConfirm', 'batchResolve', 'batchClose', 'batchActivate', 'report', 'batchChangeModule', 'batchChangeBranch');
$config->project->excludedPriv['testcase'] = array('browse', 'batchChangeModule', 'batchChangeBranch');
+2
View File
@@ -1342,6 +1342,8 @@ class project extends control
if(!empty($_POST))
{
$this->project->manageMembers($projectID);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('action')->create('team', $projectID, 'ManagedTeam');
$link = $this->createLink('project', 'team', "projectID=$projectID");
+2 -2
View File
@@ -62,12 +62,12 @@ function setRole(account, roleID)
*/
function addItem(obj)
{
var item = $('#addItem').html().replace(/%i%/g, i);
var item = $('#addItem').html().replace(/%i%/g, itemIndex);
var $tr = $('<tr class="addedItem">' + item + '</tr>').insertAfter($(obj).closest('tr'));
$tr.find('select:first')
.trigger('liszt:updated')
.picker({type: 'user'});
i++;
itemIndex++;
}
/**
+57 -55
View File
@@ -1,60 +1,62 @@
<?php
/* Actions. */
$lang->project->createGuide = 'Select Template';
$lang->project->index = 'Dashboard';
$lang->project->home = 'Home';
$lang->project->create = 'Create Project';
$lang->project->edit = 'Edit';
$lang->project->batchEdit = 'Batch Edit Projects';
$lang->project->view = 'Project View';
$lang->project->batchEdit = 'Batch Edit';
$lang->project->browse = 'Projects';
$lang->project->all = 'All';
$lang->project->start = 'Start';
$lang->project->finish = 'Finish';
$lang->project->suspend = 'Suspend';
$lang->project->delete = 'Delete';
$lang->project->close = 'Close';
$lang->project->activate = 'Activate';
$lang->project->group = 'Privilege Group';
$lang->project->createGroup = 'Create Group';
$lang->project->editGroup = 'Edit Group';
$lang->project->copyGroup = 'Copy Group';
$lang->project->manageView = 'Manage View';
$lang->project->managePriv = 'Manage Privilege';
$lang->project->manageMembers = 'Manage Team';
$lang->project->export = 'Export';
$lang->project->addProduct = 'Add Product';
$lang->project->manageGroupMember = 'Manage Group';
$lang->project->moduleSetting = 'List Settings';
$lang->project->moduleOpen = 'Program Name';
$lang->project->dynamic = 'Dynamic';
$lang->project->execution = 'Execution';
$lang->project->qa = 'QA';
$lang->project->bug = 'Bug List';
$lang->project->testcase = 'Case List';
$lang->project->testtask = 'Test Task';
$lang->project->build = 'Build';
$lang->project->updateOrder = 'Order';
$lang->project->sort = 'Order';
$lang->project->whitelist = 'Project Whitelist';
$lang->project->addWhitelist = 'Project Add Whitelist';
$lang->project->unbindWhitelist = 'Project Remove Whitelist';
$lang->project->manageProducts = 'Manage Products';
$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.';
$lang->project->errorNoProducts = 'At least one product is associated';
$lang->project->copyNoProject = 'There are no items available to copy.';
$lang->project->searchByName = 'Enter the project name to retrieve';
$lang->project->deleted = 'Deleted';
$lang->project->linkedProducts = "Linked {$lang->productCommon}s";
$lang->project->unlinkedProducts = "Unlinked {$lang->productCommon}s";
$lang->project->testreport = 'Test Report';
$lang->project->selectProgram = 'Program filtering';
$lang->project->teamMember = 'Team Member';
$lang->project->unlinkMember = 'Remove Member';
$lang->project->copyTeamTitle = 'Select a project team to copy';
$lang->project->createGuide = 'Select Template';
$lang->project->index = 'Dashboard';
$lang->project->home = 'Home';
$lang->project->create = 'Create Project';
$lang->project->edit = 'Edit';
$lang->project->batchEdit = 'Batch Edit Projects';
$lang->project->view = 'Project View';
$lang->project->batchEdit = 'Batch Edit';
$lang->project->browse = 'Projects';
$lang->project->all = 'All';
$lang->project->start = 'Start';
$lang->project->finish = 'Finish';
$lang->project->suspend = 'Suspend';
$lang->project->delete = 'Delete';
$lang->project->close = 'Close';
$lang->project->activate = 'Activate';
$lang->project->group = 'Privilege Group';
$lang->project->createGroup = 'Create Group';
$lang->project->editGroup = 'Edit Group';
$lang->project->copyGroup = 'Copy Group';
$lang->project->manageView = 'Manage View';
$lang->project->managePriv = 'Manage Privilege';
$lang->project->manageMembers = 'Manage Team';
$lang->project->export = 'Export';
$lang->project->addProduct = 'Add Product';
$lang->project->manageGroupMember = 'Manage Group';
$lang->project->moduleSetting = 'List Settings';
$lang->project->moduleOpen = 'Program Name';
$lang->project->dynamic = 'Dynamic';
$lang->project->execution = 'Execution';
$lang->project->qa = 'QA';
$lang->project->bug = 'Bug List';
$lang->project->testcase = 'Case List';
$lang->project->testtask = 'Test Task';
$lang->project->build = 'Build';
$lang->project->updateOrder = 'Order';
$lang->project->sort = 'Order';
$lang->project->whitelist = 'Project Whitelist';
$lang->project->addWhitelist = 'Project Add Whitelist';
$lang->project->unbindWhitelist = 'Project Remove Whitelist';
$lang->project->manageProducts = 'Manage Products';
$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.';
$lang->project->errorNoProducts = 'At least one product is associated';
$lang->project->copyNoProject = 'There are no items available to copy.';
$lang->project->searchByName = 'Enter the project name to retrieve';
$lang->project->deleted = 'Deleted';
$lang->project->linkedProducts = "Linked {$lang->productCommon}s";
$lang->project->unlinkedProducts = "Unlinked {$lang->productCommon}s";
$lang->project->testreport = 'Test Report';
$lang->project->selectProgram = 'Program filtering';
$lang->project->teamMember = 'Team Member';
$lang->project->unlinkMember = 'Remove Member';
$lang->project->copyTeamTitle = 'Select a project team to copy';
$lang->project->daysGreaterProject = 'Days cannot be greater than days of project『%s』';
$lang->project->errorHours = 'Hours/Day cannot be greater than『24』';
/* Fields. */
$lang->project->common = 'Project';
+57 -55
View File
@@ -1,60 +1,62 @@
<?php
/* Actions. */
$lang->project->createGuide = '选择项目模板';
$lang->project->index = '项目仪表盘';
$lang->project->home = '项目主页';
$lang->project->create = '创建项目';
$lang->project->edit = '编辑项目';
$lang->project->batchEdit = '批量编辑项目';
$lang->project->view = '项目概况';
$lang->project->batchEdit = '批量编辑';
$lang->project->browse = '项目列表';
$lang->project->all = '所有项目';
$lang->project->start = '启动项目';
$lang->project->finish = '完成项目';
$lang->project->suspend = '挂起项目';
$lang->project->delete = '删除项目';
$lang->project->close = '关闭项目';
$lang->project->activate = '激活项目';
$lang->project->group = '项目权限分组';
$lang->project->createGroup = '项目创建分组';
$lang->project->editGroup = '项目编辑分组';
$lang->project->copyGroup = '项目复制分组';
$lang->project->manageView = '项目维护视野';
$lang->project->managePriv = '项目维护权限';
$lang->project->manageMembers = '团队管理';
$lang->project->export = '导出';
$lang->project->addProduct = '新建产品';
$lang->project->manageGroupMember = '维护分组用户';
$lang->project->moduleSetting = '列表设置';
$lang->project->moduleOpen = '显示项目集名';
$lang->project->dynamic = '动态';
$lang->project->execution = '执行列表';
$lang->project->qa = '测试仪表盘';
$lang->project->bug = 'Bug列表';
$lang->project->testcase = '用例列表';
$lang->project->testtask = '测试单';
$lang->project->build = '版本';
$lang->project->updateOrder = '排序';
$lang->project->sort = '项目排序';
$lang->project->whitelist = '项目白名单';
$lang->project->addWhitelist = '项目添加白名单';
$lang->project->unbindWhitelist = '项目删除白名单';
$lang->project->manageProducts = '关联产品';
$lang->project->copyTitle = '请选择要复制的项目';
$lang->project->errorSameProducts = '项目不能关联多个相同的产品。';
$lang->project->errorSameBranches = '项目不能关联多个相同的分支。';
$lang->project->errorNoProducts = '最少关联一个产品';
$lang->project->copyNoProject = '没有可用的项目来复制';
$lang->project->searchByName = '输入项目名称进行检索';
$lang->project->deleted = '已删除';
$lang->project->linkedProducts = '已关联产品';
$lang->project->unlinkedProducts = '未关联';
$lang->project->testreport = '测试报告';
$lang->project->selectProgram = '项目集筛选';
$lang->project->teamMember = '团队成员';
$lang->project->unlinkMember = '移除成员';
$lang->project->copyTeamTitle = '选择一个项目团队来复制';
$lang->project->createGuide = '选择项目模板';
$lang->project->index = '项目仪表盘';
$lang->project->home = '项目主页';
$lang->project->create = '创建项目';
$lang->project->edit = '编辑项目';
$lang->project->batchEdit = '批量编辑项目';
$lang->project->view = '项目概况';
$lang->project->batchEdit = '批量编辑';
$lang->project->browse = '项目列表';
$lang->project->all = '所有项目';
$lang->project->start = '启动项目';
$lang->project->finish = '完成项目';
$lang->project->suspend = '挂起项目';
$lang->project->delete = '删除项目';
$lang->project->close = '关闭项目';
$lang->project->activate = '激活项目';
$lang->project->group = '项目权限分组';
$lang->project->createGroup = '项目创建分组';
$lang->project->editGroup = '项目编辑分组';
$lang->project->copyGroup = '项目复制分组';
$lang->project->manageView = '项目维护视野';
$lang->project->managePriv = '项目维护权限';
$lang->project->manageMembers = '团队管理';
$lang->project->export = '导出';
$lang->project->addProduct = '新建产品';
$lang->project->manageGroupMember = '维护分组用户';
$lang->project->moduleSetting = '列表设置';
$lang->project->moduleOpen = '显示项目集名';
$lang->project->dynamic = '动态';
$lang->project->execution = '执行列表';
$lang->project->qa = '测试仪表盘';
$lang->project->bug = 'Bug列表';
$lang->project->testcase = '用例列表';
$lang->project->testtask = '测试单';
$lang->project->build = '版本';
$lang->project->updateOrder = '排序';
$lang->project->sort = '项目排序';
$lang->project->whitelist = '项目白名单';
$lang->project->addWhitelist = '项目添加白名单';
$lang->project->unbindWhitelist = '项目删除白名单';
$lang->project->manageProducts = '关联产品';
$lang->project->copyTitle = '请选择要复制的项目';
$lang->project->errorSameProducts = '项目不能关联多个相同的产品。';
$lang->project->errorSameBranches = '项目不能关联多个相同的分支。';
$lang->project->errorNoProducts = '最少关联一个产品';
$lang->project->copyNoProject = '没有可用的项目来复制';
$lang->project->searchByName = '输入项目名称进行检索';
$lang->project->deleted = '已删除';
$lang->project->linkedProducts = '已关联产品';
$lang->project->unlinkedProducts = '未关联';
$lang->project->testreport = '测试报告';
$lang->project->selectProgram = '项目集筛选';
$lang->project->teamMember = '团队成员';
$lang->project->unlinkMember = '移除成员';
$lang->project->copyTeamTitle = '选择一个项目团队来复制';
$lang->project->daysGreaterProject = '可用工日不能大于项目的可用工日『%s』';
$lang->project->errorHours = '可用工时/天不能大于『24』';
/* Fields. */
$lang->project->common = '项目';
+20 -3
View File
@@ -790,6 +790,7 @@ class projectModel extends model
public function create()
{
$project = fixer::input('post')
->callFunc('name', 'trim')
->setDefault('status', 'wait')
->setIF($this->post->delta == 999, 'end', LONG_TIME)
->setIF($this->post->delta == 999, 'days', 0)
@@ -1035,6 +1036,7 @@ class projectModel extends model
$_POST['products'] = isset($_POST['products']) ? array_filter($_POST['products']) : $linkedProducts;
$project = fixer::input('post')
->callFunc('name', 'trim')
->setDefault('team', substr($this->post->name, 0, 30))
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', helper::now())
@@ -1530,20 +1532,35 @@ class projectModel extends model
$projectID = (int)$projectID;
$projectType = 'project';
$accounts = array_unique($accounts);
$limited = array_values($limited);
$oldJoin = $this->dao->select('`account`, `join`')->from(TABLE_TEAM)->where('root')->eq($projectID)->andWhere('type')->eq($projectType)->fetchPairs();
$this->dao->delete()->from(TABLE_TEAM)->where('root')->eq($projectID)->andWhere('type')->eq($projectType)->exec();
foreach($accounts as $key => $account)
{
if(empty($account)) continue;
if((int)$days[$key] > $project->days)
{
dao::$errors['message'][] = sprintf($this->lang->project->daysGreaterProject, $project->days);
return false;
}
if((float)$hours[$key] > 24)
{
dao::$errors['message'][] = $this->lang->project->errorHours;
return false;
}
}
$projectMember = array();
foreach($accounts as $key => $account)
{
if(empty($account)) continue;
$member = new stdclass();
$member = new stdclass();
$member->role = $roles[$key];
$member->days = $days[$key];
$member->hours = $hours[$key];
$member->limited = $limited[$key];
$member->limited = isset($limited[$key]) ? $limited[$key] : 'no';
$member->root = $projectID;
$member->account = $account;
+2 -2
View File
@@ -125,14 +125,14 @@
</tr>
</tfoot>
</table>
<?php js::set('i', $i);?>
<?php js::set('itemIndex', $i);?>
</form>
</div>
<div>
<?php $i = '%i%';?>
<table class='hidden'>
<tr id='addItem' class='hidden'>
<td><?php echo html::select("accounts[]", $users, '', "class='form-control' onchange='setRole(this.value, $i)'");?></td>
<td><?php echo html::select("accounts[$i]", $users, '', "class='form-control' onchange='setRole(this.value, $i)'");?></td>
<td><?php echo html::input("roles[$i]", '', "class='form-control'");?></td>
<td><?php echo html::input("days[$i]", $project->days, "class='form-control'");?></td>
<td><?php echo html::input("hours[$i]", $config->execution->defaultWorkhours, "class='form-control'");?></td>
+1 -1
View File
@@ -51,7 +51,7 @@ class projectrelease extends control
$this->view->products = $this->products;
$this->view->product = $product;
$this->view->branches = (isset($product->type) and $product->type == 'normal') ? array() : $this->loadModel('branch')->getPairs($productID);
$this->view->branches = (isset($product->type) and $product->type == 'normal') ? array() : $this->loadModel('branch')->getPairs($productID, 'active');
$this->view->branch = $branch;
$this->view->project = $this->project->getByID($projectID);
}
+1 -1
View File
@@ -27,7 +27,7 @@ function loadBranches(productID)
{
$('#branch').remove();
$('#branch_chosen').remove();
$.get(createLink('branch', 'ajaxGetBranches', "productID=" + productID), function(data)
$.get(createLink('branch', 'ajaxGetBranches', "productID=" + productID + '&oldBranch=0&params=active'), function(data)
{
var $product = $('#product');
var $inputGroup = $product.closest('.input-group');
+1
View File
@@ -9,3 +9,4 @@
#tabsNav .nav-tabs > li.active + li.pull-right {margin-right: 0;}
ol, ul {padding-left: 20px;}
.body-modal #mainContent {min-height: 240px;}
.c-id {width: 110px;}
+2 -2
View File
@@ -77,7 +77,7 @@
<?php $vars = "releaseID={$release->id}&type=story&link=$link&param=$param&orderBy=%s";?>
<thead>
<tr>
<th class='c-id text-left w-110px'>
<th class='c-id text-left'>
<?php if(($canBatchUnlink or $canBatchClose) and $canBeChanged):?>
<div class="checkbox-primary check-all" title="<?php echo $lang->selectAll?>">
<label></label>
@@ -172,7 +172,7 @@
<?php $vars = "releaseID={$release->id}&type=bug&link=$link&param=$param&orderBy=%s";?>
<thead>
<tr class='text-center'>
<th class='c-id text-left w-110px'>
<th class='c-id text-left'>
<?php if($canBatchUnlink and $canBeChanged):?>
<div class="checkbox-primary check-all" title="<?php echo $lang->selectAll?>">
<label></label>
+1
View File
@@ -151,6 +151,7 @@ class repo extends control
$this->view->products = $products;
$this->view->productID = $productID;
$this->view->gitlabHosts = $this->loadModel('gitlab')->getPairs();
$this->view->objectID = $objectID;
$this->display();
}
+1 -1
View File
@@ -25,7 +25,7 @@
<table class='table table-form'>
<tr>
<th><?php echo $lang->repo->product; ?></th>
<td class='required'><?php echo html::select('product[]', $products, $productID, "class='form-control chosen' multiple"); ?></td>
<td class='required'><?php echo html::select('product[]', $products, empty($objectID) ? '' : array_keys($products), "class='form-control chosen' multiple"); ?></td>
</tr>
<tr>
<th class='thWidth'><?php echo $lang->repo->type; ?></th>
+1 -1
View File
@@ -31,7 +31,7 @@
<td><?php echo $i;?></td>
<td><?php echo html::input("name[$i]", '', "class='form-control'");?></td>
<td><?php echo html::input("percent[$i]", '', "class='form-control'");?></td>
<td><?php echo html::select("type[$i]", $lang->stage->typeList, '', "class='form-control chosen'");?></td>
<td><?php echo html::select("type[$i]", array('' => '') + $lang->stage->typeList, '', "class='form-control chosen'");?></td>
</tr>
<?php endfor;?>
<tr>
+51 -10
View File
@@ -46,6 +46,10 @@ class story extends control
*/
public function create($productID = 0, $branch = 0, $moduleID = 0, $storyID = 0, $objectID = 0, $bugID = 0, $planID = 0, $todoID = 0, $extra = '', $type = 'story')
{
/* Whether there is a object to transfer story, for example feedback. */
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
if($productID == 0 and $objectID == 0) $this->locate($this->createLink('product', 'create'));
/* Get product id according to the project id when lite vision todo transfer story */
@@ -71,11 +75,23 @@ class story extends control
{
$objectID = empty($objectID) ? $this->session->execution : $objectID;
$this->execution->setMenu($objectID);
$execution = $this->dao->findById((int)$objectID)->from(TABLE_EXECUTION)->fetch();
if($execution->type == 'kanban')
{
$this->loadModel('kanban');
$regionPairs = $this->kanban->getRegionPairs($execution->id, 0, 'execution');
$regionID = isset($output['regionID']) ? $output['regionID'] : key($regionPairs);
$lanePairs = $this->kanban->getLanePairsByRegion($regionID, 'story');
$laneID = isset($output['laneID']) ? $output['laneID'] : key($lanePairs);
$this->view->executionType = $execution->type;
$this->view->regionID = $regionID;
$this->view->laneID = $laneID;
$this->view->regionPairs = $regionPairs;
$this->view->lanePairs = $lanePairs;
}
}
/* Whether there is a object to transfer story, for example feedback. */
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
foreach($output as $paramKey => $paramValue)
{
if(isset($this->config->story->fromObjects[$paramKey]))
@@ -401,6 +417,20 @@ class story extends control
}
else
{
if($execution->type == 'kanban')
{
$this->loadModel('kanban');
$regionPairs = $this->kanban->getRegionPairs($executionID, 0, 'execution');
$regionID = isset($output['regionID']) ? $output['regionID'] : key($regionPairs);
$lanePairs = $this->kanban->getLanePairsByRegion($regionID, 'story');
$laneID = isset($output['laneID']) ? $output['laneID'] : key($lanePairs);
$this->view->regionID = $regionID;
$this->view->laneID = $laneID;
$this->view->regionPairs = $regionPairs;
$this->view->lanePairs = $lanePairs;
}
$this->execution->setMenu($executionID);
$this->app->rawModule = 'execution';
$this->lang->navGroup->story = 'execution';
@@ -433,12 +463,18 @@ class story extends control
$stories = array();
foreach($mails as $mail) $stories[] = $mail->storyID;
$lanes = array();
if(isset($_POST['lanes']))
{
foreach($mails as $i => $mail) $lanes[$mail->storyID] = $_POST['lanes'][$i];
}
/* Project or execution linked stories. */
if($executionID)
{
$products = array();
foreach($mails as $story) $products[$story->storyID] = $productID;
$this->execution->linkStory($executionID, $stories, $products, $extra);
$this->execution->linkStory($executionID, $stories, $products, $extra, $lanes);
if($executionID != $this->session->project) $this->execution->linkStory($this->session->project, $stories, $products);
}
@@ -725,8 +761,6 @@ class story extends control
$productReviewers = $product->reviewer;
if(!$productReviewers and $product->acl != 'open') $productReviewers = $this->loadModel('user')->getProductViewListUsers($product, '', '', '');
$this->story->replaceURLang($story->type);
/* Process the module when branch products are switched to normal products. */
if($product->type == 'normal' and !empty($story->branch)) $this->view->moduleOptionMenu += $this->tree->getModulesName($story->module);
@@ -1120,7 +1154,9 @@ class story extends control
}
$reviewers = $this->story->getReviewerPairs($storyID, $story->version);
$checkSuperReviewed = strpos(',' . trim(zget($this->config->story, 'superReviewers', ''), ',') . ',', ',' . trim($story->reviewedBy, ',') . ',');
$reviewedBy = trim($story->reviewedBy, ',');
$superReviewers = trim(zget($this->config->story, 'superReviewers', ''), ',');
$checkSuperReviewed = empty($reviewedBy) ? false : strpos(",$superReviewers,", ",$reviewedBy,");
$this->executeHooks($storyID);
@@ -1162,17 +1198,18 @@ class story extends control
*
* @param int $storyID
* @param string $confirm yes|no
* @param string $from taskkanban
* @access public
* @return void
*/
public function delete($storyID, $confirm = 'no')
public function delete($storyID, $confirm = 'no', $from = '')
{
$story = $this->story->getById($storyID);
if($story->parent < 0) return print(js::alert($this->lang->story->cannotDeleteParent));
if($confirm == 'no')
{
return print(js::confirm($this->lang->story->confirmDelete, $this->createLink('story', 'delete', "story=$storyID&confirm=yes"), ''));
return print(js::confirm($this->lang->story->confirmDelete, $this->createLink('story', 'delete', "story=$storyID&confirm=yes&from=$from"), ''));
}
else
{
@@ -1187,6 +1224,10 @@ class story extends control
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
if($this->app->tab == 'execution' and $from == 'taskkanban') return print(js::reload('parent'));
if(isonlybody()) return print(js::reload('parent.parent'));
$locateLink = $this->session->storyList ? $this->session->storyList : $this->createLink('product', 'browse', "productID={$story->product}");
return print(js::locate($locateLink, 'parent'));
}
@@ -1345,7 +1386,7 @@ class story extends control
}
else
{
return print(js::closeModal('parent.parent', 'this'));
return print(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
}
}
+21
View File
@@ -115,3 +115,24 @@ function toggleCheck(obj)
$ditto.closest('.input-group-addon').hide();
}
}
/**
* Set lane.
*
* @param int $regionID
* @param int $num
* @access public
* @return void
*/
function setLane(regionID, num)
{
laneLink = createLink('kanban', 'ajaxGetLanes', 'regionID=' + regionID + '&type=story&field=lanes&i=' + num);
$.get(laneLink, function(lanes)
{
if(!lanes) lanes = '<select id="lanes' + num + '" name="lanes[' + num + ']" class="form-control"></select>';
$('#lanes' + num).replaceWith(lanes);
$("#lanes" + num + "_chosen").remove();
$("#lanes" + num).next('.picker').remove();
$("#lanes" + num).chosen();
});
}
+20
View File
@@ -48,6 +48,26 @@ function refreshPlan()
$('a.refresh').click();
}
/**
* Set lane.
*
* @param int $regionID
* @access public
* @return void
*/
function setLane(regionID)
{
laneLink = createLink('kanban', 'ajaxGetLanes', 'regionID=' + regionID + '&type=story&field=lane');
$.get(laneLink, function(lane)
{
if(!lane) lane = "<select id='lane' name='lane' class='form-control'></select>";
$('#lane').replaceWith(lane);
$('#lane' + "_chosen").remove();
$('#lane').next('.picker').remove();
$('#lane').chosen();
});
}
$(window).unload(function(){
if(blockID) window.parent.refreshBlock($('#block' + blockID));
});
+26 -8
View File
@@ -44,7 +44,7 @@ class storyModel extends model
if($setImgSize) $story->spec = $this->file->setImgSize($story->spec);
if($setImgSize) $story->verify = $this->file->setImgSize($story->verify);
$story->executions = $this->dao->select('t1.project, t2.name, t2.status')->from(TABLE_PROJECTSTORY)->alias('t1')
$story->executions = $this->dao->select('t1.project, t2.name, t2.status, t2.type')->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')
@@ -220,7 +220,7 @@ class storyModel extends model
->setIF($bugID > 0, 'fromBug', $bugID)
->join('mailto', ',')
->stripTags($this->config->story->editor->create['id'], $this->config->allowedTags)
->remove('files,labels,reviewer,needNotReview,newStory,uid,contactListMenu,URS')
->remove('files,labels,reviewer,needNotReview,newStory,uid,contactListMenu,URS,region,lane')
->get();
/* Check repeat story. */
@@ -289,8 +289,15 @@ class storyModel extends model
if($this->config->systemMode == 'new' and $executionID != $this->session->project) $this->linkStory($this->session->project, $this->post->product, $storyID);
$this->loadModel('kanban');
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($executionID, $output['laneID'], $output['columnID'], 'story', $storyID);
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($executionID, 'story');
$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))
@@ -1273,7 +1280,6 @@ class storyModel extends model
*/
public function review($storyID)
{
if($this->post->result == false) return print(js::alert($this->lang->story->mustChooseResult));
if($this->post->result == 'revert' and $this->post->preVersion == false) return print(js::alert($this->lang->story->mustChoosePreVersion));
if(strpos($this->config->story->review->requiredFields, 'comment') !== false and !$this->post->comment)
@@ -1282,6 +1288,12 @@ class storyModel extends model
return false;
}
if($this->post->result == false)
{
dao::$errors[] = $this->lang->story->mustChooseResult;
return false;
}
$oldStory = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch();
$now = helper::now();
$date = helper::today();
@@ -1533,8 +1545,11 @@ class storyModel extends model
->where('id')->eq($storyID)->exec();
/* Update parent story status and stage. */
if($oldStory->parent > 0) $this->updateParentStatus($storyID, $oldStory->parent);
$this->setStage($oldStory->parent);
if($oldStory->parent > 0)
{
$this->updateParentStatus($storyID, $oldStory->parent);
$this->setStage($oldStory->parent);
}
if(!dao::isError()) $this->loadModel('score')->create('story', 'close', $storyID);
return common::createChanges($oldStory, $story);
}
@@ -1982,6 +1997,9 @@ class storyModel extends model
->remove('comment')
->get();
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->where('id')->eq($storyID)->exec();
if($this->post->status == 'active') $this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->exec();
$this->setStage($storyID);
/* Update parent story status. */
@@ -2849,7 +2867,7 @@ class storyModel extends model
->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi()
->beginIF($status == 'unclosed')->andWhere('t2.status')->ne('closed')->fi()
->orderBy('t1.`order` desc')
->fetchAll();
->fetchAll('id');
return empty($stories) ? array() : $this->formatStories($stories, $type);
}
+12 -3
View File
@@ -48,6 +48,10 @@
<?php if(!isonlybody()):?>
<th class='c-plan<?php echo zget($visibleFields, 'plan', ' hidden') . zget($requiredFields, 'plan', '', ' required');?>'><?php echo $lang->story->plan;?></th>
<?php endif;?>
<?php if(isset($execution) and $execution->type == 'kanban'):?>
<th class='c-branch'><?php echo $lang->kanbancard->region;?></th>
<th class='c-branch'><?php echo $lang->kanbancard->lane;?></th>
<?php endif;?>
<th class='c-name required has-btn'><?php echo $lang->story->title;?></th>
<th class='c-spec<?php echo zget($visibleFields, 'spec', ' hidden') . zget($requiredFields, 'spec', '', ' required');?>'><?php echo $lang->story->spec;?></th>
<th class='c-source<?php echo zget($visibleFields, 'source', ' hidden') . zget($requiredFields, 'source', '', ' required');?>'><?php echo $lang->story->source;?></th>
@@ -71,6 +75,10 @@
<?php if(!isonlybody()):?>
<td class='text-left<?php echo zget($visibleFields, 'plan', ' hidden')?>' style='overflow:visible'><?php echo html::select('plan[$id]', $plans, $planID, "class='form-control chosen'");?></td>
<?php endif;?>
<?php if(isset($execution) and $execution->type == 'kanban'):?>
<td class='text-left'><?php echo html::select('regions[$id]', $regionPairs, $regionID, "class='form-control chosen' onchange='setLane(this.value, \$id)'");?>
<td class='text-left'><?php echo html::select('lanes[$id]', $lanePairs, $laneID, "class='form-control chosen'");?>
<?php endif;?>
<td style='overflow:visible'>
<div class="input-group">
<div class="input-control has-icon-right">
@@ -125,15 +133,16 @@ $(function()
$('#batchCreateForm').batchActionForm(
{
idEnd: <?php echo max((empty($titles) ? 0 : count($titles)), 9)?>,
idStart: 1,
idEnd: <?php echo max((empty($titles) ? 1 : count($titles)), 10)?>,
rowCreator: function($row, index)
{
$row.find('select.chosen,select.picker-select').each(function()
{
var $select = $(this);
if($select.hasClass('picker-select')) $select.parent().find('.picker').remove();
if(index == 0) $select.find("option[value='ditto']").remove();
if(index > 0) $select.val('ditto');
if(index == 1) $select.find("option[value='ditto']").remove();
if(index > 1) $select.val('ditto');
if($select.attr('id').indexOf('branch') >= 0) $select.val('<?php echo $branch;?>')
$select.chosen();
setTimeout(function()
+14 -2
View File
@@ -155,9 +155,9 @@
<div class='input-group' id='moduleIdBox'>
<div class="input-group-addon"><?php echo $lang->story->parent;?></div>
<?php echo html::select('parent', $stories, '', "class='form-control chosen'");?>
</div>
</div>
</td>
</tr>
</tr>
<?php else:?>
<tr>
<th><?php echo $lang->story->parent;?></th>
@@ -165,6 +165,18 @@
</tr>
<?php endif;?>
<?php endif;?>
<?php if (isset($executionType) and $executionType == 'kanban'):?>
<tr>
<th><?php echo $lang->kanbancard->region;?>
<td colspan="2"><?php echo html::select('region', $regionPairs, $regionID, "onchange='setLane(this.value)' class='form-control chosen'");?></td>
<td colspan="2">
<div class='input-group'>
<div class="input-group-addon"><?php echo $lang->kanbancard->lane;?></div>
<?php echo html::select('lane', $lanePairs, $laneID, "class='form-control chosen'");?>
</div>
</td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->story->title;?></th>
<td colspan="4">
+1 -1
View File
@@ -18,7 +18,7 @@
<tr>
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'>
<?php $color = empty($object->color) ? '#333' : $object->color;?>
<?php echo html::a(zget($this->config->mail, 'domain', common::getSysURL()) . helper::createLink('story', 'view', "storyID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
<?php echo html::a($domain . helper::createLink('story', 'view', "storyID=$object->id", 'html'), $mailTitle, '', "style='color: {$color}; text-decoration: underline;'");?>
</td>
</tr>
</table>
+3 -2
View File
@@ -244,7 +244,7 @@
echo "<div class='divider'></div>";
common::printIcon('story', 'edit', "storyID=$story->id", $story);
common::printIcon('story', 'create', "productID=$story->product&branch=$story->branch&moduleID=$story->module&storyID=$story->id&executionID=0&bugID=0&planID=0&todoID=0&extra=&type=$story->type", $story, 'button', 'copy', '', '', '', "data-width='1050'");
common::printIcon('story', 'delete', "storyID=$story->id", $story, 'button', 'trash', 'hiddenwin');
common::printIcon('story', 'delete', "storyID=$story->id", $story, 'button', 'trash', 'hiddenwin', 'showinonlybody',true);
?>
<?php endif;?>
</div>
@@ -492,7 +492,8 @@
{
foreach($story->executions as $executionID => $execution)
{
echo "<li title='$execution->name'>" . html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name, '', "class='text-muted'") . '</li>';
$execName = ($execution->type == 'kanban' and isonlybody()) ? $execution->name : html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name, '', "class='text-muted'");
echo "<li title='$execution->name'>" . $execName . '</li>';
}
}
?>
+1 -1
View File
@@ -35,7 +35,7 @@ $config->task->exportFields = '
openedBy, openedDate, assignedTo, assignedDate,
finishedBy, finishedDate, canceledBy, canceledDate,
closedBy, closedDate, closedReason,
lastEditedBy, lastEditedDate,files
lastEditedBy, lastEditedDate, avtivatedDate, files
';
$config->task->customCreateFields = 'story,estStarted,deadline,mailto,pri,estimate';
+49 -13
View File
@@ -86,6 +86,20 @@ class task extends control
$execution = $this->execution->getById($executionID);
$taskLink = $this->createLink('execution', 'browse', "executionID=$executionID&tab=task");
$this->loadModel('kanban');
if($execution->type == 'kanban')
{
$regionPairs = $this->kanban->getRegionPairs($execution->id, 0, 'execution');
$regionID = isset($output['regionID']) ? $output['regionID'] : key($regionPairs);
$lanePairs = $this->kanban->getLanePairsByRegion($regionID, 'task');
$laneID = isset($output['laneID']) ? $output['laneID'] : key($lanePairs);
$this->view->regionID = $regionID;
$this->view->laneID = $laneID;
$this->view->regionPairs = $regionPairs;
$this->view->lanePairs = $lanePairs;
}
/* Set menu. */
$this->execution->setMenu($execution->id);
@@ -129,11 +143,19 @@ class task extends control
$this->action->create('task', $taskID, 'Opened', '');
}
$this->loadModel('kanban');
/* Create task in kanban. */
$kanbanID = $execution->type == 'kanban' ? $executionID : $_POST['execution'];
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($kanbanID, $output['laneID'], $output['columnID'], 'task', $taskID);
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($kanbanID, 'task');
$laneID = isset($output['laneID']) ? $output['laneID'] : 0;
if(!empty($_POST['lane'])) $laneID = $_POST['lane'];
$columnID = $this->kanban->getColumnIDByLaneID($laneID, 'wait');
if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($kanbanID, $laneID, $columnID, 'task', $taskID);
if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($kanbanID, 'task');
/* To do status. */
if($todoID > 0)
{
$this->dao->update(TABLE_TODO)->set('status')->eq('done')->where('id')->eq($todoID)->exec();
@@ -210,14 +232,7 @@ class task extends control
if(!isset($moduleOptionMenu[$task->module])) $task->module = 0;
}
/* Fix bug #2737. When moduleID is not story module. */
$moduleIdList = array();
if($task->module)
{
$moduleID = $this->tree->getStoryModule($task->module);
$moduleIdList = $this->tree->getAllChildID($moduleID);
}
$stories = $this->story->getExecutionStoryPairs($executionID, 0, 'all', $moduleIdList, 'full', 'unclosed');
$stories = $this->story->getExecutionStoryPairs($executionID, 0, 'all', '', 'full', 'unclosed');
if($this->config->vision == 'lite') $stories = $stories + array('', '');
@@ -364,6 +379,23 @@ class task extends control
$showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : '';
$modules = $this->loadModel('tree')->getTaskOptionMenu($executionID, 0, 0, $showAllModule ? 'allModule' : '');
if($execution->type == 'kanban')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$this->loadModel('kanban');
$regionPairs = $this->kanban->getRegionPairs($executionID, 0, 'execution');
$regionID = isset($output['regionID']) ? $output['regionID'] : key($regionPairs);
$lanePairs = $this->kanban->getLanePairsByRegion($regionID, 'task');
$laneID = isset($output['laneID']) ? $output['laneID'] : key($lanePairs);
$this->view->regionID = $regionID;
$this->view->laneID = $laneID;
$this->view->regionPairs = $regionPairs;
$this->view->lanePairs = $lanePairs;
}
$title = $execution->name . $this->lang->colon . $this->lang->task->batchCreate;
$position[] = html::a($taskLink, $execution->name);
$position[] = $this->lang->task->common;
@@ -1560,17 +1592,18 @@ class task extends control
* @param int $executionID
* @param int $taskID
* @param string $confirm yes|no
* @param string $from taskkanban
* @access public
* @return void
*/
public function delete($executionID, $taskID, $confirm = 'no')
public function delete($executionID, $taskID, $confirm = 'no', $from = '')
{
$task = $this->task->getById($taskID);
if($task->parent < 0) return print(js::alert($this->lang->task->cannotDeleteParent));
if($confirm == 'no')
{
return print(js::confirm($this->lang->task->confirmDelete, inlink('delete', "executionID=$executionID&taskID=$taskID&confirm=yes")));
return print(js::confirm($this->lang->task->confirmDelete, inlink('delete', "executionID=$executionID&taskID=$taskID&confirm=yes&from=$from")));
}
else
{
@@ -1585,6 +1618,9 @@ class task extends control
$this->executeHooks($taskID);
if(isonlybody()) return print(js::reload('parent.parent'));
if($from == 'taskkanban') return print(js::reload('parent'));
$locateLink = $this->session->taskList ? $this->session->taskList : $this->createLink('execution', 'task', "executionID={$task->execution}");
return print(js::locate($locateLink, 'parent'));
}
+4
View File
@@ -19,3 +19,7 @@
.chosen-results > li.has-new-test {position: relative; color: #388E3C;}
.chosen-results > li.has-test.highlighted,
.chosen-results > li.has-new-test.highlighted {color: #fff;}
.c-multipleTask {width: 110px;}
.c-modulel {width: 110px;}
.c-selectStory {width: 120px;}
+3
View File
@@ -0,0 +1,3 @@
.c-multipleTask {width: 120px;}
.c-modulel {width: 110px;}
.c-selectStory {width: 150px;}
+22 -1
View File
@@ -87,7 +87,7 @@ function copyStoryTitle(num)
function setStoryRelated(num)
{
var storyID = $('#story' + num).val();
if(storyID && storyID != 'ditto')
if(storyID != 0 && storyID != 'ditto')
{
var link = createLink('story', 'ajaxGetInfo', 'storyID=' + storyID);
$.getJSON(link, function(storyInfo)
@@ -184,6 +184,27 @@ function markStoryTask()
});
}
/**
* Set lane.
*
* @param int $regionID
* @param int $num
* @access public
* @return void
*/
function setLane(regionID, num)
{
laneLink = createLink('kanban', 'ajaxGetLanes', 'regionID=' + regionID + '&type=task&field=lanes&i=' + num);
$.get(laneLink, function(lanes)
{
if(!lanes) lanes = '<select id="lanes' + num + '" name="lanes[' + num + ']" class="form-control"></select>';
$('#lanes' + num).replaceWith(lanes);
$("#lanes" + num + "_chosen").remove();
$("#lanes" + num).next('.picker').remove();
$("#lanes" + num).chosen();
});
}
$(document).on('chosen:showing_dropdown', 'select[name^="story"],.chosen-with-drop', function()
{
var select = $(this).closest('td').find('select');

Some files were not shown because too many files have changed in this diff Show More