From 4839b568deba443095d6e56cf1f0995ed54668fa Mon Sep 17 00:00:00 2001 From: Pontoporeia Date: Tue, 21 Apr 2026 21:49:30 +0200 Subject: [PATCH] =?UTF-8?q?Separate=20admin=20views=20from=20controllers?= =?UTF-8?q?=20=E2=80=94=20move=20HTML=20to=20templates/admin/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All admin pages refactored to thin controllers + pure view templates, mirroring the public-page pattern: Controllers (public/admin/*.php): auth, data loading, include template Views (templates/admin/*.php): pure HTML/PHP output Fragment partials (templates/admin/partials/): toast, system-log-panel, system-nginx-config-panel Pages migrated: login, tags, contenus, contenus-edit, account, acces-etudiante, thanks, add, edit, parametres, system, index Fragment endpoints refactored: system-fragment.php, toast-fragment.php Skipped (pure redirects): logout, logs, status, import --- TODO.md | 2 + app/public/admin/acces-etudiante.php | 194 +---------- app/public/admin/account.php | 102 +----- app/public/admin/add.php | 124 +------ app/public/admin/contenus-edit.php | 165 +--------- app/public/admin/contenus.php | 73 +--- app/public/admin/edit.php | 151 +-------- app/public/admin/index.php | 311 +----------------- app/public/admin/login.php | 30 +- app/public/admin/parametres.php | 310 +---------------- app/public/admin/system-fragment.php | 122 +------ app/public/admin/system.php | 301 +---------------- app/public/admin/tags.php | 76 +---- app/public/admin/thanks.php | 139 +------- app/public/admin/toast-fragment.php | 15 +- app/public/assets/css/admin.css | 32 +- app/storage/test.db | Bin 602112 -> 602112 bytes app/templates/admin/acces-etudiante.php | 183 +++++++++++ app/templates/admin/account.php | 86 +++++ app/templates/admin/add.php | 109 ++++++ app/templates/admin/contenus-edit.php | 145 ++++++++ app/templates/admin/contenus.php | 61 ++++ app/templates/admin/edit.php | 131 ++++++++ app/templates/admin/footer.php | 35 +- app/templates/admin/index.php | 302 +++++++++++++++++ app/templates/admin/login.php | 19 ++ app/templates/admin/parametres.php | 288 ++++++++++++++++ .../admin/partials/system-log-panel.php | 54 +++ .../partials/system-nginx-config-panel.php | 40 +++ app/templates/admin/partials/toast.php | 12 + app/templates/admin/system.php | 165 ++++++++++ app/templates/admin/tags.php | 62 ++++ app/templates/admin/thanks.php | 115 +++++++ app/templates/partials/toast.php | 21 -- 34 files changed, 1886 insertions(+), 2089 deletions(-) create mode 100644 app/templates/admin/acces-etudiante.php create mode 100644 app/templates/admin/account.php create mode 100644 app/templates/admin/add.php create mode 100644 app/templates/admin/contenus-edit.php create mode 100644 app/templates/admin/contenus.php create mode 100644 app/templates/admin/edit.php create mode 100644 app/templates/admin/index.php create mode 100644 app/templates/admin/login.php create mode 100644 app/templates/admin/parametres.php create mode 100644 app/templates/admin/partials/system-log-panel.php create mode 100644 app/templates/admin/partials/system-nginx-config-panel.php create mode 100644 app/templates/admin/partials/toast.php create mode 100644 app/templates/admin/system.php create mode 100644 app/templates/admin/tags.php create mode 100644 app/templates/admin/thanks.php delete mode 100644 app/templates/partials/toast.php diff --git a/TODO.md b/TODO.md index 24b4658..0600160 100644 --- a/TODO.md +++ b/TODO.md @@ -2,3 +2,5 @@ - [x] Fix broken `flash-messages.php` include in admin footer - [x] Make `.repertoire-col` columns scrollable instead of `.search-main` +- [x] Replace JS toast system with pure HTMX toast fragment (top-right, CSS-only auto-fade) +- [x] Separate admin views from controllers: move all HTML to `templates/admin/*.php`, fragments to `templates/admin/partials/` diff --git a/app/public/admin/acces-etudiante.php b/app/public/admin/acces-etudiante.php index c1379b6..69c3558 100644 --- a/app/public/admin/acces-etudiante.php +++ b/app/public/admin/acces-etudiante.php @@ -13,193 +13,7 @@ $baseUrl = $protocol . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost'); $pageTitle = 'Accès étudiant·e'; $isAdmin = true; $bodyClass = 'admin-body'; -?> - - - -
- - -
-

Accès étudiant·e

-
- -
-
- - -

Aucun lien d'accès créé. Cliquez sur « Créer un lien » pour générer un lien partageable.

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
LienStatutMot de passeUtilisationsExpirationCréé leActions
- - - - - - - - - - - -
- - 👁 Visiter - - -
- - - - -
- -
- - - - -
-
-
- -
- - - -
-

Créer un lien d'accès

- -
-
- - -
- - - Laissez vide pour un lien sans mot de passe. -
-
- - - Laissez vide pour qu'il n'expire jamais. -
- -
-
- - - -
-

Mot de passe

- -
-
- - - -
- - - Laissez vide pour supprimer le mot de passe. -

-
- -
-
- - - - +require_once APP_ROOT . '/templates/head.php'; +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/acces-etudiante.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/account.php b/app/public/admin/account.php index 4d27531..b081d75 100644 --- a/app/public/admin/account.php +++ b/app/public/admin/account.php @@ -5,104 +5,14 @@ AdminAuth::requireLogin(); $pageTitle = "Compte administrateur"; -$hasPassword = AdminAuth::hasPassword(); - -// Flash messages are consumed by the flash-messages partial below. +$hasPassword = AdminAuth::hasPassword(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } -?> - - -
-

Compte administrateur

- - - - - - - -

- -
- - - -
- -
- -
-
- - -
- -
- - Minimum 12 caractères. -
-
- -
- -
- -
-
- - -
- - - -

Zone de danger

-
-

- Supprimer la configuration du mot de passe PHP
- - Supprime le hash de la base de données. L'accès admin - dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée. - -

-
- - - - -
-
- -
- - +$isAdmin = true; $bodyClass = 'admin-body'; +require_once APP_ROOT . '/templates/head.php'; +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/account.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/add.php b/app/public/admin/add.php index 7cc6956..315d35d 100644 --- a/app/public/admin/add.php +++ b/app/public/admin/add.php @@ -23,9 +23,6 @@ $formData = $_SESSION['form_data'] ?? []; unset($_SESSION['form_data']); $autofocusField = App::consumeAutofocus(); -/** - * Merge autofocus into the $attrs array for a given field. - */ function withAutofocus(string $fieldName, array $attrs = []): array { global $autofocusField; if ($autofocusField === $fieldName) { @@ -38,130 +35,17 @@ function old($key, $default = "") { global $formData; return isset($formData[$key]) ? htmlspecialchars($formData[$key]) : $default; } + function wasSelected($key, $value) { global $formData; if (!isset($formData[$key])) return false; if (is_array($formData[$key])) return in_array($value, $formData[$key]); return $formData[$key] == $value; } -?> - - -
-
-

Ajouter un TFE

-
- - - -

* Champs obligatoires

-
- "> - - -
- Informations du TFE - - - - 'name']); include APP_ROOT . '/templates/partials/form/text-field.php'; ?> - 'email']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> - -
- - Si cette case est cochée, votre contact apparaîtra sur la page publique de votre TFE. -
- -
- - -
-
- - - - - -
- Cadre académique - - 2000, 'max' => date('Y') + 1]); - include APP_ROOT . '/templates/partials/form/text-field.php'; - ?> - - - - - - - - - -
- - -
- Fichiers - - - - -
- - -
- Métadonnées complémentaires - - - - - - - - $at['id'], 'name' => $at['name']]; - }, $enabledAccessTypes); - $defaultAccessType = 2; - $selectedAccessType = isset($formData['access_type_id']) - ? (int)$formData['access_type_id'] - : $defaultAccessType; - $name = 'access_type_id'; - $label = 'Visibilité / Accès :'; - $options = $accessOptions; - $selected = $selectedAccessType; - $placeholder = null; - $required = true; - $attrs = []; - include APP_ROOT . '/templates/partials/form/select-field.php'; - ?> -
- - -
- E-mail de confirmation - -
- - -
-
- - +include APP_ROOT . '/templates/admin/add.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/contenus-edit.php b/app/public/admin/contenus-edit.php index 2f8a351..75cca79 100644 --- a/app/public/admin/contenus-edit.php +++ b/app/public/admin/contenus-edit.php @@ -15,7 +15,6 @@ $allowedApropos = ["contacts", "credits"]; $pageSlug = $_GET["slug"] ?? ""; $aproposKey = $_GET["apropos"] ?? ""; -// Exactly one target must be specified if ($pageSlug && !in_array($pageSlug, $allowedPageSlugs)) { $pageSlug = ""; } @@ -62,171 +61,15 @@ var editor = new OT(document.getElementById('editor'), { onChange: function(value) { hidden.value = value; } }); JS; -$aproposEditorJs = null; -if ($editType === 'apropos' && in_array($aproposKey, ['contacts', 'credits'])) { - // Rich textarea for JSON arrays rendered as structured form - $aproposEditorJs = <<<'JS' -// Auto-format JSON in the hidden field for display purposes -JS; -} $initialContent = ''; if ($editType === 'page') { $initialContent = $page["content"] ?? ""; -} else { - // For apropos, show structured form } -?> - - - -
-

