%PDF- %PDF-
Direktori : /backups/router/usr/local/opnsense/www/js/ |
Current File : //backups/router/usr/local/opnsense/www/js/opnsense_bootgrid_plugin.js |
/* * Copyright (C) 2015-2024 Deciso B.V. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * reload bootgrid, return to current selected page */ function std_bootgrid_reload(gridId) { let currentpage = $("#" + gridId).bootgrid("getCurrentPage"); $("#"+gridId).bootgrid("reload"); // absolutely not perfect, bootgrid.reload doesn't seem to support when().done() setTimeout(function(){ $('#'+gridId+'-footer a[data-page="'+currentpage+'"]').click(); }, 400); } /** * creates new bootgrid object and links actions to our standard templates * uses the following data properties to define functionality: * data-editDialog : id of the edit dialog to use ( see base_dialog.volt template for details ) * data-action [add] : set data-action "add" to create a new record * data-action [deleteSelected]: set data-action "deleteSelected" to delete selected items * * and uses the following properties (params array): * search : url to search action (GET) * get : url to get data action (GET) will be suffixed by uuid * set : url to set data action (POST) will be suffixed by uuid * add : url to create a new data record (POST) * del : url to del item action (POST) will be suffixed by uuid * info : url to get data action that will be displayed informationally suffixed by the uuid * * @param params * @returns {*} * @constructor */ $.fn.UIBootgrid = function (params) { let this_grid = this; /** * register commands */ this.getCommands = function() { let result = { "add": { method: this_grid.command_add, requires: ['get', 'set'], sequence: 100 }, "edit": { method: this_grid.command_edit, classname: 'fa fa-fw fa-pencil', requires: ['get', 'set'], sequence: 100 }, "delete": { method: this_grid.command_delete, classname: 'fa fa-fw fa-trash-o', requires: ['del'], sequence: 500 }, "copy": { method: this_grid.command_copy, classname: 'fa fa-fw fa-clone', requires: ['get', 'set'], sequence: 200 }, "info": { method: this_grid.command_info, classname: 'fa fa-fw fa-info-circle', requires: ['info'], sequence: 500 }, "toggle": { method: this_grid.command_toggle, requires: ['toggle'], sequence: 100 }, "delete-selected": { method: this_grid.command_delete_selected, requires: ['del'], sequence: 100 } }; // register additional commands if ( 'commands' in params) { $.each(params['commands'], function( k, v ) { if (result[k] === undefined) { result[k] = {requires: [], sequence: 1}; } $.each(v, function(ck, cv) { result[k][ck] = cv; }); }); } return result; }; /** * construct new bootgrid */ this.construct = function() { // set defaults let gridopt = { ajax: true, selection: true, multiSelect: true, rowCount:[7,14,20,50,100,-1], url: params['search'], initialSearchPhrase: "", ajaxSettings: { contentType: 'application/json;charset=utf-8', dataType: "json", }, requestHandler: function (request) { return JSON.stringify(request); }, searchSettings: { delay: 1000, }, datakey: 'uuid', useRequestHandlerOnGet: false, triggerEditFor: '', formatters: { commands: function (column, row) { let html = []; // sort commands by sequence let commands = this_grid.getCommands(); let commandlist = Array(); Object.keys(commands).map(function (k) { let item = commands[k]; item.name = k; commandlist.push(item) }); commandlist = commandlist.sort(function(a,b) { return (a.sequence > b.sequence) ? 1 : ((b.sequence > a.sequence) ? -1 : 0);} ); let rowid = params.datakey !== undefined ? params.datakey : 'uuid'; commandlist.map(function(command){ let has_option = command.classname !== undefined; let option_title_str = command.title !== undefined ? " title=\""+command.title+"\"" : ""; for (let i=0; i < command.requires.length; i++) { if (!(command.requires[i] in params)) { has_option = false; } } if (has_option) { html.push("<button type=\"button\" " + option_title_str + " class=\"btn btn-xs btn-default bootgrid-tooltip command-"+command.name+ "\" data-row-id=\"" + row[rowid] + "\">"+ "<span class=\""+command.classname+"\"></span></button> " ); } }); return html.join('\n'); }, commandsWithInfo: function(column, row) { return '<button type="button" class="btn btn-xs btn-default command-info bootgrid-tooltip" data-row-id="' + row.uuid + '"><span class="fa fa-fw fa-info-circle"></span></button> ' + '<button type="button" class="btn btn-xs btn-default command-edit bootgrid-tooltip" data-row-id="' + row.uuid + '"><span class="fa fa-fw fa-pencil"></span></button>' + '<button type="button" class="btn btn-xs btn-default command-copy bootgrid-tooltip" data-row-id="' + row.uuid + '"><span class="fa fa-fw fa-clone"></span></button>' + '<button type="button" class="btn btn-xs btn-default command-delete bootgrid-tooltip" data-row-id="' + row.uuid + '"><span class="fa fa-fw fa-trash-o"></span></button>'; }, rowtoggle: function (column, row) { if (parseInt(row[column.id], 2) === 1) { return '<span style="cursor: pointer;" class="fa fa-fw fa-check-square-o command-toggle bootgrid-tooltip" data-value="1" data-row-id="' + row.uuid + '"></span>'; } else { return '<span style="cursor: pointer;" class="fa fa-fw fa-square-o command-toggle bootgrid-tooltip" data-value="0" data-row-id="' + row.uuid + '"></span>'; } }, boolean: function (column, row) { if (parseInt(row[column.id], 2) === 1) { return "<span class=\"fa fa-fw fa-check\" data-value=\"1\" data-row-id=\"" + row.uuid + "\"></span>"; } else { return "<span class=\"fa fa-fw fa-times\" data-value=\"0\" data-row-id=\"" + row.uuid + "\"></span>"; } }, bytes: function (column, row) { if (row[column.id] && row[column.id] > 0) { return byteFormat(row[column.id], 2); } return ''; }, statusled: function (column, row) { if (row[column.id] && row[column.id] == 'red') { return "<span class=\"fa fa-fw fa-square text-danger\"></span>"; } else if (row[column.id] && row[column.id] == 'green') { return "<span class=\"fa fa-fw fa-square text-success\"></span>"; } else { return "<span class=\"fa fa-fw fa-square text-muted\"></span>"; } }, }, onBeforeRenderDialog: null }; // merge additional options (if any) if (params['options'] !== undefined) { $.each(params['options'], function(key, value) { if (typeof(value) === 'object' && Array.isArray(value) == false) { gridopt[key] = Object.assign({}, gridopt[key], value); } else if (key == 'requestHandler'){ gridopt[key] = function(request) { let response = value(request); // automatic type conversion, we expect a json (string) as result if (typeof(response) === 'string') { return response; } else { return JSON.stringify(response); } }; } else { gridopt[key] = value; } }); } if (gridopt.useRequestHandlerOnGet) { this_grid.requestHandler = gridopt.requestHandler; } else { this_grid.requestHandler = null; } this_grid.onBeforeRenderDialog = gridopt.onBeforeRenderDialog; if (gridopt.triggerEditFor) { this_grid.command_edit(null, gridopt.triggerEditFor); } // construct a new grid return this_grid.bootgrid(gridopt).on("loaded.rs.jquery.bootgrid", function (e) { // scale footer on resize $(this).find("tfoot td:first-child").attr('colspan',$(this).find("th").length - 1); // invert colors if needed (check if there is a disabled field instead of an enabled field let inverted = $(this).find("thead th[data-column-id=disabled]").length > 0; $(this).find('tr[data-row-id]').each(function(index, entry){ ['[class*="command-toggle"]', '[class*="command-boolean"]'].forEach(function (selector) { let selected_element = $(entry).find(selector).first(); if (selected_element.length > 0) { if ((selected_element.data("value") == "0") !== inverted ) { $(entry).addClass("text-muted"); } } }); }); }); }; this.show_edit_dialog = function(event, endpoint) { const dfObj = new $.Deferred(); let editDlg = this_grid.attr('data-editDialog'); let urlMap = {}; let server_params = undefined; urlMap['frm_' + editDlg] = endpoint; if (this_grid.requestHandler !== null) { // our requestHandler returns a JSON object, convert it back first server_params = this_grid.requestHandler({}); } mapDataToFormUI(urlMap, server_params).done(function(payload){ // update selectors formatTokenizersUI(); $('.selectpicker').selectpicker('refresh'); // clear validation errors (if any) clearFormValidation('frm_' + editDlg); let target = $('#'+editDlg); if (target.hasClass('modal')) { // show dialog and hook draggable event on first show target.modal({backdrop: 'static', keyboard: false}); if (!target.hasClass('modal_draggable')) { target.addClass('modal_draggable'); let height=0, width=0, ypos=0, xpos=0; let top_boundary = parseInt($("section.page-content-main").css('padding-top')) + parseInt($("main.page-content").css('padding-top')) - parseInt($("div.modal-dialog").css('margin-top')); let this_header = target.find('.modal-header'); this_header.css("cursor","move"); this_header.on('mousedown', function(e){ this_header.addClass("drag"); height = target.outerHeight(); width = target.outerWidth(); ypos = target.offset().top + height - e.pageY; xpos = target.offset().left + width - e.pageX; }); $(document.body).on('mousemove', function(e){ let itop = e.pageY + ypos - height; let ileft = e.pageX + xpos - width; if (this_header.hasClass("drag") && itop >= top_boundary){ target.offset({top: itop, left: ileft}); } }).on('mouseup mouseleave', function(e){ this_header.removeClass("drag"); }); } else { // reset to starting position (remove drag distance) target.css('top', '').css('left', ''); } } else { // when edit dialog isn't a modal, fire click event target.click(); } if (this_grid.onBeforeRenderDialog) { this_grid.onBeforeRenderDialog(payload).done(function(){ dfObj.resolve(); }); } else { dfObj.resolve(); } }); return dfObj; }; /** * init / clear save button */ this.init_save_btn = function() { let editDlg = this_grid.attr('data-editDialog'); let saveDlg = $("#btn_"+editDlg+"_save").unbind('click'); saveDlg.find('i').removeClass("fa fa-spinner fa-pulse"); return saveDlg; } /** * add event */ this.command_add = function(event) { event.stopPropagation(); let editDlg = this_grid.attr('data-editDialog'); if (editDlg !== undefined) { let saveDlg = this_grid.init_save_btn(); this_grid.show_edit_dialog(event, params['get']).done(function(){ $('#'+editDlg).trigger('opnsense_bootgrid_mapped', ['add']); saveDlg.click(function(){ if (saveDlg.find('i').hasClass('fa-spinner')) { return; } saveDlg.find('i').addClass("fa fa-spinner fa-pulse"); saveFormToEndpoint(params['add'], 'frm_' + editDlg, function(){ if ($('#'+editDlg).hasClass('modal')) { $("#"+editDlg).modal('hide'); } else { $("#"+editDlg).change(); } std_bootgrid_reload(this_grid.attr('id')); this_grid.showSaveAlert(event); saveDlg.find('i').removeClass("fa fa-spinner fa-pulse"); }, true, function(){ saveDlg.find('i').removeClass("fa fa-spinner fa-pulse"); }); }); }); } else { console.log("[grid] action get or data-editDialog missing") } }; /** * animate alert when saved */ this.showSaveAlert = function(event) { let editAlert = this_grid.attr('data-editAlert'); if (editAlert !== undefined) { $("#"+editAlert).slideDown(1000, function(){ setTimeout(function(){ $("#"+editAlert).not(":animated").slideUp(2000); }, 2000); }); } }; /** * edit event */ this.command_edit = function(event, uuid = null) { if (uuid === null) event.stopPropagation(); let editDlg = this_grid.attr('data-editDialog'); if (editDlg !== undefined) { if (uuid === null) uuid = $(this).data("row-id") !== undefined ? $(this).data("row-id") : ''; let saveDlg = this_grid.init_save_btn(); this_grid.show_edit_dialog(event, params['get'] + uuid).done(function(){ saveDlg.unbind('click').click(function(){ if (saveDlg.find('i').hasClass('fa-spinner')) { return; } saveDlg.find('i').addClass("fa fa-spinner fa-pulse"); saveFormToEndpoint(params['set']+uuid, 'frm_' + editDlg, function(){ if ($('#'+editDlg).hasClass('modal')) { $("#"+editDlg).modal('hide'); } else { $("#"+editDlg).change(); } std_bootgrid_reload(this_grid.attr('id')); this_grid.showSaveAlert(event); saveDlg.find('i').removeClass("fa fa-spinner fa-pulse"); }, true, function(){ saveDlg.find('i').removeClass("fa fa-spinner fa-pulse"); }); }); $('#'+editDlg).trigger('opnsense_bootgrid_mapped', ['edit']); }); } else { console.log("[grid] action get or data-editDialog missing") } }; /** * delete event */ this.command_delete = function(event) { event.stopPropagation(); let uuid=$(this).data("row-id"); stdDialogRemoveItem($.fn.UIBootgrid.defaults.removeWarningText,function() { ajaxCall(params['del'] + uuid, {},function(data,status){ // reload grid after delete std_bootgrid_reload(this_grid.attr('id')); this_grid.showSaveAlert(event); }); }); }; /** * delete selected event */ this.command_delete_selected = function(event) { event.stopPropagation(); stdDialogRemoveItem($.fn.UIBootgrid.defaults.removeWarningText,function(){ const rows = $("#" + this_grid.attr('id')).bootgrid('getSelectedRows'); if (rows !== undefined){ const deferreds = []; $.each(rows, function(key,uuid){ deferreds.push(ajaxCall(params['del'] + uuid, {},null)); }); // refresh after load $.when.apply(null, deferreds).done(function(){ std_bootgrid_reload(this_grid.attr('id')); this_grid.showSaveAlert(event); }); } }); }; /** * copy event */ this.command_copy = function(event) { event.stopPropagation(); const editDlg = this_grid.attr('data-editDialog'); if (editDlg !== undefined) { const uuid = $(this).data("row-id"); const urlMap = {}; urlMap['frm_' + editDlg] = params['get'] + uuid + "?fetchmode=copy"; mapDataToFormUI(urlMap).done(function () { // update selectors formatTokenizersUI(); $('.selectpicker').selectpicker('refresh'); // clear validation errors (if any) clearFormValidation('frm_' + editDlg); if ($('#'+editDlg).hasClass('modal')) { // show dialog $('#'+editDlg).modal({backdrop: 'static', keyboard: false}); } else { // when edit dialog isn't a modal, fire click event $('#'+editDlg).click(); } // define save action let saveDlg = this_grid.init_save_btn(); saveDlg.click(function(){ if (saveDlg.find('i').hasClass('fa-spinner')) { return; } saveDlg.find('i').addClass("fa fa-spinner fa-pulse"); saveFormToEndpoint(params['add'], 'frm_' + editDlg, function(){ if ($('#'+editDlg).hasClass('modal')) { $("#"+editDlg).modal('hide'); } else { $("#"+editDlg).change(); } std_bootgrid_reload(this_grid.attr('id')); this_grid.showSaveAlert(event); saveDlg.find('i').removeClass("fa fa-spinner fa-pulse"); }, true, function(){ saveDlg.find('i').removeClass("fa fa-spinner fa-pulse"); }); }); $('#'+editDlg).trigger('opnsense_bootgrid_mapped', ['copy']); }); } else { console.log("[grid] action get or data-editDialog missing") } }; /** * info event */ this.command_info = function(event) { event.stopPropagation(); const uuid = $(this).data("row-id"); ajaxGet(params['info'] + uuid, {}, function(data, status) { if(status === 'success') { const title = data['title'] || "Information"; const message = data['message'] || "A Message"; const close = data['close'] || "Close"; stdDialogInform(title, message, close, undefined, "info"); } }); }; /** * toggle event */ this.command_toggle = function(event) { event.stopPropagation(); const uuid = $(this).data("row-id"); $(this).removeClass('fa-check-square-o fa-square-o').addClass("fa-spinner fa-pulse"); ajaxCall(params['toggle'] + uuid, {},function(data,status){ // reload grid after delete std_bootgrid_reload(this_grid.attr('id')); this_grid.showSaveAlert(event); }); }; /** * init bootgrids */ return this.each((function(){ // since we start using simple class selectors for our commands, we need to make sure "add" and // "delete selected" actions are properly marked $(this).find("*[data-action=add]").addClass('command-add bootgrid-tooltip'); $(this).find("*[data-action=deleteSelected]").addClass('command-delete-selected bootgrid-tooltip'); if (params !== undefined && params['search'] !== undefined) { // create new bootgrid component and link source const grid = this_grid.construct(); // edit dialog id to use ( see base_dialog.volt template for details) const editDlg = $(this).attr('data-editDialog'); // link edit and delete event buttons grid.on("loaded.rs.jquery.bootgrid", function(){ // toggle all automated tooltips $(this).find(".bootgrid-tooltip").each(function (index) { if ($(this).attr('title') !== undefined) { // keep this tooltip } else if ($(this).hasClass('command-add')) { $(this).attr('title', $.fn.UIBootgrid.defaults.addText); } else if ($(this).hasClass('command-delete-selected')) { $(this).attr('title', $.fn.UIBootgrid.defaults.deleteSelectedText); } else if ($(this).hasClass('command-edit')) { $(this).attr('title', $.fn.UIBootgrid.defaults.editText); } else if ($(this).hasClass('command-toggle')) { if ($(this).data('value') === 1) { $(this).attr('title', $.fn.UIBootgrid.defaults.disableText); } else { $(this).attr('title', $.fn.UIBootgrid.defaults.enableText); } } else if ($(this).hasClass('command-delete')) { $(this).attr('title', $.fn.UIBootgrid.defaults.deleteText); } else if ($(this).hasClass('command-info')) { $(this).attr('title', $.fn.UIBootgrid.defaults.infoText); } else if ($(this).hasClass('command-copy')) { $(this).attr('title', $.fn.UIBootgrid.defaults.cloneText); } else { $(this).attr('title', 'Error: no tooltip match'); } $(this).tooltip(); }); // tooltip when ellipsis is used (overflow on text elements without children) $(this).find("td").bind('mouseenter', function(){ let $this = $(this); if (this.offsetWidth < this.scrollWidth && !$this.attr('title') && $this.children().length == 0){ $this.attr('title', $this.text()).tooltip({container: 'body', trigger: 'hover'}).tooltip('show'); } }); // hook all events const commands = this_grid.getCommands(); Object.keys(commands).map(function (k) { let has_option = true; for (let i=0; i < commands[k]['requires'].length; i++) { if (!(commands[k]['requires'][i] in params)) { has_option = false; } } if (has_option) { grid.find(".command-"+k).unbind('click').on("click", commands[k].method); } else if ($(".command-"+k).length > 0) { console.log("not all requirements met to link " + k); } }); }); return grid; } })); }; $.fn.UIBootgrid.defaults = { /* translations are rendered in the default page template */ };