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/recycle-bin/realtime.php +99 -96 0.9.71.1.10 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — Recycle Bin: real-time signal layer.
3 + * OpenStation — Recycle Bin: real-time signal layer.
4 4 *
5 5 * Two non-polling paths feed the open Recycle Bin window:
6 6 *
7 7 * 1. **Fast path — chromeless iframe**
@@ -6,18 +6,18 @@
6 6 *
7 7 * 1. **Fast path — chromeless iframe**
8 8 * Every recycle-bin-relevant action (`wp_trash_post`,
9 9 * `untrash_post`, `before_delete_post`, plus our four
10 - * `desktop_mode_recycle_bin_*` actions) flips a per-request
10 + * `openstation_recycle_bin_*` actions) flips a per-request
11 11 * static flag. At `admin_footer`, if the request is chromeless
12 12 * AND the flag is set, we emit a 12-line inline script that
13 13 * `postMessage`s the parent shell with `type:
14 - * 'desktop-mode-recycle-bin-changed'`. The parent dispatches our
14 + * 'os-recycle-bin-changed'`. The parent dispatches our
15 15 * `CustomEvent`, the open window refreshes. Cost: ~zero unless
16 16 * a delete actually happened in this request. The per-domain
17 - * `desktop-mode.<type>.changed` list-refresh broadcasts ride the
17 + * `os.<type>.changed` list-refresh broadcasts ride the
18 18 * generic content-changes emitter instead (the changelog below
19 - * delegates into `includes/content-changes.php` since 0.9.7).
19 + * delegates into `includes/content-changes.php`).
20 20 *
21 21 * 2. **Catch-all path — Heartbeat**
22 22 * Every delete also bumps a single autoload=false option
23 23 * `_desktop_mode_recycle_bin_change_ts` (a millisecond timestamp).
@@ -22,9 +22,9 @@
22 22 * Every delete also bumps a single autoload=false option
23 23 * `_desktop_mode_recycle_bin_change_ts` (a millisecond timestamp).
24 24 * The Heartbeat `heartbeat_received` filter checks the client's
25 25 * last-seen ts — if the option is newer, the response includes
26 - * `desktop_mode_recycle_bin: { changed, ts }`. The bin only subscribes
26 + * `openstation_recycle_bin: { changed, ts }`. The bin only subscribes
27 27 * while its window is open, so users without the bin open pay
28 28 * zero. The cost per tick is one cached option read.
29 29 *
30 30 * Why two paths: chromeless iframes that produce a footer (form
@@ -32,15 +32,21 @@
32 32 * Trash" buttons) get instant updates. Everything else (AJAX list
33 33 * actions, REST `DELETE`, other browser tabs, WP-CLI, cron) drips
34 34 * in within the heartbeat cadence (15s active, 60s away).
35 35 *
36 - * @package WPDesktopMode
37 - * @since 0.6.0
36 + * @package OpenStation
38 37 */
39 38
40 39 defined( 'ABSPATH' ) || exit;
41 40
42 -const DESKTOP_MODE_RECYCLE_BIN_CHANGE_OPTION = '_desktop_mode_recycle_bin_change_ts';
41 +/**
42 + * The VALUE keeps its pre-rebrand spelling on purpose: it is a
43 + * persisted or externally-visible identifier, so renaming it would
44 + * orphan data already written by live installs (or break a live
45 + * URL). The mismatch between this constant's name and its value is
46 + * deliberate — it is NOT a half-finished rename.
47 + */
48 +const OPENSTATION_RECYCLE_BIN_CHANGE_OPTION = '_desktop_mode_recycle_bin_change_ts';
43 49
44 50 /**
45 51 * Per-request "did this request trigger a recycle-bin change" flag.
46 52 *
@@ -47,14 +53,12 @@
47 53 * Backed by a function-static so it survives across hook callbacks
48 54 * within the same PHP request. Reading without args returns the
49 55 * current value; passing `true` sets it.
50 56 *
51 - * @since 0.6.0
52 - *
53 57 * @param bool|null $set Set the flag.
54 58 * @return bool
55 59 */
56 -function desktop_mode_recycle_bin_request_dirty( $set = null ) {
60 +function openstation_recycle_bin_request_dirty( $set = null ) {
57 61 static $dirty = false;
58 62 if ( null !== $set ) {
59 63 $dirty = (bool) $set;
60 64 }
@@ -70,15 +74,13 @@
70 74 *
71 75 * Stored as autoload=false to keep the option out of the always-
72 76 * loaded options query — recycle-bin polling is a "you opened the
73 77 * window, you opted in" cost, not a per-pageload cost.
74 - *
75 - * @since 0.6.0
76 78 */
77 -function desktop_mode_recycle_bin_signal_change() {
79 +function openstation_recycle_bin_signal_change() {
78 80 $ts = (int) round( microtime( true ) * 1000 );
79 - update_option( DESKTOP_MODE_RECYCLE_BIN_CHANGE_OPTION, $ts, false );
80 - desktop_mode_recycle_bin_request_dirty( true );
81 + update_option( OPENSTATION_RECYCLE_BIN_CHANGE_OPTION, $ts, false );
82 + openstation_recycle_bin_request_dirty( true );
81 83
82 84 /**
83 85 * Fires after the recycle bin's "something changed" signal is
84 86 * bumped. Subscribers can use this to push their own real-time
@@ -84,13 +86,11 @@
84 86 * bumped. Subscribers can use this to push their own real-time
85 87 * signal (websocket, SSE, etc.) without re-hooking every delete
86 88 * action individually.
87 89 *
88 - * @since 0.6.0
89 - *
90 90 * @param int $ts Milliseconds-since-epoch timestamp of the change.
91 91 */
92 - do_action( 'desktop_mode_recycle_bin_signal', $ts );
92 + do_action( 'openstation_recycle_bin_signal', $ts );
93 93 }
94 94
95 95 /**
96 96 * Wrapper for `wp_trash_post`-style actions that pass `$post_id`.
@@ -95,24 +95,22 @@
95 95 /**
96 96 * Wrapper for `wp_trash_post`-style actions that pass `$post_id`.
97 97 *
98 98 * Captures the post id + post type into the per-request changelog
99 - * so the chromeless footer can emit one `desktop-mode-broadcast`
99 + * so the chromeless footer can emit one `os-broadcast`
100 100 * postMessage per affected domain (e.g. one for `post`, one for
101 101 * `attachment`, …). Subscribers — the recycle bin window, plus
102 102 * any plugin that registered a domain listener — react.
103 103 *
104 - * @since 0.6.0
105 - *
106 104 * @param int $post_id Post id being mutated.
107 105 * @param string $action One of 'trashed', 'untrashed', 'deleted'.
108 106 */
109 -function desktop_mode_recycle_bin_signal_change_for_post( $post_id, $action = 'trashed' ) {
107 +function openstation_recycle_bin_signal_change_for_post( $post_id, $action = 'trashed' ) {
110 108 $post = get_post( $post_id );
111 109 if ( $post instanceof WP_Post ) {
112 - desktop_mode_recycle_bin_record_change( (string) $post->post_type, (int) $post_id, (string) $action );
110 + openstation_recycle_bin_record_change( (string) $post->post_type, (int) $post_id, (string) $action );
113 111 }
114 - desktop_mode_recycle_bin_signal_change();
112 + openstation_recycle_bin_signal_change();
115 113 }
116 114
117 115 /**
118 116 * Per-request changelog: `[ post_type ][ action ] = int[] ids`.
@@ -117,10 +115,10 @@
117 115 /**
118 116 * Per-request changelog: `[ post_type ][ action ] = int[] ids`.
119 117 *
120 118 * Thin wrapper over the generic content-changes recorder
121 - * (`includes/content-changes.php`) — since 0.9.7 the generic module
122 - * owns the changelog AND the per-domain `desktop-mode.<type>.changed`
119 + * (`includes/content-changes.php`) — the generic module
120 + * owns the changelog AND the per-domain `os.<type>.changed`
123 121 * footer broadcasts, so a trash and a save flow through one emitter.
124 122 * The wrapper is kept because the recycle-bin hook wiring below and
125 123 * third-party code grep for it.
126 124 *
@@ -126,21 +124,18 @@
126 124 *
127 125 * Reads when called without args; records when called with a
128 126 * post_type.
129 127 *
130 - * @since 0.6.0
131 - * @since 0.9.7 Delegates to `desktop_mode_content_changes_record()`.
132 - *
133 128 * @param string $post_type Optional. Mutate this domain.
134 129 * @param int $post_id Optional. Id to record.
135 130 * @param string $action Optional. Verb (trashed/untrashed/deleted).
136 131 * @return array Full changelog when called with no args.
137 132 */
138 -function desktop_mode_recycle_bin_record_change( $post_type = '', $post_id = 0, $action = '' ) {
133 +function openstation_recycle_bin_record_change( $post_type = '', $post_id = 0, $action = '' ) {
139 134 if ( '' !== $post_type ) {
140 - desktop_mode_content_changes_record( (string) $post_type, (int) $post_id, (string) $action );
135 + openstation_content_changes_record( (string) $post_type, (int) $post_id, (string) $action );
141 136 }
142 - return desktop_mode_content_changes_log();
137 + return openstation_content_changes_log();
143 138 }
144 139
145 140 /**
146 141 * Whether the current chromeless request should emit the footer
@@ -156,17 +151,15 @@
156 151 * paint inside the iframe (typically <500ms after the click) and
157 152 * can refresh if its `seenTs` is older. The cost is one cached
158 153 * `get_option` + ~12 lines of inline JS per chromeless render.
159 154 *
160 - * @since 0.6.0
161 - *
162 155 * @return bool
163 156 */
164 -function desktop_mode_recycle_bin_should_emit_footer_signal() {
165 - if ( ! function_exists( 'desktop_mode_is_chromeless_request' ) ) {
157 +function openstation_recycle_bin_should_emit_footer_signal() {
158 + if ( ! function_exists( 'openstation_is_chromeless_request' ) ) {
166 159 return false;
167 160 }
168 - if ( ! desktop_mode_is_chromeless_request() ) {
161 + if ( ! openstation_is_chromeless_request() ) {
169 162 return false;
170 163 }
171 164
172 165 /**
@@ -172,18 +165,16 @@
172 165 /**
173 166 * Filter whether to emit the chromeless footer postMessage on
174 167 * the current request.
175 168 *
176 - * @since 0.6.0
177 - *
178 169 * @param bool $emit Default true on any chromeless render. The
179 - * `desktop_mode_recycle_bin_request_dirty()` helper
170 + * `openstation_recycle_bin_request_dirty()` helper
180 171 * reports whether THIS request itself mutated
181 172 * state — useful inside the filter for plugins
182 173 * that only want to ride the "this request
183 174 * trashed something" signal.
184 175 */
185 - return (bool) apply_filters( 'desktop_mode_recycle_bin_emit_footer_signal', true );
176 + return (bool) apply_filters( 'openstation_recycle_bin_emit_footer_signal', true );
186 177 }
187 178
188 179 /**
189 180 * Emits the chromeless-iframe → parent footer signal.
@@ -192,17 +183,15 @@
192 183 * footers so we don't race against unrelated emits. The inline
193 184 * script is ~14 lines uncompressed, doesn't import jQuery, and is
194 185 * a no-op when `window.parent === window` (defensive — the same
195 186 * gate the existing chromeless bridge uses).
196 - *
197 - * @since 0.6.0
198 187 */
199 -function desktop_mode_recycle_bin_emit_footer_signal() {
200 - if ( ! desktop_mode_recycle_bin_should_emit_footer_signal() ) {
188 +function openstation_recycle_bin_emit_footer_signal() {
189 + if ( ! openstation_recycle_bin_should_emit_footer_signal() ) {
201 190 return;
202 191 }
203 192
204 - $ts = (int) get_option( DESKTOP_MODE_RECYCLE_BIN_CHANGE_OPTION, 0 );
193 + $ts = (int) get_option( OPENSTATION_RECYCLE_BIN_CHANGE_OPTION, 0 );
205 194
206 195 if ( $ts <= 0 ) {
207 196 // Nothing has ever been trashed via this site — no point
208 197 // teaching the parent shell about a 0 high-water mark.
@@ -209,15 +198,15 @@
209 198 return;
210 199 }
211 200
212 201 // Only the bin-specific ts signal is emitted here. The per-domain
213 - // `desktop-mode.<post_type>.changed` broadcasts moved to the
202 + // `os.<post_type>.changed` broadcasts moved to the
214 203 // generic content-changes emitter (`includes/content-changes.php`,
215 204 // same `admin_footer` slot) — the bin's changelog delegates into
216 205 // it, so a trash and a save flow through one emitter and each
217 206 // type/action pair is broadcast exactly once per render.
218 207 ?>
219 - <script id="desktop-mode-recycle-bin-realtime-signal">
208 + <script id="os-recycle-bin-realtime-signal">
220 209 ( function () {
221 210 if ( window.parent === window ) {
222 211 return;
223 212 }
@@ -223,9 +212,9 @@
223 212 }
224 213 try {
225 214 window.parent.postMessage(
226 215 {
227 - type: 'desktop-mode-recycle-bin-changed',
216 + type: 'os-recycle-bin-changed',
228 217 ts: <?php echo (int) $ts; ?>,
229 218 source: 'chromeless'
230 219 },
231 220 window.location.origin
@@ -241,34 +230,32 @@
241 230 * heard from me?".
242 231 *
243 232 * The Heartbeat API runs server-side every 15s (active window),
244 233 * 60s (background tab), or 120s (idle). The bin's tab opts in by
245 - * sending `desktop_mode_recycle_bin_seen_ts` in its outgoing data; if the
234 + * sending `openstation_recycle_bin_seen_ts` in its outgoing data; if the
246 235 * key is absent we early-return so users without the bin open pay
247 236 * zero per tick.
248 237 *
249 - * @since 0.6.0
250 - *
251 238 * @param array $response Heartbeat response (passed by ref via filter).
252 239 * @param array $data Client-sent payload.
253 240 * @return array
254 241 */
255 -function desktop_mode_recycle_bin_heartbeat_received( $response, $data ) {
242 +function openstation_recycle_bin_heartbeat_received( $response, $data ) {
256 243 if ( ! is_array( $response ) ) {
257 244 $response = array();
258 245 }
259 - if ( ! isset( $data['desktop_mode_recycle_bin_seen_ts'] ) ) {
246 + if ( ! isset( $data['openstation_recycle_bin_seen_ts'] ) ) {
260 247 return $response;
261 248 }
262 - if ( function_exists( 'desktop_mode_recycle_bin_user_can_use' ) && ! desktop_mode_recycle_bin_user_can_use() ) {
249 + if ( function_exists( 'openstation_recycle_bin_user_can_use' ) && ! openstation_recycle_bin_user_can_use() ) {
263 250 return $response;
264 251 }
265 252
266 - $seen = (int) $data['desktop_mode_recycle_bin_seen_ts'];
267 - $latest = (int) get_option( DESKTOP_MODE_RECYCLE_BIN_CHANGE_OPTION, 0 );
253 + $seen = (int) $data['openstation_recycle_bin_seen_ts'];
254 + $latest = (int) get_option( OPENSTATION_RECYCLE_BIN_CHANGE_OPTION, 0 );
268 255 $changed = $latest > $seen;
269 256
270 - $response['desktop_mode_recycle_bin'] = array(
257 + $response['openstation_recycle_bin'] = array(
271 258 'changed' => $changed,
272 259 'ts' => $latest,
273 260 );
274 261
@@ -275,15 +262,15 @@
275 262 // The authoritative count only travels when something actually
276 263 // changed since the client's high-water mark. The count cannot
277 264 // drift without the change-ts bumping (every capture / restore /
278 265 // purge bumps it), so an unchanged tick would recompute the same
279 - // number — `desktop_mode_recycle_bin_count()` runs up to two
266 + // number — `openstation_recycle_bin_count()` runs up to two
280 267 // COUNT(*) WP_Querys plus a comment count, a real per-tick cost
281 268 // multiplied across every user with the shell open. The client
282 269 // treats `count` as optional and keeps its current badge value
283 270 // when the key is absent.
284 271 if ( $changed ) {
285 - $response['desktop_mode_recycle_bin']['count'] = desktop_mode_recycle_bin_count();
272 + $response['openstation_recycle_bin']['count'] = openstation_recycle_bin_count();
286 273 }
287 274
288 275 return $response;
289 276 }
@@ -290,55 +277,71 @@
290 277
291 278 /**
292 279 * Wire the deletion hooks. We listen for both the WordPress core
293 280 * verbs (`wp_trash_post`, `untrash_post`, `before_delete_post`) and
294 - * our own `desktop_mode_recycle_bin_*` lifecycle actions — the former
281 + * our own `openstation_recycle_bin_*` lifecycle actions — the former
295 282 * catches deletes that bypass our REST endpoints (Quick Edit, REST
296 283 * `DELETE`, WP-CLI, list-table bulk actions); the latter catches
297 284 * the bin's own restore/purge so other tabs see the change.
298 285 *
299 286 * Hooked together inside one bootstrap to make the wiring auditable
300 - * — `grep desktop_mode_recycle_bin_signal_change` finds every emitter.
301 - *
302 - * @since 0.6.0
287 + * — `grep openstation_recycle_bin_signal_change` finds every emitter.
303 288 */
304 -function desktop_mode_recycle_bin_register_realtime_hooks() {
305 - add_action( 'wp_trash_post', function ( $post_id ) {
306 - desktop_mode_recycle_bin_signal_change_for_post( $post_id, 'trashed' );
307 - } );
308 - add_action( 'untrash_post', function ( $post_id ) {
309 - desktop_mode_recycle_bin_signal_change_for_post( $post_id, 'untrashed' );
310 - } );
311 - add_action( 'before_delete_post', function ( $post_id ) {
312 - desktop_mode_recycle_bin_signal_change_for_post( $post_id, 'deleted' );
313 - } );
289 +function openstation_recycle_bin_register_realtime_hooks() {
290 + add_action(
291 + 'wp_trash_post',
292 + function ( $post_id ) {
293 + openstation_recycle_bin_signal_change_for_post( $post_id, 'trashed' );
294 + }
295 + );
296 + add_action(
297 + 'untrash_post',
298 + function ( $post_id ) {
299 + openstation_recycle_bin_signal_change_for_post( $post_id, 'untrashed' );
300 + }
301 + );
302 + add_action(
303 + 'before_delete_post',
304 + function ( $post_id ) {
305 + openstation_recycle_bin_signal_change_for_post( $post_id, 'deleted' );
306 + }
307 + );
314 308
315 309 // Comments use a different verb space — `trashed_comment` /
316 310 // `untrashed_comment` / `deleted_comment` fire from
317 311 // `wp_set_comment_status`. Map each into our changelog so the
318 - // chromeless footer can broadcast `desktop-mode.comment.changed`
312 + // chromeless footer can broadcast `os.comment.changed`
319 313 // to the Comments-list iframe; the bin captures and lists trashed
320 314 // comments too, and third-party plugins can subscribe to the same
321 315 // topic by hooking the changelog.
322 - add_action( 'trashed_comment', function ( $comment_id ) {
323 - desktop_mode_recycle_bin_record_change( 'comment', (int) $comment_id, 'trashed' );
324 - desktop_mode_recycle_bin_signal_change();
325 - } );
326 - add_action( 'untrashed_comment', function ( $comment_id ) {
327 - desktop_mode_recycle_bin_record_change( 'comment', (int) $comment_id, 'untrashed' );
328 - desktop_mode_recycle_bin_signal_change();
329 - } );
330 - add_action( 'deleted_comment', function ( $comment_id ) {
331 - desktop_mode_recycle_bin_record_change( 'comment', (int) $comment_id, 'deleted' );
332 - desktop_mode_recycle_bin_signal_change();
333 - } );
316 + add_action(
317 + 'trashed_comment',
318 + function ( $comment_id ) {
319 + openstation_recycle_bin_record_change( 'comment', (int) $comment_id, 'trashed' );
320 + openstation_recycle_bin_signal_change();
321 + }
322 + );
323 + add_action(
324 + 'untrashed_comment',
325 + function ( $comment_id ) {
326 + openstation_recycle_bin_record_change( 'comment', (int) $comment_id, 'untrashed' );
327 + openstation_recycle_bin_signal_change();
328 + }
329 + );
330 + add_action(
331 + 'deleted_comment',
332 + function ( $comment_id ) {
333 + openstation_recycle_bin_record_change( 'comment', (int) $comment_id, 'deleted' );
334 + openstation_recycle_bin_signal_change();
335 + }
336 + );
334 337
335 - add_action( 'desktop_mode_recycle_bin_item_captured', 'desktop_mode_recycle_bin_signal_change' );
336 - add_action( 'desktop_mode_recycle_bin_after_restore', 'desktop_mode_recycle_bin_signal_change' );
337 - add_action( 'desktop_mode_recycle_bin_after_purge', 'desktop_mode_recycle_bin_signal_change' );
338 - add_action( 'desktop_mode_recycle_bin_emptied', 'desktop_mode_recycle_bin_signal_change' );
338 + add_action( 'openstation_recycle_bin_item_captured', 'openstation_recycle_bin_signal_change' );
339 + add_action( 'openstation_recycle_bin_after_restore', 'openstation_recycle_bin_signal_change' );
340 + add_action( 'openstation_recycle_bin_after_purge', 'openstation_recycle_bin_signal_change' );
341 + add_action( 'openstation_recycle_bin_emptied', 'openstation_recycle_bin_signal_change' );
339 342
340 - add_action( 'admin_footer', 'desktop_mode_recycle_bin_emit_footer_signal', 100 );
343 + add_action( 'admin_footer', 'openstation_recycle_bin_emit_footer_signal', 100 );
341 344
342 - add_filter( 'heartbeat_received', 'desktop_mode_recycle_bin_heartbeat_received', 10, 2 );
345 + add_filter( 'heartbeat_received', 'openstation_recycle_bin_heartbeat_received', 10, 2 );
343 346 }
344 -add_action( 'init', 'desktop_mode_recycle_bin_register_realtime_hooks', 5 );
347 +add_action( 'init', 'openstation_recycle_bin_register_realtime_hooks', 5 );