Sie befinden sich hier: start » de » Interne Dokumentation » DokuWiki-Erweiterungen (WvdS) » FluentUI (Design-System-Bibliothek) » Workbench Plugin Contract v1

Workbench Plugin Contract v1

Der verbindliche, versionierte Vertrag, über den ein Plugin zur Werkbank beiträgt — Aktivitätsleiste, Seitenspalte, Zusatzspalte, unteres Panel, Statusleiste, Befehlsleiste/-palette und das Modulverzeichnis. Diese Seite ist die deutsche Kurzfassung für Betreiber und Autoren; die verbindliche, vollständige Spezifikation (API, Lebenszyklus, Fehlerverhalten, Beispiel) steht im Repository: lib/plugins/wkfluentui/docs/contract-v1.md.

Verwandt: wkfluentui-Übersicht · Styles-Contract

Warum es diesen Vertrag gibt

Bis Contract v1 lief ein Beitrag zur Werkbank über fünf getrennte, nur über method_exists() erkannte Mechanismen (getModuleInfo(), getShellRegions(), getShellBadge(), getShellViews(), das Ereignis WKFLUENTUI_ADMIN_COMMANDS). Mindestens einer davon transportierte gar keine echte Information mehr: der Rückgabewert von getShellRegions() wird nirgends gelesen — allein die Existenz der Methode war das Signal. Es gab keine Möglichkeit zu fragen „unterstützt die Werkbank ein unteres Panel überhaupt„, bevor man eines benutzt, und keine Stelle, an der ein Plugin eine Berechtigung enger als „darf dieser Benutzer den Bildschirm überhaupt öffnen“ erklären konnte.

Nichts davon wird abgeschaltet. Ein Plugin, das eine der fünf alten Methoden benutzt, funktioniert unverändert weiter — ein Adapter (helper/legacyadapter.php) übersetzt sie automatisch in dieselbe Beschreibungsform, die Contract v1 selbst benutzt.

Die drei Bauflächen

Fläche Bedeutung
classic reines do=admin (ohne mode=fluentui), oder eine gewöhnliche Wiki-Seite ohne <module>-Tag — keine Werkbank-Beteiligung
contributed Regelfall: wkfluentui baut die komplette Schale um den vom Bildschirm gelieferten Inhalt, allein aus dessen erklärten Beiträgen
selfHosted das Plugin ruft widgets→workbench() selbst auf, aus seinem eigenen html() heraus — erklärt durch eine bloße Ankündigung ohne kind

Die sieben Bereiche

Bereich Erscheint in Anmerkung
activityBar Modul-Wechselleiste nur ein Abzeichen ist hier beitragbar — das Symbol selbst kommt automatisch aus der Aufzählung installierter Verwaltungs-Plugins
sidebar linke Ansichts-Spalte wie im bisherigen Ansichts-Register benannt
auxbar rechte Ansichts-Spalte (oder zweites Dokument im geteilten Ansicht) dito
bottomPanel unteres Panel im bisherigen Register panel — hier umbenannt, weil das bloße Wort außerhalb seiner Definitionsdatei als „irgendein Panel„ gelesen wird
statusBar Statuszeile ein kind: view-Beitrag nennt eine Tatsache darüber, wo der Leser ist — siehe unten
commandPalette Befehlsleiste (sichtbar) und Befehlspalette (durchsuchbar, Strg+Umschalt+P) siehe unten
adminModule Modulverzeichnis für <module name=„…“> v1-Entsprechung von getModuleInfo()

Die Statuszeile nimmt Tatsachen, keine Auszeichnung

Jeder andere Bereich trägt vertrauenswürdiges HTML des Aufrufers; dieser trägt reinen Text und maskiert ihn selbst. Die Statuszeile hat eine Zeile, und ein Beitrag, der ein Element öffnen könnte, nähme den Rest davon.

Feld Bedeutung
label der Text
href wohin er führt, wenn er irgendwohin führt
disabledReason wird als Titel (Kurzhinweis beim Zeigen) gerendert
badge.tone warning oder error für die Einfärbung
priority wie wichtig die Angabe ist — siehe unten

Gültigkeitsbereich wie bei einem Befehl: ein Beitrag, dessen Besitzer der gerade gezeigte Bildschirm ist, erscheint; einer, dessen Besitzer überhaupt keinen Verwaltungsbildschirm hat, gilt als allgemeingültig; einer, der zu einem anderen Bildschirm gehört, erscheint nicht. Es ist buchstäblich dieselbe Prüfung, nicht eine zweite mit gleichem Wortlaut.

Zwei Stufen des Überlebens, aus einer Zahl. Unter 1024 px fällt weg, was keine Priorität trägt; unter 768 px zusätzlich alles unterhalb von helper_plugin_wkfluentui_widgets::STATUS_PHONE_PRIORITY (80). Ein Beitrag darf priority selbst nennen; der Wert wird auf 10–95 geklemmt, damit nichts Beigetragenes den Rückweg aus der Werkbank überholt, und ein Beitrag ohne Angabe bekommt 85.

