Sie befinden sich hier: start » de » Interne Dokumentation » DokuWiki-Erweiterungen (WvdS) » Erweiterungspunkte der WvdS-DW-Suite » API-Referenz: Erweiterungspunkte

API-Referenz: Erweiterungspunkte

Diese Seite ist der vollständige Katalog der Erweiterungspunkte der WvdS-DW-Suite. Sie nennt für jeden öffentlichen Punkt Zweck, Lebenszyklus, Ein- und Ausgaben, Fehlerverhalten, Berechtigungen, ein lauffähiges Minimalbeispiel und die Regel, nach der er sich versionieren darf — und sie trennt ausdrücklich, was öffentlicher Contract ist und was interne Umsetzung.

Was diese Seite besitzt und was nicht. Diese Seite besitzt die paketübergreifende Sicht: welche Erweiterungspunkte es gibt, welche Stufe sie tragen, wie man sie anspricht. Die vollständige Behandlung eines einzelnen Contracts — jedes Feld, jede Messung, jede Begründung — bleibt bei dem Paket, dem er gehört; jeder Abschnitt verweist darauf. Werte werden hier nicht zweitverwaltet.

Status und Kompatibilität

Angabe Wert
Gemessen an Arbeitsstand der Suite vom 2026-08-23
Geltungsbereich die wk-Pakete unter lib/plugins/
Plattformvoraussetzungen siehe die Kompatibilitätsmatrix in docs/KOMPATIBILITAET.md
Belegart Quelltext der genannten Dateien; keine Angabe aus Erinnerung

Geltungsbereich: öffentlich gegen intern

Die PHP-Sichtbarkeit einer Methode entscheidet nicht, ob sie Contract ist. Mehrere Klassen der Suite haben public-Methoden, die ausschließlich für eigene Bildschirme desselben Pakets gedacht sind. Verbindlich ist ausschließlich diese Tabelle.

Öffentlicher Contract

Erweiterungspunkt Träger Stufe
WKCORE_REGISTER_SERVICES wkcore stabil
WKCORE_READY wkcore stabil
WKCORE_CONTEXT_BUILD wkcore stabil
WKFLUENTUI_REGISTER_CONTRIBUTIONS wkfluentui v1
helper_plugin_wkfluentui_capabilities wkfluentui v1
helper_plugin_wkidentity_contract wkidentity v1
helper_plugin_wkrequest wkrequest v1
WKREQUEST_REGISTER_HANDLERS wkrequest v1
WKDOCORE_REGISTER_HUBS wkdocore experimentell
WKDOCORE_CONTEXT_BUILD wkdocore experimentell
WKDOCORE_PROJECT_DISCOVER wkdocore experimentell
WKDOCORE_REGISTER_REPOSITORY_SOURCES wkdocore experimentell
WKSOURCEVIEW_REGISTER_PROVIDERS wksourceview experimentell
WKSQLITEDS_REGISTER_ROUTINE_SETS wksqliteds experimentell
WKOPENGRAPH_VALUES wkopengraph experimentell
WKREQUEST_REFINE_DENIED wkrequest experimentell
Dienst-IDs des Registers verschiedene je Dienst

Veraltet, funktioniert weiter

Fünf entenartig typisierte Methoden, die der Workbench-Contract v1 ablöst. Ein Anpassungsstück (helper_plugin_wkfluentui_legacyadapter) übersetzt sie weiterhin in dieselbe Form, sodass bestehende Pakete unverändert laufen. Einzelheiten stehen weiter unten unter „Veraltete Mechanismen„.

Keine öffentliche Schnittstelle

Diese Klassen und Methoden sind kein Contract, auch wo sie public sind. Sie dürfen sich ohne Ankündigung ändern.

Element Träger Warum nicht öffentlich
helper_plugin_wkcore::dump() und ::ids() wkcore Diagnoseausgabe für Menschen. Der Text ist ausdrücklich nicht maschinenlesbar zugesichert und darf nicht ausgewertet werden. ids() ruft bootstrap() nicht und kann unvollständig sein.
helper_plugin_wkfluentui_legacyadapter wkfluentui Übergangsstück. Verschwindet, sobald kein Paket mehr getShellRegions() deklariert.
helper_plugin_wkfluentui_widgets, _adminshell, _views, _modules, _adminrail, _arrange, _surface wkfluentui Die Zeichner des Workbench. Sie lesen den Contract; sie sind nicht der Contract.
helper_plugin_wkrequest::submit(), ::pendingFor(), ::withdraw(), ::store(), ::codec() wkrequest Für die eigenen Bildschirme des Pakets. Ein Antrag entsteht über das Formular, nicht hinter ihm vorbei.
helper_plugin_wkidentity (TOTP-Geheimnisse, Codeversand, Faktorprüfung) wkidentity Bewusst nicht als zweite, konkurrierende Oberfläche freigegeben. Öffentlich ist allein helper_plugin_wkidentity_contract.
StepUp, PolicyStore wkidentity Innenleben der Zusatzprüfung. Erreichbar über require() und satisfies().
WKBIZWAY_WIKI_REGIONS Vorlage wkbizway Einhängepunkt der Hausvorlage, nicht der Plugin-Suite. Der Name entspricht der heutigen Anordnung nicht mehr und steht zur Umbenennung an.
Alle Domain-, Model- und Ui-Klassen der Pakete verschiedene Innenleben, außer wo eine Klasse in diesem Katalog ausdrücklich als Nutzlast oder Interface eines Erweiterungspunkts genannt ist.

Stabilitätsstufen und Versionierung

Die drei Stufen

Stufe Bedeutung
stabil Nutzlast und Verhalten brechen nicht ohne Abkündigungszyklus und Eintrag im Änderungsprotokoll.
experimentell Signatur oder Nutzlast können sich noch ändern. Benutzbar, aber Änderungen sind ohne Zyklus zulässig.
veraltet Zur Entfernung vorgesehen; der brechende Schritt wird im Ereigniskatalog des Trägers angekündigt.

Die Stufe eines Ereignisses steht maschinenlesbar im catalog() seines Trägers, nicht in dieser Tabelle: lib/plugins/wkcore/Events.php, lib/plugins/wkdocore/Events.php, lib/plugins/wksourceview/Events.php.

Es gibt keine Suite-Version

Jedes Paket ist ein eigenes Repository mit eigenem Manifest. plugin.info.txt hat kein Feld für eine semantische Version; das Feld date ist der Versionsstempel — es ist der Wert, den Erweiterungsmanager und dokuwiki.org vergleichen, um ein Update zu erkennen. Ein Paket mit stehendem Datum meldet sich als die Fassung von damals und wird nie als Update angeboten.

Folgerung für Sie: Fragen Sie nie eine Version ab, um eine Fähigkeit zu erschließen. Es gibt keine Zahl, die das beantworten könnte.

Fähigkeitsnamen statt Versionsvergleich

Der einzige zugesicherte Weg, vor der Benutzung nach einer Fähigkeit zu fragen, ist ein Name:

$caps = plugin_load('helper', 'wkfluentui_capabilities');
if ($caps !== null && $caps->has('workbench.contribution.bottomPanel')) { /* … */ }

Warum kein Versionsvergleich genügt, ist an dieser Suite gemessen: Der untere Panelbereich existierte in der Ablage und im Raster, lange bevor der zusammensetzende Teil ihn weiterreichte. Eine Versions- oder method_exists()-Prüfung hätte die ganze Zeit „vorhanden“ gemeldet, während er in jeder Anfrage, auf die es ankam, abwesend war. Der Fähigkeitsname wurde erst mit der Änderung wahr, die den Bereich tatsächlich funktionsfähig machte.

Die vier Regeln für brechende Änderungen

  1. Die Endung .v1 markiert das Bruchrisiko. Namen, die später eine unverträgliche zweite Fassung bekommen können — eine Beitragsart, eine Form der Berechtigungsangabe, die Bedeutung einer Identitätsforderung — tragen die Endung. Ein altes Paket fragt für immer nach .v1 und bekommt auch dann eine wahrheitsgemäße Antwort, wenn .v2 daneben existiert.
  2. Reine Vokabularnamen tragen keine Version. Region-, Befehls- und Tonnamen wachsen additiv. Ein unbekannter Wert wird verworfen, nie fatal — diese Toleranz nimmt additives Wachstum ohne Versionssprung auf.
  3. Bei gleicher Kennung gewinnt der spätere Eintrag. Das gilt für Workbench-Beiträge, Hubs, Quelltext-Provider und Repositoriumsquellen gleichermaßen. Wer eine Kennung eines anderen Pakets wiederverwendet, ersetzt dessen Eintrag.
  4. Neue Pflichtfelder sind ein Bruch, neue Kannfelder nicht. Jedes Feld eines Descriptors hat eine Vorgabe, die nichts verändert. Ein Feld hinzuzufügen ist deshalb verträglich; eines zur Pflicht zu machen, ist es nicht.

Entfernen braucht keinen Code

Es gibt in dieser Installation keinen Deinstallationshaken — auch DokuWiki selbst hat keinen: der Erweiterungsmanager löscht das Verzeichnis unmittelbar. Ein entferntes oder abgeschaltetes Plugin wird von plugin_list() nicht mehr genannt, sein Haken feuert nicht mehr, und das Register enthält bei der nächsten Anfrage keinen Eintrag dieses Eigentümers. Das ist kein Aufräumen, sondern eine Eigenschaft der Bauform: die Register rechnen bei jeder Anfrage neu und schreiben nichts fort.

