Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Site Editor]: Add fallback template content on creation #42520

Merged
merged 7 commits into from Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 60 additions & 0 deletions lib/compat/wordpress-6.1/block-template-utils.php
Expand Up @@ -296,3 +296,63 @@ function gutenberg_build_block_template_result_from_post( $post ) {
}
return $template;
}

/**
* Helper function to get the Template Hierarchy for a given slug.
* We need to Handle special cases here like `front-page`, `singular` and `archive` templates.
*
* Noting that we always add `index` as the last fallback template.
*
* @param string $slug The template slug to be created.
* @param boolean $is_custom Indicates if a template is custom or part of the template hierarchy.
* @param string $template_prefix The template prefix for the created template. This is used to extract the main template type ex. in `taxonomy-books` we extract the `taxonomy`.
*
* @return array<string> The template hierarchy.
*/
function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {
Comment on lines +311 to +312
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One concern with moving this logic to the server is accidentally committing to supporting a function forever. What do you think of:

Suggested change
*/
function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {
* @access private
* @internal
*/
function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {
Suggested change
*/
function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {
*/
function _gutenberg_get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should have a gutenberg prefix in core, also the logic in this function would need to change if the template hierarchy logic changes, and I don't imagine this will happen any time soon.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish we had something like https://github.com/Brain-WP/Hierarchy in the core.

if ( 'index' === $slug ) {
return array( 'index' );
}
if ( $is_custom ) {
return array( 'page', 'singular', 'index' );
}
if ( 'front-page' === $slug ) {
return array( 'front-page', 'home', 'index' );
}
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
$template_hierarchy = array( $slug );
// Most default templates don't have `$template_prefix` assigned.
if ( $template_prefix ) {
list($type) = explode( '-', $template_prefix );
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
// We need these checks because we always add the `$slug` above.
if ( ! in_array( $template_prefix, array( $slug, $type ), true ) ) {
$template_hierarchy[] = $template_prefix;
}
if ( $slug !== $type ) {
$template_hierarchy[] = $type;
}
}
// Handle `archive` template.
if (
str_starts_with( $slug, 'author' ) ||
str_starts_with( $slug, 'taxonomy' ) ||
str_starts_with( $slug, 'category' ) ||
str_starts_with( $slug, 'tag' ) ||
'date' === $slug
) {
$template_hierarchy[] = 'archive';
}
// Handle `single` template.
if ( 'attachment' === $slug ) {
$template_hierarchy[] = 'single';
}
// Handle `singular` template.
if (
str_starts_with( $slug, 'single' ) ||
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
str_starts_with( $slug, 'page' ) ||
'attachment' === $slug
) {
$template_hierarchy[] = 'singular';
}
$template_hierarchy[] = 'index';
return $template_hierarchy;
};
Expand Up @@ -10,6 +10,62 @@
* Base Templates REST API Controller.
*/
class Gutenberg_REST_Templates_Controller extends WP_REST_Templates_Controller {

/**
* Registers the controllers routes.
*
* @return void
*/
public function register_routes() {
// Get fallback template content.
register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/lookup',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_template_fallback' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => array(
'slug' => array(
'description' => __( 'The slug of the template to get the fallback for', 'gutenberg' ),
'type' => 'string',
),
'is_custom' => array(
'description' => __( ' Indicates if a template is custom or part of the template hierarchy', 'gutenberg' ),
'type' => 'boolean',
),
'template_prefix' => array(
'description' => __( 'The template prefix for the created template. This is used to extract the main template type ex. in `taxonomy-books` we extract the `taxonomy`', 'gutenberg' ),
'type' => 'string',
),
),
),
)
);
parent::register_routes();
}

/**
* Returns the fallback template for a given slug.
*
* @param WP_REST_Request $request The request instance.
*
* @return WP_REST_Response|WP_Error
*/
public function get_template_fallback( $request ) {
if ( empty( $request['slug'] ) ) {
return new WP_Error(
'rest_invalid_param',
__( 'Invalid slug.', 'gutenberg' ),
array( 'status' => 400 )
);
}
$hierarchy = get_template_hierarchy( $request['slug'], $request['is_custom'], $request['template_prefix'] );
$fallback_template = resolve_block_template( $request['slug'], $hierarchy, '' );
return rest_ensure_response( $fallback_template );
}

/**
* Returns a list of templates.
*
Expand Down
Expand Up @@ -192,9 +192,18 @@ function AddCustomTemplateModal( { onClose, onSelect, entityForSuggestions } ) {
isBlock
as={ Button }
onClick={ () => {
const { slug, title, description } =
entityForSuggestions.template;
onSelect( { slug, title, description } );
const {
slug,
title,
description,
templatePrefix,
} = entityForSuggestions.template;
onSelect( {
slug,
title,
description,
templatePrefix,
} );
} }
>
<Text as="span" weight={ 600 }>
Expand Down
@@ -1,6 +1,8 @@
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import {
DropdownMenu,
MenuGroup,
Expand Down Expand Up @@ -91,7 +93,19 @@ export default function NewTemplate( { postType } ) {

async function createTemplate( template, isWPSuggestion = true ) {
try {
const { title, description, slug } = template;
const { title, description, slug, templatePrefix } = template;
let templateContent = template.content;
// Try to find fallback content from existing templates.
if ( ! templateContent ) {
const fallbackTemplate = await apiFetch( {
path: addQueryArgs( '/wp/v2/templates/lookup', {
slug,
is_custom: ! isWPSuggestion,
template_prefix: templatePrefix,
} ),
} );
templateContent = fallbackTemplate.content;
}
const newTemplate = await saveEntityRecord(
'postType',
'wp_template',
Expand All @@ -101,6 +115,7 @@ export default function NewTemplate( { postType } ) {
slug: slug.toString(),
status: 'publish',
title,
content: templateContent,
// This adds a post meta field in template that is part of `is_custom` value calculation.
is_wp_suggestion: isWPSuggestion,
},
Expand Down
17 changes: 14 additions & 3 deletions packages/edit-site/src/components/add-new-template/utils.js
Expand Up @@ -152,7 +152,10 @@ export const usePostTypeMenuItems = ( onClickMenuItem ) => {
);
}
const menuItem = defaultTemplateType
? { ...defaultTemplateType }
? {
...defaultTemplateType,
templatePrefix: templatePrefixes[ slug ],
}
: {
slug: generalTemplateSlug,
title: menuItemTitle,
Expand All @@ -167,6 +170,7 @@ export const usePostTypeMenuItems = ( onClickMenuItem ) => {
icon: icon?.startsWith( 'dashicons-' )
? icon.slice( 10 )
: post,
templatePrefix: templatePrefixes[ slug ],
};
const hasEntities = postTypesInfo?.[ slug ]?.hasEntities;
// We have a different template creation flow only if they have entities.
Expand Down Expand Up @@ -210,6 +214,7 @@ export const usePostTypeMenuItems = ( onClickMenuItem ) => {
title,
description,
slug: `${ templatePrefixes[ slug ] }-${ suggestion.slug }`,
templatePrefix: templatePrefixes[ slug ],
};
},
},
Expand Down Expand Up @@ -317,7 +322,10 @@ export const useTaxonomiesMenuItems = ( onClickMenuItem ) => {
);
}
const menuItem = defaultTemplateType
? { ...defaultTemplateType }
? {
...defaultTemplateType,
templatePrefix: templatePrefixes[ slug ],
}
: {
slug: generalTemplateSlug,
title: menuItemTitle,
Expand All @@ -327,6 +335,7 @@ export const useTaxonomiesMenuItems = ( onClickMenuItem ) => {
labels.singular_name
),
icon: blockMeta,
templatePrefix: templatePrefixes[ slug ],
};
const hasEntities = taxonomiesInfo?.[ slug ]?.hasEntities;
// We have a different template creation flow only if they have entities.
Expand Down Expand Up @@ -369,6 +378,7 @@ export const useTaxonomiesMenuItems = ( onClickMenuItem ) => {
title,
description,
slug: `${ templatePrefixes[ slug ] }-${ suggestion.slug }`,
templatePrefix: templatePrefixes[ slug ],
};
},
},
Expand Down Expand Up @@ -455,7 +465,7 @@ export function useAuthorMenuItem( onClickMenuItem ) {
( { slug } ) => slug === 'author'
);
if ( authorInfo.user?.hasEntities ) {
authorMenuItem = { ...authorMenuItem };
authorMenuItem = { ...authorMenuItem, templatePrefix: 'author' };
authorMenuItem.onClick = ( template ) => {
onClickMenuItem( {
type: 'root',
Expand Down Expand Up @@ -494,6 +504,7 @@ export function useAuthorMenuItem( onClickMenuItem ) {
title,
description,
slug: `author-${ suggestion.slug }`,
templatePrefix: 'author',
};
},
},
Expand Down