registerIntegration
Load-order-safe entry point for a leaf app to register its own bespoke integration component (Path 2). It is the symmetric counterpart to installIntegrationRegistry, which OpenRegister calls to install its singleton.
A leaf app must not call installIntegrationRegistry — that replaces the global with OpenRegister's singleton and would clobber it. Use registerIntegration instead.
When to use it
Two ways a leaf surfaces in a host object's sidebar / detail page:
- Path 1 — data only. Ship a PHP
IntegrationProvider; OpenRegister's genericCnIntegrationTab/CnIntegrationCardrender the rows. No JS contribution, works cross-app automatically. - Path 2 — bespoke component. Ship a Vue component and register it with
registerIntegration. Use this only when the generic title/subtitle/url card can't express the UX.
registerIntegration is the Path-2 hook.
Signature
import { registerIntegration } from '@conduction/nextcloud-vue'
registerIntegration(descriptor) // attach to window (default)
registerIntegration(descriptor, fakeWindow) // attach to a test double
descriptor is the same shape as integrations.register() — { id, label, icon, tab, widget, group, order, referenceType, … }. Validation, the AD-13 collision policy, the required tab / widget, and referenceType defaulting all apply identically whether the call lands live or via replay.
Load-order behaviour
- OpenRegister already installed its singleton → the descriptor registers live and appears in
integrations.list()immediately. - OpenRegister not loaded yet (this leaf — or another leaf — ran first) → a
{ _queue, register }stub is installed/extended onwindow.OCA.OpenRegister.integrationsand the descriptor is queued. OpenRegister replays the queue when it later callsinstallIntegrationRegistry.
You never have to hand-roll the stub — that's the whole point of this helper.
Shipping a Path-2 leaf end-to-end
Three pieces. The JS contract (this helper) is centralised in nextcloud-vue; the other two are inherently per-app.
1. A small global JS entry (src/integration.js) — keep it tiny, just the component + the call:
import { registerIntegration } from '@conduction/nextcloud-vue'
import SyncedFromTab from './integration/SyncedFromTab.vue'
registerIntegration({
id: 'sync-contract',
label: 'Synced from',
icon: 'SyncOutline',
group: 'workflow',
order: 50,
tab: SyncedFromTab,
widget: SyncedFromTab,
})
2. A separate webpack entry so the global bundle is small (not the whole SPA):
// webpack.config.js
entry: {
'myapp-main': path.join(__dirname, 'src', 'main.js'),
'myapp-integration': path.join(__dirname, 'src', 'integration.js'),
}
3. Server-side global-script registration so the bundle loads on every Nextcloud page (so the component is present when a foreign app — e.g. OpenCatalogi — renders its detail page), not only the leaf's own SPA routes:
// lib/AppInfo/Application.php — in a BeforeTemplateRenderedEvent listener or boot()
\OCP\Util::addInitScript('myapp', 'myapp-integration');
The consuming app needs nothing — it renders whatever is in the registry via CnDetailPage / CnObjectSidebar and useIntegrationRegistry.