Was ein Verbraucher eines Beitrags sich merkt — die bevorzugte Region einer Ansicht, die Reihenfolge einer Leiste — ist davon getrennt und trägt seine eigene Regel, veraltete Einträge zu dulden und zu deckeln.

Teil A: Stabile Erweiterungspunkte

WKCORE_REGISTER_SERVICES

Signatur

$controller->register_hook('WKCORE_REGISTER_SERVICES', 'BEFORE', $this, 'registerServices');
 
public function registerServices(Event $event): void;
// $event->data === helper_plugin_wkcore

Quelle: lib/plugins/wkcore/helper.php, lib/plugins/wkcore/Events.php

Zusammenfassung

Veröffentlicht ein Objekt oder einen Wert unter einer Zeichenketten-Kennung im Dienstregister der Suite, damit andere Pakete es benutzen können, ohne Ihr Paket zu kennen.

Eingaben

$event→data ist die Registerinstanz. Sie rufen darauf:

Methode Wofür
register(string $id, mixed $service, bool $overwrite = false) Fertig gebautes Objekt. Für Billiges oder für Dienste, die vor jedem möglichen Zugriff da sein müssen.
registerFactory(string $id, callable $factory, bool $overwrite = false) Erzeugerfunktion, die das Register als einziges Argument bekommt. Wird höchstens einmal gerufen, beim ersten get().

Kennungen sind flach und nur per Konvention namensraumiert (Bereich, Punkt, Dienst — etwa storage.sqlite). Eine Kennung darf nicht leer sein.

Ausgaben

Kein Rückgabewert. Verbraucher lesen anschließend:

Methode Verhalten
has(string $id): bool Löst keine Erzeugerfunktion aus.
get(string $id): mixed Löst die Erzeugerfunktion beim ersten Mal aus, merkt das Ergebnis für die Anfrage.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Einmal je Anfrage. bootstrap() feuert zuerst WKCORE_REGISTER_SERVICES, danach WKCORE_READY; ein zweiter Aufruf ist wirkungslos. Bei gewöhnlichen Seitenaufrufen stößt action_plugin_wkcore das nach DOKUWIKI_STARTED an. Einstiegspunkte, die DOKUWIKI_STARTED nie erreichen — lib/exe/ajax.php, css.php, Kommandozeilenskripte — lösen es beim ersten has() oder get() aus. Das Register ist damit unabhängig vom Einstiegspunkt gefüllt.
Fehlerverhalten Leere Kennung: RuntimeException. Bereits belegte Kennung ohne $overwrite: RuntimeException. Unbekannte Kennung bei get(): RuntimeException, deren Meldung alle bekannten Kennungen aufzählt. Ein Verbraucher, der ohne Ausnahme auskommen will, prüft vorher mit has().
Berechtigungen Keine. Das Register ist kein Sicherheitsgrenzwert — es reicht Objekte weiter. Jeder Dienst prüft seine eigenen Rechte selbst, und jeder Verbraucher bleibt für seine eigene Prüfung zuständig. Legen Sie nichts hinein, dessen bloßer Besitz bereits eine Befugnis wäre.
Versionierung Stabil seit 2026-06-22. Nutzlast: die Registerinstanz. Neue Dienst-IDs sind additiv und verträglich; die Bedeutung einer bestehenden ID zu ändern, ist ein Bruch.

Das Register ist kein Behälter für Abhängigkeitsinjektion: keine automatische Verdrahtung, keine Reflexion über Konstruktoren, kein Konfigurationsformat.

Beispiel

Ein Paket veröffentlicht einen eigenen Dienst und benutzt einen fremden.

<?php
 
use dokuwiki\Extension\ActionPlugin;
use dokuwiki\Extension\EventHandler;
use dokuwiki\Extension\Event;
 
class action_plugin_wkexample extends ActionPlugin
{
    public function register(EventHandler $controller)
    {
        // Ausgeschrieben, nicht als Konstante: wkcore kann fehlen.
        $controller->register_hook('WKCORE_REGISTER_SERVICES', 'BEFORE', $this, 'registerServices');
    }
 
    public function registerServices(Event $event)
    {
        $registry = $event->data;
        if (!is_object($registry) || !method_exists($registry, 'registerFactory')) {
            return;   // fremde Nutzlast: nichts tun, nie annehmen
        }
 
        // Träge: erst beim ersten get() gebaut.
        $registry->registerFactory('example.reports', static function ($reg) {
            $storage = $reg->has('storage.sqlite') ? $reg->get('storage.sqlite') : null;
            return new \dokuwiki\plugin\wkexample\ReportService($storage);
        });
    }
 
    /** So benutzt ein Verbraucher einen fremden Dienst. */
    public function readVaultSecret(string $id): ?array
    {
        $core = plugin_load('helper', 'wkcore');
        if ($core === null || !$core->has('vault.secrets')) {
            return null;   // Paket abwesend: Funktion fehlt, nichts stürzt ab
        }
        return $core->get('vault.secrets')->read($id);
    }
}

Komposition

Wird von helper_plugin_wkcore::bootstrap() gefeuert. Läuft vor WKCORE_READY. Die belegten Kennungen stehen weiter unten im Dienstregister.

WKCORE_READY

Signatur

$controller->register_hook('WKCORE_READY', 'BEFORE', $this, 'onCoreReady');
 
public function onCoreReady(Event $event): void;
// $event->data === helper_plugin_wkcore

Quelle: lib/plugins/wkcore/Events.php

Zusammenfassung

Meldet, dass die Registrierungsrunde abgeschlossen ist und das Register gefahrlos abgefragt werden darf.

Eingaben

Die Registerinstanz, vollständig befüllt.

Ausgaben

Kein Rückgabewert. Der Haken beobachtet; er trägt nichts bei.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Unmittelbar nach WKCORE_REGISTER_SERVICES, in derselben Bootrunde, einmal je Anfrage.
Fehlerverhalten Wie bei jedem DokuWiki-Ereignis: die Ereignisverteilung schirmt Haken gegeneinander ab. Ein get() auf eine unbekannte Kennung wirft weiterhin.
Berechtigungen Keine.
Versionierung Stabil seit 2026-06-22.

Registrieren Sie hier nicht. Zu diesem Zeitpunkt können Verbraucher bereits gelesen haben; eine Registrierung käme für sie zu spät.

Beispiel

public function register(EventHandler $controller)
{
    $controller->register_hook('WKCORE_READY', 'BEFORE', $this, 'onCoreReady');
}
 
public function onCoreReady(Event $event)
{
    $registry = $event->data;
    if (!is_object($registry) || !method_exists($registry, 'has')) {
        return;
    }
    // Eine Fähigkeit einmal je Anfrage feststellen, statt an jeder Aufrufstelle.
    $this->hasVault = $registry->has('vault.secrets');
}

Komposition

Folgt auf WKCORE_REGISTER_SERVICES. Nutzlast identisch.

WKCORE_CONTEXT_BUILD

Signatur

$controller->register_hook('WKCORE_CONTEXT_BUILD', 'BEFORE', $this, 'enrichContext');
 
public function enrichContext(Event $event): void;
// $event->data === dokuwiki\plugin\wkcore\Context

Quelle: lib/plugins/wkcore/Context.php, lib/plugins/wkcore/action.php

Zusammenfassung

Hängt einen eigenen, typisierten Teilkontext an die Anfrage-Momentaufnahme, ohne dass Context Ihr Paket kennen muss.

Eingaben

$event→data ist die Context-Instanz. Sie rufen darauf setExtension(string $name, mixed $context). Der Name ist konventionell namensraumiert, etwa dwdo.project.

Lesbar sind auf derselben Instanz unter anderem user(), userName(), groups(), pageId(), namespace(), action(), language() und permission() — die aufgelöste ACL-Stufe als AUTH-Konstante.

Ausgaben

Kein Rückgabewert. Ihr Teilkontext ist anschließend über die Context-Instanz erreichbar, die get('core.context') liefert.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Träge: das Ereignis feuert erst beim ersten get('core.context'), nicht während der Registrierungsrunde. Danach ist der Kontext für die Anfrage gemerkt und wird nicht neu gebaut.
Fehlerverhalten Context::fromDokuWiki() schließt bewusst zu: fehlt das Authentifizierungssystem, ist die Berechtigung 0 und die Administratoreigenschaft falsch, statt unverdiente Rechte zu gewähren. Ein Aufruf vor DOKUWIKI_STARTED wirft nicht, liefert aber leere und anonyme Vorgaben.
Berechtigungen Keine für das Anhängen. Der Kontext berichtet Rechte, er vergibt keine. Ein Teilkontext, dessen bloßes Vorhandensein eine Befugnis bedeutete, gehört nicht hierher.
Versionierung Stabil seit 2026-06-22. Erweiterungsnamen sind additiv.

Lösen Sie aus diesem Haken heraus keine erneute Auflösung von core.context aus — das Ereignis feuert, während dieser Dienst gerade aufgelöst wird; ein erneuter Zugriff läuft in eine Rekursion.

Beispiel

public function register(EventHandler $controller)
{
    $controller->register_hook('WKCORE_CONTEXT_BUILD', 'BEFORE', $this, 'enrichContext');
}
 
public function enrichContext(Event $event)
{
    $ctx = $event->data;
    if (!is_object($ctx) || !method_exists($ctx, 'setExtension')) {
        return;
    }
    $ctx->setExtension('example.tenant', [
        'key'      => $this->resolveTenantKey($ctx->namespace()),
        'readonly' => $ctx->permission() < AUTH_EDIT,
    ]);
}

Komposition

