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/desktop-files/trash.php +159 -144 0.9.81.1.10 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — Files-on-the-Desktop trash + restore + purge.
3 + * OpenStation — Files-on-the-Desktop trash + restore + purge.
4 4 *
5 5 * Both placements and folders soft-trash before they ever hit the
6 6 * physical row delete. Trashed rows live in the same tables (with
7 7 * `trashed_at_ms` / `trashed_by` columns set; `trashed_via_folder`
@@ -19,18 +19,20 @@
19 19 *
20 20 * - Veto any trash / restore / purge (`*_user_can_*` filters).
21 21 * - Observe any state transition (`*_before_*` / `*_after_*`).
22 22 * - React to recycle-bin list / restore / purge of the new types
23 - * via the existing recycle-bin hooks (`desktop_mode_recycle_bin_*`).
23 + * via the existing recycle-bin hooks (`openstation_recycle_bin_*`).
24 24 *
25 - * @package WPDesktopMode
25 + * @package OpenStation
26 26 */
27 27
28 28 defined( 'ABSPATH' ) || exit;
29 29
30 -/* ================================================================== *
30 +/*
31 +================================================================== *
31 32 * Capability gates.
32 - * ================================================================== */
33 + * ==================================================================
34 + */
33 35
34 36 /**
35 37 * Default ownership check shared by every trash / restore / purge
36 38 * capability gate: the acting user must be the row's `owner_id`.
@@ -40,9 +42,9 @@
40 42 * @param int $user_id Acting user.
41 43 * @param array $row Placement or folder row.
42 44 * @return bool
43 45 */
44 -function desktop_mode_files_user_owns_row( $user_id, $row ) {
46 +function openstation_files_user_owns_row( $user_id, $row ) {
45 47 $user_id = (int) $user_id;
46 48 return ( $user_id > 0 )
47 49 && isset( $row['owner_id'] )
48 50 && (int) $row['owner_id'] === $user_id;
@@ -55,9 +57,9 @@
55 57 * @param int $user_id Acting user.
56 58 * @param array $row Placement row (raw from DB or normalized).
57 59 * @return bool
58 60 */
59 -function desktop_mode_files_user_can_trash_placement( $user_id, $row ) {
61 +function openstation_files_user_can_trash_placement( $user_id, $row ) {
60 62 /**
61 63 * Filter whether the user can trash this placement.
62 64 *
63 65 * @param bool $can Default: ownership match.
@@ -64,10 +66,10 @@
64 66 * @param int $user_id Acting user.
65 67 * @param array $row Placement row.
66 68 */
67 69 return (bool) apply_filters(
68 - 'desktop_mode_files_user_can_trash_placement',
69 - desktop_mode_files_user_owns_row( $user_id, $row ),
70 + 'openstation_files_user_can_trash_placement',
71 + openstation_files_user_owns_row( $user_id, $row ),
70 72 (int) $user_id,
71 73 $row
72 74 );
73 75 }
@@ -78,9 +80,9 @@
78 80 * @param int $user_id Acting user.
79 81 * @param array $row Placement row (already trashed).
80 82 * @return bool
81 83 */
82 -function desktop_mode_files_user_can_restore_placement( $user_id, $row ) {
84 +function openstation_files_user_can_restore_placement( $user_id, $row ) {
83 85 /**
84 86 * @param bool $can
85 87 * @param int $user_id
86 88 * @param array $row
@@ -85,10 +87,10 @@
85 87 * @param int $user_id
86 88 * @param array $row
87 89 */
88 90 return (bool) apply_filters(
89 - 'desktop_mode_files_user_can_restore_placement',
90 - desktop_mode_files_user_owns_row( $user_id, $row ),
91 + 'openstation_files_user_can_restore_placement',
92 + openstation_files_user_owns_row( $user_id, $row ),
91 93 (int) $user_id,
92 94 $row
93 95 );
94 96 }
@@ -95,9 +97,9 @@
95 97
96 98 /**
97 99 * Whether the given user can permanently purge a trashed placement.
98 100 */
99 -function desktop_mode_files_user_can_purge_placement( $user_id, $row ) {
101 +function openstation_files_user_can_purge_placement( $user_id, $row ) {
100 102 /**
101 103 * @param bool $can
102 104 * @param int $user_id
103 105 * @param array $row
@@ -102,10 +104,10 @@
102 104 * @param int $user_id
103 105 * @param array $row
104 106 */
105 107 return (bool) apply_filters(
106 - 'desktop_mode_files_user_can_purge_placement',
107 - desktop_mode_files_user_owns_row( $user_id, $row ),
108 + 'openstation_files_user_can_purge_placement',
109 + openstation_files_user_owns_row( $user_id, $row ),
108 110 (int) $user_id,
109 111 $row
110 112 );
111 113 }
@@ -112,9 +114,9 @@
112 114
113 115 /**
114 116 * Whether the given user can trash a folder. Default: folder owner.
115 117 */
116 -function desktop_mode_files_user_can_trash_folder( $user_id, $row ) {
118 +function openstation_files_user_can_trash_folder( $user_id, $row ) {
117 119 /**
118 120 * @param bool $can
119 121 * @param int $user_id
120 122 * @param array $row
@@ -119,10 +121,10 @@
119 121 * @param int $user_id
120 122 * @param array $row
121 123 */
122 124 return (bool) apply_filters(
123 - 'desktop_mode_files_user_can_trash_folder',
124 - desktop_mode_files_user_owns_row( $user_id, $row ),
125 + 'openstation_files_user_can_trash_folder',
126 + openstation_files_user_owns_row( $user_id, $row ),
125 127 (int) $user_id,
126 128 $row
127 129 );
128 130 }
@@ -129,9 +131,9 @@
129 131
130 132 /**
131 133 * Whether the given user can restore a trashed folder.
132 134 */
133 -function desktop_mode_files_user_can_restore_folder( $user_id, $row ) {
135 +function openstation_files_user_can_restore_folder( $user_id, $row ) {
134 136 /**
135 137 * @param bool $can
136 138 * @param int $user_id
137 139 * @param array $row
@@ -136,10 +138,10 @@
136 138 * @param int $user_id
137 139 * @param array $row
138 140 */
139 141 return (bool) apply_filters(
140 - 'desktop_mode_files_user_can_restore_folder',
141 - desktop_mode_files_user_owns_row( $user_id, $row ),
142 + 'openstation_files_user_can_restore_folder',
143 + openstation_files_user_owns_row( $user_id, $row ),
142 144 (int) $user_id,
143 145 $row
144 146 );
145 147 }
@@ -146,9 +148,9 @@
146 148
147 149 /**
148 150 * Whether the given user can permanently purge a trashed folder.
149 151 */
150 -function desktop_mode_files_user_can_purge_folder( $user_id, $row ) {
152 +function openstation_files_user_can_purge_folder( $user_id, $row ) {
151 153 /**
152 154 * @param bool $can
153 155 * @param int $user_id
154 156 * @param array $row
@@ -153,16 +155,17 @@
153 155 * @param int $user_id
154 156 * @param array $row
155 157 */
156 158 return (bool) apply_filters(
157 - 'desktop_mode_files_user_can_purge_folder',
158 - desktop_mode_files_user_owns_row( $user_id, $row ),
159 + 'openstation_files_user_can_purge_folder',
160 + openstation_files_user_owns_row( $user_id, $row ),
159 161 (int) $user_id,
160 162 $row
161 163 );
162 164 }
163 165
164 -/* ================================================================== *
166 +/*
167 +================================================================== *
165 168 * Ancestry snapshot + resurrection.
166 169 *
167 170 * When a placement is soft-trashed we capture every folder in
168 171 * its parent chain into a JSON blob on `placements.trashed_meta`.
@@ -170,9 +173,10 @@
170 173 * still alive are reused, trashed folders cascade-restore, and
171 174 * hard-deleted folders are recreated (with new ids; the chain is
172 175 * rewritten as it walks). The placement comes back at the same
173 176 * visual position inside the (possibly resurrected) parent.
174 - * ================================================================== */
177 + * ==================================================================
178 + */
175 179
176 180 /**
177 181 * Walk up `$parent_id` through the folders + placements tables and
178 182 * return the parent chain root-first.
@@ -194,11 +198,11 @@
194 198 *
195 199 * @param int $parent_id Immediate parent folder id.
196 200 * @return array<int, array<string, mixed>>
197 201 */
198 -function desktop_mode_files_capture_ancestry( $parent_id ) {
202 +function openstation_files_capture_ancestry( $parent_id ) {
199 203 global $wpdb;
200 - $tables = desktop_mode_files_table_names();
204 + $tables = openstation_files_table_names();
201 205 $chain = array();
202 206 $cursor = (int) $parent_id;
203 207 $guard = 0; // depth-bound — defends against accidental cycles.
204 208 while ( $cursor > 0 && $guard < 32 ) {
@@ -215,9 +219,9 @@
215 219 }
216 220 // The folder's "where I sit on the desktop tree" lives on
217 221 // its placement row. Pick any active or trashed placement
218 222 // of this folder — we just need its parent_id + (x, y).
219 - $placement = $wpdb->get_row(
223 + $placement = $wpdb->get_row(
220 224 $wpdb->prepare(
221 225 "SELECT parent_id, x, y FROM {$tables['placements']}
222 226 WHERE file_type = 'folder' AND file_ref = %s
223 227 ORDER BY id ASC LIMIT 1",
@@ -226,9 +230,9 @@
226 230 ARRAY_A
227 231 );
228 232 $share_meta_raw = isset( $folder['share_meta'] ) ? (string) $folder['share_meta'] : '';
229 233 $share_meta = '' !== $share_meta_raw ? json_decode( $share_meta_raw, true ) : null;
230 - $entry = array(
234 + $entry = array(
231 235 'folder_id' => (int) $folder['id'],
232 236 'folder_name' => (string) $folder['name'],
233 237 'folder_share_mode' => (string) $folder['share_mode'],
234 238 'folder_share_meta' => is_array( $share_meta ) ? $share_meta : null,
@@ -256,9 +260,9 @@
256 260 * @param array $ancestry Root-first chain captured at trash time.
257 261 * @return int Resolved leaf parent id (0 when the placement was
258 262 * at desktop root).
259 263 */
260 -function desktop_mode_files_resurrect_ancestry( $user_id, $ancestry ) {
264 +function openstation_files_resurrect_ancestry( $user_id, $ancestry ) {
261 265 if ( empty( $ancestry ) ) {
262 266 return 0;
263 267 }
264 268 $user_id = (int) $user_id;
@@ -272,15 +276,15 @@
272 276 $resolved_parent = isset( $id_map[ $orig_par ] )
273 277 ? (int) $id_map[ $orig_par ]
274 278 : $orig_par;
275 279
276 - $folder = desktop_mode_files_get_folder( $orig_id, true );
280 + $folder = openstation_files_get_folder( $orig_id, true );
277 281 if ( $folder ) {
278 282 // Folder still exists. If trashed, restore it (cascade
279 283 // brings back its own children that were trashed via
280 284 // folder cascade).
281 285 if ( ! empty( $folder['trashed_at_ms'] ) ) {
282 - desktop_mode_files_restore_folder( $user_id, $orig_id );
286 + openstation_files_restore_folder( $user_id, $orig_id );
283 287 }
284 288 $id_map[ $orig_id ] = $orig_id;
285 289 $resolved = $orig_id;
286 290 continue;
@@ -289,23 +293,26 @@
289 293 // Folder is gone — recreate it and place it under the
290 294 // resolved parent. Owner falls back to the acting user
291 295 // when the original owner can't be inferred (shared-
292 296 // folder edge case Phase 6 will revisit).
293 - $owner_id = (int) ( $entry['folder_owner_id'] ?: $user_id );
294 - $new_id = desktop_mode_files_create_folder( $owner_id, array(
295 - 'name' => (string) $entry['folder_name'],
296 - 'share_mode' => (string) $entry['folder_share_mode'],
297 - 'share_meta' => $entry['folder_share_meta'],
298 - ) );
297 + $owner_id = (int) ( $entry['folder_owner_id'] ? $entry['folder_owner_id'] : $user_id );
298 + $new_id = openstation_files_create_folder(
299 + $owner_id,
300 + array(
301 + 'name' => (string) $entry['folder_name'],
302 + 'share_mode' => (string) $entry['folder_share_mode'],
303 + 'share_meta' => $entry['folder_share_meta'],
304 + )
305 + );
299 306 if ( is_wp_error( $new_id ) ) {
300 307 // Fall back to root — restoring at the wrong place is
301 308 // strictly better than failing the restore outright.
302 - $resolved = $resolved_parent;
309 + $resolved = $resolved_parent;
303 310 $id_map[ $orig_id ] = $resolved;
304 311 continue;
305 312 }
306 313 // Place the recreated folder where the snapshot says.
307 - desktop_mode_files_place(
314 + openstation_files_place(
308 315 $user_id,
309 316 $resolved_parent,
310 317 'folder',
311 318 (string) $new_id,
@@ -319,11 +326,13 @@
319 326 }
320 327 return $resolved;
321 328 }
322 329
323 -/* ================================================================== *
330 +/*
331 +================================================================== *
324 332 * Placement: trash / restore / purge.
325 - * ================================================================== */
333 + * ==================================================================
334 + */
326 335
327 336 /**
328 337 * Soft-trash a placement. Sets `trashed_at_ms`, `trashed_by`. Returns
329 338 * `true` on success, `WP_Error` on permission failure / missing row.
@@ -334,13 +343,13 @@
334 343 * @param int $user_id Acting user.
335 344 * @param int $placement_id Placement id.
336 345 * @return true|WP_Error
337 346 */
338 -function desktop_mode_files_trash_placement( $user_id, $placement_id ) {
347 +function openstation_files_trash_placement( $user_id, $placement_id ) {
339 348 global $wpdb;
340 349 $user_id = (int) $user_id;
341 350 $placement_id = (int) $placement_id;
342 - $tables = desktop_mode_files_table_names();
351 + $tables = openstation_files_table_names();
343 352
344 353 $row = $wpdb->get_row(
345 354 $wpdb->prepare(
346 355 "SELECT * FROM {$tables['placements']} WHERE id = %d",
@@ -349,9 +358,9 @@
349 358 ARRAY_A
350 359 );
351 360 if ( ! $row ) {
352 361 return new WP_Error(
353 - 'desktop_mode_files_placement_not_found',
362 + 'openstation_files_placement_not_found',
354 363 __( 'Placement not found.', 'desktop-mode' ),
355 364 array( 'status' => 404 )
356 365 );
357 366 }
@@ -357,11 +366,11 @@
357 366 }
358 367 if ( null !== $row['trashed_at_ms'] && '' !== $row['trashed_at_ms'] ) {
359 368 return true;
360 369 }
361 - if ( ! desktop_mode_files_user_can_trash_placement( $user_id, $row ) ) {
370 + if ( ! openstation_files_user_can_trash_placement( $user_id, $row ) ) {
362 371 return new WP_Error(
363 - 'desktop_mode_files_forbidden',
372 + 'openstation_files_forbidden',
364 373 __( 'You do not have permission to trash this item.', 'desktop-mode' ),
365 374 array( 'status' => 403 )
366 375 );
367 376 }
@@ -372,12 +381,12 @@
372 381 * @param int $placement_id Placement id.
373 382 * @param int $user_id Acting user.
374 383 * @param array $row Placement row.
375 384 */
376 - do_action( 'desktop_mode_files_before_trash_placement', $placement_id, $user_id, $row );
385 + do_action( 'openstation_files_before_trash_placement', $placement_id, $user_id, $row );
377 386
378 - $now = desktop_mode_files_now_ms();
379 - $ancestry = desktop_mode_files_capture_ancestry( (int) $row['parent_id'] );
387 + $now = openstation_files_now_ms();
388 + $ancestry = openstation_files_capture_ancestry( (int) $row['parent_id'] );
380 389 $meta = wp_json_encode( array( 'ancestry' => $ancestry ) );
381 390 $result = $wpdb->update(
382 391 $tables['placements'],
383 392 array(
@@ -396,9 +405,9 @@
396 405 // 200 OK and the UI would show "moved to trash" with nothing
397 406 // actually trashed.
398 407 if ( false === $result ) {
399 408 return new WP_Error(
400 - 'desktop_mode_files_trash_failed',
409 + 'openstation_files_trash_failed',
401 410 isset( $wpdb->last_error ) && $wpdb->last_error
402 411 ? (string) $wpdb->last_error
403 412 : __( 'Failed to write trash row.', 'desktop-mode' ),
404 413 array( 'status' => 500 )
@@ -410,9 +419,9 @@
410 419 *
411 420 * @param int $placement_id Placement id.
412 421 * @param int $user_id Acting user.
413 422 */
414 - do_action( 'desktop_mode_files_after_trash_placement', $placement_id, $user_id );
423 + do_action( 'openstation_files_after_trash_placement', $placement_id, $user_id );
415 424
416 425 return true;
417 426 }
418 427
@@ -422,13 +431,13 @@
422 431 * @param int $user_id Acting user.
423 432 * @param int $placement_id Placement id.
424 433 * @return true|WP_Error
425 434 */
426 -function desktop_mode_files_restore_placement( $user_id, $placement_id ) {
435 +function openstation_files_restore_placement( $user_id, $placement_id ) {
427 436 global $wpdb;
428 437 $user_id = (int) $user_id;
429 438 $placement_id = (int) $placement_id;
430 - $tables = desktop_mode_files_table_names();
439 + $tables = openstation_files_table_names();
431 440
432 441 $row = $wpdb->get_row(
433 442 $wpdb->prepare(
434 443 "SELECT * FROM {$tables['placements']} WHERE id = %d",
@@ -437,9 +446,9 @@
437 446 ARRAY_A
438 447 );
439 448 if ( ! $row ) {
440 449 return new WP_Error(
441 - 'desktop_mode_files_placement_not_found',
450 + 'openstation_files_placement_not_found',
442 451 __( 'Placement not found.', 'desktop-mode' ),
443 452 array( 'status' => 404 )
444 453 );
445 454 }
@@ -445,11 +454,11 @@
445 454 }
446 455 if ( null === $row['trashed_at_ms'] || '' === $row['trashed_at_ms'] ) {
447 456 return true; // Already active — idempotent.
448 457 }
449 - if ( ! desktop_mode_files_user_can_restore_placement( $user_id, $row ) ) {
458 + if ( ! openstation_files_user_can_restore_placement( $user_id, $row ) ) {
450 459 return new WP_Error(
451 - 'desktop_mode_files_forbidden',
460 + 'openstation_files_forbidden',
452 461 __( 'You do not have permission to restore this item.', 'desktop-mode' ),
453 462 array( 'status' => 403 )
454 463 );
455 464 }
@@ -454,24 +463,24 @@
454 463 );
455 464 }
456 465
457 466 // Resolve the parent folder. Three branches:
458 - // - parent is alive → reuse the same id
459 - // - parent is trashed → cascade-restore it (and rest of the
460 - // chain) before placing the leaf
461 - // - parent is gone → walk the captured ancestry and
462 - // recreate every missing folder in
463 - // the chain
467 + // - parent is alive → reuse the same id
468 + // - parent is trashed → cascade-restore it (and rest of the
469 + // chain) before placing the leaf
470 + // - parent is gone → walk the captured ancestry and
471 + // recreate every missing folder in
472 + // the chain
464 473 $original_parent_id = (int) $row['parent_id'];
465 474 $resolved_parent_id = $original_parent_id;
466 475 if ( $original_parent_id > 0 ) {
467 - $parent_alive = desktop_mode_files_get_folder( $original_parent_id, true );
476 + $parent_alive = openstation_files_get_folder( $original_parent_id, true );
468 477 if ( $parent_alive ) {
469 478 if ( ! empty( $parent_alive['trashed_at_ms'] ) ) {
470 479 // Cascade restore — reach into the snapshot the
471 480 // folder itself stored at trash time so any chain
472 481 // above it is also resurrected.
473 - $folder_restore = desktop_mode_files_restore_folder( $user_id, $original_parent_id );
482 + $folder_restore = openstation_files_restore_folder( $user_id, $original_parent_id );
474 483 if ( is_wp_error( $folder_restore ) ) {
475 484 return $folder_restore;
476 485 }
477 486 }
@@ -478,14 +487,14 @@
478 487 $resolved_parent_id = $original_parent_id;
479 488 } else {
480 489 // Hard-deleted parent — read the ancestry snapshot we
481 490 // stored at trash time and resurrect the chain.
482 - $meta_raw = isset( $row['trashed_meta'] ) ? (string) $row['trashed_meta'] : '';
483 - $decoded = '' !== $meta_raw ? json_decode( $meta_raw, true ) : null;
484 - $ancestry = ( is_array( $decoded ) && isset( $decoded['ancestry'] ) && is_array( $decoded['ancestry'] ) )
491 + $meta_raw = isset( $row['trashed_meta'] ) ? (string) $row['trashed_meta'] : '';
492 + $decoded = '' !== $meta_raw ? json_decode( $meta_raw, true ) : null;
493 + $ancestry = ( is_array( $decoded ) && isset( $decoded['ancestry'] ) && is_array( $decoded['ancestry'] ) )
485 494 ? $decoded['ancestry']
486 495 : array();
487 - $resolved_parent_id = desktop_mode_files_resurrect_ancestry( $user_id, $ancestry );
496 + $resolved_parent_id = openstation_files_resurrect_ancestry( $user_id, $ancestry );
488 497 }
489 498 }
490 499
491 500 /**
@@ -494,9 +503,9 @@
494 503 * @param int $placement_id
495 504 * @param int $user_id
496 505 * @param array $row
497 506 */
498 - do_action( 'desktop_mode_files_before_restore_placement', $placement_id, $user_id, $row );
507 + do_action( 'openstation_files_before_restore_placement', $placement_id, $user_id, $row );
499 508
500 509 $wpdb->update(
501 510 $tables['placements'],
502 511 array(
@@ -504,9 +513,9 @@
504 513 'trashed_at_ms' => null,
505 514 'trashed_by' => null,
506 515 'trashed_via_folder' => null,
507 516 'trashed_meta' => null,
508 - 'updated_at_ms' => desktop_mode_files_now_ms(),
517 + 'updated_at_ms' => openstation_files_now_ms(),
509 518 ),
510 519 array( 'id' => $placement_id ),
511 520 array( '%d', null, null, null, null, '%d' ),
512 521 array( '%d' )
@@ -516,15 +525,15 @@
516 525 // a placement coming back to life must not carry lingering
517 526 // tombstones from an earlier (reversible) removal. Without this,
518 527 // every heartbeat tick would re-deliver those tombstones to the
519 528 // client and the row would flicker off the desktop on each tick.
520 - desktop_mode_files_clear_tombstones_for( 'placement', $placement_id );
529 + openstation_files_clear_tombstones_for( 'placement', $placement_id );
521 530
522 531 /**
523 532 * @param int $placement_id
524 533 * @param int $user_id
525 534 */
526 - do_action( 'desktop_mode_files_after_restore_placement', $placement_id, $user_id );
535 + do_action( 'openstation_files_after_restore_placement', $placement_id, $user_id );
527 536
528 537 return true;
529 538 }
530 539
@@ -534,13 +543,13 @@
534 543 * @param int $user_id
535 544 * @param int $placement_id
536 545 * @return true|WP_Error
537 546 */
538 -function desktop_mode_files_purge_placement( $user_id, $placement_id ) {
547 +function openstation_files_purge_placement( $user_id, $placement_id ) {
539 548 global $wpdb;
540 549 $user_id = (int) $user_id;
541 550 $placement_id = (int) $placement_id;
542 - $tables = desktop_mode_files_table_names();
551 + $tables = openstation_files_table_names();
543 552
544 553 $row = $wpdb->get_row(
545 554 $wpdb->prepare(
546 555 "SELECT * FROM {$tables['placements']} WHERE id = %d",
@@ -550,11 +559,11 @@
550 559 );
551 560 if ( ! $row ) {
552 561 return true; // Already gone — idempotent.
553 562 }
554 - if ( ! desktop_mode_files_user_can_purge_placement( $user_id, $row ) ) {
563 + if ( ! openstation_files_user_can_purge_placement( $user_id, $row ) ) {
555 564 return new WP_Error(
556 - 'desktop_mode_files_forbidden',
565 + 'openstation_files_forbidden',
557 566 __( 'You do not have permission to delete this item.', 'desktop-mode' ),
558 567 array( 'status' => 403 )
559 568 );
560 569 }
@@ -563,21 +572,21 @@
563 572 * @param int $placement_id
564 573 * @param int $user_id
565 574 * @param array $row
566 575 */
567 - do_action( 'desktop_mode_files_before_purge_placement', $placement_id, $user_id, $row );
576 + do_action( 'openstation_files_before_purge_placement', $placement_id, $user_id, $row );
568 577
569 578 $wpdb->delete( $tables['placements'], array( 'id' => $placement_id ), array( '%d' ) );
570 579
571 - // Mirror `desktop_mode_files_remove()`: a purge IS a permanent
580 + // Mirror `openstation_files_remove()`: a purge IS a permanent
572 581 // removal, so the same lifecycle action fires. Load-bearing for
573 582 // the `upload` type — the stored-files listener deletes the real
574 583 // bytes when the owner's last placement goes away; without this
575 584 // the recycle-bin "Delete forever" path leaked them.
576 585 do_action(
577 - 'desktop_mode_file_unplaced',
586 + 'openstation_file_unplaced',
578 587 $placement_id,
579 - desktop_mode_files_normalize_placement_row( $row )
588 + openstation_files_normalize_placement_row( $row )
580 589 );
581 590
582 591 /**
583 592 * @param int $placement_id
@@ -582,16 +591,18 @@
582 591 /**
583 592 * @param int $placement_id
584 593 * @param int $user_id
585 594 */
586 - do_action( 'desktop_mode_files_after_purge_placement', $placement_id, $user_id );
595 + do_action( 'openstation_files_after_purge_placement', $placement_id, $user_id );
587 596
588 597 return true;
589 598 }
590 599
591 -/* ================================================================== *
600 +/*
601 +================================================================== *
592 602 * Folder: trash / restore / purge (cascades to child placements).
593 - * ================================================================== */
603 + * ==================================================================
604 + */
594 605
595 606 /**
596 607 * Soft-trash a folder. Cascades to every child placement (any
597 608 * placement whose `parent_id = folder_id`), marking them with
@@ -603,13 +614,13 @@
603 614 * @param int $user_id
604 615 * @param int $folder_id
605 616 * @return true|WP_Error
606 617 */
607 -function desktop_mode_files_trash_folder( $user_id, $folder_id ) {
618 +function openstation_files_trash_folder( $user_id, $folder_id ) {
608 619 global $wpdb;
609 620 $user_id = (int) $user_id;
610 621 $folder_id = (int) $folder_id;
611 - $tables = desktop_mode_files_table_names();
622 + $tables = openstation_files_table_names();
612 623
613 624 $row = $wpdb->get_row(
614 625 $wpdb->prepare(
615 626 "SELECT * FROM {$tables['folders']} WHERE id = %d",
@@ -618,9 +629,9 @@
618 629 ARRAY_A
619 630 );
620 631 if ( ! $row ) {
621 632 return new WP_Error(
622 - 'desktop_mode_files_folder_not_found',
633 + 'openstation_files_folder_not_found',
623 634 __( 'Folder not found.', 'desktop-mode' ),
624 635 array( 'status' => 404 )
625 636 );
626 637 }
@@ -626,11 +637,11 @@
626 637 }
627 638 if ( null !== $row['trashed_at_ms'] && '' !== $row['trashed_at_ms'] ) {
628 639 return true;
629 640 }
630 - if ( ! desktop_mode_files_user_can_trash_folder( $user_id, $row ) ) {
641 + if ( ! openstation_files_user_can_trash_folder( $user_id, $row ) ) {
631 642 return new WP_Error(
632 - 'desktop_mode_files_forbidden',
643 + 'openstation_files_forbidden',
633 644 __( 'You do not have permission to trash this folder.', 'desktop-mode' ),
634 645 array( 'status' => 403 )
635 646 );
636 647 }
@@ -639,11 +650,11 @@
639 650 * @param int $folder_id
640 651 * @param int $user_id
641 652 * @param array $row
642 653 */
643 - do_action( 'desktop_mode_files_before_trash_folder', $folder_id, $user_id, $row );
654 + do_action( 'openstation_files_before_trash_folder', $folder_id, $user_id, $row );
644 655
645 - $now = desktop_mode_files_now_ms();
656 + $now = openstation_files_now_ms();
646 657 // Capture the folder's own placement-chain ancestry so a future
647 658 // restore can resurrect any parent folders that got hard-deleted
648 659 // while this one was sitting in trash.
649 660 $folder_placement = $wpdb->get_row(
@@ -654,12 +665,12 @@
654 665 (string) $folder_id
655 666 ),
656 667 ARRAY_A
657 668 );
658 - $folder_ancestry = $folder_placement
659 - ? desktop_mode_files_capture_ancestry( (int) $folder_placement['parent_id'] )
669 + $folder_ancestry = $folder_placement
670 + ? openstation_files_capture_ancestry( (int) $folder_placement['parent_id'] )
660 671 : array();
661 - $folder_meta = wp_json_encode( array( 'ancestry' => $folder_ancestry ) );
672 + $folder_meta = wp_json_encode( array( 'ancestry' => $folder_ancestry ) );
662 673
663 674 // Trash the folder row.
664 675 $folder_update = $wpdb->update(
665 676 $tables['folders'],
@@ -674,9 +685,9 @@
674 685 array( '%d' )
675 686 );
676 687 if ( false === $folder_update ) {
677 688 return new WP_Error(
678 - 'desktop_mode_files_trash_failed',
689 + 'openstation_files_trash_failed',
679 690 isset( $wpdb->last_error ) && $wpdb->last_error
680 691 ? (string) $wpdb->last_error
681 692 : __( 'Failed to trash folder.', 'desktop-mode' ),
682 693 array( 'status' => 500 )
@@ -690,9 +701,9 @@
690 701 // just one child later (after the parent folder was hard-
691 702 // deleted) can still recreate the chain — same shape as a
692 703 // direct trash. Captured per-row because every child shares
693 704 // the same parent chain, so we compute once.
694 - $ancestry = desktop_mode_files_capture_ancestry( $folder_id );
705 + $ancestry = openstation_files_capture_ancestry( $folder_id );
695 706 $meta = wp_json_encode( array( 'ancestry' => $ancestry ) );
696 707 $wpdb->query(
697 708 $wpdb->prepare(
698 709 "UPDATE {$tables['placements']}
@@ -721,9 +732,9 @@
721 732 $folder_id
722 733 )
723 734 );
724 735 foreach ( (array) $child_folder_ids as $child_id ) {
725 - desktop_mode_files_trash_folder( $user_id, (int) $child_id );
736 + openstation_files_trash_folder( $user_id, (int) $child_id );
726 737 }
727 738
728 739 /**
729 740 * @param int $folder_id
@@ -728,9 +739,9 @@
728 739 /**
729 740 * @param int $folder_id
730 741 * @param int $user_id
731 742 */
732 - do_action( 'desktop_mode_files_after_trash_folder', $folder_id, $user_id );
743 + do_action( 'openstation_files_after_trash_folder', $folder_id, $user_id );
733 744
734 745 return true;
735 746 }
736 747
@@ -743,13 +754,13 @@
743 754 * @param int $user_id
744 755 * @param int $folder_id
745 756 * @return true|WP_Error
746 757 */
747 -function desktop_mode_files_restore_folder( $user_id, $folder_id ) {
758 +function openstation_files_restore_folder( $user_id, $folder_id ) {
748 759 global $wpdb;
749 760 $user_id = (int) $user_id;
750 761 $folder_id = (int) $folder_id;
751 - $tables = desktop_mode_files_table_names();
762 + $tables = openstation_files_table_names();
752 763
753 764 $row = $wpdb->get_row(
754 765 $wpdb->prepare(
755 766 "SELECT * FROM {$tables['folders']} WHERE id = %d",
@@ -758,9 +769,9 @@
758 769 ARRAY_A
759 770 );
760 771 if ( ! $row ) {
761 772 return new WP_Error(
762 - 'desktop_mode_files_folder_not_found',
773 + 'openstation_files_folder_not_found',
763 774 __( 'Folder not found.', 'desktop-mode' ),
764 775 array( 'status' => 404 )
765 776 );
766 777 }
@@ -766,11 +777,11 @@
766 777 }
767 778 if ( null === $row['trashed_at_ms'] || '' === $row['trashed_at_ms'] ) {
768 779 return true;
769 780 }
770 - if ( ! desktop_mode_files_user_can_restore_folder( $user_id, $row ) ) {
781 + if ( ! openstation_files_user_can_restore_folder( $user_id, $row ) ) {
771 782 return new WP_Error(
772 - 'desktop_mode_files_forbidden',
783 + 'openstation_files_forbidden',
773 784 __( 'You do not have permission to restore this folder.', 'desktop-mode' ),
774 785 array( 'status' => 403 )
775 786 );
776 787 }
@@ -779,11 +790,11 @@
779 790 * @param int $folder_id
780 791 * @param int $user_id
781 792 * @param array $row
782 793 */
783 - do_action( 'desktop_mode_files_before_restore_folder', $folder_id, $user_id, $row );
794 + do_action( 'openstation_files_before_restore_folder', $folder_id, $user_id, $row );
784 795
785 - $now = desktop_mode_files_now_ms();
796 + $now = openstation_files_now_ms();
786 797 // Snapshot nested folder ids BEFORE we null `trashed_via_folder`
787 798 // on the placements — that column is the only stable link from
788 799 // a child folder's placement back to the parent cascade.
789 800 $nested_ids = $wpdb->get_col(
@@ -830,12 +841,12 @@
830 841 );
831 842 if ( $folder_placement_row ) {
832 843 $origin_parent = (int) $folder_placement_row['parent_id'];
833 844 $alive = $origin_parent > 0
834 - ? desktop_mode_files_get_folder( $origin_parent, true )
845 + ? openstation_files_get_folder( $origin_parent, true )
835 846 : null;
836 847 if ( $origin_parent > 0 && ! $alive ) {
837 - $resolved = desktop_mode_files_resurrect_ancestry( $user_id, $ancestry );
848 + $resolved = openstation_files_resurrect_ancestry( $user_id, $ancestry );
838 849 $wpdb->update(
839 850 $tables['placements'],
840 851 array(
841 852 'parent_id' => $resolved,
@@ -863,9 +874,9 @@
863 874 )
864 875 );
865 876 // Recursively restore nested folders captured in the snapshot.
866 877 foreach ( (array) $nested_ids as $nid ) {
867 - desktop_mode_files_restore_folder( $user_id, (int) $nid );
878 + openstation_files_restore_folder( $user_id, (int) $nid );
868 879 }
869 880
870 881 // Enforce the "tombstones never refer to alive rows" invariant
871 882 // across the restored cohort: the folder itself, every cascade-
@@ -873,9 +884,9 @@
873 884 // folder recursed into above already clears its own. Here we
874 885 // scrub the FOLDER's own tombstones plus those of every cascade-
875 886 // restored placement so a fresh heartbeat tick can't surface
876 887 // them as `removed.*` against the now-alive rows.
877 - desktop_mode_files_clear_tombstones_for( 'folder', $folder_id );
888 + openstation_files_clear_tombstones_for( 'folder', $folder_id );
878 889 $restored_placement_ids = $wpdb->get_col(
879 890 $wpdb->prepare(
880 891 "SELECT id FROM {$tables['placements']}
881 892 WHERE trashed_via_folder IS NULL
@@ -884,9 +895,9 @@
884 895 (string) $folder_id
885 896 )
886 897 );
887 898 foreach ( (array) $restored_placement_ids as $rpid ) {
888 - desktop_mode_files_clear_tombstones_for( 'placement', (int) $rpid );
899 + openstation_files_clear_tombstones_for( 'placement', (int) $rpid );
889 900 }
890 901
891 902 /**
892 903 * @param int $folder_id
@@ -891,9 +902,9 @@
891 902 /**
892 903 * @param int $folder_id
893 904 * @param int $user_id
894 905 */
895 - do_action( 'desktop_mode_files_after_restore_folder', $folder_id, $user_id );
906 + do_action( 'openstation_files_after_restore_folder', $folder_id, $user_id );
896 907
897 908 return true;
898 909 }
899 910
@@ -905,13 +916,13 @@
905 916 * @param int $user_id
906 917 * @param int $folder_id
907 918 * @return true|WP_Error
908 919 */
909 -function desktop_mode_files_purge_folder( $user_id, $folder_id ) {
920 +function openstation_files_purge_folder( $user_id, $folder_id ) {
910 921 global $wpdb;
911 922 $user_id = (int) $user_id;
912 923 $folder_id = (int) $folder_id;
913 - $tables = desktop_mode_files_table_names();
924 + $tables = openstation_files_table_names();
914 925
915 926 $row = $wpdb->get_row(
916 927 $wpdb->prepare(
917 928 "SELECT * FROM {$tables['folders']} WHERE id = %d",
@@ -921,11 +932,11 @@
921 932 );
922 933 if ( ! $row ) {
923 934 return true;
924 935 }
925 - if ( ! desktop_mode_files_user_can_purge_folder( $user_id, $row ) ) {
936 + if ( ! openstation_files_user_can_purge_folder( $user_id, $row ) ) {
926 937 return new WP_Error(
927 - 'desktop_mode_files_forbidden',
938 + 'openstation_files_forbidden',
928 939 __( 'You do not have permission to delete this folder.', 'desktop-mode' ),
929 940 array( 'status' => 403 )
930 941 );
931 942 }
@@ -934,9 +945,9 @@
934 945 * @param int $folder_id
935 946 * @param int $user_id
936 947 * @param array $row
937 948 */
938 - do_action( 'desktop_mode_files_before_purge_folder', $folder_id, $user_id, $row );
949 + do_action( 'openstation_files_before_purge_folder', $folder_id, $user_id, $row );
939 950
940 951 // Cascade-revoke every share + per-user decision for the folder
941 952 // BEFORE deleting the folder row. Without this, purge left
942 953 // orphan `folder_shares` + `share_user_decisions` rows pointing
@@ -942,9 +953,9 @@
942 953 // orphan `folder_shares` + `share_user_decisions` rows pointing
943 954 // at a folder id that no longer exists — `compute_visible_folders`
944 955 // would still join them, and the row leak grew with every
945 956 // recycle-bin empty. Mirrors the same cleanup
946 - // `desktop_mode_files_delete_folder_recursive` does for the
957 + // `openstation_files_delete_folder_recursive` does for the
947 958 // "delete from desktop" path.
948 959 // `target_type` scoping is load-bearing: `folder_id` carries a
949 960 // STORED-FILE id on `target_type='file'` rows, and the two id
950 961 // sequences are independent — without the predicate a folder
@@ -984,9 +995,9 @@
984 995 (string) $folder_id
985 996 )
986 997 );
987 998 foreach ( $pointing_ids as $pid ) {
988 - desktop_mode_files_write_tombstone( 'placement', (int) $pid );
999 + openstation_files_write_tombstone( 'placement', (int) $pid );
989 1000 }
990 1001 if ( ! empty( $pointing_ids ) ) {
991 1002 $placeholders = implode( ',', array_fill( 0, count( $pointing_ids ), '%d' ) );
992 1003 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
@@ -1000,9 +1011,9 @@
1000 1011
1001 1012 // Upload placements among the cascade-trashed children carry
1002 1013 // real bytes — run the stored-files deletion contract for them
1003 1014 // after the rows go. Direct guarded call (not the public
1004 - // `desktop_mode_file_unplaced` action) so cascade hook semantics
1015 + // `openstation_file_unplaced` action) so cascade hook semantics
1005 1016 // for every other type stay unchanged.
1006 1017 $cascade_upload_rows = (array) $wpdb->get_results(
1007 1018 $wpdb->prepare(
1008 1019 "SELECT * FROM {$tables['placements']}
@@ -1015,13 +1026,13 @@
1015 1026 $tables['placements'],
1016 1027 array( 'trashed_via_folder' => $folder_id ),
1017 1028 array( '%d' )
1018 1029 );
1019 - if ( function_exists( 'desktop_mode_stored_files_handle_unplaced' ) ) {
1030 + if ( function_exists( 'openstation_stored_files_handle_unplaced' ) ) {
1020 1031 foreach ( $cascade_upload_rows as $upload_row ) {
1021 - desktop_mode_stored_files_handle_unplaced(
1032 + openstation_stored_files_handle_unplaced(
1022 1033 (int) $upload_row['id'],
1023 - desktop_mode_files_normalize_placement_row( $upload_row )
1034 + openstation_files_normalize_placement_row( $upload_row )
1024 1035 );
1025 1036 }
1026 1037 }
1027 1038 $wpdb->delete( $tables['folders'], array( 'id' => $folder_id ), array( '%d' ) );
@@ -1029,16 +1040,18 @@
1029 1040 /**
1030 1041 * @param int $folder_id
1031 1042 * @param int $user_id
1032 1043 */
1033 - do_action( 'desktop_mode_files_after_purge_folder', $folder_id, $user_id );
1044 + do_action( 'openstation_files_after_purge_folder', $folder_id, $user_id );
1034 1045
1035 1046 return true;
1036 1047 }
1037 1048
1038 -/* ================================================================== *
1049 +/*
1050 +================================================================== *
1039 1051 * Recycle-bin list builder.
1040 - * ================================================================== */
1052 + * ==================================================================
1053 + */
1041 1054
1042 1055 /**
1043 1056 * Count of trashed placements + folders surfaced to the recycle bin
1044 1057 * for `$user_id`. Mirrors `_list_trashed_for_recycle_bin`'s "skip
@@ -1046,15 +1059,15 @@
1046 1059 *
1047 1060 * @param int $user_id Owner.
1048 1061 * @return int
1049 1062 */
1050 -function desktop_mode_files_count_trashed_for_recycle_bin( $user_id ) {
1063 +function openstation_files_count_trashed_for_recycle_bin( $user_id ) {
1051 1064 global $wpdb;
1052 1065 $user_id = (int) $user_id;
1053 1066 if ( $user_id <= 0 ) {
1054 1067 return 0;
1055 1068 }
1056 - $tables = desktop_mode_files_table_names();
1069 + $tables = openstation_files_table_names();
1057 1070
1058 1071 $placements = (int) $wpdb->get_var(
1059 1072 $wpdb->prepare(
1060 1073 "SELECT COUNT(*) FROM {$tables['placements']}
@@ -1063,9 +1076,9 @@
1063 1076 AND trashed_via_folder IS NULL",
1064 1077 $user_id
1065 1078 )
1066 1079 );
1067 - $folders = (int) $wpdb->get_var(
1080 + $folders = (int) $wpdb->get_var(
1068 1081 $wpdb->prepare(
1069 1082 "SELECT COUNT(*) FROM {$tables['folders']}
1070 1083 WHERE owner_id = %d AND trashed_at_ms IS NOT NULL",
1071 1084 $user_id
@@ -1081,12 +1094,12 @@
1081 1094 *
1082 1095 * @param int $user_id Owner.
1083 1096 * @return array[] List of recycle-bin item shapes.
1084 1097 */
1085 -function desktop_mode_files_list_trashed_for_recycle_bin( $user_id ) {
1098 +function openstation_files_list_trashed_for_recycle_bin( $user_id ) {
1086 1099 global $wpdb;
1087 1100 $user_id = (int) $user_id;
1088 - $tables = desktop_mode_files_table_names();
1101 + $tables = openstation_files_table_names();
1089 1102 $out = array();
1090 1103
1091 1104 // Trashed placements owned by this user.
1092 1105 $placements = $wpdb->get_results(
@@ -1103,20 +1116,22 @@
1103 1116 // the whole bundle in the recycle bin.
1104 1117 if ( ! empty( $row['trashed_via_folder'] ) ) {
1105 1118 continue;
1106 1119 }
1107 - $file = function_exists( 'desktop_mode_resolve_file' )
1108 - ? desktop_mode_resolve_file( $row['file_type'], $row['file_ref'] )
1120 + $file = function_exists( 'openstation_resolve_file' )
1121 + ? openstation_resolve_file( $row['file_type'], $row['file_ref'] )
1109 1122 : null;
1110 - $title = $file ? (string) $file->title() : (string) $row['file_type'];
1123 + $title = $file
1124 + ? openstation_plain_text_title( $file->title() )
1125 + : (string) $row['file_type'];
1111 1126 $icon = $file ? (string) $file->icon() : 'dashicons-no-alt';
1112 1127 // Two recycle-bin buckets:
1113 - // - `shortcut` → plugin-registered icons (file_type='shortcut')
1114 - // - `placement` → every other placement (post / page /
1115 - // attachment / user / term / comment / …)
1128 + // - `shortcut` → plugin-registered icons (file_type='shortcut')
1129 + // - `placement` → every other placement (post / page /
1130 + // attachment / user / term / comment / …)
1116 1131 // Lets the bin's type-filter tabs split "Shortcuts" from
1117 1132 // "Files" without overloading either label.
1118 - $bucket = ( 'shortcut' === (string) $row['file_type'] )
1133 + $bucket = ( 'shortcut' === (string) $row['file_type'] )
1119 1134 ? 'shortcut'
1120 1135 : 'placement';
1121 1136 $subtitle = ( 'shortcut' === $bucket )
1122 1137 ? __( 'Desktop shortcut', 'desktop-mode' )
@@ -1141,10 +1156,10 @@
1141 1156 'icon' => $icon,
1142 1157 'deleted_at' => gmdate( 'c', (int) round( (int) $row['trashed_at_ms'] / 1000 ) ),
1143 1158 'deleted_by' => '',
1144 1159 'deleted_by_id' => (int) $row['trashed_by'],
1145 - 'can_restore' => desktop_mode_files_user_can_restore_placement( $user_id, $row ),
1146 - 'can_purge' => desktop_mode_files_user_can_purge_placement( $user_id, $row ),
1160 + 'can_restore' => openstation_files_user_can_restore_placement( $user_id, $row ),
1161 + 'can_purge' => openstation_files_user_can_purge_placement( $user_id, $row ),
1147 1162 'edit_link' => '',
1148 1163 );
1149 1164 if ( 'link' === (string) $row['file_type'] ) {
1150 1165 $item['type_label'] = __( 'URL', 'desktop-mode' );
@@ -1169,9 +1184,9 @@
1169 1184 WHERE trashed_via_folder = %d",
1170 1185 (int) $row['id']
1171 1186 )
1172 1187 );
1173 - $out[] = array(
1188 + $out[] = array(
1174 1189 'id' => (int) $row['id'],
1175 1190 'type' => 'folder',
1176 1191 'title' => (string) $row['name'],
1177 1192 'subtitle' => $child_count > 0
@@ -1186,10 +1201,10 @@
1186 1201 'icon' => 'dashicons-portfolio',
1187 1202 'deleted_at' => gmdate( 'c', (int) round( (int) $row['trashed_at_ms'] / 1000 ) ),
1188 1203 'deleted_by' => '',
1189 1204 'deleted_by_id' => (int) $row['trashed_by'],
1190 - 'can_restore' => desktop_mode_files_user_can_restore_folder( $user_id, $row ),
1191 - 'can_purge' => desktop_mode_files_user_can_purge_folder( $user_id, $row ),
1205 + 'can_restore' => openstation_files_user_can_restore_folder( $user_id, $row ),
1206 + 'can_purge' => openstation_files_user_can_purge_folder( $user_id, $row ),
1192 1207 'edit_link' => '',
1193 1208 );
1194 1209 }
1195 1210
@@ -1200,9 +1215,9 @@
1200 1215 if ( $uid <= 0 ) {
1201 1216 continue;
1202 1217 }
1203 1218 if ( ! isset( $user_cache[ $uid ] ) ) {
1204 - $u = get_userdata( $uid );
1219 + $u = get_userdata( $uid );
1205 1220 $user_cache[ $uid ] = $u ? $u->display_name : '';
1206 1221 }
1207 1222 $item['deleted_by'] = $user_cache[ $uid ];
1208 1223 }