diff --git a/lib/template-loader.php b/lib/template-loader.php index 03ebd05bfaea92..1d82d4d346ca37 100644 --- a/lib/template-loader.php +++ b/lib/template-loader.php @@ -336,7 +336,7 @@ function gutenberg_find_template_post_and_parts( $template_type, $template_hiera if ( $current_template_post ) { $template_part_ids = array(); - if ( is_admin() ) { + if ( is_admin() || defined( 'REST_REQUEST' ) ) { foreach ( parse_blocks( $current_template_post->post_content ) as $block ) { $template_part_ids = array_merge( $template_part_ids, create_auto_draft_for_template_part_block( $block ) ); } diff --git a/lib/template-parts.php b/lib/template-parts.php index 7edf730bcaacd0..0edc337c1e9680 100644 --- a/lib/template-parts.php +++ b/lib/template-parts.php @@ -141,14 +141,22 @@ function gutenberg_render_template_part_list_table_column( $column_name, $post_i /** - * Filter for adding a `theme` parameter to `wp_template_part` queries. + * Filter for adding a `resolved`, a `template`, and a `theme` parameter to `wp_template_part` queries. * * @param array $query_params The query parameters. * @return array Filtered $query_params. */ function filter_rest_wp_template_part_collection_params( $query_params ) { $query_params += array( - 'theme' => array( + 'resolved' => array( + 'description' => __( 'Whether to filter for resolved template parts.', 'gutenberg' ), + 'type' => 'boolean', + ), + 'template' => array( + 'description' => __( 'The template slug for the template that the template part is used by.', 'gutenberg' ), + 'type' => 'string', + ), + 'theme' => array( 'description' => __( 'The theme slug for the theme that created the template part.', 'gutenberg' ), 'type' => 'string', ), @@ -158,13 +166,46 @@ function filter_rest_wp_template_part_collection_params( $query_params ) { apply_filters( 'rest_wp_template_part_collection_params', 'filter_rest_wp_template_part_collection_params', 99, 1 ); /** - * Filter for supporting the `theme` parameter in `wp_template_part` queries. + * Filter for supporting the `resolved`, `template`, and `theme` parameters in `wp_template_part` queries. * * @param array $args The query arguments. * @param WP_REST_Request $request The request object. * @return array Filtered $args. */ function filter_rest_wp_template_part_query( $args, $request ) { + /** + * Unlike `filter_rest_wp_template_query`, we resolve queries also if there's only a `template` argument set. + * The difference is that in the case of templates, we can use the `slug` field that already exists (as part + * of the entities endpoint, wheras for template parts, we have to register the extra `template` argument), + * so we need the `resolved` flag to convey the different semantics (only return 'resolved' templates that match + * the `slug` vs return _all_ templates that match it (e.g. including all auto-drafts)). + * + * A template parts query with a `template` arg but not a `resolved` one is conceivable, but probably wouldn't be + * very useful: It'd be all template parts for all templates matching that `template` slug (including auto-drafts etc). + * + * @see filter_rest_wp_template_query + * @see filter_rest_wp_template_part_collection_params + * @see https://github.com/WordPress/gutenberg/pull/21878#discussion_r436961706 + */ + if ( $request['resolved'] || $request['template'] ) { + $template_part_ids = array( 0 ); // Return nothing by default (the 0 is needed for `post__in`). + $template_types = $request['template'] ? array( $request['template'] ) : get_template_types(); + + foreach ( $template_types as $template_type ) { + // Skip 'embed' for now because it is not a regular template type. + if ( in_array( $template_type, array( 'embed' ), true ) ) { + continue; + } + + $current_template = gutenberg_find_template_post_and_parts( $template_type ); + if ( isset( $current_template ) ) { + $template_part_ids = $template_part_ids + $current_template['template_part_ids']; + } + } + $args['post__in'] = $template_part_ids; + $args['post_status'] = array( 'publish', 'auto-draft' ); + } + if ( $request['theme'] ) { $meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array(); $meta_query[] = array( @@ -174,6 +215,7 @@ function filter_rest_wp_template_part_query( $args, $request ) { $args['meta_query'] = $meta_query; } + return $args; } add_filter( 'rest_wp_template_part_query', 'filter_rest_wp_template_part_query', 99, 2 ); diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index 8b525cb048de75..fe9eb71ada311f 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -36,33 +36,27 @@ export default function Header( { const { deviceType, hasFixedToolbar, - homeTemplateId, templateId, templatePartId, templateType, - templatePartIds, page, showOnFront, } = useSelect( ( select ) => { const { __experimentalGetPreviewDeviceType, isFeatureActive, - getHomeTemplateId, getTemplateId, getTemplatePartId, getTemplateType, - getTemplatePartIds, getPage, getShowOnFront, } = select( 'core/edit-site' ); return { deviceType: __experimentalGetPreviewDeviceType(), hasFixedToolbar: isFeatureActive( 'fixedToolbar' ), - homeTemplateId: getHomeTemplateId(), templateId: getTemplateId(), templatePartId: getTemplatePartId(), templateType: getTemplateType(), - templatePartIds: getTemplatePartIds(), page: getPage(), showOnFront: getShowOnFront(), }; @@ -116,11 +110,9 @@ export default function Header( { / false ); }; + const registry = useRegistry(); + const [ homeId, setHomeId ] = useState(); + + useEffect( () => { + findTemplate( + '/', + registry.__experimentalResolveSelect( 'core' ).getEntityRecords + ).then( + ( newHomeId ) => setHomeId( newHomeId ), + () => setHomeId( null ) + ); + }, [ registry ] ); + const { currentTheme, template, templateParts } = useSelect( ( select ) => { - const { getCurrentTheme, getEntityRecord } = select( 'core' ); + const { + getCurrentTheme, + getEntityRecord, + getEntityRecords, + } = select( 'core' ); + const _template = getEntityRecord( 'postType', 'wp_template', activeId ); + return { currentTheme: getCurrentTheme(), - template: { - label: _template ? ( - - ) : ( - __( 'Loading…' ) - ), - value: activeId, - slug: _template ? _template.slug : __( 'Loading…' ), - content: _template?.content, - }, - templateParts: templatePartIds.map( ( id ) => { - const templatePart = getEntityRecord( - 'postType', - 'wp_template_part', - id - ); - return { - label: templatePart ? ( - - ) : ( - __( 'Loading…' ) - ), - value: id, - slug: templatePart - ? templatePart.slug - : __( 'Loading…' ), - }; - } ), + template: _template, + templateParts: _template + ? getEntityRecords( 'postType', 'wp_template_part', { + resolved: true, + template: _template.slug, + } ) + : null, }; }, - [ activeId, templatePartIds, homeId ] + [ activeId ] ); + const templateItem = { + label: template ? ( + + ) : ( + __( 'Loading…' ) + ), + value: activeId, + slug: template ? template.slug : __( 'Loading…' ), + content: template?.content, + }; + + const templatePartItems = templateParts?.map( ( templatePart ) => ( { + label: , + value: templatePart.id, + slug: templatePart.slug, + } ) ); + const overwriteSlug = TEMPLATE_OVERRIDES[ page.type ] && page.slug && @@ -125,10 +136,10 @@ export default function TemplateSwitcher( { slug: overwriteSlug, title: overwriteSlug, status: 'publish', - content: template.content.raw, + content: templateItem.content.raw, } ); const revertToParent = async () => { - onRemoveTemplate( template.value ); + onRemoveTemplate( activeId ); }; return ( <> @@ -141,8 +152,8 @@ export default function TemplateSwitcher( { label={ __( 'Switch Template' ) } toggleProps={ { children: ( isTemplatePart - ? templateParts - : [ template ] + ? templatePartItems + : [ templateItem ] ).find( ( choice ) => choice.value === @@ -156,10 +167,10 @@ export default function TemplateSwitcher( { onActiveIdChange( activeId ) } > - { template.label } + { templateItem.label } { overwriteSlug && - overwriteSlug !== template.slug && ( + overwriteSlug !== templateItem.slug && ( ) } - { overwriteSlug === template.slug && ( + { overwriteSlug === templateItem.slug && (