mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
Replace browser alert/confirm dialogs with <dialog> modals
- admin/index.php: alert() → no-selection dialog; confirm() bulk actions → bulk-confirm/bulk-delete dialogs; confirm() single delete → delete-thesis dialog; removed redundant confirm on Dépublier (reversible action) - admin/tags.php: confirm() merge/delete → merge-tag/delete-tag dialogs - admin/acces-etudiante.php: confirm() delete link → delete-link dialog - admin/acces.php: confirm() archive link → archive-link dialog - admin/parametres.php: confirm() maintenance/delete-all → enable-maintenance/delete-all-tfe dialogs; admin password confirm() kept with TODO comment - admin/account.php: admin password confirm() kept with TODO comment - admin.css: add .admin-dialog--sm, .admin-dialog__alert, .admin-dialog__footer styles
This commit is contained in:
9
TODO.md
9
TODO.md
@@ -36,6 +36,15 @@
|
|||||||
- [x] `templates/admin/acces.php` — archive button, archived links collapsible section
|
- [x] `templates/admin/acces.php` — archive button, archived links collapsible section
|
||||||
- [x] `scripts/setup-server.sh` — provision `/var/log/xamxam.log` with correct ownership
|
- [x] `scripts/setup-server.sh` — provision `/var/log/xamxam.log` with correct ownership
|
||||||
|
|
||||||
|
## Replace browser dialogs with `<dialog>` modals
|
||||||
|
- [x] `admin/index.php` — `alert()` (no selection) → `<dialog id="no-selection-dialog">`; `confirm()` bulk publish/unpublish → `<dialog id="bulk-confirm-dialog">`; `confirm()` bulk delete → `<dialog id="bulk-delete-dialog">`; `confirm()` single delete → `<dialog id="delete-thesis-dialog">`; inline `confirm()` on Dépublier button removed (no confirmation needed for reversible action)
|
||||||
|
- [x] `admin/tags.php` — `confirm()` merge → `<dialog id="merge-tag-dialog">`; `confirm()` delete → `<dialog id="delete-tag-dialog">`
|
||||||
|
- [x] `admin/acces-etudiante.php` — `confirm()` delete link → `<dialog id="delete-link-dialog">`
|
||||||
|
- [x] `admin/acces.php` — `confirm()` archive link → `<dialog id="archive-link-dialog">`
|
||||||
|
- [x] `admin/parametres.php` — `confirm()` enable maintenance → `<dialog id="enable-maintenance-dialog">`; `confirm()` delete all TFE → `<dialog id="delete-all-tfe-dialog">`; admin password `confirm()` kept with `TODO` comment
|
||||||
|
- [x] `admin/account.php` — admin password `confirm()` kept with `TODO` comment
|
||||||
|
- [x] `admin.css` — added `.admin-dialog--sm`, `.admin-dialog__alert`, `.admin-dialog__footer` styles
|
||||||
|
|
||||||
## Duplicate warning display fixes
|
## Duplicate warning display fixes
|
||||||
- [x] `toast-fragment.php` — 204 guard now also checks `warning`; warning was silently discarded before
|
- [x] `toast-fragment.php` — 204 guard now also checks `warning`; warning was silently discarded before
|
||||||
- [x] `partage/index.php` — warning stored as plain text (no pre-escaping); `htmlspecialchars()` applied once at render; was double-encoded before
|
- [x] `partage/index.php` — warning stored as plain text (no pre-escaping); `htmlspecialchars()` applied once at render; was double-encoded before
|
||||||
|
|||||||
@@ -915,6 +915,27 @@
|
|||||||
padding-bottom: var(--space-m);
|
padding-bottom: var(--space-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Small/confirmation dialog variant */
|
||||||
|
.admin-dialog--sm {
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-dialog__alert {
|
||||||
|
padding: var(--space-m) var(--space-l);
|
||||||
|
font-size: var(--step--1);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-dialog__alert p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-dialog__footer {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
padding: 0 var(--space-l) var(--space-m);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Import results log ─────────────────────────────────────────────── */
|
/* ── Import results log ─────────────────────────────────────────────── */
|
||||||
.admin-import-log {
|
.admin-import-log {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|||||||
1
app/storage/logs/admin.log
Normal file
1
app/storage/logs/admin.log
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"timestamp":"2026-05-04T15:36:30+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"system","action":"maintenance_on","status":"success"}
|
||||||
1
app/storage/maintenance.flag
Normal file
1
app/storage/maintenance.flag
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2026-05-04T15:36:30+00:00
|
||||||
@@ -95,11 +95,12 @@
|
|||||||
🔑
|
🔑
|
||||||
</button>
|
</button>
|
||||||
<form method="post" action="actions/acces-etudiante.php" class="publish-form"
|
<form method="post" action="actions/acces-etudiante.php" class="publish-form"
|
||||||
onsubmit="return confirm('Supprimer ce lien ? Les soumissions via ce lien seront bloquées.')">
|
id="delete-link-form-<?= $link['id'] ?>">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
<input type="hidden" name="action" value="delete">
|
<input type="hidden" name="action" value="delete">
|
||||||
<input type="hidden" name="id" value="<?= $link['id'] ?>">
|
<input type="hidden" name="id" value="<?= $link['id'] ?>">
|
||||||
<button type="submit" class="admin-btn-sm admin-btn-delete" title="Supprimer">
|
<button type="button" class="admin-btn-sm admin-btn-delete" title="Supprimer"
|
||||||
|
onclick="openDeleteLinkDialog(<?= $link['id'] ?>)">
|
||||||
🗑
|
🗑
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -180,6 +181,16 @@ document.getElementById('open-create-dialog').addEventListener('click', () => {
|
|||||||
document.getElementById('create-dialog').showModal();
|
document.getElementById('create-dialog').showModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let _pendingDeleteLinkId = null;
|
||||||
|
function openDeleteLinkDialog(id) {
|
||||||
|
_pendingDeleteLinkId = id;
|
||||||
|
document.getElementById('delete-link-dialog').showModal();
|
||||||
|
}
|
||||||
|
function _executeDeleteLink() {
|
||||||
|
const form = document.getElementById('delete-link-form-' + _pendingDeleteLinkId);
|
||||||
|
if (form) form.submit();
|
||||||
|
}
|
||||||
|
|
||||||
function copyUrl(id) {
|
function copyUrl(id) {
|
||||||
const input = document.getElementById('url-' + id);
|
const input = document.getElementById('url-' + id);
|
||||||
navigator.clipboard.writeText(input.value).then(() => {
|
navigator.clipboard.writeText(input.value).then(() => {
|
||||||
@@ -199,3 +210,19 @@ function openPasswordDialog(id, hasPassword) {
|
|||||||
document.getElementById('password-dialog').showModal();
|
document.getElementById('password-dialog').showModal();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Delete link confirm -->
|
||||||
|
<dialog id="delete-link-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="delete-link-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="delete-link-title">Supprimer ce lien</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Supprimer ce lien ? Les soumissions via ce lien seront bloquées.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn admin-btn--danger" onclick="this.closest('dialog').close(); _executeDeleteLink()">Supprimer</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if (empty($links)): ?>
|
<?php if (empty($links)): ?>
|
||||||
<p class="admin-empty">Aucun lien d'accès créé. Cliquez sur « Créer un lien » pour générer un lien partageable.</p>
|
<p class="admin-empty">Aucun lien d'accès créé. Cliquez sur « Créer un lien » pour générer un lien partageable.</p>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
$statusLabel = $isExpired ? 'Expiré' : ($link['is_active'] ? 'Actif' : 'Désactivé');
|
$statusLabel = $isExpired ? 'Expiré' : ($link['is_active'] ? 'Actif' : 'Désactivé');
|
||||||
$fullUrl = $baseUrl . '/partage/' . htmlspecialchars($link['slug']);
|
$fullUrl = $baseUrl . '/partage/' . htmlspecialchars($link['slug']);
|
||||||
$created = date('d/m/Y H:i', strtotime($link['created_at']));
|
$created = date('d/m/Y H:i', strtotime($link['created_at']));
|
||||||
$expires = $link['expires_at'] ? date('d/m/Y', strtotime($link['expires_at'])) : '—';
|
$expires = $link['expires_at'] ? date('d/m/Y', strtotime($link['expires_at'])) : '-';
|
||||||
$hasLinkPassword = !empty($link['password_hash']);
|
$hasLinkPassword = !empty($link['password_hash']);
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -92,11 +92,12 @@
|
|||||||
🔑
|
🔑
|
||||||
</button>
|
</button>
|
||||||
<form method="post" action="actions/acces-etudiante.php" class="publish-form"
|
<form method="post" action="actions/acces-etudiante.php" class="publish-form"
|
||||||
onsubmit="return confirm('Archiver ce lien ? Il ne sera plus accessible, mais les statistiques seront conservées.')">
|
id="archive-link-form-<?= $link['id'] ?>">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
<input type="hidden" name="action" value="archive">
|
<input type="hidden" name="action" value="archive">
|
||||||
<input type="hidden" name="id" value="<?= $link['id'] ?>">
|
<input type="hidden" name="id" value="<?= $link['id'] ?>">
|
||||||
<button type="submit" class="admin-btn-sm admin-btn-delete" title="Archiver">
|
<button type="button" class="admin-btn-sm admin-btn-delete" title="Archiver"
|
||||||
|
onclick="openArchiveLinkDialog(<?= $link['id'] ?>)">
|
||||||
🗄
|
🗄
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -127,7 +128,7 @@
|
|||||||
<?php foreach ($archivedLinks as $link): ?>
|
<?php foreach ($archivedLinks as $link): ?>
|
||||||
<?php
|
<?php
|
||||||
$created = date('d/m/Y H:i', strtotime($link['created_at']));
|
$created = date('d/m/Y H:i', strtotime($link['created_at']));
|
||||||
$expires = $link['expires_at'] ? date('d/m/Y', strtotime($link['expires_at'])) : '—';
|
$expires = $link['expires_at'] ? date('d/m/Y', strtotime($link['expires_at'])) : '-';
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
@@ -433,4 +434,30 @@ function openRejectDialog(requestId) {
|
|||||||
document.getElementById('reject-request-id').value = requestId;
|
document.getElementById('reject-request-id').value = requestId;
|
||||||
document.getElementById('reject-dialog').showModal();
|
document.getElementById('reject-dialog').showModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _pendingArchiveLinkId = null;
|
||||||
|
function openArchiveLinkDialog(id) {
|
||||||
|
_pendingArchiveLinkId = id;
|
||||||
|
document.getElementById('archive-link-dialog').showModal();
|
||||||
|
}
|
||||||
|
function _executeArchiveLink() {
|
||||||
|
const form = document.getElementById('archive-link-form-' + _pendingArchiveLinkId);
|
||||||
|
if (form) form.submit();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Archive link confirm -->
|
||||||
|
<dialog id="archive-link-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="archive-link-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="archive-link-title">Archiver ce lien</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Archiver ce lien ? Il ne sera plus accessible, mais les statistiques seront conservées.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn admin-btn--warning" onclick="this.closest('dialog').close(); _executeArchiveLink()">Archiver</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|||||||
@@ -74,6 +74,7 @@
|
|||||||
dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée.
|
dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée.
|
||||||
</small>
|
</small>
|
||||||
</p>
|
</p>
|
||||||
|
<?php /* TODO: replace this browser confirm() with a proper <dialog> modal like the other confirmations */ ?>
|
||||||
<form method="post" action="/admin/actions/account.php"
|
<form method="post" action="/admin/actions/account.php"
|
||||||
onsubmit="return confirm('Supprimer le mot de passe PHP ? L\'accès admin ne sera protégé que par nginx Basic Auth.')">
|
onsubmit="return confirm('Supprimer le mot de passe PHP ? L\'accès admin ne sera protégé que par nginx Basic Auth.')">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
|
|||||||
@@ -9,19 +9,40 @@ function updateBulk() {
|
|||||||
document.getElementById('selected-count').textContent = checked.length;
|
document.getElementById('selected-count').textContent = checked.length;
|
||||||
bulk.style.display = checked.length > 0 ? 'flex' : 'none';
|
bulk.style.display = checked.length > 0 ? 'flex' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pending bulk action state
|
||||||
|
let _pendingBulkAction = null;
|
||||||
|
|
||||||
function bulkAction(action) {
|
function bulkAction(action) {
|
||||||
const checked = document.querySelectorAll('input[name="selected_theses[]"]:checked');
|
const checked = document.querySelectorAll('input[name="selected_theses[]"]:checked');
|
||||||
if (!checked.length) { alert('Sélectionnez au moins un TFE.'); return; }
|
if (!checked.length) {
|
||||||
|
document.getElementById('no-selection-dialog').showModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pendingBulkAction = action;
|
||||||
let word, endpoint;
|
let word, endpoint;
|
||||||
if (action === 'publish') { word = 'publier'; endpoint = 'actions/publish.php'; }
|
if (action === 'publish') { word = 'publier'; endpoint = 'actions/publish.php'; }
|
||||||
else if (action === 'unpublish') { word = 'dépublier'; endpoint = 'actions/publish.php'; }
|
else if (action === 'unpublish') { word = 'dépublier'; endpoint = 'actions/publish.php'; }
|
||||||
else if (action === 'delete') { word = 'supprimer'; endpoint = 'actions/delete.php'; }
|
else if (action === 'delete') { word = 'supprimer'; endpoint = 'actions/delete.php'; }
|
||||||
else return;
|
else return;
|
||||||
if (action === 'delete') {
|
if (action === 'delete') {
|
||||||
if (!confirm(`Supprimer définitivement ${checked.length} TFE(s) ? Cette action est irréversible.`)) return;
|
document.getElementById('bulk-delete-count').textContent = checked.length;
|
||||||
|
document.getElementById('bulk-delete-dialog').showModal();
|
||||||
} else {
|
} else {
|
||||||
if (!confirm(`${word.charAt(0).toUpperCase()+word.slice(1)} ${checked.length} TFE(s) ?`)) return;
|
document.getElementById('bulk-confirm-word').textContent = word.charAt(0).toUpperCase() + word.slice(1);
|
||||||
|
document.getElementById('bulk-confirm-count').textContent = checked.length;
|
||||||
|
document.getElementById('bulk-confirm-dialog').showModal();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _executeBulkAction() {
|
||||||
|
const action = _pendingBulkAction;
|
||||||
|
if (!action) return;
|
||||||
|
let endpoint;
|
||||||
|
if (action === 'publish' || action === 'unpublish') { endpoint = 'actions/publish.php'; }
|
||||||
|
else if (action === 'delete') { endpoint = 'actions/delete.php'; }
|
||||||
|
else return;
|
||||||
|
const checked = document.querySelectorAll('input[name="selected_theses[]"]:checked');
|
||||||
document.getElementById('bulk-action-input').value = action;
|
document.getElementById('bulk-action-input').value = action;
|
||||||
document.getElementById('bulk-form').action = endpoint;
|
document.getElementById('bulk-form').action = endpoint;
|
||||||
const container = document.getElementById('bulk-checkboxes');
|
const container = document.getElementById('bulk-checkboxes');
|
||||||
@@ -33,11 +54,21 @@ function bulkAction(action) {
|
|||||||
});
|
});
|
||||||
document.getElementById('bulk-form').submit();
|
document.getElementById('bulk-form').submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pending single-delete state
|
||||||
|
let _pendingDeleteId = null;
|
||||||
|
|
||||||
function deleteThesis(id, title) {
|
function deleteThesis(id, title) {
|
||||||
if (!confirm(`Supprimer « ${title} » ?\nCette action est irréversible.`)) return;
|
_pendingDeleteId = id;
|
||||||
const form = document.getElementById('delete-form-' + id);
|
document.getElementById('delete-thesis-title').textContent = title;
|
||||||
|
document.getElementById('delete-thesis-dialog').showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _executeDeleteThesis() {
|
||||||
|
const form = document.getElementById('delete-form-' + _pendingDeleteId);
|
||||||
if (form) form.submit();
|
if (form) form.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb => cb.addEventListener('change', updateBulk));
|
document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb => cb.addEventListener('change', updateBulk));
|
||||||
});
|
});
|
||||||
@@ -202,8 +233,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
<input type="hidden" name="thesis_id" value="<?= $thesis['id'] ?>">
|
<input type="hidden" name="thesis_id" value="<?= $thesis['id'] ?>">
|
||||||
<?php if ($thesis['is_published']): ?>
|
<?php if ($thesis['is_published']): ?>
|
||||||
<input type="hidden" name="action" value="unpublish">
|
<input type="hidden" name="action" value="unpublish">
|
||||||
<button type="submit" class="admin-btn-sm admin-btn-unpublish"
|
<button type="submit" class="admin-btn-sm admin-btn-unpublish">Dépublier</button>
|
||||||
onclick="return confirm('Retirer de la publication ?')">Dépublier</button>
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<input type="hidden" name="action" value="publish">
|
<input type="hidden" name="action" value="publish">
|
||||||
<button type="submit" class="admin-btn-sm admin-btn-publish">Publier</button>
|
<button type="submit" class="admin-btn-sm admin-btn-publish">Publier</button>
|
||||||
@@ -236,6 +266,73 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
?>
|
?>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════════════
|
||||||
|
CONFIRM DIALOGS (replacing browser alert/confirm)
|
||||||
|
══════════════════════════════════════════════════════════════ -->
|
||||||
|
|
||||||
|
<!-- No-selection alert -->
|
||||||
|
<dialog id="no-selection-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="no-sel-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="no-sel-title">Aucune sélection</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Sélectionnez au moins un TFE avant d'effectuer une action groupée.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn" onclick="this.closest('dialog').close()">OK</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<!-- Bulk publish/unpublish confirm -->
|
||||||
|
<dialog id="bulk-confirm-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="bulk-confirm-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="bulk-confirm-title">Confirmation</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p><span id="bulk-confirm-word"></span> <span id="bulk-confirm-count"></span> TFE(s) ?</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn" onclick="this.closest('dialog').close(); _executeBulkAction()">Confirmer</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<!-- Bulk delete confirm -->
|
||||||
|
<dialog id="bulk-delete-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="bulk-delete-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="bulk-delete-title">Supprimer des TFE</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Supprimer définitivement <strong><span id="bulk-delete-count"></span> TFE(s)</strong> ? Cette action est irréversible.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn admin-btn--danger" onclick="this.closest('dialog').close(); _executeBulkAction()">Supprimer</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<!-- Single thesis delete confirm -->
|
||||||
|
<dialog id="delete-thesis-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="delete-thesis-title-label">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="delete-thesis-title-label">Supprimer ce TFE</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Supprimer « <strong id="delete-thesis-title"></strong> » ? Cette action est irréversible.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn admin-btn--danger" onclick="this.closest('dialog').close(); _executeDeleteThesis()">Supprimer</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<!-- ══════════════════════════════════════════════════════════════
|
<!-- ══════════════════════════════════════════════════════════════
|
||||||
IMPORT DIALOG
|
IMPORT DIALOG
|
||||||
══════════════════════════════════════════════════════════════ -->
|
══════════════════════════════════════════════════════════════ -->
|
||||||
|
|||||||
@@ -20,12 +20,12 @@
|
|||||||
</form>
|
</form>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<p>Site public : <strong>en ligne</strong></p>
|
<p>Site public : <strong>en ligne</strong></p>
|
||||||
<form method="post" action="actions/maintenance.php">
|
<form method="post" action="actions/maintenance.php" id="enable-maintenance-form">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
<input type="hidden" name="action" value="enable_maintenance">
|
<input type="hidden" name="action" value="enable_maintenance">
|
||||||
<input type="hidden" name="redirect" value="/admin/parametres.php">
|
<input type="hidden" name="redirect" value="/admin/parametres.php">
|
||||||
<button type="submit" class="param-btn-warning"
|
<button type="button" class="param-btn-warning"
|
||||||
onclick="return confirm('Mettre le site en maintenance ? Les visiteurs verront une page 503.')">
|
onclick="document.getElementById('enable-maintenance-dialog').showModal()">
|
||||||
Activer la maintenance
|
Activer la maintenance
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -50,12 +50,14 @@
|
|||||||
Supprime définitivement tous les TFE de la base de données, y compris auteurs,
|
Supprime définitivement tous les TFE de la base de données, y compris auteurs,
|
||||||
promoteurs, tags, fichiers associés. Cette action est <strong>irréversible</strong>.
|
promoteurs, tags, fichiers associés. Cette action est <strong>irréversible</strong>.
|
||||||
</p>
|
</p>
|
||||||
<form method="post" action="actions/delete.php"
|
<form method="post" action="actions/delete.php" id="delete-all-tfe-form">
|
||||||
onsubmit="return confirm('⚠ Supprimer définitivement TOUS les TFE ? Cette action est IRRÉVERSIBLE.')">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
<input type="hidden" name="delete_all" value="1">
|
||||||
<input type="hidden" name="delete_all" value="1">
|
<button type="button" class="param-btn-danger"
|
||||||
<button type="submit" class="param-btn-danger">Supprimer tous les TFE (<?= $stats['total'] ?? '?' ?>)</button>
|
onclick="document.getElementById('delete-all-tfe-dialog').showModal()">
|
||||||
</form>
|
Supprimer tous les TFE (<?= $stats['total'] ?? '?' ?>)
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -377,8 +379,9 @@
|
|||||||
Supprime le hash de la base de données. L'accès admin
|
Supprime le hash de la base de données. L'accès admin
|
||||||
dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée.
|
dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée.
|
||||||
</p>
|
</p>
|
||||||
|
<?php /* TODO: replace this browser confirm() with a proper <dialog> modal like the other confirmations */ ?>
|
||||||
<form method="post" action="/admin/actions/account.php"
|
<form method="post" action="/admin/actions/account.php"
|
||||||
onsubmit="return confirm('Supprimer le mot de passe PHP ? L\'accès admin ne sera protégé que par nginx Basic Auth.')">>
|
onsubmit="return confirm('Supprimer le mot de passe PHP ? L\'accès admin ne sera protégé que par nginx Basic Auth.')">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
<input type="hidden" name="action" value="remove_credentials">
|
<input type="hidden" name="action" value="remove_credentials">
|
||||||
<input type="hidden" name="redirect" value="/admin/parametres.php">
|
<input type="hidden" name="redirect" value="/admin/parametres.php">
|
||||||
@@ -584,3 +587,41 @@ document.body.addEventListener('htmx:afterSwap', function(evt) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Enable maintenance confirm -->
|
||||||
|
<dialog id="enable-maintenance-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="enable-maint-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="enable-maint-title">Activer la maintenance</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Mettre le site en maintenance ? Les visiteurs verront une page 503.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn admin-btn--warning"
|
||||||
|
onclick="this.closest('dialog').close(); document.getElementById('enable-maintenance-form').submit()">
|
||||||
|
Activer
|
||||||
|
</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<!-- Delete all TFE confirm -->
|
||||||
|
<dialog id="delete-all-tfe-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="delete-all-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="delete-all-title">Supprimer tous les TFE</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>⚠️ Supprimer définitivement <strong>TOUS les TFE</strong> ? Cette action est <strong>IRRÉVERSIBLE</strong>.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn admin-btn--danger"
|
||||||
|
onclick="this.closest('dialog').close(); document.getElementById('delete-all-tfe-form').submit()">
|
||||||
|
Supprimer tout
|
||||||
|
</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|||||||
@@ -1,3 +1,28 @@
|
|||||||
|
<script>
|
||||||
|
let _pendingTagForm = null;
|
||||||
|
|
||||||
|
function confirmMergeTag(btn) {
|
||||||
|
const form = btn.closest('form');
|
||||||
|
const select = form.querySelector('select[name="target_id"]');
|
||||||
|
const targetName = select.options[select.selectedIndex]?.text ?? '';
|
||||||
|
if (!select.value) { return true; } // let HTML validation handle empty
|
||||||
|
_pendingTagForm = form;
|
||||||
|
document.getElementById('merge-target-name').textContent = targetName;
|
||||||
|
document.getElementById('merge-tag-dialog').showModal();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmDeleteTag(btn, name) {
|
||||||
|
_pendingTagForm = btn.closest('form');
|
||||||
|
document.getElementById('delete-tag-name').textContent = name;
|
||||||
|
document.getElementById('delete-tag-dialog').showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _submitPendingTagForm() {
|
||||||
|
if (_pendingTagForm) _pendingTagForm.submit();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<main id="main-content">
|
<main id="main-content">
|
||||||
<h1>Mots-clés (<?= count($tags) ?>)</h1>
|
<h1>Mots-clés (<?= count($tags) ?>)</h1>
|
||||||
|
|
||||||
@@ -38,8 +63,8 @@
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
<button type="submit" class="admin-btn admin-btn--sm admin-btn--warning"
|
<button type="button" class="admin-btn admin-btn--sm admin-btn--warning"
|
||||||
onclick="return confirm('Fusionner ce tag dans la cible ? Le tag source sera supprimé.')">
|
onclick="return confirmMergeTag(this)">
|
||||||
Fusionner
|
Fusionner
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -49,8 +74,8 @@
|
|||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
<input type="hidden" name="action" value="delete">
|
<input type="hidden" name="action" value="delete">
|
||||||
<input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>">
|
<input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>">
|
||||||
<button type="submit" class="admin-btn admin-btn--sm admin-btn--danger"
|
<button type="button" class="admin-btn admin-btn--sm admin-btn--danger"
|
||||||
onclick="return confirm('Supprimer le tag « <?= htmlspecialchars(addslashes($tag['name'])) ?> » ? Cette action est irréversible.')">
|
onclick="confirmDeleteTag(this, <?= htmlspecialchars(json_encode($tag['name']), ENT_QUOTES) ?>)">
|
||||||
Supprimer
|
Supprimer
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -60,3 +85,35 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- Merge tag confirm -->
|
||||||
|
<dialog id="merge-tag-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="merge-tag-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="merge-tag-title">Fusionner le tag</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Fusionner ce tag dans « <strong id="merge-target-name"></strong> » ? Le tag source sera supprimé.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn admin-btn--warning" onclick="this.closest('dialog').close(); _submitPendingTagForm()">Fusionner</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<!-- Delete tag confirm -->
|
||||||
|
<dialog id="delete-tag-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="delete-tag-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="delete-tag-title">Supprimer le tag</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Supprimer « <strong id="delete-tag-name"></strong> » ? Cette action est irréversible.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="admin-btn admin-btn--danger" onclick="this.closest('dialog').close(); _submitPendingTagForm()">Supprimer</button>
|
||||||
|
<button type="button" class="admin-btn-secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|||||||
Reference in New Issue
Block a user