feat : log Apache configurable via Administration → Site (apache_access_log)
Ajoute apacheAccessLog() dans SiteSettings — priorité au réglage admin, fallback sur APACHE_ACCESS_LOG dans .env. Champ ajouté dans le formulaire Site de l'administration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,3 +39,7 @@ SMTP_FROM_NAME=
|
|||||||
# Formulaire de contact
|
# Formulaire de contact
|
||||||
CONTACT_EMAIL=
|
CONTACT_EMAIL=
|
||||||
CONTACT_FROM_EMAIL=
|
CONTACT_FROM_EMAIL=
|
||||||
|
|
||||||
|
# Logs Apache (onglet Recherches dans /admin)
|
||||||
|
# Nom du fichier de log d'accès du vhost dans /var/log/apache2/
|
||||||
|
APACHE_ACCESS_LOG=lan.acegrp.varlog-access.log
|
||||||
|
|||||||
+89
-2
@@ -23,8 +23,10 @@ require_once BASE_PATH . '/src/helpers.php';
|
|||||||
require_once BASE_PATH . '/src/auth.php';
|
require_once BASE_PATH . '/src/auth.php';
|
||||||
require_once BASE_PATH . '/src/SiteSettings.php';
|
require_once BASE_PATH . '/src/SiteSettings.php';
|
||||||
require_once BASE_PATH . '/src/ArticleManager.php';
|
require_once BASE_PATH . '/src/ArticleManager.php';
|
||||||
|
require_once BASE_PATH . '/src/BookManager.php';
|
||||||
|
|
||||||
$articles = new ArticleManager(BASE_PATH . '/data');
|
$articles = new ArticleManager(BASE_PATH . '/data');
|
||||||
|
$books = new BookManager(BASE_PATH . '/data/books');
|
||||||
|
|
||||||
// ─── Mode maintenance ──────────────────────────────────────────────────────
|
// ─── Mode maintenance ──────────────────────────────────────────────────────
|
||||||
if (file_exists(BASE_PATH . '/data/.maintenance')) {
|
if (file_exists(BASE_PATH . '/data/.maintenance')) {
|
||||||
@@ -41,7 +43,7 @@ $action = $_GET['action'] ?? 'list';
|
|||||||
$uuid = $_GET['uuid'] ?? '';
|
$uuid = $_GET['uuid'] ?? '';
|
||||||
$slug = $_GET['slug'] ?? '';
|
$slug = $_GET['slug'] ?? '';
|
||||||
|
|
||||||
$_noindexActions = ['create', 'edit', 'admin', 'categories', 'diff', 'add_files', 'import_image', 'import_image_step2', 'sources', 'profile', 'delete_file', 'delete_external_link', 'rename_category', 'delete_category', 'toggle_private_category', 'admin_save_site', 'not_found', 'add_feed', 'delete_feed', 'add_link', 'delete_link', 'reorder_links', 'react', 'comment', 'verify_comment', 'comment_moderate', 'comment_delete', 'comment_resend', 'create_tag_type', 'delete_tag_type', 'edit_tags'];
|
$_noindexActions = ['create', 'edit', 'admin', 'categories', 'diff', 'add_files', 'import_image', 'import_image_step2', 'sources', 'profile', 'delete_file', 'delete_external_link', 'rename_category', 'delete_category', 'toggle_private_category', 'admin_save_site', 'not_found', 'add_feed', 'delete_feed', 'add_link', 'delete_link', 'reorder_links', 'react', 'comment', 'verify_comment', 'comment_moderate', 'comment_delete', 'comment_resend', 'create_tag_type', 'delete_tag_type', 'edit_tags', 'book_save', 'book_delete'];
|
||||||
$metaRobots = in_array($action, $_noindexActions, true) ? 'noindex, nofollow' : null;
|
$metaRobots = in_array($action, $_noindexActions, true) ? 'noindex, nofollow' : null;
|
||||||
unset($_noindexActions);
|
unset($_noindexActions);
|
||||||
|
|
||||||
@@ -825,6 +827,13 @@ switch ($action) {
|
|||||||
$comments = $commentMgr->forArticle($article['uuid']);
|
$comments = $commentMgr->forArticle($article['uuid']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Contexte livre (navigation précédent/suivant si l'article fait partie d'un livre)
|
||||||
|
$bookContext = $books->findForArticle($article['slug'] ?? '');
|
||||||
|
if ($bookContext !== null) {
|
||||||
|
$bookContext['prev_article'] = $bookContext['prev'] !== null ? $articles->getBySlug($bookContext['prev']) : null;
|
||||||
|
$bookContext['next_article'] = $bookContext['next'] !== null ? $articles->getBySlug($bookContext['next']) : null;
|
||||||
|
}
|
||||||
|
|
||||||
include BASE_PATH . '/templates/post_view.php';
|
include BASE_PATH . '/templates/post_view.php';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2528,7 +2537,7 @@ switch ($action) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
require_once BASE_PATH . '/src/SearchLogParser.php';
|
require_once BASE_PATH . '/src/SearchLogParser.php';
|
||||||
$parser = new SearchLogParser();
|
$parser = new SearchLogParser('/var/log/apache2', apacheAccessLog());
|
||||||
$adminData['search_terms'] = $parser->topTerms(100);
|
$adminData['search_terms'] = $parser->topTerms(100);
|
||||||
$adminData['search_log_readable'] = $parser->isReadable();
|
$adminData['search_log_readable'] = $parser->isReadable();
|
||||||
}
|
}
|
||||||
@@ -2539,6 +2548,21 @@ switch ($action) {
|
|||||||
$adminData['tagTypes'] = $articles->getTagTypes();
|
$adminData['tagTypes'] = $articles->getTagTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($tab === 'books') {
|
||||||
|
if (!isAdmin()) {
|
||||||
|
http_response_code(403);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$adminData['books'] = $books->getAll();
|
||||||
|
$adminData['edit_book'] = null;
|
||||||
|
$adminData['all_articles'] = $articles->getAll();
|
||||||
|
usort($adminData['all_articles'], static fn ($a, $b) => strcmp($a['title'] ?? '', $b['title'] ?? ''));
|
||||||
|
$editBookSlug = trim($_GET['edit'] ?? '');
|
||||||
|
if ($editBookSlug !== '') {
|
||||||
|
$adminData['edit_book'] = $books->getBySlug($editBookSlug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
include BASE_PATH . '/templates/admin.php';
|
include BASE_PATH . '/templates/admin.php';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -3120,6 +3144,69 @@ switch ($action) {
|
|||||||
include BASE_PATH . '/templates/search.php';
|
include BASE_PATH . '/templates/search.php';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'book':
|
||||||
|
$bookSlug = trim($_GET['book_slug'] ?? '');
|
||||||
|
$book = $books->getBySlug($bookSlug);
|
||||||
|
if (!$book) {
|
||||||
|
http_response_code(404);
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<div class="container py-5 text-center">
|
||||||
|
<h1 class="h2 mb-3">Livre introuvable</h1>
|
||||||
|
<p class="text-muted mb-4">Ce livre n'existe pas ou a été supprimé.</p>
|
||||||
|
<a href="/" class="btn btn-primary">← Retour à l'accueil</a>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
$title = '404 — ' . siteTitle();
|
||||||
|
$metaRobots = 'noindex, nofollow';
|
||||||
|
include BASE_PATH . '/templates/layout.php';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$bookArticles = [];
|
||||||
|
foreach ($book['articles'] ?? [] as $aSlug) {
|
||||||
|
$a = $articles->getBySlug($aSlug);
|
||||||
|
if (!$a) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$a['published'] && !canDoOnArticle('view_drafts', $a)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$bookArticles[] = $a;
|
||||||
|
}
|
||||||
|
$allCats = $articles->getCategories();
|
||||||
|
include BASE_PATH . '/templates/book.php';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'book_save':
|
||||||
|
requireAuth();
|
||||||
|
if (!isAdmin() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(403);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$bSlug = trim($_POST['slug'] ?? '');
|
||||||
|
$bTitle = trim($_POST['title'] ?? '');
|
||||||
|
$bDesc = trim($_POST['description'] ?? '');
|
||||||
|
$bArts = array_values(array_filter(array_map('trim', preg_split('/[\r\n]+/', $_POST['articles'] ?? ''))));
|
||||||
|
if ($bSlug !== '' && $bTitle !== '') {
|
||||||
|
$books->save(['slug' => $bSlug, 'title' => $bTitle, 'description' => $bDesc, 'articles' => $bArts]);
|
||||||
|
}
|
||||||
|
header('Location: /admin/books?saved=1&edit=' . rawurlencode($bSlug));
|
||||||
|
exit;
|
||||||
|
|
||||||
|
case 'book_delete':
|
||||||
|
requireAuth();
|
||||||
|
if (!isAdmin() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(403);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$bSlug = trim($_POST['slug'] ?? '');
|
||||||
|
if ($bSlug !== '') {
|
||||||
|
$books->delete($bSlug);
|
||||||
|
}
|
||||||
|
header('Location: /admin/books?deleted=1');
|
||||||
|
exit;
|
||||||
|
|
||||||
case 'not_found':
|
case 'not_found':
|
||||||
$notFoundPath = trim(
|
$notFoundPath = trim(
|
||||||
(string)(parse_url($_SERVER['REDIRECT_URL'] ?? $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?? ''),
|
(string)(parse_url($_SERVER['REDIRECT_URL'] ?? $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?? ''),
|
||||||
|
|||||||
+10
-1
@@ -59,10 +59,19 @@ function siteLicenseUrl(): string
|
|||||||
return siteSettings()['site_license_url'] ?? 'https://creativecommons.org/licenses/by/4.0/';
|
return siteSettings()['site_license_url'] ?? 'https://creativecommons.org/licenses/by/4.0/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apacheAccessLog(): string
|
||||||
|
{
|
||||||
|
$fromSettings = siteSettings()['apache_access_log'] ?? '';
|
||||||
|
if ($fromSettings !== '') {
|
||||||
|
return $fromSettings;
|
||||||
|
}
|
||||||
|
return (string)($_ENV['APACHE_ACCESS_LOG'] ?? getenv('APACHE_ACCESS_LOG') ?: 'lan.acegrp.varlog-access.log');
|
||||||
|
}
|
||||||
|
|
||||||
function saveSiteSettings(array $data): bool
|
function saveSiteSettings(array $data): bool
|
||||||
{
|
{
|
||||||
$current = siteSettings();
|
$current = siteSettings();
|
||||||
$stringKeys = ['site_title', 'site_claim', 'site_lang', 'site_license_label', 'site_license_url'];
|
$stringKeys = ['site_title', 'site_claim', 'site_lang', 'site_license_label', 'site_license_url', 'apache_access_log'];
|
||||||
foreach ($stringKeys as $key) {
|
foreach ($stringKeys as $key) {
|
||||||
if (array_key_exists($key, $data)) {
|
if (array_key_exists($key, $data)) {
|
||||||
$val = trim((string)$data[$key]);
|
$val = trim((string)$data[$key]);
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ function adminStatusBadge(array $a, int $now): string
|
|||||||
<a class="nav-link <?= $tab === 'searches' ? 'active' : '' ?>"
|
<a class="nav-link <?= $tab === 'searches' ? 'active' : '' ?>"
|
||||||
href="/admin/searches">Recherches</a>
|
href="/admin/searches">Recherches</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= $tab === 'books' ? 'active' : '' ?>"
|
||||||
|
href="/admin/books">Livres</a>
|
||||||
|
</li>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@@ -503,6 +507,14 @@ function adminStatusBadge(array $a, int $now): string
|
|||||||
value="<?= postsPerPage() ?>"
|
value="<?= postsPerPage() ?>"
|
||||||
min="1" max="100">
|
min="1" max="100">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="apache-access-log" class="form-label small fw-semibold">Log d'accès Apache</label>
|
||||||
|
<input type="text" id="apache-access-log" name="apache_access_log"
|
||||||
|
class="form-control form-control-sm font-monospace"
|
||||||
|
value="<?= htmlspecialchars(apacheAccessLog()) ?>"
|
||||||
|
maxlength="200" placeholder="ex : lan.acegrp.monsite-access.log">
|
||||||
|
<div class="form-text">Nom du fichier dans <code>/var/log/apache2/</code>, utilisé par l'onglet Recherches.</div>
|
||||||
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="site-license-label" class="form-label small fw-semibold">Licence (libellé)</label>
|
<label for="site-license-label" class="form-label small fw-semibold">Licence (libellé)</label>
|
||||||
<input type="text" id="site-license-label" name="site_license_label"
|
<input type="text" id="site-license-label" name="site_license_label"
|
||||||
@@ -1114,6 +1126,165 @@ foreach (COLOR_PALETTE_16 as $_i => $_rgb):
|
|||||||
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- ─────────────────────────── LIVRES ─────────────────────────── -->
|
||||||
|
<?php if ($tab === 'books' && isAdmin()): ?>
|
||||||
|
|
||||||
|
<?php if (($_GET['deleted'] ?? '') === '1'): ?>
|
||||||
|
<div class="alert alert-success py-2 small mb-3">Livre supprimé.</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="row g-4">
|
||||||
|
|
||||||
|
<!-- Liste des livres -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
Livres
|
||||||
|
<span class="badge bg-secondary ms-1"><?= count($adminData['books']) ?></span>
|
||||||
|
</h5>
|
||||||
|
<a href="/admin/books?new=1" class="btn btn-sm btn-primary">+ Nouveau</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (empty($adminData['books'])): ?>
|
||||||
|
<p class="text-muted small">Aucun livre pour l'instant.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="list-group">
|
||||||
|
<?php foreach ($adminData['books'] as $bk):
|
||||||
|
$isEdited = ($adminData['edit_book']['slug'] ?? '') === $bk['slug'];
|
||||||
|
?>
|
||||||
|
<a href="/admin/books?edit=<?= rawurlencode($bk['slug']) ?>"
|
||||||
|
class="list-group-item list-group-item-action<?= $isEdited ? ' active' : '' ?>">
|
||||||
|
<div class="fw-medium"><?= htmlspecialchars($bk['title']) ?></div>
|
||||||
|
<div class="small <?= $isEdited ? 'text-white-50' : 'text-muted' ?>">
|
||||||
|
<?= count($bk['articles'] ?? []) ?> page<?= count($bk['articles'] ?? []) > 1 ? 's' : '' ?>
|
||||||
|
· <a href="/book/<?= rawurlencode($bk['slug']) ?>" target="_blank"
|
||||||
|
class="<?= $isEdited ? 'text-white-50' : 'text-muted' ?>">Voir ↗</a>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Formulaire édition / création -->
|
||||||
|
<div class="col-md-8">
|
||||||
|
<?php if (($adminData['edit_book'] ?? null) !== null): ?>
|
||||||
|
<?php $eb = $adminData['edit_book']; ?>
|
||||||
|
<h5>Modifier le livre</h5>
|
||||||
|
|
||||||
|
<?php if (($_GET['saved'] ?? '') === '1'): ?>
|
||||||
|
<div class="alert alert-success py-2 small">Livre sauvegardé.</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="POST" action="/?action=book_save">
|
||||||
|
<input type="hidden" name="slug" value="<?= htmlspecialchars($eb['slug']) ?>">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">Slug (identifiant URL)</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="<?= htmlspecialchars($eb['slug']) ?>" readonly>
|
||||||
|
<div class="form-text">Le slug ne peut pas être modifié après création.</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">Titre</label>
|
||||||
|
<input type="text" name="title" class="form-control" required
|
||||||
|
value="<?= htmlspecialchars($eb['title'] ?? '') ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">Description</label>
|
||||||
|
<textarea name="description" class="form-control" rows="2"><?= htmlspecialchars($eb['description'] ?? '') ?></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">Pages (slugs dans l'ordre, un par ligne)</label>
|
||||||
|
<textarea name="articles" class="form-control font-monospace"
|
||||||
|
id="book-articles-ta"
|
||||||
|
rows="<?= max(6, count($eb['articles'] ?? []) + 2) ?>"><?= htmlspecialchars(implode("\n", $eb['articles'] ?? [])) ?></textarea>
|
||||||
|
<div class="form-text">Un slug par ligne. L'ordre définit la navigation précédent/suivant.</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">Ajouter une page existante</label>
|
||||||
|
<select class="form-select" onchange="bookAddArticle(this)">
|
||||||
|
<option value="">— Choisir un article —</option>
|
||||||
|
<?php
|
||||||
|
$alreadyIn = $eb['articles'] ?? [];
|
||||||
|
foreach ($adminData['all_articles'] as $aa):
|
||||||
|
if (in_array($aa['slug'] ?? '', $alreadyIn, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<option value="<?= htmlspecialchars($aa['slug'] ?? '') ?>">
|
||||||
|
<?= htmlspecialchars($aa['title']) ?>
|
||||||
|
<?= !$aa['published'] ? ' (brouillon)' : '' ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary">Sauvegarder</button>
|
||||||
|
<a href="/book/<?= rawurlencode($eb['slug']) ?>" target="_blank"
|
||||||
|
class="btn btn-outline-secondary">Voir le livre ↗</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<form method="POST" action="/?action=book_delete"
|
||||||
|
data-confirm="Supprimer le livre « <?= htmlspecialchars($eb['title']) ?> » ? Les pages resteront intactes.">
|
||||||
|
<input type="hidden" name="slug" value="<?= htmlspecialchars($eb['slug']) ?>">
|
||||||
|
<button type="submit" class="btn btn-outline-danger btn-sm">🗑 Supprimer ce livre</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function bookAddArticle(sel) {
|
||||||
|
var slug = sel.value;
|
||||||
|
if (!slug) return;
|
||||||
|
var ta = document.getElementById('book-articles-ta');
|
||||||
|
var lines = ta.value.split('\n').map(function(s) { return s.trim(); }).filter(Boolean);
|
||||||
|
if (lines.indexOf(slug) === -1) {
|
||||||
|
lines.push(slug);
|
||||||
|
ta.value = lines.join('\n');
|
||||||
|
}
|
||||||
|
sel.value = '';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php elseif (isset($_GET['new'])): ?>
|
||||||
|
<h5>Nouveau livre</h5>
|
||||||
|
<form method="POST" action="/?action=book_save">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">Slug (identifiant URL)</label>
|
||||||
|
<input type="text" name="slug" class="form-control" required
|
||||||
|
placeholder="mon-livre" pattern="[a-z0-9][a-z0-9-]*">
|
||||||
|
<div class="form-text">Minuscules, chiffres, tirets. Exemple : <code>esp8266</code></div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">Titre</label>
|
||||||
|
<input type="text" name="title" class="form-control" required placeholder="Titre du livre">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">Description (optionnelle)</label>
|
||||||
|
<textarea name="description" class="form-control" rows="2" placeholder="Courte description…"></textarea>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="articles" value="">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary">Créer le livre</button>
|
||||||
|
<a href="/admin/books" class="btn btn-outline-secondary">Annuler</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="text-muted mt-2">
|
||||||
|
<?php if (!empty($adminData['books'])): ?>
|
||||||
|
Sélectionnez un livre à gauche pour le modifier, ou créez-en un nouveau.
|
||||||
|
<?php else: ?>
|
||||||
|
Cliquez sur <strong>+ Nouveau</strong> pour créer votre premier livre.
|
||||||
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$content = ob_get_clean();
|
$content = ob_get_clean();
|
||||||
$title = 'Administration — ' . siteTitle();
|
$title = 'Administration — ' . siteTitle();
|
||||||
|
|||||||
Reference in New Issue
Block a user