SEO : add tombi mori plugin

This commit is contained in:
isUnknown 2025-05-13 09:03:14 +02:00
parent df2843123f
commit 8f9e75126e
64 changed files with 3719 additions and 44 deletions

View file

@ -0,0 +1,82 @@
<?php
namespace tobimori\Seo\Sitemap;
use DOMDocument;
use Kirby\Cms\App;
use Kirby\Toolkit\Collection;
class Sitemap extends Collection
{
public function __construct(protected string $key, array $data = [], bool $caseSensitive = false)
{
parent::__construct($data, $caseSensitive);
}
public function key(): string
{
return $this->key;
}
public function loc(): string
{
return kirby()->site()->canonicalFor('sitemap-' . $this->key . '.xml');
}
public function lastmod(): string
{
$lastmod = 0;
foreach ($this as $url) {
$lastmod = max($lastmod, strtotime($url->lastmod()));
}
if ($lastmod > 0) {
return date('c', $lastmod);
}
return date('c');
}
public function createUrl(string $loc): SitemapUrl
{
$url = $this->makeUrl($loc);
$this->append($url);
return $url;
}
public static function makeUrl(string $url): SitemapUrl
{
return new SitemapUrl($url);
}
public function toDOMNode(DOMDocument $doc = new DOMDocument('1.0', 'UTF-8'))
{
$doc->formatOutput = true;
$root = $doc->createElement('sitemap');
foreach (['loc', 'lastmod'] as $key) {
$root->appendChild($doc->createElement($key, $this->$key()));
}
return $root;
}
public function toString(): string
{
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->formatOutput = true;
$doc->appendChild($doc->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="/sitemap.xsl"'));
$root = $doc->createElementNS('http://www.sitemaps.org/schemas/sitemap/0.9', 'urlset');
$root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
$root->setAttribute('seo-version', App::plugin('tobimori/seo')->version());
foreach ($this as $url) {
$root->appendChild($url->toDOMNode($doc));
}
$doc->appendChild($root);
return $doc->saveXML();
}
}

View file

@ -0,0 +1,100 @@
<?php
namespace tobimori\Seo\Sitemap;
use DOMDocument;
use Kirby\Cms\App;
use Kirby\Cms\Page;
use Kirby\Toolkit\Collection;
class SitemapIndex extends Collection
{
protected static $instance = null;
public static function instance(...$args): static
{
if (static::$instance === null) {
static::$instance = new static(...$args);
}
return static::$instance;
}
public function create(string $key = 'pages'): Sitemap
{
$sitemap = $this->make($key);
$this->append($sitemap);
return $sitemap;
}
public static function make(string $key = 'pages'): Sitemap
{
return new Sitemap($key);
}
public static function makeUrl(string $url): SitemapUrl
{
return new SitemapUrl($url);
}
public function toString(): string
{
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->formatOutput = true;
$doc->appendChild($doc->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="sitemap.xsl"'));
$root = $doc->createElementNS('http://www.sitemaps.org/schemas/sitemap/0.9', 'sitemapindex');
$root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
$root->setAttribute('seo-version', App::plugin('tobimori/seo')->version());
$doc->appendChild($root);
foreach ($this as $sitemap) {
$root->appendChild($sitemap->toDOMNode($doc));
}
return $doc->saveXML();
}
public function isValidIndex(string $key = null): bool
{
if ($key === null) {
return $this->count() > 1;
}
return !!$this->findBy('key', $key) && $this->count() > 1;
}
public function generate(): void
{
$generator = option('tobimori.seo.sitemap.generator');
if (is_callable($generator)) {
$generator($this);
}
}
public function render(Page $page): string|null
{
// There always has to be at least one index,
// otherwise the sitemap will fail to render
if ($this->count() === 0) {
$this->generate();
}
if ($this->count() === 0) {
$this->create();
}
if (($index = $page->content()->get('index'))->isEmpty()) {
// If there is only one index, we do not need to render the index page
return $this->count() > 1 ? $this->toString() : $this->first()->toString();
}
$sitemap = $this->findBy('key', $index->value());
if ($sitemap) {
return $sitemap->toString();
}
return null;
}
}

View file

@ -0,0 +1,113 @@
<?php
namespace tobimori\Seo\Sitemap;
use DOMDocument;
use DOMNode;
use Kirby\Exception\Exception;
class SitemapUrl
{
protected string $lastmod;
protected string $changefreq;
protected string $priority;
protected array $alternates = [];
public function __construct(protected string $loc)
{
}
public function loc(string $url = null): SitemapUrl|string
{
if ($url === null) {
return $this->loc;
}
$this->loc = $url;
return $this;
}
public function lastmod(string $lastmod = null): SitemapUrl|string
{
if ($lastmod === null) {
return $this->lastmod;
}
$this->lastmod = date('c', $lastmod);
return $this;
}
public function changefreq(string $changefreq = null): SitemapUrl|string
{
if ($changefreq === null) {
return $this->changefreq;
}
$this->changefreq = $changefreq;
return $this;
}
public function priority(string $priority = null): SitemapUrl|string
{
if ($priority === null) {
return $this->priority;
}
$this->priority = $priority;
return $this;
}
public function alternates(array $alternates = []): SitemapUrl|array
{
if (empty($alternates)) {
return $this->alternates;
}
foreach ($alternates as $alternate) {
foreach (['href', 'hreflang'] as $key) {
if (!array_key_exists($key, $alternate)) {
new Exception("[kirby-seo] The alternate link to '{$this->loc()} is missing the '{$key}' attribute");
}
}
}
$this->alternates = $alternates;
return $this;
}
public function toDOMNode(DOMDocument $doc = new DOMDocument('1.0', 'UTF-8')): DOMNode
{
$doc->formatOutput = true;
$node = $doc->createElement('url');
foreach (array_diff_key(get_object_vars($this), array_flip(['alternates'])) as $key => $value) {
$node->appendChild($doc->createElement($key, $value));
}
if (!empty($this->alternates())) {
foreach ($this->alternates() as $alternate) {
$alternateNode = $doc->createElement('xhtml:link');
foreach ($alternate as $key => $value) {
$alternateNode->setAttribute($key, $value);
}
$node->appendChild($alternateNode);
}
}
return $node;
}
public function toString(): string
{
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->formatOutput = true;
$node = $this->toDOMNode();
$doc->appendChild($node);
return $doc->saveXML($node);
}
}