Farbschema: Hell
Sie befinden sich hier: start » de » Dokumentation » DokuWiki-Erweiterungen (WvdS) » FluentUI (Design-System-Bibliothek) » FluentUI: Komponenten-Referenzen » FluentUI: Öffentliche Helper-API

FluentUI: Öffentliche Helper-API

Zurück: FluentUI (Design-System-Bibliothek)


Die öffentliche PHP-API von wkfluentui: sechs Helper-Klassen liefern framework-freie HTML-Bausteine – Raster, Felder, Schubladen, Leisten. Diese Seite ist ihre Quelle der Wahrheit.

1. Übersicht und Zweck

Status: Stufe 1 – beschreibt tatsächlich umgesetzten Code.

Diese Seite ist die Quelle der Wahrheit für die öffentliche PHP-API von wkfluentui: sechs eigenständige Helper-Klassen mit zusammen 77 öffentlichen Methoden (widgets 67, formpage 5, nav 2, adminrail/gallery/user je 1), die framework-freie HTML-Bausteine (Baum, Tabs, Drawer/Modal, Formular-Raster, Datentabelle, Tree List, Eingabefelder-Familie, NavBar, Card, CountBadge, Listen-Filter, Editor-Tab-Strip, Command-Palette, Kontextmenü-Quelle, Code-Editor-Aktivierung, Toolbar, CSV-Export), Schema-Formular-Seiten (formpage) sowie ACL-geprüfte Datenermittlung (Admin-Leiste, Bildergalerie, Cross-Plugin-Navigation, Avatar-Initialen) bereitstellen. Zielgruppe: jedes DokuWiki-Plugin oder -Template, das diese Bausteine wiederverwenden will – die API ist bewusst nicht an premium-navy-ivory gebunden.

Die meisten Methoden haben zusätzlich eine eigene, vertiefte Komponentenseite mit Demo-Skizze und Feature-Unterseiten; diese Seite verweist dort hin, statt den Vertrag zu duplizieren (Zuordnung: Tabelle in Abschnitt 6). Nur drawer(), modal(), rowActions() sowie je eine Methode in adminrail/gallery/nav/user haben keine andere Seite – hier steht ihr vollständiger Vertrag.

2. Voraussetzungen

  • DokuWiki mit aktiviertem Plugin wkfluentui (lib/plugins/wkfluentui/).
  • PHP-Kontext eines laufenden DokuWiki-Requests (die Methoden nutzen globale DokuWiki-Funktionen wie hsc(), wl(), cleanID(), auth_quickaclcheck() – kein Aufruf außerhalb einer DokuWiki-Instanz möglich).
  • Für ACL-geprüfte Methoden (listImages(), getAdminRailItems()): ein bereits initialisierter Nutzer-/ACL-Kontext (Standard in jedem regulären DokuWiki-Request).

3. Konzepte

Sechs getrennte Helper-Klassen, kein gemeinsames helper.php. wkfluentui hat bewusst kein eigenes Wurzel-helper.php – plugin_load('helper', 'wkfluentui') liefert deshalb immer null, lautlos, ohne Fehler oder Warnung. Jede Komponente liegt unter helper/<component>.php und wird einzeln geladen (Details siehe Abschnitt 11, Troubleshooting, weiter unten auf dieser Seite) – dieser Namensfehler blieb in wk-dw-msqlite-plugin mehrere Features lang unbemerkt, weil DokuWiki selbst keine Warnung für einen unbekannten Helper-Namen ausgibt.

Escaping-Konvention (gilt für alle Methoden einheitlich): jede Methode escaped die ihr übergebenen Klartext-Werte (Label, Titel, Href) selbst über hsc() (schützt gegen CWE-79/XSS). Parameter, deren Name auf Html endet (controlHtml, bodyHtml, element), sind dagegen bereits vom Aufrufer erzeugtes, vertrauenswürdiges HTML – der Aufrufer trägt dort die Escaping-Verantwortung. Eine Ausnahme von der ersten Regel ist dokumentiert in Abschnitt „gallery“ unten.

CSS-Gegenstück: die Sektion WIDGETS in wkfluentui/style.css (site-weit automatisch eingebunden über DokuWikis css_pluginstyles()).

4. Erste Schritte

Minimalbeispiel: Toolbar-Helfer laden und eine einzeilige Aktionsleiste rendern.

/** @var helper_plugin_wkfluentui_widgets $widgets */
$widgets = plugin_load('helper', 'wkfluentui_widgets');
 
echo $widgets->toolbar([
    ['label' => 'Neu', 'element' => '<button type="submit" name="do" value="new">Neu</button>'],
]);

Falsch: plugin_load('helper', 'wkfluentui') (ohne Suffix) – liefert null, jeder nachfolgende Methodenaufruf löst einen Fatal Error „Call to a member function … on null“ aus.

5. Verwendung

