manager = new PostManager($postType); } public function trash(WP_REST_Request $request): WP_REST_Response { $id = $this->routeId($request, 'post_id'); if (!current_user_can('delete_post', $id)) { return $this->sendError(__('You cannot trash this post.', 'publishdeck'), 403, 'fspd_forbidden'); } DeleteIntent::remember($id, $this->wantsCopyRemoved($request)); try { $done = $this->manager->trash($id); } finally { DeleteIntent::forget($id); } if (!$done) { return $this->sendError(__('Could not trash the post.', 'publishdeck'), 400); } return $this->sendSuccess(['id' => $id, 'status' => 'trash']); } public function restore(WP_REST_Request $request): WP_REST_Response { $id = $this->routeId($request, 'post_id'); if (!current_user_can('delete_post', $id)) { return $this->sendError( __('You cannot restore this post.', 'publishdeck'), 403, 'fspd_forbidden' ); } if (!$this->manager->restore($id)) { return $this->sendError(__('Could not restore the post.', 'publishdeck'), 400); } return $this->sendSuccess(['id' => $id]); } public function delete(WP_REST_Request $request): WP_REST_Response { $id = $this->routeId($request, 'post_id'); if (!current_user_can('delete_post', $id)) { return $this->sendError(__('You cannot delete this post.', 'publishdeck'), 403, 'fspd_forbidden'); } if (MemberCaps::actsOnOthersPost(get_current_user_id(), $id)) { $message = __( 'This post belongs to someone else, so only its author or a hub editor can delete it permanently.', 'publishdeck' ); return $this->sendError($message, 403, 'fspd_forbidden'); } DeleteIntent::remember($id, $this->wantsCopyRemoved($request)); try { $done = $this->manager->delete($id); } finally { DeleteIntent::forget($id); } if (!$done) { return $this->sendError(__('Could not delete the post.', 'publishdeck'), 400); } return $this->sendSuccess(['id' => $id]); } /** Whether the caller asked for the child's copy to go too; absent means yes. */ private function wantsCopyRemoved(WP_REST_Request $request): bool { $choice = $request->get_param('remove_copy'); return $choice === null ? true : (bool) $choice; } public function quickEdit(WP_REST_Request $request): WP_REST_Response { $id = $this->routeId($request, 'post_id'); if (!current_user_can('edit_post', $id)) { return $this->sendError(__('You cannot edit this post.', 'publishdeck'), 403, 'fspd_forbidden'); } if ($refusal = $this->refuseInTrash((string) get_post_status($id))) { return $refusal; } $fields = []; foreach (['title', 'slug'] as $key) { $value = $request->get_param($key); if ($value !== null) { $fields[$key] = $value; } } // A garbage date silently becomes 0000-00-00, so check the shape before wp_update_post. $date = $request->get_param('date'); if ($date !== null && (string) $date !== '') { $date = str_replace('T', ' ', trim((string) $date)); if (!DashboardController::validDate($date)) { return $this->sendError(__('Invalid date format.', 'publishdeck'), 400); } $fields['date'] = $date; } $status = $request->get_param('status'); if ($status !== null) { $status = sanitize_key((string) $status); if (!PostStatus::isValid($status)) { return $this->sendError(__('Invalid status.', 'publishdeck'), 400); } // Only a change to a publishing status needs the cap; keeping the current one publishes nothing. if ( PostStatus::isPublishing($status) && $status !== get_post_status($id) && !current_user_can('publish_fspd_posts') ) { return $this->sendError( __('You cannot publish posts.', 'publishdeck'), 403, 'fspd_forbidden' ); } $fields['status'] = $status; } // The new author must be able to hold remote posts, or it goes invisible and unpushable. $author = (int) $request->get_param('author'); if ($author > 0 && current_user_can('edit_others_fspd_posts')) { if (!user_can($author, 'edit_fspd_posts')) { return $this->sendError(__('That user cannot hold remote posts.', 'publishdeck'), 400); } $fields['author'] = $author; } foreach (['categories' => 'category', 'tags' => 'post_tag'] as $key => $taxonomy) { $value = $request->get_param($key); if ($value === null) { continue; } // As the block editor checks: assign_terms to assign, edit_terms for a tag that does not exist yet. $taxonomyCapabilities = get_taxonomy($taxonomy)->cap ?? null; if (!$taxonomyCapabilities || !current_user_can($taxonomyCapabilities->assign_terms)) { $message = __('You cannot assign these terms.', 'publishdeck'); return $this->sendError($message, 403, 'fspd_forbidden'); } if ($key === 'tags' && !current_user_can($taxonomyCapabilities->edit_terms)) { foreach ((array) $value as $name) { $name = trim(sanitize_text_field((string) $name)); if ($name !== '' && !term_exists($name, $taxonomy)) { $message = __('You cannot create new tags.', 'publishdeck'); return $this->sendError($message, 403, 'fspd_forbidden'); } } } $fields[$key] = (array) $value; } $result = $this->manager->quickEdit($id, $fields); if (is_wp_error($result)) { return $this->sendError($result->get_error_message(), 400); } return $this->sendSuccess(['id' => $id]); } /** Editable fields for the Quick Edit form (pre-fill). */ public function fields(WP_REST_Request $request): WP_REST_Response { $id = $this->routeId($request, 'post_id'); if (!current_user_can('edit_post', $id)) { return $this->sendError(__('You cannot edit this post.', 'publishdeck'), 403, 'fspd_forbidden'); } $post = get_post($id); if (!$post) { return $this->sendError(__('Post not found.', 'publishdeck'), 404, 'fspd_not_found'); } return $this->sendSuccess([ 'id' => $id, 'title' => $post->post_title, 'slug' => $post->post_name, 'status' => $post->post_status, 'date' => $post->post_date, 'author' => (int) $post->post_author, 'categories' => array_map('intval', (array) wp_get_post_categories($id)), 'tags' => (array) wp_get_post_tags($id, ['fields' => 'names']), 'can_publish' => current_user_can('publish_fspd_posts'), // Built here, not on the polled /posts list, and only under the cap the save checks. 'assignable_authors' => current_user_can('edit_others_fspd_posts') ? (new PostListing())->assignableAuthors((int) $post->post_author) : [], ]); } }