priority ist nicht order. order sortiert innerhalb eines Bereichs, priority entscheidet über das Überleben bei schmaler Breite. Wären sie ein Feld, würde ein Umsortieren der Seitenleiste ändern, was ein Telefon in der Statuszeile zeigt.

Dieser Bereich war zweimal eine Zusage ohne Einlösung, in zwei verschiedenen Formen — und beide standen auf dieser Seite.

Bis zum 24.08.2026 stand hier, ein Beitrag werde „derzeit noch von keinem Verbraucher gerendert“, während helper/capabilities.php den Namen workbench.contribution.statusBar seit dem ersten Tag als wahr führte. Eine Fähigkeit, deren Antwort „ja„ lautet, während es das Merkmal nicht gibt, ist schlechter als eine Versionsnummer — sie ist der Mechanismus, der eine ersetzen sollte.

Am 25.08.2026 gab es den Verbraucher, und priority stand hier beschrieben — aber normalise() baut einen festen Schlüsselsatz, und das Feld war nicht darin. Ein Beitragender konnte es setzen; es verschwand in der Registratur, bevor irgendein Verbraucher es sah. Die Folge war die umgekehrte der zuerst vermuteten: weil der Compositor ein fehlendes priority mit 85 vorbelegte, überlebte jeder beigetragene Eintrag bis in die Telefonstufe, ob sein Besitzer das wollte oder nicht — kaputt war die Steuerbarkeit, nicht die Sichtbarkeit.

Erst fehlte der Verbraucher, dann die Leitung zwischen zwei arbeitenden Enden.

Eine Ansicht darf ihr ''html'' als Callable liefern

Die Deskriptor-Tabelle nennt html seit jeher als string | callable. Bis zum 25.08.2026 schrieb der einzige Renderweg is_string(…) ? … : '' — ein Callable wurde also zur leeren Zeichenkette, und viewStack() lässt eine Ansicht mit leerem Körper weg. Kein Fehler, kein Protokolleintrag, kein Bereich.

Der Unterschied ist nicht kosmetisch. Das Registrierungs-Ereignis feuert einmal, und bei einem Verwaltungsbildschirm geschieht das, bevor der Bildschirm gerendert hat. Eine Zeichenkette muss also zur Registrierungszeit berechenbar sein; ein Callable wird zur Kompositionszeit aufgelöst, also nachdem das eigene html() gelaufen ist. Genau dafür ist es da.

Ein werfendes Callable kostet diese eine Ansicht und wird protokolliert. Fähigkeit: workbench.view.html.callable.

Ein Bildschirm darf seine Arbeitsobjekte als Reiter beitragen

Bis zum 25.08.2026 löste adminshell::tabStripHtml() jede gespeicherte Marke mit plugin_load('admin', …) auf — die Leiste konnte also ausschließlich Module benennen. Die Registratur darunter war nie die Grenze: sie führt eine geordnete Liste undurchsichtiger Zeichenketten je Arbeitsbereich und sagt das auch.

Zwei Bildschirme brauchten dasselbe gleichzeitig — ein Einstellungs-Editor, dessen Arbeitsobjekt ein Bereich ist, und eine Abfrage-Werkbank, deren Arbeitsobjekt eine Abfrage ist. Einmal im Wirt gebaut, weil die Alternative in beiden Fällen eine zweite Reiterleiste im Inhalt gewesen wäre.

Regel Warum
Die Marke wird von der Schale zu <modul>:<objekt> erweitert eine gespeicherte Marke sagt damit immer, wer sie zurückübersetzen kann
Die Reiter eines Bildschirms ersetzen dessen Modulreiter ein Reiter für „der Konfigurationsmanager“ neben Reitern für die Bereiche, die er verwaltet, wäre eine zweite Reiterebene in den Kleidern der ersten
Nur Modulreiter bekommen Verschiebe-Bedienelemente das Formular sendet die sichtbare Reihenfolge in die Modulliste; eine Objektmarke dort wäre beim nächsten Aufruf nicht auflösbar und nähme die umsortierten Modulreiter mit
Der Bildschirm schreibt seine eigene Anheftung die Schale weiß absichtlich nicht, was die Marke benennt
Ein Reiter ohne Titel oder Adresse wird verworfen er wäre nicht erreichbar, nicht als Lesezeichen zu setzen und von seinem Nachbarn nicht zu unterscheiden

Fähigkeit: workbench.screen.tabs.v1.

Vier Haken für einen mitgelieferten Bildschirm

Ein Ersatzbildschirm wird von wkfluentui für ein mitgeliefertes Plugin gezeichnet, und der Vertrag v1 erreicht ihn deshalb nicht: nativeViewsFor($owner) vergleicht den Besitzer eines Beitrags mit dem gerade gezeigten Bildschirm — der Besitzer wäre wkfluentui, der Bildschirm config, und die beiden treffen sich nie. Unter dem Namen des mitgelieferten Plugins zu registrieren hiesse, dieses Plugin gäbe vor, ein anderes zu sein.

