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/seen-intros.php +63 -75 0.9.31.1.10 View file →
@@ -1,75 +1,79 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — "Seen intros" registry.
3 + * OpenStation — "Seen intros" registry.
4 4 *
5 - * Tracks which one-time introduction dialogs the current user has
6 - * already dismissed, so the shell can show a "what's new in this
7 - * native app" dialog the first time a ported native window opens
8 - * and never bother the user again afterwards.
5 + * Tracks which one-time announcements the current user has already
6 + * dismissed, so each is shown once and never bothers them again.
9 7 *
10 - * Today the surface is the native Posts window. The same key is
11 - * intentionally generic — any future ported native app (Pages,
12 - * Comments, Users, Plugins, …) registers its own slug and reuses
13 - * this storage. OS Settings → Features exposes a "Reset what's-new
14 - * dialogs" button that clears the whole list so the user can see
15 - * every intro again from scratch.
8 + * Two surfaces use it today: the activation welcome dialog
9 + * (`includes/welcome-dialog.php`, slug `activation-welcome`), shown
10 + * in the classic admin while OpenStation is disabled, and the rebrand
11 + * notice (`src/rebrand-notice.ts`, slug `openstation-rebrand`). The
12 + * key is intentionally generic, so anything else that needs
13 + * show-once semantics registers its own slug and reuses this storage.
14 + * OpenStation Preferences → Features exposes a "Reset what's-new
15 + * dialogs" button that clears the whole list.
16 16 *
17 17 * Storage shape:
18 18 * user meta `desktop_mode_seen_intros` → array<string> of slugs.
19 - * `[ 'posts' ]`, `[ 'posts', 'pages' ]`, etc. Slug values pass
20 - * through `sanitize_key()` and the list is capped at 64 entries
21 - * so a runaway client cannot bloat user-meta indefinitely.
19 + * `[ 'activation-welcome' ]`, `[ 'openstation-rebrand' ]`, etc.
20 + * Slug values pass through `sanitize_key()` and the list is capped
21 + * at 64 entries so a runaway client cannot bloat user-meta
22 + * indefinitely.
22 23 *
23 - * @package WPDesktopMode
24 - * @since 0.8.0
24 + * @package OpenStation
25 25 */
26 26
27 27 defined( 'ABSPATH' ) || exit;
28 28
29 -/** User meta key — see file header for shape. */
30 -const DESKTOP_MODE_SEEN_INTROS_META_KEY = 'desktop_mode_seen_intros';
29 +/**
30 + * User meta key — see file header for shape.
31 + *
32 + * The VALUE keeps its pre-rebrand spelling on purpose: it is a
33 + * persisted or externally-visible identifier, so renaming it would
34 + * orphan data already written by live installs (or break a live
35 + * URL). The mismatch between this constant's name and its value is
36 + * deliberate — it is NOT a half-finished rename.
37 + */
38 +const OPENSTATION_SEEN_INTROS_META_KEY = 'desktop_mode_seen_intros';
31 39
32 40 /** Hard cap so a malicious client cannot grow the list unbounded. */
33 -const DESKTOP_MODE_SEEN_INTROS_MAX = 64;
41 +const OPENSTATION_SEEN_INTROS_MAX = 64;
34 42
35 43 /**
36 44 * Returns the list of intro slugs the user has dismissed.
37 45 *
38 - * @since 0.8.0
39 - *
40 46 * @param int $user_id User ID.
41 47 * @return string[] Sanitized list (may be empty).
42 48 */
43 -function desktop_mode_get_seen_intros( $user_id ) {
49 +function openstation_get_seen_intros( $user_id ) {
44 50 $user_id = (int) $user_id;
45 51 if ( $user_id <= 0 ) {
46 52 return array();
47 53 }
48 54
49 - $raw = get_user_meta( $user_id, DESKTOP_MODE_SEEN_INTROS_META_KEY, true );
55 + $raw = get_user_meta( $user_id, OPENSTATION_SEEN_INTROS_META_KEY, true );
50 56 if ( ! is_array( $raw ) ) {
51 57 return array();
52 58 }
53 59
54 - return desktop_mode_sanitize_seen_intros( $raw );
60 + return openstation_sanitize_seen_intros( $raw );
55 61 }
56 62
57 63 /**
58 64 * Whether the user has already dismissed the given intro.
59 65 *
60 - * @since 0.8.0
61 - *
62 66 * @param int $user_id User ID.
63 67 * @param string $slug Intro slug (e.g. `'posts'`).
64 68 * @return bool
65 69 */
66 -function desktop_mode_has_seen_intro( $user_id, $slug ) {
70 +function openstation_has_seen_intro( $user_id, $slug ) {
67 71 $slug = sanitize_key( (string) $slug );
68 72 if ( '' === $slug ) {
69 73 return false;
70 74 }
71 - return in_array( $slug, desktop_mode_get_seen_intros( $user_id ), true );
75 + return in_array( $slug, openstation_get_seen_intros( $user_id ), true );
72 76 }
73 77
74 78 /**
75 79 * Adds a slug to the user's seen-intros list.
@@ -76,15 +80,13 @@
76 80 *
77 81 * Idempotent — re-marking an already-seen intro is a no-op that
78 82 * still returns true.
79 83 *
80 - * @since 0.8.0
81 - *
82 84 * @param int $user_id User ID.
83 85 * @param string $slug Intro slug.
84 86 * @return bool True on successful write (or no-op), false otherwise.
85 87 */
86 -function desktop_mode_mark_intro_seen( $user_id, $slug ) {
88 +function openstation_mark_intro_seen( $user_id, $slug ) {
87 89 $user_id = (int) $user_id;
88 90 $slug = sanitize_key( (string) $slug );
89 91 if ( $user_id <= 0 || '' === $slug ) {
90 92 return false;
@@ -89,19 +91,19 @@
89 91 if ( $user_id <= 0 || '' === $slug ) {
90 92 return false;
91 93 }
92 94
93 - $current = desktop_mode_get_seen_intros( $user_id );
95 + $current = openstation_get_seen_intros( $user_id );
94 96 if ( in_array( $slug, $current, true ) ) {
95 97 return true;
96 98 }
97 99
98 100 $current[] = $slug;
99 - $current = array_slice( $current, 0, DESKTOP_MODE_SEEN_INTROS_MAX );
101 + $current = array_slice( $current, 0, OPENSTATION_SEEN_INTROS_MAX );
100 102
101 103 return false !== update_user_meta(
102 104 $user_id,
103 - DESKTOP_MODE_SEEN_INTROS_META_KEY,
105 + OPENSTATION_SEEN_INTROS_META_KEY,
104 106 $current
105 107 );
106 108 }
107 109
@@ -108,30 +110,26 @@
108 110 /**
109 111 * Wipes every seen-intro entry for the user. Used by the OS
110 112 * Settings → Features "Reset what's-new dialogs" button.
111 113 *
112 - * @since 0.8.0
113 - *
114 114 * @param int $user_id User ID.
115 115 * @return bool True on success.
116 116 */
117 -function desktop_mode_clear_seen_intros( $user_id ) {
117 +function openstation_clear_seen_intros( $user_id ) {
118 118 $user_id = (int) $user_id;
119 119 if ( $user_id <= 0 ) {
120 120 return false;
121 121 }
122 - return (bool) delete_user_meta( $user_id, DESKTOP_MODE_SEEN_INTROS_META_KEY );
122 + return (bool) delete_user_meta( $user_id, OPENSTATION_SEEN_INTROS_META_KEY );
123 123 }
124 124
125 125 /**
126 126 * Coerces a raw payload to a clean list of slugs.
127 127 *
128 - * @since 0.8.0
129 - *
130 128 * @param mixed $raw Raw value.
131 129 * @return string[]
132 130 */
133 -function desktop_mode_sanitize_seen_intros( $raw ) {
131 +function openstation_sanitize_seen_intros( $raw ) {
134 132 if ( ! is_array( $raw ) ) {
135 133 return array();
136 134 }
137 135 $out = array();
@@ -144,9 +142,9 @@
144 142 continue;
145 143 }
146 144 $out[] = $slug;
147 145 }
148 - return array_slice( array_values( array_unique( $out ) ), 0, DESKTOP_MODE_SEEN_INTROS_MAX );
146 + return array_slice( array_values( array_unique( $out ) ), 0, OPENSTATION_SEEN_INTROS_MAX );
149 147 }
150 148
151 149 /**
152 150 * Registers REST routes for the seen-intros surface.
@@ -156,19 +154,17 @@
156 154 * DELETE /desktop-mode/v1/intros no body — clears the list
157 155 *
158 156 * Both return the post-mutation list so the client can refresh its
159 157 * local snapshot without a follow-up GET.
160 - *
161 - * @since 0.8.0
162 158 */
163 -function desktop_mode_register_seen_intros_routes() {
159 +function openstation_register_seen_intros_routes() {
164 160 register_rest_route(
165 161 'desktop-mode/v1',
166 162 '/intros/seen',
167 163 array(
168 164 'methods' => WP_REST_Server::CREATABLE,
169 - 'callback' => 'desktop_mode_rest_mark_intro_seen',
170 - 'permission_callback' => 'desktop_mode_rest_seen_intros_permission',
165 + 'callback' => 'openstation_rest_mark_intro_seen',
166 + 'permission_callback' => 'openstation_rest_seen_intros_permission',
171 167 'args' => array(
172 168 'slug' => array(
173 169 'required' => true,
174 170 'type' => 'string',
@@ -181,28 +177,29 @@
181 177 'desktop-mode/v1',
182 178 '/intros',
183 179 array(
184 180 'methods' => WP_REST_Server::DELETABLE,
185 - 'callback' => 'desktop_mode_rest_clear_seen_intros',
186 - 'permission_callback' => 'desktop_mode_rest_seen_intros_permission',
181 + 'callback' => 'openstation_rest_clear_seen_intros',
182 + 'permission_callback' => 'openstation_rest_seen_intros_permission',
187 183 )
188 184 );
189 185 }
190 -add_action( 'rest_api_init', 'desktop_mode_register_seen_intros_routes' );
186 +add_action( 'rest_api_init', 'openstation_register_seen_intros_routes' );
191 187
192 188 /**
193 189 * Permission gate for the seen-intros routes.
194 190 *
195 - * In-shell intros (slug `posts`, `pages`, …) are only ever shown to a
196 - * user who has already entered Desktop Mode, so they keep the strict
197 - * {@see desktop_mode_rest_require_enabled()} gate — `read` alone is
191 + * In-shell announcements (the rebrand notice, and anything a plugin
192 + * registers) are only ever shown to a user who has already entered
193 + * OpenStation, so they keep the strict
194 + * {@see openstation_rest_require_enabled()} gate — `read` alone is
198 195 * insufficient (every role, Subscriber included, carries `read`).
199 196 *
200 197 * The one exception is the first-run welcome dialog
201 - * ({@see DESKTOP_MODE_WELCOME_INTRO_SLUG}): it renders in the *classic*
202 - * admin precisely when Desktop Mode is NOT enabled, which is the only
198 + * ({@see OPENSTATION_WELCOME_INTRO_SLUG}): it renders in the *classic*
199 + * admin precisely when OpenStation is NOT enabled, which is the only
203 200 * state it ever appears in. Gating its dismissal behind
204 - * `desktop_mode_rest_require_enabled()` would make the dismissal POST
201 + * `openstation_rest_require_enabled()` would make the dismissal POST
205 202 * return 403 every time, so the slug could never be recorded as seen and
206 203 * the dialog re-rendered on every classic-admin page load. We therefore
207 204 * let that single slug through for any logged-in `read`-capable account
208 205 * (the exact audience the dialog is shown to); writing one's own
@@ -208,19 +205,14 @@
208 205 * (the exact audience the dialog is shown to); writing one's own
209 206 * dismissal flag carries no privileged surface. The DELETE /intros route
210 207 * ("Reset what's-new dialogs") carries no slug and keeps the strict gate.
211 208 *
212 - * @since 0.8.0
213 - * @since 0.8.10 Hardened to require desktop mode enabled (was `read`).
214 - * @since 0.30.1 Allow the `activation-welcome` slug without the enabled
215 - * gate, so the welcome dialog's dismissal can persist.
216 - *
217 209 * @param WP_REST_Request $request The REST request.
218 210 * @return true|WP_Error
219 211 */
220 -function desktop_mode_rest_seen_intros_permission( WP_REST_Request $request ) {
212 +function openstation_rest_seen_intros_permission( WP_REST_Request $request ) {
221 213 $slug = sanitize_key( (string) $request->get_param( 'slug' ) );
222 - if ( defined( 'DESKTOP_MODE_WELCOME_INTRO_SLUG' ) && DESKTOP_MODE_WELCOME_INTRO_SLUG === $slug ) {
214 + if ( defined( 'OPENSTATION_WELCOME_INTRO_SLUG' ) && OPENSTATION_WELCOME_INTRO_SLUG === $slug ) {
223 215 if ( ! is_user_logged_in() ) {
224 216 return new WP_Error(
225 217 'rest_forbidden',
226 218 __( 'Authentication required.', 'desktop-mode' ),
@@ -236,32 +228,30 @@
236 228 }
237 229 return true;
238 230 }
239 231
240 - return desktop_mode_rest_require_enabled();
232 + return openstation_rest_require_enabled();
241 233 }
242 234
243 235 /**
244 236 * REST handler for `POST /desktop-mode/v1/intros/seen`.
245 237 *
246 - * @since 0.8.0
247 - *
248 238 * @param WP_REST_Request $request REST request.
249 239 * @return WP_REST_Response|WP_Error
250 240 */
251 -function desktop_mode_rest_mark_intro_seen( WP_REST_Request $request ) {
241 +function openstation_rest_mark_intro_seen( WP_REST_Request $request ) {
252 242 $user_id = get_current_user_id();
253 243 $slug = sanitize_key( (string) $request->get_param( 'slug' ) );
254 244 if ( '' === $slug ) {
255 245 return new WP_Error(
256 - 'desktop_mode_invalid_intro_slug',
246 + 'openstation_invalid_intro_slug',
257 247 __( 'The `slug` parameter must be a non-empty string.', 'desktop-mode' ),
258 248 array( 'status' => 400 )
259 249 );
260 250 }
261 - desktop_mode_mark_intro_seen( $user_id, $slug );
251 + openstation_mark_intro_seen( $user_id, $slug );
262 252 return rest_ensure_response(
263 - array( 'seenIntros' => desktop_mode_get_seen_intros( $user_id ) )
253 + array( 'seenIntros' => openstation_get_seen_intros( $user_id ) )
264 254 );
265 255 }
266 256
267 257 /**
@@ -266,15 +256,13 @@
266 256
267 257 /**
268 258 * REST handler for `DELETE /desktop-mode/v1/intros`.
269 259 *
270 - * @since 0.8.0
271 - *
272 260 * @return WP_REST_Response
273 261 */
274 -function desktop_mode_rest_clear_seen_intros() {
262 +function openstation_rest_clear_seen_intros() {
275 263 $user_id = get_current_user_id();
276 - desktop_mode_clear_seen_intros( $user_id );
264 + openstation_clear_seen_intros( $user_id );
277 265 return rest_ensure_response(
278 - array( 'seenIntros' => desktop_mode_get_seen_intros( $user_id ) )
266 + array( 'seenIntros' => openstation_get_seen_intros( $user_id ) )
279 267 );
280 268 }