Let plugins run their own PHP code
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s

A plugin can now bring a plugin.php that registers hooks, scheduled tasks, its own tables, public pages and admin screens. Failures stay contained to the plugin; a form on a plugin page can be submitted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5.5 committed 2026-11-09 18:56:14 +01:00
1 parent b61f958ca1
commit 9825bac3b3
17 files changed
+897 -19

No files matched your search

+15 -5
View File
@@ -16,8 +16,8 @@ use Doctrine\DBAL\Connection;
*
* Seit dem zweiten Teil von Arbeitspaket H (ADR 103) kann ein Plugin außerdem: Bausteine mit
* Kindern (Container), Einstellungen für den Betreiber, eigene öffentliche Adressen und
* Seitenvorlagen für den Editor. Alles davon ist Manifest plus Twig - PHP-Code bringt ein
* Plugin nicht mit, und deshalb gibt es auch keine Hooks in Speichern oder Veröffentlichen.
* Seitenvorlagen für den Editor. Seit ADR 127 darf es zusätzlich eine plugin.php mitbringen
* ("main" im Manifest); was die anmeldet, führt PluginRuntime aus.
*
* Eingeschaltet wird ein Plugin in der Verwaltung; der Schalter liegt in der Datenbank. Ein
* ausgeschaltetes oder fehlendes Plugin lässt seine Bausteine unangetastet im Dokument (ADR 011):
@@ -144,6 +144,12 @@ final class PluginRegistry
$this->write($name, ['settings' => json_encode($values, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)]);
}
/** Die Fassung, für die die plugin.php zuletzt eingerichtet wurde. */
public function markInstalled(string $name): void
{
$this->write($name, ['version' => $this->all()[$name]['version']]);
}
/** Die Einstellungen eines Plugins mit Vorgaben - leer, wenn es das Plugin nicht gibt. */
public function settingsOf(string $name): array
{
@@ -423,11 +429,15 @@ final class PluginRegistry
}
$routes = $this->routes($name, $data['routes'] ?? []);
$presets = $this->presets($name, $data['presets'] ?? []);
if ([] === $blocks && [] === $routes && [] === $presets) {
throw new \RuntimeException('Das Manifest braucht mindestens einen Baustein, eine Adresse oder eine Vorlage.');
$main = $data['main'] ?? null;
if (null !== $main && (!is_string($main) || 1 !== preg_match('~^[a-zA-Z0-9_-]+\.php$~D', $main) || !is_file($this->directory().'/'.$name.'/'.$main))) {
throw new \RuntimeException(sprintf('Die Datei „%s“ aus "main" fehlt im Pluginordner.', is_string($main) ? $main : ''));
}
if ([] === $blocks && [] === $routes && [] === $presets && null === $main) {
throw new \RuntimeException('Das Manifest braucht mindestens einen Baustein, eine Adresse, eine Vorlage oder eine plugin.php.');
}
return ['name' => $name, 'title' => $title, 'description' => mb_substr(trim((string) ($data['description'] ?? '')), 0, 300), 'version' => $version, 'requires' => $requires, 'blocks' => $blocks, 'settings' => $settings, 'routes' => $routes, 'presets' => $presets];
return ['main' => $main, 'name' => $name, 'title' => $title, 'description' => mb_substr(trim((string) ($data['description'] ?? '')), 0, 300), 'version' => $version, 'requires' => $requires, 'blocks' => $blocks, 'settings' => $settings, 'routes' => $routes, 'presets' => $presets];
}
private function blockDefinition(string $plugin, string $key, array $block, string $version): array