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

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

327 lines 10.7 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 /**
50 * Normalises a stored stamp to `{ at: int, via: string }`, or null.
51 *
52 * @param mixed $raw Raw option value.
53 * @return array{at:int,via:string}|null
54 */
55 function openstation_normalise_stamp( $raw ) {
56 if ( ! is_array( $raw ) || ! isset( $raw['at'] ) ) {
57 return null;
58 }
59 $via = isset( $raw['via'] ) ? sanitize_key( (string) $raw['via'] ) : 'backfill';
60 if ( ! in_array( $via, array( 'activation', 'backfill' ), true ) ) {
61 $via = 'backfill';
62 }
63 return array(
64 'at' => max( 0, (int) $raw['at'] ),
65 'via' => $via,
66 );
67 }
68
69 /**
70 * The install stamp, or null when nothing has written it yet.
71 *
72 * @return array{at:int,via:string}|null
73 */
74 function openstation_get_install_stamp() {
75 return openstation_normalise_stamp( get_option( OPENSTATION_INSTALLED_AT_OPTION, null ) );
76 }
77
78 /**
79 * The site's first-enable stamp, or null while nobody has enabled.
80 *
81 * @return array{at:int,via:string}|null
82 */
83 function openstation_get_first_enabled_stamp() {
84 return openstation_normalise_stamp( get_option( OPENSTATION_FIRST_ENABLED_AT_OPTION, null ) );
85 }
86
87 /**
88 * When the user first turned OpenStation on, in epoch seconds; 0 when
89 * they never have (or did so before the stamp existed).
90 *
91 * @param int $user_id User ID. Defaults to the current user.
92 * @return int
93 */
94 function openstation_get_user_enabled_at( $user_id = 0 ) {
95 $user_id = (int) $user_id;
96 if ( $user_id <= 0 ) {
97 $user_id = get_current_user_id();
98 }
99 if ( $user_id <= 0 ) {
100 return 0;
101 }
102 return max( 0, (int) get_user_meta( $user_id, OPENSTATION_ENABLED_AT_META_KEY, true ) );
103 }
104
105 /**
106 * Writes the install stamp if, and only if, it is absent.
107 *
108 * Idempotent on purpose: activation fires again on every deactivate /
109 * reactivate cycle, and the first activation is the one that counts.
110 *
111 * A site with prior desktop use ({@see openstation_users_with_prior_desktop_use()})
112 * predates the stamps whatever `$via` says: the plugin was there
113 * before, was deactivated or deleted with the user meta left behind,
114 * and is being activated again. Its install moment is unknown, so the
115 * stamp is written as `backfill`, and so is the first-enable stamp
116 * (`at: 0`) when nothing has written it, because someone did enable
117 * before the stamps existed and the next enable must not pass for
118 * the first. One user query, only while the stamp is absent.
119 *
120 * @param string $via `activation` or `backfill`.
121 * @return bool True when this call wrote the stamp.
122 */
123 function openstation_record_installed( $via = 'activation' ) {
124 if ( null !== openstation_get_install_stamp() ) {
125 return false;
126 }
127 $via = 'activation' === $via ? 'activation' : 'backfill';
128
129 $has_past = function_exists( 'openstation_users_with_prior_desktop_use' )
130 && count( openstation_users_with_prior_desktop_use() ) > 0;
131 if ( $has_past ) {
132 $via = 'backfill';
133 if ( null === openstation_get_first_enabled_stamp() ) {
134 add_option(
135 OPENSTATION_FIRST_ENABLED_AT_OPTION,
136 array(
137 'at' => 0,
138 'via' => 'backfill',
139 ),
140 '',
141 false
142 );
143 }
144 }
145
146 return (bool) add_option(
147 OPENSTATION_INSTALLED_AT_OPTION,
148 array(
149 'at' => time(),
150 'via' => $via,
151 ),
152 '',
153 false
154 );
155 }
156
157 /**
158 * Activation: stamp the real install moment.
159 *
160 * @return void
161 */
162 function openstation_stamp_install_on_activation() {
163 openstation_record_installed( 'activation' );
164 }
165 register_activation_hook( OPENSTATION_FILE, 'openstation_stamp_install_on_activation' );
166
167 /**
168 * Lazy backfill for installs that predate the stamp.
169 *
170 * Activation does not fire on an update in place, so an install
171 * upgrading into this version has no stamp until someone writes one.
172 * `admin_init` is the first admin request after the update, which is
173 * the earliest honest "we noticed" moment; `via: backfill` records
174 * that the age is unknown. Priority 20 so the migration runner
175 * (priority 10) has already had its turn.
176 *
177 * @return void
178 */
179 function openstation_backfill_install_stamp() {
180 openstation_record_installed( 'backfill' );
181 }
182 add_action( 'admin_init', 'openstation_backfill_install_stamp', 20 );
183
184 /**
185 * Records that a user turned OpenStation on.
186 *
187 * Stamps the user (once), stamps the site (once), then fires
188 * `openstation_user_enabled`. Call it from every path that writes
189 * `desktop_mode_mode = '1'`; the action fires on every enable, not
190 * only the first, so a listener that wants "first time" reads
191 * `$first_on_site` or compares the user's stamp against `time()`.
192 *
193 * @param int $user_id User ID.
194 * @return bool True when this enable is the first on the whole site.
195 */
196 function openstation_record_user_enabled( $user_id ) {
197 $user_id = (int) $user_id;
198 if ( $user_id <= 0 ) {
199 return false;
200 }
201
202 $now = time();
203
204 if ( 0 === openstation_get_user_enabled_at( $user_id ) ) {
205 update_user_meta( $user_id, OPENSTATION_ENABLED_AT_META_KEY, $now );
206 }
207
208 $first_on_site = false;
209 if ( null === openstation_get_first_enabled_stamp() ) {
210 $first_on_site = (bool) add_option(
211 OPENSTATION_FIRST_ENABLED_AT_OPTION,
212 array(
213 'at' => $now,
214 'via' => 'activation',
215 ),
216 '',
217 false
218 );
219 }
220
221 /**
222 * Fires when a user turns OpenStation on.
223 *
224 * Runs after the per-user and per-site first-enable stamps are
225 * written, on every enable (not only the first for that user).
226 *
227 * @param int $user_id The user who enabled OpenStation.
228 * @param bool $first_on_site True when nobody on this site had
229 * ever enabled it before this call.
230 */
231 do_action( 'openstation_user_enabled', $user_id, $first_on_site );
232
233 return $first_on_site;
234 }
235
236 /**
237 * Records that a user turned OpenStation off.
238 *
239 * No stamp is written — the enabled stamps are "first time" facts and
240 * survive a switch back to classic — but the action gives plugins
241 * the other half of the lifecycle.
242 *
243 * @param int $user_id User ID.
244 * @return void
245 */
246 function openstation_record_user_disabled( $user_id ) {
247 $user_id = (int) $user_id;
248 if ( $user_id <= 0 ) {
249 return;
250 }
251
252 /**
253 * Fires when a user turns OpenStation off.
254 *
255 * @param int $user_id The user who disabled OpenStation.
256 */
257 do_action( 'openstation_user_disabled', $user_id );
258 }
259
260 /**
261 * How many whole days since the plugin was installed, or null when the
262 * install stamp is missing or backfilled (age unknown).
263 *
264 * @return int|null
265 */
266 function openstation_install_age_days() {
267 $stamp = openstation_get_install_stamp();
268 if ( null === $stamp || 'activation' !== $stamp['via'] || $stamp['at'] <= 0 ) {
269 return null;
270 }
271 return max( 0, (int) floor( ( time() - $stamp['at'] ) / DAY_IN_SECONDS ) );
272 }
273
274 /**
275 * Whether at least one user enabled OpenStation within `$days` of the
276 * install.
277 *
278 * Returns `true` or `false` when both stamps are real, and `null` when
279 * the answer cannot be known: the install stamp is missing or
280 * backfilled, or the first enable happened before the stamps existed
281 * ({@see openstation_record_installed()} records that as `at: 0, via: backfill`). A site where
282 * nobody has enabled yet answers `false` once it is older than
283 * `$days`, and `null` while the window is still open.
284 *
285 * @param int $days Window in days, from install.
286 * @return bool|null
287 */
288 function openstation_activation_within( $days ) {
289 $days = max( 0, (int) $days );
290 $install = openstation_get_install_stamp();
291 if ( null === $install || 'activation' !== $install['via'] || $install['at'] <= 0 ) {
292 return null;
293 }
294
295 $first = openstation_get_first_enabled_stamp();
296 if ( null === $first ) {
297 // Nobody yet. The answer is only settled once the window closed.
298 if ( time() - $install['at'] > $days * DAY_IN_SECONDS ) {
299 return false;
300 }
301 return null;
302 }
303 if ( 'activation' !== $first['via'] || $first['at'] <= 0 ) {
304 return null;
305 }
306
307 return ( $first['at'] - $install['at'] ) <= $days * DAY_IN_SECONDS;
308 }
309
310 /**
311 * The three stamps as the shell config carries them, epoch seconds
312 * (0 when unknown). Read-only from the client; future in-shell gates
313 * ("you have used this for a week") read them without a round trip.
314 *
315 * @param int $user_id User ID. Defaults to the current user.
316 * @return array{installedAt:int,firstEnabledAt:int,enabledAt:int}
317 */
318 function openstation_first_run_config( $user_id = 0 ) {
319 $install = openstation_get_install_stamp();
320 $first = openstation_get_first_enabled_stamp();
321 return array(
322 'installedAt' => ( null !== $install && 'activation' === $install['via'] ) ? $install['at'] : 0,
323 'firstEnabledAt' => ( null !== $first && 'activation' === $first['via'] ) ? $first['at'] : 0,
324 'enabledAt' => openstation_get_user_enabled_at( $user_id ),
325 );
326 }
327