Files
cedricAbonnel 298f18dabe feat & fix : intégration IA éditeur + onglet admin IA + corrections CSP (v1.6.24-25)
- #96 : boutons IA sidebar éditeur (analyse critique / réécriture) via Anthropic API
- #97 : onglet admin /admin/ia — provider anthropic/claude_code, modèle, procédure CLI
- #95 : extraction scripts inline vers fichiers JS (comments.js, post_confirm.js, admin.js)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:18:38 +02:00

175 lines
8.3 KiB
PHP

<!DOCTYPE html>
<html lang="<?= htmlspecialchars(siteLang()) ?>">
<head>
<meta charset="UTF-8">
<title><?= htmlspecialchars(($seoTitle ?? '') ?: ($title ?? siteTitle())) ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- SEO -->
<meta name="description" content="<?= htmlspecialchars(($seoDescription ?? '') ?: siteClaim()) ?>">
<meta name="robots" content="<?= htmlspecialchars($metaRobots ?? 'index, follow') ?>">
<link rel="canonical" href="<?= htmlspecialchars($canonical ?? $ogUrl ?? rtrim(APP_URL, '/') . '/') ?>">
<!-- Open Graph -->
<meta property="og:title" content="<?= htmlspecialchars(($seoTitle ?? '') ?: ($title ?? siteTitle())) ?>">
<meta property="og:description" content="<?= htmlspecialchars(($seoDescription ?? '') ?: siteClaim()) ?>">
<meta property="og:type" content="<?= htmlspecialchars($ogType ?? 'website') ?>">
<meta property="og:locale" content="<?= htmlspecialchars(siteLangOgLocale()) ?>">
<meta property="og:url" content="<?= htmlspecialchars($ogUrl ?? APP_URL) ?>">
<meta property="og:site_name" content="<?= htmlspecialchars(siteTitle()) ?>">
<?php if (!empty($ogImage ?? '')): ?>
<meta property="og:image" content="<?= htmlspecialchars($ogImage) ?>">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<?php endif; ?>
<?php if (!empty($metaAuthor ?? '')): ?>
<meta name="author" content="<?= htmlspecialchars($metaAuthor) ?>">
<?php endif; ?>
<?php if (!empty($articlePublishedAt ?? '')): ?>
<meta property="article:published_time" content="<?= htmlspecialchars(date('c', strtotime((string)$articlePublishedAt))) ?>">
<?php endif; ?>
<?php if (!empty($metaAuthorUrl ?? '')): ?>
<meta property="article:author" content="<?= htmlspecialchars($metaAuthorUrl) ?>">
<?php elseif (!empty($metaAuthor ?? '')): ?>
<meta property="article:author" content="<?= htmlspecialchars($metaAuthor) ?>">
<?php endif; ?>
<?php if (!empty($jsonLd ?? '')): ?>
<script type="application/ld+json"><?= $jsonLd ?></script>
<?php endif; ?>
<!-- RSS autodiscovery -->
<link rel="alternate" type="application/rss+xml" title="<?= htmlspecialchars(siteTitle()) ?>" href="/feed">
<!-- Favicon -->
<link rel="icon" href="/assets/favicon.svg" type="image/svg+xml">
<!-- CSS -->
<link href="/assets/css/bootstrap.min.css" rel="stylesheet">
<?php
$_pub = BASE_PATH . '/public/assets/';
if (!function_exists('_av')) {
function _av(string $base, string $rel): string {
$f = $base . $rel;
return '/assets/' . $rel . (is_file($f) ? '?v=' . substr(md5_file($f), 0, 8) : '');
}
}
?>
<link rel="stylesheet" href="<?= _av($_pub, 'css/style.css') ?>">
</head>
<body<?php if (!empty($bodyClass ?? '')): ?> class="<?= htmlspecialchars($bodyClass) ?>"<?php endif; ?>>
<script src="<?= _av($_pub, 'js/density-fouc.js') ?>"></script>
<header>
<nav class="navbar navbar-expand-lg navbar-dark mb-0" role="navigation" aria-label="Navigation principale">
<div class="container-fluid">
<?php
$_layoutAction = $_GET['action'] ?? 'list';
$_layoutPrivateCats = isset($articles) ? $articles->getPrivateCategories() : [];
$_layoutCats = isset($articles) ? array_filter(
$articles->getCategories(),
function ($cat) use ($_layoutPrivateCats) {
return isLoggedIn() || !in_array($cat, $_layoutPrivateCats, true);
},
ARRAY_FILTER_USE_KEY
) : [];
$_layoutCurrentCat = trim($_GET['cat'] ?? '');
?>
<a class="navbar-brand d-flex flex-column lh-1" href="/">
<span><?= htmlspecialchars(siteTitle()) ?></span>
<small class="navbar-tagline"><?= htmlspecialchars(siteClaim()) ?></small>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Basculer la navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav me-auto"></ul>
<form class="search-form d-flex" action="/search" method="GET" role="search">
<input class="form-control form-control-sm search-input"
type="search" name="q"
value="<?= htmlspecialchars($_GET['q'] ?? '') ?>"
placeholder="Rechercher…"
aria-label="Rechercher">
</form>
<ul class="navbar-nav">
<?php if (function_exists('isLoggedIn') && isLoggedIn()): ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
<?= htmlspecialchars(function_exists('currentUserName') ? currentUserName() : (currentUserEmail() ?? '')) ?>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/profile">Mon identité</a></li>
<li><a class="dropdown-item" href="/admin"><?= (function_exists('isAdmin') && isAdmin()) ? 'Administration' : 'Mes articles' ?></a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-muted" href="/logout.php">Déconnexion</a></li>
</ul>
</li>
<?php else: ?>
<li class="nav-item"><a class="nav-link" href="/login">Connexion</a></li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
</header>
<?php if (function_exists('isAdmin') && isAdmin() && isset($_updateChecker)):
$_adminNotices = $_updateChecker->adminNotices();
foreach ($_adminNotices as $_notice):
$_isWarning = $_notice['type'] === 'warning'; ?>
<div class="alert alert-<?= $_isWarning ? 'warning' : 'info' ?> alert-dismissible rounded-0 border-0 border-bottom py-2 mb-0 small" role="alert">
<div class="container-fluid d-flex align-items-center gap-2 flex-wrap">
<span><?= $_notice['message'] ?></span>
<?php if ($_isWarning): ?>
<form method="POST" action="/?action=run_content_migrations" class="d-inline">
<button type="submit" class="btn btn-warning btn-sm py-0">Mettre à jour</button>
</form>
<?php else: ?>
<a href="/admin?tab=dashboard" class="btn btn-outline-primary btn-sm py-0">Voir dans l'admin</a>
<?php endif; ?>
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Fermer"></button>
</div>
</div>
<?php endforeach; endif; ?>
<main class="<?= htmlspecialchars($mainClass ?? 'container') ?>" role="main">
<?= $content ?>
</main>
<footer class="py-5 mt-5" role="contentinfo">
<div class="container">
<div class="footer-inner">
<div class="footer-about">
<strong><?= htmlspecialchars(siteTitle()) ?></strong>
<p><?= htmlspecialchars(siteClaim()) ?></p>
<small>&copy; <?= date('Y') ?> &mdash; <a href="<?= htmlspecialchars(siteLicenseUrl()) ?>" target="_blank" rel="noopener"><?= htmlspecialchars(siteLicenseLabel()) ?></a></small>
</div>
<nav class="footer-nav" aria-label="Liens du site">
<a href="/about">À propos</a>
<a href="/contact">Contact</a>
<a href="/legal">Mentions légales</a>
<a href="/licenses">Licences</a>
</nav>
</div>
</div>
</footer>
<!-- JS -->
<script src="/assets/js/bootstrap.bundle.min.js"></script>
<script src="<?= _av($_pub, 'js/app.js') ?>"></script>
<?php if (isset($reactionStats)): ?>
<script src="<?= _av($_pub, 'js/reactions.js') ?>"></script>
<?php endif; ?>
<?php if (!empty($shareBar ?? false)): ?>
<script src="<?= _av($_pub, 'js/share.js') ?>" defer></script>
<?php endif; ?>
<?php if (!empty($aiEditor ?? false)): ?>
<script src="<?= _av($_pub, 'js/ai-editor.js') ?>"></script>
<?php endif; ?>
</body>
</html>