From c5d0207367b9331f654ac4c63ad154dfb2fe3a77 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 19 Oct 2023 21:41:00 +0300 Subject: [PATCH 01/21] trying out API calls to render --- lib/init.php | 27 ++++++++++++++++ .../components/block-patterns-list/index.js | 32 +++++++++++++++++-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/lib/init.php b/lib/init.php index 88dcba4525f6e..837b46b4b5618 100644 --- a/lib/init.php +++ b/lib/init.php @@ -57,3 +57,30 @@ function gutenberg_menu() { ); } add_action( 'admin_menu', 'gutenberg_menu', 9 ); + +// Define the callback function +function custom_rest_endpoint_callback($request) { + // Get the JSON data from the request + $blocks = parse_blocks( $request->get_json_params() ); + + // Initialize an empty string to store the rendered HTML + $rendered_html = ''; + + // Render each block + foreach ($blocks as $block) { + $rendered_block = render_block($block); + $rendered_html .= $rendered_block; + } + + return $rendered_html; +} + +// Register the REST API endpoint +function register_custom_rest_endpoint() { + register_rest_route('wp/v2', '/render_blocks', array( + 'methods' => 'POST', + 'callback' => 'custom_rest_endpoint_callback', + )); +} + +add_action('rest_api_init', 'register_custom_rest_endpoint'); \ No newline at end of file diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index c79d6927c00f0..ca08ccccccebd 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -25,6 +25,7 @@ import BlockPreview from '../block-preview'; import InserterDraggableBlocks from '../inserter-draggable-blocks'; import BlockPatternsPaging from '../block-patterns-paging'; import { PATTERN_TYPES } from '../inserter/block-patterns-tab/utils'; +import apiFetch from '@wordpress/api-fetch'; const { CompositeV2: Composite, @@ -48,10 +49,29 @@ function BlockPattern( { showTooltip, } ) { const [ isDragging, setIsDragging ] = useState( false ); - const { blocks, viewportWidth } = pattern; + + const [ patternHTML, setPatternHTML ] = useState( '' ); + + const { content, blocks, viewportWidth } = pattern; const instanceId = useInstanceId( BlockPattern ); const descriptionId = `block-editor-block-patterns-list__item-description-${ instanceId }`; + // post pattern content to the render_blocks endpoint + // and get back the rendered html + useEffect( () => { + const getHTML = async () => { + const dataHTML = await apiFetch( { + path: '/wp/v2/render_blocks', + method: 'POST', + data: content, + } ); + setPatternHTML( dataHTML ); + }; + getHTML().catch( ( error ) => { + return error; + } ); + }, [ blocks ] ); + return ( onHover?.( null ) } > - + /> */ } + +
{ pattern.type === PATTERN_TYPES.user && From 44f621379015660723fd92291fe3d8bf70e74286 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Fri, 3 Nov 2023 16:55:13 +0200 Subject: [PATCH 02/21] first try at static render --- .../components/block-patterns-list/index.js | 21 +++++++------------ .../src/components/block-preview/auto.js | 8 ++++++- .../src/components/block-preview/index.js | 2 ++ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index ca08ccccccebd..18585bd132724 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -16,6 +16,9 @@ import { import { useInstanceId } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; import { Icon, symbol } from '@wordpress/icons'; +// ignore restricted import ESLint error for apiFetch +// eslint-disable-next-line no-restricted-imports +import apiFetch from '@wordpress/api-fetch'; /** * Internal dependencies @@ -24,8 +27,6 @@ import { unlock } from '../../lock-unlock'; import BlockPreview from '../block-preview'; import InserterDraggableBlocks from '../inserter-draggable-blocks'; import BlockPatternsPaging from '../block-patterns-paging'; -import { PATTERN_TYPES } from '../inserter/block-patterns-tab/utils'; -import apiFetch from '@wordpress/api-fetch'; const { CompositeV2: Composite, @@ -50,12 +51,11 @@ function BlockPattern( { } ) { const [ isDragging, setIsDragging ] = useState( false ); - const [ patternHTML, setPatternHTML ] = useState( '' ); - const { content, blocks, viewportWidth } = pattern; const instanceId = useInstanceId( BlockPattern ); const descriptionId = `block-editor-block-patterns-list__item-description-${ instanceId }`; + const [ patternHTML, setPatternHTML ] = useState( '' ); // post pattern content to the render_blocks endpoint // and get back the rendered html useEffect( () => { @@ -70,7 +70,7 @@ function BlockPattern( { getHTML().catch( ( error ) => { return error; } ); - }, [ blocks ] ); + }, [ blocks, content ] ); return ( onHover?.( null ) } > - { /* */ } - -
+ /> { pattern.type === PATTERN_TYPES.user && diff --git a/packages/block-editor/src/components/block-preview/auto.js b/packages/block-editor/src/components/block-preview/auto.js index 8972370cac689..031ce8ce1593e 100644 --- a/packages/block-editor/src/components/block-preview/auto.js +++ b/packages/block-editor/src/components/block-preview/auto.js @@ -21,6 +21,7 @@ const MAX_HEIGHT = 2000; function ScaledBlockPreview( { viewportWidth, + html, containerWidth, minHeight, additionalStyles = [], @@ -110,7 +111,12 @@ function ScaledBlockPreview( { > { contentResizeListener } - +
+ { /* */ } ); diff --git a/packages/block-editor/src/components/block-preview/index.js b/packages/block-editor/src/components/block-preview/index.js index 0fb7f55b9955d..dda1fbd2f660b 100644 --- a/packages/block-editor/src/components/block-preview/index.js +++ b/packages/block-editor/src/components/block-preview/index.js @@ -22,6 +22,7 @@ import { BlockListItems } from '../block-list'; export function BlockPreview( { blocks, + html, viewportWidth = 1200, minHeight, additionalStyles = [], @@ -73,6 +74,7 @@ export function BlockPreview( { > From 175da4417be8f26a910960bbf1db18309f320e4d Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Fri, 3 Nov 2023 18:21:48 +0200 Subject: [PATCH 03/21] lint --- lib/init.php | 55 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/lib/init.php b/lib/init.php index 837b46b4b5618..8b69566f278b9 100644 --- a/lib/init.php +++ b/lib/init.php @@ -58,29 +58,42 @@ function gutenberg_menu() { } add_action( 'admin_menu', 'gutenberg_menu', 9 ); -// Define the callback function -function custom_rest_endpoint_callback($request) { - // Get the JSON data from the request - $blocks = parse_blocks( $request->get_json_params() ); - - // Initialize an empty string to store the rendered HTML - $rendered_html = ''; +if ( ! function_exists( 'gutenberg_render_blocks_from_request' ) ) { + /** + * Render blocks from a REST API request. + * + * @param mixed $request The current request. + * + * @return string + */ + function gutenberg_render_blocks_from_request( $request ) { + $blocks = parse_blocks( $request->get_json_params() ); + $rendered_html = ''; + foreach ( $blocks as $block ) { + $rendered_block = render_block( $block ); + $rendered_html .= $rendered_block; + } - // Render each block - foreach ($blocks as $block) { - $rendered_block = render_block($block); - $rendered_html .= $rendered_block; - } - - return $rendered_html; + return $rendered_html; + } } -// Register the REST API endpoint -function register_custom_rest_endpoint() { - register_rest_route('wp/v2', '/render_blocks', array( - 'methods' => 'POST', - 'callback' => 'custom_rest_endpoint_callback', - )); +if ( ! function_exists( 'register_gutenberg_render_blocks_endpoint' ) ) { + /** + * Register custom REST endpoint for rendering blocks. + * + * @return void + */ + function register_gutenberg_render_blocks_endpoint() { + register_rest_route( + 'wp/v2', + '/render_blocks', + array( + 'methods' => 'POST', + 'callback' => 'gutenberg_render_blocks_from_request', + ) + ); + } } -add_action('rest_api_init', 'register_custom_rest_endpoint'); \ No newline at end of file +add_action( 'rest_api_init', 'register_gutenberg_render_blocks_endpoint' ); From be9406737e04ec949e18817cfd03c11ca1bdfbff Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Fri, 3 Nov 2023 18:24:26 +0200 Subject: [PATCH 04/21] add a public permission for now --- lib/init.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/init.php b/lib/init.php index 8b69566f278b9..1dab9924347b2 100644 --- a/lib/init.php +++ b/lib/init.php @@ -91,6 +91,7 @@ function register_gutenberg_render_blocks_endpoint() { array( 'methods' => 'POST', 'callback' => 'gutenberg_render_blocks_from_request', + 'permission_callback' => '__return_true', ) ); } From 136a92db14c395216e99dde085014a17159936a5 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Mon, 6 Nov 2023 08:40:45 +0200 Subject: [PATCH 05/21] lint --- lib/init.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/init.php b/lib/init.php index 1dab9924347b2..3c9da24bbdc01 100644 --- a/lib/init.php +++ b/lib/init.php @@ -89,8 +89,8 @@ function register_gutenberg_render_blocks_endpoint() { 'wp/v2', '/render_blocks', array( - 'methods' => 'POST', - 'callback' => 'gutenberg_render_blocks_from_request', + 'methods' => 'POST', + 'callback' => 'gutenberg_render_blocks_from_request', 'permission_callback' => '__return_true', ) ); From 64eed7959adec1db336794d7b8158a53b584817d Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Wed, 8 Nov 2023 18:42:03 +0200 Subject: [PATCH 06/21] not functional commit - refactor the implementation --- lib/init.php | 15 +- .../components/block-patterns-list/index.js | 27 +- .../src/components/block-preview/auto.js | 8 +- .../src/components/block-preview/index.js | 36 ++- .../default-block-editor-provider.js | 76 +++++ .../components/block-preview/editor-styles.js | 128 ++++++++ .../src/components/block-preview/iframe.js | 305 ++++++++++++++++++ .../src/components/block-preview/index.js | 155 +++++++++ 8 files changed, 714 insertions(+), 36 deletions(-) create mode 100644 packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js create mode 100644 packages/edit-site/src/components/block-preview/editor-styles.js create mode 100644 packages/edit-site/src/components/block-preview/iframe.js create mode 100644 packages/edit-site/src/components/block-preview/index.js diff --git a/lib/init.php b/lib/init.php index 3c9da24bbdc01..8aa5edc64563f 100644 --- a/lib/init.php +++ b/lib/init.php @@ -1,4 +1,8 @@ get_json_params() ); - $rendered_html = ''; + $blocks = $request->get_json_params(); + + $blocks_content = ''; foreach ( $blocks as $block ) { - $rendered_block = render_block( $block ); - $rendered_html .= $rendered_block; + $block['blockName'] = $block['name']; + $blocks_content .= render_block( $block ); } - return $rendered_html; + return $blocks_content; } } diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index 18585bd132724..f237d7898b845 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -6,7 +6,11 @@ import classnames from 'classnames'; /** * WordPress dependencies */ +<<<<<<< HEAD import { useEffect, useState, forwardRef } from '@wordpress/element'; +======= +import { useState, forwardRef } from '@wordpress/element'; +>>>>>>> 8bc5711934 (not functional commit - refactor the implementation) import { VisuallyHidden, Tooltip, @@ -16,9 +20,6 @@ import { import { useInstanceId } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; import { Icon, symbol } from '@wordpress/icons'; -// ignore restricted import ESLint error for apiFetch -// eslint-disable-next-line no-restricted-imports -import apiFetch from '@wordpress/api-fetch'; /** * Internal dependencies @@ -51,27 +52,10 @@ function BlockPattern( { } ) { const [ isDragging, setIsDragging ] = useState( false ); - const { content, blocks, viewportWidth } = pattern; + const { blocks, viewportWidth } = pattern; const instanceId = useInstanceId( BlockPattern ); const descriptionId = `block-editor-block-patterns-list__item-description-${ instanceId }`; - const [ patternHTML, setPatternHTML ] = useState( '' ); - // post pattern content to the render_blocks endpoint - // and get back the rendered html - useEffect( () => { - const getHTML = async () => { - const dataHTML = await apiFetch( { - path: '/wp/v2/render_blocks', - method: 'POST', - data: content, - } ); - setPatternHTML( dataHTML ); - }; - getHTML().catch( ( error ) => { - return error; - } ); - }, [ blocks, content ] ); - return ( diff --git a/packages/block-editor/src/components/block-preview/auto.js b/packages/block-editor/src/components/block-preview/auto.js index 031ce8ce1593e..8972370cac689 100644 --- a/packages/block-editor/src/components/block-preview/auto.js +++ b/packages/block-editor/src/components/block-preview/auto.js @@ -21,7 +21,6 @@ const MAX_HEIGHT = 2000; function ScaledBlockPreview( { viewportWidth, - html, containerWidth, minHeight, additionalStyles = [], @@ -111,12 +110,7 @@ function ScaledBlockPreview( { > { contentResizeListener } -
- { /* */ } + ); diff --git a/packages/block-editor/src/components/block-preview/index.js b/packages/block-editor/src/components/block-preview/index.js index dda1fbd2f660b..88018ab8cae61 100644 --- a/packages/block-editor/src/components/block-preview/index.js +++ b/packages/block-editor/src/components/block-preview/index.js @@ -22,7 +22,40 @@ import { BlockListItems } from '../block-list'; export function BlockPreview( { blocks, - html, + viewportWidth = 1200, + minHeight, + additionalStyles = [], + // Deprecated props: + __experimentalMinHeight, + __experimentalPadding, +} ) { + const settings = useSelect( + ( select ) => select( blockEditorStore ).getSettings(), + [] + ); + + if ( settings.blockPreview ) { + return settings.blockPreview( { + blocks, + viewportWidth, + minHeight, + additionalStyles, + } ); + } + return ( + + ); +} + +function DefaultBlockPreview( { + blocks, viewportWidth = 1200, minHeight, additionalStyles = [], @@ -74,7 +107,6 @@ export function BlockPreview( { > diff --git a/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js b/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js new file mode 100644 index 0000000000000..61164ca29c0db --- /dev/null +++ b/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js @@ -0,0 +1,76 @@ +/** + * WordPress dependencies + */ +import { useEntityBlockEditor } from '@wordpress/core-data'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../../store'; +import { unlock } from '../../../lock-unlock'; +import useSiteEditorSettings from '../use-site-editor-settings'; +import usePageContentBlocks from './use-page-content-blocks'; +import BlockPreview from '../../block-preview'; + +const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); + +const noop = () => {}; + +/** + * The default block editor provider for the site editor. Typically used when + * the post type is `'wp_template_part'` or `'wp_template'` and allows editing + * of the template and its nested entities. + * + * If the page content focus type is `'hideTemplate'`, the provider will provide + * a set of page content blocks wrapped in a container that, together, + * mimic the look and feel of the post editor and + * allow editing of the page content only. + * + * @param {Object} props + * @param {Element} props.children + */ +export default function DefaultBlockEditorProvider( { children } ) { + const settings = useSiteEditorSettings(); + + const { templateType, isTemplateHidden } = useSelect( ( select ) => { + const { getEditedPostType } = select( editSiteStore ); + const { getPageContentFocusType, getCanvasMode } = unlock( + select( editSiteStore ) + ); + return { + templateType: getEditedPostType(), + isTemplateHidden: + getCanvasMode() === 'edit' && + getPageContentFocusType() === 'hideTemplate', + canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(), + }; + }, [] ); + + const [ blocks, onInput, onChange ] = useEntityBlockEditor( + 'postType', + templateType + ); + const pageContentBlocks = usePageContentBlocks( { + blocks, + isPageContentFocused: isTemplateHidden, + wrapPageContent: true, + } ); + + return ( + + { children } + + ); +} diff --git a/packages/edit-site/src/components/block-preview/editor-styles.js b/packages/edit-site/src/components/block-preview/editor-styles.js new file mode 100644 index 0000000000000..23dd3d6cb7371 --- /dev/null +++ b/packages/edit-site/src/components/block-preview/editor-styles.js @@ -0,0 +1,128 @@ +/** + * External dependencies + */ +import { colord, extend } from 'colord'; +import namesPlugin from 'colord/plugins/names'; +import a11yPlugin from 'colord/plugins/a11y'; + +/** + * WordPress dependencies + */ +import { SVG } from '@wordpress/components'; +import { useCallback, useMemo } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { + transformStyles, + store as blockEditorStore, +} from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; + +extend( [ namesPlugin, a11yPlugin ] ); + +function useDarkThemeBodyClassName( styles, scope ) { + return useCallback( + ( node ) => { + if ( ! node ) { + return; + } + + const { ownerDocument } = node; + const { defaultView, body } = ownerDocument; + const canvas = scope ? ownerDocument.querySelector( scope ) : body; + + let backgroundColor; + + if ( ! canvas ) { + // The real .editor-styles-wrapper element might not exist in the + // DOM, so calculate the background color by creating a fake + // wrapper. + const tempCanvas = ownerDocument.createElement( 'div' ); + tempCanvas.classList.add( 'editor-styles-wrapper' ); + body.appendChild( tempCanvas ); + + backgroundColor = defaultView + ?.getComputedStyle( tempCanvas, null ) + .getPropertyValue( 'background-color' ); + + body.removeChild( tempCanvas ); + } else { + backgroundColor = defaultView + ?.getComputedStyle( canvas, null ) + .getPropertyValue( 'background-color' ); + } + const colordBackgroundColor = colord( backgroundColor ); + // If background is transparent, it should be treated as light color. + if ( + colordBackgroundColor.luminance() > 0.5 || + colordBackgroundColor.alpha() === 0 + ) { + body.classList.remove( 'is-dark-theme' ); + } else { + body.classList.add( 'is-dark-theme' ); + } + }, + [ styles, scope ] + ); +} + +export default function EditorStyles( { styles, scope } ) { + const overrides = useSelect( + ( select ) => unlock( select( blockEditorStore ) ).getStyleOverrides(), + [] + ); + const [ transformedStyles, transformedSvgs ] = useMemo( () => { + const _styles = Object.values( styles ?? [] ); + + for ( const [ id, override ] of overrides ) { + const index = _styles.findIndex( ( { id: _id } ) => id === _id ); + const overrideWithId = { ...override, id }; + if ( index === -1 ) { + _styles.push( overrideWithId ); + } else { + _styles[ index ] = overrideWithId; + } + } + + return [ + transformStyles( + _styles.filter( ( style ) => style?.css ), + scope + ), + _styles + .filter( ( style ) => style.__unstableType === 'svgs' ) + .map( ( style ) => style.assets ) + .join( '' ), + ]; + }, [ styles, overrides, scope ] ); + + return ( + <> + { /* Use an empty style element to have a document reference, + but this could be any element. */ } + + ) ) } + + + ); +} diff --git a/packages/edit-site/src/components/block-preview/iframe.js b/packages/edit-site/src/components/block-preview/iframe.js new file mode 100644 index 0000000000000..9523d7189c2fa --- /dev/null +++ b/packages/edit-site/src/components/block-preview/iframe.js @@ -0,0 +1,305 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { + useState, + createPortal, + forwardRef, + useMemo, + useEffect, +} from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + useResizeObserver, + useMergeRefs, + useRefEffect, + useDisabled, +} from '@wordpress/compose'; +import { __experimentalStyleProvider as StyleProvider } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { store as blockEditorStore } from '@wordpress/block-editor'; + +function bubbleEvent( event, Constructor, frame ) { + const init = {}; + + for ( const key in event ) { + init[ key ] = event[ key ]; + } + + // Check if the event is a MouseEvent generated within the iframe. + // If so, adjust the coordinates to be relative to the position of + // the iframe. This ensures that components such as Draggable + // receive coordinates relative to the window, instead of relative + // to the iframe. Without this, the Draggable event handler would + // result in components "jumping" position as soon as the user + // drags over the iframe. + if ( event instanceof frame.contentDocument.defaultView.MouseEvent ) { + const rect = frame.getBoundingClientRect(); + init.clientX += rect.left; + init.clientY += rect.top; + } + + const newEvent = new Constructor( event.type, init ); + if ( init.defaultPrevented ) { + newEvent.preventDefault(); + } + const cancelled = ! frame.dispatchEvent( newEvent ); + + if ( cancelled ) { + event.preventDefault(); + } +} + +/** + * Bubbles some event types (keydown, keypress, and dragover) to parent document + * document to ensure that the keyboard shortcuts and drag and drop work. + * + * Ideally, we should remove event bubbling in the future. Keyboard shortcuts + * should be context dependent, e.g. actions on blocks like Cmd+A should not + * work globally outside the block editor. + * + * @param {Document} iframeDocument Document to attach listeners to. + */ +function useBubbleEvents( iframeDocument ) { + return useRefEffect( ( body ) => { + const { defaultView } = iframeDocument; + if ( ! defaultView ) { + return; + } + const { frameElement } = defaultView; + const eventTypes = [ 'dragover', 'mousemove' ]; + const handlers = {}; + for ( const name of eventTypes ) { + handlers[ name ] = ( event ) => { + const prototype = Object.getPrototypeOf( event ); + const constructorName = prototype.constructor.name; + const Constructor = window[ constructorName ]; + bubbleEvent( event, Constructor, frameElement ); + }; + body.addEventListener( name, handlers[ name ] ); + } + + return () => { + for ( const name of eventTypes ) { + body.removeEventListener( name, handlers[ name ] ); + } + }; + } ); +} + +function Iframe( { + contentRef, + children, + tabIndex = 0, + scale = 1, + frameSize = 0, + expand = false, + readonly, + forwardedRef: ref, + ...props +} ) { + const { resolvedAssets } = useSelect( ( select ) => { + const settings = select( blockEditorStore ).getSettings(); + return { + resolvedAssets: settings.__unstableResolvedAssets, + isPreviewMode: settings.__unstableIsPreviewMode, + }; + }, [] ); + const { styles = '', scripts = '' } = resolvedAssets; + const [ iframeDocument, setIframeDocument ] = useState(); + const [ bodyClasses, setBodyClasses ] = useState( [] ); + const [ contentResizeListener, { height: contentHeight } ] = + useResizeObserver(); + const setRef = useRefEffect( ( node ) => { + node._load = () => { + setIframeDocument( node.contentDocument ); + }; + let iFrameDocument; + // Prevent the default browser action for files dropped outside of dropzones. + function preventFileDropDefault( event ) { + event.preventDefault(); + } + function onLoad() { + const { contentDocument, ownerDocument } = node; + iFrameDocument = contentDocument; + + // Ideally ALL classes that are added through get_body_class should + // be added in the editor too, which we'll somehow have to get from + // the server in the future (which will run the PHP filters). + setBodyClasses( + Array.from( ownerDocument.body.classList ).filter( + ( name ) => + name.startsWith( 'admin-color-' ) || + name.startsWith( 'post-type-' ) || + name === 'wp-embed-responsive' + ) + ); + + contentDocument.dir = ownerDocument.dir; + + iFrameDocument.addEventListener( + 'dragover', + preventFileDropDefault, + false + ); + iFrameDocument.addEventListener( + 'drop', + preventFileDropDefault, + false + ); + } + + node.addEventListener( 'load', onLoad ); + + return () => { + delete node._load; + node.removeEventListener( 'load', onLoad ); + iFrameDocument?.removeEventListener( + 'dragover', + preventFileDropDefault + ); + iFrameDocument?.removeEventListener( + 'drop', + preventFileDropDefault + ); + }; + }, [] ); + + const disabledRef = useDisabled( { isDisabled: ! readonly } ); + const bodyRef = useMergeRefs( [ + useBubbleEvents( iframeDocument ), + contentRef, + disabledRef, + ] ); + + // Correct doctype is required to enable rendering in standards + // mode. Also preload the styles to avoid a flash of unstyled + // content. + const html = ` + + + + + + ${ styles } + ${ scripts } + + + + +`; + + const [ src, cleanup ] = useMemo( () => { + const _src = URL.createObjectURL( + new window.Blob( [ html ], { type: 'text/html' } ) + ); + return [ _src, () => URL.revokeObjectURL( _src ) ]; + }, [ html ] ); + + useEffect( () => cleanup, [ cleanup ] ); + + // We need to counter the margin created by scaling the iframe. If the scale + // is e.g. 0.45, then the top + bottom margin is 0.55 (1 - scale). Just the + // top or bottom margin is 0.55 / 2 ((1 - scale) / 2). + const marginFromScaling = ( contentHeight * ( 1 - scale ) ) / 2; + + return ( + <> + { /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */ } + + + ); +} + +function IframeIfReady( props, ref ) { + const isInitialised = useSelect( + ( select ) => + select( blockEditorStore ).getSettings().__internalIsInitialized, + [] + ); + + // We shouldn't render the iframe until the editor settings are initialised. + // The initial settings are needed to get the styles for the srcDoc, which + // cannot be changed after the iframe is mounted. srcDoc is used to to set + // the initial iframe HTML, which is required to avoid a flash of unstyled + // content. + if ( ! isInitialised ) { + return null; + } + + return + + ); +} + +export default function BlockPreview( props ) { + const { blocks } = props; + const [ containerResizeListener, { width: containerWidth } ] = + useResizeObserver(); + + const [ html, setHTML ] = useState( '' ); + + useEffect( () => { + const getHTML = async () => { + const dataHTML = await apiFetch( { + path: '/wp/v2/render_blocks', + method: 'POST', + data: blocks, + } ); + setHTML( dataHTML ); + }; + getHTML().catch( ( error ) => { + return error; + } ); + }, [ blocks ] ); + + return ( + <> +
+ { containerResizeListener } +
+
+ { !! containerWidth && ( + + ) } +
+ + ); +} From 096d39928aa8742fec5679f54b595057d0be4613 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 9 Nov 2023 08:46:03 +0200 Subject: [PATCH 07/21] serialize blocks before sending them to render --- lib/init.php | 10 +--------- .../edit-site/src/components/block-preview/index.js | 3 ++- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/init.php b/lib/init.php index 8aa5edc64563f..fea37ed148da1 100644 --- a/lib/init.php +++ b/lib/init.php @@ -71,15 +71,7 @@ function gutenberg_menu() { * @return string */ function gutenberg_render_blocks_from_request( $request ) { - $blocks = $request->get_json_params(); - - $blocks_content = ''; - foreach ( $blocks as $block ) { - $block['blockName'] = $block['name']; - $blocks_content .= render_block( $block ); - } - - return $blocks_content; + return do_blocks( $request->get_json_params() ); } } diff --git a/packages/edit-site/src/components/block-preview/index.js b/packages/edit-site/src/components/block-preview/index.js index c0dec384fe60e..97b02a145257b 100644 --- a/packages/edit-site/src/components/block-preview/index.js +++ b/packages/edit-site/src/components/block-preview/index.js @@ -7,6 +7,7 @@ import { useSelect } from '@wordpress/data'; import { useEffect, useMemo, useState } from '@wordpress/element'; import { store as blockEditorStore } from '@wordpress/block-editor'; import apiFetch from '@wordpress/api-fetch'; +import { serialize } from '@wordpress/blocks'; /** * Internal dependencies @@ -127,7 +128,7 @@ export default function BlockPreview( props ) { const dataHTML = await apiFetch( { path: '/wp/v2/render_blocks', method: 'POST', - data: blocks, + data: serialize( blocks ), } ); setHTML( dataHTML ); }; From 4ed76e315609a723457b0f416027dc06f1915fa4 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 9 Nov 2023 18:11:36 +0200 Subject: [PATCH 08/21] fix the rendering of blocks that require global context --- lib/init.php | 25 ++++++++++++++++--- .../src/components/block-preview/index.js | 13 +++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/init.php b/lib/init.php index fea37ed148da1..580f3b7813723 100644 --- a/lib/init.php +++ b/lib/init.php @@ -1,8 +1,8 @@ get_json_params() ); + // We need to fake a global $wp_query and $post. + // This is because some blocks (e.g. Query block) rely on them, + // and we don't have them in the REST API context. + // Without them, the preview will be empty. + global $wp_query; + global $post; + $data = $request->get_json_params(); + $fake_query = new WP_Query( + array( + 'post_type' => 'post', + 'posts_per_page' => get_option( 'posts_per_page' ), + 'post_status' => 'publish', + ) + ); + // Not sure if there is a better way to achieve this. + $wp_query = $fake_query; + $post = $wp_query->posts[0]; + return do_blocks( $data['blocks'] ); } } diff --git a/packages/edit-site/src/components/block-preview/index.js b/packages/edit-site/src/components/block-preview/index.js index 97b02a145257b..578574eb9abc2 100644 --- a/packages/edit-site/src/components/block-preview/index.js +++ b/packages/edit-site/src/components/block-preview/index.js @@ -14,6 +14,7 @@ import { serialize } from '@wordpress/blocks'; */ import EditorStyles from './editor-styles'; import Iframe from './iframe'; +import { store as editSiteStore } from '../../store'; const MAX_HEIGHT = 2000; @@ -123,12 +124,22 @@ export default function BlockPreview( props ) { const [ html, setHTML ] = useState( '' ); + const { editedPostId } = useSelect( ( select ) => { + const { getEditedPostId } = select( editSiteStore ); + return { + editedPostId: getEditedPostId(), + }; + }, [] ); + useEffect( () => { const getHTML = async () => { const dataHTML = await apiFetch( { path: '/wp/v2/render_blocks', method: 'POST', - data: serialize( blocks ), + data: { + blocks: serialize( blocks ), + post_id: editedPostId, + }, } ); setHTML( dataHTML ); }; From aa9bc1000d67c9bbb1b748e720520ad4fb937578 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 9 Nov 2023 18:14:41 +0200 Subject: [PATCH 09/21] revert the pattern list component edits --- .../block-editor/src/components/block-patterns-list/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index f237d7898b845..510833313013c 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -51,7 +51,6 @@ function BlockPattern( { showTooltip, } ) { const [ isDragging, setIsDragging ] = useState( false ); - const { blocks, viewportWidth } = pattern; const instanceId = useInstanceId( BlockPattern ); const descriptionId = `block-editor-block-patterns-list__item-description-${ instanceId }`; From 6e2511ed22e82b45d435a3d64fbf7b6e1e5b67a4 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Wed, 22 Nov 2023 18:02:03 +0200 Subject: [PATCH 10/21] adjustments after trunk rebase --- .../src/components/block-patterns-list/index.js | 1 + .../src/components/block-editor/site-editor-canvas.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index 510833313013c..beee58afcd876 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -28,6 +28,7 @@ import { unlock } from '../../lock-unlock'; import BlockPreview from '../block-preview'; import InserterDraggableBlocks from '../inserter-draggable-blocks'; import BlockPatternsPaging from '../block-patterns-paging'; +import { PATTERN_TYPES } from '../inserter/block-patterns-tab/utils'; const { CompositeV2: Composite, diff --git a/packages/edit-site/src/components/block-editor/site-editor-canvas.js b/packages/edit-site/src/components/block-editor/site-editor-canvas.js index 3bba8cc26d01f..6b24640dbb871 100644 --- a/packages/edit-site/src/components/block-editor/site-editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/site-editor-canvas.js @@ -22,6 +22,7 @@ import { NAVIGATION_POST_TYPE, } from '../../utils/constants'; import { unlock } from '../../lock-unlock'; +import BlockPreview from '../block-preview'; export default function SiteEditorCanvas() { const { templateType, isFocusMode, isViewMode } = useSelect( ( select ) => { @@ -78,7 +79,10 @@ export default function SiteEditorCanvas() { > { resizeObserver } From aeae7411c1f90309423e0d33e0e3625fc0bd2e59 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Wed, 22 Nov 2023 19:17:13 +0200 Subject: [PATCH 11/21] updates for edtitor implementation refactoring --- .../default-block-editor-provider.js | 76 ------------------- .../block-editor/use-site-editor-settings.js | 3 + .../edit-site/src/components/editor/index.js | 1 + 3 files changed, 4 insertions(+), 76 deletions(-) delete mode 100644 packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js diff --git a/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js b/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js deleted file mode 100644 index 61164ca29c0db..0000000000000 --- a/packages/edit-site/src/components/block-editor/block-editor-provider/default-block-editor-provider.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * WordPress dependencies - */ -import { useEntityBlockEditor } from '@wordpress/core-data'; -import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; -import { useSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { store as editSiteStore } from '../../../store'; -import { unlock } from '../../../lock-unlock'; -import useSiteEditorSettings from '../use-site-editor-settings'; -import usePageContentBlocks from './use-page-content-blocks'; -import BlockPreview from '../../block-preview'; - -const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis ); - -const noop = () => {}; - -/** - * The default block editor provider for the site editor. Typically used when - * the post type is `'wp_template_part'` or `'wp_template'` and allows editing - * of the template and its nested entities. - * - * If the page content focus type is `'hideTemplate'`, the provider will provide - * a set of page content blocks wrapped in a container that, together, - * mimic the look and feel of the post editor and - * allow editing of the page content only. - * - * @param {Object} props - * @param {Element} props.children - */ -export default function DefaultBlockEditorProvider( { children } ) { - const settings = useSiteEditorSettings(); - - const { templateType, isTemplateHidden } = useSelect( ( select ) => { - const { getEditedPostType } = select( editSiteStore ); - const { getPageContentFocusType, getCanvasMode } = unlock( - select( editSiteStore ) - ); - return { - templateType: getEditedPostType(), - isTemplateHidden: - getCanvasMode() === 'edit' && - getPageContentFocusType() === 'hideTemplate', - canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(), - }; - }, [] ); - - const [ blocks, onInput, onChange ] = useEntityBlockEditor( - 'postType', - templateType - ); - const pageContentBlocks = usePageContentBlocks( { - blocks, - isPageContentFocused: isTemplateHidden, - wrapPageContent: true, - } ); - - return ( - - { children } - - ); -} diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js index cbe70cbee83c0..8fc844e0e87ec 100644 --- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js +++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js @@ -13,6 +13,7 @@ import { store as preferencesStore } from '@wordpress/preferences'; */ import { store as editSiteStore } from '../../store'; import { unlock } from '../../lock-unlock'; +import BlockPreview from '../block-preview'; const { useBlockEditorSettings } = unlock( editorPrivateApis ); @@ -159,6 +160,8 @@ export function useSpecificEditorSettings() { keepCaretInsideBlock, defaultRenderingMode, + blockPreview: BlockPreview, + // I wonder if they should be set in the post editor too __experimentalArchiveTitleTypeLabel: archiveLabels.archiveTypeLabel, __experimentalArchiveTitleNameLabel: archiveLabels.archiveNameLabel, diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index afae1b362ee99..b4300e2702701 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -186,6 +186,7 @@ export default function Editor( { isLoading } ) { ); const settings = useSpecificEditorSettings(); + const isReady = ! isLoading && ( ( postWithTemplate && !! contextPost && !! editedPost ) || From 611fb31c5a37d7d5b5356907553b932b21463687 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 23 Nov 2023 15:56:05 +0200 Subject: [PATCH 12/21] allow the blockPreview setting to get to the block editor provider --- .../editor/src/components/provider/use-block-editor-settings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 49f61815f663d..f704fe54c50c0 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -30,6 +30,7 @@ const BLOCK_EDITOR_SETTINGS = [ 'allowedBlockTypes', 'allowRightClickOverrides', 'blockInspectorTabs', + 'blockPreview', 'allowedMimeTypes', 'bodyPlaceholder', 'canLockBlocks', From cee067f9e9a957fb0b732be546299502361473a1 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 23 Nov 2023 17:34:33 +0200 Subject: [PATCH 13/21] add REST class for render blocks endpoint --- ...ass-gutenberg-render-blocks-controller.php | 132 ++++++++++++++++++ lib/compat/wordpress-6.5/rest-api.php | 10 ++ lib/init.php | 51 ------- lib/load.php | 1 + 4 files changed, 143 insertions(+), 51 deletions(-) create mode 100644 lib/compat/wordpress-6.5/class-gutenberg-render-blocks-controller.php diff --git a/lib/compat/wordpress-6.5/class-gutenberg-render-blocks-controller.php b/lib/compat/wordpress-6.5/class-gutenberg-render-blocks-controller.php new file mode 100644 index 0000000000000..67e378163da5c --- /dev/null +++ b/lib/compat/wordpress-6.5/class-gutenberg-render-blocks-controller.php @@ -0,0 +1,132 @@ +namespace = 'wp/v2'; + $this->rest_base = 'render_blocks'; + } + + /** + * Registers the routes for the objects of the controller. + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'render_blocks_from_request' ), + 'permission_callback' => array( $this, 'get_permissions_check' ), + 'args' => array( + 'blocks' => array( + 'required' => true, + 'validate_callback' => array( $this, 'validate_blocks' ), + 'sanitize_callback' => array( $this, 'sanitize_blocks' ), + ), + ), + 'schema' => array( $this, 'get_item_schema' ), + ), + ) + ); + } + + /** + * Checks if a given request has access to create items. + */ + public function get_permissions_check() { + return true; + } + + /** + * Checks if the blocks string is valid. + * + * @param string $blocks Full data about the request. + * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise. + */ + public function validate_blocks( $blocks ) { + $blocks = parse_blocks( $blocks ); + if ( ! is_array( $blocks ) ) { + // If parse_blocks does not return an array, it's not a valid block string. + return new WP_Error( 'rest_invalid_blocks', __( 'The blocks parameter is invalid.', 'gutenberg' ), array( 'status' => 400 ) ); + } + + return true; + } + + /** + * Sanitizes the 'blocks' parameter. + * + * @param string $blocks The blocks string. + */ + public function sanitize_blocks( $blocks ) { + // Sanitize the blocks string to ensure it's a clean string. + return wp_kses_post( $blocks ); + } + + /** + * Renders blocks from a REST API request. + * + * @param WP_REST_Request $request Full data about the request. + */ + public function render_blocks_from_request( $request ) { + global $wp_query, $post; + + $data = $request->get_json_params(); + + // We need to fake a global $wp_query and $post. + // This is because some blocks (e.g. Query block) rely on them, + // and we don't have them in the REST API context. + // Without them, the preview will be empty. + $fake_query = new WP_Query( + array( + 'post_type' => 'post', + 'posts_per_page' => get_option( 'posts_per_page' ), + 'post_status' => 'publish', + ) + ); + $wp_query = $fake_query; + $post = $wp_query->posts[0]; + + $rendered_blocks = do_blocks( $data['blocks'] ); + + return rest_ensure_response( $rendered_blocks ); + } + + /** + * Retrieves the block renderer's schema, conforming to JSON Schema. + */ + public function get_item_schema() { + return array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'block-render', + 'type' => 'object', + 'properties' => array( + 'blocks' => array( + 'description' => __( 'Serialized blocks to render', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit' ), + ), + ), + ); + } + } +} diff --git a/lib/compat/wordpress-6.5/rest-api.php b/lib/compat/wordpress-6.5/rest-api.php index 12d789fb58b86..883df033d55d3 100644 --- a/lib/compat/wordpress-6.5/rest-api.php +++ b/lib/compat/wordpress-6.5/rest-api.php @@ -21,6 +21,7 @@ function gutenberg_register_global_styles_revisions_endpoints() { add_action( 'rest_api_init', 'gutenberg_register_global_styles_revisions_endpoints' ); /** +<<<<<<< HEAD * Registers additional fields for wp_template and wp_template_part rest api. * * @access private @@ -144,3 +145,12 @@ function _gutenberg_register_wp_templates_additional_fields() { } add_action( 'rest_api_init', '_gutenberg_register_wp_templates_additional_fields' ); +/** + * Registers the Block Rederer REST API routes. + */ +function gutenberg_register_block_rederer_routes() { + $block_renderer_controller = new Gutenberg_Render_Blocks_Controller(); + $block_renderer_controller->register_routes(); +} + +add_action( 'rest_api_init', 'gutenberg_register_block_rederer_routes' ); diff --git a/lib/init.php b/lib/init.php index 580f3b7813723..3af533268644c 100644 --- a/lib/init.php +++ b/lib/init.php @@ -61,54 +61,3 @@ function gutenberg_menu() { ); } add_action( 'admin_menu', 'gutenberg_menu', 9 ); - -if ( ! function_exists( 'gutenberg_render_blocks_from_request' ) ) { - /** - * Render blocks from a REST API request. - * - * @param mixed $request The current request. - * - * @return string - */ - function gutenberg_render_blocks_from_request( $request ) { - // We need to fake a global $wp_query and $post. - // This is because some blocks (e.g. Query block) rely on them, - // and we don't have them in the REST API context. - // Without them, the preview will be empty. - global $wp_query; - global $post; - $data = $request->get_json_params(); - $fake_query = new WP_Query( - array( - 'post_type' => 'post', - 'posts_per_page' => get_option( 'posts_per_page' ), - 'post_status' => 'publish', - ) - ); - // Not sure if there is a better way to achieve this. - $wp_query = $fake_query; - $post = $wp_query->posts[0]; - return do_blocks( $data['blocks'] ); - } -} - -if ( ! function_exists( 'register_gutenberg_render_blocks_endpoint' ) ) { - /** - * Register custom REST endpoint for rendering blocks. - * - * @return void - */ - function register_gutenberg_render_blocks_endpoint() { - register_rest_route( - 'wp/v2', - '/render_blocks', - array( - 'methods' => 'POST', - 'callback' => 'gutenberg_render_blocks_from_request', - 'permission_callback' => '__return_true', - ) - ); - } -} - -add_action( 'rest_api_init', 'register_gutenberg_render_blocks_endpoint' ); diff --git a/lib/load.php b/lib/load.php index 7dd30982dbf06..0f79e51742590 100644 --- a/lib/load.php +++ b/lib/load.php @@ -43,6 +43,7 @@ function gutenberg_is_experiment_enabled( $name ) { // WordPress 6.5 compat. require_once __DIR__ . '/compat/wordpress-6.5/class-gutenberg-rest-global-styles-revisions-controller-6-5.php'; + require_once __DIR__ . '/compat/wordpress-6.5/class-gutenberg-render-blocks-controller.php'; require_once __DIR__ . '/compat/wordpress-6.5/rest-api.php'; // Plugin specific code. From d991d0a218ded6693c25fbba8625bfa131a15133 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 23 Nov 2023 17:35:47 +0200 Subject: [PATCH 14/21] remove debugging error reporting --- lib/init.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/init.php b/lib/init.php index 3af533268644c..88dcba4525f6e 100644 --- a/lib/init.php +++ b/lib/init.php @@ -1,8 +1,4 @@ Date: Tue, 28 Nov 2023 14:26:57 +0200 Subject: [PATCH 15/21] rebase cleanup --- lib/compat/wordpress-6.5/rest-api.php | 1 - .../block-editor/src/components/block-patterns-list/index.js | 4 ---- packages/edit-site/src/components/editor/index.js | 1 - 3 files changed, 6 deletions(-) diff --git a/lib/compat/wordpress-6.5/rest-api.php b/lib/compat/wordpress-6.5/rest-api.php index 883df033d55d3..f36e1ec88a8ce 100644 --- a/lib/compat/wordpress-6.5/rest-api.php +++ b/lib/compat/wordpress-6.5/rest-api.php @@ -21,7 +21,6 @@ function gutenberg_register_global_styles_revisions_endpoints() { add_action( 'rest_api_init', 'gutenberg_register_global_styles_revisions_endpoints' ); /** -<<<<<<< HEAD * Registers additional fields for wp_template and wp_template_part rest api. * * @access private diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index beee58afcd876..c79d6927c00f0 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -6,11 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -<<<<<<< HEAD import { useEffect, useState, forwardRef } from '@wordpress/element'; -======= -import { useState, forwardRef } from '@wordpress/element'; ->>>>>>> 8bc5711934 (not functional commit - refactor the implementation) import { VisuallyHidden, Tooltip, diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index b4300e2702701..afae1b362ee99 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -186,7 +186,6 @@ export default function Editor( { isLoading } ) { ); const settings = useSpecificEditorSettings(); - const isReady = ! isLoading && ( ( postWithTemplate && !! contextPost && !! editedPost ) || From 5b56232ea183a5ff0d38ed8fd1a18040eb02e2ea Mon Sep 17 00:00:00 2001 From: scruffian Date: Mon, 15 Jan 2024 17:56:30 +0000 Subject: [PATCH 16/21] add prototype for prerendering data --- lib/load.php | 28 +++++++++++++++++++ packages/block-library/src/image/block.json | 7 +++++ packages/block-library/src/image/index.js | 8 ------ .../block-library/src/site-logo/block.json | 2 +- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/load.php b/lib/load.php index 0f79e51742590..d26aeea59d2c5 100644 --- a/lib/load.php +++ b/lib/load.php @@ -259,3 +259,31 @@ function () { // Data views. require_once __DIR__ . '/experimental/data-views.php'; + + +function modify_block_attributes_before_render( $block ) { + $block_type_registry = WP_Block_Type_Registry::get_instance(); + $block_type = $block_type_registry->get_registered( $block['blockName'] ); + + if ( isset( $block_type->example ) && isset( $block_type->example[ 'attributes' ] ) ) { + // Change the attribute value. + $block['attrs'] = array_merge( $block['attrs'], $block_type->example[ 'attributes' ] ); + } + if ( $block['blockName'] === 'core/image' ) { + echo $block['blockName']; + var_dump($block['attrs']); + } + return $block; +} + +add_filter( 'render_block_data', 'modify_block_attributes_before_render', 10, 2 ); + + + + + + + + + + diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index c5191e3dd8654..0378188a39c07 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -91,6 +91,13 @@ "attribute": "target" } }, + "example": { + "attributes": { + "sizeSlug": "large", + "url": "https://s.w.org/images/core/5.3/MtBlanc1.jpg", + "caption": "Mont Blanc appears—still, snowy, and serene." + } + }, "supports": { "interactivity": true, "align": [ "left", "center", "right", "wide", "full" ], diff --git a/packages/block-library/src/image/index.js b/packages/block-library/src/image/index.js index 1477fa99c702c..9c5ef170f7ffb 100644 --- a/packages/block-library/src/image/index.js +++ b/packages/block-library/src/image/index.js @@ -20,14 +20,6 @@ export { metadata, name }; export const settings = { icon, - example: { - attributes: { - sizeSlug: 'large', - url: 'https://s.w.org/images/core/5.3/MtBlanc1.jpg', - // translators: Caption accompanying an image of the Mont Blanc, which serves as an example for the Image block. - caption: __( 'Mont Blanc appears—still, snowy, and serene.' ), - }, - }, __experimentalLabel( attributes, { context } ) { if ( context === 'accessibility' ) { const { caption, alt, url } = attributes; diff --git a/packages/block-library/src/site-logo/block.json b/packages/block-library/src/site-logo/block.json index d1e3d1b20c3da..b1dd91b512175 100644 --- a/packages/block-library/src/site-logo/block.json +++ b/packages/block-library/src/site-logo/block.json @@ -26,7 +26,7 @@ "viewportWidth": 500, "attributes": { "width": 350, - "className": "block-editor-block-types-list__site-logo-example" + "className": "block-editor-block-types-list__site-logo-example-blah" } }, "supports": { From 19c78fdace8fc67f494e90dc7fbbe63062f16c94 Mon Sep 17 00:00:00 2001 From: scruffian Date: Tue, 16 Jan 2024 15:09:06 +0000 Subject: [PATCH 17/21] source example data and update all attributes with it --- lib/load.php | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/load.php b/lib/load.php index d26aeea59d2c5..45b0df5399842 100644 --- a/lib/load.php +++ b/lib/load.php @@ -260,30 +260,29 @@ function () { // Data views. require_once __DIR__ . '/experimental/data-views.php'; - +// Updates all blocks to use their example data, if they have it. function modify_block_attributes_before_render( $block ) { $block_type_registry = WP_Block_Type_Registry::get_instance(); $block_type = $block_type_registry->get_registered( $block['blockName'] ); - if ( isset( $block_type->example ) && isset( $block_type->example[ 'attributes' ] ) ) { - // Change the attribute value. - $block['attrs'] = array_merge( $block['attrs'], $block_type->example[ 'attributes' ] ); - } - if ( $block['blockName'] === 'core/image' ) { - echo $block['blockName']; - var_dump($block['attrs']); + foreach( $block_type->example[ 'attributes' ] as $attribute_name => $attribute_value ) { + // Only replace attributes that are already set. + if ( isset( $block['attrs'][ $attribute_name ] ) ) { + $block['attrs'][ $attribute_name ] = $attribute_value; + } + $attribute_definition = $block_type->attributes[ $attribute_name ]; + // Is this attribute sourced from the block markup istead of the block json comment. + if ( isset( $attribute_definition['source'] ) && $attribute_definition['source'] === 'attribute' ) { + $processor = new WP_HTML_Tag_Processor( $block['innerHTML'] ); //Should this be innerContent? + if ( $processor->next_tag( $attribute_definition['selector'] ) ) { + $processor->set_attribute( $attribute_definition['attribute'], $attribute_value ); + $block['innerHTML'] = $processor->get_updated_html(); + $block['innerContent'] = array( $processor->get_updated_html() ); + } + } + } } return $block; } add_filter( 'render_block_data', 'modify_block_attributes_before_render', 10, 2 ); - - - - - - - - - - From 16347727617e0e5294e616044d544ad2cddbbebe Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Wed, 17 Jan 2024 15:16:15 +0000 Subject: [PATCH 18/21] Update packages/block-library/src/site-logo/block.json Co-authored-by: Dave Smith --- packages/block-library/src/site-logo/block.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/site-logo/block.json b/packages/block-library/src/site-logo/block.json index b1dd91b512175..d1e3d1b20c3da 100644 --- a/packages/block-library/src/site-logo/block.json +++ b/packages/block-library/src/site-logo/block.json @@ -26,7 +26,7 @@ "viewportWidth": 500, "attributes": { "width": 350, - "className": "block-editor-block-types-list__site-logo-example-blah" + "className": "block-editor-block-types-list__site-logo-example" } }, "supports": { From 1dd19b1941389190b90d0bef9d341879444bb059 Mon Sep 17 00:00:00 2001 From: scruffian Date: Wed, 17 Jan 2024 17:29:50 +0000 Subject: [PATCH 19/21] im sorry --- lib/load.php | 60 ++++++++++++++++++- packages/block-library/src/cover/block.json | 3 + .../src/post-featured-image/block.json | 3 + .../src/post-featured-image/index.php | 1 + 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/lib/load.php b/lib/load.php index 45b0df5399842..cf90d2403e316 100644 --- a/lib/load.php +++ b/lib/load.php @@ -264,25 +264,81 @@ function () { function modify_block_attributes_before_render( $block ) { $block_type_registry = WP_Block_Type_Registry::get_instance(); $block_type = $block_type_registry->get_registered( $block['blockName'] ); + // we should use the bindings API! if ( isset( $block_type->example ) && isset( $block_type->example[ 'attributes' ] ) ) { foreach( $block_type->example[ 'attributes' ] as $attribute_name => $attribute_value ) { // Only replace attributes that are already set. if ( isset( $block['attrs'][ $attribute_name ] ) ) { $block['attrs'][ $attribute_name ] = $attribute_value; } + $block['attrs'][ $attribute_name ] = $attribute_value; + if ( $block['blockName'] === 'core/cover' ) { + //var_dump( $block['attrs'] ); + //var_dump( $attribute_name ); + //var_dump( $attribute_value ); + } + $attribute_definition = $block_type->attributes[ $attribute_name ]; // Is this attribute sourced from the block markup istead of the block json comment. if ( isset( $attribute_definition['source'] ) && $attribute_definition['source'] === 'attribute' ) { $processor = new WP_HTML_Tag_Processor( $block['innerHTML'] ); //Should this be innerContent? if ( $processor->next_tag( $attribute_definition['selector'] ) ) { $processor->set_attribute( $attribute_definition['attribute'], $attribute_value ); - $block['innerHTML'] = $processor->get_updated_html(); + //$block['innerHTML'] = $processor->get_updated_html(); $block['innerContent'] = array( $processor->get_updated_html() ); } } } } + return $block; } -add_filter( 'render_block_data', 'modify_block_attributes_before_render', 10, 2 ); +function modify_block_attributes_during_render( $block_content, $block ) { + //var_dump( $block['blockName'] ); + $block_type_registry = WP_Block_Type_Registry::get_instance(); + $block_type = $block_type_registry->get_registered( $block['blockName'] ); + /*if ( isset( $block_type->example ) && isset( $block_type->example[ 'attributes' ] ) ) { + + if ( $block['blockName'] === 'core/cover' ) { + if ( isset( $block['attrs']['useFeaturedImage'] ) ) { + $processor = new WP_HTML_Tag_Processor( $block_content ); + $processor->next_tag(); + $processor->set_attribute( 'style', 'background-image: url('. $block_type->example[ 'attributes' ]['url'] .');' ); + $block_content = $processor->get_updated_html(); + } + } + }*/ + + if ( $block['blockName'] === 'core/cover' ) { + //var_dump( $block_content ); + /*$processor = new WP_HTML_Tag_Processor( $block_content ); + if ( ! $processor->next_tag('img') ) { + if ( isset( $block['attrs']['useFeaturedImage'] ) && $block['attrs']['useFeaturedImage'] ) { + $inner_blocks_html = $block['innerBlocks'][0]['innerHTML']; + return str_replace( '[[INNER_BLOCKS]]', $inner_blocks_html, $block_type->example[ 'preview' ] ); + } + }*/ + } + + /*if ( isset( $block_type->example[ 'preview' ] ) && $block_content === '' ) { + return $block_type->example[ 'preview' ]; + }*/ + + return $block_content; +} + +//add_filter( 'render_block_data', 'modify_block_attributes_before_render', 10, 2 ); +//add_filter( 'render_block', 'modify_block_attributes_during_render', 10, 2 ); + + +function modify_post_thumbnail_html( $html, $post_id, $post_thumbnail_id, $size, $attr ) { + $classes = isset( $attr['class'] ) ? $attr['class'] : ''; + $style = isset( $attr['style'] ) ? $attr['style'] : ''; + if ( ! $html ) { + return ''; + } + return $html; + +} +add_filter( 'post_thumbnail_html', 'modify_post_thumbnail_html', 10, 5 ); diff --git a/packages/block-library/src/cover/block.json b/packages/block-library/src/cover/block.json index d2c55dd26b4d7..a5871d88b562b 100644 --- a/packages/block-library/src/cover/block.json +++ b/packages/block-library/src/cover/block.json @@ -80,6 +80,9 @@ "default": "div" } }, + "example": { + "preview": "
[[INNER_BLOCKS]]
" + }, "usesContext": [ "postId", "postType" ], "supports": { "anchor": true, diff --git a/packages/block-library/src/post-featured-image/block.json b/packages/block-library/src/post-featured-image/block.json index 34e3bd6b2325f..6e09ac74a34c9 100644 --- a/packages/block-library/src/post-featured-image/block.json +++ b/packages/block-library/src/post-featured-image/block.json @@ -53,6 +53,9 @@ "type": "string" } }, + "example": { + "preview": "
" + }, "usesContext": [ "postId", "postType", "queryId" ], "supports": { "align": [ "left", "right", "center", "wide", "full" ], diff --git a/packages/block-library/src/post-featured-image/index.php b/packages/block-library/src/post-featured-image/index.php index 4a7aa2f3d8ab9..75d706664cd30 100644 --- a/packages/block-library/src/post-featured-image/index.php +++ b/packages/block-library/src/post-featured-image/index.php @@ -54,6 +54,7 @@ function render_block_core_post_featured_image( $attributes, $content, $block ) } $featured_image = get_the_post_thumbnail( $post_ID, $size_slug, $attr ); + //var_dump( $featured_image ); if ( ! $featured_image ) { return ''; } From c1776160787b706ae688399649aee6983f07a3c7 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 18 Jan 2024 10:33:46 +0200 Subject: [PATCH 20/21] example of block attribute convention for dynamic rendering --- docs/reference-guides/core-blocks.md | 2 +- lib/load.php | 6 ++++++ .../src/post-featured-image/block.json | 8 ++++++++ .../src/post-featured-image/index.php | 17 +++++++++++++++-- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 9f25ad0a594b8..894ba4cc5ae6e 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -630,7 +630,7 @@ Display a post's featured image. ([Source](https://github.com/WordPress/gutenber - **Name:** core/post-featured-image - **Category:** theme - **Supports:** align (center, full, left, right, wide), color (~~background~~, ~~text~~), spacing (margin, padding), ~~html~~ -- **Attributes:** aspectRatio, customGradient, customOverlayColor, dimRatio, gradient, height, isLink, linkTarget, overlayColor, rel, scale, sizeSlug, width +- **Attributes:** aspectRatio, customGradient, customOverlayColor, dimRatio, featured_image, gradient, height, isLink, linkTarget, overlayColor, rel, scale, sizeSlug, width ## Post Navigation Link diff --git a/lib/load.php b/lib/load.php index 45b0df5399842..0e7627c18e4b7 100644 --- a/lib/load.php +++ b/lib/load.php @@ -262,6 +262,12 @@ function () { // Updates all blocks to use their example data, if they have it. function modify_block_attributes_before_render( $block ) { + + + if ( ! isset( $_GET['block_preview'] ) ) { + return $block; + }; + $block_type_registry = WP_Block_Type_Registry::get_instance(); $block_type = $block_type_registry->get_registered( $block['blockName'] ); if ( isset( $block_type->example ) && isset( $block_type->example[ 'attributes' ] ) ) { diff --git a/packages/block-library/src/post-featured-image/block.json b/packages/block-library/src/post-featured-image/block.json index 34e3bd6b2325f..a757dd7b38432 100644 --- a/packages/block-library/src/post-featured-image/block.json +++ b/packages/block-library/src/post-featured-image/block.json @@ -6,7 +6,15 @@ "category": "theme", "description": "Display a post's featured image.", "textdomain": "default", + "example": { + "attributes": { + "featured_image": "
\"\"
" + } + }, "attributes": { + "featured_image": { + "type": "string" + }, "isLink": { "type": "boolean", "default": false diff --git a/packages/block-library/src/post-featured-image/index.php b/packages/block-library/src/post-featured-image/index.php index 4a7aa2f3d8ab9..9a688353c83bf 100644 --- a/packages/block-library/src/post-featured-image/index.php +++ b/packages/block-library/src/post-featured-image/index.php @@ -1,4 +1,5 @@ context['postId']; $is_link = isset( $attributes['isLink'] ) && $attributes['isLink']; - $size_slug = isset( $attributes['sizeSlug'] ) ? $attributes['sizeSlug'] : 'post-thumbnail'; $attr = get_block_core_post_featured_image_border_attributes( $attributes ); $overlay_markup = get_block_core_post_featured_image_overlay_element_markup( $attributes ); @@ -53,7 +53,8 @@ function render_block_core_post_featured_image( $attributes, $content, $block ) $attr['style'] = empty( $attr['style'] ) ? $extra_styles : $attr['style'] . $extra_styles; } - $featured_image = get_the_post_thumbnail( $post_ID, $size_slug, $attr ); + $featured_image = + $featured_image = $attributes['featured_image'] ; if ( ! $featured_image ) { return ''; } @@ -91,6 +92,18 @@ function render_block_core_post_featured_image( $attributes, $content, $block ) return "
{$featured_image}
"; } +function assign_featured_image( $block ) { + global $post; + $size_slug = isset( $block['attrs']['sizeSlug'] ) ? $block['attrs']['sizeSlug'] : 'post-thumbnail'; + $attr = get_block_core_post_featured_image_border_attributes( $block['attrs'] ); + + $block['attrs']['featured_image'] = get_the_post_thumbnail( $post->id, $size_slug, $attr ); + + return $block; +} + +add_filter( 'render_block_data', 'assign_featured_image', 8, 2 ); + /** * Generate markup for the HTML element that will be used for the overlay. * From cf78236c79deb556d814750e45b402b92c3a6b06 Mon Sep 17 00:00:00 2001 From: scruffian Date: Thu, 18 Jan 2024 11:43:35 +0000 Subject: [PATCH 21/21] use embedded svg --- lib/load.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/load.php b/lib/load.php index 2739b98eabe23..0acdbaa974259 100644 --- a/lib/load.php +++ b/lib/load.php @@ -341,8 +341,9 @@ function modify_block_attributes_during_render( $block_content, $block ) { function modify_post_thumbnail_html( $html, $post_id, $post_thumbnail_id, $size, $attr ) { $classes = isset( $attr['class'] ) ? $attr['class'] : ''; $style = isset( $attr['style'] ) ? $attr['style'] : ''; + $placeholder_svg = "%3Csvg fill='none' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 60' preserveAspectRatio='none' class='components-placeholder__illustration' aria-hidden='true' focusable='false' style='background: %23e1e1e1; stroke: %23000;'%3E%3Cpath vector-effect='non-scaling-stroke' d='M60 60 0 0'%3E%3C/path%3E%3C/svg%3E "; if ( ! $html ) { - return ''; + return ''; } return $html;