diff --git a/includes/database.php b/includes/database.php index b5e7c89..319140b 100644 --- a/includes/database.php +++ b/includes/database.php @@ -222,6 +222,15 @@ function intropage_upgrade_database(): void { $current = $info['version']; $oldv = db_fetch_cell('SELECT version FROM plugin_config WHERE directory = "intropage"'); + // The plugin's library directory moved from include/ to includes/. Repoint + // any plugin_hooks row still bound to the old path on every check - not only + // during a version-change upgrade - because an install whose version was + // already bumped without its hooks being repointed would otherwise keep + // loading the stale include/ path (or, once include/ is deleted, fail + // Cacti's plugin file-inclusion security check for it) on every page. The + // LIKE filter makes this a no-op on healthy installs. + db_execute("UPDATE plugin_hooks SET file = REPLACE(file, 'include/', 'includes/') WHERE name = 'intropage' AND file LIKE 'include/%'"); + if (!cacti_version_compare($oldv, $current, '=')) { if (cacti_version_compare($oldv, '3.0.0', '<=')) { include_once($config['base_path'] . '/plugins/intropage/includes/functions.php'); @@ -369,11 +378,6 @@ function intropage_upgrade_database(): void { ADD COLUMN `height` enum("normal","double","triple") NOT NULL DEFAULT "normal"'); } - // The plugin's library directory moved from include/ to includes/; repoint - // any hook still registered against the old path so existing installs load - // them from the new location after upgrade. - db_execute("UPDATE plugin_hooks SET file = REPLACE(file, 'include/', 'includes/') WHERE name = 'intropage' AND file LIKE 'include/%'"); - // Remove files/directories a previous version left behind (per manifest.json). plugin_intropage_prune_files(); diff --git a/tests/Unit/IntropageLifecycleTest.php b/tests/Unit/IntropageLifecycleTest.php index b9f1a94..b532c6d 100644 --- a/tests/Unit/IntropageLifecycleTest.php +++ b/tests/Unit/IntropageLifecycleTest.php @@ -3,6 +3,8 @@ +-------------------------------------------------------------------------+ | Copyright (C) 2004-2026 The Cacti Group | +-------------------------------------------------------------------------+ + | Cacti: The Complete RRDtool-based Graphing Solution | + +-------------------------------------------------------------------------+ */ /* @@ -45,8 +47,17 @@ expect(plugin_intropage_upgrade())->toBeFalse(); }); -it('does nothing when the stored version already matches the plugin version', function () { +it('repoints stale include/ hooks even when the stored version already matches', function () { plugin_intropage_check_config(); - expect($GLOBALS['__test_db_calls'])->toBeEmpty(); + // On an up-to-date install the only work is the idempotent include/ -> + // includes/ plugin_hooks repoint; no schema migration or version write runs. + expect($GLOBALS['__test_db_calls'])->toHaveCount(1); + + $call = $GLOBALS['__test_db_calls'][0]; + + expect($call['fn'])->toBe('db_execute') + ->and($call['sql'])->toContain('UPDATE plugin_hooks') + ->and($call['sql'])->toContain("REPLACE(file, 'include/', 'includes/')") + ->and($call['sql'])->toContain("file LIKE 'include/%'"); });