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/migrations.php +197 -2 1.0.01.1.10 View file →
@@ -52,10 +52,13 @@
52 52 * the shell can explain the new name once to the people it happened
53 53 * to and to nobody else. Sets user meta and nothing else — see
54 54 * {@see openstation_migrate_flag_rebrand_notice} for why that is a
55 55 * separate migration from 4.
56 + * - 6: the Trash stopped registering a desktop icon. Removes the
57 + * placement the shell had auto-placed for it and closes the hole that
58 + * leaves in the icon column.
56 59 */
57 -const OPENSTATION_MIGRATION_VERSION = 5;
60 +const OPENSTATION_MIGRATION_VERSION = 8;
58 61
59 62 /**
60 63 * Option storing the highest migration version that has run. autoload=no.
61 64 *
@@ -149,11 +152,175 @@
149 152
150 153 if ( $from < 5 ) {
151 154 openstation_migrate_flag_rebrand_notice( $from );
152 155 }
156 +
157 + if ( $from < 6 ) {
158 + openstation_migrate_close_recycle_bin_icon_gap();
159 + }
160 +
161 + if ( $from < 7 ) {
162 + openstation_migrate_seed_agent_faces();
163 + }
164 +
165 + if ( $from < 8 ) {
166 + openstation_migrate_remove_comments_ai();
167 + }
153 168 }
154 169
155 170 /**
171 + * Migration 7 — give the agents that predate faces a seed to grow one
172 + * from.
173 + *
174 + * Agents used to share a single grey robot glyph. They now carry a Mio
175 + * look, and an agent created from here on gets a seed at birth. The
176 + * ones already on the site do not, and without a seed there is nothing
177 + * to derive a face from.
178 + *
179 + * **This writes the seed and stops.** It does not write the face. The
180 + * face comes from `randomMioLook()`, which lives in TypeScript, and
181 + * porting it is exactly the wrong trade: it is a taste filter with a
182 + * dozen judgment calls in it, pinned by `mio-randomize.test.ts`, and a
183 + * PHP twin of it would drift with nothing watching. So the shell fills
184 + * the looks in on its next paint of the Agents section, rolling each
185 + * one from the seed written here.
186 + *
187 + * That is a client writing on the server's behalf, which is worth
188 + * naming rather than slipping past. It is safe because it is entirely
189 + * derived: the seed is `crc32` of the login, so two admins racing the
190 + * backfill produce byte-identical faces, and running it twice changes
191 + * nothing.
192 + *
193 + * The five shipped agents are unaffected: their faces are written out
194 + * in `default-definitions.php` and were never rolled.
195 + *
196 + * @return void
197 + */
198 +function openstation_migrate_seed_agent_faces() {
199 + // Agents is behind a feature flag, so on a site that has never
200 + // turned it on there is nothing to seed, and none of the module's
201 + // functions exist to call. A site that turns it on later creates
202 + // its agents through `openstation_agent_create`, which seeds them
203 + // at birth, so nothing is missed by returning here.
204 + if (
205 + ! function_exists( 'openstation_agent_get_agents' )
206 + || ! function_exists( 'openstation_agent_get_face_seed' )
207 + || ! defined( 'OPENSTATION_AGENT_FACE_SEED_META' )
208 + ) {
209 + return;
210 + }
211 +
212 + foreach ( openstation_agent_get_agents() as $agent ) {
213 + $user_id = isset( $agent->ID ) ? (int) $agent->ID : 0;
214 + if ( $user_id <= 0 ) {
215 + continue;
216 + }
217 + if ( openstation_agent_get_face_seed( $user_id ) > 0 ) {
218 + continue;
219 + }
220 + update_user_meta(
221 + $user_id,
222 + OPENSTATION_AGENT_FACE_SEED_META,
223 + crc32( (string) $agent->user_login )
224 + );
225 + }
226 +}
227 +
228 +/**
229 + * Grid the desktop auto-placer lays icons out on: 16px of padding, a
230 + * 96px column, a 110px row. Mirrored from `src/desktop-files/grid.ts`
231 + * via {@see openstation_files_auto_place_orphans}, which is what wrote
232 + * the coordinates this migration edits.
233 + */
234 +const OPENSTATION_DESKTOP_GRID_ROW_H = 110;
235 +
236 +/**
237 + * Migration 6 — take back the Trash's desktop icon, and close the hole.
238 + *
239 + * The bin used to register a desktop icon, and every viewer's first
240 + * hydrate auto-placed it into the icon column. Now that the
241 + * registration is gone the placement is dead weight: it is no longer
242 + * served (`OpenStation_Shortcut_File::can_read()` is false without a
243 + * registry entry), so the tile has already vanished on its own. What it
244 + * leaves behind is an empty cell with the icons that were under it
245 + * still sitting where they were.
246 + *
247 + * So: delete the row, and pull everything below it in the same column
248 + * up by one. Same column only, because the auto-placer fills
249 + * column-major, so a column is the run the bin was part of. This does
250 + * move tiles a user may have arranged, which is the point — the shell
251 + * put that icon there and the shell is taking it away, so the shell
252 + * tidies up after itself rather than leaving a gap nobody chose.
253 + *
254 + * A user who wants the bin back on the wallpaper picks "On the desktop"
255 + * in Preferences → Navigation, which promotes the dock tile and never
256 + * touches these rows.
257 + *
258 + * @return void
259 + */
260 +function openstation_migrate_close_recycle_bin_icon_gap() {
261 + global $wpdb;
262 +
263 + if ( ! function_exists( 'openstation_files_table_names' ) ) {
264 + return;
265 + }
266 + $tables = openstation_files_table_names();
267 + $tbl = $tables['placements'];
268 +
269 + // The files schema installs lazily, so a site that never opened
270 + // the desktop has no table to migrate.
271 + $table_exists = (int) $wpdb->get_var(
272 + $wpdb->prepare(
273 + 'SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
274 + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s',
275 + $tbl
276 + )
277 + );
278 + if ( 0 === $table_exists ) {
279 + return;
280 + }
281 +
282 + // Shift first, delete second: the derived table has to still find
283 + // the bin's own row to know which cell is being vacated. It is
284 + // materialized before the update runs, so reading and writing the
285 + // same table in one statement is fine here.
286 + //
287 + // The UNIQUE index on (owner_id, parent_id, file_type, file_ref)
288 + // guarantees at most one bin row per owner, so no row can be
289 + // shifted twice.
290 + $wpdb->query(
291 + $wpdb->prepare(
292 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name, not user input.
293 + "UPDATE `{$tbl}` AS p
294 + INNER JOIN (
295 + SELECT owner_id, x, y FROM `{$tbl}`
296 + WHERE parent_id = 0
297 + AND file_type = 'shortcut'
298 + AND file_ref = %s
299 + ) AS bin
300 + ON p.owner_id = bin.owner_id
301 + AND p.x = bin.x
302 + AND p.y > bin.y
303 + SET p.y = p.y - %d
304 + WHERE p.parent_id = 0
305 + AND p.trashed_at_ms IS NULL",
306 + 'desktop-mode-recycle-bin',
307 + OPENSTATION_DESKTOP_GRID_ROW_H
308 + )
309 + );
310 +
311 + $wpdb->delete(
312 + $tbl,
313 + array(
314 + 'parent_id' => 0,
315 + 'file_type' => 'shortcut',
316 + 'file_ref' => 'desktop-mode-recycle-bin',
317 + ),
318 + array( '%d', '%s', '%s' )
319 + );
320 +}
321 +
322 +/**
156 323 * User meta marking someone as a Desktop Mode user from before the rebrand.
157 324 *
158 325 * Present and truthy => the shell offers this user the one-off rebrand
159 326 * announcement, once. Absent => they never used the plugin under its old
@@ -190,9 +357,9 @@
190 357 array_merge(
191 358 get_users(
192 359 array(
193 360 'fields' => 'ID',
194 - 'meta_key' => 'desktop_mode_mode', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- runs once per install; the key is indexed in usermeta and both callers are guarded to a single pass.
361 + 'meta_key' => 'desktop_mode_mode', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- the key is indexed in usermeta; the migration callers run once per install, and the deactivation feedback route once per admin submission.
195 362 'meta_compare' => 'EXISTS',
196 363 )
197 364 ),
198 365 get_users(
@@ -451,8 +618,33 @@
451 618 wp_unschedule_hook( 'desktop_mode_ai_analyze_term' );
452 619 }
453 620
454 621 /**
622 + * Migration 8 — retire the "Score new comments with AI" feature.
623 + *
624 + * Automatic AI scoring of incoming comments was removed: nothing
625 + * schedules `desktop_mode_ai_analyze_comment` any more, and the
626 + * `desktop_mode_comments_ai_moderation` option no longer gates
627 + * anything. Queued single-events would simply no-op, but we clear
628 + * them so the cron array stays tidy and `wp cron event list` doesn't
629 + * show an orphaned hook.
630 + *
631 + * The option row is dropped too — unlike a frozen identifier that
632 + * still has a reader, this one has none left, so leaving it would
633 + * only strand a value no code consults.
634 + *
635 + * Existing `_desktop_mode_ai_analysis` comment meta is left in place
636 + * (hidden, harmless, and still what the on-demand
637 + * `desktop-mode/analyze-comment` ability writes).
638 + *
639 + * @return void
640 + */
641 +function openstation_migrate_remove_comments_ai() {
642 + wp_unschedule_hook( 'desktop_mode_ai_analyze_comment' );
643 + delete_option( 'desktop_mode_comments_ai_moderation' );
644 +}
645 +
646 +/**
455 647 * Migration 3 — delete self-managed AI credentials.
456 648 *
457 649 * WordPress 7.0 owns provider credentials (Settings → Connectors), so the
458 650 * copilot no longer stores keys of its own. Remove the platform key option and
@@ -498,4 +690,7 @@
498 690
499 691 openstation_save_os_settings( (int) $user_id, $raw );
500 692 }
501 693 }
694 +
695 +// Presence owns a verified checkpoint so failures never advance unrelated migrations.
696 +add_action( 'admin_init', 'openstation_presence_migration_tick', 20 );