Skip to content

Joomla ​

Der zweite Provider. Er ist die Antwort auf alles, was der PHP-Handler bewusst nicht tut: Anfragen speichern, im Backend verwalten, Status pflegen, exportieren, in bestehende Prozesse einbinden.

ts
forms: {
  provider: 'joomla',
  endpoint: 'https://backend.example.de/api/index.php/v1/kickastroforms/submissions',
  captcha: 'turnstile',
}

Für den Joomla-Provider verlangt die Konfiguration eine absolute https-Adresse — der Endpunkt liegt auf einer anderen Domain als die statische Website. Der Providerwechsel ist damit eine Änderung an zwei Zeilen; keine Seite und keine Komponente erfährt davon.

Noch nicht verfügbar

Die Companion-Erweiterung ist noch nicht veröffentlicht. Der Starter bringt die Frontend-Seite vollständig mit — Client, Vertrag, Konfiguration, Tests —, aber ohne Gegenstelle nimmt niemand die Übermittlung entgegen. Bis dahin ist standalone-php der Weg.

Was hier liegt und was dort ​

Die Erweiterung ist nicht Teil dieses Repositories. Sie hat einen eigenen Veröffentlichungsrhythmus, eine eigene Versionierung und eine ganz andere Abhängigkeit — Joomla selbst.

Hier im StarterIm Companion-Repository
submitForm() und der providerunabhängige Clientcom_kickastroforms
contract/form-submission.schema.jsonplg_webservices_kickastroforms
contract/examples/requests.jsonSpeicherung, Backend, CSV-Export
contract/forms.allowlist.json als VorlageFeld-Allowlist je Formular im Backend
forms.provider und forms.endpointContract-Tests gegen dieselbe Datei

Beide Seiten lesen dieselbe erzeugte Vertragsdatei und dieselben Beispielrümpfe. Das ist der ganze Grund, warum beides sprachneutral im Repository liegt (ADR 0009, ADR 0018).

Die Feld-Allowlist ​

Der PHP-Handler liest seine Allowlist als erzeugte Datei. Das Joomla-Gateway kann das nicht: Es bedient mehrere Kundenwebsites, die zu verschiedenen Zeitpunkten ausgeliefert werden, und hat keinen Zugriff auf deren Repositories. Die Felder stehen deshalb im Backend der Erweiterung, je Formular gepflegt.

Abgeschrieben wird aus contract/forms.allowlist.json — sie entsteht bei jedem pnpm forms:contract, auch in einem Projekt ohne PHP-Handler, und hat genau die Struktur, die das Gateway erwartet: je Feld name, type, required, dazu maxLength, options und consent, wo sie gelten.

Erst das Gateway, dann die Website

Weil die Liste an zwei Stellen von Hand zusammenfindet, ist form.schemaVersion die Stelle, an der eine Abweichung auffällt — und sie fällt hart auf: Das Gateway antwortet unknown_form, solange seine Fassung nicht zur Seite passt. Wer ein Feld ergänzt, erhöht schemaVersion (Regel 11), trägt das Feld zuerst im Joomla-Backend nach und liefert danach die Website aus. Andersherum steht das Formular still, bis jemand nachzieht.

Der Endpunkt ​

http
POST /api/index.php/v1/kickastroforms/submissions
Content-Type: application/json

Die Route ist public, weil Besucher kein Joomla-Konto haben. Das heißt ausdrücklich nicht „ungeprüft offen": Sie muss dieselben Hürden mitbringen wie der PHP-Handler.

Anforderungen an das Gateway

  • ausschließlich POST für Übermittlungen
  • enge Formular-Allowlist mit Feldregeln je Formular
  • Request-Größenlimit
  • Rate Limiting
  • Honeypot- und Captcha-Prüfung
  • Origin-Allowlist
  • neutrale Fehlermeldungen nach außen, Einzelheiten nur im Log
  • Schutz gegen versehentliche Doppelsubmissions über security.submissionToken
  • datensparsame Logs mit definierter Aufbewahrungsfrist
  • Speichern je Formular abschaltbar
  • E-Mail-Benachrichtigung
  • klare Berechtigungen im Administratorbereich
  • CSV-Export nur für Berechtigte
  • automatische Löschung nach Ablauf der Frist

