mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
feat: extract MediaController, wire into Dispatcher, delete media.php
This commit is contained in:
69
app/public/admin/actions/acces-etudiante.php
Normal file
69
app/public/admin/actions/acces-etudiante.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* Student-access link actions (create, toggle, set_password, delete).
|
||||
*/
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
require_once __DIR__ . '/../../../src/ShareLink.php';
|
||||
|
||||
App::adminGuard();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST'
|
||||
|| !isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
http_response_code(403);
|
||||
exit('CSRF token invalide.');
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
|
||||
$shareLink = ShareLink::make();
|
||||
|
||||
switch ($action) {
|
||||
case 'create':
|
||||
$password = !empty($_POST['password']) ? trim($_POST['password']) : null;
|
||||
$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);
|
||||
App::redirect('/admin/acces-etudiante.php', success: 'Lien d\'accès créé.');
|
||||
break;
|
||||
|
||||
case 'toggle':
|
||||
if ($id > 0) {
|
||||
$shareLink->toggleActive($id);
|
||||
App::redirect('/admin/acces-etudiante.php', success: 'Statut du lien modifié.');
|
||||
} else {
|
||||
App::redirect('/admin/acces-etudiante.php', error: 'Lien introuvable.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'set_password':
|
||||
if ($id > 0) {
|
||||
$password = isset($_POST['password']) && $_POST['password'] !== '' ? trim($_POST['password']) : null;
|
||||
$shareLink->setPassword($id, $password);
|
||||
App::redirect('/admin/acces-etudiante.php', success: 'Mot de passe mis à jour.');
|
||||
} else {
|
||||
App::redirect('/admin/acces-etudiante.php', error: 'Lien introuvable.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if ($id > 0) {
|
||||
$shareLink->delete($id);
|
||||
App::redirect('/admin/acces-etudiante.php', success: 'Lien supprimé.');
|
||||
} else {
|
||||
App::redirect('/admin/acces-etudiante.php', error: 'Lien introuvable.');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
App::redirect('/admin/acces-etudiante.php', error: 'Action inconnue.');
|
||||
break;
|
||||
}
|
||||
94
app/public/admin/actions/account.php
Normal file
94
app/public/admin/actions/account.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin account action — update or remove admin password.
|
||||
*
|
||||
* Actions:
|
||||
* POST (default) — set/change the PHP admin password
|
||||
* POST action=remove_credentials — remove the password from DB
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
// ── CSRF ──────────────────────────────────────────────────────────────────────
|
||||
if (empty($_SESSION['csrf_token']) || ($_POST['csrf_token'] ?? '') !== $_SESSION['csrf_token']) {
|
||||
http_response_code(403);
|
||||
die('Invalid CSRF token.');
|
||||
}
|
||||
|
||||
$hasPassword = AdminAuth::hasPassword();
|
||||
$action = $_POST['action'] ?? 'change_password';
|
||||
|
||||
// ── Remove credential ────────────────────────────────────────────────────────
|
||||
if ($action === 'remove_credentials') {
|
||||
$backUrl = $_POST['redirect'] ?? '/admin/parametres.php';
|
||||
if (!preg_match('#^/admin/#', $backUrl)) { $backUrl = '/admin/parametres.php'; }
|
||||
|
||||
if (!$hasPassword) {
|
||||
App::flash('error', 'Aucun mot de passe à supprimer.');
|
||||
header('Location: ' . $backUrl);
|
||||
exit;
|
||||
}
|
||||
|
||||
AdminAuth::removePasswordHash();
|
||||
// Destroy session so the user is forced to re-authenticate.
|
||||
AdminAuth::logout();
|
||||
header('Location: /admin/login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Change / set password ─────────────────────────────────────────────────────
|
||||
|
||||
// 1. If a password is already set, verify the current one.
|
||||
$backUrl = $_POST['redirect'] ?? '/admin/parametres.php';
|
||||
if (!preg_match('#^/admin/#', $backUrl)) { $backUrl = '/admin/parametres.php'; }
|
||||
|
||||
if ($hasPassword) {
|
||||
$currentPassword = $_POST['current_password'] ?? '';
|
||||
if (!AdminAuth::login($currentPassword)) {
|
||||
App::flash('error', 'Mot de passe actuel incorrect.');
|
||||
header('Location: ' . $backUrl);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Validate new password.
|
||||
$newPassword = $_POST['new_password'] ?? '';
|
||||
$confirmPassword = $_POST['confirm_password'] ?? '';
|
||||
|
||||
if (strlen($newPassword) < 12) {
|
||||
App::flash('error', 'Le nouveau mot de passe doit contenir au moins 12 caractères.');
|
||||
header('Location: ' . $backUrl);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($newPassword !== $confirmPassword) {
|
||||
App::flash('error', 'Les mots de passe ne correspondent pas.');
|
||||
header('Location: ' . $backUrl);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3. Generate bcrypt hash (cost 12).
|
||||
$hash = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => 12]);
|
||||
if ($hash === false) {
|
||||
App::flash('error', 'Erreur lors du hachage du mot de passe.');
|
||||
header('Location: ' . $backUrl);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 4. Store hash in DB.
|
||||
AdminAuth::setPasswordHash($hash);
|
||||
|
||||
// 5. Regenerate session (password changed — invalidate old sessions).
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['admin_authenticated'] = true;
|
||||
|
||||
App::flash('success', $hasPassword
|
||||
? 'Mot de passe mis à jour avec succès.'
|
||||
: 'Mot de passe défini avec succès. L\'authentification PHP est maintenant active.');
|
||||
|
||||
header('Location: ' . $backUrl);
|
||||
exit;
|
||||
76
app/public/admin/actions/apropos.php
Normal file
76
app/public/admin/actions/apropos.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Save handler for apropos contents (contacts, credits).
|
||||
* Structure: groups[] with label/role, each having entries[] of {text, url, email}.
|
||||
*/
|
||||
require_once __DIR__ . "/../../../bootstrap.php";
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
// CSRF check
|
||||
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) ||
|
||||
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
die("Erreur de sécurité : token invalide.");
|
||||
}
|
||||
|
||||
$allowedKeys = ['contacts', 'credits'];
|
||||
$aproposKey = $_POST['apropos_key'] ?? '';
|
||||
if (!in_array($aproposKey, $allowedKeys)) {
|
||||
die("Clé invalide.");
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
$groups = $_POST['groups'] ?? [];
|
||||
$cleaned = [];
|
||||
|
||||
foreach ($groups as $group) {
|
||||
if ($aproposKey === 'credits') {
|
||||
$label = trim($group['label'] ?? '');
|
||||
if ($label === '') continue;
|
||||
$entries = [];
|
||||
foreach ($group['entries'] ?? [] as $entry) {
|
||||
$text = trim($entry['text'] ?? '');
|
||||
if ($text === '') continue;
|
||||
$e = ['text' => $text];
|
||||
$url = trim($entry['url'] ?? '');
|
||||
if ($url !== '') $e['url'] = $url;
|
||||
$entries[] = $e;
|
||||
}
|
||||
if (empty($entries)) continue;
|
||||
$cleaned[] = ['label' => $label, 'entries' => $entries];
|
||||
} else { // contacts
|
||||
$role = trim($group['role'] ?? '');
|
||||
if ($role === '') continue;
|
||||
$entries = [];
|
||||
foreach ($group['entries'] ?? [] as $entry) {
|
||||
$text = trim($entry['text'] ?? '');
|
||||
if ($text === '') continue;
|
||||
$e = [
|
||||
'text' => $text,
|
||||
'email' => trim($entry['email'] ?? ''),
|
||||
];
|
||||
$url = trim($entry['url'] ?? '');
|
||||
if ($url !== '') $e['url'] = $url;
|
||||
$entries[] = $e;
|
||||
}
|
||||
if (empty($entries)) continue;
|
||||
$cleaned[] = ['role' => $role, 'entries' => $entries];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($cleaned)) {
|
||||
die("Au moins un groupe avec des entrées est requis.");
|
||||
}
|
||||
|
||||
$db->saveAproposContent($aproposKey, $cleaned);
|
||||
App::flash('success', "Contenu « $aproposKey » mis à jour avec succès.");
|
||||
} catch (Exception $e) {
|
||||
error_log("Apropos save error: " . $e->getMessage());
|
||||
die("Erreur lors de la sauvegarde : " . htmlspecialchars($e->getMessage()));
|
||||
}
|
||||
|
||||
header('Location: /admin/contenus.php');
|
||||
exit;
|
||||
60
app/public/admin/actions/delete.php
Normal file
60
app/public/admin/actions/delete.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
|
||||
// CSRF validation
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
App::flash('error', 'Erreur de sécurité : token invalide.');
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$isBulk = !empty($_POST['bulk']);
|
||||
$isDeleteAll = !empty($_POST['delete_all']);
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
|
||||
if ($isDeleteAll) {
|
||||
$count = $db->deleteAllTheses();
|
||||
App::flash('success', "$count TFE(s) supprimé(s) avec succès.");
|
||||
|
||||
} elseif ($isBulk) {
|
||||
$ids = array_filter(array_map('intval', $_POST['selected_theses'] ?? []), fn($id) => $id > 0);
|
||||
|
||||
if (empty($ids)) {
|
||||
App::flash('error', 'Aucun TFE sélectionné.');
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db->bulkDeleteTheses($ids);
|
||||
$count = count($ids);
|
||||
App::flash('success', "$count TFE(s) supprimé(s) avec succès.");
|
||||
|
||||
} else {
|
||||
$thesisId = filter_var($_POST['thesis_id'] ?? '', FILTER_VALIDATE_INT);
|
||||
|
||||
if (!$thesisId || $thesisId <= 0) {
|
||||
App::flash('error', 'ID invalide.');
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db->deleteThesis($thesisId);
|
||||
App::flash('success', 'TFE supprimé avec succès.');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('delete.php error: ' . $e->getMessage());
|
||||
App::flash('error', 'Erreur lors de la suppression : ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
53
app/public/admin/actions/edit.php
Normal file
53
app/public/admin/actions/edit.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
// Bootstrap application
|
||||
require_once __DIR__ . "/../../../bootstrap.php";
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
|
||||
// PHP-level auth guard (defence-in-depth behind nginx Basic Auth)
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
// Only handle POST requests
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: ../index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Verify CSRF token
|
||||
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) ||
|
||||
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
error_log("CSRF token validation failed in edit action");
|
||||
die("Erreur de sécurité : token invalide. Veuillez recharger le formulaire.");
|
||||
}
|
||||
|
||||
$thesisId = isset($_POST['thesis_id']) ? intval($_POST['thesis_id']) : 0;
|
||||
if ($thesisId <= 0) {
|
||||
die("ID de TFE invalide.");
|
||||
}
|
||||
|
||||
require_once APP_ROOT . '/src/Controllers/ThesisEditController.php';
|
||||
|
||||
try {
|
||||
$ctrl = ThesisEditController::create();
|
||||
$ctrl->save($thesisId, $_POST, $_FILES);
|
||||
|
||||
// Regenerate CSRF token after successful save
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
|
||||
App::flash('success', "TFE mis à jour avec succès!");
|
||||
header('Location: ../edit.php?id=' . $thesisId);
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Edit action error: " . $e->getMessage());
|
||||
|
||||
App::flash('error', $e->getMessage());
|
||||
|
||||
// WCAG 3.3.1 — map error message to field name for autofocus on re-render.
|
||||
$autofocusField = ThesisEditController::autofocusFieldForError($e->getMessage());
|
||||
if ($autofocusField !== null) {
|
||||
App::flashAutofocus($autofocusField);
|
||||
}
|
||||
|
||||
header('Location: ../edit.php?id=' . $thesisId);
|
||||
exit();
|
||||
}
|
||||
36
app/public/admin/actions/export-csv.php
Normal file
36
app/public/admin/actions/export-csv.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Export TFE listings as CSV.
|
||||
*
|
||||
* Thin dispatcher — delegates all data assembly to ExportController,
|
||||
* then streams the response.
|
||||
*/
|
||||
require_once __DIR__ . "/../../../bootstrap.php";
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
require_once APP_ROOT . '/src/Controllers/ExportController.php';
|
||||
$controller = ExportController::create();
|
||||
|
||||
$filename = 'posterg-export-' . date('Y-m-d') . '.csv';
|
||||
|
||||
header('Content-Type: text/csv; charset=UTF-8');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
|
||||
// UTF-8 BOM for Excel compatibility
|
||||
echo "\xEF\xBB\xBF";
|
||||
|
||||
$out = fopen('php://output', 'w');
|
||||
|
||||
// Column headers
|
||||
fputcsv($out, ExportController::CSV_HEADERS, ',', '"', '');
|
||||
|
||||
// Data rows
|
||||
$rows = $controller->exportAllTheses();
|
||||
foreach ($rows as $csvLine) {
|
||||
fputcsv($out, $csvLine, ',', '"', '');
|
||||
}
|
||||
|
||||
fclose($out);
|
||||
exit;
|
||||
29
app/public/admin/actions/export-db.php
Normal file
29
app/public/admin/actions/export-db.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Export the whole SQLite database as a file download.
|
||||
*
|
||||
* Thin dispatcher — delegates to ExportController.
|
||||
*/
|
||||
require_once __DIR__ . "/../../../bootstrap.php";
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
require_once APP_ROOT . '/src/Controllers/ExportController.php';
|
||||
$controller = ExportController::create();
|
||||
|
||||
$dbPath = $controller->getDatabasePath();
|
||||
|
||||
if (!file_exists($dbPath)) {
|
||||
http_response_code(500);
|
||||
exit('Base de données introuvable.');
|
||||
}
|
||||
|
||||
$filename = 'posterg-db-' . date('Y-m-d') . '.sqlite';
|
||||
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
header('Content-Length: ' . filesize($dbPath));
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
|
||||
readfile($dbPath);
|
||||
exit;
|
||||
47
app/public/admin/actions/formulaire.php
Normal file
47
app/public/admin/actions/formulaire.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
// Bootstrap application
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
|
||||
ini_set('display_errors', 0);
|
||||
ini_set('log_errors', 1);
|
||||
ini_set('error_log', 'error.log');
|
||||
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
// Verify CSRF token
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
error_log('CSRF token validation failed in formulaire.php');
|
||||
die('Erreur de sécurité : token invalide. Veuillez recharger le formulaire.');
|
||||
}
|
||||
|
||||
error_log('FILES array: ' . print_r($_FILES, true));
|
||||
|
||||
require_once APP_ROOT . '/src/Controllers/ThesisCreateController.php';
|
||||
|
||||
try {
|
||||
$ctrl = ThesisCreateController::make();
|
||||
$thesisId = $ctrl->submit($_POST, $_FILES);
|
||||
|
||||
unset($_SESSION['csrf_token']);
|
||||
|
||||
header('Location: ' . $redirect);
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('ThesisCreateController error: ' . $e->getMessage());
|
||||
|
||||
App::flash('error', $e->getMessage());
|
||||
$_SESSION['form_data'] = $_POST;
|
||||
|
||||
$redirect = '../add.php';
|
||||
|
||||
$autofocusField = ThesisCreateController::autofocusFieldForError($e->getMessage());
|
||||
if ($autofocusField !== null) {
|
||||
App::flashAutofocus($autofocusField);
|
||||
}
|
||||
|
||||
header('Location: ' . $redirect);
|
||||
exit();
|
||||
}
|
||||
33
app/public/admin/actions/maintenance.php
Normal file
33
app/public/admin/actions/maintenance.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST'
|
||||
|| !isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
http_response_code(403);
|
||||
die("Accès refusé.");
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'enable_maintenance') {
|
||||
file_put_contents(MAINTENANCE_FLAG, date('c'));
|
||||
App::flash('success', "Mode maintenance activé.");
|
||||
} elseif ($action === 'disable_maintenance') {
|
||||
if (file_exists(MAINTENANCE_FLAG)) {
|
||||
unlink(MAINTENANCE_FLAG);
|
||||
}
|
||||
App::flash('success', "Mode maintenance désactivé.");
|
||||
} else {
|
||||
App::flash('error', "Action inconnue.");
|
||||
}
|
||||
|
||||
$redirect = isset($_POST['redirect']) ? $_POST['redirect'] : '/admin/';
|
||||
// Allow only internal admin redirects for safety
|
||||
if (!preg_match('#^/admin/#', $redirect)) {
|
||||
$redirect = '/admin/';
|
||||
}
|
||||
header('Location: ' . $redirect);
|
||||
exit();
|
||||
65
app/public/admin/actions/publish.php
Normal file
65
app/public/admin/actions/publish.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
App::flash('error', 'Erreur de sécurité : token invalide.');
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$isBulk = !empty($_POST['bulk']);
|
||||
|
||||
if (!in_array($action, ['publish', 'unpublish'], true)) {
|
||||
App::flash('error', 'Action invalide.');
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$published = ($action === 'publish');
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
|
||||
if ($isBulk) {
|
||||
$ids = array_filter(array_map('intval', $_POST['selected_theses'] ?? []), fn($id) => $id > 0);
|
||||
|
||||
if (empty($ids)) {
|
||||
App::flash('error', 'Aucun TFE sélectionné.');
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db->bulkSetPublished($ids, $published);
|
||||
$count = count($ids);
|
||||
App::flash('success', $published
|
||||
? "$count TFE(s) publié(s) avec succès."
|
||||
: "$count TFE(s) retiré(s) de la publication.");
|
||||
|
||||
} else {
|
||||
$thesisId = filter_var($_POST['thesis_id'] ?? '', FILTER_VALIDATE_INT);
|
||||
|
||||
if (!$thesisId || $thesisId <= 0) {
|
||||
App::flash('error', 'ID invalide.');
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db->setPublished($thesisId, $published);
|
||||
App::flash('success', $published ? 'TFE publié avec succès.' : 'TFE retiré de la publication.');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('publish.php error: ' . $e->getMessage());
|
||||
App::flash('error', 'Erreur lors de la modification : ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
49
app/public/admin/actions/settings.php
Normal file
49
app/public/admin/actions/settings.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
App::flash('error', "Erreur de sécurité : token invalide.");
|
||||
header('Location: /admin/parametres.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once APP_ROOT . '/src/Database.php';
|
||||
require_once APP_ROOT . '/src/SmtpRelay.php';
|
||||
$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 === 'smtp') {
|
||||
$smtpData = [
|
||||
'host' => $_POST['smtp_host'] ?? '',
|
||||
'port' => $_POST['smtp_port'] ?? 587,
|
||||
'encryption' => $_POST['smtp_encryption'] ?? 'tls',
|
||||
'username' => $_POST['smtp_username'] ?? '',
|
||||
'from_email' => $_POST['smtp_from_email'] ?? '',
|
||||
'from_name' => $_POST['smtp_from_name'] ?? 'Post-ERG',
|
||||
];
|
||||
// Only update password when user actually typed something.
|
||||
$pwd = $_POST['smtp_password'] ?? '';
|
||||
if ($pwd !== '') {
|
||||
$smtpData['password'] = $pwd;
|
||||
}
|
||||
SmtpRelay::updateSettings($db, $smtpData);
|
||||
App::flash('success', "Paramètres SMTP mis à jour.");
|
||||
} else {
|
||||
App::flash('error', "Section inconnue.");
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
header('Location: /admin/parametres.php');
|
||||
exit;
|
||||
50
app/public/admin/actions/tag.php
Normal file
50
app/public/admin/actions/tag.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST'
|
||||
|| !isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
http_response_code(403);
|
||||
die("Accès refusé.");
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
switch ($action) {
|
||||
case 'rename':
|
||||
$id = filter_var($_POST['tag_id'] ?? '', FILTER_VALIDATE_INT);
|
||||
$newName = trim($_POST['new_name'] ?? '');
|
||||
if (!$id || $newName === '') throw new Exception("Paramètres invalides.");
|
||||
$db->renameTag($id, $newName);
|
||||
break;
|
||||
|
||||
case 'merge':
|
||||
$sourceId = filter_var($_POST['source_id'] ?? '', FILTER_VALIDATE_INT);
|
||||
$targetId = filter_var($_POST['target_id'] ?? '', FILTER_VALIDATE_INT);
|
||||
if (!$sourceId || !$targetId) throw new Exception("Paramètres invalides.");
|
||||
$db->mergeTag($sourceId, $targetId);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$id = filter_var($_POST['tag_id'] ?? '', FILTER_VALIDATE_INT);
|
||||
if (!$id) throw new Exception("ID invalide.");
|
||||
$db->deleteTag($id);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("Action inconnue.");
|
||||
}
|
||||
|
||||
App::flash('success', "Opération effectuée.");
|
||||
} catch (Exception $e) {
|
||||
App::flash('error', $e->getMessage());
|
||||
}
|
||||
|
||||
header('Location: /admin/tags.php');
|
||||
exit();
|
||||
55
app/public/admin/actions/visibility.php
Normal file
55
app/public/admin/actions/visibility.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
App::flash('error', "Erreur de sécurité : token invalide.");
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
|
||||
$action = $_POST['action'] ?? ''; // 'set_visibility'
|
||||
$accessTypeId = filter_var($_POST['access_type_id'] ?? '', FILTER_VALIDATE_INT) ?: null;
|
||||
$isBulk = !empty($_POST['bulk']);
|
||||
|
||||
$validAccess = [null, 1, 2, 3];
|
||||
if (!in_array($accessTypeId, $validAccess, true)) {
|
||||
App::flash('error', "Valeur de visibilité invalide.");
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
|
||||
if ($isBulk) {
|
||||
$ids = array_filter(array_map('intval', $_POST['selected_theses'] ?? []), fn($id) => $id > 0);
|
||||
if (empty($ids)) {
|
||||
App::flash('error', "Aucun TFE sélectionné.");
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
$db->bulkSetVisibility($ids, $accessTypeId);
|
||||
App::flash('success', count($ids) . " TFE(s) mis à jour.");
|
||||
} else {
|
||||
$thesisId = filter_var($_POST['thesis_id'] ?? '', FILTER_VALIDATE_INT);
|
||||
if (!$thesisId) {
|
||||
App::flash('error', "ID invalide.");
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
$db->setVisibility($thesisId, $accessTypeId);
|
||||
App::flash('success', "Visibilité mise à jour.");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("visibility.php error: " . $e->getMessage());
|
||||
App::flash('error', "Erreur : " . $e->getMessage());
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
Reference in New Issue
Block a user