Add a TTL-based cache for the expensive checks on the admin system page,
eliminating repeated systemctl subprocess calls (~4×~100ms), curl self-pings
(~200-500ms), disk_*_space() and PHP ini reads on every page load.
Changes:
- storage/migrations/007_system_cache.sql: new migration creating the
system_cache table (key TEXT PK, value TEXT, updated_at INTEGER)
- storage/schema.sql: system_cache table added before pages table
- Applied migration to live storage/posterg.db
- src/SystemCache.php: new class with get/set/isStale/ageSeconds/invalidate;
uses SQLite INSERT … ON CONFLICT upsert; no external dependencies
- src/Database.php: added getDatabasePath(): string accessor
- public/admin/system.php:
- Bootstrap SystemCache at request start using the existing DB PDO handle
- system_status: cached with 2-min TTL (systemctl + curl checks)
- php_info: cached with 1-hour TTL (PHP ini values are runtime-constant)
- disk_info: cached with 5-min TTL (total/free/used/pct tuple)
- Logs section: unchanged — always reads live log tail per active tab
- ?refresh=1 GET param invalidates all three cache keys before rendering
- Status panel heading shows cache badge: '⚡ Cache — il y a Xs' (hit)
or '⟳ Actualisé' (miss/fresh), styled via new .sys-cache-badge rules
- public/assets/css/system.css: .sys-cache-badge / --hit / --miss styles
Remove five presentational classes from admin forms and replace with
structural CSS selectors scoped to .admin-form:
- .admin-form-row → .admin-form > div:not(.admin-submit-wrap)
Grid layout (260px label col + 1fr input col) applied directly to div
children of the form; submit-wrap div excluded via :not().
- .admin-label → .admin-form > div:not(.admin-submit-wrap) > label
Scoped to the direct label child of each form row div; does not bleed
into nested checkbox labels inside .admin-checkbox-list.
- .admin-input / .admin-select / .admin-textarea
→ .admin-form input:not([type=checkbox|radio|file|hidden|submit])
→ .admin-form select
→ .admin-form textarea
Also extended to .admin-inline-form input/select (tags page) so the
tags table inputs retain identical base styling and focus colour.
Templates updated: add.php, edit.php, login.php, account.php,
pages-edit.php, import.php, tags.php,
templates/partials/form/jury-fieldset.php — all class= attributes for
the five removed classes stripped.
import.php: added 'admin-form' class alongside 'admin-import-area' so
its single file-input row gets the grid row treatment; submit div was
already using admin-submit-wrap so it is correctly excluded.
No visual change — selectors target the same elements as before.
- admin.css: replace .admin-alert / .admin-alert--error / .admin-alert--success
selectors with [role="alert"][data-type="error"] and [role="status"][data-type="success"]
- All 10 admin templates updated: <div class="admin-alert admin-alert--{type}">
becomes <p role="alert|status" data-type="error|success"> (or <div> for the
import.php multi-item list that contains a <ul>)
- flash-messages.php partial updated to match
- WCAG benefit: role="alert" is an ARIA live region — errors are announced
immediately by screen readers without focus movement (fixes WCAG 3.3.1, 4.1.2)
- role="status" (polite live region) used for success messages — announced
without interrupting the user
- Removes two BEM modifier classes; CSS now targets element semantics directly
Replace four presentational class names in admin.css with structural selectors
that target native HTML elements already present in every admin template:
.admin-main → .admin-body main
.admin-page-title → .admin-body main > h1
.admin-table → .admin-body table
.admin-fieldset → .admin-body fieldset
.admin-fieldset-legend → .admin-body legend
Also migrate the .admin-main > section / h2 / dl / dt / dd block to
.admin-body main > section so the thanks-page section styles survive.
Add .admin-body main > table { margin-top: 1.5rem } to absorb the inline
style="margin-top:1.5rem" that was on tags.php's <table class="admin-table">.
All 10 affected admin templates updated (add, edit, account, index, import,
pages, pages-edit, tags, system, thanks) — class attributes removed where
the element alone is now the selector. Zero visual changes.
- admin.css: remove .admin-hint and .admin-field-hint class rules; add
.admin-body form small with the same font-size/color/margin properties
plus display:block so it stacks below sibling inputs; stub comment left
where .admin-field-hint was to document the change
- add.php: 5× <p class="admin-hint"> → <small>
- edit.php: 3× <p class="admin-hint"> → <small>
- import.php: <div class="admin-hint"> → <small> (block hint below CSV input)
- pages-edit.php: class="admin-hint" removed from already-correct <small>
- account.php: <p class="admin-field-hint"> → <small>
Hint text is now styled purely via the semantic element selector; no class
required on any hint element in admin templates.
- Create public/assets/css/system.css with all 280 lines of CSS that were
inline in system.php: tab bar, status cards, PHP info grid, disk bar,
log viewer, nginx config viewer, and syntax-highlight classes.
- Disk bar dynamic values (width %, colour) moved from PHP-interpolated CSS
rules to CSS custom properties (--disk-pct, --disk-color) set on the
element via an inline style attribute; static .disk-bar rule in system.css
consumes them via var().
- system.php JS block (tab-select auto-nav + copy-to-clipboard) moved to
$extraJsInline heredoc; footer.php emits it before </body> — keeps it
out of the document <head> and removes the bare <script> after </main>.
- system.php now sets $extraCss = ['/assets/css/system.css'] so head.php
emits a proper <link> in <head>, consistent with all other admin pages.
- No behaviour change; system.php is now zero inline CSS/JS.
Replace 6 CSS class selectors across tfe.css, main.css, and search.css with
semantic element-based selectors, removing the corresponding classes from the
HTML templates entirely.
tfe.css:
- .tfe-meta-list → article dl / article dl > div / article dl dt / article dl dd
- .tfe-media-block → aside figure (+ img, video, embed children)
- .tfe-file-caption → aside figcaption
main.css:
- .card__media → .home-body figure (+ img/video children and hover/motion rules)
- .card__caption → .home-body li > a > p
search.css:
- .repertoire-col > h2 → .repertoire-index section > h2
Template changes:
- tfe.php: removed class= from <dl>, <figure>, and <figcaption>
- index.php: removed class= from <figure> and <p class=card__caption>;
stripped orphaned card__media from the gradient <div> (only --gradient needed)
No visual change — selectors match the same elements as before since the
semantic HTML was already in place from prior refactoring work.
- login.php: removed style= on .admin-form-row and .admin-label (already covered
by .admin-login-box scoped rules); extracted submit-wrap spacing and full-width
button to .admin-login-box .admin-submit-wrap and .admin-login-box .admin-btn
- account.php: style="margin-top:3rem" on danger-zone heading moved to
.admin-section-title--danger modifier; <span style="color:..."> replaced with
<small> element styled via .admin-danger-zone__description small
- add.php / edit.php / pages-edit.php: all style="align-items:start" removed from
.admin-form-row (redundant — already the CSS default at line 116 of admin.css);
banner preview inline styles extracted to .admin-banner-preview / .admin-banner-preview img;
add-jury button margin extracted to .admin-add-jury-btn; cancel links use .admin-cancel-link
Zero inline style= attributes remain in login, account, add, edit, pages-edit.
- admin/index.php: replace <div class="admin-stats"> with <dl>; inner
<div class="admin-stat__number"> → <dd>, <div class="admin-stat__label"> → <dt>;
use CSS order to keep number visually first; add scope="col" to all 9 <th> cells
- admin/thanks.php: replace all four <div class="admin-thesis-info"> wrappers
with <section> elements; remove the class entirely; add scope="col" to
the files table <th> cells
- admin/tags.php: add scope="col" to all 3 <th> cells
- admin/pages.php: add scope="col" to all 4 <th> cells
- admin.css: rename .admin-thesis-info selectors to .admin-main > section
(element + context selector — no class needed); add display:flex +
flex-direction:column to .admin-stat so CSS order property works correctly
Addresses TODO items: section X (admin-stats dl, th scope), XI (tags th scope),
XII (admin-thesis-info → section), XIII (pages.php th scope)
CSS: .site-search → header form[role="search"],
.site-search__icon → header form[role="search"] svg,
.site-search__input → header form[role="search"] input,
.site-search__input::placeholder → header form[role="search"] input::placeholder
HTML: Removed class="site-search", class="site-search__icon", and
class="site-search__input" from header.php and search-bar.php.
The form already uses role="search" and contains a single svg + input,
so the semantic selectors are unambiguous.
templates/admin/head.php:
- admin-nav__logo now href="/" with target="_blank" rel="noopener noreferrer"
- Left arrow prefix (← via ←, aria-hidden) signals leaving admin
- sr-only suffix "(site public, nouvel onglet)" for screen readers
public/admin/login.php:
- Same treatment on the standalone login nav (was a bare <span>)
public/assets/css/admin.css:
- admin-nav__list: flex:1 removed; margin-left:auto added
→ entire link list now right-justified inside the nav bar,
mirroring the layout of the public site header
- .admin-nav__logout { margin-left:auto } removed (no longer needed;
logout is just the last item in a right-aligned list)