Changeset 52062
- Timestamp:
- 11/08/2021 11:09:53 PM (3 years ago)
- Location:
- trunk
- Files:
-
- 5 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/block-template-utils.php
r51300 r52062 1 1 <?php 2 2 /** 3 * Utilities used to fetch and create templates .3 * Utilities used to fetch and create templates and template parts. 4 4 * 5 5 * @package WordPress … … 7 7 */ 8 8 9 // Define constants for supported wp_template_part_area taxonomy. 10 if ( ! defined( 'WP_TEMPLATE_PART_AREA_HEADER' ) ) { 11 define( 'WP_TEMPLATE_PART_AREA_HEADER', 'header' ); 12 } 13 if ( ! defined( 'WP_TEMPLATE_PART_AREA_FOOTER' ) ) { 14 define( 'WP_TEMPLATE_PART_AREA_FOOTER', 'footer' ); 15 } 16 if ( ! defined( 'WP_TEMPLATE_PART_AREA_SIDEBAR' ) ) { 17 define( 'WP_TEMPLATE_PART_AREA_SIDEBAR', 'sidebar' ); 18 } 19 if ( ! defined( 'WP_TEMPLATE_PART_AREA_UNCATEGORIZED' ) ) { 20 define( 'WP_TEMPLATE_PART_AREA_UNCATEGORIZED', 'uncategorized' ); 21 } 22 23 /** 24 * Returns a filtered list of allowed area values for template parts. 25 * 26 * @since 5.9.0 27 * 28 * @return array The supported template part area values. 29 */ 30 function get_allowed_block_template_part_areas() { 31 $default_area_definitions = array( 32 array( 33 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, 34 'label' => __( 'General' ), 35 'description' => __( 36 'General templates often perform a specific role like displaying post content, and are not tied to any particular area.' 37 ), 38 'icon' => 'layout', 39 'area_tag' => 'div', 40 ), 41 array( 42 'area' => WP_TEMPLATE_PART_AREA_HEADER, 43 'label' => __( 'Header' ), 44 'description' => __( 45 'The Header template defines a page area that typically contains a title, logo, and main navigation.' 46 ), 47 'icon' => 'header', 48 'area_tag' => 'header', 49 ), 50 array( 51 'area' => WP_TEMPLATE_PART_AREA_FOOTER, 52 'label' => __( 'Footer' ), 53 'description' => __( 54 'The Footer template defines a page area that typically contains site credits, social links, or any other combination of blocks.' 55 ), 56 'icon' => 'footer', 57 'area_tag' => 'footer', 58 ), 59 ); 60 61 /** 62 * Filters the list of allowed template part area values. 63 * 64 * @since 5.9.0 65 * 66 * @param array $default_areas An array of supported area objects. 67 */ 68 return apply_filters( 'default_wp_template_part_areas', $default_area_definitions ); 69 } 70 71 72 /** 73 * Returns a filtered list of default template types, containing their 74 * localized titles and descriptions. 75 * 76 * @since 5.9.0 77 * 78 * @return array The default template types. 79 */ 80 function get_default_block_template_types() { 81 $default_template_types = array( 82 'index' => array( 83 'title' => _x( 'Index', 'Template name' ), 84 'description' => __( 'The default template used when no other template is available. This is a required template in WordPress.' ), 85 ), 86 'home' => array( 87 'title' => _x( 'Home', 'Template name' ), 88 'description' => __( 'Template used for the main page that displays blog posts. This is the front page by default in WordPress. If a static front page is set, this is the template used for the page that contains the latest blog posts.' ), 89 ), 90 'front-page' => array( 91 'title' => _x( 'Front Page', 'Template name' ), 92 'description' => __( 'Template used to render the front page of the site, whether it displays blog posts or a static page. The front page template takes precedence over the "Home" template.' ), 93 ), 94 'singular' => array( 95 'title' => _x( 'Singular', 'Template name' ), 96 'description' => __( 'Template used for displaying single views of the content. This template is a fallback for the Single, Post, and Page templates, which take precedence when they exist.' ), 97 ), 98 'single' => array( 99 'title' => _x( 'Single Post', 'Template name' ), 100 'description' => __( 'Template used to display a single blog post.' ), 101 ), 102 'page' => array( 103 'title' => _x( 'Page', 'Template name' ), 104 'description' => __( 'Template used to display individual pages.' ), 105 ), 106 'archive' => array( 107 'title' => _x( 'Archive', 'Template name' ), 108 'description' => __( 'The archive template displays multiple entries at once. It is used as a fallback for the Category, Author, and Date templates, which take precedence when they are available.' ), 109 ), 110 'author' => array( 111 'title' => _x( 'Author', 'Template name' ), 112 'description' => __( 'Archive template used to display a list of posts from a single author.' ), 113 ), 114 'category' => array( 115 'title' => _x( 'Category', 'Template name' ), 116 'description' => __( 'Archive template used to display a list of posts from the same category.' ), 117 ), 118 'taxonomy' => array( 119 'title' => _x( 'Taxonomy', 'Template name' ), 120 'description' => __( 'Archive template used to display a list of posts from the same taxonomy.' ), 121 ), 122 'date' => array( 123 'title' => _x( 'Date', 'Template name' ), 124 'description' => __( 'Archive template used to display a list of posts from a specific date.' ), 125 ), 126 'tag' => array( 127 'title' => _x( 'Tag', 'Template name' ), 128 'description' => __( 'Archive template used to display a list of posts with a given tag.' ), 129 ), 130 'attachment' => array( 131 'title' => __( 'Media' ), 132 'description' => __( 'Template used to display individual media items or attachments.' ), 133 ), 134 'search' => array( 135 'title' => _x( 'Search', 'Template name' ), 136 'description' => __( 'Template used to display search results.' ), 137 ), 138 'privacy-policy' => array( 139 'title' => __( 'Privacy Policy' ), 140 'description' => '', 141 ), 142 '404' => array( 143 'title' => _x( '404', 'Template name' ), 144 'description' => __( 'Template shown when no content is found.' ), 145 ), 146 ); 147 148 /** 149 * Filters the list of template types. 150 * 151 * @since 5.9.0 152 * 153 * @param array $default_template_types An array of template types, formatted as [ slug => [ title, description ] ]. 154 */ 155 return apply_filters( 'default_template_types', $default_template_types ); 156 } 157 158 /** 159 * Checks whether the input 'area' is a supported value. 160 * Returns the input if supported, otherwise returns the 'uncategorized' value. 161 * 162 * @access private 163 * @since 5.9.0 164 * 165 * @param string $type Template part area name. 166 * 167 * @return string Input if supported, else the uncategorized value. 168 */ 169 function _filter_block_template_part_area( $type ) { 170 $allowed_areas = array_map( 171 static function ( $item ) { 172 return $item['area']; 173 }, 174 get_allowed_block_template_part_areas() 175 ); 176 if ( in_array( $type, $allowed_areas, true ) ) { 177 return $type; 178 } 179 180 $warning_message = sprintf( 181 /* translators: %1$s: Template area type, %2$s: the uncategorized template area value. */ 182 __( '"%1$s" is not a supported wp_template_part area value and has been added as "%2$s".' ), 183 $type, 184 WP_TEMPLATE_PART_AREA_UNCATEGORIZED 185 ); 186 trigger_error( $warning_message, E_USER_NOTICE ); 187 return WP_TEMPLATE_PART_AREA_UNCATEGORIZED; 188 } 189 190 /** 191 * Finds all nested template part file paths in a theme's directory. 192 * 193 * @access private 194 * @since 5.9.0 195 * 196 * @param string $base_directory The theme's file path. 197 * @return array $path_list A list of paths to all template part files. 198 */ 199 function _get_block_templates_paths( $base_directory ) { 200 $path_list = array(); 201 if ( file_exists( $base_directory ) ) { 202 $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) ); 203 $nested_html_files = new RegexIterator( $nested_files, '/^.+\.html$/i', RecursiveRegexIterator::GET_MATCH ); 204 foreach ( $nested_html_files as $path => $file ) { 205 $path_list[] = $path; 206 } 207 } 208 return $path_list; 209 } 210 211 /** 212 * Retrieves the template file from the theme for a given slug. 213 * 214 * @access private 215 * @since 5.9.0 216 * 217 * @param string $template_type wp_template or wp_template_part. 218 * @param string $slug template slug. 219 * 220 * @return array|null Template. 221 */ 222 function _get_block_template_file( $template_type, $slug ) { 223 if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) { 224 return null; 225 } 226 227 $template_base_paths = array( 228 'wp_template' => 'block-templates', 229 'wp_template_part' => 'block-template-parts', 230 ); 231 $themes = array( 232 get_stylesheet() => get_stylesheet_directory(), 233 get_template() => get_template_directory(), 234 ); 235 foreach ( $themes as $theme_slug => $theme_dir ) { 236 $file_path = $theme_dir . '/' . $template_base_paths[ $template_type ] . '/' . $slug . '.html'; 237 if ( file_exists( $file_path ) ) { 238 $new_template_item = array( 239 'slug' => $slug, 240 'path' => $file_path, 241 'theme' => $theme_slug, 242 'type' => $template_type, 243 ); 244 245 if ( 'wp_template_part' === $template_type ) { 246 return _add_block_template_part_area_info( $new_template_item ); 247 } 248 249 if ( 'wp_template' === $template_type ) { 250 return _add_block_template_info( $new_template_item ); 251 } 252 253 return $new_template_item; 254 } 255 } 256 257 return null; 258 } 259 260 /** 261 * Retrieves the template files from the theme. 262 * 263 * @access private 264 * @since 5.9.0 265 * 266 * @param string $template_type wp_template or wp_template_part. 267 * 268 * @return array Template. 269 */ 270 function _get_block_templates_files( $template_type ) { 271 if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) { 272 return null; 273 } 274 275 $template_base_paths = array( 276 'wp_template' => 'block-templates', 277 'wp_template_part' => 'block-template-parts', 278 ); 279 $themes = array( 280 get_stylesheet() => get_stylesheet_directory(), 281 get_template() => get_template_directory(), 282 ); 283 284 $template_files = array(); 285 foreach ( $themes as $theme_slug => $theme_dir ) { 286 $theme_template_files = _get_block_templates_paths( $theme_dir . '/' . $template_base_paths[ $template_type ] ); 287 foreach ( $theme_template_files as $template_file ) { 288 $template_base_path = $template_base_paths[ $template_type ]; 289 $template_slug = substr( 290 $template_file, 291 // Starting position of slug. 292 strpos( $template_file, $template_base_path . DIRECTORY_SEPARATOR ) + 1 + strlen( $template_base_path ), 293 // Subtract ending '.html'. 294 -5 295 ); 296 $new_template_item = array( 297 'slug' => $template_slug, 298 'path' => $template_file, 299 'theme' => $theme_slug, 300 'type' => $template_type, 301 ); 302 303 if ( 'wp_template_part' === $template_type ) { 304 $template_files[] = _add_block_template_part_area_info( $new_template_item ); 305 } 306 307 if ( 'wp_template' === $template_type ) { 308 $template_files[] = _add_block_template_info( $new_template_item ); 309 } 310 } 311 } 312 313 return $template_files; 314 } 315 316 /** 317 * Attempts to add custom template information to the template item. 318 * 319 * @access private 320 * @since 5.9.0 321 * 322 * @param array $template_item Template to add information to (requires 'slug' field). 323 * @return array Template 324 */ 325 function _add_block_template_info( $template_item ) { 326 if ( ! WP_Theme_JSON_Resolver::theme_has_support() ) { 327 return $template_item; 328 } 329 330 $theme_data = WP_Theme_JSON_Resolver::get_theme_data()->get_custom_templates(); 331 if ( isset( $theme_data[ $template_item['slug'] ] ) ) { 332 $template_item['title'] = $theme_data[ $template_item['slug'] ]['title']; 333 $template_item['postTypes'] = $theme_data[ $template_item['slug'] ]['postTypes']; 334 } 335 336 return $template_item; 337 } 338 339 /** 340 * Attempts to add the template part's area information to the input template. 341 * 342 * @access private 343 * @since 5.9.0 344 * 345 * @param array $template_info Template to add information to (requires 'type' and 'slug' fields). 346 * 347 * @return array Template. 348 */ 349 function _add_block_template_part_area_info( $template_info ) { 350 if ( WP_Theme_JSON_Resolver::theme_has_support() ) { 351 $theme_data = WP_Theme_JSON_Resolver::get_theme_data()->get_template_parts(); 352 } 353 354 if ( isset( $theme_data[ $template_info['slug'] ]['area'] ) ) { 355 $template_info['title'] = $theme_data[ $template_info['slug'] ]['title']; 356 $template_info['area'] = _filter_block_template_part_area( $theme_data[ $template_info['slug'] ]['area'] ); 357 } else { 358 $template_info['area'] = WP_TEMPLATE_PART_AREA_UNCATEGORIZED; 359 } 360 361 return $template_info; 362 } 363 364 /** 365 * Returns an array containing the references of 366 * the passed blocks and their inner blocks. 367 * 368 * @access private 369 * @since 5.9.0 370 * 371 * @param array $blocks array of blocks. 372 * 373 * @return array block references to the passed blocks and their inner blocks. 374 */ 375 function _flatten_blocks( &$blocks ) { 376 $all_blocks = array(); 377 $queue = array(); 378 foreach ( $blocks as &$block ) { 379 $queue[] = &$block; 380 } 381 382 while ( count( $queue ) > 0 ) { 383 $block = &$queue[0]; 384 array_shift( $queue ); 385 $all_blocks[] = &$block; 386 387 if ( ! empty( $block['innerBlocks'] ) ) { 388 foreach ( $block['innerBlocks'] as &$inner_block ) { 389 $queue[] = &$inner_block; 390 } 391 } 392 } 393 394 return $all_blocks; 395 } 396 397 /** 398 * Parses wp_template content and injects the current theme's 399 * stylesheet as a theme attribute into each wp_template_part 400 * 401 * @access private 402 * @since 5.9.0 403 * 404 * @param string $template_content serialized wp_template content. 405 * 406 * @return string Updated wp_template content. 407 */ 408 function _inject_theme_attribute_in_block_template_content( $template_content ) { 409 $has_updated_content = false; 410 $new_content = ''; 411 $template_blocks = parse_blocks( $template_content ); 412 413 $blocks = _flatten_blocks( $template_blocks ); 414 foreach ( $blocks as &$block ) { 415 if ( 416 'core/template-part' === $block['blockName'] && 417 ! isset( $block['attrs']['theme'] ) 418 ) { 419 $block['attrs']['theme'] = wp_get_theme()->get_stylesheet(); 420 $has_updated_content = true; 421 } 422 } 423 424 if ( $has_updated_content ) { 425 foreach ( $template_blocks as &$block ) { 426 $new_content .= serialize_block( $block ); 427 } 428 429 return $new_content; 430 } 431 432 return $template_content; 433 } 434 435 /** 436 * Build a unified template object based on a theme file. 437 * 438 * @access private 439 * @since 5.9.0 440 * 441 * @param array $template_file Theme file. 442 * @param array $template_type wp_template or wp_template_part. 443 * 444 * @return WP_Block_Template Template. 445 */ 446 function _build_block_template_result_from_file( $template_file, $template_type ) { 447 $default_template_types = get_default_block_template_types(); 448 $template_content = file_get_contents( $template_file['path'] ); 449 $theme = wp_get_theme()->get_stylesheet(); 450 451 $template = new WP_Block_Template(); 452 $template->id = $theme . '//' . $template_file['slug']; 453 $template->theme = $theme; 454 $template->content = _inject_theme_attribute_in_block_template_content( $template_content ); 455 $template->slug = $template_file['slug']; 456 $template->source = 'theme'; 457 $template->type = $template_type; 458 $template->title = ! empty( $template_file['title'] ) ? $template_file['title'] : $template_file['slug']; 459 $template->status = 'publish'; 460 $template->has_theme_file = true; 461 $template->is_custom = true; 462 463 if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) { 464 $template->description = $default_template_types[ $template_file['slug'] ]['description']; 465 $template->title = $default_template_types[ $template_file['slug'] ]['title']; 466 $template->is_custom = false; 467 } 468 469 if ( 'wp_template' === $template_type && isset( $template_file['postTypes'] ) ) { 470 $template->post_types = $template_file['postTypes']; 471 } 472 473 if ( 'wp_template_part' === $template_type && isset( $template_file['area'] ) ) { 474 $template->area = $template_file['area']; 475 } 476 477 return $template; 478 } 479 9 480 /** 10 481 * Build a unified template object based a post Object. 11 482 * 12 483 * @access private 13 * @since 5. 8.0484 * @since 5.9.0 14 485 * 15 486 * @param WP_Post $post Template post. … … 17 488 * @return WP_Block_Template|WP_Error Template. 18 489 */ 19 function _build_template_result_from_post( $post ) { 20 $terms = get_the_terms( $post, 'wp_theme' ); 490 function _build_block_template_result_from_post( $post ) { 491 $default_template_types = get_default_block_template_types(); 492 $terms = get_the_terms( $post, 'wp_theme' ); 21 493 22 494 if ( is_wp_error( $terms ) ) { … … 28 500 } 29 501 30 $theme = $terms[0]->name; 502 $theme = $terms[0]->name; 503 $has_theme_file = wp_get_theme()->get_stylesheet() === $theme && 504 null !== _get_block_template_file( $post->post_type, $post->post_name ); 31 505 32 506 $template = new WP_Block_Template(); … … 41 515 $template->title = $post->post_title; 42 516 $template->status = $post->post_status; 43 $template->has_theme_file = false; 517 $template->has_theme_file = $has_theme_file; 518 $template->is_custom = true; 519 520 if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) { 521 $template->is_custom = false; 522 } 523 524 if ( 'wp_template_part' === $post->post_type ) { 525 $type_terms = get_the_terms( $post, 'wp_template_part_area' ); 526 if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) { 527 $template->area = $type_terms[0]->name; 528 } 529 } 44 530 45 531 return $template; … … 54 540 * Optional. Arguments to retrieve templates. 55 541 * 56 * @type array $slug__in List of slugs to include. 57 * @type int $wp_id Post ID of customized template. 542 * @type array $slug__in List of slugs to include. 543 * @type int $wp_id Post ID of customized template. 544 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for wp_template_part template type only). 545 * @type string $post_type Post type to get the templates for. 58 546 * } 59 * @param string $template_type Optional. The template type (post type). Default 'wp_template'. 60 * @return WP_Block_Template[] Block template objects. 547 * @param array $template_type wp_template or wp_template_part. 548 * 549 * @return array Templates. 61 550 */ 62 551 function get_block_templates( $query = array(), $template_type = 'wp_template' ) { 552 /** 553 * Filters the block templates array before the query takes place. 554 * 555 * Return a non-null value to bypass the WordPress queries. 556 * 557 * @since 5.9 558 * 559 * @param WP_Block_Template[]|null $block_templates Return an array of block templates to short-circuit the default query, 560 * or null to allow WP to run it's normal queries. 561 * @param array $query { 562 * Optional. Arguments to retrieve templates. 563 * 564 * @type array $slug__in List of slugs to include. 565 * @type int $wp_id Post ID of customized template. 566 * @type string $post_type Post type to get the templates for. 567 * } 568 * @param array $template_type wp_template or wp_template_part. 569 */ 570 $templates = apply_filters( 'pre_get_block_templates', null, $query, $template_type ); 571 if ( ! is_null( $templates ) ) { 572 return $templates; 573 } 574 575 $post_type = isset( $query['post_type'] ) ? $query['post_type'] : ''; 63 576 $wp_query_args = array( 64 577 'post_status' => array( 'auto-draft', 'draft', 'publish' ), … … 75 588 ); 76 589 590 if ( 'wp_template_part' === $template_type && isset( $query['area'] ) ) { 591 $wp_query_args['tax_query'][] = array( 592 'taxonomy' => 'wp_template_part_area', 593 'field' => 'name', 594 'terms' => $query['area'], 595 ); 596 $wp_query_args['tax_query']['relation'] = 'AND'; 597 } 598 77 599 if ( isset( $query['slug__in'] ) ) { 78 600 $wp_query_args['post_name__in'] = $query['slug__in']; 79 601 } 80 602 81 // This is only needed for the regular templates CPT listing and editor.603 // This is only needed for the regular templates/template parts CPT listing and editor. 82 604 if ( isset( $query['wp_id'] ) ) { 83 605 $wp_query_args['p'] = $query['wp_id']; … … 89 611 $query_result = array(); 90 612 foreach ( $template_query->posts as $post ) { 91 $template = _build_template_result_from_post( $post ); 92 93 if ( ! is_wp_error( $template ) ) { 94 $query_result[] = $template; 95 } 96 } 97 98 return $query_result; 613 $template = _build_block_template_result_from_post( $post ); 614 615 if ( is_wp_error( $template ) ) { 616 continue; 617 } 618 619 if ( $post_type && ! $template->is_custom ) { 620 continue; 621 } 622 623 $query_result[] = $template; 624 } 625 626 if ( ! isset( $query['wp_id'] ) ) { 627 $template_files = _get_block_templates_files( $template_type ); 628 foreach ( $template_files as $template_file ) { 629 $template = _build_block_template_result_from_file( $template_file, $template_type ); 630 631 if ( $post_type && ! $template->is_custom ) { 632 continue; 633 } 634 635 if ( $post_type && 636 isset( $template->post_types ) && 637 ! in_array( $post_type, $template->post_types, true ) 638 ) { 639 continue; 640 } 641 642 $is_not_custom = false === array_search( 643 wp_get_theme()->get_stylesheet() . '//' . $template_file['slug'], 644 array_column( $query_result, 'id' ), 645 true 646 ); 647 $fits_slug_query = 648 ! isset( $query['slug__in'] ) || in_array( $template_file['slug'], $query['slug__in'], true ); 649 $fits_area_query = 650 ! isset( $query['area'] ) || $template_file['area'] === $query['area']; 651 $should_include = $is_not_custom && $fits_slug_query && $fits_area_query; 652 if ( $should_include ) { 653 $query_result[] = $template; 654 } 655 } 656 } 657 658 /** 659 * Filters the array of queried block templates array after they've been fetched. 660 * 661 * @since 5.9 662 * 663 * @param WP_Block_Template[] $query_result Array of found block templates. 664 * @param array $query { 665 * Optional. Arguments to retrieve templates. 666 * 667 * @type array $slug__in List of slugs to include. 668 * @type int $wp_id Post ID of customized template. 669 * } 670 * @param array $template_type wp_template or wp_template_part. 671 */ 672 return apply_filters( 'get_block_templates', $query_result, $query, $template_type ); 99 673 } 100 674 … … 105 679 * 106 680 * @param string $id Template unique identifier (example: theme_slug//template_slug). 107 * @param string $template_type Optional. The template type (post type). Default 'wp_template'. 681 * @param array $template_type Optional. Template type: `'wp_template'` or '`wp_template_part'`. 682 * Default `'wp_template'`. 683 * 108 684 * @return WP_Block_Template|null Template. 109 685 */ 110 686 function get_block_template( $id, $template_type = 'wp_template' ) { 687 /** 688 * Filters the block templates array before the query takes place. 689 * 690 * Return a non-null value to bypass the WordPress queries. 691 * 692 * @since 5.9.0 693 * 694 * @param WP_Block_Template|null $block_template Return block template object to short-circuit the default query, 695 * or null to allow WP to run its normal queries. 696 * @param string $id Template unique identifier (example: theme_slug//template_slug). 697 * @param array $template_type Template type: `'wp_template'` or '`wp_template_part'`. 698 */ 699 $block_template = apply_filters( 'pre_get_block_template', null, $id, $template_type ); 700 if ( ! is_null( $block_template ) ) { 701 return $block_template; 702 } 703 111 704 $parts = explode( '//', $id, 2 ); 112 705 if ( count( $parts ) < 2 ) { … … 132 725 133 726 if ( count( $posts ) > 0 ) { 134 $template = _build_ template_result_from_post( $posts[0] );727 $template = _build_block_template_result_from_post( $posts[0] ); 135 728 136 729 if ( ! is_wp_error( $template ) ) { … … 139 732 } 140 733 141 return null; 142 } 734 $block_template = get_block_file_template( $id, $template_type ); 735 736 /** 737 * Filters the array of queried block templates array after they've been fetched. 738 * 739 * @since 5.9 740 * 741 * @param WP_Block_Template $block_template The found block template. 742 * @param string $id Template unique identifier (example: theme_slug//template_slug). 743 * @param array $template_type Template type: `'wp_template'` or '`wp_template_part'`. 744 */ 745 return apply_filters( 'get_block_template', $block_template, $id, $template_type ); 746 } 747 748 /** 749 * Retrieves a single unified template object using its id. 750 * 751 * @since 5.9.0 752 * 753 * @param string $id Template unique identifier (example: theme_slug//template_slug). 754 * @param array $template_type Optional. Template type: `'wp_template'` or '`wp_template_part'`. 755 * Default `'wp_template'`. 756 */ 757 function get_block_file_template( $id, $template_type = 'wp_template' ) { 758 /** 759 * Filters the block templates array before the query takes place. 760 * 761 * Return a non-null value to bypass the WordPress queries. 762 * 763 * 764 * @since 5.9.0 765 * 766 * @param WP_Block_Template|null $block_template Return block template object to short-circuit the default query, 767 * or null to allow WP to run its normal queries. 768 * @param string $id Template unique identifier (example: theme_slug//template_slug). 769 * @param array $template_type Template type: `'wp_template'` or '`wp_template_part'`. 770 */ 771 $block_template = apply_filters( 'pre_get_block_file_template', null, $id, $template_type ); 772 if ( ! is_null( $block_template ) ) { 773 return $block_template; 774 } 775 776 $parts = explode( '//', $id, 2 ); 777 if ( count( $parts ) < 2 ) { 778 /** This filter is documented at the end of this function */ 779 return apply_filters( 'get_block_file_template', null, $id, $template_type ); 780 } 781 list( $theme, $slug ) = $parts; 782 783 if ( wp_get_theme()->get_stylesheet() !== $theme ) { 784 /** This filter is documented at the end of this function */ 785 return apply_filters( 'get_block_file_template', null, $id, $template_type ); 786 } 787 788 $template_file = _get_block_template_file( $template_type, $slug ); 789 if ( null === $template_file ) { 790 /** This filter is documented at the end of this function */ 791 return apply_filters( 'get_block_file_template', null, $id, $template_type ); 792 } 793 794 $block_template = _build_block_template_result_from_file( $template_file, $template_type ); 795 796 /** 797 * Filters the array of queried block templates array after they've been fetched. 798 * 799 * @since 5.9.0 800 * 801 * @param WP_Block_Template $block_template The found block template. 802 * @param string $id Template unique identifier (example: theme_slug//template_slug). 803 * @param array $template_type Template type: `'wp_template'` or '`wp_template_part'`. 804 */ 805 return apply_filters( 'get_block_file_template', $block_template, $id, $template_type ); 806 } 807 808 /** 809 * Print a template-part. 810 * 811 * @since 5.9.0 812 * 813 * @param string $part The template-part to print. Use "header" or "footer". 814 */ 815 function block_template_part( $part ) { 816 $template_part = get_block_template( get_stylesheet() . '//' . $part, 'wp_template_part' ); 817 if ( ! $template_part || empty( $template_part->content ) ) { 818 return; 819 } 820 echo do_blocks( $template_part->content ); 821 } 822 823 /** 824 * Print the header template-part. 825 * 826 * @since 5.9.0 827 */ 828 function block_header_area() { 829 block_template_part( 'header' ); 830 } 831 832 /** 833 * Print the footer template-part. 834 * 835 * @since 5.9.0 836 */ 837 function block_footer_area() { 838 block_template_part( 'footer' ); 839 } -
trunk/src/wp-includes/blocks.php
r52057 r52062 1155 1155 * It's used in QueryPaginationNext and QueryPaginationPrevious blocks. 1156 1156 * 1157 * @since 5.9.0 1158 * 1157 1159 * @param WP_Block $block Block instance. 1158 1160 * @param boolean $is_next Flag for hanlding `next/previous` blocks. -
trunk/src/wp-includes/blocks/index.php
r51176 r52062 38 38 require ABSPATH . WPINC . '/blocks/social-link.php'; 39 39 require ABSPATH . WPINC . '/blocks/tag-cloud.php'; 40 require ABSPATH . WPINC . '/blocks/template-part.php'; 40 41 41 42 /** -
trunk/src/wp-includes/class-wp-block-template.php
r51003 r52062 101 101 */ 102 102 public $has_theme_file; 103 104 /** 105 * Whether a template is a custom template. 106 * 107 * @since 5.9.0 108 * 109 * @var boolean 110 */ 111 public $is_custom = true; 103 112 } -
trunk/src/wp-includes/class-wp-theme-json.php
r52049 r52062 602 602 public function get_custom_templates() { 603 603 $custom_templates = array(); 604 if ( ! isset( $this->theme_json['customTemplates'] ) ) {604 if ( ! isset( $this->theme_json['customTemplates'] ) || ! is_array( $this->theme_json['customTemplates'] ) ) { 605 605 return $custom_templates; 606 606 } … … 626 626 public function get_template_parts() { 627 627 $template_parts = array(); 628 if ( ! isset( $this->theme_json['templateParts'] ) ) {628 if ( ! isset( $this->theme_json['templateParts'] ) || ! is_array( $this->theme_json['templateParts'] ) ) { 629 629 return $template_parts; 630 630 } -
trunk/src/wp-includes/default-filters.php
r52049 r52062 665 665 add_filter( 'render_block_context', '_block_template_render_without_post_block_context' ); 666 666 add_filter( 'pre_wp_unique_post_slug', 'wp_filter_wp_template_unique_post_slug', 10, 5 ); 667 add_action( 'save_post_wp_template_part', 'wp_set_unique_slug_on_create_template_part' ); 667 668 add_action( 'wp_footer', 'the_block_template_skip_link' ); 668 669 add_action( 'setup_theme', 'wp_enable_block_templates' ); -
trunk/src/wp-includes/post.php
r52045 r52062 356 356 ), 357 357 'map_meta_cap' => true, 358 'supports' => array( 359 'title', 360 'slug', 361 'excerpt', 362 'editor', 363 'revisions', 364 ), 365 ) 366 ); 367 368 register_post_type( 369 'wp_template_part', 370 array( 371 'labels' => array( 372 'name' => __( 'Template Parts' ), 373 'singular_name' => __( 'Template Part' ), 374 'add_new' => _x( 'Add New', 'Template Part' ), 375 'add_new_item' => __( 'Add New Template Part' ), 376 'new_item' => __( 'New Template Part' ), 377 'edit_item' => __( 'Edit Template Part' ), 378 'view_item' => __( 'View Template Part' ), 379 'all_items' => __( 'All Template Parts' ), 380 'search_items' => __( 'Search Template Parts' ), 381 'parent_item_colon' => __( 'Parent Template Part:' ), 382 'not_found' => __( 'No template parts found.' ), 383 'not_found_in_trash' => __( 'No template parts found in Trash.' ), 384 'archives' => __( 'Template part archives' ), 385 'insert_into_item' => __( 'Insert into template part' ), 386 'uploaded_to_this_item' => __( 'Uploaded to this template part' ), 387 'filter_items_list' => __( 'Filter template parts list' ), 388 'items_list_navigation' => __( 'Template parts list navigation' ), 389 'items_list' => __( 'Template parts list' ), 390 ), 391 'description' => __( 'Template parts to include in your templates.' ), 392 'public' => false, 393 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 394 'has_archive' => false, 395 'show_ui' => false, 396 'show_in_menu' => false, 397 'show_in_rest' => true, 398 'rewrite' => false, 399 'rest_base' => 'template-parts', 400 'rest_controller_class' => 'WP_REST_Templates_Controller', 401 'map_meta_cap' => true, 402 'capabilities' => array( 403 'create_posts' => 'edit_theme_options', 404 'delete_posts' => 'edit_theme_options', 405 'delete_others_posts' => 'edit_theme_options', 406 'delete_private_posts' => 'edit_theme_options', 407 'delete_published_posts' => 'edit_theme_options', 408 'edit_posts' => 'edit_theme_options', 409 'edit_others_posts' => 'edit_theme_options', 410 'edit_private_posts' => 'edit_theme_options', 411 'edit_published_posts' => 'edit_theme_options', 412 'publish_posts' => 'edit_theme_options', 413 'read' => 'edit_theme_options', 414 'read_private_posts' => 'edit_theme_options', 415 ), 358 416 'supports' => array( 359 417 'title', -
trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php
r51962 r52062 157 157 $query['area'] = $request['area']; 158 158 } 159 if ( isset( $request['post_type'] ) ) { 160 $query['post_type'] = $request['post_type']; 161 } 159 162 160 163 $templates = array(); … … 188 191 */ 189 192 public function get_item( $request ) { 190 $template = get_block_template( $request['id'], $this->post_type ); 193 if ( isset( $request['source'] ) && 'theme' === $request['source'] ) { 194 $template = get_block_file_template( $request['id'], $this->post_type ); 195 } else { 196 $template = get_block_template( $request['id'], $this->post_type ); 197 } 191 198 192 199 if ( ! $template ) { … … 221 228 if ( ! $template ) { 222 229 return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) ); 230 } 231 232 if ( isset( $request['source'] ) && 'theme' === $request['source'] ) { 233 wp_delete_post( $template->wp_id, true ); 234 return $this->prepare_item_for_response( get_block_file_template( $request['id'], $this->post_type ), $request ); 223 235 } 224 236 … … 396 408 } 397 409 410 if ( 'wp_template_part' === $this->post_type ) { 411 if ( isset( $request['area'] ) ) { 412 $changes->tax_input['wp_template_part_area'] = _filter_block_template_part_area( $request['area'] ); 413 } elseif ( null !== $template && 'custom' !== $template->source && $template->area ) { 414 $changes->tax_input['wp_template_part_area'] = _filter_block_template_part_area( $template->area ); 415 } elseif ( ! $template->area ) { 416 $changes->tax_input['wp_template_part_area'] = WP_TEMPLATE_PART_AREA_UNCATEGORIZED; 417 } 418 } 419 398 420 return $changes; 399 421 } … … 503 525 * 504 526 * @since 5.8.0 527 * @since 5.9.0 Added `'area'` and `'post_type'`. 505 528 * 506 529 * @return array Collection parameters. … … 508 531 public function get_collection_params() { 509 532 return array( 510 'context' => $this->get_context_param(),511 'wp_id' => array(533 'context' => $this->get_context_param(), 534 'wp_id' => array( 512 535 'description' => __( 'Limit to the specified post id.' ), 513 536 'type' => 'integer', 514 537 ), 538 'area' => array( 539 'description' => __( 'Limit to the specified template part area.' ), 540 'type' => 'string', 541 ), 542 'post_type' => array( 543 'description' => __( 'Post type to get the templates for.' ), 544 'type' => 'string', 545 ), 515 546 ); 516 547 } … … 520 551 * 521 552 * @since 5.8.0 553 * @since 5.9.0 Added `'area'`. 522 554 * 523 555 * @return array Item schema data. … … 597 629 ); 598 630 631 if ( 'wp_template_part' === $this->post_type ) { 632 $schema['properties']['area'] = array( 633 'description' => __( 'Where the template part is intended for use (header, footer, etc.)' ), 634 'type' => 'string', 635 'context' => array( 'embed', 'view', 'edit' ), 636 ); 637 } 638 599 639 $this->schema = $schema; 600 640 -
trunk/src/wp-includes/taxonomy.php
r52041 r52062 19 19 * 20 20 * @since 2.8.0 21 * @since 5.9.0 Added `'wp_template_part_area'` taxonomy. 21 22 * 22 23 * @global WP_Rewrite $wp_rewrite WordPress rewrite component. … … 176 177 register_taxonomy( 177 178 'wp_theme', 178 array( 'wp_template', 'wp_ global_styles' ),179 array( 'wp_template', 'wp_template_part', 'wp_global_styles' ), 179 180 array( 180 181 'public' => false, … … 183 184 'name' => __( 'Themes' ), 184 185 'singular_name' => __( 'Theme' ), 186 ), 187 'query_var' => false, 188 'rewrite' => false, 189 'show_ui' => false, 190 '_builtin' => true, 191 'show_in_nav_menus' => false, 192 'show_in_rest' => false, 193 ) 194 ); 195 196 register_taxonomy( 197 'wp_template_part_area', 198 array( 'wp_template_part' ), 199 array( 200 'public' => false, 201 'hierarchical' => false, 202 'labels' => array( 203 'name' => __( 'Template Part Areas' ), 204 'singular_name' => __( 'Template Part Area' ), 185 205 ), 186 206 'query_var' => false, -
trunk/src/wp-includes/theme-templates.php
r51199 r52062 1 1 <?php 2 3 /** 4 * Sets a custom slug when creating auto-draft template parts. 5 * 6 * This is only needed for auto-drafts created by the regular WP editor. 7 * If this page is to be removed, this won't be necessary. 8 * 9 * @since 5.9.0 10 * 11 */ 12 function wp_set_unique_slug_on_create_template_part( $post_id ) { 13 $post = get_post( $post_id ); 14 if ( 'auto-draft' !== $post->post_status ) { 15 return; 16 } 17 18 if ( ! $post->post_name ) { 19 wp_update_post( 20 array( 21 'ID' => $post_id, 22 'post_name' => 'custom_slug_' . uniqid(), 23 ) 24 ); 25 } 26 27 $terms = get_the_terms( $post_id, 'wp_theme' ); 28 if ( ! is_array( $terms ) || ! count( $terms ) ) { 29 wp_set_post_terms( $post_id, wp_get_theme()->get_stylesheet(), 'wp_theme' ); 30 } 31 } 2 32 3 33 /** … … 15 45 */ 16 46 function wp_filter_wp_template_unique_post_slug( $override_slug, $slug, $post_ID, $post_status, $post_type ) { 17 if ( 'wp_template' !== $post_type ) {47 if ( 'wp_template' !== $post_type && 'wp_template_part' !== $post_type ) { 18 48 return $override_slug; 19 49 } -
trunk/tests/phpunit/includes/functions.php
r51105 r52062 338 338 remove_action( 'init', 'register_core_block_types_from_metadata' ); 339 339 remove_action( 'init', 'register_block_core_legacy_widget' ); 340 remove_action( 'init', 'register_block_core_template_part' ); 340 341 } 341 342 tests_add_filter( 'init', '_unhook_block_registration', 1000 ); -
trunk/tests/phpunit/tests/block-template-utils.php
r52010 r52062 7 7 8 8 /** 9 * Tests for the Block Template Loaderabstraction layer.9 * Tests for the Block Templates abstraction layer. 10 10 */ 11 11 class Block_Template_Utils_Test extends WP_UnitTestCase { 12 12 private static $post; 13 private static $template_part_post; 13 14 14 15 public static function wpSetUpBeforeClass() { 16 // We may need a block theme. 17 // switch_theme( 'tt1-blocks' ); 18 19 // Set up a template post corresponding to a different theme. 20 // We do this to ensure resolution and slug creation works as expected, 21 // even with another post of that same name present for another theme. 22 $args = array( 23 'post_type' => 'wp_template', 24 'post_name' => 'my_template', 25 'post_title' => 'My Template', 26 'post_content' => 'Content', 27 'post_excerpt' => 'Description of my template', 28 'tax_input' => array( 29 'wp_theme' => array( 30 'this-theme-should-not-resolve', 31 ), 32 ), 33 ); 34 self::$post = self::factory()->post->create_and_get( $args ); 35 wp_set_post_terms( self::$post->ID, 'this-theme-should-not-resolve', 'wp_theme' ); 36 15 37 // Set up template post. 16 38 $args = array( … … 28 50 self::$post = self::factory()->post->create_and_get( $args ); 29 51 wp_set_post_terms( self::$post->ID, get_stylesheet(), 'wp_theme' ); 52 53 // Set up template part post. 54 $template_part_args = array( 55 'post_type' => 'wp_template_part', 56 'post_name' => 'my_template_part', 57 'post_title' => 'My Template Part', 58 'post_content' => 'Content', 59 'post_excerpt' => 'Description of my template part', 60 'tax_input' => array( 61 'wp_theme' => array( 62 get_stylesheet(), 63 ), 64 'wp_template_part_area' => array( 65 WP_TEMPLATE_PART_AREA_HEADER, 66 ), 67 ), 68 ); 69 self::$template_part_post = self::factory()->post->create_and_get( $template_part_args ); 70 wp_set_post_terms( self::$template_part_post->ID, WP_TEMPLATE_PART_AREA_HEADER, 'wp_template_part_area' ); 71 wp_set_post_terms( self::$template_part_post->ID, get_stylesheet(), 'wp_theme' ); 30 72 } 31 73 … … 35 77 36 78 public function test_build_template_result_from_post() { 37 $template = _build_ template_result_from_post(79 $template = _build_block_template_result_from_post( 38 80 self::$post, 39 81 'wp_template' … … 41 83 42 84 $this->assertNotWPError( $template ); 43 $this->assertSame( get_stylesheet() . '//my_template', $template->id ); 44 $this->assertSame( get_stylesheet(), $template->theme ); 45 $this->assertSame( 'my_template', $template->slug ); 46 $this->assertSame( 'publish', $template->status ); 47 $this->assertSame( 'custom', $template->source ); 48 $this->assertSame( 'My Template', $template->title ); 49 $this->assertSame( 'Description of my template', $template->description ); 50 $this->assertSame( 'wp_template', $template->type ); 85 $this->assertEquals( get_stylesheet() . '//my_template', $template->id ); 86 $this->assertEquals( get_stylesheet(), $template->theme ); 87 $this->assertEquals( 'my_template', $template->slug ); 88 $this->assertEquals( 'publish', $template->status ); 89 $this->assertEquals( 'custom', $template->source ); 90 $this->assertEquals( 'My Template', $template->title ); 91 $this->assertEquals( 'Description of my template', $template->description ); 92 $this->assertEquals( 'wp_template', $template->type ); 93 94 // Test template parts. 95 $template_part = _build_block_template_result_from_post( 96 self::$template_part_post, 97 'wp_template_part' 98 ); 99 $this->assertNotWPError( $template_part ); 100 $this->assertEquals( get_stylesheet() . '//my_template_part', $template_part->id ); 101 $this->assertEquals( get_stylesheet(), $template_part->theme ); 102 $this->assertEquals( 'my_template_part', $template_part->slug ); 103 $this->assertEquals( 'publish', $template_part->status ); 104 $this->assertEquals( 'custom', $template_part->source ); 105 $this->assertEquals( 'My Template Part', $template_part->title ); 106 $this->assertEquals( 'Description of my template part', $template_part->description ); 107 $this->assertEquals( 'wp_template_part', $template_part->type ); 108 $this->assertEquals( WP_TEMPLATE_PART_AREA_HEADER, $template_part->area ); 109 } 110 111 function test_build_block_template_result_from_file() { 112 $template = _build_block_template_result_from_file( 113 array( 114 'slug' => 'single', 115 'path' => __DIR__ . '/../data/templates/template.html', 116 ), 117 'wp_template' 118 ); 119 120 $this->assertEquals( get_stylesheet() . '//single', $template->id ); 121 $this->assertEquals( get_stylesheet(), $template->theme ); 122 $this->assertEquals( 'single', $template->slug ); 123 $this->assertEquals( 'publish', $template->status ); 124 $this->assertEquals( 'theme', $template->source ); 125 $this->assertEquals( 'Single Post', $template->title ); 126 $this->assertEquals( 'Template used to display a single blog post.', $template->description ); 127 $this->assertEquals( 'wp_template', $template->type ); 128 129 // Test template parts. 130 $template_part = _build_block_template_result_from_file( 131 array( 132 'slug' => 'header', 133 'path' => __DIR__ . '/../data/templates/template.html', 134 'area' => WP_TEMPLATE_PART_AREA_HEADER, 135 ), 136 'wp_template_part' 137 ); 138 $this->assertEquals( get_stylesheet() . '//header', $template_part->id ); 139 $this->assertEquals( get_stylesheet(), $template_part->theme ); 140 $this->assertEquals( 'header', $template_part->slug ); 141 $this->assertEquals( 'publish', $template_part->status ); 142 $this->assertEquals( 'theme', $template_part->source ); 143 $this->assertEquals( 'header', $template_part->title ); 144 $this->assertEquals( '', $template_part->description ); 145 $this->assertEquals( 'wp_template_part', $template_part->type ); 146 $this->assertEquals( WP_TEMPLATE_PART_AREA_HEADER, $template_part->area ); 147 } 148 149 function test_inject_theme_attribute_in_block_template_content() { 150 $theme = get_stylesheet(); 151 $content_without_theme_attribute = '<!-- wp:template-part {"slug":"header","align":"full", "tagName":"header","className":"site-header"} /-->'; 152 $template_content = _inject_theme_attribute_in_block_template_content( 153 $content_without_theme_attribute, 154 $theme 155 ); 156 $expected = sprintf( 157 '<!-- wp:template-part {"slug":"header","align":"full","tagName":"header","className":"site-header","theme":"%s"} /-->', 158 get_stylesheet() 159 ); 160 $this->assertEquals( $expected, $template_content ); 161 162 $content_without_theme_attribute_nested = '<!-- wp:group --><!-- wp:template-part {"slug":"header","align":"full", "tagName":"header","className":"site-header"} /--><!-- /wp:group -->'; 163 $template_content = _inject_theme_attribute_in_block_template_content( 164 $content_without_theme_attribute_nested, 165 $theme 166 ); 167 $expected = sprintf( 168 '<!-- wp:group --><!-- wp:template-part {"slug":"header","align":"full","tagName":"header","className":"site-header","theme":"%s"} /--><!-- /wp:group -->', 169 get_stylesheet() 170 ); 171 $this->assertEquals( $expected, $template_content ); 172 173 // Does not inject theme when there is an existing theme attribute. 174 $content_with_existing_theme_attribute = '<!-- wp:template-part {"slug":"header","theme":"fake-theme","align":"full", "tagName":"header","className":"site-header"} /-->'; 175 $template_content = _inject_theme_attribute_in_block_template_content( 176 $content_with_existing_theme_attribute, 177 $theme 178 ); 179 $this->assertEquals( $content_with_existing_theme_attribute, $template_content ); 180 181 // Does not inject theme when there is no template part. 182 $content_with_no_template_part = '<!-- wp:post-content /-->'; 183 $template_content = _inject_theme_attribute_in_block_template_content( 184 $content_with_no_template_part, 185 $theme 186 ); 187 $this->assertEquals( $content_with_no_template_part, $template_content ); 188 } 189 190 /** 191 * Should retrieve the template from the theme files. 192 */ 193 function test_get_block_template_from_file() { 194 $this->markTestIncomplete(); 195 // Requires switching to a block theme. 196 /* $id = get_stylesheet() . '//' . 'index'; 197 $template = get_block_template( $id, 'wp_template' ); 198 $this->assertEquals( $id, $template->id ); 199 $this->assertEquals( get_stylesheet(), $template->theme ); 200 $this->assertEquals( 'index', $template->slug ); 201 $this->assertEquals( 'publish', $template->status ); 202 $this->assertEquals( 'theme', $template->source ); 203 $this->assertEquals( 'wp_template', $template->type ); 204 205 // Test template parts. 206 $id = get_stylesheet() . '//' . 'header'; 207 $template = get_block_template( $id, 'wp_template_part' ); 208 $this->assertEquals( $id, $template->id ); 209 $this->assertEquals( get_stylesheet(), $template->theme ); 210 $this->assertEquals( 'header', $template->slug ); 211 $this->assertEquals( 'publish', $template->status ); 212 $this->assertEquals( 'theme', $template->source ); 213 $this->assertEquals( 'wp_template_part', $template->type ); 214 $this->assertEquals( WP_TEMPLATE_PART_AREA_HEADER, $template->area ); 215 */ 51 216 } 52 217 … … 57 222 $id = get_stylesheet() . '//' . 'my_template'; 58 223 $template = get_block_template( $id, 'wp_template' ); 59 $this->assertSame( $id, $template->id ); 60 $this->assertSame( get_stylesheet(), $template->theme ); 61 $this->assertSame( 'my_template', $template->slug ); 62 $this->assertSame( 'publish', $template->status ); 63 $this->assertSame( 'custom', $template->source ); 64 $this->assertSame( 'wp_template', $template->type ); 224 $this->assertEquals( $id, $template->id ); 225 $this->assertEquals( get_stylesheet(), $template->theme ); 226 $this->assertEquals( 'my_template', $template->slug ); 227 $this->assertEquals( 'publish', $template->status ); 228 $this->assertEquals( 'custom', $template->source ); 229 $this->assertEquals( 'wp_template', $template->type ); 230 231 // Test template parts. 232 $id = get_stylesheet() . '//' . 'my_template_part'; 233 $template = get_block_template( $id, 'wp_template_part' ); 234 $this->assertEquals( $id, $template->id ); 235 $this->assertEquals( get_stylesheet(), $template->theme ); 236 $this->assertEquals( 'my_template_part', $template->slug ); 237 $this->assertEquals( 'publish', $template->status ); 238 $this->assertEquals( 'custom', $template->source ); 239 $this->assertEquals( 'wp_template_part', $template->type ); 240 $this->assertEquals( WP_TEMPLATE_PART_AREA_HEADER, $template->area ); 65 241 } 66 242 67 243 /** 68 * Should retrieve block templates .244 * Should retrieve block templates (file and CPT) 69 245 */ 70 246 public function test_get_block_templates() { … … 85 261 $this->assertContains( get_stylesheet() . '//' . 'my_template', $template_ids ); 86 262 263 // The result might change in a block theme. 264 // $this->assertContains( get_stylesheet() . '//' . 'index', $template_ids ); 265 87 266 // Filter by slug. 88 267 $templates = get_block_templates( array( 'slug__in' => array( 'my_template' ) ), 'wp_template' ); 89 268 $template_ids = get_template_ids( $templates ); 90 $this->assert Same( array( get_stylesheet() . '//' . 'my_template' ), $template_ids );269 $this->assertEquals( array( get_stylesheet() . '//' . 'my_template' ), $template_ids ); 91 270 92 271 // Filter by CPT ID. 93 272 $templates = get_block_templates( array( 'wp_id' => self::$post->ID ), 'wp_template' ); 94 273 $template_ids = get_template_ids( $templates ); 95 $this->assertSame( array( get_stylesheet() . '//' . 'my_template' ), $template_ids ); 274 $this->assertEquals( array( get_stylesheet() . '//' . 'my_template' ), $template_ids ); 275 276 // Filter template part by area. 277 // Requires a block theme. 278 /*$templates = get_block_templates( array( 'area' => WP_TEMPLATE_PART_AREA_HEADER ), 'wp_template_part' ); 279 $template_ids = get_template_ids( $templates ); 280 $this->assertEquals( 281 array( 282 get_stylesheet() . '//' . 'my_template_part', 283 get_stylesheet() . '//' . 'header', 284 ), 285 $template_ids 286 ); 287 */ 288 } 289 290 /** 291 * Should flatten nested blocks 292 */ 293 function test_flatten_blocks() { 294 $content_template_part_inside_group = '<!-- wp:group --><!-- wp:template-part {"slug":"header"} /--><!-- /wp:group -->'; 295 $blocks = parse_blocks( $content_template_part_inside_group ); 296 $actual = _flatten_blocks( $blocks ); 297 $expected = array( $blocks[0], $blocks[0]['innerBlocks'][0] ); 298 $this->assertEquals( $expected, $actual ); 299 300 $content_template_part_inside_group_inside_group = '<!-- wp:group --><!-- wp:group --><!-- wp:template-part {"slug":"header"} /--><!-- /wp:group --><!-- /wp:group -->'; 301 $blocks = parse_blocks( $content_template_part_inside_group_inside_group ); 302 $actual = _flatten_blocks( $blocks ); 303 $expected = array( $blocks[0], $blocks[0]['innerBlocks'][0], $blocks[0]['innerBlocks'][0]['innerBlocks'][0] ); 304 $this->assertEquals( $expected, $actual ); 305 306 $content_without_inner_blocks = '<!-- wp:group /-->'; 307 $blocks = parse_blocks( $content_without_inner_blocks ); 308 $actual = _flatten_blocks( $blocks ); 309 $expected = array( $blocks[0] ); 310 $this->assertEquals( $expected, $actual ); 96 311 } 97 312 } -
trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php
r52051 r52062 133 133 '/wp/v2/block-types/(?P<namespace>[a-zA-Z0-9_-]+)/(?P<name>[a-zA-Z0-9_-]+)', 134 134 '/wp/v2/settings', 135 '/wp/v2/template-parts', 136 '/wp/v2/template-parts/(?P<id>[\/\w-]+)', 137 '/wp/v2/template-parts/(?P<id>[\d]+)/autosaves', 138 '/wp/v2/template-parts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', 139 '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions', 140 '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)', 135 141 '/wp/v2/templates', 136 142 '/wp/v2/templates/(?P<id>[\/\w-]+)', -
trunk/tests/qunit/fixtures/wp-api-generated.js
r52051 r52062 4165 4165 "type": "integer", 4166 4166 "required": false 4167 }, 4168 "area": { 4169 "description": "Limit to the specified template part area.", 4170 "type": "string", 4171 "required": false 4172 }, 4173 "post_type": { 4174 "description": "Post type to get the templates for.", 4175 "type": "string", 4176 "required": false 4167 4177 } 4168 4178 } … … 4551 4561 }, 4552 4562 "/wp/v2/templates/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": { 4563 "namespace": "wp/v2", 4564 "methods": [ 4565 "GET" 4566 ], 4567 "endpoints": [ 4568 { 4569 "methods": [ 4570 "GET" 4571 ], 4572 "args": { 4573 "parent": { 4574 "description": "The ID for the parent of the autosave.", 4575 "type": "integer", 4576 "required": false 4577 }, 4578 "id": { 4579 "description": "The ID for the autosave.", 4580 "type": "integer", 4581 "required": false 4582 }, 4583 "context": { 4584 "description": "Scope under which the request is made; determines fields present in response.", 4585 "type": "string", 4586 "enum": [ 4587 "view", 4588 "embed", 4589 "edit" 4590 ], 4591 "default": "view", 4592 "required": false 4593 } 4594 } 4595 } 4596 ] 4597 }, 4598 "/wp/v2/template-parts": { 4599 "namespace": "wp/v2", 4600 "methods": [ 4601 "GET", 4602 "POST" 4603 ], 4604 "endpoints": [ 4605 { 4606 "methods": [ 4607 "GET" 4608 ], 4609 "args": { 4610 "context": { 4611 "description": "Scope under which the request is made; determines fields present in response.", 4612 "type": "string", 4613 "enum": [ 4614 "view", 4615 "embed", 4616 "edit" 4617 ], 4618 "required": false 4619 }, 4620 "wp_id": { 4621 "description": "Limit to the specified post id.", 4622 "type": "integer", 4623 "required": false 4624 }, 4625 "area": { 4626 "description": "Limit to the specified template part area.", 4627 "type": "string", 4628 "required": false 4629 }, 4630 "post_type": { 4631 "description": "Post type to get the templates for.", 4632 "type": "string", 4633 "required": false 4634 } 4635 } 4636 }, 4637 { 4638 "methods": [ 4639 "POST" 4640 ], 4641 "args": { 4642 "slug": { 4643 "description": "Unique slug identifying the template.", 4644 "type": "string", 4645 "minLength": 1, 4646 "pattern": "[a-zA-Z_\\-]+", 4647 "required": true 4648 }, 4649 "theme": { 4650 "description": "Theme identifier for the template.", 4651 "type": "string", 4652 "required": false 4653 }, 4654 "content": { 4655 "default": "", 4656 "description": "Content of template.", 4657 "type": [ 4658 "object", 4659 "string" 4660 ], 4661 "required": false 4662 }, 4663 "title": { 4664 "default": "", 4665 "description": "Title of template.", 4666 "type": [ 4667 "object", 4668 "string" 4669 ], 4670 "required": false 4671 }, 4672 "description": { 4673 "default": "", 4674 "description": "Description of template.", 4675 "type": "string", 4676 "required": false 4677 }, 4678 "status": { 4679 "default": "publish", 4680 "description": "Status of template.", 4681 "type": "string", 4682 "required": false 4683 }, 4684 "area": { 4685 "description": "Where the template part is intended for use (header, footer, etc.)", 4686 "type": "string", 4687 "required": false 4688 } 4689 } 4690 } 4691 ], 4692 "_links": { 4693 "self": [ 4694 { 4695 "href": "http://example.org/index.php?rest_route=/wp/v2/template-parts" 4696 } 4697 ] 4698 } 4699 }, 4700 "/wp/v2/template-parts/(?P<id>[\\/\\w-]+)": { 4701 "namespace": "wp/v2", 4702 "methods": [ 4703 "GET", 4704 "POST", 4705 "PUT", 4706 "PATCH", 4707 "DELETE" 4708 ], 4709 "endpoints": [ 4710 { 4711 "methods": [ 4712 "GET" 4713 ], 4714 "args": { 4715 "id": { 4716 "description": "The id of a template", 4717 "type": "string", 4718 "required": false 4719 } 4720 } 4721 }, 4722 { 4723 "methods": [ 4724 "POST", 4725 "PUT", 4726 "PATCH" 4727 ], 4728 "args": { 4729 "slug": { 4730 "description": "Unique slug identifying the template.", 4731 "type": "string", 4732 "minLength": 1, 4733 "pattern": "[a-zA-Z_\\-]+", 4734 "required": false 4735 }, 4736 "theme": { 4737 "description": "Theme identifier for the template.", 4738 "type": "string", 4739 "required": false 4740 }, 4741 "content": { 4742 "description": "Content of template.", 4743 "type": [ 4744 "object", 4745 "string" 4746 ], 4747 "required": false 4748 }, 4749 "title": { 4750 "description": "Title of template.", 4751 "type": [ 4752 "object", 4753 "string" 4754 ], 4755 "required": false 4756 }, 4757 "description": { 4758 "description": "Description of template.", 4759 "type": "string", 4760 "required": false 4761 }, 4762 "status": { 4763 "description": "Status of template.", 4764 "type": "string", 4765 "required": false 4766 }, 4767 "area": { 4768 "description": "Where the template part is intended for use (header, footer, etc.)", 4769 "type": "string", 4770 "required": false 4771 } 4772 } 4773 }, 4774 { 4775 "methods": [ 4776 "DELETE" 4777 ], 4778 "args": { 4779 "force": { 4780 "type": "boolean", 4781 "default": false, 4782 "description": "Whether to bypass Trash and force deletion.", 4783 "required": false 4784 } 4785 } 4786 } 4787 ] 4788 }, 4789 "/wp/v2/template-parts/(?P<parent>[\\d]+)/revisions": { 4790 "namespace": "wp/v2", 4791 "methods": [ 4792 "GET" 4793 ], 4794 "endpoints": [ 4795 { 4796 "methods": [ 4797 "GET" 4798 ], 4799 "args": { 4800 "parent": { 4801 "description": "The ID for the parent of the revision.", 4802 "type": "integer", 4803 "required": false 4804 }, 4805 "context": { 4806 "description": "Scope under which the request is made; determines fields present in response.", 4807 "type": "string", 4808 "enum": [ 4809 "view", 4810 "embed", 4811 "edit" 4812 ], 4813 "default": "view", 4814 "required": false 4815 }, 4816 "page": { 4817 "description": "Current page of the collection.", 4818 "type": "integer", 4819 "default": 1, 4820 "minimum": 1, 4821 "required": false 4822 }, 4823 "per_page": { 4824 "description": "Maximum number of items to be returned in result set.", 4825 "type": "integer", 4826 "minimum": 1, 4827 "maximum": 100, 4828 "required": false 4829 }, 4830 "search": { 4831 "description": "Limit results to those matching a string.", 4832 "type": "string", 4833 "required": false 4834 }, 4835 "exclude": { 4836 "description": "Ensure result set excludes specific IDs.", 4837 "type": "array", 4838 "items": { 4839 "type": "integer" 4840 }, 4841 "default": [], 4842 "required": false 4843 }, 4844 "include": { 4845 "description": "Limit result set to specific IDs.", 4846 "type": "array", 4847 "items": { 4848 "type": "integer" 4849 }, 4850 "default": [], 4851 "required": false 4852 }, 4853 "offset": { 4854 "description": "Offset the result set by a specific number of items.", 4855 "type": "integer", 4856 "required": false 4857 }, 4858 "order": { 4859 "description": "Order sort attribute ascending or descending.", 4860 "type": "string", 4861 "default": "desc", 4862 "enum": [ 4863 "asc", 4864 "desc" 4865 ], 4866 "required": false 4867 }, 4868 "orderby": { 4869 "description": "Sort collection by object attribute.", 4870 "type": "string", 4871 "default": "date", 4872 "enum": [ 4873 "date", 4874 "id", 4875 "include", 4876 "relevance", 4877 "slug", 4878 "include_slugs", 4879 "title" 4880 ], 4881 "required": false 4882 } 4883 } 4884 } 4885 ] 4886 }, 4887 "/wp/v2/template-parts/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": { 4888 "namespace": "wp/v2", 4889 "methods": [ 4890 "GET", 4891 "DELETE" 4892 ], 4893 "endpoints": [ 4894 { 4895 "methods": [ 4896 "GET" 4897 ], 4898 "args": { 4899 "parent": { 4900 "description": "The ID for the parent of the revision.", 4901 "type": "integer", 4902 "required": false 4903 }, 4904 "id": { 4905 "description": "Unique identifier for the revision.", 4906 "type": "integer", 4907 "required": false 4908 }, 4909 "context": { 4910 "description": "Scope under which the request is made; determines fields present in response.", 4911 "type": "string", 4912 "enum": [ 4913 "view", 4914 "embed", 4915 "edit" 4916 ], 4917 "default": "view", 4918 "required": false 4919 } 4920 } 4921 }, 4922 { 4923 "methods": [ 4924 "DELETE" 4925 ], 4926 "args": { 4927 "parent": { 4928 "description": "The ID for the parent of the revision.", 4929 "type": "integer", 4930 "required": false 4931 }, 4932 "id": { 4933 "description": "Unique identifier for the revision.", 4934 "type": "integer", 4935 "required": false 4936 }, 4937 "force": { 4938 "type": "boolean", 4939 "default": false, 4940 "description": "Required to be true, as revisions do not support trashing.", 4941 "required": false 4942 } 4943 } 4944 } 4945 ] 4946 }, 4947 "/wp/v2/template-parts/(?P<id>[\\d]+)/autosaves": { 4948 "namespace": "wp/v2", 4949 "methods": [ 4950 "GET", 4951 "POST" 4952 ], 4953 "endpoints": [ 4954 { 4955 "methods": [ 4956 "GET" 4957 ], 4958 "args": { 4959 "parent": { 4960 "description": "The ID for the parent of the autosave.", 4961 "type": "integer", 4962 "required": false 4963 }, 4964 "context": { 4965 "description": "Scope under which the request is made; determines fields present in response.", 4966 "type": "string", 4967 "enum": [ 4968 "view", 4969 "embed", 4970 "edit" 4971 ], 4972 "default": "view", 4973 "required": false 4974 } 4975 } 4976 }, 4977 { 4978 "methods": [ 4979 "POST" 4980 ], 4981 "args": { 4982 "parent": { 4983 "description": "The ID for the parent of the autosave.", 4984 "type": "integer", 4985 "required": false 4986 }, 4987 "slug": { 4988 "description": "Unique slug identifying the template.", 4989 "type": "string", 4990 "minLength": 1, 4991 "pattern": "[a-zA-Z_\\-]+", 4992 "required": false 4993 }, 4994 "theme": { 4995 "description": "Theme identifier for the template.", 4996 "type": "string", 4997 "required": false 4998 }, 4999 "content": { 5000 "description": "Content of template.", 5001 "type": [ 5002 "object", 5003 "string" 5004 ], 5005 "required": false 5006 }, 5007 "title": { 5008 "description": "Title of template.", 5009 "type": [ 5010 "object", 5011 "string" 5012 ], 5013 "required": false 5014 }, 5015 "description": { 5016 "description": "Description of template.", 5017 "type": "string", 5018 "required": false 5019 }, 5020 "status": { 5021 "description": "Status of template.", 5022 "type": "string", 5023 "required": false 5024 }, 5025 "area": { 5026 "description": "Where the template part is intended for use (header, footer, etc.)", 5027 "type": "string", 5028 "required": false 5029 } 5030 } 5031 } 5032 ] 5033 }, 5034 "/wp/v2/template-parts/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": { 4553 5035 "namespace": "wp/v2", 4554 5036 "methods": [ … … 8611 9093 ] 8612 9094 } 9095 }, 9096 "wp_template_part": { 9097 "description": "Template parts to include in your templates.", 9098 "hierarchical": false, 9099 "name": "Template Parts", 9100 "slug": "wp_template_part", 9101 "taxonomies": [], 9102 "rest_base": "template-parts", 9103 "rest_namespace": "wp/v2", 9104 "_links": { 9105 "collection": [ 9106 { 9107 "href": "http://example.org/index.php?rest_route=/wp/v2/types" 9108 } 9109 ], 9110 "wp:items": [ 9111 { 9112 "href": "http://example.org/index.php?rest_route=/wp/v2/template-parts" 9113 } 9114 ], 9115 "curies": [ 9116 { 9117 "name": "wp", 9118 "href": "https://api.w.org/{rel}", 9119 "templated": true 9120 } 9121 ] 9122 } 8613 9123 } 8614 9124 }; -
trunk/tools/webpack/blocks.js
r52011 r52062 54 54 'social-link', 55 55 'tag-cloud', 56 'template-part', 56 57 ]; 57 58 const blockFolders = [
Note: See TracChangeset
for help on using the changeset viewer.