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:
Pontoporeia
2026-03-24 15:35:52 +01:00
parent 0933137540
commit 92e344b757
17 changed files with 661 additions and 35 deletions

View File

@@ -36,7 +36,9 @@ try {
$db->beginTransaction();
// Update thesis basic info
$editLicenseId = filter_var($_POST['license_id'] ?? '', FILTER_VALIDATE_INT) ?: null;
$editLicenseId = filter_var($_POST['license_id'] ?? '', FILTER_VALIDATE_INT) ?: null;
$editAccessTypeId = filter_var($_POST['access_type_id'] ?? '', FILTER_VALIDATE_INT) ?: null;
$editContextNote = trim($_POST['context_note'] ?? '');
$stmt = $pdo->prepare("
UPDATE theses SET
@@ -47,9 +49,11 @@ try {
ap_program_id = ?,
finality_id = ?,
synopsis = ?,
context_note = ?,
file_size_info = ?,
baiu_link = ?,
license_id = ?,
access_type_id = ?,
is_published = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
@@ -63,9 +67,11 @@ try {
intval($_POST['ap']),
intval($_POST['finality']),
trim($_POST['synopsis']),
!empty($editContextNote) ? $editContextNote : null,
!empty($_POST['duration_info']) ? trim($_POST['duration_info']) : null,
!empty($_POST['lien']) ? trim($_POST['lien']) : null,
$editLicenseId,
$editAccessTypeId,
isset($_POST['is_published']) ? 1 : 0,
$thesisId
]);
@@ -208,11 +214,15 @@ try {
$languages = $db->getAllLanguages();
$formatTypes = $db->getAllFormatTypes();
$licenseTypes = $db->getAllLicenseTypes();
$accessTypes = $db->getAccessTypes();
// Fetch raw license_id FK (view only exposes license_type name string)
$licenseStmt = $pdo->prepare("SELECT license_id FROM theses WHERE id = ?");
$licenseStmt->execute([$thesisId]);
$currentLicenseId = $licenseStmt->fetchColumn();
// Fetch raw FK IDs (view only exposes name strings)
$rawStmt = $pdo->prepare("SELECT license_id, access_type_id, context_note FROM theses WHERE id = ?");
$rawStmt->execute([$thesisId]);
$rawRow = $rawStmt->fetch();
$currentLicenseId = $rawRow['license_id'] ?? null;
$currentAccessTypeId = $rawRow['access_type_id'] ?? null;
$currentContextNote = $rawRow['context_note'] ?? '';
// Set page title for header
$pageTitle = "Éditer TFE - " . htmlspecialchars($thesis['title']);
@@ -380,6 +390,31 @@ try {
}
</script>
<div class="admin-form-row">
<label class="admin-label" for="access_type_id">Visibilité / Accès :</label>
<select class="admin-select" id="access_type_id" name="access_type_id">
<option value="">— Non défini —</option>
<?php foreach ($accessTypes as $at): ?>
<option value="<?= (int)$at['id'] ?>"
<?= ($currentAccessTypeId == $at['id']) ? 'selected' : '' ?>>
<?= htmlspecialchars($at['name']) ?>
<?php if (!empty($at['description'])): ?>
— <?= htmlspecialchars(mb_strimwidth($at['description'], 0, 60, '…')) ?>
<?php endif; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="admin-form-row" style="align-items:start;">
<label class="admin-label" for="context_note">Note contextuelle :</label>
<div>
<textarea class="admin-textarea" id="context_note" name="context_note"
rows="4" maxlength="1500"><?= htmlspecialchars($currentContextNote ?? '') ?></textarea>
<p class="admin-hint">Visible publiquement pour les TFE Interne ou Interdit. Max 1 500 caractères.</p>
</div>
</div>
<div class="admin-form-row">
<label class="admin-label" for="license_id">Licence :</label>
<select class="admin-select" id="license_id" name="license_id">