Skip to content
Closed
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Backport lib/compat/wordpress-6.0/class-wp-rest-block-patterns-contro…
…ller.php.
  • Loading branch information
hellofromtonya committed Apr 6, 2022
commit fef00003ed365f8bc7a08f5b5a04516e996fec2d
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<?php
/**
* REST API: WP_REST_Block_Patterns_Controller class
*
* @package WordPress
* @subpackage REST_API
* @since 6.0.0
*/

/**
* Core class used to access block patterns via the REST API.
*
* @since 6.0.0
*
* @see WP_REST_Controller
*/
class WP_REST_Block_Patterns_Controller extends WP_REST_Controller {

/**
* Constructs the controller.
*
* @since 6.0.0
*/
public function __construct() {
$this->namespace = '__experimental';
$this->rest_base = 'block-patterns/patterns';
}

/**
* Registers the routes for the objects of the controller.
*
* @since 6.0.0
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}

/**
* Checks whether a given request has permission to read block patterns.
*
* @since 6.0.0
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
*/
public function get_items_permissions_check( $request ) {
if ( current_user_can( 'edit_posts' ) ) {
return true;
}

foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
if ( current_user_can( $post_type->cap->edit_posts ) ) {
return true;
}
}

return new WP_Error(
'rest_cannot_view',
__( 'Sorry, you are not allowed to view the registered block patterns.' ),
array( 'status' => rest_authorization_required_code() )
);
}

/**
* Retrieves all block patterns.
*
* @since 6.0.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_items( $request ) {
// Load block patterns from w.org.
_load_remote_block_patterns(); // Patterns with the `core` keyword.
_load_remote_featured_patterns(); // Patterns in the `featured` category.
_register_remote_theme_patterns(); // Patterns requested by current theme.
Comment on lines +85 to +87
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these methods guarded against running more than once in a request? Looking at _load_remote_block_patterns at least, it seems it will try and register the patterns again. It's very important that the REST API is reentrant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not guarded. When writing them, I assumed that the handler can ever run only once. But that was wrong. What would be a good way to prevent the _load_remote_* being run twice? Some $already_loaded global variable?

Copy link

@anton-vlasenko anton-vlasenko Apr 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've checked it in the XDebug, and these methods don't get called twice during a simple GET wp/v2/block-patterns/patterns request.
But I've submitted a PR to fix this possible issue: #2576
cc: @gziolo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent, let's wait for confirmation from @TimothyBJacobs on the changes proposed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these methods don't get called twice during a simple GET

During regular requests they probably never are, but what @TimothyBJacobs probably had in mind is that the handler can be called multiple times when doing special things: batching requests, executing the endpoints when preloading, embedding linked requests into one response... In these cases, multiple requests are being processed within one parent one.

Copy link

@anton-vlasenko anton-vlasenko Apr 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @jsnajdr.
I understand it.
I was primarily concerned about single requests (fortunately, the issue doesn't affect single requests).
I wouldn't submit a fix if I weren't sure that the issue could affect batch requests etc.


$response = array();
$patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered();
foreach ( $patterns as $pattern ) {
$prepared_pattern = $this->prepare_item_for_response( $pattern, $request );
$response[] = $this->prepare_response_for_collection( $prepared_pattern );
}
return rest_ensure_response( $response );
}

/**
* Prepare a raw block pattern before it gets output in a REST API response.
*
* @since 6.0.0
*
* @param object $item Raw pattern as registered, before any changes.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function prepare_item_for_response( $item, $request ) {
$fields = $this->get_fields_for_response( $request );
$keys = array(
'name',
'title',
'description',
'viewportWidth',
'blockTypes',
'categories',
'keywords',
'content',
);
$data = array();
foreach ( $keys as $key ) {
if ( isset( $item[ $key ] ) && rest_is_field_included( $key, $fields ) ) {
$data[ $key ] = $item[ $key ];
}
}

$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->add_additional_fields_to_object( $data, $request );
$data = $this->filter_response_by_context( $data, $context );
return rest_ensure_response( $data );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing about filter support.

}

/**
* Retrieves the block pattern schema, conforming to JSON Schema.
*
* @since 6.0.0
*
* @return array Item schema data.
*/
public function get_item_schema() {
$schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'block-pattern',
'type' => 'object',
'properties' => array(
'name' => array(
'description' => __( 'The pattern name.' ),
'type' => 'string',
'readonly' => true,
'context' => array( 'view', 'embed' ),
),
'title' => array(
'description' => __( 'The pattern title, in human readable format.' ),
'type' => 'string',
'readonly' => true,
'context' => array( 'view', 'embed' ),
),
'description' => array(
'description' => __( 'The pattern detailed description.' ),
'type' => 'string',
'readonly' => true,
'context' => array( 'view', 'embed' ),
),
'viewportWidth' => array(
'description' => __( 'The pattern viewport width for inserter preview.' ),
'type' => 'number',
'readonly' => true,
'context' => array( 'view', 'embed' ),
),
'blockTypes' => array(
'description' => __( 'Block types that the pattern is intended to be used with.' ),
'type' => 'array',
'readonly' => true,
'context' => array( 'view', 'embed' ),
),
'categories' => array(
'description' => __( 'The pattern category slugs.' ),
'type' => 'array',
'readonly' => true,
'context' => array( 'view', 'embed' ),
),
'keywords' => array(
'description' => __( 'The pattern keywords.' ),
'type' => 'array',
'readonly' => true,
'context' => array( 'view', 'embed' ),
),
'content' => array(
'description' => __( 'The pattern content.' ),
'type' => 'string',
'readonly' => true,
'context' => array( 'view', 'embed' ),
),
),
);

return $this->add_additional_fields_schema( $schema );
}
}