Ein solcher Bildschirm beantwortet deshalb vier optionale Methoden unmittelbar: commands(), views(), statusItems(), tabs(). Alle vier laufen durch eine abgesicherte Naht, ein Fehlschlag kostet also genau diese eine Antwort und erreicht das Protokoll.

Die eigene Ansicht eines Bildschirms verdrängt die generische der Schale für denselben Bereich. Ein Bildschirm, der seine eigene Navigation zeichnet, darf nicht zusätzlich das generische Inhaltsverzeichnis danebengestellt bekommen; und einer mit eigener Kontextspalte nicht zusätzlich plugin.info.txt. Dieselbe Regel, die für ein selbst-hostendes Plugin schon gilt — und derselbe Fehlermodus: nichts schlägt fehl, es erscheint einfach eine zweite Seitenleiste.

Berechtigungen

permission ist ein Callable (keine Argumente, liefert bool) oder null. Zusätzlich zur ACL, nie an ihrer Stelle — der zugrundeliegende Verwaltungsbildschirm bleibt unverändert durch DokuWikis eigene isAccessibleByCurrentUser()-Prüfung geschützt. Ein permission-Wert, der weder null noch aufrufbar ist, gilt als fehlerhafte Erklärung und schließt aus — nicht als fehlende und lässt damit durch. Eine werfende Prüfung gilt als Verweigerung, nicht als Absturz, und wird nicht als Fehler protokolliert: „dieser Benutzer darf es nicht sehen„ ist der Mechanismus, der funktioniert, kein Defekt.

Fähigkeits-Erkennung

helper_plugin_wkfluentui_capabilities::has($name) — ein Name, den ein Plugin vor der Benutzung abfragt, statt eine Versionsnummer oder eine Klasse zu prüfen. Der Grund: eine Fähigkeit kann später ausgeliefert werden als die Version, die sie ankündigte — workbench.command.palette wurde erst wahr, als der Compositor den Palettendialog tatsächlich in die Seite einfügte, lange nachdem der Dialog selbst schon existierte und ungenutzt blieb.

Seit dem 25.08.2026 zwei weitere Namen:

Fähigkeit Bedeutung
workbench.view.html.callable das html einer Ansicht darf ein Callable sein und wird zur Kompositionszeit aufgelöst
workbench.screen.tabs.v1 ein Bildschirm darf seine eigenen Arbeitsobjekte in der einen Reiterleiste benennen

Aus dem Registrierungs-Ereignis heraus antwortet has() nur mit dem Kernwortschatz — und das ist die vollständige Wahrheit, kein Kompromiss. Wenn der eigene Handler läuft, ist noch kein provides irgendeines anderen Beitragenden eingesammelt. Eine Frage nach der Fähigkeit eines fremden Pakets hat zu diesem Zeitpunkt keine Antwort und bekommt false.

Das war bis zum 25.08.2026 schlimmer als eine Einschränkung: list() liest provides aus der Registratur, und die wird erst am Ende von all() gemerkt — die hier empfohlene Prüfung rief also all() erneut auf, feuerte das Ereignis erneut, rief jeden Handler erneut, und tat das, bis der Speicher ausging. In wksqliteds gemessen als Allowed memory size of 134217728 bytes exhausted, mit einem Stack, der irgendwo endete, nur nicht bei der Ursache. all() markiert sich jetzt für die Dauer des Ereignisses, und list() antwortet solange mit dem Kernwortschatz, ohne zu merken. Die Empfehlung oben ist seither gefahrlos zu befolgen; vorher war sie es nicht.

Bekannter, in diesem Durchgang behobener Fehlstand

Zwei Fähigkeiten waren bereits vollständig gebaut, aber nirgends erreichbar:

  1. Das untere Panel war bis in widgets→workbench() verdrahtet, wurde aber vom Compositor beim Kopieren der Regionen über eine fest verdrahtete Namensliste stillschweigend ausgelassen — eine dorthin verschobene Ansicht verschwand beim Rendern.
  2. Die Befehlspalette (Dialog plus JavaScript-Indexierung) war fertig gebaut, aber nirgends in die Seite eingefügt.

Beides ist mit der Vereinheitlichung auf Contract v1 behoben — siehe docs/contract-v1.md-Abschnitt „Versioning“ für die Begründung, warum gerade das der Beleg dafür ist, dass eine Fähigkeits-Prüfung einer Versionsprüfung vorzuziehen ist.

Nachweis

Geprüft in Isolation gegen Attrappen (26 Zusicherungen) und live gegen die echte Installation in drei getrennten Prozessläufen – angemeldete Administrator-Identität, anonym und mit vorübergehend umbenanntem Testplugin-Verzeichnis zur Simulation einer Deinstallation.

de/wiki/dwe/wkfluentui/contract.txt · Zuletzt geändert: von rollout