wp/wp-includes/blocks.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    10 /**
    10 /**
    11  * Registers a block type.
    11  * Registers a block type.
    12  *
    12  *
    13  * @since 5.0.0
    13  * @since 5.0.0
    14  *
    14  *
    15  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
    15  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively
    16  *                                   complete WP_Block_Type instance. In case a WP_Block_Type
    16  *                                   a complete WP_Block_Type instance. In case a WP_Block_Type
    17  *                                   is provided, the $args parameter will be ignored.
    17  *                                   is provided, the $args parameter will be ignored.
    18  * @param array                $args {
    18  * @param array                $args {
    19  *     Optional. Array of block type arguments. Any arguments may be defined, however the
    19  *     Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`.
    20  *     ones described below are supported by default. Default empty array.
    20  *     Any arguments may be defined, however the ones described below are supported by default.
       
    21  *     Default empty array.
    21  *
    22  *
    22  *     @type callable $render_callback Callback used to render blocks of this block type.
    23  *     @type callable $render_callback Callback used to render blocks of this block type.
    23  * }
    24  * }
    24  * @return WP_Block_Type|false The registered block type on success, or false on failure.
    25  * @return WP_Block_Type|false The registered block type on success, or false on failure.
    25  */
    26  */
    30 /**
    31 /**
    31  * Unregisters a block type.
    32  * Unregisters a block type.
    32  *
    33  *
    33  * @since 5.0.0
    34  * @since 5.0.0
    34  *
    35  *
    35  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
    36  * @param string|WP_Block_Type $name Block type name including namespace, or alternatively
    36  *                                   complete WP_Block_Type instance.
    37  *                                   a complete WP_Block_Type instance.
    37  * @return WP_Block_Type|false The unregistered block type on success, or false on failure.
    38  * @return WP_Block_Type|false The unregistered block type on success, or false on failure.
    38  */
    39  */
    39 function unregister_block_type( $name ) {
    40 function unregister_block_type( $name ) {
    40 	return WP_Block_Type_Registry::get_instance()->unregister( $name );
    41 	return WP_Block_Type_Registry::get_instance()->unregister( $name );
       
    42 }
       
    43 
       
    44 /**
       
    45  * Removes the block asset's path prefix if provided.
       
    46  *
       
    47  * @since 5.5.0
       
    48  *
       
    49  * @param string $asset_handle_or_path Asset handle or prefixed path.
       
    50  * @return string Path without the prefix or the original value.
       
    51  */
       
    52 function remove_block_asset_path_prefix( $asset_handle_or_path ) {
       
    53 	$path_prefix = 'file:';
       
    54 	if ( 0 !== strpos( $asset_handle_or_path, $path_prefix ) ) {
       
    55 		return $asset_handle_or_path;
       
    56 	}
       
    57 	return substr(
       
    58 		$asset_handle_or_path,
       
    59 		strlen( $path_prefix )
       
    60 	);
       
    61 }
       
    62 
       
    63 /**
       
    64  * Generates the name for an asset based on the name of the block
       
    65  * and the field name provided.
       
    66  *
       
    67  * @since 5.5.0
       
    68  *
       
    69  * @param string $block_name Name of the block.
       
    70  * @param string $field_name Name of the metadata field.
       
    71  * @return string Generated asset name for the block's field.
       
    72  */
       
    73 function generate_block_asset_handle( $block_name, $field_name ) {
       
    74 	$field_mappings = array(
       
    75 		'editorScript' => 'editor-script',
       
    76 		'script'       => 'script',
       
    77 		'editorStyle'  => 'editor-style',
       
    78 		'style'        => 'style',
       
    79 	);
       
    80 	return str_replace( '/', '-', $block_name ) .
       
    81 		'-' . $field_mappings[ $field_name ];
       
    82 }
       
    83 
       
    84 /**
       
    85  * Finds a script handle for the selected block metadata field. It detects
       
    86  * when a path to file was provided and finds a corresponding asset file
       
    87  * with details necessary to register the script under automatically
       
    88  * generated handle name. It returns unprocessed script handle otherwise.
       
    89  *
       
    90  * @since 5.5.0
       
    91  *
       
    92  * @param array  $metadata   Block metadata.
       
    93  * @param string $field_name Field name to pick from metadata.
       
    94  * @return string|bool Script handle provided directly or created through
       
    95  *                     script's registration, or false on failure.
       
    96  */
       
    97 function register_block_script_handle( $metadata, $field_name ) {
       
    98 	if ( empty( $metadata[ $field_name ] ) ) {
       
    99 		return false;
       
   100 	}
       
   101 	$script_handle = $metadata[ $field_name ];
       
   102 	$script_path   = remove_block_asset_path_prefix( $metadata[ $field_name ] );
       
   103 	if ( $script_handle === $script_path ) {
       
   104 		return $script_handle;
       
   105 	}
       
   106 
       
   107 	$script_handle     = generate_block_asset_handle( $metadata['name'], $field_name );
       
   108 	$script_asset_path = realpath(
       
   109 		dirname( $metadata['file'] ) . '/' .
       
   110 		substr_replace( $script_path, '.asset.php', - strlen( '.js' ) )
       
   111 	);
       
   112 	if ( ! file_exists( $script_asset_path ) ) {
       
   113 		$message = sprintf(
       
   114 			/* translators: %1: field name. %2: block name */
       
   115 			__( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.', 'default' ),
       
   116 			$field_name,
       
   117 			$metadata['name']
       
   118 		);
       
   119 		_doing_it_wrong( __FUNCTION__, $message, '5.5.0' );
       
   120 		return false;
       
   121 	}
       
   122 	$script_asset = require $script_asset_path;
       
   123 	$result       = wp_register_script(
       
   124 		$script_handle,
       
   125 		plugins_url( $script_path, $metadata['file'] ),
       
   126 		$script_asset['dependencies'],
       
   127 		$script_asset['version']
       
   128 	);
       
   129 	return $result ? $script_handle : false;
       
   130 }
       
   131 
       
   132 /**
       
   133  * 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
       
   135  * generated handle name. It returns unprocessed style handle otherwise.
       
   136  *
       
   137  * @since 5.5.0
       
   138  *
       
   139  * @param array  $metadata Block metadata.
       
   140  * @param string $field_name Field name to pick from metadata.
       
   141  * @return string|boolean Style handle provided directly or created through
       
   142  *                        style's registration, or false on failure.
       
   143  */
       
   144 function register_block_style_handle( $metadata, $field_name ) {
       
   145 	if ( empty( $metadata[ $field_name ] ) ) {
       
   146 		return false;
       
   147 	}
       
   148 	$style_handle = $metadata[ $field_name ];
       
   149 	$style_path   = remove_block_asset_path_prefix( $metadata[ $field_name ] );
       
   150 	if ( $style_handle === $style_path ) {
       
   151 		return $style_handle;
       
   152 	}
       
   153 
       
   154 	$style_handle = generate_block_asset_handle( $metadata['name'], $field_name );
       
   155 	$block_dir    = dirname( $metadata['file'] );
       
   156 	$result       = wp_register_style(
       
   157 		$style_handle,
       
   158 		plugins_url( $style_path, $metadata['file'] ),
       
   159 		array(),
       
   160 		filemtime( realpath( "$block_dir/$style_path" ) )
       
   161 	);
       
   162 	return $result ? $style_handle : false;
       
   163 }
       
   164 
       
   165 /**
       
   166  * Registers a block type from metadata stored in the `block.json` file.
       
   167  *
       
   168  * @since 5.5.0
       
   169  *
       
   170  * @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.
       
   172  * @param array  $args {
       
   173  *     Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`.
       
   174  *     Any arguments may be defined, however the ones described below are supported by default.
       
   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.
       
   180  */
       
   181 function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
       
   182 	$filename      = 'block.json';
       
   183 	$metadata_file = ( substr( $file_or_folder, -strlen( $filename ) ) !== $filename ) ?
       
   184 		trailingslashit( $file_or_folder ) . $filename :
       
   185 		$file_or_folder;
       
   186 	if ( ! file_exists( $metadata_file ) ) {
       
   187 		return false;
       
   188 	}
       
   189 
       
   190 	$metadata = json_decode( file_get_contents( $metadata_file ), true );
       
   191 	if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) {
       
   192 		return false;
       
   193 	}
       
   194 	$metadata['file'] = $metadata_file;
       
   195 
       
   196 	$settings          = array();
       
   197 	$property_mappings = array(
       
   198 		'title'           => 'title',
       
   199 		'category'        => 'category',
       
   200 		'parent'          => 'parent',
       
   201 		'icon'            => 'icon',
       
   202 		'description'     => 'description',
       
   203 		'keywords'        => 'keywords',
       
   204 		'attributes'      => 'attributes',
       
   205 		'providesContext' => 'provides_context',
       
   206 		'usesContext'     => 'uses_context',
       
   207 		'supports'        => 'supports',
       
   208 		'styles'          => 'styles',
       
   209 		'example'         => 'example',
       
   210 	);
       
   211 
       
   212 	foreach ( $property_mappings as $key => $mapped_key ) {
       
   213 		if ( isset( $metadata[ $key ] ) ) {
       
   214 			$settings[ $mapped_key ] = $metadata[ $key ];
       
   215 		}
       
   216 	}
       
   217 
       
   218 	if ( ! empty( $metadata['editorScript'] ) ) {
       
   219 		$settings['editor_script'] = register_block_script_handle(
       
   220 			$metadata,
       
   221 			'editorScript'
       
   222 		);
       
   223 	}
       
   224 
       
   225 	if ( ! empty( $metadata['script'] ) ) {
       
   226 		$settings['script'] = register_block_script_handle(
       
   227 			$metadata,
       
   228 			'script'
       
   229 		);
       
   230 	}
       
   231 
       
   232 	if ( ! empty( $metadata['editorStyle'] ) ) {
       
   233 		$settings['editor_style'] = register_block_style_handle(
       
   234 			$metadata,
       
   235 			'editorStyle'
       
   236 		);
       
   237 	}
       
   238 
       
   239 	if ( ! empty( $metadata['style'] ) ) {
       
   240 		$settings['style'] = register_block_style_handle(
       
   241 			$metadata,
       
   242 			'style'
       
   243 		);
       
   244 	}
       
   245 
       
   246 	return register_block_type(
       
   247 		$metadata['name'],
       
   248 		array_merge(
       
   249 			$settings,
       
   250 			$args
       
   251 		)
       
   252 	);
    41 }
   253 }
    42 
   254 
    43 /**
   255 /**
    44  * Determine whether a post or content string has blocks.
   256  * Determine whether a post or content string has blocks.
    45  *
   257  *
    46  * This test optimizes for performance rather than strict accuracy, detecting
   258  * This test optimizes for performance rather than strict accuracy, detecting
    47  * the pattern of a block but not validating its structure. For strict accuracy,
   259  * the pattern of a block but not validating its structure. For strict accuracy,
    48  * you should use the block parser on post content.
   260  * you should use the block parser on post content.
    49  *
   261  *
    50  * @since 5.0.0
   262  * @since 5.0.0
       
   263  *
    51  * @see parse_blocks()
   264  * @see parse_blocks()
    52  *
   265  *
    53  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
   266  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
    54  * @return bool Whether the post has blocks.
   267  * @return bool Whether the post has blocks.
    55  */
   268  */
    70  * This test optimizes for performance rather than strict accuracy, detecting
   283  * This test optimizes for performance rather than strict accuracy, detecting
    71  * the block type exists but not validating its structure. For strict accuracy,
   284  * the block type exists but not validating its structure. For strict accuracy,
    72  * you should use the block parser on post content.
   285  * you should use the block parser on post content.
    73  *
   286  *
    74  * @since 5.0.0
   287  * @since 5.0.0
       
   288  *
    75  * @see parse_blocks()
   289  * @see parse_blocks()
    76  *
   290  *
    77  * @param string                  $block_type Full Block type to look for.
   291  * @param string                  $block_name Full Block type to look for.
    78  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
   292  * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
    79  * @return bool Whether the post content contains the specified block.
   293  * @return bool Whether the post content contains the specified block.
    80  */
   294  */
    81 function has_block( $block_type, $post = null ) {
   295 function has_block( $block_name, $post = null ) {
    82 	if ( ! has_blocks( $post ) ) {
   296 	if ( ! has_blocks( $post ) ) {
    83 		return false;
   297 		return false;
    84 	}
   298 	}
    85 
   299 
    86 	if ( ! is_string( $post ) ) {
   300 	if ( ! is_string( $post ) ) {
    88 		if ( $wp_post instanceof WP_Post ) {
   302 		if ( $wp_post instanceof WP_Post ) {
    89 			$post = $wp_post->post_content;
   303 			$post = $wp_post->post_content;
    90 		}
   304 		}
    91 	}
   305 	}
    92 
   306 
    93 	return false !== strpos( $post, '<!-- wp:' . $block_type . ' ' );
   307 	/*
       
   308 	 * Normalize block name to include namespace, if provided as non-namespaced.
       
   309 	 * This matches behavior for WordPress 5.0.0 - 5.3.0 in matching blocks by
       
   310 	 * their serialized names.
       
   311 	 */
       
   312 	if ( false === strpos( $block_name, '/' ) ) {
       
   313 		$block_name = 'core/' . $block_name;
       
   314 	}
       
   315 
       
   316 	// Test for existence of block by its fully qualified name.
       
   317 	$has_block = false !== strpos( $post, '<!-- wp:' . $block_name . ' ' );
       
   318 
       
   319 	if ( ! $has_block ) {
       
   320 		/*
       
   321 		 * If the given block name would serialize to a different name, test for
       
   322 		 * existence by the serialized form.
       
   323 		 */
       
   324 		$serialized_block_name = strip_core_block_namespace( $block_name );
       
   325 		if ( $serialized_block_name !== $block_name ) {
       
   326 			$has_block = false !== strpos( $post, '<!-- wp:' . $serialized_block_name . ' ' );
       
   327 		}
       
   328 	}
       
   329 
       
   330 	return $has_block;
    94 }
   331 }
    95 
   332 
    96 /**
   333 /**
    97  * Returns an array of the names of all registered dynamic block types.
   334  * Returns an array of the names of all registered dynamic block types.
    98  *
   335  *
    99  * @since 5.0.0
   336  * @since 5.0.0
   100  *
   337  *
   101  * @return array Array of dynamic block names.
   338  * @return string[] Array of dynamic block names.
   102  */
   339  */
   103 function get_dynamic_block_names() {
   340 function get_dynamic_block_names() {
   104 	$dynamic_block_names = array();
   341 	$dynamic_block_names = array();
   105 
   342 
   106 	$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
   343 	$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
   109 			$dynamic_block_names[] = $block_type->name;
   346 			$dynamic_block_names[] = $block_type->name;
   110 		}
   347 		}
   111 	}
   348 	}
   112 
   349 
   113 	return $dynamic_block_names;
   350 	return $dynamic_block_names;
       
   351 }
       
   352 
       
   353 /**
       
   354  * Given an array of attributes, returns a string in the serialized attributes
       
   355  * format prepared for post content.
       
   356  *
       
   357  * The serialized result is a JSON-encoded string, with unicode escape sequence
       
   358  * substitution for characters which might otherwise interfere with embedding
       
   359  * the result in an HTML comment.
       
   360  *
       
   361  * @since 5.3.1
       
   362  *
       
   363  * @param array $block_attributes Attributes object.
       
   364  * @return string Serialized attributes.
       
   365  */
       
   366 function serialize_block_attributes( $block_attributes ) {
       
   367 	$encoded_attributes = json_encode( $block_attributes );
       
   368 	$encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
       
   369 	$encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
       
   370 	$encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
       
   371 	$encoded_attributes = preg_replace( '/&/', '\\u0026', $encoded_attributes );
       
   372 	// Regex: /\\"/
       
   373 	$encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes );
       
   374 
       
   375 	return $encoded_attributes;
       
   376 }
       
   377 
       
   378 /**
       
   379  * Returns the block name to use for serialization. This will remove the default
       
   380  * "core/" namespace from a block name.
       
   381  *
       
   382  * @since 5.3.1
       
   383  *
       
   384  * @param string $block_name Original block name.
       
   385  * @return string Block name to use for serialization.
       
   386  */
       
   387 function strip_core_block_namespace( $block_name = null ) {
       
   388 	if ( is_string( $block_name ) && 0 === strpos( $block_name, 'core/' ) ) {
       
   389 		return substr( $block_name, 5 );
       
   390 	}
       
   391 
       
   392 	return $block_name;
       
   393 }
       
   394 
       
   395 /**
       
   396  * Returns the content of a block, including comment delimiters.
       
   397  *
       
   398  * @since 5.3.1
       
   399  *
       
   400  * @param string $block_name       Block name.
       
   401  * @param array  $block_attributes Block attributes.
       
   402  * @param string $block_content    Block save content.
       
   403  * @return string Comment-delimited block content.
       
   404  */
       
   405 function get_comment_delimited_block_content( $block_name = null, $block_attributes, $block_content ) {
       
   406 	if ( is_null( $block_name ) ) {
       
   407 		return $block_content;
       
   408 	}
       
   409 
       
   410 	$serialized_block_name = strip_core_block_namespace( $block_name );
       
   411 	$serialized_attributes = empty( $block_attributes ) ? '' : serialize_block_attributes( $block_attributes ) . ' ';
       
   412 
       
   413 	if ( empty( $block_content ) ) {
       
   414 		return sprintf( '<!-- wp:%s %s/-->', $serialized_block_name, $serialized_attributes );
       
   415 	}
       
   416 
       
   417 	return sprintf(
       
   418 		'<!-- wp:%s %s-->%s<!-- /wp:%s -->',
       
   419 		$serialized_block_name,
       
   420 		$serialized_attributes,
       
   421 		$block_content,
       
   422 		$serialized_block_name
       
   423 	);
       
   424 }
       
   425 
       
   426 /**
       
   427  * Returns the content of a block, including comment delimiters, serializing all
       
   428  * attributes from the given parsed block.
       
   429  *
       
   430  * This should be used when preparing a block to be saved to post content.
       
   431  * Prefer `render_block` when preparing a block for display. Unlike
       
   432  * `render_block`, this does not evaluate a block's `render_callback`, and will
       
   433  * instead preserve the markup as parsed.
       
   434  *
       
   435  * @since 5.3.1
       
   436  *
       
   437  * @param WP_Block_Parser_Block $block A single parsed block object.
       
   438  * @return string String of rendered HTML.
       
   439  */
       
   440 function serialize_block( $block ) {
       
   441 	$block_content = '';
       
   442 
       
   443 	$index = 0;
       
   444 	foreach ( $block['innerContent'] as $chunk ) {
       
   445 		$block_content .= is_string( $chunk ) ? $chunk : serialize_block( $block['innerBlocks'][ $index++ ] );
       
   446 	}
       
   447 
       
   448 	if ( ! is_array( $block['attrs'] ) ) {
       
   449 		$block['attrs'] = array();
       
   450 	}
       
   451 
       
   452 	return get_comment_delimited_block_content(
       
   453 		$block['blockName'],
       
   454 		$block['attrs'],
       
   455 		$block_content
       
   456 	);
       
   457 }
       
   458 
       
   459 /**
       
   460  * Returns a joined string of the aggregate serialization of the given parsed
       
   461  * blocks.
       
   462  *
       
   463  * @since 5.3.1
       
   464  *
       
   465  * @param WP_Block_Parser_Block[] $blocks Parsed block objects.
       
   466  * @return string String of rendered HTML.
       
   467  */
       
   468 function serialize_blocks( $blocks ) {
       
   469 	return implode( '', array_map( 'serialize_block', $blocks ) );
       
   470 }
       
   471 
       
   472 /**
       
   473  * Filters and sanitizes block content to remove non-allowable HTML from
       
   474  * parsed block attribute values.
       
   475  *
       
   476  * @since 5.3.1
       
   477  *
       
   478  * @param string         $text              Text that may contain block content.
       
   479  * @param array[]|string $allowed_html      An array of allowed HTML elements
       
   480  *                                          and attributes, or a context name
       
   481  *                                          such as 'post'.
       
   482  * @param string[]       $allowed_protocols Array of allowed URL protocols.
       
   483  * @return string The filtered and sanitized content result.
       
   484  */
       
   485 function filter_block_content( $text, $allowed_html = 'post', $allowed_protocols = array() ) {
       
   486 	$result = '';
       
   487 
       
   488 	$blocks = parse_blocks( $text );
       
   489 	foreach ( $blocks as $block ) {
       
   490 		$block   = filter_block_kses( $block, $allowed_html, $allowed_protocols );
       
   491 		$result .= serialize_block( $block );
       
   492 	}
       
   493 
       
   494 	return $result;
       
   495 }
       
   496 
       
   497 /**
       
   498  * Filters and sanitizes a parsed block to remove non-allowable HTML from block
       
   499  * attribute values.
       
   500  *
       
   501  * @since 5.3.1
       
   502  *
       
   503  * @param WP_Block_Parser_Block $block             The parsed block object.
       
   504  * @param array[]|string        $allowed_html      An array of allowed HTML
       
   505  *                                                 elements and attributes, or a
       
   506  *                                                 context name such as 'post'.
       
   507  * @param string[]              $allowed_protocols Allowed URL protocols.
       
   508  * @return array The filtered and sanitized block object result.
       
   509  */
       
   510 function filter_block_kses( $block, $allowed_html, $allowed_protocols = array() ) {
       
   511 	$block['attrs'] = filter_block_kses_value( $block['attrs'], $allowed_html, $allowed_protocols );
       
   512 
       
   513 	if ( is_array( $block['innerBlocks'] ) ) {
       
   514 		foreach ( $block['innerBlocks'] as $i => $inner_block ) {
       
   515 			$block['innerBlocks'][ $i ] = filter_block_kses( $inner_block, $allowed_html, $allowed_protocols );
       
   516 		}
       
   517 	}
       
   518 
       
   519 	return $block;
       
   520 }
       
   521 
       
   522 /**
       
   523  * Filters and sanitizes a parsed block attribute value to remove non-allowable
       
   524  * HTML.
       
   525  *
       
   526  * @since 5.3.1
       
   527  *
       
   528  * @param string[]|string $value             The attribute value to filter.
       
   529  * @param array[]|string  $allowed_html      An array of allowed HTML elements
       
   530  *                                           and attributes, or a context name
       
   531  *                                           such as 'post'.
       
   532  * @param string[]        $allowed_protocols Array of allowed URL protocols.
       
   533  * @return string[]|string The filtered and sanitized result.
       
   534  */
       
   535 function filter_block_kses_value( $value, $allowed_html, $allowed_protocols = array() ) {
       
   536 	if ( is_array( $value ) ) {
       
   537 		foreach ( $value as $key => $inner_value ) {
       
   538 			$filtered_key   = filter_block_kses_value( $key, $allowed_html, $allowed_protocols );
       
   539 			$filtered_value = filter_block_kses_value( $inner_value, $allowed_html, $allowed_protocols );
       
   540 
       
   541 			if ( $filtered_key !== $key ) {
       
   542 				unset( $value[ $key ] );
       
   543 			}
       
   544 
       
   545 			$value[ $filtered_key ] = $filtered_value;
       
   546 		}
       
   547 	} elseif ( is_string( $value ) ) {
       
   548 		return wp_kses( $value, $allowed_html, $allowed_protocols );
       
   549 	}
       
   550 
       
   551 	return $value;
   114 }
   552 }
   115 
   553 
   116 /**
   554 /**
   117  * Parses blocks out of a content string, and renders those appropriate for the excerpt.
   555  * Parses blocks out of a content string, and renders those appropriate for the excerpt.
   118  *
   556  *
   210 /**
   648 /**
   211  * Renders a single block into a HTML string.
   649  * Renders a single block into a HTML string.
   212  *
   650  *
   213  * @since 5.0.0
   651  * @since 5.0.0
   214  *
   652  *
   215  * @global WP_Post $post The post to edit.
   653  * @global WP_Post  $post     The post to edit.
   216  *
   654  * @global WP_Query $wp_query WordPress Query object.
   217  * @param array $block A single parsed block object.
   655  *
       
   656  * @param array $parsed_block A single parsed block object.
   218  * @return string String of rendered HTML.
   657  * @return string String of rendered HTML.
   219  */
   658  */
   220 function render_block( $block ) {
   659 function render_block( $parsed_block ) {
   221 	global $post;
   660 	global $post, $wp_query;
   222 
   661 
   223 	/**
   662 	/**
   224 	 * Allows render_block() to be shortcircuited, by returning a non-null value.
   663 	 * Allows render_block() to be short-circuited, by returning a non-null value.
   225 	 *
   664 	 *
   226 	 * @since 5.1.0
   665 	 * @since 5.1.0
   227 	 *
   666 	 *
   228 	 * @param string $pre_render The pre-rendered content. Default null.
   667 	 * @param string|null $pre_render   The pre-rendered content. Default null.
   229 	 * @param array  $block      The block being rendered.
   668 	 * @param array       $parsed_block The block being rendered.
   230 	 */
   669 	 */
   231 	$pre_render = apply_filters( 'pre_render_block', null, $block );
   670 	$pre_render = apply_filters( 'pre_render_block', null, $parsed_block );
   232 	if ( ! is_null( $pre_render ) ) {
   671 	if ( ! is_null( $pre_render ) ) {
   233 		return $pre_render;
   672 		return $pre_render;
   234 	}
   673 	}
   235 
   674 
   236 	$source_block = $block;
   675 	$source_block = $parsed_block;
   237 
   676 
   238 	/**
   677 	/**
   239 	 * Filters the block being rendered in render_block(), before it's processed.
   678 	 * Filters the block being rendered in render_block(), before it's processed.
   240 	 *
   679 	 *
   241 	 * @since 5.1.0
   680 	 * @since 5.1.0
   242 	 *
   681 	 *
   243 	 * @param array $block        The block being rendered.
   682 	 * @param array $parsed_block The block being rendered.
   244 	 * @param array $source_block An un-modified copy of $block, as it appeared in the source content.
   683 	 * @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content.
   245 	 */
   684 	 */
   246 	$block = apply_filters( 'render_block_data', $block, $source_block );
   685 	$parsed_block = apply_filters( 'render_block_data', $parsed_block, $source_block );
   247 
   686 
   248 	$block_type    = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
   687 	$context = array();
   249 	$is_dynamic    = $block['blockName'] && null !== $block_type && $block_type->is_dynamic();
   688 
   250 	$block_content = '';
   689 	if ( $post instanceof WP_Post ) {
   251 	$index         = 0;
   690 		$context['postId'] = $post->ID;
   252 
   691 
   253 	foreach ( $block['innerContent'] as $chunk ) {
   692 		/*
   254 		$block_content .= is_string( $chunk ) ? $chunk : render_block( $block['innerBlocks'][ $index++ ] );
   693 		 * The `postType` context is largely unnecessary server-side, since the ID
   255 	}
   694 		 * is usually sufficient on its own. That being said, since a block's
   256 
   695 		 * manifest is expected to be shared between the server and the client,
   257 	if ( ! is_array( $block['attrs'] ) ) {
   696 		 * it should be included to consistently fulfill the expectation.
   258 		$block['attrs'] = array();
   697 		 */
   259 	}
   698 		$context['postType'] = $post->post_type;
   260 
   699 	}
   261 	if ( $is_dynamic ) {
   700 
   262 		$global_post   = $post;
   701 	if ( $wp_query instanceof WP_Query && isset( $wp_query->tax_query->queried_terms['category'] ) ) {
   263 		$block_content = $block_type->render( $block['attrs'], $block_content );
   702 		$context['query'] = array( 'categoryIds' => array() );
   264 		$post          = $global_post;
   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 		}
   265 	}
   706 	}
   266 
   707 
   267 	/**
   708 	/**
   268 	 * Filters the content of a single block.
   709 	 * Filters the default context provided to a rendered block.
   269 	 *
   710 	 *
   270 	 * @since 5.0.0
   711 	 * @since 5.5.0
   271 	 *
   712 	 *
   272 	 * @param string $block_content The block content about to be appended.
   713 	 * @param array $context      Default context.
   273 	 * @param array  $block         The full block, including name and attributes.
   714 	 * @param array $parsed_block Block being rendered, filtered by `render_block_data`.
   274 	 */
   715 	 */
   275 	return apply_filters( 'render_block', $block_content, $block );
   716 	$context = apply_filters( 'render_block_context', $context, $parsed_block );
       
   717 
       
   718 	$block = new WP_Block( $parsed_block, $context );
       
   719 
       
   720 	return $block->render();
   276 }
   721 }
   277 
   722 
   278 /**
   723 /**
   279  * Parses blocks out of a content string.
   724  * Parses blocks out of a content string.
   280  *
   725  *
   281  * @since 5.0.0
   726  * @since 5.0.0
   282  *
   727  *
   283  * @param string $content Post content.
   728  * @param string $content Post content.
   284  * @return array Array of parsed block objects.
   729  * @return array[] Array of parsed block objects.
   285  */
   730  */
   286 function parse_blocks( $content ) {
   731 function parse_blocks( $content ) {
   287 	/**
   732 	/**
   288 	 * Filter to allow plugins to replace the server-side block parser
   733 	 * Filter to allow plugins to replace the server-side block parser
   289 	 *
   734 	 *
   299 
   744 
   300 /**
   745 /**
   301  * Parses dynamic blocks out of `post_content` and re-renders them.
   746  * Parses dynamic blocks out of `post_content` and re-renders them.
   302  *
   747  *
   303  * @since 5.0.0
   748  * @since 5.0.0
   304  * @global WP_Post $post The post to edit.
   749  *
   305  *
   750  * @param string $content Post content.
   306  * @param  string $content Post content.
       
   307  * @return string Updated post content.
   751  * @return string Updated post content.
   308  */
   752  */
   309 function do_blocks( $content ) {
   753 function do_blocks( $content ) {
   310 	$blocks = parse_blocks( $content );
   754 	$blocks = parse_blocks( $content );
   311 	$output = '';
   755 	$output = '';
   355  * @return int The block format version is 1 if the content contains one or more blocks, 0 otherwise.
   799  * @return int The block format version is 1 if the content contains one or more blocks, 0 otherwise.
   356  */
   800  */
   357 function block_version( $content ) {
   801 function block_version( $content ) {
   358 	return has_blocks( $content ) ? 1 : 0;
   802 	return has_blocks( $content ) ? 1 : 0;
   359 }
   803 }
       
   804 
       
   805 /**
       
   806  * Registers a new block style.
       
   807  *
       
   808  * @since 5.3.0
       
   809  *
       
   810  * @param string $block_name       Block type name including namespace.
       
   811  * @param array  $style_properties Array containing the properties of the style name,
       
   812  *                                 label, style (name of the stylesheet to be enqueued),
       
   813  *                                 inline_style (string containing the CSS to be added).
       
   814  * @return boolean True if the block style was registered with success and false otherwise.
       
   815  */
       
   816 function register_block_style( $block_name, $style_properties ) {
       
   817 	return WP_Block_Styles_Registry::get_instance()->register( $block_name, $style_properties );
       
   818 }
       
   819 
       
   820 /**
       
   821  * Unregisters a block style.
       
   822  *
       
   823  * @since 5.3.0
       
   824  *
       
   825  * @param string $block_name       Block type name including namespace.
       
   826  * @param array  $block_style_name Block style name.
       
   827  * @return boolean True if the block style was unregistered with success and false otherwise.
       
   828  */
       
   829 function unregister_block_style( $block_name, $block_style_name ) {
       
   830 	return WP_Block_Styles_Registry::get_instance()->unregister( $block_name, $block_style_name );
       
   831 }