Commit Graph

334 Commits

Author SHA1 Message Date
Pontoporeia
507f3eb704 Consolidate nginx docs and scripts, update paths 2026-04-15 14:24:44 +02:00
Pontoporeia
3cd96ed28a Deduplicate and standardise documentation
- Consolidate 36 markdown files → 14 (plus TODO.md)
- Merge overlapping docs into authoritative files:
  - database.md (from DATABASE_SPECIFICATION + QUICK_SCHEMA_REFERENCE + DATABASE_CONFIG + SETUP)
  - deployment.md (from SERVER_SETUP + COMPLETE_DEPLOYMENT_GUIDE + DEPLOYMENT_STEPS)
  - security.md (from SECURITY_ANALYSIS + TODO.SECURITY)
  - development.md (from DEVELOPMENT_GUIDE + LIVE_RELOAD_SETUP + TEST_CENTRALIZATION)
  - migration-history.md (from 11 past migration docs)
- Standardise all filenames to lowercase
- Remove non-doc files (Context.md research notes, chat export)
- Remove superseded docs (SECURITY.md pre-SQLite, SECURITY_IMPLEMENTATION, README_SECURE_SEARCH)
- Fix stale cross-references
2026-04-15 14:24:44 +02:00
Théophile Gervreau-Mercier
5c5054d744 Investigating VM crash 2026-04-13 11:12:12 +02:00
Pontoporeia
0c29fa21e9 Prevent admin nav wrapping to match public header height 2026-04-09 14:51:43 +02:00
Pontoporeia
c5c049eace Move public search bar below header so admin and public headers have same height 2026-04-09 14:37:49 +02:00
Pontoporeia
1885f2da92 Replace random HSL gradients in homepage cards with header-style gradient; header keeps its own CSS-variables gradient 2026-04-09 14:37:00 +02:00
Pontoporeia
aa3fc50d92 use exact hard-coded gradient on header and card placeholders 2026-04-09 14:34:59 +02:00
Pontoporeia
a13e73aed3 Replace random HSL gradients in homepage cards with header gradient 2026-04-09 14:29:17 +02:00
Pontoporeia
07f0afde25 cache-bust: add filemtime-based versioning to all CSS and JS assets 2026-04-09 14:08:19 +02:00
Pontoporeia
424f79c819 typography: switch display font from Combined to Ductus 2026-04-08 18:09:35 +02:00
Pontoporeia
a333a5fdad Rebrand: replace PostErg with XAMXAM in admin header link, default title, and OG site_name 2026-04-08 18:04:35 +02:00
Pontoporeia
18045af243 favicon: replace SVG placeholder with full PNG/ICO set from assets/favicon/ 2026-04-08 18:01:41 +02:00
Pontoporeia
d68645f1b1 style: retheme system page to site light palette — remove dark --sys-* tokens 2026-04-08 17:49:50 +02:00
Pontoporeia
df414346e9 fix: SystemController php-fpm detection — probe phpX.Y-fpm from running PHP version first 2026-04-08 17:46:42 +02:00
Pontoporeia
7117934d07 fix: replace mb_strlen/mb_substr with strlen/substr — mbstring not available on prod 2026-04-08 17:42:01 +02:00
Pontoporeia
9eec5d3ac0 SPECS.md 2026-04-08 16:03:31 +02:00
Pontoporeia
49b113319a Add AP filter to admin list; fix reset as unstyled button 2026-04-08 15:22:39 +02:00
Pontoporeia
4199b206db Move CSV import to inline dialog on list page 2026-04-08 15:16:10 +02:00
Pontoporeia
603af07b68 Add Paramètres page: consolidate maintenance + account settings 2026-04-08 15:06:51 +02:00
Pontoporeia
ba135f0cb5 fix: replace 'Xamxam' with 'Posterg' in public nav header 2026-04-08 14:14:37 +02:00
Pontoporeia
f6977384b9 migrate to utopia fluid type and space scales across all CSS 2026-04-08 14:14:37 +02:00
Pontoporeia
ad06bbbcaf bump all font-size values ~10% across all CSS files 2026-04-08 14:14:37 +02:00
Pontoporeia
0c2276d5ad Split search into search.php; repertoire.php is index-only 2026-04-08 14:14:37 +02:00
Pontoporeia
e96ec572be tfe: hyperlink metadata values to repertoire.php with correct filter params 2026-04-08 14:14:37 +02:00
Pontoporeia
55c6ac21b8 fix tfe page: scope common.css header to body>header, fix grid width collapse, remove overflow-y clip 2026-04-08 14:14:37 +02:00
Pontoporeia
3a1cd5b43e tfe page: author above title, interne/externe jury split, rounded images, strip contact protocol 2026-04-08 14:14:37 +02:00
Pontoporeia
547d581e26 Removed footer navbar 2026-04-08 14:14:37 +02:00
Pontoporeia
11a665e096 Improve À propos page layout: sticky TOC nav, bordered contact rows, credits dl grid 2026-04-08 14:14:37 +02:00
Pontoporeia
dddfc8554b fonts: add --font-body/--font-display vars, use them everywhere 2026-04-08 14:14:37 +02:00
Pontoporeia
797eaf87d1 Apply correct fonts: Combined for titles, BBBDMSans for body text 2026-04-08 14:14:37 +02:00
Pontoporeia
572ef75a1e répertoire: rename search.php, 6-column layout, HTMX filter, faded entries disabled, URL-shareable 2026-04-08 14:14:37 +02:00
Pontoporeia
088324cb80 Match Accueil.png mockup: nav layout, full-width search, section label 2026-04-08 14:14:37 +02:00
Pontoporeia
8b27acec27 fix homepage card grid styling: gaps, borders, rounded corners, gradient aspect-ratio 2026-04-08 14:14:37 +02:00
Pontoporeia
0bfb24723f fix(font): rebuild Combinedd.otf with sorted kern pairs to fix browser discard 2026-04-08 14:14:37 +02:00
Pontoporeia
b8e94f1b6b fix(css): add format hint + descriptors to @font-face for police1/Combinedd.otf 2026-04-08 14:14:37 +02:00
Pontoporeia
b45e6c50cc fix: admin CSP allow inline scripts
script-src 'self' 'unsafe-inline' added to admin Content-Security-Policy.
default-src 'self' was blocking OverType editor init block and
the dev live-reload poller. Admin section is auth-gated so
unsafe-inline is acceptable.
2026-04-08 14:14:37 +02:00
Pontoporeia
e6960f0c9c fix: RateLimit permission denied — code + deploy scripts
RateLimit.php:
- Silence mkdir() with @ operator
- Guard file_put_contents with is_writable() check (graceful degrade)

