mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
feat: upload progress bar — fieldset layout, accent colors, file name display, completion animation, 800ms redirect delay; decorelate formats from fichiers; server-side poll via token; bump PeerTube embed audio player
This commit is contained in:
@@ -4,15 +4,9 @@
|
||||
*
|
||||
* Returns the combined Format(s) + Fichiers block.
|
||||
*
|
||||
* Architecture:
|
||||
* - Formats checkboxes: static, never swapped. They trigger HTMX swaps
|
||||
* on individual #slot-siteweb, #slot-video, #slot-audio elements.
|
||||
* - File inputs (couverture, note d'intention, TFE, annexes): always
|
||||
* static in the DOM — never destroyed by format toggling.
|
||||
* - Format-specific extras: each is a standalone HTMX fragment slot.
|
||||
* When unchecked → empty hidden placeholder. When checked → input
|
||||
* fields rendered via HTMX. This preserves FilePond instances on
|
||||
* the main file inputs across format changes.
|
||||
* All slots (Site web, Vidéo, Audio) are always visible — decorelated from
|
||||
* the format checkboxes. The checkboxes serve only as metadata selectors;
|
||||
* they no longer trigger HTMX swaps.
|
||||
*
|
||||
* Expected POST:
|
||||
* formats[] — array of selected format_type IDs
|
||||
@@ -28,21 +22,9 @@ $peerTubeSettings = PeerTubeService::getSettings($_ptDb);
|
||||
|
||||
$db = $_ptDb->getConnection();
|
||||
|
||||
// Load all format types in display order
|
||||
$allFormats = $db->query('SELECT id, name FROM format_types ORDER BY sort_order, id')
|
||||
->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Build name→id map for format logic
|
||||
$formatIdByName = [];
|
||||
foreach ($allFormats as $f) {
|
||||
$formatIdByName[$f['name']] = (int)$f['id'];
|
||||
}
|
||||
|
||||
$siteWebId = $formatIdByName['Site web'] ?? null;
|
||||
$videoId = $formatIdByName['Vidéo'] ?? null;
|
||||
$audioId = $formatIdByName['Audio'] ?? null;
|
||||
$imageId = $formatIdByName['Image'] ?? null;
|
||||
|
||||
$selectedFormats = isset($_POST['formats']) && is_array($_POST['formats'])
|
||||
? array_map('intval', $_POST['formats'])
|
||||
: [];
|
||||
@@ -50,25 +32,10 @@ $selectedFormats = isset($_POST['formats']) && is_array($_POST['formats'])
|
||||
$adminMode = ($_POST['admin_mode'] ?? '0') === '1';
|
||||
$editMode = ($_POST['edit_mode'] ?? '0') === '1';
|
||||
|
||||
$hasSiteWeb = $siteWebId && in_array($siteWebId, $selectedFormats, true);
|
||||
$hasVideo = $videoId && in_array($videoId, $selectedFormats, true);
|
||||
$hasAudio = $audioId && in_array($audioId, $selectedFormats, true);
|
||||
$hasImage = $imageId && in_array($imageId, $selectedFormats, true);
|
||||
|
||||
$hasNonWebFormat = !empty(array_filter(
|
||||
$selectedFormats,
|
||||
fn($id) => $id !== $siteWebId
|
||||
));
|
||||
$showUploadBlock = $hasNonWebFormat || !$hasSiteWeb;
|
||||
|
||||
$websiteUrl = htmlspecialchars($_POST['website_url'] ?? '');
|
||||
$websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
|
||||
|
||||
$hxPost = $adminMode ? '/admin/fichiers-fragment.php' : '/partage/fichiers-fragment';
|
||||
|
||||
$hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
?>
|
||||
<!-- ═══════════════════ Format(s) + Fichiers (static, never swapped) ═══════════════════ -->
|
||||
<!-- ═══════════════════ Format(s) + Fichiers ═══════════════════ -->
|
||||
<div id="format-fichiers-block">
|
||||
<input type="hidden" name="admin_mode" value="<?= $adminMode ? '1' : '0' ?>">
|
||||
<input type="hidden" name="edit_mode" value="<?= $editMode ? '1' : '0' ?>">
|
||||
@@ -76,8 +43,8 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
<input type="hidden" name="_cover" value="<?= htmlspecialchars($_POST['_cover']) ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
||||
<fieldset>
|
||||
<!-- ═══════════════════ Format(s) — sticky ═══════════════════ -->
|
||||
<fieldset id="fieldset-formats">
|
||||
<legend>Format(s)</legend>
|
||||
<div>
|
||||
<span class="admin-row-label">Format(s) du TFE :<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></span>
|
||||
@@ -91,30 +58,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
<input type="checkbox"
|
||||
name="formats[]"
|
||||
value="<?= htmlspecialchars((string)$opt['id']) ?>"
|
||||
<?= in_array((int)$opt['id'], $selectedFormats, true) ? 'checked' : '' ?>
|
||||
<?php if ((int)$opt['id'] === ($siteWebId ?? 0)): ?>
|
||||
hx-post="<?= htmlspecialchars($hxPost) ?>"
|
||||
hx-target="#slot-siteweb"
|
||||
hx-select="#slot-siteweb"
|
||||
hx-trigger="change"
|
||||
hx-include="[name='formats[]'], [name='website_url'], [name='admin_mode'], [name='edit_mode'], [name='_cover']"
|
||||
hx-swap="outerHTML"
|
||||
<?php elseif ((int)$opt['id'] === ($videoId ?? 0)): ?>
|
||||
hx-post="<?= htmlspecialchars($hxPost) ?>"
|
||||
hx-target="#slot-video"
|
||||
hx-select="#slot-video"
|
||||
hx-trigger="change"
|
||||
hx-include="[name='formats[]'], [name='admin_mode'], [name='edit_mode'], [name='_cover']"
|
||||
hx-swap="outerHTML"
|
||||
<?php elseif ((int)$opt['id'] === ($audioId ?? 0)): ?>
|
||||
hx-post="<?= htmlspecialchars($hxPost) ?>"
|
||||
hx-target="#slot-audio"
|
||||
hx-select="#slot-audio"
|
||||
hx-trigger="change"
|
||||
hx-include="[name='formats[]'], [name='admin_mode'], [name='edit_mode'], [name='_cover']"
|
||||
hx-swap="outerHTML"
|
||||
<?php endif; ?>
|
||||
>
|
||||
<?= in_array((int)$opt['id'], $selectedFormats, true) ? 'checked' : '' ?>>
|
||||
<?= htmlspecialchars($opt['name']) ?>
|
||||
</label>
|
||||
</li>
|
||||
@@ -129,16 +73,14 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
<legend>Fichiers</legend>
|
||||
|
||||
<?php
|
||||
// Existing files + cover preview (edit mode only, initial render — not re-sent on HTMX swaps)
|
||||
// Existing files + cover preview (edit mode only)
|
||||
$_efiles = $currentFiles ?? [];
|
||||
$_cover = $_POST['_cover'] ?? null;
|
||||
if ($editMode && (!empty($_efiles) || $_cover)):
|
||||
?>
|
||||
<div class="admin-form-group">
|
||||
<ul id="existing-files-sortable" class="admin-file-list">
|
||||
<?php
|
||||
// ── Couverture preview ──
|
||||
if ($_cover): ?>
|
||||
<?php if ($_cover): ?>
|
||||
<li class="admin-file-list-item">
|
||||
<span class="admin-file-icon-col">🖼️</span>
|
||||
<span class="admin-file-info">
|
||||
@@ -154,7 +96,6 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
// ── Existing files ──
|
||||
$_thesisFilesList = array_values(array_filter($_efiles, fn($f) => $f["file_type"] !== "cover"));
|
||||
foreach ($_thesisFilesList as $_f):
|
||||
$_fPath = $_f["file_path"] ?? "";
|
||||
@@ -204,7 +145,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ── 1. Couverture (always, FilePond single-file) ── -->
|
||||
<!-- ── 1. Couverture ── -->
|
||||
<?php
|
||||
$_cover = $_POST['_cover'] ?? null;
|
||||
if (!$editMode || !$_cover): ?>
|
||||
@@ -220,7 +161,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
</div>
|
||||
<?php endif; unset($_cover); ?>
|
||||
|
||||
<!-- ── 2. Note d'intention (always, FilePond single-file) ── -->
|
||||
<!-- ── 2. Note d'intention ── -->
|
||||
<div class="admin-form-group">
|
||||
<label for="note_intention">Note d'intention<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<div class="admin-file-input">
|
||||
@@ -233,7 +174,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── 3. TFE — multi-file upload (FilePond) ── -->
|
||||
<!-- ── 3. TFE ── -->
|
||||
<div class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="tfe-files-input">TFE<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<div class="admin-file-input">
|
||||
@@ -256,9 +197,8 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── 4. Annexes — multi-file upload (FilePond), always visible ── -->
|
||||
<!-- ── 4. Annexes ── -->
|
||||
<div id="annexes-input-block">
|
||||
<!-- has_annexes checkbox disabled — annexe pool always on -->
|
||||
<input type="hidden" name="has_annexes" value="0">
|
||||
<div class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="annexe-files-input">Annexes (optionnel)</label>
|
||||
@@ -272,87 +212,70 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Format-specific extras (individual swappable slots) ── -->
|
||||
<div id="format-extras-block" style="display:flex;flex-direction:column;gap:var(--space-s);">
|
||||
<!-- Slot: Site web -->
|
||||
<?php if ($hasSiteWeb): ?>
|
||||
<div id="slot-siteweb" class="admin-form-group">
|
||||
<label for="website_url">URL du site<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<div class="admin-file-input">
|
||||
<input type="url" id="website_url" name="website_url"
|
||||
value="<?= $websiteUrl ?>"
|
||||
placeholder="https://mon-tfe.erg.be"
|
||||
<?= !$adminMode ? 'required' : '' ?>>
|
||||
<small>Le TFE sera affiché comme un site embarqué sur sa page publique.</small>
|
||||
</div>
|
||||
<!-- ── 5. Site web url ── -->
|
||||
<div id="slot-siteweb" class="admin-form-group">
|
||||
<label for="website_url">URL du site (optionnel)</label>
|
||||
<div class="admin-file-input">
|
||||
<input type="url" id="website_url" name="website_url"
|
||||
value="<?= $websiteUrl ?>"
|
||||
placeholder="https://mon-tfe.erg.be">
|
||||
<small>Le TFE sera affiché comme un site embarqué sur sa page publique.</small>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div id="slot-siteweb" hidden></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Slot: Video (always visible when PeerTube enabled) -->
|
||||
<?php if ($peerTubeEnabled): ?>
|
||||
<div id="slot-video" class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="peertube-video-input">Vidéo<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="peertube-video-input"
|
||||
name="queue_file[peertube_video][]"
|
||||
multiple
|
||||
accept="video/mp4,video/webm,video/ogg,video/quicktime,.mp4,.webm,.ogv,.mov"
|
||||
class="tfe-file-picker"
|
||||
<?= !$adminMode ? 'required' : '' ?>>
|
||||
<small class="admin-file-hint">MP4, WebM ou MOV. Max 500 MB. Glissez pour réordonner. Hébergé sur <a href="<?= htmlspecialchars($peerTubeSettings['instance_url']) ?>" target="_blank" rel="noopener">PeerTube</a>.</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($hasVideo): ?>
|
||||
<div id="slot-video" class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="video-files-input">Vidéo<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="video-files-input"
|
||||
name="queue_file[video][]"
|
||||
multiple
|
||||
accept="video/mp4,video/webm,video/ogg,video/quicktime,.mp4,.webm,.ogv,.mov"
|
||||
class="tfe-file-picker"
|
||||
<?= !$adminMode ? 'required' : '' ?>>
|
||||
<small class="admin-file-hint">MP4, WebM ou MOV. Max 500 MB. Glissez pour réordonner.</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div id="slot-video" hidden></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Slot: Audio (always visible when PeerTube enabled) -->
|
||||
<?php if ($peerTubeEnabled): ?>
|
||||
<div id="slot-audio" class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="peertube-audio-input">Audio<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="peertube-audio-input"
|
||||
name="queue_file[peertube_audio][]"
|
||||
multiple
|
||||
accept="audio/mpeg,audio/ogg,audio/wav,audio/flac,audio/aac,audio/mp4,.mp3,.ogg,.oga,.wav,.flac,.aac,.m4a"
|
||||
class="tfe-file-picker"
|
||||
<?= !$adminMode ? 'required' : '' ?>>
|
||||
<small class="admin-file-hint">MP3, OGG, WAV, FLAC ou AAC. Max 500 MB. Glissez pour réordonner. Hébergé sur <a href="<?= htmlspecialchars($peerTubeSettings['instance_url']) ?>" target="_blank" rel="noopener">PeerTube</a>.</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($hasAudio): ?>
|
||||
<div id="slot-audio" class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="audio-files-input">Audio<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="audio-files-input"
|
||||
name="queue_file[audio][]"
|
||||
multiple
|
||||
accept="audio/mpeg,audio/ogg,audio/wav,audio/flac,audio/aac,audio/mp4,.mp3,.ogg,.oga,.wav,.flac,.aac,.m4a"
|
||||
class="tfe-file-picker"
|
||||
<?= !$adminMode ? 'required' : '' ?>>
|
||||
<small class="admin-file-hint">MP3, OGG, WAV, FLAC ou AAC. Max 500 MB. Glissez pour réordonner.</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div id="slot-audio" hidden></div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ── 6. Vidéo / PeerTube ── -->
|
||||
<?php if ($peerTubeEnabled): ?>
|
||||
<div id="slot-video" class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="peertube-video-input">Vidéo (optionnel)</label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="peertube-video-input"
|
||||
name="queue_file[peertube_video][]"
|
||||
multiple
|
||||
accept="video/mp4,video/webm,video/ogg,video/quicktime,.mp4,.webm,.ogv,.mov"
|
||||
class="tfe-file-picker">
|
||||
<small class="admin-file-hint">MP4, WebM ou MOV. Max 500 MB. Hébergé sur <a href="<?= htmlspecialchars($peerTubeSettings['instance_url']) ?>" target="_blank" rel="noopener">PeerTube</a>.</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div id="slot-video" class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="video-files-input">Vidéo (optionnel)</label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="video-files-input"
|
||||
name="queue_file[video][]"
|
||||
multiple
|
||||
accept="video/mp4,video/webm,video/ogg,video/quicktime,.mp4,.webm,.ogv,.mov"
|
||||
class="tfe-file-picker">
|
||||
<small class="admin-file-hint">MP4, WebM ou MOV. Max 500 MB.</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ── 7. Audio / PeerTube ── -->
|
||||
<?php if ($peerTubeEnabled): ?>
|
||||
<div id="slot-audio" class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="peertube-audio-input">Audio (optionnel)</label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="peertube-audio-input"
|
||||
name="queue_file[peertube_audio][]"
|
||||
multiple
|
||||
accept="audio/mpeg,audio/ogg,audio/wav,audio/flac,audio/aac,audio/mp4,.mp3,.ogg,.oga,.wav,.flac,.aac,.m4a"
|
||||
class="tfe-file-picker">
|
||||
<small class="admin-file-hint">MP3, OGG, WAV, FLAC ou AAC. Max 500 MB. Hébergé sur <a href="<?= htmlspecialchars($peerTubeSettings['instance_url']) ?>" target="_blank" rel="noopener">PeerTube</a>.</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div id="slot-audio" class="admin-form-group admin-files-fieldgroup">
|
||||
<label for="audio-files-input">Audio (optionnel)</label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="audio-files-input"
|
||||
name="queue_file[audio][]"
|
||||
multiple
|
||||
accept="audio/mpeg,audio/ogg,audio/wav,audio/flac,audio/aac,audio/mp4,.mp3,.ogg,.oga,.wav,.flac,.aac,.m4a"
|
||||
class="tfe-file-picker">
|
||||
<small class="admin-file-hint">MP3, OGG, WAV, FLAC ou AAC. Max 500 MB.</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</fieldset><!-- /Fichiers -->
|
||||
</div><!-- #format-fichiers-block -->
|
||||
|
||||
Reference in New Issue
Block a user