PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
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 0.8.7 All 34 releases
← All changes | includes/devtools.php +55 -67 0.8.71.1.10 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — DevTools / debug bus.
3 + * OpenStation — DevTools / debug bus.
4 4 *
5 5 * Provides a generic per-session pub/sub channel that plugins use to
6 6 * stream debug data (SQL queries, HTTP timings, hook traces, custom
7 7 * events) from a server-side capture into a client-side inspector
@@ -9,27 +9,27 @@
9 9 *
10 10 * Architecture:
11 11 *
12 12 * 1. Inspector plugin allocates a session id with
13 - * `wp.desktop.devtools.debug.startSession()` and decides which
13 + * `wp.os.devtools.debug.startSession()` and decides which
14 14 * channels it cares about (`'query'`, `'log'`, …).
15 15 * 2. Inspector contributes `X-WP-Debug-Session: <id>` to the
16 16 * target window via
17 - * `wp.desktop.devtools.addRequestHeader( windowId, 'X-WP-Debug-Session', sessionId )`.
17 + * `wp.os.devtools.addRequestHeader( windowId, 'X-WP-Debug-Session', sessionId )`.
18 18 * 3. The target window's iframe attaches that header to every
19 19 * fetch / XHR / sendBeacon (the chromeless inline bridge merges
20 20 * contributed headers into outgoing requests).
21 21 * 4. Server-side capture hooks read the header via
22 - * {@see desktop_mode_debug_session_for_request()}, run their
22 + * {@see openstation_debug_session_for_request()}, run their
23 23 * capture (SAVEQUERIES, output buffering, etc.), and publish via
24 - * {@see desktop_mode_debug_publish()}.
24 + * {@see openstation_debug_publish()}.
25 25 * 5. Inspector subscribes via
26 - * `wp.desktop.devtools.debug.subscribe( sessionId, channel, cb )`.
26 + * `wp.os.devtools.debug.subscribe( sessionId, channel, cb )`.
27 27 * The shell polls `GET /desktop-mode/v1/debug` every second and
28 28 * replays new events to subscribers.
29 29 *
30 30 * Storage: a per-session ring buffer in a transient. Bounded by
31 - * {@see DESKTOP_MODE_DEBUG_RING_SIZE} so a misconfigured capture
31 + * {@see OPENSTATION_DEBUG_RING_SIZE} so a misconfigured capture
32 32 * loop can't fill the database. TTL is 1 hour — long enough for an
33 33 * inspector session to span a few page loads, short enough that
34 34 * abandoned sessions don't squat indefinitely.
35 35 *
@@ -37,11 +37,9 @@
37 37 * logged-in AND hold `manage_options`. Debug data leaks request /
38 38 * response details (query parameters, internal IDs) — locking it to
39 39 * site admins matches the cost of getting that wrong.
40 40 *
41 - * @since 0.6.0
42 - *
43 - * @package WPDesktopMode
41 + * @package OpenStation
44 42 */
45 43
46 44 defined( 'ABSPATH' ) || exit;
47 45
@@ -52,9 +50,9 @@
52 50 * page loads) survives on the buffer without truncation. Anything
53 51 * higher and a single transient row starts to push the row-size
54 52 * sanity threshold for typical wp_options storage.
55 53 */
56 -const DESKTOP_MODE_DEBUG_RING_SIZE = 500;
54 +const OPENSTATION_DEBUG_RING_SIZE = 500;
57 55
58 56 /**
59 57 * Transient TTL for a session ring buffer, in seconds.
60 58 *
@@ -60,21 +58,19 @@
60 58 *
61 59 * One hour. Inspector windows that stay open longer than that should
62 60 * heartbeat by republishing — at which point the TTL extends.
63 61 */
64 -const DESKTOP_MODE_DEBUG_SESSION_TTL = 3600;
62 +const OPENSTATION_DEBUG_SESSION_TTL = 3600;
65 63
66 64 /**
67 65 * Build the transient key for a (session, channel) pair.
68 66 *
69 - * @since 0.6.0
70 - *
71 67 * @param string $session_id Session id (as supplied by the client).
72 68 * @param string $channel Channel name (`'query'`, `'log'`, …).
73 69 * @return string Transient key safe for `set_transient`.
74 70 */
75 -function desktop_mode_debug_transient_key( $session_id, $channel ) {
76 - return 'desktop_mode_dbg_' . md5( (string) $session_id . '|' . (string) $channel );
71 +function openstation_debug_transient_key( $session_id, $channel ) {
72 + return 'openstation_dbg_' . md5( (string) $session_id . '|' . (string) $channel );
77 73 }
78 74
79 75 /**
80 76 * Read the debug session id from the current request's headers.
@@ -83,17 +79,16 @@
83 79 * admin-ajax, REST request) call this to detect whether the request
84 80 * originated from an instrumented window. Returns an empty string
85 81 * when no session id is attached or the value fails sanitisation.
86 82 *
87 - * The header is sanitised with `sanitize_key()` — session ids
88 - * generated by the JS side (`crypto.randomUUID()` or the legacy
89 - * fallback) are alphanumeric + dashes, so this is a tight gate.
83 + * The header is sanitised with a case-preserving alphanumeric+dash
84 + * filter (`sanitize_key()` would lowercase, breaking UUID v4
85 + * round-trips); values longer than 64 characters are rejected —
86 + * a tight gate for `crypto.randomUUID()`-shaped ids.
90 87 *
91 - * @since 0.6.0
92 - *
93 88 * @return string Session id, or '' when absent / invalid.
94 89 */
95 -function desktop_mode_debug_session_for_request() {
90 +function openstation_debug_session_for_request() {
96 91 $raw = '';
97 92 if ( isset( $_SERVER['HTTP_X_WP_DEBUG_SESSION'] ) ) {
98 93 $raw = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_WP_DEBUG_SESSION'] ) );
99 94 }
@@ -114,28 +109,27 @@
114 109 * Publish a payload onto a (session, channel).
115 110 *
116 111 * The newest event is appended to the ring buffer; once the cap is
117 112 * reached, the oldest events are dropped FIFO. Fires the
118 - * `desktop_mode_debug_publish` action so observability widgets can
113 + * `openstation_debug_publish` action so observability widgets can
119 114 * tail the stream synchronously without going through the REST poll.
120 115 *
121 - * @since 0.6.0
122 - *
123 116 * @param string $session_id Session id from the client.
124 117 * @param string $channel Channel name. Free-form; convention is
125 118 * lowercase ASCII (e.g. `'query'`,
126 119 * `'log'`, `'rest_timing'`).
127 120 * @param mixed $payload Anything `wp_json_encode()` can serialise.
128 - * @return bool True on success, false when storage refused (ring full
129 - * and transient API unavailable, etc.).
121 + * @return bool True when the event was appended (queued for storage);
122 + * false only when `$session_id` or `$channel` is empty.
123 + * `set_transient()` failures are not detected.
130 124 */
131 -function desktop_mode_debug_publish( $session_id, $channel, $payload ) {
125 +function openstation_debug_publish( $session_id, $channel, $payload ) {
132 126 $session_id = (string) $session_id;
133 127 $channel = (string) $channel;
134 128 if ( '' === $session_id || '' === $channel ) {
135 129 return false;
136 130 }
137 - $key = desktop_mode_debug_transient_key( $session_id, $channel );
131 + $key = openstation_debug_transient_key( $session_id, $channel );
138 132 $existing = get_transient( $key );
139 133 if ( ! is_array( $existing ) ) {
140 134 $existing = array(
141 135 'next_id' => 0,
@@ -141,11 +135,11 @@
141 135 'next_id' => 0,
142 136 'events' => array(),
143 137 );
144 138 }
145 - $next_id = isset( $existing['next_id'] ) ? (int) $existing['next_id'] : 0;
146 - $next_id++;
147 - $existing['next_id'] = $next_id;
139 + $next_id = isset( $existing['next_id'] ) ? (int) $existing['next_id'] : 0;
140 + ++$next_id;
141 + $existing['next_id'] = $next_id;
148 142 $existing['events'][] = array(
149 143 'id' => $next_id,
150 144 't' => (int) round( microtime( true ) * 1000 ),
151 145 'channel' => $channel,
@@ -150,16 +144,16 @@
150 144 't' => (int) round( microtime( true ) * 1000 ),
151 145 'channel' => $channel,
152 146 'payload' => $payload,
153 147 );
154 - $max = (int) apply_filters( 'desktop_mode_debug_ring_size', DESKTOP_MODE_DEBUG_RING_SIZE );
148 + $max = (int) apply_filters( 'openstation_debug_ring_size', OPENSTATION_DEBUG_RING_SIZE );
155 149 if ( $max < 1 ) {
156 - $max = DESKTOP_MODE_DEBUG_RING_SIZE;
150 + $max = OPENSTATION_DEBUG_RING_SIZE;
157 151 }
158 152 if ( count( $existing['events'] ) > $max ) {
159 153 $existing['events'] = array_slice( $existing['events'], -$max );
160 154 }
161 - set_transient( $key, $existing, DESKTOP_MODE_DEBUG_SESSION_TTL );
155 + set_transient( $key, $existing, OPENSTATION_DEBUG_SESSION_TTL );
162 156
163 157 /**
164 158 * Fires after a debug event is appended to the ring buffer.
165 159 *
@@ -166,15 +160,13 @@
166 160 * Lets observability hooks tail the stream synchronously instead
167 161 * of polling the REST endpoint. The arguments mirror the JS-side
168 162 * `DebugEvent` shape minus the auto-assigned id / timestamp.
169 163 *
170 - * @since 0.6.0
171 - *
172 164 * @param string $session_id Session id from the publishing call.
173 165 * @param string $channel Channel name.
174 - * @param mixed $payload Published payload (post-filter).
166 + * @param mixed $payload Published payload.
175 167 */
176 - do_action( 'desktop_mode_debug_publish', $session_id, $channel, $payload );
168 + do_action( 'openstation_debug_publish', $session_id, $channel, $payload );
177 169 return true;
178 170 }
179 171
180 172 /**
@@ -184,19 +176,20 @@
184 176 * Returns `array( 'events' => [], 'cursor' => N )`. The cursor is the
185 177 * highest event id seen across all returned events; clients pass it
186 178 * back as `since` on the next poll.
187 179 *
188 - * @since 0.6.0
189 - *
190 180 * @param string $session_id Session id.
191 181 * @param int $since Highest id the client has seen.
192 182 * @param string|null $channel Optional channel filter.
193 183 * @return array
194 184 */
195 -function desktop_mode_debug_drain( $session_id, $since = 0, $channel = null ) {
185 +function openstation_debug_drain( $session_id, $since = 0, $channel = null ) {
196 186 $session_id = (string) $session_id;
197 187 if ( '' === $session_id ) {
198 - return array( 'events' => array(), 'cursor' => (int) $since );
188 + return array(
189 + 'events' => array(),
190 + 'cursor' => (int) $since,
191 + );
199 192 }
200 193
201 194 $channels = array();
202 195 if ( null !== $channel && '' !== (string) $channel ) {
@@ -205,11 +198,11 @@
205 198 // Without a channel filter the client wants every channel for
206 199 // this session. We don't keep an index of channels per session
207 200 // (would double-write on every publish); instead we let the
208 201 // caller pass a list, OR fan out via the
209 - // `desktop_mode_debug_channels` filter for plugins that know
202 + // `openstation_debug_channels` filter for plugins that know
210 203 // their full set up-front.
211 - $declared = apply_filters( 'desktop_mode_debug_channels', array(), $session_id );
204 + $declared = apply_filters( 'openstation_debug_channels', array(), $session_id );
212 205 if ( is_array( $declared ) ) {
213 206 foreach ( $declared as $ch ) {
214 207 if ( is_string( $ch ) && '' !== $ch ) {
215 208 $channels[] = $ch;
@@ -220,9 +213,9 @@
220 213
221 214 $cursor = (int) $since;
222 215 $out = array();
223 216 foreach ( $channels as $ch ) {
224 - $key = desktop_mode_debug_transient_key( $session_id, $ch );
217 + $key = openstation_debug_transient_key( $session_id, $ch );
225 218 $data = get_transient( $key );
226 219 if ( ! is_array( $data ) || empty( $data['events'] ) ) {
227 220 continue;
228 221 }
@@ -248,9 +241,12 @@
248 241 static function ( $a, $b ) {
249 242 return ( (int) $a['id'] ) - ( (int) $b['id'] );
250 243 }
251 244 );
252 - return array( 'events' => $out, 'cursor' => $cursor );
245 + return array(
246 + 'events' => $out,
247 + 'cursor' => $cursor,
248 + );
253 249 }
254 250
255 251 /**
256 252 * REST: GET /desktop-mode/v1/debug
@@ -256,17 +252,15 @@
256 252 * REST: GET /desktop-mode/v1/debug
257 253 *
258 254 * Returns events newer than `since` for the given session id.
259 255 * Supports both `channel=foo` (single) and `channels[]=foo&channels[]=bar`
260 - * (list); falls back to the `desktop_mode_debug_channels` filter
256 + * (list); falls back to the `openstation_debug_channels` filter
261 257 * when no channel param is supplied.
262 258 *
263 - * @since 0.6.0
264 - *
265 259 * @param WP_REST_Request $request REST request.
266 260 * @return WP_REST_Response
267 261 */
268 -function desktop_mode_rest_debug_drain( WP_REST_Request $request ) {
262 +function openstation_rest_debug_drain( WP_REST_Request $request ) {
269 263 $session_id = (string) $request->get_param( 'sessionId' );
270 264 $since = (int) $request->get_param( 'since' );
271 265 $channel = $request->get_param( 'channel' );
272 266 $channels = $request->get_param( 'channels' );
@@ -272,12 +266,12 @@
272 266 $channels = $request->get_param( 'channels' );
273 267
274 268 if ( is_array( $channels ) && count( $channels ) > 0 ) {
275 269 // Multi-channel drain — concatenate the per-channel results.
276 - $cursor = $since;
270 + $cursor = $since;
277 271 $all_events = array();
278 272 foreach ( $channels as $ch ) {
279 - $result = desktop_mode_debug_drain( $session_id, $since, (string) $ch );
273 + $result = openstation_debug_drain( $session_id, $since, (string) $ch );
280 274 foreach ( $result['events'] as $ev ) {
281 275 $all_events[] = $ev;
282 276 }
283 277 if ( $result['cursor'] > $cursor ) {
@@ -297,9 +291,9 @@
297 291 )
298 292 );
299 293 }
300 294
301 - $result = desktop_mode_debug_drain(
295 + $result = openstation_debug_drain(
302 296 $session_id,
303 297 $since,
304 298 is_string( $channel ) ? $channel : null
305 299 );
@@ -311,32 +305,26 @@
311 305 *
312 306 * Logged-in admins only — debug data exposes internal request shapes
313 307 * that should never leak to lower-privileged users. Plugins that need
314 308 * to relax this for a specific session can hook the
315 - * `desktop_mode_debug_rest_permission` filter (filters TRUE/FALSE).
309 + * `openstation_debug_rest_permission` filter (filters TRUE/FALSE).
316 310 *
317 - * @since 0.6.0
318 - *
319 311 * @return bool
320 312 */
321 -function desktop_mode_rest_debug_permission() {
313 +function openstation_rest_debug_permission() {
322 314 $allowed = is_user_logged_in() && current_user_can( 'manage_options' );
323 315 /**
324 316 * Filter the permission decision for the debug REST endpoint.
325 317 *
326 - * @since 0.6.0
327 - *
328 318 * @param bool $allowed Default: caller is a logged-in admin.
329 319 */
330 - return (bool) apply_filters( 'desktop_mode_debug_rest_permission', $allowed );
320 + return (bool) apply_filters( 'openstation_debug_rest_permission', $allowed );
331 321 }
332 322
333 323 /**
334 324 * Register the debug REST routes.
335 - *
336 - * @since 0.6.0
337 325 */
338 -function desktop_mode_register_debug_rest_routes() {
326 +function openstation_register_debug_rest_routes() {
339 327 register_rest_route(
340 328 'desktop-mode/v1',
341 329 '/debug',
342 330 array(
@@ -341,10 +329,10 @@
341 329 '/debug',
342 330 array(
343 331 array(
344 332 'methods' => WP_REST_Server::READABLE,
345 - 'callback' => 'desktop_mode_rest_debug_drain',
346 - 'permission_callback' => 'desktop_mode_rest_debug_permission',
333 + 'callback' => 'openstation_rest_debug_drain',
334 + 'permission_callback' => 'openstation_rest_debug_permission',
347 335 'args' => array(
348 336 'sessionId' => array(
349 337 'required' => true,
350 338 'type' => 'string',
@@ -364,5 +352,5 @@
364 352 ),
365 353 )
366 354 );
367 355 }
368 -add_action( 'rest_api_init', 'desktop_mode_register_debug_rest_routes' );
356 +add_action( 'rest_api_init', 'openstation_register_debug_rest_routes' );