Fix form field required states & missing fields per spec

- Admin add: add contact_public checkbox (matching edit form)
- All forms: formats checkbox-list now required
- All forms: jury promoteur·ice interne required, lecteur·ice interne/externe required
- All forms: licence select now required
- Admin edit: add E-mail de confirmation fieldset
- Partage: contact always visible when provided (no contact_public field)
- Partage: filter PACS from AP programs dropdown
- Server-side validation: formats, jury, licence required (create + edit controllers)
- Autofocus mappings for new validation errors
- No duplicate asterisks — verified across all rendered fields
- fix: add missing old() function in admin edit controller
- refactor: move admin email field to Backoffice as Contact interne, never send email
- Untrack admin.log (covered by .gitignore)
This commit is contained in:
Pontoporeia
2026-05-07 19:54:52 +02:00
parent 51f9f56e09
commit 696259afae
11 changed files with 307 additions and 45 deletions

View File

@@ -162,6 +162,54 @@ class ThesisEditController
throw new InvalidArgumentException('ID de TFE invalide.');
}
// ── Basic validation (same required fields as create) ──────────────────
$errors = [];
$titre = trim($post['titre'] ?? '');
if ($titre === '') $errors[] = 'Le titre est requis.';
$auteurice = trim($post['auteurice'] ?? '');
if ($auteurice === '') $errors[] = "L'auteur·ice est requis.";
$synopsis = trim($post['synopsis'] ?? '');
if ($synopsis === '') $errors[] = 'Le synopsis est requis.';
$annee = intval($post['année'] ?? 0);
if ($annee < 2000 || $annee > ((int)date('Y') + 1)) $errors[] = "L'année est invalide.";
$orientationId = intval($post['orientation'] ?? 0);
if ($orientationId <= 0) $errors[] = "L'orientation est requise.";
$apProgramId = intval($post['ap'] ?? 0);
if ($apProgramId <= 0) $errors[] = "L'atelier pluridisciplinaire est requis.";
$finalityId = intval($post['finality'] ?? 0);
if ($finalityId <= 0) $errors[] = 'La finalité est requise.';
// Languages
$langIds = isset($post['languages']) && is_array($post['languages']) ? $post['languages'] : [];
if (empty($langIds)) $errors[] = 'Au moins une langue est requise.';
// Formats
$fmtIds = isset($post['formats']) && is_array($post['formats']) ? $post['formats'] : [];
if (empty($fmtIds)) $errors[] = 'Au moins un format est requis.';
// Licence
$licenseId = filter_var($post['license_id'] ?? '', FILTER_VALIDATE_INT) ?: null;
$licenseCustom = trim($post['license_custom'] ?? '');
if (!$licenseId && $licenseCustom === '') $errors[] = 'Une licence est requise.';
// Jury
$hasPromoteur = !empty(trim($post['jury_promoteur'] ?? ''));
$hasLecteurInt = false;
$hasLecteurExt = false;
foreach ($post['jury_lecteur_interne'] ?? [] as $n) {
if (trim((string)$n) !== '') { $hasLecteurInt = true; break; }
}
foreach ($post['jury_lecteur_externe'] ?? [] as $n) {
if (trim((string)$n) !== '') { $hasLecteurExt = true; break; }
}
if (!$hasPromoteur) $errors[] = 'Un·e promoteur·ice interne est requis.';
if (!$hasLecteurInt) $errors[] = 'Au moins un·e lecteur·ice interne est requis.';
if (!$hasLecteurExt) $errors[] = 'Au moins un·e lecteur·ice externe est requis.';
if (!empty($errors)) {
throw new RuntimeException(implode(' ', $errors));
}
$this->db->beginTransaction();
try {
@@ -531,6 +579,33 @@ class ThesisEditController
if (str_contains($message, 'auteur') || str_contains($message, 'Auteur')) {
return 'auteurice';
}
if (str_contains($message, 'orientation')) {
return 'orientation';
}
if (str_contains($message, 'atelier')) {
return 'ap';
}
if (str_contains($message, 'finalité')) {
return 'finality';
}
if (str_contains($message, 'langue')) {
return 'languages';
}
if (str_contains($message, 'format')) {
return 'formats';
}
if (str_contains($message, 'licence')) {
return 'license_id';
}
if (str_contains($message, 'promoteur')) {
return 'jury_promoteur';
}
if (str_contains($message, 'lecteur·ice interne')) {
return 'jury_lecteur_interne[]';
}
if (str_contains($message, 'lecteur·ice externe')) {
return 'jury_lecteur_externe[]';
}
return null;
}