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.
- Override aggressive word-break: break-word from base.css
with word-break: normal + overflow-wrap: break-word + hyphens: auto
on .tfe-title, .tfe-author, .tfe-meta-item
- Replace regular spaces before colons with in all meta labels
(French typographic rule: non-breaking space before double punctuation)
- Rebuild tfe.min.css
Log level: production default was Level::Warning, but all facades
(AppLogger, AdminLogger, Audit) write at Monolog INFO level. This
silently discarded submission, admin action, and audit logs when
LOG_LEVEL env var was unset. Changed to Level::Info.
PeerTube: replaced raw error_log() calls in PeerTubeService::upload(),
deleteVideo(), FilepondHandler::process(), ThesisCreateController,
and ThesisFileHandler with structured logging:
- Successes → Logger::get('app')->info() (visible in App — soumissions tab)
- Failures → ErrorHandler::log('peertube_*', ...) (visible in Erreurs tab)
Added require_once for Logger and ErrorHandler in PeerTubeService.
redirect internal metadata links from /repertoire to /search, add query param for search bar prefill, fix filter visibility on desktop
redirect metadata links from /repertoire to /search, prefill search bar, fix filter visibility
link metadata to /search, preserve keyword filter in form, show active keyword chip
Replace var(--step-5)/--step-3/--step-1 for h1/h2/h3 with explicit
clamp() values. New hierarchy: h1 ~2rem, h2 ~1.5rem, h3 ~1.2rem.
Also update .content-section-title to use direct clamp (was --step-3,
now matches h2). Mobile override swapped from --step-2 to explicit
clamp. Kept --step-0 for body text (consistent with site baseline).
The openEditDialog JS function didn't populate the locked_year input
in the edit dialog, and the PHP template didn't pass locked_year as an
argument. Any edit to a link (even just changing the name) would clear
the locked academic year to NULL.
- Pass $linkLockedYear as 5th argument to openEditDialog in the template
- Accept and populate edit-locked-year input in the JS handler
- Fix createThesis SQL: extra ? placeholder in VALUES (27 values for 26 columns)
- Add createThesis integration tests to catch column/value mismatches
- Add CSV_COLUMNS as single source of truth in ExportController, deriving
csvHeaders() and positional fallback from it
- Add missing columns to export query + CSV: duration_pages, duration_minutes,
has_annexes, license_custom, contact_visible, objet
- Add missing columns to import INSERT: license_id, license_custom, cc2r,
exemplaire_baiu, exemplaire_erg, objet, contact_visible, duration_pages,
duration_minutes, has_annexes
- Resolve license name → license_id during import
- Fix XamxamInitFilePonds: add file-upload-filepond.js to admin-entry.js
so FilePond initialization code is available on the admin list page
- Add FilePond vendor CSS to admin.min.css bundle (import dialog styling)
- Generate .admin-file-hint from csvHeaders() instead of hardcoded stale list
- Use positional fallback from CSV_COLUMNS for import cell parsing
- fix page nav buttons, smaller default pages, violet bg
- Build toolbar after renderAllPages so it queries existing canvases;
query canvases fresh in the scroll handler so page tracking works
after re-render (zoom in/out).
- Default data-pdf-scale 0.85 (JS fallback 0.8) so pages are a bit
smaller by default.
- Add dark transparent violet background (accent-secondary 22%) to
.pdf-expanded, behind the pages.
- expand to fill column top-to-bottom via CSS flex
Use a flex chain (.tfe-files flex:1, .tfe-file-item:has(.pdf-expanded)
flex:1) so the expanded PDF wrapper fills the right column from under
the header to the bottom of the viewport, instead of viewport-based
clamp math that didn't account for the header height.
- fill right column, center pages, freeze column on expand
The expanded viewer now stays inside the right column instead of
growing the page: the column switches to overflow:hidden while the
PDF panel scrolls internally. Pages are centered horizontally via
flexbox align-items: center.
- fix toolbar not full-width, hide sibling file items
The toolbar was constrained by the parent's align-items:center; fixed
with align-self:stretch. Hiding sibling .tfe-file-item elements when
a PDF is expanded prevents them from rendering above the overlay.
- disable overscroll on expanded view
- use overscroll-behavior-y on container, none on toolbar
- disable body overscroll via .pdf-viewer-open class
- mobile full-viewport, iframe matches pdf height
- mobile overlay above everything via position:fixed
- opaque dark-violet bg on mobile overlay
- tfe: add 'Open in new tab' link above website iframes
- trim column padding when expanded for more vertical space
- 5% zoom increments, subtle padding on expanded column
- prevent horizontal overflow/overscroll
Switched from grid to flex for two-column layout
Left column (flex: 0 0 35%): author, title, meta, synopsis — scrolls independently
Right column (flex: 1): files/media — scrolls independently
Added .tfe-file-iframe class to PDF/website iframes, scoped width/height constraints
All media gets width:100% + max-width:100% to prevent overflow
Mobile (<900px): reverts to single-column page-level scrolling
Two fixes:
1. about_page edit was missing autosave-handler.js: the first if block
(editType === 'page' || 'about_page') overrode $extraJs with only
overtype.min.js, making the elseif that added autosave-handler.js
unreachable for about_page. Added autosave-handler.js to the first
block, removed dead 'about_page' from the elseif.
2. Deleting a contact group (Contacts 3/4/5) only removed the DOM
element but never triggered htmx autosave. Dispatched a 'change'
event on the form after removal+reindex so the hx-trigger fires.
Also expanded autosave-handler.js URL filter to accept apropos.php
so the status indicator (Enregistrement/Enregistré) works for contacts saves.
Migration 041 accidentally renamed contact_interne → author_email and
contact_public → author_show_contact, but all application code still
references the old names. Migration 042 rebuilds the views with the
correct column aliases. Also updated schema.sql to match.
Issue 1 — hidden seeds overriding user input:
Removed all hidden seed inputs (cc2r, want_license, license_id,
license_custom). The .licence-license-choice div is now rendered
directly from $formData on page load instead of via HTMX load
trigger. This eliminates stale-seed interference on both HTMX
radio changes and full form submits.
Issue 2 — Interne want_license uncheck clearing CC2r:
When want_license is unchecked, CC2r and licence selections are
cleared (section hidden). Added hidden want_license=0 so the
fragment handler can distinguish "arriving from another access
type" (no want_license in POST → preserve values) from "explicit
Interne toggle" (want_license present → clear CC2r if off).
CC2r is independently toggleable within the visible section.
Licence dropdown placeholder changed to 'Aucune licence' for
an explicit no-licence choice.
- cleanup-tmp.php: accepte les arrays filepond_dirs[] et trash_files[] pour suppression en lot
- cleanup-stats-fragment.php: ajout checkboxes + barre d'actions groupées + formulaire bulk HTMX
- admin-cleanup-bulk.js: module JS pour toggleAll, updateBulk, bulkDelete
- Nouvelle page /admin/cleanup.php remplace la modale (tmp-cleanup.php supprimé)
- Bouton Nettoyer dans index.php devient un lien vers la page dédiée
- Remove Mo option from duration, keep only pages and minutes
- Redesign duration fieldset: separate Pages input + Durée h:m inputs, both can be set together
- Fix minutes input visibility: wider inputs (6ch), proper CSS layout
- Add has_annexes checkbox to fichiers fragment + DB column + controllers
- Display duration on admin backoffice recap page
- Display duration on public partage recap page
- Update public TFE page for new combined duration format
- Migration 041: add duration_pages, duration_minutes, has_annexes columns; migrate data; recreate views
- Added max-height, overflow-y: auto, overscroll-behavior: contain,
and scrollbar-width: thin to .toc in the desktop media query (toc.css)
- This gives the TOC its own scrollbar when its content exceeds the
viewport height, scrolling independently from the main content
- Affects both public pages (about, charte, licence via .page-content)
and admin pages (acces, parametres, contenus via .admin-main--toc)
- Add repertoire-scroll-restore.js: snapshots scrollTop of each column <ul>
before htmx:beforeSwap, restores after htmx:afterSwap (keyed by data-col)
- Add subtle opacity transition on #repertoire-index during htmx-swapping
- Tighten rep-indicator opacity transition to 0.1s for snappier feedback
- Import new module in public-entry.js