` markup.
*/
function openstation_my_wordpress_icon_svg() {
return '';
}
/**
* Whether the current user should see My WordPress.
*
* Mirrors the recycle-bin gate — anyone who can edit posts can
* browse posts and pages.
*
* @return bool
*/
function openstation_my_wordpress_user_can_use() {
$can = current_user_can( 'edit_posts' );
/**
* Filter whether the current user can see the My WordPress
* pinned icon and window.
*
* @param bool $can Default: edit_posts capability.
*/
return (bool) apply_filters( 'openstation_my_wordpress_user_can_use', $can );
}
/**
* Build the entity list shipped to the bundle. Posts, Pages,
* Users, and Media. Future phases add
* Comments, Tags, Categories, Themes, and Plugins.
*
* The optional `kind` field tells the bundle how to render entries
* of this entity: `'post'` (default) renders the standard
* title/excerpt/featured-image tile and the rendered-HTML preview;
* `'user'` renders an avatar + display-name tile and routes to the
* user dossier preview; `'media'` renders a thumbnail-grid tile and
* routes to the media preview pane. Plugins extending the entity
* list with a post-shaped collection can omit the field; user- and
* media-shaped collections must set `'user'` / `'media'`.
* The optional `post_type` field specifies the WP post-type
* slug used for `os..changed` cross-window broadcasts.
*
* @return array[] Each entry is `array( 'id', 'label', 'icon',
* 'restPath', 'kind', 'post_type' )`. `restPath` is appended to
* the `restRoot` config to derive the list URL.
*/
function openstation_my_wordpress_entities() {
$entities = array(
array(
'id' => 'posts',
'label' => __( 'Posts', 'desktop-mode' ),
'icon' => 'dashicons-admin-post',
'restPath' => 'wp/v2/posts',
'kind' => 'post',
'post_type' => 'post',
),
array(
'id' => 'pages',
'label' => __( 'Pages', 'desktop-mode' ),
'icon' => 'dashicons-admin-page',
'restPath' => 'wp/v2/pages',
'kind' => 'post',
'post_type' => 'page',
),
array(
'id' => 'users',
'label' => __( 'Users', 'desktop-mode' ),
'icon' => 'dashicons-admin-users',
'restPath' => 'wp/v2/users',
'kind' => 'user',
),
array(
'id' => 'media',
'label' => __( 'Media', 'desktop-mode' ),
'icon' => 'dashicons-admin-media',
'restPath' => 'wp/v2/media',
'kind' => 'media',
'post_type' => 'attachment',
),
);
/**
* Filter the list of entity types shown inside the My WordPress
* window. Each entry must declare `id`, `label`, `icon`, and
* `restPath`. Returning a reordered or extended array shows up
* in the bundle on the next render.
*
* **Status: Experimental** — the entity descriptor shape may
* gain fields as new entity kinds land (Comments, Tags,
* Categories, Themes, Plugins). Stable id/label/icon/restPath
* fields will continue to work; new optional fields will not
* break existing consumers. The `kind` field is optional and
* defaults to `'post'` for back-compat.
*
* Optional fields:
* - `kind` — render strategy (`'post'` default, `'user'`, `'media'`).
* - `post_type` — WP post-type slug for cross-window broadcast topic `os..changed`.
* - `thumbnails` — set false to keep the section icon on every tile
* instead of the entity's featured image.
* - `editAction` — who edits this section's rows. A preview-action id
* (see `openstation_my_wordpress_preview_actions`)
* replaces "Open in editor" everywhere — pane button,
* context-menu open entry, tile double-click. `false`
* removes every edit affordance (double-click falls
* back to the detail dossier; the bulk "Edit…" modal
* is suppressed). Omit for the classic editor.
* - `group` — folder id this section nests under at the root
* (null / omitted renders it loose at the root).
* - `groupLabel` — folder label. `groupIcon`, `groupOrder` follow.
*
* @param array[] $entities Default entities.
*/
$filtered = apply_filters( 'openstation_my_wordpress_entities', $entities );
return is_array( $filtered ) ? array_values( $filtered ) : $entities;
}
/**
* Render the My WordPress window's static template body. The bundle
* mounts its UI into `[data-os-my-wordpress-root]`.
*/
function openstation_my_wordpress_render_template() {
ob_start();
?>
$app_title,
'icon' => $icon_uri,
'template' => 'openstation_my_wordpress_render_template',
'script' => 'desktop-mode-my-wordpress',
// `styles` (companion), not `style`: loads on first open
// rather than at every shell boot. Declared FIRST so the
// WooCommerce integration's sheet — appended to this array by
// `openstation_my_wordpress_woo_window_args()` — lands after
// it in the head and keeps winning their equal-specificity
// overrides by source order.
'styles' => array( 'desktop-mode-my-wordpress' ),
'width' => 960,
'height' => 640,
'min_width' => 640,
'min_height' => 420,
'placement' => 'none',
'config' => array(
'restRoot' => esc_url_raw( rest_url() ),
'restNonce' => wp_create_nonce( 'wp_rest' ),
'editPostUrlBase' => esc_url_raw( admin_url( 'post.php' ) ),
'editUserUrlBase' => esc_url_raw( admin_url( 'user-edit.php' ) ),
'siteName' => $site_title,
'entities' => $entities,
'groups' => function_exists( 'openstation_my_wordpress_collect_groups' )
? openstation_my_wordpress_collect_groups( $entities )
: array(),
'perPage' => 24,
'mediaPerPage' => 48,
'previewActions' => function_exists( 'openstation_my_wordpress_collect_preview_actions' )
? openstation_my_wordpress_collect_preview_actions()
: array(),
),
);
/**
* Filter the args used to register the My WordPress native window.
*
* @param array $window_args Args passed to `openstation_register_window()`.
*/
$window_args = (array) apply_filters( 'openstation_my_wordpress_window_args', $window_args );
$registered = openstation_register_window( 'desktop-mode-my-wordpress', $window_args );
if ( is_wp_error( $registered ) ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log( '[openstation] My WordPress window registration failed: ' . $registered->get_error_message() );
return;
}
$icon_args = array(
'title' => $app_title,
'icon' => $icon_uri,
'window' => 'desktop-mode-my-wordpress',
'pinned' => true,
'position' => -1,
);
/**
* Filter the args used to register the My WordPress pinned icon.
*
* Removing `pinned` here lets the icon participate in normal
* sort order — useful for sites that want the shortcut to feel
* like any other plugin icon.
*
* @param array $icon_args Args passed to `openstation_register_icon()`.
*/
$icon_args = (array) apply_filters( 'openstation_my_wordpress_icon_args', $icon_args );
openstation_register_icon( 'desktop-mode-my-wordpress', $icon_args );
}
add_action( 'init', 'openstation_my_wordpress_register_window', 99 );
/**
* Re-collect the preview-action descriptors when the window config is
* serialized for the browser, replacing the `init` 99 snapshot taken
* at registration time.
*
* The action SCRIPTS were always collected late (`admin_enqueue_scripts`
* 40, `openstation_my_wordpress_enqueue_preview_action_scripts()`), so
* a plugin registering its `openstation_my_wordpress_preview_actions`
* callback after `init` 99 used to get its JS delivered with no
* descriptor to attach to — the button simply never rendered. Emitting
* both legs from the same late collection closes that trap: any
* registration during a normal bootstrap now ships both.
*
* The entity list deliberately stays frozen at `init` 99 — see the
* registration docblock above.
*
* @param array $config Window config blob about to be emitted.
* @param string $window_id Native window id.
* @return array Config with fresh `previewActions` for our window.
*/
function openstation_my_wordpress_refresh_window_config( $config, $window_id ) {
if ( 'desktop-mode-my-wordpress' !== $window_id || ! is_array( $config ) ) {
return $config;
}
if ( function_exists( 'openstation_my_wordpress_collect_preview_actions' ) ) {
$config['previewActions'] = openstation_my_wordpress_collect_preview_actions();
}
return $config;
}
add_filter( 'openstation_native_window_config', 'openstation_my_wordpress_refresh_window_config', 10, 2 );