Merge branch 'kanban' into 'sprint/177_tianshujie_45815'

# Conflicts:
#   module/kanban/control.php
#   module/kanban/model.php
This commit is contained in:
孙广明
2021-12-09 07:54:42 +00:00
21 changed files with 1410 additions and 37 deletions
+6
View File
@@ -237,10 +237,13 @@ define('TABLE_RELATION', '`' . $config->db->prefix . 'relation`');
define('TABLE_REPOHISTORY', '`' . $config->db->prefix . 'repohistory`');
define('TABLE_REPOFILES', '`' . $config->db->prefix . 'repofiles`');
define('TABLE_REPOBRANCH', '`' . $config->db->prefix . 'repobranch`');
define('TABLE_KANBAN', '`' . $config->db->prefix . 'kanban`');
define('TABLE_KANBANSPACE', '`' . $config->db->prefix . 'kanbanspace`');
define('TABLE_KANBANREGION', '`' . $config->db->prefix . 'kanbanregion`');
define('TABLE_KANBANLANE', '`' . $config->db->prefix . 'kanbanlane`');
define('TABLE_KANBANCOLUMN', '`' . $config->db->prefix . 'kanbancolumn`');
define('TABLE_KANBANORDER', '`' . $config->db->prefix . 'kanbanorder`');
define('TABLE_KANBANGROUP', '`' . $config->db->prefix . 'kanbangroup`');
if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`');
if(!defined('TABLE_PROJECTSPEC')) define('TABLE_PROJECTSPEC', '`' . $config->db->prefix . 'projectspec`');
@@ -279,10 +282,13 @@ $config->objectTables['job'] = TABLE_JOB;
$config->objectTables['team'] = TABLE_TEAM;
$config->objectTables['pipeline'] = TABLE_PIPELINE;
$config->objectTables['mr'] = TABLE_MR;
$config->objectTables['kanban'] = TABLE_KANBAN;
$config->objectTables['kanbanspace'] = TABLE_KANBANSPACE;
$config->objectTables['kanbanregion'] = TABLE_KANBANREGION;
$config->objectTables['kanbancolumn'] = TABLE_KANBANCOLUMN;
$config->objectTables['kanbanlane'] = TABLE_KANBANLANE;
$config->objectTables['kanbanorder'] = TABLE_KANBANORDER;
$config->objectTables['kanbangroup'] = TABLE_KANBANGROUP;
$config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme');
+1
View File
@@ -36,6 +36,7 @@ $config->action->objectNameFields['mr'] = 'title';
$config->action->objectNameFields['kanbancolumn'] = 'name';
$config->action->objectNameFields['kanbanlane'] = 'name';
$config->action->objectNameFields['kanbanspace'] = 'name';
$config->action->objectNameFields['kanban'] = 'name';
$config->action->commonImgSize = 870;
+1
View File
@@ -542,6 +542,7 @@ $lang->action->label->design = 'Design|design|view|designID=%s';
$lang->action->label->stakeholder = 'Stakeholder|stakeholder|view|userID=%s';
$lang->action->label->api = 'Interface|api|index|libID=%s&moduleID=%s&apiID=%s';
$lang->action->label->kanbanspace = 'Kanban Space|kanban|space|browseType=all';
$lang->action->label->kanban = 'Kanban|kanban|view|kanbanID=%s';
$lang->action->label->kanbancolumn = 'Kanban Column|execution|kanban|execution=%s';
$lang->action->label->kanbanlane = 'Kanban Lane|execution|kanban|execution=%s&type=all';
$lang->action->label->mr = 'Merge Request|mr|view|id=%s';
+1
View File
@@ -542,6 +542,7 @@ $lang->action->label->design = '设计|design|view|designID=%s';
$lang->action->label->stakeholder = '干系人|stakeholder|view|userID=%s';
$lang->action->label->api = '接口|api|index|libID=%s&moduleID=%s&apiID=%s';
$lang->action->label->kanbanspace = '看板空间|kanban|space|browseType=all';
$lang->action->label->kanban = '看板|kanban|view|kanbanID=%s';
$lang->action->label->kanbancolumn = '看板列|execution|kanban|execution=%s';
$lang->action->label->kanbanlane = '看板泳道|execution|kanban|execution=%s&type=all';
$lang->action->label->mr = '合并请求|mr|view|id=%s';
+1 -1
View File
@@ -401,7 +401,7 @@ class design extends control
public function revision($repoID = 0, $projectID = 0)
{
$repo = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('id')->eq($repoID)->fetch();
$repoURL = $this->createLink('repo', 'revision', "repoID=$repo->repo&objectID=$projectID&revistion=$repo->revision");
$repoURL = $this->createLink('repo', 'revision', "repoID=$repo->repo&objectID=$projectID&revistion=$repo->revision", '', true);
header("location:" . $repoURL);
}
+1 -1
View File
@@ -249,7 +249,7 @@ class designModel extends model
$design->commit = '';
$relations = $this->loadModel('common')->getRelations('design', $designID, 'commit');
foreach($relations as $relation) $design->commit .= html::a(helper::createLink('design', 'revision', "repoID=$relation->BID&projectID={$design->project}"), "#$relation->BID", '_blank');
foreach($relations as $relation) $design->commit .= html::a(helper::createLink('design', 'revision', "repoID=$relation->BID&projectID={$design->project}"), "#$relation->BID", '', "class=iframe");
return $this->loadModel('file')->replaceImgURL($design, 'desc');
}
+1 -1
View File
@@ -69,7 +69,7 @@
</tr>
<tr>
<th><?php echo $lang->design->story;?></th>
<td><?php echo $design->story ? html::a($this->createLink('story', 'view', "id=$design->story"), zget($stories, $design->story)) : '';?></td>
<td><?php echo $design->story ? html::a($this->createLink('story', 'view', "id=$design->story", '', true), zget($stories, $design->story), '',"class=iframe") : '';?></td>
</tr>
<tr>
<th><?php echo $lang->design->submission;?></th>
+1
View File
@@ -26,6 +26,7 @@ $config->kanban->editor->create = array('id' => 'desc', 'tools' => 'simpleT
$config->kanban->editor->edit = array('id' => 'desc', 'tools' => 'simpleTools');
$config->kanban->editor->createspace = array('id' => 'desc', 'tools' => 'simpleTools');
$config->kanban->editor->editspace = array('id' => 'desc', 'tools' => 'simpleTools');
$config->kanban->editor->close = array('id' => 'comment', 'tools' => 'simpleTools');
$config->kanban->default = new stdclass();
$config->kanban->default->story = new stdclass();
+48 -2
View File
@@ -54,7 +54,7 @@ class kanban extends control
if(dao::isError()) die(js::error(dao::getError()));
$this->loadModel('action')->create('kanbanSpace', $spaceID, 'created');
die(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
die(js::reload('parent.parent'));
}
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
@@ -80,7 +80,7 @@ class kanban extends control
$actionID = $this->loadModel('action')->create('kanbanSpace', $spaceID, 'edited');
$this->action->logHistory($actionID, $changes);
die(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
die(js::reload('parent.parent'));
}
$this->view->space = $this->kanban->getSpaceById($spaceID);
@@ -111,7 +111,53 @@ class kanban extends control
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
$this->view->spaceID = $spaceID;
$this->view->spacePairs = array('' => '') + $this->kanban->getSpacePairs();
$this->display();
}
* Close a kanban.
*
* @param int $kanbanID
* @access public
* @return void
*/
public function close($kanbanID)
{
$this->loadModel('action');
if(!empty($_POST))
{
$changes = $this->kanban->close($kanbanID);
if(dao::isError()) die(js::error(dao::getError()));
$actionID = $this->action->create('kanban', $kanbanID, 'closed', $this->post->comment);
$this->action->logHistory($actionID, $changes);
die(js::reload('parent.parent'));
}
$this->view->kanban = $this->kanban->getByID($kanbanID);
$this->view->actions = $this->action->getList('kanban', $kanbanID);
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->display();
}
/**
* View a kanban.
*
* @param int $kanbanID
* @access public
* @return void
*/
public function view($kanbanID)
{
$kanban = $this->kanban->getByID($kanbanID);
$this->view->regions = $this->kanban->getKanbanData($kanbanID);
$this->view->kanban = $kanban;
$this->display();
}
+81
View File
@@ -0,0 +1,81 @@
body {background-color: #fff}
#mainNavbar + #mainContent {background-color: #fff;}
#mainNavbar + #mainContent.scrollbar-hover {margin-top: 0px; overflow: scroll;}
#TRAction .icon {padding: 0px 6px; font-size: 14px}
#searchbox {display: none;}
.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 {color: #ff4268;}
.dropdown-menu > li {padding: 0 10px;}
.dropdown-menu > li > a > .icon {position: relative; left: -5px;}
.region {border: 1px solid #dcdcdc; margin-bottom: 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.sort .region-header {cursor: move;}
.region .kanban {padding: 0px 10px 10px 10px; min-height: unset;}
.region .kanban .form-actions {margin: 0;}
.region .kanban .form-actions .btn {margin-right: 10px; min-width: 50px;}
.region .kanban-board {overflow-x: auto; min-width: 100%!important;}
.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 {margin: 0}
.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial;}
.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: 5px}
.region .kanban-lane {position: relative; border-bottom: none!important; min-height: 200px}
.region .kanban-lane {min-width: max-content; min-width: -moz-max-content}
.region .kanban-lane > .kanban-lane-name > .text {margin: auto 0; max-height: 100px;}
.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.drop-to {outline: 3px solid rgba(255, 190, 57, 0.5)!important; background-color: rgba(255, 190, 57, .1);}
.region .kanban-lane-col.can-drop-here {outline: none; background-color: rgba(61, 198, 253, .075);}
.region .kanban-lane-col.has-scrollbar {padding-bottom: 50px;}
.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 > .title {display: block; max-height: 38px; overflow: hidden; color: inherit; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;}
.region .kanban-item > .title:hover {color: #2272eb}
.region .kanban-item > .info {margin-top: 5px; position: relative;}
.region .kanban-item > .info > .pri {height: 14px; border-width: 1px; font-size: 12px; line-height: 12px; min-width: 14px; padding: 0 1px;}
.region .kanban-item > .info > .time {margin-left: 10px; font-size: 12px}
.region .kanban-item > .info > .user {position: absolute; right: 0; top: 0}
.region .kanban-item > .actions {position: absolute; top: 0px; right: 4px; opacity: 0;}
.region .kanban-item > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.region .kanban-item > .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 > .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-col[data-type=ADD] {display: none;}
.kanban-col[data-type=EMPTY] {display: none;}
.kanban-item:hover .title {padding-right: 10px;}
.gray .actions {display:none;}
+848
View File
@@ -0,0 +1,848 @@
function loadMore(type)
{
var method = 'browseArchived' + type;
var selector = '#more' + type + 's';
var link = createLink('kanban', method, 'kanbanID=' + kanbanID);
$(selector).load(link, function()
{
var windowHeight = $(window).height();
$(selector + ' .panel-body').css('height', windowHeight - 100);
$(selector + ' .avatar').renderAvatar();
$(selector).animate({right: 0}, 500);
});
}
/**
* Display the kanban in full screen.
*
* @access public
* @return void
*/
function fullScreen()
{
var element = document.getElementById('mainContent');
var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullscreen;
if(requestMethod)
{
var afterEnterFullscreen = function()
{
$('#mainContent').addClass('scrollbar-hover');
$.cookie('isFullScreen', 1);
};
var whenFailEnterFullscreen = function(error)
{
$.cookie('isFullScreen', 0);
};
try
{
var result = requestMethod.call(element);
if(result && (typeof result.then === 'function' || result instanceof window.Promise))
{
result.then(afterEnterFullscreen).catch(whenFailEnterFullscreen);
}
else
{
afterEnterFullscreen();
}
}
catch (error)
{
whenFailEnterFullscreen(error);
}
}
}
/**
* Exit full screen.
*
* @access public
* @return void
*/
function exitFullScreen()
{
$('#mainContent').removeClass('scrollbar-hover');
$.cookie('isFullScreen', 0);
}
document.addEventListener('fullscreenchange', function (e)
{
if(!document.fullscreenElement) exitFullScreen();
});
document.addEventListener('webkitfullscreenchange', function (e)
{
if(!document.webkitFullscreenElement) exitFullScreen();
});
document.addEventListener('mozfullscreenchange', function (e)
{
if(!document.mozFullScreenElement) exitFullScreen();
});
document.addEventListener('msfullscreenChange', function (e)
{
if(!document.msfullscreenElement) exitFullScreen();
});
/**
* Render header of a column.
*/
function renderHeaderCol($column, column, $header, kanbanData)
{
/* Render group header. */
var privs = kanbanData.actions;
if(privs.includes('sortGroup'))
{
var groups = regions[column.region].groups;
if($header.closest('.kanban').data('zui.kanban'))
{
groups = $header.closest('.kanban').data('zui.kanban').data;
}
if(groups.length > 1)
{
$column.closest('.kanban-board').addClass('sort');
$column.closest('.kanban-header').find('.kanban-group-header').remove();
$column.closest('.kanban-header').prepend('<div class="kanban-group-header"><i class="icon icon-md icon-move"></i></div>');
}
}
/* Render more menu. */
if(!$column.children('.actions').length) $column.append('<div class="actions"></div>');
var $actions = $column.children('.actions');
$actions.attr('title', lang.more);
$actions.html('<button class="btn btn-link action" data-contextmenu="column" data-column="' + column.id + '" data-kanban="' + kanban.id + '"><i class="icon icon-more-v"></i></button>');
}
/**
* Render items count of a column.
*/
function renderCount($count, count, column)
{
/* Render WIP. */
var limit = !column.limit || column.limit == '-1' ? '<i class="icon icon-md icon-infinite"></i>' : column.limit;
if($count.parent().find('.limit').length)
{
$count.parent().find('.limit').html(limit);
}
else
{
$count.parent().find('.count').before("<span class='include-first text-grey'>(</span>");
$count.parent().find('.count').after("<span class='divider text-grey'>/</span><span class='limit text-grey'>" + limit + "</span><span class='include-last text-grey'>)</span>");
}
if(column.limit != -1 && column.limit < count)
{
$count.parents('.title').parent('.kanban-header-col').css('background-color', '#F6A1A1');
$count.parents('.title').find('.text').css('max-width', $count.parents('.title').width() - 200);
$count.css('color', '#E33030');
if(!$count.parent().find('.error').length) $count.parent().find('.include-last').after("<span class='error text-grey'>(" + kanbanLang.limitExceeded + ")</span>");
}
else
{
$count.parents('.title').parent('.kanban-header-col').css('background-color', 'transparent');
$count.parents('.title').find('.text').css('max-width', $count.parents('.title').width() - 120);
$count.css('color', '#8B91A2');
$count.parent().find('.error').remove();
}
}
function renderLaneName($lane, lane, $kanban, columns, kanban)
{
var canEdit = lane.actions.includes('editLane');
var canSort = lane.actions.includes('sortLane') && kanban.lanes.length > 1;
var canDelete = lane.actions.includes('deleteLane');
$lane.parent().toggleClass('sort', canSort);
if(!$lane.children('.actions').length && (canEdit || canDelete))
{
$([
'<div class="actions" title="' + lang.more + '">',
'<a data-contextmenu="lane" data-lane="' + lane.id + '" data-kanban="' + kanban.id + '">',
'<i class="icon icon-more-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($lane);
}
}
/**
* The function for rendering kanban item
*/
function renderKanbanItem(item, $item)
{
if(item.status == 'done')
{
$item.addClass('gray');
}
var $title = $item.children('.title');
if(!$title.length)
{
$title = $('<a class="title" data-toggle="modal" data-width="80%"></a>')
.appendTo($item);
}
$title.text(item.name);
$title.attr('href', createLink('sys.task', 'view', 'taskID=' + item.id + '&from=kanban'));
var $more = $item.children('.actions');
if(!$more.length)
{
$(
[
'<div class="actions" title="' + lang.more + '">',
'<button class="btn btn-link action" data-contextmenu="task" data-id="' + item.id + '" data-status="' + item.status + '">',
'<i class="icon icon-more-v"></i>',
'</button>',
'</div>'
].join('')).appendTo($item);
}
var $info = $item.children('.info');
if(!$info.length) $info = $(
[
'<div class="info">',
'<span class="pri"></span>',
'<span class="time label label-light"></span>',
'<div class="user"></div>',
'</div>'
].join('')).appendTo($item);
$item.data('task', item);
$info.children('.pri')
.attr('class', 'pri label-pri label-pri-' + item.pri)
.text(item.pri);
var $time = $info.children('.time');
if(item.deadline && item.deadline !== '0000-00-00')
{
var deadline = $.zui.createDate(item.deadline);
var today = $.zui.createDate(today);
var isExpired = deadline.getTime() < today.getTime();
var dateFormat = (today.getFullYear() === deadline.getFullYear() ? 'MM-dd ' : 'yyyy-MM-dd ') + kanbanLang.deadline;
$time.text($.zui.formatDate(deadline, dateFormat))
.toggleClass('text-red', isExpired)
.show();
}
else
{
$time.hide();
}
var $user = $info.children('.user');
if(item.assignedTo)
{
var member = members[item.assignedTo] || {account: item.assignedTo};
var canAssign = item.status !== 'closed' && item.status !== 'cancel';
var $avatar = $(
canAssign ? '<a class="avatar avatar-sm avatar-user" data-toggle="modal"></a>'
: '<div class="avatar avatar-sm avatar-user"></div>'
).attr('data-name', member.realname || member.account)
.attr('data-id', member.id || member.account);
if(member.avatar)
{
$avatar.append('<img src="' + member.avatar + '" />');
}
$avatar.renderAvatar();
if(canAssign)
{
$avatar.attr('href', createLink('sys.task', 'assignto', 'taskID=' + item.id + '&fromKanban=1'));
}
$user.empty()
.append($avatar)
.attr('title', member.realname || member.account)
.show();
}
else
{
$user.hide();
}
$item.css('background-color', item.color);
$item.toggleClass('has-color', item.color != '#FFFFFF');
if(item.color == '#FFFFFF')
{
$item.find('.info > .label-light').css('background-color', '#F2F2F2');
}
else
{
var colors = {'#E01B1B': '#DD5858', '#F7B500': '#ECC046', '#288427': '#609F60'}
$item.find('.info > .label-light').css('background-color', colors[item.color]);
}
}
/**
* Show error message
* @param {string|object} message Message
*/
function showErrorMessager(message)
{
var html = false;
if(message instanceof Error)
{
message = message.message;
}
else if(typeof message === 'object')
{
html = [];
$.each(message, function(key, msg)
{
html.push($.isArray(msg) ? msg.join('') : String(msg));
});
message = html.join('<br/>');
}
else
{
message = String(message);
}
if(typeof message === 'string' && message.length)
{
$.zui.messager.danger(message, {html: !!html});
}
}
/**
* Update kanban
* @param {string} [regionID] Region ID
* @param {string} [groupID] Group ID
* @param {function(Error)} [callback] Callback on completed
*/
function updateRegion(regionID, groupID, callback)
{
if(!regionID) return false;
if(typeof groupID == 'function')
{
callback = groupID;
groupID = 0;
}
var url = createLink('kanban', 'ajaxGetData', 'kanbanID=' + kanbanID + '&regionID=' + regionID + '&group=' + (groupID || 0));
$.ajax(
{
method: 'get',
dataType: 'json',
url: url,
success: function(response)
{
var kanban = $('#kanban' + regionID).data('zui.kanban');
if(groupID) kanban.renderKanban(response.data);
else kanban.render(response.data);
typeof callback === 'function' && callback();
},
error: function(xhr, status, error)
{
showErrorMessager(error || lang.timeout);
typeof callback === 'function' && callback(error);
}
});
}
/**
* Hide kanban action
*/
function hideKanbanAction()
{
$('.kanban').attr('data-action-enabled', null);
$('.contextmenu').removeClass('contextmenu-show');
$('.contextmenu .contextmenu-menu').removeClass('open').removeClass('in');
$('#moreTasks, #moreColumns').animate({right: -400}, 500);
}
/**
* Open form for adding task
* @param {JQuery} $element Trigger element
*/
function openAddTaskForm($element)
{
var regionID = $element.closest('.kanban').data('id');
var groupID = $element.closest('.kanban-board').data('id');
var laneID = $element.closest('.kanban-lane').data('id');
var columnID = $element.closest('.kanban-col').data('id');
var status = $element.closest('.kanban-col').data('type');
var modalUrl = createLink('sys.task', 'create', 'kanbanID=' + kanbanID + '&from=kanban&regionID=' + regionID + '&groupID=' + groupID + '&laneID=' + laneID + '&columnID=' + columnID + '&status=' + status);
$.zui.modalTrigger.show(
{
url: modalUrl,
width: '1000px'
});
hideKanbanAction();
}
/**
* Reset lane height according to window height.
*/
function resetLaneHeight()
{
var maxHeight = '360px';
if(laneCount < 2)
{
var windowHeight = $(window).height();
var marginTop = $('#mainContent').css('margin-top');
var headerHeight = $('.kanban > .kanban-board:first > .kanban-header').outerHeight();
var actionHeight = $('.kanban > .kanban-board:first > .kanban-lane > .kanban-col:first > .kanban-lane-actions').outerHeight();
maxHeight = windowHeight - parseInt(marginTop) - headerHeight - actionHeight;
}
$('.kanban-lane-items').css('max-height', maxHeight);
}
/**
* Close modal and update kanban data.
*/
function closeModalAndUpdateKanban(regionID)
{
setTimeout(function()
{
$.zui.closeModal();
updateRegion(regionID);
}, 1200);
}
/**
* Status change map
*/
var statusChangeMap =
{
wait: ['doing', 'done', 'cancel'],
doing: ['done', 'cancel'],
done: ['doing', 'closed'],
cancel: ['doing', 'closed'],
closed: ['doing']
};
/**
* Find drop columns
* @param {JQuery} $element Drag element
* @param {JQuery} $root Dnd root element
*/
function findDropColumns($element, $root)
{
var $task = $element;
var task = $task.data('task');
var status = task.status;
var $col = $task.closest('.kanban-col');
var col = $col.data();
var lane = $col.closest('.kanban-lane').data('lane');
var allStatusCanChange = statusChangeMap[status];
hideKanbanAction();
return $root.find('.kanban-lane-col:not([data-type="EMPTY"],[data-type=""])').filter(function()
{
var $newCol = $(this);
var newCol = $newCol.data();
var $newLane = $newCol.closest('.kanban-lane');
var newLane = $newLane.data('lane');
$newCol.addClass('can-drop-here');
return true;
});
}
/**
* Handle drop task
* @param {Object} event Drop event object
*/
function handleDropTask($element, event, kanban)
{
if(!event.target || !event.isNew) return;
var $task = $element;
var $oldCol = $task.closest('.kanban-col');
var $newCol = $(event.target).closest('.kanban-col');
var oldCol = $oldCol.data();
var newCol = $newCol.data();
var oldLane = $oldCol.closest('.kanban-lane').data('lane');
var newLane = $newCol.closest('.kanban-lane').data('lane');
var kanbanID = $task.closest('.kanban-board').data('id');
var regionID = $task.closest('.kanban').data('id');
if(oldCol.id === newCol.id && newLane.id === oldLane.id) return false;
var newStatus = newCol.type;
var task = $task.data('task');
/* Task status not change */
if(newStatus === task.status && task.kanbanLane !== newLane.id)
{
var url = createLink('sys.task', 'move', 'taskID=' + task.id + '&groupID=' + newLane.group);
return $.ajax(
{
method: 'post',
dataType: 'json',
url: url,
data: {lane: newLane.id},
success: function(data)
{
if(data && data.result === 'success') updateRegion(regionID);
else showErrorMessager(data && data.message);
},
error: function(xhr, status, error)
{
showErrorMessager(error || lang.timeout);
}
});
}
/* Show dialog to user for changing status */
var methodToChangeStatus =
{
wait: 'activate',
doing: 'start',
done: 'finish',
cancel: 'cancel',
closed: 'close',
};
$.getJSON(createLink('task', 'move', 'taskID=' + task.id + '&groupID=' + newLane.group + '&laneID=' + newLane.id + '&columnID=' + newCol.id), function(response)
{
if(response.result == 'success') updateRegion(regionID);
});
}
/**
* Handle finish drop task
*/
function handleFinishDrop()
{
$('.kanban').find('.can-drop-here').removeClass('can-drop-here');
}
/**
* Adjust add button postion in column
*/
function adjustAddBtnPosition($kanban)
{
if(!$kanban)
{
$('.kanban').children('.kanban-board').each(function()
{
adjustAddBtnPosition($(this));
});
return;
}
$kanban.find('.kanban-lane-col:not([data-type="EMPTY"])').each(function()
{
var $col = $(this);
var items = $col.children('.kanban-lane-items')[0];
$col.toggleClass('has-scrollbar', items.scrollHeight > items.clientHeight);
});
}
/**
* Kanban action handlers
*/
var kanbanActionHandlers =
{
addItem: openAddTaskForm,
dropItem: handleDropTask
};
/**
* Handle kanban action
*/
function handleKanbanAction(action, $element, event, kanban)
{
$('.kanban').attr('data-action-enabled', action);
var handler = kanbanActionHandlers[action];
if(handler) handler($element, event, kanban);
}
function processMinusBtn()
{
var columnCount = $('#splitTable .child-column').size();
if(columnCount > 2 && columnCount < 10)
{
$('#splitTable .btn-plus').show();
$('#splitTable .btn-close').show();
}
else if(columnCount <= 2)
{
$('#splitTable .btn-close').hide();
}
else if(columnCount >= 10)
{
$('#splitTable .btn-plus').hide();
}
}
function createLaneMenu(options)
{
var lane = options.$trigger.closest('.kanban-lane').data('lane');
var privs = lane.actions;
if(!privs.length) return [];
var items = [];
if(privs.includes('editLane')) items.push({label: kanbanLang.editLane, icon: '', url: createLink('kanban', 'editLane', 'lane=' + lane.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('deleteLane')) items.push({label: kanbanLang.deleteLane, icon: '', url: createLink('kanban', 'deleteLane', 'lane=' + lane.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbanlaneLang.confirmDelete, 'data-confirmDetail': kanbanlaneLang.confirmDeleteDetail}});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
return items;
}
function createTaskMenu(options)
{
var task = options.$trigger.closest('.kanban-item').data('task');
var privs = task.actions;
if(!privs.length) return [];
var actions = {
'edit': {'label': taskLang.edit, 'icon': '', 'url': createLink('task', 'edit', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal', 'data-width': '1125px'}},
'move': {'label': taskLang.move, 'icon': '', 'url': createLink('task', 'move', "taskID=" + task.id + "&groupID=" + task.kanbanGroup), 'attrs': {'data-toggle': 'modal', 'data-width': 400}},
'copy': {'label': taskLang.copy, 'icon': '', 'url': createLink('task', 'copy', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal', 'data-width': '1000px'}},
'start': {'label': taskLang.start, 'icon': '', 'url': createLink('task', 'start', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'finish': {'label': taskLang.finish, 'icon': '', 'url': createLink('task', 'finish', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'activate': {'label': taskLang.activate, 'icon': '', 'url': createLink('task', 'activate', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'cancel': {'label': taskLang.cancel, 'icon': '', 'url': createLink('task', 'cancel', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'close': {'label': taskLang.close, 'icon': '', 'url': createLink('task', 'close', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'archive': {'label': taskLang.archive, 'icon': '', 'url': createLink('task', 'archive', "taskID=" + task.id + "&from=kanban"), 'attrs': {'class': 'confirmer', 'data-confirmTitle': taskLang.confirmArchive, 'data-confirmDetail': taskLang.confirmArchiveDetail, 'data-confirmButton': taskLang.archive, 'data-confirming': taskLang.archiving}},
'delete': {'label': taskLang.delete, 'icon': '', 'url': createLink('task', 'delete', "taskID=" + task.id + '&from=kanban'), 'attrs': {'class': 'confirmer', 'data-confirmTitle': taskLang.confirmDelete, 'data-confirmDetail': taskLang.confirmDeleteDetail}},
'setColor': {'label': taskLang.setColor, 'icon': '', 'url': createLink('task', 'setColor', "taskID=" + task.id), 'attrs': {'data-toggle': 'modal', 'data-width': 400}},
};
var items = [];
$.each(actions, function(actionKey, actionItem)
{
if(privs.includes(actionKey)) items.push({label: actionItem.label, icon: '', url: actionItem.url, attrs: actionItem.attrs});
});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
return items;
}
function createColumnMenu(options)
{
var column = options.$trigger.closest('.kanban-col').data('col');
var privs = column.actions;
if(!privs.length) return [];
var items = [];
if(privs.includes('setWIP')) items.push({label: kanbanLang.WIP, icon: '', url: createLink('kanban', 'setWIP', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('editColumn')) items.push({label: kanbanLang.editColumn, icon: '', url: createLink('kanban', 'editColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('createColumn'))
{
items.push({label: kanbanLang.createColumnOnLeft, icon: '', url: createLink('kanban', 'createColumn', 'columnID=' + column.id + '&position=left'), attrs: {'data-toggle': 'modal'}});
items.push({label: kanbanLang.createColumnOnRight, icon: '', url: createLink('kanban', 'createColumn', 'columnID=' + column.id + '&position=right'), attrs: {'data-toggle': 'modal'}});
}
if(privs.includes('copyColumn')) items.push({label: kanbanLang.copyColumn, icon: '', url: createLink('kanban', 'copyColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('splitColumn')) items.push({label: kanbanLang.splitColumn, icon: '', url: createLink('kanban', 'splitColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('archiveColumn')) items.push({label: kanbanLang.archiveColumn, icon: '', url: createLink('kanban', 'archiveColumn', 'columnID=' + column.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbancolumnLang.confirmArchive, 'data-confirmDetail': kanbancolumnLang.confirmArchiveDetail, 'data-confirmButton': lang.archive, 'data-confirming': lang.archiving}});
if(privs.includes('deleteColumn')) items.push({label: kanbanLang.deleteColumn, icon: '', url: createLink('kanban', 'deleteColumn', 'columnID=' + column.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbancolumnLang.confirmDelete, 'data-confirmDetail': kanbancolumnLang.confirmDeleteDetail}});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
return items;
}
/* Define menu creators */
window.menuCreators =
{
task: createTaskMenu,
lane: createLaneMenu,
column: createColumnMenu
};
/**
* init Kanban
*/
function initKanban($kanban)
{
var id = $kanban.data('id');
var region = regions[id];
$kanban.kanban(
{
data: region.groups,
maxColHeight: 510,
createColumnText: kanbanLang.createColumn,
addItemText: '',
itemRender: renderKanbanItem,
onAction: handleKanbanAction,
onRenderKanban: adjustAddBtnPosition,
onRenderLaneName: renderLaneName,
onRenderHeaderCol: renderHeaderCol,
onRenderCount: renderCount,
droppable:
{
target: findDropColumns,
finish: handleFinishDrop,
mouseButton: 'left'
}
}).on('click', '.action-cancel', hideKanbanAction);
}
/**
* Init when page ready
*/
$(function()
{
if($.cookie('isFullScreen') == 1) fullScreen();
/* Init first kanban */
$('.kanban').each(function()
{
initKanban($(this));
});
$('.icon-double-angle-up,.icon-double-angle-down').on('click', function()
{
$(this).toggleClass('icon-double-angle-up icon-double-angle-down');
$(this).parents('.region').find('.kanban').toggle();
hideKanbanAction();
});
$('.region-header').on('click', '.action', hideKanbanAction);
$('#TRAction').on('click', '.btn', hideKanbanAction);
/* Hide action box when user click document */
$(document).on('click', function(e)
{
$('.kanban').each(function()
{
var currentAction = $(this).kanban().attr('data-action-enabled');
var canHideAction = (currentAction === 'headerMore' || currentAction === 'editLaneName')
&& !$(e.target).closest('.action,.action-box').length;
if(canHideAction) hideKanbanAction();
});
});
/* Init contextmenu */
$('#kanban').on('click', '[data-contextmenu]', function(event)
{
var $trigger = $(this);
var menuType = $trigger.data('contextmenu');
var menuCreator = window.menuCreators[menuType];
if(!menuCreator) return;
var options = $.extend({event: event, $trigger: $trigger}, $trigger.data());
var items = menuCreator(options);
if(!items || !items.length) return;
$.zui.ContextMenu.show(items, items.$options || {event: event});
});
/* Adjust the add button position on window resize */
$(window).on('resize', function(a)
{
adjustAddBtnPosition();
});
resetLaneHeight();
/* Hide contextmenu when page scroll */
$(window).on('scroll', function()
{
$.zui.ContextMenu.hide();
});
$(document).on('click', '#splitTable .btn-plus', function()
{
var tr = $(this).closest('tr');
tr.after($('#childTpl').html().replace(/key/g, key));
tr.next().find('input[name^=color]').colorPicker();
key++;
processMinusBtn();
return false;
});
/* Remove a trade detail item. */
$(document).on('click', '#splitTable .btn-close', function()
{
$(this).closest('tr').remove();
processMinusBtn();
return false;
});
/* Init sortable */
var sortType = '';
$('#kanban').sortable(
{
selector: '.region, .kanban-board, .kanban-lane',
trigger: '.region.sort > .region-header, .kanban-board.sort > .kanban-header > .kanban-group-header, .kanban-lane.sort > .kanban-lane-name',
targetSelector: function($ele)
{
/* Sort regions */
if($ele.hasClass('region'))
{
sortType = 'region';
return $ele.parent().children('.region');
}
/* Sort boards */
if($ele.hasClass('kanban-board'))
{
sortType = 'board';
return $ele.parent().children('.kanban-board');
}
/* Sort lanes */
if($ele.hasClass('kanban-lane'))
{
sortType = 'lane';
return $ele.parent().children('.kanban-lane');
}
/* Sort lanes */
if($ele.hasClass('kanban-item'))
{
sortType = 'item';
return $ele.parent().children('.kanban-item');
}
},
start: function(e)
{
if(sortType == 'region')
{
$('.icon-double-angle-up').attr('class', 'icon-double-angle-down');
$('.region').find('.kanban').hide();
hideKanbanAction();
}
},
finish: function(e)
{
var url = '';
var orders = [];
e.list.each(function(index, data)
{
orders.push(data.item.data('id'));
});
if(sortType == 'region')
{
$('.icon-double-angle-down').attr('class', 'icon-double-angle-up');
$('.region').find('.kanban').show();
}
if(sortType == 'board')
{
var region = e.element.parent().data('id');
url = createLink('kanban', 'sortGroup', 'region=' + region + '&groups=' + orders.join(','));
}
if(sortType == 'lane')
{
var region = e.element.parent().parent().data('id');
url = createLink('kanban', 'sortLane', 'region=' + region + '&lanes=' + orders.join(','));
}
if(sortType == 'item')
{
url = createLink('task', 'sort', 'kanbanID=' + kanbanID + '&tasks=' + orders.join(','));
}
if(!url) return true;
$.getJSON(url, function(response)
{
if(response.result == 'fail' && response.message.length)
{
bootbox.alert(response.message);
setTimeout(function(){return location.reload()}, 3000);
}
});
}
});
});
+4
View File
@@ -92,6 +92,7 @@ $lang->kanban->laneGroup = '泳道分组';
$lang->kanban->cardsSort = '卡片排序';
$lang->kanban->moreAction = '更多操作';
$lang->kanban->noGroup = '无';
$lang->kanban->limitExceeded = '超出在制品限制';
$lang->kanban->error = new stdclass();
$lang->kanban->error->mustBeInt = '在制品数量必须是正整数。';
@@ -162,8 +163,11 @@ $lang->kanbancolumn->limit = $lang->kanban->WIPCount;
$lang->kanbanlane = new stdclass();
$lang->kanbanlane->name = $lang->kanban->laneName;
$lang->kanbanlane->common = '泳道';
$lang->kanbanlane->default = '默认泳道';
$lang->kanbanregion = new stdclass();
$lang->kanbanregion->name = '看板区域';
$lang->kanbanregion->default = '默认区域';
$lang->kanbancard = new stdclass();
+318 -27
View File
@@ -25,17 +25,17 @@ class kanbanModel extends model
{
$kanban = $this->getByID($kanbanID);
$function = $from == 'default' ? 'createDefaultRegion' : 'createRegion';
$this->$function($kanban);
$function = $type == 'default' ? 'createDefaultRegion' : 'createRegion';
$regionID = $this->$function($kanban);
if(dao::isError()) return false;
$groupID = $this->createGroup($kanban->id, $kanban->region);
$groupID = $this->createGroup($kanban->id, $regionID);
if(dao::isError()) return false;
$this->createDefaultLane($kanban, $groupID);
$this->createDefaultLane($kanban, $regionID, $groupID);
if(dao::isError()) return false;
$this->createDefaultColumns($kanban, $groupID);
$this->createDefaultColumns($kanban, $regionID, $groupID);
if(dao::isError()) return false;
}
@@ -50,8 +50,8 @@ class kanbanModel extends model
public function createGroup($kanbanID, $regionID)
{
$maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANORDER)
->where('type')->eq('group')
->andWhere('parent')->eq($regionID)
->where('objectType')->eq('group')
->andWhere('parentID')->eq($regionID)
->andWhere('parentType')->eq('region')
->andWhere('account')->eq('')
->fetch('maxOrder');
@@ -87,7 +87,6 @@ class kanbanModel extends model
$region->createdBy = $this->app->user->account;
$region->createdDate = helper::today();
$this->dao->insert(TABLE_KANBANREGION)->data($region)->exec();
return $this->createRegion($kanban, $region);
}
@@ -107,8 +106,8 @@ class kanbanModel extends model
if(!$region)
{
$maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANORDER)
->where('type')->eq('region')
->andWhere('parent')->eq($kanban->id)
->where('objectType')->eq('region')
->andWhere('parentID')->eq($kanban->id)
->andWhere('parentType')->eq('kanban')
->andWhere('account')->eq('')
->fetch('maxOrder');
@@ -140,51 +139,55 @@ class kanbanModel extends model
* Create default lane.
*
* @param object $kanban
* @param int $regionID
* @param int $groupID
* @access public
* @return int
*/
public function createDefaultLane($kanban)
public function createDefaultLane($kanban, $regionID, $groupID)
{
$lane = new stdclass();
$lane->name = $this->lang->kanbanlane->default;
$lane->region = $kanban->region;
$lane->group = $groupID;
$lane->region = $regionID;
$lane->type = 'common';
$lane->lastEditedTime = helper::now();
$this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec();
$laneID = $this->dao->lastInsertId();
$this->saveOrder($kanban->region, 'region', $laneID, 'lane', '', 1);
$this->saveOrder($regionID, 'region', $laneID, 'lane', '', 1);
return $laneID;
}
/**
* Create default kanban columns.
*
* @param int $kanban
* @param int $laneID
* @param object $kanban
* @param int $regionID
* @param int $groupID
* @access public
* @return void
*/
public function createDefaultColumns($kanban, $laneID)
public function createDefaultColumns($kanban, $regionID, $groupID)
{
$index = 1;
foreach($this->lang->kanban->defaultColumn as $columnName)
{
$column = new stdclass();
$column->region = $kanban->region;
$column->region = $regionID;
$column->group = $groupID;
$column->name = $columnName;
$column->lane = $laneID;
$column->type = $index;
$column->limit = -1;
$this->dao->insert(TABLE_KANBANCOLUMN)->data($column)->exec();
$columnID = $this->dao->lastInsertId();
$kanbanGroup = new stdclass();
$kanbanGroup->region = $kanban->region;
$kanbanGroup->column = $columnID;
$kanbanGroup->lane = $laneID;
$this->dao->insert(TABLE_KANBANGROUP)->data($kanbanGroup)->exec();
$this->saveOrder($regionID, 'region', $this->dao->lastInsertID(), 'column', '', $index);
$index ++;
}
return !dao::isError();
}
/**
@@ -202,7 +205,7 @@ class kanbanModel extends model
public function saveOrder($parentID, $parentType, $objectID, $objectType, $account, $order)
{
$kanbanOrder = new stdclass();
$kanbanOrder->parentID = $parent;
$kanbanOrder->parentID = $parentID;
$kanbanOrder->parentType = $parentType;
$kanbanOrder->objectID = $objectID;
$kanbanOrder->objectType = $objectType;
@@ -213,6 +216,191 @@ class kanbanModel extends model
return !dao::isError();
}
/**
* Get kanban by id.
*
* @param int $kanbanID
* @access public
* @return object
*/
public function getByID($kanbanID)
{
return $this->dao->findByID($kanbanID)->from(TABLE_KANBAN)->fetch();
}
/**
* Get kanban data.
*
* @param int $kanbanID
* @access public
* @return void
*/
public function getKanbanData($kanbanID)
{
$kanbanData = array();
$actions = array('sortGroup');
$regions = $this->getRegionPairs($kanbanID);
$groupGroup = $this->getGroupGroupByRegions(array_keys($regions));
$laneGroup = $this->getLaneGroupByRegions(array_keys($regions));
$columnGroup = $this->getColumnGroupByRegions(array_keys($regions));
//$taskGroup = $this->getTaskGroupByProject($kanbanID);
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 = isset($taskGroup[$lane->id]) ? $taskGroup[$lane->id] : 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 ordered region pairs.
*
* @param int $kanbanID
* @access public
* @return array
*/
public function getRegionPairs($kanbanID)
{
return $this->dao->select('id,name')->from(TABLE_KANBANREGION)
->where('kanban')->eq($kanbanID)
->andWhere('deleted')->eq('0')
->orderBy('id_asc')
->fetchPairs();
}
/**
* Get kanban group by regions.
*
* @param array $regions
* @access public
* @return array
*/
public function getGroupGroupByRegions($regions)
{
return $this->dao->select('*')->from(TABLE_KANBANGROUP)
->where('region')->in($regions)
->orderBy('id_asc')
->fetchGroup('region');
}
/**
* Get lane group by regions.
*
* @param array $regions
* @access public
* @return array
*/
public function getLaneGroupByRegions($regions)
{
$laneGroup = $this->dao->select('*')->from(TABLE_KANBANLANE)
->where('deleted')->eq('0')
->andWhere('region')->in($regions)
->orderBy('order')
->fetchGroup('group');
$actions = array('editLane', 'sortLane', 'deleteLane');
foreach($laneGroup as $lanes)
{
foreach($lanes as $lane)
{
$lane->actions = array();
foreach($actions as $action)
{
if($this->isClickable($lane, $action)) $lane->actions[] = $action;
}
}
}
return $laneGroup;
}
/**
* Get column group by regions.
*
* @param array $regions
* @access public
* @return array
*/
public function getColumnGroupByRegions($regions)
{
$columnGroup = $this->dao->select("*")->from(TABLE_KANBANCOLUMN)
->where('deleted')->eq('0')
//->andWhere('archived')->eq('0')
->andWhere('region')->in($regions)
->orderBy('order')
->fetchGroup('group', 'id');
$actions = array('createColumn', 'copyColumn', 'editColumn', 'splitColumn', 'setWIP', 'archiveColumn', 'restoreColumn', '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) 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;
$column->parentType = 'column' . $column->parent;
$columnData[$group][] = $column;
}
}
}
}
return $columnData;
}
/**
* Get Kanban by execution id.
*
@@ -453,7 +641,7 @@ class kanbanModel extends model
public function updateSpace($spaceID)
{
$spaceID = (int)$spaceID;
$oldSpace = $this->dao->findById($spaceID)->from(TABLE_KANBANSPACE)->fetch();
$oldSpace = $this->getSpaceById($spaceID);
$space = fixer::input('post')
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', helper::now())
@@ -506,6 +694,35 @@ class kanbanModel extends model
}
}
/**
* Close a kanban.
*
* @param int $kanbanID
* @access public
* @return array
*/
function close($kanbanID)
{
$kanbanID = (int)$kanbanID;
$oldKanban = $this->getByID($kanbanID);
$now = helper::now();
$kanban = fixer::input('post')
->setDefault('status', 'closed')
->setDefault('closedBy', $this->app->user->account)
->setDefault('closedDate', $now)
->setDefault('lastEditedBy', $this->app->user->account)
->setDefault('lastEditedDate', $now)
->remove('comment')
->get();
$this->dao->update(TABLE_KANBAN)->data($kanban)
->autoCheck()
->where('id')->eq($kanbanID)
->exec();
if(!dao::isError()) return common::createChanges($oldKanban, $kanban);
}
/**
* Add execution Kanban lanes and columns.
*
@@ -1291,4 +1508,78 @@ class kanbanModel extends model
}
return $menus;
}
/**
* Check if user can execute an action.
*
* @param object $object
* @param string $action
* @access public
* @return bool
*/
public function isClickable($object, $action)
{
$action = strtolower($action);
$clickable = commonModel::hasPriv('kanban', $action);
if(!$clickable) return false;
switch($action)
{
case 'sortlane' :
case 'deletelane' :
if($object->deleted != '0') return false;
$count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANLANE)
->where('deleted')->eq('0')
->andWhere('region')->eq($object->region)
->beginIF($action == 'sortlane')->andWhere('`group`')->eq($object->group)->fi()
->fetch('count');
return $count > 1;
case 'createcolumn' :
case 'copycolumn' :
case 'splitcolumn' :
if($object->parent) return false; // The current column is a child column.
$count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN)
->where('parent')->eq($object->id)
->andWhere('deleted')->eq('0')
//->andWhere('archived')->eq('0')
->fetch('count');
return $count == 0; // The column has child columns.
case 'restoreColumn' :
if($object->parent)
{
$parent = $this->getColumnByID($object->parent);
if($parent->deleted == '1' || $parent->archived == '1') return false;
}
return $object->archived == '1';
case 'archivecolumn' :
//if($object->archived != '0') return false; // The column has been archived.
case 'deletecolumn' :
if($object->deleted != '0') return false;
if($object->parent)
{
$childrenCount = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN)
->where('parent')->eq($object->parent)
->andWhere('deleted')->eq('0')
//->andWhere('archived')->eq('0')
->fetch('count');
return $childrenCount > 2;
}
$count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN)
->where('region')->eq($object->region)
->andWhere('parent')->eq(0)
->andWhere('`group`')->eq($object->group)
->andWhere('deleted')->eq('0')
//->andWhere('archived')->eq('0')
->fetch('count');
return $count > 1;
}
return true;
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
/**
* The close file of kanban module of ZenTaoPMS.
*
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Mengyi Liu <fujia@cnezsoft.com>
* @package kanban
* @version $Id: close.html.php 935 2021-12-09 10:49:24Z $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2>
<span class='label label-id'><?php echo $kanban->id;?></span>
<?php echo "<span title='$kanban->name'>" . $kanban->name . '</span>';?>
</div>
</div>
<form method='post' enctype='multipart/form-data' target='hiddenwin'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->comment;?></th>
<td colspan='2'><?php echo html::textarea('comment', '', "rows='6' class='w-p98'");?></td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton($lang->kanban->close);?>
</td>
</tr>
</table>
</form>
<hr class='small' />
<div class='main'><?php include '../../common/view/action.html.php';?></div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+46
View File
@@ -0,0 +1,46 @@
<?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('kanbanLang', $lang->kanban);
js::set('kanbanlaneLang', $lang->kanbanlane);
js::set('kanbancolumnLang', $lang->kanbancolumn);
js::set('taskLang', $lang->kanbancard);
js::set('kanbanID', $kanban->id);
//js::set('members', $members);
js::set('laneCount', $laneCount);
js::set('today', helper::today());
$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="kanban">
<?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>
<i class="icon icon-double-angle-up"></i>
<?php if($canEditRegion || $canCreateLane || $canDeleteRegion):?>
<button class="btn btn-link action" type="button" data-toggle="dropdown"><i class="icon icon-more-v"></i></button>
<ul class="dropdown-menu pull-right">
<?php if($canEditRegion) echo '<li>' . html::a(inlink('editRegion', "regionID={$region->id}"), $this->lang->kanban->editRegion, "data-toggle='modal'") . '</li>';?>
<?php if($canCreateLane) echo '<li>' . html::a(inlink('createLane', "kanbanID={$project->id}&regionID={$region->id}"), $this->lang->kanban->createLane, "data-toggle='modal'") . '</li>';?>
<?php if($canDeleteRegion) echo '<li>' . html::a(inlink('deleteRegion', "regionID={$region->id}"), $this->lang->kanban->deleteRegion, "class='confirmer' data-confirmTitle='{$lang->kanbanregion->confirmDelete}' data-confirmDetail='{$lang->kanbanregion->confirmDeleteDetail}'") . '</li>';?>
</ul>
<?php endif;?>
</div>
<div id='kanban<?php echo $region->id;?>' data-id='<?php echo $region->id;?>' class='kanban'></div>
</div>
<?php endforeach;?>
</div>
<div id='moreTasks'></div>
<div id='moreColumns'></div>
<?php include '../../common/view/footer.html.php';?>
+2
View File
@@ -19,9 +19,11 @@ $typeInfo = $type == 'file' ? '&type=file' : '';
<?php include '../../common/view/header.html.php';?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php if(!isonlybody()):?>
<?php $browseLink = $app->session->revisionList != false ? $app->session->revisionList : $this->repo->createLink('browse', "repoID={$repoID}&branchID=" . base64_encode($branchID) . "&objectID=$objectID{$preDir}");?>
<?php echo html::a($browseLink, "<i class='icon icon-back'></i> " . $lang->goback, '', "class='btn btn-link'");?>
<div class="divider"></div>
<?php endif;?>
<div class="page-title">
<?php echo $lang->repo->revisionA . ' ' . ($repo->SCM == 'Subversion' ? $revision : $this->repo->getGitRevisionName($revision, $log->commit));?>
</div>
File diff suppressed because one or more lines are too long
Binary file not shown.
+8 -3
View File
@@ -201,11 +201,15 @@
<glyph unicode="&#xe9b2;" glyph-name="side-right" d="M148.028 802.13v0c-80.406 0-145.588-65.182-145.588-145.588 0 0 0 0 0 0v-727.942c0-80.406 65.182-145.588 145.588-145.588 0 0 0 0 0 0h727.942c80.406 0 145.588 65.182 145.588 145.588 0 0 0 0 0 0v727.942c0 80.406-65.182 145.588-145.588 145.588 0 0 0 0 0 0h-727.942zM75.234 729.335h873.53v-873.53h-873.53v873.53zM584.793 656.541h291.177v-727.942h-291.177z" />
<glyph unicode="&#xe9b3;" glyph-name="side-left" d="M875.972 802.13v0c80.406 0 145.588-65.182 145.588-145.588 0 0 0 0 0 0v-727.942c0-80.406-65.182-145.588-145.588-145.588 0 0 0 0 0 0h-727.942c-80.406 0-145.588 65.182-145.588 145.588 0 0 0 0 0 0v727.942c0 80.406 65.182 145.588 145.588 145.588 0 0 0 0 0 0h727.942zM948.766 729.335h-873.53v-873.53h873.53v873.53zM439.207 656.541h-291.177v-727.942h291.177z" />
<glyph unicode="&#xe9b4;" glyph-name="list-box" horiz-adv-x="951" d="M0.002 804.571v-1024zM18.288 804.571v-1024zM36.574 804.571v-1024zM54.859 804.571v-1024zM73.145 804.571v-1024zM91.431 804.571v-1024zM109.717 804.571v-1024zM128.002 804.571v-1024zM146.288 804.571v-1024zM164.574 804.571v-1024zM182.859 804.571v-1024zM201.145 804.571v-1024zM219.431 804.571v-1024zM237.717 804.571v-1024zM256.002 804.571v-1024zM274.288 804.571v-1024zM292.574 804.571v-1024zM310.859 804.571v-1024zM329.145 804.571v-1024zM347.431 804.571v-1024zM365.717 804.571v-1024zM384.002 804.571v-1024zM402.288 804.571v-1024zM420.574 804.571v-1024zM438.859 804.571v-1024zM457.145 804.571v-1024zM475.431 804.571v-1024zM493.717 804.571v-1024zM512.002 804.571v-1024zM530.288 804.571v-1024zM548.574 804.571v-1024zM566.859 804.571v-1024zM585.145 804.571v-1024zM603.431 804.571v-1024zM621.717 804.571v-1024zM640.002 804.571v-1024zM658.288 804.571v-1024zM676.574 804.571v-1024zM694.859 804.571v-1024zM713.145 804.571v-1024zM731.431 804.571v-1024zM749.717 804.571v-1024zM768.002 804.571v-1024zM786.288 804.571v-1024zM804.574 804.571v-1024zM822.859 804.571v-1024zM841.145 804.571v-1024zM859.431 804.571v-1024zM877.717 804.571v-1024zM896.002 804.571v-1024zM914.288 804.571v-1024zM932.574 804.571v-1024zM-36.569 786.285h1024zM-36.569 768h1024zM-36.569 749.714h1024zM-36.569 731.428h1024zM-36.569 713.142h1024zM-36.569 694.857h1024zM-36.569 676.571h1024zM-36.569 658.285h1024zM-36.569 640h1024zM-36.569 621.714h1024zM-36.569 603.428h1024zM-36.569 585.142h1024zM-36.569 566.857h1024zM-36.569 548.571h1024zM-36.569 530.285h1024zM-36.569 512h1024zM-36.569 493.714h1024zM-36.569 475.428h1024zM-36.569 457.142h1024zM-36.569 438.857h1024zM-36.569 420.571h1024zM-36.569 402.285h1024zM-36.569 384h1024zM-36.569 365.714h1024zM-36.569 347.428h1024zM-36.569 329.142h1024zM-36.569 310.857h1024zM-36.569 292.571h1024zM-36.569 274.285h1024zM-36.569 256h1024zM-36.569 237.714h1024zM-36.569 219.428h1024zM-36.569 201.142h1024zM-36.569 182.857h1024zM-36.569 164.571h1024zM-36.569 146.285h1024zM-36.569 128h1024zM-36.569 109.714h1024zM-36.569 91.428h1024zM-36.569 73.142h1024zM-36.569 54.857h1024zM-36.569 36.571h1024zM-36.569 18.285h1024zM-36.569 0h1024zM-36.569-18.286h1024zM-36.569-36.572h1024zM-36.569-54.858h1024zM-36.569-73.143h1024zM-36.569-91.429h1024zM-36.569-109.715h1024zM-36.569-128h1024zM-36.569-146.286h1024zM-36.569-164.572h1024zM-36.569-182.858h1024zM-36.569-201.143h1024zM0.002 658.285c0 60.594 49.12 109.714 109.714 109.714h731.429c60.594 0 109.714-49.12 109.714-109.714v-731.429c0-60.594-49.12-109.714-109.714-109.714h-731.429c-60.594 0-109.714 49.12-109.714 109.714v731.429zM109.717 694.857c-20.199 0-36.571-16.375-36.571-36.571v-731.429c0-20.199 16.375-36.571 36.571-36.571h731.429c20.199 0 36.571 16.373 36.571 36.571v731.429c0 20.199-16.373 36.571-36.571 36.571h-731.429zM182.859 512c0 20.199 16.375 36.571 36.571 36.571s36.304-16.375 36.304-36.571c0-20.199-16.105-36.571-36.304-36.571s-36.571 16.375-36.571 36.571zM330.359 512c0 20.199 14.475 36.571 34.674 36.571h366.4c20.199 0 36.571-16.375 36.571-36.571s-16.373-36.571-36.571-36.571h-366.4c-20.199 0-34.674 16.375-34.674 36.571zM182.859 292.571c0 20.199 16.375 36.571 36.571 36.571s36.304-16.375 36.304-36.571c0-20.199-16.105-36.571-36.304-36.571s-36.571 16.375-36.571 36.571zM330.288 292.571c0 20.199 14.475 36.571 34.674 36.571h366.4c20.199 0 36.571-16.375 36.571-36.571s-16.373-36.571-36.571-36.571h-366.4c-20.199 0-34.674 16.375-34.674 36.571zM182.859 73.142c0 20.199 16.375 36.571 36.571 36.571s36.304-16.375 36.304-36.571c0-20.199-16.105-36.571-36.304-36.571s-36.571 16.375-36.571 36.571zM330.288 73.142c0 20.199 14.475 36.571 34.674 36.571h366.4c20.199 0 36.571-16.375 36.571-36.571s-16.373-36.571-36.571-36.571h-366.4c-20.199 0-34.674 16.375-34.674 36.571z" />
<<<<<<< HEAD
<glyph unicode="&#xe9b5;" glyph-name="chat-solid" horiz-adv-x="1117" d="M558.545-15.892l-245.011-190.547c-24.363-18.923-59.452-14.511-78.373 9.852-7.606 9.796-11.737 21.841-11.743 34.244v146.451h-111.709c-61.696 0-111.709 50.013-111.709 111.709v595.799c0 61.696 50.013 111.709 111.709 111.709h893.673c61.696 0 111.709-50.013 111.709-111.709v-595.799c0-61.696-50.013-111.709-111.709-111.709h-446.836zM781.964 337.891c30.848 0 55.855 25.007 55.855 55.855s-25.007 55.855-55.855 55.855c-30.848 0-55.855-25.007-55.855-55.855s25.007-55.855 55.855-55.855zM558.545 337.891c30.848 0 55.855 25.007 55.855 55.855s-25.007 55.855-55.855 55.855c-30.848 0-55.855-25.007-55.855-55.855s25.007-55.855 55.855-55.855zM335.125 337.891c30.848 0 55.855 25.007 55.855 55.855s-25.007 55.855-55.855 55.855c-30.848 0-55.855-25.007-55.855-55.855s25.007-55.855 55.855-55.855z" />
<glyph unicode="&#xe9b6;" glyph-name="minuse-solid-circle" d="M874.017 653.078c-96.701 96.231-225.225 149.232-361.97 149.232s-265.317-53.001-362.025-149.227c-96.711-96.228-150.021-224.308-150.021-360.505s53.285-264.249 150.018-360.513c96.733-96.265 225.235-149.227 361.995-149.227 136.756 0 265.307 52.942 361.968 149.227s150.018 224.357 150.018 360.551c0 136.198-53.205 264.206-149.984 360.469l0.002-0.002zM256.001 255.974c-20.198 0-36.571 16.373-36.571 36.571s16.373 36.571 36.571 36.571h512c20.198 0 36.571-16.373 36.571-36.571s-16.373-36.571-36.571-36.571h-512z" />
=======
>>>>>>> sugon
<glyph unicode="&#xe9b7;" glyph-name="diff" d="M730.378 622.413c60.304 0 109.189-48.886 109.189-109.189v0-586.892c0-60.304-48.886-109.189-109.189-109.189v0h-582.343c-60.304 0-109.189 48.886-109.189 109.189v0 586.892c0 60.304 48.886 109.189 109.189 109.189v0zM730.378 549.62h-582.343c-20.101 0-36.396-16.295-36.396-36.396v0-586.892c0-20.101 16.295-36.396 36.396-36.396v0h582.343c20.101 0 36.396 16.295 36.396 36.396v0 586.892c0 20.101-16.295 36.396-36.396 36.396v0zM875.965 767.998c59.701 0 108.21-47.654 109.175-106.803l0.014-1.796v-619.92c0-47.284-30.383-87.51-72.792-102.42l-0.001 722.34c0 19.793-15.972 35.875-35.794 36.195l-0.602 0.005h-582.343c-1.83 0-3.628-0.135-5.385-0.394l-97.73-0.001c14.73 41.961 54.617 72.185 101.687 72.785l1.428 0.010h582.343zM585.93 40.070c20.101 0 36.396-16.295 36.396-36.396 0-19.901-15.972-36.071-35.794-36.391l-0.602-0.005h-293.447c-20.101 0-36.396 16.295-36.396 36.396 0 19.901 15.972 36.071 35.794 36.391l0.602 0.005h293.447zM439.207 476.827c19.901 0 36.071-15.972 36.391-35.794l0.005-0.602v-109.189h110.327c20.101 0 36.396-16.295 36.396-36.396 0-19.901-15.972-36.071-35.794-36.391l-0.602-0.005h-110.327v-111.464c0-20.101-16.295-36.396-36.396-36.396-19.901 0-36.071 15.972-36.391 35.794l-0.005 0.602v111.464h-110.327c-20.101 0-36.396 16.295-36.396 36.396 0 19.901 15.972 36.071 35.794 36.391l0.602 0.005h110.327v109.189c0 20.101 16.295 36.396 36.396 36.396z" />
<glyph unicode="&#xe9b8;" glyph-name="card-archive" d="M108.872 302.842h488.889c51.699 0 89.797 27.204 101.593 73.486 0.903 4.516 2.709 10.893 4.516 18.118v0c4.038 18.065 8.897 35.938 14.561 53.562h188.625c19.98 0 36.292-16.368 36.292-36.292v-2.765l-13.546-170.507c0-19.98 16.254-36.292 36.235-36.292s36.292 16.368 36.292 36.292v0.903l13.602 173.274c0 59.827-48.991 108.818-108.874 108.818h-20.883v174.176c0 56.215-45.322 101.593-101.593 101.593h-554.134c-56.215 0-101.593-45.378-101.593-101.593v-320.188h-19.923c-59.94 0-108.931-48.991-108.931-111.583l36.292-332.887c0-59.827 48.991-108.874 108.818-108.874h527.946c18.927 1.526 33.526 17.303 33.582 36.292v0c-0.056 18.989-14.655 34.765-33.582 36.291h-527.946c-19.923 0-36.235 16.368-36.235 39.001l-36.292 332.887c0 19.98 16.311 36.292 36.292 36.292v0zM201.378 695.612c0 16.368 13.547 29.913 29.914 29.913h554.247c16.368 0 29.913-13.546 29.913-29.913v-175.078h-117.961c-0.847 0-1.75-0.903-2.652-0.903v0c-4.102-0.022-8.148-0.947-11.852-2.709-0.847 0-0.847-0.903-1.75-0.903v0c-3.647-1.582-7.019-3.735-9.99-6.378-19.076-18.118-28.108-54.408-39.001-99.731-1.806-6.378-2.709-11.852-3.669-16.368-1.806-6.321-4.516-19.021-30.816-19.021h-396.383v321.091zM902.428 111.733l110.68-102.496c14.277-13.934 14.553-36.804 0.619-51.080-0.204-0.209-0.41-0.415-0.619-0.619v0c-6.669-6.881-15.817-10.804-25.399-10.893v0c-9.582 0.090-18.728 4.013-25.399 10.893l-47.184 48.087v-181.4c0-19.98-16.311-36.292-36.292-36.292v0c-19.996 0.093-36.173 16.296-36.235 36.292v180.497l-47.184-48.087c-6.566-7.030-15.779-10.982-25.399-10.893v0c-9.582 0.090-18.728 4.013-25.399 10.893v0c-14.277 13.934-14.553 36.804-0.619 51.080 0.204 0.209 0.41 0.415 0.619 0.619l109.72 101.593c6.898 6.424 15.974 9.99 25.399 9.99 8.184 0 16.368-2.709 22.689-8.184v0z" />
<glyph unicode="&#xe9b9;" glyph-name="col-archive" d="M960 718.247h-896.002c-35.3 0-64-29.266-64-65.489v-769.489c0-9.004 7.199-16.371 15.999-16.371h992.002c8.8 0 15.999 7.367 15.999 16.371v769.489c0 36.223-28.699 65.489-64 65.489zM479.999-63.524c0-2.252-1.799-4.092-3.999-4.092h-408.001c-2.2 0-3.999 1.843-3.999 4.092v581.21c0 2.252 1.799 4.092 3.999 4.092h408.001c2.2 0 3.999-1.843 3.999-4.092v-581.21zM960-63.524c0-2.252-1.799-4.092-3.999-4.092h-408.001c-2.2 0-3.999 1.843-3.999 4.092v581.21c0 2.252 1.799 4.092 3.999 4.092h408.001c2.2 0 3.999-1.843 3.999-4.092v-581.21z" />
<glyph unicode="&#xe9ba;" glyph-name="zone" d="M512 804.571c282.77 0 512-229.23 512-512v0l-0.263-16.57c-8.747-275.101-234.511-495.43-511.737-495.43-282.77 0-512 229.23-512 512v0l0.263 16.57c8.747 275.101 234.511 495.43 511.737 495.43zM529.153 680.946c-8.432 16.921-32.68 16.921-41.053-0.116-26.69-54.020-72.628-147.291-97.981-197.764-3.838-7.559-12.502-16.398-20.119-17.677-37.797-6.513-149.442-22.911-214.685-32.563-18.433-2.733-26.051-25.12-13.2-38.553 49.833-52.101 100.248-104.784 151.012-157.118 8.722-9.013 11.688-16.689 9.362-29.714-12.909-71.93-25.004-144.035-37.739-219.221-3.198-18.724 16.398-33.029 33.203-24.19 53.264 28.026 144.558 76.115 193.112 102.34 12.502 6.745 22.038 7.327 34.947 0.291 48.787-26.516 140.197-74.605 193.635-102.458 16.863-8.78 36.401 5.64 33.145 24.364-10.874 62.394-21.341 122.403-31.866 182.878-9.188 0-16.165 0.233-23.143-0.058-114.030-4.826-218-8.606-332.030-12.851-7.792-0.291-19.015 0.407-24.83 12.153-4.012 8.141-5.931 15.933-2.21 24.946 2.152 5.117 8.664 7.734 14.072 11.048 73.093 44.717 146.36 89.317 219.512 133.917 6.047 3.663 12.037 7.559 22.911 14.363v0l-303.885 40.472c0.349 5.524 0.233 3.314 0.581 8.839v0h394.191c1.396 0 2.849-0.058 4.245-0.058 15.7-0.058 22.678-9.943 19.48-23.376-1.686-7.152-8.897-12.851-14.944-17.212-68.79-49.892-137.929-99.435-206.952-149.094-1.178-0.843-2.33-1.719-3.525-2.664l-13.803-11.35c85.362-7.152 165.549-13.898 247.248-20.759-0.523 8.083 0.465 14.13-1.454 19.015-8.199 20.992 2.617 33.668 16.805 47.217 19.252 18.357 54.491 53.23 88.954 87.612l59.616 59.738c13.258 13.374 5.699 36.052-12.909 38.843-72.977 10.874-144.907 21.631-216.953 31.691-13.083 1.861-19.538 7.21-25.004 18.608-24.655 51.171-70.825 144.558-97.748 198.462z" />
<glyph unicode="&#xe9bb;" glyph-name="col-add-right" d="M146.285 804.57v0c-80.791 0-146.285-65.494-146.285-146.285 0 0 0 0 0 0v-731.429c0-80.791 65.494-146.285 146.285-146.285 0 0 0 0 0 0h731.429c80.791 0 146.285 65.494 146.285 146.285 0 0 0 0 0 0v731.429c0 80.791-65.494 146.285-146.285 146.285 0 0 0 0 0 0h-731.429zM73.143 731.426h877.714v-877.714h-877.714v877.714zM585.143 658.284h292.572v-731.429h-292.572z" />
<glyph unicode="&#xe9bc;" glyph-name="col-add-left" d="M877.715 804.571v0c80.791 0 146.285-65.494 146.285-146.285 0 0 0 0 0 0v-731.429c0-80.791-65.494-146.285-146.285-146.285 0 0 0 0 0 0h-731.429c-80.791 0-146.285 65.494-146.285 146.285 0 0 0 0 0 0v731.429c0 80.791 65.494 146.285 146.285 146.285 0 0 0 0 0 0h731.429zM950.857 731.427h-877.714v-877.714h877.714v877.714zM438.857 658.285h-292.572v-731.429h292.572z" />
<glyph unicode="&#xe9bd;" glyph-name="col-split" d="M228.514 363.712h159.528v-127.192h-159.528v-153.062l-228.514 211.268 228.514 213.423v-144.438zM797.643 508.15v-146.594h-159.529v-127.192h159.529v-153.062l226.358 213.423-226.358 213.424zM513.079 695.704h-77.608v-806.266h157.373v806.266h-79.764z" />
<glyph unicode="&#xf016;" glyph-name="file, file-empty" horiz-adv-x="878" d="M838.857 587.428c21.143-21.143 38.857-63.429 38.857-93.714v-658.286c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v914.286c0 30.286 24.571 54.857 54.857 54.857h512c30.286 0 72.571-17.714 93.714-38.857zM585.143 726.857v-214.857h214.857c-3.429 9.714-8.571 19.429-12.571 23.429l-178.857 178.857c-4 4-13.714 9.143-23.429 12.571zM804.571-146.286v585.143h-237.714c-30.286 0-54.857 24.571-54.857 54.857v237.714h-438.857v-877.714h731.429z" />
<glyph unicode="&#xf02d;" glyph-name="book" d="M948.764 510.653c13.529-19.482 17.318-44.918 9.741-69.81l-148.822-490.3c-13.529-45.999-61.152-81.716-107.693-81.716h-499.501c-55.199 0-114.186 43.835-134.21 100.116-8.658 24.352-8.658 48.165-1.083 68.728 1.083 10.823 3.248 21.647 3.789 34.635 0.541 8.658-4.329 15.694-3.248 22.188 2.165 12.988 13.529 22.188 22.188 36.799 16.235 27.058 34.635 70.893 40.587 99.033 2.706 10.281-2.706 22.188 0 31.388 2.706 10.281 12.988 17.859 18.401 27.6 14.612 24.894 33.554 73.058 36.259 98.493 1.083 11.364-4.329 23.811-1.083 32.471 3.789 12.447 15.694 17.859 23.811 28.682 12.988 17.859 34.635 69.269 37.881 97.952 1.083 9.199-4.329 18.401-2.706 28.14 2.165 10.281 15.153 21.106 23.811 33.554 22.728 33.554 27.058 107.693 95.787 88.211l-0.541-1.623c9.199 2.165 18.401 4.871 27.6 4.871h411.831c25.434 0 48.165-11.364 61.694-30.306 14.070-19.482 17.318-44.918 9.741-70.352l-148.281-490.3c-25.434-83.34-39.505-101.74-108.235-101.74h-470.277c-7.035 0-15.694-1.623-20.565-8.117-4.329-6.495-4.871-11.364-0.541-23.269 10.823-31.388 48.165-37.881 77.93-37.881h499.501c20.024 0 43.293 11.364 49.247 30.846l162.352 534.135c3.248 10.281 3.248 21.106 2.706 30.846 12.447-4.871 23.811-12.447 31.929-23.269zM372.959 509.57c-3.248-9.741 2.165-17.318 11.905-17.318h329.032c9.199 0 19.482 7.576 22.728 17.318l11.364 34.635c3.248 9.741-2.165 17.318-11.905 17.318h-329.032c-9.199 0-19.482-7.576-22.728-17.318zM328.043 371.031c-3.248-9.741 2.165-17.318 11.905-17.318h329.032c9.199 0 19.482 7.576 22.728 17.318l11.364 34.635c3.248 9.741-2.165 17.318-11.905 17.318h-329.032c-9.199 0-19.482-7.576-22.728-17.318z" />
<glyph unicode="&#xf064;" glyph-name="share" d="M960.285 412.526c0-8.506-3.502-16.511-9.505-22.514l-256.163-256.163c-6.003-6.003-14.008-9.505-22.514-9.505-17.512 0-32.020 14.509-32.020 32.020v128.082h-112.072c-215.638 0-357.227-41.527-357.227-280.178 0-20.513 1.001-41.025 2.501-61.54 0.5-8.005 2.501-17.011 2.501-25.015 0-9.505-6.003-17.512-16.010-17.512-7.004 0-10.506 3.502-14.008 8.506-7.505 10.506-13.009 26.518-18.511 38.025-28.517 64.041-63.54 155.598-63.54 225.643 0 56.036 5.504 113.572 26.518 166.606 69.545 172.609 273.673 201.628 437.778 201.628h112.072v128.082c0 17.512 14.509 32.020 32.020 32.020 8.506 0 16.511-3.502 22.514-9.505l256.163-256.163c6.003-6.003 9.505-14.008 9.505-22.514z" />
@@ -223,6 +227,7 @@
<glyph unicode="&#xf0de;" glyph-name="sort-up" d="M821.728 404.932c0-21.173-17.543-38.715-38.715-38.715h-542.026c-21.173 0-38.715 17.543-38.715 38.715 0 10.284 4.237 19.966 11.492 27.221l271.013 271.013c7.263 7.263 16.937 11.492 27.221 11.492s19.966-4.237 27.221-11.492l271.013-271.013c7.263-7.263 11.492-16.937 11.492-27.221z" />
<glyph unicode="&#xf0f6;" glyph-name="file-text" horiz-adv-x="878" d="M838.857 587.428c21.143-21.143 38.857-63.429 38.857-93.714v-658.286c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v914.286c0 30.286 24.571 54.857 54.857 54.857h512c30.286 0 72.571-17.714 93.714-38.857zM585.143 726.857v-214.857h214.857c-3.429 9.714-8.571 19.429-12.571 23.429l-178.857 178.857c-4 4-13.714 9.143-23.429 12.571zM804.571-146.286v585.143h-237.714c-30.286 0-54.857 24.571-54.857 54.857v237.714h-438.857v-877.714h731.429zM219.429 347.428c0 10.286 8 18.286 18.286 18.286h402.286c10.286 0 18.286-8 18.286-18.286v-36.571c0-10.286-8-18.286-18.286-18.286h-402.286c-10.286 0-18.286 8-18.286 18.286v36.571zM640 219.428c10.286 0 18.286-8 18.286-18.286v-36.571c0-10.286-8-18.286-18.286-18.286h-402.286c-10.286 0-18.286 8-18.286 18.286v36.571c0 10.286 8 18.286 18.286 18.286h402.286zM640 73.142c10.286 0 18.286-8 18.286-18.286v-36.571c0-10.286-8-18.286-18.286-18.286h-402.286c-10.286 0-18.286 8-18.286 18.286v36.571c0 10.286 8 18.286 18.286 18.286h402.286z" />
<glyph unicode="&#xf101;" glyph-name="angle-double-right" d="M568.435 284.446c0-4.56-2.281-9.69-5.7-13.112l-265.638-265.638c-3.421-3.421-8.55-5.7-13.112-5.7s-9.69 2.281-13.112 5.7l-28.502 28.502c-3.421 3.421-5.7 8.55-5.7 13.112s2.281 9.69 5.7 13.112l224.023 224.023-224.023 224.023c-3.421 3.421-5.7 8.55-5.7 13.112s2.281 9.69 5.7 13.112l28.502 28.502c3.421 3.421 8.55 5.7 13.112 5.7s9.69-2.281 13.112-5.7l265.638-265.638c3.421-3.421 5.7-8.55 5.7-13.112zM787.329 284.446c0-4.56-2.281-9.69-5.7-13.112l-265.638-265.638c-3.421-3.421-8.55-5.7-13.112-5.7s-9.69 2.281-13.112 5.7l-28.502 28.502c-3.421 3.421-5.7 8.55-5.7 13.112s2.281 9.69 5.7 13.112l224.023 224.023-224.023 224.023c-3.421 3.421-5.7 8.55-5.7 13.112s2.281 9.69 5.7 13.112l28.502 28.502c3.421 3.421 8.55 5.7 13.112 5.7s9.69-2.281 13.112-5.7l265.638-265.638c3.421-3.421 5.7-8.55 5.7-13.112z" />
<glyph unicode="&#xf126;" glyph-name="code-fork" horiz-adv-x="585" d="M164.571-36.572c0 30.286-24.571 54.857-54.857 54.857s-54.857-24.571-54.857-54.857 24.571-54.857 54.857-54.857 54.857 24.571 54.857 54.857zM164.571 621.714c0 30.286-24.571 54.857-54.857 54.857s-54.857-24.571-54.857-54.857 24.571-54.857 54.857-54.857 54.857 24.571 54.857 54.857zM530.286 548.571c0 30.286-24.571 54.857-54.857 54.857s-54.857-24.571-54.857-54.857 24.571-54.857 54.857-54.857 54.857 24.571 54.857 54.857zM585.143 548.571c0-40.571-22.286-76-54.857-94.857-1.714-206.286-148-252-245.143-282.857-90.857-28.571-120.571-42.286-120.571-97.714v-14.857c32.571-18.857 54.857-54.286 54.857-94.857 0-60.571-49.143-109.714-109.714-109.714s-109.714 49.143-109.714 109.714c0 40.571 22.286 76 54.857 94.857v468.571c-32.571 18.857-54.857 54.286-54.857 94.857 0 60.571 49.143 109.714 109.714 109.714s109.714-49.143 109.714-109.714c0-40.571-22.286-76-54.857-94.857v-284c29.143 14.286 60 24 88 32.571 106.286 33.714 166.857 58.857 168 178.286-32.571 18.857-54.857 54.286-54.857 94.857 0 60.571 49.143 109.714 109.714 109.714s109.714-49.143 109.714-109.714z" />
<glyph unicode="&#xf15b;" glyph-name="file-alt" horiz-adv-x="878" d="M585.143 512v269.714c8-5.143 14.857-10.286 20.571-16l233.143-233.143c5.714-5.714 10.857-12.571 16-20.571h-269.714zM512 493.714c0-30.286 24.571-54.857 54.857-54.857h310.857v-603.429c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v914.286c0 30.286 24.571 54.857 54.857 54.857h457.143v-310.857z" />
<glyph unicode="&#xf15c;" glyph-name="file-text-alt" horiz-adv-x="878" d="M838.857 532.571c5.714-5.714 10.857-12.571 16-20.571h-269.714v269.714c8-5.143 14.857-10.286 20.571-16zM566.857 438.857h310.857v-603.429c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v914.286c0 30.286 24.571 54.857 54.857 54.857h457.143v-310.857c0-30.286 24.571-54.857 54.857-54.857zM658.286 18.285v36.571c0 10.286-8 18.286-18.286 18.286h-402.286c-10.286 0-18.286-8-18.286-18.286v-36.571c0-10.286 8-18.286 18.286-18.286h402.286c10.286 0 18.286 8 18.286 18.286zM658.286 164.571v36.571c0 10.286-8 18.286-18.286 18.286h-402.286c-10.286 0-18.286-8-18.286-18.286v-36.571c0-10.286 8-18.286 18.286-18.286h402.286c10.286 0 18.286 8 18.286 18.286zM658.286 310.857v36.571c0 10.286-8 18.286-18.286 18.286h-402.286c-10.286 0-18.286-8-18.286-18.286v-36.571c0-10.286 8-18.286 18.286-18.286h402.286c10.286 0 18.286 8 18.286 18.286z" />
<glyph unicode="&#xf1c1;" glyph-name="file-pdf" horiz-adv-x="878" d="M838.857 587.428c21.143-21.143 38.857-63.429 38.857-93.714v-658.286c0-30.286-24.571-54.857-54.857-54.857h-768c-30.286 0-54.857 24.571-54.857 54.857v914.286c0 30.286 24.571 54.857 54.857 54.857h512c30.286 0 72.571-17.714 93.714-38.857zM585.143 726.857v-214.857h214.857c-3.429 9.714-8.571 19.429-12.571 23.429l-178.857 178.857c-4 4-13.714 9.143-23.429 12.571zM804.571-146.286v585.143h-237.714c-30.286 0-54.857 24.571-54.857 54.857v237.714h-438.857v-877.714h731.429zM510.857 192.571c14.286-11.429 30.286-21.714 48-32 24 2.857 46.286 4 66.857 4 38.286 0 86.857-4.571 101.143-28 4-5.714 7.429-16 1.143-29.714-0.571-0.571-1.143-1.714-1.714-2.286v-0.571c-1.714-10.286-10.286-21.714-40.571-21.714-36.571 0-92 16.571-140 41.714-79.429-8.571-162.857-26.286-224-47.429-58.857-100.571-104-149.714-138.286-149.714-5.714 0-10.857 1.143-16 4l-13.714 6.857c-1.714 0.571-2.286 1.714-3.429 2.857-2.857 2.857-5.143 9.143-3.429 20.571 5.714 26.286 36.571 70.286 107.429 107.429 4.571 2.857 10.286 1.143 13.143-3.429 0.571-0.571 1.143-1.714 1.143-2.286 17.714 29.143 38.286 66.286 61.143 112.571 25.714 51.429 45.714 101.714 59.429 149.714-18.286 62.286-24 126.286-13.714 164 4 14.286 12.571 22.857 24 22.857h12.571c8.571 0 15.429-2.857 20-8.571 6.857-8 8.571-20.571 5.143-38.857-0.571-1.714-1.143-3.429-2.286-4.571 0.571-1.714 0.571-2.857 0.571-4.571v-17.143c-0.571-36-1.143-70.286-8-109.714 20-60 49.714-108.571 83.429-136zM181.714-42.286c17.143 8 41.714 32.571 78.286 90.286-42.857-33.143-69.714-70.857-78.286-90.286zM409.143 483.428c-5.714-16-5.714-43.429-1.143-75.429 1.714 9.143 2.857 17.714 4 25.143 1.143 9.714 2.857 17.714 4 24.571 0.571 1.714 1.143 2.857 2.286 4.571-0.571 0.571-0.571 1.714-1.143 2.857-0.571 10.286-4 16.571-7.429 20.571 0-1.143-0.571-1.714-0.571-2.286zM338.286 105.714c50.286 20 106.286 36 162.286 46.286-5.714 4.571-11.429 8.571-16.571 13.143-28 24.571-53.143 58.857-72.571 100.571-10.857-34.857-26.857-72-47.429-112.571-8.571-16-17.143-32-25.714-47.429zM707.429 114.857c-2.857 2.857-17.714 13.714-80 13.714 28-10.286 53.714-16 70.857-16 5.143 0 8 0 10.286 0.571 0 0.571-0.571 1.143-1.143 1.714z" />

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.
Binary file not shown.