mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
feat: add FilePond pools for couverture, note_intention, video, audio; refactor queue config
This commit is contained in:
3
TODO.md
3
TODO.md
@@ -23,3 +23,6 @@
|
|||||||
- [x] Decouple format extras from main file inputs — slot-based HTMX swaps preserve FilePond instances
|
- [x] Decouple format extras from main file inputs — slot-based HTMX swaps preserve FilePond instances
|
||||||
- [x] Fix initFilePonds → window.XamxamInitFilePonds bug
|
- [x] Fix initFilePonds → window.XamxamInitFilePonds bug
|
||||||
- [x] Verify backend $_FILES['queue_file'][*] data flow unchanged
|
- [x] Verify backend $_FILES['queue_file'][*] data flow unchanged
|
||||||
|
- [x] Add FilePond pools for couverture + note_intention (extracted from file-field.php inner <form>)
|
||||||
|
- [x] Fix video/audio pools: allowMultiple: true, not single-file
|
||||||
|
- [x] Add QUEUE_CONFIG for cover (20MB single) and note_intention (100MB PDF single)
|
||||||
|
|||||||
@@ -15,24 +15,38 @@
|
|||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// ── Constants (mirrors file-upload-queue.js ALLOWED_BY_TYPE) ──────────
|
// ── Per-queue-type configuration ────────────────────────────────────
|
||||||
|
|
||||||
var ALLOWED_BY_TYPE = {
|
var QUEUE_CONFIG = {
|
||||||
tfe: {
|
tfe: {
|
||||||
exts: ["jpg","jpeg","png","gif","webp","pdf","mp4","webm","ogv","mov","mp3","ogg","oga","wav","flac","aac","m4a","vtt","zip","tar","gz","tgz"],
|
exts: ["jpg","jpeg","png","gif","webp","pdf","mp4","webm","ogv","mov","mp3","ogg","oga","wav","flac","aac","m4a","vtt","zip","tar","gz","tgz"],
|
||||||
maxSize: function (f) { return (/\.pdf$/i.test(f.name) ? 100 : 500) * 1024 * 1024; },
|
maxSize: function (f) { return (/\.pdf$/i.test(f.name) ? 100 : 500) * 1024 * 1024; },
|
||||||
|
multiple: true,
|
||||||
},
|
},
|
||||||
video: {
|
video: {
|
||||||
exts: ["mp4","webm","ogv","mov"],
|
exts: ["mp4","webm","ogv","mov"],
|
||||||
maxSize: function () { return 500 * 1024 * 1024; },
|
maxSize: function () { return 500 * 1024 * 1024; },
|
||||||
|
multiple: true,
|
||||||
},
|
},
|
||||||
audio: {
|
audio: {
|
||||||
exts: ["mp3","ogg","oga","wav","flac","aac","m4a"],
|
exts: ["mp3","ogg","oga","wav","flac","aac","m4a"],
|
||||||
maxSize: function () { return 500 * 1024 * 1024; },
|
maxSize: function () { return 500 * 1024 * 1024; },
|
||||||
|
multiple: true,
|
||||||
},
|
},
|
||||||
annexe: {
|
annexe: {
|
||||||
exts: ["pdf","zip","tar","gz","tgz"],
|
exts: ["pdf","zip","tar","gz","tgz"],
|
||||||
maxSize: function () { return 500 * 1024 * 1024; },
|
maxSize: function () { return 500 * 1024 * 1024; },
|
||||||
|
multiple: true,
|
||||||
|
},
|
||||||
|
cover: {
|
||||||
|
exts: ["jpg","jpeg","png","webp"],
|
||||||
|
maxSize: function () { return 20 * 1024 * 1024; },
|
||||||
|
multiple: false,
|
||||||
|
},
|
||||||
|
note_intention: {
|
||||||
|
exts: ["pdf"],
|
||||||
|
maxSize: function () { return 100 * 1024 * 1024; },
|
||||||
|
multiple: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,6 +57,8 @@
|
|||||||
"video-files-input": "video",
|
"video-files-input": "video",
|
||||||
"audio-files-input": "audio",
|
"audio-files-input": "audio",
|
||||||
"annexe-files-input": "annexe",
|
"annexe-files-input": "annexe",
|
||||||
|
"couverture": "cover",
|
||||||
|
"note_intention": "note_intention",
|
||||||
};
|
};
|
||||||
|
|
||||||
function ext(fn) {
|
function ext(fn) {
|
||||||
@@ -53,10 +69,9 @@
|
|||||||
// ── FilePond configuration per queue type ─────────────────────────────
|
// ── FilePond configuration per queue type ─────────────────────────────
|
||||||
|
|
||||||
function buildFilePondOptions(queueType, input) {
|
function buildFilePondOptions(queueType, input) {
|
||||||
var rules = ALLOWED_BY_TYPE[queueType];
|
var cfg = QUEUE_CONFIG[queueType];
|
||||||
if (!rules) return null;
|
if (!cfg) return null;
|
||||||
|
|
||||||
// Build acceptedFileTypes from extensions
|
|
||||||
var mimeMap = {
|
var mimeMap = {
|
||||||
jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png",
|
jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png",
|
||||||
gif: "image/gif", webp: "image/webp",
|
gif: "image/gif", webp: "image/webp",
|
||||||
@@ -67,29 +82,20 @@
|
|||||||
vtt: "text/vtt",
|
vtt: "text/vtt",
|
||||||
zip: "application/zip", tar: "application/x-tar", gz: "application/gzip", tgz: "application/gzip",
|
zip: "application/zip", tar: "application/x-tar", gz: "application/gzip", tgz: "application/gzip",
|
||||||
};
|
};
|
||||||
var accepted = rules.exts.map(function(e) { return mimeMap[e] || ("." + e); });
|
var accepted = cfg.exts.map(function(e) { return mimeMap[e] || ("." + e); });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
allowMultiple: (queueType !== "video" && queueType !== "audio"),
|
allowMultiple: cfg.multiple,
|
||||||
allowReorder: true,
|
allowReorder: true,
|
||||||
storeAsFile: true,
|
storeAsFile: true,
|
||||||
labelIdle: "Glissez-déposez vos fichiers ou <span class='filepond--label-action'>Parcourir</span>",
|
labelIdle: "Glissez-déposez vos fichiers ou <span class='filepond--label-action'>Parcourir</span>",
|
||||||
acceptedFileTypes: accepted,
|
acceptedFileTypes: accepted,
|
||||||
labelFileTypeNotAllowed: "Type de fichier non accepté",
|
labelFileTypeNotAllowed: "Type de fichier non accepté",
|
||||||
fileValidateTypeLabelExpectedTypes: "Types acceptés : " + rules.exts.map(function(e) { return "." + e; }).join(", "),
|
fileValidateTypeLabelExpectedTypes: "Types acceptés : " + cfg.exts.map(function(e) { return "." + e; }).join(", "),
|
||||||
fileValidateSizeLabelMaxFileSize: function (fileSize) {
|
maxFileSize: function () { return "500MB"; },
|
||||||
var max = rules.maxSize({name: "", size: 0});
|
|
||||||
return "Taille maximale : " + Math.round(max / 1024 / 1024) + " MB";
|
|
||||||
},
|
|
||||||
maxFileSize: function () {
|
|
||||||
// We can't do per-file max based on extension easily with FilePond.
|
|
||||||
// Use the larger limit and validate PDFs as a special case in the
|
|
||||||
// beforeAddFile callback.
|
|
||||||
return "500MB";
|
|
||||||
},
|
|
||||||
beforeAddFile: function (item) {
|
beforeAddFile: function (item) {
|
||||||
var f = item.file;
|
var f = item.file;
|
||||||
var max = rules.maxSize(f);
|
var max = cfg.maxSize(f);
|
||||||
if (f.size > max) {
|
if (f.size > max) {
|
||||||
var maxMb = Math.round(max / 1024 / 1024);
|
var maxMb = Math.round(max / 1024 / 1024);
|
||||||
return {
|
return {
|
||||||
@@ -123,9 +129,8 @@
|
|||||||
var id = input.id;
|
var id = input.id;
|
||||||
var queueType = INPUT_ID_TO_TYPE[id];
|
var queueType = INPUT_ID_TO_TYPE[id];
|
||||||
if (!queueType) {
|
if (!queueType) {
|
||||||
// Try to infer from data attr on the container
|
// Try data-queue-type on the input itself
|
||||||
var container = input.closest("[data-queue-type]");
|
queueType = input.dataset.queueType || null;
|
||||||
if (container) queueType = container.dataset.queueType;
|
|
||||||
}
|
}
|
||||||
if (!queueType) return;
|
if (!queueType) return;
|
||||||
|
|
||||||
|
|||||||
@@ -197,36 +197,35 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- ── 1. Couverture (always) ── -->
|
<!-- ── 1. Couverture (always, FilePond single-file) ── -->
|
||||||
<div>
|
|
||||||
<?php
|
<?php
|
||||||
$_cover = $_POST['_cover'] ?? null;
|
$_cover = $_POST['_cover'] ?? null;
|
||||||
// Only show upload field in add mode or when no existing cover
|
if (!$editMode || !$_cover): ?>
|
||||||
if (!$editMode || !$_cover):
|
<div class="admin-form-group">
|
||||||
$name = 'couverture';
|
<label for="couverture">Image de couverture (optionnel)</label>
|
||||||
$label = 'Image de couverture (optionnel)';
|
<div class="admin-file-input">
|
||||||
$accept = 'image/jpeg,image/png,image/webp';
|
<input type="file" id="couverture"
|
||||||
$hint = 'JPG, PNG ou WEBP. Format 4:3 recommandé. Max 20 MB.';
|
name="couverture"
|
||||||
$required = false;
|
accept="image/jpeg,image/png,image/webp"
|
||||||
$id = 'couverture';
|
class="tfe-file-picker tfe-file-picker--single"
|
||||||
include APP_ROOT . '/templates/partials/form/file-field.php';
|
data-queue-type="cover">
|
||||||
endif;
|
<small>JPG, PNG ou WEBP. Format 4:3 recommandé. Max 20 MB.</small>
|
||||||
unset($_cover);
|
|
||||||
?>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; unset($_cover); ?>
|
||||||
|
|
||||||
<!-- ── 2. Note d'intention (always) ── -->
|
<!-- ── 2. Note d'intention (always, FilePond single-file) ── -->
|
||||||
<div>
|
<div class="admin-form-group">
|
||||||
<?php
|
<label for="note_intention">Note d'intention<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||||
$name = 'note_intention';
|
<div class="admin-file-input">
|
||||||
$label = 'Note d\'intention';
|
<input type="file" id="note_intention"
|
||||||
$accept = '.pdf';
|
name="note_intention"
|
||||||
$hint = 'PDF uniquement. Max 100 MB. Si votre fichier est trop lourd, compressez-le avec <a href="https://www.bentopdf.com" target="_blank" rel="noopener">bentopdf.com</a>.';
|
accept=".pdf"
|
||||||
$hintRaw = true;
|
class="tfe-file-picker tfe-file-picker--single"
|
||||||
$required = !$adminMode;
|
data-queue-type="note_intention"
|
||||||
$id = 'note_intention';
|
<?= !$adminMode ? 'required' : '' ?>>
|
||||||
include APP_ROOT . '/templates/partials/form/file-field.php';
|
<small>PDF uniquement. Max 100 MB. Si votre fichier est trop lourd, compressez-le avec <a href="https://www.bentopdf.com" target="_blank" rel="noopener">bentopdf.com</a>.</small>
|
||||||
?>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ── 3. TFE — multi-file upload (FilePond) ── -->
|
<!-- ── 3. TFE — multi-file upload (FilePond) ── -->
|
||||||
|
|||||||
@@ -350,6 +350,32 @@
|
|||||||
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
|
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
|
||||||
+\\\\\\\ to: yuuqpnwp 6a989604 "Fix FilePond integration: decouple format extras from main file inputs" (rebased revision)
|
+\\\\\\\ to: yuuqpnwp 6a989604 "Fix FilePond integration: decouple format extras from main file inputs" (rebased revision)
|
||||||
++ $linkName = $link['name'] ?? '';
|
++ $linkName = $link['name'] ?? '';
|
||||||
|
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: yuuqpnwp 6a989604 "Fix FilePond integration: decouple format extras from main file inputs" (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: zmvkxops 2da46d51 (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: zmvkxops 23fecead (rebased revision)
|
||||||
|
++ $linkName = $link['name'] ?? '';
|
||||||
|
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: zmvkxops 23fecead (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: rptplqsr 54c10953 "Add FilePond pools for couverture, note_intention, video, audio" (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: rptplqsr 5f9568bd "Add FilePond pools for couverture, note_intention, video, audio" (rebased revision)
|
||||||
|
++ $linkName = $link['name'] ?? '';
|
||||||
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
|
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
|
||||||
?>
|
?>
|
||||||
<tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer">
|
<tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer">
|
||||||
|
|||||||
Reference in New Issue
Block a user