Files
xamxam/TODO.md

9.0 KiB
Raw Blame History

TODO

HTMX v2 Migration

Reference: docs/autosave-system.md → "HTMX v2 Migration Plan" section.

  • contenus-edit.php (pages): Add hx-* attrs, add overtype:change dispatch in OverType onChange
  • contenus-edit.php (form_help): Add hx-* attrs, add overtype:change dispatch in OverType onChange
  • apropos-groups-form.php (contacts): Add hx-* attrs only
  • contenus-edit.php (sidebar_links): Add hx-* attrs only
  • Add handleAutosaveResponse() shared handler + htmx:beforeRequest loading state
  • Delete autosave.js
  • Fix backend $isAjax detection: also recognize HX-Request header (page.php, apropos.php, form-help.php)
  • Form-help inline editors: add OverType toolbar + HTMX auto-save + remove save buttons
  • Markdown cheatsheet modal: reusable dialog on all OverType editors

FilePond crash on TFE upload forms

  • Analyze root cause → docs/filepond-crash-analysis.md
  • Partial fixes (Content-Type headers, onerror cleanup, load object) — insufficient, crash still reproduces
  • HTMX/destroy race hypothesis investigation → docs/filepond-race-investigation.md (verdict: REFUTED)
  • Diagnostic probes + deep analysis: confirmed load-file-error dispatch path, traced via error.stack to fileValidateSizeFilter line 389
  • ROOT CAUSE FIXED: fileValidateSizeFilter accessed item.filename but FileValidateSize's LOAD_FILE filter passes the raw File/Blob (which has .name, not .filename). Changed to item.filename || item.name. Also added null guard to getExt().
  • Defensive: Wt and Fr crash guards in filepond.min.js prevent action.status.main crash
  • process.onload: replaced throw with error-marker return (prevents FilePond crash when server returns HTML)
  • Routing: partage index.php now routes /partage/actions/* directly to PHP files (was treating 'actions' as a slug and returning full HTML page)
  • All crashes resolved — verified working on partage form

Form Accessibility & Resilience — Assessment Follow-up

Reference: Assessment against progressive-enhancement / WCAG-AA / "never lose data" / low-common-denominator guidelines.

1. WCAG AA: Add field-level aria-errormessage + aria-invalid to TFE form

Current state: Flash error divs have role="alert" but individual fields are never linked to their error via aria-errormessage. The autofocusFieldForError() mechanism focuses the field after a validation redirect but does not announce the error to screen readers. Help <small> text is not linked via aria-describedby.

To do:

  • Extend text-field.php, select-field.php, checkbox-list.php partials to accept optional $error and $errorId variables
  • When $error is set, add aria-errormessage="$errorId" and aria-invalid="true" on the input
  • On validation redirect, populate per-field error IDs so each failing field references the flash error container
  • Add aria-describedby linking each <small> hint to its input (always, not just on error)
  • Test with VoiceOver and NVDA on the full add/edit/partage form flows

2. No-JS file uploads silently fail (data loss)

Current state: form.php hardcodes <input type="hidden" name="filepond_mode" value="1">. Without JS, no queue_file[] hidden inputs are populated → server gets filepond_mode=1 with empty queue → all files silently dropped. The form is supposed to work without JS.

To do:

  • Change the hidden input to <input type="hidden" name="filepond_mode" value="0" disabled> by default; JS enables it and sets value="1" on DOMContentLoaded
  • Add server-side fallback in ThesisCreateController::submit() and ThesisEditController::save(): when filepond_mode=1 but no queue_file data is present, fall through to the legacy $_FILES path
  • Test end-to-end: submit the partage form with JS disabled, verify files arrive via $_FILES

3. Autosave text fields on partage form

Current state: Only FilePond files are persisted to the server before submit. Text/select/checkbox fields live only in DOM and are lost on tab close / crash / network failure. beforeunload-guard.js warns but cannot recover data. The admin panel already has an HTMX-based autosave pattern (contenus-edit.php).

To do:

  • Implement a per-session draft endpoint (POST /partage/<slug>/draft) that saves form data to a session-backed store
  • On field blur/change, POST the field name+value via HTMX (debounced 500ms)
  • On page load, hydrate all fields from the draft endpoint
  • Draft is cleared on successful submission; expires after 24h with session
  • Add a visible "Brouillon enregistré" indicator to reassure the student

4. FilePond temp file IDs lost on partage validation redirect

Current state: After a validation error, storePrimedFiles() shows file names so the user knows what to re-select. But the actual FilePond hex IDs (server-side temp files) are not preserved — the user must re-upload everything. For large files on slow connections, this is painful.

To do:

  • After a validation error in the partage form, preserve FilePond temp file IDs in $_SESSION['share_filepond_ids_' . $slug] by queue type
  • Use these IDs to build the data-existing-files JSON attribute on the re-rendered form, seeding the FilePond pools
  • Ensure the temp files' server-side lifetime covers at least one validation round-trip (extend FilepondHandler TTL if needed)

5. Two-phase commit: protect against crash during file moves

Current state: DB transaction commits, then files are moved from tmp to storage. If the process crashes between COMMIT and file move completion, the DB has a thesis record with no files (or partial files). This is a public-facing service — no acceptable failure rate.

To do:

  • Add an is_published or status flag to theses table (if not already present)
  • In the partage submit flow: INSERT thesis with status='draft' inside the transaction → COMMIT → move files → UPDATE status='active'
  • On partage submission error recovery, roll back draft theses that are older than N minutes with no files
  • Add a periodic cleanup job (just cleanup-drafts) for orphaned drafts

6. Mobile-responsive form layout

Current state: Form field rows use grid-template-columns: 260px 1fr with no breakpoint. Below ~520px viewport width, labels wrap and inputs are cramped.

To do:

  • Add @media (max-width: 600px) rule in form.css switching to grid-template-columns: 1fr with labels stacked above inputs
  • Test on 320px wide viewport (PSP, low-end Android)
  • Ensure touch targets are at least 44×44px (WCAG 2.5.5)

7. Split form.css: shared base vs admin-only styles

Current state: form.css is 36KB (1,500+ lines). The partage form loads all of it but uses only ~40% (base input/fieldset styles). Admin-specific styles (.admin-jury-*, .admin-backoffice-*, etc.) are dead weight for students.

To do:

  • Split form.css into form-base.css (~15KB: inputs, selects, textareas, fieldsets, legends, validation states, flash messages, aria-invalid styling) and form-admin.css (~21KB: admin layout, jury fieldset, backoffice, tag pills, autocomplete)
  • Load form-base.css in the partage form; load both in admin forms
  • Update head.php to support $extraCssAdmin for admin-only stylesheets

8. Fix aria-required on <fieldset> (standards compliance)

Current state: checkbox-list.php and fichiers-fragment.php put required aria-required="true" on <fieldset> elements. The required attribute is not valid on <fieldset>. Screen readers may not interpret this correctly.

To do:

  • Move required + aria-required to individual checkboxes inside the group
  • For checkbox groups (languages, formats): mark at least one checkbox as required, keep aria-required="true" on the fieldset (without the HTML required attribute)
  • Add role="group" on the fieldset for explicit ARIA semantics

9. Refactor partage form page wrapper to a template

Current state: renderShareLinkForm() in partage/index.php outputs an entire <html> document inline (~120 lines of HTML inside a PHP function). This duplicates head.php/header.php logic.

To do:

  • Extract the partage form page chrome to templates/partage/form-page.php
  • Use App::render() or a dedicated renderPartageForm() helper
  • Share meta tags, favicon references, CSRF meta, and live-reload script from head.php

10. Reduce form bootstrap duplication across 3 entry points

Current state: admin/add.php, admin/edit.php, and partage/index.php (inside renderShareLinkForm()) each repeat ~4060 lines of identical setup: load ThesisCreateController, call loadFormData(), build $helpFn, load site settings, set up jury/default access variables, etc.

To do:

  • Add a ThesisFormSetup helper class (or static method on the controllers) that returns a standardised array of form variables
  • Each entry point becomes: $formVars = ThesisFormSetup::prepare($mode, $slug); extract($formVars); include '...form.php';