scripts/deploy-server.sh + setup-server.sh:
- mkdir -p storage/cache/rate_limit on every deploy
- chown www-data:posterg + chmod 2775 on storage/cache/
  so php-fpm can always write rate limit files
2026-04-06 16:45:14 +02:00
Pontoporeia
756ddb5765 fix: RateLimit graceful degradation on permission denied
Silence mkdir() with @ operator; guard file_put_contents with
is_writable() check. When storage/cache/rate_limit is not writable
by php-fpm, requests are allowed through instead of throwing
warnings that flood the nginx error log.
2026-04-06 16:40:55 +02:00
Pontoporeia
6a1b41ac93 css: remove dark mode, unify token system, eliminate all hardcoded colors
- Remove @media (prefers-color-scheme: dark) block from variables.css
- Delete colors.css (dead reference doc, never loaded)
- Add semantic tokens to variables.css:
    --header-gradient-fade, --header-shadow-strong/soft, --header-nav-active-border
    --search-error-bg/border/color
    --sys-bg-surface/deep/panel, --sys-border/border-deep/border-hover
    --sys-text-dim/bright/body, --sys-overlay, --sys-syntax-* (7 highlight tokens)
    --success/warning/error/blue/yellow/green -muted-bg/border/hover alpha overlays
    --danger-border-muted
- Replace all hardcoded hex/rgba in common.css, main.css, search.css, admin.css, system.css
- Fix --border-color typo -> --border-primary in search.css
- Fix view-toggle__btn active color: --text-primary -> --accent-foreground
- Admin and public share identical token set, no separate admin theme
- Update README.md
2026-04-06 16:16:11 +02:00
Pontoporeia
e73fcfd0c8 fix: drop curl_close() call (deprecated PHP 8.5, no-op since 8.0) 2026-04-06 16:11:34 +02:00
Pontoporeia
9a9dfd2b9e fix(apropos): replace straight apostrophe in l'ERG role string (parse error) 2026-04-06 15:34:06 +02:00
Pontoporeia
f6e2c77d1a Adjusting admin static pages edit page 2026-04-06 15:33:26 +02:00
Pontoporeia
480451aa2b replace EasyMDE (333KB) with OverType (118KB) in pages-edit.php
- Remove easymde.min.js (320KB) and easymde.min.css (13KB)
- Vendor overtype.min.js (118KB, v2.3.5)
- Replace <textarea name=content> + 60-line toolbar/SVG init with:
  - <input type=hidden name=content> for form submission
  - <div id=editor> as OverType mount target
  - 6-line init: value from hidden input, onChange syncs it back
