Files
Cactiplugin_monitor/includes/database.php
T
TheWitnessandCopilot 66358e0998 Adopt plugin table API and relocate schema to includes/database.php (#254)
* Relocate schema to includes/database.php via plugin table API + db_update_table upgrade

* CI: allowlist unmeasured schema bootstrap and web entry points

* CI: keep include_once for the settings-page-only lib/reports.php load (uncoverable)

* Add manifest.json + upgrade-time file pruning; consolidate themes/ into css/

* database: keep user-owned dashboards table across uninstall

api_plugin_db_table_create() records plugin_monitor_dashboards in
plugin_db_changes, so api_plugin_db_changes_remove() (run by
api_plugin_uninstall() after plugin_monitor_uninstall()) would drop it
even though monitor_drop_tables() intentionally leaves it in place. Remove
the 'create' ownership record after provisioning (install and upgrade
paths) so user dashboards survive an uninstall. Adds a regression test.

* Prune phpunit.xml on upgrade and ignore markdown-lint configs

phpunit.xml ships in the repo for CI but is a dev-only test artifact, so it
is removed from manifest.json 'expected', pruned from installs on upgrade
(like tests/), and excluded from the manifest drift check.

The retired markdown-lint configs (.mdlrc, .md_style.rb) are deleted, and
.md* is now ignored like .git*: protected from pruning and excluded from
drift if it reappears.

* Move libraries into includes/, flatten css, drop sonar config

db_functions.php, monitor_render.php and monitor_controller.php move into
includes/ (the latter two lose the monitor_ prefix, becoming render.php and
controller.php); monitor.php and the tests load them from there. Each per-theme
stylesheet css/<theme>/monitor.css becomes css/<theme>.css so users can drop
their own css/<theme>.css into the css base, and plugin_monitor_page_head()
looks there, falling back to css/monitor.css. The unreferenced
sonar-project.properties is removed. Old paths are tombstoned so existing
installs drop the stale copies on upgrade.

* Rename includes/db_functions.php to includes/functions.php

Follows the fleet convention for the plugin's shared library filename; monitor.php,
the measured-source list and the unit test load it from the new name.

* Regenerate cacti.pot for the includes/ file relocations

Moving the plugin's library files into includes/ changed the source-reference
lines in the translation template; regenerate it so the 'Verify translation
template' CI check passes.

* Harden prune against tampered-manifest path traversal

The upgrade-time prune now rejects any tombstone containing '.'/'..' segments
(which could escape the plugin directory or resolve to its root) and treats
ancestors of whitelist entries as protected, so a tombstone on a parent
directory can no longer delete a whitelisted file beneath it.

* docs: update and align the Project Structure block

Point the structure tree at the relocated files (libraries now under
includes/, data files under docs/, stylesheets under css/) and align the
trailing '# ...' comments to a single column so they no longer drift right.

* docs/tests: standardize test header and correct manifest-contract notes

- Use the full license header (from the plugin's own setup.php) in
  tests/Unit/PruneFilesTest.php instead of the abbreviated copyright banner.
- Document that the manifest drift check and the upgrade-time prune also
  handle phpunit.xml and .md* files, matching the implemented behavior.

* refactor: rename prune helpers to the plugin-slug function prefix

The repository naming contract reserves the plugin_<name>_ prefix for plugin
lifecycle / hook-registration functions; all other functions use the plain
<name>_ prefix. Rename the internal upgrade helpers accordingly:

  plugin_<name>_prune_files() -> <name>_prune_files()
  plugin_<name>_rmtree()      -> <name>_rmtree()

The call site, unit tests, and the copilot-instructions.md references are
updated to match. No behavioral change.

* chore: set the Cacti compatibility floor to 1.2.29

Align the compat metadata (and the contributor-guide references) with the
team decision to standardize the minimum supported Cacti version at 1.2.29;
the previously proposed 1.2.32 floor was not adopted.

* Bump plugin version to 3.0 so the upgrade-time cleanup migration runs

Add a 3.0 CHANGELOG section for this cycle's schema/manifest work and set
INFO version = 3.0 so existing 2.9 installs detect the version change and
run the schema refresh and manifest-based file prune.

---------

Co-authored-by: Copilot <copilot@users.noreply.github.com>
2026-10-01 01:39:53 -05:00

205 lines
9.6 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/ |
+-------------------------------------------------------------------------+
*/
/**
* The plugin_monitor_notify_history table definition (one row per
* notification event). Shared by the create and upgrade paths so both
* stay in sync from a single source.
*
* @return array<string,mixed> Table definition consumed by
* api_plugin_db_table_create()/db_update_table().
*/
function monitor_notify_history_table_data(): array {
$data = [];
$data['columns'][] = ['name' => 'id', 'type' => 'int(10)', 'unsigned' => true, 'NULL' => false, 'auto_increment' => true];
$data['columns'][] = ['name' => 'host_id', 'type' => 'int(10)', 'unsigned' => true, 'NULL' => true, 'default' => null];
$data['columns'][] = ['name' => 'notify_type', 'type' => 'tinyint(3)', 'unsigned' => true, 'NULL' => true, 'default' => null];
$data['columns'][] = ['name' => 'ping_time', 'type' => 'double', 'NULL' => true, 'default' => null];
$data['columns'][] = ['name' => 'ping_threshold', 'type' => 'int(10)', 'unsigned' => true, 'NULL' => true, 'default' => null];
$data['columns'][] = ['name' => 'notification_time', 'type' => 'timestamp', 'NULL' => false, 'default' => '0000-00-00 00:00:00'];
$data['columns'][] = ['name' => 'notes', 'type' => 'varchar(255)', 'NULL' => true, 'default' => null];
$data['primary'] = ['id'];
$data['unique_keys'][] = ['name' => 'unique_key', 'columns' => ['host_id', 'notify_type', 'notification_time']];
$data['type'] = 'InnoDB';
$data['comment'] = 'Stores Notification Event History';
return $data;
}
/**
* The plugin_monitor_reboot_history table definition (one row per detected
* device reboot).
*
* @return array<string,mixed> Table definition.
*/
function monitor_reboot_history_table_data(): array {
$data = [];
$data['columns'][] = ['name' => 'id', 'type' => 'int(10)', 'unsigned' => true, 'NULL' => false, 'auto_increment' => true];
$data['columns'][] = ['name' => 'host_id', 'type' => 'int(10)', 'unsigned' => true, 'NULL' => true, 'default' => null];
$data['columns'][] = ['name' => 'reboot_time', 'type' => 'timestamp', 'NULL' => false, 'default' => '0000-00-00 00:00:00'];
$data['columns'][] = ['name' => 'log_time', 'type' => 'timestamp', 'NULL' => false, 'default' => 'CURRENT_TIMESTAMP'];
$data['primary'] = ['id'];
$data['keys'][] = ['name' => 'host_id', 'columns' => ['host_id']];
$data['keys'][] = ['name' => 'log_time', 'columns' => ['log_time']];
$data['keys'][] = ['name' => 'reboot_time', 'columns' => ['reboot_time']];
$data['type'] = 'InnoDB';
$data['comment'] = 'Keeps Track of Device Reboot Times';
return $data;
}
/**
* The plugin_monitor_uptime table definition (last known uptime per
* device). Uses the current column layout (uptime NOT NULL).
*
* @return array<string,mixed> Table definition.
*/
function monitor_uptime_table_data(): array {
$data = [];
$data['columns'][] = ['name' => 'host_id', 'type' => 'int(10)', 'unsigned' => true, 'NULL' => false, 'default' => 0];
$data['columns'][] = ['name' => 'uptime', 'type' => 'bigint(20)', 'unsigned' => true, 'NULL' => false, 'default' => 0];
$data['primary'] = ['host_id'];
$data['keys'][] = ['name' => 'uptime', 'columns' => ['uptime']];
$data['type'] = 'InnoDB';
$data['comment'] = 'Keeps Track of the Devices last uptime to track agent restarts and reboots';
return $data;
}
/**
* The plugin_monitor_dashboards table definition (per-user predefined
* dashboards).
*
* @return array<string,mixed> Table definition.
*/
function monitor_dashboards_table_data(): array {
$data = [];
$data['columns'][] = ['name' => 'id', 'type' => 'int(10)', 'unsigned' => true, 'NULL' => false, 'auto_increment' => true];
$data['columns'][] = ['name' => 'user_id', 'type' => 'int(10)', 'unsigned' => true, 'NULL' => true, 'default' => 0];
$data['columns'][] = ['name' => 'name', 'type' => 'varchar(128)', 'NULL' => true, 'default' => ''];
$data['columns'][] = ['name' => 'url', 'type' => 'varchar(1024)', 'NULL' => true, 'default' => ''];
$data['primary'] = ['id'];
$data['keys'][] = ['name' => 'user_id', 'columns' => ['user_id']];
$data['type'] = 'InnoDB';
$data['comment'] = 'Stores predefined dashboard information for a user or users';
return $data;
}
/**
* Removes the plugin_db_changes 'create' ownership record for the user-owned
* dashboards table so api_plugin_db_changes_remove() (run by
* api_plugin_uninstall()) does not drop it, preserving user dashboards across
* an uninstall.
*
* @return void
*/
function monitor_disown_dashboards_table() {
db_execute("DELETE FROM plugin_db_changes
WHERE plugin = 'monitor'
AND `table` = 'plugin_monitor_dashboards'
AND method = 'create'");
}
/**
* Creates this plugin's own tables through Cacti's tracked plugin table
* API and ensures the core host table carries the monitoring columns this
* plugin relies on. Called from plugin_monitor_install() and (idempotently)
* from monitor_check_upgrade().
*
* @return void
*/
function monitor_setup_table() {
api_plugin_db_table_create('monitor', 'plugin_monitor_notify_history', monitor_notify_history_table_data());
api_plugin_db_table_create('monitor', 'plugin_monitor_reboot_history', monitor_reboot_history_table_data());
api_plugin_db_table_create('monitor', 'plugin_monitor_uptime', monitor_uptime_table_data());
api_plugin_db_table_create('monitor', 'plugin_monitor_dashboards', monitor_dashboards_table_data());
/* dashboards holds user data: create it but do not record plugin ownership, so uninstall preserves it. */
monitor_disown_dashboards_table();
if (db_table_exists('host')) {
$row_format = db_fetch_cell("SELECT ROW_FORMAT
FROM information_schema.tables
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'host'");
if (strtoupper((string) $row_format) !== 'DYNAMIC') {
db_execute('ALTER TABLE host ROW_FORMAT=DYNAMIC');
}
}
db_execute('SET SESSION innodb_strict_mode=0');
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor', 'type' => 'char(3)', 'NULL' => true, 'default' => 'on']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_text', 'type' => 'text', 'NULL' => false]);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_criticality', 'type' => 'tinyint', 'unsigned' => true, 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_warn', 'type' => 'double', 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_alert', 'type' => 'double', 'NULL' => false, 'default' => '0']);
api_plugin_db_add_column('monitor', 'host', ['name' => 'monitor_icon', 'type' => 'varchar(30)', 'NULL' => false, 'default' => '']);
db_execute('SET SESSION innodb_strict_mode=1');
}
/**
* Refreshes this plugin's own tables to their current definition on
* upgrade: db_update_table() diffs the live schema against the definition
* and issues the exact ALTER when the table already exists, otherwise the
* table is created outright. Called from monitor_check_upgrade().
*
* @return void
*/
function monitor_upgrade_tables() {
$tables = [
'plugin_monitor_notify_history' => monitor_notify_history_table_data(),
'plugin_monitor_reboot_history' => monitor_reboot_history_table_data(),
'plugin_monitor_uptime' => monitor_uptime_table_data(),
'plugin_monitor_dashboards' => monitor_dashboards_table_data(),
];
foreach ($tables as $table => $table_data) {
if (db_table_exists($table)) {
db_update_table($table, $table_data);
} else {
api_plugin_db_table_create('monitor', $table, $table_data);
}
}
monitor_disown_dashboards_table();
}
/**
* Drops the tables this plugin owns that hold transient monitoring state
* (notification/reboot history and uptime). The user-defined dashboards
* table is intentionally left in place. Called from
* plugin_monitor_uninstall().
*
* @return void
*/
function monitor_drop_tables() {
db_execute('DROP TABLE IF EXISTS plugin_monitor_notify_history');
db_execute('DROP TABLE IF EXISTS plugin_monitor_reboot_history');
db_execute('DROP TABLE IF EXISTS plugin_monitor_uptime');
}