feat: intégration plugin Kirby SEO
All checks were successful
Deploy / Deploy to Production (push) Successful in 22s

- Ajout de tobimori/kirby-seo via Composer
- snippet('seo/head') dans header.php (remplace les meta manuels)
- snippet('seo/schemas') dans footer.php pour JSON-LD
- Onglet SEO ajouté dans site.yml et tous les blueprints de pages
- Configuration SEO dans config.php (sitemap, robots, canonicalBase TODO)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-03-25 12:59:18 +01:00
parent baab2fb3a1
commit 58c31ea391
133 changed files with 9201 additions and 253 deletions

View file

@ -0,0 +1,39 @@
<?php
use tobimori\Seo\Ai\Drivers\Anthropic;
use tobimori\Seo\Ai\Drivers\Gemini;
use tobimori\Seo\Ai\Drivers\OpenAi;
// TODO: custom provider per task
return [
'enabled' => true,
'provider' => 'openai',
'providers' => [
'openai' => [
'driver' => OpenAi::class,
'config' => [
'apiKey' => '', // needs to be defined
],
],
'anthropic' => [
'driver' => Anthropic::class,
'config' => [
'apiKey' => '', // needs to be defined
],
],
'gemini' => [
'driver' => Gemini::class,
'config' => [
'apiKey' => '', // needs to be defined
],
],
'openrouter' => [
'driver' => OpenAi::class,
'config' => [
'apiKey' => '', // needs to be defined
'model' => 'openai/gpt-5-nano',
'endpoint' => 'https://openrouter.ai/api/v1/responses',
],
],
],
];

View file

@ -0,0 +1,29 @@
<?php
return [
'enabled' => true,
'searchEngine' => 'https://api.indexnow.org', // one will propagate to all others. so this is fine @see https://www.indexnow.org/faq
'rules' => [
// by default, only the current page is requested to be indexed (if indexable: robots allow + listed status)
// however you might want to index other pages as well. for example, the 'blog overview' page should always be reindexed when a new 'blog post' is indexed
//
// syntax: 'match pattern' => ['invalidation rules']
//
// match patterns:
// - '/blog/*' - url pattern (glob or regex)
// - 'article' - template name
// - '*' - wildcard, matches all pages
//
// invalidation rules:
// - 'parent' => true (direct parent) or number (levels up)
// - 'children' => true (all descendants) or number (depth limit)
// - 'siblings' => true (all siblings at same level)
// - 'urls' => ['/shop', '/'] (specific urls to invalidate)
// - 'templates' => ['category', 'shop'] (invalidate all pages with these templates)
//
// examples:
// '/blog/*' => ['parent' => true],
// 'article' => ['parent' => 2, 'urls' => ['/blog', '/']],
// 'product' => ['parent' => true, 'siblings' => true, 'templates' => ['category']],
],
];

View file

@ -0,0 +1,55 @@
<?php
use tobimori\Seo\Sitemap\SitemapIndex;
use tobimori\Seo\Meta;
return function (SitemapIndex $sitemap) {
$exclude = option('tobimori.seo.sitemap.excludeTemplates', []);
$pages = site()->index()->filter(fn ($page) => $page->metadata()->robotsIndex()->toBool() && !in_array($page->intendedTemplate()->name(), $exclude));
if ($group = option('tobimori.seo.sitemap.groupByTemplate')) {
$pages = $pages->group('intendedTemplate');
}
if (is_a($pages->first(), 'Kirby\Cms\Page')) {
$pages = $pages->group(fn () => 'pages');
}
foreach ($pages as $group) {
$index = $sitemap->create($group ? $group->first()->intendedTemplate()->name() : 'pages');
foreach ($group as $page) {
$url = $index->createUrl($page->metadata()->canonicalUrl())
->lastmod($page->modified() ?? (int)(date('c')))
->changefreq(is_callable($changefreq = option('tobimori.seo.sitemap.changefreq')) ? $changefreq($page) : $changefreq)
->priority(is_callable($priority = option('tobimori.seo.sitemap.priority')) ? $priority($page) : $priority);
if (kirby()->languages()->count() > 1 && kirby()->language() !== null) {
$alternates = [];
foreach (kirby()->languages() as $language) {
// only if this language is translated for this page and exists
if ($page->translation($language->code())->exists()) {
/*
* Specification: "lists every alternate version of the page, including itself."
* https://developers.google.com/search/docs/specialty/international/localized-versions#sitemap
*/
$alternates[] =
[
'hreflang' => Meta::toBCP47($language),
'href' => $page->url($language->code()),
];
}
}
// add x-default
$alternates[] =
[
'hreflang' => 'x-default',
'href' => $page->indexUrl(),
];
$url->alternates($alternates);
}
}
}
};