mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
Add delete/batch-delete and sortable columns to admin list
- Database: add deleteThesis() and bulkDeleteTheses() methods with file cleanup - Database: add SORT_MAP + buildOrderBy() for safe column sorting - Database: getThesesList() now respects sort/dir filter params - New action: actions/delete.php (single + batch delete with CSRF) - Admin index: delete button per row with confirmation dialog - Admin index: batch 'Supprimer' button in bulk actions bar - Admin index: sortable column headers (ID, Titre, Année, Orientation, AP, Statut) - Admin index: sort state preserved in pagination links - CSS: admin-btn-delete (red muted), admin-sort-link styles
This commit is contained in:
@@ -661,6 +661,37 @@ class Database {
|
||||
* @param array $filters
|
||||
* @return array
|
||||
*/
|
||||
/**
|
||||
* Allowed sort columns for the admin list.
|
||||
* Maps query-string `sort` values to safe SQL ORDER BY expressions.
|
||||
*/
|
||||
private const SORT_MAP = [
|
||||
'id' => 't.id',
|
||||
'identifier' => 't.identifier',
|
||||
'title' => 't.title',
|
||||
'year' => 't.year',
|
||||
'orientation' => 'o.name',
|
||||
'ap_program' => 'ap.name',
|
||||
'is_published' => 't.is_published',
|
||||
'submitted_at' => 't.submitted_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* Build the ORDER BY clause from sort/direction parameters.
|
||||
* Returns a safe SQL fragment (never interpolates raw user input).
|
||||
*/
|
||||
private function buildOrderBy(array $filters): string {
|
||||
$sort = $filters['sort'] ?? 'submitted_at';
|
||||
$dir = isset($filters['dir']) && strtolower($filters['dir']) === 'asc' ? 'ASC' : 'DESC';
|
||||
|
||||
$col = self::SORT_MAP[$sort] ?? self::SORT_MAP['submitted_at'];
|
||||
|
||||
// Secondary sort for stable ordering
|
||||
$secondary = ($sort === 'year') ? ', t.title ASC' : ', t.id DESC';
|
||||
|
||||
return "ORDER BY {$col} {$dir}{$secondary}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Count theses matching the given admin filters (no LIMIT).
|
||||
* Used alongside getThesesList() to calculate total pages.
|
||||
@@ -746,7 +777,8 @@ class Database {
|
||||
$params[] = intval($filters['ap']);
|
||||
}
|
||||
|
||||
$sql .= " GROUP BY t.id ORDER BY t.year DESC, t.submitted_at DESC";
|
||||
$orderBy = $this->buildOrderBy($filters);
|
||||
$sql .= " GROUP BY t.id {$orderBy}";
|
||||
|
||||
if ($limit > 0) {
|
||||
$sql .= " LIMIT :limit OFFSET :offset";
|
||||
@@ -1560,6 +1592,60 @@ class Database {
|
||||
return $thesisId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single thesis and all its related data (cascade via FK).
|
||||
* Also removes the banner file from disk if present.
|
||||
*/
|
||||
public function deleteThesis(int $thesisId): void {
|
||||
// Clean up banner file
|
||||
$bannerPath = $this->getThesisBannerPath($thesisId);
|
||||
if ($bannerPath !== null) {
|
||||
$fullPath = defined('STORAGE_ROOT') ? STORAGE_ROOT . '/' . $bannerPath : null;
|
||||
if ($fullPath && file_exists($fullPath)) {
|
||||
@unlink($fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up thesis files from disk
|
||||
$files = $this->getThesisFiles($thesisId);
|
||||
foreach ($files as $file) {
|
||||
if (!empty($file['file_path']) && file_exists($file['file_path'])) {
|
||||
@unlink($file['file_path']);
|
||||
}
|
||||
}
|
||||
|
||||
// DB cascade handles junction tables
|
||||
$this->pdo->prepare("DELETE FROM theses WHERE id = ?")->execute([$thesisId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple theses at once.
|
||||
* @param int[] $thesisIds
|
||||
*/
|
||||
public function bulkDeleteTheses(array $thesisIds): void {
|
||||
if (empty($thesisIds)) return;
|
||||
|
||||
// Clean up files for each thesis
|
||||
foreach ($thesisIds as $id) {
|
||||
$bannerPath = $this->getThesisBannerPath($id);
|
||||
if ($bannerPath !== null) {
|
||||
$fullPath = defined('STORAGE_ROOT') ? STORAGE_ROOT . '/' . $bannerPath : null;
|
||||
if ($fullPath && file_exists($fullPath)) {
|
||||
@unlink($fullPath);
|
||||
}
|
||||
}
|
||||
$files = $this->getThesisFiles($id);
|
||||
foreach ($files as $file) {
|
||||
if (!empty($file['file_path']) && file_exists($file['file_path'])) {
|
||||
@unlink($file['file_path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$this->pdo->prepare("DELETE FROM theses WHERE id IN ($placeholders)")->execute($thesisIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the stored identifier string (e.g. "2024-003") for an existing thesis.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user