Éditer :

- - -
- "> - - - - -
- - -
- - - -
- "> - - - $group): ?> -
- - - - - - - - - - - $entry): ?> -
- - - - - - - - -
- - - -
- - - - - - - - - -
- - - -
- +include APP_ROOT . "/templates/header.php"; +include APP_ROOT . '/templates/admin/contenus-edit.php'; +require_once APP_ROOT . "/templates/admin/footer.php"; diff --git a/app/public/admin/contenus.php b/app/public/admin/contenus.php index b4d6572..5ca39c5 100644 --- a/app/public/admin/contenus.php +++ b/app/public/admin/contenus.php @@ -14,72 +14,9 @@ try { error_log("Error loading contenus: " . $e->getMessage()); die("Erreur lors du chargement des contenus."); } -?> - - -
-

Contenus

- - - -

Pages statiques

- - - - - - - - - - - - - - - - - - - - -
SlugTitreMis à jourAction
- Éditer -
- -

À propos

- - - - - - - - - - - - - 'Contacts', - 'credits' => 'Crédits', - }; - ?> - - - - - - - - -
CléTypeMis à jourAction
- Éditer -
-
- - +$isAdmin = true; $bodyClass = 'admin-body'; +require_once APP_ROOT . '/templates/head.php'; +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/contenus.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/edit.php b/app/public/admin/edit.php index ed2919a..f0a4dbe 100644 --- a/app/public/admin/edit.php +++ b/app/public/admin/edit.php @@ -1,12 +1,8 @@ load($thesisId); - extract($view); // thesis, currentLanguages, currentFormats, jury, lookup tables, pageTitle … + extract($view); } catch (Exception $e) { error_log("Error loading edit page: " . $e->getMessage()); die("Erreur lors du chargement: " . $e->getMessage()); } -?> - - -
-

Modifier un TFE

