mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
Phase 1: Consolidate shared infrastructure - Create shared/ directory for common code - Consolidate Database.php from front-backend and formulaire into unified shared/Database.php - Smart path detection for test.db vs posterg.db - Secure search with wildcard escaping and input validation - Support both singleton and direct instantiation patterns - Full CRUD methods for admin functionality - Move RateLimit.php to shared/ (30 requests/min) - Update all require paths across apps to use shared/ Phase 2: Reorganize directory structure - Rename front-backend/ → apps/public/ - Rename formulaire/ → apps/admin/ - Rename db/ → database/ - Update all file paths for new structure - Create root .gitignore excluding databases, cache, logs Implement secure search feature - Add apps/public/search.php with full-text search across theses - Search filters: query, year, orientation, AP program, keywords - Security features: - SQL injection prevention (prepared statements) - Wildcard injection prevention (escape % and _) - Input validation (max 200 chars, year range 1900-2100) - Rate limiting (30 req/min per IP) - Pagination limited to 100 results/page - XSS protection (htmlspecialchars on output) Add comprehensive test suite - Create apps/public/tests/ with proper structure - tests/Integration/SearchTest.php - 12 search scenarios - tests/Security/SecurityTest.php - vulnerability testing - tests/Unit/RateLimitTest.php - rate limit behavior - Create database/fixtures/CreateTestDatabase.php - Add apps/public/run-tests.php test runner - All tests passing (4/4 suites) Update deployment configuration - Rename justfile 'sync' recipe to 'deploy' - Create deploy group with separate deploy-public and deploy-admin - Add test-deploy recipe for test database - Exclude *.db, tests/, cache/, *.md from production deploy - Deploy shared/ to both public and admin locations Stats: +4482 insertions, -654 deletions across 72 files
96 lines
2.9 KiB
PHP
96 lines
2.9 KiB
PHP
<?php
|
|
/**
|
|
* Handle publish/unpublish actions for theses
|
|
*/
|
|
session_start();
|
|
|
|
require_once __DIR__ . '/../../shared/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;
|