- Net saving: ~215KB assets, ~54 lines of inline JS
2026-04-06 15:33:08 +02:00
Pontoporeia
d51cd62088 Extract last inline style from header.php into admin.css
The SVG icon in the admin nav's public-site link had two inline styles:
  style="vertical-align:middle;margin-right:0.4em"

Moved to a new CSS rule:
  .admin-body header nav > a svg { vertical-align: middle; margin-right: 0.4em; }

templates/header.php now contains zero style= attributes.
The only remaining inline styles project-wide are:
  - dynamic gradient (hsl computed from $item['id']) in public/index.php — legitimately dynamic
  - --disk-pct/--disk-color custom properties in system.php — carry PHP runtime values
2026-04-06 15:33:08 +02:00
Pontoporeia
a60e742d1a Extract last 3 inline styles from admin templates into CSS classes
admin/thanks.php:
- <div style="margin-top:1.5rem;display:flex;gap:.75rem;flex-wrap:wrap;"> → class="admin-action-bar"
- <p style="color:var(--text-secondary);"> → class="admin-muted"

admin/pages.php:
- Éditer button style="font-size:.8rem;padding:.3rem .75rem;" → class="admin-btn admin-btn--sm"

admin.css (Thesis info sections block):
- Added .admin-action-bar { margin-top:1.5rem; display:flex; gap:0.75rem; flex-wrap:wrap }
- Added .admin-muted { color: var(--text-secondary) }

The only remaining inline style in any admin PHP file is the dynamic
--disk-pct/--disk-color custom properties on the disk bar in system.php,
which carry PHP runtime values and cannot be moved to static CSS.
2026-04-06 15:33:08 +02:00
Pontoporeia
ca8081575c Add prefers-color-scheme dark mode for public pages
Scope: variables.css, search.css, todo/04-accessibility.md

