204 'title' => _x( 'Page: 404', 'Template name' ), |
204 'title' => _x( 'Page: 404', 'Template name' ), |
205 'description' => __( 'Displays when a visitor views a non-existent page, such as a dead link or a mistyped URL.' ), |
205 'description' => __( 'Displays when a visitor views a non-existent page, such as a dead link or a mistyped URL.' ), |
206 ), |
206 ), |
207 ); |
207 ); |
208 |
208 |
|
209 // Add a title and description to post format templates. |
|
210 $post_formats = get_post_format_strings(); |
|
211 foreach ( $post_formats as $post_format_slug => $post_format_name ) { |
|
212 $default_template_types[ 'taxonomy-post_format-post-format-' . $post_format_slug ] = array( |
|
213 'title' => sprintf( |
|
214 /* translators: %s: Post format name. */ |
|
215 _x( 'Post Format: %s', 'Template name' ), |
|
216 $post_format_name |
|
217 ), |
|
218 'description' => sprintf( |
|
219 /* translators: %s: Post format name. */ |
|
220 __( 'Displays the %s post format archive.' ), |
|
221 $post_format_name |
|
222 ), |
|
223 ); |
|
224 } |
|
225 |
209 /** |
226 /** |
210 * Filters the list of default template types. |
227 * Filters the list of default template types. |
211 * |
228 * |
212 * @since 5.9.0 |
229 * @since 5.9.0 |
213 * |
230 * |
353 * @type string[] $slug__not_in List of slugs to skip. |
370 * @type string[] $slug__not_in List of slugs to skip. |
354 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for 'wp_template_part' template type only). |
371 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for 'wp_template_part' template type only). |
355 * @type string $post_type Post type to get the templates for. |
372 * @type string $post_type Post type to get the templates for. |
356 * } |
373 * } |
357 * |
374 * |
358 * @return array Template |
375 * @return array|null Template files on success, null if `$template_type` is not matched. |
359 */ |
376 */ |
360 function _get_block_templates_files( $template_type, $query = array() ) { |
377 function _get_block_templates_files( $template_type, $query = array() ) { |
361 if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) { |
378 if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) { |
362 return null; |
379 return null; |
363 } |
380 } |
590 $template->status = 'publish'; |
607 $template->status = 'publish'; |
591 $template->has_theme_file = true; |
608 $template->has_theme_file = true; |
592 $template->is_custom = true; |
609 $template->is_custom = true; |
593 $template->modified = null; |
610 $template->modified = null; |
594 |
611 |
|
612 if ( 'wp_template' === $template_type ) { |
|
613 $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $template_file['slug'] ); |
|
614 if ( $registered_template ) { |
|
615 $template->plugin = $registered_template->plugin; |
|
616 $template->title = empty( $template->title ) || $template->title === $template->slug ? $registered_template->title : $template->title; |
|
617 $template->description = empty( $template->description ) ? $registered_template->description : $template->description; |
|
618 } |
|
619 } |
|
620 |
595 if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) { |
621 if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) { |
596 $template->description = $default_template_types[ $template_file['slug'] ]['description']; |
622 $template->description = $default_template_types[ $template_file['slug'] ]['description']; |
597 $template->title = $default_template_types[ $template_file['slug'] ]['title']; |
623 $template->title = $default_template_types[ $template_file['slug'] ]['title']; |
598 $template->is_custom = false; |
624 $template->is_custom = false; |
599 } |
625 } |
604 |
630 |
605 if ( 'wp_template_part' === $template_type && isset( $template_file['area'] ) ) { |
631 if ( 'wp_template_part' === $template_type && isset( $template_file['area'] ) ) { |
606 $template->area = $template_file['area']; |
632 $template->area = $template_file['area']; |
607 } |
633 } |
608 |
634 |
609 $before_block_visitor = '_inject_theme_attribute_in_template_part_block'; |
635 if ( 'wp_template_part' === $template->type ) { |
610 $after_block_visitor = null; |
636 /* |
611 $hooked_blocks = get_hooked_blocks(); |
637 * In order for hooked blocks to be inserted at positions first_child and last_child in a template part, |
612 if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) { |
638 * we need to wrap its content a mock template part block and traverse it. |
613 $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' ); |
639 */ |
614 $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' ); |
640 $content = get_comment_delimited_block_content( |
615 } |
641 'core/template-part', |
616 $blocks = parse_blocks( $template->content ); |
642 array(), |
617 $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); |
643 $template->content |
|
644 ); |
|
645 $content = apply_block_hooks_to_content( |
|
646 $content, |
|
647 $template, |
|
648 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' |
|
649 ); |
|
650 $template->content = remove_serialized_parent_block( $content ); |
|
651 } else { |
|
652 $template->content = apply_block_hooks_to_content( |
|
653 $template->content, |
|
654 $template, |
|
655 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' |
|
656 ); |
|
657 } |
618 |
658 |
619 return $template; |
659 return $template; |
620 } |
660 } |
621 |
661 |
622 /** |
662 /** |
992 break; |
1032 break; |
993 } |
1033 } |
994 } |
1034 } |
995 } |
1035 } |
996 |
1036 |
997 $hooked_blocks = get_hooked_blocks(); |
1037 if ( 'wp_template' === $post->post_type ) { |
998 if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) { |
1038 $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $template->slug ); |
999 $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' ); |
1039 if ( $registered_template ) { |
1000 $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' ); |
1040 $template->plugin = $registered_template->plugin; |
1001 $blocks = parse_blocks( $template->content ); |
1041 $template->origin = |
1002 $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); |
1042 'theme' !== $template->origin && 'theme' !== $template->source ? |
|
1043 'plugin' : |
|
1044 $template->origin; |
|
1045 $template->title = empty( $template->title ) || $template->title === $template->slug ? $registered_template->title : $template->title; |
|
1046 $template->description = empty( $template->description ) ? $registered_template->description : $template->description; |
|
1047 } |
|
1048 } |
|
1049 |
|
1050 if ( 'wp_template_part' === $template->type ) { |
|
1051 $existing_ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ); |
|
1052 $attributes = ! empty( $existing_ignored_hooked_blocks ) ? array( 'metadata' => array( 'ignoredHookedBlocks' => json_decode( $existing_ignored_hooked_blocks, true ) ) ) : array(); |
|
1053 |
|
1054 /* |
|
1055 * In order for hooked blocks to be inserted at positions first_child and last_child in a template part, |
|
1056 * we need to wrap its content a mock template part block and traverse it. |
|
1057 */ |
|
1058 $content = get_comment_delimited_block_content( |
|
1059 'core/template-part', |
|
1060 $attributes, |
|
1061 $template->content |
|
1062 ); |
|
1063 $content = apply_block_hooks_to_content( |
|
1064 $content, |
|
1065 $template, |
|
1066 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' |
|
1067 ); |
|
1068 $template->content = remove_serialized_parent_block( $content ); |
|
1069 } else { |
|
1070 $template->content = apply_block_hooks_to_content( |
|
1071 $template->content, |
|
1072 $template, |
|
1073 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' |
|
1074 ); |
1003 } |
1075 } |
1004 |
1076 |
1005 return $template; |
1077 return $template; |
1006 } |
1078 } |
1007 |
1079 |
1107 $query_result[] = $template; |
1179 $query_result[] = $template; |
1108 } |
1180 } |
1109 |
1181 |
1110 if ( ! isset( $query['wp_id'] ) ) { |
1182 if ( ! isset( $query['wp_id'] ) ) { |
1111 /* |
1183 /* |
1112 * If the query has found some use templates, those have priority |
1184 * If the query has found some user templates, those have priority |
1113 * over the theme-provided ones, so we skip querying and building them. |
1185 * over the theme-provided ones, so we skip querying and building them. |
1114 */ |
1186 */ |
1115 $query['slug__not_in'] = wp_list_pluck( $query_result, 'slug' ); |
1187 $query['slug__not_in'] = wp_list_pluck( $query_result, 'slug' ); |
1116 $template_files = _get_block_templates_files( $template_type, $query ); |
1188 /* |
|
1189 * We need to unset the post_type query param because some templates |
|
1190 * would be excluded otherwise, like `page.html` when looking for |
|
1191 * `page` templates. We need all templates so we can exclude duplicates |
|
1192 * from plugin-registered templates. |
|
1193 * See: https://github.com/WordPress/gutenberg/issues/65584 |
|
1194 */ |
|
1195 $template_files_query = $query; |
|
1196 unset( $template_files_query['post_type'] ); |
|
1197 $template_files = _get_block_templates_files( $template_type, $template_files_query ); |
1117 foreach ( $template_files as $template_file ) { |
1198 foreach ( $template_files as $template_file ) { |
1118 $query_result[] = _build_block_template_result_from_file( $template_file, $template_type ); |
1199 // If the query doesn't specify a post type, or it does and the template matches the post type, add it. |
|
1200 if ( |
|
1201 ! isset( $query['post_type'] ) || |
|
1202 ( |
|
1203 isset( $template_file['postTypes'] ) && |
|
1204 in_array( $query['post_type'], $template_file['postTypes'], true ) |
|
1205 ) |
|
1206 ) { |
|
1207 $query_result[] = _build_block_template_result_from_file( $template_file, $template_type ); |
|
1208 } elseif ( ! isset( $template_file['postTypes'] ) ) { |
|
1209 // The custom templates with no associated post types are available for all post types as long |
|
1210 // as they are not default templates. |
|
1211 $candidate = _build_block_template_result_from_file( $template_file, $template_type ); |
|
1212 $default_template_types = get_default_block_template_types(); |
|
1213 if ( ! isset( $default_template_types[ $candidate->slug ] ) ) { |
|
1214 $query_result[] = $candidate; |
|
1215 } |
|
1216 } |
|
1217 } |
|
1218 |
|
1219 if ( 'wp_template' === $template_type ) { |
|
1220 // Add templates registered in the template registry. Filtering out the ones which have a theme file. |
|
1221 $registered_templates = WP_Block_Templates_Registry::get_instance()->get_by_query( $query ); |
|
1222 $matching_registered_templates = array_filter( |
|
1223 $registered_templates, |
|
1224 function ( $registered_template ) use ( $template_files ) { |
|
1225 foreach ( $template_files as $template_file ) { |
|
1226 if ( $template_file['slug'] === $registered_template->slug ) { |
|
1227 return false; |
|
1228 } |
|
1229 } |
|
1230 return true; |
|
1231 } |
|
1232 ); |
|
1233 $query_result = array_merge( $query_result, $matching_registered_templates ); |
1119 } |
1234 } |
1120 } |
1235 } |
1121 |
1236 |
1122 /** |
1237 /** |
1123 * Filters the array of queried block templates array after they've been fetched. |
1238 * Filters the array of queried block templates array after they've been fetched. |
1245 /** This filter is documented in wp-includes/block-template-utils.php */ |
1360 /** This filter is documented in wp-includes/block-template-utils.php */ |
1246 return apply_filters( 'get_block_file_template', null, $id, $template_type ); |
1361 return apply_filters( 'get_block_file_template', null, $id, $template_type ); |
1247 } |
1362 } |
1248 list( $theme, $slug ) = $parts; |
1363 list( $theme, $slug ) = $parts; |
1249 |
1364 |
1250 if ( get_stylesheet() !== $theme ) { |
1365 if ( get_stylesheet() === $theme ) { |
1251 /** This filter is documented in wp-includes/block-template-utils.php */ |
1366 $template_file = _get_block_template_file( $template_type, $slug ); |
1252 return apply_filters( 'get_block_file_template', null, $id, $template_type ); |
1367 if ( null !== $template_file ) { |
1253 } |
1368 $block_template = _build_block_template_result_from_file( $template_file, $template_type ); |
1254 |
1369 |
1255 $template_file = _get_block_template_file( $template_type, $slug ); |
1370 /** This filter is documented in wp-includes/block-template-utils.php */ |
1256 if ( null === $template_file ) { |
1371 return apply_filters( 'get_block_file_template', $block_template, $id, $template_type ); |
1257 /** This filter is documented in wp-includes/block-template-utils.php */ |
1372 } |
1258 return apply_filters( 'get_block_file_template', null, $id, $template_type ); |
1373 } |
1259 } |
1374 |
1260 |
1375 $block_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $slug ); |
1261 $block_template = _build_block_template_result_from_file( $template_file, $template_type ); |
|
1262 |
1376 |
1263 /** |
1377 /** |
1264 * Filters the block template object after it has been (potentially) fetched from the theme file. |
1378 * Filters the block template object after it has been (potentially) fetched from the theme file. |
1265 * |
1379 * |
1266 * @since 5.9.0 |
1380 * @since 5.9.0 |
1331 * specified path in a ZIP file. |
1445 * specified path in a ZIP file. |
1332 * |
1446 * |
1333 * @since 5.9.0 |
1447 * @since 5.9.0 |
1334 * @since 6.0.0 Adds the whole theme to the export archive. |
1448 * @since 6.0.0 Adds the whole theme to the export archive. |
1335 * |
1449 * |
1336 * @global string $wp_version The WordPress version string. |
|
1337 * |
|
1338 * @return WP_Error|string Path of the ZIP file or error on failure. |
1450 * @return WP_Error|string Path of the ZIP file or error on failure. |
1339 */ |
1451 */ |
1340 function wp_generate_block_templates_export_file() { |
1452 function wp_generate_block_templates_export_file() { |
1341 global $wp_version; |
1453 $wp_version = wp_get_wp_version(); |
1342 |
1454 |
1343 if ( ! class_exists( 'ZipArchive' ) ) { |
1455 if ( ! class_exists( 'ZipArchive' ) ) { |
1344 return new WP_Error( 'missing_zip_package', __( 'Zip Export not supported.' ) ); |
1456 return new WP_Error( 'missing_zip_package', __( 'Zip Export not supported.' ) ); |
1345 } |
1457 } |
1346 |
1458 |
1613 |
1725 |
1614 if ( is_wp_error( $template ) ) { |
1726 if ( is_wp_error( $template ) ) { |
1615 return $template; |
1727 return $template; |
1616 } |
1728 } |
1617 |
1729 |
1618 $changes->post_content = apply_block_hooks_to_content( $changes->post_content, $template, 'set_ignored_hooked_blocks_metadata' ); |
1730 if ( 'wp_template_part' === $post->post_type ) { |
|
1731 $attributes = array(); |
|
1732 $existing_ignored_hooked_blocks = isset( $post->ID ) ? get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true ) : ''; |
|
1733 |
|
1734 if ( ! empty( $existing_ignored_hooked_blocks ) ) { |
|
1735 $attributes['metadata'] = array( |
|
1736 'ignoredHookedBlocks' => json_decode( $existing_ignored_hooked_blocks, true ), |
|
1737 ); |
|
1738 } |
|
1739 |
|
1740 $content = get_comment_delimited_block_content( |
|
1741 'core/template-part', |
|
1742 $attributes, |
|
1743 $changes->post_content |
|
1744 ); |
|
1745 $content = apply_block_hooks_to_content( $content, $template, 'set_ignored_hooked_blocks_metadata' ); |
|
1746 $changes->post_content = remove_serialized_parent_block( $content ); |
|
1747 |
|
1748 $wrapper_block_markup = extract_serialized_parent_block( $content ); |
|
1749 $wrapper_block = parse_blocks( $wrapper_block_markup )[0]; |
|
1750 $ignored_hooked_blocks = $wrapper_block['attrs']['metadata']['ignoredHookedBlocks'] ?? array(); |
|
1751 if ( ! empty( $ignored_hooked_blocks ) ) { |
|
1752 if ( ! isset( $changes->meta_input ) ) { |
|
1753 $changes->meta_input = array(); |
|
1754 } |
|
1755 $changes->meta_input['_wp_ignored_hooked_blocks'] = wp_json_encode( $ignored_hooked_blocks ); |
|
1756 } |
|
1757 } else { |
|
1758 $changes->post_content = apply_block_hooks_to_content( $changes->post_content, $template, 'set_ignored_hooked_blocks_metadata' ); |
|
1759 } |
1619 |
1760 |
1620 return $changes; |
1761 return $changes; |
1621 } |
1762 } |