Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pattern Directory API: Add support for pagination parameters #45293

Merged
merged 10 commits into from
Nov 8, 2022
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* REST API: Gutenberg_REST_Pattern_Directory_Controller_6_0 class
*
* @package Gutenberg
* @subpackage REST_API
*/

/**
* Controller which provides REST endpoint for block patterns from wordpress.org/patterns.
*/
class Gutenberg_REST_Pattern_Directory_Controller_6_0 extends WP_REST_Pattern_Directory_Controller {
/**
* Include a hash of the query args, so that different requests are stored in
* separate caches.
*
* MD5 is chosen for its speed, low-collision rate, universal availability, and to stay
* under the character limit for `_site_transient_timeout_{...}` keys.
*
* @link https://stackoverflow.com/questions/3665247/fastest-hash-for-non-cryptographic-uses
*
* @since 6.0.0
* @todo This should be removed when the minimum required WordPress version is >= 6.0.
*
* @param array $query_args Query arguments to generate a transient key from.
* @return string Transient key.
*/
protected function get_transient_key( $query_args ) {
if ( method_exists( get_parent_class( $this ), __FUNCTION__ ) ) {
return parent::get_transient_key( $query_args );
}

if ( isset( $query_args['slug'] ) ) {
// This is an additional precaution because the "sort" function expects an array.
$query_args['slug'] = wp_parse_list( $query_args['slug'] );

// Empty arrays should not affect the transient key.
if ( empty( $query_args['slug'] ) ) {
unset( $query_args['slug'] );
} else {
// Sort the array so that the transient key doesn't depend on the order of slugs.
sort( $query_args['slug'] );
}
}

return 'wp_remote_block_patterns_' . md5( serialize( $query_args ) );
}
}
10 changes: 0 additions & 10 deletions lib/compat/wordpress-6.0/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,6 @@ function gutenberg_register_global_styles_endpoints() {
}
add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' );


