changeset 22 | 8c2e4d02f4ef |
parent 21 | 48c4eec2b7e6 |
21:48c4eec2b7e6 | 22:8c2e4d02f4ef |
---|---|
326 } |
326 } |
327 |
327 |
328 $style_path_norm = wp_normalize_path( realpath( dirname( $metadata['file'] ) . '/' . $style_path ) ); |
328 $style_path_norm = wp_normalize_path( realpath( dirname( $metadata['file'] ) . '/' . $style_path ) ); |
329 $style_uri = get_block_asset_url( $style_path_norm ); |
329 $style_uri = get_block_asset_url( $style_path_norm ); |
330 |
330 |
331 $version = ! $is_core_block && isset( $metadata['version'] ) ? $metadata['version'] : false; |
331 $block_version = ! $is_core_block && isset( $metadata['version'] ) ? $metadata['version'] : false; |
332 $result = wp_register_style( |
332 $version = $style_path_norm && defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? filemtime( $style_path_norm ) : $block_version; |
333 $result = wp_register_style( |
|
333 $style_handle_name, |
334 $style_handle_name, |
334 $style_uri, |
335 $style_uri, |
335 array(), |
336 array(), |
336 $version |
337 $version |
337 ); |
338 ); |
374 |
375 |
375 return $i18n_block_schema; |
376 return $i18n_block_schema; |
376 } |
377 } |
377 |
378 |
378 /** |
379 /** |
380 * Registers all block types from a block metadata collection. |
|
381 * |
|
382 * This can either reference a previously registered metadata collection or, if the `$manifest` parameter is provided, |
|
383 * register the metadata collection directly within the same function call. |
|
384 * |
|
385 * @since 6.8.0 |
|
386 * @see wp_register_block_metadata_collection() |
|
387 * @see register_block_type_from_metadata() |
|
388 * |
|
389 * @param string $path The absolute base path for the collection ( e.g., WP_PLUGIN_DIR . '/my-plugin/blocks/' ). |
|
390 * @param string $manifest Optional. The absolute path to the manifest file containing the metadata collection, in |
|
391 * order to register the collection. If this parameter is not provided, the `$path` parameter |
|
392 * must reference a previously registered block metadata collection. |
|
393 */ |
|
394 function wp_register_block_types_from_metadata_collection( $path, $manifest = '' ) { |
|
395 if ( $manifest ) { |
|
396 wp_register_block_metadata_collection( $path, $manifest ); |
|
397 } |
|
398 |
|
399 $block_metadata_files = WP_Block_Metadata_Registry::get_collection_block_metadata_files( $path ); |
|
400 foreach ( $block_metadata_files as $block_metadata_file ) { |
|
401 register_block_type_from_metadata( $block_metadata_file ); |
|
402 } |
|
403 } |
|
404 |
|
405 /** |
|
406 * Registers a block metadata collection. |
|
407 * |
|
408 * This function allows core and third-party plugins to register their block metadata |
|
409 * collections in a centralized location. Registering collections can improve performance |
|
410 * by avoiding multiple reads from the filesystem and parsing JSON. |
|
411 * |
|
412 * @since 6.7.0 |
|
413 * |
|
414 * @param string $path The base path in which block files for the collection reside. |
|
415 * @param string $manifest The path to the manifest file for the collection. |
|
416 */ |
|
417 function wp_register_block_metadata_collection( $path, $manifest ) { |
|
418 WP_Block_Metadata_Registry::register_collection( $path, $manifest ); |
|
419 } |
|
420 |
|
421 /** |
|
379 * Registers a block type from the metadata stored in the `block.json` file. |
422 * Registers a block type from the metadata stored in the `block.json` file. |
380 * |
423 * |
381 * @since 5.5.0 |
424 * @since 5.5.0 |
382 * @since 5.7.0 Added support for `textdomain` field and i18n handling for all translatable fields. |
425 * @since 5.7.0 Added support for `textdomain` field and i18n handling for all translatable fields. |
383 * @since 5.9.0 Added support for `variations` and `viewScript` fields. |
426 * @since 5.9.0 Added support for `variations` and `viewScript` fields. |
384 * @since 6.1.0 Added support for `render` field. |
427 * @since 6.1.0 Added support for `render` field. |
385 * @since 6.3.0 Added `selectors` field. |
428 * @since 6.3.0 Added `selectors` field. |
386 * @since 6.4.0 Added support for `blockHooks` field. |
429 * @since 6.4.0 Added support for `blockHooks` field. |
387 * @since 6.5.0 Added support for `allowedBlocks`, `viewScriptModule`, and `viewStyle` fields. |
430 * @since 6.5.0 Added support for `allowedBlocks`, `viewScriptModule`, and `viewStyle` fields. |
431 * @since 6.7.0 Allow PHP filename as `variations` argument. |
|
388 * |
432 * |
389 * @param string $file_or_folder Path to the JSON file with metadata definition for |
433 * @param string $file_or_folder Path to the JSON file with metadata definition for |
390 * the block or path to the folder where the `block.json` file is located. |
434 * the block or path to the folder where the `block.json` file is located. |
391 * If providing the path to a JSON file, the filename must end with `block.json`. |
435 * If providing the path to a JSON file, the filename must end with `block.json`. |
392 * @param array $args Optional. Array of block type arguments. Accepts any public property |
436 * @param array $args Optional. Array of block type arguments. Accepts any public property |
399 * Get an array of metadata from a PHP file. |
443 * Get an array of metadata from a PHP file. |
400 * This improves performance for core blocks as it's only necessary to read a single PHP file |
444 * This improves performance for core blocks as it's only necessary to read a single PHP file |
401 * instead of reading a JSON file per-block, and then decoding from JSON to PHP. |
445 * instead of reading a JSON file per-block, and then decoding from JSON to PHP. |
402 * Using a static variable ensures that the metadata is only read once per request. |
446 * Using a static variable ensures that the metadata is only read once per request. |
403 */ |
447 */ |
404 static $core_blocks_meta; |
448 |
405 if ( ! $core_blocks_meta ) { |
449 $file_or_folder = wp_normalize_path( $file_or_folder ); |
406 $core_blocks_meta = require ABSPATH . WPINC . '/blocks/blocks-json.php'; |
|
407 } |
|
408 |
450 |
409 $metadata_file = ( ! str_ends_with( $file_or_folder, 'block.json' ) ) ? |
451 $metadata_file = ( ! str_ends_with( $file_or_folder, 'block.json' ) ) ? |
410 trailingslashit( $file_or_folder ) . 'block.json' : |
452 trailingslashit( $file_or_folder ) . 'block.json' : |
411 $file_or_folder; |
453 $file_or_folder; |
412 |
454 |
413 $is_core_block = str_starts_with( $file_or_folder, ABSPATH . WPINC ); |
455 $is_core_block = str_starts_with( $file_or_folder, wp_normalize_path( ABSPATH . WPINC ) ); |
414 // If the block is not a core block, the metadata file must exist. |
|
415 $metadata_file_exists = $is_core_block || file_exists( $metadata_file ); |
456 $metadata_file_exists = $is_core_block || file_exists( $metadata_file ); |
416 if ( ! $metadata_file_exists && empty( $args['name'] ) ) { |
457 $registry_metadata = WP_Block_Metadata_Registry::get_metadata( $file_or_folder ); |
417 return false; |
458 |
418 } |
459 if ( $registry_metadata ) { |
419 |
460 $metadata = $registry_metadata; |
420 // Try to get metadata from the static cache for core blocks. |
461 } elseif ( $metadata_file_exists ) { |
421 $metadata = array(); |
|
422 if ( $is_core_block ) { |
|
423 $core_block_name = str_replace( ABSPATH . WPINC . '/blocks/', '', $file_or_folder ); |
|
424 if ( ! empty( $core_blocks_meta[ $core_block_name ] ) ) { |
|
425 $metadata = $core_blocks_meta[ $core_block_name ]; |
|
426 } |
|
427 } |
|
428 |
|
429 // If metadata is not found in the static cache, read it from the file. |
|
430 if ( $metadata_file_exists && empty( $metadata ) ) { |
|
431 $metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) ); |
462 $metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) ); |
463 } else { |
|
464 $metadata = array(); |
|
432 } |
465 } |
433 |
466 |
434 if ( ! is_array( $metadata ) || ( empty( $metadata['name'] ) && empty( $args['name'] ) ) ) { |
467 if ( ! is_array( $metadata ) || ( empty( $metadata['name'] ) && empty( $args['name'] ) ) ) { |
435 return false; |
468 return false; |
436 } |
469 } |
520 return ob_get_clean(); |
553 return ob_get_clean(); |
521 }; |
554 }; |
522 } |
555 } |
523 } |
556 } |
524 |
557 |
558 // If `variations` is a string, it's the name of a PHP file that |
|
559 // generates the variations. |
|
560 if ( ! empty( $metadata['variations'] ) && is_string( $metadata['variations'] ) ) { |
|
561 $variations_path = wp_normalize_path( |
|
562 realpath( |
|
563 dirname( $metadata['file'] ) . '/' . |
|
564 remove_block_asset_path_prefix( $metadata['variations'] ) |
|
565 ) |
|
566 ); |
|
567 if ( $variations_path ) { |
|
568 /** |
|
569 * Generates the list of block variations. |
|
570 * |
|
571 * @since 6.7.0 |
|
572 * |
|
573 * @return string Returns the list of block variations. |
|
574 */ |
|
575 $settings['variation_callback'] = static function () use ( $variations_path ) { |
|
576 $variations = require $variations_path; |
|
577 return $variations; |
|
578 }; |
|
579 // The block instance's `variations` field is only allowed to be an array |
|
580 // (of known block variations). We unset it so that the block instance will |
|
581 // provide a getter that returns the result of the `variation_callback` instead. |
|
582 unset( $settings['variations'] ); |
|
583 } |
|
584 } |
|
585 |
|
525 $settings = array_merge( $settings, $args ); |
586 $settings = array_merge( $settings, $args ); |
526 |
587 |
527 $script_fields = array( |
588 $script_fields = array( |
528 'editorScript' => 'editor_script_handles', |
589 'editorScript' => 'editor_script_handles', |
529 'script' => 'script_handles', |
590 'script' => 'script_handles', |
878 * |
939 * |
879 * @param string[] $hooked_block_types The list of hooked block types. |
940 * @param string[] $hooked_block_types The list of hooked block types. |
880 * @param string $relative_position The relative position of the hooked blocks. |
941 * @param string $relative_position The relative position of the hooked blocks. |
881 * Can be one of 'before', 'after', 'first_child', or 'last_child'. |
942 * Can be one of 'before', 'after', 'first_child', or 'last_child'. |
882 * @param string $anchor_block_type The anchor block type. |
943 * @param string $anchor_block_type The anchor block type. |
883 * @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type, |
944 * @param WP_Block_Template|WP_Post|array $context The block template, template part, post object, |
884 * or pattern that the anchor block belongs to. |
945 * or pattern that the anchor block belongs to. |
885 */ |
946 */ |
886 $hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context ); |
947 $hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context ); |
887 |
948 |
888 $markup = ''; |
949 $markup = ''; |
901 * |
962 * |
902 * @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block. |
963 * @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block. |
903 * @param string $hooked_block_type The hooked block type name. |
964 * @param string $hooked_block_type The hooked block type name. |
904 * @param string $relative_position The relative position of the hooked block. |
965 * @param string $relative_position The relative position of the hooked block. |
905 * @param array $parsed_anchor_block The anchor block, in parsed block array format. |
966 * @param array $parsed_anchor_block The anchor block, in parsed block array format. |
906 * @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type, |
967 * @param WP_Block_Template|WP_Post|array $context The block template, template part, post object, |
907 * or pattern that the anchor block belongs to. |
968 * or pattern that the anchor block belongs to. |
908 */ |
969 */ |
909 $parsed_hooked_block = apply_filters( 'hooked_block', $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ); |
970 $parsed_hooked_block = apply_filters( 'hooked_block', $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ); |
910 |
971 |
911 /** |
972 /** |
917 * |
978 * |
918 * @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block. |
979 * @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block. |
919 * @param string $hooked_block_type The hooked block type name. |
980 * @param string $hooked_block_type The hooked block type name. |
920 * @param string $relative_position The relative position of the hooked block. |
981 * @param string $relative_position The relative position of the hooked block. |
921 * @param array $parsed_anchor_block The anchor block, in parsed block array format. |
982 * @param array $parsed_anchor_block The anchor block, in parsed block array format. |
922 * @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type, |
983 * @param WP_Block_Template|WP_Post|array $context The block template, template part, post object, |
923 * or pattern that the anchor block belongs to. |
984 * or pattern that the anchor block belongs to. |
924 */ |
985 */ |
925 $parsed_hooked_block = apply_filters( "hooked_block_{$hooked_block_type}", $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ); |
986 $parsed_hooked_block = apply_filters( "hooked_block_{$hooked_block_type}", $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ); |
926 |
987 |
927 if ( null === $parsed_hooked_block ) { |
988 if ( null === $parsed_hooked_block ) { |
1004 |
1065 |
1005 /** |
1066 /** |
1006 * Runs the hooked blocks algorithm on the given content. |
1067 * Runs the hooked blocks algorithm on the given content. |
1007 * |
1068 * |
1008 * @since 6.6.0 |
1069 * @since 6.6.0 |
1070 * @since 6.7.0 Injects the `theme` attribute into Template Part blocks, even if no hooked blocks are registered. |
|
1071 * @since 6.8.0 Have the `$context` parameter default to `null`, in which case `get_post()` will be called to use the current post as context. |
|
1009 * @access private |
1072 * @access private |
1010 * |
1073 * |
1011 * @param string $content Serialized content. |
1074 * @param string $content Serialized content. |
1012 * @param WP_Block_Template|WP_Post|array $context A block template, template part, `wp_navigation` post object, |
1075 * @param WP_Block_Template|WP_Post|array|null $context A block template, template part, post object, or pattern |
1013 * or pattern that the blocks belong to. |
1076 * that the blocks belong to. If set to `null`, `get_post()` |
1014 * @param callable $callback A function that will be called for each block to generate |
1077 * will be called to use the current post as context. |
1015 * the markup for a given list of blocks that are hooked to it. |
1078 * Default: `null`. |
1016 * Default: 'insert_hooked_blocks'. |
1079 * @param callable $callback A function that will be called for each block to generate |
1080 * the markup for a given list of blocks that are hooked to it. |
|
1081 * Default: 'insert_hooked_blocks'. |
|
1017 * @return string The serialized markup. |
1082 * @return string The serialized markup. |
1018 */ |
1083 */ |
1019 function apply_block_hooks_to_content( $content, $context, $callback = 'insert_hooked_blocks' ) { |
1084 function apply_block_hooks_to_content( $content, $context = null, $callback = 'insert_hooked_blocks' ) { |
1085 // Default to the current post if no context is provided. |
|
1086 if ( null === $context ) { |
|
1087 $context = get_post(); |
|
1088 } |
|
1089 |
|
1020 $hooked_blocks = get_hooked_blocks(); |
1090 $hooked_blocks = get_hooked_blocks(); |
1021 if ( empty( $hooked_blocks ) && ! has_filter( 'hooked_block_types' ) ) { |
1091 |
1022 return $content; |
1092 $before_block_visitor = '_inject_theme_attribute_in_template_part_block'; |
1023 } |
1093 $after_block_visitor = null; |
1024 |
1094 if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) { |
1025 $blocks = parse_blocks( $content ); |
1095 $before_block_visitor = make_before_block_visitor( $hooked_blocks, $context, $callback ); |
1026 |
1096 $after_block_visitor = make_after_block_visitor( $hooked_blocks, $context, $callback ); |
1027 $before_block_visitor = make_before_block_visitor( $hooked_blocks, $context, $callback ); |
1097 } |
1028 $after_block_visitor = make_after_block_visitor( $hooked_blocks, $context, $callback ); |
1098 |
1029 |
1099 $block_allows_multiple_instances = array(); |
1030 return traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); |
1100 /* |
1031 } |
1101 * Remove hooked blocks from `$hooked_block_types` if they have `multiple` set to false and |
1032 |
1102 * are already present in `$content`. |
1033 /** |
1103 */ |
1034 * Accepts the serialized markup of a block and its inner blocks, and returns serialized markup of the inner blocks. |
1104 foreach ( $hooked_blocks as $anchor_block_type => $relative_positions ) { |
1035 * |
1105 foreach ( $relative_positions as $relative_position => $hooked_block_types ) { |
1036 * @since 6.6.0 |
1106 foreach ( $hooked_block_types as $index => $hooked_block_type ) { |
1107 $hooked_block_type_definition = |
|
1108 WP_Block_Type_Registry::get_instance()->get_registered( $hooked_block_type ); |
|
1109 |
|
1110 $block_allows_multiple_instances[ $hooked_block_type ] = |
|
1111 block_has_support( $hooked_block_type_definition, 'multiple', true ); |
|
1112 |
|
1113 if ( |
|
1114 ! $block_allows_multiple_instances[ $hooked_block_type ] && |
|
1115 has_block( $hooked_block_type, $content ) |
|
1116 ) { |
|
1117 unset( $hooked_blocks[ $anchor_block_type ][ $relative_position ][ $index ] ); |
|
1118 } |
|
1119 } |
|
1120 if ( empty( $hooked_blocks[ $anchor_block_type ][ $relative_position ] ) ) { |
|
1121 unset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] ); |
|
1122 } |
|
1123 } |
|
1124 if ( empty( $hooked_blocks[ $anchor_block_type ] ) ) { |
|
1125 unset( $hooked_blocks[ $anchor_block_type ] ); |
|
1126 } |
|
1127 } |
|
1128 |
|
1129 /* |
|
1130 * We also need to cover the case where the hooked block is not present in |
|
1131 * `$content` at first and we're allowed to insert it once -- but not again. |
|
1132 */ |
|
1133 $suppress_single_instance_blocks = static function ( $hooked_block_types ) use ( &$block_allows_multiple_instances, $content ) { |
|
1134 static $single_instance_blocks_present_in_content = array(); |
|
1135 foreach ( $hooked_block_types as $index => $hooked_block_type ) { |
|
1136 if ( ! isset( $block_allows_multiple_instances[ $hooked_block_type ] ) ) { |
|
1137 $hooked_block_type_definition = |
|
1138 WP_Block_Type_Registry::get_instance()->get_registered( $hooked_block_type ); |
|
1139 |
|
1140 $block_allows_multiple_instances[ $hooked_block_type ] = |
|
1141 block_has_support( $hooked_block_type_definition, 'multiple', true ); |
|
1142 } |
|
1143 |
|
1144 if ( $block_allows_multiple_instances[ $hooked_block_type ] ) { |
|
1145 continue; |
|
1146 } |
|
1147 |
|
1148 // The block doesn't allow multiple instances, so we need to check if it's already present. |
|
1149 if ( |
|
1150 in_array( $hooked_block_type, $single_instance_blocks_present_in_content, true ) || |
|
1151 has_block( $hooked_block_type, $content ) |
|
1152 ) { |
|
1153 unset( $hooked_block_types[ $index ] ); |
|
1154 } else { |
|
1155 // We can insert the block once, but need to remember not to insert it again. |
|
1156 $single_instance_blocks_present_in_content[] = $hooked_block_type; |
|
1157 } |
|
1158 } |
|
1159 return $hooked_block_types; |
|
1160 }; |
|
1161 add_filter( 'hooked_block_types', $suppress_single_instance_blocks, PHP_INT_MAX ); |
|
1162 $content = traverse_and_serialize_blocks( |
|
1163 parse_blocks( $content ), |
|
1164 $before_block_visitor, |
|
1165 $after_block_visitor |
|
1166 ); |
|
1167 remove_filter( 'hooked_block_types', $suppress_single_instance_blocks, PHP_INT_MAX ); |
|
1168 |
|
1169 return $content; |
|
1170 } |
|
1171 |
|
1172 /** |
|
1173 * Run the Block Hooks algorithm on a post object's content. |
|
1174 * |
|
1175 * This function is different from `apply_block_hooks_to_content` in that |
|
1176 * it takes ignored hooked block information from the post's metadata into |
|
1177 * account. This ensures that any blocks hooked as first or last child |
|
1178 * of the block that corresponds to the post type are handled correctly. |
|
1179 * |
|
1180 * @since 6.8.0 |
|
1037 * @access private |
1181 * @access private |
1038 * |
1182 * |
1039 * @param string $serialized_block The serialized markup of a block and its inner blocks. |
1183 * @param string $content Serialized content. |
1040 * @return string The serialized markup of the inner blocks. |
1184 * @param WP_Post|null $post A post object that the content belongs to. If set to `null`, |
1041 */ |
1185 * `get_post()` will be called to use the current post as context. |
1042 function remove_serialized_parent_block( $serialized_block ) { |
1186 * Default: `null`. |
1043 $start = strpos( $serialized_block, '-->' ) + strlen( '-->' ); |
1187 * @param callable $callback A function that will be called for each block to generate |
1044 $end = strrpos( $serialized_block, '<!--' ); |
1188 * the markup for a given list of blocks that are hooked to it. |
1045 return substr( $serialized_block, $start, $end - $start ); |
1189 * Default: 'insert_hooked_blocks'. |
1046 } |
1190 * @return string The serialized markup. |
1047 |
1191 */ |
1048 /** |
1192 function apply_block_hooks_to_content_from_post_object( $content, $post = null, $callback = 'insert_hooked_blocks' ) { |
1049 * Updates the wp_postmeta with the list of ignored hooked blocks where the inner blocks are stored as post content. |
1193 // Default to the current post if no context is provided. |
1050 * Currently only supports `wp_navigation` post types. |
1194 if ( null === $post ) { |
1051 * |
1195 $post = get_post(); |
1052 * @since 6.6.0 |
1196 } |
1053 * @access private |
1197 |
1054 * |
1198 if ( ! $post instanceof WP_Post ) { |
1055 * @param stdClass $post Post object. |
1199 return apply_block_hooks_to_content( $content, $post, $callback ); |
1056 * @return stdClass The updated post object. |
1200 } |
1057 */ |
1201 |
1058 function update_ignored_hooked_blocks_postmeta( $post ) { |
|
1059 /* |
1202 /* |
1060 * In this scenario the user has likely tried to create a navigation via the REST API. |
1203 * If the content was created using the classic editor or using a single Classic block |
1061 * In which case we won't have a post ID to work with and store meta against. |
1204 * (`core/freeform`), it might not contain any block markup at all. |
1205 * However, we still might need to inject hooked blocks in the first child or last child |
|
1206 * positions of the parent block. To be able to apply the Block Hooks algorithm, we wrap |
|
1207 * the content in a `core/freeform` wrapper block. |
|
1062 */ |
1208 */ |
1063 if ( empty( $post->ID ) ) { |
1209 if ( ! has_blocks( $content ) ) { |
1064 return $post; |
1210 $original_content = $content; |
1065 } |
1211 |
1066 |
1212 $content_wrapped_in_classic_block = get_comment_delimited_block_content( |
1067 /* |
1213 'core/freeform', |
1068 * Skip meta generation when consumers intentionally update specific Navigation fields |
1214 array(), |
1069 * and omit the content update. |
1215 $content |
1070 */ |
1216 ); |
1071 if ( ! isset( $post->post_content ) ) { |
1217 |
1072 return $post; |
1218 $content = $content_wrapped_in_classic_block; |
1073 } |
|
1074 |
|
1075 /* |
|
1076 * Skip meta generation when the post content is not a navigation block. |
|
1077 */ |
|
1078 if ( ! isset( $post->post_type ) || 'wp_navigation' !== $post->post_type ) { |
|
1079 return $post; |
|
1080 } |
1219 } |
1081 |
1220 |
1082 $attributes = array(); |
1221 $attributes = array(); |
1083 |
1222 |
1223 // If context is a post object, `ignoredHookedBlocks` information is stored in its post meta. |
|
1084 $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); |
1224 $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); |
1085 if ( ! empty( $ignored_hooked_blocks ) ) { |
1225 if ( ! empty( $ignored_hooked_blocks ) ) { |
1086 $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); |
1226 $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); |
1087 $attributes['metadata'] = array( |
1227 $attributes['metadata'] = array( |
1088 'ignoredHookedBlocks' => $ignored_hooked_blocks, |
1228 'ignoredHookedBlocks' => $ignored_hooked_blocks, |
1089 ); |
1229 ); |
1090 } |
1230 } |
1091 |
1231 |
1232 /* |
|
1233 * We need to wrap the content in a temporary wrapper block with that metadata |
|
1234 * so the Block Hooks algorithm can insert blocks that are hooked as first or last child |
|
1235 * of the wrapper block. |
|
1236 * To that end, we need to determine the wrapper block type based on the post type. |
|
1237 */ |
|
1238 if ( 'wp_navigation' === $post->post_type ) { |
|
1239 $wrapper_block_type = 'core/navigation'; |
|
1240 } elseif ( 'wp_block' === $post->post_type ) { |
|
1241 $wrapper_block_type = 'core/block'; |
|
1242 } else { |
|
1243 $wrapper_block_type = 'core/post-content'; |
|
1244 } |
|
1245 |
|
1246 $content = get_comment_delimited_block_content( |
|
1247 $wrapper_block_type, |
|
1248 $attributes, |
|
1249 $content |
|
1250 ); |
|
1251 |
|
1252 /* |
|
1253 * We need to avoid inserting any blocks hooked into the `before` and `after` positions |
|
1254 * of the temporary wrapper block that we create to wrap the content. |
|
1255 * See https://core.trac.wordpress.org/ticket/63287 for more details. |
|
1256 */ |
|
1257 $suppress_blocks_from_insertion_before_and_after_wrapper_block = static function ( $hooked_block_types, $relative_position, $anchor_block_type ) use ( $wrapper_block_type ) { |
|
1258 if ( |
|
1259 $wrapper_block_type === $anchor_block_type && |
|
1260 in_array( $relative_position, array( 'before', 'after' ), true ) |
|
1261 ) { |
|
1262 return array(); |
|
1263 } |
|
1264 return $hooked_block_types; |
|
1265 }; |
|
1266 |
|
1267 // Apply Block Hooks. |
|
1268 add_filter( 'hooked_block_types', $suppress_blocks_from_insertion_before_and_after_wrapper_block, PHP_INT_MAX, 3 ); |
|
1269 $content = apply_block_hooks_to_content( $content, $post, $callback ); |
|
1270 remove_filter( 'hooked_block_types', $suppress_blocks_from_insertion_before_and_after_wrapper_block, PHP_INT_MAX ); |
|
1271 |
|
1272 // Finally, we need to remove the temporary wrapper block. |
|
1273 $content = remove_serialized_parent_block( $content ); |
|
1274 |
|
1275 // If we wrapped the content in a `core/freeform` block, we also need to remove that. |
|
1276 if ( ! empty( $content_wrapped_in_classic_block ) ) { |
|
1277 /* |
|
1278 * We cannot simply use remove_serialized_parent_block() here, |
|
1279 * as that function assumes that the block wrapper is at the top level. |
|
1280 * However, there might now be a hooked block inserted next to it |
|
1281 * (as first or last child of the parent). |
|
1282 */ |
|
1283 $content = str_replace( $content_wrapped_in_classic_block, $original_content, $content ); |
|
1284 } |
|
1285 |
|
1286 return $content; |
|
1287 } |
|
1288 |
|
1289 /** |
|
1290 * Accepts the serialized markup of a block and its inner blocks, and returns serialized markup of the inner blocks. |
|
1291 * |
|
1292 * @since 6.6.0 |
|
1293 * @access private |
|
1294 * |
|
1295 * @param string $serialized_block The serialized markup of a block and its inner blocks. |
|
1296 * @return string The serialized markup of the inner blocks. |
|
1297 */ |
|
1298 function remove_serialized_parent_block( $serialized_block ) { |
|
1299 $start = strpos( $serialized_block, '-->' ) + strlen( '-->' ); |
|
1300 $end = strrpos( $serialized_block, '<!--' ); |
|
1301 return substr( $serialized_block, $start, $end - $start ); |
|
1302 } |
|
1303 |
|
1304 /** |
|
1305 * Accepts the serialized markup of a block and its inner blocks, and returns serialized markup of the wrapper block. |
|
1306 * |
|
1307 * @since 6.7.0 |
|
1308 * @access private |
|
1309 * |
|
1310 * @see remove_serialized_parent_block() |
|
1311 * |
|
1312 * @param string $serialized_block The serialized markup of a block and its inner blocks. |
|
1313 * @return string The serialized markup of the wrapper block. |
|
1314 */ |
|
1315 function extract_serialized_parent_block( $serialized_block ) { |
|
1316 $start = strpos( $serialized_block, '-->' ) + strlen( '-->' ); |
|
1317 $end = strrpos( $serialized_block, '<!--' ); |
|
1318 return substr( $serialized_block, 0, $start ) . substr( $serialized_block, $end ); |
|
1319 } |
|
1320 |
|
1321 /** |
|
1322 * Updates the wp_postmeta with the list of ignored hooked blocks |
|
1323 * where the inner blocks are stored as post content. |
|
1324 * |
|
1325 * @since 6.6.0 |
|
1326 * @since 6.8.0 Support non-`wp_navigation` post types. |
|
1327 * @access private |
|
1328 * |
|
1329 * @param stdClass $post Post object. |
|
1330 * @return stdClass The updated post object. |
|
1331 */ |
|
1332 function update_ignored_hooked_blocks_postmeta( $post ) { |
|
1333 /* |
|
1334 * In this scenario the user has likely tried to create a new post object via the REST API. |
|
1335 * In which case we won't have a post ID to work with and store meta against. |
|
1336 */ |
|
1337 if ( empty( $post->ID ) ) { |
|
1338 return $post; |
|
1339 } |
|
1340 |
|
1341 /* |
|
1342 * Skip meta generation when consumers intentionally update specific fields |
|
1343 * and omit the content update. |
|
1344 */ |
|
1345 if ( ! isset( $post->post_content ) ) { |
|
1346 return $post; |
|
1347 } |
|
1348 |
|
1349 /* |
|
1350 * Skip meta generation if post type is not set. |
|
1351 */ |
|
1352 if ( ! isset( $post->post_type ) ) { |
|
1353 return $post; |
|
1354 } |
|
1355 |
|
1356 $attributes = array(); |
|
1357 |
|
1358 $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); |
|
1359 if ( ! empty( $ignored_hooked_blocks ) ) { |
|
1360 $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); |
|
1361 $attributes['metadata'] = array( |
|
1362 'ignoredHookedBlocks' => $ignored_hooked_blocks, |
|
1363 ); |
|
1364 } |
|
1365 |
|
1366 if ( 'wp_navigation' === $post->post_type ) { |
|
1367 $wrapper_block_type = 'core/navigation'; |
|
1368 } elseif ( 'wp_block' === $post->post_type ) { |
|
1369 $wrapper_block_type = 'core/block'; |
|
1370 } else { |
|
1371 $wrapper_block_type = 'core/post-content'; |
|
1372 } |
|
1373 |
|
1092 $markup = get_comment_delimited_block_content( |
1374 $markup = get_comment_delimited_block_content( |
1093 'core/navigation', |
1375 $wrapper_block_type, |
1094 $attributes, |
1376 $attributes, |
1095 $post->post_content |
1377 $post->post_content |
1096 ); |
1378 ); |
1097 |
1379 |
1098 $serialized_block = apply_block_hooks_to_content( $markup, get_post( $post->ID ), 'set_ignored_hooked_blocks_metadata' ); |
1380 $existing_post = get_post( $post->ID ); |
1381 // Merge the existing post object with the updated post object to pass to the block hooks algorithm for context. |
|
1382 $context = (object) array_merge( (array) $existing_post, (array) $post ); |
|
1383 $context = new WP_Post( $context ); // Convert to WP_Post object. |
|
1384 $serialized_block = apply_block_hooks_to_content( $markup, $context, 'set_ignored_hooked_blocks_metadata' ); |
|
1099 $root_block = parse_blocks( $serialized_block )[0]; |
1385 $root_block = parse_blocks( $serialized_block )[0]; |
1100 |
1386 |
1101 $ignored_hooked_blocks = isset( $root_block['attrs']['metadata']['ignoredHookedBlocks'] ) |
1387 $ignored_hooked_blocks = isset( $root_block['attrs']['metadata']['ignoredHookedBlocks'] ) |
1102 ? $root_block['attrs']['metadata']['ignoredHookedBlocks'] |
1388 ? $root_block['attrs']['metadata']['ignoredHookedBlocks'] |
1103 : array(); |
1389 : array(); |
1106 $existing_ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); |
1392 $existing_ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); |
1107 if ( ! empty( $existing_ignored_hooked_blocks ) ) { |
1393 if ( ! empty( $existing_ignored_hooked_blocks ) ) { |
1108 $existing_ignored_hooked_blocks = json_decode( $existing_ignored_hooked_blocks, true ); |
1394 $existing_ignored_hooked_blocks = json_decode( $existing_ignored_hooked_blocks, true ); |
1109 $ignored_hooked_blocks = array_unique( array_merge( $ignored_hooked_blocks, $existing_ignored_hooked_blocks ) ); |
1395 $ignored_hooked_blocks = array_unique( array_merge( $ignored_hooked_blocks, $existing_ignored_hooked_blocks ) ); |
1110 } |
1396 } |
1111 update_post_meta( $post->ID, '_wp_ignored_hooked_blocks', json_encode( $ignored_hooked_blocks ) ); |
1397 |
1398 if ( ! isset( $post->meta_input ) ) { |
|
1399 $post->meta_input = array(); |
|
1400 } |
|
1401 $post->meta_input['_wp_ignored_hooked_blocks'] = json_encode( $ignored_hooked_blocks ); |
|
1112 } |
1402 } |
1113 |
1403 |
1114 $post->post_content = remove_serialized_parent_block( $serialized_block ); |
1404 $post->post_content = remove_serialized_parent_block( $serialized_block ); |
1115 return $post; |
1405 return $post; |
1116 } |
1406 } |
1137 |
1427 |
1138 return $markup; |
1428 return $markup; |
1139 } |
1429 } |
1140 |
1430 |
1141 /** |
1431 /** |
1142 * Hooks into the REST API response for the core/navigation block and adds the first and last inner blocks. |
1432 * Hooks into the REST API response for the Posts endpoint and adds the first and last inner blocks. |
1143 * |
1433 * |
1144 * @since 6.6.0 |
1434 * @since 6.6.0 |
1435 * @since 6.8.0 Support non-`wp_navigation` post types. |
|
1145 * |
1436 * |
1146 * @param WP_REST_Response $response The response object. |
1437 * @param WP_REST_Response $response The response object. |
1147 * @param WP_Post $post Post object. |
1438 * @param WP_Post $post Post object. |
1148 * @return WP_REST_Response The response object. |
1439 * @return WP_REST_Response The response object. |
1149 */ |
1440 */ |
1150 function insert_hooked_blocks_into_rest_response( $response, $post ) { |
1441 function insert_hooked_blocks_into_rest_response( $response, $post ) { |
1151 if ( ! isset( $response->data['content']['raw'] ) || ! isset( $response->data['content']['rendered'] ) ) { |
1442 if ( empty( $response->data['content']['raw'] ) ) { |
1152 return $response; |
1443 return $response; |
1153 } |
1444 } |
1154 |
1445 |
1155 $attributes = array(); |
1446 $response->data['content']['raw'] = apply_block_hooks_to_content_from_post_object( |
1156 $ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); |
1447 $response->data['content']['raw'], |
1157 if ( ! empty( $ignored_hooked_blocks ) ) { |
1448 $post, |
1158 $ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true ); |
1449 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' |
1159 $attributes['metadata'] = array( |
1450 ); |
1160 'ignoredHookedBlocks' => $ignored_hooked_blocks, |
1451 |
1161 ); |
1452 // If the rendered content was previously empty, we leave it like that. |
1162 } |
1453 if ( empty( $response->data['content']['rendered'] ) ) { |
1163 $content = get_comment_delimited_block_content( |
1454 return $response; |
1164 'core/navigation', |
1455 } |
1165 $attributes, |
1456 |
1457 // `apply_block_hooks_to_content` is called above. Ensure it is not called again as a filter. |
|
1458 $priority = has_filter( 'the_content', 'apply_block_hooks_to_content_from_post_object' ); |
|
1459 if ( false !== $priority ) { |
|
1460 remove_filter( 'the_content', 'apply_block_hooks_to_content_from_post_object', $priority ); |
|
1461 } |
|
1462 |
|
1463 /** This filter is documented in wp-includes/post-template.php */ |
|
1464 $response->data['content']['rendered'] = apply_filters( |
|
1465 'the_content', |
|
1166 $response->data['content']['raw'] |
1466 $response->data['content']['raw'] |
1167 ); |
1467 ); |
1168 |
1468 |
1169 $content = apply_block_hooks_to_content( $content, $post ); |
1469 // Restore the filter if it was set initially. |
1170 |
1470 if ( false !== $priority ) { |
1171 // Remove mock Navigation block wrapper. |
1471 add_filter( 'the_content', 'apply_block_hooks_to_content_from_post_object', $priority ); |
1172 $content = remove_serialized_parent_block( $content ); |
1472 } |
1173 |
|
1174 $response->data['content']['raw'] = $content; |
|
1175 |
|
1176 /** This filter is documented in wp-includes/post-template.php */ |
|
1177 $response->data['content']['rendered'] = apply_filters( 'the_content', $content ); |
|
1178 |
1473 |
1179 return $response; |
1474 return $response; |
1180 } |
1475 } |
1181 |
1476 |
1182 /** |
1477 /** |
1191 * @since 6.4.0 |
1486 * @since 6.4.0 |
1192 * @since 6.5.0 Added $callback argument. |
1487 * @since 6.5.0 Added $callback argument. |
1193 * @access private |
1488 * @access private |
1194 * |
1489 * |
1195 * @param array $hooked_blocks An array of blocks hooked to another given block. |
1490 * @param array $hooked_blocks An array of blocks hooked to another given block. |
1196 * @param WP_Block_Template|WP_Post|array $context A block template, template part, `wp_navigation` post object, |
1491 * @param WP_Block_Template|WP_Post|array $context A block template, template part, post object, |
1197 * or pattern that the blocks belong to. |
1492 * or pattern that the blocks belong to. |
1198 * @param callable $callback A function that will be called for each block to generate |
1493 * @param callable $callback A function that will be called for each block to generate |
1199 * the markup for a given list of blocks that are hooked to it. |
1494 * the markup for a given list of blocks that are hooked to it. |
1200 * Default: 'insert_hooked_blocks'. |
1495 * Default: 'insert_hooked_blocks'. |
1201 * @return callable A function that returns the serialized markup for the given block, |
1496 * @return callable A function that returns the serialized markup for the given block, |
1248 * @since 6.4.0 |
1543 * @since 6.4.0 |
1249 * @since 6.5.0 Added $callback argument. |
1544 * @since 6.5.0 Added $callback argument. |
1250 * @access private |
1545 * @access private |
1251 * |
1546 * |
1252 * @param array $hooked_blocks An array of blocks hooked to another block. |
1547 * @param array $hooked_blocks An array of blocks hooked to another block. |
1253 * @param WP_Block_Template|WP_Post|array $context A block template, template part, `wp_navigation` post object, |
1548 * @param WP_Block_Template|WP_Post|array $context A block template, template part, post object, |
1254 * or pattern that the blocks belong to. |
1549 * or pattern that the blocks belong to. |
1255 * @param callable $callback A function that will be called for each block to generate |
1550 * @param callable $callback A function that will be called for each block to generate |
1256 * the markup for a given list of blocks that are hooked to it. |
1551 * the markup for a given list of blocks that are hooked to it. |
1257 * Default: 'insert_hooked_blocks'. |
1552 * Default: 'insert_hooked_blocks'. |
1258 * @return callable A function that returns the serialized markup for the given block, |
1553 * @return callable A function that returns the serialized markup for the given block, |
1377 * instead preserve the markup as parsed. |
1672 * instead preserve the markup as parsed. |
1378 * |
1673 * |
1379 * @since 5.3.1 |
1674 * @since 5.3.1 |
1380 * |
1675 * |
1381 * @param array $block { |
1676 * @param array $block { |
1382 * A representative array of a single parsed block object. See WP_Block_Parser_Block. |
1677 * An associative array of a single parsed block object. See WP_Block_Parser_Block. |
1383 * |
1678 * |
1384 * @type string $blockName Name of block. |
1679 * @type string $blockName Name of block. |
1385 * @type array $attrs Attributes from block comment delimiters. |
1680 * @type array $attrs Attributes from block comment delimiters. |
1386 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
1681 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
1387 * have the same structure as this one. |
1682 * have the same structure as this one. |
1418 * |
1713 * |
1419 * @param array[] $blocks { |
1714 * @param array[] $blocks { |
1420 * Array of block structures. |
1715 * Array of block structures. |
1421 * |
1716 * |
1422 * @type array ...$0 { |
1717 * @type array ...$0 { |
1423 * A representative array of a single parsed block object. See WP_Block_Parser_Block. |
1718 * An associative array of a single parsed block object. See WP_Block_Parser_Block. |
1424 * |
1719 * |
1425 * @type string $blockName Name of block. |
1720 * @type string $blockName Name of block. |
1426 * @type array $attrs Attributes from block comment delimiters. |
1721 * @type array $attrs Attributes from block comment delimiters. |
1427 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
1722 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
1428 * have the same structure as this one. |
1723 * have the same structure as this one. |
1460 * @since 6.4.0 |
1755 * @since 6.4.0 |
1461 * @access private |
1756 * @access private |
1462 * |
1757 * |
1463 * @see serialize_block() |
1758 * @see serialize_block() |
1464 * |
1759 * |
1465 * @param array $block A representative array of a single parsed block object. See WP_Block_Parser_Block. |
1760 * @param array $block An associative array of a single parsed block object. See WP_Block_Parser_Block. |
1466 * @param callable $pre_callback Callback to run on each block in the tree before it is traversed and serialized. |
1761 * @param callable $pre_callback Callback to run on each block in the tree before it is traversed and serialized. |
1467 * It is called with the following arguments: &$block, $parent_block, $previous_block. |
1762 * It is called with the following arguments: &$block, $parent_block, $previous_block. |
1468 * Its string return value will be prepended to the serialized block markup. |
1763 * Its string return value will be prepended to the serialized block markup. |
1469 * @param callable $post_callback Callback to run on each block in the tree after it is traversed and serialized. |
1764 * @param callable $post_callback Callback to run on each block in the tree after it is traversed and serialized. |
1470 * It is called with the following arguments: &$block, $parent_block, $next_block. |
1765 * It is called with the following arguments: &$block, $parent_block, $next_block. |
1635 */ |
1930 */ |
1636 function traverse_and_serialize_blocks( $blocks, $pre_callback = null, $post_callback = null ) { |
1931 function traverse_and_serialize_blocks( $blocks, $pre_callback = null, $post_callback = null ) { |
1637 $result = ''; |
1932 $result = ''; |
1638 $parent_block = null; // At the top level, there is no parent block to pass to the callbacks; yet the callbacks expect a reference. |
1933 $parent_block = null; // At the top level, there is no parent block to pass to the callbacks; yet the callbacks expect a reference. |
1639 |
1934 |
1935 $pre_callback_is_callable = is_callable( $pre_callback ); |
|
1936 $post_callback_is_callable = is_callable( $post_callback ); |
|
1937 |
|
1640 foreach ( $blocks as $index => $block ) { |
1938 foreach ( $blocks as $index => $block ) { |
1641 if ( is_callable( $pre_callback ) ) { |
1939 if ( $pre_callback_is_callable ) { |
1642 $prev = 0 === $index |
1940 $prev = 0 === $index |
1643 ? null |
1941 ? null |
1644 : $blocks[ $index - 1 ]; |
1942 : $blocks[ $index - 1 ]; |
1645 |
1943 |
1646 $result .= call_user_func_array( |
1944 $result .= call_user_func_array( |
1647 $pre_callback, |
1945 $pre_callback, |
1648 array( &$block, &$parent_block, $prev ) |
1946 array( &$block, &$parent_block, $prev ) |
1649 ); |
1947 ); |
1650 } |
1948 } |
1651 |
1949 |
1652 if ( is_callable( $post_callback ) ) { |
1950 if ( $post_callback_is_callable ) { |
1653 $next = count( $blocks ) - 1 === $index |
1951 $next = count( $blocks ) - 1 === $index |
1654 ? null |
1952 ? null |
1655 : $blocks[ $index + 1 ]; |
1953 : $blocks[ $index + 1 ]; |
1656 |
1954 |
1657 $post_markup = call_user_func_array( |
1955 $post_markup = call_user_func_array( |
1942 * @since 5.0.0 |
2240 * @since 5.0.0 |
1943 * |
2241 * |
1944 * @global WP_Post $post The post to edit. |
2242 * @global WP_Post $post The post to edit. |
1945 * |
2243 * |
1946 * @param array $parsed_block { |
2244 * @param array $parsed_block { |
1947 * A representative array of the block being rendered. See WP_Block_Parser_Block. |
2245 * An associative array of the block being rendered. See WP_Block_Parser_Block. |
1948 * |
2246 * |
1949 * @type string $blockName Name of block. |
2247 * @type string $blockName Name of block. |
1950 * @type array $attrs Attributes from block comment delimiters. |
2248 * @type array $attrs Attributes from block comment delimiters. |
1951 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
2249 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
1952 * have the same structure as this one. |
2250 * have the same structure as this one. |
1966 * @since 5.1.0 |
2264 * @since 5.1.0 |
1967 * @since 5.9.0 The `$parent_block` parameter was added. |
2265 * @since 5.9.0 The `$parent_block` parameter was added. |
1968 * |
2266 * |
1969 * @param string|null $pre_render The pre-rendered content. Default null. |
2267 * @param string|null $pre_render The pre-rendered content. Default null. |
1970 * @param array $parsed_block { |
2268 * @param array $parsed_block { |
1971 * A representative array of the block being rendered. See WP_Block_Parser_Block. |
2269 * An associative array of the block being rendered. See WP_Block_Parser_Block. |
1972 * |
2270 * |
1973 * @type string $blockName Name of block. |
2271 * @type string $blockName Name of block. |
1974 * @type array $attrs Attributes from block comment delimiters. |
2272 * @type array $attrs Attributes from block comment delimiters. |
1975 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
2273 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
1976 * have the same structure as this one. |
2274 * have the same structure as this one. |
1992 * |
2290 * |
1993 * @since 5.1.0 |
2291 * @since 5.1.0 |
1994 * @since 5.9.0 The `$parent_block` parameter was added. |
2292 * @since 5.9.0 The `$parent_block` parameter was added. |
1995 * |
2293 * |
1996 * @param array $parsed_block { |
2294 * @param array $parsed_block { |
1997 * A representative array of the block being rendered. See WP_Block_Parser_Block. |
2295 * An associative array of the block being rendered. See WP_Block_Parser_Block. |
1998 * |
2296 * |
1999 * @type string $blockName Name of block. |
2297 * @type string $blockName Name of block. |
2000 * @type array $attrs Attributes from block comment delimiters. |
2298 * @type array $attrs Attributes from block comment delimiters. |
2001 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
2299 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
2002 * have the same structure as this one. |
2300 * have the same structure as this one. |
2040 * @since 5.5.0 |
2338 * @since 5.5.0 |
2041 * @since 5.9.0 The `$parent_block` parameter was added. |
2339 * @since 5.9.0 The `$parent_block` parameter was added. |
2042 * |
2340 * |
2043 * @param array $context Default context. |
2341 * @param array $context Default context. |
2044 * @param array $parsed_block { |
2342 * @param array $parsed_block { |
2045 * A representative array of the block being rendered. See WP_Block_Parser_Block. |
2343 * An associative array of the block being rendered. See WP_Block_Parser_Block. |
2046 * |
2344 * |
2047 * @type string $blockName Name of block. |
2345 * @type string $blockName Name of block. |
2048 * @type array $attrs Attributes from block comment delimiters. |
2346 * @type array $attrs Attributes from block comment delimiters. |
2049 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
2347 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
2050 * have the same structure as this one. |
2348 * have the same structure as this one. |
2069 * @param string $content Post content. |
2367 * @param string $content Post content. |
2070 * @return array[] { |
2368 * @return array[] { |
2071 * Array of block structures. |
2369 * Array of block structures. |
2072 * |
2370 * |
2073 * @type array ...$0 { |
2371 * @type array ...$0 { |
2074 * A representative array of a single parsed block object. See WP_Block_Parser_Block. |
2372 * An associative array of a single parsed block object. See WP_Block_Parser_Block. |
2075 * |
2373 * |
2076 * @type string $blockName Name of block. |
2374 * @type string $blockName Name of block. |
2077 * @type array $attrs Attributes from block comment delimiters. |
2375 * @type array $attrs Attributes from block comment delimiters. |
2078 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
2376 * @type array[] $innerBlocks List of inner blocks. An array of arrays that |
2079 * have the same structure as this one. |
2377 * have the same structure as this one. |
2104 * |
2402 * |
2105 * @param string $content Post content. |
2403 * @param string $content Post content. |
2106 * @return string Updated post content. |
2404 * @return string Updated post content. |
2107 */ |
2405 */ |
2108 function do_blocks( $content ) { |
2406 function do_blocks( $content ) { |
2109 $blocks = parse_blocks( $content ); |
2407 $blocks = parse_blocks( $content ); |
2110 $output = ''; |
2408 $top_level_block_count = count( $blocks ); |
2111 |
2409 $output = ''; |
2112 foreach ( $blocks as $block ) { |
2410 |
2113 $output .= render_block( $block ); |
2411 /** |
2412 * Parsed blocks consist of a list of top-level blocks. Those top-level |
|
2413 * blocks may themselves contain nested inner blocks. However, every |
|
2414 * top-level block is rendered independently, meaning there are no data |
|
2415 * dependencies between them. |
|
2416 * |
|
2417 * Ideally, therefore, the parser would only need to parse one complete |
|
2418 * top-level block at a time, render it, and move on. Unfortunately, this |
|
2419 * is not possible with {@see \parse_blocks()} because it must parse the |
|
2420 * entire given document at once. |
|
2421 * |
|
2422 * While the current implementation prevents this optimization, it’s still |
|
2423 * possible to reduce the peak memory use when calls to `render_block()` |
|
2424 * on those top-level blocks are memory-heavy (which many of them are). |
|
2425 * By setting each parsed block to `NULL` after rendering it, any memory |
|
2426 * allocated during the render will be freed and reused for the next block. |
|
2427 * Before making this change, that memory was retained and would lead to |
|
2428 * out-of-memory crashes for certain posts that now run with this change. |
|
2429 */ |
|
2430 for ( $i = 0; $i < $top_level_block_count; $i++ ) { |
|
2431 $output .= render_block( $blocks[ $i ] ); |
|
2432 $blocks[ $i ] = null; |
|
2114 } |
2433 } |
2115 |
2434 |
2116 // If there are blocks in this content, we shouldn't run wpautop() on it later. |
2435 // If there are blocks in this content, we shouldn't run wpautop() on it later. |
2117 $priority = has_filter( 'the_content', 'wpautop' ); |
2436 $priority = has_filter( 'the_content', 'wpautop' ); |
2118 if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) { |
2437 if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) { |
2275 * |
2594 * |
2276 * It's used in Query Loop, Query Pagination Numbers and Query Pagination Next blocks. |
2595 * It's used in Query Loop, Query Pagination Numbers and Query Pagination Next blocks. |
2277 * |
2596 * |
2278 * @since 5.8.0 |
2597 * @since 5.8.0 |
2279 * @since 6.1.0 Added `query_loop_block_query_vars` filter and `parents` support in query. |
2598 * @since 6.1.0 Added `query_loop_block_query_vars` filter and `parents` support in query. |
2599 * @since 6.7.0 Added support for the `format` property in query. |
|
2280 * |
2600 * |
2281 * @param WP_Block $block Block instance. |
2601 * @param WP_Block $block Block instance. |
2282 * @param int $page Current query's page. |
2602 * @param int $page Current query's page. |
2283 * |
2603 * |
2284 * @return array Returns the constructed WP_Query arguments. |
2604 * @return array Returns the constructed WP_Query arguments. |
2287 $query = array( |
2607 $query = array( |
2288 'post_type' => 'post', |
2608 'post_type' => 'post', |
2289 'order' => 'DESC', |
2609 'order' => 'DESC', |
2290 'orderby' => 'date', |
2610 'orderby' => 'date', |
2291 'post__not_in' => array(), |
2611 'post__not_in' => array(), |
2612 'tax_query' => array(), |
|
2292 ); |
2613 ); |
2293 |
2614 |
2294 if ( isset( $block->context['query'] ) ) { |
2615 if ( isset( $block->context['query'] ) ) { |
2295 if ( ! empty( $block->context['query']['postType'] ) ) { |
2616 if ( ! empty( $block->context['query']['postType'] ) ) { |
2296 $post_type_param = $block->context['query']['postType']; |
2617 $post_type_param = $block->context['query']['postType']; |
2308 * |
2629 * |
2309 * @see https://core.trac.wordpress.org/ticket/28099 |
2630 * @see https://core.trac.wordpress.org/ticket/28099 |
2310 */ |
2631 */ |
2311 $query['post__in'] = ! empty( $sticky ) ? $sticky : array( 0 ); |
2632 $query['post__in'] = ! empty( $sticky ) ? $sticky : array( 0 ); |
2312 $query['ignore_sticky_posts'] = 1; |
2633 $query['ignore_sticky_posts'] = 1; |
2313 } else { |
2634 } elseif ( 'exclude' === $block->context['query']['sticky'] ) { |
2314 $query['post__not_in'] = array_merge( $query['post__not_in'], $sticky ); |
2635 $query['post__not_in'] = array_merge( $query['post__not_in'], $sticky ); |
2636 } elseif ( 'ignore' === $block->context['query']['sticky'] ) { |
|
2637 $query['ignore_sticky_posts'] = 1; |
|
2315 } |
2638 } |
2316 } |
2639 } |
2317 if ( ! empty( $block->context['query']['exclude'] ) ) { |
2640 if ( ! empty( $block->context['query']['exclude'] ) ) { |
2318 $excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] ); |
2641 $excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] ); |
2319 $excluded_post_ids = array_filter( $excluded_post_ids ); |
2642 $excluded_post_ids = array_filter( $excluded_post_ids ); |
2336 $query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset; |
2659 $query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset; |
2337 $query['posts_per_page'] = $per_page; |
2660 $query['posts_per_page'] = $per_page; |
2338 } |
2661 } |
2339 // Migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility. |
2662 // Migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility. |
2340 if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) { |
2663 if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) { |
2341 $tax_query = array(); |
2664 $tax_query_back_compat = array(); |
2342 if ( ! empty( $block->context['query']['categoryIds'] ) ) { |
2665 if ( ! empty( $block->context['query']['categoryIds'] ) ) { |
2343 $tax_query[] = array( |
2666 $tax_query_back_compat[] = array( |
2344 'taxonomy' => 'category', |
2667 'taxonomy' => 'category', |
2345 'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ), |
2668 'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ), |
2346 'include_children' => false, |
2669 'include_children' => false, |
2347 ); |
2670 ); |
2348 } |
2671 } |
2349 if ( ! empty( $block->context['query']['tagIds'] ) ) { |
2672 if ( ! empty( $block->context['query']['tagIds'] ) ) { |
2350 $tax_query[] = array( |
2673 $tax_query_back_compat[] = array( |
2351 'taxonomy' => 'post_tag', |
2674 'taxonomy' => 'post_tag', |
2352 'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ), |
2675 'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ), |
2353 'include_children' => false, |
2676 'include_children' => false, |
2354 ); |
2677 ); |
2355 } |
2678 } |
2356 $query['tax_query'] = $tax_query; |
2679 $query['tax_query'] = array_merge( $query['tax_query'], $tax_query_back_compat ); |
2357 } |
2680 } |
2358 if ( ! empty( $block->context['query']['taxQuery'] ) ) { |
2681 if ( ! empty( $block->context['query']['taxQuery'] ) ) { |
2359 $query['tax_query'] = array(); |
2682 $tax_query = array(); |
2360 foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) { |
2683 foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) { |
2361 if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) { |
2684 if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) { |
2362 $query['tax_query'][] = array( |
2685 $tax_query[] = array( |
2363 'taxonomy' => $taxonomy, |
2686 'taxonomy' => $taxonomy, |
2364 'terms' => array_filter( array_map( 'intval', $terms ) ), |
2687 'terms' => array_filter( array_map( 'intval', $terms ) ), |
2365 'include_children' => false, |
2688 'include_children' => false, |
2366 ); |
2689 ); |
2367 } |
2690 } |
2368 } |
2691 } |
2369 } |
2692 $query['tax_query'] = array_merge( $query['tax_query'], $tax_query ); |
2693 } |
|
2694 if ( ! empty( $block->context['query']['format'] ) && is_array( $block->context['query']['format'] ) ) { |
|
2695 $formats = $block->context['query']['format']; |
|
2696 /* |
|
2697 * Validate that the format is either `standard` or a supported post format. |
|
2698 * - First, add `standard` to the array of valid formats. |
|
2699 * - Then, remove any invalid formats. |
|
2700 */ |
|
2701 $valid_formats = array_merge( array( 'standard' ), get_post_format_slugs() ); |
|
2702 $formats = array_intersect( $formats, $valid_formats ); |
|
2703 |
|
2704 /* |
|
2705 * The relation needs to be set to `OR` since the request can contain |
|
2706 * two separate conditions. The user may be querying for items that have |
|
2707 * either the `standard` format or a specific format. |
|
2708 */ |
|
2709 $formats_query = array( 'relation' => 'OR' ); |
|
2710 |
|
2711 /* |
|
2712 * The default post format, `standard`, is not stored in the database. |
|
2713 * If `standard` is part of the request, the query needs to exclude all post items that |
|
2714 * have a format assigned. |
|
2715 */ |
|
2716 if ( in_array( 'standard', $formats, true ) ) { |
|
2717 $formats_query[] = array( |
|
2718 'taxonomy' => 'post_format', |
|
2719 'field' => 'slug', |
|
2720 'operator' => 'NOT EXISTS', |
|
2721 ); |
|
2722 // Remove the `standard` format, since it cannot be queried. |
|
2723 unset( $formats[ array_search( 'standard', $formats, true ) ] ); |
|
2724 } |
|
2725 // Add any remaining formats to the formats query. |
|
2726 if ( ! empty( $formats ) ) { |
|
2727 // Add the `post-format-` prefix. |
|
2728 $terms = array_map( |
|
2729 static function ( $format ) { |
|
2730 return "post-format-$format"; |
|
2731 }, |
|
2732 $formats |
|
2733 ); |
|
2734 $formats_query[] = array( |
|
2735 'taxonomy' => 'post_format', |
|
2736 'field' => 'slug', |
|
2737 'terms' => $terms, |
|
2738 'operator' => 'IN', |
|
2739 ); |
|
2740 } |
|
2741 |
|
2742 /* |
|
2743 * Add `$formats_query` to `$query`, as long as it contains more than one key: |
|
2744 * If `$formats_query` only contains the initial `relation` key, there are no valid formats to query, |
|
2745 * and the query should not be modified. |
|
2746 */ |
|
2747 if ( count( $formats_query ) > 1 ) { |
|
2748 // Enable filtering by both post formats and other taxonomies by combining them with `AND`. |
|
2749 if ( empty( $query['tax_query'] ) ) { |
|
2750 $query['tax_query'] = $formats_query; |
|
2751 } else { |
|
2752 $query['tax_query'] = array( |
|
2753 'relation' => 'AND', |
|
2754 $query['tax_query'], |
|
2755 $formats_query, |
|
2756 ); |
|
2757 } |
|
2758 } |
|
2759 } |
|
2760 |
|
2370 if ( |
2761 if ( |
2371 isset( $block->context['query']['order'] ) && |
2762 isset( $block->context['query']['order'] ) && |
2372 in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true ) |
2763 in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true ) |
2373 ) { |
2764 ) { |
2374 $query['order'] = strtoupper( $block->context['query']['order'] ); |
2765 $query['order'] = strtoupper( $block->context['query']['order'] ); |
2389 } |
2780 } |
2390 if ( ! empty( $block->context['query']['search'] ) ) { |
2781 if ( ! empty( $block->context['query']['search'] ) ) { |
2391 $query['s'] = $block->context['query']['search']; |
2782 $query['s'] = $block->context['query']['search']; |
2392 } |
2783 } |
2393 if ( ! empty( $block->context['query']['parents'] ) && is_post_type_hierarchical( $query['post_type'] ) ) { |
2784 if ( ! empty( $block->context['query']['parents'] ) && is_post_type_hierarchical( $query['post_type'] ) ) { |
2394 $query['post_parent__in'] = array_filter( array_map( 'intval', $block->context['query']['parents'] ) ); |
2785 $query['post_parent__in'] = array_unique( array_map( 'intval', $block->context['query']['parents'] ) ); |
2395 } |
2786 } |
2396 } |
2787 } |
2397 |
2788 |
2398 /** |
2789 /** |
2399 * Filters the arguments which will be passed to `WP_Query` for the Query Loop Block. |
2790 * Filters the arguments which will be passed to `WP_Query` for the Query Loop Block. |
2509 $max_num_pages = (int) ( new WP_Comment_Query( $comment_args ) )->max_num_pages; |
2900 $max_num_pages = (int) ( new WP_Comment_Query( $comment_args ) )->max_num_pages; |
2510 if ( 0 !== $max_num_pages ) { |
2901 if ( 0 !== $max_num_pages ) { |
2511 $comment_args['paged'] = $max_num_pages; |
2902 $comment_args['paged'] = $max_num_pages; |
2512 } |
2903 } |
2513 } |
2904 } |
2514 // Set the `cpage` query var to ensure the previous and next pagination links are correct |
|
2515 // when inheriting the Discussion Settings. |
|
2516 if ( 0 === $page && isset( $comment_args['paged'] ) && $comment_args['paged'] > 0 ) { |
|
2517 set_query_var( 'cpage', $comment_args['paged'] ); |
|
2518 } |
|
2519 } |
2905 } |
2520 } |
2906 } |
2521 |
2907 |
2522 return $comment_args; |
2908 return $comment_args; |
2523 } |
2909 } |