Files
cacti/lib/html_graph.php
TheWitness 2fdc8747af Cross-filter Device and Graph Template on Preview and List views (#8363)
* Cross-filter Device and Graph Template on Preview and List views

Forward the selected Graph Template(s) to the ajax_hosts Device callback and filter the device list accordingly, so choosing a Graph Template narrows the Device list. Device-to-template narrowing already exists on these views.

* Use line comment style to satisfy php-cs-fixer

* fix(graph_view): validate raw graph_template_id against filter allowlist in html_make_device_where

The ajax_hosts endpoint calls html_make_device_where() before the graph filter is sanitized, so gnrv('graph_template_id') is raw request data. An array value such as graph_template_id[]=cg_1 reached html_transform_graph_template_ids()'s str_contains() and raised a TypeError (HTTP 500). Reject non-string input and validate against the same regexp allowlist the filter uses before transforming.

* test(graph_view): add request-coverage probe for the device/template cross-filter

The pr-php-coverage gate requires >=80 percent of changed executable PHP lines to be exercised. The new html_make_device_where() graph_template_id branch and the request_vars changes in create_graphs_preview_filter()/create_listview_filter() are only reached when a graph_template_id filter is present, which the page-level e2e suites do not drive. Add an isolated source-slice probe that runs those production functions with stubbed I/O boundaries and wire it into run_request_coverage.sh so the changed lines are covered.
2026-10-14 13:21:22 -04:00

2503 lines
78 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/ |
+-------------------------------------------------------------------------+
*/
/**
* Initializes the session variables for real-time data step and window.
*
* This function checks if the session variables 'sess_realtime_dsstep' and 'sess_realtime_window'
* are set. If they are not set, it initializes them with the values from the configuration options
* 'realtime_interval' and 'realtime_gwindow' respectively.
*
* @return void
*/
function initialize_realtime_step_and_window() : void {
if (!isset($_SESSION['sess_realtime_dsstep'])) {
$_SESSION['sess_realtime_dsstep'] = read_config_option('realtime_interval');
}
if (!isset($_SESSION['sess_realtime_window'])) {
$_SESSION['sess_realtime_window'] = read_config_option('realtime_gwindow');
}
}
/**
* Validates the number of graph columns accepted by the graph views.
*
* @param mixed $value The requested column count.
*
* @return int|false The validated count, or false when it is unsupported.
*/
function html_graph_validate_columns(mixed $value) : int|false {
$value = filter_var($value, FILTER_VALIDATE_INT);
if ($value === false || $value < 1 || $value > 6) {
return false;
}
return $value;
}
/**
* Validates a graph page size against the configured selector values.
*
* @param mixed $value The requested page size.
*
* @return int|false The validated page size, or false when it is unsupported.
*/
function html_graph_validate_page_size(mixed $value) : int|false {
global $item_rows;
$value = filter_var($value, FILTER_VALIDATE_INT);
if ($value === false || !array_key_exists($value, $item_rows)) {
return false;
}
return $value;
}
/**
* Sets the default graph action based on user settings and permissions.
*
* This function checks if a request variable 'action' is set. If not, it sets up a default action
* based on the user's settings and permissions. The function prioritizes the following actions:
* 'tree', 'list', and 'preview', in that order. If none of these actions are allowed it attempts
* to find the first action that the user has permission to. If it can not find one of these
* the user is actually in an area that they do not have permission to, so we raise a message.
*
* The function leverages the session sess_graph_view_action to remember the last page that
* the user visited. There are only three good values here: tree, preview, and list.
*
* @return void
*/
function set_default_graph_action() : void {
// go through the settings and create a default
$modes = [
'tree' => ['permission' => 'show_tree', 'id' => '1'],
'preview' => ['permission' => 'show_preview', 'id' => '3'],
'list' => ['permission' => 'show_list', 'id' => '2'],
];
if (isrv('action')) {
$action = gnrv('action');
} else {
$action = '';
}
if ($action == '') {
// setup the default action
if (!isset($_SESSION['sess_graph_view_action'])) {
$user_setting = read_user_setting('default_view_mode');
$good_mode = '';
// check the defaults
foreach ($modes as $action => $info) {
if (is_view_allowed($info['permission']) && $user_setting == $info['id']) {
$good_mode = $action;
srv('action', $action);
$_SESSION['sess_graph_view_action'] = $good_mode;
break;
}
}
if ($good_mode == '') {
foreach ($modes as $action => $info) {
if (is_view_allowed($info['permission'])) {
$good_mode = $action;
srv('action', $action);
$_SESSION['sess_graph_view_action'] = $good_mode;
break;
}
}
}
if ($good_mode == '') {
raise_message('no_mode', __('Your User account does not have access to any Graph data'), MESSAGE_LEVEL_ERROR);
}
} elseif (in_array($_SESSION['sess_graph_view_action'], array_keys($modes), true) &&
is_view_allowed('show_' . $_SESSION['sess_graph_view_action'])) {
srv('action', $_SESSION['sess_graph_view_action']);
} else {
unset($_SESSION['sess_graph_view_action']);
foreach ($modes as $fallback_action => $info) {
if (is_view_allowed($info['permission'])) {
srv('action', $fallback_action);
$_SESSION['sess_graph_view_action'] = $fallback_action;
break;
}
}
if (!isset($_SESSION['sess_graph_view_action'])) {
raise_message('no_mode', __('Your User account does not have access to any Graph data'), MESSAGE_LEVEL_ERROR);
}
}
} elseif (in_array($action, ['get_node', 'tree_content'], true)) {
$_SESSION['sess_graph_view_action'] = 'tree';
} elseif (in_array($action, array_keys($modes), true)) {
if (is_view_allowed('show_' . $action)) {
$_SESSION['sess_graph_view_action'] = $action;
}
}
}
function create_graphs_preview_filter(string $session_var) : array {
global $item_rows;
$all = ['-1' => __('All')];
$any = ['-1' => __('Any')];
$none = ['0' => __('None')];
$sites = array_rekey(
db_fetch_assoc('SELECT id, name
FROM sites
ORDER BY name'),
'id', 'name'
);
$sites = $any + $sites;
$locations = array_rekey(
db_fetch_assoc('SELECT DISTINCT location
FROM host
ORDER BY location'),
'location', 'location'
);
$locations = $any + $none + $locations;
// unset the ordering if we have a setup that does not support ordering
if (isrv('graph_template_id')) {
if (str_contains(gnrv('graph_template_id'), ',') || gnrv('graph_template_id') <= 0) {
srv('graph_order', '');
srv('graph_source', '');
}
}
/* The filter array below is what declares host_id, so nothing has validated
* it at this point and get_request_var() would return $_REQUEST untouched.
* The value reaches an interpolated SQL fragment further down, and '> 0'
* does not gate it: PHP compares a non-numeric string against an int as a
* string, so '1 AND ...' passes. Filter it here, at the read. */
if (isrv('host_id')) {
$host_id = gfrv('host_id');
} elseif (isset($_SESSION[$session_var . '_host_id'])) {
$host_id = $_SESSION[$session_var . '_host_id'];
} else {
$host_id = '-1';
}
if ($host_id > 0) {
$hostname = db_fetch_cell_prepared('SELECT description
FROM host
WHERE id = ?',
[$host_id]);
} elseif ($host_id == '') {
$host_id = '-1';
$hostname = __('Any');
} elseif ($host_id == 0) {
$host_id = '0';
$hostname = __('None');
} else {
$host_id = '-1';
$hostname = __('Any');
}
if ($host_id == 0) {
$templates = get_allowed_graph_templates_normalized('gl.host_id=0', 'name', '', $total_rows);
} elseif ($host_id > 0) {
$templates = get_allowed_graph_templates_normalized('gl.host_id=' . (int) $host_id, 'name', '', $total_rows);
} else {
$templates = get_allowed_graph_templates_normalized('', 'name', '', $total_rows);
}
$normalized_templates = [];
if (cacti_sizeof($templates)) {
foreach ($templates as $t) {
$normalized_templates[$t['id']] = $t['name'];
}
}
$normalized_templates = $all + $none + $normalized_templates;
$columns = [
'1' => __('%d Column', 1),
'2' => __('%d Columns', 2),
'3' => __('%d Columns', 3),
'4' => __('%d Columns', 4),
'5' => __('%d Columns', 5),
'6' => __('%d Columns', 6)
];
$column_setting = html_graph_validate_columns(read_user_setting('num_columns', '2'));
if ($column_setting === false) {
$column_setting = 2;
}
$metrics_array = html_graph_order_filter_array();
if (isrv('business_hours')) {
$business_hours = gnrv('business_hours');
} else {
$business_hours = read_user_setting('show_business_hours') == 'on' ? 'true' : 'false';
}
if (isrv('thumbnails')) {
$thumbnails = gnrv('thumbnails');
} else {
$thumbnails = read_user_setting('thumbnail_section_preview') == 'on' ? 'true' : 'false';
}
$filters = [
'rows' => [
[
'site_id' => [
'method' => 'drop_array',
'friendly_name' => __('Site'),
'filter' => FILTER_VALIDATE_INT,
'default' => '-1',
'pageset' => true,
'array' => $sites,
'value' => '-1'
],
'location' => [
'method' => 'drop_array',
'friendly_name' => __('Location'),
'filter' => FILTER_CALLBACK,
'filter_options' => ['options' => 'sanitize_search_string'],
'default' => '-1',
'pageset' => true,
'array' => $locations,
'value' => '-1'
],
'host_id' => [
'method' => 'drop_callback',
'friendly_name' => __('Device'),
'filter' => FILTER_VALIDATE_INT,
'default' => '-1',
'class' => 'select2-callback',
'pageset' => true,
'request_vars' => 'location,site_id,graph_template_id',
'sql' => 'SELECT DISTINCT id, description AS name FROM host ORDER BY description',
'action' => 'ajax_hosts',
'id' => $host_id,
'value' => $hostname,
'on_change' => 'applyGraphFilter()'
],
],
[
'graph_template_id' => [
'method' => 'drop_multi',
'bar' => true,
'friendly_name' => __('Template'),
'filter' => FILTER_VALIDATE_REGEXP,
'filter_options' => ['options' => ['regexp' => '/^(cg_[0-9]+|dq_[0-9]+|-?[0-9]+)(,(cg_[0-9]+|dq_[0-9]+|-?[0-9]+))*$/']],
'default' => '-1',
'dynamic' => false,
'class' => 'select2-multi-count',
'select_all_text' => __('All Graph Templates'),
'select_count_text' => __('Templates Selected'),
'select_all_value' => '-1',
'pageset' => true,
'array' => $normalized_templates,
'value' => gnrv('graph_template_id')
],
],
[
'graphs' => [
'method' => 'drop_array',
'friendly_name' => __('Graphs'),
'filter' => FILTER_CALLBACK,
'filter_options' => ['options' => 'html_graph_validate_page_size'],
'default' => '-1',
'pageset' => true,
'array' => $item_rows,
'value' => ''
],
'columns' => [
'method' => 'drop_array',
'friendly_name' => __('Columns'),
'filter' => FILTER_CALLBACK,
'filter_options' => ['options' => 'html_graph_validate_columns'],
'default' => $column_setting,
'pageset' => true,
'array' => $columns,
'value' => $column_setting
],
'thumbnails' => [
'method' => 'filter_checkbox',
'friendly_name' => __('Thumbnails'),
'filter' => FILTER_VALIDATE_REGEXP,
'filter_options' => ['options' => ['regexp' => '/^(true|false)$/']],
'default' => read_user_setting('thumbnail_section_preview') == 'on' ? 'true' : 'false',
'value' => $thumbnails
],
'business_hours' => [
'method' => 'filter_checkbox',
'friendly_name' => __('Business Hours'),
'filter' => FILTER_VALIDATE_REGEXP,
'filter_options' => ['options' => ['regexp' => '/^(true|false)$/']],
'default' => read_user_setting('show_business_hours') == 'on' ? 'true' : 'false',
'value' => $business_hours
]
],
[
'rfilter' => [
'method' => 'textbox',
'friendly_name' => __('Search'),
'filter' => FILTER_VALIDATE_IS_REGEX,
'placeholder' => __('Enter a search term'),
'size' => '55',
'default' => '',
'pageset' => true,
'max_length' => '120',
'value' => ''
]
],
[
'timespan' => [
'method' => 'timespan',
'refresh' => true,
'clear' => true,
'shifter' => true,
'default' => read_user_setting('default_timespan')
],
'graph_list' => [
'method' => 'validate',
'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
'default' => ''
],
'graph_add' => [
'method' => 'validate',
'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
'default' => ''
],
'graph_remove' => [
'method' => 'validate',
'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
'default' => ''
],
'style' => [
'method' => 'validate',
'filter' => FILTER_DEFAULT,
'default' => ''
]
]
],
'buttons' => [
'go' => [
'method' => 'submit',
'display' => __('Go'),
'title' => __('Apply filter to table'),
'callback' => 'applyGraphFilter()'
],
'clear' => [
'method' => 'button',
'display' => __('Clear'),
'title' => __('Reset filter to default values'),
'callback' => 'clearGraphFilter()'
]
]
];
if (cacti_sizeof($metrics_array)) {
$filters['rows'][1] += $metrics_array;
}
if (is_view_allowed('graph_settings')) {
$filters['buttons']['save'] = [
'method' => 'button',
'display' => __('Save'),
'title' => __('Save filter to the database'),
'callback' => 'saveGraphFilter("preview")'
];
}
return $filters;
}
function draw_graphs_preview_filter(bool $render = false, string $page = '', string $action = 'get') : void {
$header = __('Graph Preview Filters') . (isrv('style') && grv('style') != '' ? ' ' . __('[ Custom Graph List Applied - Filtering from List ]') : '');
// create the page filter
$filters = create_graphs_preview_filter('sess_pview');
$pageFilter = new CactiTableFilter($header, $page, 'form_graph_view', 'sess_pview', '', false, false);
$pageFilter->rows_label = __('Graphs');
$pageFilter->form_method = $action;
$pageFilter->set_filter_array($filters);
$pageFilter->inject_content = inject_realtime_form();
if ($render) {
$pageFilter->render();
} else {
$pageFilter->sanitize();
}
}
function inject_realtime_form() : string {
global $graphs_per_page, $realtime_window, $realtime_refresh, $graph_timeshifts, $graph_timespans;
$content = "<div class='filterTable'>
<div id='realtime' class='filterRow' style='display:none;'>
<div class='filterColumn'>" . __('Window') . "</div>
<div class='filterColumn'>
<select name='graph_start' id='graph_start' onChange='realtimeGrapher()' data-defaultLabel='" . __('Window') . "'>";
foreach ($realtime_window as $interval => $text) {
$content .= sprintf('<option value="%d"%s>%s</option>', $interval, $interval == $_SESSION['sess_realtime_window'] ? ' selected="selected"' : '', $text);
}
$content .= "</select>
</div>
<div class='filterColumn'>" . __('Interval') . "</div>
<div class='filterColumn'>
<select name='ds_step' id='ds_step' onChange='realtimeGrapher()' data-defaultLabel='" . __('Interval') . "'>";
foreach ($realtime_refresh as $interval => $text) {
$content .= sprintf('<option value="%d"%s>%s</option>', $interval, $interval == $_SESSION['sess_realtime_dsstep'] ? ' selected="selected"' : '', $text);
}
$content .= "</select>
</div>
<div class='filterColumn'>
<button type='button' class='ui-button ui-corner-all ui-widget' id='realtimeoff'>" . __esc('Stop') . "</button>
</div>
<div class='filterColumn center'>
<span id='countdown'></span>
</div>
<div class='filterColumn'>
<input id='future' type='hidden' value='" . read_config_option('allow_graph_dates_in_future') . "'>
</div>
</div>
</div>";
return $content;
}
/**
* Generates the HTML for the graph preview filter form.
*
* This function creates a form that allows users to filter and preview graphs based on various criteria such as site, location, host, template, and time span.
*
* @param string $page The current page URL.
* @param string $action The action to be performed on form submission.
* @param string $devices_where SQL condition for filtering devices (optional).
* @param string $templates_where SQL condition for filtering templates (optional).
*
* @global array $graphs_per_page Array of graphs per page options.
* @global array $realtime_window Array of real-time window options.
* @global array $realtime_refresh Array of real-time refresh interval options.
* @global array $graph_timeshifts Array of graph time shift options.
* @global array $graph_timespans Array of graph time span options.
*
* @return void
*/
function html_graph_preview_filter(string $page, string $action, string $devices_where = '', string $templates_where = '') : void {
global $graphs_per_page, $realtime_window, $realtime_refresh, $graph_timeshifts, $graph_timespans;
initialize_realtime_step_and_window();
draw_graphs_preview_filter(true, $page, $action);
?>
<script type='text/javascript'>
var refreshIsLogout = false;
var refreshMSeconds = <?php print read_user_setting('page_refresh') * 1000; ?>;
var graph_start = <?php print get_current_graph_start(); ?>;
var graph_end = <?php print get_current_graph_end(); ?>;
var timeOffset = <?php print date('Z'); ?>;
var pageAction = <?php print json_encode($action); ?>;
var graphPage = <?php print json_encode($page); ?>;
var date1Open = false;
var date2Open = false;
function initPage() {
$('#startDate').click(function() {
if (date1Open) {
date1Open = false;
$('#date1').datetimepicker('hide');
} else {
date1Open = true;
$('#date1').datetimepicker('show');
}
});
$('#endDate').click(function() {
if (date2Open) {
date2Open = false;
$('#date2').datetimepicker('hide');
} else {
date2Open = true;
$('#date2').datetimepicker('show');
}
});
$('#date1').datetimepicker({
minuteGrid: 10,
stepMinute: 1,
showAnim: 'slideDown',
numberOfMonths: 1,
timeFormat: 'HH:mm',
dateFormat: 'yy-mm-dd',
showButtonPanel: false
});
$('#date2').datetimepicker({
minuteGrid: 10,
stepMinute: 1,
showAnim: 'slideDown',
numberOfMonths: 1,
timeFormat: 'HH:mm',
dateFormat: 'yy-mm-dd',
showButtonPanel: false
});
}
$(function() {
$.when(initPage()).done(function() {
initializeGraphs();
});
});
</script>
<?php
html_spikekill_js();
}
/**
* Generates new graphs for a given host and host template.
*
* This function processes the selected graphs array and generates the corresponding graphs
* for the specified host and host template. If no fields are drawn on the form, it saves
* the graphs without prompting the user.
*
* @param string $page The page URL to redirect to after saving the graphs.
* @param int $host_id The ID of the host for which the graphs are being generated.
* @param int $host_template_id The ID of the host template used for generating the graphs.
* @param array $selected_graphs_array An array of selected graphs to be generated.
*
* @return void
*/
function html_graph_new_graphs(string $page, int $host_id, int $host_template_id, array $selected_graphs_array) : void {
$snmp_query_id = 0;
$output_sections = [];
foreach ($selected_graphs_array as $form_type => $form_array) {
foreach ($form_array as $form_id1 => $form_array2) {
ob_start();
$count = html_graph_custom_data($host_id, $host_template_id, $snmp_query_id, $form_type, $form_id1, $form_array2);
$output = (string) ob_get_clean();
if (array_sum($count)) {
$output_sections[] = $output;
}
}
}
// no fields were actually drawn on the form; just save without prompting the user
if (!cacti_sizeof($output_sections)) {
/* since the user didn't actually click "Create" to POST the data; we have to
pretend like they did here */
srv('save_component_new_graphs', '1');
srv('selected_graphs_array', serialize($selected_graphs_array));
host_new_graphs_save($host_id);
header('Location: ' . $page . '?host_id=' . $host_id);
exit;
}
top_header();
form_start('graphs_new.php', 'new_graphs');
print implode('', $output_sections);
form_hidden_box('host_template_id', $host_template_id, '0');
form_hidden_box('host_id', $host_id, '0');
form_hidden_box('save_component_new_graphs', '1', '');
form_hidden_box('selected_graphs_array', serialize($selected_graphs_array), '');
if (isset($_SERVER['HTTP_REFERER']) && !str_contains($_SERVER['HTTP_REFERER'], 'graphs_new')) {
srv('returnto', basename($_SERVER['HTTP_REFERER']));
}
load_current_session_value('returnto', 'sess_grn_returnto', '');
form_save_button(gnrv('returnto'));
bottom_footer();
}
/**
* Generates custom HTML form data for graph creation based on the provided parameters.
*
* @param int $host_id The ID of the host.
* @param int $host_template_id The ID of the host template.
* @param int $snmp_query_id The ID of the SNMP query.
* @param string $form_type The type of form ('cg' for graph template, 'sg' for SNMP query).
* @param string $form_id1 The ID of the form element.
* @param array $form_array2 An array of form elements.
*
* @return array An array of output fields for the form.
*/
function html_graph_custom_data(int $host_id, int $host_template_id, int $snmp_query_id, string $form_type, string $form_id1, array $form_array2) : array {
// ================= input validation =================
input_validate_input_number($form_id1, 'form_id1');
// ====================================================
$num_output_fields = [];
if ($form_type == 'cg') {
$graph_template_id = intval($form_id1);
$graph_template_name = db_fetch_cell_prepared('SELECT name
FROM graph_templates
WHERE id = ?',
[$graph_template_id]);
if (!graph_template_has_override($graph_template_id)) {
return [];
}
return html_graph_custom_data_template(
$graph_template_id,
$snmp_query_id,
0,
__('Create Graph from %s', htmle($graph_template_name))
);
}
if ($form_type == 'sg') {
foreach ($form_array2 as $form_id2 => $form_array3) {
// ================= input validation =================
input_validate_input_number($form_id2, 'form_id2');
// ====================================================
$snmp_query_id = $form_id1;
$snmp_query_graph_id = $form_id2;
$num_graphs = cacti_sizeof($form_array3);
$query_graph = db_fetch_row_prepared('SELECT sq.name, sqg.graph_template_id
FROM snmp_query AS sq
INNER JOIN snmp_query_graph AS sqg
ON sqg.snmp_query_id = sq.id
WHERE sq.id = ?
AND sqg.id = ?',
[$snmp_query_id, $snmp_query_graph_id]);
if (!cacti_sizeof($query_graph)) {
raise_message(
'invalid_snmp_query_graph_' . $snmp_query_graph_id,
__('The selected Data Query Graph does not belong to the selected Data Query.'),
MESSAGE_LEVEL_ERROR
);
continue;
}
$graph_template_id = (int) $query_graph['graph_template_id'];
if (!graph_template_has_override($graph_template_id)) {
continue;
}
if ($num_graphs > 1) {
$header = __('Create %s Graphs from %s', $num_graphs, htmle($query_graph['name']));
} else {
$header = __('Create Graph from %s', htmle($query_graph['name']));
}
$num_output_fields = array_merge(
$num_output_fields,
html_graph_custom_data_template($graph_template_id, (int) $snmp_query_id, (int) $snmp_query_graph_id, $header)
);
}
}
return $num_output_fields;
}
/**
* Draws override fields for one graph template.
*
* @param int $graph_template_id The graph template being rendered.
* @param int $snmp_query_id The associated SNMP query, or zero.
* @param int $snmp_query_graph_id The associated SNMP query graph, or zero.
* @param string $header The form section heading.
*
* @return array The number of fields emitted by each field renderer.
*/
function html_graph_custom_data_template(int $graph_template_id, int $snmp_query_id, int $snmp_query_graph_id, string $header) : array {
$num_output_fields = [];
html_start_box($header, '100%', false, 3, 'center', '');
// ================= input validation =================
input_validate_input_number($graph_template_id, 'graph_template_id');
// ====================================================
$data_templates = db_fetch_assoc_prepared('SELECT
data_template.name AS data_template_name,
data_template_rrd.data_source_name,
data_template_data.*
FROM (data_template, data_template_rrd, data_template_data, graph_templates_item)
WHERE graph_templates_item.task_item_id = data_template_rrd.id
AND data_template_rrd.data_template_id = data_template.id
AND data_template_data.data_template_id = data_template.id
AND data_template_rrd.local_data_id = 0
AND data_template_data.local_data_id = 0
AND graph_templates_item.local_graph_id = 0
AND graph_templates_item.graph_template_id = ?
GROUP BY data_template.id
ORDER BY data_template.name',
[$graph_template_id]);
$graph_template = db_fetch_row_prepared('SELECT gt.name AS graph_template_name, gtg.*
FROM graph_templates AS gt
INNER JOIN graph_templates_graph AS gtg
ON gt.id = gtg.graph_template_id
WHERE gt.id = ?
AND gtg.local_graph_id = 0',
[$graph_template_id]);
array_push($num_output_fields, draw_nontemplated_fields_graph($graph_template_id, $graph_template, "g_$snmp_query_id" . '_' . $graph_template_id . '_|field|', __('Graph [Template: %s]', htmle($graph_template['graph_template_name'])), true, false, $snmp_query_graph_id));
array_push($num_output_fields, draw_nontemplated_fields_graph_item($graph_template_id, 0, 'gi_' . $snmp_query_id . '_' . $graph_template_id . '_|id|_|field|', __('Graph Items [Template: %s]', htmle($graph_template['graph_template_name'])), true));
// DRAW: Data Sources
if (cacti_sizeof($data_templates)) {
foreach ($data_templates as $data_template) {
array_push($num_output_fields, draw_nontemplated_fields_data_source($data_template['data_template_id'], 0, $data_template, 'd_' . $snmp_query_id . '_' . $graph_template_id . '_' . $data_template['data_template_id'] . '_|field|', __('Data Source [Template: %s]', htmle($data_template['data_template_name'])), true, false, $snmp_query_graph_id));
$data_template_items = db_fetch_assoc_prepared('SELECT
data_template_rrd.*
FROM data_template_rrd
WHERE data_template_rrd.data_template_id = ?
AND local_data_id = 0',
[$data_template['data_template_id']]);
array_push($num_output_fields, draw_nontemplated_fields_data_source_item($data_template['data_template_id'], $data_template_items, 'di_' . $snmp_query_id . '_' . $graph_template_id . '_' . $data_template['data_template_id'] . '_|id|_|field|', '', true, false, false, $snmp_query_graph_id));
array_push($num_output_fields, draw_nontemplated_fields_custom_data($data_template['id'], 'c_' . $snmp_query_id . '_' . $graph_template_id . '_' . $data_template['data_template_id'] . '_|id|', __('Custom Data [Template: %s]', htmle($data_template['data_template_name'])), true, false, $snmp_query_id));
}
}
html_end_box(false);
return $num_output_fields;
}
function html_save_graph_settings() : void {
if (is_view_allowed('graph_settings')) {
gfrv('columns', FILTER_CALLBACK, ['options' => 'html_graph_validate_columns']);
gfrv('predefined_timespan');
gfrv('predefined_timeshift');
gfrv('graphs', FILTER_CALLBACK, ['options' => 'html_graph_validate_page_size']);
gfrv('thumbnails', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(true|false)$/']]);
gfrv('business_hours', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(true|false)$/']]);
if (isrv('predefined_timespan')) {
set_user_setting('default_timespan', grv('predefined_timespan'));
}
if (isrv('predefined_timeshift')) {
set_user_setting('default_timeshift', grv('predefined_timeshift'));
}
if (isrv('business_hours')) {
set_user_setting('show_business_hours', grv('business_hours') == 'true' ? 'on' : '');
}
if (isrv('section') && gnrv('section') == 'preview') {
if (isrv('columns')) {
set_user_setting('num_columns', grv('columns'));
}
if (isrv('graphs')) {
if (grv('graphs') != '-1') {
set_user_setting('preview_graphs_per_page', grv('graphs'));
}
}
if (isrv('thumbnails')) {
set_user_setting('thumbnail_section_preview', gnrv('thumbnails') == 'true' ? 'on' : '');
}
} else {
if (isrv('columns')) {
set_user_setting('num_columns_tree', grv('columns'));
}
if (isrv('graphs')) {
if (grv('graphs') != '-1') {
set_user_setting('treeview_graphs_per_page', grv('graphs'));
}
}
if (isrv('thumbnails')) {
set_user_setting('thumbnail_section_tree', grv('thumbnails') == 'true' ? 'on' : '');
}
}
}
}
function html_graph_preview_view() : void {
global $is_request_ajax;
if (!is_view_allowed('show_preview')) {
header('Location: permission_denied.php');
exit;
}
if (isrv('external_id')) {
$host_id = db_fetch_cell_prepared('SELECT id FROM host WHERE external_id = ?', [gnrv('external_id')]);
if (!empty($host_id)) {
srv('host_id', $host_id);
srv('reset',true);
}
}
top_graph_header();
html_graph_preview_filter('graph_view.php', 'preview');
api_plugin_hook_function('graph_tree_page_buttons',
[
'mode' => 'preview',
'timespan' => $_SESSION['sess_current_timespan'],
'starttime' => get_current_graph_start(),
'endtime' => get_current_graph_end()
]
);
// the user select a bunch of graphs of the 'list' view and wants them displayed here
$sql_or = '';
if (isrv('style')) {
if (grv('style') == 'selective') {
$graph_list = [];
// process selected graphs
if (!ierv('graph_list')) {
foreach (explode(',', grv('graph_list')) as $item) {
if (is_numeric($item)) {
$graph_list[$item] = 1;
}
}
}
if (!ierv('graph_add')) {
foreach (explode(',', grv('graph_add')) as $item) {
if (is_numeric($item)) {
$graph_list[$item] = 1;
}
}
}
// remove items
if (!ierv('graph_remove')) {
foreach (explode(',', grv('graph_remove')) as $item) {
unset($graph_list[$item]);
}
}
$i = 0;
foreach ($graph_list as $item => $value) {
$graph_array[$i] = $item;
$i++;
}
if ((isset($graph_array)) && (cacti_sizeof($graph_array) > 0)) {
// build sql string including each graph the user checked
$sql_or = array_to_sql_or($graph_array, 'gtg.local_graph_id');
}
}
}
$total_graphs = 0;
// create filter for sql
$sql_where = '';
if (!ierv('rfilter')) {
$sql_where .= ' (gtg.title_cache RLIKE ' . db_qstr(grv('rfilter'));
if (ctype_digit(grv('rfilter'))) {
$sql_where .= ' OR gl.id = ' . (int) grv('rfilter');
}
$sql_where .= ')';
}
$sql_where .= ($sql_or != '' && $sql_where != '' ? ' AND ' : '') . $sql_or;
if (!ierv('site_id') && grv('site_id') > 0) {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' h.site_id=' . grv('site_id');
} elseif (ierv('site_id')) {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' h.site_id=0';
}
if (!ierv('host_id') && grv('host_id') > 0) {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' gl.host_id=' . gfrv('host_id');
} elseif (ierv('host_id')) {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' gl.host_id=0';
}
if (grv('location') != '' && grv('location') != '-1' && grv('location') != '0') {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' h.location = ' . db_qstr(grv('location'));
} elseif (grv('location') == '0') {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' h.location = ""';
}
if (!ierv('graph_template_id') && grv('graph_template_id') != '-1' && grv('graph_template_id') != '0') {
$graph_templates = html_transform_graph_template_ids(grv('graph_template_id'));
$sql_where .= ($sql_where != '' ? ' AND ' : '') . ' ' . db_in_clause('gl.graph_template_id', $graph_templates);
} elseif (grv('graph_template_id') == '0') {
$sql_where .= ($sql_where != '' ? ' AND ' : '') . ' ' . db_in_clause('gl.graph_template_id', 0);
}
if (grv('graphs') == '-1') {
$graph_rows = read_user_setting('preview_graphs_per_page', read_config_option('preview_graphs_per_page') ?? 20);
} else {
$graph_rows = grv('graphs');
}
$graph_rows = html_graph_validate_page_size($graph_rows);
if ($graph_rows === false || $graph_rows == -1) {
$graph_rows = 20;
}
$sql_limit = ($graph_rows * (grv('page') - 1)) . ',' . $graph_rows;
if (read_config_option('dsstats_enable') == 'on' && grv('graph_source') != '' && grv('graph_order') != '') {
$sql_order = [
'data_source' => grv('graph_source'),
'order' => grv('graph_order'),
'start_time' => get_current_graph_start(),
'end_time' => get_current_graph_end(),
'cf' => grv('cf'),
'measure' => grv('measure')
];
} else {
$sql_order = 'gtg.title_cache';
}
$graphs = get_allowed_graphs($sql_where, $sql_order, $sql_limit, $total_graphs);
$nav = html_nav_bar('graph_view.php', MAX_DISPLAY_PAGES, grv('page'), $graph_rows, $total_graphs, grv('columns'), __('Graphs'), 'page', 'main');
print $nav;
html_start_box('', '100%', false, 3, 'center', '');
if (grv('thumbnails') == 'true') {
html_graph_thumbnail_area($graphs, '', 'graph_start=' . get_current_graph_start() . '&graph_end=' . get_current_graph_end(), '', grv('columns'));
} else {
html_graph_area($graphs, '', 'graph_start=' . get_current_graph_start() . '&graph_end=' . get_current_graph_end(), '', grv('columns'));
}
html_end_box();
if ($total_graphs) {
print $nav;
}
if (!$is_request_ajax) {
bottom_footer();
}
}
function create_listview_filter(string $session_var) : array {
global $item_rows;
$all = ['-1' => __('All')];
$any = ['-1' => __('Any')];
$none = ['0' => __('None')];
$sites = array_rekey(
db_fetch_assoc('SELECT id, name
FROM sites
ORDER BY name'),
'id', 'name'
);
$sites = $any + $sites;
$locations = array_rekey(
db_fetch_assoc('SELECT DISTINCT location
FROM host
ORDER BY location'),
'location', 'location'
);
$locations = $any + $none + $locations;
/* The filter array below is what declares host_id, so nothing has validated
* it at this point and get_request_var() would return $_REQUEST untouched.
* The value reaches an interpolated SQL fragment further down, and '> 0'
* does not gate it: PHP compares a non-numeric string against an int as a
* string, so '1 AND ...' passes. Filter it here, at the read. */
if (isrv('host_id')) {
$host_id = gfrv('host_id');
} elseif (isset($_SESSION[$session_var . '_host_id'])) {
$host_id = $_SESSION[$session_var . '_host_id'];
} else {
$host_id = '-1';
}
if ($host_id > 0) {
$hostname = db_fetch_cell_prepared('SELECT description
FROM host
WHERE id = ?',
[$host_id]);
} elseif ($host_id == '') {
$host_id = '-1';
$hostname = __('Any');
} elseif ($host_id == 0) {
$host_id = '0';
$hostname = __('None');
} else {
$host_id = '-1';
$hostname = __('Any');
}
if ($host_id == 0) {
$templates = get_allowed_graph_templates_normalized('gl.host_id=0', 'name', '', $total_rows);
} elseif ($host_id > 0) {
$templates = get_allowed_graph_templates_normalized('gl.host_id=' . (int) $host_id, 'name', '', $total_rows);
} else {
$templates = get_allowed_graph_templates_normalized('', 'name', '', $total_rows);
}
$normalized_templates = [];
if (cacti_sizeof($templates)) {
foreach ($templates as $t) {
$normalized_templates[$t['id']] = $t['name'];
}
}
$normalized_templates = $all + $none + $normalized_templates;
$filters = [
'rows' => [
[
'site_id' => [
'method' => 'drop_array',
'friendly_name' => __('Site'),
'filter' => FILTER_VALIDATE_INT,
'default' => '-1',
'pageset' => true,
'array' => $sites,
'value' => '-1'
],
'location' => [
'method' => 'drop_array',
'friendly_name' => __('Location'),
'filter' => FILTER_CALLBACK,
'filter_options' => ['options' => 'sanitize_search_string'],
'default' => '-1',
'pageset' => true,
'array' => $locations,
'value' => '-1'
],
'host_id' => [
'method' => 'drop_callback',
'friendly_name' => __('Device'),
'filter' => FILTER_VALIDATE_INT,
'default' => '-1',
'class' => 'select2-callback',
'pageset' => true,
'request_vars' => 'location,site_id,graph_template_id',
'sql' => 'SELECT DISTINCT id, description AS name FROM host ORDER BY description',
'action' => 'ajax_hosts',
'id' => $host_id,
'value' => $hostname,
'on_change' => 'applyGraphFilter()'
]
],
[
'rfilter' => [
'method' => 'textbox',
'friendly_name' => __('Search'),
'filter' => FILTER_VALIDATE_IS_REGEX,
'placeholder' => __('Enter a search term'),
'size' => '55',
'default' => '',
'pageset' => true,
'max_length' => '120',
'value' => ''
],
'graph_template_id' => [
'method' => 'drop_multi',
'friendly_name' => __('Template'),
'filter' => FILTER_VALIDATE_REGEXP,
'filter_options' => ['options' => ['regexp' => '/^(cg_[0-9]+|dq_[0-9]+|-?[0-9]+)(,(cg_[0-9]+|dq_[0-9]+|-?[0-9]+))*$/']],
'default' => '-1',
'dynamic' => false,
'class' => 'select2-multi-count',
'select_all_text' => __('All Graph Templates'),
'select_count_text' => __('Templates Selected'),
'select_all_value' => '-1',
'pageset' => true,
'array' => $normalized_templates,
'value' => gnrv('graph_template_id')
],
'graphs' => [
'method' => 'drop_array',
'friendly_name' => __('Graphs'),
'filter' => FILTER_CALLBACK,
'filter_options' => ['options' => 'html_graph_validate_page_size'],
'default' => '-1',
'pageset' => true,
'array' => $item_rows,
'value' => ''
],
'graph_list' => [
'method' => 'validate',
'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
'default' => ''
],
'graph_add' => [
'method' => 'validate',
'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
'default' => ''
],
'graph_remove' => [
'method' => 'validate',
'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
'default' => ''
],
'style' => [
'method' => 'validate',
'filter' => FILTER_DEFAULT,
'default' => ''
]
]
],
'buttons' => [
'go' => [
'method' => 'submit',
'display' => __('Go'),
'title' => __('Apply filter to table'),
],
'clear' => [
'method' => 'button',
'display' => __('Clear'),
'title' => __('Reset filter to default values'),
],
'view' => [
'method' => 'button',
'display' => __('View'),
'title' => __('View the selected Graphs in Preview mode'),
'callback' => 'viewGraphs()'
]
]
];
$reports = db_fetch_assoc_prepared('SELECT *
FROM reports
WHERE user_id = ?',
[$_SESSION[SESS_USER_ID]]);
if (cacti_sizeof($reports)) {
$filters['buttons']['report'] = [
'method' => 'button',
'display' => __('Report'),
'title' => __('Place the selected Graphs on an existing Report'),
'callback' => 'addReport()'
];
}
return $filters;
}
function draw_listview_filter(bool $render = false) : void {
$header = __('Graph List View Filters') . (isrv('style') && grv('style') != '' ? ' ' . __('[ Custom Graph List Applied - Filtering from List ]') : '');
// create the page filter
$filters = create_listview_filter('sess_lview');
$pageFilter = new CactiTableFilter($header, 'graph_view.php?action=list', 'form_graph_view', 'sess_lview');
$pageFilter->rows_label = __('Graphs');
$pageFilter->form_method = 'post';
$pageFilter->set_filter_array($filters);
if ($render) {
$pageFilter->render();
} else {
$pageFilter->sanitize();
}
}
function html_graph_list_view() : void {
global $graph_timespans, $alignment, $graph_sources, $item_rows;
if (!is_view_allowed('show_list')) {
header('Location: permission_denied.php');
exit;
}
// reset the graph list on a new viewing
if (!isrv('page')) {
srv('graph_list', '');
}
top_graph_header();
draw_listview_filter(true);
if (grv('graphs') == '-1') {
$graph_rows = read_config_option('num_rows_table');
} else {
$graph_rows = grv('graphs');
}
$graph_rows = html_graph_validate_page_size($graph_rows);
if ($graph_rows === false || $graph_rows == -1) {
$graph_rows = 20;
}
// check to see if site_id and location are mismatched
if (grv('site_id') >= 0) {
if (grv('location') != '0' && grv('location') != '-1') {
$exists = db_fetch_cell_prepared('SELECT COUNT(*)
FROM host
WHERE site_id = ?
AND location = ?',
[grv('site_id'), grv('location')]);
if (!$exists) {
srv('location', '-1');
}
}
}
$graph_list = [];
// save selected graphs into url
if (!ierv('graph_list')) {
foreach (explode(',', grv('graph_list')) as $item) {
if (is_numeric($item)) {
$graph_list[$item] = 1;
}
}
}
if (!ierv('graph_add')) {
foreach (explode(',', grv('graph_add')) as $item) {
if (is_numeric($item)) {
$graph_list[$item] = 1;
}
}
}
// remove items
if (!ierv('graph_remove')) {
foreach (explode(',', grv('graph_remove')) as $item) {
unset($graph_list[$item]);
}
}
// update the revised graph list session variable
if (cacti_sizeof($graph_list)) {
srv('graph_list', implode(',', array_keys($graph_list)));
}
load_current_session_value('graph_list', 'sess_gl_graph_list', '');
$reports = db_fetch_assoc_prepared('SELECT *
FROM reports
WHERE user_id = ?',
[$_SESSION[SESS_USER_ID]]);
form_start('graph_view.php', 'chk');
form_hidden_box('style', 'selective', '');
form_hidden_box('action', 'list', '');
form_hidden_box('graph_add', '', '');
form_hidden_box('graph_remove', '', '');
form_hidden_box('graph_list', grv('graph_list'), '');
// create filter for sql
$sql_where = '';
if (!ierv('rfilter')) {
$sql_where .= ' (gtg.title_cache RLIKE ' . db_qstr(grv('rfilter'));
if (ctype_digit(grv('rfilter'))) {
$sql_where .= ' OR gl.id = ' . (int) grv('rfilter');
}
$sql_where .= ')';
}
if (!ierv('site_id') && grv('site_id') > 0) {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' h.site_id=' . grv('site_id');
} elseif (ierv('site_id')) {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' h.site_id=0';
}
if (!ierv('host_id') && grv('host_id') > 0) {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' gl.host_id=' . gfrv('host_id');
} elseif (ierv('host_id')) {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' gl.host_id=0';
}
if (grv('location') != '' && grv('location') != '-1' && grv('location') != '0') {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' h.location = ' . db_qstr(grv('location'));
} elseif (grv('location') == '0') {
$sql_where .= ($sql_where == '' ? '' : ' AND') . ' h.location = ""';
}
if (!ierv('graph_template_id') && grv('graph_template_id') != '-1' && grv('graph_template_id') != '0') {
$graph_templates = html_transform_graph_template_ids(grv('graph_template_id'));
$sql_where .= ($sql_where != '' ? ' AND ' : '') . ' ' . db_in_clause('gl.graph_template_id', $graph_templates);
} elseif (grv('graph_template_id') == '0') {
$sql_where .= ($sql_where != '' ? ' AND ' : '') . ' ' . db_in_clause('gl.graph_template_id', 0);
}
$total_rows = 0;
$sql_limit = ($graph_rows * (grv('page') - 1)) . ',' . $graph_rows;
$graphs = get_allowed_graphs($sql_where, 'gtg.title_cache', $sql_limit, $total_rows);
$nav = html_nav_bar('graph_view.php?action=list', MAX_DISPLAY_PAGES, grv('page'), $graph_rows, $total_rows, 5, __('Graphs'), 'page', 'main');
print $nav;
html_start_box('', '100%', false, 3, 'center', '');
if (is_realm_allowed(10)) {
$display_text = [
'title_cache' => [
'display' => __('Graph Name'),
'align' => 'left',
'tip' => __('The Title of this Graph. Generally programmatically generated from the Graph Template definition or Suggested Naming rules. The max length of the Title is controlled under Settings->Visual.')
],
'site_name' => [
'display' => __('Site Name'),
'align' => 'left',
'tip' => __('The Site Name for this Graph.')
],
'location' => [
'display' => __('Site Location'),
'align' => 'left',
'tip' => __('The Site Location for this Graph.')
],
'local_graph_id' => [
'display' => __('Device'),
'align' => 'left',
'tip' => __('The device for this Graph.')
],
'source' => [
'display' => __('Source Type'),
'align' => 'right',
'tip' => __('The underlying source that this Graph was based upon.')
],
'name' => [
'display' => __('Source Name'),
'align' => 'left',
'tip' => __('The Graph Template or Data Query that this Graph was based upon.')
],
'height' => [
'display' => __('Size'),
'align' => 'left',
'tip' => __('The size of this Graph when not in Preview mode.')
]
];
} else {
$display_text = [
'title_cache' => [
'display' => __('Graph Name'),
'align' => 'left',
'tip' => __('The Title of this Graph. Generally programmatically generated from the Graph Template definition or Suggested Naming rules. The max length of the Title is controlled under Settings->Visual.')
],
'height' => [
'display' => __('Size'),
'align' => 'left',
'tip' => __('The size of this Graph when not in Preview mode.')
]
];
}
html_header_checkbox($display_text, false);
$i = 0;
if (cacti_sizeof($graphs)) {
foreach ($graphs as $graph) {
// we're escaping strings here, so no need to escape them on form_selectable_cell
$template_details = get_graph_template_details($graph['local_graph_id']);
if ($graph['graph_source'] == '0') { // Not Templated, customize graph source and template details.
$template_details = api_plugin_hook_function('customize_template_details', $template_details);
$graph = api_plugin_hook_function('customize_graph', $graph);
}
if (isset($template_details['graph_name'])) {
$graph['name'] = $template_details['graph_name'];
}
if (isset($template_details['graph_description'])) {
$graph['description'] = $template_details['graph_description'];
}
$current_page = get_current_page();
form_alternate_row('line' . $graph['local_graph_id'], true);
form_selectable_cell(filter_value($graph['title_cache'], grv('rfilter'), $current_page . '?action=view&local_graph_id=' . $graph['local_graph_id'] . '&rra_id=0'), $graph['local_graph_id']);
if (is_realm_allowed(10)) {
if ($graph['site_name'] != '') {
form_selectable_ecell($graph['site_name'], $graph['local_graph_id']);
} else {
form_selectable_ecell('-', $graph['local_graph_id']);
}
if ($graph['location'] != '') {
form_selectable_ecell($graph['location'], $graph['local_graph_id']);
} else {
form_selectable_ecell('-', $graph['local_graph_id']);
}
form_selectable_ecell($graph['description'], $graph['local_graph_id']);
form_selectable_cell(filter_value($graph_sources[$template_details['source']], grv('rfilter')), $graph['local_graph_id'], '', 'right');
form_selectable_cell(filter_value($template_details['name'], grv('rfilter'), $template_details['url']), $graph['local_graph_id'], '', 'left');
}
form_selectable_ecell($graph['height'] . 'x' . $graph['width'], $graph['local_graph_id']);
form_checkbox_cell($graph['title_cache'], $graph['local_graph_id']);
form_end_row();
}
}
html_end_box(false);
if (cacti_sizeof($graphs)) {
print $nav;
}
form_end();
$report_text = '';
if (cacti_sizeof($reports)) {
$report_text = '<div id="addGraphs" style="display:none;">';
$report_text .= '<p>' . __('Select the Report to add the selected Graphs to.') . '</p>';
$report_text .= '<div class="cactiTable">';
$report_text .= '<div class="filterRow">';
$report_text .= '<div class="filterColumn">' . __('Report Name') . '</div>';
$report_text .= '<div class="filterColumn"><select id="report_id">';
foreach ($reports as $report) {
$report_text .= '<option value="' . $report['id'] . '">' . htmle($report['name']) . '</option>';
}
$report_text .= '</select></div></div>';
$report_text .= '<div class="filterRow">';
$report_text .= '<div class="filterColumn">' . __('Timespan') . '</div>';
$report_text .= '<div class="filterColumn"><select id="timespan">';
foreach ($graph_timespans as $key => $value) {
$report_text .= '<option value="' . $key . '"' . ($key == read_user_setting('default_timespan') ? ' selected' : '') . '>' . $value . '</option>';
}
$report_text .= '</select></div></div>';
$report_text .= '<div class="filterRow">';
$report_text .= '<div class="filterColumn">' . __('Align') . '</div>';
$report_text .= '<div class="filterColumn"><select id="align">';
foreach ($alignment as $key => $value) {
$report_text .= '<option value="' . $key . '"' . ($key == REPORTS_ALIGN_CENTER ? ' selected' : '') . '>' . $value . '</option>';
}
$report_text .= '</select></div></div>';
$report_text .= '</div></div>';
}
?>
<div class='break'></div>
<div class='cactiTable'>
<div style='float:left'><img src='images/arrow.gif' alt=''>&nbsp;</div>
<div style='float:right'><button type='button' class='ui-button ui-corner-all ui-widget' title='<?php print __esc('View Graphs'); ?>' onClick='viewGraphs()'><?php print __esc('View'); ?></button></div>
</div>
<?php
print $report_text;
$graph_list_js = [];
$graph_list_js = sanitize_graph_id_list((string) grv('graph_list'));
$graph_list_csv = implode(',', $graph_list_js);
?>
<script type='text/javascript'>
refreshMSeconds=999999999;
refreshFunction = 'refreshGraphs()';
var graph_list_array = <?php print json_encode($graph_list_js); ?>;
function initializeChecks() {
for (var i = 0; i < graph_list_array.length; i++) {
$('#line'+graph_list_array[i]).addClass('selected');
$('#chk_'+graph_list_array[i]).prop('checked', true);
$('#chk_'+graph_list_array[i]).parent().addClass('selected');
}
}
function viewGraphs() {
graphList = $('#graph_list').val();
$('input[id^=chk_]').each(function(data) {
graphID = $(this).attr('id').replace('chk_','');
if ($(this).is(':checked')) {
graphList += (graphList.length > 0 ? ',':'') + graphID;
}
});
$('#graph_list').val(graphList);
strURL = urlPath+'graph_view.php?action=preview';
$('#chk').find('select, input').each(function() {
switch($(this).attr('id')) {
case 'rfilter':
strURL += '&' + $(this).attr('id') + '=' + base64_encode($(this).val());
break;
case 'graph_template_id':
case 'host_id':
case 'graph_add':
case 'graph_remove':
case 'graph_list':
case 'style':
case 'csrf_magic':
strURL += '&' + $(this).attr('id') + '=' + $(this).val();
break;
default:
break;
}
});
strURL += '&reset=true';
loadUrl({url:strURL})
$('#breadcrumbs').empty().html('<li><a href="' + urlPath + 'graph_view.php?action=preview"><?php print __('Preview Mode'); ?></a></li>');
$('#listview').removeClass('selected');
$('#preview').addClass('selected');
}
function url_graph(strNavURL) {
if ($('#action').val() == 'list') {
var strURL = '';
var strAdd = '';
var strDel = '';
$('input[id^=chk_]').each(function(data) {
graphID = $(this).attr('id').replace('chk_','');
if ($(this).is(':checked')) {
strAdd += (strAdd.length > 0 ? ',':'') + graphID;
} else if (graphChecked(graphID)) {
strDel += (strDel.length > 0 ? ',':'') + graphID;
}
});
strURL = '&demon=1&graph_list=<?php print $graph_list_csv; ?>&graph_add=' + strAdd + '&graph_remove=' + strDel;
return strNavURL + strURL;
} else {
return strNavURL;
}
}
function graphChecked(graph_id) {
for(var i = 0; i < graph_list_array.length; i++) {
if (graph_list_array[i] == graph_id) {
return true;
}
}
return false;
}
function addReport() {
$('#addGraphs').dialog({
title: '<?php print __('Add Selected Graphs to Report'); ?>',
minHeight: 80,
minWidth: 400,
modal: false,
resizable: false,
draggable: false,
buttons: [
{
text: '<?php print __('Cancel'); ?>',
click: function() {
$(this).dialog('close');
}
},
{
text: '<?php print __('Ok'); ?>',
click: function() {
graphList = $('#graph_list').val();
$('input[id^=chk_]').each(function(data) {
graphID = $(this).attr('id').replace('chk_','');
if ($(this).is(':checked')) {
graphList += (graphList.length > 0 ? ',':'') + graphID;
}
});
$('#graph_list').val(graphList);
$(this).dialog('close');
strURL = 'graph_view.php?action=ajax_reports' +
'&header=false' +
'&report_id=' + $('#report_id').val() +
'&timespan=' + $('#timespan').val() +
'&align=' + $('#align').val() +
'&graph_list=' + $('#graph_list').val();
loadUrl({url:strURL});
}
}
],
open: function() {
$('.ui-dialog').css('z-index', 99);
$('.ui-widget-overlay').css('z-index', 98);
},
close: function() {
$('[title]').each(function() {
if ($(this).tooltip('instance')) {
$(this).tooltip('close');
}
});
}
});
}
$(function() {
pageAction = 'list';
initializeChecks();
$('#addreport').click(function() {
addReport();
});
$('#chk').unbind().on('submit', function(event) {
event.preventDefault();
applyFilter();
});
});
</script>
<?php
bottom_footer();
}
function html_graph_update_timespan() : void {
if (isrv('date1')) {
$_SESSION['sess_current_date1'] = grv('date1');
}
if (isrv('date2')) {
$_SESSION['sess_current_date2'] = grv('date2');
}
if (isrv('predefined_timespan')) {
$return = [
'date1' => $_SESSION['sess_current_date1'],
'date2' => $_SESSION['sess_current_date2'],
'timestamp1' => $_SESSION['sess_current_timespan_begin_now'],
'timestamp2' => $_SESSION['sess_current_timespan_end_now'],
];
print json_encode($return);
}
}
function html_graph_get_reports() : void {
// Add to a report
gfrv('report_id');
gfrv('timespan');
gfrv('align');
if (isrv('graph_list')) {
$items = explode(',', grv('graph_list'));
if (cacti_sizeof($items)) {
$good = true;
foreach ($items as $item) {
if (!reports_add_graphs(gfrv('report_id'), intval($item), grv('timespan'), grv('align'))) {
raise_message('reports_add_error');
$good = false;
break;
}
}
if ($good) {
raise_message('reports_graphs_added');
}
}
} else {
raise_message('reports_no_graph');
}
header('Location: graph_view.php?action=list');
}
function html_graph_single_validate() : void {
// ================= input validation =================
gfrv('rra_id', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^([0-9]+|all)$/']]);
gfrv('local_graph_id');
gfrv('graph_end');
gfrv('graph_start');
gfrv('view_type', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^([a-zA-Z0-9]+)$/']]);
gfrv('business_hours', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^([a-zA-Z0-9]+)$/']]);
// ====================================================
if (isrv('business_hours')) {
$_SESSION['sess_business_hours'] = grv('business_hours');
} elseif (isset($_SESSION['sess_business_hours'])) {
srv('business_hours', $_SESSION['sess_business_hours']);
}
if (!isrv('rra_id')) {
srv('rra_id', 'all');
}
}
function html_graph_check_access() : void {
$exists = db_fetch_cell_prepared('SELECT local_graph_id
FROM graph_templates_graph
WHERE local_graph_id = ?',
[grv('local_graph_id')]
);
// make sure the graph requested exists (sanity)
if (!$exists) {
print '<strong><font class="txtErrorTextBox">' . __('GRAPH DOES NOT EXIST') . '</font></strong>';
bottom_footer();
exit;
}
// take graph permissions into account here
if (!is_graph_allowed(grv('local_graph_id'))) {
header('Location: permission_denied.php');
exit;
}
}
function html_graph_get_info() : array {
$rras = [];
$graph_title = null;
if (grv('rra_id') == 'all' || ierv('rra_id')) {
$sql_where = ' AND dspr.id IS NOT NULL';
} else {
$sql_where = ' AND dspr.id=' . grv('rra_id');
}
$rras = get_associated_rras(grv('local_graph_id'), $sql_where);
$graph_title = get_graph_title(grv('local_graph_id'));
return ['rras' => $rras, 'title' => $graph_title];
}
function html_graph_single_view() : void {
html_graph_single_validate();
html_graph_check_access();
$info = html_graph_get_info();
$rras = $info['rras'];
$graph_title = $info['title'];
$current_page = get_current_page();
if ($current_page == 'graph.php') {
top_header();
} elseif ($current_page == 'graphs.php') {
top_header();
} elseif ($current_page == 'graph_view.php') {
top_graph_header();
} else {
general_header();
}
if (str_contains(grv('action'), 'tree')) {
$suffix = 'tree';
} else {
$suffix = 'preview';
}
print "<div class='cactiTable graphDetailView'>";
html_start_box(__esc('Graph Utility View for Graph: %s', $graph_title), '100%', true, 3, 'center', '');
api_plugin_hook_function(
'page_buttons',
[
'lgid' => grv('local_graph_id'),
'leafid' => '', // $leaf_id,
'mode' => 'mrtg',
'rraid' => grv('rra_id')
]
);
$graph = db_fetch_row_prepared('SELECT gtg.local_graph_id, width, height, title_cache, gtg.graph_template_id, h.id AS host_id, h.disabled
FROM graph_templates_graph AS gtg
INNER JOIN graph_local AS gl
ON gtg.local_graph_id = gl.id
LEFT JOIN host AS h
ON gl.host_id = h.id
WHERE gtg.local_graph_id = ?',
[grv('local_graph_id')]);
$graph_template_id = $graph['graph_template_id'];
$i = 0;
if (cacti_sizeof($rras)) {
$graph_end = time() - 30;
print '<div class=\'graphPage\'>';
foreach ($rras as $rra) {
if (!empty($rra['timespan'])) {
$graph_start = $graph_end - $rra['timespan'];
} else {
$graph_start = $graph_end - ($rra['step'] * $rra['rows'] * $rra['steps']);
}
$aggregate_url = aggregate_build_children_url(grv('local_graph_id'), $graph_start, $graph_end, $rra['id']);
print "<div class='graphWrapperOuter cols1' data-disabled='" . ($graph['disabled'] == 'on' ? 'true' : 'false') . "'>";
print '<div>';
print "<div class='graphWrapper' id='wrapper_" . $graph['local_graph_id'] . "' graph_id='" . $graph['local_graph_id'] . "' rra_id='" . $rra['id'] . "' graph_width='" . $graph['width'] . "' graph_height='" . $graph['height'] . "' graph_start='" . $graph_start . "' graph_end='" . $graph_end . "' title_font_size='" . ((read_user_setting('custom_fonts') == 'on') ? read_user_setting('title_size') : read_config_option('title_size')) . "'></div>";
if (is_realm_allowed(27)) {
print "<div id='dd" . grv('local_graph_id') . "' style='vertical-align:top;' class='graphDrillDown noprint'>";
print "<a class='iconLink utils' href='#' id='graph_" . grv('local_graph_id') . "_util' graph_start='" . $graph_start . "' graph_end='" . $graph_end . "' rra_id='" . $rra['id'] . "'>";
print "<i class='drillDown ti ti-settings-filled actionCog' title='" . __esc('Graph Details, Zooming and Debugging Utilities') . "'></i></a><br>";
print "<a id='graph_" . $rra['id'] . "_csv' class='iconLink csv' href='" . htmle(CACTI_PATH_URL . 'graph_xport.php?local_graph_id=' . grv('local_graph_id') . '&rra_id=' . $rra['id'] . '&view_type=' . grv('view_type') . '&graph_start=' . $graph_start . '&graph_end=' . $graph_end) . "'>";
print "<i class='drillDown ti ti-file-type-csv fileCSV' title='" . __esc('CSV Export') . "'></i></a><br>";
if (is_realm_allowed(10) && $graph_template_id > 0) {
print "<a class='iconLink' role='link' href='" . htmle(CACTI_PATH_URL . 'graph_templates.php?action=template_edit&id=' . $graph_template_id) . "'>";
print "<i class='drillDown ti ti-edit editTemplate' title='" . __esc('Edit Graph Template') . "'></i></a>";
print '<br>';
}
if (read_config_option('realtime_enabled') == 'on' || is_realm_allowed(25)) {
print "<a class='iconLink' href='#' onclick=\"window.open('" . CACTI_PATH_URL . 'graph_realtime.php?top=0&left=0&local_graph_id=' . grv('local_graph_id') . "', 'popup_" . grv('local_graph_id') . "', 'directories=no,toolbar=no,menubar=no,resizable=yes,location=no,scrollbars=no,status=no,titlebar=no,width=650,height=300');return false\"><i class='drillDown ti ti-chart-area realTime' title='" . __esc('Click to view just this Graph in Real-time') . "'></i></a><br>";
}
print($aggregate_url != '' ? $aggregate_url : '');
api_plugin_hook('graph_buttons', ['hook' => 'view', 'local_graph_id' => grv('local_graph_id'), 'rra' => $rra['id'], 'view_type' => grv('view_type')]);
print '</div>';
}
print '</div><div>' . htmle($rra['name']) . '</div>';
print "<div><input type='hidden' id='thumbnails' value='" . htmle(grv('thumbnails')) . "'></div>";
print "<div><input type='hidden' id='business_hours' value='" . htmle(grv('business_hours')) . "'></div>";
print '</div>';
$i++;
}
print '</div>';
api_plugin_hook_function('tree_view_page_end');
}
?>
<script type='text/javascript'>
var suffix = <?php print json_encode((string) $suffix); ?>;
var originalWidth = null;
var refreshTime = <?php print read_user_setting('page_refresh') * 1000; ?>;
var graphTimeout = null;
function initializeGraph() {
$('a.iconLink').tooltip();
$('.graphWrapper').each(function() {
var itemWrapper = $(this);
var itemGraph = $(this).find('.graphimage');
if (itemGraph.length != 1) {
itemGraph = itemWrapper;
}
var graph_id = itemGraph.attr('graph_id');
var rra_id = itemGraph.attr('rra_id');
var graph_height = itemGraph.attr('graph_height');
var graph_width = itemGraph.attr('graph_width');
var graph_start = itemGraph.attr('graph_start');
var graph_end = itemGraph.attr('graph_end');
$.getJSON(urlPath + 'graph_json.php?action=properties-' + suffix +
'&local_graph_id=' + graph_id +
'&graph_height=' + graph_height +
'&graph_start=' + graph_start +
'&graph_end=' + graph_end +
'&rra_id=' + rra_id +
'&graph_width=' + graph_width +
'&disable_cache=true' +
'&business_hours=' + $('#business_hours').val() +
($('#thumbnails').val() == 'true' ? '&graph_nolegend=true' : ''))
.done(function(data) {
wrapper = $('#wrapper_' + data.local_graph_id + '[rra_id=\'' + data.rra_id + '\']');
wrapper.html(
"<img class='graphimage' id='graph_" + data.local_graph_id +
"' src='data:image/" + data.type + ";base64," + data.image +
"' rra_id='" + data.rra_id +
"' graph_type='" + data.type +
"' graph_id='" + data.local_graph_id +
"' graph_start='" + data.graph_start +
"' graph_end='" + data.graph_end +
"' graph_left='" + data.graph_left +
"' graph_top='" + data.graph_top +
"' graph_width='" + data.graph_width +
"' graph_height='" + data.graph_height +
"' image_width='" + data.image_width +
"' image_height='" + data.image_height +
"' canvas_left='" + data.graph_left +
"' canvas_top='" + data.graph_top +
"' canvas_width='" + data.graph_width +
"' canvas_height='" + data.graph_height +
"' width='" + data.image_width +
"' height='" + data.image_height +
"' value_min='" + data.value_min +
"' value_max='" + data.value_max + "'>"
);
$('#graph_start').val(data.graph_start);
$('#graph_end').val(data.graph_end);
var gr_location = '#graph_' + data.local_graph_id;
if (data.rra_id > 0) {
gr_location += '[rra_id=\'' + data.rra_id + '\']';
}
$(gr_location).zoom({
inputfieldStartTime: 'date1',
inputfieldEndTime: 'date2',
serverTimeOffset: <?php print date('Z'); ?>
});
responsiveResizeGraphs(true);
})
.fail(function(data) {
getPresentHTTPError(data);
});
});
$('a[id$="_util"]').rebind('click', function() {
graph_id = $(this).attr('id').replace('graph_', '').replace('_util', '');
rra_id = $(this).attr('rra_id');
graph_start = $(this).attr('graph_start');
graph_end = $(this).attr('graph_end');
var tree = $('#navigation').length ? true:false;
loadUrl({
url: pageName + '?' +
'action=zoom' + (tree ? '-tree':'-preview') +
'&local_graph_id=' + graph_id +
'&rra_id=' + rra_id +
'&graph_start=' + graph_start +
'&graph_end=' + graph_end
});
});
$('a[id$="_csv"]').each(function() {
$(this).rebind('click', function(event) {
event.preventDefault();
event.stopPropagation();
document.location = $(this).attr('href');
Pace.stop();
});
});
graphTimeout = setTimeout(initializeGraph, refreshTime);
}
$(function() {
pageAction = 'graph';
if (graphTimeout !== null) {
clearTimeout(graphTimeout);
}
initializeGraph();
$('#navigation').show();
$('#navigation_right').show();
});
</script>
<?php
html_end_box(false, true);
print '</div>';
bottom_footer();
}
/**
* Parse and sanitize a comma-separated graph list into validated integer IDs.
*
* @param string $csv_list Comma-separated list of graph IDs (from request var)
*
* @return array Array of unique positive integer graph IDs
*/
function sanitize_graph_id_list(string $csv_list): array {
$result = [];
foreach (explode(',', $csv_list) as $item) {
$item = trim($item);
if ($item !== '' && ctype_digit($item)) {
$graph_id = (int) $item;
if ($graph_id > 0) {
$result[] = $graph_id;
}
}
}
return array_values(array_unique($result));
}
function html_graph_zoom() : void {
html_graph_single_validate();
html_graph_check_access();
$info = html_graph_get_info();
$rras = $info['rras'];
$graph_title = $info['title'];
if (str_contains(gnrv('action'), 'tree')) {
$suffix = 'tree';
} else {
$suffix = 'preview';
}
// find the maximum time span a graph can show
$max_timespan = 1;
if (cacti_sizeof($rras)) {
foreach ($rras as $rra) {
if ($rra['steps'] * $rra['rows'] * $rra['rrd_step'] > $max_timespan) {
$max_timespan = $rra['steps'] * $rra['rows'] * $rra['rrd_step'];
}
}
}
// fetch information for the current RRA
if (isrv('rra_id') && grv('rra_id') > 0) {
$rra = db_fetch_row_prepared('SELECT dspr.id, step, steps, dspr.name, `rows`
FROM data_source_profiles_rra AS dspr
INNER JOIN data_source_profiles AS dsp
ON dsp.id=dspr.data_source_profile_id
WHERE dspr.id = ?', [grv('rra_id')]);
$rra['timespan'] = $rra['steps'] * $rra['step'] * $rra['rows'];
} else {
$rra = db_fetch_row_prepared('SELECT dspr.id, step, steps, dspr.name, `rows`
FROM data_source_profiles_rra AS dspr
INNER JOIN data_source_profiles AS dsp
ON dsp.id=dspr.data_source_profile_id
WHERE dspr.id = ?', [$rras[0]['id']]);
$rra['timespan'] = $rra['steps'] * $rra['step'] * $rra['rows'];
}
// define the time span, which decides which rra to use
$timespan = - ($rra['timespan']);
// find the step and how often this graph is updated with new data
$ds_step = db_fetch_cell_prepared('SELECT
data_template_data.rrd_step
FROM (data_template_data, data_template_rrd, graph_templates_item)
WHERE graph_templates_item.task_item_id = data_template_rrd.id
AND data_template_rrd.local_data_id = data_template_data.local_data_id
AND graph_templates_item.local_graph_id = ?
LIMIT 0,1', [grv('local_graph_id')]);
$ds_step = empty($ds_step) ? 300 : $ds_step;
$seconds_between_graph_updates = ($ds_step * $rra['steps']);
$now = time();
if (isrv('graph_end') && (grv('graph_end') <= $now - $seconds_between_graph_updates)) {
$graph_end = grv('graph_end');
} else {
$graph_end = $now - $seconds_between_graph_updates;
}
if (isrv('graph_start')) {
if (($graph_end - grv('graph_start')) > $max_timespan) {
$graph_start = $now - $max_timespan;
} else {
$graph_start = grv('graph_start');
}
} else {
$graph_start = $now + $timespan;
}
// required for zoom out function
if ($graph_start == $graph_end) {
$graph_start--;
}
$graph = db_fetch_row_prepared('SELECT gtg.local_graph_id, width, height, title_cache, gtg.graph_template_id, h.id AS host_id, h.disabled
FROM graph_templates_graph AS gtg
INNER JOIN graph_local AS gl
ON gtg.local_graph_id = gl.id
LEFT JOIN host AS h
ON gl.host_id = h.id
WHERE gtg.local_graph_id = ?',
[grv('local_graph_id')]);
$graph_height = $graph['height'];
$graph_width = $graph['width'];
$graph_template_id = $graph['graph_template_id'];
if (read_user_setting('custom_fonts') == 'on' && read_user_setting('title_size') != '') {
$title_font_size = read_user_setting('title_size');
} elseif (read_config_option('title_size') != '') {
$title_font_size = read_config_option('title_size');
} else {
$title_font_size = 10;
}
$current_page = get_current_page();
if ($current_page == 'graph.php') {
top_header();
} elseif ($current_page == 'graphs.php') {
top_header();
} elseif ($current_page == 'graph_view.php') {
top_graph_header();
} else {
general_header();
}
print "<div class='cactiTable'>";
html_start_box(__esc('Graph Utility View for Graph: %s', $graph_title), '100%', true, 3, 'center', '');
?>
<div class='graphPage'>
<div class='graphWrapperOuter cols1' data-disabled='<?php print($graph['disabled'] == 'on' ? 'true' : 'false'); ?>'>
<div>
<div class='graphWrapper' id='wrapper_<?php print $graph['local_graph_id'] ?>' graph_id='<?php print $graph['local_graph_id']; ?>' rra_id='<?php print $rra['id']; ?>' graph_width='<?= $graph_width ?>' graph_height='<?= $graph_height ?>' title_font_size='<?= $title_font_size ?>'></div>
<?php if (is_realm_allowed(27)) { ?>
<div id='dd<?php print $graph['local_graph_id']; ?>' style='vertical-align:top;' class='graphDrillDown noprint'>
<a href='#' id='graph_<?php print $graph['local_graph_id']; ?>_properties' class='iconLink properties'>
<i class='drillDown ti ti-tool viewSources' title='<?php print __esc('Graph Source/Properties'); ?>'></i>
</a>
<br>
<a href='#' id='graph_<?php print $graph['local_graph_id']; ?>_csv' class='iconLink properties'>
<i class='drillDown ti ti-info-circle-filled fileCSV' title='<?php print __esc('Graph Data'); ?>'></i>
</a>
<br>
<?php
if (is_realm_allowed(10) && $graph_template_id > 0) {
print "<a class='iconLink' role='link' href='" . htmle(CACTI_PATH_URL . 'graph_templates.php?action=template_edit&id=' . $graph_template_id) . "'><i class='drillDown ti ti-edit editTemplate' title='" . __esc('Edit Graph Template') . "'></i></a>";
print '<br>';
}
api_plugin_hook('graph_buttons', ['hook' => 'zoom', 'local_graph_id' => grv('local_graph_id'), 'rra' => grv('rra_id'), 'view_type' => grv('view_type')]); ?>
</div>
<?php print(read_user_setting('show_graph_title') == 'on' ? '<div>' . htmle($graph['title_cache']) . '</div>' : ''); ?>
<?php } ?>
</div>
</div>
<div>
<input type='hidden' id='date1' value=''>
<input type='hidden' id='date2' value=''>
<input type='hidden' id='graph_start' value='<?php print $graph_start; ?>'>
<input type='hidden' id='graph_end' value='<?php print $graph_end; ?>'>
<input type='hidden' id='thumbnails' value='<?php print htmlerv('thumbnails'); ?>'>
<input type='hidden' id='business_hours' value='<?php print htmlerv('business_hours'); ?>'>
</div>
</div>
<?php
html_end_box(false, true);
?>
<div class='cactiTable'><div id='data'></div></div>
<script type='text/javascript'>
var suffix = <?php print json_encode((string) $suffix); ?>;
var graph_id = <?php print (int) grv('local_graph_id') . ";\n"; ?>
var rra_id = <?php print (int) grv('rra_id') . ";\n"; ?>
var graph_start = 0;
var graph_end = 0;
var graph_height = 0;
var graph_width = 0;
var props_on = false;
var graph_data_on = true;
function graphProperties() {
loadUrl({
url: urlPath + '<?php print $current_page; ?>' + '?action=properties-' + suffix +
'&local_graph_id=' + graph_id +
'&rra_id=<?php print (int) grv('rra_id'); ?>' +
'&view_type=<?php print rawurlencode((string) grv('view_type')); ?>' +
'&business_hours=' + $('#business_hours').val() +
'&thumbnails=' + $('#thumbnails').val(),
noState: true,
elementId: 'data',
});
props_on = true;
graph_data_on = false;
}
function graphXport() {
loadUrl({
url: urlPath + 'graph_xport.php?local_graph_id=' + graph_id +
'&rra_id=0' +
'&format=table' +
'&graph_start=' + $('#graph_start').val() +
'&graph_end=' + $('#graph_end').val(),
noState: true,
elementId: 'data',
funcEnd: 'graphXportFinalize',
});
props_on = false;
graph_data_on = true;
}
function graphXportFinalize(options, data) {
if (typeof resizeWrapper !== 'undefined') {
resizeWrapper();
}
$('.download').click(function(event) {
event.preventDefault;
graph_id = $(this).attr('id').replace('graph_', '');
document.location = urlPath + 'graph_xport.php?local_graph_id=' + graph_id + '&rra_id=0&view_type=tree&graph_start=' + $('#graph_start').val() + '&graph_end=' + $('#graph_end').val();
Pace.stop();
});
}
function initializeGraph() {
$('.graphWrapper').each(function() {
graph_id = $(this).attr('id').replace('wrapper_', '');
rra_id = $(this).attr('rra_id');
graph_height = $(this).attr('graph_height');
graph_width = $(this).attr('graph_width');
if (!(rra_id > 0)) {
rra_id = 0;
}
$.getJSON(urlPath + 'graph_json.php?rra_id=' + rra_id +
'&local_graph_id=' + graph_id +
'&graph_start=' + $('#graph_start').val() +
'&graph_end=' + $('#graph_end').val() +
'&graph_height=' + graph_height +
'&graph_width=' + graph_width +
'&disable_cache=true' +
'&business_hours=' + ($('#business_hours').val() == 'true' ? 'true' : 'false') +
($('#thumbnails').val() == 'true' ? '&graph_nolegend=true' : ''))
.done(function(data) {
$('#wrapper_' + data.local_graph_id).html(
"<img class='graphimage' id='graph_" + data.local_graph_id +
"' src='data:image/" + data.type + ";base64," + data.image +
"' rra_id='" + data.rra_id +
"' graph_type='" + data.type +
"' graph_id='" + data.local_graph_id +
"' graph_start='" + data.graph_start +
"' graph_end='" + data.graph_end +
"' graph_left='" + data.graph_left +
"' graph_top='" + data.graph_top +
"' graph_width='" + data.graph_width +
"' graph_height='" + data.graph_height +
"' image_width='" + data.image_width +
"' image_height='" + data.image_height +
"' canvas_left='" + data.graph_left +
"' canvas_top='" + data.graph_top +
"' canvas_width='" + data.graph_width +
"' canvas_height='" + data.graph_height +
"' width='" + data.image_width +
"' height='" + data.image_height +
"' value_min='" + data.value_min +
"' value_max='" + data.value_max + "'>"
);
$('#graph_start').val(data.graph_start);
$('#graph_end').val(data.graph_end);
var gr_location = '#graph_' + data.local_graph_id;
if (data.rra_id > 0) {
gr_location += '[rra_id=\'' + data.rra_id + '\']';
}
$(gr_location).zoom({
inputfieldStartTime: 'date1',
inputfieldEndTime: 'date2',
serverTimeOffset: <?php print date('Z'); ?>
});
if (graph_data_on) {
graphXport();
} else if (props_on) {
graphProperties();
}
responsiveResizeGraphs(true);
})
.fail(function(data) {
getPresentHTTPError(data);
});
});
$('a[id$="_properties"]').unbind('click').click(function() {
graph_id = $(this).attr('id').replace('graph_', '').replace('_properties', '');
graphProperties();
});
$('a[id$="_csv"]').unbind('click').click(function() {
graph_id = $(this).attr('id').replace('graph_', '').replace('_csv', '');
graphXport();
});
}
$(function() {
pageAction = 'graph';
/* turn off the page refresh */
refreshMSeconds = 9999999;
clearTimeout(myRefresh);
initializeGraph();
$('#navigation').show();
$('#navigation_right').show();
$('a.iconLink').tooltip();
});
</script>
<?php
print '</div>';
bottom_footer();
}
function html_graph_properties() : void {
html_graph_single_validate();
html_graph_check_access();
$current_page = get_current_page();
if ($current_page == 'graph.php') {
top_header();
} elseif ($current_page == 'graphs.php') {
top_header();
} elseif ($current_page == 'graph_view.php') {
top_graph_header();
} else {
general_header();
}
$graph_data_array['print_source'] = true;
// override: graph start time (unix time)
if (!ierv('graph_start')) {
$graph_data_array['graph_start'] = grv('graph_start');
}
// override: graph end time (unix time)
if (!ierv('graph_end')) {
$graph_data_array['graph_end'] = grv('graph_end');
}
$graph_data_array['output_flag'] = RRDTOOL_OUTPUT_RETURN_STDERR;
$graph_data_array['print_source'] = 1;
?>
<br>
<div class='cactiTable'>
<div id="data" class='cactiTable'>
<div class='cactiTable'>
<span class='cactiTableTitleRow'><?php print __('RRDtool Command:'); ?></span>
<?php
$null_param = [];
print @rrdtool_function_graph(grv('local_graph_id'), grv('rra_id'), $graph_data_array, null, $null_param, $_SESSION[SESS_USER_ID]);
unset($graph_data_array['print_source']);
?>
<br>
<span class='cactiTableTitleRow'><?php print __('RRDtool Says:'); ?></span>
<span class='left'>
<?php
if (POLLER_ID == 1) {
print htmle((string) @rrdtool_function_graph(grv('local_graph_id'), grv('rra_id'), $graph_data_array, null, $null_param, $_SESSION[SESS_USER_ID]));
} else {
print __esc('Not Checked');
}
?>
</span>
</div>
</div>
</div>
<?php
bottom_footer();
}
/**
* Wrapper for plugins - it is used in mactrack, microtic, hmib, ...
*
* @return void
*/
function html_graph_validate_preview_request_vars() : void {
$filters = create_graphs_preview_filter('sess_pview');
$validate = [];
foreach ($filters['rows'] as $row) {
foreach ($row as $variable => $options) {
$validate[$variable] = $options;
}
}
validate_store_request_vars($validate, 'sess_pview');
}