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 +97 -72 0.9.81.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,16 +6,16 @@
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 19 * delegates into `includes/content-changes.php`).
20 20 *
21 21 * 2. **Catch-all path — Heartbeat**
@@ -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,14 +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
36 + * @package OpenStation
37 37 */
38 38
39 39 defined( 'ABSPATH' ) || exit;
40 40
41 -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';
42 49
43 50 /**
44 51 * Per-request "did this request trigger a recycle-bin change" flag.
45 52 *
@@ -49,9 +56,9 @@
49 56 *
50 57 * @param bool|null $set Set the flag.
51 58 * @return bool
52 59 */
53 -function desktop_mode_recycle_bin_request_dirty( $set = null ) {
60 +function openstation_recycle_bin_request_dirty( $set = null ) {
54 61 static $dirty = false;
55 62 if ( null !== $set ) {
56 63 $dirty = (bool) $set;
57 64 }
@@ -68,12 +75,12 @@
68 75 * Stored as autoload=false to keep the option out of the always-
69 76 * loaded options query — recycle-bin polling is a "you opened the
70 77 * window, you opted in" cost, not a per-pageload cost.
71 78 */
72 -function desktop_mode_recycle_bin_signal_change() {
79 +function openstation_recycle_bin_signal_change() {
73 80 $ts = (int) round( microtime( true ) * 1000 );
74 - update_option( DESKTOP_MODE_RECYCLE_BIN_CHANGE_OPTION, $ts, false );
75 - desktop_mode_recycle_bin_request_dirty( true );
81 + update_option( OPENSTATION_RECYCLE_BIN_CHANGE_OPTION, $ts, false );
82 + openstation_recycle_bin_request_dirty( true );
76 83
77 84 /**
78 85 * Fires after the recycle bin's "something changed" signal is
79 86 * bumped. Subscribers can use this to push their own real-time
@@ -81,9 +88,9 @@
81 88 * action individually.
82 89 *
83 90 * @param int $ts Milliseconds-since-epoch timestamp of the change.
84 91 */
85 - do_action( 'desktop_mode_recycle_bin_signal', $ts );
92 + do_action( 'openstation_recycle_bin_signal', $ts );
86 93 }
87 94
88 95 /**
89 96 * Wrapper for `wp_trash_post`-style actions that pass `$post_id`.
@@ -88,9 +95,9 @@
88 95 /**
89 96 * Wrapper for `wp_trash_post`-style actions that pass `$post_id`.
90 97 *
91 98 * Captures the post id + post type into the per-request changelog
92 - * so the chromeless footer can emit one `desktop-mode-broadcast`
99 + * so the chromeless footer can emit one `os-broadcast`
93 100 * postMessage per affected domain (e.g. one for `post`, one for
94 101 * `attachment`, …). Subscribers — the recycle bin window, plus
95 102 * any plugin that registered a domain listener — react.
96 103 *
@@ -96,14 +103,14 @@
96 103 *
97 104 * @param int $post_id Post id being mutated.
98 105 * @param string $action One of 'trashed', 'untrashed', 'deleted'.
99 106 */
100 -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' ) {
101 108 $post = get_post( $post_id );
102 109 if ( $post instanceof WP_Post ) {
103 - 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 );
104 111 }
105 - desktop_mode_recycle_bin_signal_change();
112 + openstation_recycle_bin_signal_change();
106 113 }
107 114
108 115 /**
109 116 * Per-request changelog: `[ post_type ][ action ] = int[] ids`.
@@ -109,9 +116,9 @@
109 116 * Per-request changelog: `[ post_type ][ action ] = int[] ids`.
110 117 *
111 118 * Thin wrapper over the generic content-changes recorder
112 119 * (`includes/content-changes.php`) — the generic module
113 - * owns the changelog AND the per-domain `desktop-mode.<type>.changed`
120 + * owns the changelog AND the per-domain `os.<type>.changed`
114 121 * footer broadcasts, so a trash and a save flow through one emitter.
115 122 * The wrapper is kept because the recycle-bin hook wiring below and
116 123 * third-party code grep for it.
117 124 *
@@ -122,13 +129,13 @@
122 129 * @param int $post_id Optional. Id to record.
123 130 * @param string $action Optional. Verb (trashed/untrashed/deleted).
124 131 * @return array Full changelog when called with no args.
125 132 */
126 -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 = '' ) {
127 134 if ( '' !== $post_type ) {
128 - 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 );
129 136 }
130 - return desktop_mode_content_changes_log();
137 + return openstation_content_changes_log();
131 138 }
132 139
133 140 /**
134 141 * Whether the current chromeless request should emit the footer
@@ -146,13 +153,13 @@
146 153 * `get_option` + ~12 lines of inline JS per chromeless render.
147 154 *
148 155 * @return bool
149 156 */
150 -function desktop_mode_recycle_bin_should_emit_footer_signal() {
151 - 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' ) ) {
152 159 return false;
153 160 }
154 - if ( ! desktop_mode_is_chromeless_request() ) {
161 + if ( ! openstation_is_chromeless_request() ) {
155 162 return false;
156 163 }
157 164
158 165 /**
@@ -159,15 +166,15 @@
159 166 * Filter whether to emit the chromeless footer postMessage on
160 167 * the current request.
161 168 *
162 169 * @param bool $emit Default true on any chromeless render. The
163 - * `desktop_mode_recycle_bin_request_dirty()` helper
170 + * `openstation_recycle_bin_request_dirty()` helper
164 171 * reports whether THIS request itself mutated
165 172 * state — useful inside the filter for plugins
166 173 * that only want to ride the "this request
167 174 * trashed something" signal.
168 175 */
169 - return (bool) apply_filters( 'desktop_mode_recycle_bin_emit_footer_signal', true );
176 + return (bool) apply_filters( 'openstation_recycle_bin_emit_footer_signal', true );
170 177 }
171 178
172 179 /**
173 180 * Emits the chromeless-iframe → parent footer signal.
@@ -177,14 +184,14 @@
177 184 * script is ~14 lines uncompressed, doesn't import jQuery, and is
178 185 * a no-op when `window.parent === window` (defensive — the same
179 186 * gate the existing chromeless bridge uses).
180 187 */
181 -function desktop_mode_recycle_bin_emit_footer_signal() {
182 - 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() ) {
183 190 return;
184 191 }
185 192
186 - $ts = (int) get_option( DESKTOP_MODE_RECYCLE_BIN_CHANGE_OPTION, 0 );
193 + $ts = (int) get_option( OPENSTATION_RECYCLE_BIN_CHANGE_OPTION, 0 );
187 194
188 195 if ( $ts <= 0 ) {
189 196 // Nothing has ever been trashed via this site — no point
190 197 // teaching the parent shell about a 0 high-water mark.
@@ -191,15 +198,15 @@
191 198 return;
192 199 }
193 200
194 201 // Only the bin-specific ts signal is emitted here. The per-domain
195 - // `desktop-mode.<post_type>.changed` broadcasts moved to the
202 + // `os.<post_type>.changed` broadcasts moved to the
196 203 // generic content-changes emitter (`includes/content-changes.php`,
197 204 // same `admin_footer` slot) — the bin's changelog delegates into
198 205 // it, so a trash and a save flow through one emitter and each
199 206 // type/action pair is broadcast exactly once per render.
200 207 ?>
201 - <script id="desktop-mode-recycle-bin-realtime-signal">
208 + <script id="os-recycle-bin-realtime-signal">
202 209 ( function () {
203 210 if ( window.parent === window ) {
204 211 return;
205 212 }
@@ -205,9 +212,9 @@
205 212 }
206 213 try {
207 214 window.parent.postMessage(
208 215 {
209 - type: 'desktop-mode-recycle-bin-changed',
216 + type: 'os-recycle-bin-changed',
210 217 ts: <?php echo (int) $ts; ?>,
211 218 source: 'chromeless'
212 219 },
213 220 window.location.origin
@@ -223,9 +230,9 @@
223 230 * heard from me?".
224 231 *
225 232 * The Heartbeat API runs server-side every 15s (active window),
226 233 * 60s (background tab), or 120s (idle). The bin's tab opts in by
227 - * 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
228 235 * key is absent we early-return so users without the bin open pay
229 236 * zero per tick.
230 237 *
231 238 * @param array $response Heartbeat response (passed by ref via filter).
@@ -231,24 +238,24 @@
231 238 * @param array $response Heartbeat response (passed by ref via filter).
232 239 * @param array $data Client-sent payload.
233 240 * @return array
234 241 */
235 -function desktop_mode_recycle_bin_heartbeat_received( $response, $data ) {
242 +function openstation_recycle_bin_heartbeat_received( $response, $data ) {
236 243 if ( ! is_array( $response ) ) {
237 244 $response = array();
238 245 }
239 - if ( ! isset( $data['desktop_mode_recycle_bin_seen_ts'] ) ) {
246 + if ( ! isset( $data['openstation_recycle_bin_seen_ts'] ) ) {
240 247 return $response;
241 248 }
242 - 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() ) {
243 250 return $response;
244 251 }
245 252
246 - $seen = (int) $data['desktop_mode_recycle_bin_seen_ts'];
247 - $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 );
248 255 $changed = $latest > $seen;
249 256
250 - $response['desktop_mode_recycle_bin'] = array(
257 + $response['openstation_recycle_bin'] = array(
251 258 'changed' => $changed,
252 259 'ts' => $latest,
253 260 );
254 261
@@ -255,15 +262,15 @@
255 262 // The authoritative count only travels when something actually
256 263 // changed since the client's high-water mark. The count cannot
257 264 // drift without the change-ts bumping (every capture / restore /
258 265 // purge bumps it), so an unchanged tick would recompute the same
259 - // number — `desktop_mode_recycle_bin_count()` runs up to two
266 + // number — `openstation_recycle_bin_count()` runs up to two
260 267 // COUNT(*) WP_Querys plus a comment count, a real per-tick cost
261 268 // multiplied across every user with the shell open. The client
262 269 // treats `count` as optional and keeps its current badge value
263 270 // when the key is absent.
264 271 if ( $changed ) {
265 - $response['desktop_mode_recycle_bin']['count'] = desktop_mode_recycle_bin_count();
272 + $response['openstation_recycle_bin']['count'] = openstation_recycle_bin_count();
266 273 }
267 274
268 275 return $response;
269 276 }
@@ -270,53 +277,71 @@
270 277
271 278 /**
272 279 * Wire the deletion hooks. We listen for both the WordPress core
273 280 * verbs (`wp_trash_post`, `untrash_post`, `before_delete_post`) and
274 - * our own `desktop_mode_recycle_bin_*` lifecycle actions — the former
281 + * our own `openstation_recycle_bin_*` lifecycle actions — the former
275 282 * catches deletes that bypass our REST endpoints (Quick Edit, REST
276 283 * `DELETE`, WP-CLI, list-table bulk actions); the latter catches
277 284 * the bin's own restore/purge so other tabs see the change.
278 285 *
279 286 * Hooked together inside one bootstrap to make the wiring auditable
280 - * — `grep desktop_mode_recycle_bin_signal_change` finds every emitter.
287 + * — `grep openstation_recycle_bin_signal_change` finds every emitter.
281 288 */
282 -function desktop_mode_recycle_bin_register_realtime_hooks() {
283 - add_action( 'wp_trash_post', function ( $post_id ) {
284 - desktop_mode_recycle_bin_signal_change_for_post( $post_id, 'trashed' );
285 - } );
286 - add_action( 'untrash_post', function ( $post_id ) {
287 - desktop_mode_recycle_bin_signal_change_for_post( $post_id, 'untrashed' );
288 - } );
289 - add_action( 'before_delete_post', function ( $post_id ) {
290 - desktop_mode_recycle_bin_signal_change_for_post( $post_id, 'deleted' );
291 - } );
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 + );
292 308
293 309 // Comments use a different verb space — `trashed_comment` /
294 310 // `untrashed_comment` / `deleted_comment` fire from
295 311 // `wp_set_comment_status`. Map each into our changelog so the
296 - // chromeless footer can broadcast `desktop-mode.comment.changed`
312 + // chromeless footer can broadcast `os.comment.changed`
297 313 // to the Comments-list iframe; the bin captures and lists trashed
298 314 // comments too, and third-party plugins can subscribe to the same
299 315 // topic by hooking the changelog.
300 - add_action( 'trashed_comment', function ( $comment_id ) {
301 - desktop_mode_recycle_bin_record_change( 'comment', (int) $comment_id, 'trashed' );
302 - desktop_mode_recycle_bin_signal_change();
303 - } );
304 - add_action( 'untrashed_comment', function ( $comment_id ) {
305 - desktop_mode_recycle_bin_record_change( 'comment', (int) $comment_id, 'untrashed' );
306 - desktop_mode_recycle_bin_signal_change();
307 - } );
308 - add_action( 'deleted_comment', function ( $comment_id ) {
309 - desktop_mode_recycle_bin_record_change( 'comment', (int) $comment_id, 'deleted' );
310 - desktop_mode_recycle_bin_signal_change();
311 - } );
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 + );
312 337
313 - add_action( 'desktop_mode_recycle_bin_item_captured', 'desktop_mode_recycle_bin_signal_change' );
314 - add_action( 'desktop_mode_recycle_bin_after_restore', 'desktop_mode_recycle_bin_signal_change' );
315 - add_action( 'desktop_mode_recycle_bin_after_purge', 'desktop_mode_recycle_bin_signal_change' );
316 - 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' );
317 342
318 - add_action( 'admin_footer', 'desktop_mode_recycle_bin_emit_footer_signal', 100 );
343 + add_action( 'admin_footer', 'openstation_recycle_bin_emit_footer_signal', 100 );
319 344
320 - add_filter( 'heartbeat_received', 'desktop_mode_recycle_bin_heartbeat_received', 10, 2 );
345 + add_filter( 'heartbeat_received', 'openstation_recycle_bin_heartbeat_received', 10, 2 );
321 346 }
322 -add_action( 'init', 'desktop_mode_recycle_bin_register_realtime_hooks', 5 );
347 +add_action( 'init', 'openstation_recycle_bin_register_realtime_hooks', 5 );