mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-26 00:29:18 +02:00
Index page: remove Mots-clés button, move export to bulk selection, fix ZipArchive error, move DB export to paramètres, sticky thead
- Remove 'Mots-clés' button from toolbar (redundant with admin sidebar tags) - Replace export dialog with 'Exporter CSV' + 'Exporter fichiers' buttons in bulk selection bar - Export dispatcher now accepts ?ids=1,2,3 for per-selection export - All ExportController/Database methods accept optional thesisIds array - Graceful error message when ZipArchive extension is missing on server - Move DB export (SQLite download) to paramètres → Maintenance section - Sticky table column headers (position: sticky, top: 0, z-index: 5) for index page table
This commit is contained in:
@@ -40,16 +40,17 @@ class ExportController
|
||||
// ── Files export ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Fetch all thesis file records with their thesis identifier.
|
||||
* Fetch thesis file records with their thesis identifier.
|
||||
*
|
||||
* @param int[] $thesisIds Optional filter by thesis IDs.
|
||||
* @return list<array{id:int, thesis_id:int, identifier:?string, file_type:string,
|
||||
* file_path:string, file_name:string, file_size:?int,
|
||||
* mime_type:?string, description:?string, sort_order:int,
|
||||
* display_label:?string, file_hash:?string}>
|
||||
*/
|
||||
public function getAllThesisFiles(): array
|
||||
public function getAllThesisFiles(array $thesisIds = []): array
|
||||
{
|
||||
return $this->db->getAllThesisFilesForExport();
|
||||
return $this->db->getAllThesisFilesForExport($thesisIds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,12 +59,13 @@ class ExportController
|
||||
* The manifest maps identifier → { title, files: [{type, path, name, size, mime, hash, label}] }
|
||||
* and is used on restore to re-link files to DB records.
|
||||
*
|
||||
* @param int[] $thesisIds Optional filter by thesis IDs.
|
||||
* @return array
|
||||
*/
|
||||
public function buildExportManifest(): array
|
||||
public function buildExportManifest(array $thesisIds = []): array
|
||||
{
|
||||
$files = $this->getAllThesisFiles();
|
||||
$theses = $this->db->getAllThesesForExport();
|
||||
$files = $this->getAllThesisFiles($thesisIds);
|
||||
$theses = $this->db->getAllThesesForExport($thesisIds);
|
||||
|
||||
// Index theses by id for O(1) lookup
|
||||
$byId = [];
|
||||
@@ -115,17 +117,18 @@ class ExportController
|
||||
* Returns the path to the temporary zip file. Caller is responsible
|
||||
* for unlink() after streaming.
|
||||
*
|
||||
* @param int[] $thesisIds Optional filter by thesis IDs.
|
||||
* @param string|null $baseDir Base directory path for files inside the zip.
|
||||
* Defaults to "files" (so files are at "files/theses/...").
|
||||
* @return string Absolute path to the generated zip file.
|
||||
* @throws Exception if zip creation fails.
|
||||
*/
|
||||
public function createExportZip(?string $baseDir = null): string
|
||||
public function createExportZip(array $thesisIds = [], ?string $baseDir = null): string
|
||||
{
|
||||
$baseDir ??= 'files';
|
||||
$storageRoot = defined('STORAGE_ROOT') ? STORAGE_ROOT : APP_ROOT . '/storage';
|
||||
$files = $this->getAllThesisFiles();
|
||||
$manifest = $this->buildExportManifest();
|
||||
$files = $this->getAllThesisFiles($thesisIds);
|
||||
$manifest = $this->buildExportManifest($thesisIds);
|
||||
|
||||
$tmpPath = tempnam(sys_get_temp_dir(), 'xamxam-export-');
|
||||
if ($tmpPath === false) {
|
||||
@@ -214,12 +217,13 @@ class ExportController
|
||||
*
|
||||
* Uses batch queries (one per related table) to avoid N+1.
|
||||
*
|
||||
* @param int[] $thesisIds Optional filter by thesis IDs.
|
||||
* @return list<list<string>> Each inner list has CSV_HEADERS_COUNT elements.
|
||||
*/
|
||||
public function exportAllTheses(): array
|
||||
public function exportAllTheses(array $thesisIds = []): array
|
||||
{
|
||||
// 1) Base thesis data (includes license_name via migration; fallback to license_type from the view)
|
||||
$theses = $this->db->getAllThesesForExport();
|
||||
$theses = $this->db->getAllThesesForExport($thesisIds);
|
||||
if ($theses === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -2540,9 +2540,12 @@ class Database
|
||||
* Fetch all theses (admin — includes unpublished) with every column
|
||||
* needed for the CSV export.
|
||||
*/
|
||||
public function getAllThesesForExport(): array
|
||||
/**
|
||||
* @param int[] $thesisIds Optional filter by thesis IDs.
|
||||
*/
|
||||
public function getAllThesesForExport(array $thesisIds = []): array
|
||||
{
|
||||
return $this->pdo->query('
|
||||
$sql = '
|
||||
SELECT
|
||||
t.id, t.identifier, t.title, t.subtitle, t.year,
|
||||
o.name AS orientation,
|
||||
@@ -2561,88 +2564,143 @@ class Database
|
||||
LEFT JOIN finality_types ft ON t.finality_id = ft.id
|
||||
LEFT JOIN access_types at ON t.access_type_id = at.id
|
||||
LEFT JOIN license_types lt ON t.license_id = lt.id
|
||||
ORDER BY t.year DESC, t.title ASC
|
||||
')->fetchAll();
|
||||
';
|
||||
if ($thesisIds) {
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$sql .= " WHERE t.id IN ($placeholders)";
|
||||
$stmt = $this->pdo->prepare($sql . ' ORDER BY t.year DESC, t.title ASC');
|
||||
$stmt->execute($thesisIds);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
return $this->pdo->query($sql . ' ORDER BY t.year DESC, t.title ASC')->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* All thesis→author rows with author name and email.
|
||||
* @param int[] $thesisIds Optional filter.
|
||||
*/
|
||||
public function getAllThesisAuthorsForExport(): array
|
||||
public function getAllThesisAuthorsForExport(array $thesisIds = []): array
|
||||
{
|
||||
return $this->pdo->query('
|
||||
$sql = '
|
||||
SELECT ta.thesis_id, a.name, a.email
|
||||
FROM thesis_authors ta
|
||||
JOIN authors a ON a.id = ta.author_id
|
||||
ORDER BY ta.thesis_id, ta.author_order
|
||||
')->fetchAll();
|
||||
';
|
||||
if ($thesisIds) {
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$sql .= " WHERE ta.thesis_id IN ($placeholders)";
|
||||
$stmt = $this->pdo->prepare($sql . ' ORDER BY ta.thesis_id, ta.author_order');
|
||||
$stmt->execute($thesisIds);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
return $this->pdo->query($sql . ' ORDER BY ta.thesis_id, ta.author_order')->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* All thesis→supervisor rows with name.
|
||||
* @param int[] $thesisIds Optional filter.
|
||||
*/
|
||||
public function getAllThesisSupervisorsForExport(): array
|
||||
public function getAllThesisSupervisorsForExport(array $thesisIds = []): array
|
||||
{
|
||||
return $this->pdo->query('
|
||||
$sql = '
|
||||
SELECT ts.thesis_id, s.name, ts.role, ts.is_external, ts.is_ulb
|
||||
FROM thesis_supervisors ts
|
||||
JOIN supervisors s ON s.id = ts.supervisor_id
|
||||
ORDER BY ts.thesis_id, ts.supervisor_order
|
||||
')->fetchAll();
|
||||
';
|
||||
if ($thesisIds) {
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$sql .= " WHERE ts.thesis_id IN ($placeholders)";
|
||||
$stmt = $this->pdo->prepare($sql . ' ORDER BY ts.thesis_id, ts.supervisor_order');
|
||||
$stmt->execute($thesisIds);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
return $this->pdo->query($sql . ' ORDER BY ts.thesis_id, ts.supervisor_order')->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* All thesis→tag rows with tag name.
|
||||
* @param int[] $thesisIds Optional filter.
|
||||
*/
|
||||
public function getAllThesisTagsForExport(): array
|
||||
public function getAllThesisTagsForExport(array $thesisIds = []): array
|
||||
{
|
||||
return $this->pdo->query('
|
||||
$sql = '
|
||||
SELECT tt.thesis_id, t.name
|
||||
FROM thesis_tags tt
|
||||
JOIN tags t ON t.id = tt.tag_id
|
||||
ORDER BY tt.thesis_id, t.name
|
||||
')->fetchAll();
|
||||
';
|
||||
if ($thesisIds) {
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$sql .= " WHERE tt.thesis_id IN ($placeholders)";
|
||||
$stmt = $this->pdo->prepare($sql . ' ORDER BY tt.thesis_id, t.name');
|
||||
$stmt->execute($thesisIds);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
return $this->pdo->query($sql . ' ORDER BY tt.thesis_id, t.name')->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* All thesis→language rows with language name.
|
||||
* @param int[] $thesisIds Optional filter.
|
||||
*/
|
||||
public function getAllThesisLanguagesForExport(): array
|
||||
public function getAllThesisLanguagesForExport(array $thesisIds = []): array
|
||||
{
|
||||
return $this->pdo->query('
|
||||
$sql = '
|
||||
SELECT tl.thesis_id, l.name
|
||||
FROM thesis_languages tl
|
||||
JOIN languages l ON l.id = tl.language_id
|
||||
ORDER BY tl.thesis_id, l.name
|
||||
')->fetchAll();
|
||||
';
|
||||
if ($thesisIds) {
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$sql .= " WHERE tl.thesis_id IN ($placeholders)";
|
||||
$stmt = $this->pdo->prepare($sql . ' ORDER BY tl.thesis_id, l.name');
|
||||
$stmt->execute($thesisIds);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
return $this->pdo->query($sql . ' ORDER BY tl.thesis_id, l.name')->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* All thesis→format rows with format name.
|
||||
* @param int[] $thesisIds Optional filter.
|
||||
*/
|
||||
public function getAllThesisFormatsForExport(): array
|
||||
public function getAllThesisFormatsForExport(array $thesisIds = []): array
|
||||
{
|
||||
return $this->pdo->query('
|
||||
$sql = '
|
||||
SELECT tf.thesis_id, ft.name
|
||||
FROM thesis_formats tf
|
||||
JOIN format_types ft ON ft.id = tf.format_id
|
||||
ORDER BY tf.thesis_id, ft.name
|
||||
')->fetchAll();
|
||||
';
|
||||
if ($thesisIds) {
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$sql .= " WHERE tf.thesis_id IN ($placeholders)";
|
||||
$stmt = $this->pdo->prepare($sql . ' ORDER BY tf.thesis_id, ft.name');
|
||||
$stmt->execute($thesisIds);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
return $this->pdo->query($sql . ' ORDER BY tf.thesis_id, ft.name')->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* All thesis files for the file-export ZIP.
|
||||
* Includes every thesis_files column + the thesis identifier for manifest
|
||||
* construction.
|
||||
* @param int[] $thesisIds Optional filter.
|
||||
*/
|
||||
public function getAllThesisFilesForExport(): array
|
||||
public function getAllThesisFilesForExport(array $thesisIds = []): array
|
||||
{
|
||||
return $this->pdo->query('
|
||||
$sql = '
|
||||
SELECT tf.*, t.identifier
|
||||
FROM thesis_files tf
|
||||
JOIN theses t ON t.id = tf.thesis_id
|
||||
ORDER BY t.year DESC, t.title ASC, tf.sort_order ASC
|
||||
')->fetchAll();
|
||||
';
|
||||
if ($thesisIds) {
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$sql .= " WHERE tf.thesis_id IN ($placeholders)";
|
||||
$stmt = $this->pdo->prepare($sql . ' ORDER BY t.year DESC, t.title ASC, tf.sort_order ASC');
|
||||
$stmt->execute($thesisIds);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
return $this->pdo->query($sql . ' ORDER BY t.year DESC, t.title ASC, tf.sort_order ASC')->fetchAll();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
|
||||
Reference in New Issue
Block a user