-
Fichier de configuration
+
+
Fichier de configuration
- config/admin_credentials.php
+ config/admin_credentials.php
-
-
- Aucun mot de passe PHP configuré. Le formulaire ci-dessous créera
- config/admin_credentials.php avec un hash bcrypt.
-
-
-
-
= $hasPassword ? 'Changer le mot de passe' : 'Définir le mot de passe' ?>
+
+
+ Aucun mot de passe PHP configuré. Le formulaire ci-dessous créera
+ config/admin_credentials.php avec un hash bcrypt.
+
+
-
+
-
-
Zone de danger
-
-
- Supprimer la configuration du mot de passe PHP
-
- Supprime config/admin_credentials.php. L'accès admin
- dépendra uniquement de l'authentification nginx Basic Auth si elle est configurée.
-
+
+
diff --git a/public/assets/css/admin.css b/public/assets/css/admin.css
index df19404..296e4d6 100644
--- a/public/assets/css/admin.css
+++ b/public/assets/css/admin.css
@@ -650,37 +650,56 @@
}
/* ── Thesis info sections (thanks page) ─────────────────────────────────── */
-.admin-body main > section {
+.admin-body main > section:not([aria-labelledby^="settings-"]) {
border: 1px solid var(--border-primary);
border-radius: 6px;
padding: var(--space-m);
margin-bottom: var(--space-m);
}
-.admin-body main > section h2 {
+.admin-body main > section:not([aria-labelledby^="settings-"]) h2 {
margin: 0 0 var(--space-s);
font-size: var(--step-1);
border-bottom: 1px solid var(--border-primary);
padding-bottom: var(--space-2xs);
}
-.admin-body main > section dl {
+.admin-body main > section:not([aria-labelledby^="settings-"]) dl {
display: grid;
grid-template-columns: 180px 1fr;
gap: var(--space-3xs) var(--space-s);
}
-.admin-body main > section dt {
+.admin-body main > section:not([aria-labelledby^="settings-"]) dt {
font-weight: 600;
font-size: var(--step--1);
color: var(--text-secondary);
}
-.admin-body main > section dd {
+.admin-body main > section:not([aria-labelledby^="settings-"]) dd {
margin: 0;
font-size: var(--step--1);
}
+/* ── Paramètres page top-level sections (flat, no border card) ──────────── */
+.admin-body main > section[aria-labelledby^="settings-"] {
+ border: none;
+ border-radius: 0;
+ padding: 0;
+ margin-bottom: var(--space-xl);
+}
+
+.admin-body main > section[aria-labelledby^="settings-"] > h2 {
+ font-size: var(--step-1);
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ color: var(--text-secondary);
+ margin: 0 0 var(--space-m);
+ padding-bottom: var(--space-2xs);
+ border-bottom: 1px solid var(--border-primary);
+}
+
.admin-action-bar {
margin-top: var(--space-m);
display: flex;
@@ -809,26 +828,69 @@
width: 100%;
}
-/* ── Import page ────────────────────────────────────────────────────────── */
-.admin-import-area {
- display: flex;
- flex-direction: column;
- gap: var(--space-m);
+/* ════ Import status card (dialog) ═══════════════════════════════════════ */
+.admin-import-status-card {
+ margin: 0;
+ padding: var(--space-m) var(--space-l);
}
-/* Error list inside role="alert" (import page) */
-.admin-error-list {
+.admin-import-status-card__success {
+ background: var(--success-muted-bg);
+ border: 1px solid var(--success);
+ border-left: 3px solid var(--success);
+ border-radius: 4px;
+ padding: var(--space-xs) var(--space-s);
+ font-size: var(--step--1);
+ margin: 0;
+}
+
+.admin-import-status-card__errors {
+ background: var(--accent-muted);
+ border: 1px solid var(--error);
+ border-left: 3px solid var(--error);
+ border-radius: 4px;
+ padding: var(--space-xs) var(--space-s);
+ font-size: var(--step--1);
+ margin-bottom: var(--space-s);
+}
+
+.admin-import-status-card__errors .admin-error-list {
margin: var(--space-2xs) 0 0;
padding-left: var(--space-s);
}
-/* Hint text under the file input (import page) */
+/* Import log details/summary */
+.admin-import-log-details {
+ margin: 0;
+ padding: 0 var(--space-l) var(--space-m);
+}
+
+.admin-import-log-details summary {
+ cursor: pointer;
+ font-size: var(--step--1);
+ font-weight: 600;
+ color: var(--text-secondary);
+ padding: var(--space-2xs) 0;
+ border-top: 1px solid var(--border-primary);
+}
+
+.admin-import-log-details summary:hover {
+ color: var(--text-primary);
+}
+
+/* Hint text under the file input */
.admin-file-hint {
display: block;
margin-top: var(--space-2xs);
}
-/* Import results panel */
+/* Error list inside role="alert" */
+.admin-error-list {
+ margin: var(--space-2xs) 0 0;
+ padding-left: var(--space-s);
+}
+
+/* Import results panel (legacy, kept for backward compat) */
.admin-import-results {
margin-top: var(--space-l);
}
@@ -1080,7 +1142,237 @@
.admin-import-log__item--skip::before { content: '⚠'; color: var(--warning); }
.admin-import-log__item--error::before { content: '✗'; color: var(--error); }
-/* ── Settings page sections ─────────────────────────────────────────────── */
+/* ── Paramètres page (flat, semantic) ──────────────────────────────────── */
+.param-maintenance-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-s);
+ padding: var(--space-xs) var(--space-m);
+ margin-bottom: var(--space-m);
+ font-size: var(--step--1);
+ border: 1px solid var(--border-primary);
+ border-radius: 4px;
+ background: var(--bg-secondary);
+}
+
+.param-maintenance-row:has( .param-btn-warning ) {
+ background: var(--warning-muted-bg);
+ border-color: var(--warning);
+}
+
+.param-maintenance-row p {
+ margin: 0;
+}
+
+.param-maintenance-row form {
+ flex-shrink: 0;
+}
+
+.param-form {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-m);
+}
+
+.param-form fieldset {
+ border: 1px solid var(--border-primary);
+ border-radius: 4px;
+ padding: var(--space-m);
+ background: var(--bg-secondary);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-xs);
+}
+
+.param-form legend {
+ font-weight: 600;
+ font-size: var(--step--1);
+ color: var(--text-secondary);
+ padding: 0 var(--space-xs);
+}
+
+.param-checkbox {
+ display: flex;
+ align-items: flex-start;
+ gap: var(--space-xs);
+ font-size: var(--step--1);
+ cursor: pointer;
+}
+
+.param-checkbox--disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.param-checkbox input[type="checkbox"] {
+ accent-color: var(--accent-primary);
+ width: 16px;
+ height: 16px;
+ cursor: pointer;
+ flex-shrink: 0;
+ margin-top: 2px;
+}
+
+.param-checkbox--disabled input[type="checkbox"] {
+ cursor: not-allowed;
+}
+
+.param-checkbox small {
+ color: var(--text-secondary);
+ font-size: var(--step--2);
+}
+
+.param-note {
+ font-size: var(--step--1);
+ color: var(--text-secondary);
+ margin: 0;
+}
+
+.param-account-status {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-xs);
+ font-size: var(--step--1);
+ margin-bottom: var(--space-m);
+}
+
+.param-account-status > div {
+ display: flex;
+ align-items: center;
+ gap: var(--space-xs);
+}
+
+.param-account-status dt {
+ color: var(--text-secondary);
+ min-width: 200px;
+}
+
+.param-account-status dd {
+ margin: 0;
+ display: flex;
+ align-items: center;
+ gap: var(--space-xs);
+}
+
+.param-account-status code {
+ font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
+ font-size: var(--step--2);
+ border: 1px solid var(--border-primary);
+ border-radius: 3px;
+ padding: var(--space-3xs);
+ color: var(--text-secondary);
+ background: var(--bg-secondary);
+}
+
+.param-form > div {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: var(--space-3xs);
+ border-top: 1px solid var(--border-primary);
+ padding: var(--space-xs) 0;
+}
+
+.param-form > div:first-of-type {
+ border-top: none;
+ padding-top: 0;
+}
+
+.param-form input[type="password"] {
+ width: 100%;
+ max-width: 380px;
+ background: transparent;
+ border: none;
+ border-bottom: 1px solid var(--border-primary);
+ font-size: var(--step--1);
+ font-family: inherit;
+ padding: var(--space-3xs) 0;
+ border-radius: 0;
+ transition: border-color 0.15s;
+}
+
+.param-form input[type="password"]:focus {
+ outline: none;
+ border-bottom-color: var(--accent-primary);
+}
+
+.param-form > button {
+ align-self: flex-start;
+ padding: var(--space-2xs) var(--space-l);
+ background: var(--accent-primary);
+ color: var(--accent-foreground);
+ border: none;
+ border-radius: 3px;
+ font-size: var(--step--1);
+ font-family: inherit;
+ cursor: pointer;
+ letter-spacing: 0.04em;
+ transition: background 0.15s;
+}
+
+.param-form > button:hover {
+ background: var(--accent-secondary);
+}
+
+.param-btn-warning {
+ padding: var(--space-3xs) var(--space-s);
+ background: var(--accent-yellow);
+ color: var(--text-primary);
+ border: none;
+ border-radius: 3px;
+ font-size: var(--step--1);
+ font-family: inherit;
+ cursor: pointer;
+ transition: filter 0.15s;
+}
+
+.param-btn-warning:hover {
+ filter: brightness(0.9);
+}
+
+.param-btn-danger {
+ padding: var(--space-3xs) var(--space-s);
+ background: var(--accent-red);
+ color: var(--accent-foreground);
+ border: none;
+ border-radius: 3px;
+ font-size: var(--step--1);
+ font-family: inherit;
+ cursor: pointer;
+ transition: filter 0.15s;
+}
+
+.param-btn-danger:hover {
+ filter: brightness(0.9);
+}
+
+.param-danger-zone {
+ border: 1px solid var(--danger-border-muted);
+ border-radius: 4px;
+ padding: var(--space-m);
+ margin-bottom: var(--space-m);
+ background: var(--bg-secondary);
+}
+
+.param-danger-zone legend {
+ width: auto;
+ font-weight: 600;
+ color: var(--error);
+ font-size: var(--step-0);
+ padding: 0 var(--space-xs);
+}
+
+.param-danger-zone p {
+ font-size: var(--step--1);
+ color: var(--text-secondary);
+ margin-bottom: var(--space-xs);
+}
+
+.param-danger-zone form {
+ margin-top: var(--space-xs);
+}
+
+/* ── Settings page sections — legacy aliases (kept for any remaining use) ─ */
.admin-settings-section {
border: 1px solid var(--border-primary);
border-radius: 6px;
@@ -1125,6 +1417,130 @@
flex-shrink: 0;
}
+.admin-account-status {
+ border: 1px solid var(--border-primary);
+ border-radius: 4px;
+ padding: var(--space-s) var(--space-m);
+ margin-bottom: var(--space-l);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-xs);
+}
+
+.admin-account-status__row {
+ display: flex;
+ align-items: center;
+ gap: var(--space-xs);
+ font-size: var(--step--1);
+}
+
+.admin-account-status__label {
+ color: var(--text-secondary);
+ min-width: 220px;
+}
+
+.admin-account-status__code {
+ font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
+ font-size: var(--step--2);
+ border: 1px solid var(--border-primary);
+ border-radius: 3px;
+ padding: var(--space-3xs) var(--space-3xs);
+ color: var(--text-secondary);
+ background: var(--bg-secondary);
+}
+
+.admin-account-status__note {
+ font-size: var(--step--1);
+ color: var(--text-secondary);
+ margin: 0;
+}
+
+.admin-danger-zone {
+ border: 1px solid var(--danger-border-muted);
+ border-radius: 4px;
+ padding: var(--space-m) var(--space-m);
+ display: flex;
+ align-items: center;
+ gap: var(--space-m);
+ flex-wrap: wrap;
+}
+
+.admin-danger-zone__description {
+ flex: 1;
+ font-size: var(--step--1);
+}
+
+.admin-settings-toggles {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-xs);
+ margin-bottom: var(--space-m);
+}
+
+.admin-toggle-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-m);
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-primary);
+ border-radius: 4px;
+ padding: var(--space-xs) var(--space-m);
+ cursor: pointer;
+}
+
+.admin-toggle-row--disabled {
+ opacity: 0.6;
+}
+
+.admin-toggle-label {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.admin-toggle-label strong {
+ font-size: var(--step-0);
+}
+
+.admin-toggle-label small {
+ color: var(--text-secondary);
+ font-size: var(--step--2);
+}
+
+.admin-toggle {
+ appearance: none;
+ -webkit-appearance: none;
+ width: 40px;
+ height: 22px;
+ background: var(--border-primary);
+ border-radius: 11px;
+ position: relative;
+ cursor: pointer;
+ flex-shrink: 0;
+ transition: background 0.2s;
+}
+
+.admin-toggle::after {
+ content: '';
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ width: 16px;
+ height: 16px;
+ border-radius: 50%;
+ background: #fff;
+ transition: transform 0.2s;
+}
+
+.admin-toggle:checked {
+ background: var(--accent-primary);
+}
+
+.admin-toggle:checked::after {
+ transform: translateX(18px);
+}
+
/* ── Cancel link ────────────────────────────────────────────────────────── */
.admin-cancel-link {
font-size: var(--step--1);
diff --git a/src/Database.php b/src/Database.php
index f9da3e3..a9eebd8 100644
--- a/src/Database.php
+++ b/src/Database.php
@@ -1659,6 +1659,17 @@ class Database {
return (string)$row['identifier'];
}
+ /**
+ * Delete every thesis in the database.
+ */
+ public function deleteAllTheses(): int {
+ $ids = $this->pdo->query("SELECT id FROM theses")->fetchAll(\PDO::FETCH_COLUMN);
+ if (empty($ids)) return 0;
+ $count = count($ids);
+ $this->bulkDeleteTheses($ids);
+ return $count;
+ }
+
/**
* Insert a thesis file record
*/