diff -r 34716fd837a4 -r be944660c56a wp/wp-includes/blocks.php --- a/wp/wp-includes/blocks.php Tue Dec 15 15:52:01 2020 +0100 +++ b/wp/wp-includes/blocks.php Wed Sep 21 18:19:35 2022 +0200 @@ -8,40 +8,6 @@ */ /** - * Registers a block type. - * - * @since 5.0.0 - * - * @param string|WP_Block_Type $name Block type name including namespace, or alternatively - * a complete WP_Block_Type instance. In case a WP_Block_Type - * is provided, the $args parameter will be ignored. - * @param array $args { - * Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`. - * 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( $name, $args = array() ) { - return WP_Block_Type_Registry::get_instance()->register( $name, $args ); -} - -/** - * Unregisters a block type. - * - * @since 5.0.0 - * - * @param string|WP_Block_Type $name Block type name including namespace, or alternatively - * a complete WP_Block_Type instance. - * @return WP_Block_Type|false The unregistered block type on success, or false on failure. - */ -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 @@ -71,6 +37,14 @@ * @return string Generated asset name for the block's field. */ function generate_block_asset_handle( $block_name, $field_name ) { + if ( 0 === strpos( $block_name, 'core/' ) ) { + $asset_handle = str_replace( 'core/', 'wp-block-', $block_name ); + if ( 0 === strpos( $field_name, 'editor' ) ) { + $asset_handle .= '-editor'; + } + return $asset_handle; + } + $field_mappings = array( 'editorScript' => 'editor-script', 'script' => 'script', @@ -91,8 +65,8 @@ * * @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. + * @return string|false 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 ] ) ) { @@ -110,13 +84,16 @@ 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__, + sprintf( + /* translators: 1: Field name, 2: Block name. */ + __( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.' ), + $field_name, + $metadata['name'] + ), + '5.5.0' ); - _doing_it_wrong( __FUNCTION__, $message, '5.5.0' ); return false; } $script_asset = require $script_asset_path; @@ -126,7 +103,15 @@ $script_asset['dependencies'], $script_asset['version'] ); - return $result ? $script_handle : false; + if ( ! $result ) { + return false; + } + + if ( ! empty( $metadata['textdomain'] ) ) { + wp_set_script_translations( $script_handle, $metadata['textdomain'] ); + } + + return $script_handle; } /** @@ -136,46 +121,73 @@ * * @since 5.5.0 * - * @param array $metadata Block metadata. + * @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. + * @return string|false 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; } + $is_core_block = isset( $metadata['file'] ) && 0 === strpos( $metadata['file'], ABSPATH . WPINC ); + if ( $is_core_block && ! wp_should_load_separate_core_block_assets() ) { + return false; + } + + // Check whether styles should have a ".min" suffix or not. + $suffix = SCRIPT_DEBUG ? '' : '.min'; + $style_handle = $metadata[ $field_name ]; $style_path = remove_block_asset_path_prefix( $metadata[ $field_name ] ); - if ( $style_handle === $style_path ) { + + if ( $style_handle === $style_path && ! $is_core_block ) { return $style_handle; } - $style_handle = generate_block_asset_handle( $metadata['name'], $field_name ); - $block_dir = dirname( $metadata['file'] ); - $result = wp_register_style( + $style_uri = plugins_url( $style_path, $metadata['file'] ); + if ( $is_core_block ) { + $style_path = "style$suffix.css"; + $style_uri = includes_url( 'blocks/' . str_replace( 'core/', '', $metadata['name'] ) . "/style$suffix.css" ); + } + + $style_handle = generate_block_asset_handle( $metadata['name'], $field_name ); + $block_dir = dirname( $metadata['file'] ); + $style_file = realpath( "$block_dir/$style_path" ); + $has_style_file = false !== $style_file; + $version = ! $is_core_block && isset( $metadata['version'] ) ? $metadata['version'] : false; + $style_uri = $has_style_file ? $style_uri : false; + $result = wp_register_style( $style_handle, - plugins_url( $style_path, $metadata['file'] ), + $style_uri, array(), - filemtime( realpath( "$block_dir/$style_path" ) ) + $version ); + if ( file_exists( str_replace( '.css', '-rtl.css', $style_file ) ) ) { + wp_style_add_data( $style_handle, 'rtl', 'replace' ); + } + if ( $has_style_file ) { + wp_style_add_data( $style_handle, 'path', $style_file ); + } + + $rtl_file = str_replace( "$suffix.css", "-rtl$suffix.css", $style_file ); + if ( is_rtl() && file_exists( $rtl_file ) ) { + wp_style_add_data( $style_handle, 'path', $rtl_file ); + } + return $result ? $style_handle : false; } /** - * Registers a block type from metadata stored in the `block.json` file. + * Registers a block type from the 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. Accepts any public property of `WP_Block_Type`. - * 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. - * } + * @param array $args Optional. Array of block type arguments. Accepts any public property + * of `WP_Block_Type`. See WP_Block_Type::__construct() for information + * on accepted arguments. Default empty array. * @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() ) { @@ -193,6 +205,27 @@ } $metadata['file'] = $metadata_file; + /** + * Filters the metadata provided for registering a block type. + * + * @since 5.7.0 + * + * @param array $metadata Metadata for registering a block type. + */ + $metadata = apply_filters( 'block_type_metadata', $metadata ); + + // Add `style` and `editor_style` for core blocks if missing. + if ( ! empty( $metadata['name'] ) && 0 === strpos( $metadata['name'], 'core/' ) ) { + $block_name = str_replace( 'core/', '', $metadata['name'] ); + + if ( ! isset( $metadata['style'] ) ) { + $metadata['style'] = "wp-block-$block_name"; + } + if ( ! isset( $metadata['editorStyle'] ) ) { + $metadata['editorStyle'] = "wp-block-{$block_name}-editor"; + } + } + $settings = array(); $property_mappings = array( 'title' => 'title', @@ -207,11 +240,53 @@ 'supports' => 'supports', 'styles' => 'styles', 'example' => 'example', + 'apiVersion' => 'api_version', ); foreach ( $property_mappings as $key => $mapped_key ) { if ( isset( $metadata[ $key ] ) ) { - $settings[ $mapped_key ] = $metadata[ $key ]; + $value = $metadata[ $key ]; + if ( empty( $metadata['textdomain'] ) ) { + $settings[ $mapped_key ] = $value; + continue; + } + $textdomain = $metadata['textdomain']; + switch ( $key ) { + case 'title': + case 'description': + // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralContext,WordPress.WP.I18n.NonSingularStringLiteralDomain + $settings[ $mapped_key ] = translate_with_gettext_context( $value, sprintf( 'block %s', $key ), $textdomain ); + break; + case 'keywords': + $settings[ $mapped_key ] = array(); + if ( ! is_array( $value ) ) { + continue 2; + } + + foreach ( $value as $keyword ) { + // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain + $settings[ $mapped_key ][] = translate_with_gettext_context( $keyword, 'block keyword', $textdomain ); + } + + break; + case 'styles': + $settings[ $mapped_key ] = array(); + if ( ! is_array( $value ) ) { + continue 2; + } + + foreach ( $value as $style ) { + if ( ! empty( $style['label'] ) ) { + // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain + $style['label'] = translate_with_gettext_context( $style['label'], 'block style label', $textdomain ); + } + $settings[ $mapped_key ][] = $style; + } + + break; + default: + $settings[ $mapped_key ] = $value; + } } } @@ -243,16 +318,69 @@ ); } - return register_block_type( - $metadata['name'], + /** + * Filters the settings determined from the block type metadata. + * + * @since 5.7.0 + * + * @param array $settings Array of determined settings for registering a block type. + * @param array $metadata Metadata provided for registering a block type. + */ + $settings = apply_filters( + 'block_type_metadata_settings', array_merge( $settings, $args - ) + ), + $metadata + ); + + return WP_Block_Type_Registry::get_instance()->register( + $metadata['name'], + $settings ); } /** + * Registers a block type. The recommended way is to register a block type using + * the metadata stored in the `block.json` file. + * + * @since 5.0.0 + * @since 5.8.0 First param accepts a path to the `block.json` file. + * + * @param string|WP_Block_Type $block_type Block type name including namespace, or alternatively + * a path to the JSON file with metadata definition for the block, + * or a path to the folder where the `block.json` file is located, + * or a complete WP_Block_Type instance. + * In case a WP_Block_Type is provided, the $args parameter will be ignored. + * @param array $args Optional. Array of block type arguments. Accepts any public property + * of `WP_Block_Type`. See WP_Block_Type::__construct() for information + * on accepted arguments. Default empty array. + * + * @return WP_Block_Type|false The registered block type on success, or false on failure. + */ +function register_block_type( $block_type, $args = array() ) { + if ( is_string( $block_type ) && file_exists( $block_type ) ) { + return register_block_type_from_metadata( $block_type, $args ); + } + + return WP_Block_Type_Registry::get_instance()->register( $block_type, $args ); +} + +/** + * Unregisters a block type. + * + * @since 5.0.0 + * + * @param string|WP_Block_Type $name Block type name including namespace, or alternatively + * a complete WP_Block_Type instance. + * @return WP_Block_Type|false The unregistered block type on success, or false on failure. + */ +function unregister_block_type( $name ) { + return WP_Block_Type_Registry::get_instance()->unregister( $name ); +} + +/** * Determine whether a post or content string has blocks. * * This test optimizes for performance rather than strict accuracy, detecting @@ -263,7 +391,8 @@ * * @see parse_blocks() * - * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post. + * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. + * Defaults to global $post. * @return bool Whether the post has blocks. */ function has_blocks( $post = null ) { @@ -281,15 +410,16 @@ * Determine whether a $post or a string contains a specific block type. * * This test optimizes for performance rather than strict accuracy, detecting - * the block type exists but not validating its structure. For strict accuracy, - * you should use the block parser on post content. + * whether the block type exists but not validating its structure and not checking + * reusable blocks. For strict accuracy, you should use the block parser on post content. * * @since 5.0.0 * * @see parse_blocks() * - * @param string $block_name Full Block type to look for. - * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post. + * @param string $block_name Full block type to look for. + * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. + * Defaults to global $post. * @return bool Whether the post content contains the specified block. */ function has_block( $block_name, $post = null ) { @@ -358,13 +488,17 @@ * substitution for characters which might otherwise interfere with embedding * the result in an HTML comment. * + * This function must produce output that remains in sync with the output of + * the serializeAttributes JavaScript function in the block editor in order + * to ensure consistent operation between PHP and JavaScript. + * * @since 5.3.1 * * @param array $block_attributes Attributes object. * @return string Serialized attributes. */ function serialize_block_attributes( $block_attributes ) { - $encoded_attributes = json_encode( $block_attributes ); + $encoded_attributes = wp_json_encode( $block_attributes, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes ); $encoded_attributes = preg_replace( '//', '\\u003e', $encoded_attributes ); @@ -397,12 +531,13 @@ * * @since 5.3.1 * - * @param string $block_name Block name. - * @param array $block_attributes Block attributes. - * @param string $block_content Block save content. + * @param string|null $block_name Block name. Null if the block name is unknown, + * e.g. Classic blocks have their name set to null. + * @param array $block_attributes Block attributes. + * @param string $block_content Block save content. * @return string Comment-delimited block content. */ -function get_comment_delimited_block_content( $block_name = null, $block_attributes, $block_content ) { +function get_comment_delimited_block_content( $block_name, $block_attributes, $block_content ) { if ( is_null( $block_name ) ) { return $block_content; } @@ -579,7 +714,23 @@ 'core/verse', ); - $allowed_blocks = array_merge( $allowed_inner_blocks, array( 'core/columns' ) ); + $allowed_wrapper_blocks = array( + 'core/columns', + 'core/column', + 'core/group', + ); + + /** + * Filters the list of blocks that can be used as wrapper blocks, allowing + * excerpts to be generated from the `innerBlocks` of these wrappers. + * + * @since 5.8.0 + * + * @param array $allowed_wrapper_blocks The list of allowed wrapper blocks. + */ + $allowed_wrapper_blocks = apply_filters( 'excerpt_allowed_wrapper_blocks', $allowed_wrapper_blocks ); + + $allowed_blocks = array_merge( $allowed_inner_blocks, $allowed_wrapper_blocks ); /** * Filters the list of blocks that can contribute to the excerpt. @@ -598,8 +749,8 @@ foreach ( $blocks as $block ) { if ( in_array( $block['blockName'], $allowed_blocks, true ) ) { if ( ! empty( $block['innerBlocks'] ) ) { - if ( 'core/columns' === $block['blockName'] ) { - $output .= _excerpt_render_inner_columns_blocks( $block, $allowed_inner_blocks ); + if ( in_array( $block['blockName'], $allowed_wrapper_blocks, true ) ) { + $output .= _excerpt_render_inner_blocks( $block, $allowed_blocks ); continue; } @@ -622,23 +773,28 @@ } /** - * Render inner blocks from the `core/columns` block for generating an excerpt. + * Render inner blocks from the allowed wrapper blocks + * for generating an excerpt. * - * @since 5.2.0 + * @since 5.8 * @access private * - * @param array $columns The parsed columns block. + * @param array $parsed_block The parsed block. * @param array $allowed_blocks The list of allowed inner blocks. * @return string The rendered inner blocks. */ -function _excerpt_render_inner_columns_blocks( $columns, $allowed_blocks ) { +function _excerpt_render_inner_blocks( $parsed_block, $allowed_blocks ) { $output = ''; - foreach ( $columns['innerBlocks'] as $column ) { - foreach ( $column['innerBlocks'] as $inner_block ) { - if ( in_array( $inner_block['blockName'], $allowed_blocks, true ) && empty( $inner_block['innerBlocks'] ) ) { - $output .= render_block( $inner_block ); - } + foreach ( $parsed_block['innerBlocks'] as $inner_block ) { + if ( ! in_array( $inner_block['blockName'], $allowed_blocks, true ) ) { + continue; + } + + if ( empty( $inner_block['innerBlocks'] ) ) { + $output .= render_block( $inner_block ); + } else { + $output .= _excerpt_render_inner_blocks( $inner_block, $allowed_blocks ); } } @@ -651,13 +807,12 @@ * @since 5.0.0 * * @global WP_Post $post The post to edit. - * @global WP_Query $wp_query WordPress Query object. * * @param array $parsed_block A single parsed block object. * @return string String of rendered HTML. */ function render_block( $parsed_block ) { - global $post, $wp_query; + global $post; /** * Allows render_block() to be short-circuited, by returning a non-null value. @@ -698,13 +853,6 @@ $context['postType'] = $post->post_type; } - if ( $wp_query instanceof WP_Query && isset( $wp_query->tax_query->queried_terms['category'] ) ) { - $context['query'] = array( 'categoryIds' => array() ); - foreach ( $wp_query->tax_query->queried_terms['category']['terms'] as $category_slug_or_id ) { - $context['query']['categoryIds'][] = 'slug' === $wp_query->tax_query->queried_terms['category']['field'] ? get_cat_ID( $category_slug_or_id ) : $category_slug_or_id; - } - } - /** * Filters the default context provided to a rendered block. * @@ -811,7 +959,7 @@ * @param array $style_properties Array containing the properties of the style name, * label, style (name of the stylesheet to be enqueued), * inline_style (string containing the CSS to be added). - * @return boolean True if the block style was registered with success and false otherwise. + * @return bool True if the block style was registered with success and false otherwise. */ function register_block_style( $block_name, $style_properties ) { return WP_Block_Styles_Registry::get_instance()->register( $block_name, $style_properties ); @@ -823,9 +971,171 @@ * @since 5.3.0 * * @param string $block_name Block type name including namespace. - * @param array $block_style_name Block style name. - * @return boolean True if the block style was unregistered with success and false otherwise. + * @param string $block_style_name Block style name. + * @return bool True if the block style was unregistered with success and false otherwise. */ function unregister_block_style( $block_name, $block_style_name ) { return WP_Block_Styles_Registry::get_instance()->unregister( $block_name, $block_style_name ); } + +/** + * Checks whether the current block type supports the feature requested. + * + * @since 5.8.0 + * + * @param WP_Block_Type $block_type Block type to check for support. + * @param string $feature Name of the feature to check support for. + * @param mixed $default Fallback value for feature support, defaults to false. + * + * @return boolean Whether or not the feature is supported. + */ +function block_has_support( $block_type, $feature, $default = false ) { + $block_support = $default; + if ( $block_type && property_exists( $block_type, 'supports' ) ) { + $block_support = _wp_array_get( $block_type->supports, $feature, $default ); + } + + return true === $block_support || is_array( $block_support ); +} + +/** + * Converts typography keys declared under `supports.*` to `supports.typography.*`. + * + * Displays a `_doing_it_wrong()` notice when a block using the older format is detected. + * + * @since 5.8.0 + * + * @param array $metadata Metadata for registering a block type. + * @return array Filtered metadata for registering a block type. + */ +function wp_migrate_old_typography_shape( $metadata ) { + if ( ! isset( $metadata['supports'] ) ) { + return $metadata; + } + + $typography_keys = array( + '__experimentalFontFamily', + '__experimentalFontStyle', + '__experimentalFontWeight', + '__experimentalLetterSpacing', + '__experimentalTextDecoration', + '__experimentalTextTransform', + 'fontSize', + 'lineHeight', + ); + + foreach ( $typography_keys as $typography_key ) { + $support_for_key = _wp_array_get( $metadata['supports'], array( $typography_key ), null ); + + if ( null !== $support_for_key ) { + _doing_it_wrong( + 'register_block_type_from_metadata()', + sprintf( + /* translators: 1: Block type, 2: Typography supports key, e.g: fontSize, lineHeight, etc. 3: block.json, 4: Old metadata key, 5: New metadata key. */ + __( 'Block "%1$s" is declaring %2$s support in %3$s file under %4$s. %2$s support is now declared under %5$s.' ), + $metadata['name'], + "$typography_key", + 'block.json', + "supports.$typography_key", + "supports.typography.$typography_key" + ), + '5.8.0' + ); + + _wp_array_set( $metadata['supports'], array( 'typography', $typography_key ), $support_for_key ); + unset( $metadata['supports'][ $typography_key ] ); + } + } + + return $metadata; +} + +/** + * Helper function that constructs a WP_Query args array from + * a `Query` block properties. + * + * It's used in Query Loop, Query Pagination Numbers and Query Pagination Next blocks. + * + * @since 5.8.0 + * + * @param WP_Block $block Block instance. + * @param int $page Current query's page. + * + * @return array Returns the constructed WP_Query arguments. + */ +function build_query_vars_from_query_block( $block, $page ) { + $query = array( + 'post_type' => 'post', + 'order' => 'DESC', + 'orderby' => 'date', + 'post__not_in' => array(), + ); + + if ( isset( $block->context['query'] ) ) { + if ( ! empty( $block->context['query']['postType'] ) ) { + $post_type_param = $block->context['query']['postType']; + if ( is_post_type_viewable( $post_type_param ) ) { + $query['post_type'] = $post_type_param; + } + } + if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) { + $sticky = get_option( 'sticky_posts' ); + if ( 'only' === $block->context['query']['sticky'] ) { + $query['post__in'] = $sticky; + } else { + $query['post__not_in'] = array_merge( $query['post__not_in'], $sticky ); + } + } + if ( ! empty( $block->context['query']['exclude'] ) ) { + $excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] ); + $excluded_post_ids = array_filter( $excluded_post_ids ); + $query['post__not_in'] = array_merge( $query['post__not_in'], $excluded_post_ids ); + } + if ( + isset( $block->context['query']['perPage'] ) && + is_numeric( $block->context['query']['perPage'] ) + ) { + $per_page = absint( $block->context['query']['perPage'] ); + $offset = 0; + + if ( + isset( $block->context['query']['offset'] ) && + is_numeric( $block->context['query']['offset'] ) + ) { + $offset = absint( $block->context['query']['offset'] ); + } + + $query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset; + $query['posts_per_page'] = $per_page; + } + if ( ! empty( $block->context['query']['categoryIds'] ) ) { + $term_ids = array_map( 'intval', $block->context['query']['categoryIds'] ); + $term_ids = array_filter( $term_ids ); + $query['category__in'] = $term_ids; + } + if ( ! empty( $block->context['query']['tagIds'] ) ) { + $term_ids = array_map( 'intval', $block->context['query']['tagIds'] ); + $term_ids = array_filter( $term_ids ); + $query['tag__in'] = $term_ids; + } + if ( + isset( $block->context['query']['order'] ) && + in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true ) + ) { + $query['order'] = strtoupper( $block->context['query']['order'] ); + } + if ( isset( $block->context['query']['orderBy'] ) ) { + $query['orderby'] = $block->context['query']['orderBy']; + } + if ( + isset( $block->context['query']['author'] ) && + (int) $block->context['query']['author'] > 0 + ) { + $query['author'] = (int) $block->context['query']['author']; + } + if ( ! empty( $block->context['query']['search'] ) ) { + $query['s'] = $block->context['query']['search']; + } + } + return $query; +}