Merge branch 'master' of https://gitlab.zcorp.cc/easycorp/zentaopms into sprint/master_lanzongjun_fixbug

This commit is contained in:
lanzongjun
2022-09-01 09:11:23 +08:00
30 changed files with 393 additions and 104 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ if($config->edition != 'open')
$lang->navIcons['oa'] = "<i class='icon icon-oa'></i>";
$lang->navIcons['workflow'] = "<i class='icon icon-flow'></i>";
$lang->mainNav->feedback = $lang->navIcons['feedback'] . 'Feedback|feedback|browse|';
$lang->mainNav->feedback = $lang->navIcons['feedback'] . 'Feedback|feedback|browse|browseType=unclosed';
$lang->mainNav->oa = $lang->navIcons['oa'] . 'OA|attend|personal|';
$lang->mainNav->workflow = $lang->navIcons['workflow'] . 'Workflow|workflow|browseFlow|';
+1 -1
View File
@@ -20,7 +20,7 @@ if($config->edition != 'open')
$lang->navIcons['oa'] = "<i class='icon icon-oa'></i>";
$lang->navIcons['workflow'] = "<i class='icon icon-flow'></i>";
$lang->mainNav->feedback = $lang->navIcons['feedback'] . 'Feedback|feedback|browse|';
$lang->mainNav->feedback = $lang->navIcons['feedback'] . 'Feedback|feedback|browse|browseType=unclosed';
$lang->mainNav->oa = $lang->navIcons['oa'] . 'OA|attend|personal|';
$lang->mainNav->workflow = $lang->navIcons['workflow'] . 'Workflow|workflow|browseFlow|';
+1 -1
View File
@@ -20,7 +20,7 @@ if($config->edition != 'open')
$lang->navIcons['oa'] = "<i class='icon icon-oa'></i>";
$lang->navIcons['workflow'] = "<i class='icon icon-flow'></i>";
$lang->mainNav->feedback = $lang->navIcons['feedback'] . 'Feedback|feedback|browse|';
$lang->mainNav->feedback = $lang->navIcons['feedback'] . 'Feedback|feedback|browse|browseType=unclosed';
$lang->mainNav->oa = $lang->navIcons['oa'] . 'OA|attend|personal|';
$lang->mainNav->workflow = $lang->navIcons['workflow'] . 'Workflow|workflow|browseFlow|';
@@ -20,7 +20,7 @@ if($config->edition != 'open')
$lang->navIcons['oa'] = "<i class='icon icon-oa'></i>";
$lang->navIcons['workflow'] = "<i class='icon icon-flow'></i>";
$lang->mainNav->feedback = $lang->navIcons['feedback'] . '反馈|feedback|browse|';
$lang->mainNav->feedback = $lang->navIcons['feedback'] . '反馈|feedback|browse|browseType=unclosed';
$lang->mainNav->oa = $lang->navIcons['oa'] . '办公|attend|personal|';
$lang->mainNav->workflow = $lang->navIcons['workflow'] . '工作流|workflow|browseFlow|';
+2 -1
View File
@@ -78,7 +78,8 @@ $(function()
hiddenRequireFields();
});
var maxAutoDropWidth = document.body.scrollWidth + (document.getElementById('resolutions1').offsetWidth / 2) - document.getElementById('resolutions1').getBoundingClientRect().right;
var firstResolution = $('select[id^="resolutions"]').eq(0);
var maxAutoDropWidth = document.body.scrollWidth + ($(firstResolution)[0].offsetWidth / 2) - $(firstResolution)[0].getBoundingClientRect().right;
$('select[id^="duplicateBugs"]').picker(
{
disableEmptySearch : true,
+15 -1
View File
@@ -2949,7 +2949,21 @@ class bugModel extends model
/* If search criteria don't have products, append the selected product from the top left dropdown-menu. */
if(is_array($productIDList)) $productIDList = implode(',', $productIDList);
if(strpos($bugQuery, '`product`') === false) $bugQuery .= ' AND `product` IN (' . $productIDList . ')';
if(strpos($bugQuery, '`product`') === false)
{
$bugQuery .= ' AND `product` IN (' . $productIDList . ')';
}
else
{
$productParis = $this->loadModel('product')->getPairs();
$productIDList = array_keys($productParis);
if(!empty($productIDList))
{
$productIDList = implode(',', $productIDList);
$bugQuery .= ' AND `product` IN (' . $productIDList . ')';
}
}
$allBranch = "`branch` = 'all'";
if($branch !== 'all' and strpos($bugQuery, '`branch` =') === false) $bugQuery .= " AND `branch` in('0','$branch')";
+14 -1
View File
@@ -1028,6 +1028,8 @@ class commonModel extends model
{
foreach($lang->$currentModule->menu as $menu)
{
if(!isset($menu['link'])) continue;
$linkPart = explode('|', $menu['link']);
if(!isset($linkPart[2])) continue;
$method = $linkPart[2];
@@ -1457,7 +1459,18 @@ class commonModel extends model
echo "<a href='javascript:;'>" . "<i class='icon icon-download'></i> " . $lang->clientName . "</a><ul class='dropdown-menu pull-left'>";
echo '<li>' . html::a(helper::createLink('misc', 'downloadClient', '', '', true), $lang->downloadClient, '', "title='$lang->downloadClient' class='iframe text-ellipsis' data-width='600'") . '</li>';
echo "<li class='dropdown-submenu' id='downloadMobile'><a href='javascript:;'>" . $lang->downloadMobile . "</a><ul class='dropdown-menu pull-left''>";
echo "<li><div class='mobile-qrcode'><iframe src='https://www.zentao.net/page/appqrcode.html?v={$config->version}' frameborder='0' scrolling='no' seamless></iframe></div></li>";
$appqrcode = self::http('https://www.zentao.net/page/appqrcode.json');
/* Intranet users use local pictures. */
if(!empty($appqrcode))
{
echo "<li><div class='mobile-qrcode'><iframe src='https://www.zentao.net/page/appqrcode.html?v={$config->version}' frameborder='0' scrolling='no' seamless></iframe></div></li>";
}
else
{
echo "<li><div class='mobile-qrcode local-img'><img src='{$config->webRoot}theme/default/images/main/mobile_qrcode.png' /><div class='mobile-version'><span>v1.2</span></div></li>";
}
echo "</ul></li>";
echo '<li>' . html::a($lang->clientHelpLink, $lang->clientHelp, '', "title='$lang->clientHelp' target='_blank'") . '</li>';
echo '</ul></li>';
+1 -1
View File
@@ -461,7 +461,7 @@ class doc extends control
if(!empty($objectType) and $objectType != 'doc' and $doc->type != 'chapter' and $doc->type != 'article')
{
$link = $this->createLink('doc', 'objectLibs', "type={$lib->type}&objectID=$objectID&libID={$doc->lib}&docID=$docID");
$link = $this->createLink('doc', 'objectLibs', "type={$lib->type}&objectID=$objectID&libID={$doc->lib}&docID=$docID") . "#app={$this->app->tab}";
}
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
+1 -1
View File
@@ -152,7 +152,7 @@ class group extends control
$this->view->title = $this->lang->company->common . $this->lang->colon . $group->name . $this->lang->colon . $this->lang->group->manageView;
$this->view->group = $group;
$this->view->programs = $this->loadModel('program')->getPairs(true, 'order_desc');
$this->view->programs = $this->loadModel('program')->getParentPairs('', '', false);
$this->view->projects = $this->loadModel('project')->getPairsByProgram('', 'all', true, 'order_desc');
$this->view->executions = $this->loadModel('execution')->getPairs(0, 'all', 'all');
$this->view->products = $this->loadModel('product')->getPairs();
+4 -7
View File
@@ -243,6 +243,7 @@ class groupModel extends model
{
$objects = $this->dao->select('id, name, path, type, project, grade, parent')->from(TABLE_PROJECT)
->where('vision')->eq($this->config->vision)
->andWhere('type')->ne('program')
->andWhere('deleted')->eq(0)
->fetchAll('id');
@@ -251,7 +252,8 @@ class groupModel extends model
->andWhere('deleted')->eq(0)
->fetchAll('id');
$programs = array();
$programs = $this->loadModel('program')->getParentPairs('', '', false);
$projects = array();
$executions = array();
$products = array();
@@ -261,12 +263,7 @@ class groupModel extends model
$path = explode(',', trim($object->path, ','));
$topID = $path[0];
if($type == 'program')
{
if($topID != $object->id) $object->name = isset($objects[$topID]) ? $objects[$topID]->name . '/' . $object->name : $object->name;
$programs[$object->id] = $object->name;
}
elseif($type == 'project')
if($type == 'project')
{
if($topID != $object->id) $object->name = isset($objects[$topID]) ? $objects[$topID]->name . '/' . $object->name : $object->name;
$projects[$object->id] = $object->name;
+23 -10
View File
@@ -388,12 +388,15 @@ class kanban extends control
/**
* View a kanban.
*
* @param int $kanbanID
* @param int $kanbanID
* @param int|string $regionID
* @access public
* @return void
*/
public function view($kanbanID)
public function view($kanbanID, $regionID = 'all')
{
$this->session->set('kanbanView', $this->app->getURI(true), 'kanban');
$kanban = $this->kanban->getByID($kanbanID);
$users = $this->loadModel('user')->getPairs('noletter|nodeleted');
@@ -419,11 +422,14 @@ class kanban extends control
$userList[$account]['avatar'] = $avatar;
}
$regions = $this->kanban->getKanbanData($kanbanID);
$this->view->users = $users;
$this->view->title = $this->lang->kanban->view;
$this->view->regions = $this->kanban->getKanbanData($kanbanID);
$this->view->userList = $userList;
$this->view->kanban = $kanban;
$this->view->regions = $regions;
$this->view->regionID = isset($regions[$regionID]) ? $regionID : 'all';
$this->display();
}
@@ -469,7 +475,14 @@ class kanban extends control
$regionID = $this->kanban->createRegion($kanban, '', $copyRegionID, $from);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
if($from == 'kanban')
{
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => "parent.loadKanban($kanbanID, $regionID)"));
}
else
{
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
}
}
$regions = $this->kanban->getRegionPairs($kanbanID, 0, $from);
@@ -935,7 +948,7 @@ class kanban extends control
{
$this->kanban->batchCreateCard($kanbanID, $regionID, $groupID, $columnID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $backLink));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->session->kanbanView));
}
$kanbanUsers = $kanbanID == 0 ? ',' : trim($kanban->owner) . ',' . trim($kanban->team);
@@ -1130,7 +1143,7 @@ class kanban extends control
$this->loadModel('action')->create('kanbancard', $cardID, 'importedcard', '', $cards2Imported[$cardID]->kanban);
}
return print(js::locate($this->createLink('kanban', 'view', "kanbanID=$kanbanID"), 'parent.parent'));
return print(js::locate($this->session->kanbanView, 'parent.parent'));
}
/* Find Kanban other than this kanban. */
@@ -1177,7 +1190,7 @@ class kanban extends control
$this->loadModel('action')->create('kanbancard', $cardID, 'importedProductplan', '', $planID);
}
return print(js::locate($this->createLink('kanban', 'view', "kanbanID=$kanbanID"), 'parent.parent'));
return print(js::locate($this->session->kanbanView, 'parent.parent'));
}
$this->loadModel('product');
@@ -1229,7 +1242,7 @@ class kanban extends control
$this->loadModel('action')->create('kanbancard', $cardID, 'importedRelease', '', $releaseID);
}
return print(js::locate($this->createLink('kanban', 'view', "kanbanID=$kanbanID"), 'parent.parent'));
return print(js::locate($this->session->kanbanView, 'parent.parent'));
}
$this->loadModel('product');
@@ -1278,7 +1291,7 @@ class kanban extends control
$this->loadModel('action')->create('kanbancard', $cardID, 'importedBuild', '', $buildID);
}
return print(js::locate($this->createLink('kanban', 'view', "kanbanID=$kanbanID"), 'parent.parent'));
return print(js::locate($this->session->kanbanView, 'parent.parent'));
}
$this->loadModel('build');
@@ -1340,7 +1353,7 @@ class kanban extends control
$this->loadModel('action')->create('kanbancard', $cardID, 'importedExecution', '', $executionID);
}
return print(js::locate($this->createLink('kanban', 'view', "kanbanID=$kanbanID"), 'parent.parent'));
return print(js::locate($this->session->kanbanView, 'parent.parent'));
}
$this->loadModel('project');
+20 -1
View File
@@ -13,7 +13,7 @@
#kanbanContainer.fullscreen {overflow: auto;}
.region {border: 0px solid #dcdcdc; background-color: #fff;}
.region + .region {margin-top: 20px;}
.region + .region {margin-top: 10px;}
.region .region-header {padding: 10px;}
.region .region-header strong {font-size: 14px; color: #3C4353; float: left;}
.region .region-header label {color: #999; background: transparent; border: 1px solid #ddd; margin-left: 10px; margin-right: 10px}
@@ -156,3 +156,22 @@
.region .kanban-board .kanban-header-sub-cols .kanban-header-col > .title > span {max-width: calc(100% - 165px) !important;}
.region .kanban-board .kanban-header-sub-cols .kanban-header-col.left > .title > span {max-width: calc(100% - 120px) !important;}
.executionName .delayed {margin-right: 10px;}
#kanbanBox {position: relative;}
#regionTabs {background-color: #efefef; z-index: 100; display: flex;}
#regionTabs .leftBtn.disabled i, #regionTabs .rightBtn.disabled i{color: #c2c2c2;}
#regionTabs .icon-angle-left, #regionTabs .icon-angle-right {font-size: 20px; vertical-align: -webkit-baseline-middle;}
#regionNavTabs {display: inline-flex; overflow: hidden; width: 100%;}
#regionNavTabs > ul {display: inline-flex; border-bottom: unset;}
#regionNavTabs > ul > li {padding: 6px 15px; margin-bottom: 6px; border-radius: 4px 4px 0px 0px;}
#regionNavTabs > ul > li.active {background-color: #fff;}
#regionNavTabs > ul > li.active::before {content: ''; width: 10px; height: 10px; position: absolute; left: -10px; top: 21px; background: radial-gradient(circle at 0% 0%,transparent 10px,#fff 0);}
#regionNavTabs > ul > li.active::after {content: ''; width: 10px; height: 10px; position: absolute; left: calc(100%); top: 21px; background: radial-gradient(circle at 100% 0%,transparent 10px,#fff 0);}
#regionNavTabs > ul > li > a {max-width: 195px; overflow: hidden; white-space: nowrap; padding: 0;}
#regionNavTabs > ul > li.active > a:before {background: unset;}
#regionTabs.affixed {margin-bottom: 5px; position: fixed; top: 0px;}
#regionTabs.affixed + #kanbanContainer .kanban-board.kanban-affixed > .kanban-header {margin-top: 30px;}
#regionTabs.affixed + #kanbanContainer .kanban-board.kanban-affixed > .kanban-header + .kanban-lane {margin-top: 38px;}
#regionTabs.affixed ul > li {margin-bottom: 0px !important;}
#regionTabs .region-actions .dropdown-menu {top: 25px;}
#region-tab-actions {display: flex; position: relative; right: 0px; top: -2px;}
+13
View File
@@ -148,3 +148,16 @@ function loadOwners(spaceID)
$('#owner').chosen();
});
}
/**
* Load kanban.
*
* @param int $kanbanID
* @param int $regionID
* @access public
* @return void
*/
function loadKanban(kanbanID, regionID)
{
location.href = createLink('kanban', 'view', 'kanbanID=' + kanbanID + '&regionID=' + regionID);
}
+142
View File
@@ -910,6 +910,9 @@ function updateRegion(regionID, regionData)
function updateRegionName(regionID, name)
{
$('.region[data-id="' + regionID + '"] > .region-header > strong:first').text(name);
$('#regionNavTabs li[data-id="' + regionID + '"]').attr('title', name);
$('#regionNavTabs li[data-id="' + regionID + '"]').find('a > span').text(name);
initRegionTabs();
}
/**
@@ -1508,6 +1511,7 @@ $(function()
$(window).on('scroll', function()
{
$.zui.ContextMenu.hide();
if($('#regionTabs').length > 0) updateRegionTabAffixState();
});
$(document).on('click', '#splitTable .btn-plus', function()
@@ -1547,8 +1551,28 @@ $(function()
if(!CRKanban && kanbanInfo.status == 'closed') $('.kanban-col.kanban-header-col').css('padding', '0px 0px 0px 0px');
setToolTip();
$(window).on('resize', initRegionTabs);
$('.leftBtn').click(function()
{
if($(this).hasClass('disabled')) return;
swipeRegionNavTabs($('#regionNavTabs').find('ul'), 'left');
});
$('.rightBtn').click(function()
{
if($(this).hasClass('disabled')) return;
swipeRegionNavTabs($('#regionNavTabs').find('ul'), 'right');
});
});
/**
* Init sortable.
*
* @access public
* @return void
*/
function initSortable()
{
var sortType = '';
@@ -1777,3 +1801,121 @@ function setToolTip()
{
$('[data-toggle="tooltip"]').tooltip({container: 'body'});
}
/**
* Update kanban affix state for all boards in page.
*
* @access public
* @return void
*/
function updateRegionTabAffixState()
{
var $kanbanContainer = $('#kanbanContainer');
var kanbanContainer = $kanbanContainer[0].getBoundingClientRect();
var $regionTabs = $('#regionTabs');
var regionTabs = $regionTabs[0].getBoundingClientRect();
if(regionTabs.top <= 0 && !$regionTabs.hasClass('affixed'))
{
$regionTabs.addClass('affixed');
$regionTabs.find('#region-tab-actions').addClass('hidden');
}
else if($regionTabs.hasClass('affixed') && kanbanContainer.top >= 0)
{
$regionTabs.removeClass('affixed');
$regionTabs.find('#region-tab-actions').removeClass('hidden');
}
initRegionTabs();
}
/**
* Swipe region navigation tabs.
*
* @param object $object
* @param string $direction
* @access public
* @return bool
*/
function swipeRegionNavTabs($object, direction)
{
var $regionNavTabs = $('#regionNavTabs');
var offsetWidth = $regionNavTabs[0].offsetWidth;
var objectWidth = $object[0].offsetWidth;
$object.css('transition-duration', '1s');
$object.find('li').each(function()
{
var radius = $(this).hasClass('active') ? radiusWidth : 0;
/* Get the offset of the item. */
var itemLeft = $(this)[0].offsetLeft;
var itemWidth = $(this)[0].offsetWidth;
var itemOffset = itemLeft + itemWidth;
/* Calculate the offset after sliding. */
if(direction == 'left' && (itemOffset + distance) >= 0)
{
/* If you swipe left, the distance is equal to the item's left. */
distance = - itemLeft + radius;
if(distance + radius >= 0)
{
distance = radius;
$('.leftBtn').addClass('disabled');
}
$object[0].style.transform = 'translateX(' + distance + 'px)';
/* If the width of regionNavTabs plus offsetWidth is less than the width of object, change rightBtn to clickable. */
if(offsetWidth - distance < objectWidth) $('.rightBtn').removeClass('disabled');
return false;
}
if(direction == 'right' && itemOffset > (offsetWidth - distance))
{
/* If you swipe right, the distance is equal to the left distance of item plus the width of item minus the width of the regionNavTabs. */
distance = offsetWidth - itemOffset - radius;
if($(this).next().length == 0)
{
distance = - objectWidth + offsetWidth - radius;
$('.rightBtn').addClass('disabled');
}
$object[0].style.transform = 'translateX(' + distance + 'px)';
/* If distance is less than 0, change leftBtn to clickable. */
if(distance < 0) $('.leftBtn').removeClass('disabled');
return false;
}
});
}
/**
* Init region tabs.
*
* @access public
* @return void
*/
function initRegionTabs()
{
var $regionNavTabs = $('#regionNavTabs');
if($regionNavTabs.length == 0) return;
/* Set the width of regionTab. */
$('#regionTabs').width($('#kanban').outerWidth());
var regionTabsWidth = $regionNavTabs[0].offsetWidth;
var regionTabULWidth = $regionNavTabs.find('ul')[0].offsetWidth;
var $acitiveItem = $('#regionNavTabs > ul > li.active');
var acitiveItemWidth = $acitiveItem[0].offsetWidth;
var acitiveItemLeft = $acitiveItem[0].offsetLeft;
/* Print left and right button. */
if(regionTabULWidth > regionTabsWidth) $('.leftBtn, .rightBtn').removeClass('hidden');
/* Locate the position of the currently selected item. */
radiusWidth = 10;
distance = (acitiveItemLeft + acitiveItemWidth) > regionTabsWidth ? - (acitiveItemLeft + acitiveItemWidth - regionTabsWidth + radiusWidth) : 0;
if($acitiveItem.prev().length == 0) distance = radiusWidth;
$regionNavTabs.find('ul')[0].style.transform = 'translateX(' + distance + 'px)';
if(distance < 0) $('#regionTabs').find('.leftBtn').removeClass('disabled');
if($acitiveItem.next().length != 0 && regionTabULWidth > regionTabsWidth) $('#regionTabs').find('.rightBtn').removeClass('disabled');
}
+1
View File
@@ -361,6 +361,7 @@ $lang->kanbanregion = new stdclass();
$lang->kanbanregion->name = 'Region Name';
$lang->kanbanregion->default = 'Default Region';
$lang->kanbanregion->style = 'Region Style';
$lang->kanbanregion->all = 'All Region';
$lang->kanbanregion->confirmDelete = 'Are you sure to delete this region? After deleting this region, all data in this region will be deleted.';
+1
View File
@@ -361,6 +361,7 @@ $lang->kanbanregion = new stdclass();
$lang->kanbanregion->name = 'Region Name';
$lang->kanbanregion->default = 'Default Region';
$lang->kanbanregion->style = 'Region Style';
$lang->kanbanregion->all = 'All Region';
$lang->kanbanregion->confirmDelete = 'Are you sure to delete this region? After deleting this region, all data in this region will be deleted.';
+1
View File
@@ -361,6 +361,7 @@ $lang->kanbanregion = new stdclass();
$lang->kanbanregion->name = 'Region Name';
$lang->kanbanregion->default = 'Default Region';
$lang->kanbanregion->style = 'Region Style';
$lang->kanbanregion->all = 'All Region';
$lang->kanbanregion->confirmDelete = 'Are you sure to delete this region? After deleting this region, all data in this region will be deleted.';
+1
View File
@@ -361,6 +361,7 @@ $lang->kanbanregion = new stdclass();
$lang->kanbanregion->name = '区域名称';
$lang->kanbanregion->default = '默认区域';
$lang->kanbanregion->style = '区域样式';
$lang->kanbanregion->all = '所有区域';
$lang->kanbanregion->confirmDelete = '您确认删除该区域吗?删除该区域后,该区域中所有数据将会被删除。';
Executable → Regular
+101 -41
View File
@@ -45,56 +45,116 @@ js::set('mode', $config->systemMode);
js::set('alignment', $kanban->alignment);
js::set('priv', array('canAssignCard' => common::hasPriv('kanban', 'assigncard')));
$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1;
$canEditRegion = commonModel::hasPriv('kanban', 'editRegion');
$canCreateRegion = commonModel::hasPriv('kanban', 'createRegion');
$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion');
$canCreateLane = commonModel::hasPriv('kanban', 'createLane');
$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1;
$canEditRegion = commonModel::hasPriv('kanban', 'editRegion');
$canCreateRegion = commonModel::hasPriv('kanban', 'createRegion');
$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion');
$canCreateLane = commonModel::hasPriv('kanban', 'createLane');
$canViewArchivedCard = commonModel::hasPriv('kanban', 'viewArchivedCard');
$canViewArchivedColumn = commonModel::hasPriv('kanban', 'viewArchivedColumn');
?>
<div class='panel' id='kanbanContainer'>
<div class='panel-body'>
<div id="kanban" data-id='<?php echo $kanban->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">
<strong><?php echo $region->name;?></strong>
<i class="icon icon-angle-top btn-link" data-id="<?php echo $region->id;?>"></i>
<div class='region-actions'>
<?php if(($canEditRegion || $canCreateLane || $canDeleteRegion || $canCreateRegion) and !(isset($this->config->CRKanban) and $this->config->CRKanban == '0' and $kanban->status == 'closed')):?>
<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($canCreateRegion) echo '<li>' . html::a(inlink('createRegion', "kanbanID={$kanban->id}", '', 1), '<i class="icon icon-plus"></i>' . $this->lang->kanban->createRegion, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
<?php if($canEditRegion) echo '<li>' . html::a(inlink('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(inlink('createLane', "kanbanID={$kanban->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(inlink('deleteRegion', "regionID={$region->id}"), '<i class="icon icon-trash"></i>' . $this->lang->kanban->deleteRegion, "hiddenwin") . '</li>';?>
</ul>
<?php endif;?>
</div>
<div class='region-actions'>
<?php $canViewArchivedCard = commonModel::hasPriv('kanban', 'viewArchivedCard');?>
<?php $canViewArchivedColumn = commonModel::hasPriv('kanban', 'viewArchivedColumn');?>
<?php if(($canViewArchivedCard or $canViewArchivedColumn) and $kanban->archived):?>
<div>
<button data-toggle="dropdown" class="btn btn-link action" type="button" title=<?php echo $this->lang->kanban->archived;?>>
<span><?php echo $this->lang->kanban->archived;?></span>
<span class="caret"></span>
</button>
<div id='kanbanBox'>
<?php if(count($regions) > 1):?>
<div id='regionTabs'>
<div class='leftBtn hidden disabled'><a><i class='icon icon-angle-left'></i></a></div>
<div id='regionNavTabs'>
<ul class="nav nav-tabs">
<li data-id='all' class="<?php echo $regionID === 'all' ? 'active' : '';?>" title="<?php echo $lang->kanbanregion->all;?>">
<a href="<?php echo inlink('view', "kanbanID=$kanban->id&regionID=all");?>" class="<?php echo $regionID === 'all' ? 'btn-active-text' : '';?>">
<span class='text'><?php echo $lang->kanbanregion->all;?></span>
</a>
</li>
<?php foreach($regions as $region):?>
<li data-id="<?php echo $region->id;?>" class="<?php echo $region->id == $regionID ? 'active' : '';?>" title="<?php echo $region->name;?>">
<a href="<?php echo inlink('view', "kanbanID=$kanban->id&regionID=$region->id");?>" class="<?php echo $region->id == $regionID ? 'btn-active-text' : '';?>">
<span class='text'><?php echo $region->name;?></span>
</a>
</li>
<?php endforeach;?>
</ul>
</div>
<div class='rightBtn hidden disabled'><a><i class='icon icon-angle-right'></i></a></div>
<?php if($regionID !== 'all'):?>
<div id='region-tab-actions'>
<div class='region-actions'>
<?php if(($canViewArchivedCard or $canViewArchivedColumn) and $kanban->archived):?>
<div>
<button data-toggle="dropdown" class="btn btn-link action" type="button" title=<?php echo $this->lang->kanban->archived;?>>
<span><?php echo $this->lang->kanban->archived;?></span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right">
<?php if($canViewArchivedCard) echo '<li>' . html::a("javascript:loadMore(\"Card\", $regionID)", '<i class="icon icon-card-archive"></i>' . $this->lang->kanban->viewArchivedCard) . '</li>';?>
<?php if($canViewArchivedColumn) echo '<li>' . html::a("javascript:loadMore(\"Column\", $regionID)", '<i class="icon icon-col-archive"></i>' . $this->lang->kanban->viewArchivedColumn) . '</li>';?>
</ul>
</div>
<?php endif;?>
<ul class="dropdown-menu pull-right">
<?php echo '<li>' . html::a(inlink('viewArchivedCard', "", '', 1), '<i class="icon icon-card-archive"></i>' . $this->lang->kanban->viewArchivedCard, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
<?php echo '<li>' . html::a(inlink('viewArchivedColumn', "", '', 1), '<i class="icon icon-col-archive"></i>' . $this->lang->kanban->viewArchivedColumn, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
</ul>
</div>
<div class='region-actions'>
<?php if(($canEditRegion || $canCreateLane || $canDeleteRegion || $canCreateRegion) and !(isset($this->config->CRKanban) and $this->config->CRKanban == '0' and $kanban->status == 'closed')):?>
<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($canCreateRegion) echo '<li>' . html::a(inlink('createRegion', "kanbanID={$kanban->id}", '', 1), '<i class="icon icon-plus"></i>' . $this->lang->kanban->createRegion, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
<?php if($canEditRegion) echo '<li>' . html::a(inlink('editRegion', "regionID={$regionID}", '', 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(inlink('createLane', "kanbanID={$kanban->id}&regionID={$regionID}", '', 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(inlink('deleteRegion', "regionID={$regionID}"), '<i class="icon icon-trash"></i>' . $this->lang->kanban->deleteRegion, "hiddenwin") . '</li>';?>
</ul>
<?php endif;?>
</div>
</div>
<?php endif;?>
</div>
<?php endif;?>
<div class='panel' id='kanbanContainer'>
<div class='panel-body'>
<div id="kanban" data-id='<?php echo $kanban->id;?>'>
<?php $regionGroup = ($regionID !== 'all' and isset($regions[$regionID])) ? array($regionID => $regions[$regionID]) : $regions;?>
<?php foreach($regionGroup as $region):?>
<div class="region<?php if($canSortRegion) echo ' sort';?>" data-id="<?php echo $region->id;?>">
<div class="region-header dropdown">
<?php if($regionID === 'all'):?>
<strong><?php echo $region->name;?></strong>
<i class="icon icon-angle-top btn-link" data-id="<?php echo $region->id;?>"></i>
<div class='region-actions'>
<?php if(($canEditRegion || $canCreateLane || $canDeleteRegion || $canCreateRegion) and !(isset($this->config->CRKanban) and $this->config->CRKanban == '0' and $kanban->status == 'closed')):?>
<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($canViewArchivedCard) echo '<li>' . html::a("javascript:loadMore(\"Card\", $region->id)", '<i class="icon icon-card-archive"></i>' . $this->lang->kanban->viewArchivedCard) . '</li>';?>
<?php if($canViewArchivedColumn) echo '<li>' . html::a("javascript:loadMore(\"Column\", $region->id)", '<i class="icon icon-col-archive"></i>' . $this->lang->kanban->viewArchivedColumn) . '</li>';?>
<?php if($canCreateRegion) echo '<li>' . html::a(inlink('createRegion', "kanbanID={$kanban->id}", '', 1), '<i class="icon icon-plus"></i>' . $this->lang->kanban->createRegion, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
<?php if($canEditRegion) echo '<li>' . html::a(inlink('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(inlink('createLane', "kanbanID={$kanban->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(inlink('deleteRegion', "regionID={$region->id}"), '<i class="icon icon-trash"></i>' . $this->lang->kanban->deleteRegion, "hiddenwin") . '</li>';?>
</ul>
<?php endif;?>
</div>
<div class='region-actions'>
<?php if(($canViewArchivedCard or $canViewArchivedColumn) and $kanban->archived):?>
<div>
<button data-toggle="dropdown" class="btn btn-link action" type="button" title=<?php echo $this->lang->kanban->archived;?>>
<span><?php echo $this->lang->kanban->archived;?></span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right">
<?php if($canViewArchivedCard) echo '<li>' . html::a("javascript:loadMore(\"Card\", $region->id)", '<i class="icon icon-card-archive"></i>' . $this->lang->kanban->viewArchivedCard) . '</li>';?>
<?php if($canViewArchivedColumn) echo '<li>' . html::a("javascript:loadMore(\"Column\", $region->id)", '<i class="icon icon-col-archive"></i>' . $this->lang->kanban->viewArchivedColumn) . '</li>';?>
</ul>
</div>
<?php endif;?>
<ul class="dropdown-menu pull-right">
<?php echo '<li>' . html::a(inlink('viewArchivedCard', "", '', 1), '<i class="icon icon-card-archive"></i>' . $this->lang->kanban->viewArchivedCard, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
<?php echo '<li>' . html::a(inlink('viewArchivedColumn', "", '', 1), '<i class="icon icon-col-archive"></i>' . $this->lang->kanban->viewArchivedColumn, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
</ul>
</div>
<?php endif;?>
<ul class="dropdown-menu pull-right">
<?php echo '<li>' . html::a(inlink('viewArchivedCard', "", '', 1), '<i class="icon icon-card-archive"></i>' . $this->lang->kanban->viewArchivedCard, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
<?php echo '<li>' . html::a(inlink('viewArchivedColumn', "", '', 1), '<i class="icon icon-col-archive"></i>' . $this->lang->kanban->viewArchivedColumn, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
</ul>
</div>
<div id='kanban<?php echo $region->id;?>' data-id='<?php echo $region->id;?>' class='kanban'></div>
</div>
<div id='kanban<?php echo $region->id;?>' data-id='<?php echo $region->id;?>' class='kanban'></div>
<?php endforeach;?>
</div>
<?php endforeach;?>
</div>
</div>
</div>
+2
View File
@@ -1,5 +1,7 @@
#budget {border-right: 0px;}
#budget:focus {border-right: 1px solid #0c64eb;}
#budgetUnit {border-left: 0px;}
#budgetUnit:focus {border-left: 1px solid #0c64eb;}
#dateRange, .futureBox {vertical-align: top !important; padding-top: 13px !important;}
#endList {vertical-align: top; padding-top: 13px;}
[lang^=en] #endList {vertical-align: top; padding-top: 20px;}
+16 -10
View File
@@ -1201,10 +1201,12 @@ class programModel extends model
* Get program parent pairs
*
* @param string $model
* @param string $mode
* @param bool $showRoot
* @access public
* @return array
*/
public function getParentPairs($model = '', $mode = 'noclosed')
public function getParentPairs($model = '', $mode = 'noclosed', $showRoot = true)
{
$modules = $this->dao->select('id,name,parent,path,grade')->from(TABLE_PROGRAM)
->where('type')->eq('program')
@@ -1219,7 +1221,7 @@ class programModel extends model
{
if(strpos(",{$this->app->user->view->programs},", ",{$module->id},") === false and (!$this->app->user->admin)) continue;
$moduleName = '/';
$moduleName = $showRoot ? '/' : '';
$parentModules = explode(',', $module->path);
foreach($parentModules as $parentModuleID)
{
@@ -1240,7 +1242,7 @@ class programModel extends model
$topMenu = array_shift($treeMenu);
$topMenu = empty($topMenu) ? '' : $topMenu;
$topMenu = explode("\n", trim($topMenu));
$lastMenu[] = '/';
$showRoot ? $lastMenu[] = '/' : $lastMenu = array();
foreach($topMenu as $menu)
{
if(strpos($menu, '|') === false) continue;
@@ -1409,18 +1411,22 @@ class programModel extends model
}
/* Get the number of project teams. */
$teams = $this->dao->select('root,count(*) as teams')->from(TABLE_TEAM)
->where('root')->in($projectKeys)
->andWhere('type')->eq('project')
->groupBy('root')
$teams = $this->dao->select('t1.root,count(t1.id) as teams')->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
->where('t1.root')->in($projectKeys)
->andWhere('t1.type')->eq('project')
->andWhere('t2.deleted')->eq(0)
->groupBy('t1.root')
->fetchAll('root');
/* Get the members of project teams. */
if($this->cookie->projectType and $this->cookie->projectType == 'bycard')
{
$teamMembers = $this->dao->select('root,account')->from(TABLE_TEAM)
->where('root')->in($projectKeys)
->andWhere('type')->eq('project')
$teamMembers = $this->dao->select('t1.root,t1.account')->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
->where('t1.root')->in($projectKeys)
->andWhere('t1.type')->eq('project')
->andWhere('t2.deleted')->eq(0)
->fetchGroup('root', 'account');
}
+10 -6
View File
@@ -219,9 +219,11 @@ class projectModel extends model
if(empty($projects)) return array();
$projectIdList = array_keys($projects);
$teams = $this->dao->select('root, count(*) as count')->from(TABLE_TEAM)
->where('root')->in($projectIdList)
->groupBy('root')
$teams = $this->dao->select('t1.root, count(t1.id) as count')->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
->where('t1.root')->in($projectIdList)
->andWhere('t2.deleted')->eq(0)
->groupBy('t1.root')
->fetchAll('root');
$condition = $this->config->systemMode == 'classic' ? 't2.id as project' : 't2.parent as project';
@@ -298,9 +300,11 @@ class projectModel extends model
if(empty($projects)) return array();
$projectIdList = array_keys($projects);
$teams = $this->dao->select('root, count(*) as teams')->from(TABLE_TEAM)
->where('root')->in($projectIdList)
->andWhere('type')->eq('project')
$teams = $this->dao->select('t1.root, count(t1.id) as teams')->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
->where('t1.root')->in($projectIdList)
->andWhere('t1.type')->eq('project')
->andWhere('t2.deleted')->eq(0)
->groupBy('root')->fetchPairs();
$hours = $this->dao->select('t2.parent as project, ROUND(SUM(t1.consumed), 1) AS consumed, ROUND(SUM(t1.estimate), 1) AS estimate')->from(TABLE_TASK)->alias('t1')
+1 -1
View File
@@ -105,7 +105,7 @@
<?php
foreach($setting as $value)
{
if($value->id == 'status' and $browseType !== 'all') $value->show = false;
if($value->id == 'status' and strpos(',all,bysearch,', ",$browseType,") === false) $value->show = false;
if($value->id == 'teamCount' and $browseType == 'all') $value->show = false;
if(commonModel::isTutorialMode() && ($value->id == 'PM' || $value->id == 'budget' || $value->id == 'teamCount')) $value->show = false;
if($value->show) $this->datatable->printHead($value, $orderBy, $vars, $canBatchEdit);
+1
View File
@@ -2,3 +2,4 @@
#showAllModuleBox{width:60px;}
#showAllModuleBox .no-margin {padding-left:22px;}
#taskTeamEditor .sortable .input-group .input-group-addon.required:after {top: 10px; right: 2px; z-index: 3;}
#storyIdBox .chosen-container .chosen-drop.chosen-no-wrap>.chosen-results>li {text-overflow: unset;}
+1 -1
View File
@@ -127,7 +127,7 @@ foreach(explode(',', $config->task->edit->requiredFields) as $field)
<?php if($execution->type != 'ops'):?>
<tr>
<th><?php echo $lang->task->story;?></th>
<td><span id="storyIdBox"><?php echo html::select('story', $stories, $task->story, "class='form-control chosen' data-drop_direction='down'");?></span></td>
<td><span id="storyIdBox"><?php echo html::select('story', $stories, $task->story, "class='form-control chosen' data-drop_direction='down' data-max_drop_width='0'");?></span></td>
</tr>
<?php endif;?>
<?php if($task->parent >= 0 and empty($task->team)):?>
+2
View File
@@ -1 +1,3 @@
th.required:after {position: relative; right: 10px;}
.main-form tbody > tr > th {padding: 2em;}
.main-form tfoot > tr > td {padding-top: 0px;}
+2 -2
View File
@@ -108,7 +108,7 @@
</tr>
<?php endforeach;?>
<tr>
<th colspan='2'><?php echo $lang->user->verifyPassword?></th>
<th colspan='4'><?php echo $lang->user->verifyPassword?></th>
<td colspan='2'>
<div class="required required-wrapper"></div>
<input type='text' style="display:none"> <!-- for disable autocomplete all browser -->
@@ -119,7 +119,7 @@
</tbody>
<tfoot>
<tr>
<td colspan="<?php echo count($visibleFields) + 4;?>" class="text-center form-actions">
<td colspan="<?php echo count($visibleFields) + 6;?>" class="text-center form-actions">
<?php echo html::submitButton($lang->save);?>
<?php echo html::backButton();?>
</td>
+11 -6
View File
@@ -9,9 +9,11 @@ title=测试 programModel::getParentPairs();
cid=1
pid=1
获取所有父项目集的数量 >> 7
获取瀑布类型父项目集的数量 >> 1
获取ID为1的父项目集的名称 >> /项目集1
获取所有父项目集的数量 >> 7
获取瀑布类型父项目集的数量 >> 1
获取ID为1的父项目集的名称 >> /项目集1
获取父项目集的名称显示根目录 >> /
获取ID为1的父项目集的名称不显示根目录 >> 项目集1
*/
@@ -19,7 +21,10 @@ global $tester;
$tester->loadModel('program');
$program1 = $tester->program->getParentPairs();
$program2 = $tester->program->getParentPairs('waterfall');
$program3 = $tester->program->getParentPairs('', 'noclosed', false);
r(count($program1)) && p() && e('7'); // 获取所有父项目集的数量
r(count($program2)) && p() && e('1'); // 获取瀑布类型父项目集的数量
r($program1) && p('1') && e('/项目集1'); // 获取ID为1的父项目集的名称
r(count($program1)) && p() && e('7'); // 获取所有父项目集的数量
r(count($program2)) && p() && e('1'); // 获取瀑布类型父项目集的数量
r($program1) && p('1') && e('/项目集1'); // 获取ID为1的父项目集的名称
r($program1) && p('0') && e('/'); // 获取父项目集的名称显示根目录
r(array_shift($program3)) && p() && e('项目集1'); // 获取ID为1的父项目集的名称不显示根目录
+1
View File
@@ -107,6 +107,7 @@ body.xxc-embed.m-productplan-view,body.xxc-embed.m-bug-browse,body.xxc-embed.m-p
#downloadMobile .dropdown-menu {top: -31px !important; min-width: 130px; padding: 0;}
#downloadMobile .dropdown-menu .mobile-qrcode {padding: 15px 0px 0 10px;}
#downloadMobile .dropdown-menu .mobile-qrcode.local-img {padding: 15px 5px 0 5px;}
#downloadMobile .dropdown-menu .mobile-qrcode iframe {width: 110px; height: 120px; text-align: center;}
#downloadMobile .dropdown-menu .mobile-version {text-align: center;}
#downloadMobile .dropdown-menu .mobile-version span {color: #0c64eb; font-size: 12px; font-weight: 700;}
File diff suppressed because one or more lines are too long