- - - -
- - - - 'name'], $autofocusField === 'auteurice' ? ['autofocus' => true] : []); include APP_ROOT . '/templates/partials/form/text-field.php'; ?> - 'email']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> - - -
- - Si cette case est cochée, le contact apparaît sur la page publique du TFE. -
- - true] : []; - include APP_ROOT . '/templates/partials/form/text-field.php'; - ?> - - - - - - - - - - - - $at['id'], 'name' => $label]; - }, $accessTypes); - $name = 'access_type_id'; $label = 'Visibilité / Accès :'; $options = $accessOptions; $selected = $currentAccessTypeId; $placeholder = '- Non défini -'; - include APP_ROOT . '/templates/partials/form/select-field.php'; - ?> - - -
- -
- - Visible publiquement pour les TFE Interne ou Interdit. Max 1 500 caractères. -
-
- - - - true] : []; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> - - - -
- - -
- - - - - - - - - - - - -
- -
- -
- Bannière actuelle - -
- - - JPG, PNG ou WEBP. Format paysage recommandé (4:1). Max 5 MB. -
-
- - -
- - -
- - -
-
- - +$isAdmin = true; $bodyClass = 'admin-body'; +require_once APP_ROOT . '/templates/head.php'; +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/edit.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/index.php b/app/public/admin/index.php index b307ffc..f5ae99b 100644 --- a/app/public/admin/index.php +++ b/app/public/admin/index.php @@ -323,311 +323,10 @@ try { error_log("Error loading theses list: " . $e->getMessage()); die("Erreur lors du chargement de la liste."); } -?> - - - +$isAdmin = true; $bodyClass = 'admin-body'; +require_once APP_ROOT . '/templates/head.php'; +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/index.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; -
- -
-

Liste des TFE

- -
- - - - - - - - -
- -
-
-
-
Total
-
-
-
-
Publiés
-
-
-
-
Attente
-
-
-
- Ajouter un TFE - - - Exporter CSV - -
-
- - - - -
- - - -
-
- - - -

Aucun TFE trouvé.

- -

- 1) { - echo "{$from}-{$to} sur {$totalCount} TFE"; - } else { - echo "$totalCount TFE"; - } - ?> -

- $searchQuery, - 'year' => $yearFilter ?: '', - 'orientation' => $orientationFilter ?: '', - 'ap' => $apFilter ?: '', - ]); - - $sortLink = function(string $col) use ($sortCol, $sortDir, $sortParams): string { - $params = $sortParams; - $params['sort'] = $col; - $params['dir'] = ($sortCol === $col && $sortDir === 'desc') ? 'asc' : 'desc'; - return '/admin/?' . http_build_query($params); - }; - - $sortArrow = function(string $col) use ($sortCol, $sortDir): string { - if ($sortCol !== $col) return ''; - return $sortDir === 'asc' ? ' ↑' : ' ↓'; - }; - ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IDTitreAuteur(s)AnnéeOrientationAPStatutActions
-
- -
- -
- - -
- -
-
- Voir - Éditer -
- - - - - - - - - -
-
- - - -
-
-
- - - $searchQuery, - 'year' => $yearFilter ?: '', - 'orientation' => $orientationFilter ?: '', - 'ap' => $apFilter ?: '', - 'sort' => $sortCol, - 'dir' => $sortDir, - ]); - include APP_ROOT . '/templates/partials/pagination.php'; - ?> -
- - - -
-

Importer une liste de TFE

- -
- - -
- - - - -

- -
- - -
- - -
- -
- - - Colonnes : Identifiant, Titre, Sous-titre, Auteur·ice(s), Contact, Promoteur·ice(s), Format, Année, AP, Orientation, Finalité, Mots-clés, Synopsis, Contexte, Remarques, Langue, Autorisation, License, taille, Points sur 20, lien BAIU
- Quatre premières lignes ignorées — Séparateur : virgule — UTF-8 -
-
-
- - -
- - -
- Logs d'importation ( entrées) -
    - -
  • - -
-
- -
- - - - - - diff --git a/app/public/admin/login.php b/app/public/admin/login.php index 9e45bbe..8cd2658 100644 --- a/app/public/admin/login.php +++ b/app/public/admin/login.php @@ -22,28 +22,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } $pageTitle = 'Connexion'; -?> - - - -
- -
- - +$isAdmin = true; $bodyClass = 'admin-body'; +require_once APP_ROOT . '/templates/head.php'; +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/login.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/parametres.php b/app/public/admin/parametres.php index 6d72ac6..ae938c7 100644 --- a/app/public/admin/parametres.php +++ b/app/public/admin/parametres.php @@ -5,313 +5,23 @@ AdminAuth::requireLogin(); $pageTitle = "Paramètres"; -$hasPassword = AdminAuth::hasPassword(); -$maintenanceOn = file_exists(APP_ROOT . '/storage/maintenance.flag'); +$hasPassword = AdminAuth::hasPassword(); +$maintenanceOn = file_exists(APP_ROOT . '/storage/maintenance.flag'); require_once APP_ROOT . '/src/Database.php'; require_once APP_ROOT . '/src/SmtpRelay.php'; $db = new Database(); -$siteSettings = $db->getAllSettings(); -$stats = $db->getThesesStats(); -$smtpSettings = SmtpRelay::getSettings($db); +$siteSettings = $db->getAllSettings(); +$stats = $db->getThesesStats(); +$smtpSettings = SmtpRelay::getSettings($db); $smtpConfigured = SmtpRelay::isConfigured($db); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } -?> - - -
-

Paramètres

- - - - -
-

Maintenance

- -
- -

- ⚠ Mode maintenance activé — le site public est inaccessible. -

-
- - - - -
- -

Site public : en ligne

-
- - - - -
- -
- - -
- Exporter la base de données -

Télécharger une copie complète de la base de données SQLite. - Cela inclut tous les TFE, auteurs, jury, mots-clés, paramètres, etc.

- -
- - -
- Supprimer tous les TFE -

