diff --git a/src/wp-includes/class-wp-block-metadata-registry.php b/src/wp-includes/class-wp-block-metadata-registry.php index 2214e98d10db7..2707fe9a36fdd 100644 --- a/src/wp-includes/class-wp-block-metadata-registry.php +++ b/src/wp-includes/class-wp-block-metadata-registry.php @@ -53,6 +53,14 @@ class WP_Block_Metadata_Registry { */ private static $plugin_dir = ''; + /** + * Stores the normalized WordPress theme directory path. + * + * @since 6.7.0 + * @var string + */ + private static $theme_dir = ''; + /** * Registers a block metadata collection. * @@ -94,6 +102,7 @@ public static function register_collection( $path, $manifest ) { $wpinc_path = self::get_include_path(); $plugin_dir = self::get_plugin_dir(); + $theme_dir = self::get_theme_dir(); // Check if the path is valid: if ( str_starts_with( $path, $wpinc_path ) ) { @@ -106,15 +115,17 @@ public static function register_collection( $path, $manifest ) { if ( empty( $plugin_name ) || $plugin_name === $relative_path ) { _doing_it_wrong( __METHOD__, - __( 'Block metadata collections can only be registered for a specific plugin. The provided path is neither a core path nor a valid plugin path.' ), + __( 'Block metadata collections can only be registered for a specific plugin or theme. The provided path is neither a core path, a valid plugin path, nor a valid theme path.' ), '6.7.0' ); return false; } + } elseif ( str_starts_with( $path, $theme_dir ) ) { + // Theme path is valid. } else { _doing_it_wrong( __METHOD__, - __( 'Block metadata collections can only be registered for a specific plugin. The provided path is neither a core path nor a valid plugin path.' ), + __( 'Block metadata collections can only be registered for a specific plugin or theme. The provided path is neither a core path, a valid plugin path, nor a valid theme path.' ), '6.7.0' ); return false; @@ -271,4 +282,18 @@ private static function get_plugin_dir() { } return self::$plugin_dir; } + + /** + * Gets the normalized WordPress theme directory path. + * + * @since 6.7.0 + * + * @return string The normalized WordPress theme directory path. + */ + private static function get_theme_dir() { + if ( empty( self::$theme_dir ) ) { + self::$theme_dir = wp_normalize_path( get_theme_root() ); + } + return self::$theme_dir; + } } diff --git a/tests/phpunit/tests/blocks/registerBlockTypeFromMetadataWithRegistry.php b/tests/phpunit/tests/blocks/registerBlockTypeFromMetadataWithRegistry.php index b778a3fb5c838..335d07dd08c6d 100644 --- a/tests/phpunit/tests/blocks/registerBlockTypeFromMetadataWithRegistry.php +++ b/tests/phpunit/tests/blocks/registerBlockTypeFromMetadataWithRegistry.php @@ -91,6 +91,66 @@ public function test_register_block_type_from_metadata_with_registry_and_overrid $this->assertEquals( array( 'html' => true ), $registered_block->supports ); } + public function test_register_block_type_from_metadata_with_registry_in_parent_theme() { + $parent_theme_path = get_theme_root() . '/twentytwentyone'; + $block_json_path = $parent_theme_path . '/blocks/test-block/block.json'; + + // Create a manifest file with metadata for our test block + $manifest_data = array( + 'test-block' => array( + 'name' => 'twentytwentyone/test-block', + 'title' => 'Parent Theme Test Block', + 'category' => 'widgets', + 'icon' => 'smiley', + 'description' => 'A test block registered via WP_Block_Metadata_Registry in the parent theme', + 'supports' => array( 'html' => false ), + 'textdomain' => 'twentytwentyone', + ), + ); + file_put_contents( $this->temp_manifest_file, 'temp_manifest_file ); + + // Attempt to register the block + $registered_block = register_block_type_from_metadata( $block_json_path ); + + // Assert that the block was registered successfully + $this->assertInstanceOf( 'WP_Block_Type', $registered_block ); + $this->assertEquals( 'twentytwentyone/test-block', $registered_block->name ); + $this->assertEquals( 'Parent Theme Test Block', $registered_block->title ); + } + + public function test_register_block_type_from_metadata_with_registry_in_child_theme() { + $child_theme_path = get_theme_root() . '/twentytwentyone-child'; + $block_json_path = $child_theme_path . '/blocks/test-block/block.json'; + + // Create a manifest file with metadata for our test block + $manifest_data = array( + 'test-block' => array( + 'name' => 'twentytwentyone-child/test-block', + 'title' => 'Child Theme Test Block', + 'category' => 'widgets', + 'icon' => 'smiley', + 'description' => 'A test block registered via WP_Block_Metadata_Registry in the child theme', + 'supports' => array( 'html' => false ), + 'textdomain' => 'twentytwentyone-child', + ), + ); + file_put_contents( $this->temp_manifest_file, 'temp_manifest_file ); + + // Attempt to register the block + $registered_block = register_block_type_from_metadata( $block_json_path ); + + // Assert that the block was registered successfully + $this->assertInstanceOf( 'WP_Block_Type', $registered_block ); + $this->assertEquals( 'twentytwentyone-child/test-block', $registered_block->name ); + $this->assertEquals( 'Child Theme Test Block', $registered_block->title ); + } + private function unregister_test_blocks() { $registry = WP_Block_Type_Registry::get_instance(); $block_name = 'test-suite/test-block'; diff --git a/tests/phpunit/tests/blocks/wpBlockMetadataRegistry.php b/tests/phpunit/tests/blocks/wpBlockMetadataRegistry.php index 3f0ec006b0a0a..dabfcb7a6bfc9 100644 --- a/tests/phpunit/tests/blocks/wpBlockMetadataRegistry.php +++ b/tests/phpunit/tests/blocks/wpBlockMetadataRegistry.php @@ -88,4 +88,20 @@ public function test_register_collection_with_non_existent_path() { $result = WP_Block_Metadata_Registry::register_collection( $non_existent_path, $this->temp_manifest_file ); $this->assertFalse( $result, 'Non-existent path should not be registered' ); } + + public function test_register_collection_with_valid_theme_path() { + $valid_theme_path = get_theme_root() . '/mytheme/blocks'; + + $result = WP_Block_Metadata_Registry::register_collection( $valid_theme_path, $this->temp_manifest_file ); + $this->assertTrue( $result, 'Valid theme path should be registered successfully' ); + } + + public function test_register_collection_with_invalid_theme_path() { + $invalid_plugin_path = get_theme_root(); + + $this->setExpectedIncorrectUsage( 'WP_Block_Metadata_Registry::register_collection' ); + + $result = WP_Block_Metadata_Registry::register_collection( $invalid_plugin_path, $this->temp_manifest_file ); + $this->assertFalse( $result, 'Invalid theme path should not be registered' ); + } }