posts_repository = $posts_repository; $this->content_types_repository = $content_types_repository; $this->content_type_access_checker = $content_type_access_checker; $this->user_helper = $user_helper; } /** * Registers routes with WordPress. * * @return void */ public function register_routes() { \register_rest_route( self::ROUTE_NAMESPACE, self::ROUTE_PREFIX, [ 'methods' => 'GET', 'args' => [ 'content_type' => [ 'required' => true, 'type' => 'string', 'description' => 'The content type to fetch posts for.', 'sanitize_callback' => 'sanitize_text_field', ], 'per_page' => [ 'required' => false, 'type' => 'integer', 'default' => self::DEFAULT_PER_PAGE, 'minimum' => 1, 'maximum' => self::MAX_PER_PAGE, 'description' => 'The number of posts to fetch.', 'sanitize_callback' => 'absint', ], 'page' => [ 'required' => false, 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'description' => 'The page of posts to fetch.', 'sanitize_callback' => 'absint', ], 'search' => [ 'required' => false, 'type' => 'string', 'default' => '', 'description' => 'The term to search posts by.', 'sanitize_callback' => 'sanitize_text_field', ], 'status' => [ 'required' => false, 'type' => 'array', 'default' => Posts_Collector_Interface::STATUSES, 'items' => [ 'type' => 'string', 'enum' => Posts_Collector_Interface::STATUSES, ], 'description' => 'The post statuses to include.', ], ], 'callback' => [ $this, 'get_posts' ], 'permission_callback' => [ $this, 'check_permissions' ], ], ); } /** * Returns a page of posts for the requested content type. * * @param WP_REST_Request $request The request object. * * @return WP_REST_Response|WP_Error The posts, or an error when the content type is not editable. */ public function get_posts( WP_REST_Request $request ) { $content_type = $request->get_param( 'content_type' ); if ( ! $this->is_valid_content_type( $content_type ) ) { return new WP_Error( 'rest_invalid_content_type', 'The requested content type is not editable.', [ 'status' => 400 ], ); } // An empty selection (no status filter) means all statuses, never zero results. $statuses = (array) $request->get_param( 'status' ); if ( empty( $statuses ) ) { $statuses = Posts_Collector_Interface::STATUSES; } // Narrow the query to the user's own posts when they cannot edit other authors' posts. This keeps // pagination cheap; the exact per-post edit permission is still enforced when collecting the page. $author_id = null; if ( ! $this->content_type_access_checker->can_edit_others( $content_type ) ) { $author_id = $this->user_helper->get_current_user_id(); } $query = new Posts_Query( $content_type, (int) $request->get_param( 'page' ), (int) $request->get_param( 'per_page' ), (string) $request->get_param( 'search' ), $statuses, $author_id, ); // Posts the current user cannot edit are returned locked and without their SEO data; the per-post // permission is resolved while collecting the page. return new WP_REST_Response( $this->posts_repository->get_posts( $query )->to_array() ); } /** * Checks whether the current user is allowed to use the bulk editor. * * @return bool Whether the current user is allowed to use the bulk editor. */ public function check_permissions(): bool { return \current_user_can( 'wpseo_manage_options' ); } /** * Checks whether the given content type is one the bulk editor can edit. * * @param string $content_type The content type to check. * * @return bool Whether the content type is editable. */ private function is_valid_content_type( string $content_type ): bool { foreach ( $this->content_types_repository->get_content_types() as $editable ) { if ( $editable['name'] === $content_type ) { return true; } } return false; } }