- Supprime définitivement tous les TFE de la base de données, y compris auteurs, - promoteurs, tags, fichiers associés. Cette action est irréversible. -

-
- - - -
-
-
- - -
-

Formulaire

-

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

-

L'option Libre ne sera activée qu'à partir de l'année académique prochaine.

- -
- - - -
- Types d'accès - - - - - - -
- - -
-
- - -
-

Relay SMTP

-

- Identifiants du serveur SMTP utilisé pour l'envoi d'e-mails - (notifications, partage de TFE, etc.). -

-
- - ✓ Configuré - : () - - ✗ Non configuré - -
- -
- - - -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
-
- -
- Expéditeur par défaut -
-
- - -
-
- - -
-
-
- - -
-
- - -
-

Compte administrateur

- - - - -

- Aucun mot de passe PHP configuré. Le formulaire ci-dessous stockera - un hash bcrypt dans la base de données. -

- - -
- - - - -
- - -
- - -
- - - Minimum 12 caractères. -
- -
- - -
- - -
- - - -
- Supprimer la configuration du mot de passe PHP -

- Supprime le hash de la base de données. L'accès admin - dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée. -

-
> - - - - - -
-
- -
- - - -
-

Exporter la base de données

- -
- -

Télécharger une copie complète de la base de données SQLite. - Cela inclut tous les TFE, auteurs, jury, mots-clés, paramètres, etc.

- - -
-
- - +$isAdmin = true; $bodyClass = 'admin-body'; +require_once APP_ROOT . '/templates/head.php'; +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/parametres.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/system-fragment.php b/app/public/admin/system-fragment.php index eaf3d7a..7001fd7 100644 --- a/app/public/admin/system-fragment.php +++ b/app/public/admin/system-fragment.php @@ -23,14 +23,14 @@ if (!AdminAuth::isAuthenticated()) { } // ── Validate inputs ──────────────────────────────────────────────────────── -$tab = $_GET['tab'] ?? 'nginx_access'; -if ($tab !== 'nginx_config' && !array_key_exists($tab, SystemController::LOG_FILES)) { - $tab = 'nginx_access'; +$activeTab = $_GET['tab'] ?? 'nginx_access'; +if ($activeTab !== 'nginx_config' && !array_key_exists($activeTab, SystemController::LOG_FILES)) { + $activeTab = 'nginx_access'; } -$n = isset($_GET['n']) ? (int) $_GET['n'] : 100; -if (!in_array($n, SystemController::ALLOWED_LINES, true)) { - $n = 100; +$selectedN = isset($_GET['n']) ? (int) $_GET['n'] : 100; +if (!in_array($selectedN, SystemController::ALLOWED_LINES, true)) { + $selectedN = 100; } header('Content-Type: text/html; charset=utf-8'); @@ -42,105 +42,19 @@ $_cache = new SystemCache($_db->getPDO()); $_controller = new SystemController($_db, $_cache); // ── Render ───────────────────────────────────────────────────────────────── -if ($tab === 'nginx_config') { - $data = $_controller->getNginxConfigData(); - $lines = $data['lines']; - $source = $data['source']; - $meta = $data['meta']; - $error = $data['error']; - - if ($meta): ?> -
- - - - - ● Config déployée - - ⚠ Référence locale (config live inaccessible) - -
- -
- Configuration nginx non disponible -
- -
- En développement, /etc/nginx/sites-available/posterg n'existe pas. - La config de référence se trouve dans nginx/posterg.conf. -
- -
- -
Le fichier de configuration est vide.
- -
- - $line): ?> - - -
-getNginxConfigData(); + $nginxConfigLines = $nginxData['lines']; + $nginxConfigSource = $nginxData['source']; + $nginxConfigMeta = $nginxData['meta']; + $nginxConfigError = $nginxData['error']; + include APP_ROOT . '/templates/admin/partials/system-nginx-config-panel.php'; } else { - // ── Log tab ──────────────────────────────────────────────────────────── - $data = $_controller->getLogData($tab, $n); - $logLines = $data['lines']; - $logError = $data['error']; - $logMeta = $data['meta']; - ?> -
-
- - -
- 0): ?> - ligne(s) - -
+ $logData = $_controller->getLogData($activeTab, $selectedN); + $logLines = $logData['lines']; + $logError = $logData['error']; + $logFileMeta = $logData['meta']; - -
- - - -
- - - -
- Journaux non disponibles -
- -
- En environnement de développement, les logs nginx ne sont pas disponibles. - Cette page est pleinement fonctionnelle sur le serveur de production. -
- -
- -
Le fichier journal est vide.
- -
- - $line): ?> - - -
-getPDO()); $_controller = new SystemController($_db, $_cache); -// ?refresh=1 force-busts all cached sections if (isset($_GET['refresh']) && $_GET['refresh'] === '1') { $_controller->invalidateAll(); } // ── Status / PHP / Disk data ────────────────────────────────────────────────── -$statusData = $_controller->getStatusData(); -$checks = $statusData['checks']; -$statusCached = $statusData['cached']; +$statusData = $_controller->getStatusData(); +$checks = $statusData['checks']; +$statusCached = $statusData['cached']; $statusCacheAge = $statusData['cacheAge']; -$phpInfo = $_controller->getPhpInfo(); +$phpInfo = $_controller->getPhpInfo(); $diskInfo = $_controller->getDiskInfo(); $diskTotal = $diskInfo['total']; @@ -35,7 +34,7 @@ $diskColor = SystemController::diskColor($diskPct); // ── Active tab + line count ─────────────────────────────────────────────────── $activeTab = $_GET['tab'] ?? 'nginx_access'; if ($activeTab === 'status') { - $activeTab = 'nginx_access'; // legacy redirect + $activeTab = 'nginx_access'; } elseif ($activeTab !== 'nginx_config' && !array_key_exists($activeTab, SystemController::LOG_FILES)) { $activeTab = 'nginx_access'; } @@ -46,9 +45,9 @@ if (!in_array($selectedN, SystemController::ALLOWED_LINES, true)) { } // ── Tab content data ────────────────────────────────────────────────────────── -$logLines = null; -$logError = null; -$logFileMeta = null; +$logLines = null; +$logError = null; +$logFileMeta = null; $nginxConfigLines = null; $nginxConfigSource = null; @@ -70,289 +69,11 @@ if ($activeTab === 'nginx_config') { $isAdmin = true; $bodyClass = 'admin-body'; $extraCss = ['/assets/css/system.css']; -// HTMX loaded once in footer; status collapse + copy via inline JS require_once APP_ROOT . '/templates/head.php'; -// Restore collapsed state from cookie $collapsed = $_COOKIE['sys_collapsed'] ?? null; $statusInitiallyCollapsed = $collapsed === '1'; -?> - - - -
-

