Architektur
Renick.Content behandelt Inhalte als strukturierte Daten und Layout als austauschbare Präsentation. Alles im Plugin baut auf drei Schichten auf:
- Content (JSON) — ein Dokument ist eine Liste typisierter Blöcke, die als JSON in einem Host-Feld gespeichert wird (eine Tailor-Entry-Spalte, ein einfaches Model-Attribut). Das Format ist ein veröffentlichter Vertrag: JSON Schema v1, validiert bei jedem Schreibvorgang.
- Editor — eine TipTap-3-/React-18-Insel, die vom Formular-Widget
contenteditorin ein beliebiges Backend-Formular eingebunden wird. Das Bundle ist ein committeter Vite-Build (assets/dist); Konsumenten führen niemals eine JS-Toolchain aus. - Rendering —
BlockRendererwandelt das JSON zur Request-Zeit über eine Fallback-sichere Partial-Pipeline in HTML um (Theme-Override → Plugin-Default → Platzhalter) und wendet Sichtbarkeitsregeln serverseitig an.
Komponentenübersicht
Der Editor serialisiert das Dokument in eine versteckte <textarea> innerhalb des Widget-Partials; beim Speichern schickt ContentEditor::getSaveValue() den Wert durch ContentDocument::normalize() (repariert unsaubere Eingaben), DocumentValidator (strikte Schema-Prüfung) und BlockRules (Hierarchie-Einschränkungen), bevor er das Host-Feld erreicht. Nach einem erfolgreichen Speichern des Hosts wird ein Versions-Snapshot erfasst und der Media-Usage-Index aktualisiert.
Das Dokument selbst lebt im Host — die pluginspezifischen Tabellen sind Sidecars:
| Tabelle | Zweck |
|---|---|
renick_content_versions | vollständige Dokument-Snapshots pro Speichervorgang; dient zugleich als Audit-Log |
renick_content_templates | wiederverwendbare Dokument-/Teilbaum-Vorlagen |
renick_content_media_usage | welche Medienpfade von welchem Host + Block referenziert werden |
renick_content_ai_usage | AI-Token-Abrechnung pro Request für das Monatsbudget |
Aufbau des Plugins
plugins/renick/content/
├── Plugin.php # registrations: widgets, content fields, component,
│ # twig markup, permissions, settings, schedule
├── routes.php # optional REST API (/api/renick/content/v1, off by default)
├── blocks/ # 11 shipped block definitions (YAML) + default partials
├── classes/
│ ├── BlockManager, BlockDefinition, BlockRenderer, BlockRules
│ ├── ContentDocument, DocumentValidator, VisibilityRules
│ ├── Content, ContentApi # PHP facades
│ ├── MediaUsageIndex, MediaDeleteGuard, HostResolver
│ └── ai/ # ProviderManager, drivers, Translator, Drafter, FirecrawlClient
├── components/ # renickContent CMS component
├── contentfields/ # Tailor content field wrapper
├── formwidgets/ # the ContentEditor widget + mount partial
├── console/ # prune-versions, reindex-media commands
├── models/ # Version, Template, AiSetting, AiUsage
├── schema/document-v1.json
├── assets/src/ # editor source (TypeScript + React + TipTap 3)
├── assets/dist/ # committed editor bundle (ESM + CSS) — what ships
└── tests/ # PHPUnit; Playwright e2e lives in /tests-e2e at repo rootZentrale Einstiegspunkte für Integratoren:
- Das Feld einbinden — den Editor in ein Formular einsetzen und dessen Ausgabe rendern.
- Blöcke erstellen — Blocktypen aus einem Theme oder einem anderen Plugin hinzufügen.
- PHP API und REST API — Dokumente programmatisch bearbeiten.