PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.13
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.13
1.1.13 1.1.12 1.1.11 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 All 37 releases
← All changes | includes/os-settings.php +169 -10 1.1.2 → 1.1.13 View file →
@@ -30,8 +30,11 @@
30 30
31 31 /** Valid window-radius IDs — mirrors the TS `WINDOW_RADII` constant. */
32 32 const OPENSTATION_OS_SETTINGS_WINDOW_RADII = array( 'sharp', 'default', 'round' );
33 33
34 +/** How a newly opened window lands — mirrors the TS `OPEN_WINDOWS_AS` constant. */
35 +const OPENSTATION_OS_SETTINGS_OPEN_WINDOWS_AS = array( 'default', 'maximized', 'focused' );
36 +
34 37 /**
35 38 * Valid admin-bar mode IDs — mirrors the TS `ADMIN_BAR_MODES` constant.
36 39 *
37 40 * `static` keeps the WordPress admin bar pinned above the shell (the
@@ -52,8 +55,28 @@
52 55 */
53 56 const OPENSTATION_OS_SETTINGS_DOCK_PLACEMENTS = array( 'bottom', 'left', 'right' );
54 57
55 58 /**
59 + * Valid dock-behavior IDs — mirrors the TS `DOCK_BEHAVIORS` constant.
60 + *
61 + * `static` keeps the dock always on screen (the default); `dynamic`
62 + * parks it off its edge behind a peek strip that reveals when the
63 + * pointer reaches that edge or something on it takes keyboard focus,
64 + * and releases the band it floats over from the work area.
65 + */
66 +const OPENSTATION_OS_SETTINGS_DOCK_BEHAVIORS = array( 'static', 'dynamic' );
67 +
68 +/**
69 + * `mobileLayout` — which experience the shell renders. `auto`
70 + * follows the viewport; the other two force it either way.
71 + * Mirrors `OsModePreference` in `src/mode/index.ts`.
72 + */
73 +const OPENSTATION_OS_SETTINGS_MOBILE_LAYOUTS = array( 'auto', 'desktop', 'mobile' );
74 +
75 +/** `mobileTabs` — at most this many ids pinned to the phone tab bar. */
76 +const OPENSTATION_OS_SETTINGS_MOBILE_TABS_MAX = 3;
77 +
78 +/**
56 79 * Playable range for the window-reveal duration override, in ms.
57 80 * Mirrors `MIN_REVEAL_DURATION_MS` / `MAX_REVEAL_DURATION_MS` in
58 81 * `src/reveals/registry.ts`.
59 82 *
@@ -73,8 +96,10 @@
73 96 */
74 97 function openstation_default_os_settings() {
75 98 return array(
76 99 'wallpaper' => 'galaxy',
100 + // Pulse, the brand's signature. Mirrors `DEFAULTS` in
101 + // `src/settings/constants.ts`.
77 102 'accent' => 'pulse',
78 103 // Only read when `accent` is `custom`. Seeded with Pulse so
79 104 // picking Custom before touching the wheel is a no-op rather
80 105 // than a jump to black. Mirrors `DEFAULTS` in
@@ -88,8 +113,9 @@
88 113 // `src/settings/constants.ts` — PHP seeds the first load and JS
89 114 // owns every paint after it, so a mismatch shows up as the
90 115 // corners changing shape a moment after the shell boots.
91 116 'windowRadius' => 'round',
117 + 'openWindowsAs' => 'default',
92 118 // How the WordPress admin bar presents above the shell.
93 119 // `hidden` ships as the default so a fresh desktop has ONE
94 120 // navigation surface: everything the user can open lives on the
95 121 // dock, and the dock's "Exit OpenStation" tile is the way back
@@ -95,8 +121,14 @@
95 121 // dock, and the dock's "Exit OpenStation" tile is the way back
96 122 // to classic admin. `static` (vanilla behavior) and `dynamic`
97 123 // are one pick away in OpenStation Preferences → Appearance.
98 124 'adminBarMode' => 'hidden',
125 + // Always on screen. `dynamic` (auto-hide behind a peek strip)
126 + // is one pick away in OpenStation Preferences → Appearance.
127 + 'dockBehavior' => 'static',
128 + // The Split layout's sidebar answers for itself: a folded
129 + // sidebar over a static bottom dock is a valid desk.
130 + 'sideDockBehavior' => 'static',
99 131 // One dock holding every menu, with the system tiles grouped
100 132 // behind a hairline. `classic` (side bar for core menus + bottom
101 133 // dock for plugins) is the other option; it is no longer what a
102 134 // first-run desktop looks like.
@@ -184,8 +216,13 @@
184 216 // window (e.g. array( 'author', 'tags' )). Empty array means
185 217 // every column is visible. The sticky 'title' column is always
186 218 // shown — the UI prevents toggling it.
187 219 'nativePostsHiddenColumns' => array(),
220 + // Per-user list of column keys hidden in the native Pages
221 + // window (e.g. array( 'author', 'parent' )). Empty array means
222 + // every column is visible. The sticky 'title' column is always
223 + // shown — the UI prevents toggling it.
224 + 'nativePagesHiddenColumns' => array(),
188 225 // Per-user opt-IN for the native Pages window. Same posture as
189 226 // nativePostsEnabled — defaults OFF (Beta), users opt in to swap
190 227 // the classic `edit.php?post_type=page` iframe for the native UI.
191 228 'nativePagesEnabled' => false,
@@ -202,8 +239,17 @@
202 239 // Per-user opt-IN for the native Comments window. Defaults OFF
203 240 // (Beta); the server-side cap gate (`edit_posts`) means the
204 241 // toggle only matters for users who could see the Comments tile.
205 242 'nativeCommentsEnabled' => false,
243 + // Per-user opt-IN for Station Home, the native Dashboard
244 + // window. Defaults OFF: the ordinary `index.php` Dashboard
245 + // (including any custom dashboard a plugin builds there) opens
246 + // as a chromeless iframe until the user opts in via OS
247 + // Settings → Features → Beta features.
248 + 'stationHomeEnabled' => false,
249 + // Performance enhancements are enabled unless explicitly disabled.
250 + 'adminAssetCacheEnabled' => true,
251 + 'windowPrewarmEnabled' => true,
206 252 // When true, left-clicking the empty wallpaper triggers the
207 253 // "Show desktop" toggle (macOS-style) and the matching entry is
208 254 // hidden from the wallpaper context menu. When false (default),
209 255 // the entry stays in the menu and left clicks on the wallpaper
@@ -208,13 +254,20 @@
208 254 // hidden from the wallpaper context menu. When false (default),
209 255 // the entry stays in the menu and left clicks on the wallpaper
210 256 // do nothing. Per-user.
211 257 'showDesktopOnWallpaperClick' => false,
258 + // Whether the close-all-windows shortcut (Alt+Cmd/Ctrl+W) asks
259 + // before it closes. The dialog's "Don't ask again" checkbox is
260 + // what writes false; OpenStation Preferences -> Windows is what
261 + // turns it back on. Per-user.
262 + 'confirmCloseAllWindows' => true,
212 263 // Mio — a soft-body companion that floats over
213 264 // the wallpaper, settles onto nearby windows, and watches the
214 265 // pointer. Off by default; toggled from the wallpaper context
215 266 // menu. Per-user. See `docs/mio.md`.
216 267 'mioEnabled' => false,
268 + 'mioApiEnabled' => false,
269 + 'mioShowOnWallpaper' => true,
217 270 // The user's own Mio, as built in "Make it yours": partial
218 271 // appearance + silhouette overrides, both empty until they
219 272 // touch a control. Stored per user rather than per browser
220 273 // because it is a preference about the person — ten minutes
@@ -231,11 +284,12 @@
231 284 // a glance. Per-user.
232 285 'showPostStatusRibbons' => true,
233 286 // Unlocks developer-facing surfaces meant for plugin
234 287 // authors: the Starter Widget appears in the add-widget
235 - // picker, and the OS Settings → Components tab runs its
236 - // intentional missing-import-warner demo. Off by default.
237 - // Per-user.
288 + // picker, the OS Settings → Components tab runs its
289 + // intentional missing-import-warner demo, and the Code Blue
290 + // error-log reader registers (icon, window, REST routes).
291 + // Off by default. Per-user.
238 292 'developerModeEnabled' => false,
239 293 // Per-user opt-OUT for the folder-sharing feature. Defaults
240 294 // ON. When false:
241 295 // - The Share button, share-settings modal, "Leave shared
@@ -264,8 +318,14 @@
264 318 // Per-user ordering, flat across every dock/sidebar zone. Ids
265 319 // not in the list keep their registration order and render
266 320 // after the listed ones. Unknown ids are tolerated.
267 321 'navOrder' => array(),
322 + // Which experience the shell renders: 'auto' follows the
323 + // viewport, 'desktop' / 'mobile' force it. See `includes/mobile.php`.
324 + 'mobileLayout' => 'auto',
325 + // Ids pinned to the phone tab bar, at most three. Empty means
326 + // the server default (`openstation_mobile_tab_bar`).
327 + 'mobileTabs' => array(),
268 328 // Persisted desktop position for every item the user has
269 329 // promoted to the wallpaper via `navPlacement[id]=desktop|both`.
270 330 // Keyed by item id, value is `{ x: int, y: int }`. The JS
271 331 // synthesizer reads this when building a synthetic placement so
@@ -286,14 +346,14 @@
286 346 */
287 347 function openstation_get_os_settings( $user_id ) {
288 348 $user_id = (int) $user_id;
289 349 if ( $user_id <= 0 ) {
290 - return openstation_default_os_settings();
350 + return openstation_sanitize_os_settings( array() );
291 351 }
292 352
293 353 $raw = get_user_meta( $user_id, OPENSTATION_OS_SETTINGS_META_KEY, true );
294 354 if ( ! is_array( $raw ) ) {
295 - return openstation_default_os_settings();
355 + return openstation_sanitize_os_settings( array() );
296 356 }
297 357
298 358 return openstation_sanitize_os_settings( $raw );
299 359 }
@@ -396,16 +456,22 @@
396 456 function openstation_sanitize_os_settings( $raw ) {
397 457 $defaults = openstation_default_os_settings();
398 458
399 459 if ( ! is_array( $raw ) ) {
400 - return $defaults;
460 + $raw = array();
401 461 }
402 462
403 463 // Wallpaper — any non-empty string; registry membership is validated
404 464 // client-side at apply time.
405 - $wallpaper = isset( $raw['wallpaper'] ) && is_string( $raw['wallpaper'] ) && '' !== $raw['wallpaper']
406 - ? sanitize_key( $raw['wallpaper'] )
407 - : $defaults['wallpaper'];
465 + // Key-clean per `/` segment: a desktop theme's wallpapers are
466 + // registered as `desktop-theme/<theme>/<id>`, and a flat
467 + // `sanitize_key()` would fold that into an id nothing answers to.
468 + $wallpaper = isset( $raw['wallpaper'] ) && is_string( $raw['wallpaper'] )
469 + ? implode( '/', array_filter( array_map( 'sanitize_key', explode( '/', $raw['wallpaper'] ) ) ) )
470 + : '';
471 + if ( '' === $wallpaper ) {
472 + $wallpaper = $defaults['wallpaper'];
473 + }
408 474
409 475 // Accent — non-empty string; swatch validity is enforced in the picker.
410 476 $accent = isset( $raw['accent'] ) && is_string( $raw['accent'] ) && '' !== $raw['accent']
411 477 ? sanitize_key( $raw['accent'] )
@@ -430,8 +496,13 @@
430 496 $window_radius = isset( $raw['windowRadius'] ) && in_array( $raw['windowRadius'], OPENSTATION_OS_SETTINGS_WINDOW_RADII, true )
431 497 ? (string) $raw['windowRadius']
432 498 : $defaults['windowRadius'];
433 499
500 + // How a newly opened window lands — one of the three known values.
501 + $open_windows_as = isset( $raw['openWindowsAs'] ) && in_array( $raw['openWindowsAs'], OPENSTATION_OS_SETTINGS_OPEN_WINDOWS_AS, true )
502 + ? (string) $raw['openWindowsAs']
503 + : $defaults['openWindowsAs'];
504 +
434 505 // Admin-bar mode — must be one of the three known values.
435 506 $admin_bar_mode = isset( $raw['adminBarMode'] )
436 507 && in_array( $raw['adminBarMode'], OPENSTATION_OS_SETTINGS_ADMIN_BAR_MODES, true )
437 508 ? (string) $raw['adminBarMode']
@@ -436,8 +507,19 @@
436 507 && in_array( $raw['adminBarMode'], OPENSTATION_OS_SETTINGS_ADMIN_BAR_MODES, true )
437 508 ? (string) $raw['adminBarMode']
438 509 : $defaults['adminBarMode'];
439 510
511 + // Dock behavior — must be one of the two known values. One answer
512 + // per rail: the dock, and the Split layout's sidebar.
513 + $dock_behavior = isset( $raw['dockBehavior'] )
514 + && in_array( $raw['dockBehavior'], OPENSTATION_OS_SETTINGS_DOCK_BEHAVIORS, true )
515 + ? (string) $raw['dockBehavior']
516 + : $defaults['dockBehavior'];
517 + $side_dock_behavior = isset( $raw['sideDockBehavior'] )
518 + && in_array( $raw['sideDockBehavior'], OPENSTATION_OS_SETTINGS_DOCK_BEHAVIORS, true )
519 + ? (string) $raw['sideDockBehavior']
520 + : $defaults['sideDockBehavior'];
521 +
440 522 // Desktop layout — must be one of the known values (`classic`,
441 523 // `unified`). Default `unified`.
442 524 $desktop_layout = isset( $raw['desktopLayout'] )
443 525 && in_array( $raw['desktopLayout'], OPENSTATION_OS_SETTINGS_DESKTOP_LAYOUTS, true )
@@ -718,8 +800,27 @@
718 800 // user meta indefinitely.
719 801 $native_posts_hidden_columns = array_slice( array_values( array_unique( $native_posts_hidden_columns ) ), 0, 32 );
720 802 }
721 803
804 + $native_pages_hidden_columns = $defaults['nativePagesHiddenColumns'];
805 + if ( isset( $raw['nativePagesHiddenColumns'] ) && is_array( $raw['nativePagesHiddenColumns'] ) ) {
806 + $native_pages_hidden_columns = array();
807 + foreach ( $raw['nativePagesHiddenColumns'] as $col ) {
808 + if ( ! is_string( $col ) || '' === $col ) {
809 + continue;
810 + }
811 + $slug = sanitize_key( $col );
812 + if ( '' === $slug ) {
813 + continue;
814 + }
815 + $native_pages_hidden_columns[] = $slug;
816 + }
817 + // Cap to a sane upper bound — far more than any plausible
818 + // column count, but blocks a malicious payload from bloating
819 + // user meta indefinitely.
820 + $native_pages_hidden_columns = array_slice( array_values( array_unique( $native_pages_hidden_columns ) ), 0, 32 );
821 + }
822 +
722 823 $native_pages_enabled = isset( $raw['nativePagesEnabled'] )
723 824 ? (bool) $raw['nativePagesEnabled']
724 825 : $defaults['nativePagesEnabled'];
725 826
@@ -734,15 +835,29 @@
734 835 $native_comments_enabled = isset( $raw['nativeCommentsEnabled'] )
735 836 ? (bool) $raw['nativeCommentsEnabled']
736 837 : $defaults['nativeCommentsEnabled'];
737 838
839 + $station_home_enabled = isset( $raw['stationHomeEnabled'] )
840 + ? (bool) $raw['stationHomeEnabled']
841 + : $defaults['stationHomeEnabled'];
842 +
843 + // Site-wide performance controls supersede the legacy per-user values.
844 + // Keep the snapshot keys so existing window consumers can read them.
845 + $extended_options = openstation_get_extended_options();
846 + $admin_asset_cache_enabled = $extended_options['admin_asset_cache'];
847 + $window_prewarm_enabled = $extended_options['window_prewarm'];
848 +
738 849 $show_desktop_on_wallpaper_click = isset( $raw['showDesktopOnWallpaperClick'] )
739 850 ? (bool) $raw['showDesktopOnWallpaperClick']
740 851 : $defaults['showDesktopOnWallpaperClick'];
741 852
853 + $confirm_close_all_windows = isset( $raw['confirmCloseAllWindows'] )
854 + ? (bool) $raw['confirmCloseAllWindows']
855 + : $defaults['confirmCloseAllWindows'];
856 +
742 857 $mio_enabled = isset( $raw['mioEnabled'] )
743 858 ? (bool) $raw['mioEnabled']
744 - : $defaults['mioEnabled'];
859 + : ( isset( $raw['mioApiEnabled'] ) ? (bool) $raw['mioApiEnabled'] : $defaults['mioEnabled'] );
745 860
746 861 // A missing key means "no look saved yet", which sanitizes to the
747 862 // same pair of empty arrays the defaults carry — so this needs no
748 863 // isset() branch of its own.
@@ -757,8 +872,35 @@
757 872 $developer_mode_enabled = isset( $raw['developerModeEnabled'] )
758 873 ? (bool) $raw['developerModeEnabled']
759 874 : $defaults['developerModeEnabled'];
760 875
876 + // mobileLayout — the phone/desktop override.
877 + $mobile_layout = isset( $raw['mobileLayout'] )
878 + && in_array( $raw['mobileLayout'], OPENSTATION_OS_SETTINGS_MOBILE_LAYOUTS, true )
879 + ? (string) $raw['mobileLayout']
880 + : $defaults['mobileLayout'];
881 +
882 + // mobileTabs — ordered nav ids pinned to the phone tab bar. Same
883 + // id grammar as navOrder, capped at the tab bar's slot count.
884 + $mobile_tabs = array();
885 + if ( isset( $raw['mobileTabs'] ) && is_array( $raw['mobileTabs'] ) ) {
886 + $seen_tabs = array();
887 + foreach ( $raw['mobileTabs'] as $id ) {
888 + if ( ! is_string( $id ) || '' === $id ) {
889 + continue;
890 + }
891 + $slug = sanitize_key( openstation_canonical_nav_id( $id ) );
892 + if ( '' === $slug || isset( $seen_tabs[ $slug ] ) ) {
893 + continue;
894 + }
895 + $seen_tabs[ $slug ] = true;
896 + $mobile_tabs[] = $slug;
897 + if ( count( $mobile_tabs ) >= OPENSTATION_OS_SETTINGS_MOBILE_TABS_MAX ) {
898 + break;
899 + }
900 + }
901 + }
902 +
761 903 $folders_sharing_enabled = isset( $raw['foldersSharingEnabled'] )
762 904 ? (bool) $raw['foldersSharingEnabled']
763 905 : $defaults['foldersSharingEnabled'];
764 906
@@ -876,11 +1018,14 @@
876 1018 'accent' => $accent,
877 1019 'customAccent' => $custom_accent,
878 1020 'dockSize' => $dock_size,
879 1021 'windowRadius' => $window_radius,
1022 + 'openWindowsAs' => $open_windows_as,
880 1023 'adminBarMode' => $admin_bar_mode,
881 1024 'desktopLayout' => $desktop_layout,
882 1025 'dockPlacement' => $dock_placement,
1026 + 'dockBehavior' => $dock_behavior,
1027 + 'sideDockBehavior' => $side_dock_behavior,
883 1028 'dockRailRenderer' => $dock_rail_renderer,
884 1029 'desktopTheme' => $desktop_theme,
885 1030 'appliedThemeRecommendations' => $applied_theme_recommendations,
886 1031 'unfocusEffect' => $unfocus_effect,
@@ -899,13 +1044,20 @@
899 1044 'heartbeatRate' => $heartbeat_rate,
900 1045 'nativePostsEnabled' => $native_posts_enabled,
901 1046 'nativePostsHiddenColumns' => $native_posts_hidden_columns,
902 1047 'nativePagesEnabled' => $native_pages_enabled,
1048 + 'nativePagesHiddenColumns' => $native_pages_hidden_columns,
903 1049 'nativeUsersEnabled' => $native_users_enabled,
904 1050 'nativePluginsEnabled' => $native_plugins_enabled,
905 1051 'nativeCommentsEnabled' => $native_comments_enabled,
1052 + 'stationHomeEnabled' => $station_home_enabled,
1053 + 'adminAssetCacheEnabled' => $admin_asset_cache_enabled,
1054 + 'windowPrewarmEnabled' => $window_prewarm_enabled,
906 1055 'showDesktopOnWallpaperClick' => $show_desktop_on_wallpaper_click,
1056 + 'confirmCloseAllWindows' => $confirm_close_all_windows,
907 1057 'mioEnabled' => $mio_enabled,
1058 + 'mioApiEnabled' => $mio_enabled,
1059 + 'mioShowOnWallpaper' => isset( $raw['mioShowOnWallpaper'] ) ? (bool) $raw['mioShowOnWallpaper'] : $defaults['mioShowOnWallpaper'],
908 1060 'mioStyle' => $mio_style,
909 1061 'showPostStatusRibbons' => $show_post_status_ribbons,
910 1062 'developerModeEnabled' => $developer_mode_enabled,
911 1063 'foldersSharingEnabled' => $folders_sharing_enabled,
@@ -910,8 +1062,10 @@
910 1062 'developerModeEnabled' => $developer_mode_enabled,
911 1063 'foldersSharingEnabled' => $folders_sharing_enabled,
912 1064 'navPlacement' => $nav_placement,
913 1065 'navOrder' => $nav_order,
1066 + 'mobileLayout' => $mobile_layout,
1067 + 'mobileTabs' => $mobile_tabs,
914 1068 'dockPromotedPositions' => $dock_promoted_positions,
915 1069 );
916 1070 }
917 1071
@@ -1011,8 +1165,13 @@
1011 1165 // true of every path into this handler, not just the ones the
1012 1166 // schema happens to guard.
1013 1167 if ( ! is_array( $payload ) ) {
1014 1168 return rest_ensure_response( openstation_get_os_settings( $user_id ) );
1169 + }
1170 +
1171 + // Normalize an alias-only patch before merging it with the saved master value.
1172 + if ( ! array_key_exists( 'mioEnabled', $payload ) && array_key_exists( 'mioApiEnabled', $payload ) ) {
1173 + $payload['mioEnabled'] = $payload['mioApiEnabled'];
1015 1174 }
1016 1175
1017 1176 openstation_save_os_settings(
1018 1177 $user_id,