Octibiz
Demo

Durchsucht Website und Dokumentation gemeinsam. Enter zeigt alle Treffer, Esc schließt.

Plugin-Oberfläche

Ein Plugin bringt eigene Ansichten mit und hängt sich an bestehende. Beides läuft über eine einzige Datei: frontend/plugin.manifest.ts.

Das Manifest

Sie exportiert ein Objekt. Jeder Schlüssel ist optional — ein Plugin ohne Oberfläche lässt die Datei ganz weg.

import type { OctibizPluginManifest } from '@/plugins/manifest'

const manifest: OctibizPluginManifest = {
  routes: [
    {
      path: 'acme-shipping',
      name: 'acme-shipping',
      component: () => import('./views/ListView.vue'),
      meta: { permission: 'acme.shipping.view' },
    },
  ],

  routeModules: { 'acme-shipping': 'acme-shipping' },

  localeLoaders: {
    de: () => import('./locales/de'),
    en: () => import('./locales/en'),
  },
}

export default manifest

Gefunden wird die Datei beim Bau, aus drei Quellen: eingecheckte Plugins, per Paket installierte und über den Paket-Manager eingebundene. Alle drei werden in dieselbe Oberfläche gemischt.

Vier Regeln, die nicht verhandelbar sind

1. Kern-Bausteine nur über @/sdk. Das ist die stabile Fläche. Tiefe Verweise auf andere Kern-Module werden abgewiesen, weil sie beim nächsten Umbau brechen. Zusätzlich erlaubt sind reine Typ-Verweise auf die Manifest-Fläche.

2. Komponenten immer als Nachlader, also () => import('./views/FooView.vue'). Ein direkter Verweis zieht die Komponente in das Startpaket jedes Nutzers, auch wenn er das Modul nie öffnet.

3. Beim Zusammenführen gewinnt der Erste. Der Kern vor den Plugins, Plugins in stabiler Reihenfolge. Ein Konflikt erzeugt eine Warnung, keinen Abbruch: Ein fehlerhaftes Plugin darf die Oberfläche nie zerlegen.

4. Kein eigenes Aussehen. Es gibt einen verbindlichen Baustein-Katalog. Ein Plugin, das eigene Knöpfe definiert, sieht nach zwei Systemupdates falsch aus.

Die wichtigsten Schlüssel

SchlüsselWofür
routesEigene Seiten, Pfade ohne führenden Schrägstrich
navEine neue Navigationsgruppe
navLinksEin Link in eine bestehende Gruppe, auch die des Kerns
routeModulesModul-Sperre für eigene Routen
settingsTilesKachel in den Einstellungen
widgetsBaustein für das Start-Dashboard
viewExtensionsBeiträge zu Andockstellen fremder Ansichten
columnExtensionsZusätzliche Spalten in fremden Listen
filterExtensionsZusätzliche Filter in fremden Listen
viewOverridesEine Ansicht ersetzen oder verbergen
searchTypesEigene Objekte in der globalen Suche
tagScopesEigene Objekte als Ziel für Schlagworte
localeLoadersÜbersetzungen, siehe Übersetzungen
portalRoutes, portalNavBeiträge zum Kundenportal

Die Modul-Sperre der Oberfläche

routeModules: { 'acme-shipping': 'acme-shipping' }

Links steht das Routen-Präfix, rechts der Kurzname des Modul-Schalters ohne module..

Die beiden verwechselt zu haben ist ein realer, teuer gewordener Fehler. Trifft der rechte Wert keinen echten Schalter, steht er nie in der Liste der abgeschalteten Module — und die Sperre hält das Modul immer für eingeschaltet. Sie wirkt dann nie.

Andockstellen

Der Kern rendert benannte Stellen, an die Plugins Beiträge hängen. Ohne Beitrag rendert die Stelle nichts.

