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 1 commit
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 @@ -27,16 +27,17 @@ public function register_routes() {
'callback' => array( $this, 'get_template_fallback' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => array(
'slug' => array(
'slug' => array(
'description' => __( 'The slug of the template to get the fallback for', 'gutenberg' ),
'type' => 'string',
),
'hierarchy' => array(
'description' => __( 'The template hierarchy to use to resolve the fallback template', 'gutenberg' ),
'type' => 'array',
'items' => array(
'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',
),
),
),
Expand All @@ -60,14 +61,8 @@ public function get_template_fallback( $request ) {
array( 'status' => 400 )
);
}
if ( empty( $request['hierarchy'] ) || ! is_array( $request['hierarchy'] ) ) {
return new WP_Error(
'rest_invalid_param',
__( 'Invalid hierarchy.', 'gutenberg' ),
array( 'status' => 400 )
);
}
$fallback_template = resolve_block_template( $request['slug'], $request['hierarchy'], '' );
$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 );
}

Expand Down
Expand Up @@ -41,7 +41,6 @@ import {
useTaxonomiesMenuItems,
usePostTypeMenuItems,
useAuthorMenuItem,
getTemplateHierarchy,
} from './utils';
import AddCustomGenericTemplateModal from './add-custom-generic-template-modal';
import { useHistory } from '../routes';
Expand Down Expand Up @@ -98,14 +97,11 @@ export default function NewTemplate( { postType } ) {
let templateContent = template.content;
// Try to find fallback content from existing templates.
if ( ! templateContent ) {
const templateHierarchy = getTemplateHierarchy( slug, {
templatePrefix,
isCustom: ! isWPSuggestion,
} );
const fallbackTemplate = await apiFetch( {
path: addQueryArgs( '/wp/v2/templates/lookup', {
slug,
hierarchy: templateHierarchy,
is_custom: ! isWPSuggestion,
template_prefix: templatePrefix,
} ),
} );
templateContent = fallbackTemplate.content;
Expand Down
64 changes: 2 additions & 62 deletions packages/edit-site/src/components/add-new-template/utils.js
Expand Up @@ -465,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 @@ -504,6 +504,7 @@ export function useAuthorMenuItem( onClickMenuItem ) {
title,
description,
slug: `author-${ suggestion.slug }`,
templatePrefix: 'author',
};
},
},
Expand Down Expand Up @@ -664,64 +665,3 @@ const useEntitiesInfo = (
);
return entitiesInfo;
};

/**
* 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} templateSlug The template slug to be created.
* @param {Object} meta Metadata about the template to be created.
* @param {boolean} meta.isCustom Indicates if a template is custom or part of the template hierarchy.
* @param {string} meta.templatePrefix 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 {string[]} The template hierarchy.
*/
export const getTemplateHierarchy = ( templateSlug, meta ) => {
if ( templateSlug === 'index' ) {
return [ 'index' ];
}
if ( meta?.isCustom ) {
return [ 'page', 'singular', 'index' ];
}
if ( templateSlug === 'front-page' ) {
return [ 'front-page', 'home', 'index' ];
}
const templateHierarchy = [ templateSlug ];
// Most default templates don't have `templatePrefix` property.
if ( meta?.templatePrefix ) {
const [ type ] = meta.templatePrefix.split( '-' );
// We need these checks because we always add the `templateSlug` above.
if ( ! [ templateSlug, type ].includes( meta.templatePrefix ) ) {
templateHierarchy.push( meta.templatePrefix );
}
if ( templateSlug !== type ) {
templateHierarchy.push( type );
}
}
// Handle `archive` template.
if (
templateSlug.startsWith( 'author' ) ||
templateSlug.startsWith( 'taxonomy' ) ||
templateSlug.startsWith( 'category' ) ||
templateSlug.startsWith( 'tag' ) ||
templateSlug === 'date'
) {
templateHierarchy.push( 'archive' );
}
// Handle `single` template.
if ( templateSlug === 'attachment' ) {
templateHierarchy.push( 'single' );
}
// Handle `singular` template.
if (
templateSlug.startsWith( 'single' ) ||
templateSlug.startsWith( 'page' ) ||
templateSlug === 'attachment'
) {
templateHierarchy.push( 'singular' );
}
templateHierarchy.push( 'index' );
return templateHierarchy;
};
164 changes: 164 additions & 0 deletions phpunit/block-template-utils-test.php
@@ -0,0 +1,164 @@
<?php
/**
* Tests_Block_Template_Utils class
*
* @package WordPress
*/

