diff --git a/lib/compat/wordpress-6.6/class-gutenberg-rest-templates-controller-6-6.php b/lib/compat/wordpress-6.6/class-gutenberg-rest-templates-controller-6-6.php new file mode 100644 index 00000000000000..42f8d0400393ca --- /dev/null +++ b/lib/compat/wordpress-6.6/class-gutenberg-rest-templates-controller-6-6.php @@ -0,0 +1,66 @@ + rest_authorization_required_code(), + ) + ); + } + + return true; + } + + /** + * Checks if a given request has access to read templates. + * + * @since 6.6 + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has read access, WP_Error object otherwise. + */ + public function get_item_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + /* + * Allow access to anyone who can edit posts. + */ + if ( ! current_user_can( 'edit_posts' ) ) { + return new WP_Error( + 'rest_cannot_manage_templates', + __( 'Sorry, you are not allowed to access the templates on this site.', 'default' ), + array( + 'status' => rest_authorization_required_code(), + ) + ); + } + + return true; + } +} diff --git a/lib/compat/wordpress-6.6/rest-api.php b/lib/compat/wordpress-6.6/rest-api.php new file mode 100644 index 00000000000000..bf462cd11ca4b4 --- /dev/null +++ b/lib/compat/wordpress-6.6/rest-api.php @@ -0,0 +1,31 @@ + { + const { getSettings } = select( blockEditorStore ); + return getSettings()?.supportsLayout; + }, [] ); + const [ defaultLayout ] = useSettings( 'layout' ); + const usedLayout = layout?.inherit ? defaultLayout || {} : layout; + + const [ blocks ] = useEntityBlockEditor( 'postType', 'wp_template_part', { + id, + context: 'view', + } ); + + const innerBlocksProps = useInnerBlocksProps( blockProps, { + value: blocks, + onChange: () => {}, + onInput: () => {}, + renderAppender: undefined, + layout: themeSupportsLayout ? usedLayout : undefined, + } ); + + return ; +} + export default function TemplatePartEdit( { attributes, setAttributes, clientId, } ) { const { createSuccessNotice } = useDispatch( noticesStore ); + const { canEditTemplatePart, canViewTemplatePart } = useSelect( + ( select ) => ( { + canEditTemplatePart: + select( coreStore ).canUser( 'create', 'template-parts' ) ?? + false, + canViewTemplatePart: + select( coreStore ).canUser( 'read', 'template-parts' ) ?? + false, + } ), + [] + ); + const currentTheme = useSelect( ( select ) => select( coreStore ).getCurrentTheme()?.stylesheet, [] @@ -161,6 +204,23 @@ export default function TemplatePartEdit( { setAttributes ); + if ( ! canEditTemplatePart && canViewTemplatePart ) { + return ( + + + + ); + } + // We don't want to render a missing state if we have any inner blocks. // A new template part is automatically created if we have any inner blocks but no entity. if ( diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js index 51830f622a026d..3c7012d2dccb44 100644 --- a/packages/core-data/src/entity-provider.js +++ b/packages/core-data/src/entity-provider.js @@ -145,14 +145,19 @@ const parsedBlocksCache = new WeakMap(); * `BlockEditorProvider` and are intended to be used with it, * or similar components or hooks. * - * @param {string} kind The entity kind. - * @param {string} name The entity name. + * @param {string} kind The entity kind. + * @param {string} name The entity name. * @param {Object} options - * @param {string} [options.id] An entity ID to use instead of the context-provided one. + * @param {string} [options.id] An entity ID to use instead of the context-provided one. + * @param {string} [options.context] The context param to be passed to the REST API. * * @return {[WPBlock[], Function, Function]} The block array and setters. */ -export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { +export function useEntityBlockEditor( + kind, + name, + { id: _id, context = 'edit' } = {} +) { const providerId = useEntityId( kind, name ); const id = _id ?? providerId; const { getEntityRecord, getEntityRecordEdits } = useSelect( STORE_NAME ); @@ -162,7 +167,9 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { return {}; } const { getEditedEntityRecord } = select( STORE_NAME ); - const editedRecord = getEditedEntityRecord( kind, name, id ); + const editedRecord = getEditedEntityRecord( kind, name, id, { + context, + } ); return { editedBlocks: editedRecord.blocks, content: editedRecord.content, diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js index fd44d3dae4ca4a..41043954454f26 100644 --- a/packages/edit-post/src/editor.js +++ b/packages/edit-post/src/editor.js @@ -55,12 +55,12 @@ function Editor( { getEditorSettings().supportsTemplateMode; const isViewable = getPostType( currentPost.postType )?.viewable ?? false; - const canEditTemplate = canUser( 'create', 'templates' ); + const canViewTemplate = canUser( 'read', 'templates' ); return { template: supportsTemplateMode && isViewable && - canEditTemplate && + canViewTemplate && currentPost.postType !== 'wp_template' ? getEditedPostTemplate() : null, diff --git a/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js b/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js index 2000ae5727190c..3581a49608c734 100644 --- a/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js +++ b/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; import { useEffect, useState, useRef } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { __ } from '@wordpress/i18n'; @@ -42,12 +43,20 @@ export default function EditTemplateBlocksNotification( { contentRef } ) { const { createInfoNotice, removeNotice } = useDispatch( noticesStore ); + const canEditTemplate = useSelect( + ( select ) => + select( coreStore ).canUser( 'create', 'templates' ) ?? false + ); + const [ isDialogOpen, setIsDialogOpen ] = useState( false ); const lastNoticeId = useRef( 0 ); useEffect( () => { const handleClick = async ( event ) => { + if ( ! canEditTemplate ) { + return; + } if ( ! event.target.classList.contains( 'is-root-container' ) ) { return; } @@ -104,8 +113,13 @@ export default function EditTemplateBlocksNotification( { contentRef } ) { onNavigateToEntityRecord, templateId, removeNotice, + canEditTemplate, ] ); + if ( ! canEditTemplate ) { + return null; + } + return ( + select( coreStore ).canUser( 'create', 'templates' ) ?? false + ); + const { createSuccessNotice } = useDispatch( noticesStore ); const { setRenderingMode } = useDispatch( editorStore ); @@ -81,30 +87,33 @@ export default function BlockThemeControl( { id } ) { { ( { onClose } ) => ( <> - { - onNavigateToEntityRecord( { - postId: template.id, - postType: 'wp_template', - } ); - onClose(); - createSuccessNotice( - __( - 'Editing template. Changes made here affect all posts and pages that use the template.' - ), - { - type: 'snackbar', - actions: notificationAction, - } - ); - } } - > - { __( 'Edit template' ) } - - + { canCreateTemplate && ( + { + onNavigateToEntityRecord( { + postId: template.id, + postType: 'wp_template', + } ); + onClose(); + createSuccessNotice( + __( + 'Editing template. Changes made here affect all posts and pages that use the template.' + ), + { + type: 'snackbar', + actions: notificationAction, + } + ); + } } + > + { __( 'Edit template' ) } + + ) } - + { canCreateTemplate && ( + + ) }