diff --git a/TODO.md b/TODO.md index 7f6e3d9..8adf3a7 100644 --- a/TODO.md +++ b/TODO.md @@ -61,3 +61,7 @@ - [x] Add CSS: `.file-preview-list`, `.fp-item`, `.fp-thumb`, `.fp-icon`, `.fp-meta`, `.fp-name`, `.fp-size` - [x] Add CSS: `.recap-file-list`, `.recap-file-item`, `.recap-file-thumb`, `.recap-file-icon`, `.recap-file-meta`, `.recap-file-type-badge`, `.recap-file-date` - [x] Add CSS: `.partage-recap`, `.recap-section`, `.recap-dl` for partage recap layout + +## Bug Fixes (2026-04-29) + +- [x] Fix parse error in `Database.php` line 2005 — escaped apostrophe in `d'introduction` diff --git a/app/migrations/applied/004_add_form_help_blocks.sql b/app/migrations/applied/004_add_form_help_blocks.sql new file mode 100644 index 0000000..4bd1e15 --- /dev/null +++ b/app/migrations/applied/004_add_form_help_blocks.sql @@ -0,0 +1,26 @@ +-- Form help blocks: stores per-fieldset student-facing explanatory text +-- displayed in the /partage share-link submission form. +-- Each row is keyed by a stable slug matching TODO locations in partage/index.php. + +CREATE TABLE IF NOT EXISTS form_help_blocks ( + key TEXT PRIMARY KEY, + content TEXT NOT NULL DEFAULT '', + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +CREATE TRIGGER IF NOT EXISTS update_form_help_blocks_timestamp +AFTER UPDATE ON form_help_blocks +BEGIN + UPDATE form_help_blocks SET updated_at = CURRENT_TIMESTAMP WHERE key = NEW.key; +END; + +-- Seed the eight block slots referenced in partage/index.php. +INSERT OR IGNORE INTO form_help_blocks (key, content) VALUES + ('partage_intro', ''), + ('fieldset_tfe_info', ''), + ('fieldset_synopsis', ''), + ('fieldset_jury', ''), + ('fieldset_academic', ''), + ('fieldset_files', ''), + ('fieldset_access', ''), + ('fieldset_email', ''); diff --git a/app/public/admin/actions/form-help.php b/app/public/admin/actions/form-help.php new file mode 100644 index 0000000..391edf8 --- /dev/null +++ b/app/public/admin/actions/form-help.php @@ -0,0 +1,39 @@ +setFormHelpBlock($key, $content); + App::flash('success', 'Bloc « ' . htmlspecialchars($key) . ' » mis à jour.'); +} catch (Exception $e) { + error_log('form-help save error: ' . $e->getMessage()); + App::flash('error', 'Erreur lors de la sauvegarde : ' . htmlspecialchars($e->getMessage())); +} + +$_SESSION['csrf_token'] = bin2hex(random_bytes(32)); +header('Location: /admin/contenus.php#form-help-blocks'); +exit; diff --git a/app/public/admin/contenus-edit.php b/app/public/admin/contenus-edit.php index 75cca79..2cdb379 100644 --- a/app/public/admin/contenus-edit.php +++ b/app/public/admin/contenus-edit.php @@ -12,8 +12,9 @@ if (empty($_SESSION["csrf_token"])) { $allowedPageSlugs = ["about", "licenses", "charte"]; $allowedApropos = ["contacts", "credits"]; -$pageSlug = $_GET["slug"] ?? ""; -$aproposKey = $_GET["apropos"] ?? ""; +$pageSlug = $_GET["slug"] ?? ""; +$aproposKey = $_GET["apropos"] ?? ""; +$formHelpKey = $_GET["form_block"] ?? ""; if ($pageSlug && !in_array($pageSlug, $allowedPageSlugs)) { $pageSlug = ""; @@ -21,8 +22,11 @@ if ($pageSlug && !in_array($pageSlug, $allowedPageSlugs)) { if ($aproposKey && !in_array($aproposKey, $allowedApropos)) { $aproposKey = ""; } +if ($formHelpKey && !in_array($formHelpKey, Database::FORM_HELP_KEYS, true)) { + $formHelpKey = ""; +} -if (!$pageSlug && !$aproposKey) { +if (!$pageSlug && !$aproposKey && !$formHelpKey) { header("Location: /admin/contenus.php"); exit(); } @@ -37,6 +41,10 @@ try { } $editTitle = $page["title"]; $editType = "page"; + } elseif ($formHelpKey) { + $editType = "form_help"; + $formHelpContent = $db->getFormHelpBlock($formHelpKey); + $editTitle = Database::FORM_HELP_LABELS[$formHelpKey] ?? $formHelpKey; } else { $editType = "apropos"; $value = $db->getAproposContent($aproposKey); @@ -65,6 +73,8 @@ JS; $initialContent = ''; if ($editType === 'page') { $initialContent = $page["content"] ?? ""; +} elseif ($editType === 'form_help') { + $initialContent = $formHelpContent; } $isAdmin = true; diff --git a/app/public/admin/contenus.php b/app/public/admin/contenus.php index 5ca39c5..4543709 100644 --- a/app/public/admin/contenus.php +++ b/app/public/admin/contenus.php @@ -6,10 +6,15 @@ require_once __DIR__ . '/../../src/Database.php'; $pageTitle = "Contenus"; +if (empty($_SESSION['csrf_token'])) { + $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); +} + try { $db = new Database(); - $pages = $db->getAllPages(); - $aproposKeys = $db->getAllAproposContents(); + $pages = $db->getAllPages(); + $aproposKeys = $db->getAllAproposContents(); + $formHelpBlocks = $db->getAllFormHelpBlocks(); } catch (Exception $e) { error_log("Error loading contenus: " . $e->getMessage()); die("Erreur lors du chargement des contenus."); diff --git a/app/src/Database.php b/app/src/Database.php index bd9d4df..4f15af8 100644 --- a/app/src/Database.php +++ b/app/src/Database.php @@ -1979,6 +1979,80 @@ class Database { return $stmt->fetchAll(); } + // ======================================================================== + // FORM HELP BLOCKS + // ======================================================================== + + /** + * Known form help block keys (mirrors the seeded rows in migration 004). + */ + public const FORM_HELP_KEYS = [ + 'partage_intro', + 'fieldset_tfe_info', + 'fieldset_synopsis', + 'fieldset_jury', + 'fieldset_academic', + 'fieldset_files', + 'fieldset_access', + 'fieldset_email', + ]; + + /** + * Human-readable labels for each block key (used in the admin UI). + */ + public const FORM_HELP_LABELS = [ + 'partage_intro' => 'Introduction du formulaire', + 'fieldset_tfe_info' => 'Informations du TFE — note d\'introduction', + 'fieldset_synopsis' => 'Synopsis — explication', + 'fieldset_jury' => 'Composition du jury — note', + 'fieldset_academic' => 'Cadre académique — note', + 'fieldset_files' => 'Fichiers — note', + 'fieldset_access' => 'Visibilité / Accès — explication', + 'fieldset_email' => 'E-mail de confirmation — note', + ]; + + /** + * Get a single form help block by key. Returns '' when missing. + */ + public function getFormHelpBlock(string $key): string { + $stmt = $this->pdo->prepare( + "SELECT content FROM form_help_blocks WHERE key = ? LIMIT 1" + ); + $stmt->execute([$key]); + $val = $stmt->fetchColumn(); + return ($val !== false) ? (string)$val : ''; + } + + /** + * Upsert a form help block. + */ + public function setFormHelpBlock(string $key, string $content): void { + if (!in_array($key, self::FORM_HELP_KEYS, true)) { + throw new Exception("Unknown form help block key: $key"); + } + $this->pdo->prepare( + "INSERT INTO form_help_blocks (key, content, updated_at) + VALUES (?, ?, CURRENT_TIMESTAMP) + ON CONFLICT(key) DO UPDATE SET content = excluded.content, + updated_at = CURRENT_TIMESTAMP" + )->execute([$key, $content]); + } + + /** + * Return all form help blocks as [ key => ['content' => ..., 'updated_at' => ...] ]. + */ + public function getAllFormHelpBlocks(): array { + $stmt = $this->pdo->query( + "SELECT key, content, updated_at FROM form_help_blocks ORDER BY key" + ); + $rows = $stmt->fetchAll(); + $out = []; + foreach ($rows as $r) { + $out[$r['key']] = ['content' => $r['content'], 'updated_at' => $r['updated_at']]; + } + return $out; + } + // ======================================================================== // SINGLETON PATTERN ENFORCEMENT // ======================================================================== diff --git a/app/templates/admin/contenus-edit.php b/app/templates/admin/contenus-edit.php index fd90249..342ccfd 100644 --- a/app/templates/admin/contenus-edit.php +++ b/app/templates/admin/contenus-edit.php @@ -17,6 +17,23 @@ + +
Ce texte est affiché dans le formulaire de soumission des étudiant·es (lien de partage). Supporte le Markdown.
+ +Ces textes apparaissent dans le formulaire de soumission accessible via les liens de partage. Ils permettent d'expliquer aux étudiant·es comment remplir chaque section. Supporte le Markdown.
+ +| Bloc | +Aperçu | +Mis à jour | +Action | +
|---|---|---|---|
| = htmlspecialchars($label) ?> | += $block['content'] !== '' ? htmlspecialchars($preview) : $preview ?> | += htmlspecialchars($block['updated_at'] ?? '—') ?> | ++ Éditer + | +