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/desktop-files/file-shares.php +179 -189 0.9.7 → 1.1.12 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — single-file sharing (`target_type='file'`).
3 + * OpenStation — single-file sharing (`target_type='file'`).
4 4 *
5 5 * Shares one stored upload with specific users. Reuses the folder-
6 6 * sharing tables via the `target_type` column the schema shipped
7 7 * for exactly this (the `folder_id` column carries the STORED-FILE
@@ -19,10 +19,9 @@
19 19 * → accept (placement planted at the recipient's desktop root) /
20 20 * deny / leave / revoke, every removal scrubbing the recipient's
21 21 * placement.
22 22 *
23 - * @package WPDesktopMode
24 - * @since 0.9.6
23 + * @package OpenStation
25 24 */
26 25
27 26 defined( 'ABSPATH' ) || exit;
28 27
@@ -29,41 +28,35 @@
29 28 /**
30 29 * Whether `$user_id` may manage a stored file's shares. Owner-only
31 30 * by default, filterable like the folder equivalent.
32 31 *
33 - * @since 0.9.6
34 - *
35 32 * @param int $file_id Stored-file id.
36 33 * @param int $user_id Viewer.
37 34 * @return bool
38 35 */
39 -function desktop_mode_stored_files_share_can_manage( $file_id, $user_id ) {
40 - $file = desktop_mode_stored_files_get( (int) $file_id );
36 +function openstation_stored_files_share_can_manage( $file_id, $user_id ) {
37 + $file = openstation_stored_files_get( (int) $file_id );
41 38 $can = $file && (int) $file['owner_id'] === (int) $user_id;
42 39 /**
43 40 * Filter who can manage a stored file's shares.
44 41 *
45 - * @since 0.9.6
46 - *
47 42 * @param bool $can Default: owner-only.
48 43 * @param int $file_id Stored-file id.
49 44 * @param int $user_id Viewer.
50 45 * @param array|null $file Stored-file row (null when missing).
51 46 */
52 - return (bool) apply_filters( 'desktop_mode_stored_files_share_can_manage', $can, (int) $file_id, (int) $user_id, $file );
47 + return (bool) apply_filters( 'openstation_stored_files_share_can_manage', $can, (int) $file_id, (int) $user_id, $file );
53 48 }
54 49
55 50 /**
56 51 * All share rows for one stored file (owner-internal view).
57 52 *
58 - * @since 0.9.6
59 - *
60 53 * @param int $file_id Stored-file id.
61 54 * @return array[]
62 55 */
63 -function desktop_mode_stored_files_get_file_shares( $file_id ) {
56 +function openstation_stored_files_get_file_shares( $file_id ) {
64 57 global $wpdb;
65 - $tables = desktop_mode_files_table_names();
58 + $tables = openstation_files_table_names();
66 59 $rows = $wpdb->get_results(
67 60 $wpdb->prepare(
68 61 "SELECT * FROM {$tables['shares']} WHERE target_type = 'file' AND folder_id = %d ORDER BY invited_at_ms ASC, id ASC",
69 62 (int) $file_id
@@ -69,11 +62,11 @@
69 62 (int) $file_id
70 63 ),
71 64 ARRAY_A
72 65 );
73 - $out = array();
66 + $out = array();
74 67 foreach ( (array) $rows as $row ) {
75 - $out[] = desktop_mode_files_normalize_share_row( $row );
68 + $out[] = openstation_files_normalize_share_row( $row );
76 69 }
77 70 return $out;
78 71 }
79 72
@@ -80,17 +73,15 @@
80 73 /**
81 74 * The viewer's state on a stored file: 'none' when no share row
82 75 * targets them, else the row's state.
83 76 *
84 - * @since 0.9.6
85 - *
86 77 * @param int $file_id Stored-file id.
87 78 * @param int $user_id Viewer.
88 79 * @return string 'none' | 'pending' | 'accepted' | 'denied'
89 80 */
90 -function desktop_mode_stored_file_share_state( $file_id, $user_id ) {
81 +function openstation_stored_file_share_state( $file_id, $user_id ) {
91 82 global $wpdb;
92 - $tables = desktop_mode_files_table_names();
83 + $tables = openstation_files_table_names();
93 84 $state = $wpdb->get_var(
94 85 $wpdb->prepare(
95 86 "SELECT state FROM {$tables['shares']}
96 87 WHERE target_type = 'file' AND folder_id = %d
@@ -104,44 +95,42 @@
104 95
105 96 /**
106 97 * Invite a user to a stored file. Capability is always `read`.
107 98 *
108 - * @since 0.9.6
109 - *
110 99 * @param int $file_id Stored-file id.
111 100 * @param int $actor_id Actor (must manage the file's shares).
112 101 * @param int $recipient_user_id Recipient.
113 102 * @return int|WP_Error Share id.
114 103 */
115 -function desktop_mode_stored_file_share_invite( $file_id, $actor_id, $recipient_user_id ) {
104 +function openstation_stored_file_share_invite( $file_id, $actor_id, $recipient_user_id ) {
116 105 global $wpdb;
117 106 $file_id = (int) $file_id;
118 107 $actor_id = (int) $actor_id;
119 108 $uid = (int) $recipient_user_id;
120 109
121 - $file = desktop_mode_stored_files_get( $file_id );
110 + $file = openstation_stored_files_get( $file_id );
122 111 if ( ! $file ) {
123 - return new WP_Error( 'desktop_mode_stored_files_not_found', __( 'Stored file not found.', 'desktop-mode' ), array( 'status' => 404 ) );
112 + return new WP_Error( 'openstation_stored_files_not_found', __( 'Stored file not found.', 'desktop-mode' ), array( 'status' => 404 ) );
124 113 }
125 - if ( ! desktop_mode_stored_files_share_can_manage( $file_id, $actor_id ) ) {
126 - return new WP_Error( 'desktop_mode_files_forbidden', __( 'You cannot manage shares for this file.', 'desktop-mode' ), array( 'status' => 403 ) );
114 + if ( ! openstation_stored_files_share_can_manage( $file_id, $actor_id ) ) {
115 + return new WP_Error( 'openstation_files_forbidden', __( 'You cannot manage shares for this file.', 'desktop-mode' ), array( 'status' => 403 ) );
127 116 }
128 117 if ( $uid <= 0 ) {
129 - return new WP_Error( 'desktop_mode_files_invalid_user', __( 'Invalid user id.', 'desktop-mode' ), array( 'status' => 400 ) );
118 + return new WP_Error( 'openstation_files_invalid_user', __( 'Invalid user id.', 'desktop-mode' ), array( 'status' => 400 ) );
130 119 }
131 120 if ( $uid === (int) $file['owner_id'] ) {
132 - return new WP_Error( 'desktop_mode_files_share_owner', __( 'You cannot share with the file owner.', 'desktop-mode' ), array( 'status' => 400 ) );
121 + return new WP_Error( 'openstation_files_share_owner', __( 'You cannot share with the file owner.', 'desktop-mode' ), array( 'status' => 400 ) );
133 122 }
134 123 $user = get_userdata( $uid );
135 124 if ( ! $user ) {
136 - return new WP_Error( 'desktop_mode_files_unknown_user', __( 'Unknown user.', 'desktop-mode' ), array( 'status' => 404 ) );
125 + return new WP_Error( 'openstation_files_unknown_user', __( 'Unknown user.', 'desktop-mode' ), array( 'status' => 404 ) );
137 126 }
138 127 if ( ! user_can( $user, 'edit_posts' ) ) {
139 - return new WP_Error( 'desktop_mode_files_ineligible_principal', __( 'This user is not eligible.', 'desktop-mode' ), array( 'status' => 400 ) );
128 + return new WP_Error( 'openstation_files_ineligible_principal', __( 'This user is not eligible.', 'desktop-mode' ), array( 'status' => 400 ) );
140 129 }
141 130
142 - $tables = desktop_mode_files_table_names();
143 - $now = desktop_mode_files_now_ms();
131 + $tables = openstation_files_table_names();
132 + $now = openstation_files_now_ms();
144 133
145 134 // Idempotent invite, mirroring the folder rules: denied →
146 135 // pending again; pending/accepted keep their state. Capability
147 136 // stays 'read' unconditionally.
@@ -185,17 +174,17 @@
185 174 ),
186 175 array( '%s', '%d', '%s', '%s', '%s', '%s', '%d', '%d' )
187 176 );
188 177 if ( false === $ok ) {
189 - return new WP_Error( 'desktop_mode_files_share_insert_failed', __( 'Failed to record share.', 'desktop-mode' ), array( 'status' => 500 ) );
178 + return new WP_Error( 'openstation_files_share_insert_failed', __( 'Failed to record share.', 'desktop-mode' ), array( 'status' => 500 ) );
190 179 }
191 180 $id = (int) $wpdb->insert_id;
192 181 }
193 182
194 - $row = desktop_mode_files_get_share( $id );
183 + $row = openstation_files_get_share( $id );
195 184
196 185 /** This action is documented in includes/desktop-files/shares-store.php */
197 - do_action( 'desktop_mode_files_share_invited', $id, $row, $actor_id );
186 + do_action( 'openstation_files_share_invited', $id, $row, $actor_id );
198 187
199 188 return $id;
200 189 }
201 190
@@ -202,38 +191,42 @@
202 191 /**
203 192 * Recipient accepts a file share. Plants an `upload` placement at
204 193 * their desktop root.
205 194 *
206 - * @since 0.9.6
207 - *
208 195 * @param int $share_id Share id.
209 196 * @param int $user_id Recipient.
210 197 * @return array|WP_Error Updated share row.
211 198 */
212 -function desktop_mode_stored_file_share_accept( $share_id, $user_id ) {
199 +function openstation_stored_file_share_accept( $share_id, $user_id ) {
213 200 global $wpdb;
214 201 $share_id = (int) $share_id;
215 202 $user_id = (int) $user_id;
216 - $row = desktop_mode_files_get_share( $share_id );
203 + $row = openstation_files_get_share( $share_id );
217 204 if ( ! $row || 'file' !== $row['target_type'] ) {
218 - return new WP_Error( 'desktop_mode_files_share_not_found', __( 'Share not found.', 'desktop-mode' ), array( 'status' => 404 ) );
205 + return new WP_Error( 'openstation_files_share_not_found', __( 'Share not found.', 'desktop-mode' ), array( 'status' => 404 ) );
219 206 }
220 207 if ( 'user' !== $row['principal_type'] || (int) $row['principal_ref'] !== $user_id ) {
221 - return new WP_Error( 'desktop_mode_files_share_not_recipient', __( 'This invite is not for you.', 'desktop-mode' ), array( 'status' => 403 ) );
208 + return new WP_Error( 'openstation_files_share_not_recipient', __( 'This invite is not for you.', 'desktop-mode' ), array( 'status' => 403 ) );
222 209 }
223 210 if ( 'accepted' === $row['state'] ) {
224 - return $row;
211 + // A previous attempt may have accepted the invite but failed to plant
212 + // its tile (for example, a busy upload lock). Allow that step to retry.
213 + $tables = openstation_files_table_names();
214 + $placed = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$tables['placements']} WHERE owner_id = %d AND file_type = 'upload' AND file_ref = %s LIMIT 1", $user_id, (string) $row['folder_id'] ) );
215 + if ( $placed ) {
216 + return $row;
217 + }
225 218 }
226 219 if ( 'denied' === $row['state'] ) {
227 - return new WP_Error( 'desktop_mode_files_share_already_denied', __( 'This invite was denied.', 'desktop-mode' ), array( 'status' => 410 ) );
220 + return new WP_Error( 'openstation_files_share_already_denied', __( 'This invite was denied.', 'desktop-mode' ), array( 'status' => 410 ) );
228 221 }
229 222
230 - $tables = desktop_mode_files_table_names();
223 + $tables = openstation_files_table_names();
231 224 $wpdb->update(
232 225 $tables['shares'],
233 226 array(
234 227 'state' => 'accepted',
235 - 'decided_at_ms' => desktop_mode_files_now_ms(),
228 + 'decided_at_ms' => openstation_files_now_ms(),
236 229 ),
237 230 array( 'id' => $share_id ),
238 231 array( '%s', '%d' ),
239 232 array( '%d' )
@@ -242,15 +235,18 @@
242 235 // Plant the tile — AFTER the state flip so the placement's
243 236 // `can_read` gate sees the accepted share.
244 237 $file_id = (int) $row['folder_id'];
245 238 /** This filter is documented in includes/desktop-files/shares-store.php */
246 - $parent_id = (int) apply_filters( 'desktop_mode_folder_share_accept_default_parent', 0, $file_id, $user_id, $row );
247 - desktop_mode_files_place_at_next_free_slot( $user_id, $parent_id, 'upload', (string) $file_id );
239 + $parent_id = (int) apply_filters( 'openstation_folder_share_accept_default_parent', 0, $file_id, $user_id, $row );
240 + $placed = openstation_files_place_at_next_free_slot( $user_id, $parent_id, 'upload', (string) $file_id );
241 + if ( is_wp_error( $placed ) ) {
242 + return $placed;
243 + }
248 244
249 - $next = desktop_mode_files_get_share( $share_id );
245 + $next = openstation_files_get_share( $share_id );
250 246
251 247 /** This action is documented in includes/desktop-files/shares-store.php */
252 - do_action( 'desktop_mode_files_share_accepted', $share_id, $next, $user_id );
248 + do_action( 'openstation_files_share_accepted', $share_id, $next, $user_id );
253 249
254 250 return $next;
255 251 }
256 252
@@ -256,24 +252,22 @@
256 252
257 253 /**
258 254 * Recipient denies a file share.
259 255 *
260 - * @since 0.9.6
261 - *
262 256 * @param int $share_id Share id.
263 257 * @param int $user_id Recipient.
264 258 * @return array|WP_Error Updated share row.
265 259 */
266 -function desktop_mode_stored_file_share_deny( $share_id, $user_id ) {
260 +function openstation_stored_file_share_deny( $share_id, $user_id ) {
267 261 global $wpdb;
268 262 $share_id = (int) $share_id;
269 263 $user_id = (int) $user_id;
270 - $row = desktop_mode_files_get_share( $share_id );
264 + $row = openstation_files_get_share( $share_id );
271 265 if ( ! $row || 'file' !== $row['target_type'] ) {
272 - return new WP_Error( 'desktop_mode_files_share_not_found', __( 'Share not found.', 'desktop-mode' ), array( 'status' => 404 ) );
266 + return new WP_Error( 'openstation_files_share_not_found', __( 'Share not found.', 'desktop-mode' ), array( 'status' => 404 ) );
273 267 }
274 268 if ( 'user' !== $row['principal_type'] || (int) $row['principal_ref'] !== $user_id ) {
275 - return new WP_Error( 'desktop_mode_files_share_not_recipient', __( 'This invite is not for you.', 'desktop-mode' ), array( 'status' => 403 ) );
269 + return new WP_Error( 'openstation_files_share_not_recipient', __( 'This invite is not for you.', 'desktop-mode' ), array( 'status' => 403 ) );
276 270 }
277 271 if ( 'denied' === $row['state'] ) {
278 272 return $row;
279 273 }
@@ -278,14 +272,14 @@
278 272 return $row;
279 273 }
280 274 $was_accepted = 'accepted' === $row['state'];
281 275
282 - $tables = desktop_mode_files_table_names();
276 + $tables = openstation_files_table_names();
283 277 $wpdb->update(
284 278 $tables['shares'],
285 279 array(
286 280 'state' => 'denied',
287 - 'decided_at_ms' => desktop_mode_files_now_ms(),
281 + 'decided_at_ms' => openstation_files_now_ms(),
288 282 ),
289 283 array( 'id' => $share_id ),
290 284 array( '%s', '%d' ),
291 285 array( '%d' )
@@ -290,15 +284,15 @@
290 284 array( '%s', '%d' ),
291 285 array( '%d' )
292 286 );
293 287 if ( $was_accepted ) {
294 - desktop_mode_files_trash_upload_for_user( (int) $row['folder_id'], $user_id );
288 + openstation_files_trash_upload_for_user( (int) $row['folder_id'], $user_id );
295 289 }
296 290
297 - $next = desktop_mode_files_get_share( $share_id );
291 + $next = openstation_files_get_share( $share_id );
298 292
299 293 /** This action is documented in includes/desktop-files/shares-store.php */
300 - do_action( 'desktop_mode_files_share_denied', $share_id, $next, $user_id );
294 + do_action( 'openstation_files_share_denied', $share_id, $next, $user_id );
301 295
302 296 return $next;
303 297 }
304 298
@@ -304,27 +298,25 @@
304 298
305 299 /**
306 300 * Recipient leaves a previously accepted file share.
307 301 *
308 - * @since 0.9.6
309 - *
310 302 * @param int $file_id Stored-file id.
311 303 * @param int $user_id Recipient.
312 304 * @return true|WP_Error
313 305 */
314 -function desktop_mode_stored_file_share_leave( $file_id, $user_id ) {
306 +function openstation_stored_file_share_leave( $file_id, $user_id ) {
315 307 global $wpdb;
316 308 $file_id = (int) $file_id;
317 309 $user_id = (int) $user_id;
318 - $file = desktop_mode_stored_files_get( $file_id );
310 + $file = openstation_stored_files_get( $file_id );
319 311 if ( ! $file ) {
320 - return new WP_Error( 'desktop_mode_files_not_found', __( 'File not found.', 'desktop-mode' ), array( 'status' => 404 ) );
312 + return new WP_Error( 'openstation_files_not_found', __( 'File not found.', 'desktop-mode' ), array( 'status' => 404 ) );
321 313 }
322 314 if ( (int) $file['owner_id'] === $user_id ) {
323 - return new WP_Error( 'desktop_mode_files_owner_cannot_leave', __( 'Owners cannot leave their own file.', 'desktop-mode' ), array( 'status' => 400 ) );
315 + return new WP_Error( 'openstation_files_owner_cannot_leave', __( 'Owners cannot leave their own file.', 'desktop-mode' ), array( 'status' => 400 ) );
324 316 }
325 317
326 - $tables = desktop_mode_files_table_names();
318 + $tables = openstation_files_table_names();
327 319 $row = $wpdb->get_row(
328 320 $wpdb->prepare(
329 321 "SELECT * FROM {$tables['shares']}
330 322 WHERE target_type = 'file' AND folder_id = %d
@@ -336,19 +328,19 @@
336 328 );
337 329
338 330 // Scrub the recipient's tile regardless — lingering placements
339 331 // from a previously revoked share must go too.
340 - desktop_mode_files_trash_upload_for_user( $file_id, $user_id );
332 + openstation_files_trash_upload_for_user( $file_id, $user_id );
341 333
342 334 if ( ! $row ) {
343 - return new WP_Error( 'desktop_mode_files_not_member', __( 'You do not have access to this file.', 'desktop-mode' ), array( 'status' => 404 ) );
335 + return new WP_Error( 'openstation_files_not_member', __( 'You do not have access to this file.', 'desktop-mode' ), array( 'status' => 404 ) );
344 336 }
345 - $normalized = desktop_mode_files_normalize_share_row( $row );
337 + $normalized = openstation_files_normalize_share_row( $row );
346 338 $wpdb->update(
347 339 $tables['shares'],
348 340 array(
349 341 'state' => 'denied',
350 - 'decided_at_ms' => desktop_mode_files_now_ms(),
342 + 'decided_at_ms' => openstation_files_now_ms(),
351 343 ),
352 344 array( 'id' => (int) $row['id'] ),
353 345 array( '%s', '%d' ),
354 346 array( '%d' )
@@ -354,9 +346,9 @@
354 346 array( '%d' )
355 347 );
356 348
357 349 /** This action is documented in includes/desktop-files/shares-store.php */
358 - do_action( 'desktop_mode_files_share_left', (int) $row['id'], $normalized, $user_id );
350 + do_action( 'openstation_files_share_left', (int) $row['id'], $normalized, $user_id );
359 351
360 352 return true;
361 353 }
362 354
@@ -362,36 +354,34 @@
362 354
363 355 /**
364 356 * Owner revokes a file share.
365 357 *
366 - * @since 0.9.6
367 - *
368 358 * @param int $share_id Share id.
369 359 * @param int $actor_id Actor.
370 360 * @return true|WP_Error
371 361 */
372 -function desktop_mode_stored_file_share_revoke( $share_id, $actor_id ) {
362 +function openstation_stored_file_share_revoke( $share_id, $actor_id ) {
373 363 global $wpdb;
374 364 $share_id = (int) $share_id;
375 365 $actor_id = (int) $actor_id;
376 - $row = desktop_mode_files_get_share( $share_id );
366 + $row = openstation_files_get_share( $share_id );
377 367 if ( ! $row || 'file' !== $row['target_type'] ) {
378 - return new WP_Error( 'desktop_mode_files_share_not_found', __( 'Share not found.', 'desktop-mode' ), array( 'status' => 404 ) );
368 + return new WP_Error( 'openstation_files_share_not_found', __( 'Share not found.', 'desktop-mode' ), array( 'status' => 404 ) );
379 369 }
380 - if ( ! desktop_mode_stored_files_share_can_manage( (int) $row['folder_id'], $actor_id ) ) {
381 - return new WP_Error( 'desktop_mode_files_forbidden', __( 'You cannot manage shares for this file.', 'desktop-mode' ), array( 'status' => 403 ) );
370 + if ( ! openstation_stored_files_share_can_manage( (int) $row['folder_id'], $actor_id ) ) {
371 + return new WP_Error( 'openstation_files_forbidden', __( 'You cannot manage shares for this file.', 'desktop-mode' ), array( 'status' => 403 ) );
382 372 }
383 373
384 - $tables = desktop_mode_files_table_names();
374 + $tables = openstation_files_table_names();
385 375 $wpdb->delete( $tables['shares'], array( 'id' => $share_id ), array( '%d' ) );
386 376 $wpdb->delete( $tables['decisions'], array( 'share_id' => $share_id ), array( '%d' ) );
387 377
388 378 if ( 'accepted' === $row['state'] ) {
389 - desktop_mode_files_trash_upload_for_user( (int) $row['folder_id'], (int) $row['principal_ref'] );
379 + openstation_files_trash_upload_for_user( (int) $row['folder_id'], (int) $row['principal_ref'] );
390 380 }
391 381
392 382 /** This action is documented in includes/desktop-files/shares-store.php */
393 - do_action( 'desktop_mode_files_share_revoked', $share_id, $row, $actor_id );
383 + do_action( 'openstation_files_share_revoked', $share_id, $row, $actor_id );
394 384
395 385 return true;
396 386 }
397 387
@@ -402,15 +392,13 @@
402 392 * the normal flow; this administrative scrub bypasses it. No
403 393 * tombstones: soft-trash rides the heartbeat's `trashed_at_ms`
404 394 * channel (same invariant as the folder scrub).
405 395 *
406 - * @since 0.9.6
407 - *
408 396 * @param int $file_id Stored-file id.
409 397 * @param int $user_id Recipient whose placements to scrub.
410 398 * @return int Rows scrubbed.
411 399 */
412 -function desktop_mode_files_trash_upload_for_user( $file_id, $user_id ) {
400 +function openstation_files_trash_upload_for_user( $file_id, $user_id ) {
413 401 global $wpdb;
414 402 $file_id = (int) $file_id;
415 403 $user_id = (int) $user_id;
416 404 if ( $file_id <= 0 || $user_id <= 0 ) {
@@ -415,10 +403,10 @@
415 403 $user_id = (int) $user_id;
416 404 if ( $file_id <= 0 || $user_id <= 0 ) {
417 405 return 0;
418 406 }
419 - $tables = desktop_mode_files_table_names();
420 - $now = desktop_mode_files_now_ms();
407 + $tables = openstation_files_table_names();
408 + $now = openstation_files_now_ms();
421 409 $rows = $wpdb->get_results(
422 410 $wpdb->prepare(
423 411 "SELECT id FROM {$tables['placements']}
424 412 WHERE owner_id = %d
@@ -429,9 +417,9 @@
429 417 (string) $file_id
430 418 ),
431 419 ARRAY_A
432 420 );
433 - $count = 0;
421 + $count = 0;
434 422 foreach ( (array) $rows as $row ) {
435 423 $wpdb->update(
436 424 $tables['placements'],
437 425 array(
@@ -441,9 +429,9 @@
441 429 array( 'id' => (int) $row['id'] ),
442 430 array( '%d', '%d' ),
443 431 array( '%d' )
444 432 );
445 - $count++;
433 + ++$count;
446 434 }
447 435 return $count;
448 436 }
449 437
@@ -450,21 +438,19 @@
450 438 /**
451 439 * Pending file-share invites for a user (heartbeat + shell-config
452 440 * delivery). User-principal only.
453 441 *
454 - * @since 0.9.6
455 - *
456 442 * @param int $user_id Viewer.
457 443 * @param int $since_ms Only rows with `invited_at_ms > since`.
458 444 * @return array[] Normalized share rows.
459 445 */
460 -function desktop_mode_files_get_pending_file_shares_for_user( $user_id, $since_ms = 0 ) {
446 +function openstation_files_get_pending_file_shares_for_user( $user_id, $since_ms = 0 ) {
461 447 global $wpdb;
462 448 $user_id = (int) $user_id;
463 449 if ( $user_id <= 0 ) {
464 450 return array();
465 451 }
466 - $tables = desktop_mode_files_table_names();
452 + $tables = openstation_files_table_names();
467 453 $rows = $wpdb->get_results(
468 454 $wpdb->prepare(
469 455 "SELECT s.* FROM {$tables['shares']} s
470 456 INNER JOIN {$tables['stored_files']} sf ON sf.id = s.folder_id
@@ -478,11 +464,11 @@
478 464 (string) $user_id
479 465 ),
480 466 ARRAY_A
481 467 );
482 - $out = array();
468 + $out = array();
483 469 foreach ( (array) $rows as $row ) {
484 - $out[] = desktop_mode_files_normalize_share_row( $row );
470 + $out[] = openstation_files_normalize_share_row( $row );
485 471 }
486 472 return $out;
487 473 }
488 474
@@ -488,15 +474,13 @@
488 474
489 475 /**
490 476 * Wire shape for a file share, enriched for the invite banner.
491 477 *
492 - * @since 0.9.6
493 - *
494 478 * @param array $row Normalized share row (`target_type='file'`).
495 479 * @return array
496 480 */
497 -function desktop_mode_files_shape_file_share( $row ) {
498 - $file = desktop_mode_stored_files_get( (int) $row['folder_id'] );
481 +function openstation_files_shape_file_share( $row ) {
482 + $file = openstation_stored_files_get( (int) $row['folder_id'] );
499 483 $shape = array(
500 484 'id' => (int) $row['id'],
501 485 'targetType' => 'file',
502 486 'fileId' => (int) $row['folder_id'],
@@ -508,17 +492,17 @@
508 492 'invitedAtMs' => (int) $row['invited_at_ms'],
509 493 'decidedAtMs' => isset( $row['decided_at_ms'] ) ? $row['decided_at_ms'] : null,
510 494 );
511 495 if ( $file ) {
512 - $shape['fileName'] = (string) $file['display_name'];
513 - $shape['ownerId'] = (int) $file['owner_id'];
514 - $owner = get_userdata( (int) $file['owner_id'] );
515 - $shape['ownerName'] = $owner ? $owner->display_name : '';
496 + $shape['fileName'] = (string) $file['display_name'];
497 + $shape['ownerId'] = (int) $file['owner_id'];
498 + $owner = get_userdata( (int) $file['owner_id'] );
499 + $shape['ownerName'] = $owner ? openstation_plain_text_title( $owner->display_name ) : '';
516 500 $shape['ownerAvatar'] = $owner ? get_avatar_url( $owner->ID, array( 'size' => 48 ) ) : '';
517 501 }
518 502 // Principal enrichment for the owner-side share list.
519 - $principal = get_userdata( (int) $row['principal_ref'] );
520 - $shape['displayName'] = $principal ? $principal->display_name : '';
503 + $principal = get_userdata( (int) $row['principal_ref'] );
504 + $shape['displayName'] = $principal ? openstation_plain_text_title( $principal->display_name ) : '';
521 505 $shape['avatarUrl'] = $principal ? get_avatar_url( $principal->ID, array( 'size' => 48 ) ) : '';
522 506 return $shape;
523 507 }
524 508
@@ -527,66 +511,86 @@
527 511 // ---------------------------------------------------------------------------
528 512
529 513 /**
530 514 * Register the file-share routes. Same 404-when-disabled gate as
531 - * every other share route (`desktop_mode_files_rest_share_permission`).
532 - *
533 - * @since 0.9.6
515 + * every other share route (`openstation_files_rest_share_permission`).
534 516 */
535 -function desktop_mode_files_register_file_share_rest_routes() {
517 +function openstation_files_register_file_share_rest_routes() {
536 518 $ns = 'desktop-mode/v1';
537 519
538 - register_rest_route( $ns, '/files/uploads/(?P<id>\d+)/shares', array(
520 + register_rest_route(
521 + $ns,
522 + '/files/uploads/(?P<id>\d+)/shares',
539 523 array(
540 - 'methods' => WP_REST_Server::READABLE,
541 - 'permission_callback' => 'desktop_mode_files_rest_share_permission',
542 - 'callback' => 'desktop_mode_files_rest_list_file_shares',
543 - ),
524 + array(
525 + 'methods' => WP_REST_Server::READABLE,
526 + 'permission_callback' => 'openstation_files_rest_share_permission',
527 + 'callback' => 'openstation_files_rest_list_file_shares',
528 + ),
529 + array(
530 + 'methods' => WP_REST_Server::CREATABLE,
531 + 'permission_callback' => 'openstation_files_rest_share_permission',
532 + 'callback' => 'openstation_files_rest_create_file_share',
533 + 'args' => array(
534 + 'userId' => array(
535 + 'type' => 'integer',
536 + 'required' => true,
537 + ),
538 + ),
539 + ),
540 + )
541 + );
542 + register_rest_route(
543 + $ns,
544 + '/files/uploads/(?P<id>\d+)/shares/(?P<shareId>\d+)',
544 545 array(
546 + 'methods' => WP_REST_Server::DELETABLE,
547 + 'permission_callback' => 'openstation_files_rest_share_permission',
548 + 'callback' => 'openstation_files_rest_delete_file_share',
549 + )
550 + );
551 + register_rest_route(
552 + $ns,
553 + '/files/uploads/(?P<id>\d+)/shares/(?P<shareId>\d+)/accept',
554 + array(
545 555 'methods' => WP_REST_Server::CREATABLE,
546 - 'permission_callback' => 'desktop_mode_files_rest_share_permission',
547 - 'callback' => 'desktop_mode_files_rest_create_file_share',
548 - 'args' => array(
549 - 'userId' => array( 'type' => 'integer', 'required' => true ),
550 - ),
551 - ),
552 - ) );
553 - register_rest_route( $ns, '/files/uploads/(?P<id>\d+)/shares/(?P<shareId>\d+)', array(
554 - 'methods' => WP_REST_Server::DELETABLE,
555 - 'permission_callback' => 'desktop_mode_files_rest_share_permission',
556 - 'callback' => 'desktop_mode_files_rest_delete_file_share',
557 - ) );
558 - register_rest_route( $ns, '/files/uploads/(?P<id>\d+)/shares/(?P<shareId>\d+)/accept', array(
559 - 'methods' => WP_REST_Server::CREATABLE,
560 - 'permission_callback' => 'desktop_mode_files_rest_share_permission',
561 - 'callback' => 'desktop_mode_files_rest_accept_file_share',
562 - ) );
563 - register_rest_route( $ns, '/files/uploads/(?P<id>\d+)/shares/(?P<shareId>\d+)/deny', array(
564 - 'methods' => WP_REST_Server::CREATABLE,
565 - 'permission_callback' => 'desktop_mode_files_rest_share_permission',
566 - 'callback' => 'desktop_mode_files_rest_deny_file_share',
567 - ) );
568 - register_rest_route( $ns, '/files/uploads/(?P<id>\d+)/leave', array(
569 - 'methods' => WP_REST_Server::CREATABLE,
570 - 'permission_callback' => 'desktop_mode_files_rest_share_permission',
571 - 'callback' => 'desktop_mode_files_rest_leave_file_share',
572 - ) );
556 + 'permission_callback' => 'openstation_files_rest_share_permission',
557 + 'callback' => 'openstation_files_rest_accept_file_share',
558 + )
559 + );
560 + register_rest_route(
561 + $ns,
562 + '/files/uploads/(?P<id>\d+)/shares/(?P<shareId>\d+)/deny',
563 + array(
564 + 'methods' => WP_REST_Server::CREATABLE,
565 + 'permission_callback' => 'openstation_files_rest_share_permission',
566 + 'callback' => 'openstation_files_rest_deny_file_share',
567 + )
568 + );
569 + register_rest_route(
570 + $ns,
571 + '/files/uploads/(?P<id>\d+)/leave',
572 + array(
573 + 'methods' => WP_REST_Server::CREATABLE,
574 + 'permission_callback' => 'openstation_files_rest_share_permission',
575 + 'callback' => 'openstation_files_rest_leave_file_share',
576 + )
577 + );
573 578 }
574 -add_action( 'rest_api_init', 'desktop_mode_files_register_file_share_rest_routes' );
579 +add_action( 'rest_api_init', 'openstation_files_register_file_share_rest_routes' );
575 580
576 581 /**
577 582 * Resolve the `{shareId}` inside `{id}` or fail with a masked 404.
578 583 *
579 - * @since 0.9.6
580 584 * @internal
581 585 *
582 586 * @param WP_REST_Request $req Request.
583 587 * @return array|WP_Error Normalized share row.
584 588 */
585 -function desktop_mode_files_rest_resolve_file_share( WP_REST_Request $req ) {
586 - $row = desktop_mode_files_get_share( (int) $req['shareId'] );
589 +function openstation_files_rest_resolve_file_share( WP_REST_Request $req ) {
590 + $row = openstation_files_get_share( (int) $req['shareId'] );
587 591 if ( ! $row || 'file' !== $row['target_type'] || (int) $row['folder_id'] !== (int) $req['id'] ) {
588 - return new WP_Error( 'desktop_mode_files_share_not_found', __( 'Share not found.', 'desktop-mode' ), array( 'status' => 404 ) );
592 + return new WP_Error( 'openstation_files_share_not_found', __( 'Share not found.', 'desktop-mode' ), array( 'status' => 404 ) );
589 593 }
590 594 return $row;
591 595 }
592 596
@@ -591,20 +595,18 @@
591 595 }
592 596
593 597 /**
594 598 * GET /files/uploads/<id>/shares (managers only).
595 - *
596 - * @since 0.9.6
597 599 */
598 -function desktop_mode_files_rest_list_file_shares( WP_REST_Request $req ) {
600 +function openstation_files_rest_list_file_shares( WP_REST_Request $req ) {
599 601 $file_id = (int) $req['id'];
600 602 $user_id = get_current_user_id();
601 - if ( ! desktop_mode_stored_files_share_can_manage( $file_id, $user_id ) ) {
602 - return desktop_mode_files_download_not_found();
603 + if ( ! openstation_stored_files_share_can_manage( $file_id, $user_id ) ) {
604 + return openstation_files_download_not_found();
603 605 }
604 606 $out = array();
605 - foreach ( desktop_mode_stored_files_get_file_shares( $file_id ) as $row ) {
606 - $out[] = desktop_mode_files_shape_file_share( $row );
607 + foreach ( openstation_stored_files_get_file_shares( $file_id ) as $row ) {
608 + $out[] = openstation_files_shape_file_share( $row );
607 609 }
608 610 return rest_ensure_response( array( 'shares' => $out ) );
609 611 }
610 612
@@ -610,21 +612,19 @@
610 612
611 613 /**
612 614 * POST /files/uploads/<id>/shares — invite (read tier, always).
613 615 * A `capability` param, if sent, must be `read` — `write` is 400.
614 - *
615 - * @since 0.9.6
616 616 */
617 -function desktop_mode_files_rest_create_file_share( WP_REST_Request $req ) {
617 +function openstation_files_rest_create_file_share( WP_REST_Request $req ) {
618 618 $capability = $req->get_param( 'capability' );
619 619 if ( null !== $capability && 'read' !== (string) $capability ) {
620 620 return new WP_Error(
621 - 'desktop_mode_files_invalid_capability',
621 + 'openstation_files_invalid_capability',
622 622 __( 'Uploaded files can only be shared read-only.', 'desktop-mode' ),
623 623 array( 'status' => 400 )
624 624 );
625 625 }
626 - $id = desktop_mode_stored_file_share_invite(
626 + $id = openstation_stored_file_share_invite(
627 627 (int) $req['id'],
628 628 get_current_user_id(),
629 629 (int) $req->get_param( 'userId' )
630 630 );
@@ -630,22 +630,20 @@
630 630 );
631 631 if ( is_wp_error( $id ) ) {
632 632 return $id;
633 633 }
634 - return rest_ensure_response( desktop_mode_files_shape_file_share( desktop_mode_files_get_share( $id ) ) );
634 + return rest_ensure_response( openstation_files_shape_file_share( openstation_files_get_share( $id ) ) );
635 635 }
636 636
637 637 /**
638 638 * DELETE /files/uploads/<id>/shares/<shareId> — revoke.
639 - *
640 - * @since 0.9.6
641 639 */
642 -function desktop_mode_files_rest_delete_file_share( WP_REST_Request $req ) {
643 - $row = desktop_mode_files_rest_resolve_file_share( $req );
640 +function openstation_files_rest_delete_file_share( WP_REST_Request $req ) {
641 + $row = openstation_files_rest_resolve_file_share( $req );
644 642 if ( is_wp_error( $row ) ) {
645 643 return $row;
646 644 }
647 - $ok = desktop_mode_stored_file_share_revoke( (int) $row['id'], get_current_user_id() );
645 + $ok = openstation_stored_file_share_revoke( (int) $row['id'], get_current_user_id() );
648 646 if ( is_wp_error( $ok ) ) {
649 647 return $ok;
650 648 }
651 649 return rest_ensure_response( array( 'deleted' => true ) );
@@ -652,47 +650,41 @@
652 650 }
653 651
654 652 /**
655 653 * POST .../accept
656 - *
657 - * @since 0.9.6
658 654 */
659 -function desktop_mode_files_rest_accept_file_share( WP_REST_Request $req ) {
660 - $row = desktop_mode_files_rest_resolve_file_share( $req );
655 +function openstation_files_rest_accept_file_share( WP_REST_Request $req ) {
656 + $row = openstation_files_rest_resolve_file_share( $req );
661 657 if ( is_wp_error( $row ) ) {
662 658 return $row;
663 659 }
664 - $next = desktop_mode_stored_file_share_accept( (int) $row['id'], get_current_user_id() );
660 + $next = openstation_stored_file_share_accept( (int) $row['id'], get_current_user_id() );
665 661 if ( is_wp_error( $next ) ) {
666 662 return $next;
667 663 }
668 - return rest_ensure_response( desktop_mode_files_shape_file_share( $next ) );
664 + return rest_ensure_response( openstation_files_shape_file_share( $next ) );
669 665 }
670 666
671 667 /**
672 668 * POST .../deny
673 - *
674 - * @since 0.9.6
675 669 */
676 -function desktop_mode_files_rest_deny_file_share( WP_REST_Request $req ) {
677 - $row = desktop_mode_files_rest_resolve_file_share( $req );
670 +function openstation_files_rest_deny_file_share( WP_REST_Request $req ) {
671 + $row = openstation_files_rest_resolve_file_share( $req );
678 672 if ( is_wp_error( $row ) ) {
679 673 return $row;
680 674 }
681 - $next = desktop_mode_stored_file_share_deny( (int) $row['id'], get_current_user_id() );
675 + $next = openstation_stored_file_share_deny( (int) $row['id'], get_current_user_id() );
682 676 if ( is_wp_error( $next ) ) {
683 677 return $next;
684 678 }
685 - return rest_ensure_response( desktop_mode_files_shape_file_share( $next ) );
679 + return rest_ensure_response( openstation_files_shape_file_share( $next ) );
686 680 }
687 681
688 682 /**
689 683 * POST /files/uploads/<id>/leave
690 - *
691 - * @since 0.9.6
692 684 */
693 -function desktop_mode_files_rest_leave_file_share( WP_REST_Request $req ) {
694 - $ok = desktop_mode_stored_file_share_leave( (int) $req['id'], get_current_user_id() );
685 +function openstation_files_rest_leave_file_share( WP_REST_Request $req ) {
686 + $ok = openstation_stored_file_share_leave( (int) $req['id'], get_current_user_id() );
695 687 if ( is_wp_error( $ok ) ) {
696 688 return $ok;
697 689 }
698 690 return rest_ensure_response( array( 'left' => true ) );
@@ -707,17 +699,15 @@
707 699 * `serverPendingShares` array (after the folder injection at 20).
708 700 * File shapes carry `targetType: 'file'` + `fileId` / `fileName`
709 701 * so the invite banner can branch.
710 702 *
711 - * @since 0.9.6
712 - *
713 703 * @param array $config Shell config.
714 704 * @return array
715 705 */
716 -function desktop_mode_files_file_share_inject_shell_config( $config ) {
706 +function openstation_files_file_share_inject_shell_config( $config ) {
717 707 $user_id = get_current_user_id();
718 - $sharing_enabled = function_exists( 'desktop_mode_files_sharing_enabled_for' )
719 - ? desktop_mode_files_sharing_enabled_for( $user_id )
708 + $sharing_enabled = function_exists( 'openstation_files_sharing_enabled_for' )
709 + ? openstation_files_sharing_enabled_for( $user_id )
720 710 : true;
721 711 if ( $user_id <= 0 || ! $sharing_enabled ) {
722 712 return $config;
723 713 }
@@ -723,11 +713,11 @@
723 713 }
724 714 $pending = isset( $config['serverPendingShares'] ) && is_array( $config['serverPendingShares'] )
725 715 ? $config['serverPendingShares']
726 716 : array();
727 - foreach ( desktop_mode_files_get_pending_file_shares_for_user( $user_id, 0 ) as $row ) {
728 - $pending[] = desktop_mode_files_shape_file_share( $row );
717 + foreach ( openstation_files_get_pending_file_shares_for_user( $user_id, 0 ) as $row ) {
718 + $pending[] = openstation_files_shape_file_share( $row );
729 719 }
730 720 $config['serverPendingShares'] = $pending;
731 721 return $config;
732 722 }
733 -add_filter( 'desktop_mode_shell_config', 'desktop_mode_files_file_share_inject_shell_config', 21 );
723 +add_filter( 'openstation_shell_config', 'openstation_files_file_share_inject_shell_config', 21 );