- variables.css: add @media (prefers-color-scheme: dark) block scoped to
  body:not(.admin-body); overrides all semantic tokens with dark equivalents:
  --bg-* (#111→#333 range), --text-* (#eee/aaa/777),
  --border-* (#333/#444), --accent-primary lightened to #b87fd4
  (4.5:1 contrast on #111 background), --accent-secondary stays #9557b5,
  --accent-foreground flipped to #111111 for dark buttons,
  --accent-muted adjusted to rgba(184,127,212,0.15),
  status colours muted for dark (success #4db886, error #e05555,
  warning #d4a830); new --search-error-{bg,border,color} tokens added
  to :root (light: #fff0f0/#c00) and overridden in dark (#2a1515/#e05555)

- search.css: replace three hardcoded hex values in .search-error rule
  with var(--search-error-bg/border/color) so dark mode applies cleanly

- Admin pages are entirely unaffected: .admin-body body class is excluded
  from the dark-mode selector; system.css already has its own dark palette
2026-04-06 15:33:08 +02:00
Pontoporeia
2841e05716 Extract ThesisCreateController; add Database publish methods
Consolidate action handlers into controller methods (todo/02-php-components.md).

src/ThesisCreateController.php (new, 435 lines)
  Mirrors ThesisEditController for the add-thesis flow.

  make()           — factory; instantiates Database via new Database()
  loadFormData()   — returns all lookup tables needed by admin/add.php
                     (orientations, apPrograms, finalityTypes, languages,
                      formatTypes, licenseTypes)
  submit(post, files) — full new-thesis creation pipeline:
    1. validateAndSanitise() — trims/strips HTML, validates required fields,
       year range, orientation/ap/finality IDs, language selection, max-10
       keywords, URL format; throws named Exception on failure
    2. findOrCreateAuthor() — reuses existing DB method
    3. Transaction: createThesis + setThesisJury + setThesisLanguages +
       setThesisFormats + setThesisTags; rolls back on any failure
    4. File uploads outside transaction: cover image (JPG/PNG only, stored in
       storage/covers/), banner via handleBannerUpload(), thesis files
       (PDF/JPG/PNG/MP4/ZIP/VTT, stored in storage/theses/YEAR/IDENT/,
       file_type auto-detected: caption/annex/main/other)
  autofocusFieldForError() — static; maps exception messages to field names
    for WCAG 3.3.1 autofocus on re-render (same contract as
    ThesisEditController::autofocusFieldForError)

admin/actions/formulaire.php  346 → 45 lines
  Now: bootstrap + CSRF guard + ThesisCreateController::make()->submit() +
  flash/redirect on error. All validation, DB logic, and file handling removed.

admin/add.php
  Lookup-table block (new Database() + 6 individual DB calls) replaced with
  ThesisCreateController::make()->loadFormData() + extract().

src/Database.php — two new methods added
  setPublished(int , bool ): void
    UPDATE theses SET is_published = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?
  bulkSetPublished(int[] , bool ): void
    Same but with an IN (...) clause for multiple IDs

admin/actions/publish.php  100 → 65 lines
  Raw SQL (->prepare('UPDATE theses SET is_published = ?...')) replaced
  with ->setPublished() / ->bulkSetPublished(). No raw PDO calls remain
  in any action handler file.
2026-04-06 15:33:08 +02:00
Pontoporeia
b1e70a2bf1 Extract HomeController from public/index.php
Move all data-fetching and view-variable assembly out of public/index.php
into a new src/HomeController.php, following the same pattern as
SearchController, TfeController, SystemController, and ThesisEditController.

HomeController::create() builds the Database singleton dependency.
HomeController::handle() encapsulates:
- GET param parsing (page, year) with safe type coercion
- Display-mode detection: default random-latest view / year-filtered /
  paginated-all theses
- All DB calls: getLatestPublishedYear, getLatestYearTheses, searchTheses,
  countSearchResults, getPublishedTheses, countPublishedTheses,
  getCoverPathsForTheses, getAvailableYears
- Batch cover-image loading for theses without a banner_path
- baseParams assembly for the pagination partial
- OG / meta tag array construction
- Graceful error handling (logs exception, returns safe empty state)
- Returns a flat array of view variables

public/index.php is now a 6-line dispatcher (require + create + handle +
extract) followed by a pure view template. Reduced from 100 to 71 lines.
All error-handling and data logic removed from the view layer entirely.
2026-04-06 15:33:08 +02:00
Pontoporeia
89067a521f Extract TfeController from public/tfe.php
src/TfeController.php (new, 195 lines):
- Dedicated controller for the public TFE detail page
- create(): Database singleton injection, ready-to-use factory
- handle(): validates id param (redirect to index.php on missing/invalid/404),
  loads thesis via getThesisById(), fetches access type via getThesisAccessTypeId()
- buildMetaDescription(): strip_tags + 160-char mb_substr truncation
- resolveOgImage(): banner_path → first image file → empty string resolution
- buildOgTags(): full og:type/title/description/url/image/image_alt/site_name +
  article:author / article:published_time assembly
- collectCaptionPaths(): ordered list of VTT paths for N-th-video pairing
- returns flat array of all view variables including ogTags, captionFiles,
  pageTitle, metaDescription, isInterdit, bodyClass, extraCss, currentNav

public/tfe.php (271 → 206 lines):
- Reduced to 9-line dispatcher: require TfeController, create(), handle(), extract()
- $db reference removed from view layer entirely
- Inline OG tag block (~20 lines) removed
- Inline meta-description block (~5 lines) removed
- Inline caption-collection loop (~10 lines) removed
- $captionFiles replaces $_captionFiles in the video pairing section

todo/02-php-components.md:
- TfeController extraction marked done
- 'Move OG tag construction into controller logic' marked done
- Remaining item narrowed to public/index.php home-page controller
2026-04-06 15:33:08 +02:00
Pontoporeia
41629398d3 Extract ThesisEditController from admin/edit.php and actions/edit.php
src/ThesisEditController.php (285 lines) centralises all data-fetching and
mutation logic for the thesis-edit workflow:

  load(int $thesisId): array
    Fetches the thesis row, current language/format/jury selections, and all
    lookup tables (orientations, AP programmes, finality types, languages,
    formats, licences, access types) in one call.  Returns a flat view-variable
    array that the dispatcher extracts directly.

  save(int $thesisId, array $post, array $files): void
    Runs the full edit inside a transaction: thesis metadata, authors, jury,
    languages, formats, tags.  Banner upload/removal is handled outside the
    transaction (filesystem op).  Rolls back and re-throws on any failure.

  static autofocusFieldForError(string $msg): ?string
    Centralises the WCAG 3.3.1 exception-message → field-name mapping that
    was previously duplicated inline in actions/edit.php.

Dispatcher changes:
  admin/edit.php      191 → 162 lines  (pure view + ThesisEditController::create() + load())
  actions/edit.php    153 →  53 lines  (CSRF guard + ThesisEditController::save() call)

Follows the same pattern as SearchController and SystemController.
2026-04-06 15:33:08 +02:00