Merge branch 'sprint/168' into 'sprint/168_dingna_enlang'
# Conflicts: # module/kanban/lang/zh-cn.php
This commit is contained in:
@@ -843,6 +843,7 @@ class bug extends control
|
||||
}
|
||||
}
|
||||
}
|
||||
if(isonlybody()) die(js::reload('parent.parent'));
|
||||
die(js::locate($this->createLink('bug', 'view', "bugID=$bugID"), 'parent'));
|
||||
}
|
||||
|
||||
|
||||
@@ -1831,7 +1831,7 @@ class execution extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function kanban($executionID, $type = 'all', $orderBy = 'order_asc', $groupBy = 'default')
|
||||
public function kanban($executionID, $browseType = 'all', $orderBy = 'order_asc', $groupBy = 'default')
|
||||
{
|
||||
/* Save to session. */
|
||||
$uri = $this->app->getURI(true);
|
||||
@@ -1844,7 +1844,7 @@ class execution extends control
|
||||
$kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType);
|
||||
if(empty($kanbanGroup))
|
||||
{
|
||||
$this->kanban->createLanes($executionID);
|
||||
$this->kanban->createLanes($executionID, $browseType, $groupBy);
|
||||
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType);
|
||||
}
|
||||
|
||||
@@ -1875,10 +1875,9 @@ class execution extends control
|
||||
$this->view->executionID = $executionID;
|
||||
$this->view->productID = $productID;
|
||||
$this->view->allPlans = $allPlans;
|
||||
$this->view->browseType = '';
|
||||
$this->view->browseType = $browseType;
|
||||
$this->view->kanbanGroup = $kanbanGroup;
|
||||
$this->view->execution = $execution;
|
||||
$this->view->type = $type;
|
||||
$this->view->groupBy = $groupBy;
|
||||
$this->view->canBeChanged = $canBeChanged;
|
||||
|
||||
|
||||
@@ -556,14 +556,17 @@ function createStoryMenu(options)
|
||||
{
|
||||
var $card = options.$trigger.closest('.kanban-item');
|
||||
var story = $card.data('item');
|
||||
var items =
|
||||
[
|
||||
{label: '编辑需求', icon: 'edit', url: $.createLink('story', 'edit', 'storyID=' + story.id), className: 'iframe'},
|
||||
{label: '变更需求', icon: 'change', url: $.createLink('story', 'change', 'storyID=' + story.id), className: 'iframe'},
|
||||
{label: '移除需求', icon: 'unlink', url: $.createLink('story', 'unlink', 'storyID=' + story.id), className: 'iframe'},
|
||||
{label: '分解任务', icon: 'plus', url: $.createLink('task', 'create', 'storyID=' + story.id), className: 'iframe'},
|
||||
{label: '批量分解', icon: 'pluses', url: $.createLink('task', 'batchCreate', 'storyID=' + story.id), className: 'iframe'},
|
||||
];
|
||||
|
||||
var items = [];
|
||||
$.each(story.menus, function()
|
||||
{
|
||||
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'}};
|
||||
items.push(item);
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -575,13 +578,16 @@ function createBugMenu(options)
|
||||
{
|
||||
var $card = options.$trigger.closest('.kanban-item');
|
||||
var bug = $card.data('item');
|
||||
var items =
|
||||
[
|
||||
{label: '编辑Bug', icon: 'edit', url: $.createLink('bug', 'edit', 'bugID=' + bug.id), className: 'iframe'},
|
||||
{label: '确认Bug', icon: 'ok', url: $.createLink('bug', 'confirm', 'bugID=' + bug.id), className: 'iframe'},
|
||||
{label: '复制Bug', icon: 'copy', url: $.createLink('bug', 'copy', 'bugID=' + bug.id), className: 'iframe'},
|
||||
{label: '转软件需求', icon: 'lightbulb', url: $.createLink('story', 'create', 'bugID=' + bug.id), className: 'iframe'},
|
||||
];
|
||||
|
||||
var items = [];
|
||||
$.each(bug.menus, function()
|
||||
{
|
||||
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;
|
||||
|
||||
items.push(item);
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -593,13 +599,16 @@ function createTaskMenu(options)
|
||||
{
|
||||
var $card = options.$trigger.closest('.kanban-item');
|
||||
var task = $card.data('item');
|
||||
var items =
|
||||
[
|
||||
{label: '编辑任务', icon: 'edit', url: $.createLink('task', 'edit', 'taskID=' + task.id), className: 'iframe'},
|
||||
{label: '拆分子任务', icon: 'plus', url: $.createLink('task', 'create', 'taskID=' + task.id), className: 'iframe'},
|
||||
{label: '复制任务', icon: 'copy', url: $.createLink('task', 'copy', 'taskID=' + task.id), className: 'iframe'},
|
||||
{label: '取消任务', icon: 'cancel', url: $.createLink('task', 'cancel', 'taskID=' + task.id), className: 'iframe'},
|
||||
];
|
||||
|
||||
var items = [];
|
||||
$.each(task.menus, function()
|
||||
{
|
||||
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;
|
||||
|
||||
items.push(item);
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<div class='btn-toolbar pull-left'>
|
||||
<div class="input-control space c-type">
|
||||
<i class="icon icon-list-all"></i>
|
||||
<?php echo html::select('type', $lang->kanban->type, $type, 'class="form-control chosen" data-max_drop_width="215"');?>
|
||||
<?php echo html::select('type', $lang->kanban->type, $browseType, 'class="form-control chosen" data-max_drop_width="215"');?>
|
||||
</div>
|
||||
<div class="input-control space c-group">
|
||||
<?php echo html::select('group', $lang->kanban->group->$type, $groupBy, 'class="form-control chosen" data-max_drop_width="215"');?>
|
||||
<?php echo html::select('group', $lang->kanban->group->$browseType, $groupBy, 'class="form-control chosen" data-max_drop_width="215"');?>
|
||||
</div>
|
||||
</div>
|
||||
<div class='btn-toolbar pull-right'>
|
||||
|
||||
@@ -50,6 +50,7 @@ $lang->kanban->columnColor = 'Column Color';
|
||||
$lang->kanban->noColumnUniqueName = 'The Kanban column name already exists.';
|
||||
|
||||
$lang->kanban->error = new stdclass();
|
||||
$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.';
|
||||
|
||||
|
||||
@@ -34,19 +34,25 @@ $lang->kanban->group->bug['module'] = "Bug模块";
|
||||
$lang->kanban->group->bug['severity'] = "Bug严重程度";
|
||||
$lang->kanban->group->bug['assignedTo'] = "指派人员";
|
||||
|
||||
$lang->kanban->WIP = 'WIP';
|
||||
$lang->kanban->setWIP = '在制品设置';
|
||||
$lang->kanban->WIPStatus = '在制品状态';
|
||||
$lang->kanban->WIPStage = '在制品阶段';
|
||||
$lang->kanban->WIPType = '在制品类型';
|
||||
$lang->kanban->WIPCount = '在制品数量';
|
||||
$lang->kanban->noLimit = '不限制∞';
|
||||
$lang->kanban->setLane = '泳道设置';
|
||||
$lang->kanban->laneName = '泳道名称';
|
||||
$lang->kanban->laneColor = '泳道颜色';
|
||||
$lang->kanban->setColumn = '看板列设置';
|
||||
$lang->kanban->columnName = '看板列名称';
|
||||
$lang->kanban->columnColor = '看板列颜色';
|
||||
$lang->kanban->WIP = 'WIP';
|
||||
$lang->kanban->setWIP = '在制品设置';
|
||||
$lang->kanban->WIPStatus = '在制品状态';
|
||||
$lang->kanban->WIPStage = '在制品阶段';
|
||||
$lang->kanban->WIPType = '在制品类型';
|
||||
$lang->kanban->WIPCount = '在制品数量';
|
||||
$lang->kanban->noLimit = '不限制∞';
|
||||
$lang->kanban->setLane = '泳道设置';
|
||||
$lang->kanban->laneName = '泳道名称';
|
||||
$lang->kanban->laneColor = '泳道颜色';
|
||||
$lang->kanban->setLaneColumn = '看板列设置';
|
||||
$lang->kanban->columnName = '看板列名称';
|
||||
$lang->kanban->columnColor = '看板列颜色';
|
||||
$lang->kanban->noColumnUniqueName = '看板列名称已存在';
|
||||
|
||||
$lang->kanban->error = new stdclass();
|
||||
$lang->kanban->error->mustBeInt = '在制品数量必须是正整数。';
|
||||
$lang->kanban->error->parentLimitNote = '父列的在制品数量不能小于子列在制品数量之和';
|
||||
$lang->kanban->error->childLimitNote = '子列在制品数量之和不能大于父列的在制品数量';
|
||||
|
||||
$this->lang->kanban->laneTypeList = array();
|
||||
$this->lang->kanban->laneTypeList['story'] = $lang->SRCommon;
|
||||
|
||||
+92
-6
@@ -22,12 +22,12 @@ class kanbanModel extends model
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getExecutionKanban($executionID, $objectType = 'all', $groupBy = 'default')
|
||||
public function getExecutionKanban($executionID, $browseType = 'all', $groupBy = 'default')
|
||||
{
|
||||
$lanes = $this->dao->select('*')->from(TABLE_KANBANLANE)
|
||||
->where('execution')->eq($executionID)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->beginIF($objectType != 'all')->andWhere('type')->eq($objectType)
|
||||
->beginIF($browseType != 'all')->andWhere('type')->eq($browseType)
|
||||
->beginIF($groupBy != 'default')->andWhere('extra')->eq($groupBy)
|
||||
->fetchAll('id');
|
||||
|
||||
@@ -52,6 +52,11 @@ class kanbanModel extends model
|
||||
if($browseType == 'all' or $browseType == 'bug') $objectGroup['bug'] = $this->loadModel('bug')->getExecutionBugs($executionID);
|
||||
if($browseType == 'all' or $browseType == 'task') $objectGroup['task'] = $this->loadModel('execution')->getKanbanTasks($executionID, "id");
|
||||
|
||||
/* Get objects cards menus. */
|
||||
if($browseType == 'all' or $browseType == 'story') $storyCardMenu = $this->getKanbanCardMenu($executionID, $objectGroup['story'], 'story');
|
||||
if($browseType == 'all' or $browseType == 'bug') $bugCardMenu = $this->getKanbanCardMenu($executionID, $objectGroup['bug'], 'bug');
|
||||
if($browseType == 'all' or $browseType == 'task') $taskCardMenu = $this->getKanbanCardMenu($executionID, $objectGroup['task'], 'task');
|
||||
|
||||
/* Build kanban group data. */
|
||||
$kanbanGroup = array();
|
||||
foreach($lanes as $laneID => $lane)
|
||||
@@ -107,6 +112,10 @@ class kanbanModel extends model
|
||||
$cardData['title'] = $object->title;
|
||||
}
|
||||
|
||||
if($laneType == 'story') $cardData['menus'] = $storyCardMenu[$object->id];
|
||||
if($laneType == 'bug') $cardData['menus'] = $bugCardMenu[$object->id];
|
||||
if($laneType == 'task') $cardData['menus'] = $taskCardMenu[$object->id];
|
||||
|
||||
$laneData['cards'][$column->type][] = $cardData;
|
||||
$cardOrder ++;
|
||||
}
|
||||
@@ -491,10 +500,12 @@ class kanbanModel extends model
|
||||
public function setWIP($columnID)
|
||||
{
|
||||
$oldColumn = $this->getColumnById($columnID);
|
||||
$column = fixer::input('post')
|
||||
->cleanInt('limit')
|
||||
->remove('WIPCount,noLimit')
|
||||
->get();
|
||||
$column = fixer::input('post')->remove('WIPCount,noLimit')->get();
|
||||
if(!preg_match("/^-?\d+$/", $column->limit))
|
||||
{
|
||||
dao::$errors['limit'] = $this->lang->kanban->error->mustBeInt;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check column limit. */
|
||||
$sumChildLimit = 0;
|
||||
@@ -591,4 +602,79 @@ class kanbanModel extends model
|
||||
$changes = common::createChanges($column, $data);
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Kanban cards menus by execution id.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param array $objects
|
||||
* @param string $objecType story|bug|task
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getKanbanCardMenu($executionID, $objects, $objecType)
|
||||
{
|
||||
$menus = array();
|
||||
switch ($objecType)
|
||||
{
|
||||
case 'story':
|
||||
if(!isset($this->story)) $this->loadModel('story');
|
||||
|
||||
$objects = $this->story->mergeReviewer($objects);
|
||||
foreach($objects as $story)
|
||||
{
|
||||
$menu = array();
|
||||
|
||||
$toTaskPriv = strpos('draft,closed', $story->status) !== false ? false : true;
|
||||
if(common::hasPriv('story', 'edit') and $this->story->isClickable($story, 'edit')) $menu[] = array('label' => $this->lang->story->edit, 'icon' => 'edit', 'url' => helper::createLink('story', 'edit', "storyID=$story->id", '', true), 'size' => '95%');
|
||||
if(common::hasPriv('story', 'change') and $this->story->isClickable($story, 'change')) $menu[] = array('label' => $this->lang->story->change, 'icon' => 'alter', 'url' => helper::createLink('story', 'change', "storyID=$story->id", '', true), 'size' => '95%');
|
||||
if(common::hasPriv('story', 'review') and $this->story->isClickable($story, 'review')) $menu[] = array('label' => $this->lang->story->review, 'icon' => 'search', 'url' => helper::createLink('story', 'review', "storyID=$story->id", '', true), 'size' => '95%');
|
||||
if(common::hasPriv('task', 'create') and $toTaskPriv) $menu[] = array('label' => $this->lang->execution->wbs, 'icon' => 'plus', 'url' => helper::createLink('task', 'create', "executionID=$executionID&storyID=$story->id&moduleID=$story->module", '', true), 'size' => '95%');
|
||||
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->change, '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));
|
||||
|
||||
$menus[$story->id] = $menu;
|
||||
}
|
||||
break;
|
||||
case 'bug':
|
||||
if(!isset($this->bug)) $this->loadModel('bug');
|
||||
|
||||
foreach($objects as $bug)
|
||||
{
|
||||
$menu = array();
|
||||
|
||||
if(common::hasPriv('bug', 'edit') and $this->bug->isClickable($bug, 'edit')) $menu[] = array('label' => $this->lang->bug->edit, 'icon' => 'edit', 'url' => helper::createLink('bug', 'edit', "bugID=$bug->id", '', true), 'size' => '95%');
|
||||
if(common::hasPriv('bug', 'confirmBug') and $this->bug->isClickable($bug, 'confirmBug')) $menu[] = array('label' => $this->lang->bug->confirmBug, 'icon' => 'ok', 'url' => helper::createLink('bug', 'confirmBug', "bugID=$bug->id", '', true));
|
||||
if(common::hasPriv('bug', 'resolve') and $this->bug->isClickable($bug, 'resolve')) $menu[] = array('label' => $this->lang->bug->resolve, 'icon' => 'checked', 'url' => helper::createLink('bug', 'resolve', "bugID=$bug->id", '', true));
|
||||
if(common::hasPriv('bug', 'close') and $this->bug->isClickable($bug, 'close')) $menu[] = array('label' => $this->lang->bug->close, 'icon' => 'plus', 'url' => helper::createLink('bug', 'close', "bugID=$bug->id", '', true));
|
||||
if(common::hasPriv('bug', 'create') and $this->bug->isClickable($bug, 'create')) $menu[] = array('label' => $this->lang->bug->copy, 'icon' => 'pluses', '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' => 'unlink', 'url' => helper::createLink('story', 'create', "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id", '', true), 'size' => '95%');
|
||||
|
||||
$menus[$bug->id] = $menu;
|
||||
}
|
||||
break;
|
||||
case 'task':
|
||||
if(!isset($this->task)) $this->loadModel('task');
|
||||
|
||||
foreach($objects as $task)
|
||||
{
|
||||
$menu = array();
|
||||
|
||||
if(common::hasPriv('task', 'edit') and $this->task->isClickable($task, 'edit')) $menu[] = array('label' => $this->lang->task->edit, 'icon' => 'edit', 'url' => helper::createLink('task', 'edit', "taskID=$task->id", '', true), 'size' => '95%');
|
||||
if(common::hasPriv('task', 'pause') and $this->task->isClickable($task, 'pause')) $menu[] = array('label' => $this->lang->task->pause, 'icon' => 'ok', 'url' => helper::createLink('task', 'pause', "taskID=$task->id", '', true));
|
||||
if(common::hasPriv('task', 'restart') and $this->task->isClickable($task, 'restart')) $menu[] = array('label' => $this->lang->task->restart, 'icon' => 'play', 'url' => helper::createLink('task', 'restart', "taskID=$task->id", '', true));
|
||||
if(common::hasPriv('task', 'recordEstimate') and $this->task->isClickable($task, 'recordEstimate')) $menu[] = array('label' => $this->lang->task->recordEstimate, 'icon' => 'time', 'url' => helper::createLink('task', 'recordEstimate', "taskID=$task->id", '', true));
|
||||
if(common::hasPriv('task', 'activate') and $this->task->isClickable($task, 'activate')) $menu[] = array('label' => $this->lang->task->activate, 'icon' => 'magic', 'url' => helper::createLink('task', 'activate', "taskID=$task->id", '', true));
|
||||
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));
|
||||
|
||||
$menus[$task->id] = $menu;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $menus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,6 +589,7 @@ class story extends control
|
||||
|
||||
$this->executeHooks($storyID);
|
||||
|
||||
if(isonlybody()) die(js::reload('parent.parent'));
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success', 'data' => $storyID));
|
||||
die(js::locate($this->createLink($this->app->rawModule, 'view', "storyID=$storyID"), 'parent'));
|
||||
}
|
||||
@@ -838,6 +839,7 @@ class story extends control
|
||||
|
||||
$module = $this->app->tab == 'project' ? 'projectstory' : 'story';
|
||||
|
||||
if(isonlybody()) die(js::reload('parent.parent'));
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
|
||||
die(js::locate($this->createLink($module, 'view', "storyID=$storyID"), 'parent'));
|
||||
}
|
||||
|
||||
@@ -408,6 +408,7 @@ class task extends control
|
||||
}
|
||||
}
|
||||
|
||||
if(isonlybody()) die(js::reload('parent.parent'));
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api')
|
||||
{
|
||||
return $this->send(array('status' => 'success', 'data' => $taskID));
|
||||
|
||||
Reference in New Issue
Block a user