class Tests_Block_Template_Utils extends WP_UnitTestCase {
public function set_up() {
parent::set_up();
switch_theme( 'emptytheme' );
}

public function test_get_template_hierarchy() {
$hierarchy = get_template_hierarchy( 'front-page' );
$this->assertEquals( array( 'front-page', 'home', 'index' ), $hierarchy );
// Custom templates.
$hierarchy = get_template_hierarchy( 'whatever-slug', true );
$this->assertEquals( array( 'page', 'singular', 'index' ), $hierarchy );
// Single slug templates(ex. page, tag, author, etc..
$hierarchy = get_template_hierarchy( 'page' );
$this->assertEquals( array( 'page', 'singular', 'index' ), $hierarchy );
$hierarchy = get_template_hierarchy( 'tag' );
$this->assertEquals( array( 'tag', 'archive', 'index' ), $hierarchy );
$hierarchy = get_template_hierarchy( 'author' );
$this->assertEquals( array( 'author', 'archive', 'index' ), $hierarchy );
$hierarchy = get_template_hierarchy( 'date' );
$this->assertEquals( array( 'date', 'archive', 'index' ), $hierarchy );
$hierarchy = get_template_hierarchy( 'taxonomy' );
$this->assertEquals( array( 'taxonomy', 'archive', 'index' ), $hierarchy );
$hierarchy = get_template_hierarchy( 'attachment' );
$this->assertEquals(
array(
'attachment',
'single',
'singular',
'index',
),
$hierarchy
);
$hierarchy = get_template_hierarchy( 'singular' );
$this->assertEquals( array( 'singular', 'index' ), $hierarchy );
$hierarchy = get_template_hierarchy( 'single' );
$this->assertEquals( array( 'single', 'singular', 'index' ), $hierarchy );
$hierarchy = get_template_hierarchy( 'archive' );
$this->assertEquals( array( 'archive', 'index' ), $hierarchy );
$hierarchy = get_template_hierarchy( 'index' );
$this->assertEquals( array( 'index' ), $hierarchy );

// Taxonomies.
$hierarchy = get_template_hierarchy( 'taxonomy-books', false, 'taxonomy-books' );
$this->assertEquals( array( 'taxonomy-books', 'taxonomy', 'archive', 'index' ), $hierarchy );
// Single word category.
$hierarchy = get_template_hierarchy( 'category-fruits', false, 'category' );
$this->assertEquals(
array(
'category-fruits',
'category',
'archive',
'index',
),
$hierarchy
);
// Multi word category.
$hierarchy = get_template_hierarchy( 'category-fruits-yellow', false, 'category' );
$this->assertEquals(
array(
'category-fruits-yellow',
'category',
'archive',
'index',
),
$hierarchy
);
// Single word taxonomy.
$hierarchy = get_template_hierarchy( 'taxonomy-books-action', false, 'taxonomy-books' );
$this->assertEquals(
array(
'taxonomy-books-action',
'taxonomy-books',
'taxonomy',
'archive',
'index',
),
$hierarchy
);
$hierarchy = get_template_hierarchy( 'taxonomy-books-action-adventure', false, 'taxonomy-books' );
$this->assertEquals(
array(
'taxonomy-books-action-adventure',
'taxonomy-books',
'taxonomy',
'archive',
'index',
),
$hierarchy
);
// Multi word taxonomy/terms.
$hierarchy = get_template_hierarchy( 'taxonomy-greek-books-action-adventure', false, 'taxonomy-greek-books' );
$this->assertEquals(
array(
'taxonomy-greek-books-action-adventure',
'taxonomy-greek-books',
'taxonomy',
'archive',
'index',
),
$hierarchy
);
// Post types.
$hierarchy = get_template_hierarchy( 'single-book', false, 'single-book' );
$this->assertEquals(
array(
'single-book',
'single',
'singular',
'index',
),
$hierarchy
);
$hierarchy = get_template_hierarchy( 'single-art-project', false, 'single-art-project' );
$this->assertEquals(
array(
'single-art-project',
'single',
'singular',
'index',
),
$hierarchy
);
$hierarchy = get_template_hierarchy( 'single-art-project-imagine', false, 'single-art-project' );
$this->assertEquals(
array(
'single-art-project-imagine',
'single-art-project',
'single',
'singular',
'index',
),
$hierarchy
);
$hierarchy = get_template_hierarchy( 'page-hi', false, 'page' );
$this->assertEquals(
array(
'page-hi',
'page',
'singular',
'index',
),
$hierarchy
);
// Authors.
$hierarchy = get_template_hierarchy( 'author-rigas', false, 'author' );
$this->assertEquals(
array(
'author-rigas',
'author',
'archive',
'index',
),
$hierarchy
);
}
}