| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | - * Desktop Mode — Widgets registry. | |
| 3 | + * OpenStation — Widgets registry. | |
| 4 | 4 | * |
| 5 | 5 | * Server-side registration API + payload builder + asset enqueue |
| 6 | 6 | * for the right-column widget layer. Plugin-side JS publishes |
| 7 | - * the full widget def on `window.desktopModeWidgets[ id ]`; this | |
| 7 | + * the full widget def on `window.openStationWidgets[ id ]`; this | |
| 8 | 8 | * module is the PHP side that announces them and ships their |
| 9 | 9 | * script handles into the boot payload. |
| 10 | 10 | * |
| 11 | 11 | * Extracted from `components.php` during the architecture-0.8.1 |
| @@ -10,10 +10,9 @@ | ||
| 10 | 10 | * |
| 11 | 11 | * Extracted from `components.php` during the architecture-0.8.1 |
| 12 | 12 | * PHP slicing (phase 6). |
| 13 | 13 | * |
| 14 | - * @package Desktop_Mode | |
| 15 | - * @since 0.8.1 | |
| 14 | + * @package OpenStation | |
| 16 | 15 | */ |
| 17 | 16 | |
| 18 | 17 | defined( 'ABSPATH' ) || exit; |
| 19 | 18 | |
| @@ -18,9 +17,9 @@ | ||
| 18 | 17 | defined( 'ABSPATH' ) || exit; |
| 19 | 18 | |
| 20 | 19 | /** |
| 21 | 20 | * Register a server-side desktop widget. Symmetric to |
| 22 | - * {@see desktop_mode_register_window()} for the right-column widget | |
| 21 | + * {@see openstation_register_window()} for the right-column widget | |
| 23 | 22 | * layer: plugin declares the widget's metadata + script handle in |
| 24 | 23 | * PHP; shell syncs its registry from the live payload so |
| 25 | 24 | * activation / deactivation map to picker add / remove without a |
| 26 | 25 | * browser reload. |
| @@ -26,9 +25,9 @@ | ||
| 26 | 25 | * browser reload. |
| 27 | 26 | * |
| 28 | 27 | * The mount callback still lives in JS — not serializable across |
| 29 | 28 | * the wire. Plugins register it on |
| 30 | - * `window.desktopModeWidgets[ <id> ]` as a `(container, ctx) => | |
| 29 | + * `window.openStationWidgets[ <id> ]` as a `(container, ctx) => | |
| 31 | 30 | * teardown` function. The shell reads that global once the |
| 32 | 31 | * declared script loads and wraps it into a WidgetDef. |
| 33 | 32 | * |
| 34 | 33 | * Example: |
| @@ -33,9 +32,9 @@ | ||
| 33 | 32 | * |
| 34 | 33 | * Example: |
| 35 | 34 | * |
| 36 | 35 | * ```php |
| 37 | - * desktop_mode_register_widget( 'myplugin/stats', array( | |
| 36 | + * openstation_register_widget( 'myplugin/stats', array( | |
| 38 | 37 | * 'label' => __( 'Stats', 'my-plugin' ), |
| 39 | 38 | * 'description' => __( 'Live analytics rollup', 'my-plugin' ), |
| 40 | 39 | * 'icon' => 'dashicons-chart-bar', |
| 41 | 40 | * 'script' => 'my-plugin-desktop-widgets', |
| @@ -47,22 +46,17 @@ | ||
| 47 | 46 | * ``` |
| 48 | 47 | * |
| 49 | 48 | * ```js |
| 50 | 49 | * // Inside my-plugin-desktop-widgets.js: |
| 51 | - * window.desktopModeWidgets = window.desktopModeWidgets || {}; | |
| 52 | - * window.desktopModeWidgets[ 'myplugin/stats' ] = function ( container, ctx ) { | |
| 50 | + * window.openStationWidgets = window.openStationWidgets || {}; | |
| 51 | + * window.openStationWidgets[ 'myplugin/stats' ] = function ( container, ctx ) { | |
| 53 | 52 | * container.append( buildDOM() ); |
| 54 | 53 | * return function teardown() { }; |
| 55 | 54 | * }; |
| 56 | 55 | * ``` |
| 57 | 56 | * |
| 58 | - * @since 0.8.1 | |
| 59 | - * @since 0.8.1 Returns `WP_Error` on validation failure instead of | |
| 60 | - * silent `false`. Legacy `if ( $result )` callers remain | |
| 61 | - * correct because `WP_Error` is truthy. | |
| 62 | - * | |
| 63 | 57 | * @param string $id Widget id. Must match the key the JS side |
| 64 | - * uses on `window.desktopModeWidgets[ … ]`. | |
| 58 | + * uses on `window.openStationWidgets[ … ]`. | |
| 65 | 59 | * @param array $args { |
| 66 | 60 | * @type string $label Human-readable picker label. Required. |
| 67 | 61 | * @type string $description Picker subtitle. Default empty. |
| 68 | 62 | * @type string $icon Dashicons class for the picker. |
| @@ -81,17 +75,17 @@ | ||
| 81 | 75 | * @type int $default_width First-mount floating width. |
| 82 | 76 | * @type int $default_height First-mount floating height. |
| 83 | 77 | * @type string[] $capabilities Gate: ALL caps must match. Any |
| 84 | 78 | * missed cap returns |
| 85 | - * `WP_Error desktop_mode_capability_denied`. | |
| 79 | + * `WP_Error openstation_capability_denied`. | |
| 86 | 80 | * } |
| 87 | 81 | * @return true|WP_Error `true` on success; `WP_Error` otherwise. |
| 88 | 82 | */ |
| 89 | -function desktop_mode_register_widget( $id, $args = array() ) { | |
| 83 | +function openstation_register_widget( $id, $args = array() ) { | |
| 90 | 84 | $id = (string) $id; |
| 91 | 85 | if ( '' === $id ) { |
| 92 | - return desktop_mode_registration_error( | |
| 93 | - 'desktop_mode_missing_id', | |
| 86 | + return openstation_registration_error( | |
| 87 | + 'openstation_missing_id', | |
| 94 | 88 | __( 'Widget id is required.', 'desktop-mode' ) |
| 95 | 89 | ); |
| 96 | 90 | } |
| 97 | 91 | |
| @@ -109,20 +103,23 @@ | ||
| 109 | 103 | 'default_width' => 0, |
| 110 | 104 | 'default_height' => 0, |
| 111 | 105 | 'capabilities' => array(), |
| 112 | 106 | ); |
| 113 | - $args = wp_parse_args( $args, $defaults ); | |
| 107 | + $args = wp_parse_args( $args, $defaults ); | |
| 114 | 108 | |
| 115 | 109 | foreach ( (array) $args['capabilities'] as $cap ) { |
| 116 | 110 | if ( ! current_user_can( (string) $cap ) ) { |
| 117 | - return desktop_mode_registration_error( | |
| 118 | - 'desktop_mode_capability_denied', | |
| 111 | + return openstation_registration_error( | |
| 112 | + 'openstation_capability_denied', | |
| 119 | 113 | sprintf( |
| 120 | 114 | /* translators: %s: capability slug. */ |
| 121 | 115 | __( 'Current user lacks the %s capability required to register this widget.', 'desktop-mode' ), |
| 122 | 116 | (string) $cap |
| 123 | 117 | ), |
| 124 | - array( 'capability' => (string) $cap, 'id' => $id ) | |
| 118 | + array( | |
| 119 | + 'capability' => (string) $cap, | |
| 120 | + 'id' => $id, | |
| 121 | + ) | |
| 125 | 122 | ); |
| 126 | 123 | } |
| 127 | 124 | } |
| 128 | 125 | |
| @@ -129,10 +126,10 @@ | ||
| 129 | 126 | // Required fields. The script handle isn't strictly required — |
| 130 | 127 | // a plugin could register a widget whose mount callback is |
| 131 | 128 | // declared on the shell page's own JS (edge case; still valid). |
| 132 | 129 | if ( '' === (string) $args['label'] ) { |
| 133 | - return desktop_mode_registration_error( | |
| 134 | - 'desktop_mode_missing_label', | |
| 130 | + return openstation_registration_error( | |
| 131 | + 'openstation_missing_label', | |
| 135 | 132 | __( 'Widget registration requires a non-empty `label`.', 'desktop-mode' ), |
| 136 | 133 | array( 'id' => $id ) |
| 137 | 134 | ); |
| 138 | 135 | } |
| @@ -151,22 +148,20 @@ | ||
| 151 | 148 | 'max_height' => (int) $args['max_height'], |
| 152 | 149 | 'default_width' => (int) $args['default_width'], |
| 153 | 150 | 'default_height' => (int) $args['default_height'], |
| 154 | 151 | ); |
| 155 | - desktop_mode_desktop_widget_registry( $id, $entry ); | |
| 152 | + openstation_desktop_widget_registry( $id, $entry ); | |
| 156 | 153 | |
| 157 | 154 | /** |
| 158 | 155 | * Fires after a desktop widget is successfully registered. |
| 159 | 156 | * |
| 160 | - * Does NOT fire when `desktop_mode_register_widget()` returns a | |
| 157 | + * Does NOT fire when `openstation_register_widget()` returns a | |
| 161 | 158 | * `WP_Error`. |
| 162 | 159 | * |
| 163 | - * @since 0.8.1 | |
| 164 | - * | |
| 165 | 160 | * @param string $id The widget id. |
| 166 | 161 | * @param array $entry The stored registry entry. |
| 167 | 162 | */ |
| 168 | - do_action( 'desktop_mode_widget_registered', $id, $entry ); | |
| 163 | + do_action( 'openstation_widget_registered', $id, $entry ); | |
| 169 | 164 | |
| 170 | 165 | return true; |
| 171 | 166 | } |
| 172 | 167 | |
| @@ -171,15 +166,14 @@ | ||
| 171 | 166 | } |
| 172 | 167 | |
| 173 | 168 | /** |
| 174 | 169 | * Internal module-level registry for widgets registered via |
| 175 | - * {@see desktop_mode_register_widget()}. Same pattern as | |
| 176 | - * {@see desktop_mode_native_window_registry()}. | |
| 170 | + * {@see openstation_register_widget()}. Same pattern as | |
| 171 | + * {@see openstation_native_window_registry()}. | |
| 177 | 172 | * |
| 178 | - * @since 0.8.1 | |
| 179 | 173 | * @internal |
| 180 | 174 | */ |
| 181 | -function desktop_mode_desktop_widget_registry( $id = '', $entry = null ) { | |
| 175 | +function openstation_desktop_widget_registry( $id = '', $entry = null ) { | |
| 182 | 176 | static $store = array(); |
| 183 | 177 | |
| 184 | 178 | if ( '' === (string) $id ) { |
| 185 | 179 | return $store; |
| @@ -191,19 +185,17 @@ | ||
| 191 | 185 | } |
| 192 | 186 | |
| 193 | 187 | /** |
| 194 | 188 | * Build the widget list for the shell payload. Runs through |
| 195 | - * every entry registered via `desktop_mode_register_widget()` and | |
| 189 | + * every entry registered via `openstation_register_widget()` and | |
| 196 | 190 | * attaches the resolved script URL (`wp_scripts()` lookup) so |
| 197 | 191 | * the shell can dynamically inject the script on mid-session |
| 198 | 192 | * plugin activation. |
| 199 | 193 | * |
| 200 | - * @since 0.8.1 | |
| 201 | - * | |
| 202 | 194 | * @return array[] |
| 203 | 195 | */ |
| 204 | -function desktop_mode_build_desktop_widgets_payload() { | |
| 205 | - $registry = desktop_mode_desktop_widget_registry(); | |
| 196 | +function openstation_build_desktop_widgets_payload() { | |
| 197 | + $registry = openstation_desktop_widget_registry(); | |
| 206 | 198 | if ( ! is_array( $registry ) || empty( $registry ) ) { |
| 207 | 199 | return array(); |
| 208 | 200 | } |
| 209 | 201 | |
| @@ -208,28 +200,35 @@ | ||
| 208 | 200 | } |
| 209 | 201 | |
| 210 | 202 | $out = array(); |
| 211 | 203 | foreach ( $registry as $entry ) { |
| 212 | - $script_payload = desktop_mode_resolve_script_payload( $entry['script'] ); | |
| 204 | + $script_payload = openstation_resolve_script_payload( $entry['script'] ); | |
| 213 | 205 | |
| 214 | 206 | $out[] = array( |
| 215 | - 'id' => $entry['id'], | |
| 216 | - 'label' => $entry['label'], | |
| 217 | - 'description' => $entry['description'], | |
| 218 | - 'icon' => $entry['icon'], | |
| 219 | - 'movable' => $entry['movable'], | |
| 220 | - 'resizable' => $entry['resizable'], | |
| 221 | - 'minWidth' => $entry['min_width'], | |
| 222 | - 'minHeight' => $entry['min_height'], | |
| 223 | - 'maxWidth' => $entry['max_width'], | |
| 224 | - 'maxHeight' => $entry['max_height'], | |
| 225 | - 'defaultWidth' => $entry['default_width'], | |
| 226 | - 'defaultHeight' => $entry['default_height'], | |
| 227 | - 'scriptUrl' => $script_payload['url'], | |
| 228 | - 'scriptHandle' => $entry['script'], | |
| 229 | - 'scriptBefore' => $script_payload['before'], | |
| 230 | - 'scriptAfter' => $script_payload['after'], | |
| 231 | - 'scriptL10n' => $script_payload['l10n'], | |
| 207 | + 'id' => $entry['id'], | |
| 208 | + 'label' => $entry['label'], | |
| 209 | + 'description' => $entry['description'], | |
| 210 | + 'icon' => $entry['icon'], | |
| 211 | + 'movable' => $entry['movable'], | |
| 212 | + 'resizable' => $entry['resizable'], | |
| 213 | + 'minWidth' => $entry['min_width'], | |
| 214 | + 'minHeight' => $entry['min_height'], | |
| 215 | + 'maxWidth' => $entry['max_width'], | |
| 216 | + 'maxHeight' => $entry['max_height'], | |
| 217 | + 'defaultWidth' => $entry['default_width'], | |
| 218 | + 'defaultHeight' => $entry['default_height'], | |
| 219 | + 'scriptUrl' => $script_payload['url'], | |
| 220 | + // The packages this widget declares, in load order. | |
| 221 | + // WordPress resolves a script's dependencies when it | |
| 222 | + // enqueues it; a widget bundle is delivered lazily and | |
| 223 | + // never goes through that, so a widget declaring | |
| 224 | + // `wp-api-fetch` found `wp.apiFetch` undefined at mount. | |
| 225 | + // See docs/migration-wp-package-globals.md. | |
| 226 | + 'scriptDeps' => openstation_resolve_script_dependencies( $entry['script'] ), | |
| 227 | + 'scriptHandle' => $entry['script'], | |
| 228 | + 'scriptBefore' => $script_payload['before'], | |
| 229 | + 'scriptAfter' => $script_payload['after'], | |
| 230 | + 'scriptL10n' => $script_payload['l10n'], | |
| 232 | 231 | 'scriptTranslations' => $script_payload['translations'], |
| 233 | 232 | ); |
| 234 | 233 | } |
| 235 | 234 | return $out; |
| @@ -234,26 +233,25 @@ | ||
| 234 | 233 | } |
| 235 | 234 | return $out; |
| 236 | 235 | } |
| 237 | 236 | |
| 238 | -/** | |
| 239 | - * Enqueue plugin-registered widget scripts on the shell page so | |
| 240 | - * widgets active at boot time have their mount callbacks | |
| 241 | - * available without any dynamic-load roundtrip. | |
| 237 | +/* | |
| 238 | + * Widget scripts are NOT enqueued here, and that is deliberate. | |
| 242 | 239 | * |
| 243 | - * @since 0.8.1 | |
| 240 | + * Everything the widget picker shows — label, description, icon, | |
| 241 | + * size constraints — is metadata declared right here in PHP and | |
| 242 | + * shipped in the boot payload. The only thing a plugin's bundle | |
| 243 | + * contributes is the `mount` callback, so the shell assembles the | |
| 244 | + * whole def from the payload and loads the script the first time | |
| 245 | + * the widget is actually mounted. | |
| 246 | + * | |
| 247 | + * A widget the user has never enabled therefore costs a row in the | |
| 248 | + * picker and nothing else. This file used to `wp_enqueue_script()` | |
| 249 | + * every registered one on every admin page, which meant the nine | |
| 250 | + * built-in widget bundles — Drafts at 46 KB, Focus Timer at 41 KB, | |
| 251 | + * Notes at 31 KB, and the rest — were downloaded and parsed by | |
| 252 | + * every user whether or not a single widget was on their desktop. | |
| 253 | + * | |
| 254 | + * See `src/widgets/server-sync.ts`. `scriptUrl` in the payload | |
| 255 | + * (built above) is what makes that possible; nothing else on the | |
| 256 | + * PHP side is involved. | |
| 244 | 257 | */ |
| 245 | -function desktop_mode_enqueue_desktop_widget_scripts() { | |
| 246 | - if ( ! desktop_mode_is_enabled() || desktop_mode_is_chromeless_request() || desktop_mode_is_classic_request() ) { | |
| 247 | - return; | |
| 248 | - } | |
| 249 | - $registry = desktop_mode_desktop_widget_registry(); | |
| 250 | - if ( ! is_array( $registry ) ) { | |
| 251 | - return; | |
| 252 | - } | |
| 253 | - foreach ( $registry as $entry ) { | |
| 254 | - if ( ! empty( $entry['script'] ) ) { | |
| 255 | - wp_enqueue_script( $entry['script'] ); | |
| 256 | - } | |
| 257 | - } | |
| 258 | -} | |
| 259 | -add_action( 'admin_enqueue_scripts', 'desktop_mode_enqueue_desktop_widget_scripts', 20 ); | |