Gefeuert von der Erzeugerfunktion des Dienstes core.context. Das DevOps-Gegenstück auf der Ebene darunter ist WKDOCORE_CONTEXT_BUILD.

WKFLUENTUI_REGISTER_CONTRIBUTIONS

Signatur

$controller->register_hook('WKFLUENTUI_REGISTER_CONTRIBUTIONS', 'BEFORE', $this, 'registerContributions');
 
public function registerContributions(Event $event): void;
// $event->data['contributions'] ist eine veränderliche Liste von Descriptor-Arrays

Quelle: lib/plugins/wkfluentui/helper/contract.php. Contract im Wortlaut: lib/plugins/wkfluentui/docs/contract-v1.md

Zusammenfassung

Das eine Registrierungsereignis, über das ein Paket alles beiträgt, was im Workbench sichtbar wird: Abzeichen, Ansichten, Befehle, Statusleisteneinträge, Module — und die Erklärung, dass es seine Schale selbst zeichnet.

Eingaben

Sie hängen Descriptor-Arrays an $event→data['contributions'] an. Pflicht sind nur owner und id.

Feld Typ Vorgabe Bedeutung
owner string Ihr Paketname. Wird nie erraten: ein gefeuertes PHP-Ereignis kennt den laufenden Haken nicht.
id string Lokale Kennung. Wird automatisch mit dem Eigentümer namensraumiert.
kind view, action, route oder null null null heißt: reine Ankündigung ohne Inhalt.
region siehe unten leer Pflicht, sobald kind gesetzt ist.
label string leer Beschriftung.
icon string leer Symbolkennung.
order int 100 Sortierschlüssel, aufsteigend.
surface contributed oder selfHosted contributed selfHosted: der Bildschirm zeichnet seine Schale selbst und darf nicht ein zweites Mal eingefasst werden.
html string oder null null Inhalt einer Ansicht.
href string leer Ziel eines Befehls.
form, name, value string leer Für einen Befehl, der ein bestehendes Formular abschickt.
moduleId string leer Für die Region adminModule.
permission callable oder null null „Darf diese Sitzung das überhaupt sehen?„
enabled callable oder null null „Ist es gerade ausführbar?“
disabledReason string leer Der Satz, den ein ausgegrautes Bedienelement seinem Leser schuldet. Klartext.
requiresIdentity bool false Fordert einen Identitätsnachweis.
capabilities string oder Liste leer Was dieser Beitrag vom Wirt verlangt.
provides string oder Liste leer Was sein Eigentümer für alle anderen als wahr zusichert.
badge int, Array oder null null Felder count, tone und label.

Regionen (geschlossenes Vokabular): activityBar, sidebar, auxbar, bottomPanel, statusBar, commandPalette, adminModule.

Zwei Regionen setzen etwas beim Eigentümer voraus, nicht beim Descriptor. activityBar (und das badge, das denselben Eintrag schmückt) und adminModule verlangen, dass der Eigentümer ein Admin-Plugin ist. Die Aktivitätsleiste wird aus plugin_list('admin') gebaut, das Modulverzeichnis löst den Wirt mit plugin_load('admin', $owner) auf. Ein Beitrag eines Nicht-Admin-Pakets wird hier trotzdem angenommen, erscheint im Register — und zeichnet nichts.

Ausgaben

Kein Rückgabewert. Das Ergebnis ist über helper_plugin_wkfluentui_contract lesbar:

Methode Rückgabe
all() Kennung auf Descriptor, in Anzeigereihenfolge
forRegion(string $region) Beiträge einer Region, in Anzeigereihenfolge
badgeFor(string $owner) Abzeichenwerte oder null
ownsItsShell(string $owner) bool
commandsForScreen(string $owner) Befehle für einen Bildschirm

Jeder normalisierte Eintrag trägt zusätzlich source (native oder legacy) und disabled.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Einmal je Anfrage beim ersten all(); danach für die Anfrage gemerkt. Zuerst werden die aus den fünf veralteten Mechanismen erzeugten Einträge angehängt, dann die des Ereignisses — deshalb bedeutet „der spätere gewinnt„ zugleich „nativ schlägt veraltet“. Das Ereignis zu verhindern hat keine Bedeutung und wird nicht beachtet; eine leere Beitragsliste drückt man durch Nichtanhängen aus.
Fehlerverhalten Fehlender Eigentümer oder fehlende Kennung: still verworfen — was anonym ist, kann nie wieder gefunden, ersetzt oder entfernt werden. Unbekanntes kind: still verworfen (fehlerhaft, nicht kleiner). kind gesetzt, region fehlt oder unbekannt: still verworfen. permission wirft oder ist nicht aufrufbar: verweigert, nicht protokolliert. enabled wirft oder ist nicht aufrufbar: nicht ausführbar. Ein wirklicher Fehler kostet diesen einen Beitrag, nie die Anfrage; er wird protokolliert, damit er auffindbar bleibt statt für immer still zu fehlen.
Berechtigungen Drei Stufen, in dieser Reihenfolge. Erstens: Ist der Eigentümer ein installiertes Admin-Plugin, entscheidet dessen eigenes isAccessibleByCurrentUser(); scheitert das, entfällt der Beitrag. Zweitens: permission ist zusätzlich zur ACL, nie ein Ersatz — der darunterliegende Bildschirm bleibt durch DokuWikis ACL geschützt. Schließt zu: ein Wert, der weder null noch aufrufbar ist, verweigert. Drittens: enabled entscheidet danach nur noch, ob das sichtbare Bedienelement betätigt werden kann. Unsichtbar schlägt untätig: was diese Sitzung nicht haben darf, darf nicht als ausgegrautes Element erscheinen — ein ausgegrautes Element verrät immer noch, dass der Befehl existiert. Ein Paket ohne eigenen Admin-Bildschirm hat keine solche Prüfung und muss permission selbst mitliefern.
Versionierung v1. Fähigkeitsname workbench.contract.v1. Regionsnamen tragen keine Version: ein unbekannter Wert wird verworfen, nie fatal, was additives Wachstum ohne Versionssprung aufnimmt. Namen mit der Endung .v1 markieren, wo eine unverträgliche zweite Fassung denkbar ist. Bei gleicher Kennung gewinnt der spätere Eintrag.

Beispiel

Vollständige action-Komponente. Sie registriert ein Abzeichen, eine Ansicht und einen Befehl und ist ohne weitere Datei lauffähig, sobald das Paket ein plugin.info.txt und — wegen des Abzeichens — eine admin.php hat.

<?php
 
use dokuwiki\Extension\ActionPlugin;
use dokuwiki\Extension\EventHandler;
use dokuwiki\Extension\Event;
 
class action_plugin_wkexample extends ActionPlugin
{
    public function register(EventHandler $controller)
    {
        $controller->register_hook(
            'WKFLUENTUI_REGISTER_CONTRIBUTIONS', 'BEFORE', $this, 'registerContributions'
        );
    }
 
    public function registerContributions(Event $event)
    {
        $event->data['contributions'][] = [
            'owner' => 'wkexample',
            'id'    => 'badge',
            'badge' => ['count' => 3, 'tone' => 'warning', 'label' => $this->getLang('badge_label')],
        ];
 
        $event->data['contributions'][] = [
            'owner'  => 'wkexample',
            'id'     => 'sidebar',
            'kind'   => 'view',
            'region' => 'sidebar',
            'label'  => $this->getLang('view_title'),
            'html'   => '<p>' . hsc($this->getLang('view_body')) . '</p>',
        ];
 
        $event->data['contributions'][] = [
            'owner'          => 'wkexample',
            'id'             => 'command',
            'kind'           => 'action',
            'region'         => 'commandPalette',
            'label'          => $this->getLang('command_label'),
            'href'           => DOKU_BASE . 'lib/plugins/wkexample/plugin.info.txt',
            // Sichtbar nur für Angemeldete …
            'permission'     => static fn(): bool => isset($_SERVER['REMOTE_USER']),
            // … und betätigbar nur, wenn es etwas zu tun gibt.
            'enabled'        => fn(): bool => $this->pendingCount() > 0,
            'disabledReason' => $this->getLang('nothing_pending'),
        ];
    }
 
    private function pendingCount(): int
    {
        return 3;
    }
}

Die vollständig gebaute Fassung liegt als installierbares Paket im Baum: lib/plugins/wkfluentuicontracttest/action.php.

Komposition

Gelesen von den Zeichnern des Workbench, die selbst kein Contract sind. Vorher abzufragen mit helper_plugin_wkfluentui_capabilities. Löst die veralteten Mechanismen ab.

helper_plugin_wkfluentui_capabilities

Signatur

$caps = plugin_load('helper', 'wkfluentui_capabilities');
 
public function has(string $capability): bool;
public function list(): array;   // string[]

Quelle: lib/plugins/wkfluentui/helper/capabilities.php

Zusammenfassung

Beantwortet mit einem Namen, ob der Wirt eine Fähigkeit tatsächlich erbringt — die Frage, die weder eine Versionsnummer noch method_exists() beantworten kann.

Eingaben

Ein Fähigkeitsname. Das Kernvokabular dieser Fassung:

  • workbench.contract.v1
  • workbench.contribution.activityBar
  • workbench.contribution.sidebar
  • workbench.contribution.auxbar
  • workbench.contribution.bottomPanel
  • workbench.contribution.statusBar
  • workbench.contribution.adminModule
  • workbench.command.registry
  • workbench.command.palette
  • workbench.identity.requirement.v1
  • workbench.surface.selfHosted

Dazu kommt alles, was ein aktives Paket über das Feld provides seines Descriptors zusichert.

