mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
* security: batch 7 develop - consolidate remaining develop-only GHSA advisories (12 fixes + 8 regression confirmations) Fixes: f7jw (automation preview XSS), rp5g (db_column_exists LIKE), vx2m (automation pivot CASE WHEN), h5wg (plugin DDL identifiers), 929h (aggregate LIKE filter), mfw8 (add_graphs REGEXP), m49v (user_admin sort_column allowlist), mpfm (rrd error-image theme allowlist), m3fh (SNMPv3 control chars), cg45 (remember-me row revoke), xq26 (remember-me retention window), m67r (import xml_path containment). Confirmed already fixed on develop, pinned with regression tests: 4gph, 977w, mgcq, 5p79, cfhh, wfcf, pjq5, wp33. Excluded: 227p (1.2.x-only), mx5c (maintainer private branch). * security: address batch-7 review feedback (lexical-first xml_path containment, session-wide sort allowlist, connection-aware db_column_exists quoting, reject-not-normalize plugin DDL identifiers, behavior-based test names) * style: apply php-cs-fixer single-quote fix to api_automation pivot builders; add WSL dev-environment note to copilot instructions * fix phpstan string typing * test: refresh SQL interpolation baseline for db_column_exists db_qstr change (database.php 9->8) --------- Co-authored-by: ci <ci@local>
2180 lines
69 KiB
PHP
2180 lines
69 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/ |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
require('./include/auth.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/api_graph.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/api_tree.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/api_data_source.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/api_aggregate.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/data_query.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_tree.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_form_template.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/poller.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/reports.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/rrd.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/template.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/utility.php');
|
|
|
|
aggregate_prune_graphs();
|
|
|
|
$actions = [
|
|
1 => __('Delete'),
|
|
5 => __('Convert to Normal Graph'),
|
|
4 => __('Place Graphs on Report'),
|
|
2 => __('Migrate Aggregate to use a Template'),
|
|
3 => __('Create New Aggregate from Aggregates')
|
|
];
|
|
|
|
$agg_item_actions = [
|
|
10 => __('Associate with Aggregate'),
|
|
11 => __('Disassociate with Aggregate')
|
|
];
|
|
|
|
// set default action
|
|
set_default_action();
|
|
|
|
switch (grv('action')) {
|
|
case 'save':
|
|
form_save();
|
|
|
|
break;
|
|
case 'actions':
|
|
form_actions();
|
|
|
|
break;
|
|
case 'edit':
|
|
top_header();
|
|
graph_edit();
|
|
bottom_footer();
|
|
|
|
break;
|
|
case 'item_remove':
|
|
item_remove();
|
|
|
|
header('Location: aggregate_graphs.php?action=edit&id=' . gfrv('local_graph_id'));
|
|
|
|
break;
|
|
case 'item_edit':
|
|
top_header();
|
|
|
|
item_edit();
|
|
|
|
bottom_footer();
|
|
|
|
break;
|
|
case 'item_movedown':
|
|
item_movedown();
|
|
|
|
header('Location: aggregate_graphs.php?action=edit&id=' . gfrv('local_graph_id'));
|
|
|
|
break;
|
|
case 'item_moveup':
|
|
item_moveup();
|
|
|
|
header('Location: aggregate_graphs.php?action=edit&id=' . gfrv('local_graph_id'));
|
|
|
|
break;
|
|
default:
|
|
top_header();
|
|
aggregate_graph();
|
|
bottom_footer();
|
|
|
|
break;
|
|
}
|
|
|
|
function add_tree_names_to_actions_array() : void {
|
|
global $actions;
|
|
|
|
// add a list of tree names to the actions dropdown
|
|
$trees = db_fetch_assoc('SELECT id, name FROM graph_tree ORDER BY name');
|
|
|
|
if (cacti_sizeof($trees)) {
|
|
foreach ($trees as $tree) {
|
|
$actions['tr_' . $tree['id']] = __('Place on a Tree (%s)', $tree['name']);
|
|
}
|
|
}
|
|
}
|
|
|
|
function form_save() : void {
|
|
if (!isrv('save_component_graph') && !isrv('save_component_input') && !isrv('save_component_item')) {
|
|
header('Location: aggregate_graphs.php?action=edit&id=' . gfrv('id'));
|
|
|
|
exit();
|
|
}
|
|
|
|
if (isrv('save_component_graph')) {
|
|
// remember some often used values
|
|
$local_graph_id = gnrv('local_graph_id', 0);
|
|
$graph_template_id = gnrv('graph_template_id', 0);
|
|
$aggregate_template_id = gnrv('aggregate_template_id', 0);
|
|
$graph_title = form_input_validate(gnrv('title_format'), 'title_format', '', false, 3);
|
|
|
|
if (is_error_message()) {
|
|
raise_message(2);
|
|
header('Location: aggregate_graphs.php?action=edit&id=' . $local_graph_id);
|
|
|
|
exit();
|
|
}
|
|
|
|
// get the aggregate graph id
|
|
$aggregate_graph_id = db_fetch_cell_prepared('SELECT id
|
|
FROM aggregate_graphs
|
|
WHERE local_graph_id = ?', [$local_graph_id]);
|
|
|
|
// if user disabled template propagation we need to get graph data from form
|
|
if (!isrv('template_propogation')) {
|
|
$aggregate_template_id = 0;
|
|
$new_data = aggregate_validate_graph_params($_POST, false);
|
|
} else {
|
|
$new_data = [];
|
|
}
|
|
|
|
if (is_error_message()) {
|
|
raise_message(2);
|
|
header('Location: aggregate_graphs.php?action=edit&id=' . $local_graph_id);
|
|
|
|
exit();
|
|
}
|
|
|
|
// save graph data to cacti tables
|
|
$graph_templates_graph_id = aggregate_graph_templates_graph_save(
|
|
$local_graph_id,
|
|
$graph_template_id,
|
|
$graph_title,
|
|
$aggregate_template_id,
|
|
$new_data
|
|
);
|
|
|
|
// update title in aggregate graphs table
|
|
db_execute_prepared('UPDATE aggregate_graphs
|
|
SET title_format = ?
|
|
WHERE id = ?',
|
|
[$graph_title, $aggregate_graph_id]
|
|
);
|
|
|
|
/* next lets see if any of the aggregate has changed and save as applicable
|
|
* if the graph is templates, we can simply ignore. A simple check will
|
|
* determine if aggregation propagation is enabled
|
|
*/
|
|
if (!isrv('template_propogation')) {
|
|
// template propagation is disabled
|
|
$save = [];
|
|
|
|
$save['id'] = $aggregate_graph_id;
|
|
$save['aggregate_template_id'] = $aggregate_template_id;
|
|
$save['template_propogation'] = '';
|
|
$save['gprint_prefix'] = gnrv('gprint_prefix');
|
|
$save['gprint_format'] = isrv('gprint_format') ? 'on' : '';
|
|
$save['total_prefix'] = gnrv('total_prefix');
|
|
|
|
$save['total'] = gfrv('total');
|
|
$save['graph_type'] = gfrv('graph_type');
|
|
$save['total_type'] = gfrv('total_type');
|
|
$save['order_type'] = gfrv('order_type');
|
|
|
|
// see if anything changed, if so, we will have to push out the aggregate
|
|
if (!empty($aggregate_graph_id)) {
|
|
$old = db_fetch_row_prepared('SELECT *
|
|
FROM aggregate_graphs
|
|
WHERE id = ?',
|
|
[$aggregate_graph_id]
|
|
);
|
|
|
|
$save_me = 0;
|
|
|
|
$save_me += ($old['aggregate_template_id'] != $save['aggregate_template_id']);
|
|
$save_me += ($old['template_propogation'] != $save['template_propogation']);
|
|
$save_me += ($old['gprint_prefix'] != $save['gprint_prefix']);
|
|
$save_me += ($old['gprint_format'] != $save['gprint_format']);
|
|
$save_me += ($old['graph_type'] != $save['graph_type']);
|
|
$save_me += ($old['total'] != $save['total']);
|
|
$save_me += ($old['total_type'] != $save['total_type']);
|
|
$save_me += ($old['total_prefix'] != $save['total_prefix']);
|
|
$save_me += ($old['order_type'] != $save['order_type']);
|
|
|
|
if ($save_me) {
|
|
$aggregate_graph_id = sql_save($save, 'aggregate_graphs');
|
|
}
|
|
|
|
// save the template items now
|
|
// get existing item ids and sequences from graph template
|
|
$graph_templates_items = array_rekey(
|
|
db_fetch_assoc_prepared('SELECT id, sequence
|
|
FROM graph_templates_item
|
|
WHERE local_graph_id = 0
|
|
AND graph_template_id = ?
|
|
ORDER BY sequence', [$graph_template_id]),
|
|
'id',
|
|
['sequence']
|
|
);
|
|
|
|
// get existing aggregate template items
|
|
$aggregate_graph_items_old = array_rekey(
|
|
db_fetch_assoc_prepared('SELECT *
|
|
FROM aggregate_graphs_graph_item
|
|
WHERE aggregate_graph_id = ?
|
|
ORDER BY sequence', [$aggregate_graph_id]),
|
|
'graph_templates_item_id',
|
|
['aggregate_graph_id', 'graph_templates_item_id', 'sequence', 'color_template', 't_graph_type_id', 'graph_type_id', 't_cdef_id', 'cdef_id', 'item_skip', 'item_total']
|
|
);
|
|
|
|
// update graph template item values with posted values
|
|
aggregate_validate_graph_items($_POST, $graph_templates_items);
|
|
|
|
$items_changed = false;
|
|
$items_to_save = [];
|
|
$sequence = 1;
|
|
|
|
foreach ($graph_templates_items as $item_id => $data) {
|
|
$item_new = [];
|
|
$item_new['aggregate_graph_id'] = $aggregate_graph_id;
|
|
$item_new['graph_templates_item_id'] = $item_id;
|
|
|
|
$item_new['color_template'] = isset($data['color_template']) ? $data['color_template'] : 0;
|
|
$item_new['item_skip'] = isset($data['item_skip']) ? 'on' : '';
|
|
$item_new['item_total'] = isset($data['item_total']) ? 'on' : '';
|
|
$item_new['sequence'] = $sequence;
|
|
|
|
// compare with old item to see if we need to push out.
|
|
if (!isset($aggregate_graph_items_old[$item_id])) {
|
|
// this item does not yet exist
|
|
$items_changed = true;
|
|
} else {
|
|
// compare data from user to data from DB
|
|
foreach ($item_new as $field => $new_value) {
|
|
if ($aggregate_graph_items_old[$item_id][$field] != $new_value) {
|
|
$items_changed = true;
|
|
}
|
|
}
|
|
// fill in missing fields with db values
|
|
$item_new = array_merge($aggregate_graph_items_old[$item_id], $item_new);
|
|
}
|
|
$items_to_save[] = $item_new;
|
|
|
|
$sequence++;
|
|
}
|
|
|
|
if ($items_changed) {
|
|
aggregate_graph_items_save($items_to_save, 'aggregate_graphs_graph_item');
|
|
}
|
|
|
|
if ($save_me || $items_changed) {
|
|
if (!push_out_aggregates(0, $local_graph_id)) {
|
|
raise_message(2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save helpers mutate the global message registry; PHPStan cannot observe that side effect.
|
|
/** @phpstan-ignore booleanNot.alwaysTrue */
|
|
if (!is_error_message()) {
|
|
raise_message(1);
|
|
}
|
|
|
|
header('Location: aggregate_graphs.php?action=edit&id=' . $local_graph_id);
|
|
} elseif (isrv('save_component_item')) {
|
|
global $graph_item_types;
|
|
|
|
$items[0] = [];
|
|
|
|
// handle saving aggregate graph items in separate function
|
|
if (grv('aggregate_template_id') > 0 || grv('aggregate_graph_id') > 0) {
|
|
form_save_aggregate();
|
|
}
|
|
|
|
if ($graph_item_types[grv('graph_type_id')] == 'LEGEND') {
|
|
/* this can be a major time saver when creating lots of graphs with the typical
|
|
GPRINT LAST/AVERAGE/MAX legends */
|
|
$items = [
|
|
0 => [
|
|
'color_id' => '0',
|
|
'color2_id' => '0',
|
|
'alpha' => '0',
|
|
'alpha2' => '0',
|
|
'graph_type_id' => '9',
|
|
'consolidation_function_id' => '4',
|
|
'text_format' => 'Current:',
|
|
'hard_return' => ''
|
|
],
|
|
1 => [
|
|
'color_id' => '0',
|
|
'color2_id' => '0',
|
|
'alpha' => '0',
|
|
'graph_type_id' => '9',
|
|
'consolidation_function_id' => '1',
|
|
'text_format' => 'Average:',
|
|
'hard_return' => ''
|
|
],
|
|
2 => [
|
|
'color_id' => '0',
|
|
'color2_id' => '0',
|
|
'alpha' => '0',
|
|
'graph_type_id' => '9',
|
|
'consolidation_function_id' => '3',
|
|
'text_format' => 'Maximum:',
|
|
'hard_return' => 'on'
|
|
]
|
|
];
|
|
}
|
|
|
|
$graph_template_item_id = null;
|
|
|
|
foreach ($items as $item) {
|
|
// generate a new sequence if needed
|
|
if (ierv('sequence')) {
|
|
$sequence = gfrv('sequence');
|
|
srv('sequence', get_sequence($sequence, 'sequence', 'graph_templates_item', ['local_graph_id' => gfrv('local_graph_id')]));
|
|
}
|
|
|
|
$save['id'] = gfrv('graph_template_item_id');
|
|
$save['graph_template_id'] = gfrv('graph_template_id');
|
|
$save['local_graph_template_item_id'] = gfrv('local_graph_template_item_id');
|
|
$save['local_graph_id'] = gfrv('local_graph_id');
|
|
$save['task_item_id'] = form_input_validate(gfrv('task_item_id'), 'task_item_id', '', true, 3);
|
|
$save['color_id'] = form_input_validate((isset($item['color_id']) ? $item['color_id'] : gfrv('color_id')), 'color_id', '', true, 3);
|
|
|
|
// if alpha is disabled, use invisible_alpha instead
|
|
if (!isrv('alpha')) {
|
|
srv('alpha', gnrv('invisible_alpha'));
|
|
}
|
|
|
|
$save['alpha'] = form_input_validate((isset($item['alpha']) ? $item['alpha'] : gnrv('alpha')), 'alpha', '', true, 3);
|
|
$save['graph_type_id'] = form_input_validate((isset($item['graph_type_id']) ? $item['graph_type_id'] : gfrv('graph_type_id')), 'graph_type_id', '', true, 3);
|
|
$save['cdef_id'] = form_input_validate(gfrv('cdef_id'), 'cdef_id', '', true, 3);
|
|
$save['consolidation_function_id'] = form_input_validate((isset($item['consolidation_function_id']) ? $item['consolidation_function_id'] : gfrv('consolidation_function_id')), 'consolidation_function_id', '', true, 3);
|
|
$save['text_format'] = form_input_validate((isset($item['text_format']) ? $item['text_format'] : gnrv('text_format')), 'text_format', '', true, 3);
|
|
$save['value'] = form_input_validate(gnrv('value'), 'value', '', true, 3);
|
|
$save['hard_return'] = form_input_validate(((isset($item['hard_return']) ? $item['hard_return'] : (isrv('hard_return') ? gnrv('hard_return') : ''))), 'hard_return', '', true, 3);
|
|
$save['gprint_id'] = form_input_validate(gfrv('gprint_id'), 'gprint_id', '', true, 3);
|
|
$save['sequence'] = gfrv('sequence');
|
|
|
|
if (!is_error_message()) {
|
|
$graph_template_item_id = sql_save($save, 'graph_templates_item');
|
|
|
|
if ($graph_template_item_id) {
|
|
raise_message(1);
|
|
} else {
|
|
raise_message(2);
|
|
}
|
|
}
|
|
|
|
srv('sequence', 0);
|
|
}
|
|
|
|
if (is_error_message()) {
|
|
header('Location: ' . CACTI_PATH_URL . 'aggregate_graphs.php?action=item_edit&graph_template_item_id=' . ($graph_template_item_id === null ? gfrv('graph_template_item_id') : $graph_template_item_id) . '&id=' . gfrv('local_graph_id'));
|
|
|
|
exit;
|
|
} else {
|
|
header('Location: ' . CACTI_PATH_URL . 'aggregate_graphs.php?action=edit&id=' . gfrv('local_graph_id'));
|
|
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* form_save_aggregate - save aggregate graph item
|
|
* This saves any overrides to item properties from graph template item.
|
|
* Inserting new items here is not possible. Just editing existing ones.
|
|
*/
|
|
function form_save_aggregate() : mixed {
|
|
if (!isrv('save_component_item')) {
|
|
return false;
|
|
}
|
|
|
|
// two possible tables to save to - aggregate template or aggregate graph
|
|
// with different key column combination
|
|
$save_to = 'aggregate_graph_templates_item';
|
|
$key_cols = ['aggregate_template_id', 'graph_templates_item_id'];
|
|
$location_success = 'aggregate_templates.php?action=edit&id=' . gfrv('aggregate_template_id');
|
|
$location_failure = 'aggregate_graphs.php?action=item_edit&aggregate_template_id=' . gfrv('aggregate_template_id') . '&id=' . gfrv('graph_template_item_id');
|
|
|
|
if (gfrv('aggregate_graph_id') > 0) {
|
|
$save_to = 'aggregate_graphs_graph_item';
|
|
$key_cols = ['aggregate_graph_id', 'graph_templates_item_id'];
|
|
$location_success = 'aggregate_graphs.php?action=edit&id=' . gfrv('local_graph_id');
|
|
$location_failure = 'aggregate_graphs.php?action=item_edit&aggregate_graph_id=' . gfrv('aggregate_graph_id') . '&id=' . gfrv('graph_template_item_id');
|
|
}
|
|
|
|
// only some properties can be saved here
|
|
$save = [];
|
|
$save['t_graph_type_id'] = form_input_validate((isrv('t_graph_type_id') ? gnrv('t_graph_type_id') : ''), 't_graph_type_id', '', true, 3);
|
|
$save['graph_type_id'] = form_input_validate((($save['t_graph_type_id']) ? gfrv('graph_type_id') : 0), 'graph_type_id', '', true, 3);
|
|
$save['t_cdef_id'] = form_input_validate((isrv('t_cdef_id') ? gnrv('t_cdef_id') : ''), 't_cdef_id', '', true, 3);
|
|
$save['cdef_id'] = form_input_validate((($save['t_cdef_id']) ? gfrv('cdef_id') : 0), 'cdef_id', '', true, 3);
|
|
|
|
if (!is_error_message()) {
|
|
// sql_save will not give useful return values when row key is
|
|
// composed from multiple columns. need to manually build query
|
|
$sql_set = 'SET ';
|
|
|
|
foreach ($save as $key => $value) {
|
|
$sql_set .= $key . '=' . db_qstr($value) . ', ';
|
|
}
|
|
$sql_set = substr($sql_set, 0, -2);
|
|
|
|
$sql_where = 'graph_templates_item_id = ' . gfrv('graph_template_item_id') . ' AND ';
|
|
|
|
if ($save_to == 'aggregate_graph_templates_item') {
|
|
$sql_where .= 'aggregate_template_id=' . gfrv('aggregate_template_id');
|
|
} else {
|
|
$sql_where .= 'aggregate_graph_id=' . gfrv('aggregate_graph_id');
|
|
}
|
|
$sql = "UPDATE $save_to $sql_set WHERE $sql_where LIMIT 1";
|
|
$success = db_execute($sql);
|
|
|
|
if ($success) {
|
|
raise_message(1);
|
|
} else {
|
|
raise_message(2);
|
|
}
|
|
|
|
// update existing graphs with the changes to this item
|
|
if ($save_to == 'aggregate_graphs_graph_item') {
|
|
if (!push_out_aggregates(0, gfrv('local_graph_id'))) {
|
|
raise_message(2);
|
|
}
|
|
} elseif ($save_to == 'aggregate_graph_templates_item') {
|
|
if (!push_out_aggregates(gfrv('aggregate_template_id'))) {
|
|
raise_message(2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_error_message()) {
|
|
header('Location: ' . CACTI_PATH_URL . $location_failure);
|
|
|
|
exit;
|
|
} else {
|
|
header('Location: ' . CACTI_PATH_URL . $location_success);
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
function item_movedown() : void {
|
|
global $graph_item_types;
|
|
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
gfrv('local_graph_id');
|
|
// ====================================================
|
|
|
|
$arr = get_graph_group(grv('id'));
|
|
$next_id = get_graph_parent(grv('id'), 'next');
|
|
|
|
if ((!empty($next_id)) && (isset($arr[grv('id')]))) {
|
|
move_graph_group(grv('id'), $arr, $next_id, 'next');
|
|
} elseif (preg_match('/(GPRINT|VRULE|HRULE|COMMENT)/', $graph_item_types[db_fetch_cell_prepared('SELECT graph_type_id FROM graph_templates_item WHERE id = ?', [grv('id')])])) {
|
|
move_item_down('graph_templates_item', grv('id'), 'local_graph_id=' . grv('local_graph_id'));
|
|
}
|
|
}
|
|
|
|
function item_moveup() : void {
|
|
global $graph_item_types;
|
|
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
gfrv('local_graph_id');
|
|
// ====================================================
|
|
|
|
$arr = get_graph_group(grv('id'));
|
|
$previous_id = get_graph_parent(grv('id'), 'previous');
|
|
|
|
if ((!empty($previous_id)) && (isset($arr[grv('id')]))) {
|
|
move_graph_group(grv('id'), $arr, $previous_id, 'previous');
|
|
} elseif (preg_match('/(GPRINT|VRULE|HRULE|COMMENT)/', $graph_item_types[db_fetch_cell_prepared('SELECT graph_type_id FROM graph_templates_item WHERE id = ?', [grv('id')])])) {
|
|
move_item_up('graph_templates_item', grv('id'), 'local_graph_id=' . grv('local_graph_id'));
|
|
}
|
|
}
|
|
|
|
function item_remove() : void {
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
db_execute_prepared('DELETE FROM graph_templates_item WHERE id = ?', [grv('id')]);
|
|
}
|
|
|
|
function item_edit() : void {
|
|
global $struct_graph_item, $graph_item_types, $consolidation_functions;
|
|
|
|
// Remove filter item
|
|
unset($struct_graph_item['data_template_id']);
|
|
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
gfrv('local_graph_id');
|
|
gfrv('aggregate_graph_id');
|
|
gfrv('aggregate_template_id');
|
|
// ====================================================
|
|
|
|
// remember these search fields in session vars so we don't have to keep passing them around
|
|
load_current_session_value('local_graph_id', 'sess_local_graph_id', '');
|
|
|
|
$id = (!ierv('id') ? '&id=' . gfrv('id') : '');
|
|
|
|
// this editor can work on aggregate template graph item or aggregate item
|
|
if (!ierv('aggregate_graph_id')) {
|
|
$id_field = 'aggregate_graph_id';
|
|
$table_name = 'aggregate_graphs_graph_item';
|
|
$page_name = 'aggregate_graphs.php';
|
|
} elseif (!ierv('aggregate_template_id')) {
|
|
$id_field = 'aggregate_template_id';
|
|
$table_name = 'aggregate_graph_templates_item';
|
|
$page_name = 'aggregate_templates.php';
|
|
} else {
|
|
// TODO redirect somewhere and show an error message, rather than die
|
|
die('We should have redirected somewhere but we ended up here instead' . PHP_EOL);
|
|
}
|
|
|
|
$template_item = [];
|
|
|
|
if (!ierv('id')) {
|
|
$template_item = db_fetch_row_prepared('SELECT *
|
|
FROM graph_templates_item
|
|
WHERE id = ?',
|
|
[grv('id')]);
|
|
}
|
|
|
|
// override some template_item values from aggregate tables
|
|
$item_overrides = db_fetch_row_prepared("SELECT *
|
|
FROM $table_name
|
|
WHERE $id_field = ?
|
|
AND graph_templates_item_id = ?",
|
|
[grv($id_field), grv('id')]);
|
|
|
|
if (cacti_sizeof($item_overrides) == 0) {
|
|
/* this item is not currently in aggregate tables
|
|
* item editor will not work in this case, so let's
|
|
* save it now
|
|
*/
|
|
$item_new = [
|
|
$id_field => grv($id_field),
|
|
'graph_templates_item_id' => grv('id'),
|
|
'sequence' => $template_item['sequence'] ?? 1
|
|
];
|
|
|
|
aggregate_graph_items_save([$item_new], $table_name);
|
|
|
|
$item_overrides = db_fetch_row_prepared("SELECT *
|
|
FROM $table_name
|
|
WHERE $id_field = ?
|
|
AND graph_templates_item_id = ?",
|
|
[grv($id_field), grv('id')]);
|
|
}
|
|
|
|
if (cacti_sizeof($template_item)) {
|
|
foreach (array_keys($template_item) as $field_name) {
|
|
if (!array_key_exists($field_name, $item_overrides)) {
|
|
continue;
|
|
}
|
|
|
|
// t_<field_name> column in aggregate table must be "on" to override
|
|
if (array_key_exists('t_' . $field_name, $item_overrides) && $item_overrides['t_' . $field_name] == 'on') {
|
|
$template_item[$field_name] = $item_overrides[$field_name];
|
|
}
|
|
}
|
|
}
|
|
|
|
html_start_box(__('Override Values for Graph Item'), '100%', true, 3, 'center', '');
|
|
|
|
$form_array = [];
|
|
|
|
foreach ($struct_graph_item as $field_name => $field_array) {
|
|
$form_array += [$field_name => $struct_graph_item[$field_name]];
|
|
|
|
// should we draw an override checkbox
|
|
if (array_key_exists('t_' . $field_name, $item_overrides)) {
|
|
$form_array[$field_name]['sub_checkbox'] = [
|
|
'name' => 't_' . $field_name,
|
|
'friendly_name' => __esc('Override this Value') . '<br>',
|
|
'value' => ($item_overrides['t_' . $field_name] == 'on' ? 'on' : ''),
|
|
'on_change' => 'toggleFieldEnabled(this.id);'
|
|
];
|
|
}
|
|
|
|
$form_array[$field_name]['value'] = (cacti_sizeof($template_item) ? $template_item[$field_name] : '');
|
|
$form_array[$field_name]['form_id'] = (cacti_sizeof($template_item) ? $template_item['id'] : '0');
|
|
}
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => [
|
|
'post_to' => CACTI_PATH_URL . 'aggregate_graphs.php'
|
|
],
|
|
'fields' => $form_array
|
|
]
|
|
);
|
|
|
|
form_hidden_box('local_graph_id', grv('local_graph_id'), '0');
|
|
form_hidden_box('graph_template_item_id', (cacti_sizeof($template_item) ? $template_item['id'] : '0'), '');
|
|
form_hidden_box('local_graph_template_item_id', (cacti_sizeof($template_item) ? $template_item['local_graph_template_item_id'] : '0'), '');
|
|
form_hidden_box('graph_template_id', (cacti_sizeof($template_item) ? $template_item['graph_template_id'] : '0'), '');
|
|
form_hidden_box('sequence', (cacti_sizeof($template_item) ? $template_item['sequence'] : '0'), '');
|
|
form_hidden_box('_graph_type_id', (cacti_sizeof($template_item) ? $template_item['graph_type_id'] : '0'), '');
|
|
form_hidden_box('save_component_item', '1', '');
|
|
form_hidden_box('invisible_alpha', $form_array['alpha']['value'], 'FF');
|
|
form_hidden_box('rrdtool_version', get_rrdtool_version(), '');
|
|
form_hidden_box('aggregate_graph_id', grv('aggregate_graph_id'), '0');
|
|
form_hidden_box('aggregate_template_id', grv('aggregate_template_id'), '0');
|
|
|
|
html_end_box(true, true);
|
|
|
|
form_save_button(CACTI_PATH_URL . "$page_name?action=edit&id=" . gfrv('local_graph_id'));
|
|
|
|
// Now we need some javascript to make it dynamic
|
|
?>
|
|
<script type='text/javascript'>
|
|
|
|
$(function() {
|
|
dynamic();
|
|
setFieldsDisabled();
|
|
});
|
|
|
|
function dynamic() {
|
|
$('#alpha').prop('disabled', true);
|
|
if ($('#color_id').val() != 0) {
|
|
$('#alpha').prop('disabled', true);
|
|
}
|
|
}
|
|
|
|
function changeColorId() {
|
|
if ($('#color_id').attr('selectedIndex') != 0) {
|
|
$('#alpha').prop('disabled', true);
|
|
}
|
|
}
|
|
|
|
// disable all items with sub-checkboxes except
|
|
// where sub-checkbox checked
|
|
function setFieldsDisabled() {
|
|
$('input[id^="t_"]').each(function() {
|
|
if (!$(this).is(':checked')) {
|
|
var fieldId = $(this).attr('id').substr(2);
|
|
|
|
$('#'+fieldId).prop('disabled', true);
|
|
$('#'+fieldId).addClass('ui-state-disabled');
|
|
|
|
if ($('#'+fieldId).selectmenu('instance')) {
|
|
$('#'+fieldId).selectmenu('disable');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// enable or disable form field based on state of corresponding checkbox
|
|
function toggleFieldEnabled(toggleFieldId) {
|
|
fieldId = toggleFieldId.substr(2);
|
|
|
|
if ($('#'+fieldId).hasClass('ui-state-disabled')) {
|
|
$('#'+fieldId).prop('disabled', false).removeClass('ui-state-disabled');
|
|
|
|
if ($('#'+fieldId).selectmenu('instance')) {
|
|
$('#'+fieldId).selectmenu('enable');
|
|
}
|
|
} else {
|
|
$('#'+fieldId).prop('disabled', true).addClass('ui-state-disabled');
|
|
|
|
if ($('#'+fieldId).selectmenu('instance')) {
|
|
$('#'+fieldId).selectmenu('disable');
|
|
}
|
|
}
|
|
}
|
|
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
function form_actions() : void {
|
|
global $actions, $agg_item_actions;
|
|
global $alignment, $graph_timespans;
|
|
|
|
// ================= input validation =================
|
|
gfrv('drp_action', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^([a-zA-Z0-9_]+)$/']]);
|
|
// ====================================================
|
|
|
|
// we are performing two set's of actions here
|
|
$actions += $agg_item_actions;
|
|
|
|
// if we are to save this form, instead of display it
|
|
if (isrv('selected_items')) {
|
|
$selected_items = sanitize_unserialize_selected_items(gnrv('selected_items'));
|
|
|
|
if ($selected_items != false) {
|
|
if (grv('drp_action') == '1') { // delete
|
|
api_aggregate_remove_multi($selected_items);
|
|
} elseif (grv('drp_action') == '2') { // migrate to template
|
|
api_aggregate_convert_template($selected_items);
|
|
} elseif (grv('drp_action') == '3') { // create aggregate from aggregate
|
|
$aggregate_name = form_input_validate(grv('aggregate_name'), 'aggregate_name', '', false, 3);
|
|
|
|
if (!is_error_message()) {
|
|
api_aggregate_create($aggregate_name, $selected_items);
|
|
}
|
|
} elseif (grv('drp_action') == '4') { // add graphs to report
|
|
$good = true;
|
|
|
|
for ($i = 0; ($i < cacti_count($selected_items)); $i++) {
|
|
if (!reports_add_graphs(gfrv('report_id'), $selected_items[$i], grv('timespan'), grv('align'))) {
|
|
raise_message('reports_add_error');
|
|
$good = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($good) {
|
|
raise_message('reports_graphs_added');
|
|
}
|
|
} elseif (grv('drp_action') == '5') { // Convert to a normal graph
|
|
api_aggregate_convert_to_graph($selected_items);
|
|
|
|
header('Location: aggregate_graphs.php');
|
|
|
|
exit;
|
|
} elseif (grv('drp_action') == '10') { // associate with aggregate
|
|
$local_graph_id = gfrv('local_graph_id');
|
|
api_aggregate_associate($local_graph_id, $selected_items);
|
|
|
|
header('Location: aggregate_graphs.php?action=edit&tab=items&id=' . $local_graph_id);
|
|
|
|
exit;
|
|
} elseif (grv('drp_action') == '11') { // dis-associate with aggregate
|
|
$local_graph_id = gfrv('local_graph_id');
|
|
api_aggregate_disassociate($local_graph_id, $selected_items);
|
|
|
|
header('Location: aggregate_graphs.php?action=edit&tab=items&id=' . $local_graph_id);
|
|
|
|
exit;
|
|
} elseif (preg_match('/^tr_([0-9]+)$/', grv('drp_action'), $matches)) { // place on tree
|
|
gfrv('tree_id');
|
|
gfrv('tree_item_id');
|
|
|
|
for ($i = 0; ($i < cacti_count($selected_items)); $i++) {
|
|
api_tree_item_save(0, gnrv('tree_id'), TREE_ITEM_TYPE_GRAPH, gnrv('tree_item_id'), '', $selected_items[$i], 0, 0, 0, 0, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
header('Location: aggregate_graphs.php');
|
|
|
|
exit;
|
|
} else {
|
|
$ilist = '';
|
|
$iarray = [];
|
|
|
|
// default form variables
|
|
$aggregate_templates = [];
|
|
$reports = [];
|
|
|
|
// loop through each of the graphs selected on the previous page and get more info about them
|
|
foreach ($_POST as $var => $val) {
|
|
if (preg_match('/^chk_([0-9]+)$/', $var, $matches)) {
|
|
// ================= input validation =================
|
|
input_validate_input_number($matches[1], 'chk[1]');
|
|
// ====================================================
|
|
|
|
$ilist .= '<li>' . htmle(get_graph_title(intval($matches[1]))) . '</li>';
|
|
|
|
$iarray[] = $matches[1];
|
|
}
|
|
}
|
|
|
|
if (cacti_sizeof($iarray)) {
|
|
if (grv('drp_action') == '2') {
|
|
$local_graph_ids = [];
|
|
|
|
// determine the common graph template if any
|
|
foreach ($_POST as $var => $val) {
|
|
if (preg_match('/^chk_([0-9]+)$/', $var, $matches)) {
|
|
$local_graph_ids[] = $matches[1];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* for whatever reason, subquery performance in mysql is sub-optimal.
|
|
* Therefore, let's do this as a few queries instead.
|
|
*/
|
|
$task_items = array_rekey(db_fetch_assoc_prepared('SELECT DISTINCT task_item_id
|
|
FROM graph_templates_item
|
|
WHERE local_graph_id IN (' . trim(str_repeat('?, ', cacti_sizeof($local_graph_ids)), ', ') . ')',
|
|
$local_graph_ids), 'task_item_id', 'task_item_id');
|
|
|
|
if (cacti_sizeof($task_items)) {
|
|
$graph_templates = db_fetch_assoc_prepared('SELECT DISTINCT graph_template_id
|
|
FROM graph_templates_item
|
|
WHERE task_item_id IN (' . trim(str_repeat('?, ', cacti_sizeof($task_items)), ', ') . ') AND graph_template_id>0',
|
|
array_values($task_items));
|
|
} else {
|
|
$graph_templates = [];
|
|
}
|
|
|
|
if (cacti_sizeof($graph_templates) > 1) {
|
|
$message = '<p>' . __('The selected Aggregate Graphs represent elements from more than one Graph Template.') . '</p>
|
|
<p>' . __('In order to migrate the Aggregate Graphs below to a Template based Aggregate, they must only be using one Graph Template.') . "</p>
|
|
<div class='itemlist'><ul>$ilist</ul></div>";
|
|
|
|
raise_message('nonmatch_templates', $message, MESSAGE_LEVEL_ERROR);
|
|
header('Location: aggregate_graphs.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
if (cacti_sizeof($graph_templates) == 0) {
|
|
$message = '<p>' . __('The selected Aggregate Graphs does not appear to have any matching Aggregate Templates.') . '</p>
|
|
<p>' . __('In order to migrate the Aggregate Graphs below use an Aggregate Template, one must already exist.') . "</p>
|
|
<div class='itemlist'><ul>$ilist</ul></div>";
|
|
|
|
raise_message('nonmatch_templates', $message, MESSAGE_LEVEL_ERROR);
|
|
header('Location: aggregate_graphs.php');
|
|
|
|
exit;
|
|
} else {
|
|
$graph_template = $graph_templates[0]['graph_template_id'];
|
|
|
|
$aggregate_templates = array_rekey(
|
|
db_fetch_assoc_prepared('SELECT id, name
|
|
FROM aggregate_graph_templates
|
|
WHERE graph_template_id = ?
|
|
ORDER BY name',
|
|
[$graph_template]),
|
|
'id', 'name'
|
|
);
|
|
|
|
if (!cacti_sizeof($aggregate_templates)) {
|
|
$name = db_fetch_cell_prepared('SELECT name
|
|
FROM graph_templates
|
|
WHERE id = ?',
|
|
[$graph_template]);
|
|
|
|
$message = '<p>' . __('There are currently no Aggregate Templates defined for the selected Legacy Aggregates.') . '</p>
|
|
<p>' . __esc('In order to migrate the Aggregate Graphs below to a Template based Aggregate, first create an Aggregate Template for the Graph Template \'%s\'.', $name) . "</p>
|
|
<div class='itemlist'><ul>$ilist</ul></div>";
|
|
|
|
raise_message('nonmatch_templates', $message, MESSAGE_LEVEL_ERROR);
|
|
header('Location: aggregate_graphs.php');
|
|
|
|
exit;
|
|
}
|
|
}
|
|
} elseif (grv('drp_action') == '4') {
|
|
$reports = db_fetch_assoc_prepared('SELECT id, name
|
|
FROM reports
|
|
WHERE user_id = ?
|
|
ORDER BY name',
|
|
[$_SESSION[SESS_USER_ID]]);
|
|
|
|
if (cacti_sizeof($reports)) {
|
|
$reports = array_rekey($reports, 'id', 'name');
|
|
} else {
|
|
$message = '<p>' . __('You currently have no reports defined.') . '</p>';
|
|
|
|
raise_message('nonmatch_templates', $message, MESSAGE_LEVEL_ERROR);
|
|
header('Location: aggregate_graphs.php');
|
|
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
$form_data = [
|
|
'general' => [
|
|
'page' => 'aggregate_graphs.php',
|
|
'actions' => $actions,
|
|
'optvar' => 'drp_action',
|
|
'item_array' => $iarray,
|
|
'item_list' => $ilist,
|
|
'eaction' => 'local_graph_id',
|
|
'eactionid' => isrv('local_graph_id') ? gfrv('local_graph_id') : '0'
|
|
],
|
|
'options' => [
|
|
1 => [
|
|
'smessage' => __('Click \'Continue\' to Delete the following Aggregate Graph.'),
|
|
'pmessage' => __('Click \'Continue\' to Delete following Aggregate Graphs.'),
|
|
'scont' => __('Delete Aggregate Graph'),
|
|
'pcont' => __('Delete Aggregate Graphs')
|
|
],
|
|
2 => [
|
|
'smessage' => __('Click \'Continue\' to Migrate the following Legacy Aggregate to a Templated Aggregate Graph.'),
|
|
'pmessage' => __('Click \'Continue\' to Migrate the following Legacy Aggregates to Templated Aggregate Graphs.'),
|
|
'scont' => __('Migrate to Aggregate Graph'),
|
|
'pcont' => __('Migrate to Aggregate Graphs'),
|
|
'extra' => [
|
|
'aggregate_template_id' => [
|
|
'method' => 'drop_array',
|
|
'array' => $aggregate_templates,
|
|
'title' => __('Aggregate Template'),
|
|
'default' => array_key_first($aggregate_templates)
|
|
]
|
|
]
|
|
],
|
|
3 => [
|
|
'message' => __('Click \'Continue\' to Combine the following Aggregates into an Aggregate Graph.'),
|
|
'cont' => __('Combine Aggregate Graphs'),
|
|
'extra' => [
|
|
'aggregate_name' => [
|
|
'method' => 'textbox',
|
|
'title' => __('Aggregate Name'),
|
|
'default' => __('New Aggregate'),
|
|
'width' => 40,
|
|
'size' => 40
|
|
]
|
|
]
|
|
],
|
|
4 => [
|
|
'smessage' => __('Click \'Continue\' to Place the following Aggregate Graph on a Report.'),
|
|
'pmessage' => __('Click \'Continue\' to Place the following Aggregate Graphs on a Report.'),
|
|
'scont' => __('Place Aggregate Graph on Report'),
|
|
'pcont' => __('Place Aggregate Graphs on Report'),
|
|
'extra' => [
|
|
'report_id' => [
|
|
'method' => 'drop_array',
|
|
'title' => __('Report Name'),
|
|
'array' => $reports,
|
|
'default' => array_key_first($reports)
|
|
],
|
|
'timespan' => [
|
|
'method' => 'drop_array',
|
|
'title' => __('Timespan'),
|
|
'array' => $graph_timespans,
|
|
'default' => read_user_setting('default_timespan')
|
|
],
|
|
'align' => [
|
|
'method' => 'drop_array',
|
|
'title' => __('Align'),
|
|
'array' => $alignment,
|
|
'default' => REPORTS_ALIGN_CENTER
|
|
]
|
|
]
|
|
],
|
|
5 => [
|
|
'smessage' => __('Click \'Continue\' to Convert the following Aggregate Graph to a Normal Graph.'),
|
|
'pmessage' => __('Click \'Continue\' to Convert the following Aggregate Graphs to Normal Graphs.'),
|
|
'scont' => __('Convert Aggregate Graph'),
|
|
'pcont' => __('Convert Aggregate Graphs')
|
|
],
|
|
10 => [
|
|
'smessage' => __('Click \'Continue\' to Associate the following Graph with the Aggregate Graph.'),
|
|
'pmessage' => __('Click \'Continue\' to Associate the following Graphs with the Aggregate Graph.'),
|
|
'scont' => __('Associate Graph with Aggregate'),
|
|
'pcont' => __('Associate Graphs with Aggregate')
|
|
],
|
|
11 => [
|
|
'smessage' => __('Click \'Continue\' to Disassociate the following Graph from the Aggregate Graph.'),
|
|
'pmessage' => __('Click \'Continue\' to Disassociate the following Graphs from the Aggregate Graph.'),
|
|
'scont' => __('Disassociate Graph with Aggregate'),
|
|
'pcont' => __('Disassociate Graphs with Aggregate')
|
|
],
|
|
]
|
|
];
|
|
|
|
$trees = db_fetch_assoc('SELECT id, name FROM graph_tree ORDER BY name');
|
|
|
|
if (cacti_sizeof($trees)) {
|
|
foreach ($trees as $tree) {
|
|
$form_data['options']['tr_' . $tree['id']] = [
|
|
'smessage' => __esc('Click \'Continue\' to Place the following Aggregate Graph on Tree %s.', $tree['name']),
|
|
'pmessage' => __esc('Click \'Continue\' to Duplicate following Aggregate Graphs on Tree %s.', $tree['name']),
|
|
'scont' => __('Place Aggregate Graph on Tree'),
|
|
'pcont' => __('Place Aggregate Graphs on Tree'),
|
|
'extra' => [
|
|
'tree_item_id' => [
|
|
'method' => 'drop_branch',
|
|
'title' => __('Destination Branch:'),
|
|
'id' => $tree['id']
|
|
]
|
|
],
|
|
'eaction' => 'tree_id',
|
|
'eactionid' => $tree['id'],
|
|
];
|
|
}
|
|
}
|
|
|
|
form_continue_confirmation($form_data);
|
|
}
|
|
}
|
|
|
|
function graph_edit() : bool {
|
|
global $struct_graph, $struct_aggregate_graph, $image_types;
|
|
global $consolidation_functions, $graph_item_types, $struct_graph_item;
|
|
|
|
// Remove filter item
|
|
unset($struct_graph_item['data_template_id']);
|
|
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
// purge any old graphs
|
|
aggregate_prune_graphs(grv('id'));
|
|
|
|
if (isrv('reset')) {
|
|
$_SESSION['aggregate_referer'] = 'aggregate_graphs.php';
|
|
} elseif (isset($_SERVER['HTTP_REFERER'])) {
|
|
if (!isset($_SESSION['aggregate_referer'])) {
|
|
$_SESSION['aggregate_referer'] = validate_redirect_url($_SERVER['HTTP_REFERER'], 'aggregate_graphs.php');
|
|
}
|
|
} else {
|
|
$_SESSION['aggregate_referer'] = 'aggregate_graphs.php';
|
|
}
|
|
|
|
$referer = $_SESSION['aggregate_referer'];
|
|
|
|
$use_graph_template = false;
|
|
$aginfo = [];
|
|
$graphs = [];
|
|
|
|
if (!ierv('id')) {
|
|
$graphs = db_fetch_row_prepared('SELECT *
|
|
FROM graph_templates_graph
|
|
WHERE local_graph_id = ?',
|
|
[grv('id')]
|
|
);
|
|
|
|
if (cacti_sizeof($graphs)) {
|
|
$aginfo = db_fetch_row_prepared('SELECT *
|
|
FROM aggregate_graphs
|
|
WHERE local_graph_id = ?',
|
|
[$graphs['local_graph_id']]
|
|
);
|
|
|
|
if ($aginfo['title_format'] == '') {
|
|
$aginfo['title_format'] = get_graph_title($graphs['local_graph_id']);
|
|
}
|
|
|
|
$header_label = __esc('[edit: %s]', get_graph_title(grv('id')));
|
|
} else {
|
|
$header_label = __('Aggregate Graph does not Exist');
|
|
}
|
|
}
|
|
|
|
$aggregate_tabs = [
|
|
'details' => __('Details'),
|
|
'items' => __('Items'),
|
|
'preview' => __('Preview')
|
|
];
|
|
|
|
if (cacti_sizeof($aginfo)) {
|
|
if ($aginfo['aggregate_template_id'] > 0) {
|
|
$template = db_fetch_row_prepared('SELECT *
|
|
FROM aggregate_graph_templates
|
|
WHERE id = ?',
|
|
[$aginfo['aggregate_template_id']]
|
|
);
|
|
} else {
|
|
$template = $aginfo;
|
|
}
|
|
} elseif (cacti_sizeof($graphs)) {
|
|
$template = [];
|
|
} else {
|
|
raise_message('missing_aggregate', __('Aggregate Graph Accessed does not exist'), MESSAGE_LEVEL_ERROR);
|
|
|
|
$referer = validate_redirect_url($_SERVER['HTTP_REFERER'] ?? '', 'aggregate_graphs.php');
|
|
|
|
header('Location: ' . $referer);
|
|
|
|
exit;
|
|
}
|
|
|
|
// ================= input validation and session storage =================
|
|
if (isrv('tab')) {
|
|
switch (gnrv('tab')) {
|
|
case 'details':
|
|
case 'items':
|
|
case 'preview':
|
|
$_SESSION['agg_tab'] = gnrv('tab');
|
|
srv('tab', gnrv('tab'));
|
|
|
|
break;
|
|
default:
|
|
if (isset($_SESSION['agg_tab'])) {
|
|
srv('tab', $_SESSION['agg_tab']);
|
|
} else {
|
|
$_SESSION['agg_tab'] = 'details';
|
|
srv('tab', 'details');
|
|
}
|
|
}
|
|
} elseif (isset($_SESSION['agg_tab'])) {
|
|
srv('tab', $_SESSION['agg_tab']);
|
|
} else {
|
|
srv('tab', 'details');
|
|
}
|
|
// ================= input validation =================
|
|
|
|
$current_tab = gnrv('tab');
|
|
|
|
// draw the categories tabs on the top of the page
|
|
print "<div class='tabs'>";
|
|
print "<div class='aggtabs'><nav><ul role='tablist'>";
|
|
|
|
if (cacti_sizeof($aggregate_tabs)) {
|
|
foreach ($aggregate_tabs as $id => $name) {
|
|
if ($id == 'details' || (!ierv('id'))) {
|
|
print "<li class='subTab'><a id='agg_" . $id . "' class='tab " . ($id == $current_tab ? "selected'" : "'") .
|
|
" href='" . htmle(CACTI_PATH_URL . 'aggregate_graphs.php?action=edit&id=' . gfrv('id') . "&tab=$id") .
|
|
"'>" . htmle($name) . '</a></li>';
|
|
}
|
|
}
|
|
}
|
|
|
|
print '</ul>';
|
|
|
|
// handle debug mode
|
|
if (isrv('debug')) {
|
|
if (gfrv('debug') == '0') {
|
|
kill_session_var('graph_debug_mode');
|
|
} elseif (gfrv('debug') == '1') {
|
|
$_SESSION['graph_debug_mode'] = true;
|
|
}
|
|
}
|
|
|
|
if (isset($_SESSION['graph_debug_mode'])) {
|
|
$message = __('Turn Off Graph Debug Mode');
|
|
} else {
|
|
$message = __('Turn On Graph Debug Mode');
|
|
}
|
|
|
|
if (!ierv('id') && $current_tab == 'preview') {
|
|
print "<ul style='float:right;'><li><a class='pic' href='" . htmle('aggregate_graphs.php?action=edit&id=' . gfrv('id') . '&tab=' . rawurlencode(grv('tab')) . '&debug=' . (isset($_SESSION['graph_debug_mode']) ? '0' : '1')) . "'>" . $message . '</a></li></ul></nav></div></div>';
|
|
} elseif (!ierv('id') && $current_tab == 'details' && (!cacti_sizeof($template))) {
|
|
print "<ul style='float:right;'><li><a id='toggle_items' class='pic' href='#'>" . __('Show Item Details') . '</a></li></ul></nav></div></div>';
|
|
} else {
|
|
print '</nav></div></div>';
|
|
}
|
|
|
|
if (!ierv('id') && $current_tab == 'preview') {
|
|
$graph = db_fetch_row_prepared('SELECT *
|
|
FROM graph_local
|
|
WHERE id = ?',
|
|
[grv('id')]
|
|
);
|
|
|
|
if (!cacti_sizeof($graph)) {
|
|
html_start_box(__('Aggregate Preview Does Not Exist'), '100%', false, 3, 'center', '');
|
|
print "<tr><td id='imagewindow' class='center'>" . __('Aggregate Graph does not Exist') . '</tr></tr>';
|
|
html_end_box(false);
|
|
raise_message('noaggregate', __('Aggregate Graph does not Exist'), MESSAGE_LEVEL_ERROR);
|
|
|
|
return false;
|
|
}
|
|
|
|
html_start_box(__('Aggregate Preview %s', $header_label), '100%', false, 3, 'center', '');
|
|
?>
|
|
<tr>
|
|
<td id='imagewindow' class='center'>
|
|
<img src='<?php print htmle(CACTI_PATH_URL . 'graph_image.php?action=edit&disable_cache=1&local_graph_id=' . gfrv('id') . '&rra_id=' . read_user_setting('default_rra_id') . '&random=' . mt_rand()); ?>' alt=''>
|
|
<script type='text/javascript'>
|
|
$(function() {
|
|
$('#agg_preview').show();
|
|
});
|
|
</script>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
if (isset($_SESSION['graph_debug_mode']) && isrv('id')) {
|
|
$graph_data_array['output_flag'] = RRDTOOL_OUTPUT_STDERR;
|
|
$graph_data_array['print_source'] = 1;
|
|
|
|
$null_param = [];
|
|
?>
|
|
<tr>
|
|
<td id='rrdtoolinfo' class='left' style='padding-left:15px;max-width:900px;overflow:scroll'>
|
|
<div style='overflow:auto;'>
|
|
<span class='textInfo'><?php print __('RRDtool Command:'); ?></span><br>
|
|
<pre class='monoSpace tableRow left'><?php print htmle(@rrdtool_function_graph(grv('id'), 1, $graph_data_array, '', $null_param, $_SESSION[SESS_USER_ID])); ?></pre>
|
|
<span class='textInfo'><?php print __('RRDtool Says:'); ?></span><br><?php unset($graph_data_array['print_source']); ?>
|
|
<pre class='monoSpace tableRow left'><?php print(POLLER_ID == 1 ? htmle(@rrdtool_function_graph(grv('id'), 1, $graph_data_array, '', $null_param, $_SESSION[SESS_USER_ID])) : __esc('Not Checked')); ?></pre>
|
|
</div>
|
|
<script type='text/javascript'>
|
|
$(function() {
|
|
var rrdwidth = $(window).width() - $('.cactiConsoleNavigationArea').width();
|
|
$('#rrdtoolinfo, #imagewindow').css('max-width', rrdwidth);
|
|
});
|
|
</script>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
}
|
|
|
|
html_end_box(false);
|
|
}
|
|
|
|
if (!ierv('id') && $current_tab == 'items') {
|
|
aggregate_items();
|
|
bottom_footer();
|
|
|
|
exit;
|
|
}
|
|
|
|
form_start('aggregate_graphs.php', 'template_edit');
|
|
|
|
// we will show the templated representation only when there is a template and propagation is enabled
|
|
if (!ierv('id') && $current_tab == 'details') {
|
|
if (cacti_sizeof($template)) {
|
|
print "<div id='templated'>";
|
|
|
|
html_start_box(__('Aggregate Graph %s', $header_label), '100%', true, 3, 'center', '');
|
|
|
|
$helper_string = '|host_description|';
|
|
|
|
$data_query = db_fetch_cell_prepared('SELECT snmp_query_id
|
|
FROM snmp_query_graph
|
|
WHERE graph_template_id = ?',
|
|
[$template['graph_template_id']]
|
|
);
|
|
|
|
if ($data_query > 0) {
|
|
$data_query_info = get_data_query_array($data_query);
|
|
|
|
foreach ($data_query_info['fields'] as $field_name => $field_array) {
|
|
if ($field_array['direction'] == 'input' || $field_array['direction'] == 'input-output') {
|
|
$helper_string .= ($helper_string != '' ? ', ' : '') . '|query_' . $field_name . '|';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Append the helper string
|
|
$struct_aggregate_graph['suggestions'] = [
|
|
'method' => 'other',
|
|
'friendly_name' => __('Prefix Replacement Values'),
|
|
'description' => __('You may use these replacement values for the Prefix in the Aggregate Graph'),
|
|
'value' => $helper_string
|
|
];
|
|
|
|
// add template propagation to the structure
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => inject_form_variables($struct_aggregate_graph, $aginfo)
|
|
]
|
|
);
|
|
|
|
html_end_box(true, true);
|
|
|
|
draw_aggregate_graph_items_list(0, $template['graph_template_id'], $aginfo);
|
|
|
|
form_hidden_box('save_component_template', '1', '');
|
|
|
|
?>
|
|
<script type='text/javascript'>
|
|
var templated_selectors = [
|
|
'#gprint_prefix',
|
|
'#gprint_format',
|
|
'#graph_type',
|
|
'#total',
|
|
'#total_type',
|
|
'#total_prefix',
|
|
'#order_type',
|
|
'select[id^="agg_color"]',
|
|
'input[id^="agg_total"]',
|
|
'input[id^="agg_skip"]',
|
|
'#image_format_id',
|
|
'#height',
|
|
'#width',
|
|
'#slope_mode',
|
|
'#auto_scale',
|
|
'#auto_scale_opts',
|
|
'#auto_scale_log',
|
|
'#scale_log_units',
|
|
'#auto_scale_rigid',
|
|
'#auto_padding',
|
|
'#export',
|
|
'#upper_limit',
|
|
'#lower_limit',
|
|
'#base_value',
|
|
'#unit_value',
|
|
'#unit_exponent_value',
|
|
'#alt_y_grid',
|
|
'#right_axis',
|
|
'#right_axis_label',
|
|
'#right_axis_format',
|
|
'#right_axis_formatter',
|
|
'#left_axis_format',
|
|
'#left_axis_formatter',
|
|
'#no_gridfit',
|
|
'#unit_length',
|
|
'#tab_width',
|
|
'#dynamic_labels',
|
|
'#force_rules_legend',
|
|
'#legend_position',
|
|
'#legend_direction',
|
|
'#vertical_label'
|
|
];
|
|
|
|
$(function() {
|
|
if (!$('input[id^="agg_total"]').is(':checked') && !$('#template_propogation').is(':checked')) {
|
|
$('#agg_preview').hide();
|
|
}
|
|
|
|
if ($('#template_propogation').is(':checked')) {
|
|
for (var i = 0; i < templated_selectors.length; i++) {
|
|
$(templated_selectors[i]).prop('disabled', true).addClass('ui-state-disabled');
|
|
if ($(templated_selectors[i]).selectmenu('instance')) {
|
|
$(templated_selectors[i]).selectmenu('disable');
|
|
}
|
|
}
|
|
} else {
|
|
$('#row_template_propogation').hide();
|
|
$('#row_spacer0').hide();
|
|
}
|
|
|
|
$('input[id^="agg_total"], input[id^="agg_skip"]').click(function() {
|
|
id = $(this).attr('id');
|
|
|
|
if (id.indexOf('skip') > 0) {
|
|
altId = id.replace('skip', 'total');
|
|
} else {
|
|
altId = id.replace('total', 'skip');
|
|
}
|
|
|
|
if ($('#' + id).is(':checked')) {
|
|
$('#' + altId).prop('checked', false);
|
|
} else {
|
|
$('#' + altId).prop('checked', true);
|
|
}
|
|
|
|
changeTotals();
|
|
|
|
updateSaveButton();
|
|
});
|
|
|
|
$('#total').change(function() {
|
|
changeTotals();
|
|
});
|
|
|
|
$('#total_type').change(function() {
|
|
changeTotalsType();
|
|
});
|
|
|
|
$('#template_propogation').change(function() {
|
|
if (!$('#template_propogation').is(':checked')) {
|
|
for (var i = 0; i < templated_selectors.length; i++) {
|
|
$(templated_selectors[i]).prop('disabled', false);
|
|
}
|
|
} else {
|
|
for (var i = 0; i < templated_selectors.length; i++) {
|
|
$(templated_selectors[i]).prop('disabled', true);
|
|
}
|
|
}
|
|
});
|
|
|
|
updateSaveButton();
|
|
});
|
|
|
|
function updateSaveButton() {
|
|
if ($('input[id^="agg_total"]').is(':checked')) {
|
|
$('#submit').prop('disabled', false);
|
|
|
|
if ($('#submit').button('instance')) {
|
|
$('#submit').button('enable');
|
|
}
|
|
} else {
|
|
$('#submit').prop('disabled', true);
|
|
|
|
if ($('#submit').button('instance')) {
|
|
$('#submit').button('disable');
|
|
}
|
|
}
|
|
}
|
|
|
|
function changeTotals() {
|
|
var totalMode = $('#total').val();
|
|
var showField = totalMode != <?= AGGREGATE_ORDER_NONE ?>;
|
|
|
|
toggleFields({
|
|
total_type: showField,
|
|
total_prefix: showField,
|
|
order_type: showField,
|
|
});
|
|
|
|
changeTotalsType();
|
|
}
|
|
|
|
function changeTotalsType() {
|
|
if ($('#total_type').val() == '<?php print AGGREGATE_TOTAL_TYPE_SIMILAR; ?>') {
|
|
if ($('#total_prefix').val() == '' && $('#id').val() == 0) {
|
|
$('#total_prefix').attr('value', '<?php print __('Total'); ?>');
|
|
}
|
|
} else if ($('#total_type').val() == '<?php print AGGREGATE_TOTAL_TYPE_ALL; ?>') {
|
|
if ($('#total_prefix').val() == '' && $('#id').val() == 0) {
|
|
if ($('#total_prefix').val() == '') {
|
|
$('#total_prefix').attr('value', '<?php print __('All Items'); ?>');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<?php
|
|
print '</div>';
|
|
}
|
|
|
|
// we will show the classic representation only when we are not templating
|
|
print "<div id='classic'>";
|
|
|
|
?>
|
|
<input type='hidden' id='graph_template_graph_id' name='graph_template_graph_id' value='<?php print(cacti_sizeof($graphs) ? (int) $graphs['id'] : 0); ?>'>
|
|
<input type='hidden' id='local_graph_template_graph_id' name='local_graph_template_graph_id' value='<?php print(cacti_sizeof($graphs) ? (int) $graphs['local_graph_template_graph_id'] : 0); ?>'>
|
|
<?php
|
|
|
|
if (empty($graphs['graph_template_id'])) {
|
|
html_start_box(__('Graph Configuration'), '100%', true, 3, 'center', '');
|
|
|
|
$form_array = [];
|
|
|
|
foreach ($struct_graph as $field_name => $field_array) {
|
|
if ($field_array['method'] != 'spacer') {
|
|
if ($field_name != 'title') {
|
|
$form_array += [$field_name => $struct_graph[$field_name]];
|
|
|
|
$form_array[$field_name]['value'] = (cacti_sizeof($graphs) ? $graphs[$field_name] : '');
|
|
$form_array[$field_name]['form_id'] = (cacti_sizeof($graphs) ? $graphs['id'] : '0');
|
|
|
|
if ($use_graph_template && $graphs['t_' . $field_name] != 'on') {
|
|
$form_array[$field_name]['method'] = 'template_' . $form_array[$field_name]['method'];
|
|
$form_array[$field_name]['description'] = '';
|
|
}
|
|
}
|
|
} else {
|
|
$form_array += [$field_name => $struct_graph[$field_name]];
|
|
}
|
|
}
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => $form_array
|
|
]
|
|
);
|
|
|
|
html_end_box(true, true);
|
|
}
|
|
|
|
form_hidden_box('save_component_graph', '1', '');
|
|
form_hidden_box('save_component_input', '1', '');
|
|
form_hidden_box('rrdtool_version', get_rrdtool_version(), '');
|
|
form_save_button($referer, 'return', 'id');
|
|
|
|
print '</div>';
|
|
|
|
?>
|
|
<script type='text/javascript'>
|
|
$(function() {
|
|
dynamic();
|
|
if (!$('#templated')) {
|
|
$('#local_graph_template_graph_id').next('table').css('display', 'none');
|
|
}
|
|
});
|
|
|
|
$('#toggle_items').click(function() {
|
|
if ($('#toggle_items').is(":contains('<?php print __('Show'); ?>')")) {
|
|
$('#local_graph_template_graph_id').next('table').css('display', '');
|
|
$('#toggle_items').text('<?php print __('Hide Item Details'); ?>');
|
|
} else {
|
|
$('#local_graph_template_graph_id').next('table').css('display', 'none');
|
|
$('#toggle_items').text('<?php print __('Show Item Details'); ?>');
|
|
}
|
|
});
|
|
|
|
function dynamic() {
|
|
if ($('#scale_log_units')) {
|
|
$('#scale_log_units').prop('disabled', true);
|
|
if (($('#rrdtool_version').val() != '1.0.0') &&
|
|
($('#auto_scale_log').is(':checked'))) {
|
|
$('#scale_log_units').prop('disabled', true);
|
|
}
|
|
}
|
|
}
|
|
|
|
function changeScaleLog() {
|
|
if ($('#scale_log_units')) {
|
|
$('#scale_log_units').prop('disabled', true);
|
|
if (($('#rrdtool_version').val() != '1.0.0') &&
|
|
($('#auto_scale_log').is(':checked'))) {
|
|
$('#scale_log_units').prop('disabled', false);
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function aggregate_items() : void {
|
|
global $agg_item_actions, $item_rows;
|
|
|
|
// ================= input validation and session storage =================
|
|
$filters = [
|
|
'rows' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'pageset' => true,
|
|
'default' => '-1'
|
|
],
|
|
'template_id' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'pageset' => true,
|
|
'default' => '-1'
|
|
],
|
|
'rfilter' => [
|
|
'filter' => FILTER_VALIDATE_IS_REGEX,
|
|
'pageset' => true,
|
|
'default' => ''
|
|
],
|
|
'page' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '1'
|
|
],
|
|
'matching' => [
|
|
'filter' => FILTER_VALIDATE_REGEXP,
|
|
'default' => 'on',
|
|
'pageset' => true,
|
|
'options' => ['options' => ['regexp' => '/^(on|true|false)$/']]
|
|
],
|
|
'sort_column' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'default' => 'title_cache',
|
|
'options' => ['options' => 'sanitize_search_string']
|
|
],
|
|
'sort_direction' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'default' => 'ASC',
|
|
'options' => ['options' => 'sanitize_search_string']
|
|
],
|
|
'custom' => [
|
|
'filter' => FILTER_VALIDATE_REGEXP,
|
|
'options' => ['options' => ['regexp' => '(true|false)']],
|
|
'pageset' => true,
|
|
'default' => ''
|
|
],
|
|
'local_graph_ids' => [
|
|
'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
|
|
'pageset' => true,
|
|
'default' => ''
|
|
]
|
|
];
|
|
|
|
validate_store_request_vars($filters, 'sess_agraph_item');
|
|
// ================= input validation =================
|
|
|
|
if (grv('rows') == -1) {
|
|
$rows = read_config_option('num_rows_table');
|
|
} else {
|
|
$rows = grv('rows');
|
|
}
|
|
|
|
// form the 'where' clause for our main sql query
|
|
$sql_where_params = [];
|
|
|
|
if (grv('rfilter') == '') {
|
|
$sql_where = '';
|
|
} elseif (validate_is_regex(grv('rfilter')) === true) {
|
|
$sql_where = 'WHERE gtg.title_cache RLIKE ?';
|
|
$sql_where_params[] = grv('rfilter');
|
|
} else {
|
|
$filters = explode(' ', grv('rfilter'));
|
|
$sql_where = '';
|
|
$sql_where = aggregate_make_sql_where($sql_where, $filters, 'gtg.title_cache');
|
|
}
|
|
|
|
if (grv('matching') != 'false') {
|
|
$sql_where .= ($sql_where != '' ? ' AND' : 'WHERE') . ' (agi.local_graph_id IS NOT NULL)';
|
|
}
|
|
|
|
$graph_template = db_fetch_cell_prepared('SELECT graph_template_id
|
|
FROM aggregate_graphs AS ag
|
|
WHERE ag.local_graph_id = ?',
|
|
[grv('id')]
|
|
);
|
|
|
|
$aggregate_id = db_fetch_cell_prepared('SELECT id
|
|
FROM aggregate_graphs
|
|
WHERE local_graph_id = ?',
|
|
[grv('id')]
|
|
);
|
|
|
|
$total_items = db_fetch_cell_prepared('SELECT COUNT(*)
|
|
FROM aggregate_graphs_items
|
|
WHERE aggregate_graph_id = ?',
|
|
[$aggregate_id]
|
|
);
|
|
|
|
if (!empty($graph_template)) {
|
|
$sql_where .= ($sql_where != '' ? ' AND' : 'WHERE') . ' (gtg.graph_template_id=?)';
|
|
$sql_where_params[] = $graph_template;
|
|
}
|
|
|
|
if (grv('local_graph_ids') != '') {
|
|
$sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . db_in_clause('agi.local_graph_id', grv('local_graph_ids'));
|
|
}
|
|
|
|
$sql_params = array_merge([$aggregate_id], $sql_where_params);
|
|
|
|
$sql = "SELECT COUNT(DISTINCT gl.id) AS total
|
|
FROM graph_templates_graph AS gtg
|
|
INNER JOIN graph_local AS gl
|
|
ON gtg.local_graph_id=gl.id
|
|
LEFT JOIN (
|
|
SELECT DISTINCT local_graph_id
|
|
FROM aggregate_graphs_items
|
|
WHERE aggregate_graph_id=?) AS agi
|
|
ON gtg.local_graph_id=agi.local_graph_id
|
|
$sql_where";
|
|
|
|
$total_rows = get_total_row_data($_SESSION[SESS_USER_ID], $sql, $sql_params, 'aggregate_graph');
|
|
|
|
$sql_order = get_order_string();
|
|
$sql_limit = ' LIMIT ' . ($rows * (grv('page') - 1)) . ',' . $rows;
|
|
|
|
$graph_list = db_fetch_assoc_prepared("SELECT
|
|
gtg.id, gtg.local_graph_id, gtg.height, gtg.width, gtg.title_cache, agi.local_graph_id AS agg_graph_id
|
|
FROM graph_templates_graph AS gtg
|
|
INNER JOIN graph_local AS gl
|
|
ON gtg.local_graph_id=gl.id
|
|
LEFT JOIN (
|
|
SELECT DISTINCT local_graph_id
|
|
FROM aggregate_graphs_items
|
|
WHERE aggregate_graph_id=?) AS agi
|
|
ON gtg.local_graph_id=agi.local_graph_id
|
|
$sql_where
|
|
$sql_order
|
|
$sql_limit",
|
|
$sql_params
|
|
);
|
|
|
|
?>
|
|
<script type='text/javascript'>
|
|
var totalItems = <?php print (int) $total_items; ?>;
|
|
|
|
$(function() {
|
|
if (totalItems == 0) {
|
|
$('#agg_preview').hide();
|
|
}
|
|
});
|
|
</script>
|
|
<?php
|
|
|
|
$filter_array = [
|
|
'rows' => [
|
|
[
|
|
'rfilter' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Search'),
|
|
'filter' => FILTER_VALIDATE_IS_REGEX,
|
|
'placeholder' => __('Enter a search term'),
|
|
'size' => '45',
|
|
'default' => '',
|
|
'pageset' => true,
|
|
'max_length' => '120',
|
|
'value' => ''
|
|
],
|
|
'rows' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Graphs'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'array' => $item_rows,
|
|
'value' => '-1'
|
|
],
|
|
'matching' => [
|
|
'method' => 'filter_checkbox',
|
|
'friendly_name' => __('Part of Aggregate'),
|
|
'filter' => FILTER_VALIDATE_REGEXP,
|
|
'filter_options' => ['options' => ['regexp' => '/^(on|true|false)$/']],
|
|
'default' => 'on',
|
|
'value' => gnrv('matching')
|
|
]
|
|
]
|
|
],
|
|
'buttons' => [
|
|
'go' => [
|
|
'method' => 'submit',
|
|
'display' => __('Go'),
|
|
'title' => __('Apply filter to table'),
|
|
],
|
|
'clear' => [
|
|
'method' => 'button',
|
|
'display' => __('Clear'),
|
|
'title' => __('Reset filter to default values'),
|
|
]
|
|
],
|
|
'sort' => [
|
|
'sort_column' => 'title_cache',
|
|
'sort_direction' => 'ASC'
|
|
]
|
|
];
|
|
|
|
$pageFilter = new CactiTableFilter(__('Matching Graphs'), 'aggregate_graphs.php?action=edit&tab=items&id=' . grv('id'), 'form_aggregate_items', 'sess_agraph_item', '', '', false);
|
|
$pageFilter->set_filter_array($filter_array);
|
|
$pageFilter->render();
|
|
|
|
// print checkbox form for validation
|
|
form_start('aggregate_graphs.php', 'chk');
|
|
|
|
html_start_box('', '100%', false, 3, 'center', '');
|
|
|
|
$nav = html_nav_bar('aggregate_graphs.php?action=edit&tab=items&id=' . gfrv('id'), MAX_DISPLAY_PAGES, (int) grv('page'), $rows, $total_rows, 5, __('Graphs'), 'page', 'main');
|
|
|
|
print $nav;
|
|
|
|
$display_text = [
|
|
'title_cache' => [
|
|
'display' => __('Graph Title'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC'
|
|
],
|
|
'local_graph_id' => [
|
|
'display' => __('ID'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC'
|
|
],
|
|
'agg_graph_id' => [
|
|
'display' => __('Included in Aggregate'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC'
|
|
],
|
|
'height' => [
|
|
'display' => __('Size'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC'
|
|
]
|
|
];
|
|
|
|
html_header_sort_checkbox($display_text, grv('sort_column'), grv('sort_direction'), false, 'aggregate_graphs.php?action=edit&id=' . gfrv('id'));
|
|
|
|
if (cacti_sizeof($graph_list) > 0) {
|
|
foreach ($graph_list as $graph) {
|
|
// we're escaping strings here, so no need to escape them on form_selectable_cell
|
|
form_alternate_row('line' . $graph['local_graph_id'], true);
|
|
|
|
if (validate_is_regex(grv('rfilter')) === true) {
|
|
form_selectable_cell(filter_value($graph['title_cache'], grv('rfilter')), $graph['local_graph_id']);
|
|
} else {
|
|
form_selectable_ecell(grv('rfilter') != '' ? aggregate_format_text($graph['title_cache'], grv('rfilter')) : $graph['title_cache'], $graph['local_graph_id']);
|
|
}
|
|
|
|
form_selectable_cell($graph['local_graph_id'], $graph['local_graph_id'], '', 'right');
|
|
form_selectable_cell(($graph['agg_graph_id'] != '' ? "<span class='associated'>" . __('Yes') . '</span>' : "<span class='notAssociated'>" . __('No') . '</span>'), $graph['local_graph_id']);
|
|
form_selectable_ecell($graph['height'] . 'x' . $graph['width'], $graph['local_graph_id'], '', 'right');
|
|
form_checkbox_cell($graph['title_cache'], $graph['local_graph_id']);
|
|
form_end_row();
|
|
}
|
|
} else {
|
|
print '<tr class="tableRow odd"><td colspan="4"><em>' . __('No Graphs Found') . '</em></td></tr>';
|
|
}
|
|
|
|
html_end_box(false);
|
|
|
|
// put the nav bar on the bottom as well
|
|
print $nav;
|
|
|
|
// add a list of tree names to the actions dropdown
|
|
add_tree_names_to_actions_array();
|
|
|
|
// draw the dropdown containing a list of available actions for this form
|
|
form_hidden_box('local_graph_id', grv('id'), '');
|
|
|
|
draw_actions_dropdown($agg_item_actions);
|
|
|
|
form_end();
|
|
}
|
|
|
|
function aggregate_make_sql_where(string $sql_where, array $items, string $field) : string {
|
|
if ($sql_where != '') {
|
|
$sql_where .= ' AND (';
|
|
} else {
|
|
$sql_where = 'WHERE (';
|
|
}
|
|
|
|
$indentation = 0;
|
|
$termcount = 0;
|
|
|
|
if (cacti_sizeof($items)) {
|
|
foreach ($items as $i) {
|
|
$i = trim($i);
|
|
|
|
while (substr($i, 0, 1) == '(') {
|
|
$indentation++;
|
|
$termcount = 0;
|
|
$sql_where .= '(';
|
|
$i = substr($i, 1);
|
|
}
|
|
|
|
$split = strpos($i, ')');
|
|
|
|
if ($split !== false) {
|
|
$end = trim(substr($i, $split));
|
|
$i = substr($i, 0, $split);
|
|
} else {
|
|
$end = '';
|
|
}
|
|
|
|
if ($i != '') {
|
|
if (cacti_strtolower($i) == 'and') {
|
|
$sql_where .= ' AND ';
|
|
} elseif (cacti_strtolower($i) == 'or') {
|
|
$sql_where .= ' OR ';
|
|
} else {
|
|
$sql_where .= ($termcount > 0 ? ' OR ' : '') . $field . ' LIKE ' . db_qstr('%' . trim($i) . '%');
|
|
$termcount++;
|
|
}
|
|
}
|
|
|
|
if ($end != '') {
|
|
while (substr($end, 0, 1) == ')') {
|
|
$indentation--;
|
|
$termcount = 0;
|
|
$sql_where .= ')';
|
|
$end = trim(substr($end, 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$sql_where .= ')';
|
|
|
|
return trim($sql_where);
|
|
}
|
|
|
|
function aggregate_format_text(string $text, string $filter) : string {
|
|
$items = explode(' ', $filter);
|
|
$tags = [];
|
|
|
|
foreach ($items as $i) {
|
|
$i = trim($i);
|
|
$i = str_replace('(', '', $i);
|
|
$i = str_replace(')', '', $i);
|
|
|
|
if (cacti_strtolower($i) == 'and' || cacti_strtolower($i) == 'or') {
|
|
continue;
|
|
}
|
|
|
|
if (substr_count($text, $i) > 0) {
|
|
$tagno = rand();
|
|
$tags[$tagno] = $i;
|
|
$text = str_replace($i, "<<$tagno>>", $text);
|
|
}
|
|
}
|
|
|
|
if (cacti_sizeof($tags)) {
|
|
foreach ($tags as $k => $t) {
|
|
$text = str_replace("<<$k>>", "<span class='filteredValue'>" . htmle($t) . '</span>', $text);
|
|
}
|
|
}
|
|
|
|
return $text;
|
|
}
|
|
|
|
function create_aggregate_filter() : array {
|
|
global $item_rows;
|
|
|
|
$any = ['-1' => __('Any')];
|
|
$none = ['0' => __('None')];
|
|
|
|
$templates = array_rekey(
|
|
db_fetch_assoc('SELECT DISTINCT at.id, at.name
|
|
FROM aggregate_graph_templates AS at
|
|
INNER JOIN aggregate_graphs AS ag
|
|
ON ag.aggregate_template_id=at.id
|
|
ORDER BY name'),
|
|
'id', 'name'
|
|
);
|
|
|
|
$templates = $any + $none + $templates;
|
|
|
|
return [
|
|
'rows' => [
|
|
[
|
|
'filter' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Search'),
|
|
'filter' => FILTER_DEFAULT,
|
|
'placeholder' => __('Enter a search term'),
|
|
'size' => '30',
|
|
'default' => '',
|
|
'pageset' => true,
|
|
'max_length' => '120',
|
|
'value' => ''
|
|
],
|
|
'template_id' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Template'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'array' => $templates,
|
|
'value' => '-1'
|
|
],
|
|
'rows' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Graphs'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'array' => $item_rows,
|
|
'value' => '-1'
|
|
],
|
|
'local_graph_ids' => [
|
|
'method' => 'validate',
|
|
'filter' => FILTER_VALIDATE_IS_NUMERIC_LIST,
|
|
'pageset' => true,
|
|
'default' => ''
|
|
]
|
|
]
|
|
],
|
|
'buttons' => [
|
|
'go' => [
|
|
'method' => 'submit',
|
|
'display' => __('Go'),
|
|
'title' => __('Apply Filter to Table'),
|
|
],
|
|
'clear' => [
|
|
'method' => 'button',
|
|
'display' => __('Clear'),
|
|
'title' => __('Reset Filter to Default Values'),
|
|
]
|
|
],
|
|
'sort' => [
|
|
'sort_column' => 'title_cache',
|
|
'sort_direction' => 'ASC'
|
|
],
|
|
'javascript' => [
|
|
'global' => '',
|
|
'ready' => ''
|
|
]
|
|
];
|
|
}
|
|
|
|
function draw_aggregate_filter(bool $render = false) : void {
|
|
$filters = create_aggregate_filter();
|
|
|
|
$header = __('Aggregate Graphs') . (grv('local_graph_ids') != '' ? __(' [ Custom Graphs List Applied - Clear to Reset ]') : '');
|
|
|
|
// create the page filter
|
|
$pageFilter = new CactiTableFilter($header, 'aggregate_graphs.php', 'forms', 'sess_agraph');
|
|
|
|
$pageFilter->set_filter_array($filters);
|
|
|
|
if ($render) {
|
|
$pageFilter->render();
|
|
} else {
|
|
$pageFilter->sanitize();
|
|
}
|
|
}
|
|
|
|
function aggregate_graph() : void {
|
|
global $actions, $item_rows;
|
|
|
|
draw_aggregate_filter(true);
|
|
|
|
if (grv('rows') == -1) {
|
|
$rows = read_config_option('num_rows_table');
|
|
} else {
|
|
$rows = grv('rows');
|
|
}
|
|
|
|
$sql_where = 'WHERE (gtg.graph_template_id = 0 AND gl.host_id = 0)';
|
|
$sql_params = [];
|
|
|
|
// form the 'where' clause for our main sql query
|
|
if (grv('filter') != '') {
|
|
$sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . '(gtg.title_cache LIKE ? OR ag.title_format LIKE ?';
|
|
|
|
$sql_params[] = '%' . grv('filter') . '%';
|
|
$sql_params[] = '%' . grv('filter') . '%';
|
|
|
|
if (ctype_digit(grv('filter'))) {
|
|
$sql_where .= ' OR gl.id = ?';
|
|
$sql_params[] = (int) grv('filter');
|
|
}
|
|
|
|
$sql_where .= ')';
|
|
}
|
|
|
|
if (grv('template_id') == '0') {
|
|
$sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . '(ag.aggregate_template_id = 0 OR ag.aggregate_template_id IS NULL)';
|
|
} elseif (grv('template_id') > 0) {
|
|
$sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . 'ag.aggregate_template_id= ?';
|
|
$sql_params[] = grv('template_id');
|
|
}
|
|
|
|
$sql = "SELECT COUNT(DISTINCT gl.id) AS total
|
|
FROM graph_templates_graph AS gtg
|
|
INNER JOIN graph_local AS gl
|
|
ON gtg.local_graph_id=gl.id
|
|
INNER JOIN aggregate_graphs AS ag
|
|
ON gtg.local_graph_id = ag.local_graph_id
|
|
LEFT JOIN aggregate_graph_templates AS agt
|
|
ON agt.id = ag.aggregate_template_id
|
|
$sql_where";
|
|
|
|
$total_rows = get_total_row_data($_SESSION[SESS_USER_ID], $sql, $sql_params, 'aggregate_graph');
|
|
|
|
$sql_order = get_order_string();
|
|
$sql_limit = ' LIMIT ' . ($rows * (grv('page') - 1)) . ',' . $rows;
|
|
|
|
$graph_list = db_fetch_assoc_prepared("SELECT
|
|
gtg.id, gtg.local_graph_id, gtg.height, gtg.width, gtg.title_cache, agt.name
|
|
FROM graph_templates_graph AS gtg
|
|
INNER JOIN graph_local AS gl
|
|
ON gtg.local_graph_id = gl.id
|
|
INNER JOIN aggregate_graphs AS ag
|
|
ON gl.id = ag.local_graph_id
|
|
LEFT JOIN aggregate_graph_templates AS agt
|
|
ON agt.id = ag.aggregate_template_id
|
|
$sql_where
|
|
AND ag.id IS NOT NULL
|
|
$sql_order
|
|
$sql_limit",
|
|
$sql_params);
|
|
|
|
$display_text = [
|
|
'title_cache' => [
|
|
'display' => __('Graph Title'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The title for the Aggregate Graphs')
|
|
],
|
|
'local_graph_id' => [
|
|
'display' => __('ID'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The internal database identifier for this object')
|
|
],
|
|
'name' => [
|
|
'display' => __('Aggregate Template'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The Aggregate Template that this Aggregate Graphs is based upon')
|
|
],
|
|
'height' => [
|
|
'display' => __('Size'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC'
|
|
]
|
|
];
|
|
|
|
$nav = html_nav_bar('aggregate_graphs.php', MAX_DISPLAY_PAGES, (int) grv('page'), $rows, $total_rows, 5, __('Aggregate Graphs'), 'page', 'main');
|
|
|
|
form_start('aggregate_graphs.php', 'chk');
|
|
|
|
print $nav;
|
|
|
|
html_start_box('', '100%', false, 3, 'center', '');
|
|
|
|
html_header_sort_checkbox($display_text, grv('sort_column'), grv('sort_direction'), false, 'aggregate_graphs.php?filter=' . rawurlencode(grv('filter')));
|
|
|
|
if (cacti_sizeof($graph_list)) {
|
|
foreach ($graph_list as $graph) {
|
|
// we're escaping strings here, so no need to escape them on form_selectable_cell
|
|
$template_name = htmle($graph['name']);
|
|
|
|
form_alternate_row('line' . $graph['local_graph_id'], true);
|
|
|
|
form_selectable_cell(filter_value(title_trim($graph['title_cache'], read_config_option('max_title_length')), grv('filter'), 'aggregate_graphs.php?action=edit&id=' . $graph['local_graph_id']), $graph['local_graph_id']);
|
|
form_selectable_cell($graph['local_graph_id'], $graph['local_graph_id'], '', 'right');
|
|
form_selectable_cell((empty($graph['name']) ? '<em>' . __('None') . '</em>' : filter_value($template_name, grv('filter'))), $graph['local_graph_id']);
|
|
form_selectable_ecell($graph['height'] . 'x' . $graph['width'], $graph['local_graph_id'], '', 'right');
|
|
|
|
form_checkbox_cell($graph['title_cache'], $graph['local_graph_id']);
|
|
|
|
form_end_row();
|
|
}
|
|
} else {
|
|
print '<tr class="tableRow odd"><td colspan="' . (cacti_sizeof($display_text) + 1) . '"><em>' . __('No Aggregate Graphs Found') . '</em></td></tr>';
|
|
}
|
|
|
|
html_end_box(false);
|
|
|
|
if (cacti_sizeof($graph_list)) {
|
|
print $nav;
|
|
}
|
|
|
|
// add a list of tree names to the actions dropdown
|
|
add_tree_names_to_actions_array();
|
|
|
|
// draw the dropdown containing a list of available actions for this form
|
|
draw_actions_dropdown($actions);
|
|
|
|
// remove old graphs
|
|
purge_old_graphs();
|
|
|
|
form_end();
|
|
}
|
|
|
|
function purge_old_graphs() : void {
|
|
// workaround to handle purged graphs
|
|
$old_graphs = array_rekey(db_fetch_assoc('SELECT DISTINCT local_graph_id
|
|
FROM aggregate_graphs_items AS pagi
|
|
LEFT JOIN graph_local AS gl
|
|
ON pagi.local_graph_id = gl.id
|
|
WHERE gl.id IS NULL
|
|
AND local_graph_id > 0'), 'local_graph_id', 'local_graph_id');
|
|
|
|
if (cacti_sizeof($old_graphs)) {
|
|
db_execute_prepared('DELETE FROM aggregate_graphs_items
|
|
WHERE local_graph_id IN (' . trim(str_repeat('?, ', cacti_sizeof($old_graphs)), ', ') . ')',
|
|
array_values($old_graphs));
|
|
}
|
|
|
|
$old_aggregates = array_rekey(db_fetch_assoc('SELECT DISTINCT local_graph_id
|
|
FROM aggregate_graphs AS pag
|
|
LEFT JOIN graph_local AS gl
|
|
ON pag.local_graph_id = gl.id
|
|
WHERE gl.id IS NULL
|
|
AND local_graph_id>0'), 'local_graph_id', 'local_graph_id');
|
|
|
|
$old_agg_ids = array_rekey(db_fetch_assoc('SELECT DISTINCT pag.id
|
|
FROM aggregate_graphs AS pag
|
|
LEFT JOIN graph_local AS gl
|
|
ON pag.local_graph_id = gl.id
|
|
WHERE gl.id IS NULL'), 'id', 'id');
|
|
|
|
if (cacti_sizeof($old_aggregates)) {
|
|
$placeholders = trim(str_repeat('?, ', cacti_sizeof($old_aggregates)), ', ');
|
|
|
|
$aggregate_ids = array_values($old_aggregates);
|
|
|
|
db_execute_prepared("DELETE FROM graph_templates_item
|
|
WHERE local_graph_id IN ($placeholders)
|
|
AND local_graph_id > 0", $aggregate_ids);
|
|
|
|
db_execute_prepared("DELETE FROM graph_templates_graph
|
|
WHERE local_graph_id IN ($placeholders)
|
|
AND local_graph_id > 0", $aggregate_ids);
|
|
|
|
db_execute_prepared("DELETE FROM aggregate_graphs
|
|
WHERE local_graph_id IN ($placeholders)", $aggregate_ids);
|
|
}
|
|
|
|
if (cacti_sizeof($old_agg_ids)) {
|
|
db_execute_prepared('DELETE FROM aggregate_graphs_items
|
|
WHERE aggregate_graph_id IN (' . trim(str_repeat('?, ', cacti_sizeof($old_agg_ids)), ', ') . ')',
|
|
array_values($old_agg_ids));
|
|
}
|
|
}
|