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
desktop-mode / includes / first-run / stamps.php

stamps.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.13, at includes/first-run/stamps.php

345 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — first-run stamps.
4 *
5 * Three timestamps that answer "did this install ever get turned on,
6 * and how fast?" — a question nothing on the site could answer before
7 * them. OpenStation is opt-in per user, so a site can carry the plugin
8 * active for months with nobody ever in the shell, and that is the
9 * install that gets deleted at the next plugin cleanup.
10 *
11 * | Name | Kind | Value |
12 * |---------------------------------|---------------|------------------------------|
13 * | `openstation_installed_at` | option (no autoload) | `{ at, via }` |
14 * | `openstation_first_enabled_at` | option (no autoload) | `{ at, via }` |
15 * | `openstation_enabled_at` | user meta | epoch seconds |
16 *
17 * `via` is `activation` when the activation hook wrote the stamp at
18 * the real moment, and `backfill` when the stamp was reconstructed
19 * later for an install that predates it. A backfilled `at` is the
20 * moment we noticed, not the moment it happened, so every age
21 * computation here reports "unknown" (`null`) rather than a number
22 * that would be wrong by an arbitrary amount. An install with a past
23 * is recognised by the user meta the shell leaves behind — nothing
24 * removes it on deactivate or delete — so a reactivation on such a
25 * site is backfilled too, whatever hook wrote it.
26 *
27 * The user stamp and the site stamp are written by ONE helper,
28 * {@see openstation_record_user_enabled()}, called from both paths
29 * that flip a user on — the admin-bar toggle's AJAX handler and the
30 * portal's auto-enable — so the two cannot drift. It also fires the
31 * `openstation_user_enabled` action, the first enable/disable hook
32 * the plugin has had; the matching `openstation_user_disabled` fires
33 * from {@see openstation_record_user_disabled()}.
34 *
35 * @package OpenStation
36 */
37
38 defined( 'ABSPATH' ) || exit;
39
40 /** Option: when the plugin was activated on this site. autoload=no. */
41 const OPENSTATION_INSTALLED_AT_OPTION = 'openstation_installed_at';
42
43 /** Option: when any user first turned OpenStation on. autoload=no. */
44 const OPENSTATION_FIRST_ENABLED_AT_OPTION = 'openstation_first_enabled_at';
45
46 /** User meta: when this user first turned OpenStation on (epoch seconds). */
47 const OPENSTATION_ENABLED_AT_META_KEY = 'openstation_enabled_at';
48
49 /** Option: the user who last activated the plugin, 0 when nobody was logged in. */
50 const OPENSTATION_ACTIVATED_BY_OPTION = 'openstation_activated_by';
51
52 /**
53 * Normalises a stored stamp to `{ at: int, via: string }`, or null.
54 *
55 * @param mixed $raw Raw option value.
56 * @return array{at:int,via:string}|null
57 */
58 function openstation_normalise_stamp( $raw ) {
59 if ( ! is_array( $raw ) || ! isset( $raw['at'] ) ) {
60 return null;
61 }
62 $via = isset( $raw['via'] ) ? sanitize_key( (string) $raw['via'] ) : 'backfill';
63 if ( ! in_array( $via, array( 'activation', 'backfill' ), true ) ) {
64 $via = 'backfill';
65 }
66 return array(
67 'at' => max( 0, (int) $raw['at'] ),
68 'via' => $via,
69 );
70 }
71
72 /**
73 * The install stamp, or null when nothing has written it yet.
74 *
75 * @return array{at:int,via:string}|null
76 */
77 function openstation_get_install_stamp() {
78 return openstation_normalise_stamp( get_option( OPENSTATION_INSTALLED_AT_OPTION, null ) );
79 }
80
81 /**
82 * The site's first-enable stamp, or null while nobody has enabled.
83 *
84 * @return array{at:int,via:string}|null
85 */
86 function openstation_get_first_enabled_stamp() {
87 return openstation_normalise_stamp( get_option( OPENSTATION_FIRST_ENABLED_AT_OPTION, null ) );
88 }
89
90 /**
91 * When the user first turned OpenStation on, in epoch seconds; 0 when
92 * they never have (or did so before the stamp existed).
93 *
94 * @param int $user_id User ID. Defaults to the current user.
95 * @return int
96 */
97 function openstation_get_user_enabled_at( $user_id = 0 ) {
98 $user_id = (int) $user_id;
99 if ( $user_id <= 0 ) {
100 $user_id = get_current_user_id();
101 }
102 if ( $user_id <= 0 ) {
103 return 0;
104 }
105 return max( 0, (int) get_user_meta( $user_id, OPENSTATION_ENABLED_AT_META_KEY, true ) );
106 }
107
108 /**
109 * Writes the install stamp if, and only if, it is absent.
110 *
111 * Idempotent on purpose: activation fires again on every deactivate /
112 * reactivate cycle, and the first activation is the one that counts.
113 *
114 * A site with prior desktop use ({@see openstation_users_with_prior_desktop_use()})
115 * predates the stamps whatever `$via` says: the plugin was there
116 * before, was deactivated or deleted with the user meta left behind,
117 * and is being activated again. Its install moment is unknown, so the
118 * stamp is written as `backfill`, and so is the first-enable stamp
119 * (`at: 0`) when nothing has written it, because someone did enable
120 * before the stamps existed and the next enable must not pass for
121 * the first. One user query, only while the stamp is absent.
122 *
123 * @param string $via `activation` or `backfill`.
124 * @return bool True when this call wrote the stamp.
125 */
126 function openstation_record_installed( $via = 'activation' ) {
127 if ( null !== openstation_get_install_stamp() ) {
128 return false;
129 }
130 $via = 'activation' === $via ? 'activation' : 'backfill';
131
132 $has_past = function_exists( 'openstation_users_with_prior_desktop_use' )
133 && count( openstation_users_with_prior_desktop_use() ) > 0;
134 if ( $has_past ) {
135 $via = 'backfill';
136 if ( null === openstation_get_first_enabled_stamp() ) {
137 add_option(
138 OPENSTATION_FIRST_ENABLED_AT_OPTION,
139 array(
140 'at' => 0,
141 'via' => 'backfill',
142 ),
143 '',
144 false
145 );
146 }
147 }
148
149 return (bool) add_option(
150 OPENSTATION_INSTALLED_AT_OPTION,
151 array(
152 'at' => time(),
153 'via' => $via,
154 ),
155 '',
156 false
157 );
158 }
159
160 /**
161 * Activation: stamp the real install moment.
162 *
163 * @return void
164 */
165 function openstation_stamp_install_on_activation() {
166 openstation_record_installed( 'activation' );
167 }
168 register_activation_hook( OPENSTATION_FILE, 'openstation_stamp_install_on_activation' );
169
170 /**
171 * Activation: remember who activated the plugin, so the welcome dialog
172 * greets that user and nobody else.
173 *
174 * Written on every activation, because the dialog is for whoever just
175 * activated. A programmatic activation (WP-CLI, a Playground Blueprint)
176 * has no user and writes 0, which shows the dialog to nobody.
177 *
178 * @return void
179 */
180 function openstation_record_activator() {
181 update_option( OPENSTATION_ACTIVATED_BY_OPTION, get_current_user_id() );
182 }
183 register_activation_hook( OPENSTATION_FILE, 'openstation_record_activator' );
184
185 /**
186 * Lazy backfill for installs that predate the stamp.
187 *
188 * Activation does not fire on an update in place, so an install
189 * upgrading into this version has no stamp until someone writes one.
190 * `admin_init` is the first admin request after the update, which is
191 * the earliest honest "we noticed" moment; `via: backfill` records
192 * that the age is unknown. Priority 20 so the migration runner
193 * (priority 10) has already had its turn.
194 *
195 * @return void
196 */
197 function openstation_backfill_install_stamp() {
198 openstation_record_installed( 'backfill' );
199 }
200 add_action( 'admin_init', 'openstation_backfill_install_stamp', 20 );
201
202 /**
203 * Records that a user turned OpenStation on.
204 *
205 * Stamps the user (once), stamps the site (once), then fires
206 * `openstation_user_enabled`. Call it from every path that writes
207 * `desktop_mode_mode = '1'`; the action fires on every enable, not
208 * only the first, so a listener that wants "first time" reads
209 * `$first_on_site` or compares the user's stamp against `time()`.
210 *
211 * @param int $user_id User ID.
212 * @return bool True when this enable is the first on the whole site.
213 */
214 function openstation_record_user_enabled( $user_id ) {
215 $user_id = (int) $user_id;
216 if ( $user_id <= 0 ) {
217 return false;
218 }
219
220 $now = time();
221
222 if ( 0 === openstation_get_user_enabled_at( $user_id ) ) {
223 update_user_meta( $user_id, OPENSTATION_ENABLED_AT_META_KEY, $now );
224 }
225
226 $first_on_site = false;
227 if ( null === openstation_get_first_enabled_stamp() ) {
228 $first_on_site = (bool) add_option(
229 OPENSTATION_FIRST_ENABLED_AT_OPTION,
230 array(
231 'at' => $now,
232 'via' => 'activation',
233 ),
234 '',
235 false
236 );
237 }
238
239 /**
240 * Fires when a user turns OpenStation on.
241 *
242 * Runs after the per-user and per-site first-enable stamps are
243 * written, on every enable (not only the first for that user).
244 *
245 * @param int $user_id The user who enabled OpenStation.
246 * @param bool $first_on_site True when nobody on this site had
247 * ever enabled it before this call.
248 */
249 do_action( 'openstation_user_enabled', $user_id, $first_on_site );
250
251 return $first_on_site;
252 }
253
254 /**
255 * Records that a user turned OpenStation off.
256 *
257 * No stamp is written — the enabled stamps are "first time" facts and
258 * survive a switch back to classic — but the action gives plugins
259 * the other half of the lifecycle.
260 *
261 * @param int $user_id User ID.
262 * @return void
263 */
264 function openstation_record_user_disabled( $user_id ) {
265 $user_id = (int) $user_id;
266 if ( $user_id <= 0 ) {
267 return;
268 }
269
270 /**
271 * Fires when a user turns OpenStation off.
272 *
273 * @param int $user_id The user who disabled OpenStation.
274 */
275 do_action( 'openstation_user_disabled', $user_id );
276 }
277
278 /**
279 * How many whole days since the plugin was installed, or null when the
280 * install stamp is missing or backfilled (age unknown).
281 *
282 * @return int|null
283 */
284 function openstation_install_age_days() {
285 $stamp = openstation_get_install_stamp();
286 if ( null === $stamp || 'activation' !== $stamp['via'] || $stamp['at'] <= 0 ) {
287 return null;
288 }
289 return max( 0, (int) floor( ( time() - $stamp['at'] ) / DAY_IN_SECONDS ) );
290 }
291
292 /**
293 * Whether at least one user enabled OpenStation within `$days` of the
294 * install.
295 *
296 * Returns `true` or `false` when both stamps are real, and `null` when
297 * the answer cannot be known: the install stamp is missing or
298 * backfilled, or the first enable happened before the stamps existed
299 * ({@see openstation_record_installed()} records that as `at: 0, via: backfill`). A site where
300 * nobody has enabled yet answers `false` once it is older than
301 * `$days`, and `null` while the window is still open.
302 *
303 * @param int $days Window in days, from install.
304 * @return bool|null
305 */
306 function openstation_activation_within( $days ) {
307 $days = max( 0, (int) $days );
308 $install = openstation_get_install_stamp();
309 if ( null === $install || 'activation' !== $install['via'] || $install['at'] <= 0 ) {
310 return null;
311 }
312
313 $first = openstation_get_first_enabled_stamp();
314 if ( null === $first ) {
315 // Nobody yet. The answer is only settled once the window closed.
316 if ( time() - $install['at'] > $days * DAY_IN_SECONDS ) {
317 return false;
318 }
319 return null;
320 }
321 if ( 'activation' !== $first['via'] || $first['at'] <= 0 ) {
322 return null;
323 }
324
325 return ( $first['at'] - $install['at'] ) <= $days * DAY_IN_SECONDS;
326 }
327
328 /**
329 * The three stamps as the shell config carries them, epoch seconds
330 * (0 when unknown). Read-only from the client; future in-shell gates
331 * ("you have used this for a week") read them without a round trip.
332 *
333 * @param int $user_id User ID. Defaults to the current user.
334 * @return array{installedAt:int,firstEnabledAt:int,enabledAt:int}
335 */
336 function openstation_first_run_config( $user_id = 0 ) {
337 $install = openstation_get_install_stamp();
338 $first = openstation_get_first_enabled_stamp();
339 return array(
340 'installedAt' => ( null !== $install && 'activation' === $install['via'] ) ? $install['at'] : 0,
341 'firstEnabledAt' => ( null !== $first && 'activation' === $first['via'] ) ? $first['at'] : 0,
342 'enabledAt' => openstation_get_user_enabled_at( $user_id ),
343 );
344 }
345