mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-07 03:29:19 +02:00
- Created /templates for main site (header.php, footer.php)
- Created /templates/admin for admin section (head.php, footer.php)
- Removed /public/includes and /public/admin/inc
- Updated all references in code and docs
- Tests passing ✅
Cleaner separation: /public only contains web-accessible files (PHP entry points + assets)
289 lines
12 KiB
PHP
289 lines
12 KiB
PHP
<?php
|
|
// Bootstrap application
|
|
require_once __DIR__ . "/../../config/bootstrap.php";
|
|
require_once __DIR__ . '/../../lib/AdminAuth.php';
|
|
|
|
// PHP-level auth guard (defence-in-depth behind nginx Basic Auth)
|
|
AdminAuth::requireLogin();
|
|
|
|
// Generate CSRF token
|
|
if (empty($_SESSION['csrf_token'])) {
|
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
}
|
|
|
|
$pageTitle = "Liste des TFE";
|
|
|
|
require_once __DIR__ . '/../../lib/Database.php';
|
|
|
|
try {
|
|
$db = new Database();
|
|
$pdo = $db->getPDO();
|
|
|
|
// Get filter parameters
|
|
$searchQuery = isset($_GET['search']) ? trim($_GET['search']) : '';
|
|
$yearFilter = isset($_GET['year']) ? intval($_GET['year']) : null;
|
|
$orientationFilter = isset($_GET['orientation']) ? intval($_GET['orientation']) : null;
|
|
|
|
// Build query
|
|
$sql = "SELECT
|
|
t.id, t.identifier, t.title, t.subtitle, t.year,
|
|
o.name as orientation,
|
|
ap.name as ap_program,
|
|
GROUP_CONCAT(DISTINCT a.name) as authors,
|
|
t.submitted_at,
|
|
t.is_published
|
|
FROM theses t
|
|
LEFT JOIN orientations o ON t.orientation_id = o.id
|
|
LEFT JOIN ap_programs ap ON t.ap_program_id = ap.id
|
|
LEFT JOIN thesis_authors ta ON t.id = ta.thesis_id
|
|
LEFT JOIN authors a ON ta.author_id = a.id
|
|
WHERE 1=1";
|
|
|
|
$params = [];
|
|
|
|
if ($searchQuery) {
|
|
$sql .= " AND (t.title LIKE ? OR t.subtitle LIKE ? OR a.name LIKE ?)";
|
|
$searchParam = "%$searchQuery%";
|
|
$params[] = $searchParam;
|
|
$params[] = $searchParam;
|
|
$params[] = $searchParam;
|
|
}
|
|
|
|
if ($yearFilter) {
|
|
$sql .= " AND t.year = ?";
|
|
$params[] = $yearFilter;
|
|
}
|
|
|
|
if ($orientationFilter) {
|
|
$sql .= " AND t.orientation_id = ?";
|
|
$params[] = $orientationFilter;
|
|
}
|
|
|
|
$sql .= " GROUP BY t.id ORDER BY t.year DESC, t.submitted_at DESC";
|
|
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
$theses = $stmt->fetchAll();
|
|
|
|
// Get unique years for filter
|
|
$yearsStmt = $pdo->query("SELECT DISTINCT year FROM theses ORDER BY year DESC");
|
|
$years = $yearsStmt->fetchAll(PDO::FETCH_COLUMN);
|
|
|
|
// Get orientations for filter
|
|
$orientations = $db->getAllOrientations();
|
|
} catch (Exception $e) {
|
|
error_log("Error loading theses list: " . $e->getMessage());
|
|
die("Erreur lors du chargement de la liste.");
|
|
}
|
|
?>
|
|
|
|
<?php require_once APP_ROOT . '/templates/admin/head.php'; ?>
|
|
<script>
|
|
function toggleAll(source) {
|
|
const checkboxes = document.querySelectorAll('input[name="selected_theses[]"]');
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.checked = source.checked;
|
|
});
|
|
updateBulkActionsVisibility();
|
|
}
|
|
|
|
function updateBulkActionsVisibility() {
|
|
const checkboxes = document.querySelectorAll('input[name="selected_theses[]"]:checked');
|
|
const bulkActions = document.getElementById('bulk-actions');
|
|
const selectedCount = document.getElementById('selected-count');
|
|
|
|
if (checkboxes.length > 0) {
|
|
bulkActions.style.display = 'flex';
|
|
selectedCount.textContent = checkboxes.length;
|
|
} else {
|
|
bulkActions.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function bulkAction(action) {
|
|
const checkboxes = document.querySelectorAll('input[name="selected_theses[]"]:checked');
|
|
if (checkboxes.length === 0) {
|
|
alert('Veuillez sélectionner au moins un TFE.');
|
|
return false;
|
|
}
|
|
|
|
const actionText = action === 'publish' ? 'publier' : 'dépublier';
|
|
if (!confirm(`Voulez-vous vraiment ${actionText} ${checkboxes.length} TFE(s) ?`)) {
|
|
return false;
|
|
}
|
|
|
|
// Set action
|
|
document.getElementById('bulk-action-input').value = action;
|
|
|
|
// Copy selected thesis IDs to hidden form
|
|
const bulkCheckboxesContainer = document.getElementById('bulk-checkboxes');
|
|
bulkCheckboxesContainer.innerHTML = '';
|
|
checkboxes.forEach(checkbox => {
|
|
const input = document.createElement('input');
|
|
input.type = 'hidden';
|
|
input.name = 'selected_theses[]';
|
|
input.value = checkbox.value;
|
|
bulkCheckboxesContainer.appendChild(input);
|
|
});
|
|
|
|
// Submit the form
|
|
document.getElementById('bulk-form').submit();
|
|
return false;
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Add change listeners to all checkboxes
|
|
document.querySelectorAll('input[name="selected_theses[]"]').forEach(checkbox => {
|
|
checkbox.addEventListener('change', updateBulkActionsVisibility);
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<main>
|
|
<?php if (isset($_SESSION['error'])): ?>
|
|
<div class="alert-error">
|
|
<strong>⚠️ Erreur:</strong> <?php echo htmlspecialchars($_SESSION['error']);
|
|
unset($_SESSION['error']); ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if (isset($_SESSION['success'])): ?>
|
|
<div class="alert-success">
|
|
<strong>✓ <?php echo htmlspecialchars($_SESSION['success']);
|
|
unset($_SESSION['success']); ?></strong>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<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>
|
|
<button type="button" class="btn-bulk-unpublish" onclick="bulkAction('unpublish')">Dépublier la sélection</button>
|
|
</div>
|
|
</div>
|
|
|
|
<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">
|
|
<div id="bulk-checkboxes"></div>
|
|
</form>
|
|
|
|
<div class="stats">
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?php echo count($theses); ?></div>
|
|
<div class="stat-label">TFE total</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?php echo count(array_filter($theses, fn($t) => $t['is_published'])); ?></div>
|
|
<div class="stat-label">Publiés</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?php echo count(array_filter($theses, fn($t) => !$t['is_published'])); ?></div>
|
|
<div class="stat-label">En attente</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filters">
|
|
<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); ?>">
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<label for="year">Année</label>
|
|
<select id="year" name="year">
|
|
<option value="">Toutes</option>
|
|
<?php foreach ($years as $year): ?>
|
|
<option value="<?php echo $year; ?>" <?php echo $yearFilter == $year ? 'selected' : ''; ?>>
|
|
<?php echo $year; ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<label for="orientation">Orientation</label>
|
|
<select id="orientation" name="orientation">
|
|
<option value="">Toutes</option>
|
|
<?php foreach ($orientations as $orientation): ?>
|
|
<option value="<?php echo $orientation['id']; ?>" <?php echo $orientationFilter == $orientation['id'] ? 'selected' : ''; ?>>
|
|
<?php echo htmlspecialchars($orientation['name']); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</fieldset>
|
|
|
|
<button type="submit">Filtrer</button>
|
|
<?php if ($searchQuery || $yearFilter || $orientationFilter): ?>
|
|
<a href="/admin/">Réinitialiser</a>
|
|
<?php endif; ?>
|
|
</form>
|
|
</div>
|
|
|
|
<?php if (empty($theses)): ?>
|
|
<p>Aucun TFE trouvé.</p>
|
|
<?php else: ?>
|
|
<table class="thesis-table">
|
|
<thead>
|
|
<tr>
|
|
<th><input type="checkbox" class="select-all-checkbox" onchange="toggleAll(this)" title="Tout sélectionner"></th>
|
|
<th>ID</th>
|
|
<th>Titre</th>
|
|
<th>Auteur(s)</th>
|
|
<th>Année</th>
|
|
<th>Orientation</th>
|
|
<th>AP</th>
|
|
<th>Statut</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($theses as $thesis): ?>
|
|
<tr>
|
|
<td><input type="checkbox" class="select-checkbox" name="selected_theses[]" value="<?php echo $thesis['id']; ?>"></td>
|
|
<td><?php echo htmlspecialchars($thesis['identifier'] ?? $thesis['id']); ?></td>
|
|
<td>
|
|
<div class="thesis-title"><?php echo htmlspecialchars($thesis['title']); ?></div>
|
|
<?php if ($thesis['subtitle']): ?>
|
|
<div class="thesis-subtitle"><?php echo htmlspecialchars($thesis['subtitle']); ?></div>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><?php echo htmlspecialchars($thesis['authors'] ?? 'N/A'); ?></td>
|
|
<td><?php echo $thesis['year']; ?></td>
|
|
<td><?php echo htmlspecialchars($thesis['orientation'] ?? 'N/A'); ?></td>
|
|
<td><?php echo htmlspecialchars($thesis['ap_program'] ?? 'N/A'); ?></td>
|
|
<td>
|
|
<?php if ($thesis['is_published']): ?>
|
|
<span class="status-badge status-published">Publié</span>
|
|
<?php else: ?>
|
|
<span class="status-badge status-pending">En attente</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<div class="actions">
|
|
<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']): ?>
|
|
<input type="hidden" name="action" value="unpublish">
|
|
<button type="submit" class="btn btn-unpublish" onclick="return confirm('Retirer ce TFE de la publication ?');">Dépublier</button>
|
|
<?php else: ?>
|
|
<input type="hidden" name="action" value="publish">
|
|
<button type="submit" class="btn btn-publish">Publier</button>
|
|
<?php endif; ?>
|
|
</form>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
</main>
|
|
|
|
<?php require_once APP_ROOT . '/templates/admin/footer.php'; ?>
|