Ausgaben

has() liefert bool. list() liefert alle derzeit angebotenen Namen, entdoppelt.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Das Kernvokabular ist eine feste Eigenschaft der installierten Fassung und wird nicht je Anfrage berechnet. Die zugesicherten Namen kommen aus dem Beitragsregister und werden je Anfrage einmal zusammengeführt und gemerkt.
Fehlerverhalten plugin_load() liefert null, wenn wkfluentui fehlt. Prüfen Sie darauf; behandeln Sie null als „Fähigkeit nicht vorhanden„. Ein unbekannter Name ist kein Fehler, sondern false.
Berechtigungen Keine. Die Antwort betrifft den Wirt, nicht die Sitzung. Sie ist keine Aussage darüber, ob der Lesende etwas benutzen darf.
Versionierung v1. Namen mit der Endung .v1 bleiben für immer wahrheitsgemäß beantwortbar, auch wenn eine zweite Fassung daneben entsteht. Neue Namen sind additiv.

Beispiel

public function registerContributions(Event $event)
{
    $caps = plugin_load('helper', 'wkfluentui_capabilities');
    $hasPanel = $caps !== null && $caps->has('workbench.contribution.bottomPanel');
 
    $event->data['contributions'][] = [
        'owner'  => 'wkexample',
        'id'     => $hasPanel ? 'panel' : 'sidebar',
        'kind'   => 'view',
        'region' => $hasPanel ? 'bottomPanel' : 'sidebar',
        'label'  => $this->getLang('view_title'),
        'html'   => $this->renderView(),
        // Eine eigene Zusicherung für andere Pakete:
        'provides' => ['example.reports.v1'],
    ];
}

Komposition

Liest das Ergebnis von WKFLUENTUI_REGISTER_CONTRIBUTIONS mit. Das inhaltliche Gegenstück bei der Identität ist helper_plugin_wkidentity_contract::supports(), beim Antragswesen helper_plugin_wkrequest::supports().

helper_plugin_wkidentity_contract — Identity Contract v1

Signatur

$identity = plugin_load('helper', 'wkidentity_contract');
 
public function currentIdentity(): array;      // login, name, mail, groups
public function currentAssurance(): string;    // NONE, AUTHENTICATED, TOTP, MAIL_OTP
public function supports(string $capability): bool;
public function satisfies(?string $requirement, ?array $scope = null): bool;
public function require(string $requirement, ?array $scope, array $returnTarget): bool;

Quelle: lib/plugins/wkidentity/helper/contract.php. Contract im Wortlaut: lib/plugins/wkidentity/docs/contract-v1.md

Zusammenfassung

Sagt, wer die Sitzung ist und was sie in dieser Sitzung bewiesen hat, und fordert bei Bedarf eine höhere Nachweisgüte an, bevor eine empfindliche Aktion läuft.

Eingaben

Parameter Werte
Forderung AUTHENTICATED, TOTP, MAIL_OTP, MFA_ANY. Bei satisfies() zusätzlich null, um allein die Richtlinie entscheiden zu lassen.
Geltungsbereich Ein Array mit type und id, oder null. Benennt die Ressource für die Richtlinienabfrage, etwa Typ adminAction mit der Kennung wkexample:remove.
Rückkehrziel Typ page mit einer bereinigten Seiten-ID, wahlweise mit act (einem Wort aus Kleinbuchstaben); oder Typ workbench mit einer bestehenden Contract-v1-Beitragskennung. Keine URL, und es nimmt auch keine an.
Fähigkeit identity.authentication, identity.stepup, identity.factor.totp, identity.factor.mailOtp, identity.assurance.anyMfa.

Ausgaben

Methode Rückgabe
currentIdentity() Anmeldename, Anzeigename, Adresse und Gruppen. Bei anonymer Sitzung leere Werte, nie fehlende Schlüssel — das Ergebnis ist bedingungslos zerlegbar.
currentAssurance() Der höchste in dieser Sitzung bewiesene Stand.
satisfies() bool, ohne Nebenwirkung.
require() true, wenn bereits erfüllt (nichts geschieht). Sonst false — entweder nach einer Umleitung in die Zusatzprüfung, aus der die Steuerung normalerweise nicht zurückkehrt, oder ohne jede Nebenwirkung, wenn die Forderung nie erfüllbar ist.

Anmerkungen

Aspekt Verhalten
Lebenszyklus require() schreibt eine schwebende Aufgabe in die Sitzung und leitet um. Nach bestandener Prüfung wird die ursprüngliche Anfrage neu gestellt, nicht fortgesetzt: kein wiederaufgenommener Aufruf, kein gespeicherter Stapel. Der erreichte Stand lebt bis zu seinem Ablaufzeitpunkt oder bis zur Abmeldung und gilt dann für jede gleich starke oder schwächere Forderung. Rufen Sie require() nur aus ACTION_ACT_PREPROCESS — aus TPL_ACT_UNKNOWN heraus schlägt die Umleitung fehl, weil die Ausgabe bereits läuft.
Fehlerverhalten Schließt zu. Eine unbekannte Forderungszeichenkette ist ein fehlerhafter Aufrufer, kein abwesender: sie wird verweigert. Eine defekte oder unlesbare Richtliniendatei wird protokolliert und trägt nichts bei — Ihre eigene ausdrückliche Forderung bleibt unberührt und wird weiter durchgesetzt. AUTHENTICATED wird nie hochgestuft: eine anonyme Sitzung anzumelden ist Aufgabe von DokuWikis eigener Verweigerung. Vor jeder Zusatzprüfung wird geprüft, ob das Konto sie überhaupt bestehen könnte — es wird nie in eine aussichtslose Prüfung geschickt.
Berechtigungen Dieser Contract ist keine zweite Berechtigungsinstanz. Er sagt, was bewiesen wurde; wer was darf, entscheidet Ihr Paket. Nachweisgüte und Befugnis bleiben getrennt. Die tatsächlich geprüfte Güte ist die strengere aus Ihrer Angabe und der hinterlegten Richtlinie: Richtlinie kann erhöhen, nie senken.
Versionierung v1, fünf Methoden, feste Fähigkeitsliste. MFA_ANY ist dauerhaft nur eine Forderung und nie ein erreichter Stand — ein Konto hat genau ein hinterlegtes Verfahren. Die Trennung zwischen hinterlegt (Konto, zeitlos) und bewiesen (Sitzung, mit Ablauf) ist Bestandteil des Contracts und wird nicht zusammengeführt.

Beispiel

private const CATEGORIES = ['secret-write' => 'MFA_ANY', 'remove' => 'MFA_ANY'];
 
/** Aus ACTION_ACT_PREPROCESS gerufen. @return string|null Sprachschlüssel einer Absage */
private function decide(): ?string
{
    global $INPUT, $ID;
 
    $category = $INPUT->str('category');
    if (!isset(self::CATEGORIES[$category])) {
        return 'stepup_bad_category';
    }
 
    $identity = plugin_load('helper', 'wkidentity_contract');
    if ($identity === null) {
        send_redirect($this->backUrl());   // Paket abwesend: nichts zu beweisen
        return null;
    }
 
    $scope  = ['type' => 'adminAction', 'id' => 'wkexample:' . $category];
    $target = ['type' => 'page', 'id' => (string) $ID, 'act' => 'wkexample'];
 
    if ($identity->require(self::CATEGORIES[$category], $scope, $target)) {
        send_redirect($this->backUrl());   // war schon erfüllt
        return null;
    }
    // Nur der unerfüllbare Fall kommt hier an: kein passender Faktor hinterlegt.
    return 'stepup_cannot';
}
 
/** An der schreibenden Stelle noch einmal fragen — ohne Nebenwirkung. */
private function mayWrite(string $category): bool
{
    if (!$this->userHoldsRole('maintainer')) {
        return false;   // Ihre eigene Berechtigung zuerst
    }
    $identity = plugin_load('helper', 'wkidentity_contract');
    return $identity === null
        || $identity->satisfies('MFA_ANY', ['type' => 'adminAction', 'id' => 'wkexample:' . $category]);
}

Schritt für Schritt, mit der Sackgassenbehandlung: How-To: eine Aktion hinter einen zweiten Faktor stellen.

Komposition

Gebaute Vorbilder im Baum: lib/plugins/wkdoado/StepUpBridge.php, lib/plugins/wkblog/StepUpBridge.php, lib/plugins/wkdoadogit/AdminSupport.php. Der Ausweg für ein Konto ohne Faktor ist helper_plugin_wkrequest::stepUpPanel().

helper_plugin_wkrequest — Admin Request Contract v1

Signatur

$req = plugin_load('helper', 'wkrequest');
 
public function supports(string $capability): bool;
public function available(string $type): bool;
public function urlFor(array $draft): string;
public function requestAction(array $draft, string $labelKey = '', ?object $labels = null): array;
public function signInAction(string $returnTo = ''): array;
public function panel(array $spec): string;
public function accessPanel(array $spec): string;
public function stepUpPanel(array $spec): string;
public function permissionLabel(int $level): string;
public function currentPermissionLabel(string $id): string;
public function assuranceLabel(string $requirement): string;

Quelle: lib/plugins/wkrequest/helper.php

Zusammenfassung

Macht aus dem Satz „wenden Sie sich an Ihren Administrator“ ein Bedienelement: einen strukturierten, auswertbaren Antrag, den ein Mensch von genau dem Bildschirm aus stellt, der ihn ausgesperrt hat.

Eingaben

Ein Entwurf — nur type ist Pflicht:

