admin: unify templates, dynamic navigation, and PHP cleanup

This commit is contained in:
Théophile Gervreau-Mercier
2026-02-06 14:31:23 +01:00
parent 52decc3e5f
commit df611b0333
8 changed files with 91 additions and 602 deletions

View File

@@ -55,15 +55,15 @@ function wasSelected($key, $value)
return $formData[$key] == $value;
}
?>
<?php include "inc/head.php"?>
<?php require_once __DIR__ . "/inc/head.php"; ?>
<main>
<?php if ($error): ?>
<div class="error-message" style="background: #fee; border: 2px solid #c00; padding: 1rem; margin-bottom: 1rem; border-radius: 4px; color: #c00;">
<div class="error-message">
<strong>⚠️ Erreur:</strong> <?php echo htmlspecialchars($error); ?>
</div>
<?php endif; ?>
<form action="formulaire.php" method="post" enctype="multipart/form-data">
<form action="actions/formulaire.php" method="post" enctype="multipart/form-data">
<!-- CSRF Protection -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(
$_SESSION["csrf_token"],
@@ -175,7 +175,7 @@ function wasSelected($key, $value)
"problématique",
); ?></textarea>
<label>Langue(s) du TFE * (sélection multiple possible)</label>
<ul style="list-style: none;">
<ul class="no-style">
<?php foreach ($languages as $language): ?>
<li>
<label class="checkbox-label">
@@ -194,7 +194,7 @@ function wasSelected($key, $value)
</ul>
<label>Format(s) (sélection multiple possible)</label>
<ul style="list-style: none;">
<ul class="no-style">
<?php foreach ($formatTypes as $format): ?>
<li>
<label class="checkbox-label">
@@ -246,4 +246,4 @@ function wasSelected($key, $value)
</form>
</main>
<?php include "inc/footer.php"?>
<?php require_once __DIR__ . "/inc/footer.php"; ?>

View File

