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

Add: Modal to choose a start pattern on new templates. #46248

Merged
merged 1 commit into from
Feb 9, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions lib/compat/wordpress-6.1/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,6 @@
* @package gutenberg
*/

/**
* Update `wp_template` and `wp_template-part` post types to use
* Gutenberg's REST controller.
*
* @param array $args Array of arguments for registering a post type.
* @param string $post_type Post type key.
*/
function gutenberg_update_templates_template_parts_rest_controller( $args, $post_type ) {
if ( in_array( $post_type, array( 'wp_template', 'wp_template-part' ), true ) ) {
$args['rest_controller_class'] = 'Gutenberg_REST_Templates_Controller';
}
return $args;
}
add_filter( 'register_post_type_args', 'gutenberg_update_templates_template_parts_rest_controller', 10, 2 );


/**
* Add the post type's `icon`(menu_icon) in the response.
* When we backport this change we will need to add the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/**
* REST API: Gutenberg_REST_Templates_Controller_6_2 class
*
* @package Gutenberg
* @subpackage REST_API
*/

/**
* Base Templates REST API Controller.
*/
class Gutenberg_REST_Templates_Controller_6_2 extends Gutenberg_REST_Templates_Controller {

/**
* Registers the controllers routes.
*
* @return void
*/
public function register_routes() {

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/lookup',
Copy link
Contributor

Choose a reason for hiding this comment

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

why lookup? Should it be /fallback?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh this doesn't look like a new endpoint, what changed in this PR then?

Copy link
Member Author

Choose a reason for hiding this comment

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

My changes are in a separate commit at 505d0c0. The rest is just moving the endpoint.

array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_template_fallback' ),
'permission_callback' => array( $this, 'get_item_permissions_check' ),
'args' => array(
'slug' => array(
'description' => __( 'The slug of the template to get the fallback for', 'gutenberg' ),
'type' => 'string',
'required' => true,
),
'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();
// Get fallback template content.
}

/**
* 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 ) {
$hierarchy = get_template_hierarchy( $request['slug'], $request['is_custom'], $request['template_prefix'] );
$fallback_template = null;
do {
$fallback_template = resolve_block_template( $request['slug'], $hierarchy, '' );
array_shift( $hierarchy );
} while ( ! empty( $hierarchy ) && empty( $fallback_template->content ) );
$response = $this->prepare_item_for_response( $fallback_template, $request );
return rest_ensure_response( $response );
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can add a unit test for this endpoint?

Copy link
Member Author

Choose a reason for hiding this comment

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

I added a unit test.

15 changes: 15 additions & 0 deletions lib/compat/wordpress-6.2/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
* @package gutenberg
*/

/**
* Update `wp_template` and `wp_template-part` post types to use
* Gutenberg's REST controller.
*
* @param array $args Array of arguments for registering a post type.
* @param string $post_type Post type key.
*/
function gutenberg_update_templates_template_parts_rest_controller( $args, $post_type ) {
if ( in_array( $post_type, array( 'wp_template', 'wp_template-part' ), true ) ) {
$args['rest_controller_class'] = 'Gutenberg_REST_Templates_Controller_6_2';
}
return $args;
}
add_filter( 'register_post_type_args', 'gutenberg_update_templates_template_parts_rest_controller', 10, 2 );

/**
* Registers the block pattern categories REST API routes.
*/
Expand Down
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php';
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php';
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php';
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-templates-controller-6-2.php';
require_once __DIR__ . '/compat/wordpress-6.2/rest-api.php';
require_once __DIR__ . '/compat/wordpress-6.2/block-patterns.php';
require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import {
DropdownMenu,
MenuGroup,
Expand Down Expand Up @@ -109,19 +107,7 @@ export default function NewTemplate( {
}
setIsCreatingTemplate( true );
try {
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.raw;
}
const { title, description, slug } = template;
const newTemplate = await saveEntityRecord(
'postType',
'wp_template',
Expand All @@ -131,7 +117,6 @@ export default function NewTemplate( {
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
2 changes: 2 additions & 0 deletions packages/edit-site/src/components/editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import KeyboardShortcuts from '../keyboard-shortcuts';
import InserterSidebar from '../secondary-sidebar/inserter-sidebar';
import ListViewSidebar from '../secondary-sidebar/list-view-sidebar';
import WelcomeGuide from '../welcome-guide';
import StartTemplateOptions from '../start-template-options';
import { store as editSiteStore } from '../../store';
import { GlobalStylesRenderer } from '../global-styles-renderer';
import { GlobalStylesProvider } from '../global-styles/global-styles-provider';
Expand Down Expand Up @@ -170,6 +171,7 @@ export default function Editor() {
<GlobalStylesProvider>
<BlockContextProvider value={ blockContext }>
<SidebarComplementaryAreaFills />
{ isEditMode && <StartTemplateOptions /> }
<InterfaceSkeleton
enableRegionNavigation={ false }
className={
Expand Down
171 changes: 171 additions & 0 deletions packages/edit-site/src/components/start-template-options/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/**
* WordPress dependencies
*/
import { Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useState, useEffect, useMemo } from '@wordpress/element';
import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { useAsyncList } from '@wordpress/compose';
import { store as preferencesStore } from '@wordpress/preferences';
import { parse } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../store';
import { store as coreStore, useEntityBlockEditor } from '@wordpress/core-data';
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';

function useFallbackTemplateContent( slug, isCustom = false ) {
const [ templateContent, setTemplateContent ] = useState( '' );

useEffect( () => {
apiFetch( {
path: addQueryArgs( '/wp/v2/templates/lookup', {
youknowriad marked this conversation as resolved.
Show resolved Hide resolved
slug,
is_custom: isCustom,
ignore_empty: true,
} ),
} ).then( ( { content } ) => setTemplateContent( content.raw ) );
}, [ slug ] );
return templateContent;
}

const START_BLANK_TITLE = __( 'Start blank' );

function PatternSelection( { fallbackContent, onChoosePattern, postType } ) {
const [ , , onChange ] = useEntityBlockEditor( 'postType', postType );
const blockPatterns = useMemo(
() => [
{
name: 'fallback',
blocks: parse( fallbackContent ),
title: __( 'Fallback content' ),
},
{
name: 'start-blank',
blocks: parse(
'<!-- wp:paragraph --><p></p><!-- /wp:paragraph -->'
),
title: START_BLANK_TITLE,
},
],
[ fallbackContent ]
);
const shownBlockPatterns = useAsyncList( blockPatterns );

return (
<div
className="edit-site-start-template-options__pattern-container"
style={ {
'--wp-edit-site-start-template-options-start-blank': `"${ START_BLANK_TITLE }"`,
} }
>
<BlockPatternsList
blockPatterns={ blockPatterns }
shownPatterns={ shownBlockPatterns }
onClickPattern={ ( pattern, blocks ) => {
onChange( 'start-blank' === pattern.name ? [] : blocks, {
selection: undefined,
} );
onChoosePattern();
} }
/>
</div>
);
}

function StartModal( { slug, isCustom, onClose, postType } ) {
const fallbackContent = useFallbackTemplateContent( slug, isCustom );
if ( ! fallbackContent ) {
return null;
}
return (
<Modal
className="edit-site-start-template-options__modal"
title={ __( 'Choose a pattern' ) }
closeLabel={ __( 'Cancel' ) }
focusOnMount="firstElement"
onRequestClose={ onClose }
>
<div className="edit-site-start-template-options__modal-content">
<PatternSelection
fallbackContent={ fallbackContent }
slug={ slug }
isCustom={ isCustom }
postType={ postType }
onChoosePattern={ () => {
onClose();
} }
/>
</div>
</Modal>
);
}

const START_TEMPLATE_MODAL_STATES = {
INITIAL: 'INITIAL',
CLOSED: 'CLOSED',
};

export default function StartTemplateOptions() {
const [ modalState, setModalState ] = useState(
START_TEMPLATE_MODAL_STATES.INITIAL
);
const { shouldOpenModel, slug, isCustom, postType } = useSelect(
( select ) => {
const { getEditedPostType, getEditedPostId } =
select( editSiteStore );
const _postType = getEditedPostType();
const postId = getEditedPostId();
const {
__experimentalGetDirtyEntityRecords,
getEditedEntityRecord,
} = select( coreStore );
const templateRecord = getEditedEntityRecord(
'postType',
_postType,
postId
);

const hasDirtyEntityRecords =
__experimentalGetDirtyEntityRecords().length > 0;

return {
shouldOpenModel:
! hasDirtyEntityRecords &&
'' === templateRecord.content &&
'wp_template' === _postType &&
! select( preferencesStore ).get(
'core/edit-site',
'welcomeGuide'
),
slug: templateRecord.slug,
isCustom: templateRecord.is_custom,
postType: _postType,
};
},
[]
);

if (
( modalState === START_TEMPLATE_MODAL_STATES.INITIAL &&
! shouldOpenModel ) ||
modalState === START_TEMPLATE_MODAL_STATES.CLOSED
) {
return null;
}

return (
<StartModal
slug={ slug }
isCustom={ isCustom }
postType={ postType }
onClose={ () =>
setModalState( START_TEMPLATE_MODAL_STATES.CLOSED )
}
/>
);
}
Loading