From f0c85c4e19df25bf3c9b0e53827a8dd3101c71b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:13:17 +0200 Subject: [PATCH 1/8] Bootstrap view config API --- ...enberg-rest-view-config-controller-7-1.php | 602 +------------- lib/compat/wordpress-7.1/view-config-api.php | 751 ++++++++++++++++++ lib/load.php | 1 + 3 files changed, 757 insertions(+), 597 deletions(-) create mode 100644 lib/compat/wordpress-7.1/view-config-api.php diff --git a/lib/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php b/lib/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php index 551bc3927a3ce5..72960b1352c82e 100644 --- a/lib/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php +++ b/lib/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php @@ -82,65 +82,15 @@ public function get_items( $request ) { $kind = $request->get_param( 'kind' ); $name = $request->get_param( 'name' ); - // TODO: this data will come from a registry of view configs per entity. - $form = array(); - $default_view = array( - 'type' => 'table', - 'filters' => array(), - 'perPage' => 20, - 'sort' => array( - 'field' => 'title', - 'direction' => 'asc', - ), - 'titleField' => 'title', - 'fields' => array( 'author', 'status' ), - ); - $default_layouts = array( - 'table' => array(), - 'grid' => array(), - 'list' => array(), - ); - $all_items_title = __( 'All items', 'gutenberg' ); - if ( 'postType' === $kind ) { - $post_type_object = get_post_type_object( $name ); - if ( $post_type_object && ! empty( $post_type_object->labels->all_items ) ) { - $all_items_title = $post_type_object->labels->all_items; - } - } - $view_list = array( - array( - 'title' => $all_items_title, - 'slug' => 'all', - ), - ); - if ( 'postType' === $kind && 'page' === $name ) { - $default_layouts = $this->get_default_layouts_for_page(); - $default_view = $this->get_default_view_for_page(); - $view_list = $this->get_view_list_for_page( $all_items_title, $default_layouts ); - $form = $this->get_form_for_page(); - } elseif ( 'postType' === $kind && 'post' === $name ) { - $form = $this->get_form_for_page(); - } elseif ( 'postType' === $kind && 'wp_block' === $name ) { - $default_layouts = $this->get_default_layouts_for_wp_block(); - $default_view = $this->get_default_view_for_wp_block( $default_layouts ); - $view_list = $this->get_view_list_for_wp_block(); - } elseif ( 'postType' === $kind && 'wp_template_part' === $name ) { - $default_layouts = $this->get_default_layouts_for_wp_template_part(); - $default_view = $this->get_default_view_for_wp_template_part( $default_layouts ); - $view_list = $this->get_view_list_for_wp_template_part(); - } elseif ( 'postType' === $kind && 'wp_template' === $name ) { - $default_view = $this->get_default_view_for_wp_template(); - $default_layouts = $this->get_default_layouts_for_wp_template(); - $view_list = $this->get_view_list_for_wp_template(); - } + $config = gutenberg_get_entity_view_config( $kind, $name ); $response = array( 'kind' => $kind, 'name' => $name, - 'default_view' => $default_view, - 'default_layouts' => $default_layouts, - 'view_list' => $view_list, - 'form' => $form, + 'default_view' => $config['default_view'], + 'default_layouts' => $config['default_layouts'], + 'view_list' => $config['view_list'], + 'form' => $config['form'], ); return rest_ensure_response( $response ); @@ -727,546 +677,4 @@ private function get_form_schema() { ); } - private function get_default_view_for_page() { - return array( - 'type' => 'list', - 'filters' => array(), - 'perPage' => 20, - 'sort' => array( - 'field' => 'title', - 'direction' => 'asc', - ), - 'showLevels' => true, - 'titleField' => 'title', - 'mediaField' => 'featured_media', - 'fields' => array( 'author', 'status' ), - ); - } - - private function get_default_layouts_for_page() { - return array( - 'table' => array( - 'layout' => array( - 'styles' => array( - 'author' => array( - 'align' => 'start', - ), - ), - ), - ), - 'grid' => array(), - 'list' => array(), - ); - } - - private function get_form_for_page() { - return array( - 'layout' => array( 'type' => 'panel' ), - 'fields' => array( - array( - 'id' => 'featured_media', - 'layout' => array( - 'type' => 'regular', - 'labelPosition' => 'none', - ), - ), - array( - 'id' => 'post-content-info', - 'layout' => array( - 'type' => 'regular', - 'labelPosition' => 'none', - ), - ), - array( - 'id' => 'excerpt', - 'layout' => array( - 'type' => 'panel', - 'labelPosition' => 'top', - ), - ), - array( - 'id' => 'status', - 'label' => __( 'Status', 'gutenberg' ), - 'children' => array( - array( - 'id' => 'status', - 'layout' => array( - 'type' => 'regular', - 'labelPosition' => 'none', - ), - ), - 'scheduled_date', - 'password', - 'sticky', - ), - ), - 'date', - 'slug', - 'author', - 'template', - array( - 'id' => 'discussion', - 'label' => __( 'Discussion', 'gutenberg' ), - 'children' => array( - array( - 'id' => 'comment_status', - 'layout' => array( - 'type' => 'regular', - 'labelPosition' => 'none', - ), - ), - 'ping_status', - ), - ), - 'parent', - 'format', - ), - ); - } - - private function get_view_list_for_page( $all_items_title, $default_layouts ) { - return array( - array( - 'title' => $all_items_title, - 'slug' => 'all', - ), - array( - 'title' => __( 'Published', 'gutenberg' ), - 'slug' => 'published', - 'view' => array( - 'filters' => array( - array( - 'field' => 'status', - 'operator' => 'isAny', - 'value' => 'publish', - 'isLocked' => true, - ), - ), - ), - ), - array( - 'title' => __( 'Scheduled', 'gutenberg' ), - 'slug' => 'future', - 'view' => array( - 'filters' => array( - array( - 'field' => 'status', - 'operator' => 'isAny', - 'value' => 'future', - 'isLocked' => true, - ), - ), - ), - ), - array( - 'title' => __( 'Drafts', 'gutenberg' ), - 'slug' => 'drafts', - 'view' => array( - 'filters' => array( - array( - 'field' => 'status', - 'operator' => 'isAny', - 'value' => 'draft', - 'isLocked' => true, - ), - ), - ), - ), - array( - 'title' => __( 'Pending', 'gutenberg' ), - 'slug' => 'pending', - 'view' => array( - 'filters' => array( - array( - 'field' => 'status', - 'operator' => 'isAny', - 'value' => 'pending', - 'isLocked' => true, - ), - ), - ), - ), - array( - 'title' => __( 'Private', 'gutenberg' ), - 'slug' => 'private', - 'view' => array( - 'filters' => array( - array( - 'field' => 'status', - 'operator' => 'isAny', - 'value' => 'private', - 'isLocked' => true, - ), - ), - ), - ), - array( - 'title' => __( 'Trash', 'gutenberg' ), - 'slug' => 'trash', - 'view' => array( - 'type' => 'table', - 'layout' => $default_layouts['table']['layout'], - 'filters' => array( - array( - 'field' => 'status', - 'operator' => 'isAny', - 'value' => 'trash', - 'isLocked' => true, - ), - ), - ), - ), - ); - } - - private function get_default_layouts_for_wp_block() { - return array( - 'table' => array( - 'layout' => array( - 'styles' => array( - 'author' => array( - 'width' => '1%', - ), - ), - ), - ), - 'grid' => array( - 'layout' => array( - 'badgeFields' => array( 'sync-status' ), - ), - ), - ); - } - - private function get_default_view_for_wp_block( $default_layouts ) { - return array( - 'type' => 'grid', - 'perPage' => 20, - 'titleField' => 'title', - 'mediaField' => 'preview', - 'fields' => array( 'sync-status' ), - 'filters' => array(), - 'layout' => $default_layouts['grid']['layout'], - ); - } - - private function get_default_layouts_for_wp_template_part() { - return array( - 'table' => array( - 'layout' => array( - 'styles' => array( - 'author' => array( - 'width' => '1%', - ), - ), - ), - ), - 'grid' => array( - 'layout' => array(), - ), - ); - } - - private function get_default_view_for_wp_template_part( $default_layouts ) { - return array( - 'type' => 'grid', - 'perPage' => 20, - 'titleField' => 'title', - 'mediaField' => 'preview', - 'fields' => array( 'author' ), - 'filters' => array(), - 'layout' => $default_layouts['grid']['layout'], - ); - } - - /** - * Returns the original source of a template. - * - * @param WP_Block_Template $template_object Template instance. - * @return string The original source ('theme', 'plugin', 'site', or 'user'). - */ - private static function get_wp_templates_original_source_field( $template_object ) { - if ( 'wp_template' === $template_object->type || 'wp_template_part' === $template_object->type ) { - /* - * Added by theme. - * Template originally provided by a theme, but customized by a user. - * Templates originally didn't have the 'origin' field so identify - * older customized templates by checking for no origin and a 'theme' - * or 'custom' source. - */ - if ( $template_object->has_theme_file && - ( 'theme' === $template_object->origin || ( - empty( $template_object->origin ) && in_array( - $template_object->source, - array( - 'theme', - 'custom', - ), - true - ) ) - ) - ) { - return 'theme'; - } - - // Added by plugin. - if ( 'plugin' === $template_object->origin ) { - return 'plugin'; - } - - /* - * Added by site. - * Template was created from scratch, but has no author. Author support - * was only added to templates in WordPress 5.9. Fallback to showing the - * site logo and title. - */ - if ( empty( $template_object->has_theme_file ) && 'custom' === $template_object->source && empty( $template_object->author ) ) { - return 'site'; - } - } - - // Added by user. - return 'user'; - } - - /** - * Returns a human readable text for the author of a template. - * - * @param WP_Block_Template $template_object Template instance. - * @return string Human readable text for the author. - */ - private static function get_wp_templates_author_text_field( $template_object ) { - $original_source = self::get_wp_templates_original_source_field( $template_object ); - switch ( $original_source ) { - case 'theme': - $theme_name = wp_get_theme( $template_object->theme )->get( 'Name' ); - return empty( $theme_name ) ? $template_object->theme : $theme_name; - case 'plugin': - if ( ! function_exists( 'get_plugins' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - } - if ( isset( $template_object->plugin ) ) { - $plugins = wp_get_active_and_valid_plugins(); - - foreach ( $plugins as $plugin_file ) { - $plugin_basename = plugin_basename( $plugin_file ); - list( $plugin_slug, ) = explode( '/', $plugin_basename ); - - if ( $plugin_slug === $template_object->plugin ) { - $plugin_data = get_plugin_data( $plugin_file ); - - if ( ! empty( $plugin_data['Name'] ) ) { - return $plugin_data['Name']; - } - - break; - } - } - } - - /* - * Fall back to the theme name if the plugin is not defined. That's needed to keep backwards - * compatibility with templates that were registered before the plugin attribute was added. - */ - $plugins = get_plugins(); - $plugin_basename = plugin_basename( sanitize_text_field( $template_object->theme . '.php' ) ); - if ( isset( $plugins[ $plugin_basename ] ) && isset( $plugins[ $plugin_basename ]['Name'] ) ) { - return $plugins[ $plugin_basename ]['Name']; - } - return $template_object->plugin ?? $template_object->theme; - case 'site': - return get_bloginfo( 'name' ); - case 'user': - $author = get_user_by( 'id', $template_object->author ); - if ( ! $author ) { - return __( 'Unknown author', 'gutenberg' ); - } - return $author->get( 'display_name' ); - } - - // Fail-safe to return a string should the original source ever fall through. - return ''; - } - - /** - * Returns the view list for the wp_template_part post type. - * - * Builds entries from the registered template part areas (header, footer, etc.). - * - * @return array View list entries. - */ - private function get_view_list_for_wp_template_part() { - $view_list = array( - array( - 'title' => __( 'All template parts', 'gutenberg' ), - 'slug' => 'all-parts', - ), - ); - - $areas = get_allowed_block_template_part_areas(); - - // Ensure default areas appear in a consistent order. - $preferred_order = array( 'header', 'footer', 'sidebar', 'navigation-overlay', 'uncategorized' ); - $ordered_areas = array(); - $remaining_areas = array(); - foreach ( $areas as $area ) { - $position = array_search( $area['area'], $preferred_order, true ); - if ( false !== $position ) { - $ordered_areas[ $position ] = $area; - } else { - $remaining_areas[] = $area; - } - } - ksort( $ordered_areas ); - $areas = array_merge( array_values( $ordered_areas ), $remaining_areas ); - - foreach ( $areas as $area ) { - $view_list[] = array( - 'title' => $area['label'], - 'slug' => $area['area'], - 'view' => array( - 'filters' => array( - array( - 'field' => 'area', - 'operator' => 'is', - 'value' => $area['area'], - 'isLocked' => true, - ), - ), - ), - ); - } - - return $view_list; - } - - /** - * Returns the view list for the wp_block (patterns) post type. - * - * Builds entries from registered block pattern categories and user pattern categories. - * - * @return array View list entries. - */ - private function get_view_list_for_wp_block() { - $view_list = array( - array( - 'title' => __( 'All patterns', 'gutenberg' ), - 'slug' => 'all-patterns', - ), - array( - 'title' => __( 'My patterns', 'gutenberg' ), - 'slug' => 'my-patterns', - ), - ); - - // Gather categories from the block pattern categories registry. - $registry = WP_Block_Pattern_Categories_Registry::get_instance(); - $categories = array(); - - foreach ( $registry->get_all_registered() as $category ) { - $categories[ $category['name'] ] = $category['label']; - } - - // Ensure "Uncategorized" is always included for patterns - // that have no category assigned. - $categories['uncategorized'] ??= __( 'Uncategorized', 'gutenberg' ); - - // Also gather user-created pattern categories (wp_pattern_category taxonomy). - $user_terms = get_terms( - array( - 'taxonomy' => 'wp_pattern_category', - 'hide_empty' => false, - ) - ); - - if ( ! is_wp_error( $user_terms ) ) { - foreach ( $user_terms as $term ) { - $categories[ $term->slug ] = $term->name; - } - } - - // Sort categories alphabetically by label. - asort( $categories, SORT_NATURAL | SORT_FLAG_CASE ); - - foreach ( $categories as $name => $label ) { - $view_list[] = array( - 'title' => $label, - 'slug' => $name, - ); - } - - return $view_list; - } - - private function get_default_view_for_wp_template() { - return array( - 'type' => 'grid', - 'perPage' => 20, - 'sort' => array( - 'field' => 'title', - 'direction' => 'asc', - ), - 'titleField' => 'title', - 'descriptionField' => 'description', - 'mediaField' => 'preview', - 'fields' => array( 'author', 'active', 'slug', 'theme' ), - 'filters' => array(), - 'showMedia' => true, - ); - } - - private function get_default_layouts_for_wp_template() { - return array( - 'table' => array( 'showMedia' => false ), - 'grid' => array( 'showMedia' => true ), - 'list' => array( 'showMedia' => false ), - ); - } - - private function get_view_list_for_wp_template() { - $view_list = array( - array( - 'title' => __( 'All templates', 'gutenberg' ), - 'slug' => 'all', - ), - ); - - $templates = get_block_templates( array(), 'wp_template' ); - - // Collect unique authors, tracking whether they come from a registered - // source (theme, plugin, site) so we can sort those before user ones. - $seen_authors = array(); - $registered_authors = array(); - $user_authors = array(); - foreach ( $templates as $template ) { - $original_source = self::get_wp_templates_original_source_field( $template ); - $author_text = self::get_wp_templates_author_text_field( $template ); - if ( ! empty( $author_text ) && ! isset( $seen_authors[ $author_text ] ) ) { - $seen_authors[ $author_text ] = true; - $entry = array( - 'title' => $author_text, - 'slug' => $author_text, - 'view' => array( - 'filters' => array( - array( - 'field' => 'author', - 'operator' => 'is', - 'value' => $author_text, - 'isLocked' => true, - ), - ), - ), - ); - if ( 'user' === $original_source ) { - $user_authors[] = $entry; - } else { - $registered_authors[] = $entry; - } - } - } - - $view_list = array_merge( $view_list, $registered_authors, $user_authors ); - - return $view_list; - } } diff --git a/lib/compat/wordpress-7.1/view-config-api.php b/lib/compat/wordpress-7.1/view-config-api.php new file mode 100644 index 00000000000000..d55515759d744d --- /dev/null +++ b/lib/compat/wordpress-7.1/view-config-api.php @@ -0,0 +1,751 @@ + 'table', + 'filters' => array(), + 'perPage' => 20, + 'sort' => array( + 'field' => 'title', + 'direction' => 'asc', + ), + 'titleField' => 'title', + 'fields' => array( 'author', 'status' ), + ); + $default_layouts = array( + 'table' => array(), + 'grid' => array(), + 'list' => array(), + ); + $all_items_title = __( 'All items', 'gutenberg' ); + if ( 'postType' === $kind ) { + $post_type_object = get_post_type_object( $name ); + if ( $post_type_object && ! empty( $post_type_object->labels->all_items ) ) { + $all_items_title = $post_type_object->labels->all_items; + } + } + $view_list = array( + array( + 'title' => $all_items_title, + 'slug' => 'all', + ), + ); + + $config = array( + 'default_view' => $default_view, + 'default_layouts' => $default_layouts, + 'view_list' => $view_list, + 'form' => array(), + ); + + /** + * Filters the view configuration for a given entity. + * + * The dynamic portions of the hook name, `$kind` and `$name`, refer to the + * entity kind (e.g. `postType`) and the entity name (e.g. `page`). + * + * @param array $config { + * The view configuration for the entity. + * + * @type array $default_view Default view configuration. + * @type array $default_layouts Default layouts configuration. + * @type array $view_list List of available views. + * @type array $form Form configuration. + * } + * @param array $entity { + * The entity the configuration is built for. + * + * @type string $kind The entity kind. + * @type string $name The entity name. + * @type string $all_items_title The localized "all items" title for the entity. + * } + */ + return apply_filters( + "get_entity_view_config_{$kind}_{$name}", + $config, + array( + 'kind' => $kind, + 'name' => $name, + 'all_items_title' => $all_items_title, + ) + ); +} + +/** + * Provides the view configuration for the `page` post type. + * + * @param array $config { + * The view configuration for the entity. + * } + * @param array $entity { + * The entity the configuration is built for. + * + * @type string $kind The entity kind. + * @type string $name The entity name. + * @type string $all_items_title The localized "all items" title for the entity. + * } + * @return array The filtered view configuration. + */ +function _gutenberg_get_entity_view_config_postType_page( $config, $entity ) { + $config['default_layouts'] = array( + 'table' => array( + 'layout' => array( + 'styles' => array( + 'author' => array( + 'align' => 'start', + ), + ), + ), + ), + 'grid' => array(), + 'list' => array(), + ); + + $config['default_view'] = array( + 'type' => 'list', + 'filters' => array(), + 'perPage' => 20, + 'sort' => array( + 'field' => 'title', + 'direction' => 'asc', + ), + 'showLevels' => true, + 'titleField' => 'title', + 'mediaField' => 'featured_media', + 'fields' => array( 'author', 'status' ), + ); + + $config['view_list'] = array( + array( + 'title' => $entity['all_items_title'], + 'slug' => 'all', + ), + array( + 'title' => __( 'Published', 'gutenberg' ), + 'slug' => 'published', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'publish', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Scheduled', 'gutenberg' ), + 'slug' => 'future', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'future', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Drafts', 'gutenberg' ), + 'slug' => 'drafts', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'draft', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Pending', 'gutenberg' ), + 'slug' => 'pending', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'pending', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Private', 'gutenberg' ), + 'slug' => 'private', + 'view' => array( + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'private', + 'isLocked' => true, + ), + ), + ), + ), + array( + 'title' => __( 'Trash', 'gutenberg' ), + 'slug' => 'trash', + 'view' => array( + 'type' => 'table', + 'layout' => $config['default_layouts']['table']['layout'], + 'filters' => array( + array( + 'field' => 'status', + 'operator' => 'isAny', + 'value' => 'trash', + 'isLocked' => true, + ), + ), + ), + ), + ); + + $config['form'] = array( + 'layout' => array( 'type' => 'panel' ), + 'fields' => array( + array( + 'id' => 'featured_media', + 'layout' => array( + 'type' => 'regular', + 'labelPosition' => 'none', + ), + ), + array( + 'id' => 'post-content-info', + 'layout' => array( + 'type' => 'regular', + 'labelPosition' => 'none', + ), + ), + array( + 'id' => 'excerpt', + 'layout' => array( + 'type' => 'panel', + 'labelPosition' => 'top', + ), + ), + array( + 'id' => 'status', + 'label' => __( 'Status', 'gutenberg' ), + 'children' => array( + array( + 'id' => 'status', + 'layout' => array( + 'type' => 'regular', + 'labelPosition' => 'none', + ), + ), + 'scheduled_date', + 'password', + 'sticky', + ), + ), + 'date', + 'slug', + 'author', + 'template', + array( + 'id' => 'discussion', + 'label' => __( 'Discussion', 'gutenberg' ), + 'children' => array( + array( + 'id' => 'comment_status', + 'layout' => array( + 'type' => 'regular', + 'labelPosition' => 'none', + ), + ), + 'ping_status', + ), + ), + 'parent', + 'format', + ), + ); + + return $config; +} +add_filter( 'get_entity_view_config_postType_page', '_gutenberg_get_entity_view_config_postType_page', 10, 2 ); + +/** + * Provides the view configuration for the `post` post type. + * + * @param array $config { + * The view configuration for the entity. + * } + * @param array $entity { + * The entity the configuration is built for. + * + * @type string $kind The entity kind. + * @type string $name The entity name. + * @type string $all_items_title The localized "all items" title for the entity. + * } + * @return array The filtered view configuration. + */ +function _gutenberg_get_entity_view_config_postType_post( $config, $entity ) { + $config['form'] = array( + 'layout' => array( 'type' => 'panel' ), + 'fields' => array( + array( + 'id' => 'featured_media', + 'layout' => array( + 'type' => 'regular', + 'labelPosition' => 'none', + ), + ), + array( + 'id' => 'post-content-info', + 'layout' => array( + 'type' => 'regular', + 'labelPosition' => 'none', + ), + ), + array( + 'id' => 'excerpt', + 'layout' => array( + 'type' => 'panel', + 'labelPosition' => 'top', + ), + ), + array( + 'id' => 'status', + 'label' => __( 'Status', 'gutenberg' ), + 'children' => array( + array( + 'id' => 'status', + 'layout' => array( + 'type' => 'regular', + 'labelPosition' => 'none', + ), + ), + 'scheduled_date', + 'password', + 'sticky', + ), + ), + 'date', + 'slug', + 'author', + 'template', + array( + 'id' => 'discussion', + 'label' => __( 'Discussion', 'gutenberg' ), + 'children' => array( + array( + 'id' => 'comment_status', + 'layout' => array( + 'type' => 'regular', + 'labelPosition' => 'none', + ), + ), + 'ping_status', + ), + ), + 'parent', + 'format', + ), + ); + + return $config; +} +add_filter( 'get_entity_view_config_postType_post', '_gutenberg_get_entity_view_config_postType_post', 10, 2 ); + +/** + * Provides the view configuration for the `wp_block` post type. + * + * @param array $config { + * The view configuration for the entity. + * } + * @param array $entity { + * The entity the configuration is built for. + * + * @type string $kind The entity kind. + * @type string $name The entity name. + * @type string $all_items_title The localized "all items" title for the entity. + * } + * @return array The filtered view configuration. + */ +function _gutenberg_get_entity_view_config_postType_wp_block( $config, $entity ) { + $config['default_layouts'] = array( + 'table' => array( + 'layout' => array( + 'styles' => array( + 'author' => array( + 'width' => '1%', + ), + ), + ), + ), + 'grid' => array( + 'layout' => array( + 'badgeFields' => array( 'sync-status' ), + ), + ), + ); + + $config['default_view'] = array( + 'type' => 'grid', + 'perPage' => 20, + 'titleField' => 'title', + 'mediaField' => 'preview', + 'fields' => array( 'sync-status' ), + 'filters' => array(), + 'layout' => $config['default_layouts']['grid']['layout'], + ); + + $view_list = array( + array( + 'title' => __( 'All patterns', 'gutenberg' ), + 'slug' => 'all-patterns', + ), + array( + 'title' => __( 'My patterns', 'gutenberg' ), + 'slug' => 'my-patterns', + ), + ); + + // Gather categories from the block pattern categories registry. + $registry = WP_Block_Pattern_Categories_Registry::get_instance(); + $categories = array(); + + foreach ( $registry->get_all_registered() as $category ) { + $categories[ $category['name'] ] = $category['label']; + } + + // Ensure "Uncategorized" is always included for patterns + // that have no category assigned. + $categories['uncategorized'] ??= __( 'Uncategorized', 'gutenberg' ); + + // Also gather user-created pattern categories (wp_pattern_category taxonomy). + $user_terms = get_terms( + array( + 'taxonomy' => 'wp_pattern_category', + 'hide_empty' => false, + ) + ); + + if ( ! is_wp_error( $user_terms ) ) { + foreach ( $user_terms as $term ) { + $categories[ $term->slug ] = $term->name; + } + } + + // Sort categories alphabetically by label. + asort( $categories, SORT_NATURAL | SORT_FLAG_CASE ); + + foreach ( $categories as $category_name => $label ) { + $view_list[] = array( + 'title' => $label, + 'slug' => $category_name, + ); + } + + $config['view_list'] = $view_list; + + return $config; +} +add_filter( 'get_entity_view_config_postType_wp_block', '_gutenberg_get_entity_view_config_postType_wp_block', 10, 2 ); + +/** + * Provides the view configuration for the `wp_template_part` post type. + * + * @param array $config { + * The view configuration for the entity. + * } + * @param array $entity { + * The entity the configuration is built for. + * + * @type string $kind The entity kind. + * @type string $name The entity name. + * @type string $all_items_title The localized "all items" title for the entity. + * } + * @return array The filtered view configuration. + */ +function _gutenberg_get_entity_view_config_postType_wp_template_part( $config, $entity ) { + $config['default_layouts'] = array( + 'table' => array( + 'layout' => array( + 'styles' => array( + 'author' => array( + 'width' => '1%', + ), + ), + ), + ), + 'grid' => array( + 'layout' => array(), + ), + ); + + $config['default_view'] = array( + 'type' => 'grid', + 'perPage' => 20, + 'titleField' => 'title', + 'mediaField' => 'preview', + 'fields' => array( 'author' ), + 'filters' => array(), + 'layout' => $config['default_layouts']['grid']['layout'], + ); + + $view_list = array( + array( + 'title' => __( 'All template parts', 'gutenberg' ), + 'slug' => 'all-parts', + ), + ); + + $areas = get_allowed_block_template_part_areas(); + + // Ensure default areas appear in a consistent order. + $preferred_order = array( 'header', 'footer', 'sidebar', 'navigation-overlay', 'uncategorized' ); + $ordered_areas = array(); + $remaining_areas = array(); + foreach ( $areas as $area ) { + $position = array_search( $area['area'], $preferred_order, true ); + if ( false !== $position ) { + $ordered_areas[ $position ] = $area; + } else { + $remaining_areas[] = $area; + } + } + ksort( $ordered_areas ); + $areas = array_merge( array_values( $ordered_areas ), $remaining_areas ); + + foreach ( $areas as $area ) { + $view_list[] = array( + 'title' => $area['label'], + 'slug' => $area['area'], + 'view' => array( + 'filters' => array( + array( + 'field' => 'area', + 'operator' => 'is', + 'value' => $area['area'], + 'isLocked' => true, + ), + ), + ), + ); + } + + $config['view_list'] = $view_list; + + return $config; +} +add_filter( 'get_entity_view_config_postType_wp_template_part', '_gutenberg_get_entity_view_config_postType_wp_template_part', 10, 2 ); + +/** + * Provides the view configuration for the `wp_template` post type. + * + * @param array $config { + * The view configuration for the entity. + * } + * @param array $entity { + * The entity the configuration is built for. + * + * @type string $kind The entity kind. + * @type string $name The entity name. + * @type string $all_items_title The localized "all items" title for the entity. + * } + * @return array The filtered view configuration. + */ +function _gutenberg_get_entity_view_config_postType_wp_template( $config, $entity ) { + $config['default_view'] = array( + 'type' => 'grid', + 'perPage' => 20, + 'sort' => array( + 'field' => 'title', + 'direction' => 'asc', + ), + 'titleField' => 'title', + 'descriptionField' => 'description', + 'mediaField' => 'preview', + 'fields' => array( 'author', 'active', 'slug', 'theme' ), + 'filters' => array(), + 'showMedia' => true, + ); + + $config['default_layouts'] = array( + 'table' => array( 'showMedia' => false ), + 'grid' => array( 'showMedia' => true ), + 'list' => array( 'showMedia' => false ), + ); + + $view_list = array( + array( + 'title' => __( 'All templates', 'gutenberg' ), + 'slug' => 'all', + ), + ); + + $templates = get_block_templates( array(), 'wp_template' ); + + // Collect unique authors, tracking whether they come from a registered + // source (theme, plugin, site) so we can sort those before user ones. + $seen_authors = array(); + $registered_authors = array(); + $user_authors = array(); + foreach ( $templates as $template ) { + /* + * Determine the original source of the template ('theme', 'plugin', + * 'site', or 'user'). + */ + $original_source = 'user'; + if ( 'wp_template' === $template->type || 'wp_template_part' === $template->type ) { + if ( $template->has_theme_file && + ( 'theme' === $template->origin || ( + empty( $template->origin ) && in_array( + $template->source, + array( + 'theme', + 'custom', + ), + true + ) ) + ) + ) { + /* + * Added by theme. + * Template originally provided by a theme, but customized by a user. + * Templates originally didn't have the 'origin' field so identify + * older customized templates by checking for no origin and a 'theme' + * or 'custom' source. + */ + $original_source = 'theme'; + } elseif ( 'plugin' === $template->origin ) { + // Added by plugin. + $original_source = 'plugin'; + } elseif ( empty( $template->has_theme_file ) && 'custom' === $template->source && empty( $template->author ) ) { + /* + * Added by site. + * Template was created from scratch, but has no author. Author support + * was only added to templates in WordPress 5.9. Fallback to showing the + * site logo and title. + */ + $original_source = 'site'; + } + } + + // Determine a human readable text for the author of the template. + $author_text = ''; + switch ( $original_source ) { + case 'theme': + $theme_name = wp_get_theme( $template->theme )->get( 'Name' ); + $author_text = empty( $theme_name ) ? $template->theme : $theme_name; + break; + case 'plugin': + if ( ! function_exists( 'get_plugins' ) ) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + $plugin_name = ''; + if ( isset( $template->plugin ) ) { + $plugins = wp_get_active_and_valid_plugins(); + + foreach ( $plugins as $plugin_file ) { + $plugin_basename = plugin_basename( $plugin_file ); + list( $plugin_slug, ) = explode( '/', $plugin_basename ); + + if ( $plugin_slug === $template->plugin ) { + $plugin_data = get_plugin_data( $plugin_file ); + + if ( ! empty( $plugin_data['Name'] ) ) { + $plugin_name = $plugin_data['Name']; + } + + break; + } + } + } + + /* + * Fall back to the theme name if the plugin is not defined. That's needed to keep backwards + * compatibility with templates that were registered before the plugin attribute was added. + */ + if ( '' === $plugin_name ) { + $plugins = get_plugins(); + $plugin_basename = plugin_basename( sanitize_text_field( $template->theme . '.php' ) ); + if ( isset( $plugins[ $plugin_basename ] ) && isset( $plugins[ $plugin_basename ]['Name'] ) ) { + $plugin_name = $plugins[ $plugin_basename ]['Name']; + } else { + $plugin_name = $template->plugin ?? $template->theme; + } + } + $author_text = $plugin_name; + break; + case 'site': + $author_text = get_bloginfo( 'name' ); + break; + case 'user': + $author = get_user_by( 'id', $template->author ); + if ( ! $author ) { + $author_text = __( 'Unknown author', 'gutenberg' ); + } else { + $author_text = $author->get( 'display_name' ); + } + break; + } + + if ( ! empty( $author_text ) && ! isset( $seen_authors[ $author_text ] ) ) { + $seen_authors[ $author_text ] = true; + $entry = array( + 'title' => $author_text, + 'slug' => $author_text, + 'view' => array( + 'filters' => array( + array( + 'field' => 'author', + 'operator' => 'is', + 'value' => $author_text, + 'isLocked' => true, + ), + ), + ), + ); + if ( 'user' === $original_source ) { + $user_authors[] = $entry; + } else { + $registered_authors[] = $entry; + } + } + } + + $config['view_list'] = array_merge( $view_list, $registered_authors, $user_authors ); + + return $config; +} +add_filter( 'get_entity_view_config_postType_wp_template', '_gutenberg_get_entity_view_config_postType_wp_template', 10, 2 ); diff --git a/lib/load.php b/lib/load.php index 6e537ca727f3a2..760e6ba5d6ea41 100644 --- a/lib/load.php +++ b/lib/load.php @@ -77,6 +77,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-7.0/global-styles.php'; // WordPress 7.1 compat. + require __DIR__ . '/compat/wordpress-7.1/view-config-api.php'; require __DIR__ . '/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php'; require __DIR__ . '/compat/wordpress-7.1/rest-api.php'; require __DIR__ . '/compat/wordpress-7.1/collaboration.php'; From 98c0d5d8563aefe37865f18bc53431b50f848476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 5 Jun 2026 12:49:10 +0200 Subject: [PATCH 2/8] Fix PHPCS violations and add backport changelog entry - Rename per-entity callbacks to snake_case (PHPCS WordPress.NamingConventions) - Remove blank line before class closing brace - Add PR to existing wordpress-develop #11272 backport changelog Co-Authored-By: Claude Opus 4.8 (1M context) --- backport-changelog/7.1/11272.md | 1 + ...enberg-rest-view-config-controller-7-1.php | 1 - lib/compat/wordpress-7.1/view-config-api.php | 20 +++++++++---------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backport-changelog/7.1/11272.md b/backport-changelog/7.1/11272.md index a5a1ddab991ad0..d7be06f1f1251c 100644 --- a/backport-changelog/7.1/11272.md +++ b/backport-changelog/7.1/11272.md @@ -7,3 +7,4 @@ https://github.com/WordPress/wordpress-develop/pull/11272 * https://github.com/WordPress/gutenberg/pull/76953 * https://github.com/WordPress/gutenberg/pull/76903 * https://github.com/WordPress/gutenberg/pull/77290 +* https://github.com/WordPress/gutenberg/pull/78977 diff --git a/lib/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php b/lib/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php index 72960b1352c82e..578d9ecb4135cc 100644 --- a/lib/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php +++ b/lib/compat/wordpress-7.1/class-gutenberg-rest-view-config-controller-7-1.php @@ -676,5 +676,4 @@ private function get_form_schema() { ), ); } - } diff --git a/lib/compat/wordpress-7.1/view-config-api.php b/lib/compat/wordpress-7.1/view-config-api.php index d55515759d744d..ced79eaee3b1bb 100644 --- a/lib/compat/wordpress-7.1/view-config-api.php +++ b/lib/compat/wordpress-7.1/view-config-api.php @@ -113,7 +113,7 @@ function gutenberg_get_entity_view_config( $kind, $name ) { * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_postType_page( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_page( $config, $entity ) { $config['default_layouts'] = array( 'table' => array( 'layout' => array( @@ -300,7 +300,7 @@ function _gutenberg_get_entity_view_config_postType_page( $config, $entity ) { return $config; } -add_filter( 'get_entity_view_config_postType_page', '_gutenberg_get_entity_view_config_postType_page', 10, 2 ); +add_filter( 'get_entity_view_config_postType_page', '_gutenberg_get_entity_view_config_post_type_page', 10, 2 ); /** * Provides the view configuration for the `post` post type. @@ -317,7 +317,7 @@ function _gutenberg_get_entity_view_config_postType_page( $config, $entity ) { * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_postType_post( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_post( $config, $entity ) { $config['form'] = array( 'layout' => array( 'type' => 'panel' ), 'fields' => array( @@ -383,7 +383,7 @@ function _gutenberg_get_entity_view_config_postType_post( $config, $entity ) { return $config; } -add_filter( 'get_entity_view_config_postType_post', '_gutenberg_get_entity_view_config_postType_post', 10, 2 ); +add_filter( 'get_entity_view_config_postType_post', '_gutenberg_get_entity_view_config_post_type_post', 10, 2 ); /** * Provides the view configuration for the `wp_block` post type. @@ -400,7 +400,7 @@ function _gutenberg_get_entity_view_config_postType_post( $config, $entity ) { * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_postType_wp_block( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_wp_block( $config, $entity ) { $config['default_layouts'] = array( 'table' => array( 'layout' => array( @@ -479,7 +479,7 @@ function _gutenberg_get_entity_view_config_postType_wp_block( $config, $entity ) return $config; } -add_filter( 'get_entity_view_config_postType_wp_block', '_gutenberg_get_entity_view_config_postType_wp_block', 10, 2 ); +add_filter( 'get_entity_view_config_postType_wp_block', '_gutenberg_get_entity_view_config_post_type_wp_block', 10, 2 ); /** * Provides the view configuration for the `wp_template_part` post type. @@ -496,7 +496,7 @@ function _gutenberg_get_entity_view_config_postType_wp_block( $config, $entity ) * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_postType_wp_template_part( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_wp_template_part( $config, $entity ) { $config['default_layouts'] = array( 'table' => array( 'layout' => array( @@ -567,7 +567,7 @@ function _gutenberg_get_entity_view_config_postType_wp_template_part( $config, $ return $config; } -add_filter( 'get_entity_view_config_postType_wp_template_part', '_gutenberg_get_entity_view_config_postType_wp_template_part', 10, 2 ); +add_filter( 'get_entity_view_config_postType_wp_template_part', '_gutenberg_get_entity_view_config_post_type_wp_template_part', 10, 2 ); /** * Provides the view configuration for the `wp_template` post type. @@ -584,7 +584,7 @@ function _gutenberg_get_entity_view_config_postType_wp_template_part( $config, $ * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_postType_wp_template( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_wp_template( $config, $entity ) { $config['default_view'] = array( 'type' => 'grid', 'perPage' => 20, @@ -748,4 +748,4 @@ function _gutenberg_get_entity_view_config_postType_wp_template( $config, $entit return $config; } -add_filter( 'get_entity_view_config_postType_wp_template', '_gutenberg_get_entity_view_config_postType_wp_template', 10, 2 ); +add_filter( 'get_entity_view_config_postType_wp_template', '_gutenberg_get_entity_view_config_post_type_wp_template', 10, 2 ); From e46da1f89716e7bd057f4b4b3395943838a24723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 5 Jun 2026 12:52:46 +0200 Subject: [PATCH 3/8] Drop unused $entity param from view config callbacks PHPCS flags the unused parameter; callbacks that don't read entity context now accept only $config and register with one accepted arg. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/compat/wordpress-7.1/view-config-api.php | 44 ++++---------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/lib/compat/wordpress-7.1/view-config-api.php b/lib/compat/wordpress-7.1/view-config-api.php index ced79eaee3b1bb..ae05e3d34cd378 100644 --- a/lib/compat/wordpress-7.1/view-config-api.php +++ b/lib/compat/wordpress-7.1/view-config-api.php @@ -308,16 +308,9 @@ function _gutenberg_get_entity_view_config_post_type_page( $config, $entity ) { * @param array $config { * The view configuration for the entity. * } - * @param array $entity { - * The entity the configuration is built for. - * - * @type string $kind The entity kind. - * @type string $name The entity name. - * @type string $all_items_title The localized "all items" title for the entity. - * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_post_type_post( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_post( $config ) { $config['form'] = array( 'layout' => array( 'type' => 'panel' ), 'fields' => array( @@ -383,7 +376,7 @@ function _gutenberg_get_entity_view_config_post_type_post( $config, $entity ) { return $config; } -add_filter( 'get_entity_view_config_postType_post', '_gutenberg_get_entity_view_config_post_type_post', 10, 2 ); +add_filter( 'get_entity_view_config_postType_post', '_gutenberg_get_entity_view_config_post_type_post', 10, 1 ); /** * Provides the view configuration for the `wp_block` post type. @@ -391,16 +384,9 @@ function _gutenberg_get_entity_view_config_post_type_post( $config, $entity ) { * @param array $config { * The view configuration for the entity. * } - * @param array $entity { - * The entity the configuration is built for. - * - * @type string $kind The entity kind. - * @type string $name The entity name. - * @type string $all_items_title The localized "all items" title for the entity. - * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_post_type_wp_block( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_wp_block( $config ) { $config['default_layouts'] = array( 'table' => array( 'layout' => array( @@ -479,7 +465,7 @@ function _gutenberg_get_entity_view_config_post_type_wp_block( $config, $entity return $config; } -add_filter( 'get_entity_view_config_postType_wp_block', '_gutenberg_get_entity_view_config_post_type_wp_block', 10, 2 ); +add_filter( 'get_entity_view_config_postType_wp_block', '_gutenberg_get_entity_view_config_post_type_wp_block', 10, 1 ); /** * Provides the view configuration for the `wp_template_part` post type. @@ -487,16 +473,9 @@ function _gutenberg_get_entity_view_config_post_type_wp_block( $config, $entity * @param array $config { * The view configuration for the entity. * } - * @param array $entity { - * The entity the configuration is built for. - * - * @type string $kind The entity kind. - * @type string $name The entity name. - * @type string $all_items_title The localized "all items" title for the entity. - * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_post_type_wp_template_part( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_wp_template_part( $config ) { $config['default_layouts'] = array( 'table' => array( 'layout' => array( @@ -567,7 +546,7 @@ function _gutenberg_get_entity_view_config_post_type_wp_template_part( $config, return $config; } -add_filter( 'get_entity_view_config_postType_wp_template_part', '_gutenberg_get_entity_view_config_post_type_wp_template_part', 10, 2 ); +add_filter( 'get_entity_view_config_postType_wp_template_part', '_gutenberg_get_entity_view_config_post_type_wp_template_part', 10, 1 ); /** * Provides the view configuration for the `wp_template` post type. @@ -575,16 +554,9 @@ function _gutenberg_get_entity_view_config_post_type_wp_template_part( $config, * @param array $config { * The view configuration for the entity. * } - * @param array $entity { - * The entity the configuration is built for. - * - * @type string $kind The entity kind. - * @type string $name The entity name. - * @type string $all_items_title The localized "all items" title for the entity. - * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_post_type_wp_template( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_wp_template( $config ) { $config['default_view'] = array( 'type' => 'grid', 'perPage' => 20, @@ -748,4 +720,4 @@ function _gutenberg_get_entity_view_config_post_type_wp_template( $config, $enti return $config; } -add_filter( 'get_entity_view_config_postType_wp_template', '_gutenberg_get_entity_view_config_post_type_wp_template', 10, 2 ); +add_filter( 'get_entity_view_config_postType_wp_template', '_gutenberg_get_entity_view_config_post_type_wp_template', 10, 1 ); From 9b59e849820294a0cd7eb1c97c8ae76e28372c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 5 Jun 2026 13:37:33 +0200 Subject: [PATCH 4/8] Check filtered config --- lib/compat/wordpress-7.1/view-config-api.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/compat/wordpress-7.1/view-config-api.php b/lib/compat/wordpress-7.1/view-config-api.php index ae05e3d34cd378..3303c7de25b388 100644 --- a/lib/compat/wordpress-7.1/view-config-api.php +++ b/lib/compat/wordpress-7.1/view-config-api.php @@ -87,7 +87,16 @@ function gutenberg_get_entity_view_config( $kind, $name ) { * @type string $all_items_title The localized "all items" title for the entity. * } */ - return apply_filters( + /** + * Filters the view configuration for a given entity. + * + * Because this filter is intended to be used by third parties, a callback may + * accidentally return a non-array value, drop one of the expected top-level + * keys, or add unexpected ones. Normalize the result back to an array and + * re-merge it with the defaults so consumers can always rely on exactly the + * documented top-level keys being present. + */ + $filtered_config = apply_filters( "get_entity_view_config_{$kind}_{$name}", $config, array( @@ -96,6 +105,15 @@ function gutenberg_get_entity_view_config( $kind, $name ) { 'all_items_title' => $all_items_title, ) ); + + if ( ! is_array( $filtered_config ) ) { + $filtered_config = array(); + } + + // Backfill any dropped keys with their defaults, then discard any keys the + // filter introduced that are not part of the documented configuration shape. + $filtered_config = array_merge( $config, $filtered_config ); + return array_intersect_key( $filtered_config, $config ); } /** From a4f059985c41028824854f09d1fd787ab11b7158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 9 Jun 2026 10:57:05 +0200 Subject: [PATCH 5/8] Remove unnecessary docblock --- lib/compat/wordpress-7.1/view-config-api.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/compat/wordpress-7.1/view-config-api.php b/lib/compat/wordpress-7.1/view-config-api.php index 3303c7de25b388..986e3ea7077a56 100644 --- a/lib/compat/wordpress-7.1/view-config-api.php +++ b/lib/compat/wordpress-7.1/view-config-api.php @@ -87,15 +87,6 @@ function gutenberg_get_entity_view_config( $kind, $name ) { * @type string $all_items_title The localized "all items" title for the entity. * } */ - /** - * Filters the view configuration for a given entity. - * - * Because this filter is intended to be used by third parties, a callback may - * accidentally return a non-array value, drop one of the expected top-level - * keys, or add unexpected ones. Normalize the result back to an array and - * re-merge it with the defaults so consumers can always rely on exactly the - * documented top-level keys being present. - */ $filtered_config = apply_filters( "get_entity_view_config_{$kind}_{$name}", $config, From 498aa8a9bfcc8966e22f8195d472a0b6e5324770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 9 Jun 2026 10:58:57 +0200 Subject: [PATCH 6/8] Improve readibility --- lib/compat/wordpress-7.1/view-config-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-7.1/view-config-api.php b/lib/compat/wordpress-7.1/view-config-api.php index 986e3ea7077a56..124c97ab987f24 100644 --- a/lib/compat/wordpress-7.1/view-config-api.php +++ b/lib/compat/wordpress-7.1/view-config-api.php @@ -98,7 +98,7 @@ function gutenberg_get_entity_view_config( $kind, $name ) { ); if ( ! is_array( $filtered_config ) ) { - $filtered_config = array(); + return $config; } // Backfill any dropped keys with their defaults, then discard any keys the From 8959e675bae21e8c0b5e33d3298976658c7990fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:09:25 +0200 Subject: [PATCH 7/8] Remove future core filters --- lib/compat/wordpress-7.1/view-config-api.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/compat/wordpress-7.1/view-config-api.php b/lib/compat/wordpress-7.1/view-config-api.php index 124c97ab987f24..6c239df22340ae 100644 --- a/lib/compat/wordpress-7.1/view-config-api.php +++ b/lib/compat/wordpress-7.1/view-config-api.php @@ -309,6 +309,9 @@ function _gutenberg_get_entity_view_config_post_type_page( $config, $entity ) { return $config; } +if ( has_filter( 'get_entity_view_config_postType_page', '_wp_get_entity_view_config_post_type_page' ) ) { + remove_filter( 'get_entity_view_config_postType_page', '_wp_get_entity_view_config_post_type_page' ); +} add_filter( 'get_entity_view_config_postType_page', '_gutenberg_get_entity_view_config_post_type_page', 10, 2 ); /** @@ -385,6 +388,9 @@ function _gutenberg_get_entity_view_config_post_type_post( $config ) { return $config; } +if ( has_filter( 'get_entity_view_config_postType_post', '_wp_get_entity_view_config_post_type_post' ) ) { + remove_filter( 'get_entity_view_config_postType_post', '_wp_get_entity_view_config_post_type_post' ); +} add_filter( 'get_entity_view_config_postType_post', '_gutenberg_get_entity_view_config_post_type_post', 10, 1 ); /** @@ -474,6 +480,9 @@ function _gutenberg_get_entity_view_config_post_type_wp_block( $config ) { return $config; } +if ( has_filter( 'get_entity_view_config_postType_wp_block', '_wp_get_entity_view_config_post_type_wp_block' ) ) { + remove_filter( 'get_entity_view_config_postType_wp_block', '_wp_get_entity_view_config_post_type_wp_block' ); +} add_filter( 'get_entity_view_config_postType_wp_block', '_gutenberg_get_entity_view_config_post_type_wp_block', 10, 1 ); /** @@ -555,6 +564,9 @@ function _gutenberg_get_entity_view_config_post_type_wp_template_part( $config ) return $config; } +if ( has_filter( 'get_entity_view_config_postType_wp_template_part', '_wp_get_entity_view_config_post_type_wp_template_part' ) ) { + remove_filter( 'get_entity_view_config_postType_wp_template_part', '_wp_get_entity_view_config_post_type_wp_template_part' ); +} add_filter( 'get_entity_view_config_postType_wp_template_part', '_gutenberg_get_entity_view_config_post_type_wp_template_part', 10, 1 ); /** @@ -729,4 +741,7 @@ function _gutenberg_get_entity_view_config_post_type_wp_template( $config ) { return $config; } +if ( has_filter( 'get_entity_view_config_postType_wp_template', '_wp_get_entity_view_config_post_type_wp_template' ) ) { + remove_filter( 'get_entity_view_config_postType_wp_template', '_wp_get_entity_view_config_post_type_wp_template' ); +} add_filter( 'get_entity_view_config_postType_wp_template', '_gutenberg_get_entity_view_config_post_type_wp_template', 10, 1 ); From b28a21e8298ebef307c1d1c08b73cafc13eed038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:20:51 +0200 Subject: [PATCH 8/8] Remove all_items_title from the entity config --- lib/compat/wordpress-7.1/view-config-api.php | 28 +++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/lib/compat/wordpress-7.1/view-config-api.php b/lib/compat/wordpress-7.1/view-config-api.php index 6c239df22340ae..e58c803471b5b1 100644 --- a/lib/compat/wordpress-7.1/view-config-api.php +++ b/lib/compat/wordpress-7.1/view-config-api.php @@ -82,18 +82,16 @@ function gutenberg_get_entity_view_config( $kind, $name ) { * @param array $entity { * The entity the configuration is built for. * - * @type string $kind The entity kind. - * @type string $name The entity name. - * @type string $all_items_title The localized "all items" title for the entity. + * @type string $kind The entity kind. + * @type string $name The entity name. * } */ $filtered_config = apply_filters( "get_entity_view_config_{$kind}_{$name}", $config, array( - 'kind' => $kind, - 'name' => $name, - 'all_items_title' => $all_items_title, + 'kind' => $kind, + 'name' => $name, ) ); @@ -113,16 +111,9 @@ function gutenberg_get_entity_view_config( $kind, $name ) { * @param array $config { * The view configuration for the entity. * } - * @param array $entity { - * The entity the configuration is built for. - * - * @type string $kind The entity kind. - * @type string $name The entity name. - * @type string $all_items_title The localized "all items" title for the entity. - * } * @return array The filtered view configuration. */ -function _gutenberg_get_entity_view_config_post_type_page( $config, $entity ) { +function _gutenberg_get_entity_view_config_post_type_page( $config ) { $config['default_layouts'] = array( 'table' => array( 'layout' => array( @@ -152,10 +143,9 @@ function _gutenberg_get_entity_view_config_post_type_page( $config, $entity ) { ); $config['view_list'] = array( - array( - 'title' => $entity['all_items_title'], - 'slug' => 'all', - ), + // Reuse the base "all items" view, whose title is derived from the post + // type's `all_items` label in gutenberg_get_entity_view_config(). + $config['view_list'][0], array( 'title' => __( 'Published', 'gutenberg' ), 'slug' => 'published', @@ -312,7 +302,7 @@ function _gutenberg_get_entity_view_config_post_type_page( $config, $entity ) { if ( has_filter( 'get_entity_view_config_postType_page', '_wp_get_entity_view_config_post_type_page' ) ) { remove_filter( 'get_entity_view_config_postType_page', '_wp_get_entity_view_config_post_type_page' ); } -add_filter( 'get_entity_view_config_postType_page', '_gutenberg_get_entity_view_config_post_type_page', 10, 2 ); +add_filter( 'get_entity_view_config_postType_page', '_gutenberg_get_entity_view_config_post_type_page', 10, 1 ); /** * Provides the view configuration for the `post` post type.