Système

- -

- Affiché le — - Rafraîchir — - Forcer actualisation -

- - -
-
-

Statut - - - ⚡ Cache — il y a s - - - - ⟳ Actualisé - - -

- -
- -
> -
- - -
-
- - -
- -
- -
- -
- -
-
-

Environnement PHP

-
- $val): ?> -
-
-
-
- -
-
-
-

Espace disque

-
-
-
-
- utilisé (%) - libre / -
-
-
-
-
- - - - - -
- - - - - -
- - - - - ● Config déployée - - ⚠ Référence locale (config live inaccessible) - -
- - - -
- Configuration nginx non disponible -
- -
- En développement, /etc/nginx/sites-available/posterg n'existe pas. - La config de référence se trouve dans nginx/posterg.conf. -
- -
- - -
Le fichier de configuration est vide.
- - -
- - $line): ?> - - -
- - - - - - -
- -
- - -
- 0): ?> - ligne(s) - -
- - - -
- - - -
- - - - -
- Journaux non disponibles -
- -
- En environnement de développement, les logs nginx ne sont pas disponibles. - Cette page est pleinement fonctionnelle sur le serveur de production. -
- -
- - -
Le fichier journal est vide.
- - -
- - $line): ?> - - -
- - - - -
- -
- - - - +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/system.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/tags.php b/app/public/admin/tags.php index 899dd82..dea685d 100644 --- a/app/public/admin/tags.php +++ b/app/public/admin/tags.php @@ -18,74 +18,8 @@ try { die("Erreur : " . htmlspecialchars($e->getMessage())); } -// Flash messages are consumed by the flash-messages partial below. -?> - - - -
-

Mots-clés ()

- - - - - - - - - - - - - - - - - - - - -
NomTFE associésActions
- -
- - - - - -
- - -
- - - - - -
- - -
- - - - -
-
-
- - +$isAdmin = true; $bodyClass = 'admin-body'; +require_once APP_ROOT . '/templates/head.php'; +include APP_ROOT . '/templates/header.php'; +include APP_ROOT . '/templates/admin/tags.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/thanks.php b/app/public/admin/thanks.php index 60a6be4..1ad00ab 100644 --- a/app/public/admin/thanks.php +++ b/app/public/admin/thanks.php @@ -1,9 +1,7 @@ 0) { try { $db = new Database(); - - // Get thesis data $thesis = $db->getThesis($thesisId); if (!$thesis) { @@ -48,7 +43,6 @@ if (isset($_GET['id'])) { $error = "Aucun identifiant spécifié."; } -// Helper function to format file size function formatFileSize($bytes) { if ($bytes >= 1073741824) { return number_format($bytes / 1073741824, 2) . ' GB'; @@ -61,135 +55,12 @@ function formatFileSize($bytes) { } } -// Set page title for header $pageTitle = "Récapitulatif TFE"; -?> - - -
- - -
- -
-

⚠ Oups…

-

- ← Retour au formulaire -
- - -
-

Merci 🎉

-

- Ton TFE a bien été soumis. -

- + Ajouter un nouveau TFE -
- -
-

Erreur

-

Aucune donnée à afficher.

- ← Retour au formulaire -
- -
- - - -

Récapitulatif TFE

- - - -

Retour au formulaire

- - -
-

Informations de base

-
-
Identifiant
-
Titre
- -
Sous-titre
- -
Auteur·ice(s)
-
Année
-
-
- -
-

Détails académiques

-
-
Orientation
-
Atelier pratique
-
Finalité
- -
Promoteur·ice(s)
- -
-
- -
-

Contenu

-
- -
Langue(s)
- - -
Format(s)
- - -
Mots-clés
- - -
Durée / Taille
- - -
Lien
- -
-
- - -
-

Fichiers

- - - - - - - - - - - - -
TypeFichierTailleDate
-
- - - - - -

Aucune donnée à afficher.

-

Retour au formulaire

- - - -
- - +include APP_ROOT . '/templates/admin/thanks.php'; +require_once APP_ROOT . '/templates/admin/footer.php'; diff --git a/app/public/admin/toast-fragment.php b/app/public/admin/toast-fragment.php index 3862765..2f61873 100644 --- a/app/public/admin/toast-fragment.php +++ b/app/public/admin/toast-fragment.php @@ -17,16 +17,5 @@ if (!$flash['error'] && !$flash['success']) { http_response_code(204); exit; } -?> - - - - -

- - -

