diff --git a/TODO.md b/TODO.md index 3ab5925..fe92a64 100644 --- a/TODO.md +++ b/TODO.md @@ -18,7 +18,10 @@ - [x] Disable CURLOPT_FOLLOWLOCATION to preserve Location header - [x] Add cancelUpload() helper for Delete-on-error cleanup - [x] PeerTube upload fixed — simple multipart POST /api/v1/videos/upload works -- [x] Add upload-progress.js — XHR form submit with progress bar for admin add/edit forms +- [x] Upload progress: 0-25% browser upload, 25-99% server polling via /admin/actions/upload-progress.php +- [x] Decorelate formats from fichiers: no HTMX toggling; Site web/Vidéo/Audio always visible +- [x] Sticky formats fieldset inside parent container +- [x] Server-side progress: PeerTubeService writes to temp file, client polls progress endpoint ## HTMX Toast Feedback for Settings Checkboxes (contenus.php) diff --git a/app/public/admin/actions/edit.php b/app/public/admin/actions/edit.php index 8e60540..13dba01 100644 --- a/app/public/admin/actions/edit.php +++ b/app/public/admin/actions/edit.php @@ -32,7 +32,12 @@ require_once APP_ROOT . '/src/ErrorHandler.php'; try { $ctrl = ThesisEditController::create(); - $ctrl->save($thesisId, $_POST, $_FILES); + $progressToken = $_POST['progress_token'] ?? bin2hex(random_bytes(8)); + $ctrl->save($thesisId, $_POST, $_FILES, $progressToken); + + // Clean up progress file + require_once APP_ROOT . '/src/PeerTubeService.php'; + PeerTubeService::clearProgress($progressToken); // Regenerate CSRF token after successful save $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); diff --git a/app/public/admin/actions/upload-progress.php b/app/public/admin/actions/upload-progress.php new file mode 100644 index 0000000..53c8912 --- /dev/null +++ b/app/public/admin/actions/upload-progress.php @@ -0,0 +1,43 @@ + + * + * Response: JSON + * { "stage": "upload"|"processing"|"done", "pct": 45, "file": "video.mp4" } + * + * Progress data is written by ThesisEditController / ThesisCreateController + * to a temp file during processing. + */ + +require_once __DIR__ . '/../../../bootstrap.php'; +require_once __DIR__ . '/../../../src/AdminAuth.php'; +AdminAuth::requireLogin(); + +header('Content-Type: application/json'); + +$token = $_GET['token'] ?? ''; +if (!preg_match('/^[a-f0-9]{16}$/', $token)) { + echo json_encode(['stage' => 'error', 'error' => 'Invalid token']); + exit; +} + +$progressFile = sys_get_temp_dir() . '/xamxam_upload_' . $token . '.json'; + +if (!file_exists($progressFile)) { + // No progress file yet — still in upload phase (or token invalid) + echo json_encode(['stage' => 'upload', 'pct' => 0, 'file' => '']); + exit; +} + +$data = json_decode(file_get_contents($progressFile), true); +if (!$data) { + echo json_encode(['stage' => 'upload', 'pct' => 0, 'file' => '']); + exit; +} + +echo json_encode($data); diff --git a/app/public/assets/css/form.css b/app/public/assets/css/form.css index 9ba4870..a154ae0 100644 --- a/app/public/assets/css/form.css +++ b/app/public/assets/css/form.css @@ -322,7 +322,7 @@ .licence-explanation { background: var(--bg-secondary); border-left: 4px solid var(--border-secondary, var(--border-primary)); - padding: var(--space-m); + /* padding: var(--space-m); */ margin: 0; } @@ -1287,3 +1287,78 @@ a.recap-file-name:hover { font-size: var(--step--2); color: var(--text-tertiary); } + +/* ── Upload progress bar ─────────────────────────────────── */ + +#upload-progress-wrap { + border: 1px solid var(--accent-muted); + border-radius: var(--radius); + padding: var(--space-s); + margin-bottom: var(--space-s); +} + +#upload-progress-wrap legend { + font-weight: 600; + font-size: var(--step--1); + color: var(--text-primary); + padding: 0 var(--space-2xs); +} + +#upload-progress-bar { + display: block; + width: 100%; + height: 0.85rem; + border-radius: var(--radius); + overflow: hidden; + background: var(--accent-muted); + border: none; +} + +#upload-progress-bar::-webkit-progress-bar { + background: var(--accent-muted); + border-radius: var(--radius); +} + +#upload-progress-bar::-webkit-progress-value { + background: var(--accent-primary); + border-radius: var(--radius); + transition: width 0.3s ease; +} + +#upload-progress-bar::-moz-progress-bar { + background: var(--accent-primary); + border-radius: var(--radius); +} + +/* Completion state */ +#upload-progress-bar[data-complete] { + box-shadow: 0 0 12px rgba(149, 87, 181, 0.4); +} + +#upload-progress-bar[data-complete]::-webkit-progress-value { + background: var(--accent-green); + box-shadow: 0 0 8px rgba(76, 175, 80, 0.3); +} + +#upload-progress-bar[data-complete]::-moz-progress-bar { + background: var(--accent-green); +} + +/* ── Sticky formats fieldset ──────────────────────────────── */ + +#fieldset-formats { + position: sticky; + top: var(--space-s); + z-index: 10; + /* background: var(--bg-primary); */ + border-radius: var(--radius); +} + +legend { + text-shadow: var(--bg-primary) 0px 0px 2px; +} + +/* Stickiness is scoped to the parent container */ +#format-fichiers-block { + container-type: inline-size; +} diff --git a/app/public/assets/js/file-upload-filepond.js b/app/public/assets/js/file-upload-filepond.js index 9e685a5..5985ad8 100644 --- a/app/public/assets/js/file-upload-filepond.js +++ b/app/public/assets/js/file-upload-filepond.js @@ -162,7 +162,8 @@ * Value: pipe-separated list of file names. */ function syncOrderInput(queueType, pond) { - var form = pond.element ? pond.element.closest("form") : null; + if (!pond || !pond.element) return; + var form = pond.element.closest("form"); if (!form) return; var orderInput = form.querySelector("input[name='queue_order[" + queueType + "]']"); diff --git a/app/public/assets/js/upload-progress.js b/app/public/assets/js/upload-progress.js index 964f60f..660acb0 100644 --- a/app/public/assets/js/upload-progress.js +++ b/app/public/assets/js/upload-progress.js @@ -1,12 +1,14 @@ /** * upload-progress.js * - * Intercepts admin form submissions (add.php / edit.php) and submits via - * XMLHttpRequest to display a progress bar for large file uploads. - * Falls back to native form POST when JavaScript is unavailable. + * Intercepts admin form submissions with files and submits via XMLHttpRequest. + * Polls GET /admin/actions/upload-progress.php?token=xxx for server-side + * processing progress (PeerTube uploads, file moves). * - * Requires an element with id="upload-progress-bar" inside the form. - * The progress bar is normally hidden (display:none), shown only during upload. + * Progress display: + * 0%–25% : browser → server upload (XHR upload.progress) + * 25%–99% : server processing (polled from progress endpoint) + * 100% : response received — "Téléversé avec succès", then redirect */ (() => { 'use strict'; @@ -14,65 +16,133 @@ const FORMS = document.querySelectorAll('form[data-upload-progress]'); if (!FORMS.length) return; + const POLL_INTERVAL = 400; + const UPLOAD_CAP = 25; + const PROCESSING_MAX = 99; + const SUCCESS_DELAY = 800; + for (const form of FORMS) { - const progressWrap = form.querySelector('#upload-progress-wrap'); - const progressBar = form.querySelector('#upload-progress-bar'); - const progressText = form.querySelector('#upload-progress-text'); - const submitBtn = form.querySelector('button[type="submit"]'); + const progressWrap = form.querySelector('#upload-progress-wrap'); + const progressBar = form.querySelector('#upload-progress-bar'); + const progressLabel = form.querySelector('#upload-progress-label'); + const progressFile = form.querySelector('#upload-progress-file'); + const submitBtn = form.querySelector('button[type="submit"]'); + const tokenInput = form.querySelector('input[name="progress_token"]'); if (!progressBar || !progressWrap) continue; - form.addEventListener('submit', function (e) { - // Only intercept if files are actually attached (FilePond inputs have files) - const fileInputs = form.querySelectorAll('input[type="file"]'); - let hasFiles = false; - for (const fi of fileInputs) { - if (fi.files && fi.files.length > 0) { - hasFiles = true; - break; + function collectFileNames() { + const names = []; + const inputs = form.querySelectorAll('input[type="file"]'); + for (const fi of inputs) { + if (fi.files) { + for (const f of fi.files) { + if (f.name) names.push(f.name); + } } } - if (!hasFiles) return; // let native submit handle it + return names; + } + + form.addEventListener('submit', function (e) { + const fileNames = collectFileNames(); + if (!fileNames.length) return; e.preventDefault(); - // Show progress bar - progressWrap.style.display = 'block'; + const token = tokenInput ? tokenInput.value : ''; + + progressWrap.style.display = ''; progressBar.value = 0; - progressText.textContent = '0%'; + progressBar.removeAttribute('data-complete'); + progressLabel.textContent = 'Téléversement en cours…'; + progressFile.textContent = fileNames.length === 1 + ? fileNames[0] + : fileNames.length + ' fichiers'; if (submitBtn) submitBtn.disabled = true; - // Build FormData const fd = new FormData(form); - // Ensure any FilePond-managed files are included — FilePond with - // storeAsFile:true copies files into the .files, so FormData - // picks them up automatically from the DOM inputs. - // But we must also respect queue_order hidden inputs. - // FormData(form) already handles this since it reads all form fields. - const xhr = new XMLHttpRequest(); + let uploadDone = false; + let lastUploadPct = 0; + let pollingTimer = null; + + /** Poll server-side progress */ + function startPolling() { + if (pollingTimer || !token) return; + progressLabel.textContent = 'Traitement en cours…'; + pollingTimer = setInterval(function () { + fetch('/admin/actions/upload-progress.php?token=' + encodeURIComponent(token)) + .then(function (r) { return r.json(); }) + .then(function (data) { + if (data && data.stage && data.stage !== 'upload') { + const pct = Math.min(PROCESSING_MAX, Math.max(UPLOAD_CAP, data.pct || UPLOAD_CAP)); + progressBar.value = pct; + if (data.file) { + progressFile.textContent = data.file; + } + } + }) + .catch(function () { /* ignore poll errors */ }); + }, POLL_INTERVAL); + } + + function stopPolling() { + if (pollingTimer) { + clearInterval(pollingTimer); + pollingTimer = null; + } + } + + function finishSuccess() { + stopPolling(); + progressBar.value = 100; + progressBar.setAttribute('data-complete', ''); + progressLabel.textContent = 'Téléversé avec succès'; + progressFile.textContent = ''; + } + + // ── Upload phase (0% → UPLOAD_CAP) ── xhr.upload.addEventListener('progress', function (evt) { if (evt.lengthComputable) { - const pct = Math.round((evt.loaded / evt.total) * 100); - progressBar.value = pct; - progressText.textContent = pct + '%'; + const rawPct = Math.round((evt.loaded / evt.total) * 100); + const scaled = Math.round((rawPct / 100) * UPLOAD_CAP); + if (scaled > lastUploadPct) { + lastUploadPct = scaled; + progressBar.value = scaled; + } } }); - xhr.addEventListener('load', function () { - // Server returns a redirect (302) on success, or re-renders the form on error. - // We can't follow 302 with XHR directly — the response body is the target page. - // Check if we got a redirect by examining the response URL. - const finalUrl = xhr.responseURL || ''; - const isRedirect = xhr.status >= 200 && xhr.status < 300 && finalUrl !== '' && !finalUrl.endsWith(form.action); + xhr.upload.addEventListener('loadend', function () { + uploadDone = true; + progressBar.value = UPLOAD_CAP; + startPolling(); + }); - if (isRedirect) { - // Success — navigate to the redirect target - window.location.href = finalUrl; + // ── Response handling ── + xhr.addEventListener('readystatechange', function () { + if (xhr.readyState !== XMLHttpRequest.DONE) return; + + stopPolling(); + + if (xhr.status >= 200 && xhr.status < 300) { + finishSuccess(); + + setTimeout(function () { + const finalUrl = xhr.responseURL || ''; + if (finalUrl && finalUrl !== form.action) { + window.location.href = finalUrl; + } else { + document.open(); + document.write(xhr.responseText); + document.close(); + } + }, SUCCESS_DELAY); } else { - // Error — the server returned the form HTML with flash messages. - // Replace the current page content. + progressLabel.textContent = 'Erreur'; + progressFile.textContent = 'Échec du téléversement'; document.open(); document.write(xhr.responseText); document.close(); @@ -80,11 +150,14 @@ }); xhr.addEventListener('error', function () { - progressText.textContent = 'Erreur réseau'; + stopPolling(); + progressLabel.textContent = 'Erreur réseau'; + progressFile.textContent = ''; if (submitBtn) submitBtn.disabled = false; }); xhr.addEventListener('abort', function () { + stopPolling(); progressWrap.style.display = 'none'; if (submitBtn) submitBtn.disabled = false; }); diff --git a/app/public/partage/fichiers-fragment.php b/app/public/partage/fichiers-fragment.php index 9013845..3225c2b 100644 --- a/app/public/partage/fichiers-fragment.php +++ b/app/public/partage/fichiers-fragment.php @@ -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']); ?> - +
@@ -76,8 +43,8 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']); - -
+ +
Format(s)
Format(s) du TFE :*' : '' ?> @@ -91,30 +58,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']); - - hx-post="" - 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" - - hx-post="" - 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" - - hx-post="" - 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" - -> + > @@ -129,16 +73,14 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']); Fichiers
    - +
  • 🖼️ @@ -154,7 +96,6 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
  • $f["file_type"] !== "cover")); foreach ($_thesisFilesList as $_f): $_fPath = $_f["file_path"] ?? ""; @@ -204,7 +145,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
