The share image and featured image of a page become image fields with the library dialog, as do image settings of plugins. The editor offers forms, sections, categories and tags as lists, a page search next to every link target and in text links, the redirect target has a page search, and the form editor picks the reply field, the condition and the option images. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
76 lines
2.8 KiB
PHP
76 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Twig;
|
|
|
|
use App\Content\MediaLibrary;
|
|
use App\Domain\InvalidDocument;
|
|
use App\Plugin\PluginRegistry;
|
|
use Twig\Extension\AbstractExtension;
|
|
use Twig\Loader\FilesystemLoader;
|
|
use Twig\TwigFunction;
|
|
|
|
/**
|
|
* Was eine Plugin-Vorlage vom Kern bekommt: die Definition ihres Bausteins, die geprüften
|
|
* Eigenschaften und Bilder aus der Mediathek. Mehr nicht - keine Datenbank, keine Einstellungen.
|
|
*/
|
|
final class PluginExtension extends AbstractExtension
|
|
{
|
|
public function __construct(private PluginRegistry $plugins, private MediaLibrary $media, FilesystemLoader $loader)
|
|
{
|
|
// Der Namensraum @plugins zeigt auf den Pluginordner - aber nur, wenn es ihn gibt. Ein
|
|
// fester Pfad in twig.yaml liesse eine Installation ohne plugins/ mit einem Ladefehler
|
|
// stehen, bevor sie eine einzige Seite ausgeliefert hat.
|
|
$directory = $plugins->directory();
|
|
if (is_dir($directory) && !in_array($directory, $loader->getPaths('plugins'), true)) {
|
|
$loader->addPath($directory, 'plugins');
|
|
}
|
|
}
|
|
|
|
public function getFunctions(): array
|
|
{
|
|
return [
|
|
new TwigFunction('kant_plugin_block', $this->plugins->block(...)),
|
|
new TwigFunction('kant_plugin_props', $this->props(...)),
|
|
new TwigFunction('kant_plugin_media', $this->mediaItem(...)),
|
|
new TwigFunction('kant_plugin_settings', $this->plugins->settingsOf(...)),
|
|
new TwigFunction('kant_media', $this->adminMedia(...)),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Ein Bild fuer ein Bildfeld der Verwaltung - auch ein archiviertes: es ist noch gewaehlt und
|
|
* wird noch ausgeliefert, also soll es auch zu sehen sein.
|
|
*/
|
|
public function adminMedia(string $id): ?array
|
|
{
|
|
return 1 === preg_match('/^[a-f0-9]{32}$/D', $id) ? $this->media->find($id) : null;
|
|
}
|
|
|
|
/** Die geprüften Eigenschaften eines Bausteins - oder null, wenn sie nicht ins Schema passen. */
|
|
public function props(array $block): ?array
|
|
{
|
|
try {
|
|
return $this->plugins->validateProps((string) ($block['type'] ?? ''), is_array($block['props'] ?? null) ? $block['props'] : []);
|
|
} catch (InvalidDocument) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ein Bild der Mediathek für eine Vorlage: Kennung, Titel, Alternativtext und die Fassungen
|
|
* (src, width, height), die kleinste zuerst.
|
|
*/
|
|
public function mediaItem(string $id): ?array
|
|
{
|
|
if (1 !== preg_match('/^[a-f0-9]{32}$/D', $id)) {
|
|
return null;
|
|
}
|
|
$item = $this->media->find($id);
|
|
if (null === $item || ($item['archived'] ?? false)) {
|
|
return null;
|
|
}
|
|
|
|
return ['id' => $item['id'], 'title' => (string) ($item['title'] ?? ''), 'alt' => (string) ($item['alt'] ?? ''), 'variants' => $item['variants']];
|
|
}
|
|
}
|