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/recycle-bin/store.php +154 -190 0.9.31.1.10 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 ? 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( wp_strip_all_tags( (string) $comment->comment_content ), 18, '…' );
478 + $subtitle = wp_trim_words( openstation_recycle_bin_plain_text( (string) $comment->comment_content ), 18, '…' );
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,25 +499,42 @@
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 /**
510 + * Collapse a title/subtitle to plain text for the wire.
511 + *
512 + * `get_the_title()` runs the `the_title` filter chain, and
513 + * `wptexturize` in it encodes punctuation as numeric entities
514 + * (apostrophe → `&#8217;`, quotes, dashes) — correct for HTML
515 + * output, wrong for the bin table, which renders every cell via
516 + * `textContent` and would show the literal entity. Strip tags first,
517 + * then decode entities back to characters.
518 + *
519 + * @param string $text Raw filtered text.
520 + * @return string
521 + */
522 +function openstation_recycle_bin_plain_text( $text ) {
523 + return html_entity_decode(
524 + wp_strip_all_tags( (string) $text ),
525 + ENT_QUOTES,
526 + get_bloginfo( 'charset' )
527 + );
528 +}
529 +
530 +/**
549 531 * Shape one WP_Post into the JSON the JS table consumes.
550 532 *
551 - * @since 0.6.0
552 - *
553 533 * @param WP_Post $post Trashed post.
554 534 * @return array
555 535 */
556 -function desktop_mode_recycle_bin_shape_item( $post ) {
536 +function openstation_recycle_bin_shape_item( $post ) {
557 537 $user_id = (int) get_post_meta( $post->ID, '_desktop_mode_trash_user_id', true );
558 538 $deleted_at = (string) get_post_meta( $post->ID, '_desktop_mode_trash_time_gmt', true );
559 539
560 540 // Fall back to post_modified_gmt — set when wp_trash_post runs and
@@ -563,9 +543,9 @@
563 543 $deleted_at = (string) $post->post_modified_gmt;
564 544 }
565 545
566 546 $type = (string) $post->post_type;
567 - $title = (string) get_the_title( $post );
547 + $title = openstation_recycle_bin_plain_text( (string) get_the_title( $post ) );
568 548 $mime = (string) $post->post_mime_type;
569 549 $preview = '';
570 550 $icon = '';
571 551 $subtitle = '';
@@ -577,18 +557,33 @@
577 557 $thumb = wp_get_attachment_image_src( $post->ID, array( 64, 64 ), true );
578 558 if ( is_array( $thumb ) ) {
579 559 $preview = (string) $thumb[0];
580 560 }
581 - $icon = desktop_mode_recycle_bin_icon_for_mime( $mime );
561 + $icon = openstation_recycle_bin_icon_for_mime( $mime );
582 562 $subtitle = $mime;
583 563 } elseif ( 'post' === $type ) {
584 564 $icon = 'dashicons-admin-post';
585 - $subtitle = wp_trim_words( wp_strip_all_tags( (string) $post->post_excerpt ?: (string) $post->post_content ), 18, '…' );
565 + $excerpt = (string) $post->post_excerpt;
566 + $subtitle = wp_trim_words( openstation_recycle_bin_plain_text( $excerpt ? $excerpt : (string) $post->post_content ), 18, '…' );
586 567 } elseif ( 'page' === $type ) {
587 568 $icon = 'dashicons-admin-page';
588 - $subtitle = wp_trim_words( wp_strip_all_tags( (string) $post->post_content ), 18, '…' );
569 + $subtitle = wp_trim_words( openstation_recycle_bin_plain_text( (string) $post->post_content ), 18, '…' );
589 570 } else {
590 - $icon = 'dashicons-media-default';
571 + // Custom post types: reuse the type's own menu Dashicon when it
572 + // registered one, so a trashed product row reads as a product
573 + // instead of a generic file. Content excerpt as the subtitle,
574 + // same as posts.
575 + $icon = 'dashicons-media-default';
576 + $post_type_obj = get_post_type_object( $type );
577 + if (
578 + $post_type_obj
579 + && is_string( $post_type_obj->menu_icon )
580 + && str_starts_with( $post_type_obj->menu_icon, 'dashicons-' )
581 + ) {
582 + $icon = $post_type_obj->menu_icon;
583 + }
584 + $excerpt = (string) $post->post_excerpt;
585 + $subtitle = wp_trim_words( openstation_recycle_bin_plain_text( $excerpt ? $excerpt : (string) $post->post_content ), 18, '…' );
591 586 }
592 587
593 588 $user = $user_id ? get_userdata( $user_id ) : false;
594 589 $user_name = $user ? $user->display_name : '';
@@ -620,10 +615,10 @@
620 615 'icon' => $icon,
621 616 'deleted_at' => $deleted_at,
622 617 'deleted_by' => $user_name,
623 618 'deleted_by_id' => $user_id,
624 - 'can_restore' => desktop_mode_recycle_bin_user_can_restore( $post ),
625 - 'can_purge' => desktop_mode_recycle_bin_user_can_purge( $post ),
619 + 'can_restore' => openstation_recycle_bin_user_can_restore( $post ),
620 + 'can_purge' => openstation_recycle_bin_user_can_purge( $post ),
626 621 'edit_link' => (string) get_edit_post_link( $post->ID, 'raw' ),
627 622 );
628 623
629 624 /**
@@ -632,25 +627,21 @@
632 627 * Add custom columns or override the icon/preview for a custom
633 628 * post type. The id/type/deleted_at trio is load-bearing — keep
634 629 * them in the returned array.
635 630 *
636 - * @since 0.6.0
637 - *
638 631 * @param array $item Item shape.
639 632 * @param WP_Post $post Source post.
640 633 */
641 - return (array) apply_filters( 'desktop_mode_recycle_bin_item', $item, $post );
634 + return (array) apply_filters( 'openstation_recycle_bin_item', $item, $post );
642 635 }
643 636
644 637 /**
645 638 * Map a mime type to a Dashicon for the type cell.
646 639 *
647 - * @since 0.6.0
648 - *
649 640 * @param string $mime Mime type.
650 641 * @return string Dashicon class.
651 642 */
652 -function desktop_mode_recycle_bin_icon_for_mime( $mime ) {
643 +function openstation_recycle_bin_icon_for_mime( $mime ) {
653 644 if ( '' === $mime ) {
654 645 return 'dashicons-media-default';
655 646 }
656 647 if ( str_starts_with( $mime, 'image/' ) ) {
@@ -693,51 +684,46 @@
693 684 * everything else through `wp_untrash_post`. The legacy single-arg
694 685 * call (id only) defaults to `'post'` so older clients that haven't
695 686 * migrated to the typed API keep working.
696 687 *
697 - * @since 0.6.0
698 - * @since 0.6.0 Added `$type` parameter.
699 - *
700 688 * @param int $id Post id (or comment id when `$type === 'comment'`).
701 689 * @param string $type Entity type — '', 'post', 'page', 'attachment', or 'comment'.
702 690 * @return true|WP_Error
703 691 */
704 -function desktop_mode_recycle_bin_restore( $id, $type = '' ) {
692 +function openstation_recycle_bin_restore( $id, $type = '' ) {
705 693 $id = (int) $id;
706 694 if ( 'comment' === $type ) {
707 - return desktop_mode_recycle_bin_restore_comment( $id );
695 + return openstation_recycle_bin_restore_comment( $id );
708 696 }
709 - if ( ( 'placement' === $type || 'shortcut' === $type ) && function_exists( 'desktop_mode_files_restore_placement' ) ) {
710 - return desktop_mode_files_restore_placement( get_current_user_id(), $id );
697 + if ( ( 'placement' === $type || 'shortcut' === $type ) && function_exists( 'openstation_files_restore_placement' ) ) {
698 + return openstation_files_restore_placement( get_current_user_id(), $id );
711 699 }
712 - if ( 'folder' === $type && function_exists( 'desktop_mode_files_restore_folder' ) ) {
713 - return desktop_mode_files_restore_folder( get_current_user_id(), $id );
700 + if ( 'folder' === $type && function_exists( 'openstation_files_restore_folder' ) ) {
701 + return openstation_files_restore_folder( get_current_user_id(), $id );
714 702 }
715 703
716 704 $post = get_post( $id );
717 705 if ( ! $post ) {
718 - return new WP_Error( 'desktop_mode_recycle_bin_not_found', __( 'Item not found.', 'desktop-mode' ), array( 'status' => 404 ) );
706 + return new WP_Error( 'openstation_recycle_bin_not_found', __( 'Item not found.', 'desktop-mode' ), array( 'status' => 404 ) );
719 707 }
720 708 if ( 'trash' !== $post->post_status ) {
721 - return new WP_Error( 'desktop_mode_recycle_bin_not_trashed', __( 'Item is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
709 + return new WP_Error( 'openstation_recycle_bin_not_trashed', __( 'Item is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
722 710 }
723 - if ( ! desktop_mode_recycle_bin_user_can_restore( $post ) ) {
724 - return new WP_Error( 'desktop_mode_recycle_bin_forbidden', __( 'You are not allowed to restore this item.', 'desktop-mode' ), array( 'status' => 403 ) );
711 + if ( ! openstation_recycle_bin_user_can_restore( $post ) ) {
712 + return new WP_Error( 'openstation_recycle_bin_forbidden', __( 'You are not allowed to restore this item.', 'desktop-mode' ), array( 'status' => 403 ) );
725 713 }
726 714
727 715 /**
728 716 * Fires before a recycle-bin item is restored.
729 717 *
730 - * @since 0.6.0
731 - *
732 718 * @param int $id Post id about to be restored.
733 719 * @param WP_Post $post Trashed post object.
734 720 */
735 - do_action( 'desktop_mode_recycle_bin_before_restore', $id, $post );
721 + do_action( 'openstation_recycle_bin_before_restore', $id, $post );
736 722
737 723 $ok = wp_untrash_post( $id );
738 724 if ( ! $ok ) {
739 - return new WP_Error( 'desktop_mode_recycle_bin_restore_failed', __( 'Failed to restore item.', 'desktop-mode' ), array( 'status' => 500 ) );
725 + return new WP_Error( 'openstation_recycle_bin_restore_failed', __( 'Failed to restore item.', 'desktop-mode' ), array( 'status' => 500 ) );
740 726 }
741 727
742 728 delete_post_meta( $id, '_desktop_mode_trash_user_id' );
743 729 delete_post_meta( $id, '_desktop_mode_trash_time_gmt' );
@@ -744,13 +730,11 @@
744 730
745 731 /**
746 732 * Fires after a recycle-bin item is restored.
747 733 *
748 - * @since 0.6.0
749 - *
750 734 * @param int $id Post id that was restored.
751 735 */
752 - do_action( 'desktop_mode_recycle_bin_after_restore', $id );
736 + do_action( 'openstation_recycle_bin_after_restore', $id );
753 737
754 738 return true;
755 739 }
756 740
@@ -756,40 +740,36 @@
756 740
757 741 /**
758 742 * Restore a single trashed comment.
759 743 *
760 - * @since 0.6.0
761 - *
762 744 * @param int $comment_id Comment id.
763 745 * @return true|WP_Error
764 746 */
765 -function desktop_mode_recycle_bin_restore_comment( $comment_id ) {
747 +function openstation_recycle_bin_restore_comment( $comment_id ) {
766 748 $comment_id = (int) $comment_id;
767 749 $comment = get_comment( $comment_id );
768 750
769 751 if ( ! $comment ) {
770 - return new WP_Error( 'desktop_mode_recycle_bin_not_found', __( 'Comment not found.', 'desktop-mode' ), array( 'status' => 404 ) );
752 + return new WP_Error( 'openstation_recycle_bin_not_found', __( 'Comment not found.', 'desktop-mode' ), array( 'status' => 404 ) );
771 753 }
772 754 if ( 'trash' !== $comment->comment_approved ) {
773 - return new WP_Error( 'desktop_mode_recycle_bin_not_trashed', __( 'Comment is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
755 + return new WP_Error( 'openstation_recycle_bin_not_trashed', __( 'Comment is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
774 756 }
775 - if ( ! desktop_mode_recycle_bin_user_can_restore_comment( $comment ) ) {
776 - return new WP_Error( 'desktop_mode_recycle_bin_forbidden', __( 'You are not allowed to restore this comment.', 'desktop-mode' ), array( 'status' => 403 ) );
757 + if ( ! openstation_recycle_bin_user_can_restore_comment( $comment ) ) {
758 + return new WP_Error( 'openstation_recycle_bin_forbidden', __( 'You are not allowed to restore this comment.', 'desktop-mode' ), array( 'status' => 403 ) );
777 759 }
778 760
779 761 /**
780 762 * Fires before a comment is restored from the recycle bin.
781 763 *
782 - * @since 0.6.0
783 - *
784 764 * @param int $comment_id Comment id.
785 765 * @param WP_Comment $comment Trashed comment.
786 766 */
787 - do_action( 'desktop_mode_recycle_bin_before_restore_comment', $comment_id, $comment );
767 + do_action( 'openstation_recycle_bin_before_restore_comment', $comment_id, $comment );
788 768
789 769 $ok = wp_untrash_comment( $comment_id );
790 770 if ( ! $ok ) {
791 - return new WP_Error( 'desktop_mode_recycle_bin_restore_failed', __( 'Failed to restore comment.', 'desktop-mode' ), array( 'status' => 500 ) );
771 + return new WP_Error( 'openstation_recycle_bin_restore_failed', __( 'Failed to restore comment.', 'desktop-mode' ), array( 'status' => 500 ) );
792 772 }
793 773
794 774 delete_comment_meta( $comment_id, '_desktop_mode_trash_user_id' );
795 775 delete_comment_meta( $comment_id, '_desktop_mode_trash_time_gmt' );
@@ -796,13 +776,11 @@
796 776
797 777 /**
798 778 * Fires after a comment is restored from the recycle bin.
799 779 *
800 - * @since 0.6.0
801 - *
802 780 * @param int $comment_id Comment id.
803 781 */
804 - do_action( 'desktop_mode_recycle_bin_after_restore_comment', $comment_id );
782 + do_action( 'openstation_recycle_bin_after_restore_comment', $comment_id );
805 783
806 784 return true;
807 785 }
808 786
@@ -808,47 +786,42 @@
808 786
809 787 /**
810 788 * Permanently delete a single trashed item. Dispatches by `$type`.
811 789 *
812 - * @since 0.6.0
813 - * @since 0.6.0 Added `$type` parameter.
814 - *
815 790 * @param int $id Post id (or comment id when `$type === 'comment'`).
816 791 * @param string $type Entity type — '', 'post', 'page', 'attachment', or 'comment'.
817 792 * @return true|WP_Error
818 793 */
819 -function desktop_mode_recycle_bin_purge( $id, $type = '' ) {
794 +function openstation_recycle_bin_purge( $id, $type = '' ) {
820 795 $id = (int) $id;
821 796 if ( 'comment' === $type ) {
822 - return desktop_mode_recycle_bin_purge_comment( $id );
797 + return openstation_recycle_bin_purge_comment( $id );
823 798 }
824 - if ( ( 'placement' === $type || 'shortcut' === $type ) && function_exists( 'desktop_mode_files_purge_placement' ) ) {
825 - return desktop_mode_files_purge_placement( get_current_user_id(), $id );
799 + if ( ( 'placement' === $type || 'shortcut' === $type ) && function_exists( 'openstation_files_purge_placement' ) ) {
800 + return openstation_files_purge_placement( get_current_user_id(), $id );
826 801 }
827 - if ( 'folder' === $type && function_exists( 'desktop_mode_files_purge_folder' ) ) {
828 - return desktop_mode_files_purge_folder( get_current_user_id(), $id );
802 + if ( 'folder' === $type && function_exists( 'openstation_files_purge_folder' ) ) {
803 + return openstation_files_purge_folder( get_current_user_id(), $id );
829 804 }
830 805
831 806 $post = get_post( $id );
832 807 if ( ! $post ) {
833 - return new WP_Error( 'desktop_mode_recycle_bin_not_found', __( 'Item not found.', 'desktop-mode' ), array( 'status' => 404 ) );
808 + return new WP_Error( 'openstation_recycle_bin_not_found', __( 'Item not found.', 'desktop-mode' ), array( 'status' => 404 ) );
834 809 }
835 810 if ( 'trash' !== $post->post_status ) {
836 - return new WP_Error( 'desktop_mode_recycle_bin_not_trashed', __( 'Item is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
811 + return new WP_Error( 'openstation_recycle_bin_not_trashed', __( 'Item is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
837 812 }
838 - if ( ! desktop_mode_recycle_bin_user_can_purge( $post ) ) {
839 - return new WP_Error( 'desktop_mode_recycle_bin_forbidden', __( 'You are not allowed to permanently delete this item.', 'desktop-mode' ), array( 'status' => 403 ) );
813 + if ( ! openstation_recycle_bin_user_can_purge( $post ) ) {
814 + return new WP_Error( 'openstation_recycle_bin_forbidden', __( 'You are not allowed to permanently delete this item.', 'desktop-mode' ), array( 'status' => 403 ) );
840 815 }
841 816
842 817 /**
843 818 * Fires before a recycle-bin item is permanently deleted.
844 819 *
845 - * @since 0.6.0
846 - *
847 820 * @param int $id Post id about to be deleted.
848 821 * @param WP_Post $post Trashed post object.
849 822 */
850 - do_action( 'desktop_mode_recycle_bin_before_purge', $id, $post );
823 + do_action( 'openstation_recycle_bin_before_purge', $id, $post );
851 824
852 825 if ( 'attachment' === $post->post_type ) {
853 826 // Force-delete (`true`) removes the attachment and its file
854 827 // permanently. (The item is already trashed, so even with
@@ -859,20 +832,18 @@
859 832 $result = wp_delete_post( $id, true );
860 833 }
861 834
862 835 if ( ! $result ) {
863 - return new WP_Error( 'desktop_mode_recycle_bin_purge_failed', __( 'Failed to permanently delete item.', 'desktop-mode' ), array( 'status' => 500 ) );
836 + return new WP_Error( 'openstation_recycle_bin_purge_failed', __( 'Failed to permanently delete item.', 'desktop-mode' ), array( 'status' => 500 ) );
864 837 }
865 838
866 839 /**
867 840 * Fires after a recycle-bin item is permanently deleted.
868 841 *
869 - * @since 0.6.0
870 - *
871 842 * @param int $id Post id that was purged.
872 843 * @param string $type Post type of the purged item.
873 844 */
874 - do_action( 'desktop_mode_recycle_bin_after_purge', $id, $post->post_type );
845 + do_action( 'openstation_recycle_bin_after_purge', $id, $post->post_type );
875 846
876 847 return true;
877 848 }
878 849
@@ -878,51 +849,45 @@
878 849
879 850 /**
880 851 * Permanently delete a single trashed comment.
881 852 *
882 - * @since 0.6.0
883 - *
884 853 * @param int $comment_id Comment id.
885 854 * @return true|WP_Error
886 855 */
887 -function desktop_mode_recycle_bin_purge_comment( $comment_id ) {
856 +function openstation_recycle_bin_purge_comment( $comment_id ) {
888 857 $comment_id = (int) $comment_id;
889 858 $comment = get_comment( $comment_id );
890 859
891 860 if ( ! $comment ) {
892 - return new WP_Error( 'desktop_mode_recycle_bin_not_found', __( 'Comment not found.', 'desktop-mode' ), array( 'status' => 404 ) );
861 + return new WP_Error( 'openstation_recycle_bin_not_found', __( 'Comment not found.', 'desktop-mode' ), array( 'status' => 404 ) );
893 862 }
894 863 if ( 'trash' !== $comment->comment_approved ) {
895 - return new WP_Error( 'desktop_mode_recycle_bin_not_trashed', __( 'Comment is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
864 + return new WP_Error( 'openstation_recycle_bin_not_trashed', __( 'Comment is not in the trash.', 'desktop-mode' ), array( 'status' => 409 ) );
896 865 }
897 - if ( ! desktop_mode_recycle_bin_user_can_purge_comment( $comment ) ) {
898 - return new WP_Error( 'desktop_mode_recycle_bin_forbidden', __( 'You are not allowed to permanently delete this comment.', 'desktop-mode' ), array( 'status' => 403 ) );
866 + if ( ! openstation_recycle_bin_user_can_purge_comment( $comment ) ) {
867 + return new WP_Error( 'openstation_recycle_bin_forbidden', __( 'You are not allowed to permanently delete this comment.', 'desktop-mode' ), array( 'status' => 403 ) );
899 868 }
900 869
901 870 /**
902 871 * Fires before a comment is permanently deleted via the bin.
903 872 *
904 - * @since 0.6.0
905 - *
906 873 * @param int $comment_id Comment id.
907 874 * @param WP_Comment $comment Trashed comment.
908 875 */
909 - do_action( 'desktop_mode_recycle_bin_before_purge_comment', $comment_id, $comment );
876 + do_action( 'openstation_recycle_bin_before_purge_comment', $comment_id, $comment );
910 877
911 878 $result = wp_delete_comment( $comment_id, true );
912 879
913 880 if ( ! $result ) {
914 - return new WP_Error( 'desktop_mode_recycle_bin_purge_failed', __( 'Failed to permanently delete comment.', 'desktop-mode' ), array( 'status' => 500 ) );
881 + return new WP_Error( 'openstation_recycle_bin_purge_failed', __( 'Failed to permanently delete comment.', 'desktop-mode' ), array( 'status' => 500 ) );
915 882 }
916 883
917 884 /**
918 885 * Fires after a comment is permanently deleted via the bin.
919 886 *
920 - * @since 0.6.0
921 - *
922 887 * @param int $comment_id Comment id.
923 888 */
924 - do_action( 'desktop_mode_recycle_bin_after_purge_comment', $comment_id );
889 + do_action( 'openstation_recycle_bin_after_purge_comment', $comment_id );
925 890
926 891 return true;
927 892 }
928 893
@@ -935,13 +900,11 @@
935 900 * Processes at most one chunk per call. The cap protects against PHP
936 901 * timeouts on large bins; the client iterates while `remaining > 0`
937 902 * (and bails when `remaining === skipped`, i.e. nothing the user can
938 903 * purge is left). Site owners with longer execution budgets can tune
939 - * the chunk size via the `desktop_mode_recycle_bin_empty_chunk_size`
904 + * the chunk size via the `openstation_recycle_bin_empty_chunk_size`
940 905 * filter.
941 906 *
942 - * @since 0.6.0
943 - *
944 907 * @return array {
945 908 * @type int $purged Items successfully purged in this call.
946 909 * @type int $skipped Items skipped (capability or error).
947 910 * @type int $remaining Items still in the bin after this call (across pages).
@@ -946,9 +909,9 @@
946 909 * @type int $skipped Items skipped (capability or error).
947 910 * @type int $remaining Items still in the bin after this call (across pages).
948 911 * }
949 912 */
950 -function desktop_mode_recycle_bin_empty() {
913 +function openstation_recycle_bin_empty() {
951 914 $purged = 0;
952 915 $skipped = 0;
953 916
954 917 /**
@@ -953,19 +916,17 @@
953 916
954 917 /**
955 918 * Filter the per-call chunk size for the empty-bin loop.
956 919 *
957 - * `desktop_mode_recycle_bin_empty()` only purges this many items
920 + * `openstation_recycle_bin_empty()` only purges this many items
958 921 * per invocation. The client iterates while `remaining > 0`. The
959 922 * default (200) is conservative for shared hosts; sites with
960 923 * generous PHP execution limits can raise it to make emptying a
961 924 * large bin take fewer roundtrips.
962 925 *
963 - * @since 0.8.0
964 - *
965 926 * @param int $chunk_size Items processed per call. Default 200.
966 927 */
967 - $chunk_size = (int) apply_filters( 'desktop_mode_recycle_bin_empty_chunk_size', 200 );
928 + $chunk_size = (int) apply_filters( 'openstation_recycle_bin_empty_chunk_size', 200 );
968 929 if ( $chunk_size < 1 ) {
969 930 $chunk_size = 1;
970 931 }
971 932
@@ -972,11 +933,16 @@
972 933 // Loop in chunks — `wp_delete_post()` is cheap individually but
973 934 // hammering it on a 10k-item bin without yielding back to PHP can
974 935 // still time out. The client re-invokes us until `remaining` hits
975 936 // zero (or stalls at `skipped`).
976 - $batch = desktop_mode_recycle_bin_get_items( array( 'per_page' => $chunk_size, 'page' => 1 ) );
937 + $batch = openstation_recycle_bin_get_items(
938 + array(
939 + 'per_page' => $chunk_size,
940 + 'page' => 1,
941 + )
942 + );
977 943 foreach ( $batch['items'] as $item ) {
978 - $result = desktop_mode_recycle_bin_purge(
944 + $result = openstation_recycle_bin_purge(
979 945 (int) $item['id'],
980 946 (string) ( $item['type'] ?? '' )
981 947 );
982 948 if ( is_wp_error( $result ) ) {
@@ -988,14 +954,12 @@
988 954
989 955 /**
990 956 * Fires after the recycle bin is emptied.
991 957 *
992 - * @since 0.6.0
993 - *
994 958 * @param int $purged Items successfully purged in this call.
995 959 * @param int $skipped Items skipped (capability or error).
996 960 */
997 - do_action( 'desktop_mode_recycle_bin_emptied', $purged, $skipped );
961 + do_action( 'openstation_recycle_bin_emptied', $purged, $skipped );
998 962
999 963 return array(
1000 964 'purged' => $purged,
1001 965 'skipped' => $skipped,