mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
* Add 'Deepness' theme to develop; midwinter billboard + deviceStatus hook Forward-ports the Deepness theme (1.2.x #8202 + #8205) to develop as one change: the full include/themes/deepness/ theme (emerald-dark :root palette, Paw layout/top-tab JS, full billboard.js 3.13 overload CSS, dark widget CSS), the themes allowlist entry, the i18n_themes name, the midwinter billboard overload rules, and the shared 'deviceStatus' hook class in get_colored_device_status() that the theme styles as status pills. Paper-plane is intentionally untouched (worked separately). The lib/html.php favicon/arrow tweaks from 1.2.x are omitted - develop handles those via get_md5_include_icon()/get_theme_paths() and the theme's own .actionsDropdownArrow CSS. * deepness: align main.js with Paw parent (drop stale host_id autocomplete & graphDrillDown, fix search-icon/handler leaks) * deepness: drop bundled billboard.css (obsolete per-theme file; central include/css/billboard.css + main.css .bb overloads cover it) * i18n: add 'Deepness' theme name to cacti.pot translation template * Deepness: apply php-cs-fixer formatting and refresh sink inventory baseline php-cs-fixer fixes the single-quote and // comment style in the deepness theme index.php/rrdtheme.php files, and the sink inventory baseline records the header_redirect sinks in the new deepness index.php files. * tests: stop the boost upgrade integration test leaking IN_CACTI_INSTALL BoostProcessTableUpgradeIntegrationTest defined IN_CACTI_INSTALL at file (collection) scope, so it was set for the whole shared pest process and made CactiErrorHandler() take its install-mode early-return. That broke CactiErrorHandlerSuppressionTest ('an enabled level is logged...' expected false, got true) in the pest coverage gate and the locked integration job. The upgrade files required here do not check the constant at load time (only lib/database.php does, at runtime inside its schema probes), so define it in beforeEach after the DB connects instead -- it is never set at collection time and, with no DB in CI, the test skips without defining it. Full pest suite: 2664 passed, 0 failed.
2020 lines
65 KiB
PHP
2020 lines
65 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
| |
|
|
| This program is free software; you can redistribute it and/or |
|
|
| modify it under the terms of the GNU General Public License |
|
|
| as published by the Free Software Foundation; either version 2 |
|
|
| of the License, or (at your option) any later version. |
|
|
| |
|
|
| This program is distributed in the hope that it will be useful, |
|
|
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
| GNU General Public License for more details. |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
| This code is designed, written, and maintained by the Cacti Group. See |
|
|
| about.php and/or the AUTHORS file for specific developer information. |
|
|
+-------------------------------------------------------------------------+
|
|
| http://www.cacti.net/ |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
|
|
require_once __DIR__ . '/CactiValidator.php';
|
|
|
|
/**
|
|
* Replaces all variables contained in $form_array with their actual values
|
|
*
|
|
* This function recursively processes the form array and replaces placeholders
|
|
* with corresponding values from the provided arguments. It supports up to three
|
|
* levels of argument replacement (arg1, arg2, arg3).
|
|
*
|
|
* @param array $form_array The form array to process. This array is passed by reference.
|
|
* @param mixed $arg1 Represents the |arg1:| variable (see include/global_form.php for more details)
|
|
* @param mixed $arg2 Represents the |arg2:| variable (see include/global_form.php for more details)
|
|
* @param mixed $arg3 Represents the |arg3:| variable (see include/global_form.php for more details)
|
|
* @param mixed $arg4 Represents the |arg4:| variable (see include/global_form.php for more details)
|
|
*
|
|
* @return array The processed form array with injected variables.
|
|
*/
|
|
function inject_form_variables(array &$form_array, mixed $arg1 = [], mixed $arg2 = [], mixed $arg3 = [], mixed $arg4 = []) : array {
|
|
$check_fields = ['id', 'value', 'array', 'friendly_name', 'description', 'sql', 'sql_print', 'form_id', 'items', 'tree_id'];
|
|
|
|
// loop through each available field
|
|
if (cacti_sizeof($form_array)) {
|
|
foreach ($form_array as $field_name => $field_array) {
|
|
// loop through each sub-field that we are going to check for variables
|
|
foreach ($check_fields as $field_to_check) {
|
|
if (isset($field_array[$field_to_check]) && is_array($form_array[$field_name][$field_to_check])) {
|
|
// if the field/sub-field combination is an array, resolve it recursively
|
|
$form_array[$field_name][$field_to_check] = inject_form_variables($form_array[$field_name][$field_to_check], $arg1);
|
|
} elseif (isset($field_array[$field_to_check]) && !is_array($field_array[$field_to_check])) {
|
|
$count = 0;
|
|
|
|
/* loop through the $field_to_check and replace up to three times
|
|
* for each arg1:arg2:arg3 variables.
|
|
*/
|
|
while (true) {
|
|
$matches = [];
|
|
|
|
// if (preg_match('/\|(arg[123]):([a-zA-Z0-9_]*)\|/', $form_array[$field_name][$field_to_check], $matches)) {
|
|
if (preg_match('/\|(arg[123]):([a-zA-Z0-9_]*)\|/', $field_array[$field_to_check], $matches)) {
|
|
$string = $field_array[$field_to_check];
|
|
|
|
$matches0 = $matches[0];
|
|
$matches1 = $matches[1];
|
|
$matches2 = $matches[2];
|
|
|
|
// an empty field name in the variable means don't treat this as an array
|
|
if ($matches2 == '') {
|
|
if (is_array(${$matches1})) {
|
|
// the existing value is already an array, leave it alone
|
|
$form_array[$field_name][$field_to_check] = ${$matches1};
|
|
} else {
|
|
// the existing value is probably a single variable
|
|
$form_array[$field_name][$field_to_check] = str_replace($matches0, ${$matches1}, $field_array[$field_to_check]);
|
|
}
|
|
} else {
|
|
/* copy the value down from the array/key specified in the variable
|
|
* replace up to three times for arg1:arg2:arg3 variables
|
|
*/
|
|
if (is_array(${$matches1})) {
|
|
$array = ${$matches1};
|
|
|
|
if (isset($array[$matches2]) && $array[$matches2] != '') {
|
|
$string = str_replace($matches0, $array[$matches2], $string);
|
|
} else {
|
|
$string = str_replace($matches0, '', $string);
|
|
}
|
|
}
|
|
|
|
// Double check to see if the replacement went as planned
|
|
$matches = [];
|
|
preg_match('/\|(arg[123]):([a-zA-Z0-9_]*)\|/', $string, $matches);
|
|
|
|
if (!cacti_sizeof($matches)) {
|
|
$form_array[$field_name][$field_to_check] = $string;
|
|
|
|
// No more arg[123]
|
|
break;
|
|
} else {
|
|
if ($field_to_check == 'sql') {
|
|
// Update the form value with the modified string
|
|
// Then update the field array string value to recheck
|
|
|
|
$form_array[$field_name][$field_to_check] = $string;
|
|
$field_array[$field_to_check] = $string;
|
|
} elseif (isset($form_array[$field_name]['default'])) {
|
|
// We did not find a match for this field value, use the default
|
|
|
|
$form_array[$field_name][$field_to_check] = $form_array[$field_name]['default'];
|
|
} else {
|
|
// We found no value, found no default, set to empty string
|
|
|
|
$form_array[$field_name][$field_to_check] = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if there are no more arg's, break. Since some arg's
|
|
* might not ever be replaced, continue counting till 3 as
|
|
* in the special case of 'sql' for example.
|
|
*/
|
|
$additional = preg_match('/\|(arg[123]):([a-zA-Z0-9_]*)\|/', $string);
|
|
|
|
if (empty($additional)) {
|
|
break;
|
|
}
|
|
|
|
if ($count >= 3) {
|
|
break;
|
|
} else {
|
|
$count++;
|
|
}
|
|
} else {
|
|
// No arg[123] found
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $form_array;
|
|
}
|
|
|
|
/**
|
|
* Generates a table row with alternating row colors and returns the current color.
|
|
*
|
|
* This function prints an HTML table row (`<tr>`) with a class that alternates between
|
|
* 'odd', 'even', and 'even-alternate' based on the provided row value. It also returns
|
|
* the current color used for the row.
|
|
*
|
|
* @param string $row_color1 The color for odd rows.
|
|
* @param string $row_color2 The color for even rows. If empty or 'E5E5E5', the class 'even' is used.
|
|
* @param int $row_value The current row number, used to determine if the row is odd or even.
|
|
* @param string $row_id Optional. The ID to assign to the row. If not provided, no ID is assigned.
|
|
*
|
|
* @return string The current color used for the row.
|
|
*/
|
|
function form_alternate_row_color(string $row_color1, string $row_color2, int $row_value, string $row_id = '') : string {
|
|
if ($row_value % 2 == 1) {
|
|
$class = 'odd';
|
|
$current_color = $row_color1;
|
|
} else {
|
|
if ($row_color2 == '' || $row_color2 == 'E5E5E5') {
|
|
$class = 'even';
|
|
} else {
|
|
$class = 'even-alternate';
|
|
}
|
|
$current_color = $row_color1;
|
|
}
|
|
|
|
if ($row_id != '') {
|
|
print "<tr class='$class selectable tableRow' id='$row_id'>\n";
|
|
} else {
|
|
print "<tr class='$class tableRow'>\n";
|
|
}
|
|
|
|
return $current_color;
|
|
}
|
|
|
|
/**
|
|
* Generates an HTML table row with alternating classes for styling.
|
|
*
|
|
* @param string $row_id The ID to assign to the table row. If not provided, no ID will be assigned.
|
|
* @param bool $light If true, the row will use the 'even-alternate' class for even rows.
|
|
* @param bool $disabled If true, the row will not be selectable.
|
|
*
|
|
* @return void
|
|
*/
|
|
function form_alternate_row(string $row_id = '', bool $light = false, bool $disabled = false) : void {
|
|
static $i = 1;
|
|
|
|
if ($i % 2 == 1) {
|
|
$class = 'odd';
|
|
} elseif ($light) {
|
|
$class = 'even-alternate';
|
|
} else {
|
|
$class = 'even';
|
|
}
|
|
|
|
$i++;
|
|
|
|
if ($row_id != '' && !$disabled && !str_starts_with($row_id, 'row_')) {
|
|
print "<tr class='$class selectable tableRow' id='$row_id'>\n";
|
|
} elseif (str_starts_with($row_id, 'row_')) {
|
|
print "<tr class='$class tableRow' id='$row_id'>\n";
|
|
} elseif ($row_id != '') {
|
|
print "<tr class='$class tableRow' id='$row_id'>\n";
|
|
} else {
|
|
print "<tr class='$class tableRow'>\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A wrapper to form_selectable_ecell that escapes the contents
|
|
*
|
|
* This function creates a selectable table cell with the provided contents,
|
|
* ensuring that the contents are properly escaped to prevent XSS attacks.
|
|
*
|
|
* @param mixed $contents The content to be displayed inside the cell.
|
|
* @param mixed $id The ID attribute for the cell.
|
|
* @param mixed $width The width of the cell. Default is an empty string.
|
|
* @param string $styleclass The style or class attribute for the cell. Default is an empty string.
|
|
* @param string $title The title attribute for the cell. Default is an empty string.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function form_selectable_ecell(mixed $contents, mixed $id, mixed $width = '', string $styleclass = '', string $title = '') : bool {
|
|
return form_selectable_cell(htmle($contents), $id, $width, $styleclass, $title);
|
|
}
|
|
|
|
/**
|
|
* Format's a table row such that it can be highlighted using cacti's js actions
|
|
*
|
|
* @param mixed $contents The content to be placed inside the table cell.
|
|
* @param mixed $id The ID attribute for the table cell (not used in the function).
|
|
* @param mixed $width The width of the table cell. Default is an empty string.
|
|
* @param string $styleclass The style or class attribute for the table cell.
|
|
* Default is an empty string. If it contains a colon (:),
|
|
* it is treated as a style attribute; otherwise, as a class attribute.
|
|
* @param string $title The tooltip text for the table cell. Default is an empty string.
|
|
*
|
|
* @return bool False if an error is encountered
|
|
*/
|
|
function form_selectable_cell(mixed $contents, mixed $id, mixed $width = '', string $styleclass = '', string $title = '') : bool {
|
|
global $tableCount;
|
|
|
|
static $tableColumns = null;
|
|
|
|
$table_id = form_get_table_id();
|
|
|
|
if (!isset($tableColumns[$table_id])) {
|
|
$tableColumns[$table_id] = json_decode(read_user_setting("visible_columns_{$table_id}{$tableCount[$table_id]}"), true);
|
|
}
|
|
|
|
static $col_num = null;
|
|
static $col_id = null;
|
|
static $logged = null;
|
|
|
|
if ($col_num === null) {
|
|
$col_num = 0;
|
|
$col_id = $id;
|
|
} elseif ($col_id != $id) {
|
|
$col_num = 0;
|
|
$col_id = $id;
|
|
} else {
|
|
$col_num++;
|
|
}
|
|
|
|
if (isset($tableColumns[$table_id]) && cacti_sizeof($tableColumns[$table_id])) {
|
|
$columns = array_keys($tableColumns[$table_id]);
|
|
|
|
// Check if the column is visible
|
|
if (isset($columns[$col_num])) {
|
|
if ($tableColumns[$table_id][$columns[$col_num]] !== true) {
|
|
return false;
|
|
}
|
|
} elseif (isset($columns["autocol$col_num"])) { // @phpstan-ignore-line
|
|
if ($tableColumns[$table_id][$columns["autocol$col_num"]] !== true) {
|
|
return false;
|
|
}
|
|
} elseif ($col_num < cacti_sizeof($tableColumns)) {
|
|
// We can have last item colspan > 1. So, only log if column count is larger
|
|
cacti_log("The table with the Table ID $table_id is not using form_selectable_cell() correctly");
|
|
$logged[$table_id] = true;
|
|
}
|
|
}
|
|
|
|
$output = '';
|
|
|
|
if ($styleclass != '') {
|
|
if (!str_contains($styleclass, ':')) {
|
|
$output = "class='nowrap " . $styleclass . "'";
|
|
|
|
if ($width != '') {
|
|
$output .= " style='width:$width;'";
|
|
}
|
|
} else {
|
|
$output = "class='nowrap' style='" . $styleclass;
|
|
|
|
if ($width != '') {
|
|
$output .= ";width:$width;";
|
|
}
|
|
$output .= "'";
|
|
}
|
|
} else {
|
|
$output = 'class="nowrap"';
|
|
|
|
if ($width != '') {
|
|
$output .= " style='width:$width;'";
|
|
}
|
|
}
|
|
|
|
if ($title != '') {
|
|
$wrapper = "<span class='cactiTooltipHint' style='padding:0px;margin:0px;' title='" . str_replace(['"', "'"], '', $title) . "'>" . $contents . '</span>';
|
|
} else {
|
|
$wrapper = $contents;
|
|
}
|
|
|
|
print "\t<td " . $output . '>' . $wrapper . "</td>\n";
|
|
|
|
return true;
|
|
}
|
|
|
|
function form_get_table_id(mixed $increment = false) : string {
|
|
static $table_count = 0;
|
|
|
|
if ($increment) {
|
|
$table_count++;
|
|
}
|
|
|
|
if (isset_request_var('action') && get_nfilter_request_var('action') != '' && isset_request_var('tab') && get_nfilter_request_var('tab') != '') {
|
|
return basename(get_current_page(), '.php') . ':' . $table_count . ':action-tab-' . get_nfilter_request_var('action') . '-' . get_nfilter_request_var('tab') . ':';
|
|
}
|
|
|
|
if (isset_request_var('action') && get_nfilter_request_var('action') != '') {
|
|
return basename(get_current_page(), '.php') . ':' . $table_count . ':action-' . get_nfilter_request_var('action') . ':';
|
|
}
|
|
|
|
if (isset_request_var('tab') && get_nfilter_request_var('tab') != '') {
|
|
return basename(get_current_page(), '.php') . ':tab-' . get_nfilter_request_var('tab') . ':';
|
|
} else {
|
|
return basename(get_current_page(), '.php') . ':' . $table_count;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format's a table row such that it can be highlighted using cacti's js actions
|
|
*
|
|
* @param mixed $contents The content to be placed inside the table cell.
|
|
* @param string $table_id The ID attribute for the table cell (not used in the function).
|
|
* @param string $columnid The width of the table cell. Default is an empty string.
|
|
* @param string $styleclass The style or class attribute for the table cell.
|
|
* Default is an empty string. If it contains a colon (:), it is treated as a style
|
|
* attribute; otherwise, as a class attribute.
|
|
* @param string $title The tooltip text for the table cell. Default is an empty string.
|
|
*
|
|
* @return bool False if errors are encountered
|
|
*/
|
|
function form_selectable_vcell(mixed $contents, string $table_id = '', string $columnid = '', string $styleclass = '', string $title = '') : bool {
|
|
global $tableCount;
|
|
|
|
static $tableColumns = null;
|
|
|
|
if ($table_id == '') {
|
|
$table_id = form_get_table_id();
|
|
}
|
|
|
|
if (!isset($tableColumns[$table_id])) {
|
|
$tableColumns[$table_id] = json_decode(read_user_setting("visible_columns_{$table_id}{$tableCount[$table_id]}"), true);
|
|
}
|
|
|
|
if (isset($tableColumns[$table_id]) && cacti_sizeof($tableColumns[$table_id])) {
|
|
if (!$tableColumns[$table_id][$columnid]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
$output = '';
|
|
$width = ''; // Width was undefined, adding this until we know what was intended
|
|
|
|
if ($styleclass != '') {
|
|
if (!str_contains($styleclass, ':')) {
|
|
$output = "class='nowrap " . $styleclass . "'";
|
|
|
|
if ($width != '') {
|
|
$output .= " style='width:$width;'";
|
|
}
|
|
} else {
|
|
$output = "class='nowrap' style='" . $styleclass;
|
|
|
|
if ($width != '') {
|
|
$output .= ";width:$width;";
|
|
}
|
|
$output .= "'";
|
|
}
|
|
} else {
|
|
$output = 'class="nowrap"';
|
|
|
|
if ($width != '') {
|
|
$output .= " style='width:$width;'";
|
|
}
|
|
}
|
|
|
|
if ($title != '') {
|
|
$wrapper = "<span class='cactiTooltipHint' style='padding:0px;margin:0px;' title='" . str_replace(['"', "'"], '', $title) . "'>" . $contents . '</span>';
|
|
} else {
|
|
$wrapper = $contents;
|
|
}
|
|
|
|
print "\t<td " . $output . '>' . $wrapper . "</td>\n";
|
|
|
|
return true;
|
|
}
|
|
|
|
function form_process_visible_display_text(string $table_id, array $display_text) : array {
|
|
global $tableCount;
|
|
|
|
static $tableColumns = null;
|
|
|
|
/**
|
|
* We have to support more than one table per page
|
|
* so maintain a column count per page and store settings
|
|
* accordingly.
|
|
*/
|
|
$table_id = form_get_table_id(true);
|
|
|
|
if (!isset($tableColumns[$table_id])) {
|
|
$tableCount[$table_id] = 0;
|
|
|
|
// this reset/clear functionality can be removed before production
|
|
if (isset_request_var('clear') || isset_request_var('reset')) {
|
|
db_execute_prepared('DELETE FROM settings_user
|
|
WHERE user_id = ? AND name = ?',
|
|
[$_SESSION[SESS_USER_ID], "visible_columns_{$table_id}{$tableCount[$table_id]}"]);
|
|
|
|
$tableColumns[$table_id] = [];
|
|
} else {
|
|
$tableColumns[$table_id] = json_decode(read_user_setting("visible_columns_{$table_id}{$tableCount[$table_id]}"), true);
|
|
}
|
|
} else {
|
|
$tableCount[$table_id]++;
|
|
|
|
// this reset/clear functionality can be removed before production
|
|
if (isset_request_var('clear') || isset_request_var('reset')) {
|
|
db_execute_prepared('DELETE FROM settings_user
|
|
WHERE user_id = ? AND name = ?',
|
|
[$_SESSION[SESS_USER_ID], "visible_columns_{$table_id}{$tableCount[$table_id]}"]);
|
|
|
|
$tableColumns[$table_id] = [];
|
|
} else {
|
|
$tableColumns[$table_id] = json_decode(read_user_setting("visible_columns_{$table_id}{$tableCount[$table_id]}"), true);
|
|
}
|
|
}
|
|
|
|
// reset if the developer is making changes to the page layout
|
|
if (cacti_sizeof($tableColumns[$table_id]) && cacti_sizeof($display_text) != cacti_sizeof($tableColumns[$table_id])) {
|
|
cacti_log('WARNING: Detected a change in base table topology', false, 'DEVELOP', POLLER_VERBOSITY_MEDIUM);
|
|
|
|
$tableColumns[$table_id] = [];
|
|
}
|
|
|
|
if (isset_request_var('columns_add')) {
|
|
$columns = get_nfilter_request_var('columns_add');
|
|
|
|
if (is_array($columns)) {
|
|
foreach ($columns as $column) {
|
|
if (isset($tableCount[$table_id][$column])) {
|
|
$tableColumns[$table_id][$column] = true;
|
|
}
|
|
}
|
|
} else {
|
|
$tableColumns[$table_id][$columns] = true;
|
|
}
|
|
}
|
|
|
|
if (isset_request_var('columns_remove')) {
|
|
$columns = get_nfilter_request_var('columns_remove');
|
|
|
|
if (is_array($columns)) {
|
|
foreach ($columns as $column) {
|
|
if (isset($tableCount[$table_id][$column])) {
|
|
$tableColumns[$table_id][$column] = false;
|
|
}
|
|
}
|
|
} else {
|
|
$tableColumns[$table_id][$columns] = false;
|
|
}
|
|
}
|
|
|
|
if (!cacti_sizeof($tableColumns[$table_id])) {
|
|
$initialize = true;
|
|
} else {
|
|
$initialize = false;
|
|
}
|
|
|
|
$return_array = [];
|
|
$coldata = [];
|
|
|
|
if (cacti_sizeof($display_text)) {
|
|
foreach ($display_text as $id => $column) {
|
|
// Convert the array to a standard array
|
|
if (is_numeric($id)) {
|
|
$id = "autocol$id";
|
|
|
|
if (isset($column['display'])) {
|
|
$return_array[$id] = $column;
|
|
} else {
|
|
if (is_array($column)) {
|
|
$return_array[$id]['display'] = $column[0];
|
|
|
|
if (isset($column[1])) {
|
|
$return_array[$id]['sort'] = $column[1];
|
|
}
|
|
} else {
|
|
$return_array[$id]['display'] = $column;
|
|
}
|
|
}
|
|
} elseif (isset($column['display'])) {
|
|
$return_array[$id] = $column;
|
|
} else {
|
|
if (is_array($column)) {
|
|
$return_array[$id]['display'] = $column[0];
|
|
|
|
if (isset($column[1])) {
|
|
$return_array[$id]['sort'] = $column[1];
|
|
}
|
|
} else {
|
|
$return_array[$id]['display'] = $column;
|
|
}
|
|
}
|
|
|
|
if (isset($tableColumns[$table_id][$id]) && $tableColumns[$table_id][$id] == true) {
|
|
$return_array[$id]['visible'] = true;
|
|
$coldata[$id] = true;
|
|
} elseif (isset($column['nohide']) && $column['nohide'] === true) {
|
|
$return_array[$id]['visible'] = true;
|
|
$coldata[$id] = true;
|
|
} elseif ($initialize) {
|
|
if (isset($column['default'])) {
|
|
if ($column['default'] === true) {
|
|
$return_array[$id]['visible'] = true;
|
|
$coldata[$id] = true;
|
|
} else {
|
|
$return_array[$id]['visible'] = false;
|
|
$coldata[$id] = false;
|
|
}
|
|
} else {
|
|
$return_array[$id]['visible'] = true;
|
|
$coldata[$id] = true;
|
|
}
|
|
} else {
|
|
$return_array[$id]['visible'] = false;
|
|
$coldata[$id] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($initialize) {
|
|
set_user_setting("visible_columns_{$table_id}{$tableCount[$table_id]}", json_encode($coldata));
|
|
}
|
|
|
|
// cacti_log(json_encode($return_array));
|
|
|
|
return $return_array;
|
|
}
|
|
|
|
/**
|
|
* Format's a tables checkbox form element so that the cacti js actions work on it
|
|
*
|
|
* @param string $title The title attribute for the checkbox, used for accessibility.
|
|
* @param mixed $id The unique identifier for the checkbox input element.
|
|
* @param bool $disabled Whether the checkbox should be disabled. Default is false.
|
|
* @param bool $checked Whether the checkbox should be checked. Default is false.
|
|
*
|
|
* @return void
|
|
*/
|
|
function form_checkbox_cell(string $title, mixed $id, bool $disabled = false, bool $checked = false) : void {
|
|
print "\t<td class='checkbox' style='width:1%;'>\n";
|
|
print "\t\t<input type='checkbox' title='" . htmle($title) . "' class='checkbox" . ($disabled ? ' disabled' : '') . "' " . ($disabled ? " disabled='disabled'" : '') . ($checked ? " checked='checked'" : '') . " id='chk_" . $id . "' name='chk_" . $id . "'><label class='formCheckboxLabel' for='chk_" . $id . "'></label>\n";
|
|
print "\t</td>\n";
|
|
}
|
|
|
|
/**
|
|
* Ends a table row that is started with form_alternate_row
|
|
*
|
|
* @return void
|
|
*/
|
|
function form_end_row() : void {
|
|
print "</tr>\n";
|
|
}
|
|
|
|
/**
|
|
* Returns the boolean equivalent of an HTML checkbox value
|
|
*
|
|
* This function checks if the given string is equal to 'on' and returns true if it is,
|
|
* otherwise it returns false.
|
|
*
|
|
* @param string $html_boolean The string representation of a boolean value.
|
|
*
|
|
* @return bool Returns true if the input string is 'on', otherwise false.
|
|
*/
|
|
function html_boolean(string $html_boolean) : bool {
|
|
return ($html_boolean == 'on');
|
|
}
|
|
|
|
/**
|
|
* Returns the natural language equivalent of an HTML checkbox value
|
|
*
|
|
* This function takes an HTML boolean value (typically 'on' or 'off') and
|
|
* returns a user-friendly string indicating whether the value is selected or not.
|
|
*
|
|
* @param string $html_boolean The HTML boolean value to convert. Expected values are 'on' or 'off'.
|
|
*
|
|
* @return string Returns 'Selected' if the input is 'on', otherwise returns 'Not Selected'.
|
|
*/
|
|
function html_boolean_friendly(string $html_boolean) : string {
|
|
if ($html_boolean == 'on') {
|
|
return __('Selected');
|
|
} else {
|
|
return __('Not Selected');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finds the proper CSS padding to apply based on the current client browser in use
|
|
*
|
|
* This function currently returns an empty string, indicating no specific style is applied.
|
|
*
|
|
* @return string An empty string representing the checkbox style.
|
|
*/
|
|
function get_checkbox_style() : string {
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Sets the required 'action' request variable
|
|
*
|
|
* @param string $default The default action is not set
|
|
*
|
|
* @return void
|
|
*/
|
|
function set_default_action(string $default = '') : void {
|
|
if (!isset_request_var('action')) {
|
|
set_request_var('action', $default);
|
|
} elseif (is_array(get_nfilter_request_var('action'))) {
|
|
if (read_config_option('log_validation') == 'on') {
|
|
cacti_log('WARNING: Request variable \'action\' was passed as array in ' . $_SERVER['SCRIPT_NAME'] . '.', false, 'WEBUI');
|
|
}
|
|
|
|
set_request_var('action', $_REQUEST['action'][0]);
|
|
} else {
|
|
set_request_var('action', $_REQUEST['action']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* alias of unset_request_var
|
|
*
|
|
* @param string $variable
|
|
*
|
|
* @return void
|
|
*/
|
|
function unsrv(string $variable) : void {
|
|
unset_request_var($variable);
|
|
}
|
|
|
|
/**
|
|
* Unsets the request variable.
|
|
*
|
|
* @param string $variable
|
|
*
|
|
* @return void
|
|
*/
|
|
function unset_request_var(string $variable) : void {
|
|
global $_CACTI_REQUEST;
|
|
|
|
if (isset($_CACTI_REQUEST[$variable])) {
|
|
unset($_CACTI_REQUEST[$variable]);
|
|
}
|
|
|
|
if (isset($_REQUEST[$variable])) {
|
|
unset($_REQUEST[$variable]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* alias of isset_request_var()
|
|
*
|
|
* @param string $variable
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isrv(string $variable) : bool {
|
|
return isset_request_var($variable);
|
|
}
|
|
|
|
/**
|
|
* checks to see if the $_REQUEST variable is set.
|
|
*
|
|
* @param string $variable
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isset_request_var(string $variable) : bool {
|
|
return isset($_REQUEST[$variable]);
|
|
}
|
|
|
|
/**
|
|
* alias of isempty_request_var()
|
|
*
|
|
* @param string $variable
|
|
*
|
|
* @return bool
|
|
*/
|
|
function ierv(string $variable) : bool {
|
|
return isempty_request_var($variable);
|
|
}
|
|
|
|
/**
|
|
* checks to see if the $_REQUEST variableis empty.
|
|
*
|
|
* @param string $variable
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isempty_request_var(string $variable) : bool {
|
|
if (isset_request_var($variable)) {
|
|
$value = $_REQUEST[$variable];
|
|
|
|
if (!empty($value)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* alias of set_request_var()
|
|
*
|
|
* @param string $variable The variable to set
|
|
* @param mixed $value The value to set the variable to
|
|
*
|
|
* @return void
|
|
*/
|
|
function srv(string $variable, mixed $value) : void {
|
|
set_request_var($variable, $value);
|
|
}
|
|
|
|
/**
|
|
* Sets a given $_REQUEST variable and Cacti global.
|
|
*
|
|
* @param string $variable The variable to set
|
|
* @param mixed $value The value to set the variable to
|
|
*
|
|
* @return void
|
|
*/
|
|
function set_request_var(string $variable, mixed $value) : void {
|
|
global $_CACTI_REQUEST;
|
|
|
|
$_CACTI_REQUEST[$variable] = $value;
|
|
$_REQUEST[$variable] = $value;
|
|
$_POST[$variable] = $value;
|
|
$_GET[$variable] = $value;
|
|
}
|
|
|
|
/**
|
|
* returns the current value of a PHP $_REQUEST variable, optionally
|
|
* returning a default value if the request variable does not exist. When Cacti
|
|
* has 'log_validation' set on, it will log all instances where a request variable
|
|
* has not first been filtered.
|
|
*
|
|
* @param string $name the name of the request variable. this should be a
|
|
* valid key in the $_REQUEST array
|
|
* @param mixed $default the value to return if the specified name does not
|
|
* exist in the $_REQUEST array
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function get_request_var(string $name, mixed $default = '') : mixed {
|
|
global $_CACTI_REQUEST;
|
|
|
|
$log_validation = read_config_option('log_validation');
|
|
|
|
if (isset($_CACTI_REQUEST[$name])) {
|
|
return $_CACTI_REQUEST[$name];
|
|
}
|
|
|
|
if (isset_request_var($name)) {
|
|
if ($log_validation == 'on') {
|
|
html_log_input_error($name);
|
|
}
|
|
|
|
set_request_var($name, $_REQUEST[$name]);
|
|
|
|
return $_REQUEST[$name];
|
|
} else {
|
|
return $default;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* alias of get_request_var()
|
|
*
|
|
* @deprecated v1.0
|
|
*
|
|
* @param string $name the name of the request variable. this should be a
|
|
* valid key in the $_REQUEST array
|
|
* @param mixed $default the value to return if the specified name does not
|
|
* exist in the $_REQUEST array
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function get_request_var_request(string $name, mixed $default = '') : mixed {
|
|
return get_request_var($name, $default);
|
|
}
|
|
|
|
/**
|
|
* alias of get_request_var()
|
|
*
|
|
* @param string $name the name of the request variable. this should be a
|
|
* valid key in the $_REQUEST array
|
|
* @param mixed $default the value to return if the specified name does not
|
|
* exist in the $_REQUEST array
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function grv(string $name, mixed $default = '') : mixed {
|
|
return get_request_var($name, $default);
|
|
}
|
|
|
|
/**
|
|
* alias of get_filter_request_var()
|
|
*
|
|
* @param string $name the name of the request variable. this should be a
|
|
* valid key in the $_REQUEST array
|
|
* @param int $filter the filter mode to use
|
|
* @param array $options used to pass to filter_var function or to hold the
|
|
* default value to be returned
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function gfrv(string $name, int $filter = FILTER_VALIDATE_INT, array $options = []) : mixed {
|
|
return get_filter_request_var($name, $filter, $options);
|
|
}
|
|
|
|
/**
|
|
* returns the current value of a PHP $_REQUEST variable and also
|
|
* sanitizing the value using the filter. It will also optionally
|
|
* return a default value if the request variable does not exist
|
|
*
|
|
* @param string $name the name of the request variable. this should be a
|
|
* valid key in the $_REQUEST array
|
|
* @param int $filter the filter mode to use
|
|
* @param array $options used to pass to filter_var function or to hold the
|
|
* default value to be returned
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function get_filter_request_var(string $name, int $filter = FILTER_VALIDATE_INT, array $options = []) : mixed {
|
|
$custom_error = 'Unknown Error';
|
|
|
|
if (isset_request_var($name)) {
|
|
if (isempty_request_var($name)) {
|
|
set_request_var($name, get_nfilter_request_var($name));
|
|
|
|
return get_request_var($name);
|
|
}
|
|
|
|
if (get_nfilter_request_var($name) == 'undefined') {
|
|
if (isset($options['default'])) {
|
|
set_request_var($name, $options['default']);
|
|
|
|
return $options['default'];
|
|
} else {
|
|
set_request_var($name, '');
|
|
|
|
return '';
|
|
}
|
|
} else {
|
|
if (get_nfilter_request_var($name) == '0') {
|
|
$value = '0';
|
|
} elseif (get_nfilter_request_var($name) == 'undefined') {
|
|
if (isset($options['default'])) {
|
|
$value = $options['default'];
|
|
} else {
|
|
$value = '';
|
|
}
|
|
} elseif ($filter == FILTER_VALIDATE_IS_REGEX) {
|
|
if (is_base64_encoded($_REQUEST[$name])) {
|
|
$_REQUEST[$name] = mb_convert_encoding(base64_decode($_REQUEST[$name], true), 'UTF-8');
|
|
}
|
|
|
|
$valid = validate_is_regex($_REQUEST[$name]);
|
|
|
|
if ($valid === true) {
|
|
$value = $_REQUEST[$name];
|
|
} else {
|
|
$value = false;
|
|
$custom_error = $valid;
|
|
}
|
|
} elseif ($filter == FILTER_VALIDATE_IS_NUMERIC_ARRAY) {
|
|
$valid = true;
|
|
|
|
if (is_array($_REQUEST[$name])) {
|
|
foreach ($_REQUEST[$name] as $number) {
|
|
if (!is_numeric($number)) {
|
|
$valid = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
$valid = false;
|
|
}
|
|
|
|
if ($valid == true) {
|
|
$value = $_REQUEST[$name];
|
|
} else {
|
|
$value = false;
|
|
}
|
|
} elseif ($filter == FILTER_VALIDATE_IS_NUMERIC_LIST) {
|
|
$valid = true;
|
|
$values = preg_split('/,/', $_REQUEST[$name], -1, PREG_SPLIT_NO_EMPTY);
|
|
|
|
foreach (($values ?: []) as $number) {
|
|
if (!is_numeric($number)) {
|
|
$valid = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($valid == true) {
|
|
$value = $_REQUEST[$name];
|
|
} else {
|
|
$value = false;
|
|
}
|
|
} elseif (!cacti_sizeof($options)) {
|
|
$value = filter_var($_REQUEST[$name], $filter);
|
|
} else {
|
|
$value = filter_var($_REQUEST[$name], $filter, $options);
|
|
}
|
|
}
|
|
|
|
if ($value === null && $options['default'] === null) {
|
|
$value = '';
|
|
}
|
|
|
|
if ($value === false) {
|
|
if ($filter == FILTER_VALIDATE_IS_REGEX) {
|
|
raise_message('custom', __esc('The regular expression "%s" is not valid. Error is %s', (string) get_nfilter_request_var($name), (string) $custom_error), MESSAGE_LEVEL_ERROR);
|
|
set_request_var($name, '');
|
|
} else {
|
|
die_html_input_error($name, get_nfilter_request_var($name));
|
|
}
|
|
} else {
|
|
set_request_var($name, $value);
|
|
|
|
return $value;
|
|
}
|
|
} else {
|
|
if (isset($options['default'])) {
|
|
set_request_var($name, $options['default']);
|
|
|
|
return $options['default'];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* alias of get_nfilter_request_var()
|
|
*
|
|
* @param string $name the name of the request variable. this should be a
|
|
* valid key in the $_REQUEST array
|
|
* @param mixed $default the value to return if the specified name does not
|
|
* exist in the $_REQUEST array
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function gnrv(string $name, mixed $default = '') : mixed {
|
|
return get_nfilter_request_var($name, $default);
|
|
}
|
|
|
|
/**
|
|
* returns the current value of a PHP $_REQUEST variable, optionally
|
|
* returning a default value if the request variable does not exist,
|
|
* but without using any of the filtering checks.
|
|
*
|
|
* This should only be used when the request variable has already been
|
|
* vetted via other filer request var functoins
|
|
*
|
|
* @param string $name the name of the request variable. this should be a
|
|
* valid key in the $_REQUEST array
|
|
* @param mixed $default the value to return if the specified name does not
|
|
* exist in the $_REQUEST array
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function get_nfilter_request_var(string $name, mixed $default = '') : mixed {
|
|
global $_CACTI_REQUEST;
|
|
|
|
if (isset($_CACTI_REQUEST[$name])) {
|
|
return $_CACTI_REQUEST[$name];
|
|
}
|
|
|
|
if (isset($_REQUEST[$name])) {
|
|
return $_REQUEST[$name];
|
|
} else {
|
|
return $default;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* alias of get_nfilteR_request_var
|
|
*
|
|
* @deprecated v1.0
|
|
*
|
|
* @param string $name the name of the request variable. this should be a
|
|
* valid key in the $_REQUEST array
|
|
* @param mixed $default the value to return if the specified name does not
|
|
* exist in the $_REQUEST array
|
|
*
|
|
* @return mixed
|
|
*/
|
|
function get_request_var_post(string $name, mixed $default = '') : mixed {
|
|
return get_nfilter_request_var($name, $default);
|
|
}
|
|
|
|
/**
|
|
* validate, sanitize, and store request variables into the
|
|
* custom $_CACTI_REQUEST and desired session variables for
|
|
* Cacti filtering.
|
|
*
|
|
* @param array $filters - An array keyed with the filter methods.
|
|
* @param string $sess_prefix - A string to use to prefix the session
|
|
* variable.
|
|
*
|
|
* Valid filter include those from PHP filter_var() function syntax.
|
|
* The format of the array is:
|
|
* array(
|
|
* 'varA' => array(
|
|
* 'filter' => value,
|
|
* 'pageset' => true, (optional)
|
|
* 'session' => sess_name, (optional)
|
|
* 'options' => mixed,
|
|
* 'default' => value),
|
|
* 'varB' => array(
|
|
* 'filter' => value,
|
|
* 'pageset' => true, (optional)
|
|
* 'session' => sess_name, (optional)
|
|
* 'options' => mixed,
|
|
* 'default' => value),
|
|
* ...
|
|
* );
|
|
*
|
|
* The 'pageset' attribute is optional, and when set, any changes
|
|
* between what the page returns and what is set in the session
|
|
* result in the page number being returned to 1.
|
|
*
|
|
* The 'session' attribute is also optional, and when set, all
|
|
* changes will be stored to the session variable defined and
|
|
* not to session_prefix . '_' . $variable as the default. This
|
|
* allows for the concept of global session variables such as
|
|
* 'sess_default_rows'.
|
|
*
|
|
* Validation 'filter' follow PHP conventions including:
|
|
*
|
|
* FILTER_VALIDATE_BOOLEAN - Validate that the variable is boolean
|
|
* FILTER_VALIDATE_EMAIL - Validate that the variable is an email
|
|
* FILTER_VALIDATE_FLOAT - Validate that the variable is a float
|
|
* FILTER_VALIDATE_INT - Validate that the variable is an integer
|
|
* FILTER_VALIDATE_IP - Validate that the variable is an IP address
|
|
* FILTER_VALIDATE_MAC - Validate that the variable is a MAC Address
|
|
* FILTER_VALIDATE_REGEXP - Validate against a REGEX
|
|
* FILTER_VALIDATE_URL - Validate that the variable is a valid URL
|
|
* FILTER_VALIDATE_IS_REGEX - Validate if a filter variable is a valid regex
|
|
* FILTER_VALIDATE_IS_NUMERIC_ARRAY - Validate if a filter variable is a numeric array
|
|
* FILTER_VALIDATE_IS_NUMERIC_LIST - Validate if a filter variable is a comma delimited list of numbers
|
|
*
|
|
* Sanitization 'filters' follow PHP conventions including:
|
|
*
|
|
* FILTER_SANITIZE_EMAIL - Sanitize the email address
|
|
* FILTER_SANITIZE_ENCODED - URL-encode string
|
|
* FILTER_SANITIZE_MAGIC_QUOTES - Apply addslashes()
|
|
* FILTER_SANITIZE_NUMBER_FLOAT - Remove all non float values
|
|
* FILTER_SANITIZE_NUMBER_INT - Remove everything non int
|
|
* FILTER_SANITIZE_SPECIAL_CHARS - Escape special chars
|
|
* FILTER_SANITIZE_FULL_SPECIAL_CHARS - Equivalent to htmlspecialchars adding ENT_QUOTES
|
|
* FILTER_SANITIZE_STRING - Strip tags, optionally strip or encode special chars
|
|
* FILTER_SANITIZE_URL - Remove all characters except letters, digits, etc.
|
|
* FILTER_UNSAFE_RAW - Nothing and optional strip or encode
|
|
*
|
|
* @return void
|
|
*/
|
|
function validate_store_request_vars(array $filters, string $sess_prefix = '') : void {
|
|
$changed = 0;
|
|
$custom_error = '';
|
|
$session_variable = 'sess_fallback';
|
|
|
|
if (cacti_sizeof($filters)) {
|
|
foreach ($filters as $variable => $options) {
|
|
// Establish the session variable first
|
|
if ($sess_prefix != '') {
|
|
if (isset($options['session'])) {
|
|
$session_variable = $options['session'];
|
|
} elseif ($variable != 'rows') {
|
|
$session_variable = $sess_prefix . '_' . $variable;
|
|
} else {
|
|
$session_variable = 'sess_default_rows';
|
|
}
|
|
|
|
// Check for special cases 'clear' and 'reset'
|
|
if (isset_request_var('clear')) {
|
|
kill_session_var($session_variable);
|
|
unset_request_var($variable);
|
|
} elseif (isset_request_var('reset')) {
|
|
kill_session_var($session_variable);
|
|
} elseif (isset($options['pageset']) && $options['pageset'] == true) {
|
|
$changed += check_changed($variable, (string) $session_variable);
|
|
}
|
|
}
|
|
|
|
if (!isset_request_var($variable)) {
|
|
if ($sess_prefix != '' && isset($_SESSION[$session_variable])) {
|
|
set_request_var($variable, $_SESSION[$session_variable]);
|
|
} elseif (isset($options['default'])) {
|
|
set_request_var($variable, $options['default']);
|
|
} else {
|
|
cacti_log("WARNING: Filter Variable: $variable, Must have a default and none is set", false, 'FILTER');
|
|
cacti_debug_backtrace('FILTER');
|
|
set_request_var($variable, '');
|
|
}
|
|
} else {
|
|
if (get_nfilter_request_var($variable) == '0') {
|
|
$value = '0';
|
|
} elseif (get_nfilter_request_var($variable) == 'undefined') {
|
|
if (isset($options['default'])) {
|
|
$value = $options['default'];
|
|
} else {
|
|
$value = '';
|
|
}
|
|
} elseif (isempty_request_var($variable)) {
|
|
$value = '';
|
|
} elseif ($options['filter'] == FILTER_VALIDATE_IS_REGEX) {
|
|
if (is_base64_encoded($_REQUEST[$variable])) {
|
|
$_REQUEST[$variable] = mb_convert_encoding(base64_decode($_REQUEST[$variable], true), 'UTF-8');
|
|
}
|
|
$valid = validate_is_regex($_REQUEST[$variable]);
|
|
|
|
if ($valid === true) {
|
|
$value = $_REQUEST[$variable];
|
|
} else {
|
|
$value = false;
|
|
$custom_error = $valid;
|
|
}
|
|
} elseif ($options['filter'] == FILTER_VALIDATE_IS_NUMERIC_ARRAY) {
|
|
$valid = true;
|
|
|
|
if (is_array($_REQUEST[$variable])) {
|
|
foreach ($_REQUEST[$variable] as $number) {
|
|
if (!is_numeric($number)) {
|
|
$valid = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
$valid = false;
|
|
}
|
|
|
|
if ($valid == true) {
|
|
$value = $_REQUEST[$variable];
|
|
} else {
|
|
$value = false;
|
|
}
|
|
} elseif ($options['filter'] == FILTER_VALIDATE_IS_NUMERIC_LIST) {
|
|
$valid = true;
|
|
$values = preg_split('/,/', $_REQUEST[$variable], -1, PREG_SPLIT_NO_EMPTY);
|
|
|
|
foreach (($values ?: []) as $number) {
|
|
if (!is_numeric($number)) {
|
|
$valid = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($valid == true) {
|
|
$value = $_REQUEST[$variable];
|
|
} else {
|
|
$value = false;
|
|
}
|
|
} elseif (!isset($options['options'])) {
|
|
$value = filter_var($_REQUEST[$variable], $options['filter']);
|
|
} else {
|
|
// Handle FILTER_VALIDATE_REGEXP specially to ensure proper delimiters
|
|
if ($options['filter'] == FILTER_VALIDATE_REGEXP && isset($options['options']['regexp'])) {
|
|
$regex = $options['options']['regexp'];
|
|
|
|
// Only add delimiters if they're not already present
|
|
if (!preg_match('/^\/.*\/[imsuxADJUX]*$/', $regex)) {
|
|
$options['options']['regexp'] = '/' . $regex . '/';
|
|
}
|
|
}
|
|
|
|
// Special handling for graph_template_id to allow -1
|
|
if ($variable === 'graph_template_id' && get_nfilter_request_var($variable) == '-1') {
|
|
$value = '-1';
|
|
} else {
|
|
$value = filter_var($_REQUEST[$variable], $options['filter'], $options['options']);
|
|
}
|
|
}
|
|
|
|
if ($value === false) {
|
|
if ($options['filter'] == FILTER_VALIDATE_IS_REGEX) {
|
|
raise_message('custom', __('The regular expression "%s" is not valid. Error is %s',
|
|
htmle((string) get_nfilter_request_var($variable)), htmle((string) $custom_error)), MESSAGE_LEVEL_ERROR);
|
|
set_request_var($variable, '');
|
|
} else {
|
|
die_html_input_error($variable, get_nfilter_request_var($variable), htmle((string) $custom_error));
|
|
}
|
|
} else {
|
|
set_request_var($variable, $value);
|
|
}
|
|
}
|
|
|
|
if ($sess_prefix != '') {
|
|
if (isset_request_var($variable)) {
|
|
$_SESSION[$session_variable] = get_request_var($variable);
|
|
} elseif (isset($_SESSION[$session_variable])) {
|
|
set_request_var($variable, $_SESSION[$session_variable]);
|
|
}
|
|
}
|
|
}
|
|
|
|
update_order_string();
|
|
}
|
|
|
|
if ($changed) {
|
|
set_request_var('page', 1);
|
|
set_request_var('changed', 1);
|
|
$_SESSION[$sess_prefix . '_page'] = 1;
|
|
} elseif (!isset_request_var('page') && isset($_SESSION[$sess_prefix . '_page'])) {
|
|
set_request_var('page', $_SESSION[$sess_prefix . '_page']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a sort string for standard Cacti tables
|
|
*
|
|
* This function manages the sorting order for data displayed on a page. It can update the order string
|
|
* in place or based on request variables. The function handles sorting by different columns, including
|
|
* special handling for IP addresses.
|
|
*
|
|
* @param bool $inplace If true, updates the order string in place using the current session data.
|
|
*
|
|
* @return void
|
|
*/
|
|
function update_order_string(bool $inplace = false) : void {
|
|
$page = get_order_string_page(false);
|
|
|
|
$order = '';
|
|
|
|
if (!str_contains(get_request_var('sort_column'), '(') && !str_contains(get_request_var('sort_column'), '`')) {
|
|
$del = '`';
|
|
} else {
|
|
$del = '';
|
|
}
|
|
|
|
$database = get_mysql_info();
|
|
$natural = false;
|
|
|
|
if ($database['database'] == 'MariaDB') {
|
|
if (cacti_version_compare($database['version'], '10.7', '>')) {
|
|
$natural = true;
|
|
}
|
|
}
|
|
|
|
if ($inplace) {
|
|
$_SESSION['sort_string'][$page] = 'ORDER BY ';
|
|
|
|
foreach ($_SESSION['sort_data'][$page] as $column => $direction) {
|
|
/* Re-validate keys at read time; write-time sanitization may have
|
|
* been bypassed by older session data stored before this fix. */
|
|
$column = sanitize_sql_column($column, '');
|
|
|
|
if ($column === '') {
|
|
continue;
|
|
}
|
|
|
|
$direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
|
|
|
|
if ($column == 'ip' || $column == 'ip_address') {
|
|
$order .= ($order != '' ? ', ' : '') . 'INET_ATON(' . $column . ') ' . $direction;
|
|
} elseif ($column == 'hostname' && $natural) {
|
|
$order .= ($order != '' ? ', ' : '') . 'NATURAL_SORT_KEY(' . $column . ') ' . $direction;
|
|
} else {
|
|
$order .= ($order != '' ? ', ' : '') . $column . ' ' . $direction;
|
|
}
|
|
}
|
|
|
|
$_SESSION['sort_string'][$page] .= $order;
|
|
} else {
|
|
if (isset_request_var('clear')) {
|
|
unset($_SESSION['sort_data'][$page]);
|
|
unset($_SESSION['sort_string'][$page]);
|
|
} elseif (isset_request_var('add') && get_nfilter_request_var('add') == 'reset') {
|
|
unset($_SESSION['sort_data'][$page]);
|
|
unset($_SESSION['sort_string'][$page]);
|
|
|
|
$safe_direction = strtoupper(get_request_var('sort_direction')) === 'DESC' ? 'DESC' : 'ASC';
|
|
$sort_column = sanitize_sql_column(get_request_var('sort_column'), '');
|
|
|
|
if ($sort_column === '') {
|
|
return;
|
|
}
|
|
|
|
$_SESSION['sort_data'][$page][$sort_column] = $safe_direction;
|
|
|
|
$column = $sort_column;
|
|
$direction = $safe_direction;
|
|
|
|
if (!str_contains($sort_column, '(') && !str_contains($sort_column, '`')) {
|
|
$del = '`';
|
|
} else {
|
|
$del = '';
|
|
}
|
|
|
|
if ($column == 'ip' || $column == 'ip_address') {
|
|
$_SESSION['sort_string'][$page] = 'ORDER BY INET_ATON(' . $column . ') ' . $direction;
|
|
} elseif ($column == 'hostname' && $natural) {
|
|
$_SESSION['sort_string'][$page] = 'ORDER BY NATURAL_SORT_KEY(' . $del . implode($del . '.' . $del, explode('.', $sort_column)) . $del . ') ' . $direction;
|
|
} else {
|
|
$_SESSION['sort_string'][$page] = 'ORDER BY ' . $del . implode($del . '.' . $del, explode('.', $sort_column)) . $del . ' ' . $direction;
|
|
}
|
|
} elseif (isset_request_var('sort_column')) {
|
|
if (isset_request_var('reset')) {
|
|
unset($_SESSION['sort_data'][$page]);
|
|
unset($_SESSION['sort_string'][$page]);
|
|
}
|
|
|
|
$sort_column = sanitize_sql_column(get_request_var('sort_column'), '');
|
|
|
|
if ($sort_column === '') {
|
|
return;
|
|
}
|
|
|
|
$_SESSION['sort_data'][$page][$sort_column] = strtoupper(get_nfilter_request_var('sort_direction')) === 'DESC' ? 'DESC' : 'ASC';
|
|
|
|
$_SESSION['sort_string'][$page] = 'ORDER BY ';
|
|
|
|
foreach ($_SESSION['sort_data'][$page] as $column => $direction) {
|
|
if (!str_contains($column, '(') && !str_contains($column, '`')) {
|
|
$del = '`';
|
|
} else {
|
|
$del = '';
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach ($_SESSION['sort_data'][$page] as $column => $direction) {
|
|
$column = sanitize_sql_column($column, '');
|
|
|
|
if ($column === '') {
|
|
continue;
|
|
}
|
|
|
|
$direction = strtoupper((string)$direction) === 'DESC' ? 'DESC' : 'ASC';
|
|
|
|
if ($column == 'ip' || $column == 'ip_address') {
|
|
$order .= ($order != '' ? ', ' : '') . 'INET_ATON(' . $column . ') ' . $direction;
|
|
} elseif ($column == 'hostname' && $natural) {
|
|
$order .= ($order != '' ? ', ' : '') . 'NATURAL_SORT_KEY(' . $del . implode($del . '.' . $del, explode('.', $column)) . $del . ') ' . $direction;
|
|
} else {
|
|
$order .= ($order != '' ? ', ' : '') . $del . implode($del . '.' . $del, explode('.', $column)) . $del . ' ' . $direction;
|
|
}
|
|
}
|
|
|
|
$_SESSION['sort_string'][$page] .= $order;
|
|
} else {
|
|
unset($_SESSION['sort_data'][$page]);
|
|
unset($_SESSION['sort_string'][$page]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates an SQL ORDER BY clause based on the current sorting preferences.
|
|
*
|
|
* This function constructs an ORDER BY clause using the sort column and sort direction
|
|
* specified in the request variables. It also ensures that the column name is properly
|
|
* delimited to prevent SQL injection.
|
|
*
|
|
* @return string The generated ORDER BY clause.
|
|
*/
|
|
function get_order_string() : string {
|
|
$page = get_order_string_page(true);
|
|
|
|
if (isset($_SESSION['sort_string'][$page])) {
|
|
return $_SESSION['sort_string'][$page];
|
|
}
|
|
|
|
/* Allowlist: identifiers are word chars and dots only; backtick in
|
|
* the payload would escape the quoting even with $del='`'. */
|
|
$sort_column = sanitize_sql_column(get_request_var('sort_column'), '');
|
|
|
|
if ($sort_column === '') {
|
|
return '';
|
|
}
|
|
|
|
$sort_dir = strtoupper(get_request_var('sort_direction')) === 'DESC' ? 'DESC' : 'ASC';
|
|
|
|
if (!str_contains($sort_column, '(') && !str_contains($sort_column, '`')) {
|
|
$del = '`';
|
|
} else {
|
|
$del = '';
|
|
}
|
|
|
|
return 'ORDER BY ' . $del . implode($del . '.' . $del, explode('.', $sort_column)) . $del . ' ' . $sort_dir;
|
|
}
|
|
|
|
/**
|
|
* Whitelist of the dsstats measure columns that get_dsstats_order_string()
|
|
* is allowed to sort by. This is the single source of truth for both the
|
|
* Assert\Choice constraint's `choices` list below and the fallback default,
|
|
* so a new measure only needs to be added in one place.
|
|
*
|
|
* Kept as plain const arrays rather than a backed enum so this file stays
|
|
* parseable on PHP 8.0 (see .github/workflows/rocky-linux.yml, which lints
|
|
* against Rocky Linux 9's stock php-cli package).
|
|
*/
|
|
const DSSTATS_MEASURES = ['average', 'peak', 'p25n', 'p50n', 'p75n', 'p90n', 'p95n', 'sum'];
|
|
const DSSTATS_MEASURE_DEFAULT = 'average';
|
|
|
|
/**
|
|
* Builds the validated ORDER BY clause used by get_allowed_graphs() when
|
|
* sorting graphs by a dsstats measure (see the 'rs' derived table it joins
|
|
* in). The measure and order values come from unauthenticated request
|
|
* variables ('measure', 'graph_order'), so they are checked against the
|
|
* actual data_source_stats_* columns and asc/desc before being concatenated
|
|
* into SQL, the same way get_order_string() checks sort_column/sort_direction.
|
|
*
|
|
* @param array $sql_order Expects 'measure' and 'order' keys; see
|
|
* get_allowed_graphs() for the full array shape.
|
|
*
|
|
* @return string The 'ORDER BY rs.<measure> <ASC|DESC>' clause.
|
|
*/
|
|
function get_dsstats_order_string(array $sql_order) : string {
|
|
$measure = isset($sql_order['measure']) && is_scalar($sql_order['measure']) && CactiValidator::isValid($sql_order['measure'], [new Assert\Choice(choices: DSSTATS_MEASURES)])
|
|
? $sql_order['measure']
|
|
: DSSTATS_MEASURE_DEFAULT;
|
|
|
|
$order = isset($sql_order['order']) && is_scalar($sql_order['order']) ? strtoupper((string) $sql_order['order']) : '';
|
|
|
|
$direction = CactiValidator::isValid($order, [new Assert\Choice(choices: ['ASC', 'DESC'])])
|
|
? $order
|
|
: 'ASC';
|
|
|
|
return 'ORDER BY rs.' . $measure . ' ' . $direction;
|
|
}
|
|
|
|
/**
|
|
* Removes a specified column from the order string in the session data.
|
|
*
|
|
* This function retrieves the current page's order string and checks if the specified
|
|
* column exists in the session's sort data for that page. If the column is found, it
|
|
* removes the column from the sort data and updates the order string.
|
|
*
|
|
* @param string $column The name of the column to be removed from the order string.
|
|
*
|
|
* @return void
|
|
*/
|
|
function remove_column_from_order_string(string $column) : void {
|
|
$page = get_order_string_page(false);
|
|
|
|
if (isset($_SESSION['sort_data'][$page][$column])) {
|
|
unset($_SESSION['sort_data'][$page][$column]);
|
|
update_order_string(true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a unique order string for the current page.
|
|
*
|
|
* This function creates a unique identifier for the current page by combining
|
|
* a static page count, the current page name, and optional request variables
|
|
* such as 'action' and 'tab'. The page count is incremented with each call to
|
|
* ensure uniqueness.
|
|
*
|
|
* @param bool $increment Increment the page counter if true
|
|
*
|
|
* @return string A unique order string for the current page.
|
|
*/
|
|
function get_order_string_page(bool $increment = true) : string {
|
|
static $page_count = 0;
|
|
|
|
$page = $page_count . '_' . str_replace('.php', '', get_current_page());
|
|
|
|
if (isset_request_var('action')) {
|
|
$page .= '_' . get_nfilter_request_var('action');
|
|
}
|
|
|
|
if (isset_request_var('tab')) {
|
|
$page .= '_' . get_nfilter_request_var('tab');
|
|
}
|
|
|
|
if ($increment == true) {
|
|
$page_count++;
|
|
}
|
|
|
|
return $page;
|
|
}
|
|
|
|
/**
|
|
* Validate the redirect url provider by the HTTP_REFERER from PHP
|
|
*
|
|
* @param string $url The regular expression to validate.
|
|
* @param string $default The URL to travel to upon failure
|
|
*
|
|
* @return string The validated URL, or the provided $default if invalid
|
|
*/
|
|
function validate_redirect_url($url = '', $default = 'index.php') {
|
|
if ($url === '') {
|
|
return $default;
|
|
}
|
|
|
|
$url = trim($url);
|
|
|
|
// Decode the url to make it readable if encoded
|
|
if (is_urlencoded($url)) {
|
|
$url = urldecode($url);
|
|
}
|
|
|
|
// reject URLs with protocol schemes (external redirects, javascript:, data:)
|
|
$bad_strings = [
|
|
'javascript:',
|
|
'data:',
|
|
'vbscript:',
|
|
'mailto:',
|
|
'file:'
|
|
];
|
|
|
|
foreach ($bad_strings as $bstring) {
|
|
if (stripos($url, $bstring) !== false) {
|
|
return $default;
|
|
}
|
|
}
|
|
|
|
// reject protocol-relative URLs
|
|
if (strpos($url, '//') === 0) {
|
|
return $default;
|
|
}
|
|
|
|
// reject URLs with newlines (header injection)
|
|
if (preg_match('/[\r\n]/', $url)) {
|
|
return $default;
|
|
}
|
|
|
|
// reject path traversal sequences
|
|
if (stripos($url, '..') !== false) {
|
|
return $default;
|
|
}
|
|
|
|
// Prevent referring off site
|
|
$ref_host = parse_url($url, PHP_URL_HOST);
|
|
$srv_host = null;
|
|
|
|
if (isset($_SERVER['SERVER_NAME']) && $_SERVER['SERVER_NAME'] != '') {
|
|
$srv_host = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']);
|
|
}
|
|
|
|
if ($ref_host === null || ($srv_host !== null && $ref_host === $srv_host)) {
|
|
$ref_path = parse_url($url, PHP_URL_PATH) ?: '';
|
|
$ref_query = parse_url($url, PHP_URL_QUERY);
|
|
|
|
$safe = sanitize_uri($ref_path . ($ref_query !== null ? '?' . $ref_query : ''));
|
|
|
|
return $safe;
|
|
} else {
|
|
return $default;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds a forced-HTTPS redirect using a server-configured host name.
|
|
*
|
|
* @param string $server_name The web server's configured name, optionally
|
|
* followed by ':port' (IPv6 literals must be
|
|
* bracketed first, e.g. '[::1]:8080').
|
|
* @param string $request_uri The requested local path and query string.
|
|
* @param string $default_path A local fallback when the request URI is invalid.
|
|
*
|
|
* @psalm-taint-escape header
|
|
*
|
|
* @return string A safe absolute HTTPS URL, or an empty string for an invalid host.
|
|
*/
|
|
function cacti_build_https_redirect_url(string $server_name, string $request_uri, string $default_path = '/') : string {
|
|
$server_name = trim($server_name);
|
|
|
|
if ($server_name === '') {
|
|
return '';
|
|
}
|
|
|
|
/* split an optional port off first; a bracketed IPv6 literal keeps its
|
|
* embedded colons and is only split on a ']:port' suffix, so a bare,
|
|
* unbracketed IPv6 address (multiple colons, no brackets) is never
|
|
* mistaken for a host:port pair */
|
|
if ($server_name[0] === '[') {
|
|
$close = strpos($server_name, ']');
|
|
|
|
if ($close === false) {
|
|
return '';
|
|
}
|
|
|
|
$host = substr($server_name, 1, $close - 1);
|
|
$port = '';
|
|
|
|
if (isset($server_name[$close + 1])) {
|
|
if ($server_name[$close + 1] !== ':') {
|
|
return '';
|
|
}
|
|
|
|
$port = substr($server_name, $close + 2);
|
|
}
|
|
} elseif (substr_count($server_name, ':') === 1) {
|
|
[$host, $port] = explode(':', $server_name, 2);
|
|
} else {
|
|
$host = $server_name;
|
|
$port = '';
|
|
}
|
|
|
|
if ($port !== '' && (!ctype_digit($port) || (int) $port < 1 || (int) $port > 65535)) {
|
|
return '';
|
|
}
|
|
|
|
if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
|
|
$host = '[' . $host . ']';
|
|
} elseif (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false &&
|
|
filter_var($host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) {
|
|
return '';
|
|
}
|
|
|
|
$path = validate_redirect_url($request_uri, $default_path);
|
|
$path = '/' . ltrim($path, '/');
|
|
|
|
return 'https://' . $host . ($port !== '' ? ':' . $port : '') . $path;
|
|
}
|
|
|
|
/**
|
|
* Resolves a trusted host for the force_https redirect from the admin-configured
|
|
* base_url, in preference to the request's Host/SERVER_NAME.
|
|
*
|
|
* SERVER_NAME mirrors the client Host header under the common Apache default
|
|
* UseCanonicalName Off, so validating its format alone (see
|
|
* cacti_build_https_redirect_url()) still lets an attacker redirect to another
|
|
* valid-looking hostname. base_url is the trusted source Cacti already uses for
|
|
* absolute URLs, so it is preferred when configured.
|
|
*
|
|
* @return string A bare host, or 'host:port' when base_url configures a
|
|
* non-default port, safe to pass to
|
|
* cacti_build_https_redirect_url(), or '' when base_url is unset.
|
|
*/
|
|
function cacti_force_https_host() : string {
|
|
$base = trim((string) read_config_option('base_url'));
|
|
|
|
if ($base === '') {
|
|
return '';
|
|
}
|
|
|
|
/* base_url is stored in scheme-less form (e.g. 'monitor.example/cacti')
|
|
* until the first linked graph report is rendered, see the same check in
|
|
* lib/reports.php; parse_url() cannot extract a host without a scheme, so
|
|
* add one for parsing purposes only. */
|
|
$parseable = (substr($base, 0, 4) === 'http') ? $base : 'http://' . $base;
|
|
|
|
$host = parse_url($parseable, PHP_URL_HOST);
|
|
$port = parse_url($parseable, PHP_URL_PORT);
|
|
|
|
if (!is_string($host) || $host === '') {
|
|
return '';
|
|
}
|
|
|
|
/* bracket a literal IPv6 host before appending a port so the two remain
|
|
* unambiguous when cacti_build_https_redirect_url() splits them back apart */
|
|
if (strpos($host, ':') !== false) {
|
|
$host = '[' . $host . ']';
|
|
}
|
|
|
|
return ($port !== null && $port !== false) ? $host . ':' . $port : $host;
|
|
}
|
|
|
|
/**
|
|
* Validates if the given string is a valid regular expression.
|
|
*
|
|
* This function checks if the provided regular expression is valid and safe to use.
|
|
* It prevents exploits by limiting the length of the regular expression to 50 bytes
|
|
* and disallowing the use of the semicolon character.
|
|
*
|
|
* @param string $regex The regular expression to validate.
|
|
*
|
|
* @return bool|string Returns true if the regular expression is valid, otherwise returns an error message string.
|
|
*/
|
|
function validate_is_regex(string $regex) : bool|string {
|
|
if ($regex == '') {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Prevent exploits from encoded Regular expressions that can cause
|
|
* injections in MariaDB and MySQL. We do this by limiting the
|
|
* length of the regular expression to 50 bytes or less.
|
|
*/
|
|
if (strlen($regex) > 50) {
|
|
return __('Cacti regular expressions are limited to 50 characters only for security reasons.');
|
|
}
|
|
|
|
if (str_contains($regex, ';')) {
|
|
return __('Cacti regular expressions can not includes the semi-color character.');
|
|
}
|
|
|
|
restore_error_handler();
|
|
|
|
$track_errors = ini_get('track_errors');
|
|
ini_set('track_errors', 1);
|
|
|
|
if (@preg_match("'" . $regex . "'", '') !== false) {
|
|
ini_set('track_errors', $track_errors);
|
|
|
|
return true;
|
|
}
|
|
|
|
$last_error = error_get_last();
|
|
|
|
$php_error = trim(str_replace('preg_match():', '', $last_error['message']));
|
|
|
|
ini_set('track_errors', $track_errors);
|
|
|
|
$errors = [
|
|
PREG_INTERNAL_ERROR => __('There was an internal error!'),
|
|
PREG_BACKTRACK_LIMIT_ERROR => __('Backtrack limit was exhausted!'),
|
|
PREG_RECURSION_LIMIT_ERROR => __('Recursion limit was exhausted!'),
|
|
PREG_BAD_UTF8_ERROR => __('Bad UTF-8 error!'),
|
|
PREG_BAD_UTF8_OFFSET_ERROR => __('Bad UTF-8 offset error!'),
|
|
];
|
|
|
|
$error = preg_last_error();
|
|
|
|
if (!defined('IN_CACTI_INSTALL')) {
|
|
set_error_handler('CactiErrorHandler');
|
|
}
|
|
|
|
if (empty($error)) {
|
|
return $php_error;
|
|
} else {
|
|
return $errors[$error];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads the current session value for a given request variable.
|
|
*
|
|
* This function checks if a request variable is set. If it is, the value is stored in the session.
|
|
* If the request variable is not set but the session variable is, the session value is set as the request variable.
|
|
* If neither is set, the request variable is set to a default value.
|
|
*
|
|
* @param string $request_var_name The name of the request variable to check.
|
|
* @param string $session_var_name The name of the session variable to store the value.
|
|
* @param mixed $default_value The default value to set if neither the request nor session variable is set.
|
|
*
|
|
* @return void
|
|
*/
|
|
function load_current_session_value(string $request_var_name, string $session_var_name, mixed $default_value) : void {
|
|
if (isset_request_var($request_var_name)) {
|
|
$_SESSION[$session_var_name] = get_request_var($request_var_name);
|
|
} elseif (isset($_SESSION[$session_var_name])) {
|
|
set_request_var($request_var_name, $_SESSION[$session_var_name]);
|
|
} else {
|
|
set_request_var($request_var_name, $default_value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the colored device status as an HTML span element.
|
|
*
|
|
* This function returns an HTML span element with a class and text representing
|
|
* the status of a device. The status can be 'Disabled', 'Down (Thold)', 'Down',
|
|
* 'Recovering', 'Up', 'Error', or 'Unknown'.
|
|
*
|
|
* @param bool $disabled Indicates if the device is disabled.
|
|
* @param int $status The current status of the device.
|
|
* @param int $thold_failure_count Optional. The threshold failure count. Default is -1.
|
|
* @param int $status_event_count Optional. The status event count. Default is -1.
|
|
*
|
|
* @return string The HTML span element with the appropriate class and status text.
|
|
*/
|
|
function get_colored_device_status(bool $disabled, int $status, int $thold_failure_count = -1, int $status_event_count = -1) : string {
|
|
if ($disabled) {
|
|
return "<span class='deviceStatus deviceDisabled'>" . __('Disabled') . '</span>';
|
|
} else {
|
|
if ($status != HOST_RECOVERING && $thold_failure_count > 0) {
|
|
if ($status_event_count >= $thold_failure_count) {
|
|
return "<span class='deviceStatus deviceDown'>" . __('Down (Thold)') . '</span>';
|
|
}
|
|
}
|
|
|
|
return match ($status) {
|
|
HOST_DOWN => "<span class='deviceStatus deviceDown'>" . __('Down') . '</span>',
|
|
HOST_RECOVERING => "<span class='deviceStatus deviceRecovering'>" . __('Recovering') . '</span>',
|
|
HOST_UP => "<span class='deviceStatus deviceUp'>" . __('Up') . '</span>',
|
|
HOST_ERROR => "<span class='deviceStatus deviceError'>" . __('Error') . '</span>',
|
|
default => "<span class='deviceStatus deviceUnknown'>" . __('Unknown') . '</span>',
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a device's status, return the colored text in HTML format suitable for display
|
|
*
|
|
* @param bool $disabled When true, the device is disabled, false is it is not
|
|
* @param string $site_name The name of the site to display
|
|
*
|
|
* @return string Returns a string containing html that represents the site's current
|
|
* status and name
|
|
*/
|
|
function get_colored_site_status(bool $disabled, string|null $site_name) : string {
|
|
$class = '';
|
|
|
|
if ($disabled) {
|
|
$class = 'deviceDown';
|
|
}
|
|
|
|
return "<span class='$class'>" . __esc($site_name) . '</span>';
|
|
}
|
|
|
|
/**
|
|
* Determine the correct graph start time selected using the timespan selector
|
|
*
|
|
* @return mixed The current graph start time if set and numeric, otherwise a default timespan value.
|
|
*/
|
|
function get_current_graph_start() : mixed {
|
|
if (isset($_SESSION['sess_current_timespan_begin_now']) && is_numeric($_SESSION['sess_current_timespan_begin_now'])) {
|
|
return $_SESSION['sess_current_timespan_begin_now'];
|
|
} else {
|
|
return '-' . DEFAULT_TIMESPAN;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determine the correct graph end time selected using the timespan selector
|
|
*
|
|
* @return mixed The current graph end time if set and numeric, otherwise '0'.
|
|
*/
|
|
function get_current_graph_end() : mixed {
|
|
if (isset($_SESSION['sess_current_timespan_end_now']) && is_numeric($_SESSION['sess_current_timespan_end_now'])) {
|
|
return $_SESSION['sess_current_timespan_end_now'];
|
|
} else {
|
|
return '0';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates an HTML tooltip element with the provided text.
|
|
*
|
|
* This function creates a tooltip element using a div with the class
|
|
* "cactiTooltipHint" and a FontAwesome question-circle icon. The tooltip
|
|
* text is included within a span element that is initially hidden.
|
|
*
|
|
* @param string $text The text to be displayed inside the tooltip.
|
|
*
|
|
* @return string The HTML string for the tooltip element if text is provided,
|
|
* otherwise an empty string.
|
|
*/
|
|
function display_tooltip(string $text) : string {
|
|
if ($text != '') {
|
|
return '<div class="cactiTooltipHint ti ti-help"><span style="display:none;">' . $text . "</span></div>\n";
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a paginated list of links for navigating through pages.
|
|
*
|
|
* @param int $current_page The current page number.
|
|
* @param int $pages_per_screen The number of pages to display in the pagination control.
|
|
* @param int $rows_per_page The number of rows per page.
|
|
* @param int $total_rows The total number of rows.
|
|
* @param string $url The base URL for the pagination links.
|
|
* @param string $page_var The query parameter name for the page number (default is 'page').
|
|
* @param string $return_to The ID of the HTML element to update with the new page content (default is '').
|
|
*
|
|
* @return string The HTML for the pagination control.
|
|
*/
|
|
function get_page_list(int $current_page, int $pages_per_screen, int $rows_per_page, int $total_rows, string $url, string $page_var = 'page', string $return_to = '') : string {
|
|
if (!preg_match('/^[A-Za-z_$][A-Za-z0-9_$]*$/', $page_var)) {
|
|
$page_var = 'page';
|
|
}
|
|
|
|
// By current design, $pages_per_screen means number of page no in mid of nav bar
|
|
// when $total_pages is larger than $pages_per_screen + 2(first and last)
|
|
// So actual $pages_per_screen should be $pages_per_screen+2
|
|
$pages_per_screen += 2;
|
|
$url_page_select = "<ul class='pagination'>";
|
|
|
|
if (str_contains($url, '?')) {
|
|
$url .= '&';
|
|
} else {
|
|
$url .= '?';
|
|
}
|
|
|
|
$url_ellipsis = '<li><span>...</span></li>';
|
|
|
|
if ($rows_per_page <= 0) {
|
|
$total_pages = 0;
|
|
} else {
|
|
$total_pages = ceil($total_rows / $rows_per_page);
|
|
}
|
|
|
|
$start_page = max(1, ($current_page - floor(($pages_per_screen - 1) / 2)));
|
|
$end_page = min($total_pages, ($current_page + floor(($pages_per_screen - 1) / 2)));
|
|
|
|
if ($total_pages <= $pages_per_screen) {
|
|
$start_page = 2;
|
|
$end_page = $total_pages - 1;
|
|
} else {
|
|
$start_page = max(2, ($current_page - floor(($pages_per_screen - 3) / 2)));
|
|
// When current_page > (pages_per_screen - 1) / 2
|
|
$end_page = min($total_pages - 1, ($current_page + floor(($pages_per_screen - 3) / 2)));
|
|
|
|
// adjust if we are close to the beginning of the page list
|
|
if ($current_page <= ceil(($pages_per_screen) / 2)) {
|
|
$end_page += ($pages_per_screen - $end_page - 1);
|
|
}
|
|
|
|
// adjust if we are close to the end of the page list
|
|
if (($total_pages - $current_page) < ceil(($pages_per_screen) / 2)) {
|
|
$start_page -= (($pages_per_screen - ($end_page - $start_page)) - 3);
|
|
}
|
|
|
|
// stay within limits
|
|
$start_page = max(2, $start_page);
|
|
$end_page = min($total_pages - 1, $end_page);
|
|
}
|
|
|
|
if ($total_pages > 0) {
|
|
if ($current_page == 1) {
|
|
$url_page_select .= "<li><a href='#' class='active' onClick='goto$page_var(1);return false'>1</a></li>";
|
|
} else {
|
|
$url_page_select .= "<li><a href='#' onClick='goto$page_var(1);return false'>1</a></li>";
|
|
}
|
|
}
|
|
|
|
for ($page_number = 0; (($page_number + $start_page) <= $end_page); $page_number++) {
|
|
$page = $page_number + $start_page;
|
|
|
|
if ($page_number < $pages_per_screen) {
|
|
if ($page_number == 0 && $start_page > 2) {
|
|
$url_page_select .= $url_ellipsis;
|
|
}
|
|
|
|
if ($current_page == $page) {
|
|
$url_page_select .= "<li><a href='#' class='active' onClick='goto$page_var($page);return false'>$page</a></li>";
|
|
} else {
|
|
$url_page_select .= "<li><a href='#' onClick='goto$page_var($page);return false'>$page</a></li>";
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($total_pages - 1 > $end_page) {
|
|
$url_page_select .= $url_ellipsis;
|
|
}
|
|
|
|
if ($total_pages > 1) {
|
|
if ($current_page == $total_pages) {
|
|
$url_page_select .= "<li><a href='#' class='active' onClick='goto$page_var($total_pages);return false'>$total_pages</a></li>";
|
|
} else {
|
|
$url_page_select .= "<li><a href='#' onClick='goto$page_var($total_pages);return false'>$total_pages</a></li>";
|
|
}
|
|
}
|
|
|
|
$url_page_select .= '</ul>';
|
|
|
|
if ($return_to == '') {
|
|
$return_to = 'main';
|
|
}
|
|
|
|
$url_json = cacti_js_encode($url . $page_var);
|
|
$return_to_json = cacti_js_encode($return_to);
|
|
$url_page_select .= "<script type='text/javascript'>
|
|
function goto$page_var(pageNo) {
|
|
if (typeof url_graph === 'function') {
|
|
var url_add=url_graph('')
|
|
} else {
|
|
var url_add='';
|
|
};
|
|
|
|
strURL = $url_json + '=' + pageNo + url_add;
|
|
|
|
loadUrl({
|
|
url: strURL,
|
|
elementId: $return_to_json,
|
|
});
|
|
}</script>";
|
|
|
|
return $url_page_select;
|
|
}
|