- + +include APP_ROOT . '/templates/admin/partials/toast.php'; diff --git a/app/public/assets/css/admin.css b/app/public/assets/css/admin.css index 14b4ebb..f5cf26f 100644 --- a/app/public/assets/css/admin.css +++ b/app/public/assets/css/admin.css @@ -335,18 +335,17 @@ label:has(+ div > input:required)::after { filter: brightness(0.9); } -/* ── Toast messages (bottom-center floating) ─────────────────────────── */ -#toast-container { +/* ── Toast messages (top-right, CSS-only auto-fade) ─────────────────── */ +#toast-region { position: fixed; - bottom: var(--space-l); - left: 50%; - transform: translateX(-50%); + top: var(--space-l); + right: var(--space-l); z-index: 10000; display: flex; - flex-direction: column-reverse; + flex-direction: column; gap: var(--space-xs); pointer-events: none; - max-width: calc(100vw - 2 * var(--space-l)); + max-width: min(480px, calc(100vw - 2 * var(--space-l))); } .toast { @@ -356,35 +355,32 @@ label:has(+ div > input:required)::after { border-left: 3px solid; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); pointer-events: auto; - animation: toast-enter 0.35s ease-out forwards; - max-width: 480px; backdrop-filter: blur(6px); + /* enter then fade out — total visible ~4.35 s */ + animation: toast-enter 0.35s ease-out, + toast-exit 0.4s ease-in 4s forwards; } -.toast[data-type="error"] { +.toast--error { background: var(--accent-muted); border-color: var(--error); color: var(--text-primary); } -.toast[data-type="success"] { +.toast--success { background: var(--success-muted-bg); border-color: var(--success); color: var(--text-primary); } -.toast-exit { - animation: toast-exit 0.3s ease-in forwards; -} - @keyframes toast-enter { - from { opacity: 0; transform: translateY(20px); } + from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: translateY(0); } } @keyframes toast-exit { - from { opacity: 1; transform: translateY(0); } - to { opacity: 0; transform: translateY(20px); height: 0; padding: 0; margin: 0; overflow: hidden; } + from { opacity: 1; } + to { opacity: 0; pointer-events: none; } } /* ── Stats cards ────────────────────────────────────────────────────────── */ diff --git a/app/storage/test.db b/app/storage/test.db index 5f064a8f2b64a7231409064ff26944f7ff2e3737..c1f51a0bde05f2f2b18333994ce4219bfcb0b5ad 100644 GIT binary patch delta 324 zcmZp8pwa+DEsQNpTbLOi@G>wkF!4-f;62JSmA7!Sp+Y;4RFf_f8-sL;daw3SlxN{TX5iuJv(Y delta 355 zcmZp8pwa+DEsQNpTbLOi@bVTiFmYdJ;62KHowsnap~6XSm1c1!HU{a|YUAXh)RfGU zVyrF9%m&2l+mF8Guyz6f + +
+

Accès étudiant·e

+
+ +
+
+ + +

Aucun lien d'accès créé. Cliquez sur « Créer un lien » pour générer un lien partageable.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
LienStatutMot de passeUtilisationsExpirationCréé leActions
+ + + + + + + + + + + +
+ + 👁 Visiter + + +
+ + + + +
+ +
+ + + + +
+
+
+ + + + + +
+

Créer un lien d'accès

+ +
+
+ + +
+ + + Laissez vide pour un lien sans mot de passe. +
+
+ + + Laissez vide pour qu'il n'expire jamais. +
+ +
+
+ + + +
+

Mot de passe

+ +
+
+ + + +
+ + + Laissez vide pour supprimer le mot de passe. +

+
+ +
+
+ + diff --git a/app/templates/admin/account.php b/app/templates/admin/account.php new file mode 100644 index 0000000..e689c96 --- /dev/null +++ b/app/templates/admin/account.php @@ -0,0 +1,86 @@ +
+

Compte administrateur

+ + + + + +

+ +
+ + + +
+ +
+ +
+
+ + +
+ +
+ + Minimum 12 caractères. +
+
+ +
+ +
+ +
+
+ + +
+ + + +

Zone de danger

+
+

+ Supprimer la configuration du mot de passe PHP
+ + Supprime le hash de la base de données. L'accès admin + dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée. + +

+
+ + + + +
+
+ +
diff --git a/app/templates/admin/add.php b/app/templates/admin/add.php new file mode 100644 index 0000000..e9eca13 --- /dev/null +++ b/app/templates/admin/add.php @@ -0,0 +1,109 @@ +
+
+

Ajouter un TFE

+
+ +

* Champs obligatoires

+
+ "> + + +
+ Informations du TFE + + + + 'name']); include APP_ROOT . '/templates/partials/form/text-field.php'; ?> + 'email']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> + +
+ + Si cette case est cochée, votre contact apparaîtra sur la page publique de votre TFE. +
+ +
+ + +
+
+ + + + + +
+ Cadre académique + + 2000, 'max' => date('Y') + 1]); + include APP_ROOT . '/templates/partials/form/text-field.php'; + ?> + + + + + + + + + +
+ + +
+ Fichiers + + + + +
+ + +
+ Métadonnées complémentaires + + + + + + + + $at['id'], 'name' => $at['name']]; + }, $enabledAccessTypes); + $defaultAccessType = 2; + $selectedAccessType = isset($formData['access_type_id']) + ? (int)$formData['access_type_id'] + : $defaultAccessType; + $name = 'access_type_id'; + $label = 'Visibilité / Accès :'; + $options = $accessOptions; + $selected = $selectedAccessType; + $placeholder = null; + $required = true; + $attrs = []; + include APP_ROOT . '/templates/partials/form/select-field.php'; + ?> +
+ + +
+ E-mail de confirmation + +
+ + +
+
diff --git a/app/templates/admin/contenus-edit.php b/app/templates/admin/contenus-edit.php new file mode 100644 index 0000000..fd90249 --- /dev/null +++ b/app/templates/admin/contenus-edit.php @@ -0,0 +1,145 @@ +
+

Éditer :

+ + +
+ "> + + + + +
+ + +
+ + + +
+ "> + + + $group): ?> +
+ + + + + + + + + + + $entry): ?> +
+ + + + + + + + +
+ + + +
+ + + + + + + + + +
+ + + +
diff --git a/app/templates/admin/contenus.php b/app/templates/admin/contenus.php new file mode 100644 index 0000000..cb47a18 --- /dev/null +++ b/app/templates/admin/contenus.php @@ -0,0 +1,61 @@ +
+

