- fixed padding in the tfe.php page
- fixed the layout in admin/index.php toolbar top
- the gap issued with the table theader
- rearranged button order in the toolbar top
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
The base input rule uses a high-specificity :not() chain (0,8,1) that
overpowered .header-search-input-wrap input (0,1,1). The base padding
won, so the search input's padding-left (which makes room for the icon)
was overridden. Switched to #site-search-input (1,0,0) to win the
specificity battle.
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.
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
- repertoire.css: add min-height: 0 to column <ul> scroll containers so
grid 1fr row shrinks below content and overflow-y: auto activates
- admin-toc.js: add __adminTocBuilt guard + nav.children check to prevent
double population when loaded both via admin.min.js and direct <script>
- admin-toc.php: remove duplicate <script src="admin-toc.js"> tag —
JS is already bundled in admin.min.js
The sticky chip bar added noise: users already see their active
filters highlighted in the accordion columns (with rep-entry--selected
styling and the active-count badge on each toggle). Removing chips
shifts focus back to the accordions, consistent with the expectation
that users return to the dropdowns to adjust filters.
- repertoire-index.php: wrap each filter column in rep-accordion with toggle
button, chevron, badge (active filter count); add rep-chip-bar with
removable active-filter chips above the columns
- repertoire.css: mobile (≤640px) accordion mode — columns collapse to
single-open accordion sections with 48px touch targets; chip bar becomes
sticky; desktop/tablet layout unchanged via display:none on toggle elements
- repertoire.php: JS for single-accordion-open behavior on mobile, HTMX
re-init after swap, resize-breakpoint cleanup
- docs/repertoire-mobile-propositions.md: analysis + 4 architecture proposals
- Convert .apropos-toc <nav> to <details class="toc" open> in all three templates
- Add caret-down icon to summary (visible only on mobile via media queries)
- Desktop (≥768px): sticky sidebar via CSS grid, force-open via pointer-events:none, hide caret
- Mobile (≤767px): single column, collapsible TOC with rotating caret, margin-top below search bar
- Rename .apropos-toc-link to .toc-sidebar-link
- Merge 900px and 600px breakpoints into single 600px mobile media query
- Rename apropos.css → content-page.css; update 3 controller references
- Remove duplicate 'Téléversements abandonnés' heading already communicated by the summary
- Replace #peertube-orphans-wrapper with display:contents wrapper so PeerTube details sit as direct grid siblings
- Add hx-confirm to all delete buttons (filepond, trash, PeerTube orphans)
Replace every <img src="/assets/icons/..."> with <?= icon('name') ?>
across 26 template files. The PHP helper inlines the SVG markup into the
DOM so CSS color cascades naturally through fill="currentColor".
- Add src/icon.php helper: reads SVG file, sets width/height to 1em,
injects aria-hidden, supports optional CSS class
- Fix 12 icon SVGs that had hardcoded fill="#000000" or missing fill attr
- Replace search.svg with Phosphor fill-based magnifying glass
- Add explicit SVG sizes for admin header nav icons (16px/20px)
- Scope public search icon CSS to form[role=search]:not(.header-search-form)
to avoid breaking admin header layout; change stroke to fill
- Remove <img> filter: brightness(0) invert(1) hacks from admin.css
Unify the three public pages (à propos, charte, licence) onto a single
grid layout (.page-content) with sticky TOC sidebar, replacing the old
separate / / markup.
- Merge about.php, charte.php, licence.php templates into shared
.page-content / .content-section structure
- Add CommonMark HeadingPermalinkExtension for stable heading anchors
- Use SlugNormalizer for TOC links so they match rendered heading IDs
- Standardize link styling across content blocks: bold black, accent on
hover (consistent with global link style)
- Fix code block wrapping: use pre-wrap instead of pre, constrain grid
columns with min-width:0, auto scrollbar
- Fix apropos page grid placement: force content-section into column 2
so contacts and credits stay in the content area, not the sidebar
Also includes accumulated WIP changes:
- Header gradient: hardcoded purple-to-green (replaces CSS variables)
- Search placeholder font
- Duration field: replace minutes/sec/heures with h:m:s time inputs
- TFE file optional for formats 1,4,6 with client-side JS toggle
- Licence form: em-dash to hyphen, details/summary classes
- Pill search: block Enter key form submission when no results
- Draft autosave: remove CSRF rotation (broke concurrent FilePond uploads)
- Language pill: clear hints for excluded main languages
- Search results: gradient placeholder cards for items without covers
- TFE display: format durée values as XhYm instead of decimal
Raised upload_max_filesize from 512M to 8192M (8G) and post_max_size
from 520M to 8704M to match the JS-side per-extension size caps that
allow up to 8GB for video files (mp4, webm, mov, etc.). Also raised
memory_limit to 512M and max_input_time to 600s.
The closure returned arrays when formData values were arrays (e.g.
jury_promoteur), but the PHP return type annotation was :string.
PHP 8.x enforces this strictly, causing a fatal TypeError in
jury-fieldset.php on add mode.
Also introduces $extraCssAdmin support in head.php for admin-only
stylesheets (form-admin.css, filepond CSS, system.css). Admin pages
now use $extraCssAdmin for admin-only assets and $extraCss for
shared stylesheets like form-base.css.
- New fragment endpoint POST/GET /partage/fragments/draft.php:
saves all form fields to PHP session, excludes file/csrf/slug fields
GET returns JSON for JS hydration on page load
rotates both global CSRF and share CSRF tokens in sync
- form.php accepts optional $formExtraAttrs and $showAutosaveStatus:
allows injecting HTMX attributes and 'Brouillon enregistré' indicator
- renderShareLinkForm adds hx-post with change/input debounce trigger,
loads autosave-handler.js, hydrate fields from draft on page load
- Draft cleared on successful form submission in handleShareLinkSubmission
- autosave-handler.js now also updates share_link_token hidden input
when rotating CSRF token (partage form uses both csrf_token and share_link_token)
- Added .autosave-status CSS to form.css (was admin.css-only)
- Updated fragment routing to accept GET requests (needed for draft hydration)
The partage/admin form had a hardcoded filepond_mode=1 hidden input,
so without JavaScript the server always entered the FilePond async
path — which found no hex IDs and silently dropped all files.
Three-layer fix:
1. HTML: filepond_mode input starts disabled with value=0; JS enables
it and sets value=1 on DOMContentLoaded (and after HTMX swaps).
Disabled inputs aren't submitted → server gets no filepond_mode
→ naturally falls to legacy path.
2. JS: enableFilepondMode() called on page load and hx:afterSwap so
FilePond-enhanced forms always send filepond_mode=1.
3. Server (defense-in-depth): ThesisFileHandler::hasFilePondQueueData()
scans POST['queue_file'] for 32-char hex IDs; ThesisCreateController
and ThesisEditController use it alongside filepond_mode, so even if
the flag somehow arrives without async upload IDs, the path
takes over.