-
Notifications
You must be signed in to change notification settings - Fork 41
example related entities
Any window whose content identity carries related-entity items shows a "Related" button in its title bar (network icon, right side). The dropdown lists ready-to-open navigation targets — for posts and pages the plugin builds Comments (edit-comments.php?p={id}, with count), one item per assigned term (term.php?taxonomy=…&tag_ID=…), one item per associated media (upload.php?item={id}), and one Linked posts item per internal hyperlink resolving to another post on this site, automatically. Picking an item opens it as its own desktop window. Inside the block editor the list refreshes after every save (the bridge refetches a server-recomputed identity over REST) — no reload needed.
Both ends are open: a PHP filter adds items for any screen (runs server-side in real admin context, right after the content identity resolves), and a JS filter rewrites the resolved list per window.
Built-ins cover post and page only. A CPT (or any screen that announces an identity — see window-links.md) contributes its own:
add_filter( 'openstation_window_related_entities', function ( $related, $identity, $screen ) {
if ( 'acme_order' !== $identity['type'] ) {
return $related;
}
$order_id = (int) $identity['id'];
// Jump to the order's customer profile.
$related[] = array(
'id' => 'acme/customer-' . acme_order_customer_id( $order_id ),
'group' => 'acme/customers', // your own menu section
'groupLabel' => __( 'Customer', 'acme' ), // section header
'label' => acme_order_customer_name( $order_id ),
'icon' => 'dashicons-businessperson',
'url' => admin_url( 'admin.php?page=acme-customer&c=' . acme_order_customer_id( $order_id ) ),
);
// Jump to the order's invoices, with a count suffix.
$related[] = array(
'id' => 'acme/invoices',
'group' => 'acme/invoices',
'groupLabel' => __( 'Billing', 'acme' ),
'label' => __( 'Invoices', 'acme' ),
'icon' => 'dashicons-media-spreadsheet',
'url' => admin_url( 'admin.php?page=acme-invoices&order=' . $order_id ),
'count' => acme_order_invoice_count( $order_id ), // renders "Invoices (3)"
);
return $related;
}, 10, 3 );id, group, label, and url are required (non-empty strings); malformed entries are dropped server-side, unknown fields stripped. The filter runs only when an identity resolved and after the openstation_window_content_identity filter — so an identity you inject for your own screen gets the related pass too. Removing built-ins works the same way: filter $related down.
The resolved list runs through os.related-entities.items on every visibility check and menu build. Context carries the window id and its current WindowContentRef:
wp.hooks.addFilter(
'os.related-entities.items',
'my-plugin/hide-media-group',
( items, { windowId, content } ) => {
// Drop the Media section everywhere…
items = items.filter( ( item ) => item.group !== 'media' );
// …and add a client-side target for posts.
if ( content?.type === 'post' ) {
items.push( {
id: 'my-plugin/preview',
group: 'my-plugin/tools',
groupLabel: 'Tools',
label: 'Live preview',
icon: 'dashicons-visibility',
url: `${ window.openStationConfig.adminUrl }admin.php?page=my-preview&post=${ content.id }`,
} );
}
return items;
},
);Return an empty array to hide the button for a window entirely. Malformed entries are dropped item-wise; a non-array return falls back to the identity's own list.
const ref = wp.os.relations.get( windowId );
console.log( ref?.related ); // → RelatedEntityItem[] | undefinedThe button repaints automatically whenever the window's content identity changes (os.window-links.content-changed), including in-window navigations — no manual refresh needed.
Group ordering in the menu: comments, then every terms/{taxonomy}, then media, then links, then vendor groups in arrival order. Reference: hooks-reference · javascript-reference.
This wiki is generated from the docs/ directory — edits made here are overwritten by the next sync.
To change a page, open a pull request against docs/.
Guides
- Development guide
- Releasing openstation
- Agents security model
- API Index
- Architecture
- Bridge protocol — wiring overview
- <os-*> component reference
- Native Desktop Host — Experimental
- Desktop themes
- Dock customization — two registries, one mental model
- The event-driven framework
- Files on the Desktop
- Folder sharing
- Getting Started
- Hooks Reference
- Icons
- JavaScript Reference
- The Living Tree — algorithm definition
- Mio
- Native Windows & Framework Interop
- Plugin compatibility layer
- Progressive Web App (PWA)
- Station Home
- Using openstation from your own plugin
Migration notes
- Migration: built-in activity channels move to the os/ namespace
- Migration: window, wallpaper and widget bundles load on demand
- Migration — the navigation model
- Migration: a native window's tabs move to the window chrome
All examples
- AI Agents — extend and invoke from a plugin
- wp.os.ai.ask() — programmatic AI Copilot
- Tune the AI model config
- Custom arrange-menu action
- Open a child window its owner can't cover
- Style a specific admin page inside the iframe
- Code Blue — register your plugin's log file
- Open a file in the Code editor (deep-link from any window)
- Connect to a window — title-bar button + iframe pub/sub
- Content changes — live-refresh every window listing your type
- Custom window chrome (Experimental)
- Register a custom unfocused-window effect
- Example: render a data table
- Real file storage — react to uploads, gate policy, share from PHP
- React to a window being set free onto the real desktop
- Cross-window devtools — instrumentation primitives
- Add a dock item with a badge
- Decorate the dock without forking the renderer
- Replace the dock rail entirely
- Retune the Drafts widget's AI writing assistant
- Gate OpenStation by role
- Iframe-initiated window opens
- Build a feed reader without the bookkeeping
- Inject data into openStationConfig
- Render a list without losing clicks — renderKeyedList()
- Example: layout primitives (body → panel → row → col)
- Use <os-*> components from a plugin that ships as a zip
- Restyle and drive Mio
- Add an action that works on a whole selection
- WP Explorer — custom post types and their folder
- Add an action button to a WP Explorer preview pane
- Example: native Posts window
- Example: native window with tabs
- Native windows
- Customize note → post conversion
- Send a notification
- OAuth relay — connect to an external service
- OS-file drop
- <os-flyout> — window-scoped sliding card
- Plugins window — extras
- Track who's around — wp.os.presence
- Example: progress bar
- PWA install — surface your own button
- React to window events
- Example: extend the Trash
- Register a slash-command
- Register a desktop theme from a plugin
- Register a game
- Example: register a desktop icon (Jorvy)
- Register a wallpaper
- Register a widget
- Related entities — extend the title bar's "Related" menu
- The native-window render ctx
- Programmatic folder sharing
- Share state across multi-bundle plugins — wp.os.createSharedStore()
- Example: loading spinner
- Add an opt-in card to Station Home
- Accept drops on your desktop icon
- Give a tile two icons, one per state
- Add a row to a window's ⋯ menu
- Example: window activity & the status ring
- Window controls
- Subscribe to window lifecycle events
- Window links — relate windows and restyle the ties (Experimental)
- Window loading state — spinner overlay & ready signal
- Show a banner at the top of a window
- Pulse a window's icon — Window.requestAttention()
- Register a custom window reveal
- Window slots
- Window themes
- Native window with bundle-bound config