[
    'type'          => 'REPOSITORY_ACCESS_REQUEST',
    'owner'         => 'wkexample',
    'handler'       => 'wkexample:mounts',
    'resource'      => 'example:repo/wkcore',
    'resourceLabel' => 'Beispiel-Repositorium wkcore',
    'reasonCode'    => 'acl_denied',
    'context'       => ['namespace' => $ns],
]

Antragsarten: ACCESS_REQUEST, MFA_RESET_REQUEST, ROLE_REQUEST, REPOSITORY_ACCESS_REQUEST, REPOSITORY_MOUNT_REQUEST, ACCOUNT_SUPPORT_REQUEST, ADMIN_ACTION_REQUEST. Ohne Sitzung sind nur MFA_RESET_REQUEST und ACCOUNT_SUPPORT_REQUEST stellbar.

Fähigkeiten: request.raise, request.anonymous, request.inbox, request.notify, request.panel.

accessPanel() nimmt Tatsachen, nie Bedienelemente: resourceLabel, required, current, facts, draft, alternatives, selfservice, back. Welche Bedienelemente daraus folgen, in welcher Reihenfolge und ob sie überhaupt anbietbar sind, entscheidet die Methode.

Ausgaben

Methode Rückgabe
urlFor() Absolute URL des vorbefüllten Formulars, oder leer, wenn der Entwurf keine gültige Art nennt.
requestAction(), signInAction() Ein Eintrag für panel(), mit Verfügbarkeitsmerker.
panel(), accessPanel(), stepUpPanel() HTML.
permissionLabel(), currentPermissionLabel(), assuranceLabel() Benennung in der Sprache des Lesenden.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Der Entwurf wird signiert in die URL codiert, damit der Posteingang unterscheiden kann, was das Wiki beobachtet hat, und was die antragstellende Person getippt hat. Das Formular liegt unter do=wkrequest. Erledigte Anträge werden über INDEXER_TASKS_RUN abgeräumt.
Fehlerverhalten Baut ab, bricht nie. Fehlt der Speicher, liefert available() false, die Tafel lässt den Eintrag weg, und der aufrufende Bildschirm zeichnet genau so, wie er vor diesem Paket gezeichnet hat. Ein nicht stellbarer Antrag ergibt einen Eintrag, den die Tafel verwirft — Sie brauchen keine eigene Verzweigung, sondern hängen den Eintrag bedingungslos an.
Berechtigungen Ein Antrag ist nie eine Befugnis. Alles, was dieser Contract schreibt, ist der Vermerk, dass jemand gefragt hat. Er gewährt nichts, hebt keinen zweiten Faktor auf, schreibt keine ACL-Regel und wird von keiner Prüfstelle gelesen. Deshalb löst handlers() auf einen Link zu dem Bildschirm auf, der die Sache entscheiden kann, und nicht auf eine Schaltfläche „gewähren„.
Versionierung v1, feste Fähigkeitsliste. request.anonymous und request.notify bilden zusätzlich die Konfiguration ab, nicht nur den Code: wer anonyme Anträge abschaltet, hat die Fähigkeit tatsächlich entfernt. Neue Antragsarten sind additiv. Die Abhängigkeitsrichtung ist einseitig: dieses Paket kennt keines der Pakete, die es benutzen — ein siebtes hinzuzunehmen ändert hier keine Datei.

Beispiel

Aus einer verschlossenen Tür wird ein Antrag samt Auswegen.

/** Zeichnet die vollständige Absage: warum nicht, und was jetzt. */
private function renderDenied(string $repo, string $ns): string
{
    $req = plugin_load('helper', 'wkrequest');
    if ($req === null) {
        return '<p>' . hsc($this->getLang('denied_plain')) . '</p>';
    }
 
    return $req->accessPanel([
        'title'         => $this->getLang('denied_title'),
        'resourceLabel' => $repo,
        'required'      => $req->permissionLabel(AUTH_READ),
        'current'       => $req->currentPermissionLabel($ns),
        'draft'         => [
            'type'          => 'REPOSITORY_ACCESS_REQUEST',
            'owner'         => 'wkexample',
            'handler'       => 'wkexample:mounts',
            'resource'      => $repo,
            'resourceLabel' => $repo,
            'reasonCode'    => 'acl_denied',
            'context'       => ['namespace' => $ns],
        ],
        'back'          => wl($ns, [], true, '&'),
    ]);
}

Anmelden, „meine offenen Anträge“ und der Rückweg werden von accessPanel() selbst angehängt: sie sind Eigenschaften der Sitzung, nicht des Bildschirms — ein Bildschirm, der sie sich merken müsste, vergisst einen davon.

Komposition

Sie sollten zusätzlich WKREQUEST_REGISTER_HANDLERS bedienen, damit der Posteingang weiß, wo Ihr Antrag entschieden wird. Für die Absage wegen eines fehlenden zweiten Faktors ist stepUpPanel() die richtige Tafel.

WKREQUEST_REGISTER_HANDLERS

Signatur

$controller->register_hook('WKREQUEST_REGISTER_HANDLERS', 'BEFORE', $this, 'registerRequestHandlers');
 
public function registerRequestHandlers(Event $event): void;
// $event->data['handlers'] ist eine veränderliche Liste

Quelle: lib/plugins/wkrequest/helper.php

Zusammenfassung

Erklärt, welcher Bildschirm eine bestimmte Art von Antrag tatsächlich entscheiden kann.

Eingaben

Feld Typ Pflicht Bedeutung
owner string ja Ihr Paketname.
id string ja Lokale Kennung; ergibt zusammen mit dem Eigentümer den Schlüssel.
url string ja Absolute URL des entscheidenden Bildschirms.
label string nein Beschriftung; Vorgabe ist Eigentümer und Kennung.
types Liste nein Antragsarten, die dieser Bildschirm erledigen kann. Unbekannte Werte werden herausgefiltert.

Ausgaben

Kein Rückgabewert. Lesbar über handlers(), handler(string $key) und handlersForType(string $type).

Anmerkungen

Aspekt Verhalten
Lebenszyklus Einmal je Anfrage bei der ersten Auflösung; danach gemerkt.
Fehlerverhalten Ein Eintrag ohne Eigentümer, Kennung oder URL wird still übergangen. Wirft die Ereignisverteilung, wird das protokolliert und die Auflösung liefert eine leere Liste — der Posteingang bietet dann keine Abkürzung, statt zu scheitern.
Berechtigungen Keine für die Registrierung. Der genannte Bildschirm prüft seine Rechte selbst, wenn er geöffnet wird.
Versionierung v1. types ist beratend: Es steuert die Liste „Bildschirme, die so etwas entscheiden können„ für Zeilen, deren eigener Schlüssel nicht mehr auflösbar ist. Gespeichert wird der Schlüssel, nicht die URL — deshalb löst ein Antrag eines inzwischen entfernten Pakets sauber ins Leere auf, statt auf eine tote Adresse zu zeigen.

Beispiel

public function register(EventHandler $controller)
{
    // Ausgeschrieben statt über die Konstante: wkrequest ist optional, und eine
    // Klasse zu nennen, die fehlen kann, nähme register() im Ganzen mit.
    $controller->register_hook('WKREQUEST_REGISTER_HANDLERS', 'BEFORE', $this, 'registerRequestHandlers');
}
 
public function registerRequestHandlers(Event $event)
{
    if (!is_array($event->data) || !isset($event->data['handlers'])) {
        return;
    }
    $label = (string) $this->getLang('menu');
    $event->data['handlers'][] = [
        'owner' => 'wkexample',
        'id'    => 'mounts',
        'label' => $label !== '' ? $label : 'Example mounts',
        'url'   => wl('', ['do' => 'admin', 'page' => 'wkexample', 'area' => 'mounts'], true, '&'),
        'types' => ['REPOSITORY_ACCESS_REQUEST', 'REPOSITORY_MOUNT_REQUEST'],
    ];
}

Benennen Sie den Bildschirm, der die Sache wirklich entscheidet. Liest Ihr Paket seine Rechte aus einer eigenen Matrix und nicht aus DokuWiki-Gruppen, dann ändert ein Eintrag in der Benutzerverwaltung daran nichts — und die Benutzerverwaltung ist dann die falsche Adresse.

Komposition

Gegenstück zum Admin Request Contract.

Teil B: Experimentelle Erweiterungspunkte

Die Punkte in diesem Teil sind benutzbar und werden benutzt, tragen aber im Katalog ihres Trägers die Stufe experimental: Signatur oder Nutzlast können sich ohne Abkündigungszyklus ändern.

WKDOCORE_REGISTER_HUBS

Signatur

$controller->register_hook('WKDOCORE_REGISTER_HUBS', 'BEFORE', $this, 'registerHubs');
 
public function registerHubs(Event $event): void;
// $event->data === dokuwiki\plugin\wkdocore\Domain\HubRegistry

Quelle: lib/plugins/wkdocore/Domain/HubRegistry.php, lib/plugins/wkdocore/Model/Hub.php

Zusammenfassung

Fügt der Projektnavigation einen eigenen Hub hinzu oder ersetzt einen der mitgelieferten.

Eingaben

$event→data→register($hub). Der Hub ist unveränderlich:

