wp/wp-includes/blocks.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
     4  *
     4  *
     5  * @package WordPress
     5  * @package WordPress
     6  * @subpackage Blocks
     6  * @subpackage Blocks
     7  * @since 5.0.0
     7  * @since 5.0.0
     8  */
     8  */
     9 
       
    10 /**
       
    11  * Registers a block type.
       
    12  *
       
    13  * @since 5.0.0
       
    14  *
       
    15  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively
       
    16  *                                   a complete WP_Block_Type instance. In case a WP_Block_Type
       
    17  *                                   is provided, the $args parameter will be ignored.
       
    18  * @param array                $args {
       
    19  *     Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`.
       
    20  *     Any arguments may be defined, however the ones described below are supported by default.
       
    21  *     Default empty array.
       
    22  *
       
    23  *     @type callable $render_callback Callback used to render blocks of this block type.
       
    24  * }
       
    25  * @return WP_Block_Type|false The registered block type on success, or false on failure.
       
    26  */
       
    27 function register_block_type( $name, $args = array() ) {
       
    28 	return WP_Block_Type_Registry::get_instance()->register( $name, $args );
       
    29 }
       
    30 
       
    31 /**
       
    32  * Unregisters a block type.
       
    33  *
       
    34  * @since 5.0.0
       
    35  *
       
    36  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively
       
    37  *                                   a complete WP_Block_Type instance.
       
    38  * @return WP_Block_Type|false The unregistered block type on success, or false on failure.
       
    39  */
       
    40 function unregister_block_type( $name ) {
       
    41 	return WP_Block_Type_Registry::get_instance()->unregister( $name );
       
    42 }
       
    43 
     9 
    44 /**
    10 /**
    45  * Removes the block asset's path prefix if provided.
    11  * Removes the block asset's path prefix if provided.
    46  *
    12  *
    47  * @since 5.5.0
    13  * @since 5.5.0
    69  * @param string $block_name Name of the block.
    35  * @param string $block_name Name of the block.
    70  * @param string $field_name Name of the metadata field.
    36  * @param string $field_name Name of the metadata field.
    71  * @return string Generated asset name for the block's field.
    37  * @return string Generated asset name for the block's field.
    72  */
    38  */
    73 function generate_block_asset_handle( $block_name, $field_name ) {
    39 function generate_block_asset_handle( $block_name, $field_name ) {
       
    40 	if ( 0 === strpos( $block_name, 'core/' ) ) {
       
    41 		$asset_handle = str_replace( 'core/', 'wp-block-', $block_name );
       
    42 		if ( 0 === strpos( $field_name, 'editor' ) ) {
       
    43 			$asset_handle .= '-editor';
       
    44 		}
       
    45 		return $asset_handle;
       
    46 	}
       
    47 
    74 	$field_mappings = array(
    48 	$field_mappings = array(
    75 		'editorScript' => 'editor-script',
    49 		'editorScript' => 'editor-script',
    76 		'script'       => 'script',
    50 		'script'       => 'script',
    77 		'editorStyle'  => 'editor-style',
    51 		'editorStyle'  => 'editor-style',
    78 		'style'        => 'style',
    52 		'style'        => 'style',
    89  *
    63  *
    90  * @since 5.5.0
    64  * @since 5.5.0
    91  *
    65  *
    92  * @param array  $metadata   Block metadata.
    66  * @param array  $metadata   Block metadata.
    93  * @param string $field_name Field name to pick from metadata.
    67  * @param string $field_name Field name to pick from metadata.
    94  * @return string|bool Script handle provided directly or created through
    68  * @return string|false Script handle provided directly or created through
    95  *                     script's registration, or false on failure.
    69  *                      script's registration, or false on failure.
    96  */
    70  */
    97 function register_block_script_handle( $metadata, $field_name ) {
    71 function register_block_script_handle( $metadata, $field_name ) {
    98 	if ( empty( $metadata[ $field_name ] ) ) {
    72 	if ( empty( $metadata[ $field_name ] ) ) {
    99 		return false;
    73 		return false;
   100 	}
    74 	}
   108 	$script_asset_path = realpath(
    82 	$script_asset_path = realpath(
   109 		dirname( $metadata['file'] ) . '/' .
    83 		dirname( $metadata['file'] ) . '/' .
   110 		substr_replace( $script_path, '.asset.php', - strlen( '.js' ) )
    84 		substr_replace( $script_path, '.asset.php', - strlen( '.js' ) )
   111 	);
    85 	);
   112 	if ( ! file_exists( $script_asset_path ) ) {
    86 	if ( ! file_exists( $script_asset_path ) ) {
   113 		$message = sprintf(
    87 		_doing_it_wrong(
   114 			/* translators: %1: field name. %2: block name */
    88 			__FUNCTION__,
   115 			__( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.', 'default' ),
    89 			sprintf(
   116 			$field_name,
    90 				/* translators: 1: Field name, 2: Block name. */
   117 			$metadata['name']
    91 				__( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.' ),
       
    92 				$field_name,
       
    93 				$metadata['name']
       
    94 			),
       
    95 			'5.5.0'
   118 		);
    96 		);
   119 		_doing_it_wrong( __FUNCTION__, $message, '5.5.0' );
       
   120 		return false;
    97 		return false;
   121 	}
    98 	}
   122 	$script_asset = require $script_asset_path;
    99 	$script_asset = require $script_asset_path;
   123 	$result       = wp_register_script(
   100 	$result       = wp_register_script(
   124 		$script_handle,
   101 		$script_handle,
   125 		plugins_url( $script_path, $metadata['file'] ),
   102 		plugins_url( $script_path, $metadata['file'] ),
   126 		$script_asset['dependencies'],
   103 		$script_asset['dependencies'],
   127 		$script_asset['version']
   104 		$script_asset['version']
   128 	);
   105 	);
   129 	return $result ? $script_handle : false;
   106 	if ( ! $result ) {
       
   107 		return false;
       
   108 	}
       
   109 
       
   110 	if ( ! empty( $metadata['textdomain'] ) ) {
       
   111 		wp_set_script_translations( $script_handle, $metadata['textdomain'] );
       
   112 	}
       
   113 
       
   114 	return $script_handle;
   130 }
   115 }
   131 
   116 
   132 /**
   117 /**
   133  * Finds a style handle for the block metadata field. It detects when a path
   118  * Finds a style handle for the block metadata field. It detects when a path
   134  * to file was provided and registers the style under automatically
   119  * to file was provided and registers the style under automatically
   135  * generated handle name. It returns unprocessed style handle otherwise.
   120  * generated handle name. It returns unprocessed style handle otherwise.
   136  *
   121  *
   137  * @since 5.5.0
   122  * @since 5.5.0
   138  *
   123  *
   139  * @param array  $metadata Block metadata.
   124  * @param array  $metadata   Block metadata.
   140  * @param string $field_name Field name to pick from metadata.
   125  * @param string $field_name Field name to pick from metadata.
   141  * @return string|boolean Style handle provided directly or created through
   126  * @return string|false Style handle provided directly or created through
   142  *                        style's registration, or false on failure.
   127  *                      style's registration, or false on failure.
   143  */
   128  */
   144 function register_block_style_handle( $metadata, $field_name ) {
   129 function register_block_style_handle( $metadata, $field_name ) {
   145 	if ( empty( $metadata[ $field_name ] ) ) {
   130 	if ( empty( $metadata[ $field_name ] ) ) {
   146 		return false;
   131 		return false;
   147 	}
   132 	}
       
   133 	$is_core_block = isset( $metadata['file'] ) && 0 === strpos( $metadata['file'], ABSPATH . WPINC );
       
   134 	if ( $is_core_block && ! wp_should_load_separate_core_block_assets() ) {
       
   135 		return false;
       
   136 	}
       
   137 
       
   138 	// Check whether styles should have a ".min" suffix or not.
       
   139 	$suffix = SCRIPT_DEBUG ? '' : '.min';
       
   140 
   148 	$style_handle = $metadata[ $field_name ];
   141 	$style_handle = $metadata[ $field_name ];
   149 	$style_path   = remove_block_asset_path_prefix( $metadata[ $field_name ] );
   142 	$style_path   = remove_block_asset_path_prefix( $metadata[ $field_name ] );
   150 	if ( $style_handle === $style_path ) {
   143 
       
   144 	if ( $style_handle === $style_path && ! $is_core_block ) {
   151 		return $style_handle;
   145 		return $style_handle;
   152 	}
   146 	}
   153 
   147 
   154 	$style_handle = generate_block_asset_handle( $metadata['name'], $field_name );
   148 	$style_uri = plugins_url( $style_path, $metadata['file'] );
   155 	$block_dir    = dirname( $metadata['file'] );
   149 	if ( $is_core_block ) {
   156 	$result       = wp_register_style(
   150 		$style_path = "style$suffix.css";
       
   151 		$style_uri  = includes_url( 'blocks/' . str_replace( 'core/', '', $metadata['name'] ) . "/style$suffix.css" );
       
   152 	}
       
   153 
       
   154 	$style_handle   = generate_block_asset_handle( $metadata['name'], $field_name );
       
   155 	$block_dir      = dirname( $metadata['file'] );
       
   156 	$style_file     = realpath( "$block_dir/$style_path" );
       
   157 	$has_style_file = false !== $style_file;
       
   158 	$version        = ! $is_core_block && isset( $metadata['version'] ) ? $metadata['version'] : false;
       
   159 	$style_uri      = $has_style_file ? $style_uri : false;
       
   160 	$result         = wp_register_style(
   157 		$style_handle,
   161 		$style_handle,
   158 		plugins_url( $style_path, $metadata['file'] ),
   162 		$style_uri,
   159 		array(),
   163 		array(),
   160 		filemtime( realpath( "$block_dir/$style_path" ) )
   164 		$version
   161 	);
   165 	);
       
   166 	if ( file_exists( str_replace( '.css', '-rtl.css', $style_file ) ) ) {
       
   167 		wp_style_add_data( $style_handle, 'rtl', 'replace' );
       
   168 	}
       
   169 	if ( $has_style_file ) {
       
   170 		wp_style_add_data( $style_handle, 'path', $style_file );
       
   171 	}
       
   172 
       
   173 	$rtl_file = str_replace( "$suffix.css", "-rtl$suffix.css", $style_file );
       
   174 	if ( is_rtl() && file_exists( $rtl_file ) ) {
       
   175 		wp_style_add_data( $style_handle, 'path', $rtl_file );
       
   176 	}
       
   177 
   162 	return $result ? $style_handle : false;
   178 	return $result ? $style_handle : false;
   163 }
   179 }
   164 
   180 
   165 /**
   181 /**
   166  * Registers a block type from metadata stored in the `block.json` file.
   182  * Registers a block type from the metadata stored in the `block.json` file.
   167  *
   183  *
   168  * @since 5.5.0
   184  * @since 5.5.0
   169  *
   185  *
   170  * @param string $file_or_folder Path to the JSON file with metadata definition for
   186  * @param string $file_or_folder Path to the JSON file with metadata definition for
   171  *                               the block or path to the folder where the `block.json` file is located.
   187  *                               the block or path to the folder where the `block.json` file is located.
   172  * @param array  $args {
   188  * @param array  $args           Optional. Array of block type arguments. Accepts any public property
   173  *     Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`.
   189  *                               of `WP_Block_Type`. See WP_Block_Type::__construct() for information
   174  *     Any arguments may be defined, however the ones described below are supported by default.
   190  *                               on accepted arguments. Default empty array.
   175  *     Default empty array.
       
   176  *
       
   177  *     @type callable $render_callback Callback used to render blocks of this block type.
       
   178  * }
       
   179  * @return WP_Block_Type|false The registered block type on success, or false on failure.
   191  * @return WP_Block_Type|false The registered block type on success, or false on failure.
   180  */
   192  */
   181 function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
   193 function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
   182 	$filename      = 'block.json';
   194 	$filename      = 'block.json';
   183 	$metadata_file = ( substr( $file_or_folder, -strlen( $filename ) ) !== $filename ) ?
   195 	$metadata_file = ( substr( $file_or_folder, -strlen( $filename ) ) !== $filename ) ?
   190 	$metadata = json_decode( file_get_contents( $metadata_file ), true );
   202 	$metadata = json_decode( file_get_contents( $metadata_file ), true );
   191 	if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) {
   203 	if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) {
   192 		return false;
   204 		return false;
   193 	}
   205 	}
   194 	$metadata['file'] = $metadata_file;
   206 	$metadata['file'] = $metadata_file;
       
   207 
       
   208 	/**
       
   209 	 * Filters the metadata provided for registering a block type.
       
   210 	 *
       
   211 	 * @since 5.7.0
       
   212 	 *
       
   213 	 * @param array $metadata Metadata for registering a block type.
       
   214 	 */
       
   215 	$metadata = apply_filters( 'block_type_metadata', $metadata );
       
   216 
       
   217 	// Add `style` and `editor_style` for core blocks if missing.
       
   218 	if ( ! empty( $metadata['name'] ) && 0 === strpos( $metadata['name'], 'core/' ) ) {
       
   219 		$block_name = str_replace( 'core/', '', $metadata['name'] );
       
   220 
       
   221 		if ( ! isset( $metadata['style'] ) ) {
       
   222 			$metadata['style'] = "wp-block-$block_name";
       
   223 		}
       
   224 		if ( ! isset( $metadata['editorStyle'] ) ) {
       
   225 			$metadata['editorStyle'] = "wp-block-{$block_name}-editor";
       
   226 		}
       
   227 	}
   195 
   228 
   196 	$settings          = array();
   229 	$settings          = array();
   197 	$property_mappings = array(
   230 	$property_mappings = array(
   198 		'title'           => 'title',
   231 		'title'           => 'title',
   199 		'category'        => 'category',
   232 		'category'        => 'category',
   205 		'providesContext' => 'provides_context',
   238 		'providesContext' => 'provides_context',
   206 		'usesContext'     => 'uses_context',
   239 		'usesContext'     => 'uses_context',
   207 		'supports'        => 'supports',
   240 		'supports'        => 'supports',
   208 		'styles'          => 'styles',
   241 		'styles'          => 'styles',
   209 		'example'         => 'example',
   242 		'example'         => 'example',
       
   243 		'apiVersion'      => 'api_version',
   210 	);
   244 	);
   211 
   245 
   212 	foreach ( $property_mappings as $key => $mapped_key ) {
   246 	foreach ( $property_mappings as $key => $mapped_key ) {
   213 		if ( isset( $metadata[ $key ] ) ) {
   247 		if ( isset( $metadata[ $key ] ) ) {
   214 			$settings[ $mapped_key ] = $metadata[ $key ];
   248 			$value = $metadata[ $key ];
       
   249 			if ( empty( $metadata['textdomain'] ) ) {
       
   250 				$settings[ $mapped_key ] = $value;
       
   251 				continue;
       
   252 			}
       
   253 			$textdomain = $metadata['textdomain'];
       
   254 			switch ( $key ) {
       
   255 				case 'title':
       
   256 				case 'description':
       
   257 					// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralContext,WordPress.WP.I18n.NonSingularStringLiteralDomain
       
   258 					$settings[ $mapped_key ] = translate_with_gettext_context( $value, sprintf( 'block %s', $key ), $textdomain );
       
   259 					break;
       
   260 				case 'keywords':
       
   261 					$settings[ $mapped_key ] = array();
       
   262 					if ( ! is_array( $value ) ) {
       
   263 						continue 2;
       
   264 					}
       
   265 
       
   266 					foreach ( $value as $keyword ) {
       
   267 						// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
       
   268 						$settings[ $mapped_key ][] = translate_with_gettext_context( $keyword, 'block keyword', $textdomain );
       
   269 					}
       
   270 
       
   271 					break;
       
   272 				case 'styles':
       
   273 					$settings[ $mapped_key ] = array();
       
   274 					if ( ! is_array( $value ) ) {
       
   275 						continue 2;
       
   276 					}
       
   277 
       
   278 					foreach ( $value as $style ) {
       
   279 						if ( ! empty( $style['label'] ) ) {
       
   280 							// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
       
   281 							$style['label'] = translate_with_gettext_context( $style['label'], 'block style label', $textdomain );
       
   282 						}
       
   283 						$settings[ $mapped_key ][] = $style;
       
   284 					}
       
   285 
       
   286 					break;
       
   287 				default:
       
   288 					$settings[ $mapped_key ] = $value;
       
   289 			}
   215 		}
   290 		}
   216 	}
   291 	}
   217 
   292 
   218 	if ( ! empty( $metadata['editorScript'] ) ) {
   293 	if ( ! empty( $metadata['editorScript'] ) ) {
   219 		$settings['editor_script'] = register_block_script_handle(
   294 		$settings['editor_script'] = register_block_script_handle(
   241 			$metadata,
   316 			$metadata,
   242 			'style'
   317 			'style'
   243 		);
   318 		);
   244 	}
   319 	}
   245 
   320 
   246 	return register_block_type(
   321 	/**
   247 		$metadata['name'],
   322 	 * Filters the settings determined from the block type metadata.
       
   323 	 *
       
   324 	 * @since 5.7.0
       
   325 	 *
       
   326 	 * @param array $settings Array of determined settings for registering a block type.
       
   327 	 * @param array $metadata Metadata provided for registering a block type.
       
   328 	 */
       
   329 	$settings = apply_filters(
       
   330 		'block_type_metadata_settings',
   248 		array_merge(
   331 		array_merge(
   249 			$settings,
   332 			$settings,
   250 			$args
   333 			$args
   251 		)
   334 		),
   252 	);
   335 		$metadata
       
   336 	);
       
   337 
       
   338 	return WP_Block_Type_Registry::get_instance()->register(
       
   339 		$metadata['name'],
       
   340 		$settings
       
   341 	);
       
   342 }
       
   343 
       
   344 /**
       
   345  * Registers a block type. The recommended way is to register a block type using
       
   346  * the metadata stored in the `block.json` file.
       
   347  *
       
   348  * @since 5.0.0
       
   349  * @since 5.8.0 First param accepts a path to the `block.json` file.
       
   350  *
       
   351  * @param string|WP_Block_Type $block_type Block type name including namespace, or alternatively
       
   352  *                                         a path to the JSON file with metadata definition for the block,
       
   353  *                                         or a path to the folder where the `block.json` file is located,
       
   354  *                                         or a complete WP_Block_Type instance.
       
   355  *                                         In case a WP_Block_Type is provided, the $args parameter will be ignored.
       
   356  * @param array                $args       Optional. Array of block type arguments. Accepts any public property
       
   357  *                                         of `WP_Block_Type`. See WP_Block_Type::__construct() for information
       
   358  *                                         on accepted arguments. Default empty array.
       
   359  *
       
   360  * @return WP_Block_Type|false The registered block type on success, or false on failure.
       
   361  */
       
   362 function register_block_type( $block_type, $args = array() ) {
       
   363 	if ( is_string( $block_type ) && file_exists( $block_type ) ) {
       
   364 		return register_block_type_from_metadata( $block_type, $args );
       
   365 	}
       
   366 
       
   367 	return WP_Block_Type_Registry::get_instance()->register( $block_type, $args );
       
   368 }
       
   369 
       
   370 /**
       
   371  * Unregisters a block type.
       
   372  *
       
   373  * @since 5.0.0
       
   374  *
       
   375  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively
       
   376  *                                   a complete WP_Block_Type instance.
       
   377  * @return WP_Block_Type|false The unregistered block type on success, or false on failure.
       
   378  */
       
   379 function unregister_block_type( $name ) {
       
   380 	return WP_Block_Type_Registry::get_instance()->unregister( $name );
   253 }
   381 }
   254 
   382 
   255 /**
   383 /**
   256  * Determine whether a post or content string has blocks.
   384  * Determine whether a post or content string has blocks.
   257  *
   385  *
   261  *
   389  *
   262  * @since 5.0.0
   390  * @since 5.0.0
   263  *
   391  *
   264  * @see parse_blocks()
   392  * @see parse_blocks()
   265  *
   393  *
   266  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
   394  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object.
       
   395  *                                      Defaults to global $post.
   267  * @return bool Whether the post has blocks.
   396  * @return bool Whether the post has blocks.
   268  */
   397  */
   269 function has_blocks( $post = null ) {
   398 function has_blocks( $post = null ) {
   270 	if ( ! is_string( $post ) ) {
   399 	if ( ! is_string( $post ) ) {
   271 		$wp_post = get_post( $post );
   400 		$wp_post = get_post( $post );
   279 
   408 
   280 /**
   409 /**
   281  * Determine whether a $post or a string contains a specific block type.
   410  * Determine whether a $post or a string contains a specific block type.
   282  *
   411  *
   283  * This test optimizes for performance rather than strict accuracy, detecting
   412  * This test optimizes for performance rather than strict accuracy, detecting
   284  * the block type exists but not validating its structure. For strict accuracy,
   413  * whether the block type exists but not validating its structure and not checking
   285  * you should use the block parser on post content.
   414  * reusable blocks. For strict accuracy, you should use the block parser on post content.
   286  *
   415  *
   287  * @since 5.0.0
   416  * @since 5.0.0
   288  *
   417  *
   289  * @see parse_blocks()
   418  * @see parse_blocks()
   290  *
   419  *
   291  * @param string                  $block_name Full Block type to look for.
   420  * @param string                  $block_name Full block type to look for.
   292  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
   421  * @param int|string|WP_Post|null $post       Optional. Post content, post ID, or post object.
       
   422  *                                            Defaults to global $post.
   293  * @return bool Whether the post content contains the specified block.
   423  * @return bool Whether the post content contains the specified block.
   294  */
   424  */
   295 function has_block( $block_name, $post = null ) {
   425 function has_block( $block_name, $post = null ) {
   296 	if ( ! has_blocks( $post ) ) {
   426 	if ( ! has_blocks( $post ) ) {
   297 		return false;
   427 		return false;
   356  *
   486  *
   357  * The serialized result is a JSON-encoded string, with unicode escape sequence
   487  * The serialized result is a JSON-encoded string, with unicode escape sequence
   358  * substitution for characters which might otherwise interfere with embedding
   488  * substitution for characters which might otherwise interfere with embedding
   359  * the result in an HTML comment.
   489  * the result in an HTML comment.
   360  *
   490  *
       
   491  * This function must produce output that remains in sync with the output of
       
   492  * the serializeAttributes JavaScript function in the block editor in order
       
   493  * to ensure consistent operation between PHP and JavaScript.
       
   494  *
   361  * @since 5.3.1
   495  * @since 5.3.1
   362  *
   496  *
   363  * @param array $block_attributes Attributes object.
   497  * @param array $block_attributes Attributes object.
   364  * @return string Serialized attributes.
   498  * @return string Serialized attributes.
   365  */
   499  */
   366 function serialize_block_attributes( $block_attributes ) {
   500 function serialize_block_attributes( $block_attributes ) {
   367 	$encoded_attributes = json_encode( $block_attributes );
   501 	$encoded_attributes = wp_json_encode( $block_attributes, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
   368 	$encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
   502 	$encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
   369 	$encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
   503 	$encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
   370 	$encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
   504 	$encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
   371 	$encoded_attributes = preg_replace( '/&/', '\\u0026', $encoded_attributes );
   505 	$encoded_attributes = preg_replace( '/&/', '\\u0026', $encoded_attributes );
   372 	// Regex: /\\"/
   506 	// Regex: /\\"/
   395 /**
   529 /**
   396  * Returns the content of a block, including comment delimiters.
   530  * Returns the content of a block, including comment delimiters.
   397  *
   531  *
   398  * @since 5.3.1
   532  * @since 5.3.1
   399  *
   533  *
   400  * @param string $block_name       Block name.
   534  * @param string|null $block_name       Block name. Null if the block name is unknown,
   401  * @param array  $block_attributes Block attributes.
   535  *                                      e.g. Classic blocks have their name set to null.
   402  * @param string $block_content    Block save content.
   536  * @param array       $block_attributes Block attributes.
       
   537  * @param string      $block_content    Block save content.
   403  * @return string Comment-delimited block content.
   538  * @return string Comment-delimited block content.
   404  */
   539  */
   405 function get_comment_delimited_block_content( $block_name = null, $block_attributes, $block_content ) {
   540 function get_comment_delimited_block_content( $block_name, $block_attributes, $block_content ) {
   406 	if ( is_null( $block_name ) ) {
   541 	if ( is_null( $block_name ) ) {
   407 		return $block_content;
   542 		return $block_content;
   408 	}
   543 	}
   409 
   544 
   410 	$serialized_block_name = strip_core_block_namespace( $block_name );
   545 	$serialized_block_name = strip_core_block_namespace( $block_name );
   577 		'core/quote',
   712 		'core/quote',
   578 		'core/table',
   713 		'core/table',
   579 		'core/verse',
   714 		'core/verse',
   580 	);
   715 	);
   581 
   716 
   582 	$allowed_blocks = array_merge( $allowed_inner_blocks, array( 'core/columns' ) );
   717 	$allowed_wrapper_blocks = array(
       
   718 		'core/columns',
       
   719 		'core/column',
       
   720 		'core/group',
       
   721 	);
       
   722 
       
   723 	/**
       
   724 	 * Filters the list of blocks that can be used as wrapper blocks, allowing
       
   725 	 * excerpts to be generated from the `innerBlocks` of these wrappers.
       
   726 	 *
       
   727 	 * @since 5.8.0
       
   728 	 *
       
   729 	 * @param array $allowed_wrapper_blocks The list of allowed wrapper blocks.
       
   730 	 */
       
   731 	$allowed_wrapper_blocks = apply_filters( 'excerpt_allowed_wrapper_blocks', $allowed_wrapper_blocks );
       
   732 
       
   733 	$allowed_blocks = array_merge( $allowed_inner_blocks, $allowed_wrapper_blocks );
   583 
   734 
   584 	/**
   735 	/**
   585 	 * Filters the list of blocks that can contribute to the excerpt.
   736 	 * Filters the list of blocks that can contribute to the excerpt.
   586 	 *
   737 	 *
   587 	 * If a dynamic block is added to this list, it must not generate another
   738 	 * If a dynamic block is added to this list, it must not generate another
   596 	$output         = '';
   747 	$output         = '';
   597 
   748 
   598 	foreach ( $blocks as $block ) {
   749 	foreach ( $blocks as $block ) {
   599 		if ( in_array( $block['blockName'], $allowed_blocks, true ) ) {
   750 		if ( in_array( $block['blockName'], $allowed_blocks, true ) ) {
   600 			if ( ! empty( $block['innerBlocks'] ) ) {
   751 			if ( ! empty( $block['innerBlocks'] ) ) {
   601 				if ( 'core/columns' === $block['blockName'] ) {
   752 				if ( in_array( $block['blockName'], $allowed_wrapper_blocks, true ) ) {
   602 					$output .= _excerpt_render_inner_columns_blocks( $block, $allowed_inner_blocks );
   753 					$output .= _excerpt_render_inner_blocks( $block, $allowed_blocks );
   603 					continue;
   754 					continue;
   604 				}
   755 				}
   605 
   756 
   606 				// Skip the block if it has disallowed or nested inner blocks.
   757 				// Skip the block if it has disallowed or nested inner blocks.
   607 				foreach ( $block['innerBlocks'] as $inner_block ) {
   758 				foreach ( $block['innerBlocks'] as $inner_block ) {
   620 
   771 
   621 	return $output;
   772 	return $output;
   622 }
   773 }
   623 
   774 
   624 /**
   775 /**
   625  * Render inner blocks from the `core/columns` block for generating an excerpt.
   776  * Render inner blocks from the allowed wrapper blocks
   626  *
   777  * for generating an excerpt.
   627  * @since 5.2.0
   778  *
       
   779  * @since 5.8
   628  * @access private
   780  * @access private
   629  *
   781  *
   630  * @param array $columns        The parsed columns block.
   782  * @param array $parsed_block   The parsed block.
   631  * @param array $allowed_blocks The list of allowed inner blocks.
   783  * @param array $allowed_blocks The list of allowed inner blocks.
   632  * @return string The rendered inner blocks.
   784  * @return string The rendered inner blocks.
   633  */
   785  */
   634 function _excerpt_render_inner_columns_blocks( $columns, $allowed_blocks ) {
   786 function _excerpt_render_inner_blocks( $parsed_block, $allowed_blocks ) {
   635 	$output = '';
   787 	$output = '';
   636 
   788 
   637 	foreach ( $columns['innerBlocks'] as $column ) {
   789 	foreach ( $parsed_block['innerBlocks'] as $inner_block ) {
   638 		foreach ( $column['innerBlocks'] as $inner_block ) {
   790 		if ( ! in_array( $inner_block['blockName'], $allowed_blocks, true ) ) {
   639 			if ( in_array( $inner_block['blockName'], $allowed_blocks, true ) && empty( $inner_block['innerBlocks'] ) ) {
   791 			continue;
   640 				$output .= render_block( $inner_block );
   792 		}
   641 			}
   793 
       
   794 		if ( empty( $inner_block['innerBlocks'] ) ) {
       
   795 			$output .= render_block( $inner_block );
       
   796 		} else {
       
   797 			$output .= _excerpt_render_inner_blocks( $inner_block, $allowed_blocks );
   642 		}
   798 		}
   643 	}
   799 	}
   644 
   800 
   645 	return $output;
   801 	return $output;
   646 }
   802 }
   649  * Renders a single block into a HTML string.
   805  * Renders a single block into a HTML string.
   650  *
   806  *
   651  * @since 5.0.0
   807  * @since 5.0.0
   652  *
   808  *
   653  * @global WP_Post  $post     The post to edit.
   809  * @global WP_Post  $post     The post to edit.
   654  * @global WP_Query $wp_query WordPress Query object.
       
   655  *
   810  *
   656  * @param array $parsed_block A single parsed block object.
   811  * @param array $parsed_block A single parsed block object.
   657  * @return string String of rendered HTML.
   812  * @return string String of rendered HTML.
   658  */
   813  */
   659 function render_block( $parsed_block ) {
   814 function render_block( $parsed_block ) {
   660 	global $post, $wp_query;
   815 	global $post;
   661 
   816 
   662 	/**
   817 	/**
   663 	 * Allows render_block() to be short-circuited, by returning a non-null value.
   818 	 * Allows render_block() to be short-circuited, by returning a non-null value.
   664 	 *
   819 	 *
   665 	 * @since 5.1.0
   820 	 * @since 5.1.0
   696 		 * it should be included to consistently fulfill the expectation.
   851 		 * it should be included to consistently fulfill the expectation.
   697 		 */
   852 		 */
   698 		$context['postType'] = $post->post_type;
   853 		$context['postType'] = $post->post_type;
   699 	}
   854 	}
   700 
   855 
   701 	if ( $wp_query instanceof WP_Query && isset( $wp_query->tax_query->queried_terms['category'] ) ) {
       
   702 		$context['query'] = array( 'categoryIds' => array() );
       
   703 		foreach ( $wp_query->tax_query->queried_terms['category']['terms'] as $category_slug_or_id ) {
       
   704 			$context['query']['categoryIds'][] = 'slug' === $wp_query->tax_query->queried_terms['category']['field'] ? get_cat_ID( $category_slug_or_id ) : $category_slug_or_id;
       
   705 		}
       
   706 	}
       
   707 
       
   708 	/**
   856 	/**
   709 	 * Filters the default context provided to a rendered block.
   857 	 * Filters the default context provided to a rendered block.
   710 	 *
   858 	 *
   711 	 * @since 5.5.0
   859 	 * @since 5.5.0
   712 	 *
   860 	 *
   809  *
   957  *
   810  * @param string $block_name       Block type name including namespace.
   958  * @param string $block_name       Block type name including namespace.
   811  * @param array  $style_properties Array containing the properties of the style name,
   959  * @param array  $style_properties Array containing the properties of the style name,
   812  *                                 label, style (name of the stylesheet to be enqueued),
   960  *                                 label, style (name of the stylesheet to be enqueued),
   813  *                                 inline_style (string containing the CSS to be added).
   961  *                                 inline_style (string containing the CSS to be added).
   814  * @return boolean True if the block style was registered with success and false otherwise.
   962  * @return bool True if the block style was registered with success and false otherwise.
   815  */
   963  */
   816 function register_block_style( $block_name, $style_properties ) {
   964 function register_block_style( $block_name, $style_properties ) {
   817 	return WP_Block_Styles_Registry::get_instance()->register( $block_name, $style_properties );
   965 	return WP_Block_Styles_Registry::get_instance()->register( $block_name, $style_properties );
   818 }
   966 }
   819 
   967 
   821  * Unregisters a block style.
   969  * Unregisters a block style.
   822  *
   970  *
   823  * @since 5.3.0
   971  * @since 5.3.0
   824  *
   972  *
   825  * @param string $block_name       Block type name including namespace.
   973  * @param string $block_name       Block type name including namespace.
   826  * @param array  $block_style_name Block style name.
   974  * @param string $block_style_name Block style name.
   827  * @return boolean True if the block style was unregistered with success and false otherwise.
   975  * @return bool True if the block style was unregistered with success and false otherwise.
   828  */
   976  */
   829 function unregister_block_style( $block_name, $block_style_name ) {
   977 function unregister_block_style( $block_name, $block_style_name ) {
   830 	return WP_Block_Styles_Registry::get_instance()->unregister( $block_name, $block_style_name );
   978 	return WP_Block_Styles_Registry::get_instance()->unregister( $block_name, $block_style_name );
   831 }
   979 }
       
   980 
       
   981 /**
       
   982  * Checks whether the current block type supports the feature requested.
       
   983  *
       
   984  * @since 5.8.0
       
   985  *
       
   986  * @param WP_Block_Type $block_type Block type to check for support.
       
   987  * @param string        $feature    Name of the feature to check support for.
       
   988  * @param mixed         $default    Fallback value for feature support, defaults to false.
       
   989  *
       
   990  * @return boolean                  Whether or not the feature is supported.
       
   991  */
       
   992 function block_has_support( $block_type, $feature, $default = false ) {
       
   993 	$block_support = $default;
       
   994 	if ( $block_type && property_exists( $block_type, 'supports' ) ) {
       
   995 		$block_support = _wp_array_get( $block_type->supports, $feature, $default );
       
   996 	}
       
   997 
       
   998 	return true === $block_support || is_array( $block_support );
       
   999 }
       
  1000 
       
  1001 /**
       
  1002  * Converts typography keys declared under `supports.*` to `supports.typography.*`.
       
  1003  *
       
  1004  * Displays a `_doing_it_wrong()` notice when a block using the older format is detected.
       
  1005  *
       
  1006  * @since 5.8.0
       
  1007  *
       
  1008  * @param array $metadata Metadata for registering a block type.
       
  1009  * @return array Filtered metadata for registering a block type.
       
  1010  */
       
  1011 function wp_migrate_old_typography_shape( $metadata ) {
       
  1012 	if ( ! isset( $metadata['supports'] ) ) {
       
  1013 		return $metadata;
       
  1014 	}
       
  1015 
       
  1016 	$typography_keys = array(
       
  1017 		'__experimentalFontFamily',
       
  1018 		'__experimentalFontStyle',
       
  1019 		'__experimentalFontWeight',
       
  1020 		'__experimentalLetterSpacing',
       
  1021 		'__experimentalTextDecoration',
       
  1022 		'__experimentalTextTransform',
       
  1023 		'fontSize',
       
  1024 		'lineHeight',
       
  1025 	);
       
  1026 
       
  1027 	foreach ( $typography_keys as $typography_key ) {
       
  1028 		$support_for_key = _wp_array_get( $metadata['supports'], array( $typography_key ), null );
       
  1029 
       
  1030 		if ( null !== $support_for_key ) {
       
  1031 			_doing_it_wrong(
       
  1032 				'register_block_type_from_metadata()',
       
  1033 				sprintf(
       
  1034 					/* translators: 1: Block type, 2: Typography supports key, e.g: fontSize, lineHeight, etc. 3: block.json, 4: Old metadata key, 5: New metadata key. */
       
  1035 					__( 'Block "%1$s" is declaring %2$s support in %3$s file under %4$s. %2$s support is now declared under %5$s.' ),
       
  1036 					$metadata['name'],
       
  1037 					"<code>$typography_key</code>",
       
  1038 					'<code>block.json</code>',
       
  1039 					"<code>supports.$typography_key</code>",
       
  1040 					"<code>supports.typography.$typography_key</code>"
       
  1041 				),
       
  1042 				'5.8.0'
       
  1043 			);
       
  1044 
       
  1045 			_wp_array_set( $metadata['supports'], array( 'typography', $typography_key ), $support_for_key );
       
  1046 			unset( $metadata['supports'][ $typography_key ] );
       
  1047 		}
       
  1048 	}
       
  1049 
       
  1050 	return $metadata;
       
  1051 }
       
  1052 
       
  1053 /**
       
  1054  * Helper function that constructs a WP_Query args array from
       
  1055  * a `Query` block properties.
       
  1056  *
       
  1057  * It's used in Query Loop, Query Pagination Numbers and Query Pagination Next blocks.
       
  1058  *
       
  1059  * @since 5.8.0
       
  1060  *
       
  1061  * @param WP_Block $block Block instance.
       
  1062  * @param int      $page  Current query's page.
       
  1063  *
       
  1064  * @return array Returns the constructed WP_Query arguments.
       
  1065  */
       
  1066 function build_query_vars_from_query_block( $block, $page ) {
       
  1067 	$query = array(
       
  1068 		'post_type'    => 'post',
       
  1069 		'order'        => 'DESC',
       
  1070 		'orderby'      => 'date',
       
  1071 		'post__not_in' => array(),
       
  1072 	);
       
  1073 
       
  1074 	if ( isset( $block->context['query'] ) ) {
       
  1075 		if ( ! empty( $block->context['query']['postType'] ) ) {
       
  1076 			$post_type_param = $block->context['query']['postType'];
       
  1077 			if ( is_post_type_viewable( $post_type_param ) ) {
       
  1078 				$query['post_type'] = $post_type_param;
       
  1079 			}
       
  1080 		}
       
  1081 		if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) {
       
  1082 			$sticky = get_option( 'sticky_posts' );
       
  1083 			if ( 'only' === $block->context['query']['sticky'] ) {
       
  1084 				$query['post__in'] = $sticky;
       
  1085 			} else {
       
  1086 				$query['post__not_in'] = array_merge( $query['post__not_in'], $sticky );
       
  1087 			}
       
  1088 		}
       
  1089 		if ( ! empty( $block->context['query']['exclude'] ) ) {
       
  1090 			$excluded_post_ids     = array_map( 'intval', $block->context['query']['exclude'] );
       
  1091 			$excluded_post_ids     = array_filter( $excluded_post_ids );
       
  1092 			$query['post__not_in'] = array_merge( $query['post__not_in'], $excluded_post_ids );
       
  1093 		}
       
  1094 		if (
       
  1095 			isset( $block->context['query']['perPage'] ) &&
       
  1096 			is_numeric( $block->context['query']['perPage'] )
       
  1097 		) {
       
  1098 			$per_page = absint( $block->context['query']['perPage'] );
       
  1099 			$offset   = 0;
       
  1100 
       
  1101 			if (
       
  1102 				isset( $block->context['query']['offset'] ) &&
       
  1103 				is_numeric( $block->context['query']['offset'] )
       
  1104 			) {
       
  1105 				$offset = absint( $block->context['query']['offset'] );
       
  1106 			}
       
  1107 
       
  1108 			$query['offset']         = ( $per_page * ( $page - 1 ) ) + $offset;
       
  1109 			$query['posts_per_page'] = $per_page;
       
  1110 		}
       
  1111 		if ( ! empty( $block->context['query']['categoryIds'] ) ) {
       
  1112 			$term_ids              = array_map( 'intval', $block->context['query']['categoryIds'] );
       
  1113 			$term_ids              = array_filter( $term_ids );
       
  1114 			$query['category__in'] = $term_ids;
       
  1115 		}
       
  1116 		if ( ! empty( $block->context['query']['tagIds'] ) ) {
       
  1117 			$term_ids         = array_map( 'intval', $block->context['query']['tagIds'] );
       
  1118 			$term_ids         = array_filter( $term_ids );
       
  1119 			$query['tag__in'] = $term_ids;
       
  1120 		}
       
  1121 		if (
       
  1122 			isset( $block->context['query']['order'] ) &&
       
  1123 				in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true )
       
  1124 		) {
       
  1125 			$query['order'] = strtoupper( $block->context['query']['order'] );
       
  1126 		}
       
  1127 		if ( isset( $block->context['query']['orderBy'] ) ) {
       
  1128 			$query['orderby'] = $block->context['query']['orderBy'];
       
  1129 		}
       
  1130 		if (
       
  1131 			isset( $block->context['query']['author'] ) &&
       
  1132 			(int) $block->context['query']['author'] > 0
       
  1133 		) {
       
  1134 			$query['author'] = (int) $block->context['query']['author'];
       
  1135 		}
       
  1136 		if ( ! empty( $block->context['query']['search'] ) ) {
       
  1137 			$query['s'] = $block->context['query']['search'];
       
  1138 		}
       
  1139 	}
       
  1140 	return $query;
       
  1141 }