Helper Konsument Verwendete Methoden
adminrail lib/tpl/wkbizway/inc/wkbizway-render.php getAdminRailItems()
gallery lib/tpl/wkbizway/inc/wkbizway-render.php listImages() 1)
nav lib/tpl/wkbizway/inc/nav.php getBlogCategoryNav(), getAcmenuFlyout() 2)
user lib/tpl/wkbizway/main.php, inc/wiki-shell.php avatarInitials() 3)
widgets lib/plugins/wksqliteds/dbadmin/*.php die neun Bestandsmethoden (Table Designer, Object Explorer, „Connect to…“-Dialog); die neu hinzugekommenen Methoden (Eingabefelder-Familie, treeList(), navBar() u. a.) warten auf ihren ersten Konsumenten

Weitere WvdS-Plugins sind eingeladen, dieselben Helper zu nutzen (siehe FluentUI (Design-System-Bibliothek), Abschnitt „Rolle“).

6. API-Referenz

Klasse Datei plugin_load()-Aufruf Methode(n) Vertrag
helper_plugin_wkfluentui_adminrail helper/adminrail.php plugin_load('helper', 'wkfluentui_adminrail') getAdminRailItems() unten, Abschnitt „adminrail“
helper_plugin_wkfluentui_gallery helper/gallery.php plugin_load('helper', 'wkfluentui_gallery') listImages() unten, Abschnitt „gallery“
helper_plugin_wkfluentui_nav helper/nav.php plugin_load('helper', 'wkfluentui_nav') getBlogCategoryNav(), getAcmenuFlyout() unten, Abschnitt „nav“
helper_plugin_wkfluentui_user helper/user.php plugin_load('helper', 'wkfluentui_user') avatarInitials() unten, Abschnitt „user“
helper_plugin_wkfluentui_formpage helper/formpage.php plugin_load('helper', 'wkfluentui_formpage') loadSchema(), validate(), serialize(), parse(), formFields() formpage → Schema-Formular-Seiten
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') tree() treeview → Grundlagen
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') tabs() tabcontrol → Grundlagen
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') drawer() unten, Abschnitt „drawer“
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') modal() unten, Abschnitt „modal“
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') propertyGrid() property-grid → Grundlagen
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') dataGrid() datagrid → Grundlagen
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') rowActions() unten, Abschnitt „rowActions“
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') codeEditor() editors → codeEditor()-Baustein
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') toolbar() toolbar → Grundlagen
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') treeList() treelist → Grundlagen
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') navBar() navbar → Grundlagen
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') button() form-fields → Button
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') calendar() form-fields → Calendar
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') checkbox() form-fields → Checkbox
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') comboBox() form-fields → ComboBox
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') dateTime() form-fields → DateTime
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') dropdown() form-fields → Dropdown Editor
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') listEditor() form-fields → List Editor
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') progressBar() form-fields → ProgressBar
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') exportRows() datagrid → Data Export
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') countBadge() widgets → Katalog (CountBadge)
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') card() widgets → Katalog (Card/Tile)
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') declarativeTable() widgets → Katalog (DeclarativeTable)
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') listFilter() widgets → Katalog (InputBox-Filter)
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') editorTabs() widgets → Katalog (Editor-Tab-Strip)
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') commandPalette() widgets → Katalog (Command-Palette)
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') workbench() unten, Abschnitt „workbench“
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') statusbar() unten, Abschnitt „statusbar“
helper_plugin_wkfluentui_widgets helper/widgets.php plugin_load('helper', 'wkfluentui_widgets') accordion() unten, Abschnitt „accordion“
helper_plugin_wkfluentui_coreform helper/coreform.php plugin_load('helper', 'wkfluentui_coreform') render(), trigger(), formOpen(), elements(), formClose() core-screens → Kernformulare

adminrail

Metadatum Wert
Sprache PHP
Namespace helper_plugin_wkfluentui_adminrail
Datei lib/plugins/wkfluentui/helper/adminrail.php
Sichtbarkeit public
Stabilität stabil (Stufe 1)

getAdminRailItems(string $currentPage): array – liefert die ACL-gefilterte, in die drei festen Buckets admin/manager/other gruppierte und alphabetisch (Fallback: Menü-Sortierindex) sortierte Liste aller für den angemeldeten Nutzer sichtbaren Admin-Plugins, im selben Format wie DokuWikis eigene do=admin-Kachelansicht (\dokuwiki\Ui\Admin), aber flach statt gruppiert und um ein Inline-SVG-icon-Feld sowie ein active-Flag für $currentPage ergänzt.

Parameter Typ Erforderlich Beschreibung
$currentPage string Ja der page-Request-Parameter des aktuell angezeigten Admin-Screens ($INPUT->str('page')); leerer String auf dem nackten Admin-Index

Rückgabewert: Array von ['id' => string, 'label' => string, 'href' => string, 'icon' => string, 'active' => bool] je sichtbarem Admin-Plugin. Reihenfolge: Bucket admin → manager → other, innerhalb jedes Buckets alphabetisch nach Label. Leeres Array, wenn der Nutzer kein einziges Admin-Plugin sehen darf – kein Fehler.

Sicherheit: Die Bucket-Zugehörigkeit (welche Plugin-IDs zu admin/ manager zählen) ist ein wörtliches Duplikat der Liste in DokuWikis eigenem \dokuwiki\Ui\Admin – bewusst, damit dieselbe Installation in beiden Ansichten dieselbe Gruppierung zeigt; ein Core-Update, das diese Liste ändert, erfordert einen synchronen Abgleich hier. ACL-Prüfung über isAccessibleByCurrentUser() je Plugin (CWE-284/Broken Access Control) – der Aufrufer muss nicht zusätzlich filtern.

Nebenwirkungen: keine (reine Lese-/Enumerationsmethode).

Fehlerverhalten: wirft keine Exceptions. Ein Plugin, dessen Menü-Icon-Datei fehlt/zu groß/unlesbar ist, erhält icon' ⇒ ''`` — dieselbe stille Degradation wie in DokuWikis eigenem Ui\Admin::showMenuItem() (kein Platzhalter-Icon).

$rail = plugin_load('helper', 'wkfluentui_adminrail');
$items = $rail->getAdminRailItems($INPUT->str('page'));
foreach ($items as $item) {
    echo '<a href="' . $item['href'] . '"' . ($item['active'] ? ' class="active"' : '') . '>'
        . $item['icon'] . hsc($item['label']) . '</a>';
}
Metadatum Wert
Sprache PHP
Namespace helper_plugin_wkfluentui_gallery
Datei lib/plugins/wkfluentui/helper/gallery.php
Sichtbarkeit public
Stabilität stabil (Stufe 1)

listImages(string $ns, int $thumb = 200): array – liefert alle lesbaren Bilddateien direkt in $ns (nicht rekursiv, keine Unter-Namespaces).

Parameter Typ Erforderlich Standardwert Beschreibung
$ns string Ja – Medien-Namespace; wird intern über cleanID() normalisiert
$thumb int Nein 200 Thumbnail-Breite in Pixeln, intern auf 32–1024 begrenzt

Rückgabewert: Array von ['id' => string, 'full' => string (ml()-URL), 'thumb' => string (ml()-URL), 'name' => string] je Bild. Leeres Array bei ungültigem/leerem $ns oder fehlendem Leserecht.

Sicherheit – abweichende Escaping-Regel (Ausnahme zur allgemeinen Konvention aus Abschnitt 3): $ns wird über cleanID() normalisiert (CWE-22, Path Traversal) und vor dem Dateisystemzugriff per auth_quickaclcheck("$ns:*") < AUTH_READ geprüft (CWE-284). Das name-Feld im Rückgabewert ist roh (noNS($id), nicht hsc()-escaped) – anders als bei jeder anderen Methode dieser API. Der Aufrufer muss name vor der Ausgabe selbst escapen; full/thumb sind bereits fertige, in sich sichere URLs (ml()).

Nebenwirkungen: ein Dateisystem-Scan (search() über $conf['mediadir'], Tiefe 1) je Aufruf – kein Cache.

Fehlerverhalten: wirft keine Exceptions; alle Fehlerfälle (ungültiger Namespace, fehlendes Leserecht, leerer Namespace) liefern ein leeres Array, nicht null und nicht false – ein Aufrufer kann immer direkt iterieren.

$gallery = plugin_load('helper', 'wkfluentui_gallery');
foreach ($gallery->listImages('de:blog:media', 300) as $img) {
    echo '<img src="' . $img['thumb'] . '" alt="' . hsc($img['name']) . '">';
}
Metadatum Wert
Sprache PHP
Namespace helper_plugin_wkfluentui_nav
Datei lib/plugins/wkfluentui/helper/nav.php
Sichtbarkeit public
Stabilität stabil (Stufe 1)

Zwei Methoden, beide null-tolerant: ist das jeweilige Ziel-Plugin deaktiviert/nicht installiert, liefern beide den leeren String statt eines Fehlers.

  • getBlogCategoryNav(string $rootNs): string – Blog-Kategorie-Flyout-Fragment für einen Blog-Root-Namespace (z. B. de:blog), delegiert an wkblog_tags::getCategoryTree().
  • getAcmenuFlyout(string $rootNs): string – Namespace-Baum-Flyout-Fragment für einen Wiki-Root-Namespace (z. B. de:wiki), delegiert an wkacmenu::renderNavFlyout().

Rückgabewert (beide): HTML-Fragment (eine <li>-Liste), leerer String wenn das jeweilige Ziel-Plugin fehlt oder keine Daten liefert.

Nebenwirkungen: beide Methoden cachen intern (DokuWikis \dokuwiki\Cache\Cache, 1800 Sekunden Lebensdauer, CACHE_AGE-Konstante) – je Namespace UND je Gruppenmitgliedschaft-Set des aktuell angemeldeten Nutzers (Sicherheitsgrund, s. u.), nicht global. getBlogCategoryNav() hängt seinen Cache-Eintrag zusätzlich an wkblog_tagss eigene Speicherdatei (cacheDependencyFile()), damit eine reine Tag-Änderung (die die Blogseite selbst nicht berührt) den Cache sofort invalidiert statt bis zu 1800 Sekunden veraltet zu bleiben. getAcmenuFlyout() hängt seinen Cache-Eintrag zusätzlich an DokuWikis Seitenindex (data/index/page.idx): der wächst, sobald eine neu angelegte Seite erstindiziert wird (Taskrunner beim nächsten Seitenaufruf) – neue Seiten erscheinen damit ohne 30-Minuten-Wartezeit im Flyout. Dokumentierte Grenze: Löschungen berühren page.idx nicht (die PID-Zeilen bleiben stabil), eine gelöschte Seite kann daher bis zum Ablauf der Altersgrenze im Flyout verbleiben.

Sicherheit: Der Cache-Schlüssel enthält bewusst die Gruppenmitgliedschaft des Nutzers (sortierte @Gruppe-Liste) – ohne diesen Bestandteil würde ein ACL-gefilterter Navigationsbaum, den ein privilegierter Nutzer zuerst aufruft, im Cache landen und danach fälschlicherweise auch einem nicht-privilegierten Nutzer ausgeliefert (Cache-basiertes ACL-Leck, CWE-284).

Fehlerverhalten: keine Exceptions; fehlendes Ziel-Plugin → leerer String (kein Log-Eintrag, kein Fehler).

$nav = plugin_load('helper', 'wkfluentui_nav');
echo $nav->getBlogCategoryNav('de:blog');
echo $nav->getAcmenuFlyout('de:wiki');

user

Metadatum Wert
Sprache PHP
Namespace helper_plugin_wkfluentui_user
Datei lib/plugins/wkfluentui/helper/user.php
Sichtbarkeit public
Stabilität stabil (Stufe 1)

avatarInitials(string $realName, string $login): string – liefert ein bis zwei Großbuchstaben-Initialen für einen Avatar-Platzhalter, wenn keine hochgeladene Avatar-Datei existiert (die Datei-Lookup-Logik selbst liegt bewusst nicht hier, s. Abgrenzung unten).

Parameter Typ Erforderlich Beschreibung
$realName string Ja (darf leer sein) Anzeigename des Nutzers
$login string Ja Login-Name, nur als Fallback genutzt

Algorithmus: erstes + letztes Wort von $realName bei 2+ Wörtern, erste zwei Zeichen von $realName bei genau einem Wort, erste zwei Zeichen von $login als Fallback wenn $realName leer ist. Multibyte- sicher (mb_substr/mb_strtoupper).

Rückgabewert: string, immer nicht-leer, sofern $login nicht ebenfalls leer ist.

Sicherheit: Die Methode escaped ihre Ausgabe nicht selbst (anders als die allgemeine Konvention aus Abschnitt 3, die nur für widgets.php gilt) – $realName stammt aus dem Nutzerprofil und kann theoretisch beliebige Zeichen enthalten. Der Aufrufer muss den Rückgabewert vor der Ausgabe selbst hsc()-escapen.

Abgrenzung: bewusst getrennt von der Avatar-Datei-Lookup-Logik in wvdsavatar/action.php::currentAvatarUrl() – kleine, stabile String-Logik bleibt hier dupliziert statt eine harte Cross-Plugin- Laufzeitabhängigkeit für wenige Zeilen einzuführen (dokumentierte, bewusste Entscheidung, kein Versehen).

$user = plugin_load('helper', 'wkfluentui_user');
$initials = $user->avatarInitials($INFO['userinfo']['name'] ?? '', $_SERVER['REMOTE_USER'] ?? '');
echo '<span class="avatar-placeholder">' . hsc($initials) . '</span>';

drawer

Metadatum Wert
Sprache PHP
Namespace helper_plugin_wkfluentui_widgets
Datei lib/plugins/wkfluentui/helper/widgets.php
Sichtbarkeit public
Stabilität stabil (Stufe 1)

drawer(string $title, string $bodyHtml, string $cancelHref): string – rechtsseitiges Slide-in-Panel: ein Vollflächen-Scrim (Klick-/Escape-Ziel = $cancelHref, ein normaler Link, funktioniert dadurch vollständig ohne JavaScript) plus ein Panel fester Breite. Generalisiert die zuvor in DbAdmin::renderNewTableDrawer()/renderAddColumnDrawer()/ renderEditDrawer() duplizierte Struktur.

Parameter Typ Beschreibung
$title string Drawer-Überschrift, hsc()-escaped
$bodyHtml string vertrauenswürdiges HTML (z. B. ein Formular) – Aufrufer-escaped
$cancelHref string Scrim-/Abbrechen-Ziel-URL, hsc()-escaped

Erzeugtes Markup: <div class="wk-drawer"> → <a class="wk-scrim" href="…"> → <div class="wk-drawer__panel" role="dialog" aria-modal="true"> mit <h3>-Titel und $bodyHtml.

Nebenwirkungen: keine (reine Render-Methode, kein State/Request-Zugriff).

Fehlerverhalten: keines – alle drei Parameter sind Pflicht-Strings, ein leerer String ist für jeden Parameter ein gültiger, wenn auch funktional sinnloser Wert (leerer Titel/Body/Scrim-Ziel).

echo $widgets->drawer(
    'Neue Tabelle',
    $widgets->propertyGrid($spaltenFelder),
    wl($ID, ['ns' => 'tabellen'])
);
Metadatum Wert
Sprache PHP
Namespace helper_plugin_wkfluentui_widgets
Datei lib/plugins/wkfluentui/helper/widgets.php
Sichtbarkeit public
Stabilität stabil (Stufe 1)

modal(string $title, string $bodyHtml, string $cancelHref, array $opts = []): string – zentriertes Dialogfenster, gleiches Scrim-Prinzip wie drawer(), mittig statt seitlich verankert.

Parameter Typ Erforderlich Beschreibung
$title string Ja Dialog-Überschrift, hsc()-escaped
$bodyHtml string Ja vertrauenswürdiges HTML – Aufrufer-escaped
$cancelHref string Ja Scrim-/Abbrechen-Ziel-URL, hsc()-escaped
$opts['size'] string Nein normal (Default, 640px) oder workbench (1024×768, resize:both, Viewport-Clamp, <1024px vollflächig)
$opts['context'] string Nein setzt data-wk-dialog-context; scripts/dialog.js merkt die zuletzt gewählte Größe je Kontext in localStorage unter wk-dialog-size:<context>

Rückwärtskompatibilität: die 3-Parameter-Aufrufform (ohne $opts) rendert byte-identisch zur Vorversion bis auf ein zusätzliches tabindex=„-1“ am Panel (programmatisches Fokusziel der Fokusfalle – ohne JavaScript wirkungslos, kein sichtbarer Unterschied).

Abgrenzung: das Laden eines fremden Admin-Moduls in einen Dialog läuft nicht über modal(), sondern über den a[data-wk-dialog-src]-Trigger-Link-Vertrag (der Link navigiert ohne JavaScript normal und wird nur progressiv zum Inline-Workbench-Dialog) – Details: admin-layout → „Fremd-Modul-Aufrufe als Inline-Dialog“.

Nebenwirkungen: keine serverseitig; clientseitig (mit scripts/dialog.js): Fokusfalle, Escape-Handler, localStorage- Schreibzugriff bei workbench-Größe mit gesetztem context.

Fehlerverhalten: unbekannter $opts['size']-Wert degradiert still auf normal (kein Fehler, keine Warnung).

echo $widgets->modal(
    'Verbindung herstellen',
    $widgets->propertyGrid($verbindungsFelder),
    wl($ID),
    ['size' => 'workbench', 'context' => 'connect']
);

rowActions

Metadatum Wert
Sprache PHP
Namespace helper_plugin_wkfluentui_widgets
Datei lib/plugins/wkfluentui/helper/widgets.php
Sichtbarkeit public
Stabilität stabil (Stufe 1)

rowActions(array $items): string – verpackt bereits vorhandene, sichtbare Aktions-Elemente einer Baum-/Grid-Zeile als versteckte <ul class="wk-row-actions" hidden> (Kontextmenü-Quelle für scripts/contextmenu.js, s. u.).

Parameter Typ Beschreibung
$items array Liste ['label' => string, 'element' => string] – element ist das echte, bereits sichtbar gerenderte <a>-/<button>-HTML

Rückgabewert: HTML-String; leerer String bei leerem $items (keine Menü-Anzeige für eine Zeile ohne Aktionen – kein leeres, verwirrendes Kontextmenü).

Wichtige Invariante: rowActions() dupliziert keine Logik – jedes element muss identisch mit dem Element sein, das bereits sichtbar in der Zeile steht. Das Kontextmenü triggert bei Klick .click() auf das echte Original-Element, es öffnet keinen zweiten Code-Pfad.

Client-Gegenstück scripts/contextmenu.js (progressive Erweiterung): bei Rechtsklick auf ein Element mit data-wk-menu wird das native Kontextmenü unterdrückt und ein Popup aus dem benachbarten .wk-row-actions-Fragment gerendert. Ohne JavaScript bleibt jede Aktion über die sichtbaren Buttons erreichbar. Einzige Zusatzfunktion ohne No-JS-Äquivalent: „Copy“/„Copy with Headers“ über data-wk-copy/data-wk-copy-headers-Attribute (navigator.clipboard.writeText(…)).

Tastatur (WAI-ARIA-Menu-Muster): Die Einträge tragen role="menuitem"; beim Öffnen erhält der erste Eintrag den Fokus, Pfeil hoch/runter wandern zyklisch, Pos1/Ende springen zum ersten/letzten Eintrag, Escape schließt; beim Schließen kehrt der Fokus zum nächstliegenden fokussierbaren Element der Ursprungszeile zurück (dieselbe Mechanik wie die Menüs von scripts/admin-rail.js). Hinweis: derzeit rendert kein Konsument im lib/-Baum das data-wk-menu-Attribut – der Vertrag ist gegen das ausgelieferte Skript (scripts/contextmenu.js) verifiziert, der frühere Erstkonsument (SQL-Workbench-Ergebniszeilen) setzt es seit seiner Umstrukturierung nicht mehr.

Nebenwirkungen: keine serverseitig.

Fehlerverhalten: keines – ein element-Wert, der kein valides HTML ist, wird unverändert ausgegeben (der Aufrufer trägt die Verantwortung für valides, sicheres HTML in element, s. Escaping-Konvention Abschnitt 3).

$actions = $widgets->rowActions([
    ['label' => 'Bearbeiten', 'element' => '<a href="' . wl($ID, ['do' => 'edit']) . '">Bearbeiten</a>'],
    ['label' => 'Löschen', 'element' => '<button type="submit" name="do" value="delete">Löschen</button>'],
]);

workbench

workbench(array $regions, array $opts = []): string – komponiert die Shell-Regionen in das generische Workbench-Grid .wk-workbench (Layout-Schicht; die Region-Optik bleibt bei den .wk-shell-*-Primitives, die Innenleben bleiben Aufrufer-HTML).

  • $regions: Schlüssel commandbar · sidebar · content (Pflicht) · auxbar · rail · statusbar. Jeder Wert ist ein vertrauenswürdiges Aufrufer-HTML-Fragment (Aufrufer escapet – derselbe Vertrag wie drawer()/modal()). Fehlende/leere Regionen emittieren kein Element (die Grid-Variante lässt den Track kollabieren statt eine tote Lücke zu lassen). Trägt das äußerste Element des Fragments bereits die Region-Klasse, wird es unverändert durchgereicht (Attribute wie is-collapsed oder data-wk-auxbar-src bleiben erhalten); nackte Fragmente werden in das Standard-Element der Region gehüllt.
  • $opts['variant']: '''' (4-spaltig) · flat · no-sidebar · no-auxbar · auxbar-collapsed · split – muss einem CSS-Archetyp entsprechen; unbekannte Werte fallen auf das volle Grid zurück. split stellt Content und Auxbar als zwei gleich breite Hälften nebeneinander (minmax(0,1fr) minmax(0,1fr)): der Archetyp für eine zweite, eigenständige Arbeitsfläche neben der ersten – im Unterschied zu no-sidebar, dessen feste 280px-Spalte für „Eigenschaften der Auswahl“ gedacht ist und für eine Arbeitsfläche zu schmal. Unter 1024px stapeln die Hälften; die zweite bleibt sichtbar (sie ist offen, weil der Benutzer sie geöffnet hat – anders als die generische Auxbar, die dort ausgeblendet wird).
  • $opts['class']: zusätzliche Container-Klassen (Klartext, escaped) – der plugin-lokale Scope-Hook (z. B. wkq-shell).
  • $opts['context']: stabiler Flächen-Bezeichner (Klartext, escaped) → data-wk-workbench-context, der Persistenz-Schlüssel für Client-Präferenzen (Sash-Breiten). Ohne Kontext keine Persistenz.
  • $opts['auxbarOpen']: true sagt, dass die Auxbar hält, wonach der Leser gerade gefragt hat – die gewählte Datei, den geöffneten Datensatz. Unter 1280px öffnet scripts/workbench.js sie beim Laden als Overlay, ohne den Fokus zu verschieben; ohne JavaScript fließt sie unter 1024px unter den Inhalt. Wirkt nur, wenn es eine Auxbar gibt. Nicht durch is-open vom Server ersetzen: der Tab-Fang von workbench.js prüft nur diese Klasse und hielte die Tastatur sonst auch bei Desktopbreite in der Spalte fest.
  • $opts['sidebarDocked']: true hält die Seitenleiste bis 600px Fensterbreite als Spalte, statt sie unter 1024px zur Überlagerung zu machen (mit Skript) oder unter 768px auszublenden (ohne). Für ein Werkzeugfenster, dessen Aufgabe das Finden des Orts ist – das Medien-Popup des Editors öffnet mit 750×500, und der Namensraumbaum hinter einem Umschalter machte die Ordnerwahl zu zwei Schritten. Unter 600px bleibt es die Überlagerung wie überall: eine Spalte ließe dem Inhalt dort zu wenig Platz. Wird nur geschrieben, wenn es eine Seitenleiste gibt.
  • $opts['sidebarTitle']: Name der Seitenleiste (Klartext, escaped). Die Region bekommt eine eigene, oben haftende Kopfzeile mit diesem Namen, in der auch der Knopf zum Verkleinern sitzt; scripts/workbench.js beschriftet damit auf schmalen Breiten den schwebenden Umschalter der Seitenleiste. Ohne Namen keine Kopfzeile, und der Umschalter bleibt ein unbeschriftetes Symbol.

Verhaltens-Schicht: scripts/workbench.js (Overlay/Scrim/Region-Toggles) und scripts/sash.js (Resize) docken über die Container-Klasse bzw. das Kontext-Attribut an – Regeln in FluentUI: Admin-Bereich (vertieft).

statusbar

statusbar(array $left, array $right = [], array $opts = []): string – Statusbar-Region mit Gruppen-/Item-Anatomie und Prioritäts-Vertrag.

  • Item-Form: ['text' => …] (Klartext, escaped) oder ['html' => …] (vertrauenswürdiges Aufrufer-HTML, gewinnt über text); optional 'priority' => int (bereits die Anwesenheit ist das Signal – Items ohne Priorität verschwinden unter 1024px) und 'title' => … (Klartext-Tooltip, escaped).
  • Beide Gruppen-Spans rendern immer (Flex-Anker auch bei leerer Seite); der Mitteltrenner zwischen Items ist dekoratives CSS, nie Textinhalt.
  • $opts['class']: zusätzliche Container-Klassen (Klartext, escaped).

Warnung: eine Statusbar, deren Items alle keine Priorität tragen, ist unter 1024px leer – die überlebenswichtigen Items immer markieren.

accordion

Seit 2026-07-17 (Design-System-Vereinheitlichung, WDX-WdxAccordion-inspiriert, s. widgets → Katalog (Accordion)).

accordion(array $items, array $opts = []): string – Progressive-Disclosure-Akkordeon aus nativen, exklusiven <details name="…">-Gruppen (Öffnen eines Abschnitts schließt die anderen derselben Gruppe automatisch) – kein JavaScript für die Kernfunktion nötig.

  • $items: je Eintrag ['summary' => string (Klartext, escaped), 'bodyHtml' => string (vertrauenswürdiges Aufrufer-HTML), 'id' => string (optional, HTML-''id'' des ''<details>''), 'open' => bool (optional, Standard-offener Abschnitt)].
  • $opts['context']: Exklusivitäts-Gruppenname, validiert gegen ^[a-z][a-z0-9-]*$ (ungültig/fehlend → default). Zwei accordion()-Aufrufe mit demselben Kontext auf derselben Seite bilden eine Exklusivitätsgruppe über beide Aufrufe hinweg (der Wert wandert 1:1 in <details name="wk-accordion-<context>">; HTML gruppiert per name unabhängig von der DOM-Position) – nützlich, wenn ein Aufrufer mehrere accordion()-Blöcke rendert, die trotzdem gemeinsam exklusiv bleiben sollen (Erstkonsument: wkfluentui/action/admintoc.php, ein Akkordeon-Block je getTOC()-Abschnitt, gemeinsamer Kontext admintoc).

Rückgabewert: string – <div class="wk-accordion">…</div>. Leerer String bei leerem $items.

Kompatibilität: <details name>-Exklusivität landete in Chrome/Edge 120, Firefox 122, Safari 17.2 (~2023/24). Ältere Browser ignorieren die Gruppierung und lassen jeden Abschnitt unabhängig offen – identisch zu einer einfachen <details>-Liste ohne name, kein Funktionsverlust, nur ohne die Exklusivitäts-Politur.

$widgets = plugin_load('helper', 'wkfluentui_widgets');
echo $widgets->accordion([
    ['summary' => 'Sprache', 'bodyHtml' => $langFieldsetHtml, 'id' => 'lang', 'open' => true],
    ['summary' => 'Titel', 'bodyHtml' => $titleFieldsetHtml, 'id' => 'title'],
], ['context' => 'admintoc']);

JS-Verträge: wkToast, Sash, Dichte

  • wkToast(type, text, opts) (scripts/toast.js): type ∈ info/success/warning/error (unbekannt → info); text ist Klartext und wird nie als HTML interpretiert; opts.timeout in ms (Default 6000; error ignoriert ihn und bleibt bis zum Schließen); Rückgabe ist eine Dismiss-Funktion. Nur für asynchrone JS-Rückmeldungen – msg() bleibt der Kanal für Vollseiten-POSTs.
  • Sash (scripts/sash.js): injiziert je vorhandener Sidebar/Auxbar eine role="separator"-Leiste; schreibt --wk-workbench-<region>-width inline auf den Container; Persistenz unter wk-workbench-size:<context>:<region> (re-geklemmt: Sidebar 180–480px, Auxbar 200–520px).
  • Dichte: body[data-wk-density="compact"] ist der einzige Schalter der Wirkschicht (Sektion DENSITY in style.css); wer den Umschalter stellt (Template-User-Menü) und wie persistiert wird (wk-density:<login>), gehört dem Template.

Der Verwaltungsbildschirm: listView und was dazugehört

Seit dem 20.09.2026 folgen alle Verwaltungsbildschirme einer Anordnung, und der Bausatz liefert sie als einen Aufruf. Die Regel, wem welcher Ort gehört, steht in docs/how-to-admin-screen.md.

  • listView(array $opts) – die Anordnung von oben nach unten: noticeHtml (Ausgang der letzten Handlung), views/active/viewsLabel (die Nebenansichten als Reiter, als benanntes <nav>), commands/commandsLabel (Befehle der Liste), filtersHtml (was die Liste einengt), bodyHtml (der Arbeitsvorrat, meist ein füllendes Raster), drawersHtml (Anlegen, Bearbeiten, Ansehen – geschlossen, bis ein Befehl sie öffnet). Nur der Rumpf nimmt die Resthöhe; ein Raster darin rollt seine Zeilen unter einem stehenden Kopf.
  • prose($html, $opts) – ein Block Fließtext auf einer Werkbankfläche. Hält das Zeilenmaß (68 Zeichen) und schneidet den ersten und letzten Abstand ab. Leerer Text ergibt nichts. Für fertig gerendertes Markup, nie für Werte aus einer Anfrage.
  • menuButton($label, array $elements, array $opts) – verwandte Befehle hinter einem Knopf; mit primary wird daraus ein geteilter Knopf (die übliche Handlung neben dem Menü ihrer Spielarten).
  • rowMenu(array $elements, array $opts) – das Kebab hinter dem Benennungsfeld einer Zeile. Die Einträge sind fertige Bedienelemente des Aufrufers; label benennt die Zeile für den zugänglichen Namen.

Dazu kamen vier Angaben an bestehenden Bausteinen:

  • button(['badge' => '23', 'badgeLabel' => '…']) – die Zahl am Knopf, aus demselben countBadge() wie am Reiter.
  • textBox/textArea(['mono' => true]) – der Inhalt ist Maschinentext (Protokollzeile, Pfad, Schlüssel).
  • textArea(['fill' => true]) – das Feld nimmt die Resthöhe seiner Fläche, wie ein füllendes Raster.
  • form(['inline' => true]) – ein Formular, das liegt: die Filterleiste über einer Liste.
  • dataGrid([... 'selectionName' => 'sel', 'selectionForm' => 'wkq-bf']) – wie die angehakten Zeilen auf der Leitung heißen und mit welchem Formular sie abgeschickt werden. Für Bildschirme, deren Gegenstelle älter ist als das Raster.

detailLayout

Die Ansicht eines einzelnen Datensatzes: eine Hauptspalte für das, was man liest – Beschreibung, Unterhaltung –, und eine Seitenspalte für das, was man nachschlägt – die Eigenschaften als propertyGrid(). Das ist die Anordnung, in der die Vorbild-Werkbänke ein Arbeitselement zeigen.

$w->detailLayout([
    'main'       => $beschreibungUndKommentare,  // vertrauenswürdiges HTML
    'aside'      => $w->propertyGrid($zeilen, ['mode' => 'display']),
    'asideLabel' => 'Felder',                      // Name der Seitenspalte
    'stackBelow' => 720,                           // Vorgabe
]);
  • Gestapelt wird nach der Breite des Behälters, nicht des Fensters. Eine solche Ansicht steht oft in der Lesespalte des Wikis; eine Fensterabfrage würde dort zwei Spalten erzwingen, wo nur Platz für eine ist. Gemessen wird wie bei propertyGrid() über data-wk-stack-below und scripts/adaptive.js. Ohne Skript gilt unter 768 px Fensterbreite als Boden immer eine Spalte.
  • Die Seitenspalte hat eine feste Breite aus --wk-detail-aside-width (320 px), die Hauptspalte nimmt den Rest.
  • Die Seitenspalte ist ein benanntes <aside> und steht im Dokument nach der Hauptspalte – die Lesereihenfolge ist in beiden Anordnungen dieselbe.
  • Ohne Seitenspalte entsteht eine einzelne Spalte statt einer leeren, die 320 px belegt.
  • Die Fläche stellt die Schrift des Bausatzes her (--wk-fs-body / --wk-lh-body).

Erster Verbraucher: die Arbeitselement-Ansicht (wkdoadowi). Gemessen auf live: 527 px + 320 px in der Lesespalte bei 1400 px Fenster, eine Spalte bei 390 px.

recordHeader

Der Kopf eines Datensatzes: eine kleine Kennzeile (was es ist), darunter der Titel, rechts die Befehle. Das ist die Anatomie, in der die Vorbild-Werkbänke einen einzelnen Datensatz eröffnen – das Arbeitselement in Azure DevOps, das Objekt in Azure Data Studio.

$w->recordHeader([
    'kickerHtml'   => $symbol . '<span>User Story 2228</span>',  // vertrauenswürdiges HTML
    'titleField'   => $w->textBox([... , 'heading' => true, 'bare' => true, 'form' => 'mein-formular']),
    'title'        => 'Ohne titleField: der Titel als Klartext',
    'commandsHtml' => $w->toolbar($befehle, ['variant' => 'bare']),
    'metaHtml'     => '<span>1602 gefunden</span>',
    'sticky'       => true,   // Vorgabe
    'stackBelow'   => 560,    // Vorgabe
]);
  • Wer bearbeiten darf, bearbeitet den Titel an seiner Stelle. titleField nimmt ein Eingabefeld (textBox mit 'heading' => true) und stellt es dorthin, wo sonst der Titel steht – ein Titel, nicht ein Titel und darunter ein Feld mit demselben Text. Das Feld liegt in der <h1>, damit die Überschriftennavigation eines Vorleseprogramms den Datensatz weiter findet.
  • Das Feld sieht erst beim Anfassen wie eines aus: Überschriftgröße (--wk-fs-heading / --wk-lh-heading), rahmenlos, Rahmen beim Überfahren, Fokusring beim Fokus. Ein Feld, das nie wie eines aussieht, wird nicht als bearbeitbar erkannt; eines, das immer so aussieht, macht den Kopf zum Formular.
  • Der Kopf klebt oben (position: sticky) in einem rollenden Behälter, damit die Befehle – allen voran Speichern – erreichbar bleiben, während man in einer langen Beschreibung arbeitet. 'sticky' => false für Bildschirme, die nicht rollen.
  • Gestapelt wird nach der Breite des Behälters (data-wk-stack-below, scripts/adaptive.js): die Befehle gehen unter den Titel.
  • Titelfeld und Befehle stehen vor dem Formular; sie gehören ihm über das HTML-Attribut form. Ein zweites Speichern im Formular wäre ein zweiter Knopf für dieselbe Sache.

Erster Verbraucher: die Arbeitselement-Ansicht, ihre Liste und ihr Anlegeformular (wkdoadowi).

8. Vollständige Beispiele

Admin-Rail plus Avatar-Initialen in einer Template-Kopfzeile kombiniert (realistischer Ausschnitt, wie ihn ein Template nach Merge des Refactor-Branches einsetzen würde):

<?php
/** @var helper_plugin_wkfluentui_adminrail $rail */
$rail = plugin_load('helper', 'wkfluentui_adminrail');
/** @var helper_plugin_wkfluentui_user $userHelper */
$userHelper = plugin_load('helper', 'wkfluentui_user');
 
