| @@ -1,12 +1,12 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | - * Desktop Mode — generic content-change realtime layer. | |
| 3 | + * OpenStation — generic content-change realtime layer. | |
| 4 | 4 | * |
| 5 | 5 | * Records create/update/trash mutations of posts, pages, CPTs, |
| 6 | 6 | * comments, and WooCommerce orders into a per-request changelog and |
| 7 | 7 | * relays them to the parent shell as cross-window broadcasts |
| 8 | - * (`desktop-mode.<type>.changed`, payload `{ source, action, ids }`), | |
| 8 | + * (`os.<type>.changed`, payload `{ source, action, ids }`), | |
| 9 | 9 | * so every window listing that content type can refresh without an F5. |
| 10 | 10 | * |
| 11 | 11 | * Three delivery paths, mirroring the Recycle Bin realtime layer |
| 12 | 12 | * (`includes/recycle-bin/realtime.php`) which delegates its own |
| @@ -13,9 +13,9 @@ | ||
| 13 | 13 | * changelog into this module: |
| 14 | 14 | * |
| 15 | 15 | * 1. **Chromeless footer — fast path.** At `admin_footer` on a |
| 16 | 16 | * chromeless render, one inline script postMessages a |
| 17 | - * `desktop-mode-broadcast` envelope per `[type][action]` entry | |
| 17 | + * `os-broadcast` envelope per `[type][action]` entry | |
| 18 | 18 | * to the parent shell. Because the dominant admin mutation flow |
| 19 | 19 | * is form-POST → 302 → GET (the mutating request renders no |
| 20 | 20 | * footer), the changelog is buffered across the redirect in a |
| 21 | 21 | * short-TTL per-user transient and flushed on the next |
| @@ -32,29 +32,36 @@ | ||
| 32 | 32 | * "entries newer than your seen ts" for opted-in shells. Covers |
| 33 | 33 | * Quick Edit, AJAX status flips, other browser tabs, REST and |
| 34 | 34 | * WP-CLI mutations within one tick (15–60 s). |
| 35 | 35 | * |
| 36 | - * @package WPDesktopMode | |
| 36 | + * @package OpenStation | |
| 37 | 37 | */ |
| 38 | 38 | |
| 39 | 39 | defined( 'ABSPATH' ) || exit; |
| 40 | 40 | |
| 41 | -const DESKTOP_MODE_CONTENT_CHANGES_LOG_OPTION = '_desktop_mode_content_changes_log'; | |
| 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_CONTENT_CHANGES_LOG_OPTION = '_desktop_mode_content_changes_log'; | |
| 42 | 49 | |
| 43 | 50 | /** |
| 44 | 51 | * Milliseconds of heartbeat-changelog history to retain. |
| 45 | 52 | */ |
| 46 | -const DESKTOP_MODE_CONTENT_CHANGES_LOG_WINDOW_MS = 300000; | |
| 53 | +const OPENSTATION_CONTENT_CHANGES_LOG_WINDOW_MS = 300000; | |
| 47 | 54 | |
| 48 | 55 | /** |
| 49 | 56 | * Maximum retained heartbeat-changelog entries. |
| 50 | 57 | */ |
| 51 | -const DESKTOP_MODE_CONTENT_CHANGES_LOG_MAX = 100; | |
| 58 | +const OPENSTATION_CONTENT_CHANGES_LOG_MAX = 100; | |
| 52 | 59 | |
| 53 | 60 | /** |
| 54 | 61 | * TTL for the redirect-surviving per-user changelog buffer. |
| 55 | 62 | */ |
| 56 | -const DESKTOP_MODE_CONTENT_CHANGES_BUFFER_TTL = 60; | |
| 63 | +const OPENSTATION_CONTENT_CHANGES_BUFFER_TTL = 60; | |
| 57 | 64 | |
| 58 | 65 | /** |
| 59 | 66 | * Per-request state shared by the recorder, the footer emitter, and |
| 60 | 67 | * the shutdown handler. |
| @@ -70,9 +77,9 @@ | ||
| 70 | 77 | * shutdown handler doesn't buffer it a second time. |
| 71 | 78 | * |
| 72 | 79 | * @return array Per-request state, by reference. |
| 73 | 80 | */ |
| 74 | -function &desktop_mode_content_changes_state() { | |
| 81 | +function &openstation_content_changes_state() { | |
| 75 | 82 | static $state = null; |
| 76 | 83 | if ( null === $state ) { |
| 77 | 84 | $state = array( |
| 78 | 85 | 'log' => array(), |
| @@ -88,10 +95,10 @@ | ||
| 88 | 95 | * Resets the per-request state. Test isolation only. |
| 89 | 96 | * |
| 90 | 97 | * @internal |
| 91 | 98 | */ |
| 92 | -function desktop_mode_content_changes_reset() { | |
| 93 | - $state = &desktop_mode_content_changes_state(); | |
| 99 | +function openstation_content_changes_reset() { | |
| 100 | + $state = &openstation_content_changes_state(); | |
| 94 | 101 | $state = array( |
| 95 | 102 | 'log' => array(), |
| 96 | 103 | 'seen' => array(), |
| 97 | 104 | 'staged' => array(), |
| @@ -105,9 +112,9 @@ | ||
| 105 | 112 | * This is the public entry point for third-party plugins with their |
| 106 | 113 | * own storage (an HPOS-style custom table, a settings screen, …): |
| 107 | 114 | * call it from your mutation path and every open window listing your |
| 108 | 115 | * type refreshes, exactly like core content. Pair it with a |
| 109 | - * `desktop_mode_soft_reload_rules` filter entry if your list screen | |
| 116 | + * `openstation_soft_reload_rules` filter entry if your list screen | |
| 110 | 117 | * is not a standard `edit.php?post_type=<type>` page. |
| 111 | 118 | * |
| 112 | 119 | * Dedupe is first-writer-wins per `type:id` within the request: core |
| 113 | 120 | * fires the trash verbs (`wp_trash_post`, `untrash_post`) BEFORE the |
| @@ -117,15 +124,15 @@ | ||
| 117 | 124 | * double-fires (post hooks + `woocommerce_update_order`). |
| 118 | 125 | * |
| 119 | 126 | * @param string $type Content type slug — a post type, `comment`, |
| 120 | 127 | * or `shop_order`. Becomes the broadcast topic |
| 121 | - * `desktop-mode.<type>.changed`. | |
| 128 | + * `os.<type>.changed`. | |
| 122 | 129 | * @param int $id Mutated object id. |
| 123 | 130 | * @param string $action One of 'created', 'updated', 'trashed', |
| 124 | 131 | * 'untrashed', 'deleted'. |
| 125 | 132 | * @return bool Whether the change was recorded. |
| 126 | 133 | */ |
| 127 | -function desktop_mode_content_changes_record( $type, $id, $action ) { | |
| 134 | +function openstation_content_changes_record( $type, $id, $action ) { | |
| 128 | 135 | $type = (string) $type; |
| 129 | 136 | $id = (int) $id; |
| 130 | 137 | $action = (string) $action; |
| 131 | 138 | |
| @@ -146,13 +153,13 @@ | ||
| 146 | 153 | * @param string $type Content type slug. |
| 147 | 154 | * @param int $id Mutated object id. |
| 148 | 155 | * @param string $action Verb (created/updated/trashed/untrashed/deleted). |
| 149 | 156 | */ |
| 150 | - if ( ! apply_filters( 'desktop_mode_content_changes_should_record', true, $type, $id, $action ) ) { | |
| 157 | + if ( ! apply_filters( 'openstation_content_changes_should_record', true, $type, $id, $action ) ) { | |
| 151 | 158 | return false; |
| 152 | 159 | } |
| 153 | 160 | |
| 154 | - $state = &desktop_mode_content_changes_state(); | |
| 161 | + $state = &openstation_content_changes_state(); | |
| 155 | 162 | $key = $type . ':' . $id; |
| 156 | 163 | if ( isset( $state['seen'][ $key ] ) ) { |
| 157 | 164 | return false; |
| 158 | 165 | } |
| @@ -182,9 +189,9 @@ | ||
| 182 | 189 | * @param string $type Content type slug. |
| 183 | 190 | * @param int $id Mutated object id. |
| 184 | 191 | * @param string $action Verb (created/updated/trashed/untrashed/deleted). |
| 185 | 192 | */ |
| 186 | - do_action( 'desktop_mode_content_change_recorded', $type, $id, $action ); | |
| 193 | + do_action( 'openstation_content_change_recorded', $type, $id, $action ); | |
| 187 | 194 | |
| 188 | 195 | return true; |
| 189 | 196 | } |
| 190 | 197 | |
| @@ -192,10 +199,10 @@ | ||
| 192 | 199 | * Returns the per-request changelog: `[ type ][ action ] = int[] ids`. |
| 193 | 200 | * |
| 194 | 201 | * @return array |
| 195 | 202 | */ |
| 196 | -function desktop_mode_content_changes_log() { | |
| 197 | - $state = &desktop_mode_content_changes_state(); | |
| 203 | +function openstation_content_changes_log() { | |
| 204 | + $state = &openstation_content_changes_state(); | |
| 198 | 205 | return $state['log']; |
| 199 | 206 | } |
| 200 | 207 | |
| 201 | 208 | /** |
| @@ -205,12 +212,12 @@ | ||
| 205 | 212 | * @param array $a Base changelog. |
| 206 | 213 | * @param array $b Changelog to merge in. |
| 207 | 214 | * @return array |
| 208 | 215 | */ |
| 209 | -function desktop_mode_content_changes_merge( $a, $b ) { | |
| 216 | +function openstation_content_changes_merge( $a, $b ) { | |
| 210 | 217 | foreach ( (array) $b as $type => $by_action ) { |
| 211 | 218 | foreach ( (array) $by_action as $action => $ids ) { |
| 212 | - $existing = isset( $a[ $type ][ $action ] ) ? (array) $a[ $type ][ $action ] : array(); | |
| 219 | + $existing = isset( $a[ $type ][ $action ] ) ? (array) $a[ $type ][ $action ] : array(); | |
| 213 | 220 | $a[ $type ][ $action ] = array_values( array_unique( array_merge( $existing, array_map( 'intval', (array) $ids ) ) ) ); |
| 214 | 221 | } |
| 215 | 222 | } |
| 216 | 223 | return $a; |
| @@ -221,10 +228,10 @@ | ||
| 221 | 228 | * |
| 222 | 229 | * @param int $user_id User id. |
| 223 | 230 | * @return string |
| 224 | 231 | */ |
| 225 | -function desktop_mode_content_changes_buffer_key( $user_id ) { | |
| 226 | - return 'desktop_mode_content_buf_' . (int) $user_id; | |
| 232 | +function openstation_content_changes_buffer_key( $user_id ) { | |
| 233 | + return 'openstation_content_buf_' . (int) $user_id; | |
| 227 | 234 | } |
| 228 | 235 | |
| 229 | 236 | /** |
| 230 | 237 | * `wp_after_insert_post` handler — posts, pages, and every `show_ui` |
| @@ -238,9 +245,9 @@ | ||
| 238 | 245 | * |
| 239 | 246 | * Post types without `show_ui` are skipped: they have no list screen |
| 240 | 247 | * to refresh, and internal types (notes, …) run their own realtime. |
| 241 | 248 | * Plugins that want one tracked anyway can call |
| 242 | - * `desktop_mode_content_changes_record()` from their own hooks. | |
| 249 | + * `openstation_content_changes_record()` from their own hooks. | |
| 243 | 250 | * |
| 244 | 251 | * @param int $post_id Post id. |
| 245 | 252 | * @param WP_Post $post Saved post. |
| 246 | 253 | * @param bool $update Whether this is an update. |
| @@ -245,9 +252,9 @@ | ||
| 245 | 252 | * @param WP_Post $post Saved post. |
| 246 | 253 | * @param bool $update Whether this is an update. |
| 247 | 254 | * @param WP_Post|null $post_before Pre-save post, null on creation. |
| 248 | 255 | */ |
| 249 | -function desktop_mode_content_changes_on_after_insert_post( $post_id, $post, $update, $post_before ) { | |
| 256 | +function openstation_content_changes_on_after_insert_post( $post_id, $post, $update, $post_before ) { | |
| 250 | 257 | if ( ! $post instanceof WP_Post ) { |
| 251 | 258 | return; |
| 252 | 259 | } |
| 253 | 260 | if ( wp_is_post_revision( $post ) || wp_is_post_autosave( $post ) ) { |
| @@ -267,9 +274,9 @@ | ||
| 267 | 274 | // The first real save of a new post arrives as an "update" of the |
| 268 | 275 | // auto-draft shell `post-new.php` created — report it as created. |
| 269 | 276 | $is_created = ! $update || ( $post_before instanceof WP_Post && 'auto-draft' === $post_before->post_status ); |
| 270 | 277 | |
| 271 | - desktop_mode_content_changes_record( $post->post_type, (int) $post_id, $is_created ? 'created' : 'updated' ); | |
| 278 | + openstation_content_changes_record( $post->post_type, (int) $post_id, $is_created ? 'created' : 'updated' ); | |
| 272 | 279 | } |
| 273 | 280 | |
| 274 | 281 | /** |
| 275 | 282 | * `transition_comment_status` handler. |
| @@ -282,9 +289,9 @@ | ||
| 282 | 289 | * @param string $new_status New comment status. |
| 283 | 290 | * @param string $old_status Old comment status. |
| 284 | 291 | * @param WP_Comment $comment Comment object. |
| 285 | 292 | */ |
| 286 | -function desktop_mode_content_changes_on_comment_transition( $new_status, $old_status, $comment ) { | |
| 293 | +function openstation_content_changes_on_comment_transition( $new_status, $old_status, $comment ) { | |
| 287 | 294 | if ( 'trash' === $new_status || 'trash' === $old_status ) { |
| 288 | 295 | return; |
| 289 | 296 | } |
| 290 | 297 | if ( ! $comment instanceof WP_Comment ) { |
| @@ -289,9 +296,9 @@ | ||
| 289 | 296 | } |
| 290 | 297 | if ( ! $comment instanceof WP_Comment ) { |
| 291 | 298 | return; |
| 292 | 299 | } |
| 293 | - desktop_mode_content_changes_record( 'comment', (int) $comment->comment_ID, 'updated' ); | |
| 300 | + openstation_content_changes_record( 'comment', (int) $comment->comment_ID, 'updated' ); | |
| 294 | 301 | } |
| 295 | 302 | |
| 296 | 303 | /** |
| 297 | 304 | * Wires the WooCommerce order hooks. No-op unless WooCommerce is |
| @@ -301,38 +308,56 @@ | ||
| 301 | 308 | * in custom tables and none of the post hooks fire. Under legacy |
| 302 | 309 | * (posts-table) storage the post hooks fire too; the recorder's |
| 303 | 310 | * per-request dedupe collapses the double-fire. The type is always |
| 304 | 311 | * recorded as `shop_order` so one broadcast topic |
| 305 | - * (`desktop-mode.shop_order.changed`) serves both storage modes. | |
| 312 | + * (`os.shop_order.changed`) serves both storage modes. | |
| 306 | 313 | * |
| 307 | 314 | * @return bool Whether the hooks were registered. |
| 308 | 315 | */ |
| 309 | -function desktop_mode_content_changes_register_wc_hooks() { | |
| 316 | +function openstation_content_changes_register_wc_hooks() { | |
| 310 | 317 | if ( ! class_exists( 'WooCommerce' ) || ! function_exists( 'wc_get_order' ) ) { |
| 311 | 318 | return false; |
| 312 | 319 | } |
| 313 | 320 | |
| 314 | - add_action( 'woocommerce_new_order', function ( $order_id ) { | |
| 315 | - desktop_mode_content_changes_record( 'shop_order', (int) $order_id, 'created' ); | |
| 316 | - } ); | |
| 317 | - add_action( 'woocommerce_update_order', function ( $order_id ) { | |
| 318 | - desktop_mode_content_changes_record( 'shop_order', (int) $order_id, 'updated' ); | |
| 319 | - } ); | |
| 321 | + add_action( | |
| 322 | + 'woocommerce_new_order', | |
| 323 | + function ( $order_id ) { | |
| 324 | + openstation_content_changes_record( 'shop_order', (int) $order_id, 'created' ); | |
| 325 | + } | |
| 326 | + ); | |
| 327 | + add_action( | |
| 328 | + 'woocommerce_update_order', | |
| 329 | + function ( $order_id ) { | |
| 330 | + openstation_content_changes_record( 'shop_order', (int) $order_id, 'updated' ); | |
| 331 | + } | |
| 332 | + ); | |
| 320 | 333 | // Some AJAX status-flip paths reach `woocommerce_order_status_changed` |
| 321 | 334 | // without `woocommerce_update_order`; the dedupe set absorbs the |
| 322 | 335 | // overlap when both fire. |
| 323 | - add_action( 'woocommerce_order_status_changed', function ( $order_id ) { | |
| 324 | - desktop_mode_content_changes_record( 'shop_order', (int) $order_id, 'updated' ); | |
| 325 | - } ); | |
| 326 | - add_action( 'woocommerce_trash_order', function ( $order_id ) { | |
| 327 | - desktop_mode_content_changes_record( 'shop_order', (int) $order_id, 'trashed' ); | |
| 328 | - } ); | |
| 329 | - add_action( 'woocommerce_untrash_order', function ( $order_id ) { | |
| 330 | - desktop_mode_content_changes_record( 'shop_order', (int) $order_id, 'untrashed' ); | |
| 331 | - } ); | |
| 332 | - add_action( 'woocommerce_delete_order', function ( $order_id ) { | |
| 333 | - desktop_mode_content_changes_record( 'shop_order', (int) $order_id, 'deleted' ); | |
| 334 | - } ); | |
| 336 | + add_action( | |
| 337 | + 'woocommerce_order_status_changed', | |
| 338 | + function ( $order_id ) { | |
| 339 | + openstation_content_changes_record( 'shop_order', (int) $order_id, 'updated' ); | |
| 340 | + } | |
| 341 | + ); | |
| 342 | + add_action( | |
| 343 | + 'woocommerce_trash_order', | |
| 344 | + function ( $order_id ) { | |
| 345 | + openstation_content_changes_record( 'shop_order', (int) $order_id, 'trashed' ); | |
| 346 | + } | |
| 347 | + ); | |
| 348 | + add_action( | |
| 349 | + 'woocommerce_untrash_order', | |
| 350 | + function ( $order_id ) { | |
| 351 | + openstation_content_changes_record( 'shop_order', (int) $order_id, 'untrashed' ); | |
| 352 | + } | |
| 353 | + ); | |
| 354 | + add_action( | |
| 355 | + 'woocommerce_delete_order', | |
| 356 | + function ( $order_id ) { | |
| 357 | + openstation_content_changes_record( 'shop_order', (int) $order_id, 'deleted' ); | |
| 358 | + } | |
| 359 | + ); | |
| 335 | 360 | |
| 336 | 361 | return true; |
| 337 | 362 | } |
| 338 | 363 | |
| @@ -342,29 +367,29 @@ | ||
| 342 | 367 | * Merges the in-memory changelog with the redirect-surviving buffer |
| 343 | 368 | * (consumed on read), builds one broadcast envelope per |
| 344 | 369 | * `[ type ][ action ]`, and prints one inline script that postMessages |
| 345 | 370 | * each to the parent shell. The parent's broadcast receiver fans them |
| 346 | - * out as `desktop-mode.<type>.changed` — iframe list pages soft-reload | |
| 371 | + * out as `os.<type>.changed` — iframe list pages soft-reload | |
| 347 | 372 | * and native list windows refetch. |
| 348 | 373 | * |
| 349 | 374 | * Runs at `admin_footer` priority 100, same slot as the Recycle Bin's |
| 350 | 375 | * bin-specific ts signal. |
| 351 | 376 | */ |
| 352 | -function desktop_mode_content_changes_emit_footer() { | |
| 353 | - if ( ! function_exists( 'desktop_mode_is_chromeless_request' ) || ! desktop_mode_is_chromeless_request() ) { | |
| 377 | +function openstation_content_changes_emit_footer() { | |
| 378 | + if ( ! function_exists( 'openstation_is_chromeless_request' ) || ! openstation_is_chromeless_request() ) { | |
| 354 | 379 | return; |
| 355 | 380 | } |
| 356 | 381 | |
| 357 | - $state = &desktop_mode_content_changes_state(); | |
| 382 | + $state = &openstation_content_changes_state(); | |
| 358 | 383 | $log = $state['log']; |
| 359 | 384 | |
| 360 | 385 | $user_id = get_current_user_id(); |
| 361 | 386 | if ( $user_id > 0 ) { |
| 362 | - $key = desktop_mode_content_changes_buffer_key( $user_id ); | |
| 387 | + $key = openstation_content_changes_buffer_key( $user_id ); | |
| 363 | 388 | $buffered = get_transient( $key ); |
| 364 | 389 | if ( is_array( $buffered ) && ! empty( $buffered ) ) { |
| 365 | 390 | delete_transient( $key ); |
| 366 | - $log = desktop_mode_content_changes_merge( $buffered, $log ); | |
| 391 | + $log = openstation_content_changes_merge( $buffered, $log ); | |
| 367 | 392 | } |
| 368 | 393 | } |
| 369 | 394 | |
| 370 | 395 | // The in-memory log is consumed regardless of whether anything is |
| @@ -381,13 +406,13 @@ | ||
| 381 | 406 | foreach ( $by_action as $action => $ids ) { |
| 382 | 407 | /** |
| 383 | 408 | * Filters the broadcast topic for a content-change type. |
| 384 | 409 | * |
| 385 | - * @param string $topic Default `desktop-mode.<type>.changed`. | |
| 410 | + * @param string $topic Default `os.<type>.changed`. | |
| 386 | 411 | * @param string $type Content type slug. |
| 387 | 412 | * @param string $action Verb for this envelope. |
| 388 | 413 | */ |
| 389 | - $topic = (string) apply_filters( 'desktop_mode_content_change_topic', 'desktop-mode.' . $type . '.changed', $type, $action ); | |
| 414 | + $topic = (string) apply_filters( 'openstation_content_change_topic', 'os.' . $type . '.changed', $type, $action ); | |
| 390 | 415 | |
| 391 | 416 | $broadcasts[] = array( |
| 392 | 417 | 'topic' => $topic, |
| 393 | 418 | 'payload' => array( |
| @@ -407,9 +432,9 @@ | ||
| 407 | 432 | * Return an empty array to suppress the emit. |
| 408 | 433 | * |
| 409 | 434 | * @param array $broadcasts Broadcast envelopes. |
| 410 | 435 | */ |
| 411 | - $broadcasts = (array) apply_filters( 'desktop_mode_content_changes_broadcasts', $broadcasts ); | |
| 436 | + $broadcasts = (array) apply_filters( 'openstation_content_changes_broadcasts', $broadcasts ); | |
| 412 | 437 | if ( empty( $broadcasts ) ) { |
| 413 | 438 | return; |
| 414 | 439 | } |
| 415 | 440 | |
| @@ -418,9 +443,9 @@ | ||
| 418 | 443 | return; |
| 419 | 444 | } |
| 420 | 445 | |
| 421 | 446 | ?> |
| 422 | - <script id="desktop-mode-content-changes-signal"> | |
| 447 | + <script id="os-content-changes-signal"> | |
| 423 | 448 | ( function () { |
| 424 | 449 | if ( window.parent === window ) { |
| 425 | 450 | return; |
| 426 | 451 | } |
| @@ -428,9 +453,9 @@ | ||
| 428 | 453 | var broadcasts = <?php echo $broadcasts_json; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_json_encode output. ?>; |
| 429 | 454 | for ( var i = 0; i < broadcasts.length; i++ ) { |
| 430 | 455 | try { |
| 431 | 456 | window.parent.postMessage( { |
| 432 | - type: 'desktop-mode-broadcast', | |
| 457 | + type: 'os-broadcast', | |
| 433 | 458 | topic: broadcasts[ i ].topic, |
| 434 | 459 | payload: broadcasts[ i ].payload |
| 435 | 460 | }, origin ); |
| 436 | 461 | } catch ( _err ) { /* parent gone */ } |
| @@ -444,9 +469,9 @@ | ||
| 444 | 469 | * broadcast envelopes. |
| 445 | 470 | * |
| 446 | 471 | * @param array $broadcasts Emitted broadcast envelopes. |
| 447 | 472 | */ |
| 448 | - do_action( 'desktop_mode_content_changes_emitted', $broadcasts ); | |
| 473 | + do_action( 'openstation_content_changes_emitted', $broadcasts ); | |
| 449 | 474 | } |
| 450 | 475 | |
| 451 | 476 | /** |
| 452 | 477 | * `shutdown` handler — persists the heartbeat changelog and buffers |
| @@ -454,10 +479,10 @@ | ||
| 454 | 479 | * |
| 455 | 480 | * One `update_option` per mutating request; requests that recorded |
| 456 | 481 | * nothing pay a static-array read and return. |
| 457 | 482 | */ |
| 458 | -function desktop_mode_content_changes_on_shutdown() { | |
| 459 | - $state = &desktop_mode_content_changes_state(); | |
| 483 | +function openstation_content_changes_on_shutdown() { | |
| 484 | + $state = &openstation_content_changes_state(); | |
| 460 | 485 | |
| 461 | 486 | if ( ! empty( $state['staged'] ) ) { |
| 462 | 487 | $now = (int) round( microtime( true ) * 1000 ); |
| 463 | 488 | |
| @@ -466,9 +491,9 @@ | ||
| 466 | 491 | foreach ( $state['staged'] as $row ) { |
| 467 | 492 | $grouped[ $row['type'] . '|' . $row['action'] ][] = (int) $row['id']; |
| 468 | 493 | } |
| 469 | 494 | |
| 470 | - $log = get_option( DESKTOP_MODE_CONTENT_CHANGES_LOG_OPTION, array() ); | |
| 495 | + $log = get_option( OPENSTATION_CONTENT_CHANGES_LOG_OPTION, array() ); | |
| 471 | 496 | $entries = ( is_array( $log ) && isset( $log['entries'] ) && is_array( $log['entries'] ) ) ? $log['entries'] : array(); |
| 472 | 497 | |
| 473 | 498 | foreach ( $grouped as $group_key => $ids ) { |
| 474 | 499 | list( $type, $action ) = explode( '|', $group_key, 2 ); |
| @@ -481,18 +506,23 @@ | ||
| 481 | 506 | } |
| 482 | 507 | |
| 483 | 508 | // Prune: drop entries older than the retention window, cap the |
| 484 | 509 | // tail. The option stays small no matter how chatty the site. |
| 485 | - $cutoff = $now - DESKTOP_MODE_CONTENT_CHANGES_LOG_WINDOW_MS; | |
| 486 | - $entries = array_values( array_filter( $entries, function ( $entry ) use ( $cutoff ) { | |
| 487 | - return isset( $entry['ts'] ) && (int) $entry['ts'] >= $cutoff; | |
| 488 | - } ) ); | |
| 489 | - if ( count( $entries ) > DESKTOP_MODE_CONTENT_CHANGES_LOG_MAX ) { | |
| 490 | - $entries = array_slice( $entries, -DESKTOP_MODE_CONTENT_CHANGES_LOG_MAX ); | |
| 510 | + $cutoff = $now - OPENSTATION_CONTENT_CHANGES_LOG_WINDOW_MS; | |
| 511 | + $entries = array_values( | |
| 512 | + array_filter( | |
| 513 | + $entries, | |
| 514 | + function ( $entry ) use ( $cutoff ) { | |
| 515 | + return isset( $entry['ts'] ) && (int) $entry['ts'] >= $cutoff; | |
| 516 | + } | |
| 517 | + ) | |
| 518 | + ); | |
| 519 | + if ( count( $entries ) > OPENSTATION_CONTENT_CHANGES_LOG_MAX ) { | |
| 520 | + $entries = array_slice( $entries, -OPENSTATION_CONTENT_CHANGES_LOG_MAX ); | |
| 491 | 521 | } |
| 492 | 522 | |
| 493 | 523 | update_option( |
| 494 | - DESKTOP_MODE_CONTENT_CHANGES_LOG_OPTION, | |
| 524 | + OPENSTATION_CONTENT_CHANGES_LOG_OPTION, | |
| 495 | 525 | array( |
| 496 | 526 | 'ts' => $now, |
| 497 | 527 | 'entries' => $entries, |
| 498 | 528 | ), |
| @@ -507,12 +537,12 @@ | ||
| 507 | 537 | if ( $user_id <= 0 ) { |
| 508 | 538 | return; |
| 509 | 539 | } |
| 510 | 540 | |
| 511 | - $key = desktop_mode_content_changes_buffer_key( $user_id ); | |
| 541 | + $key = openstation_content_changes_buffer_key( $user_id ); | |
| 512 | 542 | $existing = get_transient( $key ); |
| 513 | - $merged = desktop_mode_content_changes_merge( is_array( $existing ) ? $existing : array(), $state['log'] ); | |
| 514 | - set_transient( $key, $merged, DESKTOP_MODE_CONTENT_CHANGES_BUFFER_TTL ); | |
| 543 | + $merged = openstation_content_changes_merge( is_array( $existing ) ? $existing : array(), $state['log'] ); | |
| 544 | + set_transient( $key, $merged, OPENSTATION_CONTENT_CHANGES_BUFFER_TTL ); | |
| 515 | 545 | } |
| 516 | 546 | |
| 517 | 547 | /** |
| 518 | 548 | * Heartbeat handler — answers "which content changed since you last |
| @@ -517,28 +547,28 @@ | ||
| 517 | 547 | /** |
| 518 | 548 | * Heartbeat handler — answers "which content changed since you last |
| 519 | 549 | * heard from me?". |
| 520 | 550 | * |
| 521 | - * Opt-in via the client-sent `desktop_mode_content_changes_seen_ts` | |
| 551 | + * Opt-in via the client-sent `openstation_content_changes_seen_ts` | |
| 522 | 552 | * key; requests without it early-return so non-desktop tabs pay zero |
| 523 | 553 | * per tick. The response carries the server high-water mark plus the |
| 524 | 554 | * entries newer than the client's seen ts; the shell re-broadcasts |
| 525 | - * each as `desktop-mode.<type>.changed`. | |
| 555 | + * each as `os.<type>.changed`. | |
| 526 | 556 | * |
| 527 | 557 | * @param array $response Heartbeat response. |
| 528 | 558 | * @param array $data Client-sent payload. |
| 529 | 559 | * @return array |
| 530 | 560 | */ |
| 531 | -function desktop_mode_content_changes_heartbeat_received( $response, $data ) { | |
| 561 | +function openstation_content_changes_heartbeat_received( $response, $data ) { | |
| 532 | 562 | if ( ! is_array( $response ) ) { |
| 533 | 563 | $response = array(); |
| 534 | 564 | } |
| 535 | - if ( ! isset( $data['desktop_mode_content_changes_seen_ts'] ) ) { | |
| 565 | + if ( ! isset( $data['openstation_content_changes_seen_ts'] ) ) { | |
| 536 | 566 | return $response; |
| 537 | 567 | } |
| 538 | 568 | |
| 539 | - $seen = (int) $data['desktop_mode_content_changes_seen_ts']; | |
| 540 | - $log = get_option( DESKTOP_MODE_CONTENT_CHANGES_LOG_OPTION, array() ); | |
| 569 | + $seen = (int) $data['openstation_content_changes_seen_ts']; | |
| 570 | + $log = get_option( OPENSTATION_CONTENT_CHANGES_LOG_OPTION, array() ); | |
| 541 | 571 | |
| 542 | 572 | $ts = ( is_array( $log ) && isset( $log['ts'] ) ) ? (int) $log['ts'] : 0; |
| 543 | 573 | $entries = ( is_array( $log ) && isset( $log['entries'] ) && is_array( $log['entries'] ) ) ? $log['entries'] : array(); |
| 544 | 574 | |
| @@ -548,9 +578,9 @@ | ||
| 548 | 578 | $fresh[] = $entry; |
| 549 | 579 | } |
| 550 | 580 | } |
| 551 | 581 | |
| 552 | - $response['desktop_mode_content_changes'] = array( | |
| 582 | + $response['openstation_content_changes'] = array( | |
| 553 | 583 | 'ts' => $ts, |
| 554 | 584 | 'entries' => $fresh, |
| 555 | 585 | ); |
| 556 | 586 | |
| @@ -559,9 +589,9 @@ | ||
| 559 | 589 | |
| 560 | 590 | /** |
| 561 | 591 | * Converts a plugin file path to a stable positive integer suitable |
| 562 | 592 | * for use as the `$id` parameter of |
| 563 | - * `desktop_mode_content_changes_record()`. | |
| 593 | + * `openstation_content_changes_record()`. | |
| 564 | 594 | * |
| 565 | 595 | * The record function requires a positive integer for its ID slot (used |
| 566 | 596 | * for per-request deduplication keyed as `type:id`). Plugin files are |
| 567 | 597 | * strings (`akismet/akismet.php`), so we derive a deterministic integer |
| @@ -571,9 +601,9 @@ | ||
| 571 | 601 | * |
| 572 | 602 | * @param string $plugin_file Plugin file path (relative to plugins dir). |
| 573 | 603 | * @return int Positive integer ID. |
| 574 | 604 | */ |
| 575 | -function desktop_mode_content_changes_plugin_id( $plugin_file ) { | |
| 605 | +function openstation_content_changes_plugin_id( $plugin_file ) { | |
| 576 | 606 | $hash = abs( crc32( (string) $plugin_file ) ); |
| 577 | 607 | return max( 1, $hash ); |
| 578 | 608 | } |
| 579 | 609 | |
| @@ -582,63 +612,79 @@ | ||
| 582 | 612 | * and deletions are recorded into the realtime changelog. |
| 583 | 613 | * |
| 584 | 614 | * Every open window listing plugins (native Plugins window Installed tab, |
| 585 | 615 | * classic `plugins.php`) will then refresh via the |
| 586 | - * `desktop-mode.plugin.changed` broadcast — the same mechanism | |
| 587 | - * posts/pages use for `desktop-mode.post.changed`. | |
| 616 | + * `os.plugin.changed` broadcast — the same mechanism | |
| 617 | + * posts/pages use for `os.post.changed`. | |
| 588 | 618 | */ |
| 589 | -function desktop_mode_content_changes_register_plugin_hooks() { | |
| 590 | - add_action( 'activated_plugin', function ( $plugin_file ) { | |
| 591 | - desktop_mode_content_changes_record( | |
| 592 | - 'plugin', | |
| 593 | - desktop_mode_content_changes_plugin_id( $plugin_file ), | |
| 594 | - 'activated' | |
| 595 | - ); | |
| 596 | - } ); | |
| 619 | +function openstation_content_changes_register_plugin_hooks() { | |
| 620 | + add_action( | |
| 621 | + 'activated_plugin', | |
| 622 | + function ( $plugin_file ) { | |
| 623 | + openstation_content_changes_record( | |
| 624 | + 'plugin', | |
| 625 | + openstation_content_changes_plugin_id( $plugin_file ), | |
| 626 | + 'activated' | |
| 627 | + ); | |
| 628 | + } | |
| 629 | + ); | |
| 597 | 630 | |
| 598 | - add_action( 'deactivated_plugin', function ( $plugin_file ) { | |
| 599 | - desktop_mode_content_changes_record( | |
| 600 | - 'plugin', | |
| 601 | - desktop_mode_content_changes_plugin_id( $plugin_file ), | |
| 602 | - 'deactivated' | |
| 603 | - ); | |
| 604 | - } ); | |
| 605 | - | |
| 606 | - add_action( 'deleted_plugin', function ( $plugin_file, $deleted ) { | |
| 607 | - if ( $deleted ) { | |
| 608 | - desktop_mode_content_changes_record( | |
| 631 | + add_action( | |
| 632 | + 'deactivated_plugin', | |
| 633 | + function ( $plugin_file ) { | |
| 634 | + openstation_content_changes_record( | |
| 609 | 635 | 'plugin', |
| 610 | - desktop_mode_content_changes_plugin_id( $plugin_file ), | |
| 611 | - 'deleted' | |
| 636 | + openstation_content_changes_plugin_id( $plugin_file ), | |
| 637 | + 'deactivated' | |
| 612 | 638 | ); |
| 613 | 639 | } |
| 614 | - }, 10, 2 ); | |
| 640 | + ); | |
| 615 | 641 | |
| 642 | + add_action( | |
| 643 | + 'deleted_plugin', | |
| 644 | + function ( $plugin_file, $deleted ) { | |
| 645 | + if ( $deleted ) { | |
| 646 | + openstation_content_changes_record( | |
| 647 | + 'plugin', | |
| 648 | + openstation_content_changes_plugin_id( $plugin_file ), | |
| 649 | + 'deleted' | |
| 650 | + ); | |
| 651 | + } | |
| 652 | + }, | |
| 653 | + 10, | |
| 654 | + 2 | |
| 655 | + ); | |
| 656 | + | |
| 616 | 657 | // `upgrader_process_complete` covers installs from wp-admin/plugin-install.php |
| 617 | 658 | // (AJAX path, no page navigation) and bulk installs from update.php. |
| 618 | - add_action( 'upgrader_process_complete', function ( $upgrader, $options ) { | |
| 619 | - if ( | |
| 659 | + add_action( | |
| 660 | + 'upgrader_process_complete', | |
| 661 | + function ( $upgrader, $options ) { | |
| 662 | + if ( | |
| 620 | 663 | ! isset( $options['type'], $options['action'] ) || |
| 621 | 664 | 'plugin' !== $options['type'] || |
| 622 | 665 | 'install' !== $options['action'] |
| 623 | - ) { | |
| 624 | - return; | |
| 625 | - } | |
| 626 | - $plugins = ! empty( $options['plugins'] ) ? (array) $options['plugins'] : array(); | |
| 627 | - if ( empty( $plugins ) && is_callable( array( $upgrader, 'plugin_info' ) ) ) { | |
| 628 | - $info = $upgrader->plugin_info(); | |
| 629 | - if ( $info ) { | |
| 630 | - $plugins = array( $info ); | |
| 666 | + ) { | |
| 667 | + return; | |
| 631 | 668 | } |
| 632 | - } | |
| 633 | - foreach ( $plugins as $plugin_file ) { | |
| 634 | - desktop_mode_content_changes_record( | |
| 635 | - 'plugin', | |
| 636 | - desktop_mode_content_changes_plugin_id( (string) $plugin_file ), | |
| 637 | - 'installed' | |
| 638 | - ); | |
| 639 | - } | |
| 640 | - }, 10, 2 ); | |
| 669 | + $plugins = ! empty( $options['plugins'] ) ? (array) $options['plugins'] : array(); | |
| 670 | + if ( empty( $plugins ) && is_callable( array( $upgrader, 'plugin_info' ) ) ) { | |
| 671 | + $info = $upgrader->plugin_info(); | |
| 672 | + if ( $info ) { | |
| 673 | + $plugins = array( $info ); | |
| 674 | + } | |
| 675 | + } | |
| 676 | + foreach ( $plugins as $plugin_file ) { | |
| 677 | + openstation_content_changes_record( | |
| 678 | + 'plugin', | |
| 679 | + openstation_content_changes_plugin_id( (string) $plugin_file ), | |
| 680 | + 'installed' | |
| 681 | + ); | |
| 682 | + } | |
| 683 | + }, | |
| 684 | + 10, | |
| 685 | + 2 | |
| 686 | + ); | |
| 641 | 687 | } |
| 642 | 688 | |
| 643 | 689 | /** |
| 644 | 690 | * Wires every content-change hook. |
| @@ -643,25 +689,31 @@ | ||
| 643 | 689 | /** |
| 644 | 690 | * Wires every content-change hook. |
| 645 | 691 | * |
| 646 | 692 | * One bootstrap so the wiring is auditable — |
| 647 | - * `grep desktop_mode_content_changes_record` finds every emitter. | |
| 693 | + * `grep openstation_content_changes_record` finds every emitter. | |
| 648 | 694 | */ |
| 649 | -function desktop_mode_content_changes_register_hooks() { | |
| 650 | - add_action( 'wp_after_insert_post', 'desktop_mode_content_changes_on_after_insert_post', 10, 4 ); | |
| 695 | +function openstation_content_changes_register_hooks() { | |
| 696 | + add_action( 'wp_after_insert_post', 'openstation_content_changes_on_after_insert_post', 10, 4 ); | |
| 651 | 697 | |
| 652 | - add_action( 'wp_insert_comment', function ( $comment_id ) { | |
| 653 | - desktop_mode_content_changes_record( 'comment', (int) $comment_id, 'created' ); | |
| 654 | - } ); | |
| 655 | - add_action( 'edit_comment', function ( $comment_id ) { | |
| 656 | - desktop_mode_content_changes_record( 'comment', (int) $comment_id, 'updated' ); | |
| 657 | - } ); | |
| 658 | - add_action( 'transition_comment_status', 'desktop_mode_content_changes_on_comment_transition', 10, 3 ); | |
| 698 | + add_action( | |
| 699 | + 'wp_insert_comment', | |
| 700 | + function ( $comment_id ) { | |
| 701 | + openstation_content_changes_record( 'comment', (int) $comment_id, 'created' ); | |
| 702 | + } | |
| 703 | + ); | |
| 704 | + add_action( | |
| 705 | + 'edit_comment', | |
| 706 | + function ( $comment_id ) { | |
| 707 | + openstation_content_changes_record( 'comment', (int) $comment_id, 'updated' ); | |
| 708 | + } | |
| 709 | + ); | |
| 710 | + add_action( 'transition_comment_status', 'openstation_content_changes_on_comment_transition', 10, 3 ); | |
| 659 | 711 | |
| 660 | - desktop_mode_content_changes_register_wc_hooks(); | |
| 661 | - desktop_mode_content_changes_register_plugin_hooks(); | |
| 712 | + openstation_content_changes_register_wc_hooks(); | |
| 713 | + openstation_content_changes_register_plugin_hooks(); | |
| 662 | 714 | |
| 663 | - add_action( 'admin_footer', 'desktop_mode_content_changes_emit_footer', 100 ); | |
| 664 | - add_action( 'shutdown', 'desktop_mode_content_changes_on_shutdown' ); | |
| 665 | - add_filter( 'heartbeat_received', 'desktop_mode_content_changes_heartbeat_received', 10, 2 ); | |
| 715 | + add_action( 'admin_footer', 'openstation_content_changes_emit_footer', 100 ); | |
| 716 | + add_action( 'shutdown', 'openstation_content_changes_on_shutdown' ); | |
| 717 | + add_filter( 'heartbeat_received', 'openstation_content_changes_heartbeat_received', 10, 2 ); | |
| 666 | 718 | } |
| 667 | -add_action( 'init', 'desktop_mode_content_changes_register_hooks', 5 ); | |
| 719 | +add_action( 'init', 'openstation_content_changes_register_hooks', 5 ); | |