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