You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
11 KiB
406 lines
11 KiB
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
|
|
/*
|
|
* Automatic checkbox range selection
|
|
*/
|
|
var chkbxRange = {
|
|
startbox: null, // start checkbox obj
|
|
chkboxes: {}, // ckbx list
|
|
prefix: null, // prefix for session storage variable name
|
|
pageGoName: null, // which checkboxes should be counted by Go button and saved to session storage
|
|
sessionStorageName: null,
|
|
event_handlers: null,
|
|
|
|
init: function() {
|
|
var path = new Curl();
|
|
var filename = basename(path.getPath(), '.php');
|
|
this.sessionStorageName = 'cb_' + filename + (this.prefix ? '_' + this.prefix : '');
|
|
// Erase old checkboxes.
|
|
this.chkboxes = {};
|
|
this.startbox = null;
|
|
|
|
this.resetOtherPage();
|
|
|
|
// initialize checkboxes
|
|
var chkboxes = jQuery('.list-table tbody input[type=checkbox]:not(:disabled)');
|
|
if (chkboxes.length > 0) {
|
|
for (var i = 0; i < chkboxes.length; i++) {
|
|
this.implement(chkboxes[i]);
|
|
}
|
|
}
|
|
|
|
// load selected checkboxes from session storage or cache
|
|
if (this.pageGoName != null) {
|
|
const selected_ids = this.getSelectedIds();
|
|
|
|
// check if checkboxes should be selected from session storage
|
|
if (!jQuery.isEmptyObject(selected_ids)) {
|
|
var objectIds = jQuery.map(selected_ids, function(id) { return id });
|
|
}
|
|
// no checkboxes selected, check browser cache if checkboxes are still checked and update state
|
|
else {
|
|
var checkedFromCache = jQuery('main .list-table tbody input[type=checkbox]:checked:not(:disabled)');
|
|
var objectIds = jQuery.map(checkedFromCache, jQuery.proxy(function(checkbox) {
|
|
return this.getObjectIdFromName(checkbox.name);
|
|
}, this));
|
|
}
|
|
|
|
this.checkObjects(this.pageGoName, objectIds, true);
|
|
this.update(this.pageGoName);
|
|
}
|
|
|
|
if (this.event_handlers === null) {
|
|
this.event_handlers = {
|
|
action_button_click: (e) => this.submitFooterButton(e)
|
|
};
|
|
}
|
|
|
|
for (const footer_button of document.querySelectorAll('#action_buttons button:not(.js-no-chkbxrange)')) {
|
|
footer_button.addEventListener('click', this.event_handlers.action_button_click);
|
|
}
|
|
},
|
|
|
|
implement: function(obj) {
|
|
// skip the "select all" checkbox
|
|
if (obj.name.indexOf('all_') > -1) {
|
|
return;
|
|
}
|
|
|
|
var objName = this.getObjectFromName(obj.name);
|
|
|
|
if (typeof(this.chkboxes[objName]) === 'undefined') {
|
|
this.chkboxes[objName] = [];
|
|
}
|
|
this.chkboxes[objName].push(obj);
|
|
|
|
addListener(obj, 'click', this.handleClick.bindAsEventListener(this), false);
|
|
|
|
if (objName == this.pageGoName) {
|
|
var objId = jQuery(obj).val();
|
|
if (isset(objId, this.getSelectedIds())) {
|
|
obj.checked = true;
|
|
}
|
|
}
|
|
},
|
|
|
|
getSelectedIds() {
|
|
const session_selected_ids = sessionStorage.getItem(this.sessionStorageName);
|
|
|
|
return session_selected_ids === null ? {} : JSON.parse(session_selected_ids);
|
|
},
|
|
|
|
/**
|
|
* Handles a click on one of the checkboxes.
|
|
*
|
|
* @param e
|
|
*/
|
|
handleClick: function(e) {
|
|
e = e || window.event;
|
|
var checkbox = e.target;
|
|
|
|
PageRefresh.restart();
|
|
|
|
var object = this.getObjectFromName(checkbox.name);
|
|
var objectId = this.getObjectIdFromName(checkbox.name);
|
|
|
|
// range selection
|
|
if ((e.ctrlKey || e.shiftKey) && this.startbox != null) {
|
|
this.checkObjectRange(object, this.startbox, checkbox, this.startbox.checked);
|
|
}
|
|
// an individual checkbox
|
|
else {
|
|
this.checkObjects(object, [objectId], checkbox.checked);
|
|
}
|
|
|
|
this.update(object);
|
|
|
|
this.startbox = checkbox;
|
|
},
|
|
|
|
/**
|
|
* Extracts the name of an object from the name of a checkbox.
|
|
*
|
|
* @param {string} name
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
getObjectFromName: function(name) {
|
|
return name.split('[')[0];
|
|
},
|
|
|
|
/**
|
|
* Extracts the ID of an object from the name of a checkbox.
|
|
*
|
|
* @param {string} name
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
getObjectIdFromName: function(name) {
|
|
var id = name.split('[')[1];
|
|
id = id.substring(0, id.lastIndexOf(']'));
|
|
|
|
return id;
|
|
},
|
|
|
|
/**
|
|
* Returns the checkboxes in an object group.
|
|
*
|
|
* @param string object
|
|
*
|
|
* @returns {Array}
|
|
*/
|
|
getObjectCheckboxes: function(object) {
|
|
return this.chkboxes[object] || [];
|
|
},
|
|
|
|
/**
|
|
* Toggle all checkboxes of the given objects.
|
|
*
|
|
* Checks all of the checkboxes that belong to these objects and highlights the table row.
|
|
*
|
|
* @param {string} object
|
|
* @param {Array} objectIds array of objects IDs as integers
|
|
* @param {boolean} checked
|
|
*/
|
|
checkObjects: function(object, objectIds, checked) {
|
|
const selected_ids = this.getSelectedIds();
|
|
|
|
jQuery.each(this.getObjectCheckboxes(object), jQuery.proxy(function(i, checkbox) {
|
|
var objectId = this.getObjectIdFromName(checkbox.name);
|
|
|
|
if (objectIds.indexOf(objectId) > -1) {
|
|
checkbox.checked = checked;
|
|
|
|
jQuery(checkbox).closest('tr').toggleClass('row-selected', checked);
|
|
// Remove class attribute if it's empty.
|
|
jQuery(checkbox).closest('tr').filter('*[class=""]').removeAttr('class');
|
|
|
|
if (checked) {
|
|
const actions = document.getElementById(object + '_' + objectId).getAttribute('data-actions');
|
|
selected_ids[objectId] = (actions === null) ? '' : actions;
|
|
}
|
|
else {
|
|
delete selected_ids[objectId];
|
|
}
|
|
}
|
|
}, this));
|
|
|
|
this.saveSessionStorage(object, selected_ids);
|
|
},
|
|
|
|
/**
|
|
* Toggle all objects between the two checkboxes.
|
|
*
|
|
* @param {string} object
|
|
* @param {object} startCheckbox
|
|
* @param {object} endCheckbox
|
|
* @param {bool} checked
|
|
*/
|
|
checkObjectRange: function(object, startCheckbox, endCheckbox, checked) {
|
|
var checkboxes = this.getObjectCheckboxes(object);
|
|
|
|
var startCheckboxIndex = checkboxes.indexOf(startCheckbox);
|
|
var endCheckboxIndex = checkboxes.indexOf(endCheckbox);
|
|
var start = Math.min(startCheckboxIndex, endCheckboxIndex);
|
|
var end = Math.max(startCheckboxIndex, endCheckboxIndex);
|
|
|
|
var objectIds = [];
|
|
for (var i = start; i <= end; i++) {
|
|
objectIds.push(this.getObjectIdFromName(checkboxes[i].name));
|
|
}
|
|
this.checkObjects(object, objectIds, checked);
|
|
},
|
|
|
|
/**
|
|
* Toggle all of the checkboxes belonging to the given object group.
|
|
*
|
|
* @param {string} object
|
|
*
|
|
* @param {boolean} checked
|
|
*/
|
|
checkObjectAll: function(object, checked) {
|
|
// main checkbox exists and is clickable, but other checkboxes may not exist and object may be empty
|
|
var objectIds = jQuery.map(this.getObjectCheckboxes(object), jQuery.proxy(function(checkbox) {
|
|
return this.getObjectIdFromName(checkbox.name);
|
|
}, this));
|
|
|
|
this.checkObjects(object, objectIds, checked);
|
|
},
|
|
|
|
/**
|
|
* Update the general state after toggling a checkbox.
|
|
*
|
|
* @param {string} object
|
|
*/
|
|
update: function(object) {
|
|
// update main checkbox state
|
|
this.updateMainCheckbox(object);
|
|
|
|
if (this.pageGoName == object) {
|
|
this.updateGoButton();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Update the state of the "Go" controls.
|
|
*/
|
|
updateGoButton: function() {
|
|
const object = this.pageGoName;
|
|
let selected_count = 0;
|
|
let actions = [];
|
|
|
|
Object
|
|
.values(this.getSelectedIds())
|
|
.forEach(value => {
|
|
selected_count++;
|
|
|
|
// Count the special attributes for checkboxes.
|
|
if (value !== null) {
|
|
const action_list = value.split(' ');
|
|
|
|
for (const action of action_list) {
|
|
if (!actions.hasOwnProperty(action)) {
|
|
actions[action] = 0;
|
|
}
|
|
actions[action]++;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Replace the selected count text.
|
|
const selected_count_span = document.getElementById('selected_count');
|
|
selected_count_span.innerHTML = selected_count + ' ' + selected_count_span.innerHTML.split(' ')[1];
|
|
|
|
document.querySelectorAll('#action_buttons button').forEach((button) => {
|
|
// In case button is not permanently disabled by view, enable it depending on attributes and count.
|
|
if (!button.dataset.disabled) {
|
|
// First disabled the button and then check if it can be enabled.
|
|
button.disabled = true;
|
|
|
|
// Check if a special attribute is required to enable the button.
|
|
if (button.dataset.required) {
|
|
for (const [action, count] of Object.entries(actions)) {
|
|
// Checkbox data-actions attribute must match the button attribute.
|
|
if (button.dataset.required === action) {
|
|
button.disabled = (count == 0);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// No special attributes required, enable the button depending only on selected count.
|
|
button.disabled = (selected_count == 0);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Select main checkbox if all other checkboxes are selected.
|
|
*
|
|
* @param {string} object
|
|
*/
|
|
updateMainCheckbox: function(object) {
|
|
const checkbox_list = this.getObjectCheckboxes(object);
|
|
const $main_checkbox = $(checkbox_list)
|
|
.parents('table')
|
|
.find('thead input[type=checkbox]');
|
|
|
|
if ($main_checkbox.length == 0) {
|
|
return;
|
|
}
|
|
|
|
const count_available = checkbox_list.length;
|
|
|
|
if (count_available > 0) {
|
|
const checked = [];
|
|
|
|
jQuery.each(checkbox_list, (i, checkbox) => {
|
|
if (checkbox.checked) {
|
|
checked.push(checkbox);
|
|
}
|
|
});
|
|
|
|
$main_checkbox[0].checked = (checked.length == count_available);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Save the state of the checkboxes belonging to the given object group in SessionStorage.
|
|
*
|
|
* @param {string} object
|
|
* @param {Object} selected_ids key/value pairs of selected ids.
|
|
*/
|
|
saveSessionStorage: function(object, selected_ids) {
|
|
if (Object.keys(selected_ids).length > 0) {
|
|
if (this.pageGoName == object) {
|
|
sessionStorage.setItem(this.sessionStorageName, JSON.stringify(selected_ids));
|
|
}
|
|
}
|
|
else {
|
|
sessionStorage.removeItem(this.sessionStorageName);
|
|
}
|
|
},
|
|
|
|
clearSelectedOnFilterChange: function() {
|
|
sessionStorage.removeItem(this.sessionStorageName);
|
|
},
|
|
|
|
/**
|
|
* Reset all selections on other pages.
|
|
*/
|
|
resetOtherPage: function() {
|
|
var key_;
|
|
|
|
for (var i = 0; i < sessionStorage.length; i++) {
|
|
key_ = sessionStorage.key(i);
|
|
|
|
if (key_.substring(0, 3) === 'cb_' && key_ != this.sessionStorageName) {
|
|
sessionStorage.removeItem(key_);
|
|
}
|
|
}
|
|
},
|
|
|
|
submitFooterButton: function(e) {
|
|
const checked_count = Object.keys(this.getSelectedIds()).length;
|
|
|
|
var footerButton = jQuery(e.target),
|
|
form = footerButton.closest('form'),
|
|
confirmText = checked_count > 1
|
|
? footerButton.attr('confirm_plural')
|
|
: footerButton.attr('confirm_singular');
|
|
|
|
if (confirmText && !confirm(confirmText)) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
return false;
|
|
}
|
|
|
|
const selected_ids = this.getSelectedIds();
|
|
for (let key in selected_ids) {
|
|
if (selected_ids.hasOwnProperty(key) && selected_ids[key] !== null) {
|
|
create_var(form.attr('name'), this.pageGoName + '[' + key + ']', key, false);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|