feat: profile_url auteur → article:author URL + JSON-LD author.url

This commit is contained in:
Cedric Abonnel
2026-05-12 23:45:41 +02:00
parent a031ea960e
commit e1c179b536
6 changed files with 53 additions and 16 deletions
+1
View File
@@ -0,0 +1 @@
ALTER TABLE user_profiles ADD COLUMN IF NOT EXISTS profile_url TEXT NOT NULL DEFAULT '';
+9 -4
View File
@@ -1775,6 +1775,10 @@ switch ($action) {
$profileSuccess = false; $profileSuccess = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$newName = trim($_POST['display_name'] ?? ''); $newName = trim($_POST['display_name'] ?? '');
$newUrl = trim($_POST['profile_url'] ?? '');
if ($newUrl !== '' && !filter_var($newUrl, FILTER_VALIDATE_URL)) {
$newUrl = '';
}
if ($newName === '') { if ($newName === '') {
$profileError = 'Le nom ne peut pas être vide.'; $profileError = 'Le nom ne peut pas être vide.';
} else { } else {
@@ -1782,11 +1786,11 @@ switch ($action) {
if ($pdo) { if ($pdo) {
try { try {
$st = $pdo->prepare( $st = $pdo->prepare(
'INSERT INTO user_profiles (email, display_name, updated_at) 'INSERT INTO user_profiles (email, display_name, profile_url, updated_at)
VALUES (:e, :n, now()) VALUES (:e, :n, :u, now())
ON CONFLICT (email) DO UPDATE SET display_name = :n, updated_at = now()' ON CONFLICT (email) DO UPDATE SET display_name = :n, profile_url = :u, updated_at = now()'
); );
$st->execute([':e' => currentUserEmail(), ':n' => $newName]); $st->execute([':e' => currentUserEmail(), ':n' => $newName, ':u' => $newUrl]);
$_SESSION['user_display_name'] = $newName; $_SESSION['user_display_name'] = $newName;
$profileSuccess = true; $profileSuccess = true;
} catch (\Throwable $ex) { } catch (\Throwable $ex) {
@@ -1796,6 +1800,7 @@ switch ($action) {
} }
} }
$profileCurrentName = currentUserName(); $profileCurrentName = currentUserName();
$profileCurrentUrl = authorProfileUrl(currentUserEmail() ?? '');
include BASE_PATH . '/templates/profile.php'; include BASE_PATH . '/templates/profile.php';
break; break;
+21 -6
View File
@@ -35,11 +35,21 @@ function currentUserName(): string
} }
function authorDisplayName(string $email): string function authorDisplayName(string $email): string
{
return authorProfile($email)['name'];
}
function authorProfileUrl(string $email): string
{
return authorProfile($email)['url'];
}
function authorProfile(string $email): array
{ {
static $cache = []; static $cache = [];
$key = strtolower(trim($email)); $key = strtolower(trim($email));
if ($key === '') { if ($key === '') {
return ''; return ['name' => '', 'url' => ''];
} }
if (array_key_exists($key, $cache)) { if (array_key_exists($key, $cache)) {
return $cache[$key]; return $cache[$key];
@@ -47,15 +57,20 @@ function authorDisplayName(string $email): string
$pdo = dbPdo(); $pdo = dbPdo();
if ($pdo) { if ($pdo) {
try { try {
$st = $pdo->prepare('SELECT display_name FROM user_profiles WHERE email = :e'); $st = $pdo->prepare('SELECT display_name, profile_url FROM user_profiles WHERE email = :e');
$st->execute([':e' => $key]); $st->execute([':e' => $key]);
$name = $st->fetchColumn(); $row = $st->fetch(PDO::FETCH_ASSOC);
$cache[$key] = ($name !== false && $name !== '') ? $name : explode('@', $key)[0]; if ($row) {
return $cache[$key]; $cache[$key] = [
'name' => ($row['display_name'] !== '') ? $row['display_name'] : explode('@', $key)[0],
'url' => $row['profile_url'] ?? '',
];
return $cache[$key];
}
} catch (\Throwable) { } catch (\Throwable) {
} }
} }
$cache[$key] = explode('@', $key)[0]; $cache[$key] = ['name' => explode('@', $key)[0], 'url' => ''];
return $cache[$key]; return $cache[$key];
} }
+3 -1
View File
@@ -28,7 +28,9 @@
<?php if (!empty($articlePublishedAt ?? '')): ?> <?php if (!empty($articlePublishedAt ?? '')): ?>
<meta property="article:published_time" content="<?= htmlspecialchars(date('c', strtotime((string)$articlePublishedAt))) ?>"> <meta property="article:published_time" content="<?= htmlspecialchars(date('c', strtotime((string)$articlePublishedAt))) ?>">
<?php endif; ?> <?php endif; ?>
<?php if (!empty($metaAuthor ?? '')): ?> <?php if (!empty($metaAuthorUrl ?? '')): ?>
<meta property="article:author" content="<?= htmlspecialchars($metaAuthorUrl) ?>">
<?php elseif (!empty($metaAuthor ?? '')): ?>
<meta property="article:author" content="<?= htmlspecialchars($metaAuthor) ?>"> <meta property="article:author" content="<?= htmlspecialchars($metaAuthor) ?>">
<?php endif; ?> <?php endif; ?>
+11 -5
View File
@@ -43,8 +43,9 @@ $externalLinks = $article['external_links'] ?? [];
<div class="private-ribbon">Privé</div> <div class="private-ribbon">Privé</div>
<?php endif; ?> <?php endif; ?>
<?php <?php
$authorEmail = $article['author'] ?? ''; $authorEmail = $article['author'] ?? '';
$authorName = ($authorEmail !== '' && function_exists('authorDisplayName')) ? authorDisplayName($authorEmail) : ''; $authorName = ($authorEmail !== '' && function_exists('authorDisplayName')) ? authorDisplayName($authorEmail) : '';
$authorProfileUrl = ($authorEmail !== '' && function_exists('authorProfileUrl')) ? authorProfileUrl($authorEmail) : '';
$pubDate = htmlspecialchars(date('d/m/Y', strtotime((string)($article['published_at'] ?? $article['created_at'] ?? '')))); $pubDate = htmlspecialchars(date('d/m/Y', strtotime((string)($article['published_at'] ?? $article['created_at'] ?? ''))));
$hasCover = $coverFile !== ''; $hasCover = $coverFile !== '';
$heroExtraClass = $hasCover ? '' : ' article-cover--gradient'; $heroExtraClass = $hasCover ? '' : ' article-cover--gradient';
@@ -281,8 +282,9 @@ if ($ogImage === null || $ogImage === '') {
$ogImage = $article['og_image'] ?? ''; $ogImage = $article['og_image'] ?? '';
} }
// Auteur : nom affiché résolu depuis le champ author du JSON de l'article // Auteur : nom et URL de profil résolus depuis le champ author du JSON de l'article
$metaAuthor = $authorName; $metaAuthor = $authorName;
$metaAuthorUrl = $authorProfileUrl;
// JSON-LD Article // JSON-LD Article
$jsonLdData = [ $jsonLdData = [
@@ -293,7 +295,11 @@ $jsonLdData = [
'url' => $canonical, 'url' => $canonical,
'datePublished' => date('c', strtotime((string)$articlePublishedAt)), 'datePublished' => date('c', strtotime((string)$articlePublishedAt)),
'dateModified' => date('c', strtotime((string)($article['updated_at'] ?? $articlePublishedAt))), 'dateModified' => date('c', strtotime((string)($article['updated_at'] ?? $articlePublishedAt))),
'author' => ['@type' => 'Person', 'name' => $metaAuthor !== '' ? $metaAuthor : (siteAuthor() !== '' ? siteAuthor() : siteTitle())], 'author' => array_filter([
'@type' => 'Person',
'name' => $metaAuthor !== '' ? $metaAuthor : (siteAuthor() !== '' ? siteAuthor() : siteTitle()),
'url' => $metaAuthorUrl !== '' ? $metaAuthorUrl : null,
]),
'publisher' => [ 'publisher' => [
'@type' => 'Person', '@type' => 'Person',
'name' => siteAuthor() !== '' ? siteAuthor() : siteTitle(), 'name' => siteAuthor() !== '' ? siteAuthor() : siteTitle(),
+8
View File
@@ -24,6 +24,14 @@
placeholder="Prénom Nom" required> placeholder="Prénom Nom" required>
<div class="form-text">Affiché comme auteur sur vos articles.</div> <div class="form-text">Affiché comme auteur sur vos articles.</div>
</div> </div>
<div class="mb-3">
<label class="form-label fw-semibold" for="profile_url">URL de profil</label>
<input type="url" id="profile_url" name="profile_url"
class="form-control"
value="<?= htmlspecialchars($profileCurrentUrl) ?>"
placeholder="https://example.com/~vous">
<div class="form-text">Utilisée dans les métadonnées de vos articles (article:author, JSON-LD).</div>
</div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label fw-semibold text-muted">Email</label> <label class="form-label fw-semibold text-muted">Email</label>
<input type="text" class="form-control" value="<?= htmlspecialchars(currentUserEmail() ?? '') ?>" disabled> <input type="text" class="form-control" value="<?= htmlspecialchars(currentUserEmail() ?? '') ?>" disabled>