| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Entity view configuration API. 4 * 5 * Builds the default view configuration for an entity and exposes it through 6 * the dynamic `get_entity_view_config_{$kind}_{$name}` filter so core and third 7 * parties can provide the configuration for a specific entity. 8 * 9 * @package WordPress 10 * @since 7.1.0 11 */ 12 13 /** 14 * Builds the default `form` configuration for post types that don't provide their own. 15 * 16 * It is a sensible default for `post`, `page`, and custom post types alike rather 17 * than being tailored per type. Post types that need a different shape can replace 18 * it entirely with a dedicated `form` through their own filter callback. 19 * 20 * It is intentionally NOT gated by `supports`. The registered fields are the 21 * single source of truth for what applies: each field is registered for a post 22 * type based on its `supports` (and related flags such as `theme_supports`), and 23 * the editor drops any form field whose definition is absent or whose `isVisible` 24 * returns `false`. 25 * 26 * @since 7.1.0 27 * 28 * @return array The default form configuration. 29 */ 30 function _wp_get_default_post_type_form() { 31 return array( 32 'layout' => array( 'type' => 'panel' ), 33 'fields' => array( 34 array( 35 'id' => 'featured_media', 36 'layout' => array( 37 'type' => 'regular', 38 'labelPosition' => 'none', 39 ), 40 ), 41 array( 42 'id' => 'post-content-info', 43 'layout' => array( 44 'type' => 'regular', 45 'labelPosition' => 'none', 46 ), 47 ), 48 array( 49 'id' => 'excerpt', 50 'layout' => array( 51 'type' => 'panel', 52 'labelPosition' => 'top', 53 ), 54 ), 55 array( 56 'id' => 'status', 57 'label' => __( 'Status' ), 58 'children' => array( 59 array( 60 'id' => 'status', 61 'layout' => array( 62 'type' => 'regular', 63 'labelPosition' => 'none', 64 ), 65 ), 66 'scheduled_date', 67 'password', 68 'sticky', 69 ), 70 ), 71 'date', 72 'slug', 73 'author', 74 'template', 75 array( 76 'id' => 'discussion', 77 'label' => __( 'Discussion' ), 78 'children' => array( 79 array( 80 'id' => 'comment_status', 81 'layout' => array( 82 'type' => 'regular', 83 'labelPosition' => 'none', 84 ), 85 ), 86 'ping_status', 87 ), 88 ), 89 'parent', 90 'format', 91 'revisions', 92 ), 93 ); 94 } 95 96 /** 97 * Returns the view configuration for the given entity. 98 * 99 * Builds the default configuration shared by all entities and then exposes it 100 * through the dynamic `get_entity_view_config_{$kind}_{$name}` filter so that core 101 * and third parties can provide the configuration for a specific entity. 102 * 103 * @since 7.1.0 104 * 105 * @param string $kind The entity kind (e.g. `postType`). 106 * @param string $name The entity name (e.g. `page`). 107 * @return array { 108 * The view configuration for the entity. 109 * 110 * @type array $default_view Default view configuration. 111 * @type array $default_layouts Default layouts configuration. 112 * @type array $view_list List of available views. 113 * @type array $form Form configuration. 114 * } 115 */ 116 function wp_get_entity_view_config( $kind, $name ) { 117 $default_view = array( 118 'type' => 'table', 119 'filters' => array(), 120 'sort' => array( 121 'field' => 'title', 122 'direction' => 'asc', 123 ), 124 'perPage' => 20, 125 'fields' => array( 'author', 'status' ), 126 'titleField' => 'title', 127 ); 128 $default_layouts = array( 129 'table' => array(), 130 'grid' => array(), 131 'list' => array(), 132 ); 133 $all_items_title = __( 'All items' ); 134 if ( 'postType' === $kind ) { 135 $post_type_object = get_post_type_object( $name ); 136 if ( $post_type_object && ! empty( $post_type_object->labels->all_items ) ) { 137 $all_items_title = $post_type_object->labels->all_items; 138 } 139 } 140 $view_list = array( 141 array( 142 'title' => $all_items_title, 143 'slug' => 'all', 144 ), 145 ); 146 147 $config = array( 148 'default_view' => $default_view, 149 'default_layouts' => $default_layouts, 150 'view_list' => $view_list, 151 'form' => 'postType' === $kind ? _wp_get_default_post_type_form() : array(), 152 ); 153 154 $data = new WP_View_Config_Data( $config ); 155 156 /** 157 * Filters the view configuration for a given entity. 158 * 159 * The dynamic portions of the hook name, `$kind` and `$name`, refer to the 160 * entity kind (e.g. `postType`) and the entity name (e.g. `page`). 161 * 162 * Callbacks receive a WP_View_Config_Data object and change the 163 * configuration through its methods: the `update_*()` methods merge 164 * partial changes into the current configuration, while `set()` replaces 165 * a whole top-level key. Callbacks must return the object they were 166 * given. 167 * 168 * @since 7.1.0 169 * 170 * @param WP_View_Config_Data $data The view configuration container 171 * for the entity, exposing the 172 * `default_view`, `default_layouts`, 173 * `view_list`, and `form` keys. 174 * @param array $entity { 175 * The entity the configuration is built for. 176 * 177 * @type string $kind The entity kind. 178 * @type string $name The entity name. 179 * } 180 */ 181 $filtered = apply_filters( 182 "get_entity_view_config_{$kind}_{$name}", 183 $data, 184 array( 185 'kind' => $kind, 186 'name' => $name, 187 ) 188 ); 189 190 // A well-behaved callback returns the object it was given. Fall back to the 191 // unfiltered config if a callback replaced it with something else. 192 if ( ! $filtered instanceof WP_View_Config_Data ) { 193 _doing_it_wrong( 194 __FUNCTION__, 195 sprintf( 196 /* translators: %s: the filter hook name. */ 197 esc_html__( 'A "%s" filter callback must return the WP_View_Config_Data object it was given.' ), 198 esc_html( "get_entity_view_config_{$kind}_{$name}" ) 199 ), 200 '7.1.0' 201 ); 202 return $config; 203 } 204 205 // Backfill any dropped keys with their defaults, then discard any keys the 206 // filter introduced that are not part of the documented configuration shape. 207 return array_intersect_key( array_merge( $config, $filtered->get_config() ), $config ); 208 } 209 210 /** 211 * Provides the view configuration for the `page` post type. 212 * 213 * @since 7.1.0 214 * 215 * @param WP_View_Config_Data $data The view configuration container for the entity. 216 * @return WP_View_Config_Data The updated view configuration container. 217 */ 218 function _wp_get_entity_view_config_post_type_page( $data ) { 219 $default_layouts = array( 220 'table' => array( 221 'layout' => array( 222 'styles' => array( 223 'author' => array( 224 'align' => 'start', 225 ), 226 ), 227 ), 228 ), 229 'grid' => array(), 230 'list' => array(), 231 ); 232 233 $default_view = array( 234 'type' => 'list', 235 'filters' => array(), 236 'perPage' => 20, 237 'sort' => array( 238 'field' => 'title', 239 'direction' => 'asc', 240 ), 241 'showLevels' => true, 242 'titleField' => 'title', 243 'mediaField' => 'featured_media', 244 'fields' => array( 'author', 'status' ), 245 ); 246 247 $view_list = array( 248 array( 249 'title' => __( 'Published' ), 250 'slug' => 'published', 251 'view' => array( 252 'filters' => array( 253 array( 254 'field' => 'status', 255 'operator' => 'isAny', 256 'value' => 'publish', 257 'isLocked' => true, 258 ), 259 ), 260 ), 261 ), 262 array( 263 'title' => __( 'Scheduled' ), 264 'slug' => 'future', 265 'view' => array( 266 'filters' => array( 267 array( 268 'field' => 'status', 269 'operator' => 'isAny', 270 'value' => 'future', 271 'isLocked' => true, 272 ), 273 ), 274 ), 275 ), 276 array( 277 'title' => __( 'Drafts' ), 278 'slug' => 'drafts', 279 'view' => array( 280 'filters' => array( 281 array( 282 'field' => 'status', 283 'operator' => 'isAny', 284 'value' => 'draft', 285 'isLocked' => true, 286 ), 287 ), 288 ), 289 ), 290 array( 291 'title' => __( 'Pending' ), 292 'slug' => 'pending', 293 'view' => array( 294 'filters' => array( 295 array( 296 'field' => 'status', 297 'operator' => 'isAny', 298 'value' => 'pending', 299 'isLocked' => true, 300 ), 301 ), 302 ), 303 ), 304 array( 305 'title' => __( 'Private' ), 306 'slug' => 'private', 307 'view' => array( 308 'filters' => array( 309 array( 310 'field' => 'status', 311 'operator' => 'isAny', 312 'value' => 'private', 313 'isLocked' => true, 314 ), 315 ), 316 ), 317 ), 318 array( 319 'title' => __( 'Trash' ), 320 'slug' => 'trash', 321 'view' => array( 322 'type' => 'table', 323 'layout' => $default_layouts['table']['layout'], 324 'filters' => array( 325 array( 326 'field' => 'status', 327 'operator' => 'isAny', 328 'value' => 'trash', 329 'isLocked' => true, 330 ), 331 ), 332 ), 333 ), 334 ); 335 336 $data->set( 'default_layouts', $default_layouts, 1 ); 337 $data->set( 'default_view', $default_view, 1 ); 338 // Append the status views, thereby preserving the base "all items" view, 339 // so its post-type-specific title is kept. 340 $data->update_view_list_items( array_column( $view_list, null, 'slug' ), 1 ); 341 342 return $data; 343 } 344 345 /** 346 * Provides the view configuration for the `wp_block` post type. 347 * 348 * @since 7.1.0 349 * 350 * @param WP_View_Config_Data $data The view configuration container for the entity. 351 * @return WP_View_Config_Data The updated view configuration container. 352 */ 353 function _wp_get_entity_view_config_post_type_wp_block( $data ) { 354 $default_layouts = array( 355 'table' => array( 356 'layout' => array( 357 'styles' => array( 358 'author' => array( 359 'width' => '1%', 360 ), 361 ), 362 ), 363 ), 364 'grid' => array( 365 'layout' => array( 366 'badgeFields' => array( 'sync-status' ), 367 ), 368 ), 369 ); 370 371 $default_view = array( 372 'type' => 'grid', 373 'perPage' => 20, 374 'titleField' => 'title', 375 'mediaField' => 'preview', 376 'fields' => array( 'sync-status' ), 377 'filters' => array(), 378 'layout' => $default_layouts['grid']['layout'], 379 ); 380 381 $data->set( 'default_layouts', $default_layouts, 1 ); 382 $data->set( 'default_view', $default_view, 1 ); 383 384 $view_list = array( 385 array( 386 'title' => __( 'All patterns' ), 387 'slug' => 'all-patterns', 388 ), 389 array( 390 'title' => __( 'My patterns' ), 391 'slug' => 'my-patterns', 392 ), 393 ); 394 395 // Gather categories from the block pattern categories registry. 396 $registry = WP_Block_Pattern_Categories_Registry::get_instance(); 397 $categories = array(); 398 399 foreach ( $registry->get_all_registered() as $category ) { 400 $categories[ $category['name'] ] = $category['label']; 401 } 402 403 // Ensure "Uncategorized" is always included for patterns 404 // that have no category assigned. 405 $categories['uncategorized'] ??= __( 'Uncategorized' ); 406 407 // Also gather user-created pattern categories (wp_pattern_category taxonomy). 408 $user_terms = get_terms( 409 array( 410 'taxonomy' => 'wp_pattern_category', 411 'hide_empty' => false, 412 ) 413 ); 414 415 if ( ! is_wp_error( $user_terms ) ) { 416 foreach ( $user_terms as $term ) { 417 $categories[ $term->slug ] = $term->name; 418 } 419 } 420 421 // Sort categories alphabetically by label. 422 asort( $categories, SORT_NATURAL | SORT_FLAG_CASE ); 423 424 foreach ( $categories as $category_name => $label ) { 425 $view_list[] = array( 426 'title' => $label, 427 'slug' => $category_name, 428 ); 429 } 430 431 $data->set( 'view_list', $view_list, 1 ); 432 433 $data->set( 434 'form', 435 array( 436 'layout' => array( 'type' => 'panel' ), 437 'fields' => array( 438 array( 439 'id' => 'excerpt', 440 'layout' => array( 441 'type' => 'panel', 442 'labelPosition' => 'top', 443 ), 444 ), 445 array( 446 'id' => 'post-content-info', 447 'layout' => array( 448 'type' => 'regular', 449 'labelPosition' => 'none', 450 ), 451 ), 452 'sync-status', 453 'revisions', 454 ), 455 ), 456 1 457 ); 458 459 return $data; 460 } 461 462 /** 463 * Provides the view configuration for the `wp_template_part` post type. 464 * 465 * @since 7.1.0 466 * 467 * @param WP_View_Config_Data $data The view configuration container for the entity. 468 * @return WP_View_Config_Data The updated view configuration container. 469 */ 470 function _wp_get_entity_view_config_post_type_wp_template_part( $data ) { 471 $default_layouts = array( 472 'table' => array( 473 'layout' => array( 474 'styles' => array( 475 'author' => array( 476 'width' => '1%', 477 ), 478 ), 479 ), 480 ), 481 'grid' => array( 482 'layout' => array(), 483 ), 484 ); 485 486 $default_view = array( 487 'type' => 'grid', 488 'perPage' => 20, 489 'titleField' => 'title', 490 'mediaField' => 'preview', 491 'fields' => array( 'author' ), 492 'filters' => array(), 493 'layout' => $default_layouts['grid']['layout'], 494 ); 495 496 $data->set( 'default_layouts', $default_layouts, 1 ); 497 $data->set( 'default_view', $default_view, 1 ); 498 499 $view_list = array( 500 array( 501 'title' => __( 'All template parts' ), 502 'slug' => 'all-parts', 503 ), 504 ); 505 506 $areas = get_allowed_block_template_part_areas(); 507 508 // Ensure default areas appear in a consistent order. 509 $preferred_order = array( 'header', 'footer', 'sidebar', 'navigation-overlay', 'uncategorized' ); 510 $ordered_areas = array(); 511 $remaining_areas = array(); 512 foreach ( $areas as $area ) { 513 $position = array_search( $area['area'], $preferred_order, true ); 514 if ( false !== $position ) { 515 $ordered_areas[ $position ] = $area; 516 } else { 517 $remaining_areas[] = $area; 518 } 519 } 520 ksort( $ordered_areas ); 521 $areas = array_merge( array_values( $ordered_areas ), $remaining_areas ); 522 523 foreach ( $areas as $area ) { 524 $view_list[] = array( 525 'title' => $area['label'], 526 'slug' => $area['area'], 527 'view' => array( 528 'filters' => array( 529 array( 530 'field' => 'area', 531 'operator' => 'is', 532 'value' => $area['area'], 533 'isLocked' => true, 534 ), 535 ), 536 ), 537 ); 538 } 539 540 $data->set( 'view_list', $view_list, 1 ); 541 542 $data->set( 543 'form', 544 array( 545 'layout' => array( 'type' => 'panel' ), 546 'fields' => array( 547 array( 548 'id' => 'last_edited_date', 549 'layout' => array( 550 'type' => 'panel', 551 'labelPosition' => 'none', 552 ), 553 ), 554 'revisions', 555 ), 556 ), 557 1 558 ); 559 560 return $data; 561 } 562 563 /** 564 * Provides the view configuration for the `wp_template` post type. 565 * 566 * @since 7.1.0 567 * 568 * @param WP_View_Config_Data $data The view configuration container for the entity. 569 * @return WP_View_Config_Data The updated view configuration container. 570 */ 571 function _wp_get_entity_view_config_post_type_wp_template( $data ) { 572 $default_view = array( 573 'type' => 'grid', 574 'perPage' => 20, 575 'sort' => array( 576 'field' => 'title', 577 'direction' => 'asc', 578 ), 579 'titleField' => 'title', 580 'descriptionField' => 'description', 581 'mediaField' => 'preview', 582 'fields' => array( 'author', 'active', 'slug', 'theme' ), 583 'filters' => array(), 584 'showMedia' => true, 585 ); 586 587 $default_layouts = array( 588 'table' => array( 'showMedia' => false ), 589 'grid' => array( 'showMedia' => true ), 590 'list' => array( 'showMedia' => false ), 591 ); 592 593 $data->set( 'default_view', $default_view, 1 ); 594 $data->set( 'default_layouts', $default_layouts, 1 ); 595 596 $view_list = array( 597 array( 598 'title' => __( 'All templates' ), 599 'slug' => 'all', 600 ), 601 ); 602 603 $templates = get_block_templates( array(), 'wp_template' ); 604 605 // Collect unique authors, tracking whether they come from a registered 606 // source (theme, plugin, site) so we can sort those before user ones. 607 $seen_authors = array(); 608 $registered_authors = array(); 609 $user_authors = array(); 610 foreach ( $templates as $template ) { 611 /* 612 * Determine the original source of the template ('theme', 'plugin', 613 * 'site', or 'user'). 614 */ 615 $original_source = 'user'; 616 if ( 'wp_template' === $template->type || 'wp_template_part' === $template->type ) { 617 if ( $template->has_theme_file && 618 ( 'theme' === $template->origin || ( 619 empty( $template->origin ) && in_array( 620 $template->source, 621 array( 622 'theme', 623 'custom', 624 ), 625 true 626 ) ) 627 ) 628 ) { 629 /* 630 * Added by theme. 631 * Template originally provided by a theme, but customized by a user. 632 * Templates originally didn't have the 'origin' field so identify 633 * older customized templates by checking for no origin and a 'theme' 634 * or 'custom' source. 635 */ 636 $original_source = 'theme'; 637 } elseif ( 'plugin' === $template->origin ) { 638 // Added by plugin. 639 $original_source = 'plugin'; 640 } elseif ( empty( $template->has_theme_file ) && 'custom' === $template->source && empty( $template->author ) ) { 641 /* 642 * Added by site. 643 * Template was created from scratch, but has no author. Author support 644 * was only added to templates in WordPress 5.9. Fallback to showing the 645 * site logo and title. 646 */ 647 $original_source = 'site'; 648 } 649 } 650 651 // Determine a human readable text for the author of the template. 652 $author_text = ''; 653 switch ( $original_source ) { 654 case 'theme': 655 $theme_name = wp_get_theme( $template->theme )->get( 'Name' ); 656 $author_text = empty( $theme_name ) ? $template->theme : $theme_name; 657 break; 658 case 'plugin': 659 if ( ! function_exists( 'get_plugins' ) ) { 660 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 661 } 662 $plugin_name = ''; 663 if ( isset( $template->plugin ) ) { 664 $plugins = wp_get_active_and_valid_plugins(); 665 666 foreach ( $plugins as $plugin_file ) { 667 $plugin_basename = plugin_basename( $plugin_file ); 668 list( $plugin_slug, ) = explode( '/', $plugin_basename ); 669 670 if ( $plugin_slug === $template->plugin ) { 671 $plugin_data = get_plugin_data( $plugin_file ); 672 673 if ( ! empty( $plugin_data['Name'] ) ) { 674 $plugin_name = $plugin_data['Name']; 675 } 676 677 break; 678 } 679 } 680 } 681 682 /* 683 * Fall back to the theme name if the plugin is not defined. That's needed to keep backwards 684 * compatibility with templates that were registered before the plugin attribute was added. 685 */ 686 if ( '' === $plugin_name ) { 687 $plugins = get_plugins(); 688 $plugin_basename = plugin_basename( sanitize_text_field( $template->theme . '.php' ) ); 689 $plugin_name = $plugins[ $plugin_basename ]['Name'] ?? $template->plugin ?? $template->theme; 690 } 691 $author_text = $plugin_name; 692 break; 693 case 'site': 694 $author_text = get_bloginfo( 'name' ); 695 break; 696 case 'user': 697 $author = get_user_by( 'id', $template->author ); 698 if ( ! $author ) { 699 $author_text = __( 'Unknown author' ); 700 } else { 701 $author_text = $author->get( 'display_name' ); 702 } 703 break; 704 } 705 706 if ( ! empty( $author_text ) && ! isset( $seen_authors[ $author_text ] ) ) { 707 $seen_authors[ $author_text ] = true; 708 $entry = array( 709 'title' => $author_text, 710 'slug' => $author_text, 711 'view' => array( 712 'filters' => array( 713 array( 714 'field' => 'author', 715 'operator' => 'is', 716 'value' => $author_text, 717 'isLocked' => true, 718 ), 719 ), 720 ), 721 ); 722 if ( 'user' === $original_source ) { 723 $user_authors[] = $entry; 724 } else { 725 $registered_authors[] = $entry; 726 } 727 } 728 } 729 730 $data->set( 'view_list', array_merge( $view_list, $registered_authors, $user_authors ), 1 ); 731 732 $data->set( 733 'form', 734 array( 735 'layout' => array( 'type' => 'panel' ), 736 'fields' => array( 737 array( 738 'id' => 'description', 739 'layout' => array( 740 'type' => 'panel', 741 'labelPosition' => 'top', 742 ), 743 ), 744 array( 745 'id' => 'description_readonly', 746 'layout' => array( 747 'type' => 'regular', 748 'labelPosition' => 'none', 749 ), 750 ), 751 array( 752 'id' => 'last_edited_date', 753 'layout' => array( 754 'type' => 'panel', 755 'labelPosition' => 'none', 756 ), 757 ), 758 'revisions', 759 // The following fields are only meaningful in the `home`/`index` 760 // template summary. They edit other entities (`root/site` and the 761 // posts page); the editor merges those records into the form data 762 // under a namespace and controls when the fields are shown. 763 'posts_page_title', 764 'posts_per_page', 765 'default_comment_status', 766 ), 767 ), 768 1 769 ); 770 771 return $data; 772 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue Jul 14 08:20:16 2026 | Cross-referenced by PHPXref |