viewExtensions: {
  'customer-detail.tabs': [
    {
      id: 'acmeShipping.customerShipments',
      component: () => import('./components/CustomerShipmentsTab.vue'),
      permission: 'acme.shipping.view',
      module: 'acme-shipping',
      order: 40,
      meta: { labelKey: 'acmeShipping.tab.shipments' },
    },
  ],
}

Jeder Beitrag ist doppelt gesperrt: über das Recht und über den Modul-Schalter. Beides fail-closed. Ein abgeschaltetes Plugin verschwindet aus der Oberfläche, ohne dass die Kern-Ansicht davon weiß.

Die Namen folgen einem festen Schema: <objekt>-detail.actions, .tabs, .panels für Detailansichten, <objekt>-list.actions für Listen. Die vollständige Liste aller 84 Stellen mit ihren übergebenen Werten steht in der Andockstellen-Referenz. Sie wird aus dem Code erzeugt und kann deshalb nicht veralten.

Spalten, Zeilen-Aktionen, Filter, Formularfelder

Listen und Dialoge haben eigene Stellen mit eigenen Verträgen:

  • Spalten über columnExtensions, die Zeile wird übergeben.
  • Aktionen je Zeile über viewExtensions auf <liste>.row-actions.
  • Filter über filterExtensions. Der Wert fließt in dieselbe Filterstruktur wie ein Kern-Filter.
  • Formularfelder über viewExtensions auf <objekt>-form.fields. Der Beitrag liest und

schreibt das Formularmodell direkt.

Ersetzen statt ergänzen

viewOverrides: {
  'customer-detail.panels': { mode: 'hide' },
}

Ein Override ist autoritativ: Er verdrängt alle additiven Beiträge an dieser Stelle. replace ersetzt durch eine eigene Komponente, hide blendet aus.

Das ist ein scharfes Werkzeug. Zwei Plugins, die dieselbe Stelle überschreiben, vertragen sich nicht — hier gewinnt wieder der Erste.

Das Kundenportal ist anders gesperrt

Portal-Beiträge laufen über eigene Stellen und ohne die Sperre über Recht und Modul. Das Portal hat eine eigene Anmeldung; es kennt weder die Rechte der Mitarbeitenden noch die Liste abgeschalteter Module.

Dort sperrt sich der Beitrag selbst: Er ruft seinen eigenen Endpunkt und blendet sich still aus, wenn der nichts liefert. Bei abgeschaltetem Modul antwortet der Endpunkt ohnehin mit 404.

Die stabile Oberflächen-Schnittstelle

Alles, was ein Plugin vom Kern braucht, kommt aus @/sdk: Bausteine, Formularelemente, Datenabruf, Berechtigungsprüfung, Benachrichtigungen, Formatierung.

Was dort nicht steht, ist bewusst nicht Teil der Zusage. Wenn dir etwas fehlt, ist das ein Grund, die Fläche zu erweitern, kein Grund für einen tiefen Verweis.

Übersetzungen nachladen, nicht importieren

localeLoaders: {
  de: () => import('./locales/de'),
  en: () => import('./locales/en'),
}

Nicht als direkter Verweis. Eine Messung hat gezeigt, dass statische Sprachdateien 651 KB komprimiert zum ersten Bildaufbau beitrugen, für Sprachen, die die meisten Nutzer nie sehen. Details in Übersetzungen.

Was schiefgeht, und woran es liegt

SymptomUrsache
Die Sperre wirkt nie, das Modul gilt immer als anIm routeModules steht rechts das Routen-Segment statt des Schalter-Kurznamens
Der Beitrag erscheint nichtRecht fehlt, Modul aus, oder ein Override verdrängt ihn
Das Startpaket ist plötzlich großKomponente oder Sprachdatei direkt verwiesen statt nachgeladen
Der Bau bricht mit einem abgewiesenen Verweis abTiefer Zugriff auf ein Kern-Modul statt über @/sdk
Nach einem Systemupdate sieht das Plugin fremd ausEigene Ausgabe-Elemente statt der Bausteine des Katalogs

Weiter

plugin.frontend · Gilt ab Version 0.5.0