From 3b29871c5624ba3f881ea122a8e5782f49eff426 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 2 Feb 2024 16:41:47 +0100 Subject: [PATCH 01/24] Block Hooks: Add controller to expose hooked blocks for a given anchor block --- ...berg-rest-hooked-blocks-controller-6-5.php | 255 ++++++++++++++++++ lib/compat/wordpress-6.5/rest-api.php | 10 + lib/load.php | 1 + 3 files changed, 266 insertions(+) create mode 100644 lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php new file mode 100644 index 00000000000000..18d1e818808791 --- /dev/null +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -0,0 +1,255 @@ +namespace = 'wp/v2'; + $this->rest_base = 'hooked-blocks'; + $this->block_registry = WP_Block_Type_Registry::get_instance(); + } + + /** + * Registers the routes for the objects of the controller. + * + * @since 6.5.0 + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+)', + array( + 'args' => array( + 'name' => array( + 'description' => __( 'Block name.' ), + 'type' => 'string', + ), + 'namespace' => array( + 'description' => __( 'Block namespace.' ), + 'type' => 'string', + ), + ), + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + } + + /** + * Checks whether a given request has permission to read hooked block type. + * + * @since 6.5.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has read access, WP_Error object otherwise. + */ + public function get_items_permissions_check( $request ) { + return $this->check_read_permission(); + } + + /** + * Retrieves all hooked block types, depending on user and anchor block context. + * + * @since 6.5.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function get_items( $request ) { + $data = array(); + + // TODO: Set context based on the request. + $context = null; + + $hooked_block_types = get_hooked_blocks(); + foreach( $hooked_block_types as $anchor_block_type => $hooked_block_types_for_anchor_block ) { + foreach( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { + $hooked_block_types_for_anchor_block[ $relative_position ] = + apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context ); + } + $hooked_block_types[ $anchor_block_type ] = $hooked_block_types_for_anchor_block; + } + + // Retrieve the list of registered collection query parameters. + $registered = $this->get_collection_params(); + $namespace = ''; + if ( isset( $registered['namespace'] ) && ! empty( $request['namespace'] ) ) { + $namespace = $request['namespace']; + } + + foreach ( $hooked_block_types as $anchor_block_type => $hooked_block_types_for_anchor_block ) { + if ( $namespace ) { + list ( $block_namespace ) = explode( '/', $anchor_block_type ); + + if ( $namespace !== $block_namespace ) { + continue; + } + } + $data[ $anchor_block_type ] = $this->prepare_response_for_collection( $hooked_block_types_for_anchor_block ); + } + + return rest_ensure_response( $data ); + } + + /** + * Checks if a given request has access to read a hooked block type. + * + * @since 6.5.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. + */ + public function get_item_permissions_check( $request ) { + $check = $this->check_read_permission(); + if ( is_wp_error( $check ) ) { + return $check; + } + $block_name = sprintf( '%s/%s', $request['namespace'], $request['name'] ); + $block_type = $this->get_block( $block_name ); + if ( is_wp_error( $block_type ) ) { + return $block_type; + } + + return true; + } + + /** + * Checks whether a given hooked block type should be visible. + * + * @since 6.5.0 + * + * @return true|WP_Error True if the block type is visible, WP_Error otherwise. + */ + protected function check_read_permission() { + 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_block_type_cannot_view', __( 'Sorry, you are not allowed to manage block types.' ), array( 'status' => rest_authorization_required_code() ) ); + } + + /** + * Get the block, if the name is valid. + * + * @since 6.5.0 + * + * @param string $name Block name. + * @return WP_Block_Type|WP_Error Block type object if name is valid, WP_Error otherwise. + */ + protected function get_block( $name ) { + $block_type = $this->block_registry->get_registered( $name ); + if ( empty( $block_type ) ) { + return new WP_Error( 'rest_block_type_invalid', __( 'Invalid block type.' ), array( 'status' => 404 ) ); + } + + return $block_type; + } + + /** + * Retrieves hooked blocks for a specific anchor block type. + * + * @since 6.5.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function get_item( $request ) { + $block_name = sprintf( '%s/%s', $request['namespace'], $request['name'] ); + $block_type = $this->get_block( $block_name ); + if ( is_wp_error( $block_type ) ) { + return $block_type; + } + + $all_hooked_block_types = get_hooked_blocks(); + $hooked_block_types_for_anchor_block = isset( $all_hooked_block_types[ $block_name ] ) + ? $all_hooked_block_types[ $block_name ] + : array(); + + // TODO: Set context based on the request. + $context = null; + + foreach( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { + $hooked_block_types_for_anchor_block[ $relative_position ] = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $block_name, $context ); + } + + $data = $this->prepare_item_for_response( $hooked_block_types_for_anchor_block, $request ); + + return rest_ensure_response( $data ); + } + + /** + * Prepares a hooked blocks array for serialization. + * + * @since 6.50 + * + * @param array $item Block type data. + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response Block type data. + */ + public function prepare_item_for_response( $item, $request ) { + // TODO: Filter? + $response = rest_ensure_response( $item ); + return $response; + } + + /** + * Retrieves the query params for the hooked blocks collection. + * + * @since 6.5.0 + * + * @return array Collection parameters. + */ + public function get_collection_params() { + return array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + 'entity' => array( + 'description' => __( 'Entity type to get hooked blocks for.' ), + 'type' => 'string', + ), + 'id' => array( + 'description' => __( 'Entity ID to get hooked blocks for.' ), + //'type' => 'integer', // TODO: Can be an integer, but also a string(?) (template IDs?) + ) + ); + } +} diff --git a/lib/compat/wordpress-6.5/rest-api.php b/lib/compat/wordpress-6.5/rest-api.php index 12d789fb58b869..ef59d3c9e7b53e 100644 --- a/lib/compat/wordpress-6.5/rest-api.php +++ b/lib/compat/wordpress-6.5/rest-api.php @@ -20,6 +20,16 @@ function gutenberg_register_global_styles_revisions_endpoints() { add_action( 'rest_api_init', 'gutenberg_register_global_styles_revisions_endpoints' ); +/** + * Registers the Hooked Blocks REST API routes. + */ +function gutenberg_register_hooked_blocks_endpoint() { + $hooked_blocks_controller = new Gutenberg_REST_Hooked_Blocks_Controller_6_5(); + $hooked_blocks_controller->register_routes(); +} + +add_action( 'rest_api_init', 'gutenberg_register_hooked_blocks_endpoint' ); + /** * Registers additional fields for wp_template and wp_template_part rest api. * diff --git a/lib/load.php b/lib/load.php index 27e67d2e6d320b..58dd20c89238ed 100644 --- a/lib/load.php +++ b/lib/load.php @@ -43,6 +43,7 @@ function gutenberg_is_experiment_enabled( $name ) { // WordPress 6.5 compat. require_once __DIR__ . '/compat/wordpress-6.5/class-gutenberg-rest-global-styles-revisions-controller-6-5.php'; + require_once __DIR__ . '/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php'; require_once __DIR__ . '/compat/wordpress-6.5/rest-api.php'; // Plugin specific code. From dfff06a4d51a2cc5339d659e185b93a14848723e Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 2 Feb 2024 16:57:50 +0100 Subject: [PATCH 02/24] Set context to template if applicable --- ...nberg-rest-hooked-blocks-controller-6-5.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 18d1e818808791..6951e7df1c4446 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -208,6 +208,24 @@ public function get_item( $request ) { // TODO: Set context based on the request. $context = null; + // Retrieve the list of registered collection query parameters. + $registered = $this->get_collection_params(); + $entity = ''; + if ( isset( $registered['entity'] ) && ! empty( $request['entity'] ) ) { + $entity = $request['entity']; + } + if ( isset( $registered['id'] ) && ! empty( $request['id'] ) ) { + $id = $request['id']; + } + + // TODO: Validate entity and ID. + if ( 'template' === $entity && is_string( $id ) ) { + $context = get_block_template( $id ); // TODO: How do we determine $template_type arg (template or part)? + if ( is_wp_error( $context ) ) { + return $context; + } + } + foreach( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { $hooked_block_types_for_anchor_block[ $relative_position ] = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $block_name, $context ); } From 2b91b16e17e91e5a90a98c82779b0abee459e724 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Tue, 6 Feb 2024 12:03:28 +0000 Subject: [PATCH 03/24] Add get_context() method to resolve the requested entity context --- ...berg-rest-hooked-blocks-controller-6-5.php | 72 ++++++++++++++----- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 6951e7df1c4446..0efd5ac936760a 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -20,8 +20,8 @@ class Gutenberg_REST_Hooked_Blocks_Controller_6_5 extends WP_REST_Controller { * @since 6.5.0 */ public function __construct() { - $this->namespace = 'wp/v2'; - $this->rest_base = 'hooked-blocks'; + $this->namespace = 'wp/v2'; + $this->rest_base = 'hooked-blocks'; $this->block_registry = WP_Block_Type_Registry::get_instance(); } @@ -97,8 +97,8 @@ public function get_items( $request ) { $context = null; $hooked_block_types = get_hooked_blocks(); - foreach( $hooked_block_types as $anchor_block_type => $hooked_block_types_for_anchor_block ) { - foreach( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { + foreach ( $hooked_block_types as $anchor_block_type => $hooked_block_types_for_anchor_block ) { + foreach ( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { $hooked_block_types_for_anchor_block[ $relative_position ] = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context ); } @@ -135,6 +135,8 @@ public function get_items( $request ) { * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. */ public function get_item_permissions_check( $request ) { + return true; + $check = $this->check_read_permission(); if ( is_wp_error( $check ) ) { return $check; @@ -185,6 +187,44 @@ protected function get_block( $name ) { return $block_type; } + /** + * Get the correct context for the request. + * + * @since 6.5.0 + * + * @param string $entity Context entity. + * @param string $id Context ID. + * @return WP_Block_Template|WP_Post|WP_Errpr Block type object if name is valid, WP_Error otherwise. + */ + protected function get_context( $entity, $id ) { + $entity_mapping = array( + 'template' => 'wp_template', + 'part' => 'wp_template_part', + 'navigation' => 'wp_navigation', + ); + $entity_type = isset( $entity_mapping[ $entity ] ) ? $entity_mapping[ $entity ] : null; + + if ( ! $entity_type ) { + return new WP_Error( 'rest_block_type_invalid_context', __( 'Invalid context.' ), array( 'status' => 404 ) ); + } + + if ( ( 'wp_template' === $entity_type || 'wp_template_part' === $entity_type ) && ! empty( $id ) ) { + $context = get_block_template( $id, $entity_type ); + if ( is_wp_error( $context ) ) { + return $context; + } + } + + if ( 'wp_navigation' === $entity_type && ! empty( $id ) ) { + $context = get_post( (int) $id ); + if ( is_wp_error( $context ) ) { + return $context; + } + } + + return $context; + } + /** * Retrieves hooked blocks for a specific anchor block type. * @@ -205,9 +245,6 @@ public function get_item( $request ) { ? $all_hooked_block_types[ $block_name ] : array(); - // TODO: Set context based on the request. - $context = null; - // Retrieve the list of registered collection query parameters. $registered = $this->get_collection_params(); $entity = ''; @@ -218,15 +255,12 @@ public function get_item( $request ) { $id = $request['id']; } - // TODO: Validate entity and ID. - if ( 'template' === $entity && is_string( $id ) ) { - $context = get_block_template( $id ); // TODO: How do we determine $template_type arg (template or part)? - if ( is_wp_error( $context ) ) { - return $context; - } + $context = $this->get_context( $entity, $id ); + if ( is_wp_error( $context ) ) { + return $context; } - foreach( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { + foreach ( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { $hooked_block_types_for_anchor_block[ $relative_position ] = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $block_name, $context ); } @@ -259,15 +293,15 @@ public function prepare_item_for_response( $item, $request ) { */ public function get_collection_params() { return array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - 'entity' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + 'entity' => array( 'description' => __( 'Entity type to get hooked blocks for.' ), 'type' => 'string', ), - 'id' => array( + 'id' => array( 'description' => __( 'Entity ID to get hooked blocks for.' ), - //'type' => 'integer', // TODO: Can be an integer, but also a string(?) (template IDs?) - ) + 'type' => array( 'string', 'integer' ), + ), ); } } From b58c9178f8926e20a99ff2c64cda9748cf537db8 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Wed, 7 Feb 2024 16:21:28 +0000 Subject: [PATCH 04/24] Get all hooked blocks grouped by anchor block --- ...berg-rest-hooked-blocks-controller-6-5.php | 170 ++++++++++-------- 1 file changed, 94 insertions(+), 76 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 0efd5ac936760a..8b3db3615ac08e 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -14,6 +14,15 @@ * */ class Gutenberg_REST_Hooked_Blocks_Controller_6_5 extends WP_REST_Controller { + + /* + * Valid entities for the context parameter. + * + * @since 6.5.0 + * @var string[] + */ + protected $valid_entity_types = array( 'wp_template', 'wp_template_part', 'wp_navigation' ); + /** * Constructs the controller. * @@ -93,26 +102,36 @@ public function get_items_permissions_check( $request ) { public function get_items( $request ) { $data = array(); - // TODO: Set context based on the request. - $context = null; - - $hooked_block_types = get_hooked_blocks(); - foreach ( $hooked_block_types as $anchor_block_type => $hooked_block_types_for_anchor_block ) { - foreach ( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { - $hooked_block_types_for_anchor_block[ $relative_position ] = - apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context ); - } - $hooked_block_types[ $anchor_block_type ] = $hooked_block_types_for_anchor_block; - } - // Retrieve the list of registered collection query parameters. $registered = $this->get_collection_params(); + $entity = ''; $namespace = ''; + if ( isset( $registered['entity'] ) && ! empty( $request['entity'] ) ) { + $entity = $request['entity']; + } + if ( isset( $registered['id'] ) && ! empty( $request['id'] ) ) { + $id = $request['id']; + } if ( isset( $registered['namespace'] ) && ! empty( $request['namespace'] ) ) { $namespace = $request['namespace']; } + $context = $this->get_context( $entity, $id ); + if ( is_wp_error( $context ) ) { + return $context; + } + + $hooked_block_types = get_hooked_blocks(); + $hooked_block_types_by_anchor_block = array(); foreach ( $hooked_block_types as $anchor_block_type => $hooked_block_types_for_anchor_block ) { + foreach ( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { + $hooked_block_types_for_anchor_block[ $relative_position ] = + apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context ); + } + $hooked_block_types_by_anchor_block[ $anchor_block_type ] = $hooked_block_types_for_anchor_block; + } + + foreach ( $hooked_block_types_by_anchor_block as $anchor_block_type => $hooked_block_types_for_anchor_block ) { if ( $namespace ) { list ( $block_namespace ) = explode( '/', $anchor_block_type ); @@ -170,61 +189,6 @@ protected function check_read_permission() { return new WP_Error( 'rest_block_type_cannot_view', __( 'Sorry, you are not allowed to manage block types.' ), array( 'status' => rest_authorization_required_code() ) ); } - /** - * Get the block, if the name is valid. - * - * @since 6.5.0 - * - * @param string $name Block name. - * @return WP_Block_Type|WP_Error Block type object if name is valid, WP_Error otherwise. - */ - protected function get_block( $name ) { - $block_type = $this->block_registry->get_registered( $name ); - if ( empty( $block_type ) ) { - return new WP_Error( 'rest_block_type_invalid', __( 'Invalid block type.' ), array( 'status' => 404 ) ); - } - - return $block_type; - } - - /** - * Get the correct context for the request. - * - * @since 6.5.0 - * - * @param string $entity Context entity. - * @param string $id Context ID. - * @return WP_Block_Template|WP_Post|WP_Errpr Block type object if name is valid, WP_Error otherwise. - */ - protected function get_context( $entity, $id ) { - $entity_mapping = array( - 'template' => 'wp_template', - 'part' => 'wp_template_part', - 'navigation' => 'wp_navigation', - ); - $entity_type = isset( $entity_mapping[ $entity ] ) ? $entity_mapping[ $entity ] : null; - - if ( ! $entity_type ) { - return new WP_Error( 'rest_block_type_invalid_context', __( 'Invalid context.' ), array( 'status' => 404 ) ); - } - - if ( ( 'wp_template' === $entity_type || 'wp_template_part' === $entity_type ) && ! empty( $id ) ) { - $context = get_block_template( $id, $entity_type ); - if ( is_wp_error( $context ) ) { - return $context; - } - } - - if ( 'wp_navigation' === $entity_type && ! empty( $id ) ) { - $context = get_post( (int) $id ); - if ( is_wp_error( $context ) ) { - return $context; - } - } - - return $context; - } - /** * Retrieves hooked blocks for a specific anchor block type. * @@ -240,11 +204,6 @@ public function get_item( $request ) { return $block_type; } - $all_hooked_block_types = get_hooked_blocks(); - $hooked_block_types_for_anchor_block = isset( $all_hooked_block_types[ $block_name ] ) - ? $all_hooked_block_types[ $block_name ] - : array(); - // Retrieve the list of registered collection query parameters. $registered = $this->get_collection_params(); $entity = ''; @@ -255,6 +214,11 @@ public function get_item( $request ) { $id = $request['id']; } + $all_hooked_block_types = get_hooked_blocks(); + $hooked_block_types_for_anchor_block = isset( $all_hooked_block_types[ $block_name ] ) + ? $all_hooked_block_types[ $block_name ] + : array(); + $context = $this->get_context( $entity, $id ); if ( is_wp_error( $context ) ) { return $context; @@ -279,7 +243,7 @@ public function get_item( $request ) { * @return WP_REST_Response Block type data. */ public function prepare_item_for_response( $item, $request ) { - // TODO: Filter? + // TODO: Add filter for response. $response = rest_ensure_response( $item ); return $response; } @@ -293,15 +257,69 @@ public function prepare_item_for_response( $item, $request ) { */ public function get_collection_params() { return array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - 'entity' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + 'entity' => array( 'description' => __( 'Entity type to get hooked blocks for.' ), 'type' => 'string', ), - 'id' => array( + 'id' => array( 'description' => __( 'Entity ID to get hooked blocks for.' ), 'type' => array( 'string', 'integer' ), ), + 'namespace' => array( + 'description' => __( 'Block namespace.' ), + 'type' => 'string', + ), ); } + + /** + * Get the block, if the name is valid. + * + * @since 6.5.0 + * + * @param string $name Block name. + * @return WP_Block_Type|WP_Error Block type object if name is valid, WP_Error otherwise. + */ + protected function get_block( $name ) { + $block_type = $this->block_registry->get_registered( $name ); + if ( empty( $block_type ) ) { + return new WP_Error( 'rest_block_type_invalid', __( 'Invalid block type.' ), array( 'status' => 404 ) ); + } + + return $block_type; + } + + /** + * Get the correct context for the request. + * + * @since 6.5.0 + * + * @param string $entity Context entity. + * @param string $id Context ID. + * @return WP_Block_Template|WP_Post|null Context object if name is valid, WP_Error otherwise. + */ + protected function get_context( $entity, $id ) { + $entity_type = in_array( $entity, $this->valid_entity_types, true ) ? $entity : null; + + if ( ! $entity_type || ! $id ) { + return new WP_Error( 'rest_hooked_blocks_invalid_entity_context', __( 'Invalid entity.' ), array( 'status' => 404 ) ); + } + + if ( ( 'wp_template' === $entity_type || 'wp_template_part' === $entity_type ) && ! empty( $id ) ) { + $context = get_block_template( $id, $entity_type ); + if ( is_wp_error( $context ) ) { + return $context; + } + } + + if ( 'wp_navigation' === $entity_type && ! empty( $id ) ) { + $context = get_post( (int) $id ); + if ( is_wp_error( $context ) ) { + return $context; + } + } + + return $context; + } } From 5727b03fe03a779b56ccd37594ed85101f342b88 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Thu, 8 Feb 2024 11:24:45 +0000 Subject: [PATCH 05/24] Fix get_items() to include hooked blocks by filters --- ...berg-rest-hooked-blocks-controller-6-5.php | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 8b3db3615ac08e..dff17e6814724e 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -23,6 +23,14 @@ class Gutenberg_REST_Hooked_Blocks_Controller_6_5 extends WP_REST_Controller { */ protected $valid_entity_types = array( 'wp_template', 'wp_template_part', 'wp_navigation' ); + /* + * Possible positions for hooked blocks. + * + * @since 6.5.0 + * @var string[] + */ + protected $position_types = array( 'first_child', 'last_child', 'before', 'after' ); + /** * Constructs the controller. * @@ -224,11 +232,19 @@ public function get_item( $request ) { return $context; } - foreach ( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { - $hooked_block_types_for_anchor_block[ $relative_position ] = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $block_name, $context ); + foreach ( $this->position_types as $position ) { + $hooked_block_types_for_anchor_block[ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_for_anchor_block[ $position ], $position, $block_name, $context ); } - $data = $this->prepare_item_for_response( $hooked_block_types_for_anchor_block, $request ); + // Filter non-empty arrays. + $filtered_hooked_block_types_for_anchor_block = array_filter( + $hooked_block_types_for_anchor_block, + function ( $hooked_block_types_for_position ) { + return ! empty( $hooked_block_types_for_position ); + } + ); + + $data = $this->prepare_item_for_response( $filtered_hooked_block_types_for_anchor_block, $request ); return rest_ensure_response( $data ); } From ceaf3549c674e5eb51e747d4293b35dfeacb4ce7 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Thu, 8 Feb 2024 11:29:17 +0000 Subject: [PATCH 06/24] Check if array is empty --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index dff17e6814724e..5cff7cb7484cb7 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -233,7 +233,12 @@ public function get_item( $request ) { } foreach ( $this->position_types as $position ) { - $hooked_block_types_for_anchor_block[ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_for_anchor_block[ $position ], $position, $block_name, $context ); + // Making sure that we always pass an array to the filter. + $positioned_hooked_block_types = isset( $hooked_block_types_for_anchor_block[ $position ] ) + ? $hooked_block_types_for_anchor_block[ $position ] + : array(); + + $hooked_block_types_for_anchor_block[ $position ] = apply_filters( 'hooked_block_types', $positioned_hooked_block_types, $position, $block_name, $context ); } // Filter non-empty arrays. From d93fbd757d8cd8740bdf45a9ff8b3fe879f66ec4 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Thu, 8 Feb 2024 12:49:00 +0000 Subject: [PATCH 07/24] Make get_items() work with the block hooks filter: --- ...berg-rest-hooked-blocks-controller-6-5.php | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 5cff7cb7484cb7..7253f5e5c0f530 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -129,14 +129,22 @@ public function get_items( $request ) { return $context; } + $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); + $hooked_block_types = get_hooked_blocks(); $hooked_block_types_by_anchor_block = array(); - foreach ( $hooked_block_types as $anchor_block_type => $hooked_block_types_for_anchor_block ) { - foreach ( $hooked_block_types_for_anchor_block as $relative_position => $hooked_block_types ) { - $hooked_block_types_for_anchor_block[ $relative_position ] = - apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context ); + foreach ( $block_types as $block_type ) { + foreach ( $this->position_types as $position ) { + $hooked_block_types_for_anchor_block = isset( $hooked_block_types[ $block_type->name ][ $position ] ) + ? $hooked_block_types[ $block_type->name ][ $position ] + : array(); + + $positioned_hooked_block_types = isset( $hooked_block_types[ $position ] ) + ? $hooked_block_types[ $position ] + : array(); + + $hooked_block_types_by_anchor_block[ $block_type->name ][ $position ] = apply_filters( 'hooked_block_types', $positioned_hooked_block_types, $position, $block_type->name, $context ); } - $hooked_block_types_by_anchor_block[ $anchor_block_type ] = $hooked_block_types_for_anchor_block; } foreach ( $hooked_block_types_by_anchor_block as $anchor_block_type => $hooked_block_types_for_anchor_block ) { @@ -150,6 +158,8 @@ public function get_items( $request ) { $data[ $anchor_block_type ] = $this->prepare_response_for_collection( $hooked_block_types_for_anchor_block ); } + $data = $this->filter_empty_anchor_blocks( $data ); + return rest_ensure_response( $data ); } @@ -241,13 +251,10 @@ public function get_item( $request ) { $hooked_block_types_for_anchor_block[ $position ] = apply_filters( 'hooked_block_types', $positioned_hooked_block_types, $position, $block_name, $context ); } - // Filter non-empty arrays. - $filtered_hooked_block_types_for_anchor_block = array_filter( - $hooked_block_types_for_anchor_block, - function ( $hooked_block_types_for_position ) { - return ! empty( $hooked_block_types_for_position ); - } - ); + $filtered_hooked_block_types_for_anchor_block = $this->filter_empty_anchor_blocks( array( $block_name => $hooked_block_types_for_anchor_block ) ); + $filtered_hooked_block_types_for_anchor_block = isset( $filtered_hooked_block_types_for_anchor_block[ $block_name ] ) + ? $filtered_hooked_block_types_for_anchor_block[ $block_name ] + : array(); $data = $this->prepare_item_for_response( $filtered_hooked_block_types_for_anchor_block, $request ); @@ -343,4 +350,33 @@ protected function get_context( $entity, $id ) { return $context; } + + /** + * Filters out empty anchor blocks from the response array. + * + * @since 6.5.0 + * + * @param array $hooked_blocks Array of block types and their hooked blocks. + * @return array Filtered array of block types and their hooked blocks. + */ + protected function filter_empty_anchor_blocks( $hooked_blocks ) { + $filtered_array = array(); + + foreach ( $hooked_blocks as $key => $value ) { + // If the current value is an array and it's not empty, add it to the filtered array + if ( is_array( $value ) && ! empty( $value ) ) { + // Recursively filter the children arrays + $filtered_children = $this->filter_empty_anchor_blocks( $value ); + // If any non-empty children were found, add them to the filtered array + if ( ! empty( $filtered_children ) ) { + $filtered_array[ $key ] = $filtered_children; + } + } elseif ( ! empty( $value ) ) { + // If the value is not an array but not empty, add it directly to the filtered array + $filtered_array[ $key ] = $value; + } + } + + return $filtered_array; + } } From 00bc67ff1e922a0db40c8a386ffde8a0619876ae Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Thu, 8 Feb 2024 13:07:06 +0000 Subject: [PATCH 08/24] Add TODO --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 7253f5e5c0f530..53d30c04082582 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -129,8 +129,9 @@ public function get_items( $request ) { return $context; } - $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); - + // We need to get all registered block types and loop over each of them for the filter. + // TODO: Look into whether we can optimize get_hooked_blocks() to return filtered results as well. + $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); $hooked_block_types = get_hooked_blocks(); $hooked_block_types_by_anchor_block = array(); foreach ( $block_types as $block_type ) { From 39b65b859e9c5e85d91441765b3961f5f4e9461b Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Thu, 8 Feb 2024 13:35:03 +0000 Subject: [PATCH 09/24] Add item schema --- ...berg-rest-hooked-blocks-controller-6-5.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 53d30c04082582..01d38ef9653ee1 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -302,6 +302,48 @@ public function get_collection_params() { ); } + /** + * Retrieves the hooked blocks schema, conforming to JSON Schema. + * + * @since 6.5.0 + * + * @return array + */ + public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => $this->post_type, + 'type' => 'array', + 'properties' => array( + 'block_name' => array( + 'description' => __( 'Block namespace.' ), + 'type' => array( 'array' ), + 'readonly' => true, + 'context' => array( 'view', 'edit' ), + 'properties' => array( + 'position_key' => array( + 'description' => __( 'Positive key' ), + 'type' => 'array', + 'readonly' => true, + 'context' => array( 'view', 'edit' ), + 'items' => array( + 'type' => 'string', + ), + ), + ), + ), + ), + ); + + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); + } + /** * Get the block, if the name is valid. * From fd24615552ba72d8e3cdb4bfd8c3c128865fa684 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Fri, 9 Feb 2024 10:23:28 +0000 Subject: [PATCH 10/24] get_items(): Fix logic for hooked blocks by position --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 01d38ef9653ee1..d14e04de234b40 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -136,15 +136,11 @@ public function get_items( $request ) { $hooked_block_types_by_anchor_block = array(); foreach ( $block_types as $block_type ) { foreach ( $this->position_types as $position ) { - $hooked_block_types_for_anchor_block = isset( $hooked_block_types[ $block_type->name ][ $position ] ) + $hooked_block_types_by_anchor_block_position = isset( $hooked_block_types[ $block_type->name ][ $position ] ) ? $hooked_block_types[ $block_type->name ][ $position ] : array(); - $positioned_hooked_block_types = isset( $hooked_block_types[ $position ] ) - ? $hooked_block_types[ $position ] - : array(); - - $hooked_block_types_by_anchor_block[ $block_type->name ][ $position ] = apply_filters( 'hooked_block_types', $positioned_hooked_block_types, $position, $block_type->name, $context ); + $hooked_block_types_by_anchor_block[ $block_type->name ][ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_by_anchor_block_position, $position, $block_type->name, $context ); } } From e481e1c24f327cb2fdf9902eb9440ad87b7e9a52 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 12:49:24 +0100 Subject: [PATCH 11/24] Rename var --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index d14e04de234b40..c278790ed36a9b 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -134,13 +134,13 @@ public function get_items( $request ) { $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); $hooked_block_types = get_hooked_blocks(); $hooked_block_types_by_anchor_block = array(); - foreach ( $block_types as $block_type ) { + foreach ( $block_types as $anchor_block_type ) { foreach ( $this->position_types as $position ) { - $hooked_block_types_by_anchor_block_position = isset( $hooked_block_types[ $block_type->name ][ $position ] ) - ? $hooked_block_types[ $block_type->name ][ $position ] + $hooked_block_types_by_anchor_block_position = isset( $hooked_block_types[ $anchor_block_type->name ][ $position ] ) + ? $hooked_block_types[ $anchor_block_type->name ][ $position ] : array(); - $hooked_block_types_by_anchor_block[ $block_type->name ][ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_by_anchor_block_position, $position, $block_type->name, $context ); + $hooked_block_types_by_anchor_block[ $anchor_block_type->name ][ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_by_anchor_block_position, $position, $block_type->name, $context ); } } From 0f60bd9dbdb9090cac3576f638fc6defbe4de2a5 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 12:52:36 +0100 Subject: [PATCH 12/24] Use array_column --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index c278790ed36a9b..7681623a84d3a7 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -134,13 +134,13 @@ public function get_items( $request ) { $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); $hooked_block_types = get_hooked_blocks(); $hooked_block_types_by_anchor_block = array(); - foreach ( $block_types as $anchor_block_type ) { + foreach ( array_column( $block_types, 'name' ) as $anchor_block_type ) { foreach ( $this->position_types as $position ) { - $hooked_block_types_by_anchor_block_position = isset( $hooked_block_types[ $anchor_block_type->name ][ $position ] ) - ? $hooked_block_types[ $anchor_block_type->name ][ $position ] + $hooked_block_types_by_anchor_block_position = isset( $hooked_block_types[ $anchor_block_type ][ $position ] ) + ? $hooked_block_types[ $anchor_block_type ][ $position ] : array(); - $hooked_block_types_by_anchor_block[ $anchor_block_type->name ][ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_by_anchor_block_position, $position, $block_type->name, $context ); + $hooked_block_types_by_anchor_block[ $anchor_block_type ][ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_by_anchor_block_position, $position, $block_type->name, $context ); } } From 8eb0f868166e7a6bca8e44e5a37163042683a237 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 13:16:56 +0100 Subject: [PATCH 13/24] Missed one --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 7681623a84d3a7..4a40f96f3b4c2e 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -140,7 +140,7 @@ public function get_items( $request ) { ? $hooked_block_types[ $anchor_block_type ][ $position ] : array(); - $hooked_block_types_by_anchor_block[ $anchor_block_type ][ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_by_anchor_block_position, $position, $block_type->name, $context ); + $hooked_block_types_by_anchor_block[ $anchor_block_type ][ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_by_anchor_block_position, $position, $anchor_block_type, $context ); } } From 6f9d82b737b93c08e0f46d921b1154cfc5c10cc0 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 14:08:13 +0100 Subject: [PATCH 14/24] Change schema title --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 4a40f96f3b4c2e..85aa978b8406be 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -312,7 +312,7 @@ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => $this->post_type, + 'title' => 'hooked_blocks', 'type' => 'array', 'properties' => array( 'block_name' => array( From 4790aaf1e90220fe1c9dd03c3b4358acbf5c745f Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 14:17:58 +0100 Subject: [PATCH 15/24] Try suppressing hooked blocks for context creation --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 85aa978b8406be..255d6f942ffb1c 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -374,7 +374,12 @@ protected function get_context( $entity, $id ) { } if ( ( 'wp_template' === $entity_type || 'wp_template_part' === $entity_type ) && ! empty( $id ) ) { + $suppress_all_hooked_blocks_filter = function() { + return array(); + }; + add_filter( 'hooked_block_types', $suppress_all_hooked_blocks_filter, 99999, 0 ); $context = get_block_template( $id, $entity_type ); + remove_filter( 'hooked_block_types', $suppress_all_hooked_blocks_filter, 99999 ); if ( is_wp_error( $context ) ) { return $context; } From e7370b5901304e8958c37bd3ed7ca393ee6e88d9 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Fri, 9 Feb 2024 13:29:17 +0000 Subject: [PATCH 16/24] Refactor suppression of hooked blocks whilst getting context --- ...enberg-rest-hooked-blocks-controller-6-5.php | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 255d6f942ffb1c..d5435d6525a61c 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -373,25 +373,20 @@ protected function get_context( $entity, $id ) { return new WP_Error( 'rest_hooked_blocks_invalid_entity_context', __( 'Invalid entity.' ), array( 'status' => 404 ) ); } + // Suppress all hooked blocks getting inserted into the context. + add_filter( 'hooked_block_types', '__return_empty_array', 99999, 0 ); + if ( ( 'wp_template' === $entity_type || 'wp_template_part' === $entity_type ) && ! empty( $id ) ) { - $suppress_all_hooked_blocks_filter = function() { - return array(); - }; - add_filter( 'hooked_block_types', $suppress_all_hooked_blocks_filter, 99999, 0 ); $context = get_block_template( $id, $entity_type ); - remove_filter( 'hooked_block_types', $suppress_all_hooked_blocks_filter, 99999 ); - if ( is_wp_error( $context ) ) { - return $context; - } } if ( 'wp_navigation' === $entity_type && ! empty( $id ) ) { $context = get_post( (int) $id ); - if ( is_wp_error( $context ) ) { - return $context; - } } + // Remove the filter to allow hooked blocks to be inserted for all purposes. + remove_filter( 'hooked_block_types', '__return_empty_array', 99999 ); + return $context; } From f5a494adbc40766bd9065b25640b0f9bd23c9efe Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 15:23:15 +0100 Subject: [PATCH 17/24] Initialize id --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index d5435d6525a61c..9fce8593b70f89 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -222,6 +222,7 @@ public function get_item( $request ) { // Retrieve the list of registered collection query parameters. $registered = $this->get_collection_params(); $entity = ''; + $id = null; if ( isset( $registered['entity'] ) && ! empty( $request['entity'] ) ) { $entity = $request['entity']; } From ab8fc5bed468cbb1fae899152afaff536fa9cac7 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 15:23:35 +0100 Subject: [PATCH 18/24] Allow specifying no context --- ...s-gutenberg-rest-hooked-blocks-controller-6-5.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 9fce8593b70f89..e65c03dc5d691e 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -368,20 +368,22 @@ protected function get_block( $name ) { * @return WP_Block_Template|WP_Post|null Context object if name is valid, WP_Error otherwise. */ protected function get_context( $entity, $id ) { - $entity_type = in_array( $entity, $this->valid_entity_types, true ) ? $entity : null; + if ( ! $entity && ! $id ) { + return null; // No context specified. + } - if ( ! $entity_type || ! $id ) { + if ( ! in_array( $entity, $this->valid_entity_types, true ) ) { return new WP_Error( 'rest_hooked_blocks_invalid_entity_context', __( 'Invalid entity.' ), array( 'status' => 404 ) ); } // Suppress all hooked blocks getting inserted into the context. add_filter( 'hooked_block_types', '__return_empty_array', 99999, 0 ); - if ( ( 'wp_template' === $entity_type || 'wp_template_part' === $entity_type ) && ! empty( $id ) ) { - $context = get_block_template( $id, $entity_type ); + if ( ( 'wp_template' === $entity || 'wp_template_part' === $entity ) && ! empty( $id ) ) { + $context = get_block_template( $id, $entity ); } - if ( 'wp_navigation' === $entity_type && ! empty( $id ) ) { + if ( 'wp_navigation' === $entity && ! empty( $id ) ) { $context = get_post( (int) $id ); } From ec2bb3d7ddb965c7f388d39bd8bc514b6bd71e8a Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 16:16:14 +0100 Subject: [PATCH 19/24] Add route for namespace only --- ...utenberg-rest-hooked-blocks-controller-6-5.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index e65c03dc5d691e..05ffea61b4e4a2 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -62,6 +62,20 @@ public function register_routes() { ) ); + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[a-zA-Z0-9_-]+)', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+)', @@ -113,6 +127,7 @@ public function get_items( $request ) { // Retrieve the list of registered collection query parameters. $registered = $this->get_collection_params(); $entity = ''; + $id = null; $namespace = ''; if ( isset( $registered['entity'] ) && ! empty( $request['entity'] ) ) { $entity = $request['entity']; From 5924d639a49492deb31c42d6d7c9d65618aec0ad Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 16:16:35 +0100 Subject: [PATCH 20/24] Add basic test coverage --- ...ooked-blocks-controller-gutenberg-test.php | 281 ++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 phpunit/class-gutenberg-rest-hooked-blocks-controller-gutenberg-test.php diff --git a/phpunit/class-gutenberg-rest-hooked-blocks-controller-gutenberg-test.php b/phpunit/class-gutenberg-rest-hooked-blocks-controller-gutenberg-test.php new file mode 100644 index 00000000000000..f837032615bc5e --- /dev/null +++ b/phpunit/class-gutenberg-rest-hooked-blocks-controller-gutenberg-test.php @@ -0,0 +1,281 @@ +user->create( + array( + 'role' => 'administrator', + ) + ); + self::$subscriber_id = $factory->user->create( + array( + 'role' => 'subscriber', + ) + ); + + $anchor_block_settings = array( + 'icon' => 'text', + ); + + $hooked_block_settings = array( + 'block_hooks' => array( + 'fake/anchor-block' => 'after', + ) + ); + + register_block_type( self::$anchor_block_name, $anchor_block_settings ); + register_block_type( self::$hooked_block_name, $hooked_block_settings ); + + $other_hooked_block_settings = array( + 'block_hooks' => array( + 'fake/other-anchor-block' => 'first_child', + ) + ); + + register_block_type( self::$other_anchor_block_name, array() ); + register_block_type( self::$other_hooked_block_name, $other_hooked_block_settings ); + } + + public static function wpTearDownAfterClass() { + self::delete_user( self::$admin_id ); + self::delete_user( self::$subscriber_id ); + unregister_block_type( self::$anchor_block_name ); + unregister_block_type( self::$hooked_block_name ); + unregister_block_type( self::$other_anchor_block_name ); + unregister_block_type( self::$other_hooked_block_name ); + } + + public function test_register_routes() { + $routes = rest_get_server()->get_routes(); + $this->assertArrayHasKey( '/wp/v2/hooked-blocks', $routes ); + $this->assertCount( 1, $routes['/wp/v2/hooked-blocks'] ); + $this->assertArrayHasKey( '/wp/v2/hooked-blocks/(?P[a-zA-Z0-9_-]+)', $routes ); + $this->assertCount( 1, $routes['/wp/v2/hooked-blocks/(?P[a-zA-Z0-9_-]+)'] ); + $this->assertArrayHasKey( '/wp/v2/hooked-blocks/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+)', $routes ); + $this->assertCount( 1, $routes['/wp/v2/hooked-blocks/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+)'] ); + } + + public function test_context_param() { + // Collection. + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/hooked-blocks' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + // Single. + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/hooked-blocks/fake/test' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + } + + public function test_get_items() { + $block_name = 'fake/test'; + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/fake' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertCount( 2, $data ); + $this->assertSame( $data, array( + self::$anchor_block_name => array( + 'after' => array( self::$hooked_block_name ) + ), + self::$other_anchor_block_name => array( + 'first_child' => array( self::$other_hooked_block_name ) + ), + ) ); + } + + public function test_get_item() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/' . self::$anchor_block_name ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertSame( $data, array( + 'after' => array( self::$hooked_block_name ) + ) ); + } + + public function test_get_item_with_no_hooked_blocks() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/' . self::$hooked_block_name ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertSame( $data, array() ); + } + + public function test_get_item_with_hooked_block_added_by_filter() { + $add_hooked_block = function( $hooked_block_types, $relative_position, $anchor_block_type ) { + if ( 'last_child' === $relative_position && self::$anchor_block_name === $anchor_block_type ) { + $hooked_block_types[] = 'fake/hooked-block-added-by-filter'; + } + return $hooked_block_types; + }; + add_filter( 'hooked_block_types', $add_hooked_block, 10, 3 ); + + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/' . self::$anchor_block_name ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + remove_filter( 'hooked_block_types', $add_hooked_block, 10 ); + $this->assertSame( $data, array( + 'after' => array( self::$hooked_block_name ), + 'last_child' => array( 'fake/hooked-block-added-by-filter' ) + ) ); + } + + public function test_get_block_invalid_name() { + $block_type = 'fake/block'; + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/' . $block_type ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_block_type_invalid', $response, 404 ); + } + + public function test_get_item_schema() { + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/hooked-blocks' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $properties = $data['schema']['properties']; + $this->assertCount( 1, $properties ); + $this->assertArrayHasKey( 'block_name', $properties ); + } + + public function test_get_items_wrong_permission() { + wp_set_current_user( self::$subscriber_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_block_type_cannot_view', $response, 403 ); + } + + public function test_get_item_wrong_permission() { + wp_set_current_user( self::$subscriber_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/fake/test' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_block_type_cannot_view', $response, 403 ); + } + + public function test_get_items_no_permission() { + wp_set_current_user( 0 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_block_type_cannot_view', $response, 401 ); + } + + public function test_get_item_no_permission() { + wp_set_current_user( 0 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/fake/test' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_block_type_cannot_view', $response, 401 ); + } + + public function test_prepare_item() { + $registry = new WP_Block_Type_Registry(); + $settings = array( + 'icon' => 'text', + 'render_callback' => '__return_null', + ); + $registry->register( 'fake/line', $settings ); + $block_type = $registry->get_registered( 'fake/line' ); + $endpoint = new WP_REST_Block_Types_Controller(); + $request = new WP_REST_Request(); + $request->set_param( 'context', 'edit' ); + $response = $endpoint->prepare_item_for_response( $block_type, $request ); + // $this->assertSame(); + } + + public function test_prepare_item_limit_fields() { + $registry = new WP_Block_Type_Registry(); + $settings = array( + 'icon' => 'text', + 'render_callback' => '__return_null', + ); + $registry->register( 'fake/line', $settings ); + $block_type = $registry->get_registered( 'fake/line' ); + $request = new WP_REST_Request(); + $endpoint = new WP_REST_Block_Types_Controller(); + $request->set_param( 'context', 'edit' ); + $request->set_param( '_fields', 'name' ); + $response = $endpoint->prepare_item_for_response( $block_type, $request ); + $this->assertSame( + array( + 'name', + ), + array_keys( $response->get_data() ) + ); + } + + /** + * The create_item() method does not exist for hooked blocks. + * + * @doesNotPerformAssertions + */ + public function test_create_item() { + // Controller does not implement create_item(). + } + + /** + * The update_item() method does not exist for hooked blocks. + * + * @doesNotPerformAssertions + */ + public function test_update_item() { + // Controller does not implement create_item(). + } + + /** + * The delete_item() method does not exist for hooked blocks. + * + * @doesNotPerformAssertions + */ + public function test_delete_item() { + // Controller does not implement delete_item(). + } +} From ebbfe81e79fbd7b169870bf3c20fc50c79bdfe65 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 16:17:26 +0100 Subject: [PATCH 21/24] Tweak schema --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 05ffea61b4e4a2..25bd84ba4d12df 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -332,13 +332,13 @@ public function get_item_schema() { 'type' => 'array', 'properties' => array( 'block_name' => array( - 'description' => __( 'Block namespace.' ), + 'description' => __( 'Block name.' ), 'type' => array( 'array' ), 'readonly' => true, 'context' => array( 'view', 'edit' ), 'properties' => array( - 'position_key' => array( - 'description' => __( 'Positive key' ), + 'relative_position' => array( + 'description' => __( 'Relative position.' ), 'type' => 'array', 'readonly' => true, 'context' => array( 'view', 'edit' ), From 648703ab19ff014a233e275e9caf92780f0ab3d4 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Fri, 9 Feb 2024 15:25:15 +0000 Subject: [PATCH 22/24] Refactor getting hooked blocks via filter hook into its own method --- ...berg-rest-hooked-blocks-controller-6-5.php | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index 25bd84ba4d12df..b682acece0da1e 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -40,6 +40,7 @@ public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'hooked-blocks'; $this->block_registry = WP_Block_Type_Registry::get_instance(); + $this->hooked_blocks = get_hooked_blocks(); } /** @@ -147,27 +148,21 @@ public function get_items( $request ) { // We need to get all registered block types and loop over each of them for the filter. // TODO: Look into whether we can optimize get_hooked_blocks() to return filtered results as well. $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); - $hooked_block_types = get_hooked_blocks(); $hooked_block_types_by_anchor_block = array(); - foreach ( array_column( $block_types, 'name' ) as $anchor_block_type ) { - foreach ( $this->position_types as $position ) { - $hooked_block_types_by_anchor_block_position = isset( $hooked_block_types[ $anchor_block_type ][ $position ] ) - ? $hooked_block_types[ $anchor_block_type ][ $position ] - : array(); - $hooked_block_types_by_anchor_block[ $anchor_block_type ][ $position ] = apply_filters( 'hooked_block_types', $hooked_block_types_by_anchor_block_position, $position, $anchor_block_type, $context ); - } + foreach ( array_column( $block_types, 'name' ) as $anchor_block_name ) { + $hooked_block_types_by_anchor_block[ $anchor_block_name ] = $this->get_hooked_blocks_by_anchor( $anchor_block_name, $context ); } - foreach ( $hooked_block_types_by_anchor_block as $anchor_block_type => $hooked_block_types_for_anchor_block ) { + foreach ( $hooked_block_types_by_anchor_block as $anchor_block_name => $hooked_block_types_for_anchor_block ) { if ( $namespace ) { - list ( $block_namespace ) = explode( '/', $anchor_block_type ); + list ( $block_namespace ) = explode( '/', $anchor_block_name ); if ( $namespace !== $block_namespace ) { continue; } } - $data[ $anchor_block_type ] = $this->prepare_response_for_collection( $hooked_block_types_for_anchor_block ); + $data[ $anchor_block_name ] = $this->prepare_response_for_collection( $hooked_block_types_for_anchor_block ); } $data = $this->filter_empty_anchor_blocks( $data ); @@ -175,6 +170,31 @@ public function get_items( $request ) { return rest_ensure_response( $data ); } + /** + * Retrieves hooked blocks for a specific anchor block type. + * + * @since 6.5.0 + * + * @param string $anchor_block_name Anchor block name. + * @param WP_Block_Template|WP_Post|null $context Context object. + * @return array Array of hooked blocks keyed by position. + */ + protected function get_hooked_blocks_by_anchor( $anchor_block_name, $context ) { + $hooked_block_types_for_anchor_block = isset( $this->hooked_blocks[ $anchor_block_name ] ) + ? $this->hooked_blocks[ $anchor_block_name ] + : array(); + + foreach ( $this->position_types as $position ) { + $positioned_hooked_block_types = isset( $hooked_block_types_for_anchor_block[ $position ] ) + ? $hooked_block_types_for_anchor_block[ $position ] + : array(); + + $hooked_block_types_for_anchor_block[ $position ] = apply_filters( 'hooked_block_types', $positioned_hooked_block_types, $position, $anchor_block_name, $context ); + } + + return $hooked_block_types_for_anchor_block; + } + /** * Checks if a given request has access to read a hooked block type. * @@ -245,25 +265,12 @@ public function get_item( $request ) { $id = $request['id']; } - $all_hooked_block_types = get_hooked_blocks(); - $hooked_block_types_for_anchor_block = isset( $all_hooked_block_types[ $block_name ] ) - ? $all_hooked_block_types[ $block_name ] - : array(); - $context = $this->get_context( $entity, $id ); if ( is_wp_error( $context ) ) { return $context; } - foreach ( $this->position_types as $position ) { - // Making sure that we always pass an array to the filter. - $positioned_hooked_block_types = isset( $hooked_block_types_for_anchor_block[ $position ] ) - ? $hooked_block_types_for_anchor_block[ $position ] - : array(); - - $hooked_block_types_for_anchor_block[ $position ] = apply_filters( 'hooked_block_types', $positioned_hooked_block_types, $position, $block_name, $context ); - } - + $hooked_block_types_for_anchor_block = $this->get_hooked_blocks_by_anchor( $block_name, $context ); $filtered_hooked_block_types_for_anchor_block = $this->filter_empty_anchor_blocks( array( $block_name => $hooked_block_types_for_anchor_block ) ); $filtered_hooked_block_types_for_anchor_block = isset( $filtered_hooked_block_types_for_anchor_block[ $block_name ] ) ? $filtered_hooked_block_types_for_anchor_block[ $block_name ] From 4f75d51c62f1a41c9ee1937bb43bd0b58ba49d67 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Fri, 9 Feb 2024 15:29:26 +0000 Subject: [PATCH 23/24] Update block registry reference --- .../class-gutenberg-rest-hooked-blocks-controller-6-5.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php index b682acece0da1e..3e17960cc22166 100644 --- a/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php +++ b/lib/compat/wordpress-6.5/class-gutenberg-rest-hooked-blocks-controller-6-5.php @@ -147,7 +147,7 @@ public function get_items( $request ) { // We need to get all registered block types and loop over each of them for the filter. // TODO: Look into whether we can optimize get_hooked_blocks() to return filtered results as well. - $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); + $block_types = $this->block_registry->get_all_registered(); $hooked_block_types_by_anchor_block = array(); foreach ( array_column( $block_types, 'name' ) as $anchor_block_name ) { From e193ecdff140ea9ce995842d2298c7dbbda6e69b Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 9 Feb 2024 16:32:08 +0100 Subject: [PATCH 24/24] Inline block names --- ...ooked-blocks-controller-gutenberg-test.php | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/phpunit/class-gutenberg-rest-hooked-blocks-controller-gutenberg-test.php b/phpunit/class-gutenberg-rest-hooked-blocks-controller-gutenberg-test.php index f837032615bc5e..9e49036ae19e5b 100644 --- a/phpunit/class-gutenberg-rest-hooked-blocks-controller-gutenberg-test.php +++ b/phpunit/class-gutenberg-rest-hooked-blocks-controller-gutenberg-test.php @@ -31,12 +31,6 @@ class Gutenberg_REST_Hooked_Blocks_Controller_Test extends WP_Test_REST_Controll */ protected static $subscriber_id; - protected static $anchor_block_name = 'fake/anchor-block'; - protected static $hooked_block_name = 'fake/hooked-block'; - - protected static $other_anchor_block_name = 'fake/other-anchor-block'; - protected static $other_hooked_block_name = 'fake/other-hooked-block'; - /** * Create fake data before our tests run. * @@ -66,8 +60,8 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { ) ); - register_block_type( self::$anchor_block_name, $anchor_block_settings ); - register_block_type( self::$hooked_block_name, $hooked_block_settings ); + register_block_type( 'fake/anchor-block', $anchor_block_settings ); + register_block_type( 'fake/hooked-block', $hooked_block_settings ); $other_hooked_block_settings = array( 'block_hooks' => array( @@ -75,17 +69,17 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { ) ); - register_block_type( self::$other_anchor_block_name, array() ); - register_block_type( self::$other_hooked_block_name, $other_hooked_block_settings ); + register_block_type( 'fake/other-anchor-block', array() ); + register_block_type( 'fake/other-hooked-block', $other_hooked_block_settings ); } public static function wpTearDownAfterClass() { self::delete_user( self::$admin_id ); self::delete_user( self::$subscriber_id ); - unregister_block_type( self::$anchor_block_name ); - unregister_block_type( self::$hooked_block_name ); - unregister_block_type( self::$other_anchor_block_name ); - unregister_block_type( self::$other_hooked_block_name ); + unregister_block_type( 'fake/anchor-block' ); + unregister_block_type( 'fake/hooked-block' ); + unregister_block_type( 'fake/other-anchor-block' ); + unregister_block_type( 'fake/other-hooked-block' ); } public function test_register_routes() { @@ -121,28 +115,28 @@ public function test_get_items() { $data = $response->get_data(); $this->assertCount( 2, $data ); $this->assertSame( $data, array( - self::$anchor_block_name => array( - 'after' => array( self::$hooked_block_name ) + 'fake/anchor-block' => array( + 'after' => array( 'fake/hooked-block' ) ), - self::$other_anchor_block_name => array( - 'first_child' => array( self::$other_hooked_block_name ) + 'fake/other-anchor-block' => array( + 'first_child' => array( 'fake/other-hooked-block' ) ), ) ); } public function test_get_item() { wp_set_current_user( self::$admin_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/' . self::$anchor_block_name ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/fake/anchor-block' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertSame( $data, array( - 'after' => array( self::$hooked_block_name ) + 'after' => array( 'fake/hooked-block' ) ) ); } public function test_get_item_with_no_hooked_blocks() { wp_set_current_user( self::$admin_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/' . self::$hooked_block_name ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/fake/hooked-block' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertSame( $data, array() ); @@ -150,7 +144,7 @@ public function test_get_item_with_no_hooked_blocks() { public function test_get_item_with_hooked_block_added_by_filter() { $add_hooked_block = function( $hooked_block_types, $relative_position, $anchor_block_type ) { - if ( 'last_child' === $relative_position && self::$anchor_block_name === $anchor_block_type ) { + if ( 'last_child' === $relative_position && 'fake/anchor-block' === $anchor_block_type ) { $hooked_block_types[] = 'fake/hooked-block-added-by-filter'; } return $hooked_block_types; @@ -158,13 +152,13 @@ public function test_get_item_with_hooked_block_added_by_filter() { add_filter( 'hooked_block_types', $add_hooked_block, 10, 3 ); wp_set_current_user( self::$admin_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/' . self::$anchor_block_name ); + $request = new WP_REST_Request( 'GET', '/wp/v2/hooked-blocks/fake/anchor-block' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); remove_filter( 'hooked_block_types', $add_hooked_block, 10 ); $this->assertSame( $data, array( - 'after' => array( self::$hooked_block_name ), + 'after' => array( 'fake/hooked-block' ), 'last_child' => array( 'fake/hooked-block-added-by-filter' ) ) ); }