mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
feat: student mode support for thanks page (admin-auth only)
- add hidden student_mode field in add.php form - pass mode=student through redirect to thanks.php in formulaire.php - thanks.php renders clean student thank-you page (no header, centered button) - add CSS for .thanks-student-page, .btn-new-form, .thanks-success, .thanks-error - admin auth always required; student mode is purely UI variant on the physical machine
This commit is contained in:
22
TODO.md
22
TODO.md
@@ -1,16 +1,10 @@
|
||||
# TODO
|
||||
|
||||
## Paramètres page cleanup
|
||||
- [x] Remove card syntax (`.admin-settings-section` border/radius containers)
|
||||
- [x] Replace pill toggles with native semantic checkboxes inside `<fieldset>`
|
||||
- [x] Move "delete all TFE" danger zone into Maintenance section
|
||||
- [x] Use `<fieldset>` for danger zones (semantic, with `<legend>` instead of `<div>`)
|
||||
- [x] Update CSS: new `.param-*` classes for flat semantic layout
|
||||
- [x] Exclude parametres sections from generic `.admin-body main > section` card styling via `aria-labelledby` prefix
|
||||
|
||||
## Add TFE: admin/student mode toggle
|
||||
- [x] Add `?mode=student` query param to same add.php page
|
||||
- [x] Student mode: no admin header/nav, just the form with a back-link
|
||||
- [x] Admin mode: full admin header/nav, with "Mode étudiant ↗" toggle link (opens in new tab)
|
||||
- [x] Auth guard stays the same — still requires admin login
|
||||
- [x] Add `.student-body`, `.thesis-add-header`, `.mode-toggle`, `.form-footer` CSS
|
||||
- [x] Make thanks.php respect student mode (no header, centered "add new form" button)
|
||||
- [x] Add hidden input `student_mode` in add.php form when in student mode
|
||||
- [x] Append `mode=student` to thanks redirect in formulaire.php
|
||||
- [x] Update thanks.php to detect student mode, hide header, show centered button
|
||||
- [x] Cleanup public/admin/add.php — standardise fieldsets and add licence explanation sections from docs PDF
|
||||
- [x] Organise all fields into `<fieldset>/<legend>` blocks: Informations du TFE, Composition du jury, Cadre académique, Fichiers, Métadonnées complémentaires
|
||||
- [x] Remove double-wrapping of jury-fieldset (it has its own `<fieldset>`)
|
||||
- [x] Add "Degrés d'ouverture et licences" section (Libre / Interne / Interdit + Généralités) wrapped in `if ($studentMode)` — hidden in admin
|
||||
|
||||
BIN
docs/Proposition procédure licences_V2.pdf
Normal file
BIN
docs/Proposition procédure licences_V2.pdf
Normal file
Binary file not shown.
@@ -9,6 +9,8 @@ ini_set('error_log', 'error.log');
|
||||
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
$studentMode = isset($_POST['student_mode']) && $_POST['student_mode'] === '1';
|
||||
|
||||
// Verify CSRF token
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
@@ -26,7 +28,11 @@ try {
|
||||
|
||||
unset($_SESSION['csrf_token']);
|
||||
|
||||
header('Location: ../thanks.php?id=' . urlencode($thesisId));
|
||||
$redirect = '../thanks.php?id=' . urlencode($thesisId);
|
||||
if ($studentMode) {
|
||||
$redirect .= '&mode=student';
|
||||
}
|
||||
header('Location: ' . $redirect);
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
@@ -35,11 +41,16 @@ try {
|
||||
App::flash('error', $e->getMessage());
|
||||
$_SESSION['form_data'] = $_POST;
|
||||
|
||||
$redirect = '../add.php';
|
||||
if ($studentMode) {
|
||||
$redirect .= '?mode=student';
|
||||
}
|
||||
|
||||
$autofocusField = ThesisCreateController::autofocusFieldForError($e->getMessage());
|
||||
if ($autofocusField !== null) {
|
||||
App::flashAutofocus($autofocusField);
|
||||
}
|
||||
|
||||
header('Location: ../add.php');
|
||||
header('Location: ' . $redirect);
|
||||
exit();
|
||||
}
|
||||
|
||||
@@ -23,11 +23,9 @@ try {
|
||||
$formData = $_SESSION['form_data'] ?? [];
|
||||
unset($_SESSION['form_data']);
|
||||
$autofocusField = App::consumeAutofocus();
|
||||
// Flash error consumed by the flash-messages partial below.
|
||||
|
||||
/**
|
||||
* Merge autofocus into the $attrs array for a given field.
|
||||
* Only adds the attribute when $autofocusField matches $fieldName.
|
||||
*/
|
||||
function withAutofocus(string $fieldName, array $attrs = []): array {
|
||||
global $autofocusField;
|
||||
@@ -74,13 +72,19 @@ if ($studentMode) {
|
||||
|
||||
<form action="actions/formulaire.php" method="post" enctype="multipart/form-data" class="admin-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION["csrf_token"]) ?>">
|
||||
<?php if ($studentMode): ?>
|
||||
<input type="hidden" name="student_mode" value="1">
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ═══════════════════ Informations du TFE ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Informations du TFE</legend>
|
||||
|
||||
<?php $name = 'titre'; $label = 'Titre :'; $value = old('titre'); $required = true; $attrs = withAutofocus('titre'); include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
<?php $name = 'subtitle'; $label = 'Sous-titre (si applicable) :'; $value = old('subtitle'); $required = false; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
<?php $name = 'auteurice'; $label = 'Auteur·ice(s) :'; $value = old('auteurice'); $required = true; $attrs = withAutofocus('auteurice', ['autocomplete' => 'name']); include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
<?php $name = 'mail'; $label = 'Contact(s) (optionnel) [mail/site/insta/etc.] :'; $value = old('mail'); $attrs = ['autocomplete' => 'email']; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
|
||||
<!-- Contact visibility -->
|
||||
<div class="admin-form-group">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="checkbox" name="contact_public" value="1"
|
||||
@@ -90,8 +94,21 @@ if ($studentMode) {
|
||||
<small>Si cette case est cochée, votre contact apparaîtra sur la page publique de votre TFE.</small>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="synopsis">Synopsis :</label>
|
||||
<textarea id="synopsis" name="synopsis"
|
||||
rows="7" required
|
||||
<?= $autofocusField === 'synopsis' ? 'autofocus' : '' ?>><?= old('synopsis') ?></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Composition du jury ═══════════════════ -->
|
||||
<?php require APP_ROOT . '/templates/partials/form/jury-fieldset.php'; ?>
|
||||
|
||||
<!-- ═══════════════════ Cadre académique ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Cadre académique</legend>
|
||||
|
||||
<?php
|
||||
$name = 'année'; $label = 'Année :'; $value = old('année'); $required = true;
|
||||
$type = 'number';
|
||||
@@ -101,24 +118,27 @@ if ($studentMode) {
|
||||
?>
|
||||
|
||||
<?php $name = 'orientation'; $label = 'Orientation :'; $options = $orientations; $selected = $formData['orientation'] ?? ''; $required = true; $placeholder = ''; $attrs = withAutofocus('orientation'); include APP_ROOT . '/templates/partials/form/select-field.php'; ?>
|
||||
|
||||
<?php $name = 'ap'; $label = 'Atelier pluridisciplinaire :'; $options = $apPrograms; $selected = $formData['ap'] ?? ''; $required = true; $placeholder = ''; $attrs = withAutofocus('ap'); include APP_ROOT . '/templates/partials/form/select-field.php'; ?>
|
||||
|
||||
<?php $name = 'finality'; $label = 'Finalité du master :'; $options = $finalityTypes; $selected = $formData['finality'] ?? ''; $required = true; $placeholder = ''; $attrs = withAutofocus('finality'); include APP_ROOT . '/templates/partials/form/select-field.php'; ?>
|
||||
|
||||
<?php $name = 'languages'; $label = 'Langue(s) :'; $options = $languages; $checked = $formData['languages'] ?? []; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
||||
|
||||
<?php $name = 'formats'; $label = 'Format(s) :'; $options = $formatTypes; $checked = $formData['formats'] ?? []; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
||||
|
||||
<?php $name = 'tag'; $label = 'Mots-clés :'; $value = old('tag'); $placeholder = 'sociologie, anthropologie, ...'; $hint = 'Séparez par des virgules. Max 10 mots-clés.'; $attrs = withAutofocus('tag'); include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
</fieldset>
|
||||
|
||||
<!-- Synopsis -->
|
||||
<div>
|
||||
<label for="synopsis">Synopsis :</label>
|
||||
<textarea id="synopsis" name="synopsis"
|
||||
rows="7" required
|
||||
<?= $autofocusField === 'synopsis' ? 'autofocus' : '' ?>><?= old('synopsis') ?></textarea>
|
||||
</div>
|
||||
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Fichiers</legend>
|
||||
|
||||
<?php $name = 'couverture'; $label = 'Image de couverture :'; $accept = 'image/jpeg,image/png'; $hint = 'JPG, PNG. Taille max : 10 MB.'; include APP_ROOT . '/templates/partials/form/file-field.php'; ?>
|
||||
<?php $name = 'banner'; $label = 'Image bannière (accueil) :'; $accept = 'image/jpeg,image/png,image/webp'; $hint = 'JPG, PNG ou WEBP. Format paysage recommandé (4:1). Max 5 MB.'; include APP_ROOT . '/templates/partials/form/file-field.php'; ?>
|
||||
<?php $name = 'files'; $label = 'Fichiers du TFE :'; $accept = '.pdf,.jpg,.jpeg,.png,.mp4,.zip,.vtt'; $hint = 'PDF, JPG, PNG, MP4, ZIP. Max 50 MB par fichier. Pour les vidéos, un fichier .vtt de sous-titres peut être joint (il sera associé automatiquement à la vidéo correspondante).'; $multiple = true; include APP_ROOT . '/templates/partials/form/file-field.php'; ?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Métadonnées complémentaires</legend>
|
||||
|
||||
<?php $name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes; $selected = $formData['license_id'] ?? ''; $placeholder = '— Inconnue —'; include APP_ROOT . '/templates/partials/form/select-field.php'; ?>
|
||||
|
||||
@@ -126,18 +146,10 @@ if ($studentMode) {
|
||||
|
||||
<?php $name = 'lien'; $label = 'Lien (site / ressource) :'; $value = old('lien'); $type = 'url'; $placeholder = 'https://...'; $attrs = withAutofocus('lien'); include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
|
||||
<?php $name = 'couverture'; $label = 'Image de couverture :'; $accept = 'image/jpeg,image/png'; $hint = 'JPG, PNG. Taille max : 10 MB.'; include APP_ROOT . '/templates/partials/form/file-field.php'; ?>
|
||||
|
||||
<?php $name = 'banner'; $label = 'Image bannière (accueil) :'; $accept = 'image/jpeg,image/png,image/webp'; $hint = 'JPG, PNG ou WEBP. Format paysage recommandé (4:1). Max 5 MB.'; include APP_ROOT . '/templates/partials/form/file-field.php'; ?>
|
||||
|
||||
<?php $name = 'files'; $label = 'Fichiers du TFE :'; $accept = '.pdf,.jpg,.jpeg,.png,.mp4,.zip,.vtt'; $hint = 'PDF, JPG, PNG, MP4, ZIP. Max 50 MB par fichier. Pour les vidéos, un fichier .vtt de sous-titres peut être joint (il sera associé automatiquement à la vidéo correspondante).'; $multiple = true; include APP_ROOT . '/templates/partials/form/file-field.php'; ?>
|
||||
|
||||
<?php
|
||||
// Visibility select — only show options enabled in settings
|
||||
$accessOptions = array_map(function($at) {
|
||||
return ['id' => $at['id'], 'name' => $at['name']];
|
||||
}, $enabledAccessTypes);
|
||||
// Default: Interne (id=2)
|
||||
$defaultAccessType = 2;
|
||||
$selectedAccessType = isset($formData['access_type_id'])
|
||||
? (int)$formData['access_type_id']
|
||||
@@ -151,6 +163,56 @@ if ($studentMode) {
|
||||
$attrs = [];
|
||||
include APP_ROOT . '/templates/partials/form/select-field.php';
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
<?php if ($studentMode): ?>
|
||||
<!-- ═══════════════════ Degrés d'ouverture ═══════════════════ -->
|
||||
<fieldset class="licence-explanation">
|
||||
<legend>Degrés d'ouverture et licences</legend>
|
||||
|
||||
<div class="licence-info">
|
||||
<h3>Je veux que mon TFE soit disponible sous les conditions suivantes :</h3>
|
||||
|
||||
<div class="licence-degree">
|
||||
<h4>🔓 Libre</h4>
|
||||
<p>Mon TFE est en libre accès à tout le monde sur la plateforme des TFE ainsi que dans la bibliothèque de l'erg. Je suis conscient·e des responsabilités et obligations légales qui viennent avec une diffusion externe – et acquiesce avoir lu la documentation prévue à cet effet par l'erg, ainsi qu'avoir discuté des enjeux d'une publication avec l'équipe pédagogique. J'accepte de partager mes droits de diffusion avec l'erg, ce uniquement dans le cadre d'une diffusion sur la plateforme xamxam.</p>
|
||||
<ul>
|
||||
<li><label><input type="checkbox" name="cc4r" value="1"> J'accepte les conditions collectives de réutilisation (CC4r) <em class="hint">(pas obligatoire)</em></label></li>
|
||||
<li><label><input type="checkbox" name="specific_license" value="1"> Je souhaite appliquer une licence spécifique à mon travail <em class="hint">(pas obligatoire)</em></label></li>
|
||||
</ul>
|
||||
<p class="licence-note"><em>Au moins une des deux cases doit être cochée pour le degré Libre.</em></p>
|
||||
</div>
|
||||
|
||||
<div class="licence-degree">
|
||||
<h4>🔒 Interne</h4>
|
||||
<p>Mon TFE et ma note d'intention ne sont accessibles que sur place en physique ainsi que sur la plateforme xamxam par la communauté erg. Une note descriptive est disponible sur le site à toustes. J'autorise une (ré-)utilisation et diffusion dans un contexte académique et didactique au sein de l'erg.</p>
|
||||
<p class="licence-note"><em>La diffusion limitée est protégée par le cadre académique/didactique, le travail pourrait donc être diffusé en interne et être cité par d'autres étudiant·es sans implications légales pour l'auteur·ice ni pour l'école.</em></p>
|
||||
<ul>
|
||||
<li><label><input type="checkbox" name="cc4r" value="1"> J'accepte les conditions collectives de réutilisation (CC4r) <em class="hint">(pas obligatoire)</em></label></li>
|
||||
<li><label><input type="checkbox" name="specific_license" value="1"> Je souhaite appliquer une licence spécifique à mon travail <em class="hint">(pas obligatoire)</em></label></li>
|
||||
</ul>
|
||||
<p class="licence-note"><em>Au moins une des deux cases doit être cochée.</em></p>
|
||||
</div>
|
||||
|
||||
<div class="licence-degree">
|
||||
<h4>🚫 Interdit</h4>
|
||||
<p>Mon TFE n'est pas disponible en physique ni sur le site. Une note descriptive est disponible sur le site.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="licence-generalites">
|
||||
<h3>Généralités</h3>
|
||||
<ul>
|
||||
<li>L'auteur·ice peut décider entre trois degrés de partage de son travail : <strong>libre</strong>, <strong>interne</strong>, <strong>interdit</strong>.</li>
|
||||
<li>L'auteur·ice peut, à tout moment, décider de <strong>restreindre</strong> le degré d'accès à son travail. Il ne peut néanmoins pas l'ouvrir davantage.</li>
|
||||
<li>Le choix effectué dans ce formulaire sera d'application <strong>une semaine après la soutenance orale</strong> de l'auteur·ice. Celui-ci peut donc décider de restreindre ce choix avant sa publication (mais pas l'ouvrir).</li>
|
||||
<li>L'erg se réserve le droit de restreindre le degré d'ouverture du TFE – ce en accord avec le règlement.</li>
|
||||
<li>Dans tous les cas, l'auteur·ice garde les droits d'auteurs, de diffusion, d'utilisation, etc. de son travail – sauf si la licence choisie restreindrait ses droits.</li>
|
||||
<li>La diffusion « xamxam » est indépendante de la diffusion à la BAIU.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-footer">
|
||||
<button type="submit" name="go">Soumettre</button>
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
require_once __DIR__ . "/../../config/bootstrap.php";
|
||||
require_once __DIR__ . '/../../src/AdminAuth.php';
|
||||
|
||||
// PHP-level auth guard (defence-in-depth behind nginx Basic Auth)
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
// Configure error reporting
|
||||
ini_set('display_errors', 0);
|
||||
ini_set('log_errors', 1);
|
||||
ini_set('error_log', 'error.log');
|
||||
|
||||
$studentMode = isset($_GET['mode']) && $_GET['mode'] === 'student';
|
||||
if (!$studentMode) {
|
||||
AdminAuth::requireLogin();
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../src/Database.php';
|
||||
|
||||
// Security: Validate thesis ID parameter
|
||||
@@ -62,10 +64,48 @@ function formatFileSize($bytes) {
|
||||
// Set page title for header
|
||||
$pageTitle = "Récapitulatif TFE";
|
||||
?>
|
||||
<?php $isAdmin = true; $bodyClass = 'admin-body'; require_once APP_ROOT . '/templates/head.php'; ?>
|
||||
<?php include APP_ROOT . '/templates/header.php'; ?>
|
||||
<?php
|
||||
$isAdmin = true;
|
||||
if ($studentMode) {
|
||||
$bodyClass = 'admin-body student-body';
|
||||
require_once APP_ROOT . '/templates/head.php';
|
||||
} else {
|
||||
$bodyClass = 'admin-body';
|
||||
require_once APP_ROOT . '/templates/head.php';
|
||||
include APP_ROOT . '/templates/header.php';
|
||||
}
|
||||
?>
|
||||
|
||||
<main id="main-content">
|
||||
<?php if ($studentMode): ?>
|
||||
<!-- ═══════════════════ STUDENT MODE: Thank you page ═══════════════════ -->
|
||||
<div class="thanks-student-page">
|
||||
<?php if ($error): ?>
|
||||
<div class="thanks-error">
|
||||
<h1>⚠ Oups…</h1>
|
||||
<p><?= htmlspecialchars($error) ?></p>
|
||||
<a href="/admin/add.php?mode=student" class="btn-new-form">← Retour au formulaire</a>
|
||||
</div>
|
||||
|
||||
<?php elseif ($thesis): ?>
|
||||
<div class="thanks-success">
|
||||
<h1>Merci 🎉</h1>
|
||||
<p class="thanks-message">
|
||||
Ton TFE <strong><?= htmlspecialchars($thesis['title']) ?></strong> a bien été soumis.
|
||||
</p>
|
||||
<a href="/admin/add.php?mode=student" class="btn-new-form">+ Ajouter un nouveau TFE</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="thanks-error">
|
||||
<h1>Erreur</h1>
|
||||
<p>Aucune donnée à afficher.</p>
|
||||
<a href="/admin/add.php?mode=student" class="btn-new-form">← Retour au formulaire</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
<!-- ═══════════════════ ADMIN MODE: Recap page ═══════════════════ -->
|
||||
<h1>Récapitulatif TFE</h1>
|
||||
|
||||
<?php if ($error): ?>
|
||||
@@ -148,6 +188,8 @@ $pageTitle = "Récapitulatif TFE";
|
||||
<p class="admin-muted">Aucune donnée à afficher.</p>
|
||||
<p><a href="/admin/add.php" class="admin-btn-secondary">Retour au formulaire</a></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php endif; ?>
|
||||
</main>
|
||||
|
||||
<?php require_once APP_ROOT . '/templates/admin/footer.php'; ?>
|
||||
|
||||
@@ -1748,3 +1748,66 @@
|
||||
.form-footer button:hover {
|
||||
background: var(--accent-secondary);
|
||||
}
|
||||
|
||||
/* ── Student thanks page ────────────────────────────────────────────────── */
|
||||
.thanks-student-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60vh;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.thanks-success,
|
||||
.thanks-error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.thanks-success h1,
|
||||
.thanks-error h1 {
|
||||
font-size: var(--step-3);
|
||||
margin: 0;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.thanks-message {
|
||||
font-size: var(--step-0);
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.thanks-error p {
|
||||
font-size: var(--step-0);
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-new-form {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2xs);
|
||||
padding: var(--space-m) var(--space-2xl);
|
||||
background: var(--accent-primary);
|
||||
color: var(--accent-foreground);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: var(--step-0);
|
||||
font-weight: 600;
|
||||
font-family: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0.04em;
|
||||
transition: background 0.15s, transform 0.15s;
|
||||
margin-top: var(--space-s);
|
||||
}
|
||||
|
||||
.btn-new-form:hover {
|
||||
background: var(--accent-secondary);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
1
storage/maintenance.flag
Normal file
1
storage/maintenance.flag
Normal file
@@ -0,0 +1 @@
|
||||
2026-04-15T11:53:16+00:00
|
||||
Reference in New Issue
Block a user