Parameter Typ Bedeutung
id string Stabile Kennung; zugleich Schlüssel im Register.
labelKey string Sprachschlüssel, nicht der Beschriftungstext.
icon string Voreingetragenes Symbolkürzel.
order int Sortierschlüssel. Die mitgelieferten Hubs benutzen Zehnerschritte, damit ein eigener Hub dazwischenpasst, ohne dass umnummeriert werden muss.
routeSub string Pfadsegment, das an den Projektnamensraum angehängt wird.
requiredLevel int ACL-Mindeststufe.
minRole string Projektrolle: admin, maintainer, contributor, reader, none.
flag string Merkmalsschalter, etwa hub.boards.
iconClass string Rohe CSS-Symbolklasse; hat Vorrang vor icon, wenn gesetzt. So bringt ein eigenes Paket sein Symbol mit, ohne dass ein Kürzel eingetragen werden muss.
act string Die do=-Aktion, der der Bildschirm gehört. Leer heißt: eine gewöhnliche Seite.

Ausgaben

Kein Rückgabewert. Das Register liefert danach all() (in Anzeigereihenfolge), get(string $id) und ids().

Anmerkungen

Aspekt Verhalten
Lebenszyklus Einmal je Anfrage, während der Dienst dwdo.hubs gebaut wird — also träge, beim ersten Zugriff. Registrieren Sie aus einem BEFORE-Haken. Ein AFTER-Haken bekäme technisch dieselbe, noch nicht zurückgegebene Instanz, das ist aber nicht Teil des zugesicherten Verhaltens.
Fehlerverhalten Über das hinaus, was der Konstruktor erzwingt, findet keine Prüfung statt. Bei gleicher Kennung gewinnt der spätere Eintrag: ein eigener Hub mit der Kennung eines mitgelieferten ersetzt diesen, ohne Fehler und ohne stilles Übergehen.
Berechtigungen Das Register filtert nicht. all() enthält auch Hubs, die die lesende Person nicht sehen darf; die Sichtbarkeit entscheidet ein eigener Auflöser. Die ACL-Stufe und die Projektrolle werden beide geprüft, unabhängig voneinander, als gestaffelte Absicherung.
Versionierung Experimentell seit 2026-06-23. Acht Hubs sind mitgeliefert. Derzeit registriert kein Paket der Suite einen eigenen Hub — die DevOps-Bildschirme füllen die mitgelieferten Hubs über ihre Aktionen. Sie sind damit voraussichtlich der erste Nutzer dieses Punktes; rechnen Sie mit Nachschärfungen.

Beispiel

use dokuwiki\plugin\wkdocore\Model\Hub;
use dokuwiki\plugin\wkdocore\Model\Role;
 
public function register(EventHandler $controller)
{
    $controller->register_hook('WKDOCORE_REGISTER_HUBS', 'BEFORE', $this, 'registerHubs');
}
 
public function registerHubs(Event $event)
{
    $registry = $event->data;
    if (!is_object($registry) || !method_exists($registry, 'register')) {
        return;
    }
 
    $registry->register(new Hub(
        'releases',                  // id
        'hub_releases',              // Sprachschlüssel, nicht der Text
        '',                          // kein voreingetragenes Kürzel …
        25,                          // … zwischen boards (20) und repos (30)
        'releases',                  // Route innerhalb des Projekts
        AUTH_READ,
        Role::CONTRIBUTOR,
        'hub.releases',
        'codicon codicon-rocket',    // eigenes Symbol, ohne Kürzeleintrag
        'wkexample_releases'         // die Aktion, die den Bildschirm füllt
    ));
}

Komposition

Der Hub erscheint in der Navigation, die helper_plugin_wkdocore::renderHubNav() zeichnet. Der Kontext, aus dem die Sichtbarkeit stammt, entsteht über WKDOCORE_CONTEXT_BUILD.

WKDOCORE_CONTEXT_BUILD

Signatur

$controller->register_hook('WKDOCORE_CONTEXT_BUILD', 'BEFORE', $this, 'enrichDwdoContext');
 
public function enrichDwdoContext(Event $event): void;
// $event->data === dokuwiki\plugin\wkdocore\Model\DwdoContext

Quelle: lib/plugins/wkdocore/Model/DwdoContext.php

Zusammenfassung

Hängt einen eigenen Teilkontext an den DevOps-Kontext, nachdem Projekt, Rolle, sichtbare Hubs und Merkmalsschalter feststehen.

Eingaben

$event→data→setExtension(string $name, mixed $value).

Ausgaben

Kein Rückgabewert.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Einmal je Anfrage, sobald der DevOps-Teilkontext fertig zusammengesetzt ist. Beide Phasen, BEFORE und AFTER, sind vorgesehen.
Fehlerverhalten Fehlt wkcore oder ist der Dienst dwdo.context nie registriert worden, entsteht kein Kontext und das Ereignis feuert nicht. Aufrufer behandeln „kein Kontext“ und „Paket abwesend„ einheitlich und zeichnen nichts.
Berechtigungen Keine für das Anhängen. Der Kontext berichtet die bereits entschiedene Sichtbarkeit; er entscheidet sie nicht.
Versionierung Experimentell seit 2026-06-23.

Lösen Sie aus diesem Haken heraus keine erneute Auflösung von core.context oder dwdo.context aus — das Ereignis feuert, während diese Dienste gerade aufgelöst werden.

Beispiel

public function enrichDwdoContext(Event $event)
{
    $ctx = $event->data;
    if (!is_object($ctx) || !method_exists($ctx, 'setExtension')) {
        return;
    }
    $project = method_exists($ctx, 'project') ? $ctx->project() : null;
    if ($project === null) {
        return;   // außerhalb eines Projekts gibt es nichts anzuhängen
    }
    $ctx->setExtension('example.releases', [
        'projectKey' => $project->key,
        'open'       => $this->countOpenReleases($project->key),
    ]);
}

Komposition

Die Ebene darüber ist WKCORE_CONTEXT_BUILD.

WKDOCORE_PROJECT_DISCOVER

Signatur

$controller->register_hook('WKDOCORE_PROJECT_DISCOVER', 'BEFORE', $this, 'discoverProjects');
 
public function discoverProjects(Event $event): void;
// $event->data hat die Schlüssel 'keys' und 'root'

Quelle: lib/plugins/wkdocore/Domain/AggregateProjectDirectory.php

Zusammenfassung

Steuert Projektschlüssel zur Kandidatenliste des Projektverzeichnisses bei.

Eingaben

$event→data['keys'] ist veränderlich; hängen Sie Ihre Schlüssel an. $event→data['root'] ist der Projektwurzel-Namensraum der aktiven Sprache und wird nur zum Lesen mitgegeben, damit ein Haken sieht, wohin ein Schlüssel ohne eigenen Namensraum geleitet würde.

Ausgaben

Kein Rückgabewert.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Beim Zusammenstellen der Kandidatenliste, einmal je Auflösung des Verzeichnisses.
Fehlerverhalten Nichtzeichenketten und Doppelte werden bei der weiteren Verarbeitung aussortiert. Lösen Sie aus diesem Haken heraus keine Auflösung von dwdo.projectdirectory aus — dieser Dienst feuert das Ereignis, ein erneuter Zugriff läuft in eine Rekursion.
Berechtigungen Dieses Ereignis gewährt nichts. Ein hier angehängter Schlüssel wird ein Kandidat, und jeder Kandidat wird anschließend derselben Sichtbarkeitsregel unterworfen wie jeder andere. Ein Haken kann damit kein Projekt sichtbar machen, von dem der Aufrufer nichts erfahren darf. Genau weil Beitragen folgenlos ist, braucht die Entdeckung keine Befugnis und darf ein offenes Ereignis sein.
Versionierung Experimentell seit 2026-08-23.

Beispiel

public function discoverProjects(Event $event)
{
    if (!is_array($event->data) || !isset($event->data['keys'])) {
        return;
    }
    foreach ($this->externalProjectKeys() as $key) {
        $event->data['keys'][] = $key;
    }
}

Komposition

Speist das Projektverzeichnis, das als Dienst dwdo.projectdirectory im Register steht.

WKDOCORE_REGISTER_REPOSITORY_SOURCES

Signatur

$controller->register_hook('WKDOCORE_REGISTER_REPOSITORY_SOURCES', 'BEFORE', $this, 'registerRepoSources');
 
public function registerRepoSources(Event $event): void;
// $event->data === dokuwiki\plugin\wkdocore\Domain\RepositoryDirectory

Quelle: lib/plugins/wkdocore/Domain/RepositorySource.php

Zusammenfassung

Meldet ein Repositoriums-Backend beim projektübergreifenden Verzeichnis an.

Eingaben

$event→data→register($source) mit einer Umsetzung von RepositorySource:

interface RepositorySource extends \dokuwiki\plugin\wkcore\Contract
{
    public function id(): string;
    /** @return RepositoryDescriptor[] */
    public function repositories(string $projectKey = ''): array;
}

Ausgaben

Kein Rückgabewert.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Einmal je Anfrage beim Bau des Dienstes dwdo.repodirectory.
Fehlerverhalten Bei gleicher Quellkennung gewinnt der spätere Eintrag — dieselbe Regel wie bei den Hubs.
Berechtigungen Die Quelle liefert, was sie kennt; die Sichtbarkeitsprüfung liegt beim Verzeichnis und bei den Bildschirmen, die es lesen. Prüfen Sie in repositories() dennoch selbst, was Ihr Backend nicht preisgeben darf: das Verzeichnis ist keine Zugangsschranke.
Versionierung Experimentell seit 2026-08-23. RepositorySource erbt vom Markierungsinterface \dokuwiki\plugin\wkcore\Contract, das selbst keine Methoden deklariert — es macht eine Schnittstelle als Suite-Contract typprüfbar, mehr nicht.

Das Anmelden ist zugleich das Einschalten: Das Plugin zu installieren genügt, und es zu entfernen nimmt seine Repositorien aus jeder Liste, jeder Zählung und jeder Suche, ohne dass irgendwo ein Rest darauf zeigt.