/**
* Registers the block pattern directory.
*/
function gutenberg_register_rest_pattern_directory() {
$pattern_directory_controller = new Gutenberg_REST_Pattern_Directory_Controller();
$pattern_directory_controller->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_rest_pattern_directory' );

/**
* Registers the Edit Site's Export REST API routes.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
<?php
/**
* REST API: Gutenberg_REST_Global_Styles_Controller class
* REST API: Gutenberg_REST_Pattern_Directory_Controller_6_2 class
*
* @package Gutenberg
* @subpackage REST_API
*/

/**
* Controller which provides REST endpoint for block patterns.
* Controller which provides REST endpoint for block patterns from wordpress.org/patterns.
*/
class Gutenberg_REST_Pattern_Directory_Controller extends WP_REST_Pattern_Directory_Controller {
class Gutenberg_REST_Pattern_Directory_Controller_6_2 extends Gutenberg_REST_Pattern_Directory_Controller_6_0 {
ryelle marked this conversation as resolved.
Show resolved Hide resolved
/**
* Search and retrieve block patterns metadata
*
* @since 6.0.0
* @since 5.8.0
* @since 6.0.0 Added 'slug' to request.
* @since 6.2.0 Added 'per_page', 'page', 'offset', 'order', and 'orderby' to request.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_items( $request ) {
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
/**
Expand All @@ -30,36 +31,24 @@ public function get_items( $request ) {

$gutenberg_data = get_plugin_data( dirname( dirname( dirname( __DIR__ ) ) ) . '/gutenberg.php', false );

$query_args = array(
'locale' => get_user_locale(),
'wp-version' => $wp_version, // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- it's defined in `version.php` above.
'gutenberg-version' => $gutenberg_data['Version'],
$valid_query_args = array( 'offset', 'order', 'orderby', 'page', 'per_page', 'search', 'slug' );
$query_args = array_merge(
array_intersect_key( $request->get_params(), array_flip( $valid_query_args ) ),
array(
'locale' => get_user_locale(),
'wp-version' => $wp_version, // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- it's defined in `version.php` above.
'gutenberg-version' => $gutenberg_data['Version'],
)
);

$category_id = $request['category'];
$keyword_id = $request['keyword'];
$search_term = $request['search'];
$slug = $request['slug'];

if ( $category_id ) {
$query_args['pattern-categories'] = $category_id;
}

if ( $keyword_id ) {
$query_args['pattern-keywords'] = $keyword_id;
}

if ( $search_term ) {
$query_args['search'] = $search_term;
}
$query_args['pattern-categories'] = isset( $request['category'] ) ? $request['category'] : false;
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
$query_args['pattern-keywords'] = isset( $request['keyword'] ) ? $request['keyword'] : false;

if ( $slug ) {
$query_args['slug'] = $slug;
}
$query_args = array_filter( $query_args );

$transient_key = $this->get_transient_key( $query_args );

/**
/*
* Use network-wide transient to improve performance. The locale is the only site
* configuration that affects the response, and it's included in the transient key.
*/
Expand All @@ -71,7 +60,7 @@ public function get_items( $request ) {
$api_url = set_url_scheme( $api_url, 'https' );
}

/**
/*
* Default to a short TTL, to mitigate cache stampedes on high-traffic sites.
* This assumes that most errors will be short-lived, e.g., packet loss that causes the
* first request to fail, but a follow-up one will succeed. The value should be high
Expand All @@ -90,7 +79,7 @@ public function get_items( $request ) {
$raw_patterns = new WP_Error(
'pattern_api_failed',
sprintf(
/* translators: %s: Support forums URL. */
/* translators: %s: Support forums URL. */
__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.', 'gutenberg' ),
__( 'https://wordpress.org/support/forums/', 'gutenberg' )
),
Expand Down Expand Up @@ -125,40 +114,4 @@ public function get_items( $request ) {

return new WP_REST_Response( $response );
}

/**
* Include a hash of the query args, so that different requests are stored in
* separate caches.
*
* MD5 is chosen for its speed, low-collision rate, universal availability, and to stay
* under the character limit for `_site_transient_timeout_{...}` keys.
*
* @link https://stackoverflow.com/questions/3665247/fastest-hash-for-non-cryptographic-uses
*
* @since 6.0.0
* @todo This should be removed when the minimum required WordPress version is >= 6.0.
*
* @param array $query_args Query arguments to generate a transient key from.
* @return string Transient key.
*/
protected function get_transient_key( $query_args ) {
if ( method_exists( get_parent_class( $this ), __FUNCTION__ ) ) {
return parent::get_transient_key( $query_args );
}

if ( isset( $query_args['slug'] ) ) {
// This is an additional precaution because the "sort" function expects an array.
$query_args['slug'] = wp_parse_list( $query_args['slug'] );

// Empty arrays should not affect the transient key.
if ( empty( $query_args['slug'] ) ) {
unset( $query_args['slug'] );
} else {
// Sort the array so that the transient key doesn't depend on the order of slugs.
sort( $query_args['slug'] );
}
}

return 'wp_remote_block_patterns_' . md5( serialize( $query_args ) );
}
}
70 changes: 70 additions & 0 deletions lib/compat/wordpress-6.2/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,73 @@ function gutenberg_register_rest_block_pattern_categories() {
$block_patterns->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_rest_block_pattern_categories' );

/**
* Registers the block pattern directory.
*/
function gutenberg_register_rest_pattern_directory() {
$pattern_directory_controller = new Gutenberg_REST_Pattern_Directory_Controller_6_2();
$pattern_directory_controller->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_rest_pattern_directory' );

/**
* Add extra collection params to pattern directory requests.
*
* @param array $query_params JSON Schema-formatted collection parameters.
* @return array Updated parameters.
*/
function gutenberg_pattern_directory_collection_params_6_2( $query_params ) {
$query_params['page'] = array(
'description' => __( 'Current page of the collection.', 'gutenberg' ),
'type' => 'integer',
'default' => 1,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
'minimum' => 1,
);

$query_params['per_page'] = array(
'description' => __( 'Maximum number of items to be returned in result set.', 'gutenberg' ),
'type' => 'integer',
'default' => 100,
'minimum' => 1,
'maximum' => 100,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
);

$query_params['offset'] = array(
'description' => __( 'Offset the result set by a specific number of items.', 'gutenberg' ),
'type' => 'integer',
);

$query_params['order'] = array(
'description' => __( 'Order sort attribute ascending or descending.', 'gutenberg' ),
'type' => 'string',
'default' => 'desc',
'enum' => array( 'asc', 'desc' ),
);

$query_params['orderby'] = array(
'description' => __( 'Sort collection by post attribute.', 'gutenberg' ),
'type' => 'string',
'default' => 'date',
'enum' => array(
'author',
'date',
'id',
'include',
'modified',
'parent',
'relevance',
'slug',
'include_slugs',
'title',
'favorite_count',
),
);

return $query_params;
}
add_filter( 'rest_pattern_directory_collection_params', 'gutenberg_pattern_directory_collection_params_6_2' );
3 changes: 2 additions & 1 deletion lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function gutenberg_is_experiment_enabled( $name ) {

// WordPress 6.0 compat.
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-global-styles-controller.php';
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-pattern-directory-controller.php';
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-pattern-directory-controller-6-0.php';
require_once __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-edit-site-export-controller.php';
if ( ! class_exists( 'WP_REST_Block_Pattern_Categories_Controller' ) ) {
require_once __DIR__ . '/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php';
Expand All @@ -51,6 +51,7 @@ function gutenberg_is_experiment_enabled( $name ) {

// WordPress 6.2 compat.
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php';
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php';
require_once __DIR__ . '/compat/wordpress-6.2/rest-api.php';
require_once __DIR__ . '/compat/wordpress-6.2/block-patterns.php';

Expand Down
Loading