PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.2
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.2
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / includes / recycle-bin / rest.php

rest.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.9.2, at includes/recycle-bin/rest.php

307 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Desktop Mode — Recycle Bin: REST routes.
4 *
5 * Three routes, all under `/desktop-mode/v1/recycle-bin`:
6 *
7 * GET / — list trashed items
8 * POST /restore — body { ids: int[] }
9 * POST /purge — body { ids: int[] }
10 * POST /empty — empties the visible bin
11 *
12 * Permission gating delegates to `desktop_mode_recycle_bin_user_can_*` per item;
13 * the route-level callback only checks "are you logged in and in
14 * desktop mode at all?" via `desktop_mode_is_enabled()`.
15 *
16 * @package WPDesktopMode
17 * @since 0.19.0
18 */
19
20 defined( 'ABSPATH' ) || exit;
21
22 /**
23 * Register REST routes.
24 *
25 * @since 0.19.0
26 */
27 function desktop_mode_recycle_bin_register_rest_routes() {
28 $namespace = 'desktop-mode/v1';
29
30 register_rest_route(
31 $namespace,
32 '/recycle-bin',
33 array(
34 'methods' => WP_REST_Server::READABLE,
35 'permission_callback' => 'desktop_mode_recycle_bin_rest_permission',
36 'callback' => 'desktop_mode_recycle_bin_rest_list',
37 'args' => array(
38 'page' => array(
39 'type' => 'integer',
40 'default' => 1,
41 'sanitize_callback' => 'absint',
42 ),
43 'per_page' => array(
44 'type' => 'integer',
45 'default' => 100,
46 'sanitize_callback' => 'absint',
47 ),
48 'type' => array(
49 'type' => 'string',
50 'default' => '',
51 'sanitize_callback' => 'sanitize_key',
52 ),
53 'search' => array(
54 'type' => 'string',
55 'default' => '',
56 'sanitize_callback' => 'sanitize_text_field',
57 ),
58 ),
59 )
60 );
61
62 register_rest_route(
63 $namespace,
64 '/recycle-bin/restore',
65 array(
66 'methods' => WP_REST_Server::CREATABLE,
67 'permission_callback' => 'desktop_mode_recycle_bin_rest_permission',
68 'callback' => 'desktop_mode_recycle_bin_rest_restore',
69 // Accepts either `items: [{id, type}]` (preferred) or
70 // `ids: int[]` (legacy — assumes post-ish entities).
71 // Both registered as optional; the handler validates.
72 'args' => array(
73 'items' => array(
74 'type' => 'array',
75 'required' => false,
76 ),
77 'ids' => array(
78 'type' => 'array',
79 'required' => false,
80 'items' => array( 'type' => 'integer' ),
81 ),
82 ),
83 )
84 );
85
86 register_rest_route(
87 $namespace,
88 '/recycle-bin/purge',
89 array(
90 'methods' => WP_REST_Server::CREATABLE,
91 'permission_callback' => 'desktop_mode_recycle_bin_rest_permission',
92 'callback' => 'desktop_mode_recycle_bin_rest_purge',
93 'args' => array(
94 'items' => array(
95 'type' => 'array',
96 'required' => false,
97 ),
98 'ids' => array(
99 'type' => 'array',
100 'required' => false,
101 'items' => array( 'type' => 'integer' ),
102 ),
103 ),
104 )
105 );
106
107 register_rest_route(
108 $namespace,
109 '/recycle-bin/empty',
110 array(
111 'methods' => WP_REST_Server::CREATABLE,
112 'permission_callback' => 'desktop_mode_recycle_bin_rest_permission',
113 'callback' => 'desktop_mode_recycle_bin_rest_empty',
114 )
115 );
116
117 register_rest_route(
118 $namespace,
119 '/recycle-bin/count',
120 array(
121 'methods' => WP_REST_Server::READABLE,
122 'permission_callback' => 'desktop_mode_recycle_bin_rest_permission',
123 'callback' => static function () {
124 return rest_ensure_response(
125 array( 'count' => desktop_mode_recycle_bin_count() )
126 );
127 },
128 )
129 );
130 }
131 add_action( 'rest_api_init', 'desktop_mode_recycle_bin_register_rest_routes' );
132
133 /**
134 * Route-level permission gate.
135 *
136 * Per-item capability checks happen inside the store layer; here we
137 * only enforce "logged-in user with desktop mode opted in" — same
138 * baseline as every other desktop REST surface.
139 *
140 * @since 0.19.0
141 *
142 * @return bool|WP_Error
143 */
144 function desktop_mode_recycle_bin_rest_permission() {
145 if ( ! is_user_logged_in() ) {
146 return new WP_Error(
147 'rest_forbidden',
148 __( 'Sorry, you must be logged in.', 'desktop-mode' ),
149 array( 'status' => 401 )
150 );
151 }
152 if ( function_exists( 'desktop_mode_is_enabled' ) && ! desktop_mode_is_enabled() ) {
153 return new WP_Error(
154 'rest_forbidden',
155 __( 'Desktop mode is not enabled for this user.', 'desktop-mode' ),
156 array( 'status' => 403 )
157 );
158 }
159 return true;
160 }
161
162 /**
163 * GET /recycle-bin
164 *
165 * @since 0.19.0
166 *
167 * @param WP_REST_Request $request REST request.
168 * @return WP_REST_Response
169 */
170 function desktop_mode_recycle_bin_rest_list( $request ) {
171 $payload = desktop_mode_recycle_bin_get_items(
172 array(
173 'page' => (int) $request->get_param( 'page' ),
174 'per_page' => (int) $request->get_param( 'per_page' ),
175 'type' => (string) $request->get_param( 'type' ),
176 'search' => (string) $request->get_param( 'search' ),
177 )
178 );
179
180 return rest_ensure_response( $payload );
181 }
182
183 /**
184 * POST /recycle-bin/restore
185 *
186 * @since 0.19.0
187 *
188 * @param WP_REST_Request $request REST request.
189 * @return WP_REST_Response
190 */
191 function desktop_mode_recycle_bin_rest_restore( $request ) {
192 $items = desktop_mode_recycle_bin_normalize_items( $request );
193 return rest_ensure_response( desktop_mode_recycle_bin_apply_bulk( $items, 'desktop_mode_recycle_bin_restore' ) );
194 }
195
196 /**
197 * POST /recycle-bin/purge
198 *
199 * @since 0.19.0
200 *
201 * @param WP_REST_Request $request REST request.
202 * @return WP_REST_Response
203 */
204 function desktop_mode_recycle_bin_rest_purge( $request ) {
205 $items = desktop_mode_recycle_bin_normalize_items( $request );
206 return rest_ensure_response( desktop_mode_recycle_bin_apply_bulk( $items, 'desktop_mode_recycle_bin_purge' ) );
207 }
208
209 /**
210 * Read either `items: [{id, type}]` (preferred) or `ids: int[]`
211 * (legacy) and normalise into a list of `[id, type]` pairs the
212 * bulk runner can hand straight to a typed callback.
213 *
214 * @since 0.21.0
215 *
216 * @param WP_REST_Request $request REST request.
217 * @return array<int, array{id:int, type:string}>
218 */
219 function desktop_mode_recycle_bin_normalize_items( $request ) {
220 $out = array();
221
222 $items = $request->get_param( 'items' );
223 if ( is_array( $items ) ) {
224 foreach ( $items as $entry ) {
225 if ( ! is_array( $entry ) ) {
226 continue;
227 }
228 $id = isset( $entry['id'] ) ? (int) $entry['id'] : 0;
229 $type = isset( $entry['type'] ) ? sanitize_key( (string) $entry['type'] ) : '';
230 if ( $id <= 0 ) {
231 continue;
232 }
233 $out[] = array(
234 'id' => $id,
235 'type' => $type,
236 );
237 }
238 return $out;
239 }
240
241 $ids = $request->get_param( 'ids' );
242 if ( is_array( $ids ) ) {
243 foreach ( $ids as $id ) {
244 $id = (int) $id;
245 if ( $id > 0 ) {
246 $out[] = array(
247 'id' => $id,
248 'type' => '',
249 );
250 }
251 }
252 }
253 return $out;
254 }
255
256 /**
257 * POST /recycle-bin/empty
258 *
259 * @since 0.19.0
260 *
261 * @return WP_REST_Response
262 */
263 function desktop_mode_recycle_bin_rest_empty() {
264 return rest_ensure_response( desktop_mode_recycle_bin_empty() );
265 }
266
267 /**
268 * Run a per-item callback over a list, capturing per-item results.
269 *
270 * @since 0.19.0
271 * @since 0.21.0 `$items` now carries `{id, type}` pairs; the
272 * callback receives both arguments. Legacy id-only
273 * callers go through `desktop_mode_recycle_bin_normalize_items`,
274 * which fills `type` with `''` (post default).
275 *
276 * @param array<int, array{id:int, type:string}> $items Items to operate on.
277 * @param callable $callback `($id, $type) → true|WP_Error`.
278 * @return array{ok:int[], errors:array<int, array{id:int, code:string, message:string}>}
279 */
280 function desktop_mode_recycle_bin_apply_bulk( $items, $callback ) {
281 $ok = array();
282 $errors = array();
283
284 foreach ( $items as $item ) {
285 $id = isset( $item['id'] ) ? (int) $item['id'] : 0;
286 $type = isset( $item['type'] ) ? (string) $item['type'] : '';
287 if ( $id <= 0 ) {
288 continue;
289 }
290 $result = call_user_func( $callback, $id, $type );
291 if ( is_wp_error( $result ) ) {
292 $errors[] = array(
293 'id' => $id,
294 'code' => $result->get_error_code(),
295 'message' => $result->get_error_message(),
296 );
297 } else {
298 $ok[] = $id;
299 }
300 }
301
302 return array(
303 'ok' => $ok,
304 'errors' => $errors,
305 );
306 }
307