@@ -164,39 +164,25 @@ try {
$languages = $db->getAllLanguages();
$formatTypes = $db->getAllFormatTypes();
// Set page title for header
$pageTitle = "Éditer TFE - " . htmlspecialchars($thesis['title']);
} catch (Exception $e) {
error_log("Error loading edit page: " . $e->getMessage());
die("Erreur lors du chargement: " . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Éditer TFE - <?php echo htmlspecialchars($thesis['title']); ?></title>
<link rel="stylesheet" href="assets/normalize.css">
<link rel="stylesheet" href="https://raw.githack.com/waldyrious/downstyler/master/downstyler.css" />
<link rel="shortcut icon" href="assets/icon.svg" type="image/svg">
</head>
<body>
<header>
<h1>Éditer TFE</h1>
<nav>
<a href="list.php">← Liste</a> |
<a href="thanks.php?id=<?php echo $thesisId; ?>">Voir</a>
</nav>
</header>
<?php require_once __DIR__ . '/inc/head.php'; ?>
<main>
<?php if ($error): ?>
<div style="background: #fee; border: 2px solid #c00; padding: 1rem; margin-bottom: 1rem; border-radius: 4px; color: #c00;">
<div class="alert-error">
<strong>⚠️ Erreur:</strong> <?php echo htmlspecialchars($error); ?>
</div>
<?php endif; ?>
<?php if ($success): ?>
<div style="background: #efe; border: 2px solid #0a0; padding: 1rem; margin-bottom: 1rem; border-radius: 4px; color: #0a0;">
<div class="alert-success">
<strong>✓ <?php echo htmlspecialchars($success); ?></strong>
</div>
<?php endif; ?>
@@ -319,7 +305,7 @@ try {
<h2>Publication</h2>
<fieldset>
<label style="display: flex; align-items: center; gap: 0.5rem;">
<label class="checkbox-label">
<input type="checkbox" name="is_published" value="1" <?php echo $thesis['is_published'] ? 'checked' : ''; ?>>
<span>Publier ce TFE sur le site public</span>
</label>
@@ -327,12 +313,8 @@ try {
</fieldset>
<button type="submit">Enregistrer les modifications</button>
<a href="thanks.php?id=<?php echo $thesisId; ?>">Annuler</a>
<a href="/admin/thanks.php?id=<?php echo $thesisId; ?>">Annuler</a>
</form>
</main>
<footer>
<p>Édition TFE #<?php echo $thesisId; ?></p>
</footer>
</body>
</html>
<?php require_once __DIR__ . '/inc/footer.php'; ?>

View File

@@ -1,331 +0,0 @@
<?php // formulaire.php
// Bootstrap application
require_once __DIR__ . "/../../config/bootstrap.php";
// Configure error reporting
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', 'error.log');
// Start session for CSRF protection
session_start();
// 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");
die("Erreur de sécurité : token invalide. Veuillez recharger le formulaire.");
}
// Log the content of the $_FILES array
error_log("FILES array: " . print_r($_FILES, true));
require_once __DIR__ . '/../../lib/Database.php';
// Helper function to sanitize string input
function sanitize_string($input) {
return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8');
}
// Helper function to validate required field
function validate_required($value, $fieldName) {
if (empty($value)) {
throw new Exception("Le champ '$fieldName' est requis.");
}
return $value;
}
try {
// Initialize database connection
$db = new Database();
$pdo = $db->getPDO();
// Begin transaction - all or nothing
$db->beginTransaction();
// ===== VALIDATE AND SANITIZE INPUT DATA =====
// Author information
$auteurName = validate_required(sanitize_string($_POST["auteurice"] ?? ''), "Nom/Prénom/Pseudo");
$mail = $_POST["mail"] ?? '';
if (!empty($mail)) {
// Could be email or social media handle
$mail = sanitize_string($mail);
}
// Year validation
$annee = filter_var($_POST["année"] ?? '', FILTER_VALIDATE_INT);
if ($annee === false || $annee < 2000 || $annee > (int)date('Y') + 1) {
throw new Exception("Année invalide. Veuillez entrer une année valide.");
}
// Academic details
$orientationId = filter_var($_POST["orientation"] ?? '', FILTER_VALIDATE_INT);
if ($orientationId === false) {
throw new Exception("Veuillez sélectionner une orientation.");
}
$apProgramId = filter_var($_POST["ap"] ?? '', FILTER_VALIDATE_INT);
if ($apProgramId === false) {
throw new Exception("Veuillez sélectionner un Atelier Pratique.");
}
$finalityId = filter_var($_POST["finality"] ?? '', FILTER_VALIDATE_INT);
if ($finalityId === false) {
throw new Exception("Veuillez sélectionner une finalité.");
}
// Thesis content
$titre = validate_required(sanitize_string($_POST["titre"] ?? ''), "Titre du mémoire");
$subtitle = sanitize_string($_POST["subtitle"] ?? '');
$synopsis = validate_required(sanitize_string($_POST["synopsis"] ?? ''), "Synopsis");
$problematique = sanitize_string($_POST["problématique"] ?? '');
$durationInfo = sanitize_string($_POST["duration_info"] ?? '');
// Supervisor(s)
$promoteuriceRaw = sanitize_string($_POST["promoteurice"] ?? '');
$supervisorNames = !empty($promoteuriceRaw) ? array_map('trim', explode(',', $promoteuriceRaw)) : [];
// Keywords (max 10)
$tagRaw = sanitize_string($_POST["tag"] ?? '');
$keywords = !empty($tagRaw) ? array_map('trim', explode(',', $tagRaw)) : [];
if (count($keywords) > 10) {
throw new Exception("Maximum 10 mots-clés autorisés.");
}
// Languages (at least one required)
$languageIds = $_POST["languages"] ?? [];
if (empty($languageIds)) {
throw new Exception("Veuillez sélectionner au moins une langue.");
}
$languageIds = array_map('intval', $languageIds);
// Formats (optional, multiple selection)
$formatIds = isset($_POST["formats"]) ? array_map('intval', $_POST["formats"]) : [];
// External link
$lien = $_POST["lien"] ?? '';
if (!empty($lien)) {
$lien = filter_var($lien, FILTER_VALIDATE_URL);
if ($lien === false) {
throw new Exception("Lien URL invalide.");
}
}
// File uploads
$couverture = $_FILES["couverture"] ?? null;
$files = $_FILES["files"] ?? null;
// ===== CREATE OR FIND AUTHOR =====
$authorId = $db->findOrCreateAuthor($auteurName, $mail);
error_log("Author ID: $authorId");
// ===== INSERT THESIS RECORD =====
// Generate unique identifier (YYYY-NNN format)
$stmt = $pdo->prepare("SELECT COUNT(*) as count FROM theses WHERE year = ?");
$stmt->execute([$annee]);
$count = $stmt->fetch()['count'] + 1;
$identifier = sprintf("%d-%03d", $annee, $count);
$stmt = $pdo->prepare("
INSERT INTO theses (
identifier, title, subtitle, year,
orientation_id, ap_program_id, finality_id,
synopsis, file_size_info,
baiu_link,
submitted_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
");
$stmt->execute([
$identifier,
$titre,
!empty($subtitle) ? $subtitle : null,
$annee,
$orientationId,
$apProgramId,
$finalityId,
$synopsis,
!empty($durationInfo) ? $durationInfo : null,
!empty($lien) ? $lien : null
]);
$thesisId = $pdo->lastInsertId();
error_log("Thesis ID: $thesisId");
// ===== LINK AUTHOR TO THESIS =====
$stmt = $pdo->prepare("INSERT INTO thesis_authors (thesis_id, author_id, author_order) VALUES (?, ?, 1)");
$stmt->execute([$thesisId, $authorId]);
// ===== LINK SUPERVISORS TO THESIS =====
foreach ($supervisorNames as $index => $supervisorName) {
if (!empty($supervisorName)) {
$supervisorId = $db->findOrCreateSupervisor($supervisorName);
$stmt = $pdo->prepare("INSERT INTO thesis_supervisors (thesis_id, supervisor_id, supervisor_order) VALUES (?, ?, ?)");
$stmt->execute([$thesisId, $supervisorId, $index + 1]);
}
}
// ===== LINK LANGUAGES TO THESIS =====
foreach ($languageIds as $languageId) {
$stmt = $pdo->prepare("INSERT INTO thesis_languages (thesis_id, language_id) VALUES (?, ?)");
$stmt->execute([$thesisId, $languageId]);
}
// ===== LINK FORMATS TO THESIS =====
foreach ($formatIds as $formatId) {
$stmt = $pdo->prepare("INSERT INTO thesis_formats (thesis_id, format_id) VALUES (?, ?)");
$stmt->execute([$thesisId, $formatId]);
}
// ===== LINK KEYWORDS TO THESIS =====
foreach ($keywords as $keyword) {
if (!empty($keyword)) {
$keywordId = $db->findOrCreateKeyword($keyword);
if ($keywordId) {
$stmt = $pdo->prepare("INSERT INTO thesis_keywords (thesis_id, keyword_id) VALUES (?, ?)");
$stmt->execute([$thesisId, $keywordId]);
}
}
}
// ===== HANDLE FILE UPLOADS =====
// Create necessary directories
$uploadBaseDir = __DIR__ . "/data/theses/{$annee}/{$identifier}/";
$coverDir = __DIR__ . "/data/covers/";
if (!file_exists($uploadBaseDir)) {
mkdir($uploadBaseDir, 0755, true);
}
if (!file_exists($coverDir)) {
mkdir($coverDir, 0755, true);
}
// Define security constraints
$allowedMimeTypes = ['image/jpeg', 'image/png', 'application/pdf', 'video/mp4', 'application/zip'];
$allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf', 'mp4', 'zip'];
$maxFileSize = 50 * 1024 * 1024; // 50 MB
// Process cover image
$coverPath = null;
if ($couverture && isset($couverture["error"]) && $couverture["error"] === UPLOAD_ERR_OK) {
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($couverture["tmp_name"]);
$fileExtension = strtolower(pathinfo($couverture["name"], PATHINFO_EXTENSION));
// Only allow image files for cover
if (in_array($mimeType, ['image/jpeg', 'image/png']) &&
in_array($fileExtension, ['jpg', 'jpeg', 'png'])) {
// Generate random filename
$randomName = bin2hex(random_bytes(16));
$safeFileName = $randomName . "." . $fileExtension;
$targetFile = $coverDir . $safeFileName;
if (move_uploaded_file($couverture["tmp_name"], $targetFile)) {
chmod($targetFile, 0644);
$coverPath = "data/covers/" . $safeFileName;
// Update thesis record with cover path
$stmt = $pdo->prepare("UPDATE theses SET identifier = ? WHERE id = ?");
// Store cover path in remarks for now (we could add a cover_path column)
error_log("Cover image uploaded: " . $safeFileName);
}
} else {
error_log("Invalid cover image type: " . $mimeType);
}
}
// Process thesis files
if ($files && is_array($files["name"])) {
for ($i = 0; $i < count($files["name"]); $i++) {
// Skip if no file was uploaded for this slot
if ($files["error"][$i] === UPLOAD_ERR_NO_FILE) {
continue;
}
if ($files["error"][$i] !== UPLOAD_ERR_OK) {
error_log("File upload error code " . $files["error"][$i] . ": " . $files["name"][$i]);
continue;
}
// Validate file
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($files["tmp_name"][$i]);
$fileExtension = strtolower(pathinfo($files["name"][$i], PATHINFO_EXTENSION));
if (!in_array($mimeType, $allowedMimeTypes) || !in_array($fileExtension, $allowedExtensions)) {
error_log("Invalid file type: " . $files["name"][$i] . " (MIME: $mimeType)");
continue;
}
if ($files["size"][$i] > $maxFileSize) {
error_log("File too large: " . $files["name"][$i]);
continue;
}
// Generate random filename
$randomName = bin2hex(random_bytes(16));
$safeFileName = $randomName . "." . $fileExtension;
$targetFile = $uploadBaseDir . $safeFileName;
if (move_uploaded_file($files["tmp_name"][$i], $targetFile)) {
chmod($targetFile, 0644);
// Determine file type (simplified - could be enhanced)
$fileType = 'other';
if (strpos(strtolower($files["name"][$i]), 'annex') !== false) {
$fileType = 'annex';
} else if ($fileExtension === 'pdf') {
$fileType = 'main';
}
// Insert file record
$db->insertThesisFile(
$thesisId,
$fileType,
"data/theses/{$annee}/{$identifier}/" . $safeFileName,
basename($files["name"][$i]),
$files["size"][$i],
$mimeType
);
error_log("File uploaded: " . $safeFileName);
} else {
error_log("Failed to move file: " . $files["name"][$i]);
}
}
}
// ===== COMMIT TRANSACTION =====
$db->commit();
error_log("Thesis submission completed successfully: $identifier");
// Clear CSRF token
unset($_SESSION['csrf_token']);
// Redirect to thank you page
header('Location: thanks.php?id=' . urlencode($thesisId));
exit();
} catch (Exception $e) {
// Rollback transaction on error
if (isset($db)) {
$db->rollback();
}
error_log("Form processing error: " . $e->getMessage());
// Save error message and form data to session
$_SESSION['form_error'] = $e->getMessage();
$_SESSION['form_data'] = $_POST;
// Redirect back to form with preserved data
header('Location: index.php');
exit();
}

View File

@@ -275,20 +275,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>
// <title>Import CSV - Post-ERG</title>
// <header>
// <h1>Import CSV - Post-ERG</h1>
// <nav>
// <a href="index.php">← Nouveau TFE</a> |
// <a href="list.php">📋 Liste des TFE</a>
// </nav>
// </header>
<?php require_once __DIR__ . '/inc/head.php'; ?>
<main>
<main>
<h2>Importer des TFE depuis un fichier CSV</h2>
<?php if (!empty($errors)): ?>
<div style="background: #fee; border: 2px solid #c00; padding: 1rem; margin-bottom: 1rem; border-radius: 4px; color: #c00;">
<div class="alert-error">
<strong>⚠️ Erreurs:</strong>
<ul>
<?php foreach ($errors as $error): ?>
@@ -299,7 +292,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
<?php endif; ?>
<?php if ($message): ?>
<div style="background: #efe; border: 2px solid #0a0; padding: 1rem; margin-bottom: 1rem; border-radius: 4px; color: #0a0;">
<div class="alert-success">
<strong>✓ <?php echo htmlspecialchars($message); ?></strong>
</div>
<?php endif; ?>
@@ -327,8 +320,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
<?php if (!empty($importResults)): ?>
<h3>Résultats de l'import</h3>
<div style="background: #f5f5f5; padding: 1rem; border-radius: 4px; max-height: 400px; overflow-y: auto;">
<pre style="margin: 0; font-size: 0.9em;"><?php
<div class="info-message">
<pre><?php
foreach ($importResults as $result) {
echo htmlspecialchars($result) . "\n";
}
@@ -352,4 +345,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
<p>Voir: <code>../db/Database_TFE_test.csv</code></p>
</main>
<?php include "inc/footer.php" ?>
<?php require_once __DIR__ . "/inc/footer.php"; ?>

View File

@@ -1,22 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $pageTitle ?></title>
<title><?php echo htmlspecialchars($pageTitle ?? 'Admin'); ?> - Post-ERG</title>
<link rel="stylesheet" href="/assets/modern-normalize.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
<link rel="stylesheet" href="/assets/admin.css">
<link rel="shortcut icon" href="/assets/admin_favicon.svg" type="image/svg+xml">
<?php if (php_sapi_name() === 'cli-server'): ?>
<!-- Live reload for development -->
<script>
(function poll() {
fetch('/live-reload.php')
.then(r => r.json())
.then(d => { if (d.changed) location.reload(); else setTimeout(poll, 1000); })
.catch(() => setTimeout(poll, 2000));
})();
</script>
<?php endif; ?>
</head>
<body>
<header>
<h1><?php echo $pageTitle ?></h1>
<nav style="margin-top: 1rem;">
<a href="/admin/" style="font-size: 0.9em;"><button>📋 Liste des TFE</button></a>
<a href="/admin/import.php" style="font-size: 0.9em;"><button>📥 Importer CSV</button></a>
<h1><?php echo htmlspecialchars($pageTitle ?? 'Admin'); ?></h1>
<nav>
<?php
// Detect current page
$currentPage = basename($_SERVER['PHP_SELF']);
$thesisId = $_GET['id'] ?? null;
// Build navigation based on context
$navLinks = [];
// Always show list
if ($currentPage !== 'index.php') {
$navLinks[] = '<a href="/admin/"><button>📋 Liste des TFE</button></a>';
}
// Show add thesis if not on add page
if ($currentPage !== 'add.php') {
$navLinks[] = '<a href="/admin/add.php"><button> Ajouter un TFE</button></a>';
}
// Show import if not on import page
if ($currentPage !== 'import.php') {
$navLinks[] = '<a href="/admin/import.php"><button>📥 Importer CSV</button></a>';
}
// If on edit or thanks page with thesis ID, show edit and view links
if ($thesisId && in_array($currentPage, ['edit.php', 'thanks.php'])) {
if ($currentPage !== 'edit.php') {
$navLinks[] = '<a href="/admin/edit.php?id=' . intval($thesisId) . '"><button>✏️ Modifier</button></a>';
}
if ($currentPage !== 'thanks.php') {
$navLinks[] = '<a href="/admin/thanks.php?id=' . intval($thesisId) . '"><button>👁️ Voir</button></a>';
}
}
echo implode(' ', $navLinks);
?>
</nav>
</header>

View File

@@ -76,7 +76,7 @@ try {
}
?>
<?php include "inc/head.php" ?>
<?php require_once __DIR__ . "/inc/head.php"; ?>
<script>
function toggleAll(source) {
const checkboxes = document.querySelectorAll('input[name="selected_theses[]"]');
@@ -140,20 +140,20 @@ try {
<main>
<?php if (isset($_SESSION['error'])): ?>
<div style="background: #fee; border: 2px solid #c00; padding: 1rem; margin-bottom: 1rem; border-radius: 4px; color: #c00;">
<div class="alert-error">
<strong>⚠️ Erreur:</strong> <?php echo htmlspecialchars($_SESSION['error']);
unset($_SESSION['error']); ?>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['success'])): ?>
<div style="background: #efe; border: 2px solid #0a0; padding: 1rem; margin-bottom: 1rem; border-radius: 4px; color: #0a0;">
<div class="alert-success">
<strong>✓ <?php echo htmlspecialchars($_SESSION['success']);
unset($_SESSION['success']); ?></strong>
</div>
<?php endif; ?>
<div id="bulk-actions" class="bulk-actions" style="display: none;">
<div id="bulk-actions" class="bulk-actions">
<strong><span id="selected-count">0</span> TFE(s) sélectionné(s)</strong>
<div class="bulk-actions-buttons">
<button type="button" class="btn-bulk-publish" onclick="bulkAction('publish')">Publier la sélection</button>
@@ -161,7 +161,7 @@ try {
</div>
</div>
<form id="bulk-form" method="post" action="publish.php" style="display: none;">
<form id="bulk-form" method="post" action="actions/publish.php">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<input type="hidden" id="bulk-action-input" name="action" value="">
<input type="hidden" name="bulk" value="1">
@@ -184,7 +184,7 @@ try {
</div>
<div class="filters">
<form method="get" action="list.php">
<form method="get" action="/admin/">
<fieldset>
<label for="search">Rechercher</label>
<input type="text" id="search" name="search" placeholder="Titre, auteur..." value="<?php echo htmlspecialchars($searchQuery); ?>">
@@ -216,7 +216,7 @@ try {
<button type="submit">Filtrer</button>
<?php if ($searchQuery || $yearFilter || $orientationFilter): ?>
<a href="list.php">Réinitialiser</a>
<a href="/admin/">Réinitialiser</a>
<?php endif; ?>
</form>
</div>
@@ -262,9 +262,9 @@ try {
</td>
<td>
<div class="actions">
<a href="thanks.php?id=<?php echo $thesis['id']; ?>" class="btn btn-view">Voir</a>
<a href="edit.php?id=<?php echo $thesis['id']; ?>" class="btn btn-edit">Éditer</a>
<form method="post" action="publish.php" class="publish-form">
<a href="/admin/thanks.php?id=<?php echo $thesis['id']; ?>" class="btn btn-view">Voir</a>
<a href="/admin/edit.php?id=<?php echo $thesis['id']; ?>" class="btn btn-edit">Éditer</a>
<form method="post" action="actions/publish.php" class="publish-form">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<input type="hidden" name="thesis_id" value="<?php echo $thesis['id']; ?>">
<?php if ($thesis['is_published']): ?>
@@ -284,4 +284,4 @@ try {
<?php endif; ?>
</main>
<?php include "inc/footer.php" ?>
<?php require_once __DIR__ . "/inc/footer.php"; ?>

View File

@@ -1,98 +0,0 @@
<?php
// Bootstrap application
require_once __DIR__ . "/../../config/bootstrap.php";
/**
* Handle publish/unpublish actions for theses
*/
session_start();
require_once __DIR__ . '/../../lib/Database.php';
// Verify CSRF token
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
$_SESSION['error'] = "Erreur de sécurité : token invalide.";
header('Location: list.php');
exit;
}
$action = isset($_POST['action']) ? $_POST['action'] : '';
$isBulk = isset($_POST['bulk']) && $_POST['bulk'] == '1';
if (!in_array($action, ['publish', 'unpublish'])) {
$_SESSION['error'] = "Action invalide.";
header('Location: list.php');
exit;
}
try {
$db = new Database();
$pdo = $db->getPDO();
$isPublished = ($action === 'publish') ? 1 : 0;
if ($isBulk) {
// Handle bulk action
$thesisIds = isset($_POST['selected_theses']) ? $_POST['selected_theses'] : [];
if (empty($thesisIds)) {
$_SESSION['error'] = "Aucun TFE sélectionné.";
header('Location: list.php');
exit;
}
// Validate all IDs are integers
$thesisIds = array_map('intval', $thesisIds);
$thesisIds = array_filter($thesisIds, fn($id) => $id > 0);
if (empty($thesisIds)) {
$_SESSION['error'] = "IDs invalides.";
header('Location: list.php');
exit;
}
// Prepare placeholders for IN clause
$placeholders = str_repeat('?,', count($thesisIds) - 1) . '?';
$sql = "UPDATE theses SET is_published = ?, updated_at = CURRENT_TIMESTAMP WHERE id IN ($placeholders)";
$stmt = $pdo->prepare($sql);
$params = array_merge([$isPublished], $thesisIds);
$stmt->execute($params);
$count = count($thesisIds);
if ($action === 'publish') {
$_SESSION['success'] = "$count TFE(s) publié(s) avec succès!";
} else {
$_SESSION['success'] = "$count TFE(s) retiré(s) de la publication.";
}
} else {
// Handle single action
$thesisId = isset($_POST['thesis_id']) ? intval($_POST['thesis_id']) : 0;
if ($thesisId <= 0) {
$_SESSION['error'] = "ID invalide.";
header('Location: list.php');
exit;
}
$stmt = $pdo->prepare("UPDATE theses SET is_published = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?");
$stmt->execute([$isPublished, $thesisId]);
if ($action === 'publish') {
$_SESSION['success'] = "TFE publié avec succès!";
} else {
$_SESSION['success'] = "TFE retiré de la publication.";
}
}
} catch (Exception $e) {
error_log("Publish error: " . $e->getMessage());
$_SESSION['error'] = "Erreur lors de la modification: " . $e->getMessage();
}
// Regenerate CSRF token
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
header('Location: list.php');
exit;

View File

@@ -63,36 +63,17 @@ function formatFileSize($bytes) {
return $bytes . ' bytes';
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Merci - Post-ERG</title>
<link rel="stylesheet" href="assets/normalize.css">
<link rel="stylesheet" href="assets/simple.css">
<link rel="stylesheet" href="assets/posterg.css">
<link rel="shortcut icon" href="assets/icon.svg" type="image/svg">
</head>
<body>
<header>
<h1>Merci</h1>
<?php if ($thesis): ?>
<nav style="margin-top: 1rem;">
<a href="list.php">Liste des TFE</a> |
<a href="edit.php?id=<?php echo $thesisId; ?>">✏️ Modifier ce TFE</a>
</nav>
<?php endif; ?>
</header>
// Set page title for header
$pageTitle = "Merci";
?>
<?php require_once __DIR__ . '/inc/head.php'; ?>
<main>
<?php if ($error): ?>
<div class="error">
<p>⚠️ <?php echo htmlspecialchars($error); ?></p>
<p><a href="index.php">Retour au formulaire</a></p>
<p><a href="/admin/add.php">Retour au formulaire</a></p>
</div>
<?php elseif ($thesis): ?>
@@ -205,94 +186,12 @@ function formatFileSize($bytes) {
</p>
</div>
<p><a href="index.php">Soumettre un autre TFE</a></p>
<p><a href="/admin/add.php">Soumettre un autre TFE</a></p>
<?php else: ?>
<p>Aucune donnée à afficher.</p>
<p><a href="index.php">Retour au formulaire</a></p>
<p><a href="/admin/add.php">Retour au formulaire</a></p>
<?php endif; ?>
</main>
<footer>
<p>Formulaire Post-ERG</p>
</footer>
</body>
</html>
<style>
.thesis-info {
background: #f5f5f5;
padding: 2rem;
border-radius: 8px;
margin: 2rem 0;
}
.thesis-info h2 {
margin-top: 0;
border-bottom: 2px solid #333;
padding-bottom: 0.5rem;
}
.thesis-info h3 {
margin-top: 2rem;
margin-bottom: 1rem;
color: #555;
}
.thesis-info dl {
display: grid;
grid-template-columns: 200px 1fr;
gap: 0.5rem 1rem;
margin-bottom: 1.5rem;
}
.thesis-info dt {
font-weight: bold;
color: #666;
}
.thesis-info dd {
margin: 0;
}
.thesis-info table {
width: 100%;
margin-top: 1rem;
}
.thesis-info table th {
text-align: left;
background: #ddd;
padding: 0.5rem;
}
.thesis-info table td {
padding: 0.5rem;
border-bottom: 1px solid #ddd;
}
.submitted-date {
margin-top: 2rem;
font-style: italic;
color: #666;
}
.error {
background: #fee;
border: 2px solid #c00;
padding: 1.5rem;
border-radius: 8px;
color: #c00;
}
@media (max-width: 768px) {
.thesis-info dl {
grid-template-columns: 1fr;
gap: 0.25rem;
}
.thesis-info dt {
margin-top: 1rem;
}
}
</style>
<?php require_once __DIR__ . '/inc/footer.php'; ?>