diff --git a/wp-includes/blocks.php b/wp-includes/blocks.php index 3769b936cd..0a34af25f6 100644 --- a/wp-includes/blocks.php +++ b/wp-includes/blocks.php @@ -40,6 +40,217 @@ function unregister_block_type( $name ) { return WP_Block_Type_Registry::get_instance()->unregister( $name ); } +/** + * Removes the block asset's path prefix if provided. + * + * @since 5.5.0 + * + * @param string $asset_handle_or_path Asset handle or prefixed path. + * @return string Path without the prefix or the original value. + */ +function remove_block_asset_path_prefix( $asset_handle_or_path ) { + $path_prefix = 'file:'; + if ( 0 !== strpos( $asset_handle_or_path, $path_prefix ) ) { + return $asset_handle_or_path; + } + return substr( + $asset_handle_or_path, + strlen( $path_prefix ) + ); +} + +/** + * Generates the name for an asset based on the name of the block + * and the field name provided. + * + * @since 5.5.0 + * + * @param string $block_name Name of the block. + * @param string $field_name Name of the metadata field. + * @return string Generated asset name for the block's field. + */ +function generate_block_asset_handle( $block_name, $field_name ) { + $field_mappings = array( + 'editorScript' => 'editor-script', + 'script' => 'script', + 'editorStyle' => 'editor-style', + 'style' => 'style', + ); + return str_replace( '/', '-', $block_name ) . + '-' . $field_mappings[ $field_name ]; +} + +/** + * Finds a script handle for the selected block metadata field. It detects + * when a path to file was provided and finds a corresponding + * asset file with details necessary to register the script under + * automatically generated handle name. It returns unprocessed script handle + * otherwise. + * + * @since 5.5.0 + * + * @param array $metadata Block metadata. + * @param string $field_name Field name to pick from metadata. + * @return string|bool Script handle provided directly or created through + * script's registration, or false on failure. + */ +function register_block_script_handle( $metadata, $field_name ) { + if ( empty( $metadata[ $field_name ] ) ) { + return false; + } + $script_handle = $metadata[ $field_name ]; + $script_path = remove_block_asset_path_prefix( $metadata[ $field_name ] ); + if ( $script_handle === $script_path ) { + return $script_handle; + } + + $script_handle = generate_block_asset_handle( $metadata['name'], $field_name ); + $script_asset_path = realpath( + dirname( $metadata['file'] ) . '/' . + substr_replace( $script_path, '.asset.php', - strlen( '.js' ) ) + ); + if ( ! file_exists( $script_asset_path ) ) { + $message = sprintf( + /* translators: %1: field name. %2: block name */ + __( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.', 'default' ), + $field_name, + $metadata['name'] + ); + _doing_it_wrong( __FUNCTION__, $message, '5.5.0' ); + return false; + } + $script_asset = require $script_asset_path; + $result = wp_register_script( + $script_handle, + plugins_url( $script_path, $metadata['file'] ), + $script_asset['dependencies'], + $script_asset['version'] + ); + return $result ? $script_handle : false; +} + +/** + * Finds a style handle for the block metadata field. It detects when a path + * to file was provided and registers the style under automatically + * generated handle name. It returns unprocessed style handle otherwise. + * + * @since 5.5.0 + * + * @param array $metadata Block metadata. + * @param string $field_name Field name to pick from metadata. + * @return string|boolean Style handle provided directly or created through + * style's registration, or false on failure. + */ +function register_block_style_handle( $metadata, $field_name ) { + if ( empty( $metadata[ $field_name ] ) ) { + return false; + } + $style_handle = $metadata[ $field_name ]; + $style_path = remove_block_asset_path_prefix( $metadata[ $field_name ] ); + if ( $style_handle === $style_path ) { + return $style_handle; + } + + $style_handle = generate_block_asset_handle( $metadata['name'], $field_name ); + $block_dir = dirname( $metadata['file'] ); + $result = wp_register_style( + $style_handle, + plugins_url( $style_path, $metadata['file'] ), + array(), + filemtime( realpath( "$block_dir/$style_path" ) ) + ); + return $result ? $style_handle : false; +} + +/** + * Registers a block type from metadata stored in the `block.json` file. + * + * @since 5.5.0 + * + * @param string $file_or_folder Path to the JSON file with metadata definition for + * the block or path to the folder where the `block.json` file is located. + * @param array $args { + * Optional. Array of block type arguments. Any arguments may be defined, however the + * ones described below are supported by default. Default empty array. + * + * @type callable $render_callback Callback used to render blocks of this block type. + * } + * @return WP_Block_Type|false The registered block type on success, or false on failure. + */ +function register_block_type_from_metadata( $file_or_folder, $args = array() ) { + $filename = 'block.json'; + $metadata_file = ( substr( $file_or_folder, -strlen( $filename ) ) !== $filename ) ? + trailingslashit( $file_or_folder ) . $filename : + $file_or_folder; + if ( ! file_exists( $metadata_file ) ) { + return false; + } + + $metadata = json_decode( file_get_contents( $metadata_file ), true ); + if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) { + return false; + } + $metadata['file'] = $metadata_file; + + $settings = array(); + $property_mappings = array( + 'title' => 'title', + 'category' => 'category', + 'parent' => 'parent', + 'icon' => 'icon', + 'description' => 'description', + 'keywords' => 'keywords', + 'attributes' => 'attributes', + 'providesContext' => 'provides_context', + 'usesContext' => 'uses_context', + 'supports' => 'supports', + 'styles' => 'styles', + 'example' => 'example', + ); + + foreach ( $property_mappings as $key => $mapped_key ) { + if ( isset( $metadata[ $key ] ) ) { + $settings[ $mapped_key ] = $metadata[ $key ]; + } + } + + if ( ! empty( $metadata['editorScript'] ) ) { + $settings['editor_script'] = register_block_script_handle( + $metadata, + 'editorScript' + ); + } + + if ( ! empty( $metadata['script'] ) ) { + $settings['script'] = register_block_script_handle( + $metadata, + 'script' + ); + } + + if ( ! empty( $metadata['editorStyle'] ) ) { + $settings['editor_style'] = register_block_style_handle( + $metadata, + 'editorStyle' + ); + } + + if ( ! empty( $metadata['style'] ) ) { + $settings['style'] = register_block_style_handle( + $metadata, + 'style' + ); + } + + return register_block_type( + $metadata['name'], + array_merge( + $settings, + $args + ) + ); +} + /** * Determine whether a post or content string has blocks. * diff --git a/wp-includes/version.php b/wp-includes/version.php index 735fabfb63..b6c320d6ce 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.5-alpha-48140'; +$wp_version = '5.5-alpha-48141'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.