factorisation: site_lang, posts_per_page, site_license, contact dynamique
This commit is contained in:
@@ -1278,14 +1278,34 @@ footer.mt-5 { margin-top: 0 !important; }
|
|||||||
|
|
||||||
.author-profile-link:hover { color: var(--vl-accent); }
|
.author-profile-link:hover { color: var(--vl-accent); }
|
||||||
|
|
||||||
.author-profile-bio {
|
.author-bio-wrap {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin: 0;
|
}
|
||||||
|
|
||||||
|
.author-profile-bio {
|
||||||
|
margin: 0 0 .25rem;
|
||||||
color: var(--vl-muted);
|
color: var(--vl-muted);
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
font-size: .9375rem;
|
font-size: .9375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.author-profile-bio.bio-clamped {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bio-toggle {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--vl-accent);
|
||||||
|
font-size: .875rem;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
/* ─── Page "Mes liens" ───────────────────── */
|
/* ─── Page "Mes liens" ───────────────────── */
|
||||||
|
|
||||||
.liens-page {
|
.liens-page {
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
|||||||
<title><?= htmlspecialchars(siteTitle()) ?></title>
|
<title><?= htmlspecialchars(siteTitle()) ?></title>
|
||||||
<link><?= htmlspecialchars($base) ?></link>
|
<link><?= htmlspecialchars($base) ?></link>
|
||||||
<description><?= htmlspecialchars(siteClaim()) ?></description>
|
<description><?= htmlspecialchars(siteClaim()) ?></description>
|
||||||
<language>fr</language>
|
<language><?= htmlspecialchars(siteLang()) ?></language>
|
||||||
<lastBuildDate><?= htmlspecialchars($lastBuild) ?></lastBuildDate>
|
<lastBuildDate><?= htmlspecialchars($lastBuild) ?></lastBuildDate>
|
||||||
|
|
||||||
<atom:link href="<?= htmlspecialchars($feedUrl) ?>" rel="self" type="application/rss+xml"/>
|
<atom:link href="<?= htmlspecialchars($feedUrl) ?>" rel="self" type="application/rss+xml"/>
|
||||||
|
|||||||
+6
-1
@@ -1772,6 +1772,11 @@ switch ($action) {
|
|||||||
saveSiteSettings([
|
saveSiteSettings([
|
||||||
'site_title' => $_POST['site_title'] ?? '',
|
'site_title' => $_POST['site_title'] ?? '',
|
||||||
'site_claim' => $_POST['site_claim'] ?? '',
|
'site_claim' => $_POST['site_claim'] ?? '',
|
||||||
|
'site_author' => $_POST['site_author'] ?? '',
|
||||||
|
'site_lang' => $_POST['site_lang'] ?? '',
|
||||||
|
'posts_per_page' => $_POST['posts_per_page'] ?? '',
|
||||||
|
'site_license_label' => $_POST['site_license_label'] ?? '',
|
||||||
|
'site_license_url' => $_POST['site_license_url'] ?? '',
|
||||||
]);
|
]);
|
||||||
header('Location: /admin/site?saved=1');
|
header('Location: /admin/site?saved=1');
|
||||||
exit;
|
exit;
|
||||||
@@ -2152,7 +2157,7 @@ switch ($action) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
$perPage = 12;
|
$perPage = postsPerPage();
|
||||||
$cursor = trim($_GET['cursor'] ?? '');
|
$cursor = trim($_GET['cursor'] ?? '');
|
||||||
|
|
||||||
// Trouve la position du curseur dans la liste triée
|
// Trouve la position du curseur dans la liste triée
|
||||||
|
|||||||
+33
-2
@@ -39,11 +39,36 @@ function siteAuthor(): string
|
|||||||
return siteSettings()['site_author'] ?? '';
|
return siteSettings()['site_author'] ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function siteLang(): string
|
||||||
|
{
|
||||||
|
return siteSettings()['site_lang'] ?? 'fr-FR';
|
||||||
|
}
|
||||||
|
|
||||||
|
function siteLangOgLocale(): string
|
||||||
|
{
|
||||||
|
return str_replace('-', '_', siteLang());
|
||||||
|
}
|
||||||
|
|
||||||
|
function postsPerPage(): int
|
||||||
|
{
|
||||||
|
return max(1, (int)(siteSettings()['posts_per_page'] ?? 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
function siteLicenseLabel(): string
|
||||||
|
{
|
||||||
|
return siteSettings()['site_license_label'] ?? 'CC BY 4.0';
|
||||||
|
}
|
||||||
|
|
||||||
|
function siteLicenseUrl(): string
|
||||||
|
{
|
||||||
|
return siteSettings()['site_license_url'] ?? 'https://creativecommons.org/licenses/by/4.0/';
|
||||||
|
}
|
||||||
|
|
||||||
function saveSiteSettings(array $data): void
|
function saveSiteSettings(array $data): void
|
||||||
{
|
{
|
||||||
$current = siteSettings();
|
$current = siteSettings();
|
||||||
$allowed = ['site_title', 'site_claim', 'site_author'];
|
$stringKeys = ['site_title', 'site_claim', 'site_author', 'site_lang', 'site_license_label', 'site_license_url'];
|
||||||
foreach ($allowed 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]);
|
||||||
if ($val !== '') {
|
if ($val !== '') {
|
||||||
@@ -51,6 +76,12 @@ function saveSiteSettings(array $data): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (array_key_exists('posts_per_page', $data)) {
|
||||||
|
$val = (int)$data['posts_per_page'];
|
||||||
|
if ($val > 0) {
|
||||||
|
$current['posts_per_page'] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
file_put_contents(
|
file_put_contents(
|
||||||
siteSettingsPath(),
|
siteSettingsPath(),
|
||||||
json_encode($current, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
|
json_encode($current, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
|
||||||
|
|||||||
@@ -360,6 +360,36 @@ function adminStatusBadge(array $a, int $now): string
|
|||||||
maxlength="100" placeholder="ex : Cédrix">
|
maxlength="100" placeholder="ex : Cédrix">
|
||||||
<div class="form-text">Utilisé dans les métadonnées des articles (meta author, JSON-LD).</div>
|
<div class="form-text">Utilisé dans les métadonnées des articles (meta author, JSON-LD).</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="site-lang" class="form-label small fw-semibold">Langue du site</label>
|
||||||
|
<input type="text" id="site-lang" name="site_lang"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
value="<?= htmlspecialchars(siteLang()) ?>"
|
||||||
|
maxlength="20" placeholder="ex : fr-FR">
|
||||||
|
<div class="form-text">Format BCP 47 (ex : <code>fr</code>, <code>fr-FR</code>). Utilisé dans <code><html lang></code>, og:locale, RSS et JSON-LD.</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="posts-per-page" class="form-label small fw-semibold">Articles par page</label>
|
||||||
|
<input type="number" id="posts-per-page" name="posts_per_page"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
value="<?= postsPerPage() ?>"
|
||||||
|
min="1" max="100">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<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"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
value="<?= htmlspecialchars(siteLicenseLabel()) ?>"
|
||||||
|
maxlength="80" placeholder="ex : CC BY 4.0">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="site-license-url" class="form-label small fw-semibold">Licence (URL)</label>
|
||||||
|
<input type="url" id="site-license-url" name="site_license_url"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
value="<?= htmlspecialchars(siteLicenseUrl()) ?>"
|
||||||
|
maxlength="200">
|
||||||
|
<div class="form-text">Affiché dans le footer.</div>
|
||||||
|
</div>
|
||||||
<button type="submit" class="btn btn-primary btn-sm">Enregistrer</button>
|
<button type="submit" class="btn btn-primary btn-sm">Enregistrer</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,9 +20,28 @@ $_initials = mb_strtoupper(mb_substr($_apName, 0, 1, 'UTF-8'), 'UTF-8');
|
|||||||
<?= htmlspecialchars(parse_url($_apUrl, PHP_URL_HOST) ?: $_apUrl) ?> ↗
|
<?= htmlspecialchars(parse_url($_apUrl, PHP_URL_HOST) ?: $_apUrl) ?> ↗
|
||||||
</a>
|
</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<a href="/liens/<?= rawurlencode($_apSlug) ?>" class="author-profile-link">Mes liens →</a>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($_apBio !== ''): ?>
|
<?php if ($_apBio !== ''): ?>
|
||||||
<p class="author-profile-bio"><?= nl2br(htmlspecialchars($_apBio)) ?></p>
|
<div class="author-bio-wrap">
|
||||||
|
<p class="author-profile-bio bio-clamped" id="author-bio"><?= nl2br(htmlspecialchars($_apBio)) ?></p>
|
||||||
|
<button class="bio-toggle" id="bio-toggle" hidden>plus</button>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var bio = document.getElementById('author-bio');
|
||||||
|
var btn = document.getElementById('bio-toggle');
|
||||||
|
requestAnimationFrame(function() {
|
||||||
|
if (bio.scrollHeight > bio.clientHeight + 2) { btn.hidden = false; }
|
||||||
|
});
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
var exp = btn.getAttribute('aria-expanded') === 'true';
|
||||||
|
bio.classList.toggle('bio-clamped', exp);
|
||||||
|
btn.textContent = exp ? 'plus' : 'moins';
|
||||||
|
btn.setAttribute('aria-expanded', exp ? 'false' : 'true');
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -46,13 +46,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
if (!$error && $contactEmail !== '') {
|
if (!$error && $contactEmail !== '') {
|
||||||
$subjectClean = mb_encode_mimeheader(
|
$subjectClean = mb_encode_mimeheader(
|
||||||
'[varlog contact] ' . mb_strimwidth($subject, 0, 100, '…'),
|
'[' . siteTitle() . ' contact] ' . mb_strimwidth($subject, 0, 100, '…'),
|
||||||
'UTF-8',
|
'UTF-8',
|
||||||
'B'
|
'B'
|
||||||
);
|
);
|
||||||
$nameClean = mb_encode_mimeheader($name, 'UTF-8', 'B');
|
$nameClean = mb_encode_mimeheader($name, 'UTF-8', 'B');
|
||||||
|
|
||||||
$headers = 'From: =?UTF-8?B?' . base64_encode('varlog contact') . "?= <noreply@varlog>\r\n";
|
$fromEmail = $_ENV['CONTACT_FROM_EMAIL'] ?? ('noreply@' . (parse_url(APP_URL, PHP_URL_HOST) ?? 'localhost'));
|
||||||
|
$headers = 'From: =?UTF-8?B?' . base64_encode(siteTitle() . ' contact') . "?= <{$fromEmail}>\r\n";
|
||||||
$headers .= "Reply-To: {$nameClean} <{$from}>\r\n";
|
$headers .= "Reply-To: {$nameClean} <{$from}>\r\n";
|
||||||
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
|
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
|
||||||
$headers .= "Content-Transfer-Encoding: 8bit\r\n";
|
$headers .= "Content-Transfer-Encoding: 8bit\r\n";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="<?= htmlspecialchars(siteLang()) ?>">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title><?= htmlspecialchars(($seoTitle ?? '') ?: ($title ?? siteTitle())) ?></title>
|
<title><?= htmlspecialchars(($seoTitle ?? '') ?: ($title ?? siteTitle())) ?></title>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<meta property="og:title" content="<?= htmlspecialchars(($seoTitle ?? '') ?: ($title ?? siteTitle())) ?>">
|
<meta property="og:title" content="<?= htmlspecialchars(($seoTitle ?? '') ?: ($title ?? siteTitle())) ?>">
|
||||||
<meta property="og:description" content="<?= htmlspecialchars(($seoDescription ?? '') ?: siteClaim()) ?>">
|
<meta property="og:description" content="<?= htmlspecialchars(($seoDescription ?? '') ?: siteClaim()) ?>">
|
||||||
<meta property="og:type" content="<?= htmlspecialchars($ogType ?? 'website') ?>">
|
<meta property="og:type" content="<?= htmlspecialchars($ogType ?? 'website') ?>">
|
||||||
<meta property="og:locale" content="fr_FR">
|
<meta property="og:locale" content="<?= htmlspecialchars(siteLangOgLocale()) ?>">
|
||||||
<meta property="og:url" content="<?= htmlspecialchars($ogUrl ?? APP_URL) ?>">
|
<meta property="og:url" content="<?= htmlspecialchars($ogUrl ?? APP_URL) ?>">
|
||||||
<meta property="og:site_name" content="<?= htmlspecialchars(siteTitle()) ?>">
|
<meta property="og:site_name" content="<?= htmlspecialchars(siteTitle()) ?>">
|
||||||
<?php if (!empty($ogImage ?? '')): ?>
|
<?php if (!empty($ogImage ?? '')): ?>
|
||||||
@@ -56,16 +56,16 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<?php
|
<?php
|
||||||
$_layoutAction = $_GET['action'] ?? 'list';
|
$_layoutAction = $_GET['action'] ?? 'list';
|
||||||
$_layoutPrivateCats = isset($articles) ? $articles->getPrivateCategories() : [];
|
$_layoutPrivateCats = isset($articles) ? $articles->getPrivateCategories() : [];
|
||||||
$_layoutCats = isset($articles) ? array_filter(
|
$_layoutCats = isset($articles) ? array_filter(
|
||||||
$articles->getCategories(),
|
$articles->getCategories(),
|
||||||
function ($cat) use ($_layoutPrivateCats) {
|
function ($cat) use ($_layoutPrivateCats) {
|
||||||
return isLoggedIn() || !in_array($cat, $_layoutPrivateCats, true);
|
return isLoggedIn() || !in_array($cat, $_layoutPrivateCats, true);
|
||||||
},
|
},
|
||||||
ARRAY_FILTER_USE_KEY
|
ARRAY_FILTER_USE_KEY
|
||||||
) : [];
|
) : [];
|
||||||
$_layoutCurrentCat = trim($_GET['cat'] ?? '');
|
$_layoutCurrentCat = trim($_GET['cat'] ?? '');
|
||||||
?>
|
?>
|
||||||
<a class="navbar-brand d-flex flex-column lh-1" href="/">
|
<a class="navbar-brand d-flex flex-column lh-1" href="/">
|
||||||
<span><?= htmlspecialchars(siteTitle()) ?></span>
|
<span><?= htmlspecialchars(siteTitle()) ?></span>
|
||||||
<small class="navbar-tagline"><?= htmlspecialchars(siteClaim()) ?></small>
|
<small class="navbar-tagline"><?= htmlspecialchars(siteClaim()) ?></small>
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<div class="footer-about">
|
<div class="footer-about">
|
||||||
<strong><?= htmlspecialchars(siteTitle()) ?></strong>
|
<strong><?= htmlspecialchars(siteTitle()) ?></strong>
|
||||||
<p><?= htmlspecialchars(siteClaim()) ?></p>
|
<p><?= htmlspecialchars(siteClaim()) ?></p>
|
||||||
<small>© <?= date('Y') ?> — <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noopener">CC BY 4.0</a></small>
|
<small>© <?= date('Y') ?> — <a href="<?= htmlspecialchars(siteLicenseUrl()) ?>" target="_blank" rel="noopener"><?= htmlspecialchars(siteLicenseLabel()) ?></a></small>
|
||||||
</div>
|
</div>
|
||||||
<nav class="footer-nav" aria-label="Liens du site">
|
<nav class="footer-nav" aria-label="Liens du site">
|
||||||
<a href="/about">À propos</a>
|
<a href="/about">À propos</a>
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ $catVal = trim($category ?? '');
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-muted fw-normal ps-0 pe-2 text-nowrap" style="width:1%">Auteur</th>
|
<th class="text-muted fw-normal ps-0 pe-2 text-nowrap" style="width:1%">Auteur</th>
|
||||||
<td>Cédrix</td>
|
<td><?= htmlspecialchars(siteAuthor() ?: '—') ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-muted fw-normal ps-0 pe-2 text-nowrap">Publication</th>
|
<th class="text-muted fw-normal ps-0 pe-2 text-nowrap">Publication</th>
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ if (empty($cursor) && $filterCat === '') {
|
|||||||
'name' => siteTitle(),
|
'name' => siteTitle(),
|
||||||
'url' => rtrim(APP_URL, '/') . '/',
|
'url' => rtrim(APP_URL, '/') . '/',
|
||||||
'description' => siteClaim(),
|
'description' => siteClaim(),
|
||||||
'inLanguage' => 'fr-FR',
|
'inLanguage' => siteLang(),
|
||||||
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ $jsonLdData = [
|
|||||||
'name' => siteAuthor() !== '' ? siteAuthor() : siteTitle(),
|
'name' => siteAuthor() !== '' ? siteAuthor() : siteTitle(),
|
||||||
'url' => rtrim(APP_URL, '/'),
|
'url' => rtrim(APP_URL, '/'),
|
||||||
],
|
],
|
||||||
'inLanguage' => 'fr-FR',
|
'inLanguage' => siteLang(),
|
||||||
];
|
];
|
||||||
if (!empty($ogImage)) {
|
if (!empty($ogImage)) {
|
||||||
$jsonLdData['image'] = $ogImage;
|
$jsonLdData['image'] = $ogImage;
|
||||||
|
|||||||
Reference in New Issue
Block a user