%PDF- %PDF-
| Direktori : /proc/thread-self/root/backups/router/usr/local/opnsense/www/js/ |
| Current File : //proc/thread-self/root/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 */ };