From c84df535b6bdc7260144872fc4e0c241a5a5ad61 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sun, 11 Sep 2016 18:32:47 -0400 Subject: Improve column restrictions --- app/Controller/TaskMovePositionController.php | 4 ++ .../ColumnMoveRestrictionCacheDecorator.php | 7 +-- app/Formatter/BoardSwimlaneFormatter.php | 4 +- app/Helper/ProjectRoleHelper.php | 55 ++++++++++++++++------ app/Model/ColumnMoveRestrictionModel.php | 29 ++---------- app/Template/board/table_tasks.php | 7 ++- assets/js/app.min.js | 2 +- assets/js/src/BoardDragAndDrop.js | 2 +- .../units/Model/ColumnMoveRestrictionModelTest.php | 46 ------------------ 9 files changed, 64 insertions(+), 92 deletions(-) diff --git a/app/Controller/TaskMovePositionController.php b/app/Controller/TaskMovePositionController.php index 0db742c3..c6e8be0c 100644 --- a/app/Controller/TaskMovePositionController.php +++ b/app/Controller/TaskMovePositionController.php @@ -30,6 +30,10 @@ class TaskMovePositionController extends BaseController $task = $this->getTask(); $values = $this->request->getJson(); + if (! $this->helper->projectRole->canMoveTask($task['project_id'], $task['column_id'], $values['column_id'])) { + throw new AccessForbiddenException(e("You don't have the permission to move this task")); + } + $result = $this->taskPositionModel->movePosition( $task['project_id'], $task['id'], diff --git a/app/Decorator/ColumnMoveRestrictionCacheDecorator.php b/app/Decorator/ColumnMoveRestrictionCacheDecorator.php index cb5e860c..2a3e9c2a 100644 --- a/app/Decorator/ColumnMoveRestrictionCacheDecorator.php +++ b/app/Decorator/ColumnMoveRestrictionCacheDecorator.php @@ -38,17 +38,18 @@ class ColumnMoveRestrictionCacheDecorator } /** - * Proxy method to get column Ids + * Proxy method to get sortable columns + * * @param int $project_id * @return array|mixed */ - public function getAllSrcColumns($project_id, $role) + public function getSortableColumns($project_id, $role) { $key = $this->cachePrefix.$project_id.$role; $columnIds = $this->cache->get($key); if ($columnIds === null) { - $columnIds = $this->columnMoveRestrictionModel->getAllSrcColumns($project_id, $role); + $columnIds = $this->columnMoveRestrictionModel->getSortableColumns($project_id, $role); $this->cache->set($key, $columnIds); } diff --git a/app/Formatter/BoardSwimlaneFormatter.php b/app/Formatter/BoardSwimlaneFormatter.php index 9b2ad935..ce67c8a8 100644 --- a/app/Formatter/BoardSwimlaneFormatter.php +++ b/app/Formatter/BoardSwimlaneFormatter.php @@ -24,7 +24,7 @@ class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface * @param array $swimlanes * @return $this */ - public function withSwimlanes($swimlanes) + public function withSwimlanes(array $swimlanes) { $this->swimlanes = $swimlanes; return $this; @@ -37,7 +37,7 @@ class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface * @param array $columns * @return $this */ - public function withColumns($columns) + public function withColumns(array $columns) { $this->columns = $columns; return $this; diff --git a/app/Helper/ProjectRoleHelper.php b/app/Helper/ProjectRoleHelper.php index 34905b52..99fa82bc 100644 --- a/app/Helper/ProjectRoleHelper.php +++ b/app/Helper/ProjectRoleHelper.php @@ -26,25 +26,45 @@ class ProjectRoleHelper extends Base } /** - * Return true if the task can be moved by the connected user + * Return true if the task can be moved by the logged user * * @param array $task * @return bool */ - public function isDraggable(array $task) + public function isDraggable(array &$task) { if ($task['is_active'] == 1 && $this->helper->user->hasProjectAccess('BoardViewController', 'save', $task['project_id'])) { - $role = $this->getProjectUserRole($task['project_id']); + return $this->isSortableColumn($task['project_id'], $task['column_id'], 'src_column_id'); + } + + return false; + } + + /** + * Return true is the column is sortable + * + * @param int $project_id + * @param int $column_id + * @param string $field + * @return bool + */ + public function isSortableColumn($project_id, $column_id, $field) + { + $role = $this->getProjectUserRole($project_id); + + if ($this->role->isCustomProjectRole($role)) { + $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($project_id, $role); - if ($this->role->isCustomProjectRole($role)) { - $srcColumnIds = $this->columnMoveRestrictionCacheDecorator->getAllSrcColumns($task['project_id'], $role); - return isset($srcColumnIds[$task['column_id']]); + foreach ($sortableColumns as $column) { + if ($column[$field] == $column_id) { + return true; + } } - return true; + return empty($sortableColumns); } - return false; + return true; } /** @@ -60,12 +80,19 @@ class ProjectRoleHelper extends Base $role = $this->getProjectUserRole($project_id); if ($this->role->isCustomProjectRole($role)) { - return $this->columnMoveRestrictionModel->isAllowed( - $project_id, - $role, - $src_column_id, - $dst_column_id - ); + if ($src_column_id == $dst_column_id) { + return true; + } + + $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($project_id, $role); + + foreach ($sortableColumns as $column) { + if ($column['src_column_id'] == $src_column_id && $column['dst_column_id'] == $dst_column_id) { + return true; + } + } + + return empty($sortableColumns); } return true; diff --git a/app/Model/ColumnMoveRestrictionModel.php b/app/Model/ColumnMoveRestrictionModel.php index 27c9afab..c2603efd 100644 --- a/app/Model/ColumnMoveRestrictionModel.php +++ b/app/Model/ColumnMoveRestrictionModel.php @@ -14,26 +14,6 @@ class ColumnMoveRestrictionModel extends Base { const TABLE = 'column_has_move_restrictions'; - /** - * Check if the custom project role is allowed to move a task - * - * @param int $project_id - * @param string $role - * @param int $src_column_id - * @param int $dst_column_id - * @return int - */ - public function isAllowed($project_id, $role, $src_column_id, $dst_column_id) - { - return $this->db->table(self::TABLE) - ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id') - ->eq(self::TABLE.'.project_id', $project_id) - ->eq(self::TABLE.'.src_column_id', $src_column_id) - ->eq(self::TABLE.'.dst_column_id', $dst_column_id) - ->eq('pr.role', $role) - ->exists(); - } - /** * Fetch one restriction * @@ -91,20 +71,21 @@ class ColumnMoveRestrictionModel extends Base } /** - * Get all source column Ids + * Get all sortable column Ids * * @param int $project_id * @param string $role * @return array */ - public function getAllSrcColumns($project_id, $role) + public function getSortableColumns($project_id, $role) { return $this->db - ->hashtable(self::TABLE) + ->table(self::TABLE) + ->columns(self::TABLE.'.src_column_id', self::TABLE.'.dst_column_id') ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id') ->eq(self::TABLE.'.project_id', $project_id) ->eq('pr.role', $role) - ->getAll('src_column_id', 'src_column_id'); + ->findAll(); } /** diff --git a/app/Template/board/table_tasks.php b/app/Template/board/table_tasks.php index 1651f5d6..a22f581b 100644 --- a/app/Template/board/table_tasks.php +++ b/app/Template/board/table_tasks.php @@ -8,7 +8,12 @@ > -
+
+ render($not_editable ? 'board/task_public' : 'board/task_private', array( 'project' => $project, diff --git a/assets/js/app.min.js b/assets/js/app.min.js index d98af493..e27eaf0c 100644 --- a/assets/js/app.min.js +++ b/assets/js/app.min.js @@ -1,2 +1,2 @@ Vue.component("chart-project-task-distribution",{props:["metrics"],template:'
',ready:function(){for(var t=[],e=0;e
',ready:function(){var t=[this.labelSpent],e=[this.labelEstimated],o=[];for(var a in this.metrics)t.push(this.metrics[a].time_spent),e.push(this.metrics[a].time_estimated),o.push("open"===a?this.labelOpen:this.labelClosed);c3.generate({data:{columns:[t,e],type:"bar"},bar:{width:{ratio:.2}},axis:{x:{type:"category",categories:o}},legend:{show:!0}})}}),Vue.component("chart-project-user-distribution",{props:["metrics"],template:'
',ready:function(){for(var t=[],e=0;e {{ labelOr }} {{ labelCancel }}
',data:function(){return{loading:!1}},computed:{isLoading:function(){return this.loading}},methods:{onSubmit:function(){this.loading=!0,this.callback()},onCancel:function(){_KB.get("Popover").close()}}}),Vue.component("task-move-position",{props:["board","saveUrl"],template:"#template-task-move-position",data:function(){return{swimlaneId:0,columnId:0,position:1,columns:[],tasks:[],positionChoice:"before"}},ready:function(){this.columns=this.board[0].columns,this.columnId=this.columns[0].id,this.tasks=this.columns[0].tasks},methods:{onChangeSwimlane:function(){var t=this;this.columnId=0,this.position=1,this.columns=[],this.tasks=[],this.positionChoice="before",this.board.forEach(function(e){e.id===t.swimlaneId&&(t.columns=e.columns,t.tasks=t.columns[0].tasks,t.columnId=t.columns[0].id)})},onChangeColumn:function(){var t=this;this.position=1,this.tasks=[],this.positionChoice="before",this.columns.forEach(function(e){e.id==t.columnId&&(t.tasks=e.tasks)})},onSubmit:function(){"after"==this.positionChoice&&this.position++,$.ajax({cache:!1,url:this.saveUrl,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:this.columnId,swimlane_id:this.swimlaneId,position:this.position}),complete:function(){window.location.reload(!0)}})}}});var Kanboard={};Kanboard.Accordion=function(t){this.app=t},Kanboard.Accordion.prototype.listen=function(){$(document).on("click",".accordion-toggle",function(t){var e=$(this).parents(".accordion-section");t.preventDefault(),e.hasClass("accordion-collapsed")?(e.find(".accordion-content").show(),e.removeClass("accordion-collapsed")):(e.find(".accordion-content").hide(),e.addClass("accordion-collapsed"))})},Kanboard.App=function(){this.controllers={}},Kanboard.App.prototype.get=function(t){return this.controllers[t]},Kanboard.App.prototype.execute=function(){for(var t in Kanboard)if("App"!==t){var e=new Kanboard[t](this);this.controllers[t]=e,"function"==typeof e.execute&&e.execute(),"function"==typeof e.listen&&e.listen(),"function"==typeof e.focus&&e.focus(),"function"==typeof e.keyboardShortcuts&&e.keyboardShortcuts()}this.focus(),this.chosen(),this.keyboardShortcuts(),this.datePicker(),this.autoComplete(),this.tagAutoComplete(),new Vue({el:"body"})},Kanboard.App.prototype.keyboardShortcuts=function(){var t=this;Mousetrap.bindGlobal("mod+enter",function(){var e=$("form");1==e.length?e.submit():e.length>1&&("INPUT"===document.activeElement.tagName||"TEXTAREA"===document.activeElement.tagName?$(document.activeElement).parents("form").submit():t.get("Popover").isOpen()&&$("#popover-container form").submit())}),Mousetrap.bind("b",function(t){t.preventDefault(),$("#board-selector").trigger("chosen:open")}),Mousetrap.bindGlobal("esc",function(){t.get("Popover").close(),t.get("Dropdown").close()}),Mousetrap.bind("?",function(){t.get("Popover").open($("body").data("keyboard-shortcut-url"))})},Kanboard.App.prototype.focus=function(){$(document).on("focus",".auto-select",function(){$(this).select()}),$(document).on("mouseup",".auto-select",function(t){t.preventDefault()})},Kanboard.App.prototype.chosen=function(){$(".chosen-select").each(function(){var t=$(this).data("search-threshold");void 0===t&&(t=10),$(this).chosen({width:"180px",no_results_text:$(this).data("notfound"),disable_search_threshold:t})}),$(".select-auto-redirect").change(function(){var t=new RegExp($(this).data("redirect-regex"),"g");window.location=$(this).data("redirect-url").replace(t,$(this).val())})},Kanboard.App.prototype.datePicker=function(){var t=$("body"),e=t.data("js-date-format"),o=t.data("js-time-format"),a=t.data("js-lang");$.datepicker.setDefaults($.datepicker.regional[a]),$.timepicker.setDefaults($.timepicker.regional[a]),$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:e,constrainInput:!1}),$(".form-datetime").datetimepicker({dateFormat:e,timeFormat:o,constrainInput:!1})},Kanboard.App.prototype.tagAutoComplete=function(){$(".tag-autocomplete").select2({tags:!0})},Kanboard.App.prototype.autoComplete=function(){$(".autocomplete").each(function(){var t=$(this),e=t.data("dst-field"),o=t.data("dst-extra-field");""==$("#form-"+e).val()&&t.parent().find("button[type=submit]").attr("disabled","disabled"),t.autocomplete({source:t.data("search-url"),minLength:1,select:function(a,n){$("input[name="+e+"]").val(n.item.id),o&&$("input[name="+o+"]").val(n.item[o]),t.parent().find("button[type=submit]").removeAttr("disabled")}})})},Kanboard.App.prototype.hasId=function(t){return!!document.getElementById(t)},Kanboard.App.prototype.showLoadingIcon=function(){$("body").append(' ')},Kanboard.App.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()},Kanboard.App.prototype.formatDuration=function(t){return t>=86400?Math.round(t/86400)+"d":t>=3600?Math.round(t/3600)+"h":t>=60?Math.round(t/60)+"m":t+"s"},Kanboard.App.prototype.isVisible=function(){var t="";return"undefined"!=typeof document.hidden?t="visibilityState":"undefined"!=typeof document.mozHidden?t="mozVisibilityState":"undefined"!=typeof document.msHidden?t="msVisibilityState":"undefined"!=typeof document.webkitHidden&&(t="webkitVisibilityState"),""==t||"visible"==document[t]},Kanboard.AvgTimeColumnChart=function(t){this.app=t},Kanboard.AvgTimeColumnChart.prototype.execute=function(){this.app.hasId("analytic-avg-time-column")&&this.show()},Kanboard.AvgTimeColumnChart.prototype.show=function(){var t=$("#chart"),e=t.data("metrics"),o=[t.data("label")],a=[];for(var n in e)o.push(e[n].average),a.push(e[n].title);c3.generate({data:{columns:[o],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:this.app.formatDuration}}},legend:{show:!1}})},Kanboard.BoardCollapsedMode=function(t){this.app=t},Kanboard.BoardCollapsedMode.prototype.keyboardShortcuts=function(){var t=this;t.app.hasId("board")&&Mousetrap.bind("s",function(){t.toggle()})},Kanboard.BoardCollapsedMode.prototype.toggle=function(){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(e){$(".filter-display-mode").toggle(),t.app.get("BoardDragAndDrop").refresh(e)}})},Kanboard.BoardColumnView=function(t){this.app=t},Kanboard.BoardColumnView.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardColumnView.prototype.listen=function(){var t=this;$(document).on("click",".board-toggle-column-view",function(){t.toggle($(this).data("column-id"))})},Kanboard.BoardColumnView.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardColumnView.prototype.render=function(){var t=this;$(".board-column-header").each(function(){var e=$(this).data("column-id");localStorage.getItem("hidden_column_"+e)&&t.hideColumn(e)})},Kanboard.BoardColumnView.prototype.toggle=function(t){localStorage.getItem("hidden_column_"+t)?this.showColumn(t):this.hideColumn(t)},Kanboard.BoardColumnView.prototype.hideColumn=function(t){$(".board-column-"+t+" .board-column-expanded").hide(),$(".board-column-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t+" .board-column-expanded").hide(),$(".board-column-header-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t).each(function(){$(this).removeClass("board-column-compact"),$(this).addClass("board-column-header-collapsed")}),$(".board-column-"+t).each(function(){$(this).addClass("board-column-task-collapsed")}),$(".board-column-"+t+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+t).height())}),localStorage.setItem("hidden_column_"+t,1)},Kanboard.BoardColumnView.prototype.showColumn=function(t){$(".board-column-"+t+" .board-column-expanded").show(),$(".board-column-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t+" .board-column-expanded").show(),$(".board-column-header-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t).removeClass("board-column-header-collapsed"),$(".board-column-"+t).removeClass("board-column-task-collapsed"),0==localStorage.getItem("horizontal_scroll")&&$(".board-column-header-"+t).addClass("board-column-compact"),localStorage.removeItem("hidden_column_"+t)},Kanboard.BoardHorizontalScrolling=function(t){this.app=t},Kanboard.BoardHorizontalScrolling.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardHorizontalScrolling.prototype.listen=function(){var t=this;$(document).on("click",".filter-toggle-scrolling",function(e){e.preventDefault(),t.toggle()})},Kanboard.BoardHorizontalScrolling.prototype.keyboardShortcuts=function(){var t=this;t.app.hasId("board")&&Mousetrap.bind("c",function(){t.toggle()})},Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardHorizontalScrolling.prototype.toggle=function(){var t=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",0==t?1:0),this.render()},Kanboard.BoardHorizontalScrolling.prototype.render=function(){0==localStorage.getItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))},Kanboard.BoardPolling=function(t){this.app=t},Kanboard.BoardPolling.prototype.execute=function(){if(this.app.hasId("board")){var t=parseInt($("#board").attr("data-check-interval"));t>0&&window.setInterval(this.check.bind(this),1e3*t)}},Kanboard.BoardPolling.prototype.check=function(){if(this.app.isVisible()&&!this.app.get("BoardDragAndDrop").savingInProgress){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$("#board").data("check-url"),statusCode:{200:function(e){t.app.get("BoardDragAndDrop").refresh(e)},304:function(){t.app.hideLoadingIcon()}}})}},Kanboard.BoardTask=function(t){this.app=t},Kanboard.BoardTask.prototype.listen=function(){var t=this;$(document).on("click",".task-board-change-assignee",function(e){e.preventDefault(),e.stopPropagation(),t.app.get("Popover").open($(this).data("url"))}),$(document).on("click",".task-board",function(t){"A"!=t.target.tagName&&"IMG"!=t.target.tagName&&(window.location=$(this).data("task-url"))})},Kanboard.BoardTask.prototype.keyboardShortcuts=function(){var t=this;t.app.hasId("board")&&Mousetrap.bind("n",function(){t.app.get("Popover").open($("#board").data("task-creation-url"))})},Kanboard.BurndownChart=function(t){this.app=t},Kanboard.BurndownChart.prototype.execute=function(){this.app.hasId("analytic-burndown")&&this.show()},Kanboard.BurndownChart.prototype.show=function(){for(var t=$("#chart"),e=t.data("metrics"),o=[[t.data("label-total")]],a=[],n=d3.time.format("%Y-%m-%d"),i=d3.time.format(t.data("date-format")),r=0;r0&&(void 0==o[0][r]&&o[0].push(0),o[0][r]+=e[r][s]),0==s&&a.push(i(n.parse(e[r][s]))));c3.generate({data:{columns:o},axis:{x:{type:"category",categories:a}}})},Kanboard.Calendar=function(t){this.app=t},Kanboard.Calendar.prototype.execute=function(){var t=$("#calendar");1==t.length&&this.show(t)},Kanboard.Calendar.prototype.show=function(t){t.fullCalendar({lang:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(e){$.ajax({cache:!1,url:t.data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:e.id,date_due:e.start.format()})})},viewRender:function(){var e=t.data("check-url"),o={start:t.fullCalendar("getView").start.format(),end:t.fullCalendar("getView").end.format()};for(var a in o)e+="&"+a+"="+o[a];$.getJSON(e,function(e){t.fullCalendar("removeEvents"),t.fullCalendar("addEventSource",e),t.fullCalendar("rerenderEvents")})}})},Kanboard.Column=function(t){this.app=t},Kanboard.Column.prototype.listen=function(){this.dragAndDrop()},Kanboard.Column.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".columns-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,o){var a=o.item;a.removeClass("draggable-item-selected"),t.savePosition(a.data("column-id"),a.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Column.prototype.savePosition=function(t,e){var o=$(".columns-table").data("save-position-url"),a=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:o,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:t,position:e}),complete:function(){a.app.hideLoadingIcon()}})},Kanboard.CumulativeFlowDiagram=function(t){this.app=t},Kanboard.CumulativeFlowDiagram.prototype.execute=function(){this.app.hasId("analytic-cfd")&&this.show()},Kanboard.CumulativeFlowDiagram.prototype.show=function(){for(var t=$("#chart"),e=t.data("metrics"),o=[],a=[],n=[],i=d3.time.format("%Y-%m-%d"),r=d3.time.format(t.data("date-format")),s=0;s0&&a.push(e[s][d])):(o[d].push(e[s][d]),0==d&&n.push(r(i.parse(e[s][d]))));c3.generate({data:{columns:o,type:"area-spline",groups:[a]},axis:{x:{type:"category",categories:n}}})},Kanboard.Dropdown=function(t){this.app=t},Kanboard.Dropdown.prototype.listen=function(){var t=this;$(document).on("click",function(){t.close()}),$(document).on("click",".dropdown-menu",function(e){e.preventDefault(),e.stopImmediatePropagation(),t.close();var o=$(this).next("ul"),a=$(this).offset();$("body").append(jQuery("
",{id:"dropdown"})),o.clone().appendTo("#dropdown");var n=$("#dropdown ul");n.addClass("dropdown-submenu-open");var i=n.outerHeight(),r=n.outerWidth();a.top+i-$(window).scrollTop()<$(window).height()||$(window).scrollTop()+a.top$(window).width()?n.css("left",a.left-r+$(this).outerWidth()):n.css("left",a.left)}),$(document).on("click",".dropdown-submenu-open li",function(t){$(t.target).is("li")&&$(this).find("a:visible")[0].click()})},Kanboard.Dropdown.prototype.close=function(){$("#dropdown").remove()},Kanboard.Dropdown.prototype.onPopoverOpened=function(){this.close()},Kanboard.FileUpload=function(t){this.app=t,this.files=[],this.currentFile=0},Kanboard.FileUpload.prototype.onPopoverOpened=function(){var t=document.getElementById("file-dropzone"),e=this;t&&(t.ondragover=t.ondragenter=function(t){t.stopPropagation(),t.preventDefault()},t.ondrop=function(t){t.stopPropagation(),t.preventDefault(),e.files=t.dataTransfer.files,e.show(),$("#file-error-max-size").hide()},$(document).on("click","#file-browser",function(t){t.preventDefault(),$("#file-form-element").get(0).click()}),$(document).on("click","#file-upload-button",function(t){t.preventDefault(),e.currentFile=0,e.checkFiles()}),$("#file-form-element").change(function(){e.files=document.getElementById("file-form-element").files,e.show(),$("#file-error-max-size").hide()}))},Kanboard.FileUpload.prototype.show=function(){if($("#file-list").remove(),this.files.length>0){$("#file-upload-button").prop("disabled",!1),$("#file-dropzone-inner").hide();for(var t=jQuery("