Contenus

+ +

Pages statiques

+ + + + + + + + + + + + + + + + + + + + +
SlugTitreMis à jourAction
+ Éditer +
+ +

À propos

+ + + + + + + + + + + + + 'Contacts', + 'credits' => 'Crédits', + }; + ?> + + + + + + + + +
CléTypeMis à jourAction
+ Éditer +
+
diff --git a/app/templates/admin/edit.php b/app/templates/admin/edit.php new file mode 100644 index 0000000..4c65479 --- /dev/null +++ b/app/templates/admin/edit.php @@ -0,0 +1,131 @@ +
+

Modifier un TFE

+ +
+ + + + 'name'], $autofocusField === 'auteurice' ? ['autofocus' => true] : []); include APP_ROOT . '/templates/partials/form/text-field.php'; ?> + 'email']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> + + +
+ + Si cette case est cochée, le contact apparaît sur la page publique du TFE. +
+ + true] : []; + include APP_ROOT . '/templates/partials/form/text-field.php'; + ?> + + + + + + + + + + + + $at['id'], 'name' => $label]; + }, $accessTypes); + $name = 'access_type_id'; $label = 'Visibilité / Accès :'; $options = $accessOptions; $selected = $currentAccessTypeId; $placeholder = '- Non défini -'; + include APP_ROOT . '/templates/partials/form/select-field.php'; + ?> + + +
+ +
+ + Visible publiquement pour les TFE Interne ou Interdit. Max 1 500 caractères. +
+
+ + + + true] : []; include APP_ROOT . '/templates/partials/form/text-field.php'; ?> + + + +
+ + +
+ + + + + + + + + + + + +
+ +
+ +
+ Bannière actuelle + +
+ + + JPG, PNG ou WEBP. Format paysage recommandé (4:1). Max 5 MB. +
+
+ + +
+ + +
+ + +
+
diff --git a/app/templates/admin/footer.php b/app/templates/admin/footer.php index 2bfb0d7..f6c5028 100644 --- a/app/templates/admin/footer.php +++ b/app/templates/admin/footer.php @@ -1,5 +1,11 @@ - -
+ + @@ -8,30 +14,5 @@ - diff --git a/app/templates/admin/index.php b/app/templates/admin/index.php new file mode 100644 index 0000000..f18c80e --- /dev/null +++ b/app/templates/admin/index.php @@ -0,0 +1,302 @@ + + +
+ +
+

Liste des TFE

+ +
+ + + + + + + + +
+ +
+
+
+
Total
+
+
+
+
Publiés
+
+
+
+
Attente
+
+
+
+ Ajouter un TFE + + + Exporter CSV + +
+
+ + + + +
+ + + +
+
+ + + +

Aucun TFE trouvé.

+ +

+ 1) { + echo "{$from}-{$to} sur {$totalCount} TFE"; + } else { + echo "$totalCount TFE"; + } + ?> +

+ $searchQuery, + 'year' => $yearFilter ?: '', + 'orientation' => $orientationFilter ?: '', + 'ap' => $apFilter ?: '', + ]); + + $sortLink = function(string $col) use ($sortCol, $sortDir, $sortParams): string { + $params = $sortParams; + $params['sort'] = $col; + $params['dir'] = ($sortCol === $col && $sortDir === 'desc') ? 'asc' : 'desc'; + return '/admin/?' . http_build_query($params); + }; + + $sortArrow = function(string $col) use ($sortCol, $sortDir): string { + if ($sortCol !== $col) return ''; + return $sortDir === 'asc' ? ' ↑' : ' ↓'; + }; + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDTitreAuteur(s)AnnéeOrientationAPStatutActions
+
+ +
+ +
+ + +
+ +
+
+ Voir + Éditer +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + $searchQuery, + 'year' => $yearFilter ?: '', + 'orientation' => $orientationFilter ?: '', + 'ap' => $apFilter ?: '', + 'sort' => $sortCol, + 'dir' => $sortDir, + ]); + include APP_ROOT . '/templates/partials/pagination.php'; + ?> +
+ + + +
+

Importer une liste de TFE

+ +
+ + +
+ + + + +

+ +
+ + +
+ + +
+ +
+ + + Colonnes : Identifiant, Titre, Sous-titre, Auteur·ice(s), Contact, Promoteur·ice(s), Format, Année, AP, Orientation, Finalité, Mots-clés, Synopsis, Contexte, Remarques, Langue, Autorisation, License, taille, Points sur 20, lien BAIU
+ Quatre premières lignes ignorées — Séparateur : virgule — UTF-8 +
+
+
+ + +
+ + +
+ Logs d'importation ( entrées) +
    + +
  • + +
+
+ +
+ + + + diff --git a/app/templates/admin/login.php b/app/templates/admin/login.php new file mode 100644 index 0000000..89aa895 --- /dev/null +++ b/app/templates/admin/login.php @@ -0,0 +1,19 @@ +
+ +
diff --git a/app/templates/admin/parametres.php b/app/templates/admin/parametres.php new file mode 100644 index 0000000..cc3ef09 --- /dev/null +++ b/app/templates/admin/parametres.php @@ -0,0 +1,288 @@ +
+

Paramètres

+ + +
+

Maintenance

+ +
+ +

+ ⚠ Mode maintenance activé — le site public est inaccessible. +

+
+ + + + +
+ +

Site public : en ligne

+
+ + + + +
+ +
+ + +
+ Exporter la base de données +

Télécharger une copie complète de la base de données SQLite. + Cela inclut tous les TFE, auteurs, jury, mots-clés, paramètres, etc.

+ +
+ + +
+ Supprimer tous les TFE +

