From d961f9533c2caff1cc3a0dc9604095faceb66c92 Mon Sep 17 00:00:00 2001 From: Pontoporeia Date: Wed, 22 Apr 2026 14:06:05 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20add=20objet=20field=20(tfe/th=C3=A8se/f?= =?UTF-8?q?rart)=20with=20share-link=20restriction=20and=20site-settings?= =?UTF-8?q?=20toggles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 1 + app/public/admin/actions/acces-etudiante.php | 5 +- app/public/admin/actions/settings.php | 5 +- app/public/partage/index.php | 30 ++++++++++++ .../Controllers/ThesisCreateController.php | 7 ++- app/src/Database.php | 7 ++- app/src/ShareLink.php | 12 +++-- app/storage/posterg.db | Bin 270336 -> 270336 bytes app/storage/schema.sql | 7 ++- app/storage/test.db | Bin 602112 -> 602112 bytes app/templates/admin/acces-etudiante.php | 18 +++++++ app/templates/admin/parametres.php | 46 ++++++++++++++++++ 12 files changed, 128 insertions(+), 10 deletions(-) diff --git a/TODO.md b/TODO.md index 25941af..001d62a 100644 --- a/TODO.md +++ b/TODO.md @@ -11,4 +11,5 @@ - [x] Extract form CSS into `form.css`; load it in admin add/edit via `$extraCss` and in student partage form directly; `system.css` now only used by `system.php`; `partage/thanks.php` rewritten to use design-system classes - [x] Fix student form: add missing `v_smtp_active` view to `schema.sql` (SMTP was silently skipped on fresh installs); fix `thanks.php` redirect (was `/partage/thanks.php` — blocked by nginx PHP deny rule); route `/partage/thanks` through `index.php` special-case handler - [x] Merge all migration SQL into schema.sql; delete migrations/ folder; simplify migrate.sh (009 share_links, 014 ap_programs, 011 apropos seed, missing semicolon fix) +- [x] Add `objet` field (tfe/thèse/frart) to theses; `objet_restriction` on share_links; objet_these/frart_enabled site_settings; wire into partage form, parametres, and acces-etudiante - [x] Fix student form scroll (add `overflow-y:auto` to `.student-body`); move all remaining inline styles from partage error/password-gate pages into `form.css` diff --git a/app/public/admin/actions/acces-etudiante.php b/app/public/admin/actions/acces-etudiante.php index 498e3d4..1938a01 100644 --- a/app/public/admin/actions/acces-etudiante.php +++ b/app/public/admin/actions/acces-etudiante.php @@ -25,13 +25,14 @@ switch ($action) { $expiresRaw = !empty($_POST['expires_at']) ? trim($_POST['expires_at']) : null; $expiresAt = null; if ($expiresRaw) { - // datetime-local gives "YYYY-MM-DDTHH:MM" $expiresAt = date('Y-m-d H:i:s', strtotime($expiresRaw)); if ($expiresAt <= date('Y-m-d H:i:s')) { App::redirect('/admin/acces-etudiante.php', error: "La date d'expiration doit être dans le futur."); } } - $shareLink->create(1, $password, $expiresAt); + $objetRaw = $_POST['objet_restriction'] ?? ''; + $objetRestriction = in_array($objetRaw, ['tfe', 'thèse', 'frart'], true) ? $objetRaw : null; + $shareLink->create(1, $password, $expiresAt, $objetRestriction); App::redirect('/admin/acces-etudiante.php', success: 'Lien d\'accès créé.'); break; diff --git a/app/public/admin/actions/settings.php b/app/public/admin/actions/settings.php index eedef5b..8d023b4 100644 --- a/app/public/admin/actions/settings.php +++ b/app/public/admin/actions/settings.php @@ -17,13 +17,16 @@ $db = new Database(); $section = $_POST['section'] ?? ''; if ($section === 'formulaire') { - // Save access-type toggle settings $allowed = ['access_type_libre_enabled', 'access_type_interne_enabled', 'access_type_interdit_enabled']; foreach ($allowed as $key) { $value = isset($_POST[$key]) ? '1' : '0'; $db->setSetting($key, $value); } App::flash('success', "Paramètres du formulaire mis à jour."); +} elseif ($section === 'objet_types') { + $db->setSetting('objet_these_enabled', isset($_POST['objet_these_enabled']) ? '1' : '0'); + $db->setSetting('objet_frart_enabled', isset($_POST['objet_frart_enabled']) ? '1' : '0'); + App::flash('success', "Types de travaux mis à jour."); } elseif ($section === 'smtp') { $smtpData = [ 'host' => $_POST['smtp_host'] ?? '', diff --git a/app/public/partage/index.php b/app/public/partage/index.php index 267b755..e3b7dc1 100644 --- a/app/public/partage/index.php +++ b/app/public/partage/index.php @@ -185,6 +185,19 @@ function renderShareLinkForm(string $slug, array $link): void $formData = $_SESSION['form_data_share_' . $slug] ?? []; unset($_SESSION['form_data_share_' . $slug]); + // Determine allowed objet values for this link + $siteSettings = Database::getInstance()->getAllSettings(); + $objetRestriction = $link['objet_restriction'] ?? null; + if ($objetRestriction !== null) { + // Link is locked to one type — always show only that + $allowedObjet = [$objetRestriction]; + } else { + // Build from enabled site settings + $allowedObjet = ['tfe']; + if (($siteSettings['objet_these_enabled'] ?? '1') === '1') $allowedObjet[] = 'thèse'; + if (($siteSettings['objet_frart_enabled'] ?? '1') === '1') $allowedObjet[] = 'frart'; + } + // Generate a CSRF token specific to this share link (stored in session) $shareCsrfKey = 'share_csrf_' . $slug; if (empty($_SESSION[$shareCsrfKey])) { @@ -236,6 +249,23 @@ function renderShareLinkForm(string $slug, array $link): void
Informations du TFE + 1): ?> +
+ +
+ + + +
+
+ + + + 'name']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> diff --git a/app/src/Controllers/ThesisCreateController.php b/app/src/Controllers/ThesisCreateController.php index 7ae021a..ba0ba22 100644 --- a/app/src/Controllers/ThesisCreateController.php +++ b/app/src/Controllers/ThesisCreateController.php @@ -127,6 +127,7 @@ class ThesisCreateController 'baiu_link' => $data['lien'], 'license_id' => $data['licenseId'], 'access_type_id' => $data['accessTypeId'], + 'objet' => $data['objet'], 'author_id' => $authorId, ]); @@ -275,6 +276,10 @@ class ThesisCreateController $accessTypeId = 2; // Interne } + // Objet — restricted to valid values + $validObjet = ['tfe', 'thèse', 'frart']; + $objet = in_array($post['objet'] ?? '', $validObjet, true) ? $post['objet'] : 'tfe'; + // External link (optional) $lien = ''; if (!empty($post['lien'])) { @@ -298,7 +303,7 @@ class ThesisCreateController 'auteurName', 'mail', 'showContact', 'confirmationEmail', 'annee', 'orientationId', 'apProgramId', 'finalityId', 'titre', 'subtitle', 'synopsis', 'durationInfo', 'juryMembers', 'keywords', 'languageIds', 'formatIds', - 'licenseId', 'lien', 'accessTypeId' + 'licenseId', 'lien', 'accessTypeId', 'objet' ); } diff --git a/app/src/Database.php b/app/src/Database.php index bc604f4..323f49e 100644 --- a/app/src/Database.php +++ b/app/src/Database.php @@ -1566,11 +1566,15 @@ class Database { synopsis, file_size_info, baiu_link, license_id, access_type_id, + objet, is_published, submitted_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP) + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP) "); + $validObjet = ['tfe', 'thèse', 'frart']; + $objet = in_array($data['objet'] ?? '', $validObjet, true) ? $data['objet'] : 'tfe'; + $stmt->execute([ $identifier, $data['title'], @@ -1584,6 +1588,7 @@ class Database { !empty($data['baiu_link']) ? $data['baiu_link'] : null, isset($data['license_id']) ? $data['license_id'] : null, isset($data['access_type_id']) ? (int)$data['access_type_id'] : 2, // default: Interne + $objet, ]); $thesisId = (int)$this->pdo->lastInsertId(); diff --git a/app/src/ShareLink.php b/app/src/ShareLink.php index f7c33b6..74a2cda 100644 --- a/app/src/ShareLink.php +++ b/app/src/ShareLink.php @@ -48,16 +48,20 @@ class ShareLink * @param string|null $expiresAt ISO-8601 expiration date, null = never expires * @return array The created link row */ - public function create(int $createdBy, ?string $password = null, ?string $expiresAt = null): array + public function create(int $createdBy, ?string $password = null, ?string $expiresAt = null, ?string $objetRestriction = null): array { $slug = self::generateSlug(); $passwordHash = $password !== null ? password_hash($password, PASSWORD_BCRYPT) : null; + $validObjet = ['tfe', 'thèse', 'frart']; + $objetRestriction = ($objetRestriction !== null && in_array($objetRestriction, $validObjet, true)) + ? $objetRestriction + : null; $stmt = $this->db->getConnection()->prepare( - "INSERT INTO share_links (slug, password_hash, is_active, created_by, expires_at) - VALUES (?, ?, 1, ?, ?)" + "INSERT INTO share_links (slug, objet_restriction, password_hash, is_active, created_by, expires_at) + VALUES (?, ?, ?, 1, ?, ?)" ); - $stmt->execute([$slug, $passwordHash, $createdBy, $expiresAt]); + $stmt->execute([$slug, $objetRestriction, $passwordHash, $createdBy, $expiresAt]); return $this->findBySlug($slug); } diff --git a/app/storage/posterg.db b/app/storage/posterg.db index 23b4b123f8520ae142b801c8aa7961246c019867..7060bab4fb32ae5a5dac226ced2c437a430ccc23 100644 GIT binary patch delta 502 zcmZoTAkc6?V1l%uEdv9C6A;6I=|ml4M%%`OtqF`P>lv4B=W1Zw$i~F3JAH2(V*v|L zRmEgMffBC%AW?R4NlC_e_1n$c8O_*O74nm^Qm0?EXO=KjFG)*PS8(=lb@o=!011~U zc={=5fQ5Aw)JrlBuP9Dc2eQ(N5{pXIH8r>A^fJ0Lvf0b9i3jUWKiJPG-#)*ear^v! zrqmAtYTRtBb4CdS&V2*o8Csl}=I6i+wnXFeyu#vjGN|A7Ah|04b_{wV$;psjBF z2I9=dj9BbsLx`go&$_*9K67~iH_tT&PL?DF{uY*`?K}c3VJuv&)pqRS%F2x0w%Z@b zuqolt;ob2Tt-WMksnJbiB)V*yt` zvnac`q$K0q#O>?b8O_+XoAfcdF|q(PAD=8JaG1?rhD|(JclyD8M)~&n{fyh^_cNt_ z;9=o6W8gofExdFvwr4tK;bS1{s;UA_!sea0fnphw|C8FE-&B)n#ReV zz`), e.g. 20260416-a3f9k2 + objet_restriction TEXT CHECK (objet_restriction IN ('tfe', 'thèse', 'frart')), -- NULL = no restriction password_hash TEXT, -- bcrypt hash; NULL = no password required is_active INTEGER NOT NULL DEFAULT 1, -- 1 = active, 0 = disabled usage_count INTEGER NOT NULL DEFAULT 0, -- Number of successful submissions via this link @@ -478,6 +482,7 @@ SELECT t.subtitle, t.year, t.is_doctoral, + t.objet, o.name as orientation, ap.name as ap_program, ft.name as finality_type, diff --git a/app/storage/test.db b/app/storage/test.db index 39df89d5ceb9430541eb3e61a8c9cbd060c92e3a..8be9e64458987272c8c9d7f943e78617e11e826e 100644 GIT binary patch delta 523 zcmZp8pwjR_WrDPz8v_HwWFUqC(}_C9jBbqyTN4;p)-x{M&eg!Uk&TI6clzEo#sU_g zs*1^m3ME|qL89#9l9G(`>bIM>Gn%onD&!|+rB1(S&n#i6UXqrouHfw9>g=tc0TM1z z@bpvA01N9VsF!3MUQwK?4rHYjB^H&aYie%K>1A|hWV4rH6A#v%ez2cWzI}c_6j1&xwtPD)8OpLWz5sFJPQj1gZDV}cD&wNgRjX#Qk{{jC2{zd#<{89Wxn*}Gh z@f(OU8#7|DlMNw`Vm#~iuKCR61>8K>7&uvy82DRQlD6{*u!OO2wN~4)iz_QLcH3@$ zAj7hlDNqL-ekJimsl_ElnaL%Y`FRQvEEX&o@cRx!1= X$g==3D-g2*F*^`*Y;Te0tPubJFN&JJ delta 229 zcmZp8pwjR_WrDPz3j+hgWFUqC zW>I!=NlC`JiQCt=Gn%n&H|b+^V`KqpK0et{;V_%M44Zhc?(~EGjPmXC`x&>-?`KN= zz{A3C#=w7ye=2_xzu9KN32OY)&H9Ox}XDMUlYSp%57gtth?2_G{Bf_$niBYq?MV>=V(>r^ diff --git a/app/templates/admin/acces-etudiante.php b/app/templates/admin/acces-etudiante.php index 2b0cf61..b2ae72d 100644 --- a/app/templates/admin/acces-etudiante.php +++ b/app/templates/admin/acces-etudiante.php @@ -16,6 +16,7 @@ Lien + Objet Statut Mot de passe Utilisations @@ -48,6 +49,13 @@ + + + + + Tous + + @@ -114,6 +122,16 @@
+
+ + + Restreint ce lien à un seul type de soumission. +
diff --git a/app/templates/admin/parametres.php b/app/templates/admin/parametres.php index 9e58c96..fa01ec5 100644 --- a/app/templates/admin/parametres.php +++ b/app/templates/admin/parametres.php @@ -106,6 +106,52 @@ + +
+

Types de travaux

+

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é.

+ +
+ + + +
+ Types disponibles + + + + + + +
+ + +
+
+