Version 1 bekommt keinen Drag-and-drop-Formularbuilder und keine Workflow-Engine. Die Felder stehen im Markup der Astro-Seite; die Erweiterung kennt sie über die Allowlist.

CORS und Herkunft ​

Anders als beim PHP-Handler ist die Anfrage hier immer Cross-Origin. Daraus folgt zweierlei:

  • Das Gateway muss den Preflight (OPTIONS) beantworten und Access-Control-Allow-Origin auf die Adresse der Website setzen — nicht auf *.
  • Die Origin-Allowlist des Gateways ist die Liste der Kundenwebsites, die es bedient.

Der Client sendet ohne Anmeldedaten (credentials: 'omit'). Es gibt keine Sitzung und kein Geheimnis im Browser; geschützt wird über Origin, Honeypot, Captcha und Rate Limit.

Unterstützte Joomla-Versionen ​

JoomlaStatusBegründung
5.4 LTSunterstütztAktuelle LTS-Reihe; läuft in Kundenprojekten
6.xunterstütztAktuelle Hauptreihe
4.x und älternicht unterstütztAndere Namespace- und Service-Provider-Struktur

Gemeinsame Unterstützung von 5.4 und 6.x ist ohne Compatibility Shims möglich: Beide Reihen teilen den Namespace-Aufbau, services/provider.php, SubscriberInterface und den API-Router. Was 6.0 entfernt hat — allen voran Factory::getDbo() —, ist in 5.4 bereits deprecated und lässt sich in einer Fassung vermeiden, die in beiden läuft. Die Empfehlung ist deshalb: eine Codebasis, minimale Anforderung PHP 8.3, keine Sonderpfade.

Zwei Joomla-Reihen parallel zu pflegen wäre der Fehler, der aus einer Erweiterung zwei macht.

Kompatibilität von Vertrag und Gateway ​

Der Vertrag trägt eine Version (version: '1'), und sie steht auch im Pfad der Schema-Datei. Ein Gateway, das eine Version nicht kennt, lehnt mit invalid_request ab, statt zu raten.

Die Dokumentation der Erweiterung nennt zu jeder Release, welche Vertragsversionen sie bedient. Beim Aktualisieren gilt: erst das Gateway, dann die Website. Ein Gateway, das die neue Version schon kennt, bedient auch die alte; andersherum bricht die erste Übermittlung.

Womit das Gateway prüft, steht in contract/examples/requests.json: gültige und ungültige Rümpfe mit dem Fehlercode, der zu jedem gehört. Der PHP-Handler prüft gegen dieselbe Datei — sie ist die Stelle, an der beide Backends dieselbe Auslegung des Vertrags haben, und nicht nur dasselbe Schema.

Same-Origin-Gateway ​

Eine dokumentierte Betriebsart, noch nicht umgesetzt:

text
www.example.de/api/forms/submit.php
    → backend.example.de/api/index.php/v1/kickastroforms/submissions

Hier ist der PHP-Endpunkt tatsächlich ein Proxy — er verarbeitet nicht selbst, sondern reicht weiter. Das vermeidet CORS vollständig, verbirgt die Adresse des Backends und erlaubt es, Rate Limit und Origin-Prüfung schon vor dem Backend anzuwenden.

Die Architektur verbaut das nicht: submitForm() kennt ohnehin nur Adresse und CORS-Modus, und der Vertrag ist auf beiden Seiten derselbe. Was fehlt, ist die Weiterleitung selbst.

„Proxy" heißt weiterleiten

Der Handler aus M7 verarbeitet selbst und ist deshalb keiner. Die Wörter auseinanderzuhalten ist keine Wortklauberei: Wer einen verarbeitenden Endpunkt „Proxy" nennt, sucht die Anfragen später auf dem falschen Server.