From 528325b8f09768e45456538b90e634c9d477b938 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Tue, 1 Mar 2022 11:08:31 +0100 Subject: [PATCH 01/30] Add REST endpoint to fetch registered block patterns --- ...lass-wp-rest-block-patterns-controller.php | 121 ++++++++++++++++++ lib/load.php | 3 + lib/rest-api.php | 9 ++ 3 files changed, 133 insertions(+) create mode 100644 lib/class-wp-rest-block-patterns-controller.php diff --git a/lib/class-wp-rest-block-patterns-controller.php b/lib/class-wp-rest-block-patterns-controller.php new file mode 100644 index 0000000000000..8e9dded670234 --- /dev/null +++ b/lib/class-wp-rest-block-patterns-controller.php @@ -0,0 +1,121 @@ +namespace = 'wp/v2'; + $this->rest_base = 'block-patterns'; + } + + /** + * Registers the routes for the objects of the controller. + * + * @see register_rest_route() + */ + 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' ), + 'allow_batch' => array( 'v1' => true ), + ) + ); + } + + /** + * Checks whether a given request has permission to read navigation areas. + * + * @param WP_REST_Request $request Full details about the request. + * + * @return WP_Error|bool True if the request has read access, WP_Error object otherwise. + */ + public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + 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.', 'gutenberg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + /** + * Retrieves all block patterns. + * + * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. + */ + public function get_items() { + $data = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); + return rest_ensure_response( $data ); + } + + /** + * Retrieves the block pattern schema, conforming to JSON Schema. + * + * @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( + 'title' => array( + 'description' => __( 'The title of the block pattern.', 'gutenberg' ), + 'type' => 'string', + 'readonly' => true, + ), + 'name' => array( + 'description' => __( 'The name of the block pattern.', 'gutenberg' ), + 'type' => 'string', + 'readonly' => true, + ), + 'blockTypes' => array( + 'description' => __( 'Block types that the pattern is intended to be used with.', 'gutenberg' ), + 'type' => 'array', + 'readonly' => true, + ), + 'categories' => array( + 'description' => __( 'Categories that the block pattern belongs to.', 'gutenberg' ), + 'type' => 'array', + 'readonly' => true, + ), + 'content' => array( + 'description' => __( 'Block HTML Markup for the pattern.', 'gutenberg' ), + 'type' => 'string', + 'readonly' => true, + ), + ), + ); + + return $this->add_additional_fields_schema( $schema ); + } +} diff --git a/lib/load.php b/lib/load.php index b72686b1f4c42..b7e3cd0c15b93 100644 --- a/lib/load.php +++ b/lib/load.php @@ -40,6 +40,9 @@ function gutenberg_is_experiment_enabled( $name ) { if ( ! class_exists( 'WP_REST_Block_Navigation_Areas_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-block-navigation-areas-controller.php'; } + if ( ! class_exists( 'WP_REST_Block_Patterns_Controller' ) ) { + require_once __DIR__ . '/class-wp-rest-block-patterns-controller.php'; + } if ( ! class_exists( 'WP_REST_Menu_Locations_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-menu-locations-controller.php'; } diff --git a/lib/rest-api.php b/lib/rest-api.php index a28d23e6912e2..3173ff180a3ff 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -40,6 +40,15 @@ function gutenberg_register_rest_navigation_areas() { } add_action( 'rest_api_init', 'gutenberg_register_rest_navigation_areas' ); +/** + * Registers the block patterns REST API routes. + */ +function gutenberg_register_rest_block_patterns() { + $block_patterns = new WP_REST_Block_Patterns_Controller(); + $block_patterns->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); + /** * Registers the customizer nonces REST API routes. */ From fa4e1f5b605444314813c8ff878312ca5a79b44b Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Tue, 1 Mar 2022 11:27:04 +0100 Subject: [PATCH 02/30] Core Data: add getBlockPatterns selector (with resolver and state) --- docs/reference-guides/data/data-core.md | 12 ++++++++++++ packages/core-data/README.md | 12 ++++++++++++ packages/core-data/src/reducer.js | 10 ++++++++++ packages/core-data/src/resolvers.js | 5 +++++ packages/core-data/src/selectors.js | 11 +++++++++++ 5 files changed, 50 insertions(+) diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index 3353b1dd41c24..1b4f58d64fc73 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -94,6 +94,18 @@ _Returns_ - `?Array`: An array of autosaves for the post, or undefined if there is none. +### getBlockPatterns + +Retrieve the list of registered block patterns + +_Parameters_ + +- _state_ `Object`: Data state. + +_Returns_ + +- `Array`: Block pattern list. + ### getCurrentTheme Return the current theme. diff --git a/packages/core-data/README.md b/packages/core-data/README.md index aa26746a4df95..a7f8f306197fb 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -341,6 +341,18 @@ _Returns_ - `?Array`: An array of autosaves for the post, or undefined if there is none. +### getBlockPatterns + +Retrieve the list of registered block patterns + +_Parameters_ + +- _state_ `Object`: Data state. + +_Returns_ + +- `Array`: Block pattern list. + ### getCurrentTheme Return the current theme. diff --git a/packages/core-data/src/reducer.js b/packages/core-data/src/reducer.js index 0c901c8ab7a2f..4a913d7fec6d5 100644 --- a/packages/core-data/src/reducer.js +++ b/packages/core-data/src/reducer.js @@ -592,6 +592,15 @@ export function autosaves( state = {}, action ) { return state; } +export function blockPatterns( state = [], action ) { + switch ( action.type ) { + case 'RECEIVE_BLOCK_PATTERNS': + return action.patterns; + } + + return state; +} + export default combineReducers( { terms, users, @@ -606,4 +615,5 @@ export default combineReducers( { embedPreviews, userPermissions, autosaves, + blockPatterns, } ); diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index fda58b2fe9ea8..6c3b3b6dd8ecf 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -453,3 +453,8 @@ export const __experimentalGetCurrentThemeGlobalStylesVariations = () => async ( variations ); }; + +export const getBlockPatterns = () => async ( { dispatch } ) => { + const patterns = await apiFetch( { path: '/wp/v2/block-patterns' } ); + dispatch( { type: 'RECEIVE_BLOCK_PATTERNS', patterns } ); +}; diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index 01dc3024152af..7cc554c16e87e 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -962,3 +962,14 @@ export function __experimentalGetCurrentThemeGlobalStylesVariations( state ) { } return state.themeGlobalStyleVariations[ currentTheme.stylesheet ]; } + +/** + * Retrieve the list of registered block patterns + * + * @param {Object} state Data state. + * + * @return {Array} Block pattern list. + */ +export function getBlockPatterns( state ) { + return state.blockPatterns; +} From 78cbd5678b94730de7c4e149d754755d9d5215d3 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Tue, 1 Mar 2022 11:30:43 +0100 Subject: [PATCH 03/30] Editor: fetch block patterns from REST if not supplied in editor settings --- .../components/provider/use-block-editor-settings.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 68849f85a6b53..1d7ed4a78e3b2 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -61,6 +61,15 @@ function useBlockEditorSettings( settings, hasTemplate ) { }; }, [] ); + const { __experimentalBlockPatterns: settingsBlockPatterns } = settings; + const { blockPatterns } = useSelect( + ( select ) => ( { + blockPatterns: + settingsBlockPatterns ?? select( coreStore ).getBlockPatterns(), + } ), + [ settingsBlockPatterns ] + ); + const { undo } = useDispatch( editorStore ); const { saveEntityRecord } = useDispatch( coreStore ); @@ -86,7 +95,6 @@ function useBlockEditorSettings( settings, hasTemplate ) { ...pick( settings, [ '__experimentalBlockDirectory', '__experimentalBlockPatternCategories', - '__experimentalBlockPatterns', '__experimentalDiscussionSettings', '__experimentalFeatures', '__experimentalPreferredStyleVariations', @@ -128,6 +136,7 @@ function useBlockEditorSettings( settings, hasTemplate ) { ] ), mediaUpload: hasUploadPermissions ? mediaUpload : undefined, __experimentalReusableBlocks: reusableBlocks, + __experimentalBlockPatterns: blockPatterns, __experimentalFetchLinkSuggestions: ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ), __experimentalFetchRichUrlData: fetchUrlData, @@ -143,6 +152,7 @@ function useBlockEditorSettings( settings, hasTemplate ) { settings, hasUploadPermissions, reusableBlocks, + blockPatterns, canUseUnfilteredHTML, undo, hasTemplate, From e810c38001f71ac2c6c080ba69bb57660cc01a0b Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 3 Mar 2022 10:22:52 +0100 Subject: [PATCH 04/30] Site Editor: fetch block patterns from REST, don't preload in settings --- lib/full-site-editing/edit-site-page.php | 1 - .../src/components/block-editor/index.js | 37 ++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/full-site-editing/edit-site-page.php b/lib/full-site-editing/edit-site-page.php index 50ff0d0eb2b20..4b3e42a4ec4ef 100644 --- a/lib/full-site-editing/edit-site-page.php +++ b/lib/full-site-editing/edit-site-page.php @@ -120,7 +120,6 @@ static function( $classes ) { 'defaultTemplateTypes' => $indexed_template_types, 'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(), '__unstableHomeTemplate' => gutenberg_resolve_home_template(), - '__experimentalBlockPatterns' => WP_Block_Patterns_Registry::get_instance()->get_all_registered(), '__experimentalBlockPatternCategories' => WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(), ); diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js index de18566e66fff..06b7b13fc9f2b 100644 --- a/packages/edit-site/src/components/block-editor/index.js +++ b/packages/edit-site/src/components/block-editor/index.js @@ -8,7 +8,7 @@ import classnames from 'classnames'; */ import { useSelect, useDispatch } from '@wordpress/data'; import { useCallback, useRef } from '@wordpress/element'; -import { useEntityBlockEditor } from '@wordpress/core-data'; +import { useEntityBlockEditor, store as coreStore } from '@wordpress/core-data'; import { BlockList, BlockEditorProvider, @@ -42,17 +42,35 @@ const LAYOUT = { }; export default function BlockEditor( { setIsInserterOpen } ) { - const { settings, templateType, templateId, page } = useSelect( + const { settings } = useSelect( ( select ) => { - const { - getSettings, - getEditedPostType, - getEditedPostId, - getPage, - } = select( editSiteStore ); + let storedSettings = select( editSiteStore ).getSettings( + setIsInserterOpen + ); + + if ( ! storedSettings.__experimentalBlockPatterns ) { + storedSettings = { + ...storedSettings, + __experimentalBlockPatterns: select( + coreStore + ).getBlockPatterns(), + }; + } + + return { + settings: storedSettings, + }; + }, + [ setIsInserterOpen ] + ); + + const { templateType, templateId, page } = useSelect( + ( select ) => { + const { getEditedPostType, getEditedPostId, getPage } = select( + editSiteStore + ); return { - settings: getSettings( setIsInserterOpen ), templateType: getEditedPostType(), templateId: getEditedPostId(), page: getPage(), @@ -60,6 +78,7 @@ export default function BlockEditor( { setIsInserterOpen } ) { }, [ setIsInserterOpen ] ); + const [ blocks, onInput, onChange ] = useEntityBlockEditor( 'postType', templateType From 4b43ef7abf1af9fe8207eb82fdc181a542bcaf40 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 3 Mar 2022 12:03:24 +0100 Subject: [PATCH 05/30] Remove __experimentalBlockPatterns from settings generated by Core --- lib/compat/wordpress-6.0/block-editor-settings.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/compat/wordpress-6.0/block-editor-settings.php b/lib/compat/wordpress-6.0/block-editor-settings.php index 7a8b5091a279e..364da067699f6 100644 --- a/lib/compat/wordpress-6.0/block-editor-settings.php +++ b/lib/compat/wordpress-6.0/block-editor-settings.php @@ -166,3 +166,17 @@ function_exists( 'gutenberg_is_edit_site_page' ) && } add_filter( 'block_editor_settings_all', 'gutenberg_get_block_editor_settings', PHP_INT_MAX ); + +/** + * Removes the unwanted `__experimentalBlockPatterns` field from block editor settings. + * + * @param array $settings Existing block editor settings. + * + * @return array New block editor settings. + */ +function gutenberg_remove_block_patterns_settings( $settings ) { + unset( $settings['__experimentalBlockPatterns'] ); + return $settings; +} + +add_filter( 'block_editor_settings_all', 'gutenberg_remove_block_patterns_settings', PHP_INT_MAX ); From f7d681a555233492d2808bf040bfde0cfd2fc497 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Fri, 4 Mar 2022 14:17:48 +0100 Subject: [PATCH 06/30] Re-add the request param --- lib/class-wp-rest-block-patterns-controller.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/class-wp-rest-block-patterns-controller.php b/lib/class-wp-rest-block-patterns-controller.php index 8e9dded670234..88dfd949cb67c 100644 --- a/lib/class-wp-rest-block-patterns-controller.php +++ b/lib/class-wp-rest-block-patterns-controller.php @@ -70,9 +70,11 @@ public function get_items_permissions_check( $request ) { // phpcs:ignore Variab /** * Retrieves all block patterns. * + * @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() { + public function get_items( $request ) { $data = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); return rest_ensure_response( $data ); } From 3fc42fd36495df26999d4e706b958d54c79f295d Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Wed, 16 Mar 2022 13:57:17 +0100 Subject: [PATCH 07/30] Move the patterns endpoint to experimental namespace, change name --- lib/class-wp-rest-block-patterns-controller.php | 4 ++-- packages/core-data/src/resolvers.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/class-wp-rest-block-patterns-controller.php b/lib/class-wp-rest-block-patterns-controller.php index 88dfd949cb67c..77c0378b4f68a 100644 --- a/lib/class-wp-rest-block-patterns-controller.php +++ b/lib/class-wp-rest-block-patterns-controller.php @@ -17,8 +17,8 @@ class WP_REST_Block_Patterns_Controller extends WP_REST_Controller { * Constructor. */ public function __construct() { - $this->namespace = 'wp/v2'; - $this->rest_base = 'block-patterns'; + $this->namespace = '__experimental'; + $this->rest_base = 'block-patterns/patterns'; } /** diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 6c3b3b6dd8ecf..13d8dab379a11 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -455,6 +455,8 @@ export const __experimentalGetCurrentThemeGlobalStylesVariations = () => async ( }; export const getBlockPatterns = () => async ( { dispatch } ) => { - const patterns = await apiFetch( { path: '/wp/v2/block-patterns' } ); + const patterns = await apiFetch( { + path: '/__experimental/block-patterns/patterns', + } ); dispatch( { type: 'RECEIVE_BLOCK_PATTERNS', patterns } ); }; From 84d3c4ea6c855466af4256fa8fd3ca60797b474c Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 11:03:12 +0100 Subject: [PATCH 08/30] Disable lint error about unused parameter --- lib/class-wp-rest-block-patterns-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-rest-block-patterns-controller.php b/lib/class-wp-rest-block-patterns-controller.php index 77c0378b4f68a..c4b4ee42235b8 100644 --- a/lib/class-wp-rest-block-patterns-controller.php +++ b/lib/class-wp-rest-block-patterns-controller.php @@ -74,7 +74,7 @@ public function get_items_permissions_check( $request ) { // phpcs:ignore Variab * * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. */ - public function get_items( $request ) { + public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $data = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); return rest_ensure_response( $data ); } From 65735f580e5b6ba2356b9a8f7b3ade6f90bf98e7 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 11:52:34 +0100 Subject: [PATCH 09/30] Sync schema descriptions with pattern directory endpoint --- lib/class-wp-rest-block-patterns-controller.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/class-wp-rest-block-patterns-controller.php b/lib/class-wp-rest-block-patterns-controller.php index c4b4ee42235b8..ac37d3bfe0220 100644 --- a/lib/class-wp-rest-block-patterns-controller.php +++ b/lib/class-wp-rest-block-patterns-controller.php @@ -91,12 +91,12 @@ public function get_item_schema() { 'type' => 'object', 'properties' => array( 'title' => array( - 'description' => __( 'The title of the block pattern.', 'gutenberg' ), + 'description' => __( 'The pattern title, in human readable format.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, ), 'name' => array( - 'description' => __( 'The name of the block pattern.', 'gutenberg' ), + 'description' => __( "The pattern name.", 'gutenberg' ), 'type' => 'string', 'readonly' => true, ), @@ -106,12 +106,12 @@ public function get_item_schema() { 'readonly' => true, ), 'categories' => array( - 'description' => __( 'Categories that the block pattern belongs to.', 'gutenberg' ), + 'description' => __( "The pattern's category slugs.", 'gutenberg' ), 'type' => 'array', 'readonly' => true, ), 'content' => array( - 'description' => __( 'Block HTML Markup for the pattern.', 'gutenberg' ), + 'description' => __( 'The pattern content.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, ), From 2d01bebdf7b622161e1f8af74a555c22e173bc7c Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 12:09:23 +0100 Subject: [PATCH 10/30] Move the REST endpoint to lib/compat/wordpress-6.0 --- .../class-gutenberg-rest-block-patterns-controller.php} | 0 lib/compat/wordpress-6.0/rest-api.php | 9 +++++++++ lib/load.php | 4 +--- lib/rest-api.php | 9 --------- 4 files changed, 10 insertions(+), 12 deletions(-) rename lib/{class-wp-rest-block-patterns-controller.php => compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php} (100%) diff --git a/lib/class-wp-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php similarity index 100% rename from lib/class-wp-rest-block-patterns-controller.php rename to lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php diff --git a/lib/compat/wordpress-6.0/rest-api.php b/lib/compat/wordpress-6.0/rest-api.php index da1f6977e73ca..a02ae89a47c59 100644 --- a/lib/compat/wordpress-6.0/rest-api.php +++ b/lib/compat/wordpress-6.0/rest-api.php @@ -34,3 +34,12 @@ function gutenberg_register_edit_site_export_endpoint() { $editor_settings->register_routes(); } add_action( 'rest_api_init', 'gutenberg_register_edit_site_export_endpoint' ); + +/** + * Registers the block patterns REST API routes. + */ +function gutenberg_register_rest_block_patterns() { + $block_patterns = new WP_REST_Block_Patterns_Controller(); + $block_patterns->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); diff --git a/lib/load.php b/lib/load.php index b7e3cd0c15b93..72f1d7bc2124b 100644 --- a/lib/load.php +++ b/lib/load.php @@ -40,9 +40,6 @@ function gutenberg_is_experiment_enabled( $name ) { if ( ! class_exists( 'WP_REST_Block_Navigation_Areas_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-block-navigation-areas-controller.php'; } - if ( ! class_exists( 'WP_REST_Block_Patterns_Controller' ) ) { - require_once __DIR__ . '/class-wp-rest-block-patterns-controller.php'; - } if ( ! class_exists( 'WP_REST_Menu_Locations_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-menu-locations-controller.php'; } @@ -97,6 +94,7 @@ 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/block-template-utils.php'; +require __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.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-gutenberg-rest-edit-site-export-controller.php'; diff --git a/lib/rest-api.php b/lib/rest-api.php index 3173ff180a3ff..a28d23e6912e2 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -40,15 +40,6 @@ function gutenberg_register_rest_navigation_areas() { } add_action( 'rest_api_init', 'gutenberg_register_rest_navigation_areas' ); -/** - * Registers the block patterns REST API routes. - */ -function gutenberg_register_rest_block_patterns() { - $block_patterns = new WP_REST_Block_Patterns_Controller(); - $block_patterns->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); - /** * Registers the customizer nonces REST API routes. */ From 2368d789ce5769fe5954d0faf1c88f75bb934488 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 12:22:02 +0100 Subject: [PATCH 11/30] Load remote block patterns when processing the REST request --- .../class-gutenberg-rest-block-patterns-controller.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php index ac37d3bfe0220..e6743aa3a4680 100644 --- a/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php @@ -75,6 +75,10 @@ public function get_items_permissions_check( $request ) { // phpcs:ignore Variab * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. */ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + // Load block patterns from w.org. + _load_remote_block_patterns(); + _load_remote_featured_patterns(); + $data = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); return rest_ensure_response( $data ); } From c7c6fbf0cd59b417bf2bdd6b8ed746f5c5400304 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 12:49:31 +0100 Subject: [PATCH 12/30] Implement prepare_item_for_response --- ...tenberg-rest-block-patterns-controller.php | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php index e6743aa3a4680..6b3e2e4762513 100644 --- a/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php @@ -79,8 +79,34 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna _load_remote_block_patterns(); _load_remote_featured_patterns(); - $data = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); - return rest_ensure_response( $data ); + $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. + * + * @param object $item Raw pattern as registered, before any changes. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response + */ + public function prepare_item_for_response( $item, $request ) { + $prepared_pattern = array( + 'name' => $item['name'], + 'title' => $item['title'], + 'blockTypes' => $item['blockTypes'], + 'categories' => $item['categories'], + 'content' => $item['content'], + ); + + $prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request ); + + return new WP_REST_Response( $prepared_pattern ); } /** From c1a55d9c6664513178176284cffa57c43563338a Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 14:13:17 +0100 Subject: [PATCH 13/30] Prevent loading remote block patterns in site/post editor, limit to REST --- lib/compat/wordpress-5.9/block-patterns.php | 20 ++++------------ lib/compat/wordpress-6.0/block-patterns.php | 23 +++++++++++-------- ...tenberg-rest-block-patterns-controller.php | 5 ++-- lib/compat/wordpress-6.0/edit-form-blocks.php | 16 +++++++++++-- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/lib/compat/wordpress-5.9/block-patterns.php b/lib/compat/wordpress-5.9/block-patterns.php index 46fed5f2a26cc..b79869ea39bd2 100644 --- a/lib/compat/wordpress-5.9/block-patterns.php +++ b/lib/compat/wordpress-5.9/block-patterns.php @@ -17,9 +17,11 @@ function _load_remote_featured_patterns() { * * @param bool $should_load_remote */ - $should_load_remote = apply_filters( 'should_load_remote_block_patterns', true ); + if ( ! apply_filters( 'should_load_remote_block_patterns', true ) ) { + return; + } - if ( ! $should_load_remote ) { + if ( ! get_theme_support( 'core-block-patterns' ) ) { return; } @@ -43,18 +45,4 @@ function _load_remote_featured_patterns() { } } } - - add_action( - 'current_screen', - function( $current_screen ) { - if ( ! get_theme_support( 'core-block-patterns' ) ) { - return; - } - - $is_site_editor = ( function_exists( 'gutenberg_is_edit_site_page' ) && gutenberg_is_edit_site_page( $current_screen->id ) ); - if ( $current_screen->is_block_editor || $is_site_editor ) { - _load_remote_featured_patterns(); - } - } - ); } diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index b9ca9107fe413..c769d878b7e8a 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -10,10 +10,23 @@ * `theme.json` file. */ function gutenberg_register_remote_theme_patterns() { + if ( ! get_theme_support( 'core-block-patterns' ) ) { + return; + } + + if ( ! apply_filters( 'should_load_remote_block_patterns', true ) ) { + return; + } + + if ( ! WP_Theme_JSON_Resolver_Gutenberg::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 ); @@ -35,16 +48,6 @@ function gutenberg_register_remote_theme_patterns() { add_action( 'current_screen', function( $current_screen ) { - if ( ! get_theme_support( 'core-block-patterns' ) ) { - return; - } - if ( ! apply_filters( 'should_load_remote_block_patterns', true ) ) { - return; - } - if ( ! WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) { - return; - } - $is_site_editor = ( function_exists( 'gutenberg_is_edit_site_page' ) && gutenberg_is_edit_site_page( $current_screen->id ) ); if ( $current_screen->is_block_editor || $is_site_editor ) { gutenberg_register_remote_theme_patterns(); diff --git a/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php index 6b3e2e4762513..f7bc812c4fe95 100644 --- a/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php @@ -76,8 +76,9 @@ public function get_items_permissions_check( $request ) { // phpcs:ignore Variab */ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable // Load block patterns from w.org. - _load_remote_block_patterns(); - _load_remote_featured_patterns(); + _load_remote_block_patterns(); // patterns with the `core` keyword + _load_remote_featured_patterns(); // patterns in the `featured` category + gutenberg_register_remote_theme_patterns(); // patterns requested by current theme $response = array(); $patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); diff --git a/lib/compat/wordpress-6.0/edit-form-blocks.php b/lib/compat/wordpress-6.0/edit-form-blocks.php index f4d4bbb5e546e..e6ce38df0c9f0 100644 --- a/lib/compat/wordpress-6.0/edit-form-blocks.php +++ b/lib/compat/wordpress-6.0/edit-form-blocks.php @@ -1,6 +1,6 @@ id ) ); + if ( $is_site_editor || $current_screen->is_block_editor() ) { + add_filter( 'should_load_remote_block_patterns', '__return_false' ); + } +} +add_action( 'current_screen', 'disable_load_remote_patterns' ); From a71161ee0656183d59a2844d213a65d1a41ba218 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 14:29:22 +0100 Subject: [PATCH 14/30] Fix name of the REST controller file --- ...ntroller.php => class-wp-rest-block-patterns-controller.php} | 0 lib/load.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/compat/wordpress-6.0/{class-gutenberg-rest-block-patterns-controller.php => class-wp-rest-block-patterns-controller.php} (100%) diff --git a/lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php similarity index 100% rename from lib/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.php rename to lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php diff --git a/lib/load.php b/lib/load.php index 72f1d7bc2124b..6d5665b4ba044 100644 --- a/lib/load.php +++ b/lib/load.php @@ -94,10 +94,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/block-template-utils.php'; -require __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-block-patterns-controller.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-gutenberg-rest-edit-site-export-controller.php'; +require __DIR__ . '/compat/wordpress-6.0/class-wp-rest-block-patterns-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'; From 28f9c911fb34555696211393b7a656a833fc21ab Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 14:31:27 +0100 Subject: [PATCH 15/30] Fix code style nits reported by linter --- ...lass-wp-rest-block-patterns-controller.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php index f7bc812c4fe95..cccb733a6e506 100644 --- a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php @@ -76,15 +76,15 @@ public function get_items_permissions_check( $request ) { // phpcs:ignore Variab */ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable // Load block patterns from w.org. - _load_remote_block_patterns(); // patterns with the `core` keyword - _load_remote_featured_patterns(); // patterns in the `featured` category - gutenberg_register_remote_theme_patterns(); // patterns requested by current theme + _load_remote_block_patterns(); // Patterns with the `core` keyword. + _load_remote_featured_patterns(); // Patterns in the `featured` category. + gutenberg_register_remote_theme_patterns(); // Patterns requested by current theme. $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 ); + $response[] = $this->prepare_response_for_collection( $prepared_pattern ); } return rest_ensure_response( $response ); } @@ -98,11 +98,11 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna */ public function prepare_item_for_response( $item, $request ) { $prepared_pattern = array( - 'name' => $item['name'], - 'title' => $item['title'], - 'blockTypes' => $item['blockTypes'], - 'categories' => $item['categories'], - 'content' => $item['content'], + 'name' => $item['name'], + 'title' => $item['title'], + 'blockTypes' => $item['blockTypes'], + 'categories' => $item['categories'], + 'content' => $item['content'], ); $prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request ); @@ -127,7 +127,7 @@ public function get_item_schema() { 'readonly' => true, ), 'name' => array( - 'description' => __( "The pattern name.", 'gutenberg' ), + 'description' => __( 'The pattern name.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, ), From 3642a83e8d01e13a1ae4158ca8228f253ab2a096 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 14:43:31 +0100 Subject: [PATCH 16/30] Add context support --- .../class-wp-rest-block-patterns-controller.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php index cccb733a6e506..842d9da74c490 100644 --- a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php @@ -97,7 +97,7 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna * @return WP_REST_Response */ public function prepare_item_for_response( $item, $request ) { - $prepared_pattern = array( + $data = array( 'name' => $item['name'], 'title' => $item['title'], 'blockTypes' => $item['blockTypes'], @@ -105,9 +105,10 @@ public function prepare_item_for_response( $item, $request ) { 'content' => $item['content'], ); - $prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern, $request ); - - return new WP_REST_Response( $prepared_pattern ); + $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 ); } /** @@ -125,26 +126,31 @@ public function get_item_schema() { 'description' => __( 'The pattern title, in human readable format.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, + 'context' => array( 'view', 'embed' ), ), 'name' => array( 'description' => __( 'The pattern name.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, + 'context' => array( 'view', 'embed' ), ), 'blockTypes' => array( 'description' => __( 'Block types that the pattern is intended to be used with.', 'gutenberg' ), 'type' => 'array', 'readonly' => true, + 'context' => array( 'view', 'embed' ), ), 'categories' => array( 'description' => __( "The pattern's category slugs.", 'gutenberg' ), 'type' => 'array', 'readonly' => true, + 'context' => array( 'view', 'embed' ), ), 'content' => array( 'description' => __( 'The pattern content.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, + 'context' => array( 'view', 'embed' ), ), ), ); From 92b5c2a46bbf8682273bd9c51fa46fa443338d8c Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 15:08:24 +0100 Subject: [PATCH 17/30] Stop calling gutenberg_register_remote_theme_patterns in site/post editor --- lib/compat/wordpress-6.0/block-patterns.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index c769d878b7e8a..6c7517888124d 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -45,16 +45,6 @@ function gutenberg_register_remote_theme_patterns() { } } -add_action( - 'current_screen', - function( $current_screen ) { - $is_site_editor = ( function_exists( 'gutenberg_is_edit_site_page' ) && gutenberg_is_edit_site_page( $current_screen->id ) ); - if ( $current_screen->is_block_editor || $is_site_editor ) { - gutenberg_register_remote_theme_patterns(); - } - } -); - /** * Register any patterns that the active theme may provide under its * `./patterns/` directory. Each pattern is defined as a PHP file and defines From b6cd4ddd154b289f34f5c46e86190bb4156fa890 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 15:11:36 +0100 Subject: [PATCH 18/30] Document filter param --- lib/compat/wordpress-6.0/edit-form-blocks.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/compat/wordpress-6.0/edit-form-blocks.php b/lib/compat/wordpress-6.0/edit-form-blocks.php index e6ce38df0c9f0..68fc938e9e742 100644 --- a/lib/compat/wordpress-6.0/edit-form-blocks.php +++ b/lib/compat/wordpress-6.0/edit-form-blocks.php @@ -54,6 +54,8 @@ function optimize_preload_paths( $preload_paths ) { * Disables loading remote block patterns from REST while initializing the editor. * Nowadays these loads are done in the `block-patterns/patterns` REST endpoint, and * are undesired when initializing the block editor page, both in post and site editor. + * + * @param WP_Screen $current_screen WordPress current screen object. */ function disable_load_remote_patterns( $current_screen ) { $is_site_editor = ( function_exists( 'gutenberg_is_edit_site_page' ) && gutenberg_is_edit_site_page( $current_screen->id ) ); From 9663f48bf4d79a17728567a9d78aa4d753023aa3 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 15:32:37 +0100 Subject: [PATCH 19/30] Add REST endpoint for block pattern categories --- ...st-block-pattern-categories-controller.php | 134 ++++++++++++++++++ ...lass-wp-rest-block-patterns-controller.php | 2 +- lib/compat/wordpress-6.0/rest-api.php | 9 ++ lib/load.php | 1 + 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php diff --git a/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php new file mode 100644 index 0000000000000..9e9410462dc36 --- /dev/null +++ b/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php @@ -0,0 +1,134 @@ +namespace = '__experimental'; + $this->rest_base = 'block-patterns/categories'; + } + + /** + * Registers the routes for the objects of the controller. + * + * @see register_rest_route() + */ + 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' ), + 'allow_batch' => array( 'v1' => true ), + ) + ); + } + + /** + * Checks whether a given request has permission to read block patterns. + * + * @param WP_REST_Request $request Full details about the request. + * + * @return WP_Error|bool True if the request has read access, WP_Error object otherwise. + */ + public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + 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 pattern categories.', 'gutenberg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + /** + * Retrieves all block pattern categories. + * + * @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 ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $response = array(); + $categories = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(); + foreach ( $categories as $category ) { + $prepared_category = $this->prepare_item_for_response( $category, $request ); + $response[] = $this->prepare_response_for_collection( $prepared_category ); + } + return rest_ensure_response( $response ); + } + + /** + * Prepare a raw block pattern category before it gets output in a REST API response. + * + * @param object $item Raw category as registered, before any changes. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response + */ + public function prepare_item_for_response( $item, $request ) { + $data = array( + 'name' => $item['name'], + 'label' => $item['label'], + ); + + $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 ); + } + + /** + * Retrieves the block pattern category schema, conforming to JSON Schema. + * + * @return array Item schema data. + */ + public function get_item_schema() { + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'block-pattern-category', + 'type' => 'object', + 'properties' => array( + 'name' => array( + 'description' => __( 'The category name.', 'gutenberg' ), + 'type' => 'string', + 'readonly' => true, + 'context' => array( 'view', 'embed' ), + ), + 'label' => array( + 'description' => __( 'The category label, in human readable format.', 'gutenberg' ), + 'type' => 'string', + 'readonly' => true, + 'context' => array( 'view', 'embed' ), + ), + ), + ); + + return $this->add_additional_fields_schema( $schema ); + } +} diff --git a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php index 842d9da74c490..85e9982a921a1 100644 --- a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php @@ -43,7 +43,7 @@ public function register_routes() { } /** - * Checks whether a given request has permission to read navigation areas. + * Checks whether a given request has permission to read block patterns. * * @param WP_REST_Request $request Full details about the request. * diff --git a/lib/compat/wordpress-6.0/rest-api.php b/lib/compat/wordpress-6.0/rest-api.php index a02ae89a47c59..13e64dcf0ff13 100644 --- a/lib/compat/wordpress-6.0/rest-api.php +++ b/lib/compat/wordpress-6.0/rest-api.php @@ -43,3 +43,12 @@ function gutenberg_register_rest_block_patterns() { $block_patterns->register_routes(); } add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); + +/** + * Registers the block pattern categories REST API routes. + */ +function gutenberg_register_rest_block_pattern_categories() { + $block_patterns = new WP_REST_Block_Pattern_Categories_Controller(); + $block_patterns->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_block_pattern_categories' ); diff --git a/lib/load.php b/lib/load.php index 6d5665b4ba044..0e2b3e2796ed7 100644 --- a/lib/load.php +++ b/lib/load.php @@ -98,6 +98,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-pattern-directory-controller.php'; require __DIR__ . '/compat/wordpress-6.0/class-gutenberg-rest-edit-site-export-controller.php'; require __DIR__ . '/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php'; +require __DIR__ . '/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-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'; From b864fa0493522e24751d18d0c1847be5be795789 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 16:10:52 +0100 Subject: [PATCH 20/30] Core Data: add getBlockPatternCategories selector (with resolver and state) --- docs/reference-guides/data/data-core.md | 14 +++++++++++++- packages/core-data/CHANGELOG.md | 1 + packages/core-data/README.md | 14 +++++++++++++- packages/core-data/src/reducer.js | 10 ++++++++++ packages/core-data/src/resolvers.js | 7 +++++++ packages/core-data/src/selectors.js | 13 ++++++++++++- 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index 1b4f58d64fc73..aa493c7dcd997 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -94,9 +94,21 @@ _Returns_ - `?Array`: An array of autosaves for the post, or undefined if there is none. +### getBlockPatternCategories + +Retrieve the list of registered block pattern categories. + +_Parameters_ + +- _state_ `Object`: Data state. + +_Returns_ + +- `Array`: Block pattern category list. + ### getBlockPatterns -Retrieve the list of registered block patterns +Retrieve the list of registered block patterns. _Parameters_ diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index 4624ba5c9a6de..4a7722232adb5 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -6,6 +6,7 @@ ### New Features – The saveEntityRecord, saveEditedEntityRecord, and deleteEntityRecord actions now accept an optional throwOnError option (defaults to false). When set to true, any exceptions occurring when the action was executing are re-thrown, causing dispatch().saveEntityRecord() to reject with an error. ([#39258](https://github.com/WordPress/gutenberg/pull/39258)) +- Added support for fetching block patterns and their categories, with the `getBlockPatterns` and `getBlockPatternCategories` selectors. ## 4.2.0 (2022-03-11) diff --git a/packages/core-data/README.md b/packages/core-data/README.md index a7f8f306197fb..2dc7df4a04d71 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -341,9 +341,21 @@ _Returns_ - `?Array`: An array of autosaves for the post, or undefined if there is none. +### getBlockPatternCategories + +Retrieve the list of registered block pattern categories. + +_Parameters_ + +- _state_ `Object`: Data state. + +_Returns_ + +- `Array`: Block pattern category list. + ### getBlockPatterns -Retrieve the list of registered block patterns +Retrieve the list of registered block patterns. _Parameters_ diff --git a/packages/core-data/src/reducer.js b/packages/core-data/src/reducer.js index 4a913d7fec6d5..16b265bcf892e 100644 --- a/packages/core-data/src/reducer.js +++ b/packages/core-data/src/reducer.js @@ -601,6 +601,15 @@ export function blockPatterns( state = [], action ) { return state; } +export function blockPatternCategories( state = [], action ) { + switch ( action.type ) { + case 'RECEIVE_BLOCK_PATTERN_CATEGORIES': + return action.categories; + } + + return state; +} + export default combineReducers( { terms, users, @@ -616,4 +625,5 @@ export default combineReducers( { userPermissions, autosaves, blockPatterns, + blockPatternCategories, } ); diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 13d8dab379a11..1220b62c1d8a4 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -460,3 +460,10 @@ export const getBlockPatterns = () => async ( { dispatch } ) => { } ); dispatch( { type: 'RECEIVE_BLOCK_PATTERNS', patterns } ); }; + +export const getBlockPatternCategories = () => async ( { dispatch } ) => { + const patterns = await apiFetch( { + path: '/__experimental/block-patterns/categories', + } ); + dispatch( { type: 'RECEIVE_BLOCK_PATTERN_CATEGORIES', patterns } ); +}; diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index 7cc554c16e87e..67cd6b1aa5087 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -964,7 +964,7 @@ export function __experimentalGetCurrentThemeGlobalStylesVariations( state ) { } /** - * Retrieve the list of registered block patterns + * Retrieve the list of registered block patterns. * * @param {Object} state Data state. * @@ -973,3 +973,14 @@ export function __experimentalGetCurrentThemeGlobalStylesVariations( state ) { export function getBlockPatterns( state ) { return state.blockPatterns; } + +/** + * Retrieve the list of registered block pattern categories. + * + * @param {Object} state Data state. + * + * @return {Array} Block pattern category list. + */ +export function getBlockPatternCategories( state ) { + return state.blockPatternCategories; +} From 124196eb4cba07e765722839c82919004345e994 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 16:15:32 +0100 Subject: [PATCH 21/30] Load block pattern categories from REST instead of settings --- .../src/components/block-editor/index.js | 9 +++++++++ .../provider/use-block-editor-settings.js | 16 ++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js index 06b7b13fc9f2b..d0e390165fd7d 100644 --- a/packages/edit-site/src/components/block-editor/index.js +++ b/packages/edit-site/src/components/block-editor/index.js @@ -57,6 +57,15 @@ export default function BlockEditor( { setIsInserterOpen } ) { }; } + if ( ! storedSettings.__experimentalBlockPatternCategories ) { + storedSettings = { + ...storedSettings, + __experimentalBlockPatternCategories: select( + coreStore + ).getBlockPatternCategories(), + }; + } + return { settings: storedSettings, }; diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 1d7ed4a78e3b2..65f0577f9fd16 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -61,13 +61,20 @@ function useBlockEditorSettings( settings, hasTemplate ) { }; }, [] ); - const { __experimentalBlockPatterns: settingsBlockPatterns } = settings; - const { blockPatterns } = useSelect( + const { + __experimentalBlockPatterns: settingsBlockPatterns, + __experimentalBlockPatternCategories: settingsBlockPatternCategories, + } = settings; + + const { blockPatterns, blockPatternCategories } = useSelect( ( select ) => ( { blockPatterns: settingsBlockPatterns ?? select( coreStore ).getBlockPatterns(), + blockPatternCategories: + settingsBlockPatternCategories ?? + select( coreStore ).getBlockPatternCategories(), } ), - [ settingsBlockPatterns ] + [ settingsBlockPatterns, settingsBlockPatternCategories ] ); const { undo } = useDispatch( editorStore ); @@ -94,7 +101,6 @@ function useBlockEditorSettings( settings, hasTemplate ) { () => ( { ...pick( settings, [ '__experimentalBlockDirectory', - '__experimentalBlockPatternCategories', '__experimentalDiscussionSettings', '__experimentalFeatures', '__experimentalPreferredStyleVariations', @@ -137,6 +143,7 @@ function useBlockEditorSettings( settings, hasTemplate ) { mediaUpload: hasUploadPermissions ? mediaUpload : undefined, __experimentalReusableBlocks: reusableBlocks, __experimentalBlockPatterns: blockPatterns, + __experimentalBlockPatternCategories: blockPatternCategories, __experimentalFetchLinkSuggestions: ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ), __experimentalFetchRichUrlData: fetchUrlData, @@ -153,6 +160,7 @@ function useBlockEditorSettings( settings, hasTemplate ) { hasUploadPermissions, reusableBlocks, blockPatterns, + blockPatternCategories, canUseUnfilteredHTML, undo, hasTemplate, From f5be609d37092c5b1c9c5e8fa8754f9b6dac22c0 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 16:15:59 +0100 Subject: [PATCH 22/30] Remove block pattern categories from server-generated editor settings --- lib/compat/wordpress-6.0/block-editor-settings.php | 3 ++- lib/full-site-editing/edit-site-page.php | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-editor-settings.php b/lib/compat/wordpress-6.0/block-editor-settings.php index 364da067699f6..c070f64c7bf51 100644 --- a/lib/compat/wordpress-6.0/block-editor-settings.php +++ b/lib/compat/wordpress-6.0/block-editor-settings.php @@ -168,7 +168,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) && add_filter( 'block_editor_settings_all', 'gutenberg_get_block_editor_settings', PHP_INT_MAX ); /** - * Removes the unwanted `__experimentalBlockPatterns` field from block editor settings. + * Removes the unwanted block patterns fields from block editor settings. * * @param array $settings Existing block editor settings. * @@ -176,6 +176,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) && */ function gutenberg_remove_block_patterns_settings( $settings ) { unset( $settings['__experimentalBlockPatterns'] ); + unset( $settings['__experimentalBlockPatternCategories'] ); return $settings; } diff --git a/lib/full-site-editing/edit-site-page.php b/lib/full-site-editing/edit-site-page.php index 4b3e42a4ec4ef..5f96994942e15 100644 --- a/lib/full-site-editing/edit-site-page.php +++ b/lib/full-site-editing/edit-site-page.php @@ -120,7 +120,6 @@ static function( $classes ) { 'defaultTemplateTypes' => $indexed_template_types, 'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(), '__unstableHomeTemplate' => gutenberg_resolve_home_template(), - '__experimentalBlockPatternCategories' => WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(), ); /** From e1f3e3ecd7317db6a5284d65c51b906266db6e97 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 17 Mar 2022 18:09:21 +0100 Subject: [PATCH 23/30] Fix spacing issues --- ...s-wp-rest-block-pattern-categories-controller.php | 10 +++++----- lib/full-site-editing/edit-site-page.php | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php index 9e9410462dc36..7a8e5ac1d9d6d 100644 --- a/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php +++ b/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php @@ -75,7 +75,7 @@ public function get_items_permissions_check( $request ) { // phpcs:ignore Variab * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. */ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - $response = array(); + $response = array(); $categories = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(); foreach ( $categories as $category ) { $prepared_category = $this->prepare_item_for_response( $category, $request ); @@ -93,8 +93,8 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna */ public function prepare_item_for_response( $item, $request ) { $data = array( - 'name' => $item['name'], - 'label' => $item['label'], + 'name' => $item['name'], + 'label' => $item['label'], ); $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; @@ -114,13 +114,13 @@ public function get_item_schema() { 'title' => 'block-pattern-category', 'type' => 'object', 'properties' => array( - 'name' => array( + 'name' => array( 'description' => __( 'The category name.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, 'context' => array( 'view', 'embed' ), ), - 'label' => array( + 'label' => array( 'description' => __( 'The category label, in human readable format.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, diff --git a/lib/full-site-editing/edit-site-page.php b/lib/full-site-editing/edit-site-page.php index 5f96994942e15..bd9bb17286a38 100644 --- a/lib/full-site-editing/edit-site-page.php +++ b/lib/full-site-editing/edit-site-page.php @@ -114,12 +114,12 @@ static function( $classes ) { } $custom_settings = array( - 'siteUrl' => site_url(), - 'postsPerPage' => get_option( 'posts_per_page' ), - 'styles' => gutenberg_get_editor_styles(), - 'defaultTemplateTypes' => $indexed_template_types, - 'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(), - '__unstableHomeTemplate' => gutenberg_resolve_home_template(), + 'siteUrl' => site_url(), + 'postsPerPage' => get_option( 'posts_per_page' ), + 'styles' => gutenberg_get_editor_styles(), + 'defaultTemplateTypes' => $indexed_template_types, + 'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(), + '__unstableHomeTemplate' => gutenberg_resolve_home_template(), ); /** From 8d41a92963e517055a7315fe27ba93725e2cb9a0 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Tue, 22 Mar 2022 13:06:48 +0100 Subject: [PATCH 24/30] Return only requested fields --- ...p-rest-block-pattern-categories-controller.php | 12 ++++++++---- .../class-wp-rest-block-patterns-controller.php | 15 ++++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php index 7a8e5ac1d9d6d..8edc2080b33dd 100644 --- a/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php +++ b/lib/compat/wordpress-6.0/class-wp-rest-block-pattern-categories-controller.php @@ -92,10 +92,14 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna * @return WP_REST_Response */ public function prepare_item_for_response( $item, $request ) { - $data = array( - 'name' => $item['name'], - 'label' => $item['label'], - ); + $fields = $this->get_fields_for_response( $request ); + $keys = array( 'name', 'label' ); + $data = array(); + foreach ( $keys as $key ) { + if ( 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 ); diff --git a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php index 85e9982a921a1..d11a962fde8e5 100644 --- a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php @@ -97,13 +97,14 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna * @return WP_REST_Response */ public function prepare_item_for_response( $item, $request ) { - $data = array( - 'name' => $item['name'], - 'title' => $item['title'], - 'blockTypes' => $item['blockTypes'], - 'categories' => $item['categories'], - 'content' => $item['content'], - ); + $fields = $this->get_fields_for_response( $request ); + $keys = array( 'name', 'title', 'blockTypes', 'categories', 'content' ); + $data = array(); + foreach ( $keys as $key ) { + if ( 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 ); From 5896ea3d97a8c1c04dc5f53e54e287822484adc6 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Tue, 22 Mar 2022 14:59:11 +0100 Subject: [PATCH 25/30] Unit tests for the REST controllers --- ...ock-pattern-categories-controller-test.php | 64 ++++++++++++++++++ ...wp-rest-block-patterns-controller-test.php | 65 +++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 phpunit/class-wp-rest-block-pattern-categories-controller-test.php create mode 100644 phpunit/class-wp-rest-block-patterns-controller-test.php diff --git a/phpunit/class-wp-rest-block-pattern-categories-controller-test.php b/phpunit/class-wp-rest-block-pattern-categories-controller-test.php new file mode 100644 index 0000000000000..551392e37ffb2 --- /dev/null +++ b/phpunit/class-wp-rest-block-pattern-categories-controller-test.php @@ -0,0 +1,64 @@ +user->create( + array( + 'role' => 'administrator', + ) + ); + } + + public function test_register_routes() { + $routes = rest_get_server()->get_routes(); + $this->assertArrayHasKey( + '/__experimental/block-patterns/categories', + $routes, + 'The categories route does not exist' + ); + } + + public function test_get_items() { + // Change this when the registered Core categories change. + $expected_names = array( + 'buttons', + 'columns', + 'gallery', + 'header', + 'text', + 'query', + ); + $expected_fields = array( 'name', 'label' ); + + wp_set_current_user( self::$admin_id ); + + $request = new WP_REST_Request( 'GET', '/__experimental/block-patterns/categories' ); + $request['_fields'] = 'name,label'; + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertCount( count( $expected_names ), $data ); + foreach ( $data as $idx => $item ) { + $this->assertEquals( $expected_names[ $idx ], $item['name'] ); + $this->assertEquals( $expected_fields, array_keys( $item ) ); + } + } + + /** + * Abstract methods that we must implement. + */ + public function test_context_param() {} + public function test_get_item() {} + public function test_create_item() {} + public function test_update_item() {} + public function test_delete_item() {} + public function test_prepare_item() {} + public function test_get_item_schema() {} +} diff --git a/phpunit/class-wp-rest-block-patterns-controller-test.php b/phpunit/class-wp-rest-block-patterns-controller-test.php new file mode 100644 index 0000000000000..cb6493d9d88e1 --- /dev/null +++ b/phpunit/class-wp-rest-block-patterns-controller-test.php @@ -0,0 +1,65 @@ +user->create( + array( + 'role' => 'administrator', + ) + ); + } + + public function test_register_routes() { + $routes = rest_get_server()->get_routes(); + $this->assertArrayHasKey( + '/__experimental/block-patterns/patterns', + $routes, + 'The patterns route does not exist' + ); + } + + public function test_get_items() { + // Change this when the registered Core patterns change. + $expected_names = array( + 'core/query-standard-posts', + 'core/query-medium-posts', + 'core/query-small-posts', + 'core/query-grid-posts', + 'core/query-large-title-posts', + 'core/query-offset-posts', + 'core/social-links-shared-background-color', + ); + $expected_fields = array( 'name', 'content' ); + + wp_set_current_user( self::$admin_id ); + + $request = new WP_REST_Request( 'GET', '/__experimental/block-patterns/patterns' ); + $request['_fields'] = 'name,content'; + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertCount( count( $expected_names ), $data ); + foreach ( $data as $idx => $item ) { + $this->assertEquals( $expected_names[ $idx ], $item['name'] ); + $this->assertEquals( $expected_fields, array_keys( $item ) ); + } + } + + /** + * Abstract methods that we must implement. + */ + public function test_context_param() {} + public function test_get_item() {} + public function test_create_item() {} + public function test_update_item() {} + public function test_delete_item() {} + public function test_prepare_item() {} + public function test_get_item_schema() {} +} From 0f35b2c3508a81ccd825cdc95958935fac83d7ed Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Wed, 23 Mar 2022 15:37:48 +0100 Subject: [PATCH 26/30] Fixup action field name when receiving categories --- packages/core-data/src/resolvers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 1220b62c1d8a4..ad7b56befca3e 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -462,8 +462,8 @@ export const getBlockPatterns = () => async ( { dispatch } ) => { }; export const getBlockPatternCategories = () => async ( { dispatch } ) => { - const patterns = await apiFetch( { + const categories = await apiFetch( { path: '/__experimental/block-patterns/categories', } ); - dispatch( { type: 'RECEIVE_BLOCK_PATTERN_CATEGORIES', patterns } ); + dispatch( { type: 'RECEIVE_BLOCK_PATTERN_CATEGORIES', categories } ); }; From 71d6c78015fbf288b78d54dae729df7da6cf5510 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 24 Mar 2022 13:29:33 +0100 Subject: [PATCH 27/30] Use mock registries in unit tests --- ...ock-pattern-categories-controller-test.php | 26 +++++++----- ...wp-rest-block-patterns-controller-test.php | 42 +++++++++++++------ 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/phpunit/class-wp-rest-block-pattern-categories-controller-test.php b/phpunit/class-wp-rest-block-pattern-categories-controller-test.php index 551392e37ffb2..474770a4acee5 100644 --- a/phpunit/class-wp-rest-block-pattern-categories-controller-test.php +++ b/phpunit/class-wp-rest-block-pattern-categories-controller-test.php @@ -16,6 +16,17 @@ public static function wpSetupBeforeClass( $factory ) { ); } + public function setup_mock_registry() { + $categories_reflection = new ReflectionClass( 'WP_Block_Pattern_Categories_Registry' ); + $categories_instance_property = $categories_reflection->getProperty( 'instance' ); + $categories_instance_property->setAccessible( true ); + $categories_instance = new WP_Block_Pattern_Categories_Registry(); + $categories_reflection->setStaticPropertyValue( 'instance', $categories_instance ); + + $categories_instance->register( 'test', array( 'label' => 'Test' ) ); + $categories_instance->register( 'query', array( 'label' => 'Query' ) ); + } + public function test_register_routes() { $routes = rest_get_server()->get_routes(); $this->assertArrayHasKey( @@ -26,19 +37,12 @@ public function test_register_routes() { } public function test_get_items() { - // Change this when the registered Core categories change. - $expected_names = array( - 'buttons', - 'columns', - 'gallery', - 'header', - 'text', - 'query', - ); - $expected_fields = array( 'name', 'label' ); - + $this->setup_mock_registry(); wp_set_current_user( self::$admin_id ); + $expected_names = array( 'test', 'query' ); + $expected_fields = array( 'name', 'label' ); + $request = new WP_REST_Request( 'GET', '/__experimental/block-patterns/categories' ); $request['_fields'] = 'name,label'; $response = rest_get_server()->dispatch( $request ); diff --git a/phpunit/class-wp-rest-block-patterns-controller-test.php b/phpunit/class-wp-rest-block-patterns-controller-test.php index cb6493d9d88e1..470c213748329 100644 --- a/phpunit/class-wp-rest-block-patterns-controller-test.php +++ b/phpunit/class-wp-rest-block-patterns-controller-test.php @@ -16,6 +16,32 @@ public static function wpSetupBeforeClass( $factory ) { ); } + public function setup_mock_registry() { + $block_patterns_reflection = new ReflectionClass( 'WP_Block_Patterns_Registry' ); + $block_patterns_instance_property = $block_patterns_reflection->getProperty( 'instance' ); + $block_patterns_instance_property->setAccessible( true ); + $block_patterns_instance = new WP_Block_Patterns_Registry(); + $block_patterns_reflection->setStaticPropertyValue( 'instance', $block_patterns_instance ); + + $block_patterns_instance->register( + 'test/one', + array( + 'title' => 'Pattern One', + 'categories' => array( 'test' ), + 'viewportWidth' => 1440, + 'content' => '

One

', + ) + ); + $block_patterns_instance->register( + 'test/two', + array( + 'title' => 'Pattern Two', + 'categories' => array( 'test' ), + 'content' => '

Two

', + ) + ); + } + public function test_register_routes() { $routes = rest_get_server()->get_routes(); $this->assertArrayHasKey( @@ -26,20 +52,12 @@ public function test_register_routes() { } public function test_get_items() { - // Change this when the registered Core patterns change. - $expected_names = array( - 'core/query-standard-posts', - 'core/query-medium-posts', - 'core/query-small-posts', - 'core/query-grid-posts', - 'core/query-large-title-posts', - 'core/query-offset-posts', - 'core/social-links-shared-background-color', - ); - $expected_fields = array( 'name', 'content' ); - + $this->setup_mock_registry(); wp_set_current_user( self::$admin_id ); + $expected_names = array( 'test/one', 'test/two' ); + $expected_fields = array( 'name', 'content' ); + $request = new WP_REST_Request( 'GET', '/__experimental/block-patterns/patterns' ); $request['_fields'] = 'name,content'; $response = rest_get_server()->dispatch( $request ); From bbe130c9e0408009f2e3c014cb165d313fbcabc4 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 24 Mar 2022 13:30:01 +0100 Subject: [PATCH 28/30] Add missing pattern properties --- ...lass-wp-rest-block-patterns-controller.php | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php index d11a962fde8e5..f42e733a1f824 100644 --- a/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.0/class-wp-rest-block-patterns-controller.php @@ -98,10 +98,19 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna */ public function prepare_item_for_response( $item, $request ) { $fields = $this->get_fields_for_response( $request ); - $keys = array( 'name', 'title', 'blockTypes', 'categories', 'content' ); + $keys = array( + 'name', + 'title', + 'description', + 'viewportWidth', + 'blockTypes', + 'categories', + 'keywords', + 'content', + ); $data = array(); foreach ( $keys as $key ) { - if ( rest_is_field_included( $key, $fields ) ) { + if ( isset( $item[ $key ] ) && rest_is_field_included( $key, $fields ) ) { $data[ $key ] = $item[ $key ]; } } @@ -123,31 +132,49 @@ public function get_item_schema() { 'title' => 'block-pattern', 'type' => 'object', 'properties' => array( - 'title' => array( + 'name' => array( + 'description' => __( 'The pattern name.', 'gutenberg' ), + 'type' => 'string', + 'readonly' => true, + 'context' => array( 'view', 'embed' ), + ), + 'title' => array( 'description' => __( 'The pattern title, in human readable format.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, 'context' => array( 'view', 'embed' ), ), - 'name' => array( - 'description' => __( 'The pattern name.', 'gutenberg' ), + 'description' => array( + 'description' => __( 'The pattern detailed description.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, 'context' => array( 'view', 'embed' ), ), - 'blockTypes' => array( + 'viewportWidth' => array( + 'description' => __( 'The pattern viewport width for inserter preview.', 'gutenberg' ), + 'type' => 'number', + 'readonly' => true, + 'context' => array( 'view', 'embed' ), + ), + 'blockTypes' => array( 'description' => __( 'Block types that the pattern is intended to be used with.', 'gutenberg' ), 'type' => 'array', 'readonly' => true, 'context' => array( 'view', 'embed' ), ), - 'categories' => array( - 'description' => __( "The pattern's category slugs.", 'gutenberg' ), + 'categories' => array( + 'description' => __( 'The pattern category slugs.', 'gutenberg' ), + 'type' => 'array', + 'readonly' => true, + 'context' => array( 'view', 'embed' ), + ), + 'keywords' => array( + 'description' => __( 'The pattern keywords.', 'gutenberg' ), 'type' => 'array', 'readonly' => true, 'context' => array( 'view', 'embed' ), ), - 'content' => array( + 'content' => array( 'description' => __( 'The pattern content.', 'gutenberg' ), 'type' => 'string', 'readonly' => true, From e8c1a9c70b7d82606752c4855d3cb31694b4716e Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Mon, 28 Mar 2022 09:56:55 +0200 Subject: [PATCH 29/30] Restore original registry instance after test is finished --- ...ock-pattern-categories-controller-test.php | 34 +++++++++------- ...wp-rest-block-patterns-controller-test.php | 39 +++++++++++-------- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/phpunit/class-wp-rest-block-pattern-categories-controller-test.php b/phpunit/class-wp-rest-block-pattern-categories-controller-test.php index 474770a4acee5..0aded2563d647 100644 --- a/phpunit/class-wp-rest-block-pattern-categories-controller-test.php +++ b/phpunit/class-wp-rest-block-pattern-categories-controller-test.php @@ -2,6 +2,7 @@ class WP_REST_Block_Pattern_Categories_Controller_Test extends WP_Test_REST_Controller_Testcase { protected static $admin_id; + protected static $orig_registry; public function set_up() { parent::set_up(); @@ -9,22 +10,28 @@ public function set_up() { } public static function wpSetupBeforeClass( $factory ) { - self::$admin_id = $factory->user->create( - array( - 'role' => 'administrator', - ) - ); + // Create a test user. + self::$admin_id = $factory->user->create( array( 'role' => 'administrator' ) ); + + // Setup an empty testing instance of `WP_Block_Pattern_Categories_Registry` and save the original. + $reflection = new ReflectionClass( 'WP_Block_Pattern_Categories_Registry' ); + $reflection->getProperty( 'instance' )->setAccessible( true ); + self::$orig_registry = $reflection->getStaticPropertyValue( 'instance' ); + $test_registry = new WP_Block_Pattern_Categories_Registry(); + $reflection->setStaticPropertyValue( 'instance', $test_registry ); + + // Register some categories in the test registry. + $test_registry->register( 'test', array( 'label' => 'Test' ) ); + $test_registry->register( 'query', array( 'label' => 'Query' ) ); } - public function setup_mock_registry() { - $categories_reflection = new ReflectionClass( 'WP_Block_Pattern_Categories_Registry' ); - $categories_instance_property = $categories_reflection->getProperty( 'instance' ); - $categories_instance_property->setAccessible( true ); - $categories_instance = new WP_Block_Pattern_Categories_Registry(); - $categories_reflection->setStaticPropertyValue( 'instance', $categories_instance ); + public static function wpTearDownAfterClass() { + // Delete the test user. + self::delete_user( self::$admin_id ); - $categories_instance->register( 'test', array( 'label' => 'Test' ) ); - $categories_instance->register( 'query', array( 'label' => 'Query' ) ); + // Restore the original registry instance. + $reflection = new ReflectionClass( 'WP_Block_Pattern_Categories_Registry' ); + $reflection->setStaticPropertyValue( 'instance', self::$orig_registry ); } public function test_register_routes() { @@ -37,7 +44,6 @@ public function test_register_routes() { } public function test_get_items() { - $this->setup_mock_registry(); wp_set_current_user( self::$admin_id ); $expected_names = array( 'test', 'query' ); diff --git a/phpunit/class-wp-rest-block-patterns-controller-test.php b/phpunit/class-wp-rest-block-patterns-controller-test.php index 470c213748329..4cecb355f3a69 100644 --- a/phpunit/class-wp-rest-block-patterns-controller-test.php +++ b/phpunit/class-wp-rest-block-patterns-controller-test.php @@ -2,28 +2,26 @@ class WP_REST_Block_Patterns_Controller_Test extends WP_Test_REST_Controller_Testcase { protected static $admin_id; + protected static $orig_registry; public function set_up() { parent::set_up(); switch_theme( 'emptytheme' ); } - public static function wpSetupBeforeClass( $factory ) { - self::$admin_id = $factory->user->create( - array( - 'role' => 'administrator', - ) - ); - } + public static function wpSetUpBeforeClass( $factory ) { + // Create a test user. + self::$admin_id = $factory->user->create( array( 'role' => 'administrator' ) ); - public function setup_mock_registry() { - $block_patterns_reflection = new ReflectionClass( 'WP_Block_Patterns_Registry' ); - $block_patterns_instance_property = $block_patterns_reflection->getProperty( 'instance' ); - $block_patterns_instance_property->setAccessible( true ); - $block_patterns_instance = new WP_Block_Patterns_Registry(); - $block_patterns_reflection->setStaticPropertyValue( 'instance', $block_patterns_instance ); + // Setup an empty testing instance of `WP_Block_Patterns_Registry` and save the original. + $reflection = new ReflectionClass( 'WP_Block_Patterns_Registry' ); + $reflection->getProperty( 'instance' )->setAccessible( true ); + self::$orig_registry = $reflection->getStaticPropertyValue( 'instance' ); + $test_registry = new WP_Block_Patterns_Registry(); + $reflection->setStaticPropertyValue( 'instance', $test_registry ); - $block_patterns_instance->register( + // Register some patterns in the test registry. + $test_registry->register( 'test/one', array( 'title' => 'Pattern One', @@ -32,7 +30,8 @@ public function setup_mock_registry() { 'content' => '

One

', ) ); - $block_patterns_instance->register( + + $test_registry->register( 'test/two', array( 'title' => 'Pattern Two', @@ -42,6 +41,15 @@ public function setup_mock_registry() { ); } + public static function wpTearDownAfterClass() { + // Delete the test user. + self::delete_user( self::$admin_id ); + + // Restore the original registry instance. + $reflection = new ReflectionClass( 'WP_Block_Patterns_Registry' ); + $reflection->setStaticPropertyValue( 'instance', self::$orig_registry ); + } + public function test_register_routes() { $routes = rest_get_server()->get_routes(); $this->assertArrayHasKey( @@ -52,7 +60,6 @@ public function test_register_routes() { } public function test_get_items() { - $this->setup_mock_registry(); wp_set_current_user( self::$admin_id ); $expected_names = array( 'test/one', 'test/two' ); From f2edbd56c791f02af4d7c6cbaa90b5dba9b5bba7 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Mon, 28 Mar 2022 12:01:57 +0200 Subject: [PATCH 30/30] Add PHP docs comments to unit test suites --- ...rest-block-pattern-categories-controller-test.php | 12 ++++++++++++ .../class-wp-rest-block-patterns-controller-test.php | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/phpunit/class-wp-rest-block-pattern-categories-controller-test.php b/phpunit/class-wp-rest-block-pattern-categories-controller-test.php index 0aded2563d647..8f22b0eefad5b 100644 --- a/phpunit/class-wp-rest-block-pattern-categories-controller-test.php +++ b/phpunit/class-wp-rest-block-pattern-categories-controller-test.php @@ -1,5 +1,17 @@