Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

--- develop ---

* feature: Turn the graph drilldown button into an 'Enable Tags'/'Disable Tags' toggle that reflects the current session state and shows or hides this plugin's tag legend lines on graphs, rather than a static link, so it reads cleanly as a label in Cacti's graph action menu
* security: Replace the confirmation pages' inline `onClick='cactiReturnTo()'` Cancel buttons with the CSP-safe `cactiReturnTo` class so the pages no longer trip Cacti's Content-Security-Policy script-src-attr directive; see the README for the one-time `applySkin()` snippet needed when running Cacti below 1.2.31 (1.2.31+ binds the class automatically)
* security: Move the filter selects' inline onChange handlers into the ready block (and bind them via change, not click) so the page no longer trips Cacti's Content-Security-Policy script-src-attr directive
* dev: Measure CI coverage with xdebug instead of pcov so the plugin's own sources are instrumented (pcov auto-scopes to the Composer root and skipped cacti/plugins/, leaving the patch-coverage gate with nothing to measure)
Expand Down
56 changes: 45 additions & 11 deletions include/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,12 @@ function plugin_tags_device_remove($ids) {
function plugin_tags_rrd_graph_graph_options($data) {
global $config, $tags_colors;

// Respect the graph drilldown 'Enable/Disable Tags' toggle (session state
// set by plugin_tags_graph_button()); skip drawing when tags are disabled.
if (isset($_SESSION['sess_config_array']['tags_draw']) && $_SESSION['sess_config_array']['tags_draw'] == 'off') {
return $data;
}

require($config['base_path'] . '/plugins/tags/include/arrays.php');

$tags = [];
Expand Down Expand Up @@ -682,29 +688,57 @@ function plugin_tags_rrd_graph_graph_options($data) {

/**
* Hook implementation for Cacti's 'graph_buttons'/'graph_buttons_thumbnails'
* filters. Prints a link/icon that opens tags.php filtered to the current
* graph's device. Called by Cacti core via
* filters. Prints the drilldown action icon that toggles whether this
* plugin's tag legend lines are drawn on graphs, showing 'Enable Tags' or
* 'Disable Tags' per the current session state. Called by Cacti core via
* api_plugin_hook('graph_buttons'/'graph_buttons_thumbnails', ...) while
* rendering a graph's action buttons.
*
* @param array $data Hook payload; $data[1]['local_graph_id'] identifies
* the current graph.
*
* @return void Outputs HTML directly.
*
* @global array $config Cacti global configuration array; used to build
* the tags.php link URL.
*/
function plugin_tags_graph_button($data) {
global $config;
if (isset_request_var('tags_draw') || isset($_SESSION['sess_config_array']['tags_draw'])) {
if (isset_request_var('tags_draw')) {
if (get_nfilter_request_var('tags_draw') == 'on') {
$draw = 'off';
} else {
$draw = 'on';
}

$host_id = db_fetch_cell_prepared('SELECT host_id FROM graph_local
WHERE id = ?',[$data[1]['local_graph_id']]);
$_SESSION['sess_config_array']['tags_draw'] = $draw;
} else {
$draw = $_SESSION['sess_config_array']['tags_draw'];
}
} else {
// Tags are drawn by default, preserving the plugin's prior behaviour.
$draw = 'on';
$_SESSION['sess_config_array']['tags_draw'] = $draw;
}

// Drop any existing tags_draw parameter (? or & form) so a click never
// leaves a duplicated, conflicting tags_draw in the URL.
$url = $_SERVER['REQUEST_URI'];
$url = preg_replace('/([?&])tags_draw=(?:on|off)/', '$1', $url);
$url = str_replace('?&', '?', $url);
$url = rtrim($url, '?&');

if (!substr_count($url, '?')) {
$separator = '?';
} else {
$separator = '&';
}

$redir = $config['url_path'] . 'plugins/tags/tags.php?host_id=' . $host_id;
// Two complete strings so the label stays grammatical when translated.
if ($draw == 'on') {
$title = __esc('Disable Tags', 'tags');
} else {
$title = __esc('Enable Tags', 'tags');
Comment thread
TheWitness marked this conversation as resolved.
}

$fav = '<i class="fas fa-tags" title="' . __esc('Show tags for this graph', 'tags') . '"></i>';
print '<a class="iconLink" href="' . html_escape($redir) . '">' . $fav . '</a><br/>';
print '<a class="iconLink tagsDraw" href="' . html_escape($url . $separator . 'tags_draw=' . $draw) . '" title="' . $title . '"><i class="tagsDraw fas fa-tags"></i></a><br/>';
}

/**
Expand Down
10 changes: 7 additions & 3 deletions locales/po/cacti.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ msgid ""
msgstr ""
"Project-Id-Version: Cacti \n"
"Report-Msgid-Bugs-To: developers@cacti.net\n"
"POT-Creation-Date: 2026-10-07 16:11-0400\n"
"POT-Creation-Date: 2026-10-13 10:03-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Cacti Developers <developers@cacti.net>>\n"
"Last-Translator: Cacti Developers <developers@cacti.net>\n"
"Language-Team: Cacti Developers <developers@cacti.net>\n"
"Language: \n"
"MIME-Version: 1.0\n"
Expand Down Expand Up @@ -371,7 +371,11 @@ msgid "Restart, uptime was %s"
msgstr ""

#: include/functions.php
msgid "Show tags for this graph"
msgid "Disable Tags"
msgstr ""

#: include/functions.php
msgid "Enable Tags"
msgstr ""

#: setup.php
Expand Down
105 changes: 105 additions & 0 deletions tests/Unit/TagsGraphButtonTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?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. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
| http://www.cacti.net/ |
+-------------------------------------------------------------------------+
*/

/*
* Unit coverage for plugin_tags_graph_button() and the tag-draw gate in
* plugin_tags_rrd_graph_graph_options() (include/functions.php): the
* 'graph_buttons' hook prints a drilldown toggle that enables/disables
* drawing this plugin's tag legend lines on graphs for the session.
*/

beforeAll(function () {
require_once __DIR__ . '/../../include/functions.php';
});

beforeEach(function () {
$GLOBALS['__test_request'] = array();
$_SESSION = array();
$_SERVER['REQUEST_URI'] = '/cacti/graph_view.php?action=preview';
});

it('shows the Disable Tags toggle by default with tags drawn', function () {
ob_start();
plugin_tags_graph_button(array(1 => array('local_graph_id' => 42)));
$html = ob_get_clean();

expect($html)->toContain('class="iconLink tagsDraw"')
->and($html)->toContain('fas fa-tags')
->and($html)->toContain('title="Disable Tags"')
->and($html)->toContain('tags_draw=on')
->and($html)->not->toContain('Show tags for this graph');

expect($_SESSION['sess_config_array']['tags_draw'])->toBe('on');
});

it('flips an on request to off and shows Enable Tags, prefixing ? when the url has none', function () {
test_set_request(array('tags_draw' => 'on'));
$_SERVER['REQUEST_URI'] = '/cacti/graph_view.php';

ob_start();
plugin_tags_graph_button(array(1 => array('local_graph_id' => 42)));
$html = ob_get_clean();

expect($html)->toContain('title="Enable Tags"')
->and($html)->toContain('?tags_draw=off');

expect($_SESSION['sess_config_array']['tags_draw'])->toBe('off');
});

it('flips an off request back to on and shows Disable Tags', function () {
test_set_request(array('tags_draw' => 'off'));

ob_start();
plugin_tags_graph_button(array(1 => array('local_graph_id' => 42)));
$html = ob_get_clean();

expect($html)->toContain('title="Disable Tags"')
->and($html)->toContain('tags_draw=on');

expect($_SESSION['sess_config_array']['tags_draw'])->toBe('on');
});

it('reads the draw state from the session when no request var is set', function () {
$_SESSION['sess_config_array']['tags_draw'] = 'off';

ob_start();
plugin_tags_graph_button(array(1 => array('local_graph_id' => 42)));
$html = ob_get_clean();

expect($html)->toContain('title="Enable Tags"')
->and($html)->toContain('tags_draw=off');
});

it('replaces an existing first-parameter tags_draw instead of duplicating it', function () {
test_set_request(array('tags_draw' => 'on'));
$_SERVER['REQUEST_URI'] = '/cacti/graph_view.php?tags_draw=on';

ob_start();
plugin_tags_graph_button(array(1 => array('local_graph_id' => 42)));
$html = ob_get_clean();

expect($html)->toContain('?tags_draw=off')
->and($html)->not->toContain('tags_draw=on');
});

it('skips drawing tag legends when the session toggle is off', function () {
$_SESSION['sess_config_array']['tags_draw'] = 'off';

$data = array('graph_id' => 10, 'start' => 0, 'end' => 100, 'txt_graph_items' => 'ORIGINAL');
$result = plugin_tags_rrd_graph_graph_options($data);

expect($result)->toBe($data);
});
6 changes: 6 additions & 0 deletions tests/bootstrap-unit.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,12 @@ function get_nfilter_request_var($name) {
}
}

if (!function_exists('isset_request_var')) {
function isset_request_var($name) {
return isset($GLOBALS['__test_request'][$name]);
}
}

if (!function_exists('get_filter_request_var')) {
function get_filter_request_var($name) {
return isset($GLOBALS['__test_request'][$name]) ? $GLOBALS['__test_request'][$name] : '';
Expand Down
Loading