- nginx/docs/PRODUCTION_DEPLOYMENT.md: drop stale htpasswd/Basic-auth model,
manual server-side deploy step, /var/www/posterg paths and hardcoded IP; describe
the PHP-layer AdminAuth, automated 📋 Deploying nginx configuration…
xamxam.conf
sent 145 bytes received 125 bytes 540.00 bytes/sec
total size is 10,012 speedup is 37.08
deploy-server.sh
sent 143 bytes received 113 bytes 512.00 bytes/sec
total size is 8,519 speedup is 33.28, and what deploy-server.sh
actually does.
- nginx/docs/QUICK_REFERENCE.md: replace htpasswd user management with AdminAuth
guidance; fix php8.2->php8.4, log paths, admin rate limit (300r/m), socket path.
- nginx/README.md, nginx/SETUP.md: note 📋 Deploying nginx configuration…
xamxam.conf
sent 145 bytes received 125 bytes 540.00 bytes/sec
total size is 10,012 speedup is 37.08
deploy-server.sh
sent 143 bytes received 113 bytes 170.67 bytes/sec
total size is 8,519 speedup is 33.28 automates
deploy-server.sh (no manual ssh/sudo step).
- docs/deployment.md: correct deploy-code scope (code-only, no --chown), fix the
/var/log/xamxam provisioning step (deploy-server.sh via deploy-nginx, not
deploy-code), document the setup-server.sh prerequisite and fresh-box caveat.
- README.md: replace the incomplete manual first-time setup snippet with the
real setup-server.sh + provision-server flow and a fresh-box caveat.
Replace the eager <img> on the home page with an htmx placeholder <figure>
that fetches a /cover-fragment endpoint when it scrolls into view
(hx-trigger="revealed"), so heavy cover bytes load only on demand.
Add spinner + settle-fade transition CSS, and load htmx.min.js on home.
Ajoute une fonction SQLite UDF accfold() (app/src/AccentFolding.php) qui plie les caractères accentués latin vers leur base ASCII (é→e, ç→c, …), et l'applique à chaque condition de recherche côté colonne ET côté terme (accfold(column) LIKE accfold(:term)). Comme le runtime n'a ni intl (Normalizer) ni iconv, le repli passe par une map de translitération manuelle (Western Latin-1 + Latin Extended-A, NULL-safe).
Couvert :
- recherche publique (searchTheses / countSearchResults) : titre, sous-titre, synopsis, auteurs, promoteurs, tags ;
- popover étudiant (getThesesByAuthorName / getThesesForAuthors) : a.name ;
- recherche backoffice (getThesesList / getThesesListCount) : titre, sous-titre, identifiant, auteur.
L'UDF est enregistré dans Database::registerSqliteFunctions() (appelé par le constructeur et par le harnais de test TestDatabaseInstance). Évite aussi la dépréciation PHP 8.5 de PDO::sqliteCreateFunction() : la connexion est créée via \Pdo\Sqlite quand disponible (createFunction()) avec repli sur PDO (sqliteCreateFunction()) sur les anciens runtimes ; les tests passent de @dataProvider docblock à l'attribut #[DataProvider] (fin de la dépréciation PHPUnit). Détache .php-cs-fixer.cache (gitignoré) pour ne plus polluer le working copy.
294 tests PHPUnit verts sans dépréciation, phpstan niveau 5 OK.
- fins de ligne \r\n/\r → \n,
- blocs de lignes vides → \n\n, è
- espaces/tabulations/NBSP → espace unique,
- trim.
Les retours à la ligne simples intra-paragraphe sont LAISSÉS INTACTS :
une coupure de ligne peut être une vraie frontière de mot (→ espace)
ou une coupure du mot ("dyna\nmiques" → "dynamiques"),
indistinguables sans dictionnaire — automatiser corromprait l'entrée
("poursuivantsur").
Migration batch 045 (backfill des données existantes) RETIRÉE : le
nettoyage automatique fiable des synopsis existants est impossible.
Chaque modification est testée (277 tests PHPUnit).
(heure de dépôt was showing UTC)
- feat: add date_depot column (real TFE deposit date)
with CSV round-trip + Brussels→UTC sanitization
- fix: keep PHP default tz at UTC to preserve token/share-link
expiry consistency; convert to Brussels only in db_datetime()
.tfe-main (main) is a block container by default, so .tfe-layout's
height:100% and .tfe-content-row's flex:1 never resolved to a
definite height. The columns' overflow-y:auto thus had nothing to
constrain, and main's overflow:hidden clipped the content — scroll
was visually broken. Adding display:flex lets the column/height
chain resolve so both columns scroll independently as intended.
- Move 12 historical/superseded docs + 1 session log + 1 PDF + 1 HTML plan to archive/
- Merge 4 VM-crash docs into archive/vm-crash-incident.md
- Merge LDAP plan + spec into ldap.md
- Merge FilePond race investigation into filepond-crash-analysis.md
- Merge SPECS.md client notes into spec-sheet.md appendix
- Update README index and security.md cross-reference
- admin parametres: move Compte administrateur into Identifiants as a fieldset
- admin parametres: space the account fieldset and drop confirm-password top border
- admin parametres: tighten param-form row spacing, relabel password button
- admin parametres: move password submit button outside the account fieldset
Clicking orientation, AP program, finality, year, format, or keyword links
on the TFE page was passing both a dedicated filter param AND a query=
param. The query= text search doesn't look at the filter columns
(orientation, ap_program, etc.), so the AND combination yielded zero
results when the filter value didn't appear in title/authors/etc fields.
Also fix the language link: it was passing language as query= which
searches only title/authors/etc, not the languages column. Now uses
the dedicated language= param (DB layer already supported it, and
collectSearchParams now collects it).
FilePond 4.x wraps the original input element in its own DOM structure
which can break CSS attribute selectors. querySelector with
.tfe-file-picker[data-queue-type='...'] returned null even though the
element existed and was initialized.
Switched to getElementById with a queueType→inputId map:
cover → couverture
note_intention → note_intention
tfe → tfe-files-input
annexe → annexe-files-input
getElementById is both faster and immune to FilePond's DOM wrapping.
Switched from pond.addFile(type:'limbo') to pond.addFiles([{source, options:{type:'local'}}])
in both XamxamRelinkFile and XamxamRelinkPeerTube.
addFile with type 'limbo' doesn't trigger server.load to fetch the file
for the preview/thumbnail. The data-existing-files format (type:'local')
correctly tells FilePond to call server.load for the file identified by
the source (DB ID), rendering it properly in the pool.
- Single unified 'Corbeille' table replaces the two separate lists
(stale/orphelin + restorable). Each row has a Statut column showing
'↺ Restaurable' or 'Orphelin', plus a checkbox for bulk ops.
- Checkboxes on restorable rows carry data-restorable='1' attribute
so the bulk JS can distinguish them.
- Bulk actions bar now has both 'Supprimer la sélection' and
'↺ Restaurer la sélection' buttons. The restore button only appears
when at least one restorable item is checked.
- New cleanupBulkRestore() JS function populates a dedicated hidden
form and confirms before submitting. Only restorable items are sent.
- restore-trash.php refactored to handle both single (trash_file) and
bulk (trash_files[]) inputs via a loop, accumulating restored/skipped
counts and re-rendering the fragment on HTMX requests.
Two critical fixes:
1. Relink flow no longer destroys/recreates FilePond instances:
The relink (XamxamRelinkFile) and PeerTube relink (XamxamRelinkPeerTube)
previously refreshed the entire fichiers fragment via HTMX after
pond.addFile(). This triggered destroyFilePondsIn on ALL pools, which
could fire server.remove callbacks and move existing files to corbeille.
Now just closes the modal — the file is already added to the pool in-place,
and syncOrderInput creates the hidden form input.
2. Cleanup page « Corbeille (restaurable) » now actually shows files:
_cleanup-stats-data.php previously classified trash files by checking if
the thesis_files DB row still existed. But both deleteThesisFileToTrash
and FilepondHandler::handleRemove DELETE the DB row. So ALL trash files
appeared as `stale` (not restorable). Now uses the JSON sidecar file
presence as the classification criterion — if the sidecar exists and is
recent, the file is restorable regardless of DB row state.
Also removed unused DB query from _cleanup-stats-data.php.
Wrap .log-output and the copy button in a .log-output-wrapper div
with position:relative, so the button sits outside the overflow-x:auto
scroll container and stays fixed at the top-right corner.