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/recycle-bin/store.php +139 -194 0.9.6 → 1.1.12 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — Recycle Bin: store.
3 + * OpenStation — Recycle Bin: store.
4 4 *
5 5 * Read/restore/purge primitives that the REST layer wraps. Backed
6 6 * entirely by core post-table state — no custom tables, no options
7 7 * blob. "Trashed" items are exactly the rows with
@@ -6,14 +6,13 @@
6 6 * entirely by core post-table state — no custom tables, no options
7 7 * blob. "Trashed" items are exactly the rows with
8 8 * `post_status = 'trash'` for the post types the bin tracks.
9 9 *
10 - * Every read goes through `desktop_mode_recycle_bin_query_args` so
10 + * Every read goes through `openstation_recycle_bin_query_args` so
11 11 * plugins can scope the bin (e.g. show only the current user's
12 12 * trash, or filter by author/role for compliance use cases).
13 13 *
14 - * @package WPDesktopMode
15 - * @since 0.6.0
14 + * @package OpenStation
16 15 */
17 16
18 17 defined( 'ABSPATH' ) || exit;
19 18
@@ -20,10 +19,8 @@
20 19 /**
21 20 * Returns the list of trashed items the current user is allowed to
22 21 * see, shaped for the table component.
23 22 *
24 - * @since 0.6.0
25 - *
26 23 * @param array $args {
27 24 * Optional. Query overrides.
28 25 *
29 26 * @type int $per_page Default 100.
@@ -35,9 +32,9 @@
35 32 * @type array $items List of items shaped for the JS layer.
36 33 * @type int $total Total matching rows (across pages).
37 34 * }
38 35 */
39 -function desktop_mode_recycle_bin_get_items( $args = array() ) {
36 +function openstation_recycle_bin_get_items( $args = array() ) {
40 37 $args = wp_parse_args(
41 38 $args,
42 39 array(
43 40 'per_page' => 100,
@@ -59,9 +56,9 @@
59 56
60 57 // Source gates: each `$type` filter narrows down to the
61 58 // owning store. `''` (All) loads every source. The files-on-
62 59 // desktop sources (`shortcut` / `placement` / `folder`) live in
63 - // `desktop_mode_files_list_trashed_for_recycle_bin` — never run
60 + // `openstation_files_list_trashed_for_recycle_bin` — never run
64 61 // the WP-core post / comment queries when one of those is the
65 62 // active filter, otherwise trashed posts leak into the
66 63 // "Shortcuts" / "Folders" tabs.
67 64 $files_types = array( 'desktop', 'placement', 'shortcut', 'folder' );
@@ -68,12 +65,12 @@
68 65 $is_files_filter = in_array( $type, $files_types, true );
69 66 $wants_post_types = '' === $type
70 67 || ( 'comment' !== $type && ! $is_files_filter );
71 68 $wants_comments = ( '' === $type || 'comment' === $type )
72 - && desktop_mode_recycle_bin_comments_enabled();
69 + && openstation_recycle_bin_comments_enabled();
73 70
74 71 if ( $wants_post_types ) {
75 - $post_types = desktop_mode_recycle_bin_capture_post_types();
72 + $post_types = openstation_recycle_bin_capture_post_types();
76 73 if ( '' !== $type && in_array( $type, $post_types, true ) ) {
77 74 $post_types = array( $type );
78 75 }
79 76
@@ -90,21 +87,19 @@
90 87
91 88 /**
92 89 * Filter the WP_Query args used to populate the recycle bin.
93 90 *
94 - * @since 0.6.0
95 - *
96 91 * @param array $query_args Args passed to WP_Query.
97 92 * @param array $args Caller-provided args.
98 93 */
99 - $query_args = apply_filters( 'desktop_mode_recycle_bin_query_args', $query_args, $args );
94 + $query_args = apply_filters( 'openstation_recycle_bin_query_args', $query_args, $args );
100 95
101 96 $query = new WP_Query( $query_args );
102 97 foreach ( $query->posts as $post ) {
103 - if ( ! desktop_mode_recycle_bin_user_can_view( $post ) ) {
98 + if ( ! openstation_recycle_bin_user_can_view( $post ) ) {
104 99 continue;
105 100 }
106 - $items_posts[] = desktop_mode_recycle_bin_shape_item( $post );
101 + $items_posts[] = openstation_recycle_bin_shape_item( $post );
107 102 }
108 103 }
109 104
110 105 if ( $wants_comments ) {
@@ -120,17 +115,15 @@
120 115
121 116 /**
122 117 * Filter the `WP_Comment_Query` args used to populate the
123 118 * recycle bin's comments. Mirror of
124 - * `desktop_mode_recycle_bin_query_args` for comments.
119 + * `openstation_recycle_bin_query_args` for comments.
125 120 *
126 - * @since 0.6.0
127 - *
128 121 * @param array $comment_args Args passed to `get_comments()`.
129 122 * @param array $args Caller-provided args.
130 123 */
131 124 $comment_args = apply_filters(
132 - 'desktop_mode_recycle_bin_comment_query_args',
125 + 'openstation_recycle_bin_comment_query_args',
133 126 $comment_args,
134 127 $args
135 128 );
136 129
@@ -136,12 +129,12 @@
136 129
137 130 $comments = get_comments( $comment_args );
138 131 if ( is_array( $comments ) ) {
139 132 foreach ( $comments as $comment ) {
140 - if ( ! desktop_mode_recycle_bin_user_can_view_comment( $comment ) ) {
133 + if ( ! openstation_recycle_bin_user_can_view_comment( $comment ) ) {
141 134 continue;
142 135 }
143 - $items_comments[] = desktop_mode_recycle_bin_shape_comment_item( $comment );
136 + $items_comments[] = openstation_recycle_bin_shape_comment_item( $comment );
144 137 }
145 138 }
146 139 }
147 140
@@ -148,9 +141,9 @@
148 141 // Files-on-the-Desktop trash — soft-trashed placements
149 142 // (shortcuts) and folders. Returned in the same item shape so
150 143 // the JS layer treats them uniformly. The `placement` and
151 144 // `folder` types route to the desktop-files trash module on
152 - // restore / purge (see `desktop_mode_recycle_bin_handle_files_*`).
145 + // restore / purge (see `openstation_recycle_bin_handle_files_*`).
153 146 $items_files = array();
154 147 // Map UI filter → set of `type` values to keep from the
155 148 // files-on-desktop helper. The "Shortcuts" segment in the bin
156 149 // UI now covers both registered icons (`shortcut`) AND user
@@ -174,11 +167,11 @@
174 167 break;
175 168 }
176 169 if (
177 170 ! empty( $wanted_files_types )
178 - && function_exists( 'desktop_mode_files_list_trashed_for_recycle_bin' )
171 + && function_exists( 'openstation_files_list_trashed_for_recycle_bin' )
179 172 ) {
180 - $file_items = desktop_mode_files_list_trashed_for_recycle_bin(
173 + $file_items = openstation_files_list_trashed_for_recycle_bin(
181 174 get_current_user_id()
182 175 );
183 176 foreach ( (array) $file_items as $item ) {
184 177 if ( ! in_array( (string) $item['type'], $wanted_files_types, true ) ) {
@@ -193,11 +186,14 @@
193 186 // Sort the merged list chronologically by deleted_at desc. The
194 187 // shape always carries a sortable string in `deleted_at`, so a
195 188 // straight string compare is enough (`Y-m-d H:i:s` is sortable
196 189 // lexicographically).
197 - usort( $items, static function ( $a, $b ) {
198 - return strcmp( (string) $b['deleted_at'], (string) $a['deleted_at'] );
199 - } );
190 + usort(
191 + $items,
192 + static function ( $a, $b ) {
193 + return strcmp( (string) $b['deleted_at'], (string) $a['deleted_at'] );
194 + }
195 + );
200 196
201 197 // `total` reports the GLOBAL trash count (every type, every
202 198 // row, ignoring the current filter / search). The dock-tile
203 199 // + desktop-icon badge consume this directly — `setRecycleBinBadge`
@@ -203,9 +199,9 @@
203 199 // + desktop-icon badge consume this directly — `setRecycleBinBadge`
204 200 // only cares about "how many things are sitting in the bin
205 201 // right now". A future paginated UI that needs a filtered
206 202 // count can compute it from `count( $items )` itself.
207 - $total = desktop_mode_recycle_bin_count();
203 + $total = openstation_recycle_bin_count();
208 204
209 205 $offset = max( 0, ( max( 1, (int) $args['page'] ) - 1 ) * $per_page );
210 206 $sliced = array_slice( $items, $offset, $per_page );
211 207
@@ -211,15 +207,13 @@
211 207
212 208 /**
213 209 * Filter the final list of items returned to the JS layer.
214 210 *
215 - * @since 0.6.0
216 - *
217 211 * @param array $items Shaped list (id, title, type, deleted_at, …).
218 212 * @param array|null $query Underlying post query, or null for the
219 - * merged post+comment shape (since 0.6.0).
213 + * merged post+comment shape.
220 214 */
221 - $sliced = apply_filters( 'desktop_mode_recycle_bin_items', $sliced, null );
215 + $sliced = apply_filters( 'openstation_recycle_bin_items', $sliced, null );
222 216
223 217 return array(
224 218 'items' => $sliced,
225 219 'total' => $total,
@@ -229,9 +223,9 @@
229 223 /**
230 224 * Total number of items in the recycle bin, summed across every
231 225 * tracked source (post types + comments).
232 226 *
233 - * Cheaper than `desktop_mode_recycle_bin_get_items()` because it never
227 + * Cheaper than `openstation_recycle_bin_get_items()` because it never
234 228 * loads the row data — just the COUNT(*) under the hood. Used by
235 229 * the badge on the dock tile + desktop icon, and by the REST
236 230 * `/count` endpoint subscribers refresh on broadcasts.
237 231 *
@@ -240,14 +234,12 @@
240 234 * at all contribute zero, and types where the user can only edit their
241 235 * own posts are counted author-scoped — so the badge never discloses
242 236 * the global trash total to low-capability users.
243 237 *
244 - * @since 0.6.0
245 - *
246 238 * @return int
247 239 */
248 -function desktop_mode_recycle_bin_count() {
249 - $post_types = desktop_mode_recycle_bin_capture_post_types();
240 +function openstation_recycle_bin_count() {
241 + $post_types = openstation_recycle_bin_capture_post_types();
250 242
251 243 // Bucket the tracked types by what the current user may edit:
252 244 // full count when they hold the type's `edit_others_posts`,
253 245 // author-scoped count when they only hold `edit_posts`, nothing
@@ -298,9 +290,9 @@
298 290 $post_count += (int) $own_query->found_posts;
299 291 }
300 292
301 293 $comment_count = 0;
302 - if ( desktop_mode_recycle_bin_comments_enabled() ) {
294 + if ( openstation_recycle_bin_comments_enabled() ) {
303 295 $comment_count = (int) get_comments(
304 296 array(
305 297 'status' => 'trash',
306 298 'count' => true,
@@ -308,10 +300,10 @@
308 300 );
309 301 }
310 302
311 303 $files_count = 0;
312 - if ( function_exists( 'desktop_mode_files_count_trashed_for_recycle_bin' ) ) {
313 - $files_count = (int) desktop_mode_files_count_trashed_for_recycle_bin( get_current_user_id() );
304 + if ( function_exists( 'openstation_files_count_trashed_for_recycle_bin' ) ) {
305 + $files_count = (int) openstation_files_count_trashed_for_recycle_bin( get_current_user_id() );
314 306 }
315 307
316 308 $total = $post_count + $comment_count + $files_count;
317 309
@@ -317,17 +309,15 @@
317 309
318 310 /**
319 311 * Filter the total count surfaced to the badge.
320 312 *
321 - * @since 0.6.0
322 - *
323 313 * @param int $total Default sum (posts + comments + files visible to the user).
324 314 * @param int $post_count Items in trash from the post-type query, capability-scoped
325 315 * to what the current user can edit.
326 316 * @param int $comment_count Items in trash from the comment query.
327 - * @param int $files_count Items in trash from the desktop-files trash (since 0.8.0).
317 + * @param int $files_count Items in trash from the desktop-files trash.
328 318 */
329 - return (int) apply_filters( 'desktop_mode_recycle_bin_count', $total, $post_count, $comment_count, $files_count );
319 + return (int) apply_filters( 'openstation_recycle_bin_count', $total, $post_count, $comment_count, $files_count );
330 320 }
331 321
332 322 /**
333 323 * Whether comments are part of the bin. Filterable so installs
@@ -333,23 +323,19 @@
333 323 * Whether comments are part of the bin. Filterable so installs
334 324 * that don't moderate comments at all (read-only blogs, headless
335 325 * setups) can hide the segment without touching the JS.
336 326 *
337 - * @since 0.6.0
338 - *
339 327 * @return bool
340 328 */
341 -function desktop_mode_recycle_bin_comments_enabled() {
329 +function openstation_recycle_bin_comments_enabled() {
342 330 $on = current_user_can( 'moderate_comments' );
343 331
344 332 /**
345 333 * Filter whether the recycle bin tracks comments.
346 334 *
347 - * @since 0.6.0
348 - *
349 335 * @param bool $on Default: current user has `moderate_comments`.
350 336 */
351 - return (bool) apply_filters( 'desktop_mode_recycle_bin_comments_enabled', $on );
337 + return (bool) apply_filters( 'openstation_recycle_bin_comments_enabled', $on );
352 338 }
353 339
354 340 /**
355 341 * Whether the current user can see a given trashed item.
@@ -357,70 +343,58 @@
357 343 * Mirrors `current_user_can( 'edit_post', $id )` for the consistent
358 344 * "if you can edit it, you can manage its trash" rule. Filterable for
359 345 * stricter / looser policies.
360 346 *
361 - * @since 0.6.0
362 - *
363 347 * @param WP_Post $post Trashed post.
364 348 * @return bool
365 349 */
366 -function desktop_mode_recycle_bin_user_can_view( $post ) {
350 +function openstation_recycle_bin_user_can_view( $post ) {
367 351 $can = current_user_can( 'edit_post', $post->ID );
368 352
369 353 /**
370 354 * Filter whether the current user can see a given trashed item.
371 355 *
372 - * @since 0.6.0
373 - *
374 356 * @param bool $can Default: edit_post capability check.
375 357 * @param WP_Post $post Trashed post.
376 358 */
377 - return (bool) apply_filters( 'desktop_mode_recycle_bin_user_can_view', $can, $post );
359 + return (bool) apply_filters( 'openstation_recycle_bin_user_can_view', $can, $post );
378 360 }
379 361
380 362 /**
381 363 * Whether the current user can restore a given trashed item.
382 364 *
383 - * @since 0.6.0
384 - *
385 365 * @param WP_Post $post Trashed post.
386 366 * @return bool
387 367 */
388 -function desktop_mode_recycle_bin_user_can_restore( $post ) {
368 +function openstation_recycle_bin_user_can_restore( $post ) {
389 369 $can = current_user_can( 'delete_post', $post->ID );
390 370
391 371 /**
392 372 * Filter whether the current user can restore a given trashed item.
393 373 *
394 - * @since 0.6.0
395 - *
396 374 * @param bool $can Default: delete_post capability check (the same
397 375 * gate WP itself uses for trash/untrash).
398 376 * @param WP_Post $post Trashed post.
399 377 */
400 - return (bool) apply_filters( 'desktop_mode_recycle_bin_user_can_restore', $can, $post );
378 + return (bool) apply_filters( 'openstation_recycle_bin_user_can_restore', $can, $post );
401 379 }
402 380
403 381 /**
404 382 * Whether the current user can permanently delete a trashed item.
405 383 *
406 - * @since 0.6.0
407 - *
408 384 * @param WP_Post $post Trashed post.
409 385 * @return bool
410 386 */
411 -function desktop_mode_recycle_bin_user_can_purge( $post ) {
387 +function openstation_recycle_bin_user_can_purge( $post ) {
412 388 $can = current_user_can( 'delete_post', $post->ID );
413 389
414 390 /**
415 391 * Filter whether the current user can permanently delete a trashed item.
416 392 *
417 - * @since 0.6.0
418 - *
419 393 * @param bool $can Default: delete_post capability check.
420 394 * @param WP_Post $post Trashed post.
421 395 */
422 - return (bool) apply_filters( 'desktop_mode_recycle_bin_user_can_purge', $can, $post );
396 + return (bool) apply_filters( 'openstation_recycle_bin_user_can_purge', $can, $post );
423 397 }
424 398
425 399 /**
426 400 * Capability gates for trashed comments. Mirror of the post gates,
@@ -425,56 +399,47 @@
425 399 /**
426 400 * Capability gates for trashed comments. Mirror of the post gates,
427 401 * with `edit_comment`/`moderate_comments` as the WP-native checks.
428 402 *
429 - * @since 0.6.0
430 - *
431 403 * @param WP_Comment $comment Trashed comment.
432 404 * @return bool
433 405 */
434 -function desktop_mode_recycle_bin_user_can_view_comment( $comment ) {
406 +function openstation_recycle_bin_user_can_view_comment( $comment ) {
435 407 $can = current_user_can( 'edit_comment', $comment->comment_ID );
436 408
437 409 /**
438 - * @since 0.6.0
439 410 * @param bool $can Default: edit_comment capability check.
440 411 * @param WP_Comment $comment Trashed comment.
441 412 */
442 - return (bool) apply_filters( 'desktop_mode_recycle_bin_user_can_view_comment', $can, $comment );
413 + return (bool) apply_filters( 'openstation_recycle_bin_user_can_view_comment', $can, $comment );
443 414 }
444 415
445 416 /**
446 - * @since 0.6.0
447 - *
448 417 * @param WP_Comment $comment Trashed comment.
449 418 * @return bool
450 419 */
451 -function desktop_mode_recycle_bin_user_can_restore_comment( $comment ) {
420 +function openstation_recycle_bin_user_can_restore_comment( $comment ) {
452 421 $can = current_user_can( 'edit_comment', $comment->comment_ID );
453 422
454 423 /**
455 - * @since 0.6.0
456 424 * @param bool $can Default: edit_comment capability check.
457 425 * @param WP_Comment $comment Trashed comment.
458 426 */
459 - return (bool) apply_filters( 'desktop_mode_recycle_bin_user_can_restore_comment', $can, $comment );
427 + return (bool) apply_filters( 'openstation_recycle_bin_user_can_restore_comment', $can, $comment );
460 428 }
461 429
462 430 /**
463 - * @since 0.6.0
464 - *
465 431 * @param WP_Comment $comment Trashed comment.
466 432 * @return bool
467 433 */
468 -function desktop_mode_recycle_bin_user_can_purge_comment( $comment ) {
434 +function openstation_recycle_bin_user_can_purge_comment( $comment ) {
469 435 $can = current_user_can( 'edit_comment', $comment->comment_ID );
470 436
471 437 /**
472 - * @since 0.6.0
473 438 * @param bool $can Default: edit_comment capability check.
474 439 * @param WP_Comment $comment Trashed comment.
475 440 */
476 - return (bool) apply_filters( 'desktop_mode_recycle_bin_user_can_purge_comment', $can, $comment );
441 + return (bool) apply_filters( 'openstation_recycle_bin_user_can_purge_comment', $can, $comment );
477 442 }
478 443
479 444 /**
480 445 * Shape a `WP_Comment` into the JSON the JS table consumes.
@@ -483,14 +448,12 @@
483 448 * have to special-case the row by `type`. The `title` reads as
484 449 * "<author> on <post title>"; the `subtitle` carries a 100-char
485 450 * excerpt of `comment_content`.
486 451 *
487 - * @since 0.6.0
488 - *
489 452 * @param WP_Comment $comment Trashed comment.
490 453 * @return array
491 454 */
492 -function desktop_mode_recycle_bin_shape_comment_item( $comment ) {
455 +function openstation_recycle_bin_shape_comment_item( $comment ) {
493 456 $user_id = (int) get_comment_meta( $comment->comment_ID, '_desktop_mode_trash_user_id', true );
494 457 $deleted_at = (string) get_comment_meta( $comment->comment_ID, '_desktop_mode_trash_time_gmt', true );
495 458
496 459 if ( '' === $deleted_at ) {
@@ -497,9 +460,9 @@
497 460 $deleted_at = (string) $comment->comment_date_gmt;
498 461 }
499 462
500 463 $parent = $comment->comment_post_ID ? get_post( (int) $comment->comment_post_ID ) : null;
501 - $parent_text = $parent ? desktop_mode_recycle_bin_plain_text( get_the_title( $parent ) ) : '';
464 + $parent_text = $parent ? openstation_recycle_bin_plain_text( get_the_title( $parent ) ) : '';
502 465 $author = $comment->comment_author
503 466 ? (string) $comment->comment_author
504 467 : __( 'Anonymous', 'desktop-mode' );
505 468
@@ -511,9 +474,9 @@
511 474 $parent_text
512 475 )
513 476 : $author;
514 477
515 - $subtitle = wp_trim_words( desktop_mode_recycle_bin_plain_text( (string) $comment->comment_content ), 18, '…' );
478 + $subtitle = openstation_recycle_bin_excerpt( (string) $comment->comment_content );
516 479
517 480 $user = $user_id ? get_userdata( $user_id ) : false;
518 481 $user_name = $user ? $user->display_name : '';
519 482
@@ -528,10 +491,10 @@
528 491 'icon' => 'dashicons-admin-comments',
529 492 'deleted_at' => $deleted_at,
530 493 'deleted_by' => $user_name,
531 494 'deleted_by_id' => $user_id,
532 - 'can_restore' => desktop_mode_recycle_bin_user_can_restore_comment( $comment ),
533 - 'can_purge' => desktop_mode_recycle_bin_user_can_purge_comment( $comment ),
495 + 'can_restore' => openstation_recycle_bin_user_can_restore_comment( $comment ),
496 + 'can_purge' => openstation_recycle_bin_user_can_purge_comment( $comment ),
534 497 'edit_link' => (string) get_edit_comment_link( $comment->comment_ID ),
535 498 );
536 499
537 500 /**
@@ -536,14 +499,12 @@
536 499
537 500 /**
538 501 * Filter the comment item shape.
539 502 *
540 - * @since 0.6.0
541 - *
542 503 * @param array $item Item shape.
543 504 * @param WP_Comment $comment Source comment.
544 505 */
545 - return (array) apply_filters( 'desktop_mode_recycle_bin_comment_item', $item, $comment );
506 + return (array) apply_filters( 'openstation_recycle_bin_comment_item', $item, $comment );
546 507 }
547 508
548 509 /**
549 510 * Collapse a title/subtitle to plain text for the wire.
@@ -554,16 +515,14 @@
554 515 * output, wrong for the bin table, which renders every cell via
555 516 * `textContent` and would show the literal entity. Strip tags first,
556 517 * then decode entities back to characters.
557 518 *
558 - * @since 0.9.6
559 - *
560 519 * @param string $text Raw filtered text.
561 520 * @return string
562 521 */
563 -function desktop_mode_recycle_bin_plain_text( $text ) {
522 +function openstation_recycle_bin_plain_text( $text ) {
564 523 return html_entity_decode(
565 - wp_strip_all_tags( (string) $text ),
524 + openstation_strip_all_tags( $text ),
566 525 ENT_QUOTES,
567 526 get_bloginfo( 'charset' )
568 527 );
569 528 }
@@ -568,16 +527,31 @@
568 527 );
569 528 }
570 529
571 530 /**
531 + * The first words of a body, as the plain text of a row's subtitle.
532 + *
533 + * @param string $text Raw excerpt or content.
534 + * @return string
535 + */
536 +function openstation_recycle_bin_excerpt( $text ) {
537 + // `wp_trim_words()` strips tags itself, and in plain text that cuts
538 + // at a `<`. Trimming before the decode would avoid it, but where
539 + // WordPress counts characters instead of words an `&amp;` would
540 + // count as five and could be cut in half. So each `<` sits out the
541 + // trim as a control character, one for one.
542 + $text = str_replace( '<', "\x1A", openstation_recycle_bin_plain_text( $text ) );
543 +
544 + return str_replace( "\x1A", '<', wp_trim_words( $text, 18, '…' ) );
545 +}
546 +
547 +/**
572 548 * Shape one WP_Post into the JSON the JS table consumes.
573 549 *
574 - * @since 0.6.0
575 - *
576 550 * @param WP_Post $post Trashed post.
577 551 * @return array
578 552 */
579 -function desktop_mode_recycle_bin_shape_item( $post ) {
553 +function openstation_recycle_bin_shape_item( $post ) {
580 554 $user_id = (int) get_post_meta( $post->ID, '_desktop_mode_trash_user_id', true );
581 555 $deleted_at = (string) get_post_meta( $post->ID, '_desktop_mode_trash_time_gmt', true );
582 556
583 557 // Fall back to post_modified_gmt — set when wp_trash_post runs and
@@ -586,9 +560,9 @@
586 560 $deleted_at = (string) $post->post_modified_gmt;
587 561 }
588 562
589 563 $type = (string) $post->post_type;
590 - $title = desktop_mode_recycle_bin_plain_text( (string) get_the_title( $post ) );
564 + $title = openstation_recycle_bin_plain_text( (string) get_the_title( $post ) );
591 565 $mime = (string) $post->post_mime_type;
592 566 $preview = '';
593 567 $icon = '';
594 568 $subtitle = '';
@@ -600,16 +574,17 @@
600 574 $thumb = wp_get_attachment_image_src( $post->ID, array( 64, 64 ), true );
601 575 if ( is_array( $thumb ) ) {
602 576 $preview = (string) $thumb[0];
603 577 }
604 - $icon = desktop_mode_recycle_bin_icon_for_mime( $mime );
578 + $icon = openstation_recycle_bin_icon_for_mime( $mime );
605 579 $subtitle = $mime;
606 580 } elseif ( 'post' === $type ) {
607 581 $icon = 'dashicons-admin-post';
608 - $subtitle = wp_trim_words( desktop_mode_recycle_bin_plain_text( (string) $post->post_excerpt ?: (string) $post->post_content ), 18, '…' );
582 + $excerpt = (string) $post->post_excerpt;
583 + $subtitle = openstation_recycle_bin_excerpt( $excerpt ? $excerpt : (string) $post->post_content );
609 584 } elseif ( 'page' === $type ) {
610 585 $icon = 'dashicons-admin-page';
611 - $subtitle = wp_trim_words( desktop_mode_recycle_bin_plain_text( (string) $post->post_content ), 18, '…' );
586 + $subtitle = openstation_recycle_bin_excerpt( (string) $post->post_content );
612 587 } else {
613 588 // Custom post types: reuse the type's own menu Dashicon when it
614 589 // registered one, so a trashed product row reads as a product
615 590 // instead of a generic file. Content excerpt as the subtitle,
@@ -622,9 +597,10 @@
622 597 && str_starts_with( $post_type_obj->menu_icon, 'dashicons-' )
623 598 ) {
624 599 $icon = $post_type_obj->menu_icon;
625 600 }
626 - $subtitle = wp_trim_words( desktop_mode_recycle_bin_plain_text( (string) $post->post_excerpt ?: (string) $post->post_content ), 18, '…' );
601 + $excerpt = (string) $post->post_excerpt;
602 + $subtitle = openstation_recycle_bin_excerpt( $excerpt ? $excerpt : (string) $post->post_content );
627 603 }
628 604
629 605 $user = $user_id ? get_userdata( $user_id ) : false;
630 606 $user_name = $user ? $user->display_name : '';
@@ -656,10 +632,10 @@
656 632 'icon' => $icon,
657 633 'deleted_at' => $deleted_at,
658 634 'deleted_by' => $user_name,
659 635 'deleted_by_id' => $user_id,
660 - 'can_restore' => desktop_mode_recycle_bin_user_can_restore( $post ),
661 - 'can_purge' => desktop_mode_recycle_bin_user_can_purge( $post ),
636 + 'can_restore' => openstation_recycle_bin_user_can_restore( $post ),
637 + 'can_purge' => openstation_recycle_bin_user_can_purge( $post ),
662 638 'edit_link' => (string) get_edit_post_link( $post->ID, 'raw' ),
663 639 );
664 640
665 641 /**
@@ -668,25 +644,21 @@
668 644 * Add custom columns or override the icon/preview for a custom
669 645 * post type. The id/type/deleted_at trio is load-bearing — keep
670 646 * them in the returned array.
671 647 *
672 - * @since 0.6.0
673 - *
674 648 * @param array $item Item shape.
675 649 * @param WP_Post $post Source post.
676 650 */
677 - return (array) apply_filters( 'desktop_mode_recycle_bin_item', $item, $post );
651 + return (array) apply_filters( 'openstation_recycle_bin_item', $item, $post );
678 652 }
679 653
680 654 /**
681 655 * Map a mime type to a Dashicon for the type cell.
682 656 *
683 - * @since 0.6.0
684 - *
685 657 * @param string $mime Mime type.
686 658 * @return string Dashicon class.
687 659 */
688 -function desktop_mode_recycle_bin_icon_for_mime( $mime ) {
660 +function openstation_recycle_bin_icon_for_mime( $mime ) {
689 661 if ( '' === $mime ) {
690 662 return 'dashicons-media-default';
691 663 }
692 664 if ( str_starts_with( $mime, 'image/' ) ) {
@@ -729,51 +701,46 @@
729 701 * everything else through `wp_untrash_post`. The legacy single-arg
730 702 * call (id only) defaults to `'post'` so older clients that haven't
731 703 * migrated to the typed API keep working.
732 704 *
733 - * @since 0.6.0
734 - * @since 0.6.0 Added `$type` parameter.
735 - *
736 705 * @param int $id Post id (or comment id when `$type === 'comment'`).
737 706 * @param string $type Entity type — '', 'post', 'page', 'attachment', or 'comment'.
738 707 * @return true|WP_Error
739 708 */
740 -function desktop_mode_recycle_bin_restore( $id, $type = '' ) {
709 +function openstation_recycle_bin_restore( $id, $type = '' ) {
741 710 $id = (int) $id;
742 711 if ( 'comment' === $type ) {
743 - return desktop_mode_recycle_bin_restore_comment( $id );
712 + return openstation_recycle_bin_restore_comment( $id );
744 713 }
745 - if ( ( 'placement' === $type || 'shortcut' === $type ) && function_exists( 'desktop_mode_files_restore_placement' ) ) {
746 - return desktop_mode_files_restore_placement( get_current_user_id(), $id );
714 + if ( ( 'placement' === $type || 'shortcut' === $type ) && function_exists( 'openstation_files_restore_placement' ) ) {
715 + return openstation_files_restore_placement( get_current_user_id(), $id );
747 716 }
748 - if ( 'folder' === $type && function_exists( 'desktop_mode_files_restore_folder' ) ) {
749 - return desktop_mode_files_restore_folder( get_current_user_id(), $id );
717 + if ( 'folder' === $type && function_exists( 'openstation_files_restore_folder' ) ) {
718 + return openstation_files_restore_folder( get_current_user_id(), $id );
750 719 }
751 720
752 721 $post = get_post( $id );
753 722 if ( ! $post ) {
754 - return new WP_Error( 'desktop_mode_recycle_bin_not_found', __( 'Item not found.', 'desktop-mode' ), array( 'status' => 404 ) );
723 + return new WP_Error( 'openstation_recycle_bin_not_found', __( 'Item not found.', 'desktop-mode' ), array( 'status' => 404 ) );
755 724 }
756 725 if ( 'trash' !== $post->post_status ) {
757 - return new WP_Error( 'desktop_mode_recycle_bin_not_trashed', __( 'Item is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
726 + return new WP_Error( 'openstation_recycle_bin_not_trashed', __( 'Item is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
758 727 }
759 - if ( ! desktop_mode_recycle_bin_user_can_restore( $post ) ) {
760 - return new WP_Error( 'desktop_mode_recycle_bin_forbidden', __( 'You are not allowed to restore this item.', 'desktop-mode' ), array( 'status' => 403 ) );
728 + if ( ! openstation_recycle_bin_user_can_restore( $post ) ) {
729 + return new WP_Error( 'openstation_recycle_bin_forbidden', __( 'You are not allowed to restore this item.', 'desktop-mode' ), array( 'status' => 403 ) );
761 730 }
762 731
763 732 /**
764 733 * Fires before a recycle-bin item is restored.
765 734 *
766 - * @since 0.6.0
767 - *
768 735 * @param int $id Post id about to be restored.
769 736 * @param WP_Post $post Trashed post object.
770 737 */
771 - do_action( 'desktop_mode_recycle_bin_before_restore', $id, $post );
738 + do_action( 'openstation_recycle_bin_before_restore', $id, $post );
772 739
773 740 $ok = wp_untrash_post( $id );
774 741 if ( ! $ok ) {
775 - return new WP_Error( 'desktop_mode_recycle_bin_restore_failed', __( 'Failed to restore item.', 'desktop-mode' ), array( 'status' => 500 ) );
742 + return new WP_Error( 'openstation_recycle_bin_restore_failed', __( 'Failed to restore item.', 'desktop-mode' ), array( 'status' => 500 ) );
776 743 }
777 744
778 745 delete_post_meta( $id, '_desktop_mode_trash_user_id' );
779 746 delete_post_meta( $id, '_desktop_mode_trash_time_gmt' );
@@ -780,13 +747,11 @@
780 747
781 748 /**
782 749 * Fires after a recycle-bin item is restored.
783 750 *
784 - * @since 0.6.0
785 - *
786 751 * @param int $id Post id that was restored.
787 752 */
788 - do_action( 'desktop_mode_recycle_bin_after_restore', $id );
753 + do_action( 'openstation_recycle_bin_after_restore', $id );
789 754
790 755 return true;
791 756 }
792 757
@@ -792,40 +757,36 @@
792 757
793 758 /**
794 759 * Restore a single trashed comment.
795 760 *
796 - * @since 0.6.0
797 - *
798 761 * @param int $comment_id Comment id.
799 762 * @return true|WP_Error
800 763 */
801 -function desktop_mode_recycle_bin_restore_comment( $comment_id ) {
764 +function openstation_recycle_bin_restore_comment( $comment_id ) {
802 765 $comment_id = (int) $comment_id;
803 766 $comment = get_comment( $comment_id );
804 767
805 768 if ( ! $comment ) {
806 - return new WP_Error( 'desktop_mode_recycle_bin_not_found', __( 'Comment not found.', 'desktop-mode' ), array( 'status' => 404 ) );
769 + return new WP_Error( 'openstation_recycle_bin_not_found', __( 'Comment not found.', 'desktop-mode' ), array( 'status' => 404 ) );
807 770 }
808 771 if ( 'trash' !== $comment->comment_approved ) {
809 - return new WP_Error( 'desktop_mode_recycle_bin_not_trashed', __( 'Comment is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
772 + return new WP_Error( 'openstation_recycle_bin_not_trashed', __( 'Comment is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
810 773 }
811 - if ( ! desktop_mode_recycle_bin_user_can_restore_comment( $comment ) ) {
812 - return new WP_Error( 'desktop_mode_recycle_bin_forbidden', __( 'You are not allowed to restore this comment.', 'desktop-mode' ), array( 'status' => 403 ) );
774 + if ( ! openstation_recycle_bin_user_can_restore_comment( $comment ) ) {
775 + return new WP_Error( 'openstation_recycle_bin_forbidden', __( 'You are not allowed to restore this comment.', 'desktop-mode' ), array( 'status' => 403 ) );
813 776 }
814 777
815 778 /**
816 779 * Fires before a comment is restored from the recycle bin.
817 780 *
818 - * @since 0.6.0
819 - *
820 781 * @param int $comment_id Comment id.
821 782 * @param WP_Comment $comment Trashed comment.
822 783 */
823 - do_action( 'desktop_mode_recycle_bin_before_restore_comment', $comment_id, $comment );
784 + do_action( 'openstation_recycle_bin_before_restore_comment', $comment_id, $comment );
824 785
825 786 $ok = wp_untrash_comment( $comment_id );
826 787 if ( ! $ok ) {
827 - return new WP_Error( 'desktop_mode_recycle_bin_restore_failed', __( 'Failed to restore comment.', 'desktop-mode' ), array( 'status' => 500 ) );
788 + return new WP_Error( 'openstation_recycle_bin_restore_failed', __( 'Failed to restore comment.', 'desktop-mode' ), array( 'status' => 500 ) );
828 789 }
829 790
830 791 delete_comment_meta( $comment_id, '_desktop_mode_trash_user_id' );
831 792 delete_comment_meta( $comment_id, '_desktop_mode_trash_time_gmt' );
@@ -832,13 +793,11 @@
832 793
833 794 /**
834 795 * Fires after a comment is restored from the recycle bin.
835 796 *
836 - * @since 0.6.0
837 - *
838 797 * @param int $comment_id Comment id.
839 798 */
840 - do_action( 'desktop_mode_recycle_bin_after_restore_comment', $comment_id );
799 + do_action( 'openstation_recycle_bin_after_restore_comment', $comment_id );
841 800
842 801 return true;
843 802 }
844 803
@@ -844,47 +803,42 @@
844 803
845 804 /**
846 805 * Permanently delete a single trashed item. Dispatches by `$type`.
847 806 *
848 - * @since 0.6.0
849 - * @since 0.6.0 Added `$type` parameter.
850 - *
851 807 * @param int $id Post id (or comment id when `$type === 'comment'`).
852 808 * @param string $type Entity type — '', 'post', 'page', 'attachment', or 'comment'.
853 809 * @return true|WP_Error
854 810 */
855 -function desktop_mode_recycle_bin_purge( $id, $type = '' ) {
811 +function openstation_recycle_bin_purge( $id, $type = '' ) {
856 812 $id = (int) $id;
857 813 if ( 'comment' === $type ) {
858 - return desktop_mode_recycle_bin_purge_comment( $id );
814 + return openstation_recycle_bin_purge_comment( $id );
859 815 }
860 - if ( ( 'placement' === $type || 'shortcut' === $type ) && function_exists( 'desktop_mode_files_purge_placement' ) ) {
861 - return desktop_mode_files_purge_placement( get_current_user_id(), $id );
816 + if ( ( 'placement' === $type || 'shortcut' === $type ) && function_exists( 'openstation_files_purge_placement' ) ) {
817 + return openstation_files_purge_placement( get_current_user_id(), $id );
862 818 }
863 - if ( 'folder' === $type && function_exists( 'desktop_mode_files_purge_folder' ) ) {
864 - return desktop_mode_files_purge_folder( get_current_user_id(), $id );
819 + if ( 'folder' === $type && function_exists( 'openstation_files_purge_folder' ) ) {
820 + return openstation_files_purge_folder( get_current_user_id(), $id );
865 821 }
866 822
867 823 $post = get_post( $id );
868 824 if ( ! $post ) {
869 - return new WP_Error( 'desktop_mode_recycle_bin_not_found', __( 'Item not found.', 'desktop-mode' ), array( 'status' => 404 ) );
825 + return new WP_Error( 'openstation_recycle_bin_not_found', __( 'Item not found.', 'desktop-mode' ), array( 'status' => 404 ) );
870 826 }
871 827 if ( 'trash' !== $post->post_status ) {
872 - return new WP_Error( 'desktop_mode_recycle_bin_not_trashed', __( 'Item is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
828 + return new WP_Error( 'openstation_recycle_bin_not_trashed', __( 'Item is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
873 829 }
874 - if ( ! desktop_mode_recycle_bin_user_can_purge( $post ) ) {
875 - return new WP_Error( 'desktop_mode_recycle_bin_forbidden', __( 'You are not allowed to permanently delete this item.', 'desktop-mode' ), array( 'status' => 403 ) );
830 + if ( ! openstation_recycle_bin_user_can_purge( $post ) ) {
831 + return new WP_Error( 'openstation_recycle_bin_forbidden', __( 'You are not allowed to permanently delete this item.', 'desktop-mode' ), array( 'status' => 403 ) );
876 832 }
877 833
878 834 /**
879 835 * Fires before a recycle-bin item is permanently deleted.
880 836 *
881 - * @since 0.6.0
882 - *
883 837 * @param int $id Post id about to be deleted.
884 838 * @param WP_Post $post Trashed post object.
885 839 */
886 - do_action( 'desktop_mode_recycle_bin_before_purge', $id, $post );
840 + do_action( 'openstation_recycle_bin_before_purge', $id, $post );
887 841
888 842 if ( 'attachment' === $post->post_type ) {
889 843 // Force-delete (`true`) removes the attachment and its file
890 844 // permanently. (The item is already trashed, so even with
@@ -895,20 +849,18 @@
895 849 $result = wp_delete_post( $id, true );
896 850 }
897 851
898 852 if ( ! $result ) {
899 - return new WP_Error( 'desktop_mode_recycle_bin_purge_failed', __( 'Failed to permanently delete item.', 'desktop-mode' ), array( 'status' => 500 ) );
853 + return new WP_Error( 'openstation_recycle_bin_purge_failed', __( 'Failed to permanently delete item.', 'desktop-mode' ), array( 'status' => 500 ) );
900 854 }
901 855
902 856 /**
903 857 * Fires after a recycle-bin item is permanently deleted.
904 858 *
905 - * @since 0.6.0
906 - *
907 859 * @param int $id Post id that was purged.
908 860 * @param string $type Post type of the purged item.
909 861 */
910 - do_action( 'desktop_mode_recycle_bin_after_purge', $id, $post->post_type );
862 + do_action( 'openstation_recycle_bin_after_purge', $id, $post->post_type );
911 863
912 864 return true;
913 865 }
914 866
@@ -914,51 +866,45 @@
914 866
915 867 /**
916 868 * Permanently delete a single trashed comment.
917 869 *
918 - * @since 0.6.0
919 - *
920 870 * @param int $comment_id Comment id.
921 871 * @return true|WP_Error
922 872 */
923 -function desktop_mode_recycle_bin_purge_comment( $comment_id ) {
873 +function openstation_recycle_bin_purge_comment( $comment_id ) {
924 874 $comment_id = (int) $comment_id;
925 875 $comment = get_comment( $comment_id );
926 876
927 877 if ( ! $comment ) {
928 - return new WP_Error( 'desktop_mode_recycle_bin_not_found', __( 'Comment not found.', 'desktop-mode' ), array( 'status' => 404 ) );
878 + return new WP_Error( 'openstation_recycle_bin_not_found', __( 'Comment not found.', 'desktop-mode' ), array( 'status' => 404 ) );
929 879 }
930 880 if ( 'trash' !== $comment->comment_approved ) {
931 - return new WP_Error( 'desktop_mode_recycle_bin_not_trashed', __( 'Comment is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
881 + return new WP_Error( 'openstation_recycle_bin_not_trashed', __( 'Comment is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
932 882 }
933 - if ( ! desktop_mode_recycle_bin_user_can_purge_comment( $comment ) ) {
934 - return new WP_Error( 'desktop_mode_recycle_bin_forbidden', __( 'You are not allowed to permanently delete this comment.', 'desktop-mode' ), array( 'status' => 403 ) );
883 + if ( ! openstation_recycle_bin_user_can_purge_comment( $comment ) ) {
884 + return new WP_Error( 'openstation_recycle_bin_forbidden', __( 'You are not allowed to permanently delete this comment.', 'desktop-mode' ), array( 'status' => 403 ) );
935 885 }
936 886
937 887 /**
938 888 * Fires before a comment is permanently deleted via the bin.
939 889 *
940 - * @since 0.6.0
941 - *
942 890 * @param int $comment_id Comment id.
943 891 * @param WP_Comment $comment Trashed comment.
944 892 */
945 - do_action( 'desktop_mode_recycle_bin_before_purge_comment', $comment_id, $comment );
893 + do_action( 'openstation_recycle_bin_before_purge_comment', $comment_id, $comment );
946 894
947 895 $result = wp_delete_comment( $comment_id, true );
948 896
949 897 if ( ! $result ) {
950 - return new WP_Error( 'desktop_mode_recycle_bin_purge_failed', __( 'Failed to permanently delete comment.', 'desktop-mode' ), array( 'status' => 500 ) );
898 + return new WP_Error( 'openstation_recycle_bin_purge_failed', __( 'Failed to permanently delete comment.', 'desktop-mode' ), array( 'status' => 500 ) );
951 899 }
952 900
953 901 /**
954 902 * Fires after a comment is permanently deleted via the bin.
955 903 *
956 - * @since 0.6.0
957 - *
958 904 * @param int $comment_id Comment id.
959 905 */
960 - do_action( 'desktop_mode_recycle_bin_after_purge_comment', $comment_id );
906 + do_action( 'openstation_recycle_bin_after_purge_comment', $comment_id );
961 907
962 908 return true;
963 909 }
964 910
@@ -971,13 +917,11 @@
971 917 * Processes at most one chunk per call. The cap protects against PHP
972 918 * timeouts on large bins; the client iterates while `remaining > 0`
973 919 * (and bails when `remaining === skipped`, i.e. nothing the user can
974 920 * purge is left). Site owners with longer execution budgets can tune
975 - * the chunk size via the `desktop_mode_recycle_bin_empty_chunk_size`
921 + * the chunk size via the `openstation_recycle_bin_empty_chunk_size`
976 922 * filter.
977 923 *
978 - * @since 0.6.0
979 - *
980 924 * @return array {
981 925 * @type int $purged Items successfully purged in this call.
982 926 * @type int $skipped Items skipped (capability or error).
983 927 * @type int $remaining Items still in the bin after this call (across pages).
@@ -982,9 +926,9 @@
982 926 * @type int $skipped Items skipped (capability or error).
983 927 * @type int $remaining Items still in the bin after this call (across pages).
984 928 * }
985 929 */
986 -function desktop_mode_recycle_bin_empty() {
930 +function openstation_recycle_bin_empty() {
987 931 $purged = 0;
988 932 $skipped = 0;
989 933
990 934 /**
@@ -989,19 +933,17 @@
989 933
990 934 /**
991 935 * Filter the per-call chunk size for the empty-bin loop.
992 936 *
993 - * `desktop_mode_recycle_bin_empty()` only purges this many items
937 + * `openstation_recycle_bin_empty()` only purges this many items
994 938 * per invocation. The client iterates while `remaining > 0`. The
995 939 * default (200) is conservative for shared hosts; sites with
996 940 * generous PHP execution limits can raise it to make emptying a
997 941 * large bin take fewer roundtrips.
998 942 *
999 - * @since 0.8.0
1000 - *
1001 943 * @param int $chunk_size Items processed per call. Default 200.
1002 944 */
1003 - $chunk_size = (int) apply_filters( 'desktop_mode_recycle_bin_empty_chunk_size', 200 );
945 + $chunk_size = (int) apply_filters( 'openstation_recycle_bin_empty_chunk_size', 200 );
1004 946 if ( $chunk_size < 1 ) {
1005 947 $chunk_size = 1;
1006 948 }
1007 949
@@ -1008,11 +950,16 @@
1008 950 // Loop in chunks — `wp_delete_post()` is cheap individually but
1009 951 // hammering it on a 10k-item bin without yielding back to PHP can
1010 952 // still time out. The client re-invokes us until `remaining` hits
1011 953 // zero (or stalls at `skipped`).
1012 - $batch = desktop_mode_recycle_bin_get_items( array( 'per_page' => $chunk_size, 'page' => 1 ) );
954 + $batch = openstation_recycle_bin_get_items(
955 + array(
956 + 'per_page' => $chunk_size,
957 + 'page' => 1,
958 + )
959 + );
1013 960 foreach ( $batch['items'] as $item ) {
1014 - $result = desktop_mode_recycle_bin_purge(
961 + $result = openstation_recycle_bin_purge(
1015 962 (int) $item['id'],
1016 963 (string) ( $item['type'] ?? '' )
1017 964 );
1018 965 if ( is_wp_error( $result ) ) {
@@ -1024,14 +971,12 @@
1024 971
1025 972 /**
1026 973 * Fires after the recycle bin is emptied.
1027 974 *
1028 - * @since 0.6.0
1029 - *
1030 975 * @param int $purged Items successfully purged in this call.
1031 976 * @param int $skipped Items skipped (capability or error).
1032 977 */
1033 - do_action( 'desktop_mode_recycle_bin_emptied', $purged, $skipped );
978 + do_action( 'openstation_recycle_bin_emptied', $purged, $skipped );
1034 979
1035 980 return array(
1036 981 'purged' => $purged,
1037 982 'skipped' => $skipped,