Merge branch 'sprint/182_liumengyi_47334' into 'kanban'

* Finish task #47334.Add default view of professional RD Kanban.

See merge request easycorp/zentaopms!1354
This commit is contained in:
孙广明
2022-01-12 02:21:19 +00:00
9 changed files with 1001 additions and 1226 deletions
+25 -4
View File
@@ -1263,8 +1263,7 @@ class execution extends control
}
$project = $this->project->getByID($projectID);
if(!empty($projectID) and $project->model == 'kanban')
if(!empty($project) and $project->model == 'kanban')
{
$this->lang->execution->createExec = str_replace($this->lang->execution->common, $this->lang->execution->kanban, $this->lang->execution->createExec);
$this->lang->execution->execName = str_replace($this->lang->execution->common, $this->lang->execution->kanban, $this->lang->execution->execName);
@@ -1962,12 +1961,34 @@ class execution extends control
* @param int $executionID
* @param string $browseType
* @param string $orderBy
* @param string $group
* @param string $groupBy
* @access public
* @return void
*/
public function kanban($executionID, $browseType = 'all', $orderBy = 'id_asc', $group = 'all')
public function kanban($executionID, $browseType = 'all', $orderBy = 'id_asc', $groupBy = 'all')
{
unset($this->lang->execution->menu);
$users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $browseType, $orderBy, $groupBy);
$execution = $this->execution->getById($executionID);
$userList = array();
$avatarPairs = $this->dao->select('account, avatar')->from(TABLE_USER)->where('deleted')->eq(0)->fetchPairs();
foreach($avatarPairs as $account => $avatar)
{
if(!$avatar) continue;
$userList[$account]['avatar'] = $avatar;
}
$this->view->title = $this->lang->kanban->view;
$this->view->users = $users;
$this->view->regions = $kanbanData;
$this->view->execution = $execution;
$this->view->userList = $userList;
$this->view->browseType = $browseType;
$this->view->orderBy = $orderBy;
$this->view->groupBy = $groupBy;
$this->display();
}
+97 -45
View File
@@ -1,58 +1,110 @@
#main {padding-bottom: 10px;}
#main > .container {max-width: 1960px!important; padding: 0 10px}
#TRAction .icon {padding: 0px 6px; font-size: 14px}
.kanban + .kanban {margin-top: 15px}
.kanban-card > .title {display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
.kanban-card > .infos {position: relative; margin-top: 5px}
.kanban-card > .infos > .info + .info {margin-left: 8px}
.kanban-card > .infos > .info-id,
.kanban-card > .infos > .info-deadline,
.kanban-card > .infos > .info-estimate {font-size: 12px; position: relative; top: 2px}
.kanban-card > .infos > .label-pri {min-width: 16px; line-height: 14px; height: 16px; padding: 0}
.kanban-card > .infos > .label-severity {transform: scale(.75)}
.kanban-card > .infos > .avatar {position: absolute; right: 0; top: 0}
.kanban-card > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
.has-error .form-control {border-color: #ff4268!important;}
.has-error .form-control:focus {box-shadow: inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 6px #f5b2a5!important;}
.has-error .form-control::placeholder {color: #ff4268!important;}
.label.text-red {background: #ff4268 !important; color: #ffffff;}
.dropdown-menu > li {padding: 0 10px;}
.dropdown-menu > li > a > .icon {position: relative; left: -5px;}
#kanbanContainer {padding-bottom: 0; margin-bottom: 0;}
#kanbanContainer.fullscreen {overflow: auto;}
.region {border: 1px solid #dcdcdc; background-color: #fff;}
.region + .region {margin-top: 20px;}
.region .region-header {padding: 10px;}
.region .region-header span {font-size: 14px; color: #999EAB}
.region .region-header label {color: #999; background: transparent; border: 1px solid #ddd; margin-left: 10px; margin-right: 10px}
.region .region-header .action {float: right}
.region .region-header .icon-double-angle-up,.icon-double-angle-down {cursor: pointer;}
.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}
.region .kanban .form-actions {margin: 0;}
.region .kanban .form-actions .btn {margin-right: 10px; min-width: 50px;}
.region .kanban-board + .kanban-board {margin-top: 20px;}
.region .kanban-board > .kanban-header > .kanban-group-header {padding: 8px 2px; width: 20px;}
.region .kanban-board > .kanban-header > .kanban-group-header:hover {cursor: move;}
.region .kanban-board.sort > .kanban-header {padding-left: 0;}
.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 > .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-parent-col > .actions {position: absolute; right: 0px;}
.region .kanban-header-sub-cols .actions {position: absolute; right: 0px;}
.region .kanban-lane {position: relative; border-bottom: none!important; min-height: 200px !important}
.region .kanban-lane > .kanban-lane-name > .text {margin: auto 0; max-height: 150px; overflow: hidden; text-overflow: ellipsis}
.region .kanban-lane > .kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;}
.region .kanban-lane > .kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;}
.region .kanban-lane > .kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;}
.region .kanban-lane > .kanban-lane-name:hover > .actions {opacity: 1;}
.region .kanban-lane.sort > .kanban-lane-name {cursor: move;}
.region .kanban-lane-col {max-height: unset !important; overflow: auto!important; position: relative;}
.region .kanban-lane-col.has-scrollbar > .kanban-lane-actions {position: absolute; background-color: inherit; bottom: 0; left: 0; right: 0;}
.region .kanban-lane-items {overflow: auto; padding-bottom: 10px;}
.region .kanban-item {position: relative}
.region .kanban-item > .kanban-card > .title:hover {color: #2272eb}
.region .kanban-item > .kanban-card > .info {margin-top: 5px; position: relative;}
.region .kanban-item > .kanban-card > .info > .pri {height: 14px; border-width: 1px; font-size: 12px; line-height: 12px; min-width: 14px; padding: 0 1px;}
.region .kanban-item > .kanban-card > .info > .estimate{margin-left: 4px; color: #999; background-color: rgb(255, 255, 255); padding: 3px 0px}
.region .kanban-item > .kanban-card > .info > .time {margin-left: 4px; font-size: 12px; background: rgb(242, 242, 242)}
.region .kanban-item > .kanban-card > .info > .user {position: absolute; right: 0; top: -2px}
.region .kanban-item > .kanban-card > .info > .user > .avatar {display: inline-block; width: 24px; height: 24px; line-height: 24px; margin-right: 0px; margin-left: 2px}
.region .kanban-item > .kanban-card > .info > .user > span {display: inline-block; color: transparent; width: 2px; height: 2px; background-color: #8990a2; position: sticky; border-radius: 50%; top: 3px; margin: 0 7px;}
.region .kanban-item > .kanban-card > .info > .user > span:before,
.region .kanban-item > .kanban-card > .info > .user > span:after {content: ''; display: block; position: absolute; width: 2px; height: 2px; background-color: #8990a2; top: 0; border-radius: 50%}
.region .kanban-item > .kanban-card > .info > .user > span:before {left: -4px;}
.region .kanban-item > .kanban-card > .info > .user > span:after {right: -4px;}
.region .kanban-item > .kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.region .kanban-item > .kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.region .kanban-item:hover > .actions {opacity: 1;}
.region .kanban-item .has-color .title,
.region .kanban-item .has-color .actions .icon-more-v,
.region .kanban-item .has-color .info > .label-pri,
.region .kanban-item .has-color .info > .estimate,
.region .kanban-item .has-color .info > .user .lable-teamSumCount,
.region .kanban-item .has-color .info > .label-light {color: #FFFFFF;}
.region .kanban-item .has-color .info > .label-pri {border-color: #FFFFFF;}
.region .kanban-item .item-more {position: absolute; right: 6px; top: 0px;}
.kanban-card {height: auto !important; padding: 8px 14px !important}
.kanban-card > .title {word-break: break-all;}
.kanban-card > .info {position: relative; margin-top: 5px}
.kanban-card > .info > .info + .info {margin-left: 8px}
.kanban-card:hover > .actions {opacity: 1;}
.kanban-card > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
.kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-header-col > .actions {padding-top: 22px}
.kanban-header-parent-col .kanban-header-col > .actions {padding-top: 6px; padding-right: 2px;}
.kanban-header-col > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-header-col > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.cardcolor {width:40px; height: 14px; float: left; margin-left: 5px; margin-right: 5px; margin-top: 2px; border-radius: 2px;}
#cardcolormenu {padding:2px 2px; width: 100px;}
.kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;}
.kanban-lane-name:hover > .actions {opacity: 1;}
.kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;}
.kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;}
.kanban-col[data-type=ADD] {display: none;}
.kanban-col[data-type=EMPTY] {display: none;}
.kanban-item:hover .title {padding-right: 10px;}
.gray .actions {display:none;}
.kanban-affixed .kanban-header-col > .title > .text,
.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;}
#kanbanContainer {margin: 0;}
#kanbanContainer > .panel-body {overflow: auto;}
#kanbans .kanban {min-height: initial;}
#kanbans .kanban-lane {min-height: 150px; margin-top: 2px;}
#kanbans .kanban-affixed > .kanban-header {top: 100px;}
#kanbans .kanban-card[data-scale-size="4"] {padding: 8px;}
#kanbans .kanban-card[data-scale-size="3"] {padding: 3px 4px;}
#kanbans .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;}
#kanbans .kanban-card[data-scale-size="2"] {padding: 3px 4px; overflow: hidden;}
#kanbans .kanban-card[data-scale-size="2"] > .title {display: inline; line-height: 18px; white-space: normal; max-height: 100%; word-break: break-all;}
#kanbans .kanban-card[data-scale-size="2"] > .infos {display: inline-block; height: 18px; vertical-align: top; margin: 0;}
#kanbans .kanban-card[data-scale-size="2"] > .infos > .info-pri {transform: scale(.8); vertical-align: top;}
#kanbans .kanban-card[data-scale-size="2"] > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -2px; vertical-align: top; margin-right: -1px;}
.c-type {width: 150px !important; overflow: unset;}
.c-group {width: 190px !important; overflow: unset;}
.c-type {margin-left: 0px !important;}
#kanbanScaleControl {width: 100px;}
#kanbanScaleControl .input-group-addon {background: #fff; padding: 5px;}
#kanbanScaleControl > .input-group-btn:first-child > .btn {border-radius: 16px 0 0 16px; border-right-color: transparent;}
#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;}
#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;}
#type_chosen .icon-kanban {padding-right: 10px; font-size: 15px;}
.dropdown-menu a {text-align: left;}
.scrollbar-hover {max-height: 2000px; overflow: scroll !important;}
.c-type {width: 150px !important; overflow: unset;}
.c-group {width: 190px !important; overflow: unset;}
.c-type {margin-left: 0px !important;}
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,6 +1,6 @@
function changeView(view)
{
var link = createLink('execution', 'kanban', "executionID=" + executionID + '&type=' + view);
var link = createLink('execution', 'taskKanban', "executionID=" + executionID + '&type=' + view);
location.href = link;
}
@@ -597,7 +597,7 @@ function changeCardColType(card, fromColType, toColType, kanbanID)
}
else if(toColType == 'fixed')
{
if(fromColType == 'fixing' || fromColType == 'confirmed' || fromColType == 'unconfirmed')
if(fromColType == 'fixing' || fromColType == 'confirmed' || fromColType == 'unconfirmed')
{
var link = createLink('bug', 'resolve', 'bugID=' + objectID, '', true);
showIframe = true;
@@ -1055,7 +1055,7 @@ $('#type').change(function()
})
}
var link = createLink('execution', 'kanban', "executionID=" + executionID + '&type=' + type);
var link = createLink('execution', 'taskKanban', "executionID=" + executionID + '&type=' + type);
location.href = link;
});
@@ -1065,6 +1065,6 @@ $('.c-group').change(function()
var type = $('#type').val();
var group = $('#group').val();
var link = createLink('execution', 'kanban', 'executionID=' + executionID + '&type=' + type + '&orderBy=order_asc' + '&groupBy=' + group);
var link = createLink('execution', 'taskKanban', 'executionID=' + executionID + '&type=' + type + '&orderBy=order_asc' + '&groupBy=' + group);
location.href = link;
});
+1 -1
View File
@@ -2480,7 +2480,7 @@ class executionModel extends model
/**
* Get members by execution id list.
*
*
* @param array $executionIdList
* @access public
* @return void
+52 -136
View File
@@ -1,15 +1,42 @@
<?php
/**
* The kanban view file of execution module of ZenTaoPMS.
* The kanban file of execution module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Wang Yidong, Zhu Jinyong
* @copyright Copyright 2009-2022 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Mengyi Liu<liumengyi@easycorp.ltd>
* @package execution
* @version $Id: kanban.html.php $
* @version $Id: kanban.html.php 935 2022-01-11 16:49:24Z $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kanban.html.php';?>
<?php
$laneCount = 0;
foreach($regions as $region) $laneCount += $region->laneCount;
js::set('regions', $regions);
js::set('execution', $execution);
js::set('kanbanLang', $lang->kanban);
js::set('kanbanlaneLang', $lang->kanbanlane);
js::set('kanbancolumnLang', $lang->kanbancolumn);
js::set('kanbancardLang', $lang->kanbancard);
js::set('executionID', $execution->id);
js::set('laneCount', $laneCount);
js::set('userList', $userList);
js::set('noAssigned', $lang->kanbancard->noAssigned);
js::set('users', $users);
js::set('colorListLang', $lang->kanbancard->colorList);
js::set('colorList', $this->config->kanban->cardColorList);
$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1;
$canEditRegion = commonModel::hasPriv('kanban', 'editRegion');
$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion');
$canCreateLane = commonModel::hasPriv('kanban', 'createLane');
?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<div class="input-control space c-type">
@@ -32,144 +59,33 @@
<button class='btn btn-icon' type='button' data-type='+'><i class='icon icon-plus-solid-circle text-muted'></i></button>
</span>
</div>
<div class='btn-toolbar pull-right'>
<?php
$link = $this->createLink('task', 'export', "execution=$executionID&orderBy=$orderBy&type=kanban");
if(common::hasPriv('task', 'export')) echo html::a($link, "<i class='icon-export muted'></i> " . $lang->export, '', "class='btn btn-link iframe export' data-width='700'");
?>
<?php if($canBeChanged):?>
<div class='btn-group' style="margin-right: 0">
<button type='button' class='btn btn-link dropdown-toggle' data-toggle='dropdown' id='importAction'>
<i class='icon-import muted'></i> <?php echo $lang->import ?>
<span class='caret'></span>
</button>
<ul class='dropdown-menu' id='importActionMenu'>
<?php
$misc = common::hasPriv('execution', 'importTask') ? '' : "class=disabled";
$link = common::hasPriv('execution', 'importTask') ? $this->createLink('execution', 'importTask', "execution=$execution->id") : '#';
echo "<li $misc>" . html::a($link, $lang->execution->importTask, '', $misc) . "</li>";
$misc = common::hasPriv('execution', 'importBug') ? '' : "class=disabled";
$link = common::hasPriv('execution', 'importBug') ? $this->createLink('execution', 'importBug', "execution=$execution->id") : '#';
echo "<li $misc>" . html::a($link, $lang->execution->importBug, '', $misc) . "</li>";
?>
</ul>
</div>
<?php
echo "<div class='btn-group menu-actions'>";
echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");
echo "<ul class='dropdown-menu pull-right'>";
if(common::hasPriv('execution', 'printKanban')) echo '<li>' .html::a($this->createLink('execution', 'printKanban', "executionID=$executionID"), "<i class='icon-printer muted'></i> " . $lang->execution->printKanban, '', "class='iframe btn btn-link' id='printKanban' title='{$lang->execution->printKanban}' data-width='500'") . '</li>';
echo '<li>' .html::a('javascript:fullScreen()', "<i class='icon-fullscreen muted'></i> " . $lang->execution->fullScreen, '', "class='btn btn-link' title='{$lang->execution->fullScreen}' data-width='500'") . '</li>';
echo '</ul></div>';
?>
<?php
$checkObject = new stdclass();
$checkObject->execution = $executionID;
$canCreateTask = common::hasPriv('task', 'create', $checkObject);
$canBatchCreateTask = common::hasPriv('task', 'batchCreate', $checkObject);
$canCreateBug = common::hasPriv('bug', 'create');
$canBatchCreateBug = common::hasPriv('bug', 'batchCreate');
$canCreateStory = ($productID and common::hasPriv('story', 'create'));
$canBatchCreateStory = ($productID and common::hasPriv('story', 'batchCreate'));
$canLinkStory = ($productID and common::hasPriv('execution', 'linkStory'));
$canLinkStoryByPlane = ($productID and common::hasPriv('execution', 'importplanstories'));
$hasStoryButton = ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlane);
$hasTaskButton = ($canCreateTask or $canBatchCreateTask);
$hasBugButton = ($canCreateBug or $canBatchCreateBug);
?>
<?php if($canCreateTask or $canBatchCreateTask or $canCreateBug or $canBatchCreateBug or $canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlane):?>
<div class='dropdown' id='createDropdown'>
<button class='btn btn-primary' type='button' data-toggle='dropdown'><i class='icon icon-plus'></i> <?php echo $this->lang->create;?> <span class='caret'></span></button>
<ul class='dropdown-menu pull-right'>
<?php if($canCreateStory) echo '<li>' . html::a(helper::createLink('story', 'create', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id", '', true), $lang->execution->createStory, '', "class='iframe'") . '</li>';?>
<?php if($canBatchCreateStory) echo '<li>' . html::a(helper::createLink('story', 'batchCreate', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id", '', true), $lang->execution->batchCreateStroy, '', "class='iframe' data-width='90%'") . '</li>';?>
<?php if($canLinkStory) echo '<li>' . html::a(helper::createLink('execution', 'linkStory', "execution=$execution->id", '', true), $lang->execution->linkStory, '', "class='iframe' data-width='90%'") . '</li>';?>
<?php if($canLinkStoryByPlane) echo '<li>' . html::a('#linkStoryByPlan', $lang->execution->linkStoryByPlan, '', 'data-toggle="modal"') . '</li>';?>
<?php if($hasStoryButton and $hasBugButton) echo '<li class="divider"></li>';?>
<?php if($canCreateBug) echo '<li>' . html::a(helper::createLink('bug', 'create', "productID=$productID&branch=0&extra=executionID=$execution->id", '', true), $lang->bug->create, '', "class='iframe'") . '</li>';?>
<?php if($canBatchCreateBug) echo '<li>' . html::a(helper::createLink('bug', 'batchCreate', "productID=$productID&branch=0&executionID=$execution->id", '', true), $lang->bug->batchCreate, '', "class='iframe'") . '</li>';?>
<?php if(($hasStoryButton or $hasBugButton) and $hasTaskButton) echo '<li class="divider"></li>';?>
<?php if($canCreateTask) echo '<li>' . html::a(helper::createLink('task', 'create', "execution=$execution->id", '', true), $lang->task->create, '', "class='iframe'") . '</li>';?>
<?php if($canBatchCreateTask) echo '<li>' . html::a(helper::createLink('task', 'batchCreate', "execution=$execution->id", '', true), $lang->execution->batchCreateTask, '', "class='iframe'") . '</li>';?>
</ul>
</div>
<?php endif;?>
<?php else:?>
<?php $canCreateTask = $canBatchCreateTask = $canCreateBug = $canBatchCreateBug = $canCreateStory = $canBatchCreateStory = $canLinkStory = $canLinkStoryByPlane = false;?>
<?php endif;?>
</div>
</div>
<div class='panel' id='kanbanContainer'>
<div class='panel-body'>
<div id='kanbans'></div>
</div>
</div>
<div class="modal fade" id="linkStoryByPlan">
<div class="modal-dialog mw-500px">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="icon icon-close"></i></button>
<h4 class="modal-title"><?php echo $lang->execution->linkStoryByPlan;?></h4><?php echo '(' . $lang->execution->linkStoryByPlanTips . ')';?>
</div>
<div class="modal-body">
<div class='input-group'>
<?php echo html::select('plan', $allPlans, '', "class='form-control chosen' id='plan'");?>
<span class='input-group-btn'><?php echo html::commonButton($lang->execution->linkStory, "id='toStoryButton'", 'btn btn-primary');?></span>
<div id="kanban" data-id='<?php echo $execution->id;?>'>
<?php foreach($regions as $region):?>
<div class="region<?php if($canSortRegion) echo ' sort';?>" data-id="<?php echo $region->id;?>">
<div class="region-header dropdown">
<span class="strong"><?php echo $region->name;?></span>
<label class="label label-region"><?php echo $this->lang->kanbanlane->common . ' ' . $region->laneCount;?></label>
<span><i class="icon icon-chevron-double-up" data-id="<?php echo $region->id;?>"></i></span>
<span class='regionActions'>
<?php if($canEditRegion || $canCreateLane || $canDeleteRegion):?>
<button class="btn btn-link action" type="button" data-toggle="dropdown"><i class="icon icon-ellipsis-v"></i></button>
<ul class="dropdown-menu pull-right">
<?php if($canEditRegion) echo '<li>' . html::a(helper::createLink('kanban', 'editRegion', "regionID={$region->id}", '', 1), '<i class="icon icon-edit"></i>' . $this->lang->kanban->editRegion, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
<?php if($canCreateLane) echo '<li>' . html::a(helper::createLink('kanban', 'createLane', "executionID={$execution->id}&regionID={$region->id}", '', 1), '<i class="icon icon-plus"></i>' . $this->lang->kanban->createLane, '', "class='iframe'") . '</li>';?>
<?php if($canDeleteRegion and count($regions) > 1) echo '<li>' . html::a(helper::createLink('kanban', 'deleteRegion', "regionID={$region->id}"), '<i class="icon icon-trash"></i>' . $this->lang->kanban->deleteRegion, "hiddenwin") . '</li>';?>
</ul>
<?php endif;?>
</span>
</div>
<div id='kanban<?php echo $region->id;?>' data-id='<?php echo $region->id;?>' class='kanban'></div>
</div>
<?php endforeach;?>
</div>
</div>
</div>
<?php js::set('executionID', $executionID);?>
<?php js::set('productID', $productID);?>
<?php js::set('kanbanGroup', $kanbanGroup);?>
<?php js::set('kanbanList', array_keys($kanbanGroup));?>
<?php js::set('browseType', $browseType);?>
<?php js::set('groupBy', $groupBy);?>
<?php
js::set('priv',
array(
'canEditName' => common::hasPriv('kanban', 'setColumn'),
'canSetWIP' => common::hasPriv('kanban', 'setWIP'),
'canSetLane' => common::hasPriv('kanban', 'setLane'),
'canMoveLane' => common::hasPriv('kanban', 'laneMove'),
'canSortCards' => common::hasPriv('kanban', 'cardsSort'),
'canCreateTask' => $canCreateTask,
'canBatchCreateTask' => $canBatchCreateTask,
'canCreateBug' => $canCreateBug,
'canBatchCreateBug' => $canBatchCreateBug,
'canCreateStory' => $canCreateStory,
'canBatchCreateStory' => $canBatchCreateStory,
'canLinkStory' => $canLinkStory,
'canLinkStoryByPlane' => $canLinkStoryByPlane,
'canAssignTask' => common::hasPriv('task', 'assignto'),
'canAssignStory' => common::hasPriv('story', 'assignto'),
'canFinishTask' => common::hasPriv('task', 'finish'),
'canPauseTask' => common::hasPriv('task', 'pause'),
'canCancelTask' => common::hasPriv('task', 'cancel'),
'canCloseTask' => common::hasPriv('task', 'close'),
'canActivateTask' => common::hasPriv('task', 'activate'),
'canStartTask' => common::hasPriv('task', 'start'),
'canAssignBug' => common::hasPriv('bug', 'assignto'),
'canConfirmBug' => common::hasPriv('bug', 'confirmBug'),
'canActivateBug' => common::hasPriv('bug', 'activate')
)
);
?>
<?php js::set('executionLang', $lang->execution);?>
<?php js::set('storyLang', $lang->story);?>
<?php js::set('taskLang', $lang->task);?>
<?php js::set('bugLang', $lang->bug);?>
<?php js::set('editName', $lang->execution->editName);?>
<?php js::set('setWIP', $lang->execution->setWIP);?>
<?php js::set('sortColumn', $lang->execution->sortColumn);?>
<?php js::set('kanbanLang', $lang->kanban);?>
<?php js::set('deadlineLang', $lang->task->deadlineAB);?>
<?php js::set('noAssigned', $lang->task->noAssigned);?>
<?php js::set('userList', $userList);?>
<?php js::set('entertime', time());?>
<div id='archivedCards'></div>
<div id='archivedColumns'></div>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -58,7 +58,7 @@
}
}
echo "<li id='kanbanTab'>"; common::printLink('execution', 'kanban', "executionID=$executionID", $lang->execution->kanban); echo '</li>';
echo "<li id='kanbanTab'>"; common::printLink('execution', 'taskKanban', "executionID=$executionID", $lang->execution->kanban); echo '</li>';
if($execution->type == 'sprint' or $execution->type == 'waterfall')
{
echo "<li id='burnTab'>";
+2 -2
View File
@@ -1,11 +1,11 @@
<?php
/**
* The kanban view file of execution module of ZenTaoPMS.
* The task kanban view file of execution module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Wang Yidong, Zhu Jinyong
* @package execution
* @version $Id: kanban.html.php $
* @version $Id: taskkanban.html.php $
*/
?>
<?php include '../../common/view/header.html.php';?>
+156 -6
View File
@@ -614,6 +614,62 @@ class kanbanModel extends model
return $kanbanData;
}
/**
* Get a RD kanban data.
*
* @param int $executionID
* @param string $browseType all|story|task|bug
* @param string $orderBy
* @param string $groupBy
*
* @access public
* @return array
*/
public function getRDKanban($executionID, $browseType = 'all', $orderBy = 'id_desc', $groupBy = 'all')
{
$kanbanData = array();
$actions = array('sortGroup');
$regions = $this->getRegionPairs($executionID);
$regionIDList = array_keys($regions);
$groupGroup = $this->getGroupGroupByRegions($regionIDList);
$laneGroup = $this->getLaneGroupByRegions($regionIDList, $browseType);
$columnGroup = $this->getRDColumnGroupByRegions($regionIDList, array_keys($laneGroup));
$cardGroup = $this->getCardGroupByExecution($executionID, $browseType, $orderBy);
foreach($regions as $regionID => $regionName)
{
$region = new stdclass();
$region->id = $regionID;
$region->name = $regionName;
$region->laneCount = 0;
$groups = zget($groupGroup, $regionID, array());
foreach($groups as $group)
{
$lanes = zget($laneGroup, $group->id, array());
if(!$lanes) continue;
foreach($lanes as $lane) $lane->items = array();
$group->columns = zget($columnGroup, $group->id, array());
$group->lanes = $lanes;
$group->actions = array();
foreach($actions as $action)
{
if(commonModel::hasPriv('kanban', $action)) $group->actions[] = $action;
}
$region->groups[] = $group;
$region->laneCount += count($lanes);
}
$kanbanData[$regionID] = $region;
}
return $kanbanData;
}
/**
* Get region by id.
*
@@ -660,15 +716,17 @@ class kanbanModel extends model
/**
* Get lane group by regions.
*
* @param array $regions
* @param array $regions
* @param string $browseType
* @access public
* @return array
*/
public function getLaneGroupByRegions($regions)
public function getLaneGroupByRegions($regions, $browseType = 'all')
{
$laneGroup = $this->dao->select('*')->from(TABLE_KANBANLANE)
->where('deleted')->eq('0')
->andWhere('region')->in($regions)
->beginIf($browseType != 'all')->andWhere('type')->eq($browseType)->fi()
->orderBy('order')
->fetchGroup('group');
@@ -781,6 +839,96 @@ class kanbanModel extends model
return $cardGroup;
}
/**
* Get RD column group by regions.
*
* @param array $regions
* @param array $groupIDList
* @access public
* @return array
*/
public function getRDColumnGroupByRegions($regions, $groupIDList = array())
{
$columnGroup = $this->dao->select("*")->from(TABLE_KANBANCOLUMN)
->where('deleted')->eq('0')
->andWhere('region')->in($regions)
->beginIF(!empty($groupIDList))->andWhere('`group`')->in($groupIDList)->fi()
->orderBy('id_asc')
->fetchGroup('group');
$actions = array('setColumn', 'setWIP', 'deleteColumn');
/* Group by parent. */
$parentColumnGroup = array();
foreach($columnGroup as $group => $columns)
{
foreach($columns as $column)
{
$column->actions = array();
/* Judge column action priv. */
foreach($actions as $action)
{
if($this->isClickable($column, $action)) $column->actions[] = $action;
}
if($column->parent > 0) continue;
$parentColumnGroup[$group][] = $column;
}
}
$columnData = array();
foreach($parentColumnGroup as $group => $parentColumns)
{
foreach($parentColumns as $parentColumn)
{
$columnData[$group][] = $parentColumn;
foreach($columnGroup[$group] as $column)
{
if($column->parent == $parentColumn->id)
{
$parentColumn->asParent = true;
if(strpos(',developing,developed,', $column->type) !== false) $column->parentType = 'develop';
if(strpos(',testing,tested,', $column->type) !== false) $column->parentType = 'test';
if(strpos(',fixing,fixed,', $column->type) !== false) $column->parentType = 'resolving';
$columnData[$group][] = $column;
}
}
}
}
return $columnData;
}
/**
* Get card group by execution id.
*
* @param int $kanbanID
* @param string $browseType all|task|bug|story
* @param string $orderBy
* @access public
* @return array
*/
public function getCardGroupByExecution($executionID, $browseType = 'all', $orderBy = 'id_asc')
{
$cards = $this->dao->select("id, title, pri, type, severity, assignedTo, '' as estimate, deadline")->from(TABLE_BUG)
->where('deleted')->eq(0)
->andWhere('execution')->eq($executionID)
->fetchAll('id');
/*
$cards .= $this->dao->select("id, name, pri, '' as severity, assignedTo, deadline")->from(TABLE_TASK)
->where('deleted')->eq(0)
->andWhere('execution')->eq($executionID)
->fetchAll('id');
*/
$planList = $this->loadModel('execution')->getById($executionID);
return $cards;
}
/**
* Get Kanban by execution id.
*
@@ -1634,10 +1782,12 @@ class kanbanModel extends model
foreach($columnList as $type => $name)
{
$data = new stdClass();
$data->lane = $laneID;
$data->name = $name;
$data->color = '#333';
$data->type = $type;
$data->lane = $laneID;
$data->name = $name;
$data->color = '#333';
$data->type = $type;
$data->group = $groupID;
$data->region = $regionID;
if(strpos(',developing,developed,', $type) !== false) $data->parent = $devColumnID;
if(strpos(',testing,tested,', $type) !== false) $data->parent = $testColumnID;