-
Notifications
You must be signed in to change notification settings - Fork 4.7k
[Pattern Directory]: Allow pattern registration from directory with theme.json #38323
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
Changes from all commits
3d68a4f
5d795ef
6741a08
2f1473c
0852d94
c435c6a
73c0d96
9cca636
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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’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 ); | ||
| } | ||
| } |
| 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(); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of copying the entire file to lib/compat/wordpress-6.0, should we instead keep the original file under The advantage of this is that we know exactly what's on 5.9 and what was added on 6.0
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems in many cases including this one, this is quite hard due to private functions and properties. In this case for making it work we need to override the I then tried to move more functions to the new child class but we have so many private functions/properties there used, that we would need to move so much code there, changing the names(prefix I think we cannot avoid this easily - at least from what I could see, and maybe we should copy the whole class as is here in What do you think? Am I missing something php related?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like the problem here is the change in For now, I guess we don't have any other option than copying everything 🤷
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally, I think each PR should add its own needed changes, if we can land this without copying everything, I'd do that. If we need to make another change later, we'll need to figure how to do it at that point.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if we add a couple of filters to the JSON parser in WP-Core for 5.9.1?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's the thing though, the way I see it it as I explained above, we cannot avoid copying the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @youknowriad @ntsekouras I haven't yet dug into an alternative way to extend this. I'll do it later. I wanted you to be aware that in https://github.com/WordPress/gutenberg/pull/37140/files#r801650187 we'll need to change from Perhaps we can do similar modifications to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The required changes in core for this to work with a child class that changes
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1243,6 +1243,13 @@ | |
| "required": [ "name" ], | ||
| "additionalProperties": false | ||
| } | ||
| }, | ||
| "patterns": { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for doing this! I thought the reference doc for theme.json was updated upon the schema changes. Apparently, it doesn't. I've now realized that it only contains data coming from the settings & styles keys, but not from
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| "description": "An array of pattern slugs to be registered from the Pattern Directory.", | ||
| "type": "array", | ||
| "items": { | ||
| "type": "string" | ||
| } | ||
| } | ||
| }, | ||
| "required": [ "version" ], | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.