global $INFO, $INPUT;
?>
<nav class="wk-shell-rail">
<?php foreach ($rail->getAdminRailItems($INPUT->str('page')) as $item): ?>
    <a href="<?php echo $item['href']; ?>"<?php echo $item['active'] ? ' class="active"' : ''; ?>
       aria-current="<?php echo $item['active'] ? 'page' : 'false'; ?>">
        <?php echo $item['icon']; ?>
        <span class="a11y"><?php echo hsc($item['label']); ?></span>
    </a>
<?php endforeach; ?>
</nav>
<div class="wk-avatar-placeholder">
    <?php echo hsc($userHelper->avatarInitials($INFO['userinfo']['name'] ?? '', $INFO['userinfo']['login'] ?? '')); ?>
</div>

9. Einschränkungen und Randfälle

  • Kein Helper dieser API greift auf $_REQUEST/$_POST zu oder verändert Zustand — reine Render-/Enumerations-Methoden. Formular-Verarbeitung (POST-Handling, sectok-Prüfung) bleibt vollständig Sache des Aufrufers.
  • navs Cache-Lebensdauer (1800s) ist fest verdrahtet (CACHE_AGE-Konstante), nicht konfigurierbar über $opts — eine Änderung erfordert einen Code-Eingriff.
  • gallery::listImages() ist nicht rekursiv — Bilder in Unter-Namespaces von $ns werden nicht gefunden, das ist bewusst so (keine versehentliche Tiefenexplosion bei großen Medienbäumen).

