7 Commits
Author SHA1 Message Date
TheWitness 4d432a2e20 thold: status pills with sortable/clickable Status column and zebra rows (#851)
* thold: status pills with sortable/clickable Status column and zebra rows

Replace the whole-row status colour on the Thresholds (thold.php) and Device Status (thold_graph.php) pages with a rounded status pill in a new Status column right of Name; rows now fall back to zebra striping. The column is server-side sortable by severity via a new thold_severity_sql() CASE (a faithful SQL mirror of get_thold_severity(), legend-order rank 1=Alert..7=Disabled), the Status filter gains the seven per-severity options (values 101-107 handled in thold_get_state_filter()), and clicking a pill sets that filter and reloads. Pill shape lives in a theme-agnostic css/pills.css; all colours stay theme-aware (reusing each theme's .thold<State> backgrounds, with light pill text added to the saturated classic/modern/paw themes). Adds Pest coverage for thold_severity_sql() and the severity filter branch.

* thold: regenerate cacti.pot for the status-pill i18n strings

* thold: address review - pill contrast, time_warning_low '0' parity, keyboard a11y, stronger test

Light themes (classic/modern/paw all have white/snow backgrounds) now use dark pill text instead of white, which failed 4.5:1 at 11px. thold_severity_sql() treats time_warning_low '0' as falsy (NOT IN ('','0')) to match get_thold_severity(), so the pill never disagrees with sort/filter. Pills are now focusable role=button spans with Enter/Space activation on both pages. TholdSeveritySqlTest now executes the CASE over an in-memory SQLite row and asserts the SQL rank equals get_thold_severity() across branch/boundary scenarios incl. the '0' case.
2026-10-08 14:58:54 -04:00
TheWitnessandTheWitness 1158c70de0 Use a CSS class for the status legend footer instead of inline styles (#850)
* Pin status legends to the bottom of the page (match MacTrack)

Wrap the thold, host and log legend renders in the same fixed-position footer container MacTrack uses, so the legend stays pinned to the bottom of the viewport instead of scrolling away with the list.

* Use a CSS class for the legend footer instead of inline styles

Replace the inline fixed-position wrapper around the status legend with a
plugin-specific *LegendFooter class defined in the plugin's stylesheet. The
class lays the legend out as a centered footer in normal document flow, so it
no longer sits outside flow and overlay the final rows, pagination, or action
controls at the bottom of the page.

---------

Co-authored-by: TheWitness <1439914+TheWitness@users.noreply.github.com>
2026-10-07 21:00:07 -04:00
TheWitness cd1db48096 thold: make legend chips fill their grid cell (#848)
The legend chips carry both the new .tholdLegendItem class and a legacy
state class (.tholdAlert, .deviceUpFull, etc.) that still declares a fixed
width (14%/11.11%). The grid rule never set a width, so that legacy width
leaked onto the grid items: chips did not stretch to fill their cell and
long labels overflowed the coloured box. Give .tholdLegendItem
width:100% + box-sizing:border-box + min-width:0 so each chip fills its
grid track, keeping the label centred inside the colour boundary.
2026-10-07 14:14:34 -04:00
TheWitness d35a0e6614 thold: keep legend chips equal width using the longest label (#847)
* thold: keep legend chips equal width using the longest label

The Thold/Host/Log status legends used flex:1 1 0 with no shared minimum, so chips grew to unequal widths (and could clip their labels when tight). CSS can't measure the widest sibling, so the three legend renderers now compute the longest label and emit it as a --thold-chip-min CSS variable (calc(<longest>ch + 1.5rem)); every theme's .tholdLegendItem uses it as a shared min-width (falling back to max-content). All chips share one minimum, so they stay equal width as the legend wraps. Updated TholdLegendTest assertions to match the new container markup.

* thold: use CSS Grid for truly equal-width legend chips; assert exact chip min

Review follow-up (#847). The flex: 1 1 0 + flex-wrap layout distributes free space per wrapped line, so a partial last row stretched its chips wider than full rows - chips were not equal across rows. Switch .tholdLegend to a grid (repeat(auto-fill, minmax(var(--thold-chip-min), 1fr))) so every chip shares an equal column across all rows. The longest-label seed still drives the column minimum. Tests now assert the exact computed calc(Nch + 1.5rem) value instead of mere presence.

* thold: use auto-fit so chips fill the legend width when they fit on one row

Review follow-up: auto-fill keeps empty implicit tracks, so on a wide viewport where all chips fit in one row they no longer stretch to fill the legend (as they did under flex). auto-fit collapses the empty tracks while still sharing equal columns when the legend wraps to multiple rows.

* thold: escape status-legend chip labels at output

Defence-in-depth: wrap the legend chip display text in html_escape() so markup or HTML entities in a translated label cannot alter the legend DOM. Applied consistently across the thold/monitor/servcheck/mactrack/syslog fleet legends. The chip-min sizing still measures the unescaped label length, so layout is unchanged.
2026-10-07 13:20:14 -04:00
TheWitness 5b41aa07c9 thold: style the Resume glyph (fix duplicate .tholdGlyphAcknowledge selector) (#842)
* thold: style tholdGlyphAcknowledgeResume glyph in all themes

Each theme stylesheet carried a duplicated `.tholdGlyphAcknowledge`
selector. thold_graph.php emits three distinct acknowledge action
glyphs - tholdGlyphAcknowledge, tholdGlyphAcknowledgeSuspend, and
tholdGlyphAcknowledgeResume (the "Resume Notifications" action) - so
the second copy was clearly meant to target the Resume glyph, which
was left unstyled. Rename the duplicate to .tholdGlyphAcknowledgeResume
so the resume action receives its intended color in every theme.

Remediates the GitHub Copilot review findings on PR #841 (dark,
midwinter, sunrise, paper-plane) and fixes the identical latent
duplicate in the light themes (classic, modern, paw).

* thold: style tholdGlyphAcknowledgeResume glyph in light themes

Apply the same duplicate-selector fix to the light themes (paper-plane,
classic, modern, paw): rename the second .tholdGlyphAcknowledge copy to
.tholdGlyphAcknowledgeResume so the "Resume Notifications" graph-view
glyph receives its intended color instead of falling through unstyled.
2026-10-05 09:51:24 -04:00
TheWitnessandCacti Group 01da1e07cc Add theme-appropriate colors for graph-view threshold glyphs (#841)
The toggle-threshold-VRULEs and create-threshold action glyphs rendered on the graph view page had no color defined in any theme. Add a theme-appropriate color for each (.tholdVRules, .tholdEdit) across all packaged themes, and brighten the existing .tholdGlyph* action colors on the dark themes (dark, midwinter, sunrise, paper-plane) where the original values were too dark to read against the dark backgrounds.

Co-authored-by: Cacti Group <devteam@cacti.net>
2026-10-05 08:09:39 -04:00
TheWitnessandCopilot 9b217e0e3a INFO: set compat to 1.2.29 (#838)
* INFO: set compat to 1.2.32

* docs: update Copilot instructions Cacti baseline to 1.2.32 (matches INFO compat)

* INFO: back off compat to 1.2.29

* Add manifest.json + upgrade-time file pruning

* 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 thold_functions.php and thold_webapi.php into includes/; flatten css

The main threshold library becomes includes/functions.php and the web API
helper becomes includes/webapi.php; every caller (setup.php, includes/*, the
CLI/poller/notify entry points and the test suite) loads them from includes/.
Each per-theme stylesheet css/<theme>/main.css becomes css/<theme>.css so users
can drop their own css/<theme>.css into the css base, and the page_head hook
looks there. Old paths are tombstoned so existing installs drop the stale
copies on upgrade.

* 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 the hook-include test and make RPN integer ops explicit

The HookFunctionsIncludeTest invokes real hooks with placeholder input purely
to execute their relocated include_once lines; it now swallows engine
notices/warnings/deprecations during those calls (and restores the error
handler and config afterward) so it cannot trip the suite's failOnDeprecation
or leak state into other tests. The RPN % and ^ operators now cast their
operands to int explicitly, matching their long-standing integer-truncation
behaviour while silencing the implicit float-to-int conversion deprecation.

* 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.

---------

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