From d5fee1acfbdbdce705d0bd123233b514605998e1 Mon Sep 17 00:00:00 2001 From: Pontoporeia Date: Sun, 10 May 2026 18:08:27 +0200 Subject: [PATCH] fix: repair form submission with queued files + add comprehensive debug logging - Replace fetch(redirect:manual) with XMLHttpRequest in file-upload-queue.js. The previous fetch-based redirect detection was broken because opaque redirects hide the Location header. XHR's responseURL reliably exposes the final URL after server-side redirects. - Add console.log tracing at every decision point in submit interception: entry, hasFiles check, enctype check, double-submit guard, XHR status, redirect detection, error fallback. - Add error_log entry-point logging to all 16 admin action files plus the partage/index.php submission handler and password gate. Each logs: request method, content type/length, POST keys, file counts, and queue-specific file counts where applicable. - Add double-submit guard (_xamxamActiveSubmit) to prevent duplicate XHR sends when the native submit handler fires after interception. --- TODO.md | 5 + app/public/admin/actions/acces-etudiante.php | 2 + app/public/admin/actions/access-request.php | 1 + app/public/admin/actions/account.php | 2 + app/public/admin/actions/apropos.php | 1 + app/public/admin/actions/delete.php | 1 + app/public/admin/actions/edit.php | 2 + app/public/admin/actions/form-help.php | 1 + app/public/admin/actions/formulaire.php | 4 +- app/public/admin/actions/language.php | 1 + app/public/admin/actions/maintenance.php | 1 + app/public/admin/actions/page.php | 1 + app/public/admin/actions/publish.php | 1 + app/public/admin/actions/settings.php | 1 + app/public/admin/actions/smtp-test.php | 1 + app/public/admin/actions/tag.php | 1 + app/public/admin/actions/visibility.php | 1 + app/public/admin/index.php | 1 + app/public/admin/login.php | 2 + app/public/assets/js/file-upload-queue.js | 93 ++++++++++++++----- app/public/partage/index.php | 2 + app/templates/admin/acces.php | 13 +++ app/templates/partials/form/checkbox-list.php | 4 +- 23 files changed, 114 insertions(+), 28 deletions(-) diff --git a/TODO.md b/TODO.md index 7410efc..c7c0808 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,8 @@ # TODO - [x] Replace HTMX+PHP file upload queues with client-side JS +- [x] Fix submit button on all forms — add JS/PHP debug logging + - [x] Fix file-upload-queue.js: redirect detection broken due to opaque redirect (switched from fetch to XHR for reliable responseURL) + - [x] Add `console.log` tracing on JS submit interception + - [x] Add `error_log` entry-point logging to all 16 PHP action files + - [x] Add double-submit guard (`_xamxamActiveSubmit`) diff --git a/app/public/admin/actions/acces-etudiante.php b/app/public/admin/actions/acces-etudiante.php index 5ce6ea9..33a0441 100644 --- a/app/public/admin/actions/acces-etudiante.php +++ b/app/public/admin/actions/acces-etudiante.php @@ -7,6 +7,8 @@ require_once __DIR__ . '/../../../src/AdminAuth.php'; require_once __DIR__ . '/../../../src/ShareLink.php'; require_once __DIR__ . '/../../../src/AdminLogger.php'; +error_log('[acces-etudiante.php] ENTRY | method=' . $_SERVER['REQUEST_METHOD'] . ' | action=' . ($_POST['action'] ?? 'none') . ' | post_keys=' . implode(',', array_keys($_POST))); + App::adminGuard(); if ($_SERVER['REQUEST_METHOD'] !== 'POST' diff --git a/app/public/admin/actions/access-request.php b/app/public/admin/actions/access-request.php index 7a8740a..8b4d628 100644 --- a/app/public/admin/actions/access-request.php +++ b/app/public/admin/actions/access-request.php @@ -6,6 +6,7 @@ */ require_once __DIR__ . '/../../../bootstrap.php'; require_once __DIR__ . '/../../../src/AdminAuth.php'; +error_log('[access-request.php] ENTRY | method=' . $_SERVER['REQUEST_METHOD'] . ' | action=' . ($_POST['action'] ?? 'none') . ' | request_id=' . ($_POST['request_id'] ?? 0) . ' | post_keys=' . implode(',', array_keys($_POST))); AdminAuth::requireLogin(); if (!isset($_POST['csrf_token'], $_SESSION['csrf_token']) diff --git a/app/public/admin/actions/account.php b/app/public/admin/actions/account.php index 87f8814..57f93de 100644 --- a/app/public/admin/actions/account.php +++ b/app/public/admin/actions/account.php @@ -11,6 +11,8 @@ require_once __DIR__ . '/../../../bootstrap.php'; require_once __DIR__ . '/../../../src/AdminAuth.php'; require_once __DIR__ . '/../../../src/Database.php'; +error_log('[account.php] ENTRY | method=' . $_SERVER['REQUEST_METHOD'] . ' | action=' . ($_POST['action'] ?? 'change_password') . ' | post_keys=' . implode(',', array_keys($_POST))); + AdminAuth::requireLogin(); // ── CSRF ────────────────────────────────────────────────────────────────────── diff --git a/app/public/admin/actions/apropos.php b/app/public/admin/actions/apropos.php index 24276eb..3fa006c 100644 --- a/app/public/admin/actions/apropos.php +++ b/app/public/admin/actions/apropos.php @@ -5,6 +5,7 @@ */ require_once __DIR__ . "/../../../bootstrap.php"; require_once __DIR__ . '/../../../src/AdminAuth.php'; +error_log('[apropos.php] ENTRY | method=' . $_SERVER['REQUEST_METHOD'] . ' | key=' . ($_POST['apropos_key'] ?? 'none') . ' | post_keys=' . implode(',', array_keys($_POST))); AdminAuth::requireLogin(); // CSRF check diff --git a/app/public/admin/actions/delete.php b/app/public/admin/actions/delete.php index e1e53d9..68fcb1f 100644 --- a/app/public/admin/actions/delete.php +++ b/app/public/admin/actions/delete.php @@ -1,6 +1,7 @@ 0; }); - if (!hasFiles) return; // Normal submit + + console.log("[file-upload-queue] submit event fired | action=" + (form.getAttribute("action") || "") + " | hasFiles=" + hasFiles + " | enctype=" + form.enctype); + + if (!hasFiles) { + console.log("[file-upload-queue] no queued files, passing through native submit"); + markClean(); + return; // Normal submit + } // Check if the form can accept multipart - if (form.enctype !== "multipart/form-data") return; + if (form.enctype !== "multipart/form-data") { + console.log("[file-upload-queue] form enctype is not multipart/form-data, passing through"); + markClean(); + return; + } + + // Prevent double-submit (in case native submit falls through) + if (_xamxamActiveSubmit) { + console.log("[file-upload-queue] already submitting, skipping"); + e.preventDefault(); + return; + } + _xamxamActiveSubmit = true; e.preventDefault(); @@ -392,32 +414,53 @@ markClean(); - // Use fetch so we can inspect the response before navigation. - // The server either redirects (302 → we navigate to that URL) - // or returns the form page with errors (200 → replace the page). - fetch(form.getAttribute("action") || "", { - method: "POST", - body: fd, - redirect: "manual", - }).then(function (resp) { - if (resp.type === "opaqueredirect" || resp.status === 302 || resp.status === 301) { - // Browser redirect — navigate the whole page - window.location.href = resp.headers.get("Location") || form.getAttribute("action"); + console.log("[file-upload-queue] injecting " + QUEUE_TYPES.map(function(qt) { return qt + ":" + queues[qt].length; }).join(", ") + " files into FormData"); + + // Use XMLHttpRequest instead of fetch so we can reliably read the + // final URL after redirects (fetch with redirect:manual hides headers). + var xhr = new XMLHttpRequest(); + xhr.open("POST", form.getAttribute("action") || "", true); + xhr.responseType = "text"; + + xhr.onload = function () { + _xamxamActiveSubmit = false; + var finalUrl = xhr.responseURL || ""; + + console.log("[file-upload-queue] XHR status=" + xhr.status + " | finalUrl=" + finalUrl + " | responseLength=" + (xhr.responseText ? xhr.responseText.length : 0)); + + // If the server redirected us (responseURL differs from action), navigate there. + // This handles the 302 redirect pattern used by formulaire.php and edit.php. + var actionUrl = form.getAttribute("action") || ""; + if (finalUrl && finalUrl !== actionUrl && finalUrl !== window.location.href) { + console.log("[file-upload-queue] redirecting to " + finalUrl); + window.location.href = finalUrl; return; } - // Success or error — render the HTML response - return resp.text(); - }).then(function (html) { - if (!html) return; - document.open(); - document.write(html); - document.close(); - // Re-bind after DOM replacement (for error re-renders) - setTimeout(window.XamxamInitFileUploads, 0); - }).catch(function () { - // Network error — fall back to native submit + + // 200 with HTML — likely form errors, replace page + if (xhr.status >= 200 && xhr.status < 300 && xhr.responseText) { + console.log("[file-upload-queue] rendering error response HTML"); + document.open(); + document.write(xhr.responseText); + document.close(); + // Re-bind after DOM replacement (for error re-renders) + setTimeout(window.XamxamInitFileUploads, 0); + return; + } + + // Fallback: reload current page + console.log("[file-upload-queue] unexpected response, reloading page"); + window.location.reload(); + }; + + xhr.onerror = function () { + _xamxamActiveSubmit = false; + console.error("[file-upload-queue] XHR network error, falling back to native submit"); + // Fall back to native submit form.submit(); - }); + }; + + xhr.send(fd); }); }); } diff --git a/app/public/partage/index.php b/app/public/partage/index.php index 60693a0..7bcbd98 100644 --- a/app/public/partage/index.php +++ b/app/public/partage/index.php @@ -93,6 +93,7 @@ App::boot(); // ── POST: form submission ───────────────────────────────────────────────────── if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'submit') { + error_log('[partage/index.php] SUBMIT ENTRY | slug=' . $slug . ' | content_type=' . ($_SERVER['CONTENT_TYPE'] ?? 'none') . ' | content_length=' . ($_SERVER['CONTENT_LENGTH'] ?? '0') . ' | post_keys=' . implode(',', array_keys($_POST)) . ' | files_count=' . count($_FILES) . ' | files_keys=' . implode(',', array_keys($_FILES)) . ' | queue_tfe=' . (isset($_FILES['queue_file']['name']['tfe']) ? count(array_filter($_FILES['queue_file']['name']['tfe'])) : 0)); handleShareLinkSubmission($slug); exit; } @@ -187,6 +188,7 @@ function renderShareLinkError(string $title, string $message): void function requirePasswordGate(array $link, string $slug): void { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['share_password'])) { + error_log('[partage/password-gate] ENTRY | slug=' . $slug . ' | post_keys=' . implode(',', array_keys($_POST))); require_once APP_ROOT . '/src/ShareLink.php'; $shareLinkModel = new ShareLink(Database::getInstance()); diff --git a/app/templates/admin/acces.php b/app/templates/admin/acces.php index bd61c48..a2a9ff5 100644 --- a/app/templates/admin/acces.php +++ b/app/templates/admin/acces.php @@ -259,6 +259,19 @@ +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +\\\\\\\ to: ownwlmpo c4c4c8c2 "Replace HTMX+PHP file upload queues with client-side JS" (rebased revision) ++ $linkName = $link['name'] ?? ''; +++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: ownwlmpo c4c4c8c2 "Replace HTMX+PHP file upload queues with client-side JS" (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: muzswpkw 732e19e8 "fix: repair form submission with queued files + add comprehensive debug logging" (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: muzswpkw dbe1ac28 "fix: repair form submission with queued files + add comprehensive debug logging" (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/checkbox-list.php b/app/templates/partials/form/checkbox-list.php index 7829e58..1e1ab42 100644 --- a/app/templates/partials/form/checkbox-list.php +++ b/app/templates/partials/form/checkbox-list.php @@ -16,7 +16,7 @@ * string $hxPost — optional hx-post URL for HTMX live update * string $hxTarget — optional hx-target CSS selector for HTMX swap * string $hxSwap — optional hx-swap value; default 'outerHTML' - * string $hxInclude — optional hx-include selector; default 'this, #website-url-fieldset' + * string $hxInclude — optional hx-include selector; default 'this' */ $checked = $checked ?? []; @@ -24,7 +24,7 @@ $required = $required ?? false; $hxPost = $hxPost ?? ''; $hxTarget = $hxTarget ?? ''; $hxSwap = $hxSwap ?? 'outerHTML'; -$hxInclude = $hxInclude ?? 'this, #website-url-fieldset'; +$hxInclude = $hxInclude ?? 'this'; ?>
*' : '' ?>