Skip to content

Commit

Permalink
[Pattern Directory]: Allow pattern registration from directory with t…
Browse files Browse the repository at this point in the history
…heme.json (#38323)

* [Pattern Directory]: Allow pattern registration from directory with theme.json

* fix php linting issues

* identation fix

* review feedback

* always return an array

* Revert changes to existing WP_Theme_JSON_Gutenberg class

* Override the parts that this PR needs

* fix after rebase

Co-authored-by: André <[email protected]>
  • Loading branch information
ntsekouras and oandregal authored Feb 10, 2022
1 parent c2b5011 commit cab8cc5
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* WP_Theme_JSON_Gutenberg class
* WP_Theme_JSON_5_9 class
*
* @package gutenberg
*/
Expand All @@ -14,7 +14,7 @@
*
* @access private
*/
class WP_Theme_JSON_Gutenberg {
class WP_Theme_JSON_5_9 {

/**
* Container of data in theme.json format.
Expand Down
41 changes: 41 additions & 0 deletions lib/compat/wordpress-6.0/block-patterns.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
/**
* Block patterns registration from `theme.json` and Pattern Directory.
*
* @package gutenberg
*/

/**
* Registers patterns from Pattern Directory provided by a theme's
* `theme.json` file.
*/
function gutenberg_register_remote_theme_patterns() {
$should_load_remote = apply_filters( 'should_load_remote_block_patterns', true );
$theme_has_support = WP_Theme_JSON_Resolver_Gutenberg::theme_has_support();
if ( ! get_theme_support( 'core-block-patterns' ) || ! $should_load_remote || ! $theme_has_support ) {
return;
}

$pattern_settings = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_patterns();
if ( empty( $pattern_settings ) ) {
return;
}
$request = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' );
$request['slug'] = implode( ',', $pattern_settings );
$response = rest_do_request( $request );
if ( $response->is_error() ) {
return;
}
$patterns = $response->get_data();
$patterns_registry = WP_Block_Patterns_Registry::get_instance();
foreach ( $patterns as $pattern ) {
$pattern_name = sanitize_title( $pattern['title'] );
// Some patterns might be already registered as core patterns with the `core` prefix.
$is_registered = $patterns_registry->is_registered( $pattern_name ) || $patterns_registry->is_registered( "core/$pattern_name" );
if ( ! $is_registered ) {
register_block_pattern( $pattern_name, (array) $pattern );
}
}
}

add_action( 'init', 'gutenberg_register_remote_theme_patterns' );
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php
/**
* REST API: Gutenberg_REST_Global_Styles_Controller class
*
* @package Gutenberg
* @subpackage REST_API
*/

/**
* Controller which provides REST endpoint for block patterns.
*/
class Gutenberg_REST_Pattern_Directory_Controller extends WP_REST_Pattern_Directory_Controller {
/**
* Search and retrieve block patterns metadata
*
* @since 6.0.0
*
* @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.
*/
public function get_items( $request ) {
/**
* Include an unmodified `$wp_version`, so the API can craft a response that's tailored to
* it. Some plugins modify the version in a misguided attempt to improve security by
* obscuring the version, which can cause invalid requests.
*/
require ABSPATH . WPINC . '/version.php';
require_once ABSPATH . 'wp-admin/includes/plugin.php';

$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'],
);

$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;
}

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

/**
* 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
*/
$transient_key = 'wp_remote_block_patterns_' . md5( implode( '-', $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.
*/
$raw_patterns = get_site_transient( $transient_key );

if ( ! $raw_patterns ) {
$api_url = add_query_arg(
array_map( 'rawurlencode', $query_args ),
'http://api.wordpress.org/patterns/1.0/'
);

if ( wp_http_supports( array( 'ssl' ) ) ) {
$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
* enough to avoid stampedes, but low enough to not interfere with users manually
* re-trying a failed request.
*/
$cache_ttl = 5;
$wporg_response = wp_remote_get( $api_url );
$raw_patterns = json_decode( wp_remote_retrieve_body( $wporg_response ) );

if ( is_wp_error( $wporg_response ) ) {
$raw_patterns = $wporg_response;

} elseif ( ! is_array( $raw_patterns ) ) {
// HTTP request succeeded, but response data is invalid.
$raw_patterns = new WP_Error(
'pattern_api_failed',
sprintf(
/* 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' )
),
array(
'response' => wp_remote_retrieve_body( $wporg_response ),
)
);

} else {
// Response has valid data.
$cache_ttl = HOUR_IN_SECONDS;
}

set_site_transient( $transient_key, $raw_patterns, $cache_ttl );
}

if ( is_wp_error( $raw_patterns ) ) {
$raw_patterns->add_data( array( 'status' => 500 ) );

return $raw_patterns;
}

$response = array();

if ( $raw_patterns ) {
foreach ( $raw_patterns as $pattern ) {
$response[] = $this->prepare_response_for_collection(
$this->prepare_item_for_response( $pattern, $request )
);
}
}

return new WP_REST_Response( $response );
}
}
45 changes: 45 additions & 0 deletions lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/**
* WP_Theme_JSON_Gutenberg class
*
* @package gutenberg
*/

/**
* Class that encapsulates the processing of structures that adhere to the theme.json spec.
*
* This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes).
* This is a low-level API that may need to do breaking changes. Please,
* use get_global_settings, get_global_styles, and get_global_stylesheet instead.
*
* @access private
*/
class WP_Theme_JSON_Gutenberg extends WP_Theme_JSON_5_9 {

/**
* The top-level keys a theme.json can have.
*
* @var string[]
*/
const VALID_TOP_LEVEL_KEYS = array(
'customTemplates',
'patterns',
'settings',
'styles',
'templateParts',
'version',
);

/**
* Returns the current theme's wanted patterns(slugs) to be
* registered from Pattern Directory.
*
* @return array
*/
public function get_patterns() {
if ( isset( $this->theme_json['patterns'] ) && is_array( $this->theme_json['patterns'] ) ) {
return $this->theme_json['patterns'];
}
return array();
}
}
10 changes: 10 additions & 0 deletions lib/compat/wordpress-6.0/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ function gutenberg_register_global_styles_endpoints() {
$editor_settings->register_routes();
}
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_filter( 'rest_api_init', 'gutenberg_register_rest_pattern_directory' );
5 changes: 4 additions & 1 deletion lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-5.9/theme-templates.php';
require __DIR__ . '/editor-settings.php';
require __DIR__ . '/compat/wordpress-5.9/class-wp-theme-json-schema-gutenberg.php';
require __DIR__ . '/compat/wordpress-5.9/class-wp-theme-json-gutenberg.php';
require __DIR__ . '/compat/wordpress-5.9/class-wp-theme-json-5-9.php';
require __DIR__ . '/compat/wordpress-5.9/class-wp-theme-json-resolver-gutenberg.php';
require __DIR__ . '/compat/wordpress-5.9/theme.php';
require __DIR__ . '/compat/wordpress-5.9/admin-menu.php';
Expand All @@ -99,7 +99,10 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-6.0/post-lock.php';
require __DIR__ . '/compat/wordpress-6.0/blocks.php';
require __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-global-styles-controller.php';
require __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-pattern-directory-controller.php';
require __DIR__ . '/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php';
require __DIR__ . '/compat/wordpress-6.0/rest-api.php';
require __DIR__ . '/compat/wordpress-6.0/block-patterns.php';
require __DIR__ . '/compat/experimental/blocks.php';

require __DIR__ . '/blocks.php';
Expand Down
7 changes: 7 additions & 0 deletions schemas/json/theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,13 @@
"required": [ "name" ],
"additionalProperties": false
}
},
"patterns": {
"description": "An array of pattern slugs to be registered from the Pattern Directory.",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [ "version" ],
Expand Down
5 changes: 3 additions & 2 deletions test/emptytheme/theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"contentSize": "840px",
"wideSize": "1100px"
}
}
}
},
"patterns": [ "short-text-surrounded-by-round-images", "partner-logos" ]
}

0 comments on commit cab8cc5

Please sign in to comment.