Add autosave draft system for partage form with HTMX-based session persistence

- 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)
This commit is contained in:
Pontoporeia
2026-06-11 10:32:53 +02:00
parent 4b37a05be3
commit 99125cc8e3
33 changed files with 1388 additions and 806 deletions

View File

@@ -252,20 +252,20 @@ class DatabaseExtendedTest extends TestCase
$pdo = TestDatabase::getPDO();
// Count seed languages first (français, anglais, néerlandais, italian)
$seedCount = (int)$pdo->query("SELECT COUNT(*) FROM languages WHERE deleted_at IS NULL")->fetchColumn();
$seedCount = (int)$pdo->query('SELECT COUNT(*) FROM languages WHERE deleted_at IS NULL')->fetchColumn();
// Insert two languages that differ only by case
$pdo->prepare("INSERT INTO languages (name) VALUES ('TestLang')")->execute();
$pdo->prepare("INSERT INTO languages (name) VALUES ('testlang')")->execute();
// Both seed + 2 new should exist before dedup
$before = $pdo->query("SELECT COUNT(*) FROM languages WHERE deleted_at IS NULL")->fetchColumn();
$before = $pdo->query('SELECT COUNT(*) FROM languages WHERE deleted_at IS NULL')->fetchColumn();
$this->assertSame($seedCount + 2, (int)$before);
$this->db->deduplicateLanguages();
// One of the dupes should be soft-deleted
$after = $pdo->query("SELECT COUNT(*) FROM languages WHERE deleted_at IS NULL")->fetchColumn();
$after = $pdo->query('SELECT COUNT(*) FROM languages WHERE deleted_at IS NULL')->fetchColumn();
$this->assertSame($seedCount + 1, (int)$after);
}
@@ -293,7 +293,7 @@ class DatabaseExtendedTest extends TestCase
// Create a thesis linked to 'Français'
[$authorId, $thesisId] = TestDatabase::seedBasicThesis('Merge Test', 'Author', 2024);
$pdo->prepare("INSERT INTO thesis_languages (thesis_id, language_id) VALUES (?, ?)")
$pdo->prepare('INSERT INTO thesis_languages (thesis_id, language_id) VALUES (?, ?)')
->execute([$thesisId, $francaisId]);
// Merge Français → French
@@ -333,7 +333,7 @@ class DatabaseExtendedTest extends TestCase
$tagB = (int)$pdo->lastInsertId();
[$authorId, $thesisId] = TestDatabase::seedBasicThesis('Tag Merge', 'Author', 2024);
$pdo->prepare("INSERT INTO thesis_tags (tag_id, thesis_id) VALUES (?, ?)")
$pdo->prepare('INSERT INTO thesis_tags (tag_id, thesis_id) VALUES (?, ?)')
->execute([$tagB, $thesisId]);
$this->db->mergeTag($tagB, $tagA);