diff --git a/CHANGELOG.md b/CHANGELOG.md index ce6752a..002deb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- develop --- +* issue: Load Thold's relocated function library from `includes/functions.php` (with a legacy `thold_functions.php` fallback) and skip the threshold poller when Thold's API is unavailable, so intropage no longer fatals after Thold moved its functions out of the plugin root * dev: Rename the plugin's stylesheet directory from `themes/` to `css/`; the old directory is tombstoned in `manifest.json` and removed on upgrade * dev: Move the plugin's library directory from `include/` to `includes/` and re-register the affected hooks on upgrade (a `plugin_hooks` file repoint in `intropage_upgrade_database()`) so existing installations load them from the new path * 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) diff --git a/panellib/thold.php b/panellib/thold.php index 1db4fce..fa85410 100644 --- a/panellib/thold.php +++ b/panellib/thold.php @@ -24,6 +24,36 @@ +-------------------------------------------------------------------------+ */ +/** + * Loads the Thold plugin's function library. Thold relocated its helpers + * from the legacy plugins/thold/thold_functions.php to + * plugins/thold/includes/functions.php, so prefer the new path and fall + * back to the old one for older Thold installs. + * + * @return bool True when a Thold function library was found and loaded. + * + * @global array $config Cacti global configuration array; used to resolve + * the plugin base path. + */ +function intropage_load_thold_functions(): bool { + global $config; + + $candidates = [ + $config['base_path'] . '/plugins/thold/includes/functions.php', + $config['base_path'] . '/plugins/thold/thold_functions.php', + ]; + + foreach ($candidates as $file) { + if (file_exists($file)) { + include_once($file); + + return true; + } + } + + return false; +} + /** * Registers the 'thold' panel category and its 'Last Threshold Events' * and 'Threshold' (trend graph) panels with the panel library. Called @@ -239,7 +269,7 @@ function graph_thold($panel, $user_id, $timespan = 0): void { $panel['alarm'] = 'green'; - include_once($config['base_path'] . '/plugins/thold/thold_functions.php'); + intropage_load_thold_functions(); $graph = [ 'line' => [ @@ -374,7 +404,7 @@ function graph_thold($panel, $user_id, $timespan = 0): void { function graph_thold_detail(): array { global $config, $sql_where; - include_once($config['base_path'] . '/plugins/thold/thold_functions.php'); + intropage_load_thold_functions(); $panel = [ 'name' => __('Threshold Details', 'intropage'), @@ -498,7 +528,7 @@ function graph_thold_detail(): array { function thold_event_detail(): array { global $config; - include_once($config['base_path'] . '/plugins/thold/thold_functions.php'); + intropage_load_thold_functions(); $panel = [ 'name' => __('Last Threshold Events', 'intropage'), @@ -592,7 +622,11 @@ function thold_collect(): void { // update in poller $users = get_user_list(); - include_once($config['base_path'] . '/plugins/thold/thold_functions.php'); + // Skip quietly when Thold is absent or does not expose its threshold API, + // rather than fataling the intropage poller. + if (!intropage_load_thold_functions() || !function_exists('get_allowed_thresholds')) { + return; + } foreach ($users as $user) { $t_brea = 0; diff --git a/tests/Unit/TholdFunctionLoaderTest.php b/tests/Unit/TholdFunctionLoaderTest.php new file mode 100644 index 0000000..0485cb2 --- /dev/null +++ b/tests/Unit/TholdFunctionLoaderTest.php @@ -0,0 +1,103 @@ +toBeTrue(); +}); + +it('falls back to the legacy thold_functions.php path', function () { + $base = intropage_thold_sandbox('legacy'); + mkdir($base . '/plugins/thold', 0777, true); + file_put_contents($base . '/plugins/thold/thold_functions.php', "toBeTrue(); +}); + +it('returns false when no Thold library is present', function () { + $base = intropage_thold_sandbox('none'); + mkdir($base . '/plugins/thold', 0777, true); + + expect(intropage_load_thold_functions())->toBeFalse(); +}); + +it('renders the thold graph panel as grey when Thold is not installed', function () { + intropage_thold_sandbox('graph'); + + $panel = array('id' => 1, 'refresh' => 300, 'alarm' => 'green'); + graph_thold($panel, 1); + + $updates = array_values(array_filter($GLOBALS['__test_db_calls'], function ($call) { + return $call['fn'] === 'db_execute_prepared' + && stripos($call['sql'], 'UPDATE plugin_intropage_panel_data') !== false; + })); + + expect($updates)->toHaveCount(1); + expect($updates[0]['params'][1])->toBe('grey'); +}); + +it('renders the thold detail panels when Thold is not installed', function () { + intropage_thold_sandbox('detail'); + + $detail = graph_thold_detail(); + $events = thold_event_detail(); + + expect($detail['alarm'])->toBe('grey'); + expect($events['alarm'])->toBe('yellow'); +}); + +it('skips thold_collect when the Thold threshold API is unavailable', function () { + intropage_thold_sandbox('collect'); + + thold_collect(); + + $trend_writes = array_filter($GLOBALS['__test_db_calls'], function ($call) { + return isset($call['sql']) && stripos($call['sql'], 'plugin_intropage_trends') !== false; + }); + + expect($trend_writes)->toBeEmpty(); +});