Fix develop client behavior, popup isolation and PHP method visibility (#8292)

* fix(core): correct client state and declare public method contracts

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(security): isolate Midwinter external homepage popup

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(client): isolate menu state and simplify validated JavaScript guards

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* test(auth): use certificate expiry truth in rendering assertions

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* refactor(js): remove unused local storage while retaining initializer calls

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(js): encode inline translated values through the shared JSON boundary

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* style(js): wrap encoded translation literals without changing messages

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(client): remove unused checkbox reads from graph resizing

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* refactor(client): declare repeated function locals once

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* test(ui): run column width regression in browser CI

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
This commit is contained in:
Thomas Vincent authored and GitHub committed 2026-10-11 21:15:39 -04:00
1 parent 391c6ead18
commit 08d0f94f24
32 files changed
+835 -330

No files matched your search

@@ -0,0 +1,30 @@
name: Develop client security
on:
pull_request:
branches: [develop]
push:
branches: [develop]
permissions:
contents: read
jobs:
chromium:
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: '22'
cache: npm
cache-dependency-path: tests/e2e/package-lock.json
- name: Install locked browser tooling
working-directory: tests/e2e
run: |
set -euo pipefail
npm ci --ignore-scripts
npx --no-install playwright install --with-deps chromium
- name: Verify popup isolation, navigation state and column widths
working-directory: tests/e2e
run: npx --no-install playwright test tests/develop-client-security.spec.ts tests/develop-menu-state.spec.ts tests/table-column-widths.spec.ts --project=chromium
+1 -1
View File
@@ -261,7 +261,7 @@ midwinter.navigationBox.table = {
*/
init: function($box) {
if (!$box || !$box.length) return;
const helper = $box.data('helper');
$box.data('helper');
const ns = midwinter.navigationBox.table;
// bind toggle checkboxes
+1 -1
View File
@@ -34,7 +34,7 @@ midwinter.navigationBox.tree = {
*/
getDefaultConfig: function(overrides = {}) {
// derive the type from the object path
const type = 'tree';
const base = 'midwinter.navigationBox';
const defaults = {
+4 -4
View File
@@ -16,15 +16,15 @@ $.toastPopup = function(options) {
loader: true,
}
if (typeof options == "undefined") {
if (options === undefined) {
options = {};
} else if (typeof options == "string" || typeof options == "String") {
} else if (typeof options === "string" || options instanceof String) {
options = {
text: options
text: String(options)
};
}
toastOptions = $.extend(defaultOptions, options, fixedOptions);
const toastOptions = $.extend(defaultOptions, options, fixedOptions);
return $.toast(toastOptions);
}
+179 -168
View File
@@ -199,7 +199,7 @@ jQuery.ajaxQ = (function () {
*/
function basename(path, suffix) {
var b = path;
var lastChar = b.charAt(b.length - 1);
b.charAt(b.length - 1);
// Don't remove trailing slash for now //
/*
@@ -229,12 +229,12 @@ function basename(path, suffix) {
/** getTimestampFromDate - Simple function to convert a MySQL Date
* to a timestamp */
function getTimestampFromDate(dateStamp) {
if (typeof dateStamp != 'undefined') {
if (dateStamp !== undefined) {
var dateParts = dateStamp.split(' ');
var timeParts = dateParts[1].split(':');
dateParts = dateParts[0].split('-');
var date = new Date(dateParts[0], parseInt(dateParts[1], 10) - 1, dateParts[2], timeParts[0], timeParts[1]);
var date = new Date(dateParts[0], Number.parseInt(dateParts[1], 10) - 1, dateParts[2], timeParts[0], timeParts[1]);
return date.getTime() / 1000;
}
@@ -267,7 +267,7 @@ function getQueryString(name) {
*/
$.fn.delayKeyup = function (callback, ms) {
var timer = 0;
if (typeof ms === 'undefined' || ms < 500) {
if (ms === undefined || ms < 500) {
ms = keyup_delay;
}
@@ -388,7 +388,7 @@ $.fn.enableDisableOptions = function (values, optionEnabled, valueCheckFunc) {
}
var selectMenu = $(this).selectmenu('instance');
if (typeof selectMenu != 'undefined') {
if (selectMenu !== undefined) {
$(this).selectmenu('refresh');
}
@@ -448,11 +448,12 @@ $.fn.textWidth = function (text) {
* without any wrapping.
*/
$.fn.textBoxWidth = function () {
var text;
var org = $(this);
if (org.val()) {
var text = encodeURIComponent(org.val());
text = encodeURIComponent(org.val());
} else {
var text = encodeURIComponent(org.text());
text = encodeURIComponent(org.text());
}
var html = $('<span style="display:none;white-space:nowrap;position:absolute;width:auto;left:-9999px">' + text + '</span>');
@@ -842,7 +843,7 @@ function dqUpdateDeps(snmp_query_id) {
$('tr[id^="dqline' + snmp_query_id + '_"]').each(function (data) {
var id = $(this).attr('id');
var pieces = id.split('_');
var dq = pieces[0].substr(6);
pieces[0].substr(6);
var hash = pieces[1];
if ($.inArray(hash, created_graphs[snmp_query_graph_id]) >= 0) {
@@ -1104,12 +1105,13 @@ function applySkin() {
$(document).tooltip({
items: 'div.cactiTooltipHint, span.cactiTooltipHint, .checkboxSlider',
content: function () {
var text;
var element = $(this);
if (element.is('div')) {
var text = DOMPurify.sanitize($(this).find('span').html());
text = DOMPurify.sanitize($(this).find('span').html());
} else if (element.is('span') || element.is('a')) {
var text = DOMPurify.sanitize($(this).prop('title'));
text = DOMPurify.sanitize($(this).prop('title'));
}
return text;
}
@@ -1171,7 +1173,7 @@ function applySkin() {
$('#main').css('display', 'table');
var showPage = $('#main').map(function (i, el) {
$('#main').map(function (i, el) {
var dfd = $.Deferred();
$(el).show(function () {
dfd.resolve();
@@ -1569,19 +1571,19 @@ function sessionNoticesDisplay() {
var title = '';
var id = '';
if (typeof notice.message !== 'undefined') {
if (notice.message !== undefined) {
message = notice.message;
}
if (typeof notice.title !== 'undefined') {
if (notice.title !== undefined) {
titLe = notice.tite;
}
if (typeof notice.level !== 'undefined') {
if (notice.level !== undefined) {
level = notice.level;
}
if (typeof notice.id !== 'undefined') {
if (notice.id !== undefined) {
id = '-' + notice.id;
}
@@ -1617,6 +1619,7 @@ function sessionNoticesDisplay() {
}
function sessionMessageDisplay() {
var sessionMessageButtons;
var title = '';
var header = '';
@@ -1628,11 +1631,11 @@ function sessionMessageDisplay() {
return;
}
if (typeof sessionMessage.level != 'undefined') {
if (sessionMessage.level !== undefined) {
if (sessionMessage.level == MESSAGE_LEVEL_ERROR) {
title = errorReasonTitle;
header = errorOnPage;
var sessionMessageButtons = {
sessionMessageButtons = {
'Ok': {
text: sessionMessageOk,
id: 'btnSessionMessageOk',
@@ -1646,7 +1649,7 @@ function sessionMessageDisplay() {
} else if (sessionMessage.level == MESSAGE_LEVEL_MIXED) {
title = mixedReasonTitle;
header = mixedOnPage;
var sessionMessageButtons = {
sessionMessageButtons = {
'Ok': {
text: sessionMessageOk,
id: 'btnSessionMessageOk',
@@ -1663,7 +1666,7 @@ function sessionMessageDisplay() {
} else {
title = sessionMessageTitle;
header = sessionMessageSave;
var sessionMessageButtons = {
sessionMessageButtons = {
'Pause': {
text: sessionMessagePause,
id: 'btnSessionMessagePause',
@@ -2006,10 +2009,11 @@ function responsiveUI(event) {
/* change textbox and textarea widths */
$('input[type="text"], textarea').each(function () {
var offset;
if ($(this).attr('type') == 'text') {
var offset = 20;
offset = 20;
} else {
var offset = 5;
offset = 5;
}
if (mainWidth != 100) {
@@ -2039,11 +2043,12 @@ function responsiveUI(event) {
}
function getMainWidth() {
var mainWidth;
// Subtract 15px for the scroll bar
if ($('#navigation').length && $('#navigation').is(':visible')) {
var mainWidth = $('body').outerWidth() - $('#navigation').width() - 15;
mainWidth = $('body').outerWidth() - $('#navigation').width() - 15;
} else {
var mainWidth = $('body').outerWidth() - 15;
mainWidth = $('body').outerWidth() - 15;
}
return mainWidth;
@@ -2060,21 +2065,20 @@ function getCactiHelp(cactiPage) {
}
function responsiveResizeGraphs(initialize) {
var graphRow, drillDown;
var mainWidth = getMainWidth() - 30;
var myColumns = $('#columns').val();
var isThumb = $('#thumbnails').is(':checked') || $('#thumbnails').val() == 'true';
var isBusiness = $('#business_hours').is(':checked') || $('#business_hours').val() == 'true';
if ($('.tableRowGraph:first').length) {
var graphRow = $('.tableRowGraph:first').width();
graphRow = $('.tableRowGraph:first').width();
} else {
var graphRow = 0;
graphRow = 0;
}
if ($('.graphDrillDown:first').length) {
var drillDown = $('.graphDrillDown:first').outerWidth() + 15;
drillDown = $('.graphDrillDown:first').outerWidth() + 15;
} else {
var drillDown = 0;
drillDown = 0;
}
if (myColumns == null) {
@@ -2088,12 +2092,12 @@ function responsiveResizeGraphs(initialize) {
}
// Dont resize if nothing changed
if (typeof initialize == 'undefined' && (previousMainWidth == null || (previousMainWidth == mainWidth && previousColumns == myColumns))) {
if (initialize === undefined && (previousMainWidth == null || (previousMainWidth == mainWidth && previousColumns == myColumns))) {
previousMainWidth = mainWidth;
return true;
}
var myWidth = parseInt((graphRow - (drillDown * myColumns)) / myColumns);
var myWidth = Number.parseInt((graphRow - (drillDown * myColumns)) / myColumns);
$('.graphimage').each(function () {
var graph_id = $(this).attr('graph_id');
@@ -2103,11 +2107,11 @@ function responsiveResizeGraphs(initialize) {
graph_id = $(this).attr('id').replace('graph_', '');
}
var rra_id = $(this).attr('rra_id');
$(this).attr('rra_id');
var type = $(this).attr('graph_type');
var original_cwidth = $('#wrapper_' + graph_id).attr('graph_width');
var original_cheight = $('#wrapper_' + graph_id).attr('graph_height');
$('#wrapper_' + graph_id).attr('graph_height');
/* original image attributes */
var image_width = $(this).attr('image_width');
@@ -2131,12 +2135,12 @@ function responsiveResizeGraphs(initialize) {
ratio = 1;
}
var new_image_width = parseInt(image_width * ratio)
var new_image_height = parseInt(image_height * ratio)
var new_canvas_width = parseInt(canvas_width * ratio);
var new_canvas_height = parseInt(canvas_height * ratio);
var new_canvas_graph_top = parseInt(canvas_top * ratio);
var new_canvas_graph_left = parseInt(canvas_left * ratio);
var new_image_width = Number.parseInt(image_width * ratio)
var new_image_height = Number.parseInt(image_height * ratio)
var new_canvas_width = Number.parseInt(canvas_width * ratio);
var new_canvas_height = Number.parseInt(canvas_height * ratio);
var new_canvas_graph_top = Number.parseInt(canvas_top * ratio);
var new_canvas_graph_left = Number.parseInt(canvas_left * ratio);
$(this).attr('graph_width', new_canvas_width);
$(this).attr('graph_height', new_canvas_height);
@@ -2163,8 +2167,9 @@ function responsiveResizeGraphs(initialize) {
}
function resizeTreePanel() {
var navWidth;
var docHeight = $(window).outerHeight();
var navWidth = $('.cactiTreeNavigationArea').width();
navWidth = $('.cactiTreeNavigationArea').width();
var searchHeight = $('.cactiTreeSearch').outerHeight();
var pageHeadHeight = $('.cactiPageHead').outerHeight();
var breadCrHeight = $('.breadCrumbBar').outerHeight();
@@ -2214,7 +2219,7 @@ function resizeTreePanel() {
$('.cactiTreeNavigationArea').css('overflow-x', '');
}
var navWidth = $('#navigation').width();
navWidth = $('#navigation').width();
$('#searcher').css('width', getTreeSearchWidth(navWidth));
}
@@ -2279,10 +2284,10 @@ function tuneTable(object, width) {
var width = width;
var tableWidth = $(object).width();
var totalCols = $(object).find('th').length;
var reducedWidth = 0;
var columnsHidden = countHiddenCols(object);
var visibleColumns = totalCols - columnsHidden;
var id = $(object).attr('id');
$(object).attr('id');
if (rows > 101) return false;
@@ -2306,7 +2311,7 @@ function tuneTable(object, width) {
var debug = false;
var tableHeaders = $(object).find('th');
var tableCheckBox = $(tableHeaders).each(function () {
$(tableHeaders).each(function () {
if ($(this).index() == tableHeaders.length) {
calculatedColumns.addClass('noHide');
}
@@ -2343,7 +2348,7 @@ function tuneTable(object, width) {
// and See which need to be hidden
if (!calculatedColumns.includes($(this).index())) {
var columnWidth = $.textMetrics(this).width + calculatedPadding;
var name = $(this).find('div.sortinfo').attr('sort-column');
$(this).find('div.sortinfo').attr('sort-column');
if (debug) {
console.log(
@@ -2376,7 +2381,6 @@ function tuneTable(object, width) {
tableChanged = false;
if (allSeenWidth < width) {
calculatedColumns = calculatedColumns.sort();
// Since we can show hidden columns now, let's go
// in reverse until we run out of space
@@ -2384,7 +2388,7 @@ function tuneTable(object, width) {
if (!calculatedColumns.includes($(this).index())) {
if ($(this).is(':hidden') && stopExpand == false) {
var columnWidth = $.textMetrics(this).width + calculatedPadding;
var name = $(this).find('div.sortinfo').attr('sort-column');
$(this).find('div.sortinfo').attr('sort-column');
if (allSeenWidth + columnWidth < tableWidth) {
$(this).show();
@@ -2424,17 +2428,18 @@ function tuneTable(object, width) {
}
function tuneFilter(object, width) {
var timespan, clearWidth;
if ($(object).find('#timespan').length && $(object).find('#timespan').is(':visible')) {
var timespan = true;
timespan = true;
var timeShiftWidth = $(object).find('.shiftArrow').closest('td').width();
var dateWidth = $(object).find('#date1').closest('td').width() + $(object).find('#date1').closest('td').prev('td').width();
var clearWidth = $('#tsclear').width();
clearWidth = $('#tsclear').width();
var refreshWidth = $('#tsrefresh').width();
} else {
var timespan = false;
timespan = false;
var clearWidth = $(object).find('#clear').width();
clearWidth = $(object).find('#clear').width();
var saveWidth = $(object).find('#save').width();
var exportWidth = $(object).find('#export').width();
var importWidth = $(object).find('#import').width();
@@ -2573,7 +2578,7 @@ function tuneFilter(object, width) {
function handleUserMenu(toggle) {
var storage = Storages.sessionStorage;
var windowWidth = $(window).width();
$(window).width();
/* set the navigation id is not set */
if (currentNavId == 'undefined' || currentNavId == null) {
@@ -2610,7 +2615,7 @@ function handleUserMenu(toggle) {
function menuHide(store) {
var myClass = '';
var curMargin = parseInt($('#navigation_right').css('margin-left'));
var curMargin = Number.parseInt($('#navigation_right').css('margin-left'));
if ($('.cactiTreeNavigationArea').length) {
myClass = '.cactiTreeNavigationArea';
@@ -2691,7 +2696,7 @@ function loadTopTab(href, id, force) {
var storage = Storages.sessionStorage;
/* used for debugging */
var stack = ''; //getStackTrace(); // new Error().stack;
//getStackTrace(); // new Error().stack;
var url = href;// + (href.indexOf('?') > 0 ? '&' : '?') + 'headercontent=true';
/* close all toasts */
@@ -2788,8 +2793,6 @@ function userMenuNavigationExists(url) {
return myFound;
}
return false;
}
function loadPageUsingPost(href, postData, returnLocation) {
@@ -2942,7 +2945,7 @@ function loadUrl(options) {
/* get rid of old selectmenu's */
$('.ui-selectmenu-menu').empty();
if (typeof options.noState == 'undefined' || options.noState == false) {
if (options.noState === undefined || options.noState == false) {
/* close all toasts */
$.toast().reset('all');
}
@@ -3008,7 +3011,7 @@ function postUrl(options, data) {
statePushed = false;
cont = false;
if (typeof options.noState == 'undefined' || options.noState == false) {
if (options.noState === undefined || options.noState == false) {
/* close all toasts */
$.toast().reset('all');
}
@@ -3035,7 +3038,7 @@ function findElement(htmlObject, element) {
if (htmlObject !== null) {
wanted = htmlObject.find(element);
if (typeof wanted == 'undefined' || wanted.length == 0) {
if (wanted === undefined || wanted.length == 0) {
wanted = htmlObject.filter(element);
}
}
@@ -3060,7 +3063,7 @@ function sanitizeAjaxOptions(check) {
redirect: '',
};
if (typeof check == 'undefined') {
if (check === undefined) {
check = {};
}
@@ -3071,11 +3074,11 @@ function sanitizeAjaxOptions(check) {
options.noState = options.url.indexOf('nostate') > -1;
}
if (typeof check.noState != 'undefined') {
if (check.noState !== undefined) {
options.noState = check.noState;
}
if (typeof check.scroll != 'undefined') {
if (check.scroll !== undefined) {
options.scroll = check.scroll;
}
@@ -3143,7 +3146,7 @@ function handleAjaxResponse(html, options) {
}
var matches = null;
if (typeof html.match !== 'undefined') {
if (html.match !== undefined) {
matches = html.match(/<title>(.*?)<\/title>/);
}
@@ -3158,7 +3161,7 @@ function handleAjaxResponse(html, options) {
myTitle = htmlTitle;
}
if (typeof htmlContent == 'undefined') {
if (htmlContent === undefined) {
htmlContent = html;
}
@@ -3172,7 +3175,7 @@ function handleAjaxResponse(html, options) {
let confirmation = '#action_confirm';
let htmlConfirm = findElement(htmlObject, confirmation).parent().html();
if (typeof htmlConfirm !== 'undefined' && typeof window['preparePopOver'] === 'function') {
if (htmlConfirm !== undefined && typeof window['preparePopOver'] === 'function') {
preparePopOver(htmlConfirm);
}else {
$(elementId).hide().empty().hide();
@@ -3217,7 +3220,7 @@ function handleAjaxResponse(html, options) {
}
if (options.funcEnd != '') {
if (typeof window[options.funcEnd] === 'undefined') {
if (window[options.funcEnd] === undefined) {
console.log('WARNING: Missing function "' + options.funcEnd + '".');
} else {
window[options.funcEnd](options, html);
@@ -3236,13 +3239,14 @@ function getPresentHTTPError(data) {
}
function getPresentHTTPErrorOrRedirect(data, url) {
if (typeof data.status != 'undefined') {
var errorSub, errorText;
if (data.status !== undefined) {
var errorStr = data.status;
var errorSub = data.statusText;
var errorText = errorReasonUnexpected;
errorSub = data.statusText;
errorText = errorReasonUnexpected;
var found = false;
if (typeof data.responseText != 'undefined') {
if (data.responseText !== undefined) {
var dataText = data.responseText;
var title_match = dataText.match(/<title>(.*?)<\/title>/);
@@ -3250,22 +3254,22 @@ function getPresentHTTPErrorOrRedirect(data, url) {
var para_match = dataText.match(/<p>(.*?)<\/p>/);
if (title_match != null) {
var errorSub = title_match[1];
errorSub = title_match[1];
found = true;
}
if (head_match != null) {
var errorSub = head_match[1];
errorSub = head_match[1];
found = true;
}
if (para_match != null) {
var errorText = para_match[1];
errorText = para_match[1];
found = true;
}
if (!found && dataText != '') {
var errorText = dataText;
errorText = dataText;
}
var returnStr = '<div id="httperror" style="display:none">' +
@@ -3299,7 +3303,7 @@ function getPresentHTTPErrorOrRedirect(data, url) {
}
}
if (typeof url != 'undefined') {
if (url !== undefined) {
if (data.status >= 500) {
// Let the HTTP Error stick log an error
$.get(urlPath + 'help.php?page=' + encodeURIComponent(url) + '&error=' + data.status);
@@ -3313,7 +3317,7 @@ function getPresentHTTPErrorOrRedirect(data, url) {
}
function ajaxAnchors() {
var page = basename(location.pathname);
basename(location.pathname);
$(document).rebind('click.cactiPostAction', 'a.cactiPostAction', function (event) {
if (!shouldCaptureClick(event)) {
@@ -3389,11 +3393,11 @@ function checkFormStatus(href, type, scroll_or_id) {
var formID = $(this).attr('id');
var submitData = $(this).serializeForm();
if (typeof formArray != 'undefined' && typeof formArray[formID] != 'undefined') {
if (typeof formArray != 'undefined' && formArray[formID] !== undefined) {
var formData = formArray[formID];
$.each(submitData, function (index, value) {
if (typeof formData[index] != 'undefined') {
if (formData[index] !== undefined) {
if (formData[index] != value) {
if (index == 'settings_sendmail_path' || index == 'rrd_archive' || index == '__csrf_magicSubmit' || index == '__csrf_magic' || index == 'settings_smtp_password' || index == 'settings_smtp_password_confirm') {
// Ignore this entry
@@ -3424,11 +3428,12 @@ function setupCollapsible() {
var storage = Storages.sessionStorage;
$('.collapsible').each(function (data) {
var state;
var id = $(this).attr('id') + '_cs';
if (storage.isSet(id)) {
var state = storage.get(id);
state = storage.get(id);
} else {
var state = 'show';
state = 'show';
}
if (state == 'hide') {
@@ -3541,7 +3546,7 @@ function setupSortable() {
var $target = $(e.target);
var sortAdd = '';
var href = '';
if (!$target.is('.ui-resizable-handle')) {
var page = $(this).find('.sortinfo').attr('sort-page');
@@ -3598,10 +3603,11 @@ function setupBreadcrumbs() {
function saveTableWidths(initial) {
// We will save columns widths persistently
var storage = Storages.localStorage;
var key;
// Initialize table width on the page
$('table.cactiTable').each(function (data) {
var sizes, items;
var table = $(this);
// Persist widths under the action-independent data-colsizing key when it
@@ -3615,27 +3621,27 @@ function saveTableWidths(initial) {
}
if (storage.isSet(key)) {
var sizes = storage.get(key);
sizes = storage.get(key);
} else {
var sizes = new Array();
sizes = new Array();
}
var items = sizes ? sizes : new Array();
items = sizes ? sizes : new Array();
var width = $(document).width();
// if the table width changes, reset the columns
if (key !== undefined && sizes == undefined) {
storage.remove(key);
var sizes = new Array();
var items = new Array();
sizes = new Array();
items = new Array();
} else if (key !== undefined && initial) {
if (items.length > 0) {
if (items[0] + 18 < width) {
storage.remove(key);
var sizes = new Array();
var items = new Array();
sizes = new Array();
items = new Array();
}
}
}
@@ -3912,7 +3918,7 @@ function setSelectMenus() {
that._renderItemData( ul, item );
});
if (typeof(attr['data-defaultLabel']) !== 'undefined') {
if (attr['data-defaultLabel'] !== undefined) {
if(this.menuWrap.find('input').length === 0) {
this.menuWrap.prepend('<div class="ui-selectmenu-search"><input type="search" class="ui-state-default ui-corner-all" data-scope="theme" placeholder="' + searchPlaceholder + '"></div>');
}
@@ -3957,17 +3963,17 @@ function setSelectMenus() {
}
},
_renderButtonItem: function(item) {
let that = this;
let attr = this.element[0].attributes;
let showDefaultLabel = select2Setup["displayDefaultLabel"];
if (typeof(attr['data-defaultLabel']) !== 'undefined') {
if (attr['data-defaultLabel'] !== undefined) {
let defaultLabel = attr['data-defaultLabel'].value;
let filterActive = '';
let defaultIndex = 0;
let defaultValue = this.element.find("option").eq(0).val();
if (typeof(attr['data-defaultValue']) !== 'undefined') {
if (attr['data-defaultValue'] !== undefined) {
let defaultValue = attr['data-defaultValue'].value;
}
@@ -4019,6 +4025,7 @@ function setSelectMenus() {
$.widget('custom.dropicon', $.ui.selectmenu, {
_renderItem: function( ul, item ) {
var style;
var li = $('<li>');
var wrapper = $('<div>', {text: item.label});
@@ -4027,9 +4034,9 @@ function setSelectMenus() {
}
if (item.element.attr('data-style') != '') {
var style = item.element.attr('data-style')+';background:transparent;min-width:20px;';
style = item.element.attr('data-style')+';background:transparent;min-width:20px;';
} else {
var style = 'background:transparent;min-width:20px;';
style = 'background:transparent;min-width:20px;';
}
var iclass = item.element.attr('data-class');
@@ -4273,10 +4280,10 @@ function setupEllipsis() {
/* re-position */
var position = $(this).position();
if (position.left - parseInt(submenu.outerWidth()) < 0) {
if (position.left - Number.parseInt(submenu.outerWidth()) < 0) {
submenu.css({ 'left': 0 }).slideDown(120);
} else {
submenu.css({ 'left': position.left - parseInt(submenu.outerWidth()) + parseInt($(this).outerWidth()) }).slideDown(120);
submenu.css({ 'left': position.left - Number.parseInt(submenu.outerWidth()) + Number.parseInt($(this).outerWidth()) }).slideDown(120);
}
} else {
submenu.slideUp(120);
@@ -4308,6 +4315,7 @@ function setupEllipsis() {
function keepWindowSize() {
$(window).on('resize', function (event) {
waitForFinalEvent(function () {
var graphTabWidth, userTabs, items, done;
$('.cactiGraphContentArea').show();
var resizeTime = new Date();
@@ -4355,15 +4363,15 @@ function keepWindowSize() {
$('.dropdownMenu > ul').hide();
if ($('#gtabs > .tabs').is(':visible')) {
var graphTabWidth = $('#gtabs > .tabs').outerWidth();
graphTabWidth = $('#gtabs > .tabs').outerWidth();
} else {
var graphTabWidth = 0;
graphTabWidth = 0;
}
if ($('.usertabs').length) {
var userTabs = $('.usertabs').outerWidth();
userTabs = $('.usertabs').outerWidth();
} else {
var userTabs = 0;
userTabs = 0;
}
var bodyWidth = $('body').width();
@@ -4372,7 +4380,7 @@ function keepWindowSize() {
otherWidth += $(this).outerWidth();
});
var ellipsisWidth = $('.maintabs-submenu-ellipsis').outerWidth();
$('.maintabs-submenu-ellipsis').outerWidth();
var tabHeight = $('#tabs').outerHeight();
var mainTabPos = false;
@@ -4390,8 +4398,8 @@ function keepWindowSize() {
pageWidth = bodyWidth;
var items = $($('.maintabs nav ul li a.lefttab').get());
var done = false;
items = $($('.maintabs nav ul li a.lefttab').get());
done = false;
items.each(function () {
var id = $(this).attr('id');
@@ -4399,8 +4407,8 @@ function keepWindowSize() {
});
// Hide top menus if you have to
var items = $($('.maintabs nav ul li a.lefttab').get().reverse());
var done = false;
items = $($('.maintabs nav ul li a.lefttab').get().reverse());
done = false;
items.each(function () {
var id = $(this).attr('id');
@@ -4447,15 +4455,16 @@ function showCurrentTab(id) {
}
function tabsWrapping() {
var mainTabHeight, userTabPos, gtabsWidth, utabsWidth;
var mainTabPos = $('.maintabs:first').position();
var tabHeight = $('#tabs').height();
if ($('.usertabs').length) {
var mainTabHeight = tabHeight;
var userTabPos = $('.usertabs').position();
mainTabHeight = tabHeight;
userTabPos = $('.usertabs').position();
} else {
var mainTabHeight = $('.maintabs:first nav').height();
var userTabPos = mainTabPos;
mainTabHeight = $('.maintabs:first nav').height();
userTabPos = mainTabPos;
}
var bodyWidth = $('body').width();
@@ -4468,15 +4477,15 @@ function tabsWrapping() {
var mtabsWidth = $('.maintabs:not(.usertabs)').outerWidth();
if ($('#gtabs>.tabs').length) {
var gtabsWidth = $('#gtabs>.tabs').outerWidth();
gtabsWidth = $('#gtabs>.tabs').outerWidth();
} else {
var gtabsWidth = 0;
gtabsWidth = 0;
}
if ($('.usertabs').length) {
var utabsWidth = $('.usertabs').outerWidth();
utabsWidth = $('.usertabs').outerWidth();
} else {
var utabsWidth = 0;
utabsWidth = 0;
}
if (gtabsWidth + mtabsWidth + utabsWidth + otherWidth + ellipsisWidth > bodyWidth - 20) {
@@ -4521,7 +4530,7 @@ function setCactiTabCookie() {
}
function checkForRedirects(data, href) {
if (typeof data == 'undefined') {
if (data === undefined) {
return true;
} else if (typeof data == 'object') {
return true;
@@ -4535,7 +4544,7 @@ function checkForRedirects(data, href) {
$.ajaxQ.abortAll();
document.location = urlPath + 'permission_denied.php';
} else if (data.indexOf('cactiRedirect') >= 0) {
if (typeof href == 'undefined' || href == null) {
if (href === undefined || href == null) {
$.ajaxQ.abortAll();
location.reload();
} else {
@@ -4590,13 +4599,13 @@ function finalizeGraphFilter(options, data) {
function applyGraphFilter() {
var href = correctUrlParameters(graphPage + '?action=' + pageAction +
'&rfilter=' + base64_encode($('#rfilter').val()) +
(typeof $('#site_id').val() != 'undefined' ? '&site_id=' + $('#site_id').val() : '') +
(typeof $('#location').val() != 'undefined' ? '&location=' + $('#location').val() : '') +
(typeof $('#host_id').val() != 'undefined' ? '&host_id=' + $('#host_id').val() : '') +
(typeof $('#graph_source').val() != 'undefined' ? '&graph_source=' + $('#graph_source').val() : '') +
(typeof $('#graph_order').val() != 'undefined' ? '&graph_order=' + $('#graph_order').val() : '') +
(typeof $('#cf').val() != 'undefined' ? '&cf=' + $('#cf').val() : '') +
(typeof $('#measure').val() != 'undefined' ? '&measure=' + $('#measure').val() : '') +
($('#site_id').val() !== undefined ? '&site_id=' + $('#site_id').val() : '') +
($('#location').val() !== undefined ? '&location=' + $('#location').val() : '') +
($('#host_id').val() !== undefined ? '&host_id=' + $('#host_id').val() : '') +
($('#graph_source').val() !== undefined ? '&graph_source=' + $('#graph_source').val() : '') +
($('#graph_order').val() !== undefined ? '&graph_order=' + $('#graph_order').val() : '') +
($('#cf').val() !== undefined ? '&cf=' + $('#cf').val() : '') +
($('#measure').val() !== undefined ? '&measure=' + $('#measure').val() : '') +
'&columns=' + $('#columns').val() +
'&graphs=' + $('#graphs').val() +
'&graph_template_id=' + $('#graph_template_id').val() +
@@ -4618,7 +4627,7 @@ function pushState(myTitle, options) {
options = sanitizeAjaxOptions(options);
if (!options.noState) {
if (statePushed == false) {
if (typeof window.history.pushState != 'undefined') {
if (window.history.pushState !== undefined) {
var myObject = { Page: myTitle, Url: cleanUrlParameters(options.url), Options: Object.assign({}, options) };
window.history.pushState(myObject, myObject.Page, myObject.Url);
}
@@ -4630,11 +4639,11 @@ function pushState(myTitle, options) {
function handlePopState(e) {
var href = document.location.href;
if (typeof e !== 'undefined' && e.state != 'undefined' && e.state != null) {
if (typeof e.state.Options != 'undefined' && e.state.Options != null) {
if (e !== undefined && e.state != 'undefined' && e.state != null) {
if (e.state.Options !== undefined && e.state.Options != null) {
loadUrl(e.state.Options);
return false;
} else if (typeof e.state.Url != 'undefined' && e.state.Url != null) {
} else if (e.state.Url !== undefined && e.state.Url != null) {
loadUrl({ url: href });
return false;
}
@@ -4647,13 +4656,13 @@ function applyGraphTimespan() {
($('#rfilter').length ? '&rfilter=' + base64_encode($('#rfilter').val()) : '') +
'&predefined_timespan=' + $('#predefined_timespan').val() +
'&predefined_timeshift=' + $('#predefined_timeshift').val() +
(typeof $('#site_id').val() != 'undefined' ? '&site_id=' + $('#site_id').val() : '') +
(typeof $('#location').val() != 'undefined' ? '&location=' + $('#location').val() : '') +
(typeof $('#host_id').val() != 'undefined' ? '&host_id=' + $('#host_id').val() : '') +
(typeof $('#graph_source').val() != 'undefined' ? '&graph_source=' + $('#graph_source').val() : '') +
(typeof $('#graph_order').val() != 'undefined' ? '&graph_order=' + $('#graph_order').val() : '') +
(typeof $('#cf').val() != 'undefined' ? '&cf=' + $('#cf').val() : '') +
(typeof $('#measure').val() != 'undefined' ? '&measure=' + $('#measure').val() : '') +
($('#site_id').val() !== undefined ? '&site_id=' + $('#site_id').val() : '') +
($('#location').val() !== undefined ? '&location=' + $('#location').val() : '') +
($('#host_id').val() !== undefined ? '&host_id=' + $('#host_id').val() : '') +
($('#graph_source').val() !== undefined ? '&graph_source=' + $('#graph_source').val() : '') +
($('#graph_order').val() !== undefined ? '&graph_order=' + $('#graph_order').val() : '') +
($('#cf').val() !== undefined ? '&cf=' + $('#cf').val() : '') +
($('#measure').val() !== undefined ? '&measure=' + $('#measure').val() : '') +
'&columns=' + $('#columns').val() +
'&graphs=' + $('#graphs').val() +
'&graph_template_id=' + $('#graph_template_id').val() +
@@ -4795,15 +4804,15 @@ function redrawGraph(graph_id) {
'&business_hours=' + (isBusiness ? 'true' : 'false') +
$.getJSON(href, function(data) {
if (typeof data.status == 'undefined') {
if (data.status === undefined) {
if (myWidth < data.image_width) {
ratio = myWidth / data.image_width;
data.image_width = parseInt(data.image_width * ratio);
data.image_height = parseInt(data.image_height * ratio);
data.graph_width = parseInt(data.graph_width * ratio);
data.graph_height = parseInt(data.graph_height * ratio);
data.graph_top = parseInt(data.graph_top * ratio);
data.graph_left = parseInt(data.graph_left * ratio);
data.image_width = Number.parseInt(data.image_width * ratio);
data.image_height = Number.parseInt(data.image_height * ratio);
data.graph_width = Number.parseInt(data.graph_width * ratio);
data.graph_height = Number.parseInt(data.graph_height * ratio);
data.graph_top = Number.parseInt(data.graph_top * ratio);
data.graph_left = Number.parseInt(data.graph_left * ratio);
}
$('#wrapper_' + data.local_graph_id).empty().html(
@@ -4916,7 +4925,7 @@ function initGraphDrilldown() {
}
function initializeGraphs(disable_cache) {
disable_cache = (typeof disable_cache == 'undefined') ? false : true;
disable_cache = (disable_cache === undefined) ? false : true;
$.ajaxQ.abortAll();
@@ -5044,12 +5053,12 @@ function initializeGraphs(disable_cache) {
if (myWidth < data.image_width) {
ratio = myWidth / data.image_width;
data.image_width = parseInt(data.image_width * ratio);
data.image_height = parseInt(data.image_height * ratio);
data.graph_width = parseInt(data.graph_width * ratio);
data.graph_height = parseInt(data.graph_height * ratio);
data.graph_top = parseInt(data.graph_top * ratio);
data.graph_left = parseInt(data.graph_left * ratio);
data.image_width = Number.parseInt(data.image_width * ratio);
data.image_height = Number.parseInt(data.image_height * ratio);
data.graph_width = Number.parseInt(data.graph_width * ratio);
data.graph_height = Number.parseInt(data.graph_height * ratio);
data.graph_top = Number.parseInt(data.graph_top * ratio);
data.graph_left = Number.parseInt(data.graph_left * ratio);
}
var wrapper_id = '#wrapper_' + data.local_graph_id;
@@ -5417,7 +5426,7 @@ $.widget('custom.dropcolor', {
_select: function (event, ui) {
var regExp = /\(([^)]+)\)/;
var hex = regExp.exec(ui.item.label);
var id = $(ui.item.option).attr('value');
$(ui.item.option).attr('value');
if (hex !== null) {
color = hex[1];
@@ -5512,7 +5521,7 @@ function expandClipboardSection(section) {
section.slideDown('fast');
}
var children = section.find('table').each(function (i) {
section.find('table').each(function (i) {
expandClipboardSection($(this));
});
}
@@ -5821,16 +5830,17 @@ function setSNMP() {
}
function checkSNMPPassphrase(type) {
var pass, conf, span;
var minChars = 8;
if (type == 'priv') {
var pass = '#snmp_priv_passphrase';
var conf = '#snmp_priv_passphrase_confirm';
var span = 'priv';
pass = '#snmp_priv_passphrase';
conf = '#snmp_priv_passphrase_confirm';
span = 'priv';
} else {
var pass = '#snmp_password';
var conf = '#snmp_password_confirm';
var span = 'auth';
pass = '#snmp_password';
conf = '#snmp_password_confirm';
span = 'auth';
}
if ($(pass).val().length == 0) {
@@ -5848,18 +5858,19 @@ function checkSNMPPassphrase(type) {
}
function checkSNMPPassphraseConfirm(type) {
var pass, conf, span, spanconf;
var minChars = 8;
if (type == 'priv') {
var pass = '#snmp_priv_passphrase';
var conf = '#snmp_priv_passphrase_confirm';
var span = 'priv';
var spanconf = 'privconf';
pass = '#snmp_priv_passphrase';
conf = '#snmp_priv_passphrase_confirm';
span = 'priv';
spanconf = 'privconf';
} else {
var pass = '#snmp_password';
var conf = '#snmp_password_confirm';
var span = 'auth';
var spanconf = 'authconf';
pass = '#snmp_password';
conf = '#snmp_password_confirm';
span = 'auth';
spanconf = 'authconf';
}
if ($(conf).val().length < minChars) {
@@ -5897,7 +5908,7 @@ const streamAvailabilityToggleOptions = ['0', '1', '2', '3', '4', '5', '6'];
const streamSNMPToggleOptions = ['1', '2', '3'];
function setAvailability() {
var hasChanged = false;
if ($('#availability_method').val() != '7') {
if ($('#snmp_version').val() == '0') {
@@ -6020,7 +6031,7 @@ function formValidate(formId, href) {
}
if (name !== null && name.length > 0) {
if (typeof formRules[formId] !== 'undefined') {
if (formRules[formId] !== undefined) {
formRules[formId][name] = {
required: true,
email: true,
@@ -6080,7 +6091,7 @@ function toggleFields(fields, prefix = '#row_') {
Object.keys(fields).forEach(index => {
curParent = $(prefix + index).parent().attr('id');
if (typeof curParent != 'undefined' && curParent != lastParent) {
if (curParent !== undefined && curParent != lastParent) {
parents.push(curParent);
}
@@ -6092,7 +6103,7 @@ function toggleFields(fields, prefix = '#row_') {
$(prefix + index).hide();
}
if (typeof curParent != 'undefined') {
if (curParent !== undefined) {
lastParent = curParent;
}
});
+13 -12
View File
@@ -38,16 +38,17 @@ var url;
var local_graph_id = null;
function realtimeDetectBrowser() {
var browser;
if (navigator.userAgent.indexOf('MSIE') >= 0) {
var browser = 'IE';
browser = 'IE';
} else if (navigator.userAgent.indexOf('Chrome') >= 0) {
var browser = 'Chrome';
browser = 'Chrome';
} else if (navigator.userAgent.indexOf('Mozilla') >= 0) {
var browser = 'FF';
browser = 'FF';
} else if (navigator.userAgent.indexOf('Opera') >= 0) {
var browser = 'Opera';
browser = 'Opera';
} else {
var browser = 'Other';
browser = 'Other';
}
return browser;
@@ -55,7 +56,7 @@ function realtimeDetectBrowser() {
function imageOptionsChanged(action) {
const graph_start = $('#graph_start').val();
var graph_end = 0;
const ds_step = $('#ds_step').val();
var size = $('#size').val();
const isThumb = $('#thumbnails').is(':checked');
@@ -78,12 +79,12 @@ function imageOptionsChanged(action) {
if (action === 'countdown') {
url = `graph_realtime.php?action=countdown&top=0&left=0&local_graph_id=${local_graph_id}&ds_step=${ds_step}&count=${count}&size=${size}&graph_nolegend=${isThumb}`;
} else if (action === 'initial') {
url = `graph_realtime.php?action=initial&top=0&left=0&local_graph_id=${local_graph_id}&graph_start=-${parseInt(graph_start) > 0 ? graph_start:'60'}&ds_step=${ds_step}&count=${count}&size=${size}`;
url = `graph_realtime.php?action=initial&top=0&left=0&local_graph_id=${local_graph_id}&graph_start=-${Number.parseInt(graph_start) > 0 ? graph_start:'60'}&ds_step=${ds_step}&count=${count}&size=${size}`;
} else {
url = `graph_realtime.php?action=${action}&top=0&left=0&local_graph_id=${local_graph_id}&graph_start=-${parseInt(graph_start) > 0 ? graph_start:'60'}&ds_step=${ds_step}&count=${count}&size=${size}&graph_nolegend=${isThumb}`;
url = `graph_realtime.php?action=${action}&top=0&left=0&local_graph_id=${local_graph_id}&graph_start=-${Number.parseInt(graph_start) > 0 ? graph_start:'60'}&ds_step=${ds_step}&count=${count}&size=${size}&graph_nolegend=${isThumb}`;
}
Pace.stop;
Pace.stop();
$.getJSON(url)
.done(function(data) {
@@ -170,7 +171,7 @@ function countRealtimeGraphs() {
}
function stopRealtime() {
var graph;
for (key in realtimeArray) {
var graph_id = key;
@@ -239,7 +240,7 @@ function realtimeGrapher() {
}
const graph_start = $('#graph_start').val();
var graph_end = 0;
const ds_step = $('#ds_step').val();
var size = $('#size').val();
const isThumb = $('#thumbnails').is(':checked');
@@ -276,7 +277,7 @@ function realtimeGrapher() {
position = $('body').position();
}
$.get(`${urlPath}graph_realtime.php?action=countdown&top=${parseInt(position.top)}&left=${parseInt(position.left)}${isThumb ? '&graph_nolegend=true':'&graph_nolegend=false'}&graph_end=0&graph_start=-${parseInt(graph_start) > 0 ? graph_start:'60'}&local_graph_id=${local_graph_id}&ds_step=${ds_step}&count=${count}&size=${size}`)
$.get(`${urlPath}graph_realtime.php?action=countdown&top=${Number.parseInt(position.top)}&left=${Number.parseInt(position.left)}${isThumb ? '&graph_nolegend=true':'&graph_nolegend=false'}&graph_end=0&graph_start=-${Number.parseInt(graph_start) > 0 ? graph_start:'60'}&local_graph_id=${local_graph_id}&ds_step=${ds_step}&count=${count}&size=${size}`)
.done(function(data) {
var results = (typeof data === 'string') ? JSON.parse(data) : data;
+3 -5
View File
@@ -134,12 +134,12 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
$('#navigation').hide();
$('li.menuitem').each(function() {
active = storage.get($(this).attr('id'));
const active = storage.get($(this).attr('id'));
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
$(this).next('a').show();
@@ -155,7 +155,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')){
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -172,8 +172,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+3 -5
View File
@@ -134,12 +134,12 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
$('#navigation').hide();
$('li.menuitem').each(function() {
active = storage.get($(this).attr('id'));
const active = storage.get($(this).attr('id'));
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
$(this).next('a').show();
@@ -155,7 +155,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')){
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -172,8 +172,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+4 -10
View File
@@ -96,18 +96,14 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
// This will setup the initial visibility of the menu
$('li.menuitem').each(function() {
var id = $(this).attr('id');
const id = $(this).attr('id');
if (storage.isSet(id)) {
var active = storage.get(id);
} else {
var active = null;
}
const active = storage.isSet(id) ? storage.get(id) : null;
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
@@ -132,7 +128,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')) {
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -149,8 +145,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+4 -10
View File
@@ -135,18 +135,14 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
// This will setup the initial visibility of the menu
$('li.menuitem').each(function() {
var id = $(this).attr('id');
const id = $(this).attr('id');
if (storage.isSet(id)) {
var active = storage.get(id);
} else {
var active = null;
}
const active = storage.isSet(id) ? storage.get(id) : null;
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
@@ -172,7 +168,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')) {
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -189,8 +185,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+3 -5
View File
@@ -134,12 +134,12 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
$('#navigation').hide();
$('li.menuitem').each(function() {
active = storage.get($(this).attr('id'));
const active = storage.get($(this).attr('id'));
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
$(this).next('a').show();
@@ -155,7 +155,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')){
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -172,8 +172,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+8 -8
View File
@@ -456,7 +456,7 @@ function setupTheme() {
});
} else {
$("#navBackdrop").on('click', function() {
window.open('https://cacti.net', '_blank');
window.open('https://cacti.net', '_blank', 'noopener,noreferrer');
});
}
}
@@ -597,7 +597,7 @@ function toggleCactiNavigationBoxPin(event) {
let caller = $(event.currentTarget);
let helper = caller.attr('data-helper');
let navigationBox = $('[class^="mdw-ConsoleNavigationBox"][data-helper="' + helper + '"]');
let compact_nav_icon = $('[class^="compact_nav_icon"][data-helper="' + helper + '"]');
$('[class^="compact_nav_icon"][data-helper="' + helper + '"]');
if(event.data && event.data.dock) {
event.data.dock = event.data.dock.replace(/^./, str => str.toUpperCase());
@@ -670,7 +670,7 @@ function hideDropDownMenu() {
function toggleTableColumn(event) {
let storage = Storages.localStorage;
let tableHash = event.target.dataset.table;
let cIndex = parseInt(event.target.dataset.column);
let cIndex = Number.parseInt(event.target.dataset.column);
let cClass = 'no-col'+cIndex;
let storage_table_headers = storage.get('midWinter_' + tableHash);
@@ -914,7 +914,7 @@ function changeGuiFontSize(change=true) {
setDocumentAttribute('zoom-level', mdw.session.theme.font.zoom);
}
/* update output field beside input selector */
$('#mdw_themeFontSizeValue').val((parseFloat(mdw.session.theme.font.zoom) + 25).toFixed(1) + ' %');
$('#mdw_themeFontSizeValue').val((Number.parseFloat(mdw.session.theme.font.zoom) + 25).toFixed(1) + ' %');
}
function toggleGuiAnimations() {
@@ -1022,9 +1022,9 @@ function togglePopOver(force) {
function fullScreen(event) {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().then( r => fullScreenChangeHandler() );
document.documentElement.requestFullscreen().then(() => fullScreenChangeHandler()).catch(() => fullScreenChangeHandler());
}else if (document.exitFullscreen) {
document.exitFullscreen().then( r => fullScreenChangeHandler() );
document.exitFullscreen().then(() => fullScreenChangeHandler()).catch(() => fullScreenChangeHandler());
}
}
@@ -1328,7 +1328,7 @@ registry.midwinter = {
let midWinter_Color_Mode_Auto = mdw.session.theme.color.auto;
let midWinter_Font_Size = mdw.session.theme.font.zoom;
let midWinter_Animations = mdw.session.theme.boxes.animated
let midWinter_ShownFontSizeValue = parseFloat(midWinter_Font_Size) + 25;
let midWinter_ShownFontSizeValue = Number.parseFloat(midWinter_Font_Size) + 25;
let midWinter_Auto_Table_Layout = mdw.session.theme.mobile.autoTableLayout;
let midWinter_Controls_SubTitle = mdw.session.theme.controls.subTitle;
@@ -1413,4 +1413,4 @@ registry.midwinter = {
}
}
}
}
}
+4 -10
View File
@@ -88,18 +88,14 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
// This will setup the initial visibility of the menu
$('li.menuitem').each(function() {
var id = $(this).attr('id');
const id = $(this).attr('id');
if (storage.isSet(id)) {
var active = storage.get(id);
} else {
var active = null;
}
const active = storage.isSet(id) ? storage.get(id) : null;
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
@@ -125,7 +121,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')) {
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -142,8 +138,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+4 -10
View File
@@ -153,18 +153,14 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
// This will setup the initial visibility of the menu
$('li.menuitem').each(function() {
var id = $(this).attr('id');
const id = $(this).attr('id');
if (storage.isSet(id)) {
var active = storage.get(id);
} else {
var active = null;
}
const active = storage.isSet(id) ? storage.get(id) : null;
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
@@ -190,7 +186,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')) {
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -207,8 +203,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+4 -10
View File
@@ -135,18 +135,14 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
// This will setup the initial visibility of the menu
$('li.menuitem').each(function() {
var id = $(this).attr('id');
const id = $(this).attr('id');
if (storage.isSet(id)) {
var active = storage.get(id);
} else {
var active = null;
}
const active = storage.isSet(id) ? storage.get(id) : null;
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
@@ -172,7 +168,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')) {
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -189,8 +185,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+3 -5
View File
@@ -134,12 +134,12 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
$('#navigation').hide();
$('li.menuitem').each(function() {
active = storage.get($(this).attr('id'));
const active = storage.get($(this).attr('id'));
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
$(this).next('a').show();
@@ -155,7 +155,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')){
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -172,8 +172,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+5 -11
View File
@@ -28,7 +28,7 @@ function themeReady() {
$('#navigation_right').rebind('scroll', function (event) {
var scroll_position_x = $('#navigation_right').scrollLeft();
var scroll_position_y = $('#navigation_right').scrollTop();
$('.bottom_scroll_up').css({'color': ((scroll_position_x == 0 & scroll_position_y == 0) ? '' : '#93CEFF') });
$('.bottom_scroll_up').css({'color': ((scroll_position_x === 0 && scroll_position_y === 0) ? '' : '#93CEFF') });
});
// Add nice search filter to filters
@@ -158,18 +158,14 @@ function themeReady() {
}
function setMenuVisibility() {
storage=Storages.sessionStorage;
const storage = Storages.sessionStorage;
// Initialize the navigation settings
// This will setup the initial visibility of the menu
$('li.menuitem').each(function() {
var id = $(this).attr('id');
const id = $(this).attr('id');
if (storage.isSet(id)) {
var active = storage.get(id);
} else {
var active = null;
}
const active = storage.isSet(id) ? storage.get(id) : null;
if (active != null && active == 'active') {
$(this).find('ul').attr('aria-hidden', 'false').attr('aria-expanded', 'true').show();
@@ -195,7 +191,7 @@ function setMenuVisibility() {
$('#nav li:has(ul) a.active').rebind('click', function(event) {
event.preventDefault();
id = $(this).closest('.menuitem').attr('id');
const id = $(this).closest('.menuitem').attr('id');
if ($(this).next().is(':visible')) {
$(this).next('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
@@ -212,8 +208,6 @@ function setMenuVisibility() {
}
$('li.menuitem').not('#'+id).each(function() {
text = $(this).attr('id');
id = $(this).attr('id');
$(this).find('ul').attr('aria-hidden', 'true').attr('aria-expanded', 'false');
$(this).find('ul').slideUp( { duration: 200, easing: 'swing' } );
+8 -8
View File
@@ -127,7 +127,7 @@ function setButtonData(buttonName, buttonData) {
button.button();
button.data('buttonData', buttonData);
if (buttonData != null) {
var buttonCheck = button.data('buttonData');
button.data('buttonData');
if (buttonData.Enabled) {
button.button('enable');
} else {
@@ -231,7 +231,7 @@ function getDefaultInstallData() {
}
function toggleHeader(key, initial) {
if (typeof initial === 'undefined') {
if (initial === undefined) {
initial = null;
}
@@ -254,7 +254,7 @@ function toggleHeader(key, initial) {
}
function toggleSection(key, initial) {
if (typeof initial === 'undefined') {
if (initial === undefined) {
initial = null;
}
@@ -559,7 +559,7 @@ function prepareInstallData(installStep, stepOnly) {
}
// No installation step if we have never started.
if (typeof installStep === 'undefined' && installData.Step == STEP_NONE) {
if (installStep === undefined && installData.Step == STEP_NONE) {
newData = [];
} else {
newData = getDefaultInstallData();
@@ -572,12 +572,12 @@ function prepareInstallData(installStep, stepOnly) {
}
}
if (typeof installStep !== 'undefined') {
if (installStep !== undefined) {
step = installData.Step;
if (step == STEP_WELCOME) prepareStepWelcome(newData);
// Assume we want all data if stepOnly not set
if (typeof stepOnly === 'undefined') {
if (stepOnly === undefined) {
if (step == STEP_INSTALL_TYPE) prepareStepInstallType(newData);
else if (step == STEP_BINARY_LOCATIONS) prepareStepBinaryLocations(newData);
else if (step == STEP_PROFILE_AND_AUTOMATION) prepareStepProfileAndAutomation(newData);
@@ -700,7 +700,7 @@ function performStep(installStep, suppressRefresh, forceReload, csrfRetry) {
$('#installContent').html(data.Html);
$('#installContent').show();
if (typeof $('#installData').data('debug') !== 'undefined') {
if ($('#installData').data('debug') !== undefined) {
debugData = data;
debugData.Html = '';
debug = $('#installDebug');
@@ -822,7 +822,7 @@ function performTestConnection(csrfRetry) {
$('#installContent').removeClass('cactiInstallLoaderBlur');
var isSuccessful = false, statusText = testFailed;
if (typeof data.status !== 'undefined') {
if (data.status !== undefined) {
if (data.status == 'true') {
isSuccessful = true;
statusText = testSuccessful;
+4 -2
View File
@@ -3200,7 +3200,8 @@ function html_common_header(string $title, string $selectedTheme = '') : void {
var showHideFilter = '<?php print __esc('Click to Show/Hide Filter'); ?>';
var spikeKillResults = '<?php print __esc('SpikeKill Results'); ?>';
var standardGraphicalUserInterface = '<?php print __esc('Standard Mode'); ?>';
var tableConstraints = '<?php print __esc('Allow or limit the table columns to extend beyond the current windows limits.'); ?>';
var tableConstraints = <?php print cacti_js_encode(__esc('Allow or limit the table columns to extend beyond the current windows ' .
'limits.')); ?>;
var testFailed = '<?php print __esc('Connection Failed'); ?>';
var testSuccessful = '<?php print __esc('Connection Successful'); ?>';
var theme = '<?php print $selectedTheme; ?>';
@@ -3245,7 +3246,8 @@ function html_common_header(string $title, string $selectedTheme = '') : void {
var zoom_i18n_zoom_out_factor = '<?php print __esc('Zoom Out Factor'); ?>';
var zoom_i18n_zoom_out_positioning = '<?php print __esc('Zoom Out Positioning'); ?>';
var zoom_outOfRangeTitle='<?php print __esc('Zoom Window Out of Range'); ?>';
var zoom_outOfRangeMessage='<?php print __esc('Zoom Dates before January 1, 1993 are not supported in Cacti! Pick a more recent date.'); ?>';
var zoom_outOfRangeMessage= <?php print cacti_js_encode(__esc('Zoom Dates before January 1, 1993 are not supported in Cacti! Pick a ' .
'more recent date.')); ?>;
</script>
<?php
// Global icons
+13 -13
View File
@@ -477,7 +477,7 @@ class Ldap {
public string $cn_full_name;
public string $cn_email;
function __construct() {
public function __construct() {
// No DB lookup here: Cacti\Auth\LdapLoginProvider::buildLdap() is the
// single source of truth for mapping login_providers.parameters onto
// these properties, so this class stays a pure connection/protocol
@@ -486,14 +486,14 @@ class Ldap {
$this->host = '';
}
function __destruct() {
public function __destruct() {
}
function ErrorHandler(int $level, string $message, string $file, int $line, array $context = []) : bool {
public function ErrorHandler(int $level, string $message, string $file, int $line, array $context = []) : bool {
return true;
}
function SetLdapHandler() : void {
public function SetLdapHandler() : void {
// drop out of cactis error handler
restore_error_handler();
@@ -503,7 +503,7 @@ class Ldap {
cacti_session_close();
}
function RestoreCactiHandler() : void {
public function RestoreCactiHandler() : void {
// drop out of ldaps error handler
restore_error_handler();
@@ -513,13 +513,13 @@ class Ldap {
cacti_session_start();
}
function RecordError(array $output, string $section = 'LDAP') : void {
public function RecordError(array $output, string $section = 'LDAP') : void {
$logDN = empty($output['dn']) ? '' : (', DN: ' . $output['dn']);
cacti_log($section . ': ' . $output['error_text'] . $logDN, false, 'AUTH');
cacti_log($section . ': ' . $output['stack'], false, 'AUTH', $this->debug);
}
function Connect() : array {
public function Connect() : array {
$output = [];
$ldap_conn = null;
@@ -698,7 +698,7 @@ class Ldap {
}
}
function Authenticate() : array {
public function Authenticate() : array {
$output = [];
cacti_log('LDAP: Authentication Start', false, 'AUTH', $this->debug);
@@ -838,7 +838,7 @@ class Ldap {
return $output;
}
function GetMask() : int {
public function GetMask() : int {
if (!defined('ENT_HTML401')) {
return ENT_COMPAT;
} else {
@@ -869,7 +869,7 @@ class Ldap {
* failure) so the caller can leave assignments untouched
* rather than revoking them on a transient outage.
*/
function ResolveGroupMemberships(string $username, array $groupNames) : ?array {
public function ResolveGroupMemberships(string $username, array $groupNames) : ?array {
if (!cacti_sizeof($groupNames)) {
return [];
}
@@ -1136,7 +1136,7 @@ class Ldap {
return $queries;
}
function Search() : array {
public function Search() : array {
$output = [];
// Determine connection method and create LDAP Object
@@ -1256,7 +1256,7 @@ class Ldap {
return $output;
}
function Getcn() : array {
public function Getcn() : array {
$output = [];
// Determine connection method and create LDAP Object
@@ -1397,7 +1397,7 @@ class Ldap {
return $output;
}
function isUserInLDAPGroup(object $ldapConn, string $ldapbasedn, string $groupDN, string $ldapUser) : bool {
public function isUserInLDAPGroup(object $ldapConn, string $ldapbasedn, string $groupDN, string $ldapUser) : bool {
$query = cacti_ldap_filter(
'(&(distinguishedName=<user>)(memberOf:1.2.840.113556.1.4.1941:=<group>))',
['user' => $ldapUser, 'group' => $groupDN]
+8 -8
View File
@@ -56,13 +56,13 @@ class MibParser extends MibCache {
/**
* Constructor
*/
function __construct() {
public function __construct() {
set_time_limit(0);
ini_set('memory_limit', '-1');
error_reporting(E_ALL);
}
function add_mib(string $filename, string $mib_name): void {
public function add_mib(string $filename, string $mib_name): void {
$mibtext = file_get_contents($filename);
if ($mibtext === false) {
@@ -78,7 +78,7 @@ class MibParser extends MibCache {
* @param string $text
* @return array
*/
function get_tokens($text) {
public function get_tokens($text) {
$in_quote = false;
$in_comment = false;
$token = '';
@@ -197,7 +197,7 @@ class MibParser extends MibCache {
* @param array $allowed
* @return array
*/
function parse_simple_token($tokens, &$index, $allowed = null) {
public function parse_simple_token($tokens, &$index, $allowed = null) {
$index++;
if (is_array($allowed)) {
@@ -222,7 +222,7 @@ class MibParser extends MibCache {
* @param integer $index
* @return array
*/
function parse_SYNTAX_token($tokens, &$index) {
public function parse_SYNTAX_token($tokens, &$index) {
$ret = null;
switch($tokens[$index + 1]) {
@@ -316,7 +316,7 @@ class MibParser extends MibCache {
* @param string $end
* @return array
*/
function parse_bracket_token($tokens, &$index, $start, $end) {
public function parse_bracket_token($tokens, &$index, $start, $end) {
$begin = $index + 1;
while ($index + 1 < count($tokens) && $tokens[$index] != $end) {
@@ -338,7 +338,7 @@ class MibParser extends MibCache {
* @param boolean $full
* @param mixed $mib_name
*/
function parse_mib($mibtext, $mib_name, $full = false): void {
public function parse_mib($mibtext, $mib_name, $full = false): void {
$tokens = MibParser::get_tokens($mibtext);
$cnt = count($tokens);
$rec = [];
@@ -424,7 +424,7 @@ class MibParser extends MibCache {
}
}
function generate(): void {
public function generate(): void {
$this->oids['enterprises'] = ['oid' => '.1.3.6.1.4.1'];
foreach ($this->parsed as $object) {
+21 -21
View File
@@ -42,23 +42,23 @@ class Net_Ping {
public int $avail_method;
public int $ping_type;
function __construct() {
public function __construct() {
$this->port = 33439;
}
function __destruct() {
public function __destruct() {
}
function close_socket() : void {
public function close_socket() : void {
@socket_shutdown($this->socket, 2);
socket_close($this->socket);
}
function start_time() : void {
public function start_time() : void {
$this->timer_start_time = microtime(true);
}
function get_time(int $acc = 2) : mixed {
public function get_time(int $acc = 2) : mixed {
// format start time
$start_time = $this->timer_start_time;
// get and format end time
@@ -67,7 +67,7 @@ class Net_Ping {
return number_format($end_time - $start_time, $acc);
}
function build_udp_packet() : void {
public function build_udp_packet() : void {
$data = 'cacti-monitoring-system'; // the actual test data
// now lets build the actual UDP packet
@@ -75,19 +75,19 @@ class Net_Ping {
$this->request_len = strlen($this->request);
}
function ping_error_handler(int $errno, string $errmsg, string $filename, int $linenum, array $vars = []) : bool {
public function ping_error_handler(int $errno, string $errmsg, string $filename, int $linenum, array $vars = []) : bool {
return true;
}
function set_ping_error_handler() : void {
public function set_ping_error_handler() : void {
set_error_handler([$this, 'ping_error_handler']);
}
function restore_cacti_error_handler() : void {
public function restore_cacti_error_handler() : void {
restore_error_handler();
}
function build_icmp_packet() : void {
public function build_icmp_packet() : void {
$seq_low = random_int(0,255);
$seq_high = random_int(0,255);
@@ -107,7 +107,7 @@ class Net_Ping {
$this->request_len = strlen($this->request);
}
function get_checksum(string $data) : string {
public function get_checksum(string $data) : string {
if (strlen($data) % 2) {
$data .= "\x00";
}
@@ -122,7 +122,7 @@ class Net_Ping {
return pack('n*', ~$sum);
}
function ping_icmp() : bool {
public function ping_icmp() : bool {
// ping me
if ($this->host['hostname']) {
// initialize variables
@@ -299,7 +299,7 @@ class Net_Ping {
}
}
function seteuid() : int {
public function seteuid() : int {
$cacti_user = '';
// if we are unix, set the effective userid to root and then create
@@ -312,7 +312,7 @@ class Net_Ping {
return $cacti_user;
}
function setuid(int $cacti_poller_account) : void {
public function setuid(int $cacti_poller_account) : void {
// if we are unix, set the effective userid to root and then create
if ((CACTI_SERVER_OS == 'unix') &&
(function_exists('posix_getuid'))) {
@@ -320,7 +320,7 @@ class Net_Ping {
}
}
function ping_snmp() : mixed {
public function ping_snmp() : mixed {
// initialize variables
$this->snmp_status = 'down';
$this->snmp_response = 'Device did not respond to SNMP';
@@ -352,7 +352,7 @@ class Net_Ping {
return $result;
}
function get_snmp_result(string $oid) : bool {
public function get_snmp_result(string $oid) : bool {
$h = $this->host;
// getnext does not work in php versions less than 5
@@ -389,7 +389,7 @@ class Net_Ping {
}
} // ping_snmp
function ping_udp() : bool {
public function ping_udp() : bool {
$this->set_ping_error_handler();
// hostname must be nonblank
@@ -519,7 +519,7 @@ class Net_Ping {
}
} // end ping_udp
function ping_tcp() : bool {
public function ping_tcp() : bool {
$this->set_ping_error_handler();
// hostname must be nonblank
@@ -651,7 +651,7 @@ class Net_Ping {
}
} // end ping_tcp
function ping(int $avail_method = AVAIL_SNMP_AND_PING, int $ping_type = PING_ICMP, int $timeout = 500, int $retries = 3) : bool {
public function ping(int $avail_method = AVAIL_SNMP_AND_PING, int $ping_type = PING_ICMP, int $timeout = 500, int $retries = 3) : bool {
$this->set_ping_error_handler();
// initialize variables
@@ -761,7 +761,7 @@ class Net_Ping {
};
} // end_ping
function is_ipaddress(string $ip_address = '') : bool {
public function is_ipaddress(string $ip_address = '') : bool {
// check for ipv4/v6
if (function_exists('filter_var')) {
if (filter_var($ip_address, FILTER_VALIDATE_IP) !== false) {
@@ -776,7 +776,7 @@ class Net_Ping {
}
}
function strip_ip_address(string $ip_address) : string {
public function strip_ip_address(string $ip_address) : string {
// clean up hostname if specifying snmp_transport
if (str_contains($ip_address, 'tcp6:')) {
$ip_address = str_replace('tcp6:', '', cacti_strtolower($ip_address));
+7 -2
View File
@@ -262,8 +262,13 @@ function display_settings() : void {
var currentTab = '<?php print $current_tab; ?>';
var dataCollectors = '<?php print $data_collectors; ?>';
var permsTitle = '<?php print __esc('Changing Permission Model Warning'); ?>';
var permsHeader = '<?php print __esc('Changing Permission Model will alter a users effective Graph permissions.'); ?>';
var permsMessage = '<?php print __esc('After you change the Graph Permission Model you should audit your Users and User Groups Effective Graph permission to ensure that you still have adequate control of your Graphs. NOTE: If you want to restrict all Graphs at the Device or Graph Template Graph Permission Model, the default Graph Policy should be set to \'Deny\'.'); ?>';
var permsHeader = <?php print cacti_js_encode(__esc('Changing Permission Model will alter a users effective Graph ' .
'permissions.')); ?>;
var permsMessage = <?php print cacti_js_encode(__esc('After you change the Graph Permission Model you should audit your ' .
'Users and User Groups Effective Graph permission to ensure that you ' .
'still have adequate control of your Graphs. NOTE: If you want to ' .
'restrict all Graphs at the Device or Graph Template Graph Permission ' .
'Model, the default Graph Policy should be set to \'Deny\'.')); ?>;
$(function() {
$('#filter').keyup(function() {
@@ -0,0 +1,59 @@
<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
| Licensed under the GNU General Public License, version 2 or later. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
*/
function cacti_test_public_method_contracts(): array {
static $contracts = null;
if ($contracts !== null) {
return $contracts;
}
// Load the production classes in a separate process. Collecting this test
// must not alter class/function ownership in other Cacti unit test files.
$probe = dirname(__DIR__, 2) . '/fixtures/Core/PublicMethodContractProbe.php';
$process = proc_open([PHP_BINARY, $probe], [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
], $pipes);
if (!is_resource($process)) {
throw new RuntimeException('Unable to start the public API probe.');
}
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
$errors = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
if (proc_close($process) !== 0) {
throw new RuntimeException($errors);
}
$contracts = json_decode($output, true, 512, JSON_THROW_ON_ERROR);
return $contracts;
}
$baseline = json_decode(file_get_contents(
dirname(__DIR__, 2) . '/fixtures/Core/public-method-signatures.json'
), true, 512, JSON_THROW_ON_ERROR);
$cases = [];
foreach ($baseline as $name => $contract) {
$cases[$name] = [$name, $contract];
}
test('the public method retains its complete Cacti develop call contract', function (string $name, array $expected) {
$actual = cacti_test_public_method_contracts();
expect($actual)->toHaveCount(46);
expect($actual[$name])->toBe($expected);
})->with($cases);
@@ -127,7 +127,9 @@ test('form_cert_box() shows the expiration date of a pasted valid certificate',
form_cert_box('idp_x509cert', $pem, '', 6, 60);
$html = ob_get_clean();
$expected = date('Y-m-d', strtotime('+30 days'));
$certificate = openssl_x509_parse($pem);
expect($certificate)->not->toBeFalse();
$expected = date('Y-m-d', $certificate['validTo_time_t']);
expect($html)->toContain('<textarea');
expect($html)->toContain('Good Till: ' . $expected);
@@ -0,0 +1,41 @@
<?php
// Copyright (C) 2004-2026 The Cacti Group. GPL-2.0-or-later.
require_once dirname(__DIR__, 3) . '/Helpers/CactiStubs.php';
require_once CACTI_PATH_INCLUDE . '/global.php';
test('inline translated values use the shared JSON boundary and preserve HTML escaping', function (string $path, string $variable, string $catalogMessage) {
$source = file_get_contents(CACTI_PATH_BASE . '/' . $path);
$pattern = '/\bvar\s+' . preg_quote($variable, '/') . '\s*=\s*[\'\"]?<\?php\s+print\s+(.*?)\?>/s';
expect(preg_match($pattern, $source, $match))->toBe(1);
$parser = (new \PhpParser\ParserFactory())->createForNewestSupportedVersion();
$statements = $parser->parse('<?php print ' . trim($match[1]));
$expression = $statements[0]->expr;
expect($expression)->toBeInstanceOf(\PhpParser\Node\Expr\Print_::class);
$encodedCall = $expression->expr;
expect($encodedCall)->toBeInstanceOf(\PhpParser\Node\Expr\FuncCall::class);
expect($encodedCall->name->toString())->toBe('cacti_js_encode');
$translatedCall = $encodedCall->args[0]->value;
expect($translatedCall)->toBeInstanceOf(\PhpParser\Node\Expr\FuncCall::class);
expect($translatedCall->name->toString())->toBe('__esc');
// Supply adversarial catalog text to the actual production expression;
// neither the translation helper nor the JSON encoder is replaced.
$translatedCall->args[0]->value = new \PhpParser\Node\Expr\Variable('catalogMessage');
$printer = new \PhpParser\PrettyPrinter\Standard();
$encoded = eval('return ' . $printer->prettyPrintExpr($encodedCall) . ';');
expect(json_decode($encoded, true, 512, JSON_THROW_ON_ERROR))->toBe(__esc($catalogMessage));
expect($encoded)->not->toContain('</script>');
expect($encoded)->not->toContain("\u{2028}");
expect($encoded)->not->toContain("\u{2029}");
})->with([
['settings.php', 'permsHeader'],
['settings.php', 'permsMessage'],
['lib/html.php', 'tableConstraints'],
['lib/html.php', 'zoom_outOfRangeMessage'],
])->with([
'quotes and backslashes' => ["single' double\" backslash\\"],
'script closing text' => ['</script><script>window.untrusted=true</script>'],
'line terminators' => ["first\nsecond\u{2028}third\u{2029}fourth"],
'HTML entities' => ['&quot; &apos; <b>caption</b>'],
]);
@@ -0,0 +1,27 @@
/* Copyright (C) 2004-2026 The Cacti Group. GPL-2.0-or-later. */
import { test, expect } from '@playwright/test';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
test('Midwinter homepage popup cannot access its opener', async ({ page, context }) => {
const source = readFileSync(resolve(__dirname, '../../../include/themes/midwinter/main.js'), 'utf8');
const sink = source.indexOf("window.open('https://cacti.net'");
expect(sink).toBeGreaterThan(0);
const start = source.lastIndexOf('function()', sink);
const end = source.indexOf('});', sink);
expect(start).toBeGreaterThan(0);
expect(end).toBeGreaterThan(sink);
const handler = source.slice(start, end + 1);
await context.route('https://cacti.net/**', route => route.fulfill({
contentType: 'text/html', body: '<!DOCTYPE html><title>Fixture homepage</title>',
}));
await page.setContent('<!DOCTYPE html><button id="homepage">Homepage</button>');
// Execute the repository-owned callback with a real click; no fixture
// implementation of window.open or opener isolation is substituted.
await page.addScriptTag({ content: `document.querySelector('#homepage').addEventListener('click', ${handler});` });
const popupPromise = context.waitForEvent('page');
await page.locator('#homepage').click();
const popup = await popupPromise;
await popup.waitForLoadState();
expect(await popup.evaluate(() => window.opener)).toBeNull();
});
@@ -0,0 +1,67 @@
/* Copyright (C) 2004-2026 The Cacti Group. GPL-2.0-or-later. */
import { test, expect } from '@playwright/test';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
const root = resolve(__dirname, '../../..');
const layout = readFileSync(resolve(root, 'include/layout.js'), 'utf8');
const start = layout.indexOf('$.fn.rebind = function');
const body = layout.indexOf('{', start);
let depth = 0;
let end = body;
for (; end < layout.length; end++) {
if (layout[end] === '{') depth++;
if (layout[end] === '}' && --depth === 0) break;
}
const rebind = layout.slice(start, end + 1) + ';';
for (const theme of ['cacti', 'carrot', 'dark', 'deepness', 'hollyberry', 'modern', 'paper-plane', 'paw', 'raspberry', 'sunrise']) {
for (const stored of [false, true]) {
test(`${theme} navigation keeps menu state local with ${stored ? 'persisted' : 'empty'} storage`, async ({ page }) => {
await page.route('http://cacti.test/**', route => route.fulfill({
contentType: 'text/html',
body: '<!DOCTYPE html><div id="navigation"><div id="nav"><ul>' +
'<li class="menuitem" id="menu-a"><a class="active" href="#a">A</a><ul><li><a class="selected">Selected</a></li></ul></li>' +
'<li class="menuitem" id="menu-b"><a class="active" href="#b">B</a><ul><li><a>Other</a></li></ul></li>' +
'</ul></div></div>',
}));
await page.goto('http://cacti.test/');
await page.addScriptTag({ path: resolve(root, 'include/js/jquery.js') });
await page.addScriptTag({ path: resolve(root, 'include/js/js.storage.js') });
await page.addScriptTag({ content: rebind });
const source = readFileSync(resolve(root, `include/themes/${theme}/main.js`), 'utf8');
const menu = source.indexOf('function setMenuVisibility() {');
expect(menu).toBeGreaterThan(0);
await page.addScriptTag({ content: source.slice(menu) });
await page.evaluate(stored => {
const host = window as any;
host.jQuery.fx.off = true;
for (const name of ['id', 'active', 'text', 'storage']) host[name] = `host-${name}`;
if (stored) {
host.Storages.sessionStorage.set('menu-a', 'active');
host.Storages.sessionStorage.set('menu-b', 'collapsed');
}
host.setMenuVisibility();
host.setMenuVisibility();
}, stored);
const menuB = page.locator('#menu-b > ul');
await expect(menuB).toBeHidden();
await page.locator('#menu-b > a').click();
await expect(menuB).toBeVisible();
await expect(menuB).toHaveAttribute('aria-expanded', 'true');
await expect(menuB).toHaveAttribute('aria-hidden', 'false');
await expect(page.locator('#menu-a > ul')).toBeHidden();
expect(await page.evaluate(() => {
const storage = (window as any).Storages.sessionStorage;
return [storage.get('menu-a'), storage.get('menu-b')];
})).toEqual(['collapsed', 'active']);
await page.locator('#menu-b > a').click();
await expect(menuB).toBeHidden();
await expect(menuB).toHaveAttribute('aria-expanded', 'false');
await expect(menuB).toHaveAttribute('aria-hidden', 'true');
expect(await page.evaluate(() => (window as any).Storages.sessionStorage.get('menu-b'))).toBe('collapsed');
expect(await page.evaluate(() => ['id', 'active', 'text', 'storage'].map(name => (window as any)[name])))
.toEqual(['host-id', 'host-active', 'host-text', 'host-storage']);
});
}
}
@@ -0,0 +1,49 @@
/* Copyright (C) 2004-2026 The Cacti Group. GPL-2.0-or-later. */
import { test, expect } from '@playwright/test';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
test('column widths follow the table attribute across action changes and retain the ID fallback', async ({ page }) => {
const root = resolve(__dirname, '../../..');
const source = readFileSync(resolve(root, 'include/layout.js'), 'utf8');
const start = source.indexOf('function saveTableWidths(initial)');
const end = source.indexOf('\n}\n', start);
expect(start).toBeGreaterThan(0);
expect(end).toBeGreaterThan(start);
await page.route('http://cacti.test/column-widths', route => route.fulfill({
contentType: 'text/html',
body: '<!DOCTYPE html><table id="hosts_view" class="cactiTable" data-colsizing="hosts"><tr><th class="ui-resizable">Name</th><th class="ui-resizable">Count</th></tr></table>',
}));
await page.goto('http://cacti.test/column-widths');
await page.addScriptTag({ path: resolve(root, 'include/js/jquery.js') });
await page.addScriptTag({ path: resolve(root, 'include/js/js.storage.js') });
await page.addScriptTag({ content: source.slice(start, end + 2) });
await page.evaluate(() => {
const browser = window as any;
browser.Storages.localStorage.set('hosts', [browser.$(document).width(), 210, 180]);
browser.saveTableWidths(true);
});
expect(await page.locator('th').first().evaluate(element => element.getAttribute('resizeWidth'))).toBe('210');
await page.evaluate(() => {
const browser = window as any;
document.querySelector('table')!.id = 'hosts_edit_report';
browser.$('th').css('width', 50);
browser.saveTableWidths(true);
});
expect(await page.locator('th').first().evaluate(element => element.getAttribute('resizeWidth'))).toBe('210');
await page.evaluate(() => {
const browser = window as any;
const table = document.querySelector('table')!;
table.removeAttribute('data-colsizing');
browser.Storages.localStorage.set(table.id, [browser.$(document).width(), 260, 190]);
browser.saveTableWidths(true);
});
expect(await page.locator('th').first().evaluate(element => element.getAttribute('resizeWidth'))).toBe('260');
await page.evaluate(() => {
const browser = window as any;
document.querySelector('table')!.setAttribute('data-colsizing', '');
browser.saveTableWidths(true);
});
expect(await page.locator('th').first().evaluate(element => element.getAttribute('resizeWidth'))).toBe('260');
expect(await page.evaluate(() => Object.hasOwn(window, 'sizes') || Object.hasOwn(window, 'items'))).toBe(false);
});
+49
View File
@@ -0,0 +1,49 @@
<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
| Licensed under the GNU General Public License, version 2 or later. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
*/
require_once dirname(__DIR__, 3) . '/lib/ping.php';
require_once dirname(__DIR__, 3) . '/lib/ldap.php';
require_once dirname(__DIR__, 3) . '/lib/mib_cache.php';
require_once dirname(__DIR__, 3) . '/lib/mib_parser.php';
$contracts = [];
foreach (['Net_Ping', 'Ldap', 'MibParser'] as $class) {
foreach ((new ReflectionClass($class))->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $class) {
continue;
}
$parameters = [];
foreach ($method->getParameters() as $parameter) {
$parameters[] = [
'name' => $parameter->getName(),
'reference' => $parameter->isPassedByReference(),
'variadic' => $parameter->isVariadic(),
'type' => $parameter->hasType() ? (string) $parameter->getType() : null,
'optional' => $parameter->isOptional(),
'default' => !$parameter->isDefaultValueAvailable() ? null :
($parameter->isDefaultValueConstant() ?
['constant' => $parameter->getDefaultValueConstantName()] :
['value' => $parameter->getDefaultValue()]),
];
}
$contracts[$class . '::' . $method->getName()] = [
'public' => $method->isPublic(),
'static' => $method->isStatic(),
'parameters' => $parameters,
'return' => $method->hasReturnType() ? (string) $method->getReturnType() : null,
];
}
}
print json_encode($contracts, JSON_THROW_ON_ERROR);
+1
View File
@@ -0,0 +1 @@
{"Net_Ping::__construct":{"public":true,"static":false,"parameters":[],"return":null},"Net_Ping::__destruct":{"public":true,"static":false,"parameters":[],"return":null},"Net_Ping::close_socket":{"public":true,"static":false,"parameters":[],"return":"void"},"Net_Ping::start_time":{"public":true,"static":false,"parameters":[],"return":"void"},"Net_Ping::get_time":{"public":true,"static":false,"parameters":[{"name":"acc","reference":false,"variadic":false,"type":"int","optional":true,"default":{"value":2}}],"return":"mixed"},"Net_Ping::build_udp_packet":{"public":true,"static":false,"parameters":[],"return":"void"},"Net_Ping::ping_error_handler":{"public":true,"static":false,"parameters":[{"name":"errno","reference":false,"variadic":false,"type":"int","optional":false,"default":null},{"name":"errmsg","reference":false,"variadic":false,"type":"string","optional":false,"default":null},{"name":"filename","reference":false,"variadic":false,"type":"string","optional":false,"default":null},{"name":"linenum","reference":false,"variadic":false,"type":"int","optional":false,"default":null},{"name":"vars","reference":false,"variadic":false,"type":"array","optional":true,"default":{"value":[]}}],"return":"bool"},"Net_Ping::set_ping_error_handler":{"public":true,"static":false,"parameters":[],"return":"void"},"Net_Ping::restore_cacti_error_handler":{"public":true,"static":false,"parameters":[],"return":"void"},"Net_Ping::build_icmp_packet":{"public":true,"static":false,"parameters":[],"return":"void"},"Net_Ping::get_checksum":{"public":true,"static":false,"parameters":[{"name":"data","reference":false,"variadic":false,"type":"string","optional":false,"default":null}],"return":"string"},"Net_Ping::ping_icmp":{"public":true,"static":false,"parameters":[],"return":"bool"},"Net_Ping::seteuid":{"public":true,"static":false,"parameters":[],"return":"int"},"Net_Ping::setuid":{"public":true,"static":false,"parameters":[{"name":"cacti_poller_account","reference":false,"variadic":false,"type":"int","optional":false,"default":null}],"return":"void"},"Net_Ping::ping_snmp":{"public":true,"static":false,"parameters":[],"return":"mixed"},"Net_Ping::get_snmp_result":{"public":true,"static":false,"parameters":[{"name":"oid","reference":false,"variadic":false,"type":"string","optional":false,"default":null}],"return":"bool"},"Net_Ping::ping_udp":{"public":true,"static":false,"parameters":[],"return":"bool"},"Net_Ping::ping_tcp":{"public":true,"static":false,"parameters":[],"return":"bool"},"Net_Ping::ping":{"public":true,"static":false,"parameters":[{"name":"avail_method","reference":false,"variadic":false,"type":"int","optional":true,"default":{"constant":"AVAIL_SNMP_AND_PING"}},{"name":"ping_type","reference":false,"variadic":false,"type":"int","optional":true,"default":{"constant":"PING_ICMP"}},{"name":"timeout","reference":false,"variadic":false,"type":"int","optional":true,"default":{"value":500}},{"name":"retries","reference":false,"variadic":false,"type":"int","optional":true,"default":{"value":3}}],"return":"bool"},"Net_Ping::is_ipaddress":{"public":true,"static":false,"parameters":[{"name":"ip_address","reference":false,"variadic":false,"type":"string","optional":true,"default":{"value":""}}],"return":"bool"},"Net_Ping::strip_ip_address":{"public":true,"static":false,"parameters":[{"name":"ip_address","reference":false,"variadic":false,"type":"string","optional":false,"default":null}],"return":"string"},"Ldap::__construct":{"public":true,"static":false,"parameters":[],"return":null},"Ldap::__destruct":{"public":true,"static":false,"parameters":[],"return":null},"Ldap::ErrorHandler":{"public":true,"static":false,"parameters":[{"name":"level","reference":false,"variadic":false,"type":"int","optional":false,"default":null},{"name":"message","reference":false,"variadic":false,"type":"string","optional":false,"default":null},{"name":"file","reference":false,"variadic":false,"type":"string","optional":false,"default":null},{"name":"line","reference":false,"variadic":false,"type":"int","optional":false,"default":null},{"name":"context","reference":false,"variadic":false,"type":"array","optional":true,"default":{"value":[]}}],"return":"bool"},"Ldap::SetLdapHandler":{"public":true,"static":false,"parameters":[],"return":"void"},"Ldap::RestoreCactiHandler":{"public":true,"static":false,"parameters":[],"return":"void"},"Ldap::RecordError":{"public":true,"static":false,"parameters":[{"name":"output","reference":false,"variadic":false,"type":"array","optional":false,"default":null},{"name":"section","reference":false,"variadic":false,"type":"string","optional":true,"default":{"value":"LDAP"}}],"return":"void"},"Ldap::Connect":{"public":true,"static":false,"parameters":[],"return":"array"},"Ldap::Authenticate":{"public":true,"static":false,"parameters":[],"return":"array"},"Ldap::GetMask":{"public":true,"static":false,"parameters":[],"return":"int"},"Ldap::ResolveGroupMemberships":{"public":true,"static":false,"parameters":[{"name":"username"Line truncated
+205
View File
@@ -0,0 +1,205 @@
/* Copyright (C) 2004-2026 The Cacti Group. GPL-2.0-or-later. */
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const vm = require('node:vm');
const root = path.resolve(__dirname, '../..');
function source(file) {
return fs.readFileSync(path.join(root, file), 'utf8');
}
function functionAt(text, start) {
assert.ok(start >= 0);
const body = text.indexOf('{', start);
let depth = 0;
for (let offset = body; offset < text.length; offset++) {
if (text[offset] === '{') depth++;
if (text[offset] === '}' && --depth === 0) return text.slice(start, offset + 1);
}
throw new Error('Unclosed production function');
}
function load(file, names, globals) {
const text = source(file);
const context = vm.createContext(globals);
for (const name of names) {
vm.runInContext(functionAt(text, text.indexOf(`function ${name}(`)), context);
}
return context;
}
test('fullscreen controls reconcile state after successful and rejected requests', async () => {
for (const fullscreen of [false, true]) {
for (const rejected of [false, true]) {
let entries = 0;
let exits = 0;
let updates = 0;
const result = () => rejected ? Promise.reject(new Error('Browser denied request')) : Promise.resolve();
const context = load('include/themes/midwinter/main.js', ['fullScreen'], {
document: {
fullscreenElement: fullscreen,
documentElement: { requestFullscreen: () => { entries++; return result(); } },
exitFullscreen: () => { exits++; return result(); },
},
fullScreenChangeHandler: () => { updates++; },
});
context.fullScreen({});
await new Promise(resolve => setImmediate(resolve));
assert.equal(entries, fullscreen ? 0 : 1);
assert.equal(exits, fullscreen ? 1 : 0);
assert.equal(updates, 1);
}
}
const context = load('include/themes/midwinter/main.js', ['fullScreen'], {
document: { fullscreenElement: true },
});
assert.doesNotThrow(() => context.fullScreen({}));
});
test('toast accepts primitive and boxed strings without leaking global state', () => {
const text = source('include/js/ui-notices.js');
const start = text.indexOf('function(options)');
let rendered;
const jquery = {
extend: (...options) => Object.assign({}, ...options),
toast: options => { rendered = options; return options; },
};
const toast = vm.runInNewContext(`(${functionAt(text, start)})`, { $: jquery, String });
for (const value of ['plain', new String('boxed'), { text: 'configured' }, undefined]) {
toast(value);
assert.equal(typeof rendered.text, 'string');
assert.equal(rendered.text, value === undefined ? 'No text was set, please check the usage of showToast' : typeof value === 'object' && !(value instanceof String) ? value.text : String(value));
}
});
test('realtime requests stop Pace for countdown, initial load and refresh', () => {
for (const action of ['countdown', 'initial', 'refresh']) {
let stops = 0;
let requested;
const jquery = selector => ({
val: () => ({ '#graph_start': '60', '#ds_step': '5', '#size': '50', '#local_graph_id': '42' })[selector],
is: () => false, width: () => 600, height: () => 400,
});
jquery.getJSON = url => { requested = url; return { done: () => ({ fail: () => {} }) }; };
const context = load('include/realtime.js', ['imageOptionsChanged'], {
$: jquery, window: {}, Pace: { stop: () => { stops++; } },
count: 0, rtWidth: 0, rtHeight: 0, local_graph_id: null,
});
context.imageOptionsChanged(action);
assert.equal(stops, 1);
assert.match(requested, new RegExp(`action=${action}&`));
assert.match(requested, /local_graph_id=42&/);
}
});
test('Sunrise scroll indicator covers horizontal and vertical scrolling', () => {
const text = source('include/themes/sunrise/main.js');
const marker = ".rebind('scroll', ";
const start = text.indexOf(marker) + marker.length;
for (const x of [0, 10]) {
for (const y of [0, 10]) {
let color;
const callback = vm.runInNewContext(`(${functionAt(text, start)})`, {
$: selector => selector === '#navigation_right'
? { scrollLeft: () => x, scrollTop: () => y }
: { css: styles => { color = styles.color; } },
});
callback({});
assert.equal(color, x === 0 && y === 0 ? '' : '#93CEFF');
}
}
});
test('navigation membership handles the console, matching and absent links', () => {
const jquery = () => ({ find: () => ({ each: callback => {
for (const href of ['host.php', 'graphs.php']) callback.call({ href });
} }) });
const context = load('include/layout.js', ['userMenuNavigationExists'], {
$: value => typeof value === 'object' ? { attr: () => value.href } : jquery(),
basename: value => value.split('/').pop(),
});
for (const [url, expected] of [['index.php', true], ['/cacti/index.php', true], ['graphs.php', true], ['missing.php', false]]) {
assert.equal(context.userMenuNavigationExists(url), expected);
}
});
test('wide tables restore hidden cells even when their column indexes exceed nine', () => {
const headers = Array.from({ length: 12 }, (_, index) => ({
index, visible: [0, 1, 10, 11].includes(index),
locked: [0, 10].includes(index), checkbox: index === 11,
}));
const cells = headers.map((header) => ({ index: header.index, visible: header.visible }));
const table = { table: true };
function collection(nodes) {
return {
length: nodes.length,
get: () => nodes,
each: (callback) => {
nodes.forEach((node, index) => callback.call(node, index, node));
return collection(nodes);
},
find: (selector) => collection(selector === 'th' ? headers : selector === 'td' ? cells : selector === 'tr' ? [{}, {}] : []),
width: () => 300,
attr: () => 'table-fixture',
index: () => nodes[0].index,
css: (property) => property === 'display' ? (nodes[0].visible ? 'table-cell' : 'none') : undefined,
hasClass: (name) => name === 'noHide' ? nodes[0].locked : name === 'tableSubHeaderCheckbox' && nodes[0].checkbox,
is: (selector) => selector === ':visible' ? nodes[0].visible : !nodes[0].visible,
show: () => { nodes.forEach((node) => { node.visible = true; }); },
hide: () => { nodes.forEach((node) => { node.visible = false; }); },
};
}
const jquery = (value) => value && typeof value.each === 'function' ? value : collection(Array.isArray(value) ? value : [value]);
jquery.textMetrics = () => ({ width: 5 });
const context = load('include/layout.js', ['countHiddenCols', 'tuneTable'], { $: jquery, hScroll: false });
context.tuneTable(table, 500);
assert.equal(headers.every((header) => header.visible), true);
assert.equal(cells.every((cell) => cell.visible), true);
});
test('realtime shutdown restores snapshots populated by the layout click handler', () => {
const initial = '<img id="graph_42" alt="Traffic graph">';
let content = initial;
let filters = 0;
const link = {};
function jquery(selector) {
const chain = {
attr: () => 'graph_42_realtime',
html: (value) => {
if (selector === '#wrapper_42') {
if (value === undefined) return content;
content = value;
}
return chain;
},
trigger: () => chain, empty: () => chain, find: () => chain,
tooltip: () => chain, children: () => chain, on: () => chain,
zoom: () => chain, remove: () => chain, css: () => chain,
};
return chain;
}
const context = load('include/realtime.js', ['stopRealtime'], {
$: jquery, realtimeArray: [], keepRealtime: [], timeOffset: 0,
realtimeClickOn: 'Start', realtimeClickOff: 'Stop', urlPath: '',
setFilters: () => { filters++; }, tuneFilter: () => {}, realtimeGrapher: () => {}, shouldCaptureClick: () => true,
});
const layout = functionAt(source('include/layout.js'), source('include/layout.js').indexOf('function initializeGraphs('));
const marker = "$(this).rebind('click', function (event) {";
const scope = layout.indexOf("$('a[id$=\"_realtime\"]')");
assert.notEqual(scope, -1);
const start = layout.indexOf(marker, scope) + marker.indexOf('function (event)');
assert.ok(start > 0);
const click = vm.runInContext(`(${functionAt(layout, start)})`, context);
click.call(link, { preventDefault: () => {}, stopPropagation: () => {} });
assert.equal(context.realtimeArray[42], true);
assert.equal(context.keepRealtime[42], initial);
content = 'realtime frame';
context.stopRealtime();
assert.equal(content, initial);
assert.equal(context.realtimeArray[42], false);
assert.equal(filters, 2);
});