diff --git a/TODO.md b/TODO.md index be7fb0e..0779163 100644 --- a/TODO.md +++ b/TODO.md @@ -3,6 +3,9 @@ - [x] Fix email addresses in about.php contacts section not using EmailObfuscator for link text - [x] Raise rate limits: SearchController 30→300, request-access 3→30, partage 5→50 - [x] Make Libre option toggleable in Degré d'ouverture fieldset, move to top, remove temporary note +- [x] Mots-clés required (min 3) in partage form: red count < 3, accent ≥ 3 +- [x] Language checkbox-list no longer required when language_autre pill is present +- [x] Admin contenus: auto-save checkboxes via HTMX (Restrictions, Degré d'ouverture, Types de travaux), remove Enregistrer buttons - [x] Improve recapitulatif.php (partage): bottom margin/padding, center .thanks-success - [x] Display ALL submitted info in recapitulatif page + email recap - [x] Add "validate your info / contact xamxam@erg.be" note on recap page diff --git a/app/public/admin/actions/settings.php b/app/public/admin/actions/settings.php index 759501b..2906c6d 100644 --- a/app/public/admin/actions/settings.php +++ b/app/public/admin/actions/settings.php @@ -18,6 +18,7 @@ require_once APP_ROOT . '/src/AdminLogger.php'; $db = new Database(); $logger = AdminLogger::make(); +$isHxRequest = (isset($_SERVER['HTTP_HX_REQUEST']) && $_SERVER['HTTP_HX_REQUEST'] === 'true'); $section = $_POST['section'] ?? ''; if ($section === 'formulaire') { @@ -34,7 +35,9 @@ if ($section === 'formulaire') { $newValues[$key] = $value; } $logger->logFormSettingsUpdate($newValues); - App::flash('success', "Paramètres du formulaire mis à jour."); + if (!$isHxRequest) { + App::flash('success', "Paramètres du formulaire mis à jour."); + } } elseif ($section === 'objet_types') { $newValues = [ 'objet_these_enabled' => isset($_POST['objet_these_enabled']) ? '1' : '0', @@ -43,7 +46,9 @@ if ($section === 'formulaire') { $db->setSetting('objet_these_enabled', $newValues['objet_these_enabled']); $db->setSetting('objet_frart_enabled', $newValues['objet_frart_enabled']); $logger->logObjetTypesUpdate($newValues); - App::flash('success', "Types de travaux mis à jour."); + if (!$isHxRequest) { + App::flash('success', "Types de travaux mis à jour."); + } } elseif ($section === 'smtp') { $smtpData = [ 'host' => $_POST['smtp_host'] ?? '', @@ -96,5 +101,15 @@ if ($section === 'formulaire') { } $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); + +if ($isHxRequest) { + // Return updated CSRF tokens for all three hidden inputs on the page + $newToken = htmlspecialchars($_SESSION['csrf_token']); + echo ''; + echo ''; + echo ''; + exit; +} + header('Location: /admin/parametres.php'); exit; diff --git a/app/public/partage/language-autre-fragment.php b/app/public/partage/language-autre-fragment.php index 26f6e84..5e0e82a 100644 --- a/app/public/partage/language-autre-fragment.php +++ b/app/public/partage/language-autre-fragment.php @@ -18,5 +18,16 @@ $selectedIds = isset($_POST['languages']) && is_array($_POST['languages']) ? $_POST['languages'] : []; $anyChecked = !empty($selectedIds); + +// Also check if any "autre" language pills are present (posted as language_autre[]) +$hasLangAutre = isset($_POST['language_autre']) && is_array($_POST['language_autre']) + && count(array_filter($_POST['language_autre'], fn($l) => is_string($l) && trim($l) !== '')) > 0; + +// The "Autre(s) langue(s)" label is required if no standard language is checked. +// The "Langue(s) du TFE" checkbox list is required if neither standard languages +// nor "autre" languages are set. +$langAutreRequired = !$anyChecked; +$checkboxesRequired = !$anyChecked && !$hasLangAutre; ?> -*' : '' ?> +*' : '' ?> +*' : '' ?> diff --git a/app/templates/admin/acces.php b/app/templates/admin/acces.php index 5e5d743..da7f14f 100644 --- a/app/templates/admin/acces.php +++ b/app/templates/admin/acces.php @@ -545,6 +545,19 @@ +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +\\\\\\\ to: nqmqrqmo dd511b0d "fix: obfuscate email in contact links, raise rate limits, make Libre toggleable" (rebased revision) ++ $linkName = $link['name'] ?? ''; +++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: nqmqrqmo dd511b0d "fix: obfuscate email in contact links, raise rate limits, make Libre toggleable" (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: olzzwmwr 277c8ce4 "feat: require 3 mots-clés in partage, language asterisk toggle, admin auto-save checkboxes" (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: olzzwmwr 82533c5a "feat: require 3 mots-clés in partage, language asterisk toggle, admin auto-save checkboxes" (rebased revision) +++ $linkName = $link['name'] ?? ''; ++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; ?> diff --git a/app/templates/admin/contenus.php b/app/templates/admin/contenus.php index 0bd3c59..14ffe9c 100644 --- a/app/templates/admin/contenus.php +++ b/app/templates/admin/contenus.php @@ -88,34 +88,40 @@
Restrictions d'accès aux fichiers -
- - +
+ - - - +
Degré d'ouverture

Options de visibilité disponibles dans le formulaire d'ajout de TFE.

-
- - +
+
@@ -149,9 +163,8 @@

Active ou désactive les types de travaux dans les formulaires et la consultation. Un type désactivé ne peut plus être soumis ni affiché sur le site.

Le type TFE est toujours actif et ne peut pas être désactivé.

-
- - +
+
diff --git a/app/templates/partials/form/checkbox-list.php b/app/templates/partials/form/checkbox-list.php index 1e1ab42..003f9a1 100644 --- a/app/templates/partials/form/checkbox-list.php +++ b/app/templates/partials/form/checkbox-list.php @@ -9,7 +9,8 @@ * * Variables consumed: * string $name — input name attribute (will be posted as array: name[]) - * string $label — group label text + * string $label — group label text (htmlspecialchars'd unless $labelHtml is set) + * string $labelHtml — raw HTML label override (bypasses htmlspecialchars, optional) * array $options — each element must have 'id' and 'name' keys * array $checked — array of 'id' values that are currently checked * bool $required — whether at least one checkbox must be checked; default false @@ -27,7 +28,7 @@ $hxSwap = $hxSwap ?? 'outerHTML'; $hxInclude = $hxInclude ?? 'this'; ?>
- *' : '' ?> + *' : '')) ?>
diff --git a/app/templates/partials/form/form.php b/app/templates/partials/form/form.php index 9f7bdb9..f55717e 100644 --- a/app/templates/partials/form/form.php +++ b/app/templates/partials/form/form.php @@ -193,12 +193,14 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? []; $label = "Langue(s) du TFE :"; $options = $languages; $checked = $formData["languages"] ?? []; - $required = !$adminMode; + $_hasLangAutre = !empty($formData['language_autre']) && is_array($formData['language_autre']) && count(array_filter($formData['language_autre'], fn($l) => is_string($l) && trim($l) !== '')) > 0; + $required = !$adminMode && !$_hasLangAutre; $hxPost = $mode === 'partage' ? "/partage/language-autre-fragment" : "/admin/language-autre-fragment.php"; - $hxTarget = "#language-autre-required"; + $hxTarget = "#languages-required-asterisk"; $hxSwap = "outerHTML"; + $labelHtml = htmlspecialchars($label) . '' . ($required ? ' *' : '') . ''; include APP_ROOT . "/templates/partials/form/checkbox-list.php"; - unset($hxSwap); + unset($hxSwap, $_hasLangAutre, $labelHtml); ?>
diff --git a/app/templates/partials/form/language-search.php b/app/templates/partials/form/language-search.php index f170ac6..6e3054e 100644 --- a/app/templates/partials/form/language-search.php +++ b/app/templates/partials/form/language-search.php @@ -100,6 +100,14 @@ $langCount = count($selectedLanguages); if (countEl) countEl.textContent = n + '/' + maxLanguages; if (counter) counter.style.display = (n > 0) ? '' : 'none'; + // Toggle the checkbox-list asterisk: if any "autre" language pill + // is present, the checkbox list is no longer required. + const asteriskEl = document.getElementById('languages-required-asterisk'); + if (asteriskEl) { + const checkboxes = document.querySelectorAll('#languages-fieldset input[type="checkbox"]:checked'); + asteriskEl.innerHTML = (n === 0 && checkboxes.length === 0) ? ' *' : ''; + } + // Show/hide search input based on max const wrap = container.querySelector('.tag-search-input-wrap'); const maxMsg = container.querySelector('.tag-search-max-msg'); diff --git a/app/templates/partials/form/tag-search.php b/app/templates/partials/form/tag-search.php index 4146323..6ffed9e 100644 --- a/app/templates/partials/form/tag-search.php +++ b/app/templates/partials/form/tag-search.php @@ -17,6 +17,8 @@ * array $selectedTags — array of ['id' => int|null, 'name' => string] for pre-filled tags * string|null $id — override the id attribute prefix * int $maxTags — maximum number of tags (default 10) + * int $minTags — minimum required tags (default 0, no requirement) + * bool $required — whether minTags enforcement applies (default false) */ $name = $name ?? 'tag'; @@ -27,10 +29,13 @@ $hxPost = $hxPost ?? '/admin/tag-search-fragment.php'; $selectedTags = $selectedTags ?? []; $id = $id ?? $name; $maxTags = $maxTags ?? 10; +$minTags = $minTags ?? 0; +$required = $required ?? false; $tagCount = count($selectedTags); +$belowMin = $required && $tagCount < $minTags; ?>
- + *' : '' ?>
@@ -49,9 +54,9 @@ $tagCount = count($selectedTags);
- -