PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.12
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.12
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 0.8.9 All 36 releases
← All changes | includes/seen-intros.php +80 -27 1.0.0 → 1.1.12 View file →
@@ -1,25 +1,28 @@
1 1 <?php
2 2 /**
3 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 + * Three 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, the rebrand
11 + * notice (`src/rebrand-notice.ts`, slug `openstation-rebrand`), and
12 + * the usage feedback prompt (`includes/feedback/usage.php`, slug
13 + * `usage-feedback`, marked server-side on a successful send). The
14 + * key is intentionally generic, so anything else that needs
15 + * show-once semantics registers its own slug and reuses this storage.
16 + * OpenStation Preferences → Features exposes a "Reset what's-new
17 + * dialogs" button that clears the whole list.
16 18 *
17 19 * Storage shape:
18 20 * 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.
21 + * `[ 'activation-welcome' ]`, `[ 'openstation-rebrand' ]`, etc.
22 + * Slug values pass through `sanitize_key()` and the list is capped
23 + * at 64 entries so a runaway client cannot bloat user-meta
24 + * indefinitely.
22 25 *
23 26 * @package OpenStation
24 27 */
25 28
@@ -77,9 +80,10 @@
77 80 /**
78 81 * Adds a slug to the user's seen-intros list.
79 82 *
80 83 * Idempotent — re-marking an already-seen intro is a no-op that
81 - * still returns true.
84 + * still returns true. A slug that supersedes others
85 + * ({@see openstation_seen_intros_superseded_by()}) removes them first.
82 86 *
83 87 * @param int $user_id User ID.
84 88 * @param string $slug Intro slug.
85 89 * @return bool True on successful write (or no-op), false otherwise.
@@ -91,23 +95,47 @@
91 95 return false;
92 96 }
93 97
94 98 $current = openstation_get_seen_intros( $user_id );
95 - if ( in_array( $slug, $current, true ) ) {
99 + $kept = array_values( array_diff( $current, openstation_seen_intros_superseded_by( $slug ) ) );
100 + if ( in_array( $slug, $kept, true ) && count( $kept ) === count( $current ) ) {
96 101 return true;
97 102 }
98 103
99 - $current[] = $slug;
100 - $current = array_slice( $current, 0, OPENSTATION_SEEN_INTROS_MAX );
104 + if ( ! in_array( $slug, $kept, true ) ) {
105 + $kept[] = $slug;
106 + }
107 + $kept = array_slice( $kept, 0, OPENSTATION_SEEN_INTROS_MAX );
101 108
102 109 return false !== update_user_meta(
103 110 $user_id,
104 111 OPENSTATION_SEEN_INTROS_META_KEY,
105 - $current
112 + $kept
106 113 );
107 114 }
108 115
109 116 /**
117 + * Slugs a newly recorded one makes obsolete: facts where only the
118 + * latest counts, which an append-only list cannot otherwise express.
119 + *
120 + * The shell tour's two outcomes are the one pair. A run ends skipped
121 + * or finished, and the relaunch icon asks about the LATEST run: kept
122 + * side by side, one finished run long ago hid the icon after every
123 + * skip that came later. The strings mirror the constants in
124 + * `includes/first-run/shell-tour.php`, which loads after this file.
125 + *
126 + * @param string $slug The slug being recorded.
127 + * @return string[] Slugs it replaces.
128 + */
129 +function openstation_seen_intros_superseded_by( $slug ) {
130 + $pairs = array(
131 + 'shell-tour-skipped' => array( 'shell-tour-done' ),
132 + 'shell-tour-done' => array( 'shell-tour-skipped' ),
133 + );
134 + return isset( $pairs[ $slug ] ) ? $pairs[ $slug ] : array();
135 +}
136 +
137 +/**
110 138 * Wipes every seen-intro entry for the user. Used by the OS
111 139 * Settings → Features "Reset what's-new dialogs" button.
112 140 *
113 141 * @param int $user_id User ID.
@@ -184,24 +212,49 @@
184 212 }
185 213 add_action( 'rest_api_init', 'openstation_register_seen_intros_routes' );
186 214
187 215 /**
216 + * The intro slugs whose dismissal is accepted from an account that has
217 + * NOT enabled OpenStation.
218 + *
219 + * Exactly the intros that render in the classic admin while the shell
220 + * is off: the welcome dialog and the activation nudge. Everything else
221 + * is shown inside the shell and keeps the strict gate. Adding a slug
222 + * here is adding a classic-admin surface; the allowlist is the review
223 + * point, so keep it a literal list.
224 + *
225 + * @return string[]
226 + */
227 +function openstation_seen_intros_classic_admin_slugs() {
228 + $slugs = array();
229 + if ( defined( 'OPENSTATION_WELCOME_INTRO_SLUG' ) ) {
230 + $slugs[] = OPENSTATION_WELCOME_INTRO_SLUG;
231 + }
232 + if ( defined( 'OPENSTATION_ACTIVATION_NUDGE_INTRO_SLUG' ) ) {
233 + $slugs[] = OPENSTATION_ACTIVATION_NUDGE_INTRO_SLUG;
234 + }
235 + return $slugs;
236 +}
237 +
238 +/**
188 239 * Permission gate for the seen-intros routes.
189 240 *
190 - * In-shell intros (slug `posts`, `pages`, …) are only ever shown to a
191 - * user who has already entered OpenStation, so they keep the strict
241 + * In-shell announcements (the rebrand notice, and anything a plugin
242 + * registers) are only ever shown to a user who has already entered
243 + * OpenStation, so they keep the strict
192 244 * {@see openstation_rest_require_enabled()} gate — `read` alone is
193 245 * insufficient (every role, Subscriber included, carries `read`).
194 246 *
195 - * The one exception is the first-run welcome dialog
196 - * ({@see OPENSTATION_WELCOME_INTRO_SLUG}): it renders in the *classic*
247 + * The exceptions are the classic-admin intros
248 + * ({@see openstation_seen_intros_classic_admin_slugs()}): the first-run
249 + * welcome dialog and the activation nudge both render in the *classic*
197 250 * admin precisely when OpenStation is NOT enabled, which is the only
198 - * state it ever appears in. Gating its dismissal behind
251 + * state they ever appear in. Gating their dismissal behind
199 252 * `openstation_rest_require_enabled()` would make the dismissal POST
200 253 * return 403 every time, so the slug could never be recorded as seen and
201 - * the dialog re-rendered on every classic-admin page load. We therefore
202 - * let that single slug through for any logged-in `read`-capable account
203 - * (the exact audience the dialog is shown to); writing one's own
254 + * the dialog / notice re-rendered on every classic-admin page load. We
255 + * therefore let those slugs through for any logged-in `read`-capable
256 + * account (the exact audience they are shown to); writing one's own
204 257 * dismissal flag carries no privileged surface. The DELETE /intros route
205 258 * ("Reset what's-new dialogs") carries no slug and keeps the strict gate.
206 259 *
207 260 * @param WP_REST_Request $request The REST request.
@@ -208,9 +261,9 @@
208 261 * @return true|WP_Error
209 262 */
210 263 function openstation_rest_seen_intros_permission( WP_REST_Request $request ) {
211 264 $slug = sanitize_key( (string) $request->get_param( 'slug' ) );
212 - if ( defined( 'OPENSTATION_WELCOME_INTRO_SLUG' ) && OPENSTATION_WELCOME_INTRO_SLUG === $slug ) {
265 + if ( '' !== $slug && in_array( $slug, openstation_seen_intros_classic_admin_slugs(), true ) ) {
213 266 if ( ! is_user_logged_in() ) {
214 267 return new WP_Error(
215 268 'rest_forbidden',
216 269 __( 'Authentication required.', 'desktop-mode' ),