mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
* PHPStan Level 8 - Phase 1 Conversion
Reduces the real (no-baseline) PHPStan level 8 error count from ~2,724 to 425
and regenerates phpstan-baseline.neon accordingly (8,941 -> 1,921 lines).
Core type-safety changes
- lib/database.php: narrow db_fetch_row()/db_fetch_row_prepared() to return
array (was bool|array) and db_fetch_assoc()/db_fetch_assoc_prepared() to
return array (was mixed); the _prepared workers coerce the connection-failure
path to an empty array so the declared type is honest. empty()/cacti_sizeof()
checks are unaffected.
- lib/database.php: narrow $db_conn guards from is_object() to instanceof PDO in
the transaction/quote/insert-id helpers so the PDO calls resolve.
- lib/mib_cache.php: the fluent MibCache methods mib()/object()/table()/row()
now return self instead of a bare object so chained calls type-check.
Type-safety casts and guards (mechanical, behaviour-preserving)
- Cast string|false/|null builtin results feeding string parameters
(json_decode, base64_encode, trim, htmle, preg_replace, explode, etc.).
- Cast int|string / int|false values feeding int id/pid parameters.
- Guard resource|false fopen()/popen() handles before fwrite/fclose/fread, and
wrap foreach over array|false/list|false results with `?: []`.
Genuine bugs found and fixed
- cli/add_tree.php / lib/api_tree.php: site tree nodes could not be created via
the CLI - the --site-id option was never parsed, $nodeTypes had no 'site'
entry (passing null to api_tree_item_save(int $type) -> TypeError), and
api_tree_item_save() never persisted site_id. All three fixed.
- tree.php: leaves_exist() is typed : int but returned db_fetch_assoc_prepared()
(an array of rows) for a SELECT COUNT(*); now uses db_fetch_cell_prepared()
and returns the scalar count.
- lib/mib_cache.php: $entries_per_object was a float (division / ceil) used as
an array key (PHP 8.1 implicit float-to-int key deprecation); now cast to int.
Config
- .phpstan.neon: add notIdentical.alwaysTrue and booleanAnd.alwaysFalse to the
existing always-true/false ignore family (same "PHPStan gets confused"
dynamic-constant cause already covered by identical.alwaysTrue,
notEqual.alwaysTrue, booleanAnd.leftAlwaysTrue, etc.).
- phpstan-baseline.neon: regenerated; the remaining 425 entries are the
untouched long tail for Phase 2.
* Remove redundant @phpstan-ignore comments
Follow-up cleanup for the level 8 Phase 1 work. Removes 14 inline
@phpstan-ignore comments that no longer suppress any reported error:
- cli/repair_database.php: `@phpstan-ignore booleanAnd.alwaysFalse` is now
covered by the global booleanAnd.alwaysFalse ignore added to .phpstan.neon.
- 13 others (host.php, include/global.php, include/session.php,
lib/api_automation.php x2, lib/functions.php x2, lib/html_utility.php x2,
lib/installer.php, lib/package.php, lib/reports.php, lib/rrd.php) no longer
match any reported error after the type-safety fixes (e.g. the
db_fetch_row_prepared() return-type narrowing removed the offset-assign
errors the api_automation ignores were suppressing).
Verified with reportUnmatchedIgnoredErrors enabled: 0 unmatched ignores
remain and the full level 8 run still reports 0 errors.
* ci: satisfy validator ratchet and refresh security sink baselines
- lib/api_tree.php: give the new tree-item site_id a real numeric
constraint ('^[-0-9]+$', matching html_reports.php) instead of an
empty regex, so the validator constraint ratchet passes without
bumping its baseline.
- tests/security/baselines: refresh the three sink/hotspot signatures
whose code text changed from the (string) casts added in this branch
(lib/poller.php setsid, cli/rrdresize.php json_encode, lib/import.php
simplexml_load_string).
* ci(js): allow DOMPurify 3.4.16 and update sync_js pin test
The dependabot bump (#8134) moved package.json/package-lock.json to
DOMPurify 3.4.16 on develop but left tests/js/sync_js.test.mjs pinned to
3.4.15, so the PR merge commit fails the 'DOMPurify is pinned' contract.
Bring package.json/package-lock.json to 3.4.16 (matching develop) and
update the pin test to expect 3.4.16.
* test: update sql.php null-handling assertion for the (string) cast
This branch changed scripts/sql.php from trim(\ ?? '') to
trim((string) \) (the phpstan-safe form that coerces both null and
false from shell_exec()). Update SqlScriptsTest's null-return assertion
to match the new cast instead of the removed '?? ''.
* fix: address Copilot review - ownership IDs, fopen guards, mib_cache bound
- chown()/chgrp(): pass integer UID/GID from fileowner()/filegroup()
(guarding the false lookup) instead of string-casting them, which made
them resolve account/group names like '1001' and fail. Covers
lib/api_device.php, lib/boost.php and poller_maintenance.php (x2).
- fopen() failure now short-circuits instead of reporting success:
lib/package.php returns false, lib/clog_webapi.php and utilities.php
render an error, and snmpagent_mibcachechild.php skips the cache write
and sentinel unlink so a concurrent writer's lock is preserved.
- lib/mib_cache.php: keep the float quotient as the loop bound and cast
only the array offset, so a non-even entries/columns split no longer
truncates the loop and drops a cached row.
* revert: keep db_fetch_* returning false on error; baseline Level 8 instead
Per review, restoring the intentional fail-safe: db_fetch_row[_prepared]()
return bool|array and db_fetch_assoc[_prepared]() return mixed again, so a
genuine SQL error (not just a missing connection) is signalled as false and
the === false checks in cdef_is_in_use(), boost_process_* and aggregate.php
work as before. Dropped the is_array() ? : [] coercion.
Level 8 stays green by regenerating phpstan-baseline.neon to capture the
re-introduced bool|array/mixed call-site errors (the ~1,850 the narrowing
had eliminated). Tightening those callers to drop them from the baseline is
Phase 2 work.
---------
Co-authored-by: TheWitness <copilot@users.noreply.github.com>
2303 lines
62 KiB
PHP
2303 lines
62 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_tree.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_tree.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/data_query.php');
|
|
|
|
$actions = [
|
|
1 => __x('dropdown action', 'Delete'),
|
|
2 => __x('dropdown action', 'Publish'),
|
|
3 => __x('dropdown action', 'Un-Publish'),
|
|
4 => __x('dropdown action', 'Un-Lock')
|
|
];
|
|
|
|
// set default action
|
|
set_default_action();
|
|
|
|
if (grv('action') != '') {
|
|
// ================= input validation and session storage =================
|
|
$filters = [
|
|
'tree_id' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => ''
|
|
],
|
|
'leaf_id' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => ''
|
|
],
|
|
'graph_tree_id' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => ''
|
|
],
|
|
'parent_item_id' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => ''
|
|
],
|
|
'parent' => [
|
|
'filter' => FILTER_VALIDATE_REGEXP,
|
|
'options' => ['options' => ['regexp' => '/([_\-a-z:0-9#]+)/']],
|
|
'pageset' => true,
|
|
'default' => ''
|
|
],
|
|
'position' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => ''
|
|
],
|
|
'nodeid' => [
|
|
'filter' => FILTER_VALIDATE_REGEXP,
|
|
'options' => ['options' => ['regexp' => '/([_\-a-z:0-9#]+)/']],
|
|
'pageset' => true,
|
|
'default' => ''
|
|
],
|
|
'id' => [
|
|
'filter' => FILTER_VALIDATE_REGEXP,
|
|
'options' => ['options' => ['regexp' => '/([_\-a-z:0-9#]+)/']],
|
|
'pageset' => true,
|
|
'default' => ''
|
|
]
|
|
];
|
|
|
|
validate_store_request_vars($filters);
|
|
// ================= input validation =================
|
|
}
|
|
|
|
switch (grv('action')) {
|
|
case 'save':
|
|
form_save();
|
|
|
|
break;
|
|
case 'actions':
|
|
form_actions();
|
|
|
|
break;
|
|
case 'sortasc':
|
|
tree_sort_name_asc();
|
|
header('Location: tree.php');
|
|
|
|
break;
|
|
case 'sortdesc':
|
|
tree_sort_name_desc();
|
|
header('Location: tree.php');
|
|
|
|
break;
|
|
case 'edit':
|
|
top_header();
|
|
tree_edit();
|
|
bottom_footer();
|
|
|
|
break;
|
|
case 'sites':
|
|
display_sites();
|
|
|
|
break;
|
|
case 'hosts':
|
|
display_hosts();
|
|
|
|
break;
|
|
case 'graphs':
|
|
display_graphs();
|
|
|
|
break;
|
|
case 'tree_up':
|
|
tree_up();
|
|
|
|
break;
|
|
case 'tree_down':
|
|
tree_down();
|
|
|
|
break;
|
|
case 'ajax_dnd':
|
|
tree_dnd();
|
|
|
|
break;
|
|
case 'lock':
|
|
api_tree_lock(grv('id'), $_SESSION[SESS_USER_ID]);
|
|
tree_edit(true);
|
|
|
|
break;
|
|
case 'unlock':
|
|
api_tree_unlock(grv('id'), $_SESSION[SESS_USER_ID]);
|
|
tree_edit(true);
|
|
|
|
break;
|
|
case 'copy_node':
|
|
if (!is_tree_allowed((int) grv('tree_id'))) {
|
|
raise_message('tree_idor', __('You do not have permission to modify this tree.'), MESSAGE_LEVEL_ERROR);
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
api_tree_copy_node(grv('tree_id'), grv('id'), grv('parent'), grv('position'));
|
|
|
|
break;
|
|
case 'create_node':
|
|
if (!is_tree_allowed((int) grv('tree_id'))) {
|
|
raise_message('tree_idor', __('You do not have permission to modify this tree.'), MESSAGE_LEVEL_ERROR);
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
api_tree_create_node(grv('tree_id'), grv('id'), grv('position'), gnrv('text'));
|
|
|
|
break;
|
|
case 'delete_node':
|
|
if (!is_tree_allowed((int) grv('tree_id'))) {
|
|
raise_message('tree_idor', __('You do not have permission to modify this tree.'), MESSAGE_LEVEL_ERROR);
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
api_tree_delete_node(grv('tree_id'), grv('id'));
|
|
|
|
break;
|
|
case 'move_node':
|
|
if (!is_tree_allowed((int) grv('tree_id'))) {
|
|
raise_message('tree_idor', __('You do not have permission to modify this tree.'), MESSAGE_LEVEL_ERROR);
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
api_tree_move_node(grv('tree_id'), grv('id'), grv('parent'), grv('position'));
|
|
|
|
break;
|
|
case 'rename_node':
|
|
if (!is_tree_allowed((int) grv('tree_id'))) {
|
|
raise_message('tree_idor', __('You do not have permission to modify this tree.'), MESSAGE_LEVEL_ERROR);
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
api_tree_rename_node(grv('tree_id'), grv('id'), gnrv('text'));
|
|
|
|
break;
|
|
case 'get_node':
|
|
api_tree_get_node(grv('tree_id'), grv('id'));
|
|
|
|
break;
|
|
case 'get_host_sort':
|
|
get_host_sort_type();
|
|
|
|
break;
|
|
case 'set_host_sort':
|
|
set_host_sort_type();
|
|
|
|
break;
|
|
case 'get_branch_sort':
|
|
get_branch_sort_type();
|
|
|
|
break;
|
|
case 'set_branch_sort':
|
|
set_branch_sort_type();
|
|
|
|
break;
|
|
default:
|
|
top_header();
|
|
tree();
|
|
bottom_footer();
|
|
|
|
break;
|
|
}
|
|
|
|
function tree_check_sequences() : void {
|
|
$bad_seq = db_fetch_cell('SELECT COUNT(sequence)
|
|
FROM graph_tree
|
|
WHERE sequence <= 0');
|
|
|
|
$dup_seq = db_fetch_cell('SELECT SUM(count)
|
|
FROM (
|
|
SELECT sequence, COUNT(sequence) AS count
|
|
FROM graph_tree
|
|
GROUP BY sequence
|
|
) AS t
|
|
WHERE t.count > 1');
|
|
|
|
// report any bad or duplicate sequences to the log for reporting purposes
|
|
if ($bad_seq > 0) {
|
|
cacti_log('WARN: Found ' . $bad_seq . ' Sequences in graph_tree Table', false, 'TREE', POLLER_VERBOSITY_HIGH);
|
|
}
|
|
|
|
if ($dup_seq > 0) {
|
|
cacti_log('WARN: Found ' . $dup_seq . ' Sequences in graph_tree Table', false, 'TREE', POLLER_VERBOSITY_HIGH);
|
|
}
|
|
|
|
if ($bad_seq > 0 || $dup_seq > 0) {
|
|
// resequence the list so it has no gaps, and 0 values will appear at the top
|
|
// since that's where they would have been displayed
|
|
db_execute('SET @seq = 0; UPDATE graph_tree SET sequence = (@seq:=@seq+1) ORDER BY sequence, id;');
|
|
}
|
|
}
|
|
|
|
function tree_sort_name_asc() : void {
|
|
// resequence the list so it has no gaps, alphabetically ascending
|
|
db_execute('SET @seq = 0; UPDATE graph_tree SET sequence = (@seq:=@seq+1) ORDER BY name;');
|
|
}
|
|
|
|
function tree_sort_name_desc() : void {
|
|
// resequence the list so it has no gaps, alphabetically ascending
|
|
db_execute('SET @seq = 0; UPDATE graph_tree SET sequence = (@seq:=@seq+1) ORDER BY name DESC;');
|
|
}
|
|
|
|
function tree_down() : void {
|
|
tree_check_sequences();
|
|
|
|
$tree_id = gfrv('id');
|
|
|
|
$seq = db_fetch_cell_prepared('SELECT sequence
|
|
FROM graph_tree
|
|
WHERE id = ?',
|
|
[$tree_id]);
|
|
|
|
$new_seq = $seq + 1;
|
|
|
|
// update the old tree first
|
|
db_execute_prepared('UPDATE graph_tree
|
|
SET sequence = ?
|
|
WHERE sequence = ?',
|
|
[$seq, $new_seq]);
|
|
|
|
// update the tree in question
|
|
db_execute_prepared('UPDATE graph_tree
|
|
SET sequence = ?
|
|
WHERE id = ?',
|
|
[$new_seq, $tree_id]);
|
|
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
function tree_up() : void {
|
|
tree_check_sequences();
|
|
|
|
$tree_id = gfrv('id');
|
|
|
|
$seq = db_fetch_cell_prepared('SELECT sequence
|
|
FROM graph_tree
|
|
WHERE id = ?',
|
|
[$tree_id]);
|
|
|
|
$new_seq = $seq - 1;
|
|
|
|
// update the old tree first
|
|
db_execute_prepared('UPDATE graph_tree
|
|
SET sequence = ?
|
|
WHERE sequence = ?',
|
|
[$seq, $new_seq]);
|
|
|
|
// update the tree in question
|
|
db_execute_prepared('UPDATE graph_tree
|
|
SET sequence = ?
|
|
WHERE id = ?',
|
|
[$new_seq, $tree_id]);
|
|
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
function tree_dnd() : void {
|
|
if (isrv('tree_ids') && is_array(gnrv('tree_ids'))) {
|
|
$tids = gnrv('tree_ids');
|
|
$sequence = 1;
|
|
|
|
foreach ($tids as $id) {
|
|
$id = str_replace('line', '', $id);
|
|
input_validate_input_number($id, 'id');
|
|
|
|
db_execute_prepared('UPDATE graph_tree
|
|
SET sequence = ?
|
|
WHERE id = ?',
|
|
[$sequence, $id]);
|
|
|
|
$sequence++;
|
|
}
|
|
|
|
/**
|
|
* Save the last time a tree branch was created/updated
|
|
* for Caching.
|
|
*/
|
|
set_config_option('time_last_change_branch', time());
|
|
}
|
|
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
function get_host_sort_type() : void {
|
|
if (isrv('nodeid')) {
|
|
$ndata = explode('_', grv('nodeid'));
|
|
|
|
if (cacti_sizeof($ndata)) {
|
|
foreach ($ndata as $n) {
|
|
$parts = explode(':', $n);
|
|
|
|
if ($parts[0] == 'tbranch') {
|
|
$branch = $parts[1];
|
|
input_validate_input_number($branch, 'branch');
|
|
|
|
$sort_type = db_fetch_cell_prepared('SELECT host_grouping_type
|
|
FROM graph_tree_items
|
|
WHERE id = ?',
|
|
[$branch]);
|
|
|
|
if ($sort_type == HOST_GROUPING_GRAPH_TEMPLATE) {
|
|
print 'hsgt';
|
|
} else {
|
|
print 'hsdq';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function set_host_sort_type() : void {
|
|
$type = '';
|
|
$branch = '';
|
|
|
|
// clean up type string
|
|
if (isrv('type')) {
|
|
srv('type', sanitize_search_string(grv('type')));
|
|
}
|
|
|
|
if (isrv('nodeid')) {
|
|
$ndata = explode('_', grv('nodeid'));
|
|
|
|
if (cacti_sizeof($ndata)) {
|
|
foreach ($ndata as $n) {
|
|
$parts = explode(':', $n);
|
|
|
|
if ($parts[0] == 'tbranch') {
|
|
$branch = $parts[1];
|
|
input_validate_input_number($branch, 'branch');
|
|
|
|
if (grv('type') == 'hsgt') {
|
|
$type = HOST_GROUPING_GRAPH_TEMPLATE;
|
|
} else {
|
|
$type = HOST_GROUPING_DATA_QUERY_INDEX;
|
|
}
|
|
|
|
db_execute_prepared('UPDATE graph_tree_items
|
|
SET host_grouping_type = ?
|
|
WHERE id = ?',
|
|
[$type, $branch]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function get_branch_sort_type() : void {
|
|
if (isrv('nodeid')) {
|
|
$ndata = explode('_', grv('nodeid'));
|
|
|
|
if (cacti_sizeof($ndata)) {
|
|
foreach ($ndata as $n) {
|
|
$parts = explode(':', $n);
|
|
|
|
if ($parts[0] == 'tbranch') {
|
|
$branch = $parts[1];
|
|
|
|
input_validate_input_number($branch, 'branch');
|
|
|
|
$sort_type = db_fetch_cell_prepared('SELECT sort_children_type
|
|
FROM graph_tree_items
|
|
WHERE id = ?',
|
|
[$branch]);
|
|
|
|
switch($sort_type) {
|
|
case TREE_ORDERING_INHERIT:
|
|
print __x('ordering of tree items', 'inherit');
|
|
|
|
break;
|
|
case TREE_ORDERING_NONE:
|
|
print __x('ordering of tree items', 'manual');
|
|
|
|
break;
|
|
case TREE_ORDERING_ALPHABETIC:
|
|
print __x('ordering of tree items', 'alpha');
|
|
|
|
break;
|
|
case TREE_ORDERING_NATURAL:
|
|
print __x('ordering of tree items', 'natural');
|
|
|
|
break;
|
|
case TREE_ORDERING_NUMERIC:
|
|
print __x('ordering of tree items', 'numeric');
|
|
|
|
break;
|
|
default:
|
|
print '';
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
print '';
|
|
}
|
|
}
|
|
|
|
function set_branch_sort_type() : void {
|
|
$type = '';
|
|
$branch = '';
|
|
|
|
// clean up type string
|
|
if (isrv('type')) {
|
|
srv('type', sanitize_search_string(grv('type')));
|
|
}
|
|
|
|
if (isrv('nodeid')) {
|
|
$ndata = explode('_', grv('nodeid'));
|
|
|
|
if (cacti_sizeof($ndata)) {
|
|
foreach ($ndata as $n) {
|
|
$parts = explode(':', $n);
|
|
|
|
if ($parts[0] == 'tbranch') {
|
|
$branch = $parts[1];
|
|
input_validate_input_number($branch, 'branch');
|
|
|
|
switch(grv('type')) {
|
|
case 'inherit':
|
|
$type = TREE_ORDERING_INHERIT;
|
|
|
|
break;
|
|
case 'manual':
|
|
$type = TREE_ORDERING_NONE;
|
|
|
|
break;
|
|
case 'alpha':
|
|
$type = TREE_ORDERING_ALPHABETIC;
|
|
|
|
break;
|
|
case 'natural':
|
|
$type = TREE_ORDERING_NATURAL;
|
|
|
|
break;
|
|
case 'numeric':
|
|
$type = TREE_ORDERING_NUMERIC;
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (is_numeric($type) && is_numeric($branch)) {
|
|
db_execute_prepared('UPDATE graph_tree_items
|
|
SET sort_children_type = ?
|
|
WHERE id = ?',
|
|
[$type, $branch]);
|
|
}
|
|
|
|
$first_child = db_fetch_row_prepared('SELECT id, graph_tree_id
|
|
FROM graph_tree_items
|
|
WHERE parent = ?
|
|
ORDER BY position
|
|
LIMIT 1',
|
|
[$branch]);
|
|
|
|
if (!empty($first_child)) {
|
|
api_tree_sort_branch($first_child['id'], $first_child['graph_tree_id']);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function form_save() : void {
|
|
// clear graph tree cache on save - affects current user only, other users should see changes in <5 minutes
|
|
if (isset($_SESSION['dhtml_tree'])) {
|
|
unset($_SESSION['dhtml_tree']);
|
|
}
|
|
|
|
if (isrv('save_component_tree')) {
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
gfrv('sequence');
|
|
// ====================================================
|
|
|
|
if (gfrv('id') > 0) {
|
|
$prev_order = db_fetch_cell_prepared('SELECT sort_type
|
|
FROM graph_tree
|
|
WHERE id = ?',
|
|
[grv('id')]);
|
|
} else {
|
|
$prev_order = 1;
|
|
}
|
|
|
|
$save['id'] = grv('id');
|
|
$save['name'] = form_input_validate(gnrv('name'), 'name', '', false, 3);
|
|
$save['sort_type'] = form_input_validate(gnrv('sort_type'), 'sort_type', '', true, 3);
|
|
$save['last_modified'] = date('Y-m-d H:i:s', time());
|
|
$save['enabled'] = grv('enabled') == 'true' || grv('enabled') == 'on' ? 'on' : 'off';
|
|
$save['modified_by'] = $_SESSION[SESS_USER_ID];
|
|
|
|
if (ierv('sequence')) {
|
|
$save['sequence'] = api_tree_get_max_sequence() + 1;
|
|
} else {
|
|
$save['sequence'] = grv('sequence');
|
|
}
|
|
|
|
if (empty($save['id'])) {
|
|
$save['user_id'] = $_SESSION[SESS_USER_ID];
|
|
}
|
|
|
|
if (!is_error_message()) {
|
|
$tree_id = sql_save($save, 'graph_tree');
|
|
|
|
if ($tree_id) {
|
|
raise_message(1);
|
|
|
|
// sort the tree using the algorithm chosen by the user
|
|
if ($save['sort_type'] != $prev_order) {
|
|
if ($save['sort_type'] != TREE_ORDERING_NONE) {
|
|
sort_recursive(0, $tree_id);
|
|
}
|
|
}
|
|
|
|
if (empty($save['id'])) {
|
|
/**
|
|
* Save the last time a tree was created/updated
|
|
* for Caching.
|
|
*/
|
|
set_config_option('time_last_change_tree', time());
|
|
}
|
|
} else {
|
|
raise_message(2);
|
|
}
|
|
}
|
|
|
|
header('Location: tree.php?action=edit&id=' . (isset($tree_id) ? $tree_id : ''));
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
function sort_recursive(int $branch, int $tree_id) : void {
|
|
$leaves = db_fetch_assoc_prepared('SELECT *
|
|
FROM graph_tree_items
|
|
WHERE graph_tree_id = ?
|
|
AND parent = ?
|
|
AND local_graph_id = 0
|
|
AND host_id = 0',
|
|
[$tree_id, $branch]);
|
|
|
|
if (cacti_sizeof($leaves)) {
|
|
foreach ($leaves as $leaf) {
|
|
if ($leaf['sort_children_type'] == TREE_ORDERING_INHERIT) {
|
|
$first_child = db_fetch_cell_prepared('SELECT id
|
|
FROM graph_tree_items
|
|
WHERE parent = ?',
|
|
[$leaf['id']]);
|
|
|
|
if (!empty($first_child)) {
|
|
api_tree_sort_branch($first_child, $tree_id);
|
|
|
|
if (leaves_exist($leaf['id'], $tree_id)) {
|
|
sort_recursive($first_child, $tree_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function leaves_exist(int $parent, int $tree_id) : int {
|
|
return (int) db_fetch_cell_prepared('SELECT COUNT(*)
|
|
FROM graph_tree_items
|
|
WHERE graph_tree_id = ?
|
|
AND parent = ?
|
|
AND local_graph_id = 0
|
|
AND host_id = 0',
|
|
[$tree_id, $parent]);
|
|
}
|
|
|
|
function form_actions() : void {
|
|
global $actions;
|
|
|
|
// ================= input validation =================
|
|
gfrv('drp_action', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^([a-zA-Z0-9_]+)$/']]);
|
|
// ====================================================
|
|
|
|
// 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 (gnrv('drp_action') == '1') { // delete
|
|
db_execute('DELETE FROM graph_tree WHERE ' . array_to_sql_or($selected_items, 'id'));
|
|
db_execute('DELETE FROM graph_tree_items WHERE ' . array_to_sql_or($selected_items, 'graph_tree_id'));
|
|
|
|
/**
|
|
* Save the last time a tree or branch was created/updated
|
|
* for Caching.
|
|
*/
|
|
set_config_option('time_last_change_tree', time());
|
|
set_config_option('time_last_change_branch', time());
|
|
} elseif (gnrv('drp_action') == '2') { // publish
|
|
db_execute("UPDATE graph_tree
|
|
SET enabled='on',
|
|
last_modified=NOW(),
|
|
modified_by=" . $_SESSION[SESS_USER_ID] . '
|
|
WHERE ' . array_to_sql_or($selected_items, 'id'));
|
|
|
|
/**
|
|
* Save the last time a tree or branch was created/updated
|
|
* for Caching.
|
|
*/
|
|
set_config_option('time_last_change_tree', time());
|
|
set_config_option('time_last_change_branch', time());
|
|
} elseif (gnrv('drp_action') == '3') { // un-publish
|
|
db_execute("UPDATE graph_tree
|
|
SET enabled='off',
|
|
last_modified=NOW(),
|
|
modified_by=" . $_SESSION[SESS_USER_ID] . '
|
|
WHERE ' . array_to_sql_or($selected_items, 'id'));
|
|
|
|
/**
|
|
* Save the last time a tree or branch was created/updated
|
|
* for Caching.
|
|
*/
|
|
set_config_option('time_last_change_tree', time());
|
|
set_config_option('time_last_change_branch', time());
|
|
} elseif (gnrv('drp_action') == '4') { // un-lock
|
|
db_execute('UPDATE graph_tree
|
|
SET locked=0,
|
|
last_modified=NOW(),
|
|
modified_by=' . $_SESSION[SESS_USER_ID] . '
|
|
WHERE ' . array_to_sql_or($selected_items, 'id'));
|
|
}
|
|
}
|
|
|
|
header('Location: tree.php');
|
|
|
|
exit;
|
|
} else {
|
|
$ilist = '';
|
|
$iarray = [];
|
|
|
|
// loop through each of the selected items
|
|
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(db_fetch_cell_prepared('SELECT name FROM graph_tree WHERE id = ?', [$matches[1]])) . '</li>';
|
|
|
|
$iarray[] = $matches[1];
|
|
}
|
|
}
|
|
|
|
$form_data = [
|
|
'general' => [
|
|
'page' => 'tree.php',
|
|
'actions' => $actions,
|
|
'optvar' => 'drp_action',
|
|
'item_array' => $iarray,
|
|
'item_list' => $ilist
|
|
],
|
|
'options' => [
|
|
1 => [
|
|
'smessage' => __('Click \'Continue\' to Delete the following Tree.'),
|
|
'pmessage' => __('Click \'Continue\' to Delete following Trees.'),
|
|
'scont' => __('Delete Tree'),
|
|
'pcont' => __('Delete Trees')
|
|
],
|
|
2 => [
|
|
'smessage' => __('Click \'Continue\' to Publish the following Tree.'),
|
|
'pmessage' => __('Click \'Continue\' to Publish following Trees.'),
|
|
'scont' => __('Publish Tree'),
|
|
'pcont' => __('Publish Trees')
|
|
],
|
|
3 => [
|
|
'smessage' => __('Click \'Continue\' to Un-Publish the following Tree.'),
|
|
'pmessage' => __('Click \'Continue\' to Un-Publish following Trees.'),
|
|
'scont' => __('Un-Publish Tree'),
|
|
'pcont' => __('Un-Publish Trees')
|
|
],
|
|
4 => [
|
|
'smessage' => __('Click \'Continue\' to Un-Lock the following Tree.'),
|
|
'pmessage' => __('Click \'Continue\' to Un-Lock following Trees.'),
|
|
'scont' => __('Un-Lock Tree'),
|
|
'pcont' => __('Un-Lock Trees')
|
|
],
|
|
]
|
|
];
|
|
|
|
form_continue_confirmation($form_data);
|
|
}
|
|
}
|
|
|
|
function tree_edit(bool $partial = false) : void {
|
|
global $fields_tree_edit;
|
|
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
gfrv('type');
|
|
// ====================================================
|
|
|
|
load_current_session_value('type', 'sess_tree_edit_type', '0');
|
|
|
|
if (!ierv('id')) {
|
|
$tree = db_fetch_row_prepared('SELECT *
|
|
FROM graph_tree
|
|
WHERE id = ?',
|
|
[grv('id')]);
|
|
|
|
$header_label = __esc('Trees [edit: %s]', $tree['name']);
|
|
|
|
// Reset the cookie state if tree id has changed
|
|
if (isset($_SESSION['sess_tree_id']) && $_SESSION['sess_tree_id'] != grv('id')) {
|
|
$select_first = true;
|
|
} else {
|
|
$select_first = false;
|
|
}
|
|
|
|
$_SESSION['sess_tree_id'] = grv('id');
|
|
} else {
|
|
$tree = [];
|
|
|
|
$header_label = __('Trees [new]');
|
|
}
|
|
|
|
print '<div id="tree_edit_container">';
|
|
|
|
form_start('tree.php', 'tree_edit');
|
|
|
|
// Remove inherit from the main tree option
|
|
unset($fields_tree_edit['sort_type']['array'][0]);
|
|
|
|
html_start_box($header_label, '100%', true, 3, 'center', '');
|
|
|
|
if (!cacti_sizeof($tree)) {
|
|
unset($fields_tree_edit['enabled']);
|
|
}
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => inject_form_variables($fields_tree_edit, $tree)
|
|
]
|
|
);
|
|
|
|
html_end_box(true, true);
|
|
|
|
$lockdiv = '';
|
|
$editable = true;
|
|
|
|
if (isset($tree['locked']) && $tree['locked'] == 0) {
|
|
$lockdiv = "<div style='padding:5px 5px 5px 0px'><table><tr>
|
|
<td><button type='button' class='ui-button ui-corner-all ui-widget' id='lock'>" . __esc('Edit Tree') . "</button></td>
|
|
<td style='font-weight:bold;'>" . __('To Edit this tree, you must first lock it by pressing the Edit Tree button.') . '</td>
|
|
</tr></table></div>';
|
|
|
|
$editable = false;
|
|
} elseif (isset($tree['locked']) && $tree['locked'] == 1) {
|
|
$lockdiv = "<div style='padding:5px 5px 5px 0px'><table><tr>
|
|
<td><button type='button' class='ui-button ui-corner-all ui-widget' id='unlock'>" . __esc('Finish Editing Tree') . "</button></td>
|
|
<td><button type='button' class='ui-button ui-corner-all ui-widget' id='addbranch' onClick='createNode()'>" . __esc('Add Root Branch') . "</button></td>
|
|
<td style='font-weight:bold;'>" . __('This tree has been locked for Editing on %s by %s.', $tree['locked_date'], get_username($tree['modified_by']));
|
|
|
|
if ($tree['modified_by'] == $_SESSION[SESS_USER_ID]) {
|
|
$lockdiv .= '</td></tr></table></div>';
|
|
} else {
|
|
$editable = false;
|
|
$lockdiv .= __('To edit the tree, you must first unlock it and then lock it as yourself') . '</td></tr></table></div>';
|
|
}
|
|
} else {
|
|
$tree['id'] = 0;
|
|
}
|
|
|
|
if ($editable) {
|
|
form_save_button('tree.php', 'return');
|
|
}
|
|
|
|
if (!ierv('id')) {
|
|
print $lockdiv;
|
|
}
|
|
|
|
print '</div>';
|
|
|
|
if ($partial) {
|
|
return;
|
|
}
|
|
|
|
if (!ierv('id')) {
|
|
print "<table class='treeTable' style='width:100%;'>\n";
|
|
|
|
print "<tr class='even' id='tree_filter'>\n";
|
|
print "<td colspan='4'>";
|
|
print '<table><tr><td>' . __('Display') . '</td>';
|
|
print "<td>\n";
|
|
print "<select id='element'>\n";
|
|
print "<option id='0'>" . __('All') . '</option>';
|
|
print "<option id='1'>" . __('Sites') . '</option>';
|
|
print "<option id='2'>" . __('Devices') . '</option>';
|
|
print "<option id='3'>" . __('Graphs') . '</option>';
|
|
print '</select></td></tr></table></td></tr>';
|
|
|
|
print "<tr class='tableRow'><td class='treeArea'>\n";
|
|
|
|
html_start_box(__('Tree Items'), '100%', false, 3, 'center', '');
|
|
|
|
print "<tr class='tableRow'><td style='padding:7px;'><div id='ctree'></div></td></tr>\n";
|
|
|
|
html_end_box();
|
|
|
|
print "</td><td class='treeItemsArea treeItemsAreaSite'>\n";
|
|
|
|
html_start_box(__('Available Sites'), '100%', false, 3, 'center', '');
|
|
|
|
?>
|
|
<tr class='even noprint'>
|
|
<td>
|
|
<form id='form_tree_sites' action='tree.php'>
|
|
<table class='filterTable'>
|
|
<tr>
|
|
<td>
|
|
<?php print __('Search'); ?>
|
|
</td>
|
|
<td>
|
|
<input type='text' class='ui-state-default ui-corner-all' id='sfilter' name='sfilter' size='25' value='<?php print htmlerv('sfilter'); ?>'>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
|
|
html_end_box(false);
|
|
|
|
$display_text = [__('Site Name')];
|
|
|
|
html_start_box('', '100%', false, 3, 'center', '');
|
|
html_header($display_text);
|
|
|
|
print "<tr class='tableRow'><td style='padding:7px;'><div id='sites' style='display:none;'>\n";
|
|
display_sites();
|
|
print "</div></td></tr>\n";
|
|
|
|
html_end_box();
|
|
|
|
print "</td><td class='treeItemsArea treeItemsAreaDevice'>\n";
|
|
|
|
html_start_box(__('Available Devices'), '100%', false, 3, 'center', '');
|
|
|
|
?>
|
|
<tr class='even noprint'>
|
|
<td>
|
|
<form id='form_tree_devices' action='tree.php'>
|
|
<table class='filterTable'>
|
|
<tr>
|
|
<td>
|
|
<?php print __('Search'); ?>
|
|
</td>
|
|
<td>
|
|
<input type='text' class='ui-state-default ui-corner-all' id='hfilter' name='hfilter' size='25' value='<?php print htmlerv('hfilter'); ?>'>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
|
|
html_end_box(false);
|
|
|
|
$display_text = [__('Device Description')];
|
|
|
|
html_start_box('', '100%', false, 3, 'center', '');
|
|
html_header($display_text);
|
|
|
|
print "<tr class='tableRow'><td style='padding:7px;'><div id='hosts' style='display:none;'>\n";
|
|
display_hosts();
|
|
print "</div></td></tr>\n";
|
|
|
|
html_end_box();
|
|
|
|
print "</td><td class='treeItemsArea treeItemsAreaGraph'>\n";
|
|
|
|
html_start_box(__('Available Graphs'), '100%', false, 3, 'center', '');
|
|
|
|
?>
|
|
<tr class='even noprint'>
|
|
<td>
|
|
<form id='form_tree_graphs' action='tree.php'>
|
|
<table class='filterTable'>
|
|
<tr>
|
|
<td>
|
|
<?php print __('Search'); ?>
|
|
</td>
|
|
<td>
|
|
<input type='text' class='ui-state-default ui-corner-all' id='gfilter' name='gfilter' size='25' value='<?php print htmlerv('gfilter'); ?>'>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
html_end_box(false);
|
|
|
|
$display_text = [__('Graph Name')];
|
|
|
|
html_start_box('', '100%', false, 3, 'center', '');
|
|
html_header($display_text);
|
|
|
|
print "<tr class='tableRow'><td style='padding:7px;'><div id='graphs' style='display:none;'>\n";
|
|
display_graphs();
|
|
print "</div></td></tr>\n";
|
|
|
|
html_end_box();
|
|
|
|
print "</td></tr></table>\n";
|
|
|
|
?>
|
|
<script type='text/javascript'>
|
|
<?php
|
|
if ($select_first) {
|
|
print "var reset=true;\n";
|
|
} else {
|
|
print "var reset=false;\n";
|
|
}
|
|
?>
|
|
|
|
var hostSortInfo = {};
|
|
var branchSortInfo = {};
|
|
var selectedItem = {};
|
|
|
|
if ($('#lock').length) {
|
|
var editable = false;
|
|
} else {
|
|
var editable = true;
|
|
}
|
|
|
|
function createNode() {
|
|
var ref = $('#ctree').jstree(true);
|
|
sel = ref.create_node('#', '<?php print __esc('New Node'); ?>', '0');
|
|
if (sel) {
|
|
ref.edit(sel);
|
|
}
|
|
};
|
|
|
|
function loadTreeEdit(url) {
|
|
var myMagic = csrfMagicToken;
|
|
|
|
$.post(url, { __csrf_magic: csrfMagicToken })
|
|
.done(function(data) {
|
|
$('#tree_edit_container').replaceWith(data);
|
|
$('#tree_edit').append('<input type="hidden" name="__csrf_magic" value="'+myMagic+'">');
|
|
initializeTreeEdit();
|
|
applySkin();
|
|
})
|
|
.fail(function(data) {
|
|
getPresentHTTPError(data);
|
|
});
|
|
}
|
|
|
|
function getGraphData() {
|
|
$.get('tree.php?action=graphs&filter='+$('#gfilter').val()
|
|
+ '&site_id=' + (selectedItem.site_id ? selectedItem.site_id:'')
|
|
+ '&host_id=' + (selectedItem.host_id ? selectedItem.host_id:''))
|
|
.done(function(data) {
|
|
$('#graphs').jstree('destroy');
|
|
$('#graphs').html(data);
|
|
draggable('graphs');
|
|
})
|
|
.fail(function(data) {
|
|
getPresentHTTPError(data);
|
|
});
|
|
}
|
|
|
|
function getHostData() {
|
|
$.get('tree.php?action=hosts&filter='+$('#hfilter').val()
|
|
+ '&site_id=' + (selectedItem.site_id ? selectedItem.site_id:''))
|
|
.done(function(data) {
|
|
$('#hosts').jstree('destroy');
|
|
$('#hosts').html(data);
|
|
draggable('hosts');
|
|
draggable('graphs');
|
|
})
|
|
.fail(function(data) {
|
|
getPresentHTTPError(data);
|
|
});
|
|
}
|
|
|
|
function getSiteData() {
|
|
$.get('tree.php?action=sites&filter='+$('#sfilter').val(), function(data) {
|
|
$('#sites').jstree('destroy');
|
|
$('#sites').html(data);
|
|
draggable('sites');
|
|
draggable('hosts');
|
|
draggable('graphs');
|
|
enableKeyups();
|
|
});
|
|
}
|
|
|
|
function setHostSortIcon(nodeid) {
|
|
if (hostSortInfo[nodeid]) {
|
|
// Already set
|
|
} else {
|
|
$.get('tree.php?action=get_host_sort&nodeid='+nodeid)
|
|
.done(function(data) {
|
|
hostSortInfo[nodeid] = data;
|
|
})
|
|
.fail(function(data) {
|
|
getPresentHTTPError(data);
|
|
});
|
|
}
|
|
}
|
|
|
|
function setBranchSortIcon(nodeid) {
|
|
if (branchSortInfo[nodeid]) {
|
|
// Already set
|
|
} else {
|
|
$.get('tree.php?action=get_branch_sort&nodeid='+nodeid)
|
|
.done(function(data) {
|
|
branchSortInfo[nodeid] = data;
|
|
})
|
|
.fail(function(data) {
|
|
getPresentHTTPError(data);
|
|
});
|
|
}
|
|
}
|
|
|
|
function getHostSortIcon(type, nodeid) {
|
|
if (hostSortInfo[nodeid] == type) {
|
|
return 'ti ti-check';
|
|
} else {
|
|
return 'false';
|
|
}
|
|
}
|
|
|
|
function getBranchSortIcon(type, nodeid) {
|
|
if (branchSortInfo[nodeid] == type) {
|
|
return 'ti ti-check';
|
|
} else {
|
|
return 'false';
|
|
}
|
|
}
|
|
|
|
function setBranchSortOrder(type, nodeid) {
|
|
$.get('tree.php?action=set_branch_sort&type='+type+'&nodeid='+nodeid)
|
|
.done(function(data) {
|
|
branchSortInfo[nodeid] = type;
|
|
})
|
|
.fail(function(data) {
|
|
getPresentHTTPError(data);
|
|
});
|
|
}
|
|
|
|
function setHostSortOrder(type, nodeid) {
|
|
$.get('tree.php?action=set_host_sort&type='+type+'&nodeid='+nodeid)
|
|
.done(function(data) {
|
|
hostSortInfo[nodeid] = type;
|
|
})
|
|
.fail(function(data) {
|
|
getPresentHTTPError(data);
|
|
});
|
|
}
|
|
|
|
function initializeTreeEdit() {
|
|
$('#lock').click(function() {
|
|
strURL = 'tree.php?action=lock&id=<?php print $tree['id']; ?>';
|
|
|
|
loadTreeEdit(strURL);
|
|
|
|
$('#sfilter, #hfilter, #gfilter').each(function() {
|
|
$(this).prop('disabled', false);
|
|
$(this).removeClass('ui-state-disabled');
|
|
if ($(this).selectmenu('instance') !== undefined) {
|
|
$(this).selectmenu('enable');
|
|
}
|
|
});
|
|
|
|
editable = true;
|
|
reset = false;
|
|
|
|
drawTree();
|
|
|
|
draggable('graphs');
|
|
draggable('hosts');
|
|
draggable('sites');
|
|
|
|
enableKeyups();
|
|
});
|
|
|
|
$('#unlock').click(function() {
|
|
strURL = 'tree.php?action=unlock&id=<?php print $tree['id']; ?>';
|
|
|
|
loadTreeEdit(strURL);
|
|
|
|
$('#sfilter, #hfilter, #gfilter').each(function() {
|
|
$(this).prop('disabled', true);
|
|
$(this).addClass('ui-state-disabled');
|
|
if ($(this).selectmenu('instance') !== undefined) {
|
|
$(this).selectmenu('disable');
|
|
}
|
|
});
|
|
|
|
editable = false;
|
|
reset = false;
|
|
|
|
drawTree();
|
|
|
|
draggable('graphs');
|
|
draggable('hosts');
|
|
draggable('sites');
|
|
|
|
enableKeyups();
|
|
});
|
|
|
|
if ($('#lock').length) {
|
|
editable = false;
|
|
|
|
$('select, input').not('#lock, #element').each(function() {
|
|
$(this).prop('disabled', true);
|
|
$(this).addClass('ui-state-disabled');
|
|
|
|
if ($(this).selectmenu('instance') !== undefined) {
|
|
$(this).selectmenu('disable');
|
|
}
|
|
});
|
|
} else {
|
|
editable = true;
|
|
}
|
|
}
|
|
|
|
function drawTree() {
|
|
if (editable) {
|
|
var plugins = [ 'state', 'wholerow', 'contextmenu', 'dnd', 'types' ];
|
|
} else {
|
|
var plugins = [ 'state', 'wholerow', 'types' ];
|
|
}
|
|
|
|
if ($('#ctree').jstree('instance')) {
|
|
$('#ctree').jstree('destroy');
|
|
}
|
|
|
|
$("#ctree").jstree({
|
|
'types' : {
|
|
'site' : {
|
|
icon : 'ti ti-building',
|
|
max_children : 0
|
|
},
|
|
'device' : {
|
|
icon : 'ti ti-server',
|
|
max_children : 0
|
|
},
|
|
'graph' : {
|
|
icon : 'ti ti-chart-line',
|
|
max_children : 0
|
|
},
|
|
'default' : {
|
|
icon : 'ti ti-folder'
|
|
}
|
|
},
|
|
'contextmenu' : {
|
|
'items': function(node) {
|
|
if (node.id.search('tgraph') > 0) {
|
|
var dataType = 'graph';
|
|
} else if (node.id.search('thost') > 0) {
|
|
var dataType = 'host';
|
|
} else if (node.id.search('tsite') > 0) {
|
|
var dataType = 'site';
|
|
}else {
|
|
var dataType = 'branch';
|
|
}
|
|
|
|
if (dataType == 'graph') {
|
|
return graphContext(node.id);
|
|
} else if (dataType == 'host') {
|
|
return hostContext(node.id);
|
|
} else if (dataType == 'site') {
|
|
return siteContext(node.id);
|
|
} else {
|
|
return branchContext(node.id);
|
|
}
|
|
}
|
|
},
|
|
'core' : {
|
|
'data' : {
|
|
'url' : 'tree.php?action=get_node&tree_id='+$('#id').val(),
|
|
'data' : function(node) {
|
|
return { 'id' : node.id }
|
|
}
|
|
},
|
|
'animation' : 0,
|
|
'check_callback' : true,
|
|
'force_text' : true
|
|
},
|
|
'themes' : {
|
|
'name' : 'default',
|
|
'responsive' : true,
|
|
'url' : true,
|
|
'dots' : false
|
|
},
|
|
'state': { 'key': 'tree_<?php print grv('id'); ?>' },
|
|
'plugins' : plugins
|
|
})
|
|
.on('ready.jstree', function(e, data) {
|
|
if (reset == true) {
|
|
$('#ctree').jstree('clear_state');
|
|
}
|
|
|
|
if (!editable) {
|
|
$("#ctree").children().on('contextmenu', function(event) {
|
|
return false;
|
|
});
|
|
}
|
|
})
|
|
.on('delete_node.jstree', function (e, data) {
|
|
$.post('?action=delete_node', { 'id' : data.node.id, 'tree_id' : $('#id').val(), '__csrf_magic' : csrfMagicToken })
|
|
.always(function() {
|
|
var st = data.instance.get_state();
|
|
data.instance.load_node(data.instance.get_parent(data.node.id), function () { this.set_state(st); });
|
|
});
|
|
})
|
|
.on('hover_node.jstree', function (e, data) {
|
|
if (data.node.id.search('thost') >= 0) {
|
|
setHostSortIcon(data.node.id);
|
|
} else if (data.node.id.search('thost') < 0 && data.node.id.search('tgraph') < 0 && data.node.id.search('tsite')) {
|
|
setBranchSortIcon(data.node.id);
|
|
}
|
|
})
|
|
.on('create_node.jstree', function (e, data) {
|
|
$.post('?action=create_node', { 'id' : data.node.parent, 'tree_id' : $('#id').val(), 'position' : data.position, 'text' : data.node.text, '__csrf_magic' : csrfMagicToken })
|
|
.done(function (d) {
|
|
data.instance.set_id(data.node, d.id);
|
|
data.instance.set_text(data.node, d.text);
|
|
data.instance.edit(data.node);
|
|
|
|
if (d.text != '<?php print __esc('New Node'); ?>') {
|
|
$('.jstree').jstree(true).refresh();
|
|
}
|
|
})
|
|
.fail(function () {
|
|
var st = data.instance.get_state();
|
|
data.instance.load_node(data.instance.get_parent(data.node.id), function () { this.set_state(st); });
|
|
});
|
|
})
|
|
.on('rename_node.jstree', function (e, data) {
|
|
$.post('?action=rename_node', { 'id' : data.node.id, 'tree_id' : $('#id').val(), 'text' : data.text, '__csrf_magic' : csrfMagicToken })
|
|
.done(function (d) {
|
|
if (d.result == 'false') {
|
|
data.instance.set_text(data.node, d.text);
|
|
data.instance.edit(data.node);
|
|
} else {
|
|
var st = data.instance.get_state();
|
|
data.instance.load_node(data.instance.get_parent(data.node.id), function () { this.set_state(st); });
|
|
}
|
|
})
|
|
.fail(function(data) {
|
|
getPresentHTTPError(data);
|
|
});
|
|
})
|
|
.on('move_node.jstree', function (e, data) {
|
|
$.post('?action=move_node', { 'id' : data.node.id, 'tree_id' : $('#id').val(), 'parent' : data.parent, 'position' : data.position, '__csrf_magic' : csrfMagicToken })
|
|
.always(function () {
|
|
var st = data.instance.get_state();
|
|
data.instance.load_node(data.instance.get_parent(data.node.id), function () { this.set_state(st); });
|
|
});
|
|
})
|
|
.on('copy_node.jstree', function (e, data) {
|
|
oid = data.original.id;
|
|
|
|
if (oid.search('thost') >= 0) {
|
|
$('#hosts').jstree().deselect_all();
|
|
} else if (oid.search('tsite') >= 0) {
|
|
$('#sites').jstree().deselect_all();
|
|
} else {
|
|
$('#graphs').jstree().deselect_all();
|
|
}
|
|
|
|
$.post('?action=copy_node', { 'id' : data.original.id, 'tree_id' : $('#id').val(), 'parent' : data.parent, 'position' : data.position, '__csrf_magic' : csrfMagicToken })
|
|
.always(function () {
|
|
var st = data.instance.get_state();
|
|
data.instance.load_node(data.instance.get_parent(data.node.id), function () { this.set_state(st); });
|
|
});
|
|
});
|
|
}
|
|
|
|
function enableKeyups() {
|
|
$('#gfilter').delayKeyup(getGraphData);
|
|
|
|
$('#hfilter').delayKeyup(getHostData);
|
|
|
|
$('#sfilter').delayKeyup(getSiteData);
|
|
}
|
|
|
|
function resizer() {
|
|
if ($('#ctree').length) {
|
|
var wheight = $(window).height();
|
|
var cTop = $('#ctree').parent().offset().top;
|
|
var sTop = $('#sites').parent().offset().top;
|
|
var height = wheight - cTop - 10;
|
|
var sheight = wheight - sTop - 10;
|
|
|
|
$('#ctree').css('height', height).css('overflow','auto');
|
|
$('#hosts').css('height', sheight).css('overflow','auto');
|
|
$('#sites').css('height', sheight).css('overflow','auto');
|
|
$('#graphs').css('height', sheight).css('overflow','auto');
|
|
|
|
switchDisplay();
|
|
}
|
|
}
|
|
|
|
function switchDisplay() {
|
|
var selected = $('#element').prop('selectedIndex');
|
|
var windowWidth = parseInt($(window).outerWidth());
|
|
var clientWidth = parseInt($(document).width());
|
|
|
|
if (selected == 0) {
|
|
if (clientWidth > windowWidth) {
|
|
$('#element').prop('selectedIndex', 1);
|
|
if ($('#element').selectmenu('instance')) {
|
|
$('#element').selectmenu('refresh');
|
|
}
|
|
selected = $('#element').prop('selectedIndex');
|
|
}
|
|
}
|
|
|
|
switch(selected) {
|
|
case 0:
|
|
$('.treeItemsAreaSite').show();
|
|
$('.treeItemsAreaDevice').show();
|
|
$('.treeItemsAreaGraph').show();
|
|
break;
|
|
case 1:
|
|
$('.treeItemsAreaSite').show();
|
|
$('.treeItemsAreaDevice').hide();
|
|
$('.treeItemsAreaGraph').hide();
|
|
break;
|
|
case 2:
|
|
$('.treeItemsAreaSite').hide();
|
|
$('.treeItemsAreaDevice').show();
|
|
$('.treeItemsAreaGraph').hide();
|
|
break;
|
|
case 3:
|
|
$('.treeItemsAreaSite').hide();
|
|
$('.treeItemsAreaDevice').hide();
|
|
$('.treeItemsAreaGraph').show();
|
|
break;
|
|
}
|
|
}
|
|
|
|
graphsDropSet = '';
|
|
hostsDropSet = '';
|
|
sitesDropSet = '';
|
|
|
|
$(function() {
|
|
drawTree();
|
|
|
|
if ($('#unlock').length) {
|
|
enableKeyups();
|
|
|
|
$('select, input').each(function() {
|
|
$(this).prop('disabled', false);
|
|
});
|
|
} else {
|
|
$('select, input').not('#lock, #element').each(function() {
|
|
$(this).prop('disabled', true);
|
|
$(this).addClass('ui-state-disabled');
|
|
|
|
if ($(this).selectmenu('instance') !== undefined) {
|
|
$(this).selectmenu('disable');
|
|
}
|
|
});
|
|
}
|
|
|
|
$('form').unbind().submit(function(event) {
|
|
event.preventDefault();
|
|
|
|
if ($(this).attr('id') == 'tree_edit') {
|
|
var options = {
|
|
url: 'tree.php',
|
|
}
|
|
|
|
var data = {
|
|
action: 'save',
|
|
name: $('#name').val(),
|
|
sort_type: $('#sort_type').val(),
|
|
enabled: $('#enabled').is(':checked'),
|
|
id: $('#id').val(),
|
|
save_component_tree: 1,
|
|
__csrf_magic: csrfMagicToken
|
|
}
|
|
|
|
postUrl(options, data);
|
|
}
|
|
});
|
|
|
|
var height = parseInt($(window).height()-$('#ctree').offset().top-10)+'px';
|
|
var sheight = parseInt($(window).height()-$('#sites').offset().top-10)+'px';
|
|
var hheight = parseInt($(window).height()-$('#hosts').offset().top-10)+'px';
|
|
var gheight = parseInt($(window).height()-$('#graphs').offset().top-10)+'px';
|
|
|
|
$('#element').change(function() {
|
|
resizer();
|
|
});
|
|
|
|
$(window).resize(function() {
|
|
resizer();
|
|
});
|
|
|
|
initializeTreeEdit();
|
|
|
|
$('#ctree').css('height', height).css('overflow','auto');;
|
|
|
|
draggable('graphs');
|
|
draggable('hosts');
|
|
draggable('sites');
|
|
});
|
|
|
|
function draggable(element) {
|
|
var id = '#'+element;
|
|
var divdata = '';
|
|
|
|
if (editable) {
|
|
var plugins = [ 'wholerow', 'dnd', 'types' ];
|
|
} else {
|
|
var plugins = [ 'wholerow', 'types' ];
|
|
}
|
|
|
|
// We have to reload the various trees due
|
|
// to plugins being imutable after load
|
|
// so reconstruct the tree from the current elements
|
|
// destroy the tree, and repaint to elements
|
|
// as they would have been.
|
|
if ($(id).hasClass('jstree')) {
|
|
$(id).find('.jstree-node').each(function() {
|
|
var text = DOMPurify.sanitize($(this).find('.jstree-anchor').text());
|
|
var id = $(this).attr('id');
|
|
var jsdata = $(this).attr('data-jstree');
|
|
|
|
divdata += "<ul><li id='" + id + "' data-jstree='" + jsdata + "'>" + text + '</li></ul>';
|
|
});
|
|
|
|
$(id).jstree('destroy').html(divdata);
|
|
}
|
|
|
|
$(id)
|
|
.jstree({
|
|
'types' : {
|
|
'site' : {
|
|
icon : 'ti ti-building',
|
|
valid_children: 'none',
|
|
max_children : 0
|
|
},
|
|
'device' : {
|
|
icon : 'ti ti-server',
|
|
valid_children: 'none',
|
|
max_children : 0
|
|
},
|
|
'graph' : {
|
|
icon : 'ti ti-chart-line',
|
|
valid_children: 'none',
|
|
max_children : 0
|
|
}
|
|
},
|
|
'core' : {
|
|
'animation' : 0,
|
|
'check_callback' : function(operation, node, node_parent, node_position, more) {
|
|
return false; // not dragging onto self
|
|
}
|
|
},
|
|
'dnd' : {
|
|
'always_copy' : true,
|
|
'check_while_dragging': true
|
|
},
|
|
'themes' : { 'stripes' : true, 'dots' : false },
|
|
'plugins' : plugins
|
|
})
|
|
.on('ready.jstree', function(e, data) {
|
|
if (reset == true) {
|
|
$('#ctree').jstree('clear_state');
|
|
}
|
|
});
|
|
|
|
if (editable) {
|
|
$(id)
|
|
.on('select_node.jstree', function(e, data) {
|
|
if (element == 'graphs') {
|
|
graphsDropSet = data;
|
|
} else {
|
|
hostsDropSet = data;
|
|
}
|
|
})
|
|
.on('activate_node.jstree', function(e, data) {
|
|
if (element == 'sites') {
|
|
var sites = [];
|
|
|
|
$('#sites').find('.jstree-clicked').each(function() {
|
|
sites.push(this.id.split(':')[1].replace('_anchor', ''));
|
|
});
|
|
selectedItem.site_id = sites;
|
|
selectedItem.host_id = '';
|
|
getHostData();
|
|
getGraphData();
|
|
} else if (element == 'hosts'){
|
|
var hosts = [];
|
|
|
|
$('#hosts').find('.jstree-clicked').each(function() {
|
|
hosts.push(this.id.split(':')[1].replace('_anchor', ''));
|
|
});
|
|
|
|
selectedItem.host_id = hosts;
|
|
getGraphData();
|
|
}
|
|
})
|
|
.on('deselect_node.jstree', function(e, data) {
|
|
if (element == 'graphs') {
|
|
graphsDropSet = data;
|
|
} else {
|
|
hostsDropSet = data;
|
|
}
|
|
});
|
|
}
|
|
|
|
$(id).find('.jstree-ocl').hide();
|
|
$(id).children().bind('contextmenu', function(event) {
|
|
return false;
|
|
});
|
|
$(id).show();
|
|
}
|
|
|
|
function branchContext(nodeid) {
|
|
return {
|
|
'create' : {
|
|
'separator_before' : false,
|
|
'separator_after' : true,
|
|
'icon' : 'ti ti-folder-filled',
|
|
'_disabled' : false,
|
|
'label' : '<?php print __esc('Create'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
inst.create_node(obj, {}, 'last', function (new_node) {
|
|
setTimeout(function () { inst.edit(new_node); },0);
|
|
});
|
|
}
|
|
},
|
|
'rename' : {
|
|
'separator_before' : false,
|
|
'separator_after' : false,
|
|
'icon' : 'ti ti-pencil',
|
|
'_disabled' : false,
|
|
'label' : '<?php print __esc('Rename'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
inst.edit(obj);
|
|
}
|
|
},
|
|
'remove' : {
|
|
'separator_before' : false,
|
|
'icon' : 'ti ti-x',
|
|
'separator_after' : false,
|
|
'_disabled' : false,
|
|
'label' : '<?php print __esc('Delete'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.delete_node(inst.get_selected());
|
|
} else {
|
|
inst.delete_node(obj);
|
|
}
|
|
}
|
|
},
|
|
'bst' : {
|
|
'separator_before' : true,
|
|
'icon' : 'ti ti-caret-up-down-filled',
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Branch Sorting'); ?>',
|
|
'action' : false,
|
|
'submenu' : {
|
|
'inherit' : {
|
|
'separator_before' : false,
|
|
'separator_after' : false,
|
|
'icon' : getBranchSortIcon('inherit', nodeid),
|
|
'label' : '<?php print __esc('Inherit'); ?>',
|
|
'action' : function (data) {
|
|
setBranchSortOrder('inherit', nodeid);
|
|
var inst = $.jstree.reference(data.reference);
|
|
var st = inst.get_state();
|
|
var obj = inst.get_node();
|
|
inst.refresh(obj);
|
|
inst.load_node(nodeid, function() { this.set_state(st); });
|
|
}
|
|
},
|
|
'manual' : {
|
|
'separator_before' : false,
|
|
'separator_after' : false,
|
|
'icon' : getBranchSortIcon('manual', nodeid),
|
|
'label' : '<?php print __esc('Manual'); ?>',
|
|
'action' : function (data) {
|
|
setBranchSortOrder('manual', nodeid);
|
|
var inst = $.jstree.reference(data.reference);
|
|
var st = inst.get_state();
|
|
var obj = inst.get_node();
|
|
inst.refresh(obj);
|
|
inst.load_node(nodeid, function() { this.set_state(st); });
|
|
}
|
|
},
|
|
'alpha' : {
|
|
'separator_before' : false,
|
|
'icon' : getBranchSortIcon('alpha', nodeid),
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Alphabetic'); ?>',
|
|
'action' : function (data) {
|
|
setBranchSortOrder('alpha', nodeid);
|
|
var inst = $.jstree.reference(data.reference);
|
|
var st = inst.get_state();
|
|
var obj = inst.get_node();
|
|
inst.refresh(obj);
|
|
inst.load_node(nodeid, function() { this.set_state(st); });
|
|
}
|
|
},
|
|
'natural' : {
|
|
'separator_before' : false,
|
|
'icon' : getBranchSortIcon('natural', nodeid),
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Natural'); ?>',
|
|
'action' : function (data) {
|
|
setBranchSortOrder('natural', nodeid);
|
|
var inst = $.jstree.reference(data.reference);
|
|
var st = inst.get_state();
|
|
var obj = inst.get_node();
|
|
inst.refresh(obj);
|
|
inst.load_node(nodeid, function () { this.set_state(st); });
|
|
}
|
|
},
|
|
'numeric' : {
|
|
'separator_before' : false,
|
|
'icon' : getBranchSortIcon('numeric', nodeid),
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Numeric'); ?>',
|
|
'action' : function (data) {
|
|
setBranchSortOrder('numeric', nodeid);
|
|
var inst = $.jstree.reference(data.reference);
|
|
var st = inst.get_state();
|
|
var obj = inst.get_node();
|
|
inst.refresh(obj);
|
|
inst.load_node(nodeid, function () { this.set_state(st); });
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'ccp' : {
|
|
'separator_before' : true,
|
|
'icon' : 'ti ti-edit',
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Edit'); ?>',
|
|
'action' : false,
|
|
'submenu' : {
|
|
'cut' : {
|
|
'separator_before' : false,
|
|
'separator_after' : false,
|
|
'icon' : 'ti ti-scissors',
|
|
'label' : '<?php print __esc('Cut'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.cut(inst.get_selected());
|
|
} else {
|
|
inst.cut(obj);
|
|
}
|
|
}
|
|
},
|
|
'copy' : {
|
|
'separator_before' : false,
|
|
'icon' : 'ti ti-copy',
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Copy'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.copy(inst.get_selected());
|
|
} else {
|
|
inst.copy(obj);
|
|
}
|
|
}
|
|
},
|
|
'paste' : {
|
|
'separator_before' : false,
|
|
'icon' : 'ti ti-clipboard-copy',
|
|
'_disabled' : function (data) {
|
|
return !$.jstree.reference(data.reference).can_paste();
|
|
},
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Paste'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
inst.paste(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function graphContext(nodeid) {
|
|
return {
|
|
'remove' : {
|
|
'separator_before' : false,
|
|
'icon' : 'ti ti-x',
|
|
'separator_after' : false,
|
|
'_disabled' : false, //(this.check('delete_node', data.reference, this.get_parent(data.reference), '')),
|
|
'label' : '<?php print __esc('Delete'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.delete_node(inst.get_selected());
|
|
} else {
|
|
inst.delete_node(obj);
|
|
}
|
|
}
|
|
},
|
|
'ccp' : {
|
|
'separator_before' : true,
|
|
'icon' : 'ti ti-edit',
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Edit'); ?>',
|
|
'action' : false,
|
|
'submenu' : {
|
|
'cut' : {
|
|
'separator_before' : false,
|
|
'separator_after' : false,
|
|
'icon' : 'ti ti-scissors',
|
|
'label' : '<?php print __esc('Cut'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.cut(inst.get_selected());
|
|
} else {
|
|
inst.cut(obj);
|
|
}
|
|
}
|
|
},
|
|
'copy' : {
|
|
'separator_before' : false,
|
|
'icon' : 'ti ti-copy',
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Copy'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.copy(inst.get_selected());
|
|
} else {
|
|
inst.copy(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function siteContext(nodeid) {
|
|
return {
|
|
'remove' : {
|
|
'separator_before' : false,
|
|
'icon' : 'ti ti-x',
|
|
'separator_after' : false,
|
|
'_disabled' : false,
|
|
'label' : '<?php print __esc('Delete'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.delete_node(inst.get_selected());
|
|
} else {
|
|
inst.delete_node(obj);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function hostContext(nodeid) {
|
|
return {
|
|
'remove' : {
|
|
'separator_before' : false,
|
|
'icon' : 'ti ti-x',
|
|
'separator_after' : false,
|
|
'_disabled' : false,
|
|
'label' : '<?php print __esc('Delete'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference);
|
|
var obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.delete_node(inst.get_selected());
|
|
} else {
|
|
inst.delete_node(obj);
|
|
}
|
|
}
|
|
},
|
|
'hso' : {
|
|
'separator_before' : true,
|
|
'separator_after' : false,
|
|
'icon' : 'ti ti-caret-up-down-filled',
|
|
'label' : '<?php print __esc('Sorting Type'); ?>',
|
|
'action' : false,
|
|
'submenu' : {
|
|
'hsgt' : {
|
|
'separator_before' : false,
|
|
'icon' : getHostSortIcon('hsgt', nodeid),
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Graph Template'); ?>',
|
|
'action' : function (data) {
|
|
setHostSortOrder('hsgt', nodeid);
|
|
}
|
|
},
|
|
'hsdq' : {
|
|
'separator_before' : false,
|
|
'icon' : getHostSortIcon('hsdq', nodeid),
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Data Query Index'); ?>',
|
|
'action' : function (data) {
|
|
setHostSortOrder('hsdq', nodeid);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'ccp' : {
|
|
'separator_before' : true,
|
|
'icon' : 'ti ti-edit',
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Edit'); ?>',
|
|
'action' : false,
|
|
'submenu' : {
|
|
'cut' : {
|
|
'separator_before' : false,
|
|
'separator_after' : false,
|
|
'icon' : 'ti ti-scissors',
|
|
'label' : '<?php print __esc('Cut'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference),
|
|
obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.cut(inst.get_selected());
|
|
} else {
|
|
inst.cut(obj);
|
|
}
|
|
}
|
|
},
|
|
'copy' : {
|
|
'separator_before' : false,
|
|
'icon' : 'ti ti-copy',
|
|
'separator_after' : false,
|
|
'label' : '<?php print __esc('Copy'); ?>',
|
|
'action' : function (data) {
|
|
var inst = $.jstree.reference(data.reference),
|
|
obj = inst.get_node(data.reference);
|
|
if(inst.is_selected(obj)) {
|
|
inst.copy(inst.get_selected());
|
|
} else {
|
|
inst.copy(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
</script>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
function display_sites() : void {
|
|
if (gnrv('filter') != '') {
|
|
$sql_where = 'WHERE
|
|
name LIKE ' . db_qstr('%' . gnrv('filter') . '%') . '
|
|
OR city LIKE ' . db_qstr('%' . gnrv('filter') . '%') . '
|
|
OR state LIKE ' . db_qstr('%' . gnrv('filter') . '%') . '
|
|
OR country LIKE ' . db_qstr('%' . gnrv('filter') . '%');
|
|
} else {
|
|
$sql_where = '';
|
|
}
|
|
|
|
$sites = db_fetch_assoc("SELECT * FROM sites $sql_where");
|
|
|
|
if (cacti_sizeof($sites)) {
|
|
foreach ($sites as $s) {
|
|
print "<ul><li id='tsite:" . $s['id'] . "' data-jstree='{ \"type\" : \"site\"}'>" . htmle($s['name']) . "</li></ul>\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
function display_hosts() : void {
|
|
$sql_where = '';
|
|
|
|
$site_ids = gfrv('site_id', FILTER_VALIDATE_IS_NUMERIC_LIST);
|
|
|
|
if (gnrv('filter') != '') {
|
|
$sql_where .= 'h.hostname LIKE ' . db_qstr('%' . gnrv('filter') . '%') . '
|
|
OR h.description LIKE ' . db_qstr('%' . gnrv('filter') . '%');
|
|
}
|
|
|
|
if ($site_ids != '') {
|
|
$sql_where .= ($sql_where != '' ? ' AND ' : '') . 'h.site_id IN( ' . $site_ids . ')';
|
|
}
|
|
|
|
$hosts = get_allowed_devices($sql_where, 'description', read_config_option('autocomplete_rows'));
|
|
|
|
if (cacti_sizeof($hosts)) {
|
|
foreach ($hosts as $h) {
|
|
print "<ul><li id='thost:" . $h['id'] . "' data-jstree='{ \"type\" : \"device\"}'>" . htmle($h['description']) . ' (' . htmle($h['hostname']) . ')' . "</li></ul>\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
function display_graphs() : void {
|
|
$sql_where = '';
|
|
$sql_params = [];
|
|
|
|
$site_ids = gfrv('site_id', FILTER_VALIDATE_IS_NUMERIC_LIST);
|
|
$host_ids = gfrv('host_id', FILTER_VALIDATE_IS_NUMERIC_LIST);
|
|
|
|
if (gnrv('filter') != '') {
|
|
$sql_where .= 'WHERE (title_cache LIKE ? OR gt.name LIKE ? OR hsc.field_value LIKE ?) AND local_graph_id > 0';
|
|
|
|
$sql_params[] = '%' . gnrv('filter') . '%';
|
|
$sql_params[] = '%' . gnrv('filter') . '%';
|
|
$sql_params[] = '%' . gnrv('filter') . '%';
|
|
} else {
|
|
$sql_where .= 'WHERE local_graph_id > 0';
|
|
}
|
|
|
|
if ($site_ids != '') {
|
|
$sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . 'h.site_id IN( ' . $site_ids . ')';
|
|
}
|
|
|
|
if ($host_ids != '') {
|
|
$sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . 'gl.host_id IN( ' . $host_ids . ')';
|
|
}
|
|
|
|
$graphs = db_fetch_assoc_prepared("SELECT gtg.local_graph_id AS id,
|
|
gtg.title_cache AS title, gt.name AS template_name,
|
|
GROUP_CONCAT(DISTINCT hsc.field_name) AS field_name,
|
|
GROUP_CONCAT(DISTINCT hsc.field_value) AS field_value
|
|
FROM graph_templates_graph AS gtg
|
|
LEFT JOIN graph_templates AS gt
|
|
ON gt.id = gtg.graph_template_id
|
|
LEFT JOIN graph_local AS gl
|
|
ON gtg.local_graph_id = gl.id
|
|
LEFT JOIN host as h
|
|
ON gl.host_id = h.id
|
|
LEFT JOIN host_snmp_cache AS hsc
|
|
ON hsc.host_id = h.id
|
|
AND hsc.snmp_query_id = gl.snmp_query_id
|
|
AND hsc.snmp_index = gl.snmp_index
|
|
$sql_where
|
|
GROUP BY gtg.local_graph_id
|
|
ORDER BY title_cache
|
|
LIMIT " . read_config_option('autocomplete_rows'),
|
|
$sql_params);
|
|
|
|
if (cacti_sizeof($graphs)) {
|
|
foreach ($graphs as $g) {
|
|
if (gnrv('filter') != '') {
|
|
$title = $g['title'];
|
|
|
|
if ($g['field_value'] != '' && stripos($g['field_value'], gnrv('filter')) !== false) {
|
|
$parts = explode(',', $g['field_value']);
|
|
$nparts = explode(',', $g['field_name']);
|
|
|
|
foreach ($parts as $index => $p) {
|
|
if ($p != '' && stripos($p, gnrv('filter')) !== false) {
|
|
$g['title'] .= " [ {$nparts[$index]}:$p ]";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_graph_allowed($g['id'])) {
|
|
print "<ul><li id='tgraph:" . $g['id'] . "' data-jstree='{ \"type\": \"graph\" }'>" . htmle($g['title']) . '</li></ul>';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function tree() : void {
|
|
global $actions, $item_rows;
|
|
|
|
$button1 = [
|
|
'method' => 'button',
|
|
'href' => 'tree.php?action=sortasc',
|
|
'callback' => true,
|
|
'title' => __esc('Sort Trees Ascending'),
|
|
'class' => 'ti ti-sort-ascending-letters'
|
|
];
|
|
|
|
$button2 = [
|
|
'method' => 'button',
|
|
'href' => 'tree.php?action=sortdesc',
|
|
'callback' => true,
|
|
'title' => __esc('Sort Trees Descending'),
|
|
'class' => 'ti ti-sort-descending-letters'
|
|
];
|
|
|
|
// create the page filter
|
|
$pageFilter = new CactiTableFilter(__('Trees'), 'tree.php', 'form_tree', 'sess_tree', 'tree.php?action=edit');
|
|
$pageFilter->rows_label = __('Trees');
|
|
$pageFilter->set_sort_array('sequence', 'ASC');
|
|
$pageFilter->add_button('sortasc', $button1);
|
|
$pageFilter->add_button('sortdesc', $button2);
|
|
|
|
$pageFilter->render();
|
|
|
|
// if the number of rows is -1, set it to the default
|
|
if (grv('rows') == -1) {
|
|
$rows = read_config_option('num_rows_table');
|
|
} else {
|
|
$rows = grv('rows');
|
|
}
|
|
|
|
$sql_where = '';
|
|
$sql_params = [];
|
|
|
|
// form the 'where' clause for our main sql query
|
|
if (grv('filter') != '') {
|
|
$sql_where .= 'WHERE (t.name LIKE ? OR ti.title LIKE ?)';
|
|
|
|
$sql_params[] = '%' . grv('filter') . '%';
|
|
$sql_params[] = '%' . grv('filter') . '%';
|
|
}
|
|
|
|
$sql_order = get_order_string();
|
|
$sql_limit = ' LIMIT ' . ($rows * (grv('page') - 1)) . ',' . $rows;
|
|
|
|
$trees = db_fetch_assoc_prepared("SELECT t.*,
|
|
SUM(CASE WHEN ti.host_id > 0 THEN 1 ELSE 0 END) AS hosts,
|
|
SUM(CASE WHEN ti.local_graph_id > 0 THEN 1 ELSE 0 END) AS graphs,
|
|
SUM(CASE WHEN ti.local_graph_id = 0 AND host_id = 0 AND site_id = 0 THEN 1 ELSE 0 END) AS branches,
|
|
SUM(CASE WHEN ti.site_id > 0 THEN 1 ELSE 0 END) AS sites
|
|
FROM graph_tree AS t
|
|
LEFT JOIN graph_tree_items AS ti
|
|
ON t.id=ti.graph_tree_id
|
|
$sql_where
|
|
GROUP BY t.id
|
|
$sql_order
|
|
$sql_limit",
|
|
$sql_params);
|
|
|
|
$sql = "SELECT COUNT(DISTINCT(t.id))
|
|
FROM graph_tree AS t
|
|
LEFT JOIN graph_tree_items AS ti
|
|
ON t.id=ti.graph_tree_id
|
|
$sql_where";
|
|
|
|
$total_rows = get_total_row_data($_SESSION[SESS_USER_ID], $sql, $sql_params, 'tree');
|
|
|
|
$nav = html_nav_bar('tree.php?filter=' . grv('filter'), MAX_DISPLAY_PAGES, grv('page'), $rows, $total_rows, 11, __('Trees'), 'page', 'main');
|
|
|
|
form_start('tree.php', 'chk');
|
|
|
|
print $nav;
|
|
|
|
html_start_box('', '100%', false, 3, 'center', '');
|
|
|
|
$display_text = [
|
|
'name' => [
|
|
'display' => __('Tree Name'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The name by which this Tree will be referred to as.')
|
|
],
|
|
'id' => [
|
|
'display' => __('ID'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The internal database ID for this Tree. Useful when performing automation or debugging.')
|
|
],
|
|
'enabled' => [
|
|
'display' => __('Published'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('Unpublished Trees cannot be viewed from the Graph tab')
|
|
],
|
|
'locked' => [
|
|
'display' => __('Locked'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('A Tree must be locked in order to be edited.')
|
|
],
|
|
'user_id' => [
|
|
'display' => __('Owner'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The original author of this Tree.')
|
|
],
|
|
'sequence' => [
|
|
'display' => __('Order'),
|
|
'align' => 'center',
|
|
'sort' => 'ASC',
|
|
'tip' => __('To change the order of the trees, first sort by this column, press the up or down arrows once they appear.')
|
|
],
|
|
'last_modified' => [
|
|
'display' => __('Last Edited'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The date that this Tree was last edited.')
|
|
],
|
|
'modified_by' => [
|
|
'display' => __('Edited By'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The last user to have modified this Tree.')
|
|
],
|
|
'sites' => [
|
|
'display' => __('Sites'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The total number of Site Branches in this Tree.')
|
|
],
|
|
'branches' => [
|
|
'display' => __('Branches'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The total number of Branches in this Tree.')
|
|
],
|
|
'hosts' => [
|
|
'display' => __('Devices'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The total number of individual Devices in this Tree.')
|
|
],
|
|
'graphs' => [
|
|
'display' => __('Graphs'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The total number of individual Graphs in this Tree.')
|
|
]
|
|
];
|
|
|
|
html_header_sort_checkbox($display_text, grv('sort_column'), grv('sort_direction'), false);
|
|
|
|
$i = 1;
|
|
|
|
if (cacti_sizeof($trees)) {
|
|
foreach ($trees as $tree) {
|
|
$sequence = '';
|
|
|
|
if (grv('sort_column') == 'sequence' && grv('sort_direction') == 'ASC') {
|
|
if ($i == 1 && cacti_sizeof($trees) == 1) {
|
|
$sequence .= '<span class="moveArrowNone"></span>';
|
|
$sequence .= '<span class="moveArrowNone"></span>';
|
|
} elseif ($i == 1) {
|
|
$sequence .= '<a class="pic ti ti-caret-down-filled moveArrow cactiPostAction" href="#" data-url="' . html_escape_url('tree.php?action=tree_down&id=' . $tree['id']) . '" title="' . __esc('Move Down') . '"></a>';
|
|
$sequence .= '<span class="moveArrowNone"></span>';
|
|
} elseif ($i == cacti_sizeof($trees)) {
|
|
$sequence .= '<span class="moveArrowNone"></span>';
|
|
$sequence .= '<a class="pic ti ti-caret-up-filled moveArrow cactiPostAction" href="#" data-url="' . html_escape_url('tree.php?action=tree_up&id=' . $tree['id']) . '" title="' . __esc('Move Up') . '"></a>';
|
|
} else {
|
|
$sequence .= '<a class="pic ti ti-caret-down-filled moveArrow cactiPostAction" href="#" data-url="' . html_escape_url('tree.php?action=tree_down&id=' . $tree['id']) . '" title="' . __esc('Move Down') . '"></a>';
|
|
$sequence .= '<a class="pic ti ti-caret-up-filled moveArrow cactiPostAction" href="#" data-url="' . html_escape_url('tree.php?action=tree_up&id=' . $tree['id']) . '" title="' . __esc('Move Up') . '"></a>';
|
|
}
|
|
}
|
|
|
|
form_alternate_row('line' . $tree['id'], true);
|
|
|
|
form_selectable_cell(filter_value($tree['name'], grv('filter'), 'tree.php?action=edit&id=' . $tree['id']), $tree['id']);
|
|
form_selectable_cell($tree['id'], $tree['id'], '', 'right');
|
|
form_selectable_cell($tree['enabled'] == 'on' ? __('Yes') : __('No'), $tree['id']);
|
|
form_selectable_cell($tree['locked'] == '1' ? __('Yes') : __('No'), $tree['id']);
|
|
form_selectable_cell(get_username($tree['user_id']), $tree['id']);
|
|
form_selectable_cell($sequence, $tree['id'], '', 'nowrap center');
|
|
form_selectable_cell(substr($tree['last_modified'],0,16), $tree['id'], '', 'right');
|
|
form_selectable_cell(get_username($tree['modified_by']), $tree['id'], '', 'right');
|
|
form_selectable_cell($tree['sites'] > 0 ? number_format_i18n($tree['sites'], -1) : '-', $tree['id'], '', 'right');
|
|
form_selectable_cell($tree['branches'] > 0 ? number_format_i18n($tree['branches'], -1) : '-', $tree['id'], '', 'right');
|
|
form_selectable_cell($tree['hosts'] > 0 ? number_format_i18n($tree['hosts'], -1) : '-', $tree['id'], '', 'right');
|
|
form_selectable_cell($tree['graphs'] > 0 ? number_format_i18n($tree['graphs'], -1) : '-', $tree['id'], '', 'right');
|
|
|
|
form_checkbox_cell($tree['name'], $tree['id']);
|
|
|
|
form_end_row();
|
|
|
|
$i++;
|
|
}
|
|
} else {
|
|
print "<tr class='tableRow odd'><td colspan='" . (cacti_sizeof($display_text) + 1) . "'><em>" . __('No Trees Found') . '</em></td></tr>';
|
|
}
|
|
|
|
html_end_box(false);
|
|
|
|
if (cacti_sizeof($trees)) {
|
|
print $nav;
|
|
}
|
|
|
|
// draw the dropdown containing a list of available actions for this form
|
|
draw_actions_dropdown($actions);
|
|
|
|
form_end();
|
|
|
|
if (grv('sort_column') == 'sequence' && grv('sort_direction') == 'ASC') {
|
|
?>
|
|
<script type='text/javascript'>
|
|
$(function() {
|
|
$('#tree2_child').attr('id', 'tree_ids');
|
|
|
|
<?php if (read_config_option('drag_and_drop') == 'on') { ?>
|
|
|
|
$('form#chk').find('table').unbind().tableDnD({
|
|
onDrop: function(table, row) {
|
|
loadUrl({url:'tree.php?action=ajax_dnd&'+$.tableDnD.serialize()})
|
|
}
|
|
});
|
|
<?php } ?>
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
}
|