PluginProbe
Gutenberg / 23.7.2
Gutenberg v23.7.2
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.7.2, at lib/compat/wordpress-7.1/view-config-api.php

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