PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.11
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.11
1.1.11 1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 All 35 releases
← All changes | includes/my-wordpress/window.php +145 -141 0.9.81.1.11 View file →
@@ -1,46 +1,139 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — My WordPress: window + pinned icon registration.
3 + * OpenStation — My WordPress: the explorer's identity + shared helpers.
4 4 *
5 - * Native window with id `desktop-mode-my-wordpress`, opened from a
6 - * pinned desktop icon that always sits in the top-left of the grid
7 - * (`pinned: true`, `position: -1`). The bundle renders a two-pane
8 - * file-explorer UI with breadcrumb navigation: root shows Posts,
9 - * Pages, Users, and Media folder tiles, and clicking one drills into
10 - * an infinite-scroll list of entities with a per-kind preview pane.
5 + * The app people open is `apps/my-wordpress/` — WP Explorer, rebuilt
6 + * on the App Framework. This module keeps what the port shares: the
7 + * name and the folder-mark icon (the app reads both from the helpers
8 + * here), the capability gate, and the inert entities compatibility
9 + * surface (see the note at the end of the file).
11 10 *
12 - * Filterable surface (mirrors the recycle-bin / posts-window modules):
11 + * **The app is called WP Explorer; the module is called my-wordpress.**
12 + * The function prefix and every `openstation_my_wordpress_*` filter
13 + * are names plugins already reference — the frozen-values rule in
14 + * AGENTS.md keeps them put.
13 15 *
14 - * - `desktop_mode_my_wordpress_window_args`
15 - * - `desktop_mode_my_wordpress_icon_args`
16 - * - `desktop_mode_my_wordpress_user_can_use`
17 - * - `desktop_mode_my_wordpress_entities`
18 - * - `desktop_mode_my_wordpress_template_html`
16 + * Filterable surface:
19 17 *
20 - * @package WPDesktopMode
18 + * - `openstation_my_wordpress_user_can_use`
19 + * - `openstation_my_wordpress_entities` (inert — see the end note)
20 + *
21 + * (`openstation_my_wordpress_icon_args`, `_window_args` and
22 + * `_template_html` went with the legacy window they configured.)
23 + *
24 + * @package OpenStation
21 25 */
22 26
23 27 defined( 'ABSPATH' ) || exit;
24 28
25 29 /**
30 + * Display name of the app, for the window title and the pinned icon.
31 + *
32 + * A file explorer, named after what it explores. The window used to
33 + * carry the site title instead, on the reasoning that the desktop holds
34 + * objects rather than a mention of the software you are already standing
35 + * in — but a site title is what the *root folder* is called, not what
36 + * the app is called, and the breadcrumb still says it. The two are no
37 + * longer the same string.
38 + *
39 + * @return string
40 + */
41 +function openstation_my_wordpress_app_title() {
42 + return __( 'WP Explorer', 'desktop-mode' );
43 +}
44 +
45 +/**
46 + * The app's icon: a folder wearing the WordPress mark.
47 + *
48 + * The window used to wear `dashicons-wordpress`, the double-ringed
49 + * wp.org mark, which said "WordPress" and nothing about what the app
50 + * does — every other window here is WordPress too. A folder says
51 + * "browse", and the single-ringed mark inside it says whose files.
52 + *
53 + * Drawn in `currentColor` so both painters mask it: the dock masks
54 + * image icons to the rail's own glyph colour so a plugin's brand
55 + * colours cannot break the monochrome rail, and `renderIcon()` masks it
56 + * to the title bar's text colour. A literal fill would silently turn it
57 + * back into a background image in one and a solid blob in the other.
58 + *
59 + * The folder is outlined rather than filled so the mark inside it stays
60 + * a mark: on a filled folder, a same-colour mark is invisible. Hand-
61 + * placed at 64×64, the established canvas for the custom icons here,
62 + * and held to two shapes because it renders as small as 20px in the
63 + * dock. The mark's own path is the upstream single-ringed logo, a
64 + * filled disc with the W knocked out by the nonzero fill rule.
65 + *
66 + * On the sizing: the front panel runs `y 19.1 → 52.1` on the
67 + * centreline, so at stroke 3 the clear height inside it is 30 units.
68 + * The mark takes 20 of those at scale 1, centred on 35.6, which leaves
69 + * five units above and below. It used to be scaled 1.15× for 23 units
70 + * and barely three units of clearance, less than one stroke width, and
71 + * a disc that close to the panel edge reads as crammed in rather than
72 + * placed. Horizontal clearance was never the constraint: the panel is
73 + * 50 units wide against the mark's 20.
74 + *
75 + * @return string Raw `<svg>` markup.
76 + */
77 +function openstation_my_wordpress_icon_svg() {
78 + return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">'
79 + // The folder: back tab, notch, front panel, all one outline.
80 + . '<path d="M11 13h12.2a3 3 0 0 1 2.4 1.2l2.8 3.7a3 3 0 0 0 2.4 1.2H53a4 4 0 0 1 4 4v25a4 4 0 0 1-4 4H11a4 4 0 0 1-4-4V17a4 4 0 0 1 4-4z"'
81 + . ' fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"/>'
82 + // The WordPress mark, centred on the front panel at r=10.
83 + . '<g transform="translate(22 25.6) scale(1)">'
84 + . '<path fill="currentColor" d="M20 10c0-5.51-4.49-10-10-10C4.48 0 0 4.49 0 10c0 5.52 4.48 10 10 10 5.51 0 10-4.48 10-10zM7.78 15.37L4.37 6.22c.55-.02 1.17-.08 1.17-.08.5-.06.44-1.13-.06-1.11 0 0-1.45.11-2.37.11-.18 0-.37 0-.58-.01C4.12 2.69 6.87 1.11 10 1.11c2.33 0 4.45.87 6.05 2.34-.68-.11-1.65.39-1.65 1.58 0 .74.45 1.36.9 2.1.35.61.55 1.36.55 2.46 0 1.49-1.4 5-1.4 5l-3.03-8.37c.54-.02.82-.17.82-.17.5-.05.44-1.25-.06-1.22 0 0-1.44.12-2.38.12-.87 0-2.33-.12-2.33-.12-.5-.03-.56 1.2-.06 1.22l.92.08 1.26 3.41zM17.41 10c.24-.64.74-1.87.43-4.25.7 1.29 1.05 2.71 1.05 4.25 0 3.29-1.73 6.24-4.4 7.78.97-2.59 1.94-5.2 2.92-7.78zM6.1 18.09C3.12 16.65 1.11 13.53 1.11 10c0-1.3.23-2.48.72-3.59C3.25 10.3 4.67 14.2 6.1 18.09zm4.03-6.63l2.58 6.98c-.86.29-1.76.45-2.71.45-.79 0-1.57-.11-2.29-.33.81-2.38 1.62-4.74 2.42-7.1z"/>'
85 + . '</g>'
86 + . '</svg>';
87 +}
88 +
89 +/**
26 90 * Whether the current user should see My WordPress.
27 91 *
28 92 * Mirrors the recycle-bin gate — anyone who can edit posts can
29 93 * browse posts and pages.
30 94 *
95 + * This is the module's one capability gate, and it is a **server-side
96 + * authorization** gate, not just a visibility one. It decides:
97 + *
98 + * - The non-REST post-type bridge: whether
99 + * `desktop-mode/v1/post-type/<slug>` routes register at all
100 + * (`rest-post-type.php`).
101 + * - The per-comment dossier route
102 + * `desktop-mode/v1/comment-stats/<id>` (`comment-stats.php`).
103 + * - The user and term dossier routes
104 + * `desktop-mode/v1/user-stats/<id>`,
105 + * `desktop-mode/v1/user-footprint/<id>` and
106 + * `desktop-mode/v1/term-stats/<taxonomy>/<id>` (`user-stats.php`,
107 + * `user-footprint.php`, `term-stats.php`), which then scope their
108 + * payloads to what the caller may read.
109 + * - Whether the WooCommerce integration's boot config ships, so the
110 + * client can reach the order / customer / product surfaces at all
111 + * — those routes still enforce their own Woo capabilities on top
112 + * (`integrations/woocommerce.php`).
113 + * - Whether preview-action scripts registered by plugins are
114 + * enqueued (`preview-actions.php`).
115 + * - Whether Station Home offers the "WP Explorer" quick action
116 + * (`apps/station-home/parts/snapshot.php`).
117 + *
118 + * It does **not** gate the app's own window or pinned launcher: WP
119 + * Explorer is an App Framework app and declares
120 + * `->capabilities( 'edit_posts' )` itself (`apps/my-wordpress/`).
121 + * Returning `true` here opens the surfaces above without opening the
122 + * window; to move the window too, filter the app's manifest.
123 + *
31 124 * @return bool
32 125 */
33 -function desktop_mode_my_wordpress_user_can_use() {
126 +function openstation_my_wordpress_user_can_use() {
34 127 $can = current_user_can( 'edit_posts' );
35 128
36 129 /**
37 - * Filter whether the current user can see the My WordPress
38 - * pinned icon and window.
130 + * Filter whether the current user can reach the My WordPress
131 + * module's surfaces — its REST routes included.
39 132 *
40 133 * @param bool $can Default: edit_posts capability.
41 134 */
42 - return (bool) apply_filters( 'desktop_mode_my_wordpress_user_can_use', $can );
135 + return (bool) apply_filters( 'openstation_my_wordpress_user_can_use', $can );
43 136 }
44 137
45 138 /**
46 139 * Build the entity list shipped to the bundle. Posts, Pages,
@@ -55,15 +148,15 @@
55 148 * routes to the media preview pane. Plugins extending the entity
56 149 * list with a post-shaped collection can omit the field; user- and
57 150 * media-shaped collections must set `'user'` / `'media'`.
58 151 * The optional `post_type` field specifies the WP post-type
59 - * slug used for `desktop-mode.<slug>.changed` cross-window broadcasts.
152 + * slug used for `os.<slug>.changed` cross-window broadcasts.
60 153 *
61 154 * @return array[] Each entry is `array( 'id', 'label', 'icon',
62 155 * 'restPath', 'kind', 'post_type' )`. `restPath` is appended to
63 156 * the `restRoot` config to derive the list URL.
64 157 */
65 -function desktop_mode_my_wordpress_entities() {
158 +function openstation_my_wordpress_entities() {
66 159 $entities = array(
67 160 array(
68 161 'id' => 'posts',
69 162 'label' => __( 'Posts', 'desktop-mode' ),
@@ -110,132 +203,43 @@
110 203 * break existing consumers. The `kind` field is optional and
111 204 * defaults to `'post'` for back-compat.
112 205 *
113 206 * Optional fields:
114 - * - `kind` — render strategy (`'post'` default, `'user'`, `'media'`).
115 - * - `post_type` — WP post-type slug for cross-window broadcast topic `desktop-mode.<slug>.changed`.
207 + * - `kind` — render strategy (`'post'` default, `'user'`, `'media'`).
208 + * - `post_type` — WP post-type slug for cross-window broadcast topic `os.<slug>.changed`.
209 + * - `thumbnails` — set false to keep the section icon on every tile
210 + * instead of the entity's featured image.
211 + * - `editAction` — who edits this section's rows. A preview-action id
212 + * (see `openstation_my_wordpress_preview_actions`)
213 + * replaces "Open in editor" everywhere — pane button,
214 + * context-menu open entry, tile double-click. `false`
215 + * removes every edit affordance (double-click falls
216 + * back to the detail dossier; the bulk "Edit…" modal
217 + * is suppressed). Omit for the classic editor.
218 + * - `group` — folder id this section nests under at the root
219 + * (null / omitted renders it loose at the root).
220 + * - `groupLabel` — folder label. `groupIcon`, `groupOrder` follow.
116 221 *
117 222 * @param array[] $entities Default entities.
118 223 */
119 - $filtered = apply_filters( 'desktop_mode_my_wordpress_entities', $entities );
224 + $filtered = apply_filters( 'openstation_my_wordpress_entities', $entities );
120 225 return is_array( $filtered ) ? array_values( $filtered ) : $entities;
121 226 }
122 227
123 -/**
124 - * Render the My WordPress window's static template body. The bundle
125 - * mounts its UI into `[data-desktop-mode-my-wordpress-root]`.
228 +/*
229 + * No window, no template, no launcher. The explorer people open is
230 + * the `my-wordpress` APP (`apps/my-wordpress/`), which reclaimed the
231 + * "WP Explorer" name and the folder mark from this module's helpers
232 + * above. The legacy native window, its bundle and its pinned icon are
233 + * gone; every deep surface it used to host moved with the port — the
234 + * detail dossiers and the activity footprint render inside the app,
235 + * "open this object" travels through the shared stores in
236 + * `src/open-targets/explorer-open.ts` / `footprint-target.ts`, and
237 + * the Recycle Bin trashes dropped rows by their payload's `restPath`.
238 + *
239 + * `openstation_my_wordpress_entities()` and its filter remain as an
240 + * inert compatibility surface: subscribers registered against them
241 + * (WooCommerce's own among them) still run, and the pure helpers are
242 + * still what the tests pin — but nothing consumes the list to build a
243 + * window any more. The `openstation_my_wordpress_window_args` /
244 + * `openstation_my_wordpress_template_html` filters no longer fire.
126 245 */
127 -function desktop_mode_my_wordpress_render_template() {
128 - ob_start();
129 - ?>
130 - <div class="desktop-mode-my-wordpress" data-desktop-mode-my-wordpress-root>
131 - <header data-desktop-mode-my-wordpress-breadcrumbs></header>
132 - <div class="desktop-mode-my-wordpress__body" data-desktop-mode-my-wordpress-body>
133 - <div class="desktop-mode-my-wordpress__loading" data-desktop-mode-my-wordpress-loading hidden>
134 - <wpd-spinner></wpd-spinner>
135 - </div>
136 - </div>
137 - <div class="desktop-mode-folder-status-bar" data-desktop-mode-my-wordpress-status></div>
138 - </div>
139 - <?php
140 - $html = (string) ob_get_clean();
141 -
142 - /**
143 - * Filter the My WordPress window's template HTML.
144 - *
145 - * @param string $html Default template HTML.
146 - */
147 - $filtered = (string) apply_filters( 'desktop_mode_my_wordpress_template_html', $html );
148 -
149 - $allowed_html = function_exists( 'desktop_mode_native_window_allowed_html' )
150 - ? desktop_mode_native_window_allowed_html()
151 - : wp_kses_allowed_html( 'post' );
152 -
153 - echo wp_kses( $filtered, $allowed_html );
154 -}
155 -
156 -/**
157 - * Register the native window + the pinned wallpaper icon on `init`,
158 - * priority 20 — after `components.php` boots the registry.
159 - */
160 -function desktop_mode_my_wordpress_register_window() {
161 - if ( ! desktop_mode_my_wordpress_user_can_use() ) {
162 - return;
163 - }
164 -
165 - $site_title = desktop_mode_site_title();
166 -
167 - $window_args = array(
168 - 'title' => $site_title,
169 - 'icon' => 'dashicons-wordpress',
170 - 'template' => 'desktop_mode_my_wordpress_render_template',
171 - 'script' => 'desktop-mode-my-wordpress',
172 - 'style' => 'desktop-mode-my-wordpress',
173 - 'width' => 960,
174 - 'height' => 640,
175 - 'min_width' => 640,
176 - 'min_height' => 420,
177 - 'placement' => 'none',
178 - 'config' => array(
179 - 'restRoot' => esc_url_raw( rest_url() ),
180 - 'restNonce' => wp_create_nonce( 'wp_rest' ),
181 - 'editPostUrlBase' => esc_url_raw( admin_url( 'post.php' ) ),
182 - 'editUserUrlBase' => esc_url_raw( admin_url( 'user-edit.php' ) ),
183 - 'siteName' => $site_title,
184 - 'entities' => desktop_mode_my_wordpress_entities(),
185 - 'perPage' => 24,
186 - 'mediaPerPage' => 48,
187 - 'previewActions' => function_exists( 'desktop_mode_my_wordpress_collect_preview_actions' )
188 - ? desktop_mode_my_wordpress_collect_preview_actions()
189 - : array(),
190 - ),
191 - );
192 -
193 - /**
194 - * Filter the args used to register the My WordPress native window.
195 - *
196 - * @param array $window_args Args passed to `desktop_mode_register_window()`.
197 - */
198 - $window_args = (array) apply_filters( 'desktop_mode_my_wordpress_window_args', $window_args );
199 -
200 - $registered = desktop_mode_register_window( 'desktop-mode-my-wordpress', $window_args );
201 - if ( is_wp_error( $registered ) ) {
202 - // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
203 - error_log( '[desktop-mode] My WordPress window registration failed: ' . $registered->get_error_message() );
204 - return;
205 - }
206 -
207 - $icon_args = array(
208 - 'title' => $site_title,
209 - 'icon' => 'dashicons-wordpress',
210 - 'window' => 'desktop-mode-my-wordpress',
211 - 'pinned' => true,
212 - 'position' => -1,
213 - );
214 -
215 - /**
216 - * Filter the args used to register the My WordPress pinned icon.
217 - *
218 - * Removing `pinned` here lets the icon participate in normal
219 - * sort order — useful for sites that want the shortcut to feel
220 - * like any other plugin icon.
221 - *
222 - * @param array $icon_args Args passed to `desktop_mode_register_icon()`.
223 - */
224 - $icon_args = (array) apply_filters( 'desktop_mode_my_wordpress_icon_args', $icon_args );
225 -
226 - desktop_mode_register_icon( 'desktop-mode-my-wordpress', $icon_args );
227 -}
228 -add_action( 'init', 'desktop_mode_my_wordpress_register_window', 20 );
229 -
230 -/**
231 - * Enqueue the bundle's CSS in admin context. The script is lazy-
232 - * loaded by the native-window sync and so does not need an
233 - * `admin_enqueue_scripts` call.
234 - */
235 -function desktop_mode_my_wordpress_enqueue_styles() {
236 - if ( ! desktop_mode_my_wordpress_user_can_use() ) {
237 - return;
238 - }
239 - wp_enqueue_style( 'desktop-mode-my-wordpress' );
240 -}
241 -add_action( 'admin_enqueue_scripts', 'desktop_mode_my_wordpress_enqueue_styles', 30 );