10. Accessibility und Kompatibilität

  • PHP: kompatibel mit der PHP-Mindestversion von DokuWiki selbst (keine über den Core hinausgehenden Sprachfeature-Anforderungen in dieser API).
  • Browser/JavaScript: jede Methode dieser API liefert funktionsfähiges Markup ohne JavaScript (Grundsatz, s. Abschnitt 3/Konzepte); die zugehörigen scripts/*.js-Dateien sind ausnahmslos progressive Erweiterungen, nie Voraussetzung.
  • Barrierefreiheits-Details je Methode stehen in ihrem jeweiligen Abschnitt oben bzw. — für die Methoden mit eigener Komponentenseite (tree(), tabs(), propertyGrid(), dataGrid(), treeList(), toolbar(), navBar(), codeEditor() und die Eingabefelder-Familie) — auf deren dedizierten Seiten (Verweise: Abschnitt 6).

11. Troubleshooting

Symptom: Fatal error: Call to a member function toolbar() on null (oder jede andere Methode dieser API) direkt nach plugin_load('helper', 'wkfluentui').

Ursache: wkfluentui hat kein Wurzel-helper.php — der Aufruf ohne Komponenten-Suffix liefert null, lautlos, ohne eigenen Fehler (s. Abschnitt 3). Dieser Namensfehler blieb in wk-dw-msqlite-plugin längere Zeit unbemerkt, weil DokuWiki selbst keine Warnung dafür ausgibt.

Lösung: plugin_load('helper', 'wkfluentui_<component>') mit dem korrekten Suffix aufrufen — widgets, adminrail, gallery, nav, user oder formpage (Tabelle in Abschnitt 6).

12. Verwandte Themen

1) , 2) , 3)
s. vorige Fußnote – erst ab Merge des Branches.
de/wiki/dwe/wkfluentui/component/api.txt · Zuletzt geändert: von 0.0.0.0