Commit Graph

11 Commits

Author SHA1 Message Date
Pontoporeia
871e919efa system.css: use only variables.css tokens, remove undefined custom properties
Replace the two undefined variables that had crept in:
- var(--admin-border) → #555  (in .log-output border)
- var(--admin-text-muted) → #969696  (inline style on log toolbar label,
  in both system.php and system-fragment.php)

Revert the incorrect intermediate attempt that mapped dark-UI hex values
to light-theme tokens (--bg-primary: #fff, --border-primary: #ddd, etc.)
and also revert the .admin-body override block that was added to
variables.css — variables.css is shared and must not have per-component
overrides.

All remaining var() calls in system.css now reference tokens that exist
in variables.css:
  --accent-primary, --accent-green, --error, --warning, --success,
  --text-tertiary
The dark surface colours (#1a1a1a, #242424, #0d0d0d, #555, #969696, etc.)
stay as literal hex values, consistent with how admin.css handles them.
2026-04-06 15:32:41 +02:00
Pontoporeia
b981223ff4 admin/system: fetch()-based tab switching, no full-page reload
Add system-fragment.php — a thin authenticated endpoint that returns only
the tab-panel HTML (toolbar + meta + log/nginx-config output) for a given
?tab=&n= combination. No page shell, no status section, no DB queries.

system.php changes:
- Tab <a> elements gain data-tab= attributes used by JS to identify the
  target without parsing hrefs.
- Tab panel content wrapped in <div id=sys-tab-panel data-tab= data-n=>
  which JS uses as both the swap target and its own state store.
- JS rewritten: tab clicks and lines-select changes call loadPanel()
  which fetch()es system-fragment.php, swaps innerHTML, updates active
  tab ARIA attributes, and pushes state via history.pushState.
- Browser back/forward handled via popstate listener.
- bindPanelControls() re-wires the lines-select and copy-to-clipboard
  button after every innerHTML swap (event delegation not feasible here
  because log-output is replaced wholesale).
- fetch() failure falls back to window.location.href (full page load).
- Tabs without JS still work: <a href> links go to system.php?tab=…
  as before.

system-fragment.php:
- Requires AdminAuth::isAuthenticated(); returns 403 on failure.
- Validates tab and n params against the same whitelist as system.php.
- All helper functions namespaced with frag_ prefix to avoid redeclaration
  if PHP ever includes both files in the same process.
- Renders identical HTML to the corresponding section in system.php.

system.css:
- #sys-tab-panel gets min-height:8rem and position:relative to prevent
  layout jump during fetch.
- .sys-panel-loading: opacity 0.4 + pointer-events:none + subtle
  diagonal-stripe ::after overlay with shimmer animation.
2026-04-06 15:32:41 +02:00
Pontoporeia
c86781b9be admin/system: move status panel above tabs, add collapse toggle
Status (services, PHP env, disk) is now always visible above the log/config
tab bar rather than being one of the tab targets:

- Status section rendered unconditionally above <nav class="sys-tabs">.
- Services grid, PHP info grid and disk bar grouped inside a collapsible
  <section> with a header row containing the cache-freshness badge and a
  toggle button (▲ Réduire / ▼ Développer).
- Collapse state persisted in localStorage so the preference survives
  page reloads (e.g. when switching log tabs).
- Tab bar now only contains the three log tabs + nginx config; the 'Statut'
  tab is removed. Legacy ?tab=status URLs fall through to nginx_access.
- PHP/disk sub-sections laid out in a 2-col grid inside the status panel;
  responsive single-col below 700px.
- system.css: new .sys-status-section / .sys-status-header /
  .sys-status-toggle / .sys-status-meta rules added.
- aria-current="page" added to active tab links.
- todo/03-system-cache.md: all items marked done; notes added explaining
  why log caching was deliberately omitted.
2026-04-06 15:32:41 +02:00
Pontoporeia
ba7814c6dc feat: system page caching via SystemCache + system_cache SQLite table
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
2026-04-02 13:04:00 +02:00
Pontoporeia
0ab08f3aa0 admin.css: replace .admin-main, .admin-page-title, .admin-table, .admin-fieldset with semantic selectors
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.
2026-04-02 12:16:59 +02:00
Pontoporeia
f208423e8d Extract system.php inline <style> and <script> to system.css / $extraJsInline
- 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.
2026-04-01 17:24:36 +02:00
Pontoporeia
780b1b2a13 merge head/nav templates into unified head.php + header.php; semantic CSS for nav 2026-04-01 15:55:12 +02:00
Pontoporeia
1181cfa88b encapsulate raw PDO queries leaking from callers into Database.php methods
- Add getThesisAccessTypeId(int $id): ?int — replaces raw SELECT in tfe.php
- Add getCoverPathsForTheses(array $ids): array — replaces raw SELECT/IN query in index.php
- Add getFileVisibility(string $path): ?int — replaces raw join query in media.php
- Add getThesisBannerPath(int $id): ?string — replaces unparameterised SQL injection in
  edit.php (SELECT banner_path FROM theses WHERE id = $thesisId was interpolating $thesisId
  directly into the query string; now parameterised via prepared statement)
- Add getThesisRawFields(int $id): ?array — replaces raw SELECT license_id/access_type_id/
  context_note in edit.php
- Add getThesisCount(): int — replaces raw SELECT COUNT(*) in system.php

Callers updated: public/tfe.php, public/index.php, public/media.php,
public/admin/edit.php, public/admin/system.php
2026-03-28 13:32:34 +01:00
Pontoporeia
42af4644c5 perf+a11y: WAL mode for SQLite, skip links, :focus-visible, .sr-only
SQLite performance (Database::__construct):
- PRAGMA journal_mode = WAL: eliminates full-DB read locks on write, safe
  for concurrent PHP-FPM workers
- PRAGMA synchronous = NORMAL: durable on commit without full fsync per write
- PRAGMA cache_size = -8000: ~8 MB page cache per connection

Accessibility foundation (WCAG 2.1 AA):
- common.css: add .sr-only utility, .skip-link (hidden until focused),
  global :focus-visible (2px purple outline, 2px offset),
  prefers-reduced-motion guard; remove bare outline:none from
  .site-search__input
- admin.css: same :focus-visible, skip-link, and motion guard scoped to
  admin purple; remove outline:none from .admin-input/.admin-select/
  .admin-textarea and .admin-filters select (both had :focus border rules
  already, so focus is still visually communicated)
- search.css: remove outline:none from .search-filter-select (already has
  :focus border-color rule)
- All 5 public pages (index, search, tfe, apropos, licence): add
  <a href="#main-content" class="skip-link"> as first child of <body>;
  add id="main-content" to <main>
- templates/admin/head.php: same skip link; aria-label="Navigation admin"
  on <nav>; id="main-content" on all 10 admin <main> elements

All 4 test suites pass (unit, integration, security, rate-limit).
2026-03-27 13:45:01 +01:00
Pontoporeia
e4be230a04 admin/system: add nginx config viewer tab
Add a 'nginx — config' tab to the Système admin page (system.php).

- Reads /etc/nginx/sites-available/posterg (live deployed config) first;
  falls back to nginx/posterg.conf (local reference copy) when the live
  path is inaccessible (e.g. in dev, or wrong permissions).
- Displays a colour-coded badge: green '● Config déployée' for live,
  amber '⚠ Référence locale' for the fallback.
- Renders the full config in the shared .log-output code block with
  line numbers (data-n gutter via CSS ::before) and lightweight nginx
  syntax colouring (comments grey, block keywords purple, directives blue).
- Reuses the existing copy-to-clipboard button.
- Tab routing: activeTab validation extended to accept 'nginx_config';
  log pre-loading guards skip when activeTab is 'nginx_config'.
- No remote execution: read-only, zero new attack surface.
2026-03-26 11:23:18 +01:00
Pontoporeia
37f3a07c6e admin: merge status + logs into unified system.php with instant tabs
Replace the separate /admin/status.php and /admin/logs.php pages with a
single /admin/system.php page organised around a tab bar.

- system.php — top-level tab bar: 'Statut' + one tab per log file
  (nginx accès, nginx erreurs, PHP-FPM).  Switching tabs is a plain
  href (?tab=…) so no JS required for navigation; the lines-selector
  SELECT triggers a location change on 'change' for instant reload
  without a submit button.
- Status tab preserves all existing service cards, PHP runtime grid,
  and disk-usage bar from the old status.php.
- Log tabs preserve line-count selector, file metadata bar, and
  per-line colour coding from the old logs.php.
- New: copy-to-clipboard button on each log output block (Clipboard
  API with textarea execCommand fallback).
- status.php / logs.php replaced with 301 redirect stubs so existing
  bookmarks and links keep working.
- templates/admin/head.php: 'Statut' + 'Journaux' nav items replaced
  with a single 'Système' item; active state covers all three page
  names for redirect compatibility.
2026-03-24 15:55:48 +01:00