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,126 @@
<?php
namespace tobimori\Seo;
use Kirby\Content\Field;
use Kirby\Data\Yaml;
/**
* Value object for structured alt text data.
* Handles both YAML object format and plain string migration.
*/
class AltText
{
public const string SOURCE_AI = 'ai';
public const string SOURCE_MANUAL = 'manual';
public const string SOURCE_REVIEWED = 'reviewed';
public function __construct(
protected string $text = '',
protected bool $decorative = false,
protected string $source = self::SOURCE_MANUAL,
) {
}
/**
* Parses a raw field value into an AltText instance.
* Handles YAML object format and plain string migration.
*/
public static function parse(string|null $value): static
{
if ($value === null || $value === '') {
return new static();
}
try {
$data = Yaml::decode($value);
if (is_array($data) && (array_key_exists('text', $data) || array_key_exists('decorative', $data))) {
return new static(
text: (string)($data['text'] ?? ''),
decorative: (bool)($data['decorative'] ?? false),
source: (string)($data['source'] ?? self::SOURCE_MANUAL),
);
}
} catch (\Throwable) {
// not valid YAML
}
// plain string = migration from old alt field
return new static(text: $value, source: self::SOURCE_MANUAL);
}
public static function fromField(Field $field): static
{
return static::parse($field->value());
}
public function text(): string
{
return $this->text;
}
public function isDecorative(): bool
{
return $this->decorative;
}
public function source(): string
{
return $this->source;
}
public function isMissing(): bool
{
return !$this->decorative && trim($this->text) === '';
}
public function isAiGenerated(): bool
{
return $this->source === self::SOURCE_AI;
}
public function isReviewed(): bool
{
return $this->source === self::SOURCE_REVIEWED;
}
public function toArray(): array
{
return [
'text' => $this->text,
'decorative' => $this->decorative,
'source' => $this->source,
];
}
/**
* Returns HTML attributes for the alt text.
*/
public function toAttr(): array
{
return ['alt' => $this->decorative ? '' : $this->text];
}
public function toYaml(): string
{
return Yaml::encode($this->toArray());
}
/**
* Returns the resolved alt text as a Kirby Field for chaining (.or(), .isNotEmpty(), etc.)
*/
public function toField(): Field
{
return new Field(null, 'alt', (string)$this);
}
public function __toString(): string
{
if ($this->decorative) {
return '';
}
return $this->text;
}
}