- + @@ -220,7 +161,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
- +
@@ -233,7 +174,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
- +
@@ -256,9 +197,8 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
- +
-
@@ -272,87 +212,70 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
- -
- - -
- -
- > - Le TFE sera affiché comme un site embarqué sur sa page publique. -
+ +
+ +
+ + Le TFE sera affiché comme un site embarqué sur sa page publique.
- - - - - - -
- -
- > - MP4, WebM ou MOV. Max 500 MB. Glissez pour réordonner. Hébergé sur PeerTube. -
-
- -
- -
- > - MP4, WebM ou MOV. Max 500 MB. Glissez pour réordonner. -
-
- - - - - - -
- -
- > - MP3, OGG, WAV, FLAC ou AAC. Max 500 MB. Glissez pour réordonner. Hébergé sur PeerTube. -
-
- -
- -
- > - MP3, OGG, WAV, FLAC ou AAC. Max 500 MB. Glissez pour réordonner. -
-
- - - -
+ + +
+ +
+ + MP4, WebM ou MOV. Max 500 MB. Hébergé sur PeerTube. +
+
+ +
+ +
+ + MP4, WebM ou MOV. Max 500 MB. +
+
+ + + + +
+ +
+ + MP3, OGG, WAV, FLAC ou AAC. Max 500 MB. Hébergé sur PeerTube. +
+
+ +
+ +
+ + MP3, OGG, WAV, FLAC ou AAC. Max 500 MB. +
+
+ +
diff --git a/app/src/Controllers/ThesisEditController.php b/app/src/Controllers/ThesisEditController.php index fe05fdf..8b1d91e 100644 --- a/app/src/Controllers/ThesisEditController.php +++ b/app/src/Controllers/ThesisEditController.php @@ -161,7 +161,7 @@ class ThesisEditController * the transaction is still open, but this method rolls * back internally before re-throwing). */ - public function save(int $thesisId, array $post, array $files): void + public function save(int $thesisId, array $post, array $files, ?string $progressToken = null): void { if ($thesisId <= 0) { throw new InvalidArgumentException('ID de TFE invalide.'); @@ -440,8 +440,8 @@ class ThesisEditController // ── PeerTube video / audio uploads (from FilePond queue) ────────────── $qPTVideo = $this->extractFilesSubArray($queueFiles, 'peertube_video'); $qPTAudio = $this->extractFilesSubArray($queueFiles, 'peertube_audio'); - $this->handlePeerTubeQueueFiles($thesisId, trim($post['titre'] ?? ''), $qPTVideo, 'video'); - $this->handlePeerTubeQueueFiles($thesisId, trim($post['titre'] ?? ''), $qPTAudio, 'audio'); + $this->handlePeerTubeQueueFiles($thesisId, trim($post['titre'] ?? ''), $qPTVideo, 'video', $progressToken); + $this->handlePeerTubeQueueFiles($thesisId, trim($post['titre'] ?? ''), $qPTAudio, 'audio', $progressToken); // ── Website URL — add or update ────────────────────────────────────── $this->handleWebsiteUrl($thesisId, $post); @@ -583,7 +583,7 @@ class ThesisEditController * @param array|null $uploads Flat $_FILES-style array from extractFilesSubArray(). * @param string $fileType 'video' or 'audio'. */ - private function handlePeerTubeQueueFiles(int $thesisId, string $title, ?array $uploads, string $fileType): void + private function handlePeerTubeQueueFiles(int $thesisId, string $title, ?array $uploads, string $fileType, ?string $progressToken = null): void { if (!$uploads || !is_array($uploads['name'] ?? null)) { return; @@ -594,17 +594,23 @@ class ThesisEditController return; } + $label = $fileType === 'video' ? 'Vidéo' : 'Audio'; $count = count($uploads['name']); for ($i = 0; $i < $count; $i++) { if (($uploads['error'][$i] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { continue; } + $fileName = $uploads['name'][$i]; + if ($progressToken) { + PeerTubeService::writeProgress($progressToken, 'peertube', 25 + (int)(($i / max($count, 1)) * 74), $label . ' : ' . $fileName); + } + try { $result = PeerTubeService::upload( $this->db, $uploads['tmp_name'][$i], - $uploads['name'][$i], + $fileName, $title, '' ); @@ -614,7 +620,7 @@ class ThesisEditController $thesisId, $fileType, $storedPath, - basename($uploads['name'][$i]), + basename($fileName), $uploads['size'][$i], $uploads['type'][$i] ?? 'application/octet-stream', null, diff --git a/app/src/PeerTubeService.php b/app/src/PeerTubeService.php index 07f9970..0207169 100644 --- a/app/src/PeerTubeService.php +++ b/app/src/PeerTubeService.php @@ -436,4 +436,30 @@ class PeerTubeService return ['status' => $status, 'body' => (string)$responseBody, 'headers' => $responseHeaders]; } + + // ------------------------------------------------------------------------- + // Progress reporting (for upload-progress.js polling) + // ------------------------------------------------------------------------- + + /** + * Write upload progress to a temp file polled by the progress endpoint. + */ + public static function writeProgress(string $token, string $stage, int $pct, string $file = ''): void + { + $progressFile = sys_get_temp_dir() . '/xamxam_upload_' . $token . '.json'; + file_put_contents($progressFile, json_encode([ + 'stage' => $stage, + 'pct' => $pct, + 'file' => $file, + ]), LOCK_EX); + } + + /** + * Remove the progress file for a given token. + */ + public static function clearProgress(string $token): void + { + $progressFile = sys_get_temp_dir() . '/xamxam_upload_' . $token . '.json'; + @unlink($progressFile); + } } diff --git a/app/templates/admin/acces.php b/app/templates/admin/acces.php index 10776ad..da97c74 100644 --- a/app/templates/admin/acces.php +++ b/app/templates/admin/acces.php @@ -1013,6 +1013,58 @@ +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +\\\\\\\ to: txzqmwnx fe4b8d24 "fix PeerTube upload: switch to simple multipart POST /api/v1/videos/upload with CURLFile; remove resumable protocol" (rebased revision) ++ $linkName = $link['name'] ?? ''; +++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: txzqmwnx fe4b8d24 "fix PeerTube upload: switch to simple multipart POST /api/v1/videos/upload with CURLFile; remove resumable protocol" (rebased revision) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +- $linkName = $link['name'] ?? ''; +- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: qlvmyvuo 284872ea "Bump peertube embed audio player height + remove figure for iframes in tfe.php" (rebased revision) + $linkName = $link['name'] ?? ''; + $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; + $linkLockedYear = $link['locked_year'] ?? null; ++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) ++\\\\\\\ to: qlvmyvuo ac39e754 "Bump peertube embed audio player height + remove figure for iframes in tfe.php" (rebased revision) +++ $linkName = $link['name'] ?? ''; +++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: qlvmyvuo ac39e754 "Bump peertube embed audio player height + remove figure for iframes in tfe.php" (rebased revision) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +- $linkName = $link['name'] ?? ''; +- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: qvnunynl f12d35f5 "upload progress bar: fieldset layout, accent colors, file name display, completion animation, 800ms delay before redirect" (rebased revision) + $linkName = $link['name'] ?? ''; + $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; + $linkLockedYear = $link['locked_year'] ?? null; ++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) ++\\\\\\\ to: qvnunynl cd701e3c "upload progress bar: fieldset layout, accent colors, file name display, completion animation, 800ms delay before redirect" (rebased revision) +++ $linkName = $link['name'] ?? ''; +++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: qvnunynl cd701e3c "upload progress bar: fieldset layout, accent colors, file name display, completion animation, 800ms delay before redirect" (rebased revision) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +- $linkName = $link['name'] ?? ''; +- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: nwozruss 84ec9655 "decorelate formats from fichiers: no HTMX toggles, all slots always visible; progress bar 25/75 split; sticky formats fieldset" (rebased revision) + $linkName = $link['name'] ?? ''; + $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; + $linkLockedYear = $link['locked_year'] ?? null; ++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) ++\\\\\\\ to: nwozruss f8d22bfa "decorelate formats from fichiers: no HTMX toggles, all slots always visible; progress bar 25/75 split; sticky formats fieldset" (rebased revision) +++ $linkName = $link['name'] ?? ''; +++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: nwozruss f8d22bfa "decorelate formats from fichiers: no HTMX toggles, all slots always visible; progress bar 25/75 split; sticky formats fieldset" (rebased revision) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +- $linkName = $link['name'] ?? ''; +- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: mrtoqozz 734f8a46 "server-side upload progress: poll /admin/actions/upload-progress.php via token; progress file written during PeerTube uploads" (rebased revision) + $linkName = $link['name'] ?? ''; + $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; + $linkLockedYear = $link['locked_year'] ?? null; ++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) ++\\\\\\\ to: mrtoqozz a1fbaf50 "server-side upload progress: poll /admin/actions/upload-progress.php via token; progress file written during PeerTube uploads" (rebased revision) +++ $linkName = $link['name'] ?? ''; ++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; ?> diff --git a/app/templates/partials/form/form.php b/app/templates/partials/form/form.php index bd7a022..8368cd7 100644 --- a/app/templates/partials/form/form.php +++ b/app/templates/partials/form/form.php @@ -144,6 +144,7 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? [];
+ @@ -484,10 +485,12 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? [];