mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
feat: admin tag management, maintenance mode, TFE visibility states
Tags admin: - Database: getAllTagsWithCount(), renameTag(), mergeTag(), deleteTag() - public/admin/tags.php: table with inline rename/merge/delete forms, CSRF-guarded - public/admin/actions/tag.php: routes on action=rename|merge|delete - templates/admin/head.php: 'Mots-clés' nav link - admin.css: admin-inline-form, admin-btn--sm/warning/danger variants Maintenance mode: - config/bootstrap.php: gate on MAINTENANCE_FLAG file; admin/ and maintenance.php exempt - public/maintenance.php: 503 dark minimal page - public/admin/actions/maintenance.php: enable/disable toggle - public/admin/index.php: status bar with toggle button - admin.css: admin-maintenance-bar styles TFE Visibility (Libre/Interne/Interdit via existing access_type_id): - migration 002_add_visibility.sql: seeds access_types if missing - Database: setVisibility(), bulkSetVisibility(), getAccessTypes() - public/media.php: blocks thesis files for access_type_id=3 - public/tfe.php: shows access_type, context_note; hides file panel for Interdit - public/admin/edit.php: access_type_id select + context_note textarea; saves both - public/admin/index.php: three-state badge (Libre/Interne/Interdit) per row - public/admin/actions/visibility.php: single + bulk visibility action handler - admin.css: status-access badge variants
This commit is contained in:
101
src/Database.php
101
src/Database.php
@@ -640,6 +640,69 @@ class Database {
|
||||
return $this->findOrCreateTag((string)$keyword);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// TAG MANAGEMENT (admin)
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Return all tags with a count of associated (published) theses.
|
||||
*/
|
||||
public function getAllTagsWithCount(): array {
|
||||
$stmt = $this->pdo->query("
|
||||
SELECT tg.id, tg.name,
|
||||
COUNT(DISTINCT tt.thesis_id) as thesis_count
|
||||
FROM tags tg
|
||||
LEFT JOIN thesis_tags tt ON tg.id = tt.tag_id
|
||||
GROUP BY tg.id
|
||||
ORDER BY tg.name COLLATE NOCASE
|
||||
");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a tag. Throws if the new name already exists.
|
||||
*/
|
||||
public function renameTag(int $id, string $newName): void {
|
||||
$newName = trim($newName);
|
||||
if ($newName === '') throw new Exception("Le nom du tag ne peut pas être vide.");
|
||||
// Check uniqueness
|
||||
$stmt = $this->pdo->prepare("SELECT id FROM tags WHERE name = ? AND id != ?");
|
||||
$stmt->execute([$newName, $id]);
|
||||
if ($stmt->fetch()) throw new Exception("Un tag avec ce nom existe déjà.");
|
||||
$this->pdo->prepare("UPDATE tags SET name = ? WHERE id = ?")->execute([$newName, $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge sourceId into targetId: reassign all thesis_tags rows, then delete source.
|
||||
* Uses INSERT OR IGNORE to avoid PK conflicts.
|
||||
*/
|
||||
public function mergeTag(int $sourceId, int $targetId): void {
|
||||
if ($sourceId === $targetId) throw new Exception("Source et destination identiques.");
|
||||
$this->pdo->beginTransaction();
|
||||
try {
|
||||
// Re-point thesis_tags rows from source → target (skip conflicts)
|
||||
$this->pdo->prepare("
|
||||
INSERT OR IGNORE INTO thesis_tags (tag_id, thesis_id)
|
||||
SELECT ?, thesis_id FROM thesis_tags WHERE tag_id = ?
|
||||
")->execute([$targetId, $sourceId]);
|
||||
// Delete the old source rows
|
||||
$this->pdo->prepare("DELETE FROM thesis_tags WHERE tag_id = ?")->execute([$sourceId]);
|
||||
// Delete the source tag itself
|
||||
$this->pdo->prepare("DELETE FROM tags WHERE id = ?")->execute([$sourceId]);
|
||||
$this->pdo->commit();
|
||||
} catch (\Throwable $e) {
|
||||
$this->pdo->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a tag and all its thesis_tags rows (cascades via FK).
|
||||
*/
|
||||
public function deleteTag(int $id): void {
|
||||
$this->pdo->prepare("DELETE FROM tags WHERE id = ?")->execute([$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get orientation ID by name
|
||||
*/
|
||||
@@ -749,6 +812,44 @@ class Database {
|
||||
return $this->getLicenseTypes();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// VISIBILITY METHODS
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Set the access_type_id (visibility) for a single thesis.
|
||||
* @param int $thesisId
|
||||
* @param int|null $accessTypeId 1=Libre, 2=Interne, 3=Interdit, null=unset
|
||||
*/
|
||||
public function setVisibility(int $thesisId, ?int $accessTypeId): void {
|
||||
$stmt = $this->pdo->prepare(
|
||||
"UPDATE theses SET access_type_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"
|
||||
);
|
||||
$stmt->execute([$accessTypeId, $thesisId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility for multiple theses at once.
|
||||
* @param int[] $thesisIds
|
||||
* @param int|null $accessTypeId
|
||||
*/
|
||||
public function bulkSetVisibility(array $thesisIds, ?int $accessTypeId): void {
|
||||
if (empty($thesisIds)) return;
|
||||
$placeholders = implode(',', array_fill(0, count($thesisIds), '?'));
|
||||
$params = array_merge([$accessTypeId], $thesisIds);
|
||||
$this->pdo->prepare(
|
||||
"UPDATE theses SET access_type_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id IN ($placeholders)"
|
||||
)->execute($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all access types (visibility options).
|
||||
*/
|
||||
public function getAccessTypes(): array {
|
||||
$stmt = $this->pdo->query("SELECT * FROM access_types ORDER BY id");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// JURY METHODS
|
||||
// ========================================================================
|
||||
|
||||
Reference in New Issue
Block a user