Beispiel

use dokuwiki\plugin\wkdocore\Domain\RepositorySource;
use dokuwiki\plugin\wkdocore\Model\RepositoryDescriptor;
 
final class ExampleRepositorySource implements RepositorySource
{
    public function id(): string
    {
        return 'example';
    }
 
    /** @return RepositoryDescriptor[] */
    public function repositories(string $projectKey = ''): array
    {
        $out = [];
        foreach ($this->backend->list($projectKey) as $row) {
            $out[] = new RepositoryDescriptor(/* … */);
        }
        return $out;
    }
}
 
// in der action-Komponente:
public function registerRepoSources(Event $event)
{
    $directory = $event->data;
    if (is_object($directory) && method_exists($directory, 'register')) {
        $directory->register(new ExampleRepositorySource());
    }
}

Komposition

Das Verzeichnis steht als dwdo.repodirectory im Dienstregister.

WKSOURCEVIEW_REGISTER_PROVIDERS

Signatur

$controller->register_hook('WKSOURCEVIEW_REGISTER_PROVIDERS', 'BEFORE', $this, 'registerProviders');
 
public function registerProviders(Event $event): void;
// $event->data === dokuwiki\plugin\wksourceview\Provider\SourceProviderRegistry

Quelle: lib/plugins/wksourceview/Events.php, lib/plugins/wksourceview/Provider/WkSourceProvider.php

Zusammenfassung

Liefert Quelltext aus einem eigenen Backend an {{source>…}} und an die Quelltext-Schublade.

Eingaben

$event→data→register(string $scheme, $provider):

interface WkSourceProvider extends \dokuwiki\plugin\wkcore\Contract
{
    public function scheme(): string;
    public function supports(SourceRef $ref): bool;
    public function fetch(SourceRef $ref, string $pageId): SourceDocument;
}

Ausgaben

fetch() liefert immer ein SourceDocument, nie null. Ein Misserfolg wird über dessen Status ausgedrückt, nicht über eine Ausnahme.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Träge: einmal je Anfrage, beim ersten Zugriff auf den Dienst source.providers. Registrieren Sie im BEFORE-Haken.
Fehlerverhalten Ein Misserfolg ist ein SourceDocument mit abweichendem Status — nicht null, keine Ausnahme. Bei gleichem Schema gewinnt der spätere Eintrag.
Berechtigungen fetch() ist die einzige Stelle im ganzen Paket, die etwas lesen darf, und sie trägt die volle Prüfkette selbst: Abweisen von Pfadausbrüchen, Auflösen der schema-eigenen Kennung, ACL-Durchsetzung, Existenzprüfung, Größen- und Binärschranken. Eine Umsetzung, die einen dieser Schritte auslässt, ist ein Loch — der Aufrufer prüft nicht für Sie.
Versionierung Experimentell seit 2026-06-23. Der Katalog nennt als Bedingung für die Höherstufung, dass ein Git-Provider ausgeliefert ist; das ist inzwischen der Fall, die Stufe im Katalog steht aber weiterhin auf experimental.

Beispiel

public function register(EventHandler $controller)
{
    $controller->register_hook('WKSOURCEVIEW_REGISTER_PROVIDERS', 'BEFORE', $this, 'registerProviders');
}
 
public function registerProviders(Event $event)
{
    $registry = $event->data;
    if (!is_object($registry) || !method_exists($registry, 'register')) {
        return;
    }
    $provider = new \dokuwiki\plugin\wkexample\Source\ExampleSourceProvider(
        $this->backend(),
        (int) $this->getConf('max_bytes'),
        (int) $this->getConf('max_lines')
    );
    // Ein Provider darf mehrere Schemata bedienen.
    $registry->register('example', $provider);
    $registry->register('ex', $provider);
}

Damit löst {{source>example:pfad/zur/datei.php}} über Ihr Backend auf.

Komposition

Die mitgelieferten Schemata sind media und page; wkdogit trägt git und repo bei, wkdoadogit das Schema ado.

WKSQLITEDS_REGISTER_ROUTINE_SETS

Signatur

$controller->register_hook('WKSQLITEDS_REGISTER_ROUTINE_SETS', 'BEFORE', $this, 'registerRoutineSets');
 
public function registerRoutineSets(Event $event): void;

Quelle: lib/plugins/wksqliteds/helper.php

Zusammenfassung

Meldet ein Verzeichnis mit benannten SQL-Routinen an, damit ein eigenes Paket seine Abfragen als versionierte Dateien mit Manifest ausliefern kann, statt SQL im PHP-Quelltext zu führen.

Eingaben

Der Satzname und das Basisverzeichnis. Der Helfer deklariert seine öffentliche Oberfläche zusätzlich über DokuWikis eigene getMethods()-Konvention:

Methode Parameter Rückgabe
execute eine Verbindung (PDO, SQLiteDB, Verbindungsname oder null), ein Routinenname mit oder ohne Satzpräfix, sowie Parameterwerte laut Manifest: betroffene Zeilen, Einfügekennung, Zeilenmenge, Einzelzeile, Skalar oder null
registerSet Name, Verzeichnis, wahlweise Optionen nichts
getConnection Name eine PDO-Verbindung

Ausgaben

Siehe Tabelle. Der Rückgabetyp von execute() steht im Manifest der Routine, nicht im Aufruf.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Das Satzregister wird einmal je Anfrage aufgebaut; ein Satz gilt für die laufende Anfrage.
Fehlerverhalten Jeder Fehlschlag beim Auflösen oder Ausführen ist eine RoutineException. Ein Manifest, das nicht auswertbar ist, wird mit seinem Fehler aufgeführt statt versteckt; die Anzahl solcher Routinen trägt das Paket als Abzeichen in die Aktivitätsleiste.
Berechtigungen Das Paket führt eine eigene Fähigkeitsmatrix und liest nicht DokuWiki-Gruppen als Berechtigungsquelle. Jemanden einer Wiki-Gruppe hinzuzufügen ändert hier nichts.
Versionierung Kein Katalogeintrag; die Stufe ist damit nicht zugesichert. Behandeln Sie den Punkt als experimentell. Derselbe Ausführer steht zusätzlich als Dienst sql.routines im Register.

Beispiel

public function register(EventHandler $controller)
{
    $controller->register_hook('WKSQLITEDS_REGISTER_ROUTINE_SETS', 'BEFORE', $this, 'registerRoutineSets');
}
 
public function registerRoutineSets(Event $event)
{
    $registry = $event->data;
    if (is_object($registry) && method_exists($registry, 'register')) {
        $registry->register('wkexample', __DIR__ . '/sql');
    }
}
 
/** Aufruf einer Routine aus dem eigenen Satz. */
public function closeItem(int $id, string $user): int
{
    $routines = plugin_load('helper', 'wksqliteds');
    if ($routines === null) {
        throw new \RuntimeException('wksqliteds is required for this feature');
    }
    return $routines->execute('reporting', 'wkexample:close_item', ['id' => $id, 'user' => $user]);
}

Komposition

Der Dienst sql.routines im Dienstregister liefert denselben Ausführer.

WKOPENGRAPH_VALUES

Signatur

$controller->register_hook('WKOPENGRAPH_VALUES', 'BEFORE', $this, 'provideOgValues');
 
public function provideOgValues(Event $event): void;
// $event->data hat die Schlüssel 'id' und 'og'

Quelle: lib/plugins/wkopengraph/action.php

Zusammenfassung

Ersetzt einzelne Open-Graph-Werte, nachdem die reguläre Auflösungskette durchlaufen ist — der Weg für Angaben, die außerhalb der Seitenmetadaten gepflegt werden.

Eingaben

$event→data['id'] ist die Seiten-ID (nur zum Lesen). $event→data['og'] ist das veränderliche Wertefeld; erwartet werden title, description, url, site_name, type und image.

Ausgaben

Kein Rückgabewert. Ein zurückgegebener Wert überschreibt den zuvor aufgelösten.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Einmal je Seitenausgabe, nach der regulären Kette: Seitenmetadaten, dann paketweite Vorgaben, dann DokuWikis eigene Vorgaben.
Fehlerverhalten Ein Wert, der zu einer leeren Zeichenkette auflöst, wird übergangen und die Eigenschaft gar nicht ausgegeben. Das ist Absicht: ein leeres Inhaltsattribut ist eine Aussage („diese Seite hat keine Beschreibung“) und hindert Empfänger daran, auf ihre eigene Auswertung zurückzufallen.
Berechtigungen Keine. Die Werte landen in öffentlich abrufbaren Metadaten — geben Sie hier nichts zurück, was nicht jeder sehen darf.
Versionierung Kein Katalogeintrag; behandeln Sie den Punkt als experimentell. Die Eigenschaftsliste ist fest.

Für image genügt eine Wiki-Medien-Kennung mit führendem Doppelpunkt; sie wird für Sie zu einer absoluten URL aufgelöst. Zeichenkettenwerte dürfen sprachabhängige Varianten tragen, geschrieben als Sprachkürzel, Doppelpunkt, Text, getrennt durch senkrechte Striche.

Beispiel

public function provideOgValues(Event $event)
{
    if (!is_array($event->data) || !is_array($event->data['og'] ?? null)) {
        return;
    }
    $post = $this->postFor((string) $event->data['id']);
    if ($post === null) {
        return;   // keine eigene Angabe: die reguläre Kette behält recht
    }
    $event->data['og']['description'] = $post['teaser'];
    $event->data['og']['image']       = ':' . $post['cover'];   // wird aufgelöst
}

