PluginProbe
Gutenberg / 23.5.0
Gutenberg v23.5.0
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / compat / wordpress-7.1 / view-config-api.php

view-config-api.php in Gutenberg 23.5.0, at lib/compat/wordpress-7.1/view-config-api.php

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