diff --git a/lib/compat/wordpress-6.1/block-template-utils.php b/lib/compat/wordpress-6.1/block-template-utils.php index 2521be25c3bcdf..e63e45a4db4472 100644 --- a/lib/compat/wordpress-6.1/block-template-utils.php +++ b/lib/compat/wordpress-6.1/block-template-utils.php @@ -165,6 +165,73 @@ function gutenberg_get_block_templates( $query = array(), $template_type = 'wp_t return apply_filters( 'get_block_templates', $query_result, $query, $template_type ); } +function gutenberg_get_template_slugs( $template ) { + $limit = 2; + if ( strpos( $template, 'single-' ) === 0 || strpos( $template, 'taxonomy-' ) === 0 ) { + // E.g. single-post-mypost or taxonomy-recipes-vegetarian. + $limit = 3; + } + $parts = explode( '-', $template, $limit ); + $type = array_shift( $parts ); + $slugs = array( $type ); + + foreach ( $parts as $part ) { + array_unshift( $slugs, $slugs[0] . '-' . $part ); + } + return $slugs; +} + +function gutenberg_get_block_template_type_for_slug( $slug ) { + $slug_parts = explode( '-', $slug, 3 ); + + if ( count( $slug_parts ) > 1 ) { + if ( 'single' === $slug_parts [0] ) { + // Get CPT labels + $post_type = get_post_type_object( $slug_parts[1] ); + $labels = $post_type->labels; + + if ( count( $slug_parts ) > 2 ) { + // Now we look for the CPT with slug as defined in $slug_parts[2] + $post = get_page_by_path( $slug_parts[2], OBJECT, $slug_parts[1] ); + $title = sprintf( + // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the singular name of a post type and %2$s is the name of the post, e.g. "Post: Hello, WordPress" + __( '%1$s: %2$s' ), + $labels->singular_name, + $post->post_title + ); + $description = sprintf( + // translators: Represents the description of a user's custom template in the Site Editor, e.g. "Template for Post: Hello, WordPress" + __( 'Template for %1$s' ), + $title + ); + } else { + $title = sprintf( + // translators: %s: Name of the post type e.g: "Post". + __( 'Single item: %s' ), + $labels->singular_name + ); + $description = sprintf( + // translators: %s: Name of the post type e.g: "Post". + __( 'Displays a single item: %s.' ), + $labels->singular_name + ); + } + } + } else { + $default_template_types = get_default_block_template_types(); + + if ( array_key_exists( $slug, $default_template_types ) ) { + $title = $default_template_types[ $slug ]['title']; + $description = $default_template_types[ $slug ]['description']; + } + } + + return array( + 'title' => $title, + 'description' => $description, + ); +} + /** * Retrieves a single unified template object using its id. * @@ -195,33 +262,21 @@ function gutenberg_get_block_template( $id, $template_type = 'wp_template' ) { if ( count( $parts ) < 2 ) { return null; } - list( $theme, $slug ) = $parts; - $wp_query_args = array( - 'post_name__in' => array( $slug ), - 'post_type' => $template_type, - 'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ), - 'posts_per_page' => 1, - 'no_found_rows' => true, - 'tax_query' => array( - array( - 'taxonomy' => 'wp_theme', - 'field' => 'name', - 'terms' => $theme, - ), - ), - ); - $template_query = new WP_Query( $wp_query_args ); - $posts = $template_query->posts; - - if ( count( $posts ) > 0 ) { - $template = gutenberg_build_block_template_result_from_post( $posts[0] ); + list( , $slug ) = $parts; - if ( ! is_wp_error( $template ) ) { - return $template; - } + $templates = gutenberg_get_template_slugs( $slug ); + $block_template = resolve_block_template( $slug, $templates, '' ); + if ( ! $block_template ) { + $block_template = resolve_block_template( 'index', array(), '' ); } + // This might give us a fallback template with a different ID, + // so we have to override it to make sure it's correct. + $block_template->id = $id; + $block_template->slug = $slug; - $block_template = get_block_file_template( $id, $template_type ); + $template_type_info = gutenberg_get_block_template_type_for_slug( $slug ); + $block_template->title = $template_type_info['title']; + $block_template->description = $template_type_info['description']; /** * Filters the queried block template object after it's been fetched. diff --git a/lib/compat/wordpress-6.1/class-gutenberg-rest-templates-controller.php b/lib/compat/wordpress-6.1/class-gutenberg-rest-templates-controller.php index 7c73d799aa8436..6de58796085944 100644 --- a/lib/compat/wordpress-6.1/class-gutenberg-rest-templates-controller.php +++ b/lib/compat/wordpress-6.1/class-gutenberg-rest-templates-controller.php @@ -95,6 +95,23 @@ public function create_item( $request ) { ); } + /** + * Deletes a single template. + * + * @since 5.8.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function delete_item( $request ) { + $template = get_block_template( $request['id'], $this->post_type ); + if ( ! $template || $template->id !== $request['id'] ) { // Make sure there is a template for this ID (and not just a fallback one). + return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) ); + } + + return parent::delete_item( $request ); + } + /** * Updates a single template. * diff --git a/packages/edit-site/src/components/add-new-template/new-template.js b/packages/edit-site/src/components/add-new-template/new-template.js index f8b2f9ec26d318..bb2c8a1a6890e7 100644 --- a/packages/edit-site/src/components/add-new-template/new-template.js +++ b/packages/edit-site/src/components/add-new-template/new-template.js @@ -32,7 +32,6 @@ import { tag, } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; -import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -79,7 +78,7 @@ export default function NewTemplate( { postType } ) { const [ showCustomTemplateModal, setShowCustomTemplateModal ] = useState( false ); const [ entityForSuggestions, setEntityForSuggestions ] = useState( {} ); - const { existingTemplates, defaultTemplateTypes } = useSelect( + const { existingTemplates, defaultTemplateTypes, theme } = useSelect( ( select ) => ( { existingTemplates: select( coreStore ).getEntityRecords( 'postType', @@ -88,52 +87,25 @@ export default function NewTemplate( { postType } ) { ), defaultTemplateTypes: select( editorStore ).__experimentalGetDefaultTemplateTypes(), + theme: select( coreStore ).getCurrentTheme(), } ), [] ); const postTypesEntitiesInfo = usePostTypesEntitiesInfo( existingTemplates ); - const { saveEntityRecord } = useDispatch( coreStore ); - const { createErrorNotice } = useDispatch( noticesStore ); + const { setTemplate } = useDispatch( editSiteStore ); async function createTemplate( template ) { - try { - const { title, description, slug } = template; - const newTemplate = await saveEntityRecord( - 'postType', - 'wp_template', - { - description, - // Slugs need to be strings, so this is for template `404` - slug: slug.toString(), - status: 'publish', - title, - // This adds a post meta field in template that is part of `is_custom` value calculation. - is_wp_suggestion: true, - }, - { throwOnError: true } - ); - - // Set template before navigating away to avoid initial stale value. - setTemplate( newTemplate.id, newTemplate.slug ); - - // Navigate to the created template editor. - history.push( { - postId: newTemplate.id, - postType: newTemplate.type, - } ); + const { slug } = template; + const templateId = theme.stylesheet + '//' + slug.toString(); - // TODO: Add a success notice? - } catch ( error ) { - const errorMessage = - error.message && error.code !== 'unknown_error' - ? error.message - : __( 'An error occurred while creating the template.' ); + // Set template before navigating away to avoid initial stale value. + setTemplate( templateId, slug ); - createErrorNotice( errorMessage, { - type: 'snackbar', - } ); - } + history.push( { + postId: templateId, + postType: 'wp_template', + } ); } const existingTemplateSlugs = ( existingTemplates || [] ).map( ( { slug } ) => slug diff --git a/phpunit/compat/wordpress-6.1/block-template-utils-test.php b/phpunit/compat/wordpress-6.1/block-template-utils-test.php new file mode 100644 index 00000000000000..1a77a01f365523 --- /dev/null +++ b/phpunit/compat/wordpress-6.1/block-template-utils-test.php @@ -0,0 +1,67 @@ + 'post', + 'post_name' => 'hello-world', + 'post_title' => 'Hello World!', + 'post_content' => 'Welcome to WordPress. This is your first post. Edit or delete it, then start writing!', + 'post_excerpt' => 'Welcome to WordPress.', + ); + self::$post = self::factory()->post->create_and_get( $args ); + } + + public static function wpTearDownAfterClass() { + wp_delete_post( self::$post->ID ); + } + + public function test_get_template_slugs() { + $templates = gutenberg_get_template_slugs( 'single-post-hello-world' ); + $this->assertSame( + array( + 'single-post-hello-world', + // Should *not* fall back to `single-post-hello`! + 'single-post', + 'single', + ), + $templates + ); + } + + public function test_gutenberg_get_block_template_type_for_slug_generic() { + $template_slug = 'single-' . self::$post->post_type; + $template_info = gutenberg_get_block_template_type_for_slug( $template_slug ); + $this->assertSame( + array( + 'title' => 'Single item: Post', + 'description' => 'Displays a single item: Post.', + ), + $template_info + ); + } + + public function test_gutenberg_get_block_template_type_for_slug_individual() { + $template_slug = 'single-' . self::$post->post_type . '-' . self::$post->post_name; + $template_info = gutenberg_get_block_template_type_for_slug( $template_slug ); + $this->assertSame( + array( + 'title' => 'Post: Hello World!', + 'description' => 'Template for Post: Hello World!', + ), + $template_info + ); + } +}