Komposition

Eigenständig. Keine Abhängigkeit zu wkcore.

WKREQUEST_REFINE_DENIED

Signatur

$controller->register_hook('WKREQUEST_REFINE_DENIED', 'BEFORE', $this, 'refineDenied');
 
public function refineDenied(Event $event): void;
// $event->data === die Tafelangabe (Array)

Quelle: lib/plugins/wkrequest/action.php

Zusammenfassung

Lässt das Paket, dem der verschlossene Bereich gehört, die Absagetafel schärfen, die wkrequest auf einer verweigerten Seite gleich zeichnen wird.

Eingaben

Die bereits von wkrequest zusammengestellte Angabe. Übernommen werden ausschließlich diese Schlüssel:

Schlüssel Bedingung für die Übernahme
resourceLabel, required, current nur eine nichtleere Zeichenkette
facts, alternatives nur ein Array
draft nur als Ganzes, und nur wenn er weiterhin eine bekannte Antragsart nennt

Ausgaben

Kein Rückgabewert; Sie verändern das Datenfeld.

Anmerkungen

Aspekt Verhalten
Lebenszyklus Einmal je verweigerter Seitenansicht, unmittelbar bevor die Tafel gezeichnet wird.
Fehlerverhalten Wirft die Ereignisverteilung, wird das protokolliert und die ursprüngliche Angabe gezeichnet. Jeder Schlüssel wird einzeln zurückgeholt, statt dem Feld als Ganzem zu vertrauen: ein Haken, der einen Schlüssel fallen lässt, den er nie anfassen wollte, würde sonst eine bereits richtig ermittelte Tatsache leeren.
Berechtigungen Keine für das Schärfen. Sie können damit keine Tür öffnen — die Tafel ist eine Darstellung der Absage, nicht ihre Aufhebung.
Versionierung Kein Katalogeintrag; behandeln Sie den Punkt als experimentell. Ein Entwurf wird nur als Ganzes ersetzt, damit nie ein Antrag entsteht, dessen Ressource das eine Paket und dessen Bearbeiter ein anderes benannt hat.

Beispiel

public function refineDenied(Event $event)
{
    if (!is_array($event->data)) {
        return;
    }
    $ns = getNS((string) ($GLOBALS['ID'] ?? ''));
    if (!$this->ownsNamespace($ns)) {
        return;   // fremder Bereich: nichts sagen
    }
    $event->data['resourceLabel'] = $this->humanNameFor($ns);
    $event->data['facts'][] = [
        'label' => $this->getLang('fact_group'),
        'value' => $this->openingGroupFor($ns),
    ];
}

Komposition

Verfeinert die Tafel des Admin Request Contract.

Dienstregister

Die Kennungen, die die Pakete der Suite belegen. Ein Verbraucher prüft immer zuerst mit has(); ein get() auf eine unbekannte Kennung wirft.

Kennung Eintragendes Paket Art Was dahintersteht
core.info wkcore Wert Kennung, Name und Fassung — die Fassung ist das Manifestdatum
core.clock wkcore träge Minimaler Zeitgeber. Vorführbeispiel für träge Auflösung, kein Dienst, auf den man bauen sollte
core.context wkcore träge Kontext der Anfrage; feuert beim Bau WKCORE_CONTEXT_BUILD
core.ready wkcore Wert Zeitstempel des Bootlaufs
storage.sqlite wkstorage Wert SQLite-Speicherdienst
vault.secrets wkvault Wert Geheimnisauflösung mit read() und has()
sql.routines wksqliteds träge Ausführer benannter SQL-Routinen
source.providers wksourceview träge Providerregister; feuert beim Bau WKSOURCEVIEW_REGISTER_PROVIDERS
ui.components wkfluentui Wert Bausteine der Oberfläche
ui.icons wkfluentui Wert Symbolanbieter
ui.tokens wkfluentui Wert Gestaltungsmarken
ado.client wkdoado träge Gehärteter Azure-DevOps-REST-Zugang
git.runner wkdogit träge Nur lesende Git-Ausführung, ohne Shell
blog.engine wkblog beides Blog-Maschine
dwdo.storage, dwdo.repos, dwdo.export wkdostorage träge DevOps-Datenhaltung
dwdo.projects, dwdo.hubs, dwdo.permissions, dwdo.context, dwdo.nav wkdocore träge DevOps-Grundlagen
dwdo.projectdirectory wkdocore träge Projektverzeichnis; feuert WKDOCORE_PROJECT_DISCOVER
dwdo.repodirectory wkdocore träge Repositoriumsverzeichnis; feuert WKDOCORE_REGISTER_REPOSITORY_SOURCES

Stabilität: Die Kennung selbst und die Tatsache, dass etwas dahintersteht, sind so stabil wie das eintragende Paket. Die Form des hinterlegten Objekts gehört dem eintragenden Paket; wo es dafür ein Interface gibt, ist es in diesem Katalog genannt. Prüfen Sie vor der Benutzung mit method_exists(), statt einen Typ anzunehmen — genau so tun es die Pakete der Suite untereinander.

Veraltete Mechanismen

Diese fünf entenartig typisierten Mechanismen sind vom Workbench-Contract v1 abgelöst. Sie funktionieren unverändert weiter: ein Anpassungsstück übersetzt sie in dieselbe Descriptor-Form, und ein Paket, das auf ihnen geschrieben ist, läuft ohne Änderung.

Mechanismus Was er sagte Ersatz in v1
getShellRegions() Allein die Anwesenheit der Methode war das Signal „ich zeichne meine Schale selbst„. Der Rückgabewert wurde nie gelesen. Feld surface mit dem Wert selfHosted
getShellBadge() Abzeichen auf dem eigenen Leisteneintrag Feld badge
getShellViews() Ansichten in Seiten- und Nebenleiste kind view mit einer region
getModuleInfo() Das Paket ist ein Modul und darf auf einer Wiki-Seite laufen region adminModule
WKFLUENTUI_ADMIN_COMMANDS Befehle für einen Bildschirm; wird je Bildschirm beantwortet, nicht vorab aufgezählt kind action mit region commandPalette

Warum sich der Umstieg lohnt, auch wenn nichts kaputtgeht. Eine Fähigkeit, die man nur prüfen kann, indem man eine Methode ruft und nachsieht, ob das Ergebnis zufällig leer ist, ist nicht auffindbar — sie muss einmal im Quelltext gelesen und dann erinnert werden. Der Contract ist das Gegenteil: ein Ereignis, ein Descriptor, und eine Fähigkeit, nach der man fragen kann, bevor man etwas versucht.

Drei Regeln aus vier durchgeführten Umstellungen:

  1. Die Schalenankündigung gehört außerhalb jeder Absicherung. Sie ist eine feste Eigenschaft des Pakets. Steht sie in derselben Absicherung wie ein Haken, der für ein Abzeichen eine Datenbank befragt, dann hebt ein Datenbankausfall die Selbstzeichnung auf — und der Bildschirm erscheint in zwei Schalen. Die Ursache wäre eine fehlgeschlagene Zählabfrage in einer anderen Datei.
  2. Erst jeden Verbraucher belehren, dann den ersten Erzeuger umstellen. Die Eigentümerfrage wird an drei Stellen gestellt. Ein Verbraucher, der nur den alten Weg kennt, fängt am Tag der ersten Umstellung an, das Falsche zu tun — und nichts scheitert dabei: es erscheint lediglich ein zweites Inhaltsverzeichnis neben der Navigation, die das Modul schon selbst zeichnet.
  3. Eigentümerschaft und Nutzlast sind zwei Fragen. Eine v1-Anmeldung kündigt eine Fläche an; sie übergibt keine fertig gezeichneten Regionen.

Sichtbarkeit folgt dem Eigentümer. Ein Beitrag, dessen Eigentümer ein installiertes Admin-Plugin ist, entfällt, wenn die Sitzung dieses Plugin nicht erreichen darf. Ein Paket ohne Admin-Bildschirm hat diese Prüfung nicht und muss permission selbst mitbringen — passend zu dem, was sein Ziel durchsetzt. Das falsch zu machen ist unsichtbar: das Register reicht einen Descriptor heraus, den niemand zeichnet, bis eines Tages jemand ihn zeichnet.

Das Anpassungsstück verschwindet, sobald kein Paket mehr seine Schale über getShellRegions() erklärt. Eines tut es noch — die Datenbankumgebung, deren Umsetzung eine echte Nutzlast trägt, die die Hausvorlage vor dem Inhalt platziert.

Verwandte Themen

  • Übersicht — der Einstieg, mit der Routing-Karte.
  • Tutorial — ein Plugin von Grund auf in den Workbench hängen.
  • How-To — eine Aktion hinter einen zweiten Faktor stellen.
  • Fehlerbehebung — Symptome und ihre Ursachen.
  • die Kompatibilitätsmatrix in docs/KOMPATIBILITAET.md — ob ein Paket auf dieser Installation überhaupt lauffähig ist.
  • Contracts im Wortlaut: lib/plugins/wkfluentui/docs/contract-v1.md, lib/plugins/wkidentity/docs/contract-v1.md
  • Ereigniskataloge zum Nachschlagen der Stufe: lib/plugins/wkcore/Events.php, lib/plugins/wkdocore/Events.php, lib/plugins/wksourceview/Events.php
  • lib/plugins/wkcore/docs/upgrading.md — was ein Upgrade an Contracts und Daten verändert.
de/wiki/dwe/sdk/api-reference.txt · Zuletzt geändert: von 0.0.0.0