+ Supprime définitivement tous les TFE de la base de données, y compris auteurs, + promoteurs, tags, fichiers associés. Cette action est irréversible. +

+
+ + + +
+
+
+ + +
+

Formulaire

+

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

+

L'option Libre ne sera activée qu'à partir de l'année académique prochaine.

+ +
+ + + +
+ Types d'accès + + + + + + +
+ + +
+
+ + +
+

Relay SMTP

+

+ Identifiants du serveur SMTP utilisé pour l'envoi d'e-mails + (notifications, partage de TFE, etc.). +

+
+ + ✓ Configuré + : () + + ✗ Non configuré + +
+ +
+ + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ Expéditeur par défaut +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+

Compte administrateur

+ + + + +

+ Aucun mot de passe PHP configuré. Le formulaire ci-dessous stockera + un hash bcrypt dans la base de données. +

+ + +
+ + + + +
+ + +
+ + +
+ + + Minimum 12 caractères. +
+ +
+ + +
+ + +
+ + + +
+ Supprimer la configuration du mot de passe PHP +

+ Supprime le hash de la base de données. L'accès admin + dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée. +

+
> + + + + + +
+
+ +
+ + + +
+

Exporter la base de données

+ +
+ +

Télécharger une copie complète de la base de données SQLite. + Cela inclut tous les TFE, auteurs, jury, mots-clés, paramètres, etc.

+ + +
+
diff --git a/app/templates/admin/partials/system-log-panel.php b/app/templates/admin/partials/system-log-panel.php new file mode 100644 index 0000000..ff51795 --- /dev/null +++ b/app/templates/admin/partials/system-log-panel.php @@ -0,0 +1,54 @@ +
+
+ + +
+ 0): ?> + ligne(s) + +
+ + +
+ + + +
+ + + +
+ Journaux non disponibles +
+ +
+ En environnement de développement, les logs nginx ne sont pas disponibles. + Cette page est pleinement fonctionnelle sur le serveur de production. +
+ +
+ + +
Le fichier journal est vide.
+ + +
+ + $line): ?> + + +
+ diff --git a/app/templates/admin/partials/system-nginx-config-panel.php b/app/templates/admin/partials/system-nginx-config-panel.php new file mode 100644 index 0000000..46a3101 --- /dev/null +++ b/app/templates/admin/partials/system-nginx-config-panel.php @@ -0,0 +1,40 @@ + +
+ + + + + ● Config déployée + + ⚠ Référence locale (config live inaccessible) + +
+ + + +
+ Configuration nginx non disponible +
+ +
+ En développement, /etc/nginx/sites-available/posterg n'existe pas. + La config de référence se trouve dans nginx/posterg.conf. +
+ +
+ + +
Le fichier de configuration est vide.
+ + +
+ + $line): ?> + + +
+ diff --git a/app/templates/admin/partials/toast.php b/app/templates/admin/partials/toast.php new file mode 100644 index 0000000..ae703eb --- /dev/null +++ b/app/templates/admin/partials/toast.php @@ -0,0 +1,12 @@ + + + + +

+ + +

+ diff --git a/app/templates/admin/system.php b/app/templates/admin/system.php new file mode 100644 index 0000000..4d627d6 --- /dev/null +++ b/app/templates/admin/system.php @@ -0,0 +1,165 @@ +
+

Système

+ +

+ Affiché le — + Rafraîchir — + Forcer actualisation +

+ + +
+
+

Statut + + + ⚡ Cache — il y a s + + + + ⟳ Actualisé + + +

+ +
+ +
> +
+ + +
+
+ + +
+ +
+ +
+ +
+ +
+
+

Environnement PHP

+
+ $val): ?> +
+
+
+
+ +
+
+
+

Espace disque

+
+
+
+
+ utilisé (%) + libre / +
+
+
+
+
+ + + + + +
+ + + + + + + +
+ +
+ + diff --git a/app/templates/admin/tags.php b/app/templates/admin/tags.php new file mode 100644 index 0000000..0b490a2 --- /dev/null +++ b/app/templates/admin/tags.php @@ -0,0 +1,62 @@ +
+

Mots-clés ()

+ + + + + + + + + + + + + + + + + + +
NomTFE associésActions
+ +
+ + + + + +
+ + +
+ + + + + +
+ + +
+ + + + +
+
+
diff --git a/app/templates/admin/thanks.php b/app/templates/admin/thanks.php new file mode 100644 index 0000000..d0501e5 --- /dev/null +++ b/app/templates/admin/thanks.php @@ -0,0 +1,115 @@ +
+ + +
+ +
+

⚠ Oups…

+

+ ← Retour au formulaire +
+ + +
+

Merci 🎉

+

+ Ton TFE a bien été soumis. +

+ + Ajouter un nouveau TFE +
+ +
+

Erreur

+

Aucune donnée à afficher.

+ ← Retour au formulaire +
+ +
+ + + +

Récapitulatif TFE

+ + + +

Retour au formulaire

+ + +
+

Informations de base

+
+
Identifiant
+
Titre
+ +
Sous-titre
+ +
Auteur·ice(s)
+
Année
+
+
+ +
+

Détails académiques

+
+
Orientation
+
Atelier pratique
+
Finalité
+ +
Promoteur·ice(s)
+ +
+
+ +
+

Contenu

+
+ +
Langue(s)
+ + +
Format(s)
+ + +
Mots-clés
+ + +
Durée / Taille
+ + +
Lien
+ +
+
+ + +
+

Fichiers

+ + + + + + + + + + + + +
TypeFichierTailleDate
+
+ + + + + +

Aucune donnée à afficher.

+

Retour au formulaire

+ + + +
diff --git a/app/templates/partials/toast.php b/app/templates/partials/toast.php deleted file mode 100644 index 7397091..0000000 --- a/app/templates/partials/toast.php +++ /dev/null @@ -1,21 +0,0 @@ - - - -