From 3e54df46bc9a43716d50f66de4705b91897009cd Mon Sep 17 00:00:00 2001 From: epiqueras Date: Tue, 2 Jul 2019 16:43:43 -0400 Subject: [PATCH 01/70] Block Library: Add new post-title block. --- packages/block-library/src/index.js | 6 +++++ .../block-library/src/post-title/block.json | 9 +++++++ packages/block-library/src/post-title/edit.js | 24 +++++++++++++++++++ .../block-library/src/post-title/index.js | 19 +++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 packages/block-library/src/post-title/block.json create mode 100644 packages/block-library/src/post-title/edit.js create mode 100644 packages/block-library/src/post-title/index.js diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index a9a9b78f34ca92..1a721efaa62d61 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -62,6 +62,9 @@ import * as tagCloud from './tag-cloud'; import * as classic from './classic'; +// Top-level template blocks. +import * as postTitle from './post-title'; + /** * Function to register core blocks provided by the block editor. * @@ -124,6 +127,9 @@ export const registerCoreBlocks = () => { textColumns, verse, video, + + // Register top-level template blocks. + postTitle, ].forEach( ( block ) => { if ( ! block ) { return; diff --git a/packages/block-library/src/post-title/block.json b/packages/block-library/src/post-title/block.json new file mode 100644 index 00000000000000..62a4899b043914 --- /dev/null +++ b/packages/block-library/src/post-title/block.json @@ -0,0 +1,9 @@ +{ + "name": "core/post-title", + "category": "common", + "attributes": { + "title": { + "type": "string" + } + } +} diff --git a/packages/block-library/src/post-title/edit.js b/packages/block-library/src/post-title/edit.js new file mode 100644 index 00000000000000..207c610162bd74 --- /dev/null +++ b/packages/block-library/src/post-title/edit.js @@ -0,0 +1,24 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * WordPress dependencies + */ +import { RichText } from '@wordpress/block-editor'; + +export default function PostTitleEdit( { + attributes: { title }, + setAttributes, +} ) { + return ( + setAttributes( { title: value } ) } + tagName="h1" + placeholder={ __( 'Title' ) } + formattingControls={ [] } + /> + ); +} diff --git a/packages/block-library/src/post-title/index.js b/packages/block-library/src/post-title/index.js new file mode 100644 index 00000000000000..b056c40a0a130c --- /dev/null +++ b/packages/block-library/src/post-title/index.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import edit from './edit'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + title: __( 'Post Title' ), + edit, +}; From 8265a3986c62bda514c4ff7e3c40755234ad6abd Mon Sep 17 00:00:00 2001 From: epiqueras Date: Thu, 4 Jul 2019 16:19:43 -0400 Subject: [PATCH 02/70] Edit Post: Set a default post template that uses the core/post-title block. Remove the `PostTitle` component from the `VisualEditor` component. --- lib/load.php | 1 + lib/templates.php | 18 ++++++++++++++++++ .../src/components/visual-editor/index.js | 6 +----- 3 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 lib/templates.php diff --git a/lib/load.php b/lib/load.php index 7a3c97fb65553b..eb273e165ca948 100644 --- a/lib/load.php +++ b/lib/load.php @@ -30,6 +30,7 @@ require dirname( __FILE__ ) . '/compat.php'; require dirname( __FILE__ ) . '/blocks.php'; +require dirname( __FILE__ ) . '/templates.php'; require dirname( __FILE__ ) . '/client-assets.php'; require dirname( __FILE__ ) . '/demo.php'; require dirname( __FILE__ ) . '/widgets.php'; diff --git a/lib/templates.php b/lib/templates.php new file mode 100644 index 00000000000000..d9c06b05f46dbc --- /dev/null +++ b/lib/templates.php @@ -0,0 +1,18 @@ +template = array( + array( 'core/post-title' ), + ); + $post_post_type_object->template_lock = 'insert'; +} +add_action( 'init', 'gutenberg_register_templates' ); diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 8287f5c27396d5..748def29170c68 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -1,10 +1,7 @@ /** * WordPress dependencies */ -import { - PostTitle, - VisualEditorGlobalKeyboardShortcuts, -} from '@wordpress/editor'; +import { VisualEditorGlobalKeyboardShortcuts } from '@wordpress/editor'; import { WritingFlow, ObserveTyping, @@ -30,7 +27,6 @@ function VisualEditor() { - From f1a7d9e6450b81b71b31ef480148837fd107c5ad Mon Sep 17 00:00:00 2001 From: epiqueras Date: Thu, 4 Jul 2019 16:42:25 -0400 Subject: [PATCH 03/70] Block Library: Add new post-content block and use it in the default post template. --- lib/templates.php | 1 + packages/block-library/src/index.js | 2 ++ .../block-library/src/post-content/block.json | 4 ++++ .../block-library/src/post-content/edit.js | 8 ++++++++ .../block-library/src/post-content/index.js | 19 +++++++++++++++++++ 5 files changed, 34 insertions(+) create mode 100644 packages/block-library/src/post-content/block.json create mode 100644 packages/block-library/src/post-content/edit.js create mode 100644 packages/block-library/src/post-content/index.js diff --git a/lib/templates.php b/lib/templates.php index d9c06b05f46dbc..b459ca758ccefc 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -12,6 +12,7 @@ function gutenberg_register_templates() { $post_post_type_object = get_post_type_object( 'post' ); $post_post_type_object->template = array( array( 'core/post-title' ), + array( 'core/post-content', array(), array( array( 'core/paragraph' ) ) ), ); $post_post_type_object->template_lock = 'insert'; } diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 1a721efaa62d61..c5ca6c7ed5ac0e 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -64,6 +64,7 @@ import * as classic from './classic'; // Top-level template blocks. import * as postTitle from './post-title'; +import * as postContent from './post-content'; /** * Function to register core blocks provided by the block editor. @@ -130,6 +131,7 @@ export const registerCoreBlocks = () => { // Register top-level template blocks. postTitle, + postContent, ].forEach( ( block ) => { if ( ! block ) { return; diff --git a/packages/block-library/src/post-content/block.json b/packages/block-library/src/post-content/block.json new file mode 100644 index 00000000000000..aae49120413cd0 --- /dev/null +++ b/packages/block-library/src/post-content/block.json @@ -0,0 +1,4 @@ +{ + "name": "core/post-content", + "category": "common" +} diff --git a/packages/block-library/src/post-content/edit.js b/packages/block-library/src/post-content/edit.js new file mode 100644 index 00000000000000..5ac0181f8e25ec --- /dev/null +++ b/packages/block-library/src/post-content/edit.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { InnerBlocks } from '@wordpress/block-editor'; + +export default function PostContentEdit() { + return ; +} diff --git a/packages/block-library/src/post-content/index.js b/packages/block-library/src/post-content/index.js new file mode 100644 index 00000000000000..9a32cb1332fec4 --- /dev/null +++ b/packages/block-library/src/post-content/index.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import edit from './edit'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + title: __( 'Post Content' ), + edit, +}; From e2f298b1f13e75810ec0977d9e7fa37b6889caf8 Mon Sep 17 00:00:00 2001 From: epiqueras Date: Fri, 5 Jul 2019 16:58:27 -0400 Subject: [PATCH 04/70] Templates: Implement persistence using a template CPT. --- lib/templates.php | 38 ++++++++++++++++++++++++---- packages/blocks/src/api/templates.js | 2 +- packages/editor/src/store/actions.js | 19 +++++++++++--- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/lib/templates.php b/lib/templates.php index b459ca758ccefc..5101c0a91d2966 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -9,11 +9,39 @@ * Registers Gutenberg core block editor templates. */ function gutenberg_register_templates() { - $post_post_type_object = get_post_type_object( 'post' ); - $post_post_type_object->template = array( - array( 'core/post-title' ), - array( 'core/post-content', array(), array( array( 'core/paragraph' ) ) ), + register_post_type( + 'wp_template', + array( + 'labels' => array( + 'name' => __( 'Templates', 'gutenberg' ), + ), + 'show_in_rest' => true, + ) ); - $post_post_type_object->template_lock = 'insert'; + + $template_query = new WP_Query( + array( + 'post_type' => 'wp_template', + 'name' => 'single-post', + ) + ); + + $template; + if ( ! $template_query->have_posts() ) { + $template_id = wp_insert_post( + array( + 'post_type' => 'wp_template', + 'post_name' => 'single-post', + 'post_content' => '

', + ) + ); + $template = get_post( $template_id ); + } else { + $template = $template_query->get_posts(); + $template = $template[0]; + } + + $post_type_object = get_post_type_object( 'post' ); + $post_type_object->template = $template; } add_action( 'init', 'gutenberg_register_templates' ); diff --git a/packages/blocks/src/api/templates.js b/packages/blocks/src/api/templates.js index d09be49453e702..8bde810549d0d7 100644 --- a/packages/blocks/src/api/templates.js +++ b/packages/blocks/src/api/templates.js @@ -54,7 +54,7 @@ export function synchronizeBlocksWithTemplate( blocks = [], template ) { return blocks; } - return map( template, ( [ name, attributes, innerBlocksTemplate ], index ) => { + return map( template, ( { name, attributes, innerBlocks: innerBlocksTemplate }, index ) => { const block = blocks[ index ]; if ( block && block.name === name ) { diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 8ea2c9a408de84..0079f38fe62fcc 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -170,9 +170,8 @@ export function* setupEditor( post, edits, template ) { let blocks = parse( content ); // Apply a template for new posts only, if exists. - const isNewPost = post.status === 'auto-draft'; - if ( isNewPost && template ) { - blocks = synchronizeBlocksWithTemplate( blocks, template ); + if ( template ) { + blocks = synchronizeBlocksWithTemplate( blocks, parse( template.post_content ) ); } yield resetPost( post ); @@ -474,11 +473,23 @@ export function* savePost( options = {} ) { 'getCurrentPost' ); - const editedPostContent = yield select( + let editedPostContent = yield select( STORE_KEY, 'getEditedPostContent' ); + const template = ( yield select( STORE_KEY, 'getEditorSettings' ) ).template; + if ( template ) { + yield apiFetch( { + path: `/wp/v2/${ template.post_type }/${ template.ID }`, + method: 'PUT', + data: { + content: editedPostContent, + }, + } ); + editedPostContent = ''; + } + let toSend = { ...edits, content: editedPostContent, From 301d5783b9676e58ea32024c9181f51b4d0d235d Mon Sep 17 00:00:00 2001 From: epiqueras Date: Mon, 8 Jul 2019 11:37:39 -0500 Subject: [PATCH 05/70] Templates: Integrate with demo content. --- lib/templates.php | 6 ++++++ packages/blocks/src/api/templates.js | 5 ++++- post-content.php | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/templates.php b/lib/templates.php index 5101c0a91d2966..3f9194f0b16937 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -41,6 +41,12 @@ function gutenberg_register_templates() { $template = $template[0]; } + if ( isset( $_GET['gutenberg-demo'] ) ) { + ob_start(); + include gutenberg_dir_path() . 'post-content.php'; + $template->post_content = ob_get_clean(); + } + $post_type_object = get_post_type_object( 'post' ); $post_type_object->template = $template; } diff --git a/packages/blocks/src/api/templates.js b/packages/blocks/src/api/templates.js index 8bde810549d0d7..dc378afdff4543 100644 --- a/packages/blocks/src/api/templates.js +++ b/packages/blocks/src/api/templates.js @@ -54,7 +54,10 @@ export function synchronizeBlocksWithTemplate( blocks = [], template ) { return blocks; } - return map( template, ( { name, attributes, innerBlocks: innerBlocksTemplate }, index ) => { + return map( template, ( templateBlock, index ) => { + const [ name, attributes, innerBlocksTemplate ] = Array.isArray( templateBlock ) ? + templateBlock : + [ templateBlock.name, templateBlock.attributes, templateBlock.innerBlocksTemplate ]; const block = blocks[ index ]; if ( block && block.name === name ) { diff --git a/post-content.php b/post-content.php index 032c4ad10be6ab..519d4a352efea0 100644 --- a/post-content.php +++ b/post-content.php @@ -6,6 +6,8 @@ */ ?> + +

@@ -184,3 +186,4 @@

👋

+ From 657f4e96118e13d4e08b2699424f3270eff189e9 Mon Sep 17 00:00:00 2001 From: epiqueras Date: Mon, 8 Jul 2019 15:05:14 -0500 Subject: [PATCH 06/70] Editor: Integrate post-title and post-content with custom sources. --- .../block-library/src/post-content/block.json | 5 +- .../block-library/src/post-title/block.json | 4 +- packages/blocks/src/api/templates.js | 17 +++++-- packages/editor/src/store/actions.js | 4 +- .../editor/src/store/block-sources/index.js | 3 +- .../editor/src/store/block-sources/post.js | 46 +++++++++++++++++++ 6 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 packages/editor/src/store/block-sources/post.js diff --git a/packages/block-library/src/post-content/block.json b/packages/block-library/src/post-content/block.json index aae49120413cd0..f74458792e5038 100644 --- a/packages/block-library/src/post-content/block.json +++ b/packages/block-library/src/post-content/block.json @@ -1,4 +1,7 @@ { "name": "core/post-content", - "category": "common" + "category": "common", + "supports": { + "multiple": false + } } diff --git a/packages/block-library/src/post-title/block.json b/packages/block-library/src/post-title/block.json index 62a4899b043914..e5b162478055f4 100644 --- a/packages/block-library/src/post-title/block.json +++ b/packages/block-library/src/post-title/block.json @@ -3,7 +3,9 @@ "category": "common", "attributes": { "title": { - "type": "string" + "type": "string", + "source": "post", + "attribute": "title" } } } diff --git a/packages/blocks/src/api/templates.js b/packages/blocks/src/api/templates.js index dc378afdff4543..7653d513266950 100644 --- a/packages/blocks/src/api/templates.js +++ b/packages/blocks/src/api/templates.js @@ -55,14 +55,21 @@ export function synchronizeBlocksWithTemplate( blocks = [], template ) { } return map( template, ( templateBlock, index ) => { - const [ name, attributes, innerBlocksTemplate ] = Array.isArray( templateBlock ) ? + const [ name, attributes, _innerBlocksTemplate ] = Array.isArray( templateBlock ) ? templateBlock : - [ templateBlock.name, templateBlock.attributes, templateBlock.innerBlocksTemplate ]; + [ templateBlock.name, templateBlock.attributes, templateBlock.innerBlocks ]; const block = blocks[ index ]; + let innerBlocksTemplate = _innerBlocksTemplate; - if ( block && block.name === name ) { - const innerBlocks = synchronizeBlocksWithTemplate( block.innerBlocks, innerBlocksTemplate ); - return { ...block, innerBlocks }; + if ( block ) { + if ( block.name === name ) { + const innerBlocks = synchronizeBlocksWithTemplate( block.innerBlocks, innerBlocksTemplate ); + return { ...block, innerBlocks }; + } + + if ( 'core/post-content' === name ) { + innerBlocksTemplate = blocks; + } } // To support old templates that were using the "children" format diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 0079f38fe62fcc..f9b3b64ea1fc11 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -12,6 +12,7 @@ import { dispatch, select, apiFetch } from '@wordpress/data-controls'; import { parse, synchronizeBlocksWithTemplate, + serialize, } from '@wordpress/blocks'; import isShallowEqual from '@wordpress/is-shallow-equal'; @@ -487,7 +488,8 @@ export function* savePost( options = {} ) { content: editedPostContent, }, } ); - editedPostContent = ''; + const postContentBlock = ( yield select( STORE_KEY, 'getBlocksForSerialization' ) ).find( ( block ) => block.name === 'core/post-content' ); + editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; } let toSend = { diff --git a/packages/editor/src/store/block-sources/index.js b/packages/editor/src/store/block-sources/index.js index 542d774c313ce9..060c482db30a5e 100644 --- a/packages/editor/src/store/block-sources/index.js +++ b/packages/editor/src/store/block-sources/index.js @@ -1,6 +1,7 @@ /** * Internal dependencies */ +import * as post from './post'; import * as meta from './meta'; -export { meta }; +export { post, meta }; diff --git a/packages/editor/src/store/block-sources/post.js b/packages/editor/src/store/block-sources/post.js new file mode 100644 index 00000000000000..8e0e933cd88bf2 --- /dev/null +++ b/packages/editor/src/store/block-sources/post.js @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import { get, set } from 'lodash'; + +/** + * WordPress dependencies + */ +import { select } from '@wordpress/data-controls'; + +/** + * Internal dependencies + */ +import { editPost } from '../actions'; +import { EDIT_MERGE_PROPERTIES } from '../constants'; + +export function* getDependencies() { + return { + post: yield select( 'core/editor', 'getCurrentPost' ), + content: yield select( 'core/editor', 'getEditedPostContent' ), + edits: yield select( 'core/editor', 'getPostEdits' ), + }; +} + +export function apply( { attribute }, { post, content, edits } ) { + if ( 'content' === attribute ) { + return content; + } + + if ( undefined === get( edits, attribute ) ) { + return get( post, attribute ); + } + + if ( EDIT_MERGE_PROPERTIES.has( attribute ) ) { + return { + ...get( post, attribute ), + ...get( edits, attribute ), + }; + } + + return get( edits, attribute ); +} + +export function* update( { attribute }, value ) { + yield editPost( set( {}, attribute, value ) ); +} From b7dc9a5ee28d5a22032e147c3b16621dfc9a1896 Mon Sep 17 00:00:00 2001 From: epiqueras Date: Thu, 11 Jul 2019 11:27:02 -0500 Subject: [PATCH 07/70] Editor: Keep compatibility with current template format. --- packages/editor/src/store/actions.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index f9b3b64ea1fc11..768a9d9ac154cf 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -172,7 +172,10 @@ export function* setupEditor( post, edits, template ) { // Apply a template for new posts only, if exists. if ( template ) { - blocks = synchronizeBlocksWithTemplate( blocks, parse( template.post_content ) ); + blocks = synchronizeBlocksWithTemplate( + blocks, + template.post_content ? parse( template.post_content ) : template + ); } yield resetPost( post ); @@ -480,7 +483,7 @@ export function* savePost( options = {} ) { ); const template = ( yield select( STORE_KEY, 'getEditorSettings' ) ).template; - if ( template ) { + if ( template && template.post_content ) { yield apiFetch( { path: `/wp/v2/${ template.post_type }/${ template.ID }`, method: 'PUT', From f2abe6f2c25cbfff56eefd1a6461a81cc4522f7f Mon Sep 17 00:00:00 2001 From: epiqueras Date: Fri, 12 Jul 2019 11:52:26 -0500 Subject: [PATCH 08/70] Editor: If a template is supplied, apply it if it's editable or if it's static and the post is new. --- packages/editor/src/store/actions.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 768a9d9ac154cf..418d29f31bcf16 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -170,8 +170,9 @@ export function* setupEditor( post, edits, template ) { let blocks = parse( content ); - // Apply a template for new posts only, if exists. - if ( template ) { + // If a template is supplied, apply it if it's editable + // or if it's static and the post is new. + if ( template && ( template.post_content || post.status === 'auto-draft' ) ) { blocks = synchronizeBlocksWithTemplate( blocks, template.post_content ? parse( template.post_content ) : template From 07392e02cd16f60e96dfbc0b2433d22929d36a6f Mon Sep 17 00:00:00 2001 From: epiqueras Date: Fri, 12 Jul 2019 16:05:59 -0500 Subject: [PATCH 09/70] Post Title Block: Sync value with post slug. --- packages/block-library/src/post-title/block.json | 5 +++++ packages/block-library/src/post-title/edit.js | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/block-library/src/post-title/block.json b/packages/block-library/src/post-title/block.json index e5b162478055f4..feaa838e25e4f8 100644 --- a/packages/block-library/src/post-title/block.json +++ b/packages/block-library/src/post-title/block.json @@ -6,6 +6,11 @@ "type": "string", "source": "post", "attribute": "title" + }, + "slug": { + "type": "string", + "source": "post", + "attribute": "slug" } } } diff --git a/packages/block-library/src/post-title/edit.js b/packages/block-library/src/post-title/edit.js index 207c610162bd74..bc7d5da6236003 100644 --- a/packages/block-library/src/post-title/edit.js +++ b/packages/block-library/src/post-title/edit.js @@ -2,10 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; - -/** - * WordPress dependencies - */ +import { cleanForSlug } from '@wordpress/editor'; import { RichText } from '@wordpress/block-editor'; export default function PostTitleEdit( { @@ -15,7 +12,7 @@ export default function PostTitleEdit( { return ( setAttributes( { title: value } ) } + onChange={ ( value ) => setAttributes( { title: value, slug: cleanForSlug( value ) } ) } tagName="h1" placeholder={ __( 'Title' ) } formattingControls={ [] } From b09a54d8cf95c0bd91e24ad430b9373589ee4c96 Mon Sep 17 00:00:00 2001 From: epiqueras Date: Tue, 16 Jul 2019 17:03:03 +0200 Subject: [PATCH 10/70] Editor: Make post templates a separate feature from block templates and add a toggle between template and post mode. --- lib/templates.php | 23 ++++++++- .../components/header/header-toolbar/index.js | 2 + .../edit-post/src/components/layout/index.js | 29 +++++++---- .../src/components/visual-editor/index.js | 8 ++- packages/editor/src/components/index.js | 1 + .../editor/src/components/provider/index.js | 7 ++- .../toggle-edit-template-post/index.js | 51 +++++++++++++++++++ packages/editor/src/store/actions.js | 43 +++++++++++----- post-content.php | 3 -- 9 files changed, 136 insertions(+), 31 deletions(-) create mode 100644 packages/editor/src/components/toggle-edit-template-post/index.js diff --git a/lib/templates.php b/lib/templates.php index 3f9194f0b16937..56b0b385a05d6c 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -43,11 +43,30 @@ function gutenberg_register_templates() { if ( isset( $_GET['gutenberg-demo'] ) ) { ob_start(); + echo ''; include gutenberg_dir_path() . 'post-content.php'; + echo ''; $template->post_content = ob_get_clean(); } - $post_type_object = get_post_type_object( 'post' ); - $post_type_object->template = $template; + $post_type_object = get_post_type_object( 'post' ); + $post_type_object->template_post = $template; } add_action( 'init', 'gutenberg_register_templates' ); + +/** + * Filters the block editor settings object to add the post's template post. + * + * @param array $editor_settings The block editor settings object. + * @param \WP_Post $post The post object. + * + * @return array The maybe modified block editor settings object. + */ +function gutenberg_filter_block_editor_settings( $editor_settings, $post ) { + $post_type_object = get_post_type_object( get_post_type( $post ) ); + if ( ! empty( $post_type_object->template_post ) ) { + $editor_settings['templatePost'] = $post_type_object->template_post; + } + return $editor_settings; +} +add_filter( 'block_editor_settings', 'gutenberg_filter_block_editor_settings', 10, 2 ); diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index 8ff5e2209594df..da0309bce478a1 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -16,6 +16,7 @@ import { TableOfContents, EditorHistoryRedo, EditorHistoryUndo, + ToggleEditTemplatePost, } from '@wordpress/editor'; function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter, isTextModeEnabled } ) { @@ -45,6 +46,7 @@ function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter, isText
) } + ); } diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index ee2b55bbed5d24..1cfc95b6fd9b1b 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -56,6 +56,7 @@ function Layout( { isSaving, isMobileViewport, isRichEditingEnabled, + isEditingTemplatePost, } ) { const sidebarIsOpened = editorSidebarOpened || pluginSidebarOpened || publishSidebarOpened; @@ -91,7 +92,9 @@ function Layout( { { ( mode === 'text' || ! isRichEditingEnabled ) && } - { isRichEditingEnabled && mode === 'visual' && } + { isRichEditingEnabled && mode === 'visual' && ( + + ) }
@@ -135,16 +138,20 @@ function Layout( { } export default compose( - withSelect( ( select ) => ( { - mode: select( 'core/edit-post' ).getEditorMode(), - editorSidebarOpened: select( 'core/edit-post' ).isEditorSidebarOpened(), - pluginSidebarOpened: select( 'core/edit-post' ).isPluginSidebarOpened(), - publishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(), - hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ), - hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), - isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), - isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled, - } ) ), + withSelect( ( select ) => { + const editorSettings = select( 'core/editor' ).getEditorSettings(); + return { + mode: select( 'core/edit-post' ).getEditorMode(), + editorSidebarOpened: select( 'core/edit-post' ).isEditorSidebarOpened(), + pluginSidebarOpened: select( 'core/edit-post' ).isPluginSidebarOpened(), + publishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(), + hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ), + hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), + isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), + isRichEditingEnabled: editorSettings.richEditingEnabled, + isEditingTemplatePost: editorSettings.editTemplatePost, + }; + } ), withDispatch( ( dispatch ) => { const { closePublishSidebar, togglePublishSidebar } = dispatch( 'core/edit-post' ); return { diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 748def29170c68..00f9c86a340ce2 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -1,7 +1,10 @@ /** * WordPress dependencies */ -import { VisualEditorGlobalKeyboardShortcuts } from '@wordpress/editor'; +import { + PostTitle, + VisualEditorGlobalKeyboardShortcuts, +} from '@wordpress/editor'; import { WritingFlow, ObserveTyping, @@ -19,7 +22,7 @@ import { import BlockInspectorButton from './block-inspector-button'; import PluginBlockSettingsMenuGroup from '../block-settings-menu/plugin-block-settings-menu-group'; -function VisualEditor() { +function VisualEditor( { withPostTitle } ) { return ( @@ -27,6 +30,7 @@ function VisualEditor() { + { withPostTitle && } diff --git a/packages/editor/src/components/index.js b/packages/editor/src/components/index.js index f8999c7868042f..5b17269569c5ec 100644 --- a/packages/editor/src/components/index.js +++ b/packages/editor/src/components/index.js @@ -12,6 +12,7 @@ export { export { default as TextEditorGlobalKeyboardShortcuts } from './global-keyboard-shortcuts/text-editor-shortcuts'; export { default as EditorHistoryRedo } from './editor-history/redo'; export { default as EditorHistoryUndo } from './editor-history/undo'; +export { default as ToggleEditTemplatePost } from './toggle-edit-template-post'; export { default as EditorNotices } from './editor-notices'; export { default as ErrorBoundary } from './error-boundary'; export { default as PageAttributesCheck } from './page-attributes/check'; diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index cfa67ddbb615d7..6b96858fb4c5db 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -54,7 +54,12 @@ class EditorProvider extends Component { } props.updatePostLock( props.settings.postLock ); - props.setupEditor( props.post, props.initialEdits, props.settings.template ); + props.setupEditor( + props.post, + props.initialEdits, + props.settings.template, + props.settings.editTemplatePost && props.settings.templatePost + ); if ( props.settings.autosave ) { props.createWarningNotice( diff --git a/packages/editor/src/components/toggle-edit-template-post/index.js b/packages/editor/src/components/toggle-edit-template-post/index.js new file mode 100644 index 00000000000000..c8831fc0a7d42e --- /dev/null +++ b/packages/editor/src/components/toggle-edit-template-post/index.js @@ -0,0 +1,51 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { useCallback } from '@wordpress/element'; +import { IconButton } from '@wordpress/components'; + +export default function ToggleEditTemplatePost() { + const { post, blocks, settings } = useSelect( ( select ) => { + const { + getCurrentPost, + getBlocksForSerialization, + getEditorSettings, + } = select( 'core/editor' ); + return { + post: getCurrentPost(), + blocks: getBlocksForSerialization(), + settings: getEditorSettings(), + }; + }, [] ); + const { updateEditorSettings, setupEditor } = useDispatch( 'core/editor' ); + return ( + { + const newEditTemplatePost = ! settings.editTemplatePost; + updateEditorSettings( { + editTemplatePost: newEditTemplatePost, + } ); + + let newBlocks = blocks; + if ( ! newEditTemplatePost ) { // Leaving template mode. + const postContentBlock = blocks.find( + ( block ) => block.name === 'core/post-content' + ); + newBlocks = postContentBlock ? postContentBlock.innerBlocks : []; + } + setupEditor( + post, + { + blocks: newBlocks, + }, + settings.template, + newEditTemplatePost && settings.templatePost + ); + }, [ post, blocks, settings ] ) } + /> + ); +} diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 418d29f31bcf16..1f3e926ed9071a 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -153,11 +153,12 @@ function* resetLastBlockSourceDependencies( sourcesToUpdate = Object.values( sou * Returns an action generator used in signalling that editor has initialized with * the specified post object and editor settings. * - * @param {Object} post Post object. - * @param {Object} edits Initial edited attributes object. - * @param {Array?} template Block Template. + * @param {Object} post Post object. + * @param {Object} edits Initial edited attributes object. + * @param {Array?} template Block Template. + * @param {Object?} templatePost Post's template post object. */ -export function* setupEditor( post, edits, template ) { +export function* setupEditor( post, edits, template, templatePost ) { // In order to ensure maximum of a single parse during setup, edits are // included as part of editor setup action. Assume edited content as // canonical if provided, falling back to post. @@ -168,17 +169,35 @@ export function* setupEditor( post, edits, template ) { content = post.content.raw; } - let blocks = parse( content ); + let blocks = ( edits && edits.blocks ) || parse( content ); - // If a template is supplied, apply it if it's editable - // or if it's static and the post is new. - if ( template && ( template.post_content || post.status === 'auto-draft' ) ) { + // Apply post template. + if ( templatePost ) { blocks = synchronizeBlocksWithTemplate( blocks, - template.post_content ? parse( template.post_content ) : template + parse( templatePost.post_content ) ); } + // Apply block (post content) template. + if ( template && 'auto-draft' === post.status ) { + if ( templatePost ) { + // Post content is nested inside a post content block. + const postContentBlock = blocks.find( + ( block ) => block.name === 'core/post-content' + ); + if ( postContentBlock ) { + postContentBlock.innerBlocks = synchronizeBlocksWithTemplate( + postContentBlock.innerBlocks, + template + ); + } + } else { + // Post content is at the top level. + blocks = synchronizeBlocksWithTemplate( blocks, template ); + } + } + yield resetPost( post ); yield* resetLastBlockSourceDependencies(); yield { @@ -483,10 +502,10 @@ export function* savePost( options = {} ) { 'getEditedPostContent' ); - const template = ( yield select( STORE_KEY, 'getEditorSettings' ) ).template; - if ( template && template.post_content ) { + const { editTemplatePost, templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); + if ( editTemplatePost && templatePost ) { yield apiFetch( { - path: `/wp/v2/${ template.post_type }/${ template.ID }`, + path: `/wp/v2/${ templatePost.post_type }/${ templatePost.ID }`, method: 'PUT', data: { content: editedPostContent, diff --git a/post-content.php b/post-content.php index 519d4a352efea0..032c4ad10be6ab 100644 --- a/post-content.php +++ b/post-content.php @@ -6,8 +6,6 @@ */ ?> - -

@@ -186,4 +184,3 @@

👋

- From 6136f29f2fc4ef74b6b8511de6a2e6b51ce5a603 Mon Sep 17 00:00:00 2001 From: epiqueras Date: Thu, 18 Jul 2019 17:19:48 +0200 Subject: [PATCH 11/70] Templates: Add render callbacks for post title and content blocks and override the single post template with its editable counterpart. --- lib/blocks.php | 2 ++ lib/templates.php | 26 ++++++++++++++++ .../block-library/src/post-content/index.php | 30 +++++++++++++++++++ .../block-library/src/post-title/index.php | 30 +++++++++++++++++++ packages/editor/src/store/actions.js | 1 + 5 files changed, 89 insertions(+) create mode 100644 packages/block-library/src/post-content/index.php create mode 100644 packages/block-library/src/post-title/index.php diff --git a/lib/blocks.php b/lib/blocks.php index fc3adb8a09efa3..d4e382cd206dbd 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -28,6 +28,8 @@ function gutenberg_reregister_core_block_types() { 'shortcode.php' => 'core/shortcode', 'search.php' => 'core/search', 'tag-cloud.php' => 'core/tag-cloud', + 'post-title.php' => 'core/post-title', + 'post-content.php' => 'core/post-content', ); $registry = WP_Block_Type_Registry::get_instance(); diff --git a/lib/templates.php b/lib/templates.php index 56b0b385a05d6c..cfbef42f489c8e 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -70,3 +70,29 @@ function gutenberg_filter_block_editor_settings( $editor_settings, $post ) { return $editor_settings; } add_filter( 'block_editor_settings', 'gutenberg_filter_block_editor_settings', 10, 2 ); + +/** + * Filters template inclusion in pages to hijack the `single.php` template + * and load the Gutenberg editable counterpart instead. + * + * @param string $template The included template file name. + * + * @return string The passed in file name unless the process is hijacked. + */ +function gutenberg_filter_template_include( $template ) { + if ( ! preg_match( '/single\.php$/', $template ) ) { + return $template; + } + + $template_query = new WP_Query( + array( + 'post_type' => 'wp_template', + 'name' => 'single-post', + ) + ); + $template = $template_query->get_posts(); + $template = $template[0]; + echo wp_head() . apply_filters( 'the_content', $template->post_content ) . wp_footer(); + exit; +} +add_filter( 'template_include', 'gutenberg_filter_template_include' ); diff --git a/packages/block-library/src/post-content/index.php b/packages/block-library/src/post-content/index.php new file mode 100644 index 00000000000000..d9410a7b4e45e2 --- /dev/null +++ b/packages/block-library/src/post-content/index.php @@ -0,0 +1,30 @@ +', ']]>', get_the_content() ) ); +} + +/** + * Registers the `core/post-content` block on the server. + */ +function register_block_core_post_content() { + register_block_type( + 'core/post-content', + array( + 'render_callback' => 'render_block_core_post_content', + ) + ); +} +add_action( 'init', 'register_block_core_post_content' ); diff --git a/packages/block-library/src/post-title/index.php b/packages/block-library/src/post-title/index.php new file mode 100644 index 00000000000000..498d79408e2963 --- /dev/null +++ b/packages/block-library/src/post-title/index.php @@ -0,0 +1,30 @@ +', '', false ); +} + +/** + * Registers the `core/post-title` block on the server. + */ +function register_block_core_post_title() { + register_block_type( + 'core/post-title', + array( + 'render_callback' => 'render_block_core_post_title', + ) + ); +} +add_action( 'init', 'register_block_core_post_title' ); diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 1f3e926ed9071a..d4bd4cf2236329 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -509,6 +509,7 @@ export function* savePost( options = {} ) { method: 'PUT', data: { content: editedPostContent, + id: templatePost.ID, }, } ); const postContentBlock = ( yield select( STORE_KEY, 'getBlocksForSerialization' ) ).find( ( block ) => block.name === 'core/post-content' ); From 26e67313b4b3ddfc1d4a96297cf6454d41f6428d Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 19 Jul 2019 12:49:49 +0200 Subject: [PATCH 12/70] Adding a post date block --- lib/blocks.php | 1 + packages/block-library/src/index.js | 2 + .../block-library/src/post-date/block.json | 15 +++++++ packages/block-library/src/post-date/edit.js | 39 ++++++++++++++++++ packages/block-library/src/post-date/index.js | 19 +++++++++ .../block-library/src/post-date/index.php | 40 +++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 packages/block-library/src/post-date/block.json create mode 100644 packages/block-library/src/post-date/edit.js create mode 100644 packages/block-library/src/post-date/index.js create mode 100644 packages/block-library/src/post-date/index.php diff --git a/lib/blocks.php b/lib/blocks.php index d4e382cd206dbd..9f4ef60d3861c6 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -30,6 +30,7 @@ function gutenberg_reregister_core_block_types() { 'tag-cloud.php' => 'core/tag-cloud', 'post-title.php' => 'core/post-title', 'post-content.php' => 'core/post-content', + 'post-date.php' => 'core/post-date', ); $registry = WP_Block_Type_Registry::get_instance(); diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index c5ca6c7ed5ac0e..ab9c11db95ad21 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -65,6 +65,7 @@ import * as classic from './classic'; // Top-level template blocks. import * as postTitle from './post-title'; import * as postContent from './post-content'; +import * as postDate from './post-date'; /** * Function to register core blocks provided by the block editor. @@ -132,6 +133,7 @@ export const registerCoreBlocks = () => { // Register top-level template blocks. postTitle, postContent, + postDate, ].forEach( ( block ) => { if ( ! block ) { return; diff --git a/packages/block-library/src/post-date/block.json b/packages/block-library/src/post-date/block.json new file mode 100644 index 00000000000000..e5cc72a25b83f4 --- /dev/null +++ b/packages/block-library/src/post-date/block.json @@ -0,0 +1,15 @@ +{ + "name": "core/post-date", + "category": "common", + "attributes": { + "date": { + "type": "string", + "source": "post", + "attribute": "date" + }, + "format": { + "type": "string", + "default": "date" + } + } +} diff --git a/packages/block-library/src/post-date/edit.js b/packages/block-library/src/post-date/edit.js new file mode 100644 index 00000000000000..7cbd515bce6378 --- /dev/null +++ b/packages/block-library/src/post-date/edit.js @@ -0,0 +1,39 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { dateI18n, format as formatDate } from '@wordpress/date'; +import { InspectorControls } from '@wordpress/block-editor'; +import { PanelBody, SelectControl } from '@wordpress/components'; + +const formats = { + date: 'd/m/Y', + datetime: 'd/m/Y g:i', +}; + +export default function PostDateEdit( { + attributes: { date, format }, + setAttributes, +} ) { + return <> + + + + setAttributes( { format: value } ) } + /> + + + + ; +} diff --git a/packages/block-library/src/post-date/index.js b/packages/block-library/src/post-date/index.js new file mode 100644 index 00000000000000..08d7760c45b7da --- /dev/null +++ b/packages/block-library/src/post-date/index.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import edit from './edit'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + title: __( 'Post Date' ), + edit, +}; diff --git a/packages/block-library/src/post-date/index.php b/packages/block-library/src/post-date/index.php new file mode 100644 index 00000000000000..d293bc962938bd --- /dev/null +++ b/packages/block-library/src/post-date/index.php @@ -0,0 +1,40 @@ + 'd/m/Y', + 'datetime' => 'd/m/Y g:i', + ); + rewind_posts(); + the_post(); + return ''; +} + +/** + * Registers the `core/post-date` block on the server. + */ +function register_block_core_post_date() { + register_block_type( + 'core/post-date', + array( + 'attributes' => array( + 'format' => array( + 'type' => 'string', + 'default' => 'date', + ), + ), + 'render_callback' => 'render_block_core_post_date', + ), + ); +} +add_action( 'init', 'register_block_core_post_date' ); From efde0811abed2a6402e0e3242ee93b04399df076 Mon Sep 17 00:00:00 2001 From: epiqueras Date: Fri, 19 Jul 2019 12:51:47 +0200 Subject: [PATCH 13/70] Editor: Fix template post setup. --- packages/blocks/src/api/templates.js | 18 +++---------- packages/editor/src/store/actions.js | 38 +++++++++++----------------- 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/packages/blocks/src/api/templates.js b/packages/blocks/src/api/templates.js index 7653d513266950..d09be49453e702 100644 --- a/packages/blocks/src/api/templates.js +++ b/packages/blocks/src/api/templates.js @@ -54,22 +54,12 @@ export function synchronizeBlocksWithTemplate( blocks = [], template ) { return blocks; } - return map( template, ( templateBlock, index ) => { - const [ name, attributes, _innerBlocksTemplate ] = Array.isArray( templateBlock ) ? - templateBlock : - [ templateBlock.name, templateBlock.attributes, templateBlock.innerBlocks ]; + return map( template, ( [ name, attributes, innerBlocksTemplate ], index ) => { const block = blocks[ index ]; - let innerBlocksTemplate = _innerBlocksTemplate; - if ( block ) { - if ( block.name === name ) { - const innerBlocks = synchronizeBlocksWithTemplate( block.innerBlocks, innerBlocksTemplate ); - return { ...block, innerBlocks }; - } - - if ( 'core/post-content' === name ) { - innerBlocksTemplate = blocks; - } + if ( block && block.name === name ) { + const innerBlocks = synchronizeBlocksWithTemplate( block.innerBlocks, innerBlocksTemplate ); + return { ...block, innerBlocks }; } // To support old templates that were using the "children" format diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index d4bd4cf2236329..5ecee444b652bb 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -170,32 +170,24 @@ export function* setupEditor( post, edits, template, templatePost ) { } let blocks = ( edits && edits.blocks ) || parse( content ); + const isNewPost = 'auto-draft' === post.status; - // Apply post template. - if ( templatePost ) { - blocks = synchronizeBlocksWithTemplate( - blocks, - parse( templatePost.post_content ) - ); - } + if ( templatePost ) { // Apply template post. + const postContentInnerBlocks = blocks; + blocks = parse( templatePost.post_content ); - // Apply block (post content) template. - if ( template && 'auto-draft' === post.status ) { - if ( templatePost ) { - // Post content is nested inside a post content block. - const postContentBlock = blocks.find( - ( block ) => block.name === 'core/post-content' - ); - if ( postContentBlock ) { - postContentBlock.innerBlocks = synchronizeBlocksWithTemplate( - postContentBlock.innerBlocks, - template - ); - } - } else { - // Post content is at the top level. - blocks = synchronizeBlocksWithTemplate( blocks, template ); + // Post content is nested inside a post content block. + const postContentBlock = blocks.find( + ( block ) => block.name === 'core/post-content' + ); + if ( postContentBlock ) { + postContentBlock.innerBlocks = isNewPost ? // Apply block (post content) template. + synchronizeBlocksWithTemplate( postContentInnerBlocks, template ) : + postContentInnerBlocks; } + } else if ( isNewPost ) { // Apply block (post content) template. + // Post content is at the top level. + blocks = synchronizeBlocksWithTemplate( blocks, template ); } yield resetPost( post ); From ce46501c67e7f2fc3bbc946a3f8094f199c0486a Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 19 Jul 2019 13:26:30 +0200 Subject: [PATCH 14/70] Add: Post content does not show theme blocks (#16676) --- lib/blocks.php | 21 +++++++++++++++++++ .../block-library/src/post-content/block.json | 2 +- .../block-library/src/post-content/edit.js | 13 +++++++++++- .../block-library/src/post-title/block.json | 2 +- packages/blocks/src/store/reducer.js | 1 + 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index 9f4ef60d3861c6..ce4a0f0f82bed3 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -48,3 +48,24 @@ function gutenberg_reregister_core_block_types() { } } add_action( 'init', 'gutenberg_reregister_core_block_types' ); + +/** + * Adds new block categories needed by the Gutenberg plugin. + * + * @param array $categories Array of block categories. + * + * @return array Array of block categories plus the new categories added. + */ +function gutenberg_block_categories( $categories ) { + return array_merge( + $categories, + array( + array( + 'slug' => 'theme', + 'title' => __( 'Theme Blocks' ), + 'icon' => null, + ), + ) + ); +} +add_filter( 'block_categories', 'gutenberg_block_categories' ); diff --git a/packages/block-library/src/post-content/block.json b/packages/block-library/src/post-content/block.json index f74458792e5038..dfa216457176e9 100644 --- a/packages/block-library/src/post-content/block.json +++ b/packages/block-library/src/post-content/block.json @@ -1,6 +1,6 @@ { "name": "core/post-content", - "category": "common", + "category": "theme", "supports": { "multiple": false } diff --git a/packages/block-library/src/post-content/edit.js b/packages/block-library/src/post-content/edit.js index 5ac0181f8e25ec..9b06ff6b155839 100644 --- a/packages/block-library/src/post-content/edit.js +++ b/packages/block-library/src/post-content/edit.js @@ -2,7 +2,18 @@ * WordPress dependencies */ import { InnerBlocks } from '@wordpress/block-editor'; +import { useSelect } from '@wordpress/data'; export default function PostContentEdit() { - return ; + const allowedBlocks = useSelect( ( select ) => { + return select( 'core/blocks' ).getBlockTypes().filter( + ( { category } ) => category !== 'theme' + ).map( ( { name } ) => name ); + } ); + return ( + + ); } diff --git a/packages/block-library/src/post-title/block.json b/packages/block-library/src/post-title/block.json index feaa838e25e4f8..c21df91929f729 100644 --- a/packages/block-library/src/post-title/block.json +++ b/packages/block-library/src/post-title/block.json @@ -1,6 +1,6 @@ { "name": "core/post-title", - "category": "common", + "category": "theme", "attributes": { "title": { "type": "string", diff --git a/packages/blocks/src/store/reducer.js b/packages/blocks/src/store/reducer.js index 4e3131ca7b62e0..1f489f70a4e513 100644 --- a/packages/blocks/src/store/reducer.js +++ b/packages/blocks/src/store/reducer.js @@ -28,6 +28,7 @@ export const DEFAULT_CATEGORIES = [ { slug: 'layout', title: __( 'Layout Elements' ) }, { slug: 'widgets', title: __( 'Widgets' ) }, { slug: 'embed', title: __( 'Embeds' ) }, + { slug: 'theme', title: __( 'Theme Blocks' ) }, { slug: 'reusable', title: __( 'Reusable Blocks' ) }, ]; From f15e62145f9549fd36fd69168cd4e59fd55e4b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= Date: Fri, 19 Jul 2019 13:47:03 +0200 Subject: [PATCH 15/70] Fix date php code. --- packages/block-library/src/post-date/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/post-date/index.php b/packages/block-library/src/post-date/index.php index d293bc962938bd..31fd03c393dd32 100644 --- a/packages/block-library/src/post-date/index.php +++ b/packages/block-library/src/post-date/index.php @@ -34,7 +34,7 @@ function register_block_core_post_date() { ), ), 'render_callback' => 'render_block_core_post_date', - ), + ) ); } add_action( 'init', 'register_block_core_post_date' ); From 49731881e5f9a992aea15921714925dec7ea1b8e Mon Sep 17 00:00:00 2001 From: Jorge Date: Fri, 19 Jul 2019 13:52:41 +0200 Subject: [PATCH 16/70] Enable navigation menu in the inserter. Make menu block part of the theme block category. --- packages/block-library/src/navigation-menu/block.json | 2 +- packages/block-library/src/navigation-menu/index.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/block-library/src/navigation-menu/block.json b/packages/block-library/src/navigation-menu/block.json index ee204cfeb890d4..45732afddd8ed2 100644 --- a/packages/block-library/src/navigation-menu/block.json +++ b/packages/block-library/src/navigation-menu/block.json @@ -1,6 +1,6 @@ { "name": "core/navigation-menu", - "category": "layout", + "category": "theme", "attributes": { "automaticallyAdd": { "type": "boolean", diff --git a/packages/block-library/src/navigation-menu/index.js b/packages/block-library/src/navigation-menu/index.js index 8ca8e0c3fe560d..d59e5eee2b7cb5 100644 --- a/packages/block-library/src/navigation-menu/index.js +++ b/packages/block-library/src/navigation-menu/index.js @@ -26,7 +26,6 @@ export const settings = { align: [ 'wide', 'full' ], anchor: true, html: false, - inserter: false, }, edit, From 6a711613b5b097dabe965ed64793387718edec4d Mon Sep 17 00:00:00 2001 From: Jorge Date: Fri, 19 Jul 2019 15:49:45 +0200 Subject: [PATCH 17/70] Update initial template to a more advanced design that includes navigation and post date --- lib/templates.php | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/templates.php b/lib/templates.php index cfbef42f489c8e..5d09adef68503a 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -32,7 +32,49 @@ function gutenberg_register_templates() { array( 'post_type' => 'wp_template', 'post_name' => 'single-post', - 'post_content' => '

', + 'post_content' => ' + + + + +
+
+ + + +
+ + + +
+
+

This is a Sidebar.

+ + + +

With some block widgets.

+ + + + + +

You are now reading post:

+ + + + + + +
+
+
+ ', ) ); $template = get_post( $template_id ); From 3c00dcc2dda724675ab5c8282d230c5a06762b50 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Fri, 19 Jul 2019 13:41:05 +0200 Subject: [PATCH 18/70] Add Site Title block and support for site options attribute source --- .../developers/data/data-core.md | 65 +++++++++++++++++++ lib/blocks.php | 1 + packages/block-library/src/index.js | 2 + .../block-library/src/site-title/block.json | 11 ++++ packages/block-library/src/site-title/edit.js | 22 +++++++ .../block-library/src/site-title/index.js | 20 ++++++ .../block-library/src/site-title/index.php | 31 +++++++++ .../block-library/src/site-title/style.scss | 3 + packages/block-library/src/style.scss | 1 + packages/core-data/README.md | 65 +++++++++++++++++++ packages/core-data/src/actions.js | 45 +++++++++++++ packages/core-data/src/reducer.js | 37 +++++++++++ packages/core-data/src/resolvers.js | 6 ++ packages/core-data/src/selectors.js | 25 +++++++ packages/core-data/src/test/reducer.js | 53 ++++++++++++++- packages/editor/src/store/actions.js | 6 ++ .../editor/src/store/block-sources/index.js | 3 +- .../editor/src/store/block-sources/option.js | 47 ++++++++++++++ 18 files changed, 441 insertions(+), 2 deletions(-) create mode 100644 packages/block-library/src/site-title/block.json create mode 100644 packages/block-library/src/site-title/edit.js create mode 100644 packages/block-library/src/site-title/index.js create mode 100644 packages/block-library/src/site-title/index.php create mode 100644 packages/block-library/src/site-title/style.scss create mode 100644 packages/editor/src/store/block-sources/option.js diff --git a/docs/designers-developers/developers/data/data-core.md b/docs/designers-developers/developers/data/data-core.md index 3f823475fcca1c..3fbc2484d49011 100644 --- a/docs/designers-developers/developers/data/data-core.md +++ b/docs/designers-developers/developers/data/data-core.md @@ -153,6 +153,22 @@ _Returns_ - `Array`: Records. +# **getSiteOptions** + +Return site options as they exist locally. + +_Related_ + +- isSiteOptionsDirty + +_Parameters_ + +- _state_ `Object`: Data state. + +_Returns_ + +- `Object`: Site options. + # **getThemeSupports** Return theme supports data in the index. @@ -242,6 +258,19 @@ _Returns_ - `boolean`: Whether a request is in progress for an embed preview. +# **isSiteOptionsDirty** + +Return whether the client has local changes to site options which haven't +yet been saved to the server. + +_Parameters_ + +- _state_ `Object`: Data state. + +_Returns_ + +- `boolean`: Whether or not the local site options state is dirty. + @@ -317,6 +346,18 @@ _Returns_ - `Object`: Action object. +# **receiveSiteOptions** + +Returns an action object used in signalling that site options have been received. + +_Parameters_ + +- _siteOptions_ `Object`: Site options. + +_Returns_ + +- `Object`: Action object. + # **receiveThemeSupports** Returns an action object used in signalling that the index has been received. @@ -382,4 +423,28 @@ _Returns_ - `Object`: Updated record. +# **saveSiteOptions** + +Action triggered to save site options. + +_Parameters_ + +- _siteOptions_ `Object`: Site options. + +_Returns_ + +- `Object`: Updated site options. + +# **updateSiteOptions** + +Returns an action object used in signalling that site options have been locally updated. + +_Parameters_ + +- _siteOptions_ `Object`: Site options. + +_Returns_ + +- `Object`: Action object. + diff --git a/lib/blocks.php b/lib/blocks.php index ce4a0f0f82bed3..682a2641c85251 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -27,6 +27,7 @@ function gutenberg_reregister_core_block_types() { 'rss.php' => 'core/rss', 'shortcode.php' => 'core/shortcode', 'search.php' => 'core/search', + 'site-title.php' => 'core/site-title', 'tag-cloud.php' => 'core/tag-cloud', 'post-title.php' => 'core/post-title', 'post-content.php' => 'core/post-content', diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index ab9c11db95ad21..1566cb3eb6342e 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -50,6 +50,7 @@ import * as rss from './rss'; import * as search from './search'; import * as group from './group'; import * as separator from './separator'; +import * as siteTitle from './site-title'; import * as shortcode from './shortcode'; import * as spacer from './spacer'; import * as subhead from './subhead'; @@ -120,6 +121,7 @@ export const registerCoreBlocks = () => { rss, search, separator, + siteTitle, reusableBlock, spacer, subhead, diff --git a/packages/block-library/src/site-title/block.json b/packages/block-library/src/site-title/block.json new file mode 100644 index 00000000000000..9e590dbdad5409 --- /dev/null +++ b/packages/block-library/src/site-title/block.json @@ -0,0 +1,11 @@ +{ + "name": "core/site-title", + "category": "layout", + "attributes": { + "title": { + "type": "string", + "source": "option", + "option": "title" + } + } +} diff --git a/packages/block-library/src/site-title/edit.js b/packages/block-library/src/site-title/edit.js new file mode 100644 index 00000000000000..b918b2386a8b87 --- /dev/null +++ b/packages/block-library/src/site-title/edit.js @@ -0,0 +1,22 @@ +/** + * WordPress dependencies + */ +import { PlainText } from '@wordpress/editor'; +import { __ } from '@wordpress/i18n'; + +function SiteTitleEdit( { attributes, setAttributes } ) { + const { title } = attributes; + + return ( +
+ setAttributes( { title: newTitle } ) } + placeholder={ __( 'Site Title' ) } + aria-label={ __( 'Site Title' ) } + /> + </div> + ); +} + +export default SiteTitleEdit; diff --git a/packages/block-library/src/site-title/index.js b/packages/block-library/src/site-title/index.js new file mode 100644 index 00000000000000..ae2e2982e4fa4b --- /dev/null +++ b/packages/block-library/src/site-title/index.js @@ -0,0 +1,20 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import edit from './edit'; +import metadata from './block.json'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + title: __( 'Site Title' ), + description: __( '' ), + edit, +}; diff --git a/packages/block-library/src/site-title/index.php b/packages/block-library/src/site-title/index.php new file mode 100644 index 00000000000000..ec12b57db54cae --- /dev/null +++ b/packages/block-library/src/site-title/index.php @@ -0,0 +1,31 @@ +<?php +/** + * Server-side rendering of the `core/site-title` block. + * + * @package WordPress + */ + +/** + * Renders the `core/site-title` block on server. + * + * @param array $attributes The block attributes. + * + * @return string Returns a rendering of the the site title. + */ +function render_block_core_site_title( $attributes ) { + return sprintf( '<h1>%s</h1>', get_bloginfo( 'name' ) ); +} + +/** + * Registers the `core/site-title` block on server. + */ +function register_block_core_site_title() { + register_block_type( + 'core/site-title', + array( + 'attributes' => array(), + 'render_callback' => 'render_block_core_site_title', + ) + ); +} +add_action( 'init', 'register_block_core_site_title' ); diff --git a/packages/block-library/src/site-title/style.scss b/packages/block-library/src/site-title/style.scss new file mode 100644 index 00000000000000..95daf9bc43f51d --- /dev/null +++ b/packages/block-library/src/site-title/style.scss @@ -0,0 +1,3 @@ +.wp-block-site-title { + font-size: 300%; +} diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index f9a2909ff360a8..d93b007635a9f2 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -17,6 +17,7 @@ @import "./rss/style.scss"; @import "./search/style.scss"; @import "./separator/style.scss"; +@import "./site-title/style.scss"; @import "./spacer/style.scss"; @import "./subhead/style.scss"; @import "./table/style.scss"; diff --git a/packages/core-data/README.md b/packages/core-data/README.md index a2fb271017fcd7..eaa3c2c3846cb8 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -110,6 +110,18 @@ _Returns_ - `Object`: Action object. +<a name="receiveSiteOptions" href="#receiveSiteOptions">#</a> **receiveSiteOptions** + +Returns an action object used in signalling that site options have been received. + +_Parameters_ + +- _siteOptions_ `Object`: Site options. + +_Returns_ + +- `Object`: Action object. + <a name="receiveThemeSupports" href="#receiveThemeSupports">#</a> **receiveThemeSupports** Returns an action object used in signalling that the index has been received. @@ -175,6 +187,30 @@ _Returns_ - `Object`: Updated record. +<a name="saveSiteOptions" href="#saveSiteOptions">#</a> **saveSiteOptions** + +Action triggered to save site options. + +_Parameters_ + +- _siteOptions_ `Object`: Site options. + +_Returns_ + +- `Object`: Updated site options. + +<a name="updateSiteOptions" href="#updateSiteOptions">#</a> **updateSiteOptions** + +Returns an action object used in signalling that site options have been locally updated. + +_Parameters_ + +- _siteOptions_ `Object`: Site options. + +_Returns_ + +- `Object`: Action object. + <!-- END TOKEN(Autogenerated actions) --> ## Selectors @@ -330,6 +366,22 @@ _Returns_ - `Array`: Records. +<a name="getSiteOptions" href="#getSiteOptions">#</a> **getSiteOptions** + +Return site options as they exist locally. + +_Related_ + +- isSiteOptionsDirty + +_Parameters_ + +- _state_ `Object`: Data state. + +_Returns_ + +- `Object`: Site options. + <a name="getThemeSupports" href="#getThemeSupports">#</a> **getThemeSupports** Return theme supports data in the index. @@ -419,6 +471,19 @@ _Returns_ - `boolean`: Whether a request is in progress for an embed preview. +<a name="isSiteOptionsDirty" href="#isSiteOptionsDirty">#</a> **isSiteOptionsDirty** + +Return whether the client has local changes to site options which haven't +yet been saved to the server. + +_Parameters_ + +- _state_ `Object`: Data state. + +_Returns_ + +- `boolean`: Whether or not the local site options state is dirty. + <!-- END TOKEN(Autogenerated selectors) --> diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index f9c7940d09db53..da10158fc523a1 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -98,6 +98,51 @@ export function receiveThemeSupports( themeSupports ) { }; } +/** + * Returns an action object used in signalling that site options have been received. + * + * @param {Object} siteOptions Site options. + * + * @return {Object} Action object. + */ +export function receiveSiteOptions( siteOptions ) { + return { + type: 'RECEIVE_SITE_OPTIONS', + siteOptions, + }; +} + +/** + * Returns an action object used in signalling that site options have been locally updated. + * + * @param {Object} siteOptions Site options. + * + * @return {Object} Action object. + */ +export function updateSiteOptions( siteOptions ) { + return { + type: 'UPDATE_SITE_OPTIONS', + siteOptions, + }; +} + +/** + * Action triggered to save site options. + * + * @param {Object} siteOptions Site options. + * + * @return {Object} Updated site options. + */ +export function* saveSiteOptions( siteOptions ) { + const updatedOptions = yield apiFetch( { + path: '/wp/v2/settings', + method: 'POST', + data: siteOptions, + } ); + yield receiveSiteOptions( updatedOptions ); + return updatedOptions; +} + /** * Returns an action object used in signalling that the preview data for * a given URl has been received. diff --git a/packages/core-data/src/reducer.js b/packages/core-data/src/reducer.js index 272ab20e2693ec..73b6d09b98e0fc 100644 --- a/packages/core-data/src/reducer.js +++ b/packages/core-data/src/reducer.js @@ -118,6 +118,42 @@ export function themeSupports( state = {}, action ) { return state; } +export const siteOptions = combineReducers( { + remote( state = {}, action ) { + switch ( action.type ) { + case 'RECEIVE_SITE_OPTIONS': + return { + ...state, + ...action.siteOptions, + }; + } + + return state; + }, + local( state = {}, action ) { + switch ( action.type ) { + case 'RECEIVE_SITE_OPTIONS': + case 'UPDATE_SITE_OPTIONS': + return { + ...state, + ...action.siteOptions, + }; + } + + return state; + }, + isDirty( state = false, action ) { + switch ( action.type ) { + case 'RECEIVE_SITE_OPTIONS': + return false; + case 'UPDATE_SITE_OPTIONS': + return true; + } + + return state; + }, +} ); + /** * Higher Order Reducer for a given entity config. It supports: * @@ -283,6 +319,7 @@ export default combineReducers( { currentUser, taxonomies, themeSupports, + siteOptions, entities, embedPreviews, userPermissions, diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 8edfdbf895cded..e8cbe6e7be4b94 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -17,6 +17,7 @@ import { receiveCurrentUser, receiveEntityRecords, receiveThemeSupports, + receiveSiteOptions, receiveEmbedPreview, receiveUserPermission, receiveAutosaves, @@ -95,6 +96,11 @@ export function* getThemeSupports() { yield receiveThemeSupports( activeThemes[ 0 ].theme_supports ); } +export function *getSiteOptions() { + const siteOptions = yield apiFetch( { path: '/wp/v2/settings' } ); + yield receiveSiteOptions( siteOptions ); +} + /** * Requests a preview from the from the Embed API. * diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index 42e4a1f23d982d..de2d3022f921d8 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -136,6 +136,31 @@ export function getThemeSupports( state ) { return state.themeSupports; } +/** + * Return site options as they exist locally. + * + * @see isSiteOptionsDirty + * + * @param {Object} state Data state. + * + * @return {Object} Site options. + */ +export function getSiteOptions( state ) { + return state.siteOptions.local; +} + +/** + * Return whether the client has local changes to site options which haven't + * yet been saved to the server. + * + * @param {Object} state Data state. + * + * @return {boolean} Whether or not the local site options state is dirty. + */ +export function isSiteOptionsDirty( state ) { + return state.siteOptions.isDirty; +} + /** * Returns the embed preview for the given URL. * diff --git a/packages/core-data/src/test/reducer.js b/packages/core-data/src/test/reducer.js index ec8349d0a65d36..6154c6bc6ab623 100644 --- a/packages/core-data/src/test/reducer.js +++ b/packages/core-data/src/test/reducer.js @@ -7,7 +7,7 @@ import { filter } from 'lodash'; /** * Internal dependencies */ -import { terms, entities, embedPreviews, userPermissions, autosaves, currentUser } from '../reducer'; +import { terms, entities, embedPreviews, userPermissions, autosaves, currentUser, siteOptions } from '../reducer'; describe( 'terms()', () => { it( 'returns an empty object by default', () => { @@ -254,3 +254,54 @@ describe( 'currentUser', () => { expect( state ).toEqual( currentUserData ); } ); } ); + +describe( 'siteOptions', () => { + it( 'receives server data into both `remote` and `local`', () => { + const data = { soup: 'veggie' }; + const state = siteOptions( {}, { + type: 'RECEIVE_SITE_OPTIONS', + siteOptions: data, + } ); + + expect( state ).toEqual( { + remote: data, + local: data, + isDirty: false, + } ); + } ); + + it( 'receives client changes only into `local` and sets the dirty bit', () => { + const data = { soup: 'veggie', dessert: 'mousse' }; + const cleanState = { + remote: data, + local: data, + isDirty: false, + }; + + expect( siteOptions( cleanState, { + type: 'UPDATE_SITE_OPTIONS', + siteOptions: { soup: 'fish' }, + } ) ).toEqual( { + remote: data, + local: { soup: 'fish', dessert: 'mousse' }, + isDirty: true, + } ); + } ); + + it( 'clears dirty state upon receiving server data', () => { + const dirtyState = { + remote: { soup: 'veggie' }, + local: { soup: 'fish' }, + isDirty: true, + }; + + expect( siteOptions( dirtyState, { + type: 'RECEIVE_SITE_OPTIONS', + siteOptions: { soup: 'tomato' }, + } ) ).toEqual( { + remote: { soup: 'tomato' }, + local: { soup: 'tomato' }, + isDirty: false, + } ); + } ); +} ); diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 5ecee444b652bb..63703b8c0d7f49 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -508,6 +508,12 @@ export function* savePost( options = {} ) { editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; } + const shouldSaveSiteOptions = yield select( 'core', 'isSiteOptionsDirty' ); + if ( shouldSaveSiteOptions ) { + const siteOptions = yield select( 'core', 'getSiteOptions' ); + yield dispatch( 'core', 'saveSiteOptions', siteOptions ); + } + let toSend = { ...edits, content: editedPostContent, diff --git a/packages/editor/src/store/block-sources/index.js b/packages/editor/src/store/block-sources/index.js index 060c482db30a5e..8964e45f3feba0 100644 --- a/packages/editor/src/store/block-sources/index.js +++ b/packages/editor/src/store/block-sources/index.js @@ -3,5 +3,6 @@ */ import * as post from './post'; import * as meta from './meta'; +import * as option from './option'; -export { post, meta }; +export { post, meta, option }; diff --git a/packages/editor/src/store/block-sources/option.js b/packages/editor/src/store/block-sources/option.js new file mode 100644 index 00000000000000..7e85fe31538a61 --- /dev/null +++ b/packages/editor/src/store/block-sources/option.js @@ -0,0 +1,47 @@ +/** + * WordPress dependencies + */ +import { select, dispatch } from '@wordpress/data-controls'; + +/** + * Store control invoked upon a state change, responsible for returning an + * object of dependencies. When a change in dependencies occurs (by shallow + * equality of the returned object), blocks are reset to apply the new sourced + * value. + * + * @yield {Object} Optional yielded controls. + * + * @return {Object} Dependencies as object. + */ +export function* getDependencies() { + return { + options: yield select( 'core', 'getSiteOptions', ), + }; +} + +/** + * Given an attribute schema and dependencies data, returns a source value. + * + * @param {Object} schema Block type attribute schema. + * @param {Object} dependencies Source dependencies. + * @param {Object} dependencies.options Site options. + * + * @return {Object} Block attribute value. + */ +export function apply( schema, { options } ) { + return options[ schema.option ]; +} + +/** + * Store control invoked upon a block attributes update, responsible for + * reflecting an update in a site option value. + * + * @param {Object} schema Block type attribute schema. + * @param {*} value Updated block attribute value. + * + * @yield {Object} Yielded action objects or store controls. + */ +export function* update( schema, value ) { + const siteOptions = { [ schema.option ]: value }; + yield dispatch( 'core', 'updateSiteOptions', siteOptions ); +} From 3f898cd40c6031a6e287838832a84cd516b616d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20=28Greg=29=20Zi=C3=B3=C5=82kowski?= <grzegorz@gziolo.pl> Date: Fri, 19 Jul 2019 17:47:56 +0200 Subject: [PATCH 19/70] Full Site Editing: Add view editing modes picker (#16680) * Add view editing modes picker * Move view editing mode to preferences in state * Hide label from displaying in the header for view mode picker * Add preview mode * Fix some inconsistencies --- .../src/components/block-list/block.js | 4 ++ packages/block-editor/src/store/selectors.js | 21 +++++++ .../components/header/header-toolbar/index.js | 2 - .../edit-post/src/components/header/index.js | 32 ++-------- .../header/publish-controls/index.js | 48 ++++++++++++++ .../post-publish-button-or-toggle.js | 0 .../post-publish-button-or-toggle.js.snap} | 0 .../test/post-publish-button-or-toggle.js} | 0 .../edit-post/src/components/layout/index.js | 19 +++--- .../src/components/layout/style.scss | 10 +++ .../src/components/visual-editor/index.js | 14 ++++- packages/editor/src/components/index.js | 2 +- .../editor/src/components/provider/index.js | 4 +- .../toggle-edit-template-post/index.js | 51 --------------- .../view-editing-mode-picker/index.js | 62 +++++++++++++++++++ .../view-editing-mode-picker/style.scss | 3 + packages/editor/src/store/actions.js | 42 +++++++++---- packages/editor/src/store/defaults.js | 1 + packages/editor/src/store/reducer.js | 6 ++ packages/editor/src/store/selectors.js | 14 +++++ packages/editor/src/style.scss | 1 + 21 files changed, 229 insertions(+), 107 deletions(-) create mode 100644 packages/edit-post/src/components/header/publish-controls/index.js rename packages/edit-post/src/components/header/{ => publish-controls}/post-publish-button-or-toggle.js (100%) rename packages/edit-post/src/components/header/{test/__snapshots__/index.js.snap => publish-controls/test/__snapshots__/post-publish-button-or-toggle.js.snap} (100%) rename packages/edit-post/src/components/header/{test/index.js => publish-controls/test/post-publish-button-or-toggle.js} (100%) delete mode 100644 packages/editor/src/components/toggle-edit-template-post/index.js create mode 100644 packages/editor/src/components/view-editing-mode-picker/index.js create mode 100644 packages/editor/src/components/view-editing-mode-picker/style.scss diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 320f63555946c8..36b481c36e6385 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -101,6 +101,7 @@ function BlockListBlock( { onSelectionStart, animateOnChange, enableAnimation, + isAncestorOfPostContent, } ) { // Random state used to rerender the component if needed, ideally we don't need this const [ , updateRerenderState ] = useState( {} ); @@ -403,6 +404,7 @@ function BlockListBlock( { 'is-typing': isTypingWithinBlock, 'is-focused': isFocusMode && ( isSelected || isParentOfSelectedBlock ), 'is-focus-mode': isFocusMode, + 'is-ancestor-of-post-content': isAncestorOfPostContent, }, className ); @@ -594,6 +596,7 @@ const applyWithSelect = withSelect( getBlockIndex, getBlockOrder, __unstableGetBlockWithoutInnerBlocks, + isAncestorOfBlockTypeName, } = select( 'core/block-editor' ); const block = __unstableGetBlockWithoutInnerBlocks( clientId ); const isSelected = isBlockSelected( clientId ); @@ -639,6 +642,7 @@ const applyWithSelect = withSelect( isValid, isSelected, isParentOfSelectedBlock, + isAncestorOfPostContent: isAncestorOfBlockTypeName( clientId, 'core/post-content' ), }; } ); diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 5e8771ff94935b..1857616991d54e 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -219,6 +219,27 @@ export const getClientIdsWithDescendants = createSelector( ] ); +/** + * Checks whether a block is an ancestor of a given block type based on its name. + * + * @param {Object} state Global application state. + * @param {string} clientId Block client ID. + * @param {string} blockTypeName The name of the block type, e.g.' core/paragraph'. + * + * @return {Array} ids of top-level and descendant blocks. + */ +export const isAncestorOfBlockTypeName = createSelector( + ( state, clientId, blockTypeName ) => { + return some( getBlockOrder( state, clientId ), ( innerClientId ) => { + return getBlockName( state, innerClientId ) === blockTypeName || + isAncestorOfBlockTypeName( state, innerClientId, blockTypeName ); + } ); + }, + ( state ) => [ + state.blocks.order, + ] +); + /** * Returns the total number of blocks, or the total number of blocks with a specific name in a post. * The number returned includes nested blocks. diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index da0309bce478a1..8ff5e2209594df 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -16,7 +16,6 @@ import { TableOfContents, EditorHistoryRedo, EditorHistoryUndo, - ToggleEditTemplatePost, } from '@wordpress/editor'; function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter, isTextModeEnabled } ) { @@ -46,7 +45,6 @@ function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter, isText <BlockToolbar /> </div> ) } - <ToggleEditTemplatePost /> </NavigableToolbar> ); } diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index dff5622593603a..a035a974e64ca4 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -4,8 +4,7 @@ import { __ } from '@wordpress/i18n'; import { IconButton } from '@wordpress/components'; import { - PostPreviewButton, - PostSavedState, + ViewEditingModePicker, } from '@wordpress/editor'; import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; @@ -18,15 +17,12 @@ import FullscreenModeClose from './fullscreen-mode-close'; import HeaderToolbar from './header-toolbar'; import MoreMenu from './more-menu'; import PinnedPlugins from './pinned-plugins'; -import PostPublishButtonOrToggle from './post-publish-button-or-toggle'; +import PublishControls from './publish-controls'; import shortcuts from '../../keyboard-shortcuts'; function Header( { closeGeneralSidebar, - hasActiveMetaboxes, isEditorSidebarOpened, - isPublishSidebarOpened, - isSaving, openGeneralSidebar, } ) { const toggleGeneralSidebar = isEditorSidebarOpened ? closeGeneralSidebar : openGeneralSidebar; @@ -44,25 +40,8 @@ function Header( { <HeaderToolbar /> </div> <div className="edit-post-header__settings"> - { ! isPublishSidebarOpened && ( - // This button isn't completely hidden by the publish sidebar. - // We can't hide the whole toolbar when the publish sidebar is open because - // we want to prevent mounting/unmounting the PostPublishButtonOrToggle DOM node. - // We track that DOM node to return focus to the PostPublishButtonOrToggle - // when the publish sidebar has been closed. - <PostSavedState - forceIsDirty={ hasActiveMetaboxes } - forceIsSaving={ isSaving } - /> - ) } - <PostPreviewButton - forceIsAutosaveable={ hasActiveMetaboxes } - forcePreviewLink={ isSaving ? null : undefined } - /> - <PostPublishButtonOrToggle - forceIsDirty={ hasActiveMetaboxes } - forceIsSaving={ isSaving } - /> + <ViewEditingModePicker /> + <PublishControls /> <div> <IconButton icon="admin-generic" @@ -85,10 +64,7 @@ function Header( { export default compose( withSelect( ( select ) => ( { - hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), isEditorSidebarOpened: select( 'core/edit-post' ).isEditorSidebarOpened(), - isPublishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(), - isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), } ) ), withDispatch( ( dispatch, ownProps, { select } ) => { const { getBlockSelectionStart } = select( 'core/block-editor' ); diff --git a/packages/edit-post/src/components/header/publish-controls/index.js b/packages/edit-post/src/components/header/publish-controls/index.js new file mode 100644 index 00000000000000..6b7ec3d361e163 --- /dev/null +++ b/packages/edit-post/src/components/header/publish-controls/index.js @@ -0,0 +1,48 @@ +/** + * WordPress dependencies + */ +import { + withSelect, +} from '@wordpress/data'; +import { + PostPreviewButton, + PostSavedState, +} from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import PostPublishButtonOrToggle from './post-publish-button-or-toggle'; + +const PublishControls = ( { hasActiveMetaboxes, isPublishSidebarOpened, isSaving } ) => { + return ( + <> + { ! isPublishSidebarOpened && ( + // This button isn't completely hidden by the publish sidebar. + // We can't hide the whole toolbar when the publish sidebar is open because + // we want to prevent mounting/unmounting the PostPublishButtonOrToggle DOM node. + // We track that DOM node to return focus to the PostPublishButtonOrToggle + // when the publish sidebar has been closed. + <PostSavedState + forceIsDirty={ hasActiveMetaboxes } + forceIsSaving={ isSaving } + /> + ) } + <PostPreviewButton + forceIsAutosaveable={ hasActiveMetaboxes } + forcePreviewLink={ isSaving ? null : undefined } + /> + <PostPublishButtonOrToggle + forceIsDirty={ hasActiveMetaboxes } + forceIsSaving={ isSaving } + /> + </> + ); +}; + +export default withSelect( ( select ) => ( { + hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), + isPublishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(), + isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), +} ) )( PublishControls ); + diff --git a/packages/edit-post/src/components/header/post-publish-button-or-toggle.js b/packages/edit-post/src/components/header/publish-controls/post-publish-button-or-toggle.js similarity index 100% rename from packages/edit-post/src/components/header/post-publish-button-or-toggle.js rename to packages/edit-post/src/components/header/publish-controls/post-publish-button-or-toggle.js diff --git a/packages/edit-post/src/components/header/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/header/publish-controls/test/__snapshots__/post-publish-button-or-toggle.js.snap similarity index 100% rename from packages/edit-post/src/components/header/test/__snapshots__/index.js.snap rename to packages/edit-post/src/components/header/publish-controls/test/__snapshots__/post-publish-button-or-toggle.js.snap diff --git a/packages/edit-post/src/components/header/test/index.js b/packages/edit-post/src/components/header/publish-controls/test/post-publish-button-or-toggle.js similarity index 100% rename from packages/edit-post/src/components/header/test/index.js rename to packages/edit-post/src/components/header/publish-controls/test/post-publish-button-or-toggle.js diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 1cfc95b6fd9b1b..3fcbdeda759005 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -45,7 +45,7 @@ import PluginPrePublishPanel from '../sidebar/plugin-pre-publish-panel'; import FullscreenMode from '../fullscreen-mode'; function Layout( { - mode, + contentEditingMode, editorSidebarOpened, pluginSidebarOpened, publishSidebarOpened, @@ -56,11 +56,11 @@ function Layout( { isSaving, isMobileViewport, isRichEditingEnabled, - isEditingTemplatePost, + viewEditingMode, } ) { const sidebarIsOpened = editorSidebarOpened || pluginSidebarOpened || publishSidebarOpened; - const className = classnames( 'edit-post-layout', { + const className = classnames( 'edit-post-layout', `is-mode-${ viewEditingMode }`, { 'is-sidebar-opened': sidebarIsOpened, 'has-fixed-toolbar': hasFixedToolbar, } ); @@ -91,9 +91,9 @@ function Layout( { <KeyboardShortcutHelpModal /> <ManageBlocksModal /> <OptionsModal /> - { ( mode === 'text' || ! isRichEditingEnabled ) && <TextEditor /> } - { isRichEditingEnabled && mode === 'visual' && ( - <VisualEditor withPostTitle={ ! isEditingTemplatePost } /> + { ( contentEditingMode === 'text' || ! isRichEditingEnabled ) && <TextEditor /> } + { isRichEditingEnabled && contentEditingMode === 'visual' && ( + <VisualEditor /> ) } <div className="edit-post-layout__metaboxes"> <MetaBoxes location="normal" /> @@ -139,9 +139,10 @@ function Layout( { export default compose( withSelect( ( select ) => { - const editorSettings = select( 'core/editor' ).getEditorSettings(); + const { getEditorSettings, getViewEditingMode } = select( 'core/editor' ); + const editorSettings = getEditorSettings(); return { - mode: select( 'core/edit-post' ).getEditorMode(), + contentEditingMode: select( 'core/edit-post' ).getEditorMode(), editorSidebarOpened: select( 'core/edit-post' ).isEditorSidebarOpened(), pluginSidebarOpened: select( 'core/edit-post' ).isPluginSidebarOpened(), publishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(), @@ -149,7 +150,7 @@ export default compose( hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), isRichEditingEnabled: editorSettings.richEditingEnabled, - isEditingTemplatePost: editorSettings.editTemplatePost, + viewEditingMode: getViewEditingMode(), }; } ), withDispatch( ( dispatch ) => { diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index 66307b74c0101c..6eff8839bfbba5 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -232,3 +232,13 @@ } } } + +.is-mode-preview .block-editor-block-list__block { + opacity: 0.2; +} + +.is-mode-preview .is-ancestor-of-post-content, +.is-mode-preview [data-type="core/post-content"], +.is-mode-preview [data-type="core/post-content"] .block-editor-block-list__block { + opacity: 1; +} diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 00f9c86a340ce2..bf9b4d6d4eba2a 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -1,6 +1,9 @@ /** * WordPress dependencies */ +import { + withSelect, +} from '@wordpress/data'; import { PostTitle, VisualEditorGlobalKeyboardShortcuts, @@ -22,7 +25,7 @@ import { import BlockInspectorButton from './block-inspector-button'; import PluginBlockSettingsMenuGroup from '../block-settings-menu/plugin-block-settings-menu-group'; -function VisualEditor( { withPostTitle } ) { +function VisualEditor( { hasPostTitle } ) { return ( <BlockSelectionClearer className="edit-post-visual-editor editor-styles-wrapper"> <VisualEditorGlobalKeyboardShortcuts /> @@ -30,7 +33,7 @@ function VisualEditor( { withPostTitle } ) { <WritingFlow> <ObserveTyping> <CopyHandler> - { withPostTitle && <PostTitle /> } + { hasPostTitle && <PostTitle /> } <BlockList /> </CopyHandler> </ObserveTyping> @@ -45,4 +48,9 @@ function VisualEditor( { withPostTitle } ) { ); } -export default VisualEditor; +export default withSelect( ( select ) => { + const viewEditingMode = select( 'core/editor' ).getViewEditingMode(); + return { + hasPostTitle: viewEditingMode === 'post-content', + }; +} )( VisualEditor ); diff --git a/packages/editor/src/components/index.js b/packages/editor/src/components/index.js index 5b17269569c5ec..eace551f397c68 100644 --- a/packages/editor/src/components/index.js +++ b/packages/editor/src/components/index.js @@ -12,7 +12,6 @@ export { export { default as TextEditorGlobalKeyboardShortcuts } from './global-keyboard-shortcuts/text-editor-shortcuts'; export { default as EditorHistoryRedo } from './editor-history/redo'; export { default as EditorHistoryUndo } from './editor-history/undo'; -export { default as ToggleEditTemplatePost } from './toggle-edit-template-post'; export { default as EditorNotices } from './editor-notices'; export { default as ErrorBoundary } from './error-boundary'; export { default as PageAttributesCheck } from './page-attributes/check'; @@ -58,6 +57,7 @@ export { default as PostVisibilityCheck } from './post-visibility/check'; export { default as TableOfContents } from './table-of-contents'; export { default as UnsavedChangesWarning } from './unsaved-changes-warning'; export { default as WordCount } from './word-count'; +export { default as ViewEditingModePicker } from './view-editing-mode-picker'; // State Related Components export { default as EditorProvider } from './provider'; diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 6b96858fb4c5db..89cd2829b7b054 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -58,7 +58,7 @@ class EditorProvider extends Component { props.post, props.initialEdits, props.settings.template, - props.settings.editTemplatePost && props.settings.templatePost + props.viewEditingMode !== 'post-content' && props.settings.templatePost ); if ( props.settings.autosave ) { @@ -188,6 +188,7 @@ export default compose( [ __unstableIsEditorReady: isEditorReady, getEditorBlocks, __experimentalGetReusableBlocks, + getViewEditingMode, } = select( 'core/editor' ); const { canUser } = select( 'core' ); @@ -197,6 +198,7 @@ export default compose( [ blocks: getEditorBlocks(), reusableBlocks: __experimentalGetReusableBlocks(), hasUploadPermissions: defaultTo( canUser( 'create', 'media' ), true ), + viewEditingMode: getViewEditingMode(), }; } ), withDispatch( ( dispatch ) => { diff --git a/packages/editor/src/components/toggle-edit-template-post/index.js b/packages/editor/src/components/toggle-edit-template-post/index.js deleted file mode 100644 index c8831fc0a7d42e..00000000000000 --- a/packages/editor/src/components/toggle-edit-template-post/index.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { useCallback } from '@wordpress/element'; -import { IconButton } from '@wordpress/components'; - -export default function ToggleEditTemplatePost() { - const { post, blocks, settings } = useSelect( ( select ) => { - const { - getCurrentPost, - getBlocksForSerialization, - getEditorSettings, - } = select( 'core/editor' ); - return { - post: getCurrentPost(), - blocks: getBlocksForSerialization(), - settings: getEditorSettings(), - }; - }, [] ); - const { updateEditorSettings, setupEditor } = useDispatch( 'core/editor' ); - return ( - <IconButton - icon="search" - label={ settings.editTemplatePost ? __( 'Edit Post' ) : __( 'Edit Template' ) } - onClick={ useCallback( () => { - const newEditTemplatePost = ! settings.editTemplatePost; - updateEditorSettings( { - editTemplatePost: newEditTemplatePost, - } ); - - let newBlocks = blocks; - if ( ! newEditTemplatePost ) { // Leaving template mode. - const postContentBlock = blocks.find( - ( block ) => block.name === 'core/post-content' - ); - newBlocks = postContentBlock ? postContentBlock.innerBlocks : []; - } - setupEditor( - post, - { - blocks: newBlocks, - }, - settings.template, - newEditTemplatePost && settings.templatePost - ); - }, [ post, blocks, settings ] ) } - /> - ); -} diff --git a/packages/editor/src/components/view-editing-mode-picker/index.js b/packages/editor/src/components/view-editing-mode-picker/index.js new file mode 100644 index 00000000000000..7375dc8c126889 --- /dev/null +++ b/packages/editor/src/components/view-editing-mode-picker/index.js @@ -0,0 +1,62 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { useCallback } from '@wordpress/element'; +import { SelectControl } from '@wordpress/components'; + +const options = [ + { value: 'post-content', label: __( 'Post content' ) }, + { value: 'preview', label: __( 'Preview' ) }, + { value: 'template', label: __( 'Template' ) }, +]; + +export default function ViewEditingModePicker() { + const { post, blocks, settings, viewEditingMode } = useSelect( ( select ) => { + const { + getCurrentPost, + getBlocksForSerialization, + getEditorSettings, + getViewEditingMode, + } = select( 'core/editor' ); + return { + post: getCurrentPost(), + blocks: getBlocksForSerialization(), + settings: getEditorSettings(), + viewEditingMode: getViewEditingMode(), + }; + }, [] ); + const { setupEditor, updateViewEditingMode } = useDispatch( 'core/editor' ); + + const updateViewEditingModeCallback = useCallback( ( newViewEditingMode ) => { + updateViewEditingMode( newViewEditingMode ); + + let newBlocks = blocks; + if ( viewEditingMode !== 'post-content' ) { // Leaving template mode. + const postContentBlock = blocks.find( + ( block ) => block.name === 'core/post-content' + ); + newBlocks = postContentBlock ? postContentBlock.innerBlocks : []; + } + setupEditor( + post, + { + blocks: newBlocks, + }, + settings.template, + newViewEditingMode !== 'post-content' && settings.templatePost + ); + }, [ post, blocks, settings.template, settings.templatePost, viewEditingMode ] ); + + return ( + <SelectControl + className="editor-view-editing-mode-picker" + label={ __( 'View Editing Mode' ) } + hideLabelFromVision + options={ options } + value={ viewEditingMode } + onChange={ updateViewEditingModeCallback } + /> + ); +} diff --git a/packages/editor/src/components/view-editing-mode-picker/style.scss b/packages/editor/src/components/view-editing-mode-picker/style.scss new file mode 100644 index 00000000000000..645a256074d8d9 --- /dev/null +++ b/packages/editor/src/components/view-editing-mode-picker/style.scss @@ -0,0 +1,3 @@ +.editor-view-editing-mode-picker .components-base-control__field { + margin-bottom: 0; +} diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 63703b8c0d7f49..c9b1400339f57f 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -494,18 +494,21 @@ export function* savePost( options = {} ) { 'getEditedPostContent' ); - const { editTemplatePost, templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); - if ( editTemplatePost && templatePost ) { - yield apiFetch( { - path: `/wp/v2/${ templatePost.post_type }/${ templatePost.ID }`, - method: 'PUT', - data: { - content: editedPostContent, - id: templatePost.ID, - }, - } ); - const postContentBlock = ( yield select( STORE_KEY, 'getBlocksForSerialization' ) ).find( ( block ) => block.name === 'core/post-content' ); - editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; + const { viewEditingMode } = yield select( STORE_KEY, 'getViewEditingMode' ); + if ( viewEditingMode !== 'post-content' ) { + const { templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); + if ( templatePost ) { + yield apiFetch( { + path: `/wp/v2/${ templatePost.post_type }/${ templatePost.ID }`, + method: 'PUT', + data: { + content: editedPostContent, + id: templatePost.ID, + }, + } ); + const postContentBlock = ( yield select( STORE_KEY, 'getBlocksForSerialization' ) ).find( ( block ) => block.name === 'core/post-content' ); + editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; + } } const shouldSaveSiteOptions = yield select( 'core', 'isSiteOptionsDirty' ); @@ -905,6 +908,21 @@ export function disablePublishSidebar() { }; } +/** + * Returns an action object used in signalling that the user has changed + * the view editing mode. + * + * @param {string} viewEditingMode The name of the view editing mode. + * + * @return {Object} Action object. + */ +export function updateViewEditingMode( viewEditingMode ) { + return { + type: 'UPDATE_VIEW_EDITING_MODE', + viewEditingMode, + }; +} + /** * Returns an action object used to signal that post saving is locked. * diff --git a/packages/editor/src/store/defaults.js b/packages/editor/src/store/defaults.js index 07c92803bd0a13..22dd5df57f0050 100644 --- a/packages/editor/src/store/defaults.js +++ b/packages/editor/src/store/defaults.js @@ -6,6 +6,7 @@ import { SETTINGS_DEFAULTS } from '@wordpress/block-editor'; export const PREFERENCES_DEFAULTS = { insertUsage: {}, // Should be kept for backward compatibility, see: https://github.com/WordPress/gutenberg/issues/14580. isPublishSidebarEnabled: true, + viewEditingMode: 'post-content', // Name of view editing mode (post-content, preview, template) }; /** diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index 6676b74dcbc395..351755eb2fd24b 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -308,6 +308,12 @@ export function template( state = { isValid: true }, action ) { */ export function preferences( state = PREFERENCES_DEFAULTS, action ) { switch ( action.type ) { + case 'UPDATE_VIEW_EDITING_MODE': + return { + ...state, + viewEditingMode: action.viewEditingMode, + }; + case 'ENABLE_PUBLISH_SIDEBAR': return { ...state, diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index 7570e8b7848de6..90e4372ec74ba3 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -1108,6 +1108,20 @@ export function canUserUseUnfilteredHTML( state ) { return has( getCurrentPost( state ), [ '_links', 'wp:action-unfiltered-html' ] ); } +/** + * Returns the currently selected view editing mode. + * + * @param {Object} state Global application state. + * + * @return {boolean} The currently selected view editing mode. + */ +export function getViewEditingMode( state ) { + if ( state.preferences.hasOwnProperty( 'viewEditingMode' ) ) { + return state.preferences.viewEditingMode; + } + return PREFERENCES_DEFAULTS.viewEditingMode; +} + /** * Returns whether the pre-publish panel should be shown * or skipped when the user clicks the "publish" button. diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index 949f751bd72af5..92f023161a0e1a 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -18,3 +18,4 @@ @import "./components/post-trash/style.scss"; @import "./components/table-of-contents/style.scss"; @import "./components/template-validation-notice/style.scss"; +@import "./components/view-editing-mode-picker/style.scss"; From 01ab3a15c6748d5d5448fc03fcf91e89fcd92adc Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Sat, 20 Jul 2019 10:46:07 +0200 Subject: [PATCH 20/70] Editor: Change save copy for new editing modes. --- packages/editor/src/components/post-saved-state/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/components/post-saved-state/index.js b/packages/editor/src/components/post-saved-state/index.js index 26716757bc5886..e53ad89ab28ac1 100644 --- a/packages/editor/src/components/post-saved-state/index.js +++ b/packages/editor/src/components/post-saved-state/index.js @@ -55,6 +55,7 @@ export class PostSavedState extends Component { isAutosaving, isPending, isLargeViewport, + viewEditingMode, } = this.props; const { forceSavedMessage } = this.state; if ( isSaving ) { @@ -97,7 +98,10 @@ export class PostSavedState extends Component { return null; } - const label = isPending ? __( 'Save as Pending' ) : __( 'Save Draft' ); + let label = isPending ? __( 'Save Draft as Pending' ) : __( 'Save Draft' ); + if ( viewEditingMode ) { + label += ' & Template'; + } if ( ! isLargeViewport ) { return ( <IconButton @@ -135,6 +139,7 @@ export default compose( [ getCurrentPost, isAutosavingPost, getEditedPostAttribute, + getViewEditingMode, } = select( 'core/editor' ); return { post: getCurrentPost(), @@ -146,6 +151,7 @@ export default compose( [ isSaveable: isEditedPostSaveable(), isAutosaving: isAutosavingPost(), isPending: 'pending' === getEditedPostAttribute( 'status' ), + viewEditingMode: getViewEditingMode(), }; } ), withDispatch( ( dispatch ) => ( { From 0739ef2b079effd11d917ae26cefcc8e2c7b678a Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Sat, 20 Jul 2019 10:30:36 +0200 Subject: [PATCH 21/70] Try template part --- lib/blocks.php | 1 + lib/templates.php | 11 +++- packages/block-library/src/index.js | 2 + .../src/template-part/block.json | 9 +++ .../block-library/src/template-part/edit.js | 55 +++++++++++++++++++ .../block-library/src/template-part/index.js | 19 +++++++ .../block-library/src/template-part/index.php | 36 ++++++++++++ 7 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 packages/block-library/src/template-part/block.json create mode 100644 packages/block-library/src/template-part/edit.js create mode 100644 packages/block-library/src/template-part/index.js create mode 100644 packages/block-library/src/template-part/index.php diff --git a/lib/blocks.php b/lib/blocks.php index 682a2641c85251..418195cb37ac66 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -32,6 +32,7 @@ function gutenberg_reregister_core_block_types() { 'post-title.php' => 'core/post-title', 'post-content.php' => 'core/post-content', 'post-date.php' => 'core/post-date', + 'template-part.php' => 'core/template-part', ); $registry = WP_Block_Type_Registry::get_instance(); diff --git a/lib/templates.php b/lib/templates.php index 5d09adef68503a..9bdd80048fbd55 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -28,6 +28,14 @@ function gutenberg_register_templates() { $template; if ( ! $template_query->have_posts() ) { + $footer_template_part_id = wp_insert_post( + array( + 'post_type' => 'wp_template', + 'post_name' => 'footer-template-part', + 'post_content' => "<!-- wp:paragraph -->\n<p>Template part</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:categories /-->", + ) + ); + $template_id = wp_insert_post( array( 'post_type' => 'wp_template', @@ -74,7 +82,8 @@ function gutenberg_register_templates() { <!-- /wp:spacer --></div></div> <!-- /wp:group --></div> <!-- /wp:column --></div> - <!-- /wp:columns -->', + <!-- /wp:columns --> + <!-- wp:template-part {"id":' . $footer_template_part_id . '} /-->', ) ); $template = get_post( $template_id ); diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 1566cb3eb6342e..73c0c0145e15ce 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -64,6 +64,7 @@ import * as tagCloud from './tag-cloud'; import * as classic from './classic'; // Top-level template blocks. +import * as templatePart from './template-part'; import * as postTitle from './post-title'; import * as postContent from './post-content'; import * as postDate from './post-date'; @@ -136,6 +137,7 @@ export const registerCoreBlocks = () => { postTitle, postContent, postDate, + templatePart, ].forEach( ( block ) => { if ( ! block ) { return; diff --git a/packages/block-library/src/template-part/block.json b/packages/block-library/src/template-part/block.json new file mode 100644 index 00000000000000..9f516258da9248 --- /dev/null +++ b/packages/block-library/src/template-part/block.json @@ -0,0 +1,9 @@ +{ + "name": "core/template-part", + "category": "theme", + "attributes": { + "id": { + "type": "number" + } + } +} diff --git a/packages/block-library/src/template-part/edit.js b/packages/block-library/src/template-part/edit.js new file mode 100644 index 00000000000000..89b168b1af5753 --- /dev/null +++ b/packages/block-library/src/template-part/edit.js @@ -0,0 +1,55 @@ +/** + * External dependencies + */ +import { get } from 'lodash'; + +/** + * WordPress dependencies + */ +import { InnerBlocks } from '@wordpress/block-editor'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { useEffect, useMemo } from '@wordpress/element'; +import { parse, serialize } from '@wordpress/blocks'; + +export default function TemplatePartEdit( { attributes, clientId } ) { + const { id } = attributes; + + const { + rawTemplatePartContent, + newBlocks, + } = useSelect( ( select ) => { + return { + rawTemplatePartContent: get( + select( 'core' ).getEntityRecord( 'postType', 'wp_template', id ), + [ 'content', 'raw' ] + ), + newBlocks: select( 'core/block-editor' ).getBlocks( clientId ), + }; + }, [ id, clientId ] ); + const { replaceInnerBlocks } = useDispatch( 'core/block-editor' ); + useEffect( + () => { + if ( ! rawTemplatePartContent ) { + return; + } + replaceInnerBlocks( clientId, parse( rawTemplatePartContent ) ); + }, + [ rawTemplatePartContent, replaceInnerBlocks ] + ); + const newRawTemplatePartContent = useMemo( () => ( serialize( newBlocks ) ), [ newBlocks ] ); + const DEBUG = false; + const innerBlocks = ( + <InnerBlocks + templateLock={ false } + /> + ); + if ( DEBUG ) { + return ( + <div> + <div>{ newRawTemplatePartContent === rawTemplatePartContent ? 'IS NOT DIRTY' : 'IS DIRTY' }</div> + { innerBlocks } + </div> + ); + } + return innerBlocks; +} diff --git a/packages/block-library/src/template-part/index.js b/packages/block-library/src/template-part/index.js new file mode 100644 index 00000000000000..31ef579d543e29 --- /dev/null +++ b/packages/block-library/src/template-part/index.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import edit from './edit'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + title: __( 'Template Part' ), + edit, +}; diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php new file mode 100644 index 00000000000000..3eac645e130572 --- /dev/null +++ b/packages/block-library/src/template-part/index.php @@ -0,0 +1,36 @@ +<?php +/** + * Server-side rendering of the `core/template-part` block. + * + * @package WordPress + */ + +/** + * Renders the `core/template-part` block on the server. + * + * @return string Returns the filtered post content of the current post. + */ +function render_block_core_template_part( $attributes ) { + $content_string = ''; + if ( isset( $attributes['id'] ) ) { + $post = get_post( $attributes['id'] ); + if ( $post ) { + $content_string = $post->post_content; + } + } + + return apply_filters( 'the_content', $content_string ); +} + +/** + * Registers the `core/template-parts` block on the server. + */ +function register_block_core_template_part() { + register_block_type( + 'core/template-part', + array( + 'render_callback' => 'render_block_core_template_part', + ) + ); +} +add_action( 'init', 'register_block_core_template_part' ); From 8fbda6272d9555d808922eb535b95ee8ea983c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 11:09:35 +0200 Subject: [PATCH 22/70] Move view mode to the center. --- packages/edit-post/src/components/header/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index a035a974e64ca4..40b0ab21a40706 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -39,8 +39,8 @@ function Header( { <FullscreenModeClose /> <HeaderToolbar /> </div> + <ViewEditingModePicker /> <div className="edit-post-header__settings"> - <ViewEditingModePicker /> <PublishControls /> <div> <IconButton From 58094ad775a75ea7da798f029bd3d5fd52013b40 Mon Sep 17 00:00:00 2001 From: Riad Benguella <benguella@gmail.com> Date: Sat, 20 Jul 2019 11:12:16 +0200 Subject: [PATCH 23/70] Remove nested appenders --- .../src/components/block-list-appender/index.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/block-editor/src/components/block-list-appender/index.js b/packages/block-editor/src/components/block-list-appender/index.js index 35bf09bb89e669..559c9a54b3f0f3 100644 --- a/packages/block-editor/src/components/block-list-appender/index.js +++ b/packages/block-editor/src/components/block-list-appender/index.js @@ -14,7 +14,6 @@ import { getDefaultBlockName } from '@wordpress/blocks'; */ import IgnoreNestedEvents from '../ignore-nested-events'; import DefaultBlockAppender from '../default-block-appender'; -import ButtonBlockAppender from '../button-block-appender'; function BlockListAppender( { blockClientIds, @@ -53,14 +52,7 @@ function BlockListAppender( { // Fallback in the case no renderAppender has been provided and the // default block can't be inserted. - return ( - <div className="block-list-appender"> - <ButtonBlockAppender - rootClientId={ rootClientId } - className="block-list-appender__toggle" - /> - </div> - ); + return null; } export default withSelect( ( select, { rootClientId } ) => { @@ -73,6 +65,6 @@ export default withSelect( ( select, { rootClientId } ) => { return { isLocked: !! getTemplateLock( rootClientId ), blockClientIds: getBlockOrder( rootClientId ), - canInsertDefaultBlock: canInsertBlockType( getDefaultBlockName(), rootClientId ), + canInsertDefaultBlock: ! rootClientId && canInsertBlockType( getDefaultBlockName() ), }; } )( BlockListAppender ); From 71885282a1e68c95a06397bb1d063b06c4f5d16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 11:34:15 +0200 Subject: [PATCH 24/70] Add all modes. --- .../src/components/view-editing-mode-picker/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/components/view-editing-mode-picker/index.js b/packages/editor/src/components/view-editing-mode-picker/index.js index 7375dc8c126889..5a48a01372ccfc 100644 --- a/packages/editor/src/components/view-editing-mode-picker/index.js +++ b/packages/editor/src/components/view-editing-mode-picker/index.js @@ -7,9 +7,15 @@ import { useCallback } from '@wordpress/element'; import { SelectControl } from '@wordpress/components'; const options = [ - { value: 'post-content', label: __( 'Post content' ) }, + { value: 'post-content', label: __( 'Writing' ) }, { value: 'preview', label: __( 'Preview' ) }, + { value: 'full-site', label: __( 'Full Site Editing' ) }, + { value: 'design', label: __( 'Design' ) }, { value: 'template', label: __( 'Template' ) }, + { value: '---', label: __( '---' ) }, + { value: 'header', label: __( 'Header' ) }, + { value: 'sidebar', label: __( 'Sidebar' ) }, + { value: 'footer', label: __( 'Footer' ) }, ]; export default function ViewEditingModePicker() { From dd5b8faab724af34309952915b7889e1e7ef5e3f Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <miguelcsf@gmail.com> Date: Sat, 20 Jul 2019 12:01:56 +0200 Subject: [PATCH 25/70] Site Title: Add color support - Rough pass for demo purposes, drawing from the example of core/heading. - Switches from PlainText to RichText, even though any inline formats in the content will be escaped by the server, leading to titles with visible HTML tags. --- .../block-library/src/site-title/block.json | 6 ++ packages/block-library/src/site-title/edit.js | 66 +++++++++++++++++-- .../block-library/src/site-title/index.php | 22 ++++++- .../block-library/src/site-title/style.scss | 3 - packages/block-library/src/style.scss | 1 - 5 files changed, 86 insertions(+), 12 deletions(-) delete mode 100644 packages/block-library/src/site-title/style.scss diff --git a/packages/block-library/src/site-title/block.json b/packages/block-library/src/site-title/block.json index 9e590dbdad5409..fc02f8eea3dcd2 100644 --- a/packages/block-library/src/site-title/block.json +++ b/packages/block-library/src/site-title/block.json @@ -6,6 +6,12 @@ "type": "string", "source": "option", "option": "title" + }, + "textColor": { + "type": "string" + }, + "customTextColor": { + "type": "string" } } } diff --git a/packages/block-library/src/site-title/edit.js b/packages/block-library/src/site-title/edit.js index b918b2386a8b87..fbeaae20c41af4 100644 --- a/packages/block-library/src/site-title/edit.js +++ b/packages/block-library/src/site-title/edit.js @@ -1,22 +1,76 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ -import { PlainText } from '@wordpress/editor'; import { __ } from '@wordpress/i18n'; +import { compose } from '@wordpress/compose'; +import { + InspectorControls, + withColors, + PanelColorSettings, + RichText, +} from '@wordpress/block-editor'; +import { memo } from '@wordpress/element'; + +const SiteTitleColorUI = memo( + function( { + textColorValue, + setTextColor, + } ) { + return ( + <PanelColorSettings + title={ __( 'Color Settings' ) } + initialOpen={ false } + colorSettings={ [ + { + value: textColorValue, + onChange: setTextColor, + label: __( 'Text Color' ), + }, + ] } + /> + ); + } +); -function SiteTitleEdit( { attributes, setAttributes } ) { +function SiteTitleEdit( { + attributes, + setAttributes, + textColor, + setTextColor, +} ) { const { title } = attributes; return ( - <div className="wp-block-site-title"> - <PlainText + <> + <InspectorControls> + <SiteTitleColorUI + setTextColor={ setTextColor } + textColorValue={ textColor.color } + /> + </InspectorControls> + <RichText + tagName="h1" value={ title } onChange={ ( newTitle ) => setAttributes( { title: newTitle } ) } placeholder={ __( 'Site Title' ) } aria-label={ __( 'Site Title' ) } + wrapperClassName={ classnames( 'wp-block-site-title', { + 'has-text-color': textColor.color, + [ textColor.class ]: textColor.class, + } ) } + style={ { + color: textColor.color, + } } /> - </div> + </> ); } -export default SiteTitleEdit; +export default compose( [ + withColors( 'backgroundColor', { textColor: 'color' } ), +] )( SiteTitleEdit ); diff --git a/packages/block-library/src/site-title/index.php b/packages/block-library/src/site-title/index.php index ec12b57db54cae..9e3de58637e69c 100644 --- a/packages/block-library/src/site-title/index.php +++ b/packages/block-library/src/site-title/index.php @@ -13,7 +13,18 @@ * @return string Returns a rendering of the the site title. */ function render_block_core_site_title( $attributes ) { - return sprintf( '<h1>%s</h1>', get_bloginfo( 'name' ) ); + $classes = ''; + $style_fragment = ''; + if ( array_key_exists( 'textColor', $attributes ) ) { + $classes = sprintf( 'has-text-color has-%s-color', $attributes[ 'textColor' ] ); + } elseif ( array_key_exists( 'customTextColor', $attributes ) ) { + $classes = 'has-text-color'; + $style_fragment = sprintf( + ' style="color:%s;"', + $attributes[ 'customTextColor' ] + ); + } + return sprintf( '<h1 class="%s"%s>%s</h1>', $classes, $style_fragment, get_bloginfo( 'name' ) ); } /** @@ -23,7 +34,14 @@ function register_block_core_site_title() { register_block_type( 'core/site-title', array( - 'attributes' => array(), + 'attributes' => array( + 'textColor' => array( + 'type' => 'string', + ), + 'customTextColor' => array( + 'type' => 'string', + ), + ), 'render_callback' => 'render_block_core_site_title', ) ); diff --git a/packages/block-library/src/site-title/style.scss b/packages/block-library/src/site-title/style.scss deleted file mode 100644 index 95daf9bc43f51d..00000000000000 --- a/packages/block-library/src/site-title/style.scss +++ /dev/null @@ -1,3 +0,0 @@ -.wp-block-site-title { - font-size: 300%; -} diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index d93b007635a9f2..f9a2909ff360a8 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -17,7 +17,6 @@ @import "./rss/style.scss"; @import "./search/style.scss"; @import "./separator/style.scss"; -@import "./site-title/style.scss"; @import "./spacer/style.scss"; @import "./subhead/style.scss"; @import "./table/style.scss"; From 3a1e8de50a3c7ad11ec37dcb72342169f6877aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 12:07:35 +0200 Subject: [PATCH 26/70] Add icons to new blocks. --- packages/block-library/src/post-content/icon.js | 8 ++++++++ packages/block-library/src/post-date/icon.js | 8 ++++++++ packages/block-library/src/post-title/icon.js | 8 ++++++++ packages/block-library/src/site-title/icon.js | 8 ++++++++ 4 files changed, 32 insertions(+) create mode 100644 packages/block-library/src/post-content/icon.js create mode 100644 packages/block-library/src/post-date/icon.js create mode 100644 packages/block-library/src/post-title/icon.js create mode 100644 packages/block-library/src/site-title/icon.js diff --git a/packages/block-library/src/post-content/icon.js b/packages/block-library/src/post-content/icon.js new file mode 100644 index 00000000000000..ed7991dc791f85 --- /dev/null +++ b/packages/block-library/src/post-content/icon.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { Path, SVG } from '@wordpress/components'; + +export default ( + <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><Path fill="none" d="M0 0h24v24H0V0z" /><Path d="M3 15h18v-2H3v2zm0 4h18v-2H3v2zm0-8h18V9H3v2zm0-6v2h18V5H3z" /></SVG> +); diff --git a/packages/block-library/src/post-date/icon.js b/packages/block-library/src/post-date/icon.js new file mode 100644 index 00000000000000..023a1902171d04 --- /dev/null +++ b/packages/block-library/src/post-date/icon.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { G, Path, SVG } from '@wordpress/components'; + +export default ( + <SVG viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><Path fill="none" d="M0 0h24v24H0V0z" /><G><Path d="M7 11h2v2H7v-2zm14-5v14c0 1.1-.9 2-2 2H5c-1.11 0-2-.9-2-2l.01-14c0-1.1.88-2 1.99-2h1V2h2v2h8V2h2v2h1c1.1 0 2 .9 2 2zM5 8h14V6H5v2zm14 12V10H5v10h14zm-4-7h2v-2h-2v2zm-4 0h2v-2h-2v2z" /></G></SVG> +); diff --git a/packages/block-library/src/post-title/icon.js b/packages/block-library/src/post-title/icon.js new file mode 100644 index 00000000000000..7350b484e42eb5 --- /dev/null +++ b/packages/block-library/src/post-title/icon.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { Path, SVG } from '@wordpress/components'; + +export default ( + <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><Path fill="none" d="M0 0h24v24H0V0z" /><Path d="M5 4v3h5.5v12h3V7H19V4H5z" /></SVG> +); diff --git a/packages/block-library/src/site-title/icon.js b/packages/block-library/src/site-title/icon.js new file mode 100644 index 00000000000000..33d3d6bc809839 --- /dev/null +++ b/packages/block-library/src/site-title/icon.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { Circle, Path, SVG } from '@wordpress/components'; + +export default ( + <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><Path fill="none" d="M0 0h24v24H0V0z" /><Path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zM7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 2.88-2.88 7.19-5 9.88C9.92 16.21 7 11.85 7 9z" /><Circle cx="12" cy="9" r="2.5" /></SVG> +); From 302721675f1574f1e693266eec45a2d73c109e16 Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Sat, 20 Jul 2019 12:08:14 +0200 Subject: [PATCH 27/70] Templates: Show admin UI. --- lib/templates.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/templates.php b/lib/templates.php index 9bdd80048fbd55..9112b2893713f2 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -16,6 +16,7 @@ function gutenberg_register_templates() { 'name' => __( 'Templates', 'gutenberg' ), ), 'show_in_rest' => true, + 'show_ui' => true, ) ); From 0f6be5c943ebe9cad501d357869a8ea325d1c236 Mon Sep 17 00:00:00 2001 From: Riad Benguella <benguella@gmail.com> Date: Sat, 20 Jul 2019 12:06:14 +0200 Subject: [PATCH 28/70] Try adding the new post block --- .../src/components/block-list/index.js | 30 ++++++++++-------- .../src/components/block-list/style.scss | 7 +++++ .../src/components/inner-blocks/index.js | 2 ++ packages/block-library/src/index.js | 2 ++ .../block-library/src/post-content/edit.js | 13 +++++++- packages/block-library/src/post/block.json | 5 +++ packages/block-library/src/post/edit.js | 31 +++++++++++++++++++ packages/block-library/src/post/index.js | 25 +++++++++++++++ packages/block-library/src/post/save.js | 8 +++++ 9 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 packages/block-library/src/post/block.json create mode 100644 packages/block-library/src/post/edit.js create mode 100644 packages/block-library/src/post/index.js create mode 100644 packages/block-library/src/post/save.js diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index 3e65a52fd3b2ec..f949901047acec 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -203,6 +203,7 @@ class BlockList extends Component { selectedBlockClientId, multiSelectedBlockClientIds, hasMultiSelection, + showWireframes, renderAppender, enableAnimation, } = this.props; @@ -220,19 +221,22 @@ class BlockList extends Component { clientId={ clientId } isBlockInSelection={ isBlockInSelection } > - <BlockListBlock - rootClientId={ rootClientId } - clientId={ clientId } - blockRef={ this.setBlockRef } - onSelectionStart={ this.onSelectionStart } - isDraggable={ isDraggable } - - // This prop is explicitely computed and passed down - // to avoid being impacted by the async mode - // otherwise there might be a small delay to trigger the animation. - animateOnChange={ blockClientIds } - enableAnimation={ enableAnimation } - /> + { showWireframes && <div className="editor-block-list__wireframe"></div> } + { ! showWireframes && ( + <BlockListBlock + rootClientId={ rootClientId } + clientId={ clientId } + blockRef={ this.setBlockRef } + onSelectionStart={ this.onSelectionStart } + isDraggable={ isDraggable } + + // This prop is explicitely computed and passed down + // to avoid being impacted by the async mode + // otherwise there might be a small delay to trigger the animation. + animateOnChange={ blockClientIds } + enableAnimation={ enableAnimation } + /> + ) } </BlockAsyncModeProvider> ); } ) } diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index 8ee3558ddf6398..d25c7c578d8776 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -1094,3 +1094,10 @@ padding: 10px $block-padding; } } + +.editor-block-list__wireframe { + background: #eee; + margin: 10px; + height: 100px; + width: 100%; +} diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 0d1feef14ff39d..48dbf227a4290b 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -110,6 +110,7 @@ class InnerBlocks extends Component { hasOverlay, renderAppender, template, + showWireframes, __experimentalTemplateOptions: templateOptions, __experimentalOnSelectTemplateOption: onSelectTemplateOption, __experimentalAllowTemplateOptionSkip: allowTemplateOptionSkip, @@ -134,6 +135,7 @@ class InnerBlocks extends Component { <BlockList rootClientId={ clientId } renderAppender={ renderAppender } + showWireframes={ showWireframes } /> ) } </div> diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 73c0c0145e15ce..51c589178aec7f 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -64,6 +64,7 @@ import * as tagCloud from './tag-cloud'; import * as classic from './classic'; // Top-level template blocks. +import * as post from './post'; import * as templatePart from './template-part'; import * as postTitle from './post-title'; import * as postContent from './post-content'; @@ -134,6 +135,7 @@ export const registerCoreBlocks = () => { video, // Register top-level template blocks. + post, postTitle, postContent, postDate, diff --git a/packages/block-library/src/post-content/edit.js b/packages/block-library/src/post-content/edit.js index 9b06ff6b155839..62224133b44c2a 100644 --- a/packages/block-library/src/post-content/edit.js +++ b/packages/block-library/src/post-content/edit.js @@ -4,16 +4,27 @@ import { InnerBlocks } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; -export default function PostContentEdit() { +export default function PostContentEdit( { clientId } ) { const allowedBlocks = useSelect( ( select ) => { return select( 'core/blocks' ).getBlockTypes().filter( ( { category } ) => category !== 'theme' ).map( ( { name } ) => name ); } ); + + const { hasInnerBlocks } = useSelect( ( select ) => { + const { getBlock } = select( 'core/block-editor' ); + const block = getBlock( clientId ); + + return { + hasInnerBlocks: !! ( block && block.innerBlocks.length ), + }; + }, [ clientId ] ); + return ( <InnerBlocks templateLock={ false } allowedBlocks={ allowedBlocks } + renderAppender={ ! hasInnerBlocks && InnerBlocks.ButtonBlockAppender } /> ); } diff --git a/packages/block-library/src/post/block.json b/packages/block-library/src/post/block.json new file mode 100644 index 00000000000000..f7c7b52cba1855 --- /dev/null +++ b/packages/block-library/src/post/block.json @@ -0,0 +1,5 @@ +{ + "name": "core/post", + "category": "theme", + "attributes": {} +} diff --git a/packages/block-library/src/post/edit.js b/packages/block-library/src/post/edit.js new file mode 100644 index 00000000000000..f0b28bc228822e --- /dev/null +++ b/packages/block-library/src/post/edit.js @@ -0,0 +1,31 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { InnerBlocks } from '@wordpress/block-editor'; + +function PostEdit( { + clientId, +} ) { + const { hasInnerBlocks, viewEditingMode } = useSelect( ( select ) => { + const { getBlock } = select( 'core/block-editor' ); + const { getViewEditingMode } = select( 'core/editor' ); + const block = getBlock( clientId ); + + return { + hasInnerBlocks: !! ( block && block.innerBlocks.length ), + viewEditingMode: getViewEditingMode(), + }; + }, [ clientId ] ); + + return ( + <> + <InnerBlocks + showWireframes={ viewEditingMode === 'design' } + renderAppender={ ! hasInnerBlocks && InnerBlocks.ButtonBlockAppender } + /> + </> + ); +} + +export default PostEdit; diff --git a/packages/block-library/src/post/index.js b/packages/block-library/src/post/index.js new file mode 100644 index 00000000000000..7010e913dcb55f --- /dev/null +++ b/packages/block-library/src/post/index.js @@ -0,0 +1,25 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import edit from './edit'; +import metadata from './block.json'; +import save from './save'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + title: __( 'Post' ), + description: __( 'A Post Container block.' ), + supports: { + html: false, + }, + edit, + save, +}; diff --git a/packages/block-library/src/post/save.js b/packages/block-library/src/post/save.js new file mode 100644 index 00000000000000..17571d8f30d2de --- /dev/null +++ b/packages/block-library/src/post/save.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { InnerBlocks } from '@wordpress/block-editor'; + +export default function save() { + return <InnerBlocks.Content />; +} From dd1b149365ea62c02e567511e25147255636e730 Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Sat, 20 Jul 2019 12:12:16 +0200 Subject: [PATCH 29/70] Add template part saving --- packages/editor/src/store/actions.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index c9b1400339f57f..2b90eedc13e3f7 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -509,6 +509,28 @@ export function* savePost( options = {} ) { const postContentBlock = ( yield select( STORE_KEY, 'getBlocksForSerialization' ) ).find( ( block ) => block.name === 'core/post-content' ); editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; } + const allBlocks = yield select( + 'core/block-editor', + 'getBlocksByClientId', + yield select( 'core/block-editor', 'getClientIdsWithDescendants' ) + ); + for ( const block of allBlocks ) { + if ( block.name === 'core/template-part' ) { + const { innerBlocks, attributes } = block; + if ( ! attributes.id ) { + continue; + } + const templatePartContent = serialize( innerBlocks ); + yield apiFetch( { + path: `/wp/v2/${ templatePost.post_type }/${ attributes.id }`, + method: 'PUT', + data: { + content: templatePartContent, + id: attributes.id, + }, + } ); + } + } } const shouldSaveSiteOptions = yield select( 'core', 'isSiteOptionsDirty' ); From 28f237e0bc7932cfa2e71a58b76780cd4101c48d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 12:27:56 +0200 Subject: [PATCH 30/70] Add icons for new blocks. --- packages/block-library/src/post-content/index.js | 2 ++ packages/block-library/src/post-date/index.js | 2 ++ packages/block-library/src/post-title/index.js | 2 ++ packages/block-library/src/site-title/index.js | 4 +++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/post-content/index.js b/packages/block-library/src/post-content/index.js index 9a32cb1332fec4..c6be63ad654552 100644 --- a/packages/block-library/src/post-content/index.js +++ b/packages/block-library/src/post-content/index.js @@ -8,6 +8,7 @@ import { __ } from '@wordpress/i18n'; */ import metadata from './block.json'; import edit from './edit'; +import icon from './icon'; const { name } = metadata; @@ -16,4 +17,5 @@ export { metadata, name }; export const settings = { title: __( 'Post Content' ), edit, + icon, }; diff --git a/packages/block-library/src/post-date/index.js b/packages/block-library/src/post-date/index.js index 08d7760c45b7da..3942a304486f19 100644 --- a/packages/block-library/src/post-date/index.js +++ b/packages/block-library/src/post-date/index.js @@ -8,6 +8,7 @@ import { __ } from '@wordpress/i18n'; */ import metadata from './block.json'; import edit from './edit'; +import icon from './icon'; const { name } = metadata; @@ -16,4 +17,5 @@ export { metadata, name }; export const settings = { title: __( 'Post Date' ), edit, + icon, }; diff --git a/packages/block-library/src/post-title/index.js b/packages/block-library/src/post-title/index.js index b056c40a0a130c..082c84681145ff 100644 --- a/packages/block-library/src/post-title/index.js +++ b/packages/block-library/src/post-title/index.js @@ -8,6 +8,7 @@ import { __ } from '@wordpress/i18n'; */ import metadata from './block.json'; import edit from './edit'; +import icon from './icon'; const { name } = metadata; @@ -16,4 +17,5 @@ export { metadata, name }; export const settings = { title: __( 'Post Title' ), edit, + icon, }; diff --git a/packages/block-library/src/site-title/index.js b/packages/block-library/src/site-title/index.js index ae2e2982e4fa4b..570da787ad4d64 100644 --- a/packages/block-library/src/site-title/index.js +++ b/packages/block-library/src/site-title/index.js @@ -8,6 +8,7 @@ import { __ } from '@wordpress/i18n'; */ import edit from './edit'; import metadata from './block.json'; +import icon from './icon'; const { name } = metadata; @@ -15,6 +16,7 @@ export { metadata, name }; export const settings = { title: __( 'Site Title' ), - description: __( '' ), + description: __( 'The name of the site visitors will see.' ), edit, + icon, }; From 4d7c8ae2a2d6bcfc65059ce572761af8ef2941eb Mon Sep 17 00:00:00 2001 From: Riad Benguella <benguella@gmail.com> Date: Sat, 20 Jul 2019 12:31:36 +0200 Subject: [PATCH 31/70] Refactor mode editing --- .../src/components/visual-editor/index.js | 3 ++- .../src/components/post-saved-state/index.js | 7 +++++- .../editor/src/components/provider/index.js | 3 ++- .../view-editing-mode-picker/index.js | 24 ++++++++---------- packages/editor/src/editor-modes.js | 25 +++++++++++++++++++ packages/editor/src/index.js | 1 + packages/editor/src/store/actions.js | 5 ++-- 7 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 packages/editor/src/editor-modes.js diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index bf9b4d6d4eba2a..ec265d752b5e3c 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -7,6 +7,7 @@ import { import { PostTitle, VisualEditorGlobalKeyboardShortcuts, + getModeConfig, } from '@wordpress/editor'; import { WritingFlow, @@ -51,6 +52,6 @@ function VisualEditor( { hasPostTitle } ) { export default withSelect( ( select ) => { const viewEditingMode = select( 'core/editor' ).getViewEditingMode(); return { - hasPostTitle: viewEditingMode === 'post-content', + hasPostTitle: ! getModeConfig( viewEditingMode ).showTemplate, }; } )( VisualEditor ); diff --git a/packages/editor/src/components/post-saved-state/index.js b/packages/editor/src/components/post-saved-state/index.js index e53ad89ab28ac1..73aaa39cd6960b 100644 --- a/packages/editor/src/components/post-saved-state/index.js +++ b/packages/editor/src/components/post-saved-state/index.js @@ -20,6 +20,11 @@ import { withViewportMatch } from '@wordpress/viewport'; */ import PostSwitchToDraftButton from '../post-switch-to-draft-button'; +/** + * Internal dependencies + */ +import { getModeConfig } from '../../editor-modes'; + /** * Component showing whether the post is saved or not and displaying save links. * @@ -99,7 +104,7 @@ export class PostSavedState extends Component { } let label = isPending ? __( 'Save Draft as Pending' ) : __( 'Save Draft' ); - if ( viewEditingMode ) { + if ( getModeConfig( viewEditingMode ).showTemplate ) { label += ' & Template'; } if ( ! isLargeViewport ) { diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 89cd2829b7b054..798b8e317cce09 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -23,6 +23,7 @@ import withRegistryProvider from './with-registry-provider'; import { mediaUpload } from '../../utils'; import ReusableBlocksButtons from '../reusable-blocks-buttons'; import ConvertToGroupButtons from '../convert-to-group-buttons'; +import { getModeConfig } from '../../editor-modes'; const fetchLinkSuggestions = async ( search ) => { const posts = await apiFetch( { @@ -58,7 +59,7 @@ class EditorProvider extends Component { props.post, props.initialEdits, props.settings.template, - props.viewEditingMode !== 'post-content' && props.settings.templatePost + getModeConfig( props.viewEditingMode ).showTemplate && props.settings.templatePost ); if ( props.settings.autosave ) { diff --git a/packages/editor/src/components/view-editing-mode-picker/index.js b/packages/editor/src/components/view-editing-mode-picker/index.js index 5a48a01372ccfc..3b465ac603704a 100644 --- a/packages/editor/src/components/view-editing-mode-picker/index.js +++ b/packages/editor/src/components/view-editing-mode-picker/index.js @@ -6,17 +6,10 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { useCallback } from '@wordpress/element'; import { SelectControl } from '@wordpress/components'; -const options = [ - { value: 'post-content', label: __( 'Writing' ) }, - { value: 'preview', label: __( 'Preview' ) }, - { value: 'full-site', label: __( 'Full Site Editing' ) }, - { value: 'design', label: __( 'Design' ) }, - { value: 'template', label: __( 'Template' ) }, - { value: '---', label: __( '---' ) }, - { value: 'header', label: __( 'Header' ) }, - { value: 'sidebar', label: __( 'Sidebar' ) }, - { value: 'footer', label: __( 'Footer' ) }, -]; +/** + * Internal dependencies + */ +import { getModeConfig, modes } from '../../editor-modes'; export default function ViewEditingModePicker() { const { post, blocks, settings, viewEditingMode } = useSelect( ( select ) => { @@ -36,22 +29,25 @@ export default function ViewEditingModePicker() { const { setupEditor, updateViewEditingMode } = useDispatch( 'core/editor' ); const updateViewEditingModeCallback = useCallback( ( newViewEditingMode ) => { + const currentModeConfig = getModeConfig( viewEditingMode ); + const newModeConfig = getModeConfig( newViewEditingMode ); updateViewEditingMode( newViewEditingMode ); let newBlocks = blocks; - if ( viewEditingMode !== 'post-content' ) { // Leaving template mode. + if ( currentModeConfig.showTemplate ) { // Leaving template mode. const postContentBlock = blocks.find( ( block ) => block.name === 'core/post-content' ); newBlocks = postContentBlock ? postContentBlock.innerBlocks : []; } + setupEditor( post, { blocks: newBlocks, }, settings.template, - newViewEditingMode !== 'post-content' && settings.templatePost + newModeConfig.showTemplate && settings.templatePost ); }, [ post, blocks, settings.template, settings.templatePost, viewEditingMode ] ); @@ -60,7 +56,7 @@ export default function ViewEditingModePicker() { className="editor-view-editing-mode-picker" label={ __( 'View Editing Mode' ) } hideLabelFromVision - options={ options } + options={ modes } value={ viewEditingMode } onChange={ updateViewEditingModeCallback } /> diff --git a/packages/editor/src/editor-modes.js b/packages/editor/src/editor-modes.js new file mode 100644 index 00000000000000..262a157b50f116 --- /dev/null +++ b/packages/editor/src/editor-modes.js @@ -0,0 +1,25 @@ +/** + * External dependencies + */ +import { find } from 'lodash'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +export const modes = [ + { value: 'post-content', label: __( 'Writing' ), showTemplate: false }, + { value: 'preview', label: __( 'Preview' ), showTemplate: true }, + { value: 'full-site', label: __( 'Full Site Editing' ), showTemplate: true }, + { value: 'design', label: __( 'Design' ), showTemplate: true }, + { value: 'template', label: __( 'Template' ), showTemplate: true }, + { value: '---', label: __( '---' ), showTemplate: true }, + { value: 'header', label: __( 'Header' ), showTemplate: true }, + { value: 'sidebar', label: __( 'Sidebar' ), showTemplate: true }, + { value: 'footer', label: __( 'Footer' ), showTemplate: true }, +]; + +export const getModeConfig = ( modeId ) => { + return find( modes, ( mode ) => mode.value === modeId ); +}; diff --git a/packages/editor/src/index.js b/packages/editor/src/index.js index 67de1923a013d9..e26cfd3658f7d4 100644 --- a/packages/editor/src/index.js +++ b/packages/editor/src/index.js @@ -17,6 +17,7 @@ import './hooks'; export * from './components'; export * from './utils'; +export * from './editor-modes'; export { storeConfig } from './store'; /* diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 2b90eedc13e3f7..7de42df4979daa 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -36,6 +36,7 @@ import { } from './utils/notice-builder'; import { awaitNextStateChange, getRegistry } from './controls'; import * as sources from './block-sources'; +import { getModeConfig } from '../editor-modes'; /** * Map of Registry instance to WeakMap of dependencies by custom source. @@ -494,8 +495,8 @@ export function* savePost( options = {} ) { 'getEditedPostContent' ); - const { viewEditingMode } = yield select( STORE_KEY, 'getViewEditingMode' ); - if ( viewEditingMode !== 'post-content' ) { + const viewEditingMode = yield select( STORE_KEY, 'getViewEditingMode' ); + if ( getModeConfig( viewEditingMode ).showTemplate ) { const { templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); if ( templatePost ) { yield apiFetch( { From a47cb2338b87f25226191c1c26c65cddf2e6ee66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 13:04:47 +0200 Subject: [PATCH 32/70] COVER: Allow all blocks in inner blocks. --- packages/block-library/src/cover/edit.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index 7de37e53f3fc39..6ba81ed2d96886 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -54,7 +54,6 @@ const INNER_BLOCKS_TEMPLATE = [ placeholder: __( 'Write title…' ), } ], ]; -const INNER_BLOCKS_ALLOWED_BLOCKS = [ 'core/button', 'core/heading', 'core/paragraph' ]; function retrieveFastAverageColor() { if ( ! retrieveFastAverageColor.fastAverageColor ) { @@ -299,7 +298,6 @@ class CoverEdit extends Component { <div className="wp-block-cover__inner-container"> <InnerBlocks template={ INNER_BLOCKS_TEMPLATE } - allowedBlocks={ INNER_BLOCKS_ALLOWED_BLOCKS } /> </div> </div> From 5cbb4a78c83e08573c2bb88e6f1d70f79a8fbc1a Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Sat, 20 Jul 2019 13:13:09 +0200 Subject: [PATCH 33/70] Editor: Support nested post content blocks. --- .../view-editing-mode-picker/index.js | 5 ++--- packages/editor/src/store/actions.js | 17 ++++++++--------- packages/editor/src/utils/blocks.js | 15 +++++++++++++++ packages/editor/src/utils/index.js | 1 + 4 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 packages/editor/src/utils/blocks.js diff --git a/packages/editor/src/components/view-editing-mode-picker/index.js b/packages/editor/src/components/view-editing-mode-picker/index.js index 3b465ac603704a..a31b3aa784e103 100644 --- a/packages/editor/src/components/view-editing-mode-picker/index.js +++ b/packages/editor/src/components/view-editing-mode-picker/index.js @@ -10,6 +10,7 @@ import { SelectControl } from '@wordpress/components'; * Internal dependencies */ import { getModeConfig, modes } from '../../editor-modes'; +import { findDeepBlock } from '../../utils'; export default function ViewEditingModePicker() { const { post, blocks, settings, viewEditingMode } = useSelect( ( select ) => { @@ -35,9 +36,7 @@ export default function ViewEditingModePicker() { let newBlocks = blocks; if ( currentModeConfig.showTemplate ) { // Leaving template mode. - const postContentBlock = blocks.find( - ( block ) => block.name === 'core/post-content' - ); + const postContentBlock = findDeepBlock( blocks, 'core/post-content' ); newBlocks = postContentBlock ? postContentBlock.innerBlocks : []; } diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 7de42df4979daa..9823d1ad007dd3 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -37,6 +37,7 @@ import { import { awaitNextStateChange, getRegistry } from './controls'; import * as sources from './block-sources'; import { getModeConfig } from '../editor-modes'; +import { findDeepBlock } from '../utils'; /** * Map of Registry instance to WeakMap of dependencies by custom source. @@ -178,9 +179,7 @@ export function* setupEditor( post, edits, template, templatePost ) { blocks = parse( templatePost.post_content ); // Post content is nested inside a post content block. - const postContentBlock = blocks.find( - ( block ) => block.name === 'core/post-content' - ); + const postContentBlock = findDeepBlock( blocks, 'core/post-content' ); if ( postContentBlock ) { postContentBlock.innerBlocks = isNewPost ? // Apply block (post content) template. synchronizeBlocksWithTemplate( postContentInnerBlocks, template ) : @@ -496,6 +495,11 @@ export function* savePost( options = {} ) { ); const viewEditingMode = yield select( STORE_KEY, 'getViewEditingMode' ); + const allBlocks = yield select( + 'core/block-editor', + 'getBlocksByClientId', + yield select( 'core/block-editor', 'getClientIdsWithDescendants' ) + ); if ( getModeConfig( viewEditingMode ).showTemplate ) { const { templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); if ( templatePost ) { @@ -507,14 +511,9 @@ export function* savePost( options = {} ) { id: templatePost.ID, }, } ); - const postContentBlock = ( yield select( STORE_KEY, 'getBlocksForSerialization' ) ).find( ( block ) => block.name === 'core/post-content' ); + const postContentBlock = allBlocks.find( ( block ) => block.name === 'core/post-content' ); editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; } - const allBlocks = yield select( - 'core/block-editor', - 'getBlocksByClientId', - yield select( 'core/block-editor', 'getClientIdsWithDescendants' ) - ); for ( const block of allBlocks ) { if ( block.name === 'core/template-part' ) { const { innerBlocks, attributes } = block; diff --git a/packages/editor/src/utils/blocks.js b/packages/editor/src/utils/blocks.js new file mode 100644 index 00000000000000..02ae7c0b4d9dca --- /dev/null +++ b/packages/editor/src/utils/blocks.js @@ -0,0 +1,15 @@ +export const findDeepBlock = ( blocksToSearch, blockName ) => { + let foundBlock = blocksToSearch.find( ( block ) => block.name === blockName ); + + if ( foundBlock ) { + return foundBlock; + } + + for ( const block of blocksToSearch ) { + foundBlock = + block.innerBlocks && findDeepBlock( block.innerBlocks, blockName ); + if ( foundBlock ) { + return foundBlock; + } + } +}; diff --git a/packages/editor/src/utils/index.js b/packages/editor/src/utils/index.js index 0f246c16e4aec8..cf64dbb0bd8885 100644 --- a/packages/editor/src/utils/index.js +++ b/packages/editor/src/utils/index.js @@ -5,3 +5,4 @@ import mediaUpload from './media-upload'; export { mediaUpload }; export { cleanForSlug } from './url.js'; +export { findDeepBlock } from './blocks'; From a7960d34f3b310177e601ce13108031d9061f1e8 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski <grzegorz@gziolo.pl> Date: Sat, 20 Jul 2019 13:17:45 +0200 Subject: [PATCH 34/70] Disable template blocks in preview mode --- .../src/components/block-list/block.js | 15 ++++-- packages/block-editor/src/store/selectors.js | 22 +++++++- packages/editor/src/hooks/index.js | 1 + packages/editor/src/hooks/view-edit-mode.js | 52 +++++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 packages/editor/src/hooks/view-edit-mode.js diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 36b481c36e6385..36e3e6b0e9f756 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -102,6 +102,9 @@ function BlockListBlock( { animateOnChange, enableAnimation, isAncestorOfPostContent, + noInserters, + noMovers, + noToolbars, } ) { // Random state used to rerender the component if needed, ideally we don't need this const [ , updateRerenderState ] = useState( {} ); @@ -355,8 +358,12 @@ function BlockListBlock( { // If the block is selected and we're typing the block should not appear. // Empty paragraph blocks should always show up as unselected. - const showInserterShortcuts = ( isSelected || isHovered ) && isEmptyDefaultBlock && isValid; - const showEmptyBlockSideInserter = ( isSelected || isHovered || isLast ) && isEmptyDefaultBlock && isValid; + const showInserterShortcuts = + ! noInserters && + ( isSelected || isHovered ) && isEmptyDefaultBlock && isValid; + const showEmptyBlockSideInserter = + ! noInserters && + ( isSelected || isHovered || isLast ) && isEmptyDefaultBlock && isValid; const shouldAppearSelected = ! isFocusMode && ! showEmptyBlockSideInserter && @@ -369,6 +376,7 @@ function BlockListBlock( { ! isEmptyDefaultBlock; // We render block movers and block settings to keep them tabbale even if hidden const shouldRenderMovers = + ! noMovers && ( isSelected || hoverArea === ( isRTL ? 'right' : 'left' ) ) && ! showEmptyBlockSideInserter && ! isPartOfMultiSelection && @@ -376,13 +384,14 @@ function BlockListBlock( { const shouldShowBreadcrumb = ! isFocusMode && isHovered && ! isEmptyDefaultBlock; const shouldShowContextualToolbar = + ! noToolbars && ! hasFixedToolbar && ! showEmptyBlockSideInserter && ( ( isSelected && ( ! isTypingWithinBlock || isCaretWithinFormattedText ) ) || isFirstMultiSelected ); - const shouldShowMobileToolbar = shouldAppearSelected; + const shouldShowMobileToolbar = ! noToolbars && shouldAppearSelected; // Insertion point can only be made visible if the block is at the // the extent of a multi-selection, or not in a multi-selection. diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 1857616991d54e..5472ff16639fe1 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -226,7 +226,7 @@ export const getClientIdsWithDescendants = createSelector( * @param {string} clientId Block client ID. * @param {string} blockTypeName The name of the block type, e.g.' core/paragraph'. * - * @return {Array} ids of top-level and descendant blocks. + * @return {boolean} True when a block an ancestor of a given block type. */ export const isAncestorOfBlockTypeName = createSelector( ( state, clientId, blockTypeName ) => { @@ -240,6 +240,26 @@ export const isAncestorOfBlockTypeName = createSelector( ] ); +/** + * Checks whether a block is an descendant of a given block type based on its name. + * + * @param {Object} state Global application state. + * @param {string} clientId Block client ID. + * @param {string} blockTypeName The name of the block type, e.g.' core/paragraph'. + * + * @return {boolean} True when a block an ancestor of a given block type. + */ +export function isDescendantOfBlockTypeName( state, clientId, blockTypeName ) { + let current = clientId; + do { + current = state.blocks.parents[ current ]; + if ( getBlockName( state, current ) === blockTypeName ) { + return true; + } + } while ( current ); + return false; +} + /** * Returns the total number of blocks, or the total number of blocks with a specific name in a post. * The number returned includes nested blocks. diff --git a/packages/editor/src/hooks/index.js b/packages/editor/src/hooks/index.js index 2c8a61d9802521..4f6665e003e1c8 100644 --- a/packages/editor/src/hooks/index.js +++ b/packages/editor/src/hooks/index.js @@ -2,3 +2,4 @@ * Internal dependencies */ import './default-autocompleters'; +import './view-edit-mode'; diff --git a/packages/editor/src/hooks/view-edit-mode.js b/packages/editor/src/hooks/view-edit-mode.js new file mode 100644 index 00000000000000..eaef128cfd355e --- /dev/null +++ b/packages/editor/src/hooks/view-edit-mode.js @@ -0,0 +1,52 @@ +/** + * WordPress dependencies + */ +import { Disabled } from '@wordpress/components'; +import { createHigherOrderComponent } from '@wordpress/compose'; +import { useSelect } from '@wordpress/data'; +import { addFilter } from '@wordpress/hooks'; + +export const withViewEditMode = createHigherOrderComponent( ( BlockListBlock ) => { + return ( props ) => { + const { clientId, name } = props; + + const { isAncestorOfPostContent, isDescendantOfPostContent, viewEditingMode } = useSelect( ( select ) => { + const { isAncestorOfBlockTypeName, isDescendantOfBlockTypeName } = select( 'core/block-editor' ); + const { getViewEditingMode } = select( 'core/editor' ); + + return { + isAncestorOfPostContent: isAncestorOfBlockTypeName( clientId, 'core/post-content' ), + isDescendantOfPostContent: isDescendantOfBlockTypeName( clientId, 'core/post-content' ), + viewEditingMode: getViewEditingMode(), + }; + }, [ clientId ] ); + + if ( viewEditingMode !== 'preview' ) { + return <BlockListBlock { ...props } />; + } + + if ( isAncestorOfPostContent ) { + return <BlockListBlock { ...props } + noInserters={ true } + noMovers={ true } + noToolbars={ true } + />; + } + + if ( name === 'core/post-content' ) { + return <BlockListBlock { ...props } noMovers={ true } />; + } + + if ( isDescendantOfPostContent ) { + return <BlockListBlock { ...props } />; + } + + return ( + <Disabled> + <BlockListBlock { ...props } /> + </Disabled> + ); + }; +}, 'withViewEditMode' ); + +addFilter( 'editor.BlockListBlock', 'core/editor/with-view-edit-mode', withViewEditMode ); From f91b41b1346f5442ff0a4430b2f8cc42b3b12071 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski <grzegorz@gziolo.pl> Date: Sat, 20 Jul 2019 13:38:13 +0200 Subject: [PATCH 35/70] Disable movers for post content block as well when in preview mode --- packages/block-editor/src/components/block-list/block.js | 7 +++++-- packages/editor/src/hooks/view-edit-mode.js | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 36e3e6b0e9f756..295ec21ecb82fd 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -396,8 +396,11 @@ function BlockListBlock( { // Insertion point can only be made visible if the block is at the // the extent of a multi-selection, or not in a multi-selection. const shouldShowInsertionPoint = - ( isPartOfMultiSelection && isFirstMultiSelected ) || - ! isPartOfMultiSelection; + ! noInserters && + ( + ( isPartOfMultiSelection && isFirstMultiSelected ) || + ! isPartOfMultiSelection + ); // The wp-block className is important for editor styles. // Generate the wrapper class names handling the different states of the block. diff --git a/packages/editor/src/hooks/view-edit-mode.js b/packages/editor/src/hooks/view-edit-mode.js index eaef128cfd355e..3cf79a6226c210 100644 --- a/packages/editor/src/hooks/view-edit-mode.js +++ b/packages/editor/src/hooks/view-edit-mode.js @@ -34,7 +34,10 @@ export const withViewEditMode = createHigherOrderComponent( ( BlockListBlock ) = } if ( name === 'core/post-content' ) { - return <BlockListBlock { ...props } noMovers={ true } />; + return <BlockListBlock { ...props } + noMovers={ true } + noInserters={ true } + />; } if ( isDescendantOfPostContent ) { From 5ef619f413d1ad821dd967e2c9ca856578f5529a Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Sat, 20 Jul 2019 13:31:34 +0200 Subject: [PATCH 36/70] Add template part name --- lib/templates.php | 1 + .../src/template-part/block.json | 5 +++ .../block-library/src/template-part/edit.js | 44 ++++++++++++------- packages/editor/src/store/actions.js | 6 +-- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/lib/templates.php b/lib/templates.php index 9112b2893713f2..ee1b8735fdc9bc 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -32,6 +32,7 @@ function gutenberg_register_templates() { $footer_template_part_id = wp_insert_post( array( 'post_type' => 'wp_template', + 'post_title' => 'Footer', 'post_name' => 'footer-template-part', 'post_content' => "<!-- wp:paragraph -->\n<p>Template part</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:categories /-->", ) diff --git a/packages/block-library/src/template-part/block.json b/packages/block-library/src/template-part/block.json index 9f516258da9248..1679f28071eb88 100644 --- a/packages/block-library/src/template-part/block.json +++ b/packages/block-library/src/template-part/block.json @@ -4,6 +4,11 @@ "attributes": { "id": { "type": "number" + }, + "name": { + "type": "string", + "source": "html", + "selector": "none" } } } diff --git a/packages/block-library/src/template-part/edit.js b/packages/block-library/src/template-part/edit.js index 89b168b1af5753..3d457afdc1034f 100644 --- a/packages/block-library/src/template-part/edit.js +++ b/packages/block-library/src/template-part/edit.js @@ -6,24 +6,27 @@ import { get } from 'lodash'; /** * WordPress dependencies */ -import { InnerBlocks } from '@wordpress/block-editor'; +import { InnerBlocks, InspectorControls } from '@wordpress/block-editor'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect, useMemo } from '@wordpress/element'; import { parse, serialize } from '@wordpress/blocks'; +import { TextControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; -export default function TemplatePartEdit( { attributes, clientId } ) { +export default function TemplatePartEdit( { attributes, clientId, setAttributes } ) { const { id } = attributes; const { rawTemplatePartContent, newBlocks, + templatePartTitle, + hasInnerBlocks, } = useSelect( ( select ) => { + const template = select( 'core' ).getEntityRecord( 'postType', 'wp_template', id ); return { - rawTemplatePartContent: get( - select( 'core' ).getEntityRecord( 'postType', 'wp_template', id ), - [ 'content', 'raw' ] - ), + rawTemplatePartContent: get( template, [ 'content', 'raw' ] ), newBlocks: select( 'core/block-editor' ).getBlocks( clientId ), + templatePartTitle: get( template, [ 'title', 'raw' ] ), }; }, [ id, clientId ] ); const { replaceInnerBlocks } = useDispatch( 'core/block-editor' ); @@ -36,20 +39,31 @@ export default function TemplatePartEdit( { attributes, clientId } ) { }, [ rawTemplatePartContent, replaceInnerBlocks ] ); + useEffect( + () => { + setAttributes( { name: templatePartTitle } ); + }, + [ templatePartTitle ] + ); const newRawTemplatePartContent = useMemo( () => ( serialize( newBlocks ) ), [ newBlocks ] ); const DEBUG = false; const innerBlocks = ( <InnerBlocks templateLock={ false } + renderAppender={ ! hasInnerBlocks && InnerBlocks.ButtonBlockAppender } /> ); - if ( DEBUG ) { - return ( - <div> - <div>{ newRawTemplatePartContent === rawTemplatePartContent ? 'IS NOT DIRTY' : 'IS DIRTY' }</div> - { innerBlocks } - </div> - ); - } - return innerBlocks; + return ( + <div> + <InspectorControls> + <TextControl + label={ __( 'Template part name' ) } + value={ attributes.name } + onChange={ ( value ) => ( setAttributes( { name: value } ) ) } + /> + </InspectorControls> + { DEBUG && ( <div>{ newRawTemplatePartContent === rawTemplatePartContent ? 'IS NOT DIRTY' : 'IS DIRTY' }</div> ) } + { innerBlocks } + </div> + ); } diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 9823d1ad007dd3..cb9a579c5161c5 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -517,16 +517,14 @@ export function* savePost( options = {} ) { for ( const block of allBlocks ) { if ( block.name === 'core/template-part' ) { const { innerBlocks, attributes } = block; - if ( ! attributes.id ) { - continue; - } const templatePartContent = serialize( innerBlocks ); yield apiFetch( { path: `/wp/v2/${ templatePost.post_type }/${ attributes.id }`, - method: 'PUT', + method: attributes.id ? 'PUT' : 'POST', data: { content: templatePartContent, id: attributes.id, + title: attributes.name, }, } ); } From 96a7bff56c14c386464b7d20f00c7fa31077da65 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski <grzegorz@gziolo.pl> Date: Sat, 20 Jul 2019 14:09:51 +0200 Subject: [PATCH 37/70] Add new focus mode for the editor --- .../src/components/inner-blocks/index.js | 3 +- .../src/components/layout/style.scss | 8 +-- packages/editor/src/editor-modes.js | 1 + packages/editor/src/hooks/view-edit-mode.js | 54 ++++++++++++------- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 48dbf227a4290b..787ef42e9a337d 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -120,7 +120,8 @@ class InnerBlocks extends Component { const isPlaceholder = template === null && !! templateOptions; const classes = classnames( 'editor-inner-blocks block-editor-inner-blocks', { - 'has-overlay': hasOverlay && ! isPlaceholder, + // Disable overlay temporarily. + 'has-overlay': false && hasOverlay && ! isPlaceholder, } ); return ( diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index 6eff8839bfbba5..329952d207d795 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -233,12 +233,12 @@ } } -.is-mode-preview .block-editor-block-list__block { +.is-mode-focus .block-editor-block-list__block { opacity: 0.2; } -.is-mode-preview .is-ancestor-of-post-content, -.is-mode-preview [data-type="core/post-content"], -.is-mode-preview [data-type="core/post-content"] .block-editor-block-list__block { +.is-mode-focus .is-ancestor-of-post-content, +.is-mode-focus [data-type="core/post-content"], +.is-mode-focus [data-type="core/post-content"] .block-editor-block-list__block { opacity: 1; } diff --git a/packages/editor/src/editor-modes.js b/packages/editor/src/editor-modes.js index 262a157b50f116..f68b26a684d095 100644 --- a/packages/editor/src/editor-modes.js +++ b/packages/editor/src/editor-modes.js @@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n'; export const modes = [ { value: 'post-content', label: __( 'Writing' ), showTemplate: false }, { value: 'preview', label: __( 'Preview' ), showTemplate: true }, + { value: 'focus', label: __( 'Focus' ), showTemplate: true }, { value: 'full-site', label: __( 'Full Site Editing' ), showTemplate: true }, { value: 'design', label: __( 'Design' ), showTemplate: true }, { value: 'template', label: __( 'Template' ), showTemplate: true }, diff --git a/packages/editor/src/hooks/view-edit-mode.js b/packages/editor/src/hooks/view-edit-mode.js index 3cf79a6226c210..9c9e22ee7e14fc 100644 --- a/packages/editor/src/hooks/view-edit-mode.js +++ b/packages/editor/src/hooks/view-edit-mode.js @@ -21,33 +21,47 @@ export const withViewEditMode = createHigherOrderComponent( ( BlockListBlock ) = }; }, [ clientId ] ); - if ( viewEditingMode !== 'preview' ) { - return <BlockListBlock { ...props } />; - } + if ( viewEditingMode === 'focus' ) { + if ( isAncestorOfPostContent ) { + return ( + <BlockListBlock { ...props } + noInserters={ true } + noMovers={ true } + noToolbars={ true } + /> + ); + } - if ( isAncestorOfPostContent ) { - return <BlockListBlock { ...props } - noInserters={ true } - noMovers={ true } - noToolbars={ true } - />; - } + if ( name === 'core/post-content' ) { + return ( + <BlockListBlock { ...props } + noInserters={ true } + noMovers={ true } + /> + ); + } + + if ( isDescendantOfPostContent ) { + return <BlockListBlock { ...props } />; + } - if ( name === 'core/post-content' ) { - return <BlockListBlock { ...props } - noMovers={ true } - noInserters={ true } - />; + return ( + <Disabled> + <BlockListBlock { ...props } /> + </Disabled> + ); } - if ( isDescendantOfPostContent ) { - return <BlockListBlock { ...props } />; + if ( viewEditingMode === 'preview' ) { + return ( + <Disabled> + <BlockListBlock { ...props } /> + </Disabled> + ); } return ( - <Disabled> - <BlockListBlock { ...props } /> - </Disabled> + <BlockListBlock { ...props } /> ); }; }, 'withViewEditMode' ); From 7d92e52254482d724e2b9ce319b8c622f49a2ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 14:21:52 +0200 Subject: [PATCH 38/70] Update site title font size. --- packages/block-library/src/site-title/edit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/site-title/edit.js b/packages/block-library/src/site-title/edit.js index fbeaae20c41af4..c90bc60bd82ee9 100644 --- a/packages/block-library/src/site-title/edit.js +++ b/packages/block-library/src/site-title/edit.js @@ -65,6 +65,7 @@ function SiteTitleEdit( { } ) } style={ { color: textColor.color, + fontSize: '28px', } } /> </> From 0085e57e23b907504eb0288f4b5ec97d66b7d517 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <miguelcsf@gmail.com> Date: Sat, 20 Jul 2019 14:29:08 +0200 Subject: [PATCH 39/70] Design mode: render as faded-out boxes --- .../src/components/block-list/block.js | 16 ++++++++++++++-- .../src/components/block-list/style.scss | 7 +++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 295ec21ecb82fd..e0a8abee52b19c 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -22,7 +22,7 @@ import { isUnmodifiedDefaultBlock, getUnregisteredTypeHandlerName, } from '@wordpress/blocks'; -import { KeyboardShortcuts, withFilters } from '@wordpress/components'; +import { Disabled, KeyboardShortcuts, withFilters } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { withDispatch, @@ -610,6 +610,9 @@ const applyWithSelect = withSelect( __unstableGetBlockWithoutInnerBlocks, isAncestorOfBlockTypeName, } = select( 'core/block-editor' ); + const { + getViewEditingMode, + } = select( 'core/editor' ); const block = __unstableGetBlockWithoutInnerBlocks( clientId ); const isSelected = isBlockSelected( clientId ); const { hasFixedToolbar, focusMode, isRTL } = getSettings(); @@ -655,6 +658,7 @@ const applyWithSelect = withSelect( isSelected, isParentOfSelectedBlock, isAncestorOfPostContent: isAncestorOfBlockTypeName( clientId, 'core/post-content' ), + viewEditingMode: getViewEditingMode(), }; } ); @@ -746,10 +750,18 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { }; } ); +const applyDisabled = ( WrappedComponent ) => ( props ) => + props.viewEditingMode === 'design' ? + <Disabled><WrappedComponent { ...props } + className={ ( props.className || '' ) + ' is-disabled' } + /></Disabled> : + <WrappedComponent { ...props } />; + export default compose( pure, withViewportMatch( { isLargeViewport: 'medium' } ), applyWithSelect, applyWithDispatch, - withFilters( 'editor.BlockListBlock' ) + withFilters( 'editor.BlockListBlock' ), + applyDisabled, )( BlockListBlock ); diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index d25c7c578d8776..3a00c7217d9442 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -7,6 +7,13 @@ } } +.block-editor-block-list__layout .block-editor-block-list__block.is-disabled { + background-color: rgba(200, 200, 200, 0.5); + > .block-editor-block-list__block-edit { + opacity: 0.5; + } +} + .block-editor-block-list__layout .block-editor-block-list__block.is-selected { // Needs specificity to override inherited styles. // While block is being dragged, dim the slot dragged from, and hide some UI. &.is-dragging { From 9f4bbbd44cfc99de7192120af6db9b2c053f3077 Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Sat, 20 Jul 2019 15:01:58 +0200 Subject: [PATCH 40/70] Add logic to update template part ids saved on the template during the first template part save --- .../block-library/src/template-part/edit.js | 2 +- packages/editor/src/store/actions.js | 55 ++++++++++++------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/packages/block-library/src/template-part/edit.js b/packages/block-library/src/template-part/edit.js index 3d457afdc1034f..32aa895c533b6d 100644 --- a/packages/block-library/src/template-part/edit.js +++ b/packages/block-library/src/template-part/edit.js @@ -22,7 +22,7 @@ export default function TemplatePartEdit( { attributes, clientId, setAttributes templatePartTitle, hasInnerBlocks, } = useSelect( ( select ) => { - const template = select( 'core' ).getEntityRecord( 'postType', 'wp_template', id ); + const template = id && select( 'core' ).getEntityRecord( 'postType', 'wp_template', id ); return { rawTemplatePartContent: get( template, [ 'content', 'raw' ] ), newBlocks: select( 'core/block-editor' ).getBlocks( clientId ), diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index cb9a579c5161c5..4caf8172c96657 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -484,6 +484,40 @@ export function* savePost( options = {} ) { edits = { status: 'draft', ...edits }; } + const allBlocks = yield select( + 'core/block-editor', + 'getBlocksByClientId', + yield select( 'core/block-editor', 'getClientIdsWithDescendants' ) + ); + + const viewEditingMode = yield select( STORE_KEY, 'getViewEditingMode' ); + + if ( getModeConfig( viewEditingMode ).showTemplate ) { + for ( const block of allBlocks ) { + if ( block.name === 'core/template-part' ) { + const { innerBlocks, attributes } = block; + const templatePartContent = serialize( innerBlocks ); + const savedPost = yield apiFetch( { + path: `/wp/v2/wp_template${ attributes.id ? `/${ attributes.id }` : '' }`, + method: attributes.id ? 'PUT' : 'POST', + data: { + content: templatePartContent, + id: attributes.id, + title: attributes.name, + }, + } ); + if ( ! attributes.id ) { + yield dispatch( + 'core/block-editor', + 'updateBlockAttributes', block.clientId, { + id: savedPost.id, + } + ); + } + } + } + } + const post = yield select( STORE_KEY, 'getCurrentPost' @@ -494,12 +528,6 @@ export function* savePost( options = {} ) { 'getEditedPostContent' ); - const viewEditingMode = yield select( STORE_KEY, 'getViewEditingMode' ); - const allBlocks = yield select( - 'core/block-editor', - 'getBlocksByClientId', - yield select( 'core/block-editor', 'getClientIdsWithDescendants' ) - ); if ( getModeConfig( viewEditingMode ).showTemplate ) { const { templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); if ( templatePost ) { @@ -514,21 +542,6 @@ export function* savePost( options = {} ) { const postContentBlock = allBlocks.find( ( block ) => block.name === 'core/post-content' ); editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; } - for ( const block of allBlocks ) { - if ( block.name === 'core/template-part' ) { - const { innerBlocks, attributes } = block; - const templatePartContent = serialize( innerBlocks ); - yield apiFetch( { - path: `/wp/v2/${ templatePost.post_type }/${ attributes.id }`, - method: attributes.id ? 'PUT' : 'POST', - data: { - content: templatePartContent, - id: attributes.id, - title: attributes.name, - }, - } ); - } - } } const shouldSaveSiteOptions = yield select( 'core', 'isSiteOptionsDirty' ); From 40afa03970783c928112bb6e1a0d83ee6f2c584d Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Sat, 20 Jul 2019 15:18:57 +0200 Subject: [PATCH 41/70] Editor: Add template part view editing modes. --- lib/templates.php | 2 +- .../src/template-part/block.json | 4 +- .../src/components/visual-editor/index.js | 6 ++- .../src/components/post-saved-state/index.js | 10 +++- .../editor/src/components/provider/index.js | 6 ++- .../view-editing-mode-picker/index.js | 11 ++-- packages/editor/src/editor-modes.js | 7 +-- packages/editor/src/store/actions.js | 52 +++++++++++++------ packages/editor/src/utils/blocks.js | 12 +++++ packages/editor/src/utils/index.js | 2 +- 10 files changed, 76 insertions(+), 36 deletions(-) diff --git a/lib/templates.php b/lib/templates.php index ee1b8735fdc9bc..eb1a919c334cf7 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -85,7 +85,7 @@ function gutenberg_register_templates() { <!-- /wp:group --></div> <!-- /wp:column --></div> <!-- /wp:columns --> - <!-- wp:template-part {"id":' . $footer_template_part_id . '} /-->', + <!-- wp:template-part {"id":' . $footer_template_part_id . ',"name":"Footer"} /-->', ) ); $template = get_post( $template_id ); diff --git a/packages/block-library/src/template-part/block.json b/packages/block-library/src/template-part/block.json index 1679f28071eb88..24899934755ee9 100644 --- a/packages/block-library/src/template-part/block.json +++ b/packages/block-library/src/template-part/block.json @@ -6,9 +6,7 @@ "type": "number" }, "name": { - "type": "string", - "source": "html", - "selector": "none" + "type": "string" } } } diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index ec265d752b5e3c..a0d367f787c3f1 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -50,8 +50,10 @@ function VisualEditor( { hasPostTitle } ) { } export default withSelect( ( select ) => { - const viewEditingMode = select( 'core/editor' ).getViewEditingMode(); + const { getViewEditingMode, getEditorSettings } = select( 'core/editor' ); + const viewEditingMode = getViewEditingMode(); + const templateParts = getEditorSettings().templateParts; return { - hasPostTitle: ! getModeConfig( viewEditingMode ).showTemplate, + hasPostTitle: ! getModeConfig( viewEditingMode, templateParts ).showTemplate, }; } )( VisualEditor ); diff --git a/packages/editor/src/components/post-saved-state/index.js b/packages/editor/src/components/post-saved-state/index.js index 73aaa39cd6960b..bb93f8217cb249 100644 --- a/packages/editor/src/components/post-saved-state/index.js +++ b/packages/editor/src/components/post-saved-state/index.js @@ -61,6 +61,7 @@ export class PostSavedState extends Component { isPending, isLargeViewport, viewEditingMode, + templateParts, } = this.props; const { forceSavedMessage } = this.state; if ( isSaving ) { @@ -104,8 +105,11 @@ export class PostSavedState extends Component { } let label = isPending ? __( 'Save Draft as Pending' ) : __( 'Save Draft' ); - if ( getModeConfig( viewEditingMode ).showTemplate ) { - label += ' & Template'; + const viewEditingModeObject = getModeConfig( viewEditingMode, templateParts ); + if ( viewEditingModeObject.showTemplate ) { + label = viewEditingModeObject.id ? + `Save ${ viewEditingModeObject.label }` : + `${ label } & Template`; } if ( ! isLargeViewport ) { return ( @@ -145,6 +149,7 @@ export default compose( [ isAutosavingPost, getEditedPostAttribute, getViewEditingMode, + getEditorSettings, } = select( 'core/editor' ); return { post: getCurrentPost(), @@ -157,6 +162,7 @@ export default compose( [ isAutosaving: isAutosavingPost(), isPending: 'pending' === getEditedPostAttribute( 'status' ), viewEditingMode: getViewEditingMode(), + templateParts: getEditorSettings().templateParts, }; } ), withDispatch( ( dispatch ) => ( { diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 798b8e317cce09..2a567090e6c89a 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -54,12 +54,14 @@ class EditorProvider extends Component { return; } + const viewEditingMode = getModeConfig( props.viewEditingMode ); + props.updateViewEditingMode( viewEditingMode.value ); props.updatePostLock( props.settings.postLock ); props.setupEditor( props.post, props.initialEdits, props.settings.template, - getModeConfig( props.viewEditingMode ).showTemplate && props.settings.templatePost + viewEditingMode.showTemplate && props.settings.templatePost ); if ( props.settings.autosave ) { @@ -204,6 +206,7 @@ export default compose( [ } ), withDispatch( ( dispatch ) => { const { + updateViewEditingMode, setupEditor, updatePostLock, resetEditorBlocks, @@ -213,6 +216,7 @@ export default compose( [ const { createWarningNotice } = dispatch( 'core/notices' ); return { + updateViewEditingMode, setupEditor, updatePostLock, createWarningNotice, diff --git a/packages/editor/src/components/view-editing-mode-picker/index.js b/packages/editor/src/components/view-editing-mode-picker/index.js index a31b3aa784e103..b380c200f4a1a9 100644 --- a/packages/editor/src/components/view-editing-mode-picker/index.js +++ b/packages/editor/src/components/view-editing-mode-picker/index.js @@ -3,7 +3,7 @@ */ import { __ } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useCallback } from '@wordpress/element'; +import { useCallback, useMemo } from '@wordpress/element'; import { SelectControl } from '@wordpress/components'; /** @@ -30,8 +30,8 @@ export default function ViewEditingModePicker() { const { setupEditor, updateViewEditingMode } = useDispatch( 'core/editor' ); const updateViewEditingModeCallback = useCallback( ( newViewEditingMode ) => { - const currentModeConfig = getModeConfig( viewEditingMode ); - const newModeConfig = getModeConfig( newViewEditingMode ); + const currentModeConfig = getModeConfig( viewEditingMode, settings.templateParts ); + const newModeConfig = getModeConfig( newViewEditingMode, settings.templateParts ); updateViewEditingMode( newViewEditingMode ); let newBlocks = blocks; @@ -46,7 +46,8 @@ export default function ViewEditingModePicker() { blocks: newBlocks, }, settings.template, - newModeConfig.showTemplate && settings.templatePost + newModeConfig.showTemplate && settings.templatePost, + newModeConfig.id ); }, [ post, blocks, settings.template, settings.templatePost, viewEditingMode ] ); @@ -55,7 +56,7 @@ export default function ViewEditingModePicker() { className="editor-view-editing-mode-picker" label={ __( 'View Editing Mode' ) } hideLabelFromVision - options={ modes } + options={ useMemo( () => [ ...modes, ...( settings.templateParts || [] ) ], [ settings.templateParts ] ) } value={ viewEditingMode } onChange={ updateViewEditingModeCallback } /> diff --git a/packages/editor/src/editor-modes.js b/packages/editor/src/editor-modes.js index f68b26a684d095..40fc429f5f6e3e 100644 --- a/packages/editor/src/editor-modes.js +++ b/packages/editor/src/editor-modes.js @@ -16,11 +16,8 @@ export const modes = [ { value: 'design', label: __( 'Design' ), showTemplate: true }, { value: 'template', label: __( 'Template' ), showTemplate: true }, { value: '---', label: __( '---' ), showTemplate: true }, - { value: 'header', label: __( 'Header' ), showTemplate: true }, - { value: 'sidebar', label: __( 'Sidebar' ), showTemplate: true }, - { value: 'footer', label: __( 'Footer' ), showTemplate: true }, ]; -export const getModeConfig = ( modeId ) => { - return find( modes, ( mode ) => mode.value === modeId ); +export const getModeConfig = ( modeId, dynamicModes = [] ) => { + return find( [ ...modes, ...dynamicModes ], ( mode ) => mode.value === modeId ) || modes[ 0 ]; }; diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 4caf8172c96657..47aea076dcfe5a 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -37,7 +37,7 @@ import { import { awaitNextStateChange, getRegistry } from './controls'; import * as sources from './block-sources'; import { getModeConfig } from '../editor-modes'; -import { findDeepBlock } from '../utils'; +import { findDeepBlock, findDeepBlocks } from '../utils'; /** * Map of Registry instance to WeakMap of dependencies by custom source. @@ -159,8 +159,9 @@ function* resetLastBlockSourceDependencies( sourcesToUpdate = Object.values( sou * @param {Object} edits Initial edited attributes object. * @param {Array?} template Block Template. * @param {Object?} templatePost Post's template post object. + * @param {string?} templatePartId */ -export function* setupEditor( post, edits, template, templatePost ) { +export function* setupEditor( post, edits, template, templatePost, templatePartId ) { // In order to ensure maximum of a single parse during setup, edits are // included as part of editor setup action. Assume edited content as // canonical if provided, falling back to post. @@ -178,6 +179,11 @@ export function* setupEditor( post, edits, template, templatePost ) { const postContentInnerBlocks = blocks; blocks = parse( templatePost.post_content ); + if ( templatePartId ) { + const templatePartBlocks = findDeepBlocks( blocks, 'core/template-part' ); + blocks = [ templatePartBlocks.find( ( block ) => block.attributes.id === templatePartId ) ]; + } + // Post content is nested inside a post content block. const postContentBlock = findDeepBlock( blocks, 'core/post-content' ); if ( postContentBlock ) { @@ -528,20 +534,19 @@ export function* savePost( options = {} ) { 'getEditedPostContent' ); - if ( getModeConfig( viewEditingMode ).showTemplate ) { - const { templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); - if ( templatePost ) { - yield apiFetch( { - path: `/wp/v2/${ templatePost.post_type }/${ templatePost.ID }`, - method: 'PUT', - data: { - content: editedPostContent, - id: templatePost.ID, - }, - } ); - const postContentBlock = allBlocks.find( ( block ) => block.name === 'core/post-content' ); - editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; - } + const { templateParts, templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); + const viewEditingModeObject = getModeConfig( viewEditingMode, templateParts ); + if ( viewEditingModeObject.showTemplate && ! viewEditingModeObject.id && templatePost ) { + yield apiFetch( { + path: `/wp/v2/${ templatePost.post_type }/${ templatePost.ID }`, + method: 'PUT', + data: { + content: editedPostContent, + id: templatePost.ID, + }, + } ); + const postContentBlock = allBlocks.find( ( block ) => block.name === 'core/post-content' ); + editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; } const shouldSaveSiteOptions = yield select( 'core', 'isSiteOptionsDirty' ); @@ -1044,6 +1049,21 @@ export function* resetEditorBlocks( blocks, options = {} ) { * @return {Object} Action object */ export function updateEditorSettings( settings ) { + if ( settings.templatePost ) { + const templatePartBlocks = findDeepBlocks( + parse( settings.templatePost.post_content ), + 'core/template-part' + ); + settings = { + ...settings, + templateParts: templatePartBlocks.map( ( block ) => ( { + value: block.attributes.name.toLowerCase(), + label: block.attributes.name, + showTemplate: true, + id: block.attributes.id, + } ) ), + }; + } return { type: 'UPDATE_EDITOR_SETTINGS', settings, diff --git a/packages/editor/src/utils/blocks.js b/packages/editor/src/utils/blocks.js index 02ae7c0b4d9dca..c2fd51715dec73 100644 --- a/packages/editor/src/utils/blocks.js +++ b/packages/editor/src/utils/blocks.js @@ -13,3 +13,15 @@ export const findDeepBlock = ( blocksToSearch, blockName ) => { } } }; + +export const findDeepBlocks = ( blocksToSearch, blockName, foundBlocks = [] ) => { + foundBlocks.push( ...blocksToSearch.filter( ( block ) => block.name === blockName ) ); + + for ( const block of blocksToSearch ) { + if ( block.innerBlocks ) { + foundBlocks.push( ...findDeepBlocks( block.innerBlocks, blockName ) ); + } + } + + return foundBlocks; +}; diff --git a/packages/editor/src/utils/index.js b/packages/editor/src/utils/index.js index cf64dbb0bd8885..734929be6cdfad 100644 --- a/packages/editor/src/utils/index.js +++ b/packages/editor/src/utils/index.js @@ -5,4 +5,4 @@ import mediaUpload from './media-upload'; export { mediaUpload }; export { cleanForSlug } from './url.js'; -export { findDeepBlock } from './blocks'; +export { findDeepBlock, findDeepBlocks } from './blocks'; From df268838f4ad942483a0dd55dcd9f26355eee021 Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Sat, 20 Jul 2019 15:22:35 +0200 Subject: [PATCH 42/70] Use saveEntityRecord to save template parts --- packages/editor/src/store/actions.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 47aea076dcfe5a..9b0dc5c48232d5 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -503,15 +503,16 @@ export function* savePost( options = {} ) { if ( block.name === 'core/template-part' ) { const { innerBlocks, attributes } = block; const templatePartContent = serialize( innerBlocks ); - const savedPost = yield apiFetch( { - path: `/wp/v2/wp_template${ attributes.id ? `/${ attributes.id }` : '' }`, - method: attributes.id ? 'PUT' : 'POST', - data: { + const savedPost = yield dispatch( + 'core', + 'saveEntityRecord', + 'postType', + 'wp_template', { content: templatePartContent, id: attributes.id, title: attributes.name, - }, - } ); + } + ); if ( ! attributes.id ) { yield dispatch( 'core/block-editor', From 2613a46d677089b7974b0ac3268264481c11b39a Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Sat, 20 Jul 2019 15:45:34 +0200 Subject: [PATCH 43/70] Better template part mode. --- .../block-library/src/template-part/block.json | 3 +++ .../edit-post/src/components/layout/style.scss | 8 ++++++++ .../src/components/visual-editor/index.js | 17 +++++++++++++---- packages/editor/src/store/actions.js | 2 ++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/block-library/src/template-part/block.json b/packages/block-library/src/template-part/block.json index 24899934755ee9..449ac670ebfbab 100644 --- a/packages/block-library/src/template-part/block.json +++ b/packages/block-library/src/template-part/block.json @@ -8,5 +8,8 @@ "name": { "type": "string" } + }, + "supports": { + "align": ["full"] } } diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index 329952d207d795..bcc412dc7e3bb7 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -233,6 +233,14 @@ } } +.is-mode-template-part { + background: $light-gray-500; + + & > div > div:first-child { + background: $white; + } +} + .is-mode-focus .block-editor-block-list__block { opacity: 0.2; } diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index a0d367f787c3f1..43bc5f59b3202a 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -26,15 +31,19 @@ import { import BlockInspectorButton from './block-inspector-button'; import PluginBlockSettingsMenuGroup from '../block-settings-menu/plugin-block-settings-menu-group'; -function VisualEditor( { hasPostTitle } ) { +function VisualEditor( { viewEditingMode } ) { return ( - <BlockSelectionClearer className="edit-post-visual-editor editor-styles-wrapper"> + <BlockSelectionClearer + className={ classnames( 'edit-post-visual-editor editor-styles-wrapper', { + 'is-mode-template-part': Boolean( viewEditingMode.id ), + } ) } + > <VisualEditorGlobalKeyboardShortcuts /> <MultiSelectScrollIntoView /> <WritingFlow> <ObserveTyping> <CopyHandler> - { hasPostTitle && <PostTitle /> } + { viewEditingMode.showTemplate && <PostTitle /> } <BlockList /> </CopyHandler> </ObserveTyping> @@ -54,6 +63,6 @@ export default withSelect( ( select ) => { const viewEditingMode = getViewEditingMode(); const templateParts = getEditorSettings().templateParts; return { - hasPostTitle: ! getModeConfig( viewEditingMode, templateParts ).showTemplate, + viewEditingMode: getModeConfig( viewEditingMode, templateParts ), }; } )( VisualEditor ); diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 9b0dc5c48232d5..758576377e244b 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -548,6 +548,8 @@ export function* savePost( options = {} ) { } ); const postContentBlock = allBlocks.find( ( block ) => block.name === 'core/post-content' ); editedPostContent = postContentBlock ? serialize( postContentBlock.innerBlocks ) : ''; + } else if ( viewEditingModeObject.id ) { + editedPostContent = post.content; } const shouldSaveSiteOptions = yield select( 'core', 'isSiteOptionsDirty' ); From 39aeaf87749afb08daabc0b5438260f38dd9245d Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Sat, 20 Jul 2019 15:47:45 +0200 Subject: [PATCH 44/70] Fix template part saving. --- packages/editor/src/store/actions.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 758576377e244b..872ff6fbf2c51d 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -497,8 +497,9 @@ export function* savePost( options = {} ) { ); const viewEditingMode = yield select( STORE_KEY, 'getViewEditingMode' ); - - if ( getModeConfig( viewEditingMode ).showTemplate ) { + const { templateParts, templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); + const viewEditingModeObject = getModeConfig( viewEditingMode, templateParts ); + if ( viewEditingModeObject.showTemplate ) { for ( const block of allBlocks ) { if ( block.name === 'core/template-part' ) { const { innerBlocks, attributes } = block; @@ -535,8 +536,6 @@ export function* savePost( options = {} ) { 'getEditedPostContent' ); - const { templateParts, templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); - const viewEditingModeObject = getModeConfig( viewEditingMode, templateParts ); if ( viewEditingModeObject.showTemplate && ! viewEditingModeObject.id && templatePost ) { yield apiFetch( { path: `/wp/v2/${ templatePost.post_type }/${ templatePost.ID }`, From ce01735d8237f8d488d0a42a7fb4d1d7c5c7b33c Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Sat, 20 Jul 2019 15:49:23 +0200 Subject: [PATCH 45/70] Fix show post title. --- packages/edit-post/src/components/visual-editor/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 43bc5f59b3202a..bfa183488ba9f4 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -43,7 +43,7 @@ function VisualEditor( { viewEditingMode } ) { <WritingFlow> <ObserveTyping> <CopyHandler> - { viewEditingMode.showTemplate && <PostTitle /> } + { ! viewEditingMode.showTemplate && <PostTitle /> } <BlockList /> </CopyHandler> </ObserveTyping> From 74edaa582b20123e2c126d02c980990065952662 Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Sat, 20 Jul 2019 16:11:59 +0200 Subject: [PATCH 46/70] Fixed templatePart hasInnerBlocks definition --- packages/block-library/src/template-part/edit.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/template-part/edit.js b/packages/block-library/src/template-part/edit.js index 32aa895c533b6d..f190697b7fdd8b 100644 --- a/packages/block-library/src/template-part/edit.js +++ b/packages/block-library/src/template-part/edit.js @@ -23,10 +23,12 @@ export default function TemplatePartEdit( { attributes, clientId, setAttributes hasInnerBlocks, } = useSelect( ( select ) => { const template = id && select( 'core' ).getEntityRecord( 'postType', 'wp_template', id ); + const blocks = select( 'core/block-editor' ).getBlocks( clientId ); return { rawTemplatePartContent: get( template, [ 'content', 'raw' ] ), - newBlocks: select( 'core/block-editor' ).getBlocks( clientId ), + newBlocks: blocks, templatePartTitle: get( template, [ 'title', 'raw' ] ), + hasInnerBlocks: blocks && blocks.length > 0, }; }, [ id, clientId ] ); const { replaceInnerBlocks } = useDispatch( 'core/block-editor' ); From 7c6297d3071f078f3ecc1a7330c4be9de01ed57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 16:38:11 +0200 Subject: [PATCH 47/70] Add emoji to modes. --- packages/editor/src/editor-modes.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/editor/src/editor-modes.js b/packages/editor/src/editor-modes.js index 40fc429f5f6e3e..db902a10da7001 100644 --- a/packages/editor/src/editor-modes.js +++ b/packages/editor/src/editor-modes.js @@ -9,12 +9,12 @@ import { find } from 'lodash'; import { __ } from '@wordpress/i18n'; export const modes = [ - { value: 'post-content', label: __( 'Writing' ), showTemplate: false }, - { value: 'preview', label: __( 'Preview' ), showTemplate: true }, - { value: 'focus', label: __( 'Focus' ), showTemplate: true }, - { value: 'full-site', label: __( 'Full Site Editing' ), showTemplate: true }, - { value: 'design', label: __( 'Design' ), showTemplate: true }, - { value: 'template', label: __( 'Template' ), showTemplate: true }, + { value: 'post-content', label: __( '📝 Writing' ), showTemplate: false }, + { value: 'full-site', label: __( '🦋 Full Site Editing' ), showTemplate: true }, + { value: 'preview', label: __( '🍿 Preview' ), showTemplate: true }, + { value: 'focus', label: __( '👁 Focus' ), showTemplate: true }, + { value: 'design', label: __( '🎨 Design' ), showTemplate: true }, + { value: 'template', label: __( '📦 Template' ), showTemplate: true }, { value: '---', label: __( '---' ), showTemplate: true }, ]; From d89dd64f7f6facfea1766d10bac1e7ed8707432b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 16:40:01 +0200 Subject: [PATCH 48/70] More design mode CSS. --- .../block-editor/src/components/block-list/style.scss | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index 3a00c7217d9442..4eaa1392444b05 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -8,9 +8,16 @@ } .block-editor-block-list__layout .block-editor-block-list__block.is-disabled { - background-color: rgba(200, 200, 200, 0.5); + background-color: rgba(200, 200, 200, 0.2); > .block-editor-block-list__block-edit { - opacity: 0.5; + // opacity: 0.5; + } + // &::before { + // content: 'h'; + // } + + .editor-rich-text { + background: #ccc !important; } } From e0f8fcf59ad462ec78ff9e24d4046b12648f05f7 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <miguelcsf@gmail.com> Date: Sat, 20 Jul 2019 16:46:58 +0200 Subject: [PATCH 49/70] Design mode: add mock columns gradient --- .../src/components/block-list/style.scss | 1 + .../src/components/layout/style.scss | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index 4eaa1392444b05..fb6db319118cc9 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -7,6 +7,7 @@ } } +// TODO: move to edit-post//components/layout along with the other modes .block-editor-block-list__layout .block-editor-block-list__block.is-disabled { background-color: rgba(200, 200, 200, 0.2); > .block-editor-block-list__block-edit { diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index bcc412dc7e3bb7..a8a5f87a661af0 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -233,6 +233,29 @@ } } +/* Settings */ +:root { + --offset: 2rem; + --max_width: 72rem; + --columns: 5; + --gutter: 1rem; + --color: hsla(204, 80%, 72%, 0.15); +} + +/* Helper variables */ +:root { + --repeating-width: calc(100% / var(--columns)); + --column-width: calc((100% / var(--columns)) - var(--gutter)); + --background-width: calc(100% + var(--gutter)); + --background-columns: repeating-linear-gradient(to right, var(--color), var(--color) var(--column-width), transparent var(--column-width), transparent var(--repeating-width)); +} + +.edit-post-layout.is-mode-design { + background-image: var(--background-columns); + background-size: var(--background-width) 100%; + z-index: 1000; +} + .is-mode-template-part { background: $light-gray-500; From d14fab6494e3093ad3d038527a4f78ccf02efdf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ventura?= <mv@matiasventura.com> Date: Sat, 20 Jul 2019 18:13:06 +0200 Subject: [PATCH 50/70] Constrain frame editor of template parts. --- .../src/components/layout/style.scss | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index a8a5f87a661af0..9800ca3d06af57 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -257,13 +257,30 @@ } .is-mode-template-part { - background: $light-gray-500; + background: $light-gray-300; & > div > div:first-child { background: $white; + border: 1px solid $light-gray-500; + margin: 0 200px; } } +.is-mode-sidebar { + .is-mode-template-part > div > div:first-child { + width: 500px; + margin: 0 auto 100px; + } +} + +.is-mode-footer { + .is-mode-template-part > div > div:first-child { + width: 1300px; + margin: 0 auto 100px; + } +} + + .is-mode-focus .block-editor-block-list__block { opacity: 0.2; } From 018924fc92a641387c568f56d8719e4eca579142 Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Sat, 20 Jul 2019 18:39:00 +0200 Subject: [PATCH 51/70] Add template part preloading mechanism --- lib/templates.php | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lib/templates.php b/lib/templates.php index eb1a919c334cf7..fcf134ae83aa23 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -124,6 +124,54 @@ function gutenberg_filter_block_editor_settings( $editor_settings, $post ) { } add_filter( 'block_editor_settings', 'gutenberg_filter_block_editor_settings', 10, 2 ); +/** + * Returns an array of paths to template parts that should be preloaded, based on what template part blocks exist in the blocks passed. + * + * @param array $blocks Array of block objects. + * + * @return array Array of paths to template parts posts. + */ +function get_template_part_preload_paths( $blocks ) { + if ( empty( $blocks ) ) { + return array(); + } + $parts = array(); + foreach ( $blocks as $block ) { + if ( 'core/template-part' === $block['blockName'] && + isset( $block['attrs']['id'] ) + ) { + $parts[] = sprintf( '/wp/v2/wp_template/%s?context=edit', $block['attrs']['id'] ); + } + $parts = array_merge( + $parts, + get_template_part_preload_paths( $block['innerBlocks'] ) + ); + } + return $parts; +} + +/** + * Filter the preload_paths to include paths to template part posts referenced in the current post template. + * + * @param array $preload_paths Array of paths to preload. + * + * @return array Array of paths to preload with template part paths added. + */ +function gutenberg_preload_template_parts( $preload_paths ) { + global $post; + $post_type_object = get_post_type_object( get_post_type( $post ) ); + if ( ! empty( $post_type_object->template_post ) ) { + $blocks = parse_blocks( $post_type_object->template_post->post_content ); + return array_merge( + $preload_paths, + get_template_part_preload_paths( $blocks ) + ); + } + return $preload_paths; +} + +add_filter( 'block_editor_preload_paths', 'gutenberg_preload_template_parts' ); + /** * Filters template inclusion in pages to hijack the `single.php` template * and load the Gutenberg editable counterpart instead. From 1a77aa0f31469e73a8c8e595fd744782b9eadcb2 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <miguelcsf@gmail.com> Date: Sat, 20 Jul 2019 19:22:25 +0200 Subject: [PATCH 52/70] Site Title: Ascribe `theme` category --- packages/block-library/src/site-title/block.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/site-title/block.json b/packages/block-library/src/site-title/block.json index fc02f8eea3dcd2..910f9ac78634bb 100644 --- a/packages/block-library/src/site-title/block.json +++ b/packages/block-library/src/site-title/block.json @@ -1,6 +1,6 @@ { "name": "core/site-title", - "category": "layout", + "category": "theme", "attributes": { "title": { "type": "string", From 19adb5ca09fbb109a98b2ed7502fd8d008448cd4 Mon Sep 17 00:00:00 2001 From: iseulde <wp@iseulde.com> Date: Mon, 22 Jul 2019 11:03:13 +0200 Subject: [PATCH 53/70] Template Locking: readonly / disable editing attributes (without rewrite) --- .../developers/block-api/block-templates.md | 3 +- .../src/components/block-drop-zone/index.js | 4 +- .../src/components/block-inspector/index.js | 16 ++++++- .../src/components/block-list/block.js | 14 ++++-- .../src/components/block-mover/index.js | 3 +- .../src/components/inner-blocks/README.md | 11 ++--- .../src/components/media-placeholder/index.js | 2 +- .../src/components/placeholder/index.js | 34 +++++++++++++ .../src/components/plain-text/index.js | 30 ++++++++++-- .../src/components/rich-text/index.js | 6 +++ packages/block-library/src/classic/edit.js | 7 +-- packages/e2e-tests/plugins/cpt-locking.php | 10 ++++ .../__snapshots__/cpt-locking.test.js.snap | 28 +++++++++++ .../specs/plugins/cpt-locking.test.js | 32 +++++++++++++ packages/rich-text/src/component/index.js | 48 ++++++++++++++----- packages/rich-text/src/component/read-only.js | 15 ++++++ 16 files changed, 230 insertions(+), 33 deletions(-) create mode 100644 packages/block-editor/src/components/placeholder/index.js create mode 100644 packages/rich-text/src/component/read-only.js diff --git a/docs/designers-developers/developers/block-api/block-templates.md b/docs/designers-developers/developers/block-api/block-templates.md index b2b384a96e211f..a2610c053c897d 100644 --- a/docs/designers-developers/developers/block-api/block-templates.md +++ b/docs/designers-developers/developers/block-api/block-templates.md @@ -107,7 +107,8 @@ add_action( 'init', 'myplugin_register_template' ); *Options:* -- `all` — prevents all operations. It is not possible to insert new blocks, move existing blocks, or delete blocks. +- `readonly` — prevents all operations. It is not possible to edit any blocks, insert new blocks, move or delete existing blocks. +- `all` — prevents all block operations. It is not possible to insert new blocks, move or delete existing blocks. - `insert` — prevents inserting or removing blocks, but allows moving existing blocks. ## Nested Templates diff --git a/packages/block-editor/src/components/block-drop-zone/index.js b/packages/block-editor/src/components/block-drop-zone/index.js index e0a0d1060ce4fe..0cce1fbb86564b 100644 --- a/packages/block-editor/src/components/block-drop-zone/index.js +++ b/packages/block-editor/src/components/block-drop-zone/index.js @@ -157,8 +157,10 @@ export default compose( } ), withSelect( ( select, { rootClientId } ) => { const { getClientIdsOfDescendants, getTemplateLock, getBlockIndex } = select( 'core/block-editor' ); + const templateLock = getTemplateLock( rootClientId ); + return { - isLockedAll: getTemplateLock( rootClientId ) === 'all', + isLockedAll: templateLock === 'all' || templateLock === 'readonly', getClientIdsOfDescendants, getBlockIndex, }; diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index e04dbcbd932be1..355697a5fcfb42 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -27,7 +27,12 @@ const BlockInspector = ( { selectedBlockClientId, selectedBlockName, showNoBlockSelectedMessage = true, + isReadOnly, } ) => { + if ( isReadOnly ) { + return __( 'The selected block is read-only.' ); + } + if ( count > 1 ) { return <MultiSelectionInspector />; } @@ -91,18 +96,27 @@ const BlockInspector = ( { export default withSelect( ( select ) => { - const { getSelectedBlockClientId, getSelectedBlockCount, getBlockName } = select( 'core/block-editor' ); + const { + getSelectedBlockClientId, + getSelectedBlockCount, + getBlockName, + getTemplateLock, + getBlockRootClientId, + } = select( 'core/block-editor' ); const { getBlockStyles } = select( 'core/blocks' ); const selectedBlockClientId = getSelectedBlockClientId(); const selectedBlockName = selectedBlockClientId && getBlockName( selectedBlockClientId ); const blockType = selectedBlockClientId && getBlockType( selectedBlockName ); const blockStyles = selectedBlockClientId && getBlockStyles( selectedBlockName ); + const rootClientId = getBlockRootClientId( selectedBlockClientId ); + const isReadOnly = getTemplateLock( rootClientId ) === 'readonly'; return { count: getSelectedBlockCount(), hasBlockStyles: blockStyles && blockStyles.length > 0, selectedBlockName, selectedBlockClientId, blockType, + isReadOnly, }; } )( BlockInspector ); diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index e0a8abee52b19c..49790435a3816b 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -69,6 +69,7 @@ function BlockListBlock( { isFocusMode, hasFixedToolbar, isLocked, + isReadOnly, clientId, rootClientId, isSelected, @@ -444,6 +445,7 @@ function BlockListBlock( { insertBlocksAfter={ isLocked ? undefined : onInsertBlocksAfter } onReplace={ isLocked ? undefined : onReplace } mergeBlocks={ isLocked ? undefined : onMerge } + isReadOnly={ isReadOnly } clientId={ clientId } isSelectionEnabled={ isSelectionEnabled } toggleSelection={ toggleSelection } @@ -518,7 +520,7 @@ function BlockListBlock( { } /> ) } - { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ( + { ( shouldShowContextualToolbar || isForcingContextualToolbar.current ) && ! isReadOnly && ( <BlockContextualToolbar // If the toolbar is being shown because of being forced // it should focus the toolbar right after the mount. @@ -640,8 +642,9 @@ const applyWithSelect = withSelect( initialPosition: isSelected ? getSelectedBlocksInitialCaretPosition() : null, isEmptyDefaultBlock: name && isUnmodifiedDefaultBlock( { name, attributes } ), - isMovable: 'all' !== templateLock, + isMovable: templateLock !== 'all' && templateLock !== 'readonly', isLocked: !! templateLock, + isReadOnly: templateLock === 'readonly', isFocusMode: focusMode && isLargeViewport, hasFixedToolbar: hasFixedToolbar && isLargeViewport, isLast: index === blockOrder.length - 1, @@ -678,7 +681,12 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { return { setAttributes( newAttributes ) { - const { clientId } = ownProps; + const { clientId, isReadOnly } = ownProps; + + if ( isReadOnly ) { + return; + } + updateBlockAttributes( clientId, newAttributes ); }, onSelect( clientId = ownProps.clientId, initialPosition ) { diff --git a/packages/block-editor/src/components/block-mover/index.js b/packages/block-editor/src/components/block-mover/index.js index dd09dc1c86b7c5..0842d93beed283 100644 --- a/packages/block-editor/src/components/block-mover/index.js +++ b/packages/block-editor/src/components/block-mover/index.js @@ -125,10 +125,11 @@ export default compose( const blockOrder = getBlockOrder( rootClientId ); const firstIndex = getBlockIndex( firstClientId, rootClientId ); const lastIndex = getBlockIndex( last( normalizedClientIds ), rootClientId ); + const templateLock = getTemplateLock( rootClientId ); return { blockType: block ? getBlockType( block.name ) : null, - isLocked: getTemplateLock( rootClientId ) === 'all', + isLocked: templateLock === 'all' || templateLock === 'readonly', rootClientId, firstIndex, isFirst: firstIndex === 0, diff --git a/packages/block-editor/src/components/inner-blocks/README.md b/packages/block-editor/src/components/inner-blocks/README.md index 303e24730f7fe3..8dd9e8725e5aea 100644 --- a/packages/block-editor/src/components/inner-blocks/README.md +++ b/packages/block-editor/src/components/inner-blocks/README.md @@ -98,7 +98,7 @@ To present the user with a set of template choices for the inner blocks, you may A template option is an object consisting of the following properties: -- `title` (`string`): A human-readable label which describes the template. +- `title` (`string`): A human-readable label which describes the template. - `icon` (`WPElement|string`): An element or [Dashicon](https://developer.wordpress.org/resource/dashicons/) slug to show as a visual approximation of the template. - `template` (`Array<Array>`): The template to apply when the option has been selected. See [`template` documentation](#template) for more information. @@ -168,7 +168,8 @@ Template locking of `InnerBlocks` is similar to [Custom Post Type templates lock Template locking allows locking the `InnerBlocks` area for the current template. *Options:* -- `'all'` — prevents all operations. It is not possible to insert new blocks. Move existing blocks or delete them. +- `'readonly'` — prevents all operations. It is not possible to edit any blocks, insert new blocks, or move or delete existing blocks. +- `'all'` — prevents all block operations. It is not possible to insert new blocks. Move existing blocks or delete them. - `'insert'` — prevents inserting or removing blocks, but allows moving existing ones. - `false` — prevents locking from being applied to an `InnerBlocks` area even if a parent block contains locking. ( Boolean ) @@ -184,7 +185,7 @@ A 'render prop' function that can be used to customize the block's appender. #### Notes * For convenience two predefined appender components are exposed on `InnerBlocks` which can be consumed within the render function: - - `<InnerBlocks.ButtonBlockAppender />` - display a `+` (plus) icon button that, when clicked, displays the block picker menu. No default Block is inserted. + - `<InnerBlocks.ButtonBlockAppender />` - display a `+` (plus) icon button that, when clicked, displays the block picker menu. No default Block is inserted. - `<InnerBlocks.DefaultBlockAppender />` - display the default block appender as set by `wp.blocks.setDefaultBlockName`. Typically this is the `paragraph` block. * Consumers are also free to pass any valid render function. This provides the full flexibility to define a bespoke block appender. @@ -205,7 +206,3 @@ A 'render prop' function that can be used to customize the block's appender. ) } /> ``` - - - - diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js index efad860f586e7a..8bc77352aa34c8 100644 --- a/packages/block-editor/src/components/media-placeholder/index.js +++ b/packages/block-editor/src/components/media-placeholder/index.js @@ -16,7 +16,6 @@ import classnames from 'classnames'; import { Button, FormFileUpload, - Placeholder, DropZone, IconButton, withFilters, @@ -32,6 +31,7 @@ import { withSelect } from '@wordpress/data'; import MediaUpload from '../media-upload'; import MediaUploadCheck from '../media-upload/check'; import URLPopover from '../url-popover'; +import Placeholder from '../placeholder'; const InsertFromURLPopover = ( { src, onChange, onSubmit, onClose } ) => ( <URLPopover onClose={ onClose }> diff --git a/packages/block-editor/src/components/placeholder/index.js b/packages/block-editor/src/components/placeholder/index.js new file mode 100644 index 00000000000000..7a4a5c41aa049c --- /dev/null +++ b/packages/block-editor/src/components/placeholder/index.js @@ -0,0 +1,34 @@ +/** + * WordPress dependencies + */ +import { Placeholder } from '@wordpress/components'; +import { withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { withBlockEditContext } from '../block-edit/context'; + +function PlaceholderInBlockContext( { isReadOnly, ...props } ) { + if ( ! isReadOnly ) { + return null; + } + + delete props.clientId; + + return <Placeholder { ...props } />; +} + +export default compose( [ + withBlockEditContext( ( { clientId } ) => ( { clientId } ) ), + withSelect( ( select, { clientId } ) => { + const { + getBlockRootClientId, + getTemplateLock, + } = select( 'core/block-editor' ); + const rootClientId = getBlockRootClientId( clientId ); + const isReadOnly = getTemplateLock( rootClientId ) === 'readonly'; + return { isReadOnly }; + } ), +] )( PlaceholderInBlockContext ); diff --git a/packages/block-editor/src/components/plain-text/index.js b/packages/block-editor/src/components/plain-text/index.js index b05e48ed60b61c..d18acbbc51c48c 100644 --- a/packages/block-editor/src/components/plain-text/index.js +++ b/packages/block-editor/src/components/plain-text/index.js @@ -1,8 +1,9 @@ - /** * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; +import { withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; /** * External dependencies @@ -11,17 +12,38 @@ import TextareaAutosize from 'react-autosize-textarea'; import classnames from 'classnames'; /** - * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/plain-text/README.md + * Internal dependencies */ -const PlainText = forwardRef( ( { onChange, className, ...props }, ref ) => { +import { withBlockEditContext } from '../block-edit/context'; + +const PlainText = forwardRef( ( { onChange, className, isReadOnly, ...props }, ref ) => { + delete props.clientId; return ( <TextareaAutosize ref={ ref } className={ classnames( 'editor-plain-text block-editor-plain-text', className ) } onChange={ ( event ) => onChange( event.target.value ) } + disabled={ isReadOnly } { ...props } /> ); } ); -export default PlainText; +/** + * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/plain-text/README.md + */ +export default compose( [ + withBlockEditContext( ( { clientId } ) => ( { clientId } ) ), + withSelect( ( select, { + clientId, + } ) => { + const { + getBlockRootClientId, + getTemplateLock, + } = select( 'core/block-editor' ); + + const isReadOnly = getTemplateLock( getBlockRootClientId( clientId ) ) === 'readonly'; + + return { isReadOnly }; + } ), +] )( PlainText ); diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 48c2cf817f74ac..18e23ffa1f7d23 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -255,6 +255,7 @@ class RichTextWraper extends Component { render() { const { + isReadOnly, tagName, value: originalValue, onChange: originalOnChange, @@ -307,6 +308,7 @@ class RichTextWraper extends Component { return ( <RichText { ...experimentalProps } + isReadOnly={ isReadOnly } value={ adjustedValue } onChange={ adjustedOnChange } selectionStart={ selectionStart } @@ -375,8 +377,11 @@ const RichTextContainer = compose( [ getSelectionStart, getSelectionEnd, getSettings, + getBlockRootClientId, + getTemplateLock, } = select( 'core/block-editor' ); + const isReadOnly = getTemplateLock( getBlockRootClientId( clientId ) ) === 'readonly'; const selectionStart = getSelectionStart(); const selectionEnd = getSelectionEnd(); const { __experimentalCanUserUseUnfilteredHTML } = getSettings(); @@ -393,6 +398,7 @@ const RichTextContainer = compose( [ selectionStart: isSelected ? selectionStart.offset : undefined, selectionEnd: isSelected ? selectionEnd.offset : undefined, isSelected, + isReadOnly, }; } ), withDispatch( ( dispatch, { diff --git a/packages/block-library/src/classic/edit.js b/packages/block-library/src/classic/edit.js index 5b30ecb7f90692..978fa841d0a6e8 100644 --- a/packages/block-library/src/classic/edit.js +++ b/packages/block-library/src/classic/edit.js @@ -62,11 +62,12 @@ export default class ClassicEdit extends Component { } initialize() { - const { clientId } = this.props; + const { clientId, isReadOnly } = this.props; const { settings } = window.wpEditorL10n.tinymce; wp.oldEditor.initialize( `editor-${ clientId }`, { tinymce: { ...settings, + readonly: isReadOnly, inline: true, content_css: false, fixed_toolbar_container: `#toolbar-${ clientId }`, @@ -176,7 +177,7 @@ export default class ClassicEdit extends Component { } render() { - const { clientId } = this.props; + const { clientId, isReadOnly } = this.props; // Disable reasons: // @@ -190,7 +191,7 @@ export default class ClassicEdit extends Component { /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ return [ - <div + ! isReadOnly && <div key="toolbar" id={ `toolbar-${ clientId }` } ref={ ( ref ) => this.ref = ref } diff --git a/packages/e2e-tests/plugins/cpt-locking.php b/packages/e2e-tests/plugins/cpt-locking.php index ba8a8d611c428c..3ebec40da541b4 100644 --- a/packages/e2e-tests/plugins/cpt-locking.php +++ b/packages/e2e-tests/plugins/cpt-locking.php @@ -21,6 +21,16 @@ function gutenberg_test_cpt_locking() { ), array( 'core/quote' ), ); + register_post_type( + 'locked-readonly-post', + array( + 'public' => true, + 'label' => 'Locked Readonly Post', + 'show_in_rest' => true, + 'template' => $template, + 'template_lock' => 'readonly', + ) + ); register_post_type( 'locked-all-post', array( diff --git a/packages/e2e-tests/specs/plugins/__snapshots__/cpt-locking.test.js.snap b/packages/e2e-tests/specs/plugins/__snapshots__/cpt-locking.test.js.snap index 24166c89f310b3..943253ab4e1b4d 100644 --- a/packages/e2e-tests/specs/plugins/__snapshots__/cpt-locking.test.js.snap +++ b/packages/e2e-tests/specs/plugins/__snapshots__/cpt-locking.test.js.snap @@ -69,3 +69,31 @@ exports[`cpt locking template_lock insert should allow blocks to be moved 1`] = <blockquote class=\\"wp-block-quote\\"><p></p></blockquote> <!-- /wp:quote -->" `; + +exports[`cpt locking template_lock readonly should only allow block selection 1`] = ` +"<!-- wp:image --> +<figure class=\\"wp-block-image\\"><img alt=\\"\\"/></figure> +<!-- /wp:image --> + +<!-- wp:paragraph {\\"placeholder\\":\\"Add a description\\"} --> +<p></p> +<!-- /wp:paragraph --> + +<!-- wp:quote --> +<blockquote class=\\"wp-block-quote\\"><p></p></blockquote> +<!-- /wp:quote -->" +`; + +exports[`cpt locking template_lock readonly should only allow block selection 2`] = ` +"<!-- wp:image --> +<figure class=\\"wp-block-image\\"><img alt=\\"\\"/></figure> +<!-- /wp:image --> + +<!-- wp:paragraph {\\"placeholder\\":\\"Add a description\\"} --> +<p></p> +<!-- /wp:paragraph --> + +<!-- wp:quote --> +<blockquote class=\\"wp-block-quote\\"><p></p></blockquote> +<!-- /wp:quote -->" +`; diff --git a/packages/e2e-tests/specs/plugins/cpt-locking.test.js b/packages/e2e-tests/specs/plugins/cpt-locking.test.js index 74f8f81bf67e3f..f97b23ad9b55a9 100644 --- a/packages/e2e-tests/specs/plugins/cpt-locking.test.js +++ b/packages/e2e-tests/specs/plugins/cpt-locking.test.js @@ -45,6 +45,38 @@ describe( 'cpt locking', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); }; + describe( 'template_lock readonly', () => { + beforeEach( async () => { + await createNewPost( { postType: 'locked-readonly-post' } ); + } ); + + it( 'should remove the inserter', shouldRemoveTheInserter ); + + it( 'should only allow block selection', async () => { + // Expect the title to be selected. + expect( await page.evaluate( () => { + return document.activeElement.matches( 'textarea' ); + } ) ).toBe( true ); + + await page.keyboard.press( 'Enter' ); + + // Expect no content to be created. + expect( await getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'Tab' ); + await page.keyboard.press( 'Tab' ); + + // Expect the second block to be selected. + expect( await page.evaluate( () => { + return document.activeElement.matches( '[data-type="core/paragraph"]' ); + } ) ).toBe( true ); + + await page.keyboard.press( 'Backspace' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); + } ); + describe( 'template_lock all', () => { beforeEach( async () => { await createNewPost( { postType: 'locked-all-post' } ); diff --git a/packages/rich-text/src/component/index.js b/packages/rich-text/src/component/index.js index c2f76c4a22c296..9b5d87367c2c3a 100644 --- a/packages/rich-text/src/component/index.js +++ b/packages/rich-text/src/component/index.js @@ -23,6 +23,7 @@ import isShallowEqual from '@wordpress/is-shallow-equal'; */ import FormatEdit from './format-edit'; import Editable from './editable'; +import ReadOnly from './read-only'; import { pickAriaProps } from './aria'; import { isEmpty } from '../is-empty'; import { create } from '../create'; @@ -852,6 +853,7 @@ class RichText extends Component { render() { const { + isReadOnly, tagName: Tagname = 'div', style, wrapperClassName, @@ -871,10 +873,41 @@ class RichText extends Component { // prevent Editable component updates. const key = Tagname; const MultilineTag = this.multilineTag; - const ariaProps = pickAriaProps( this.props ); const record = this.getRecord(); - const isPlaceholderVisible = placeholder && ( ! isSelected || keepPlaceholderOnFocus ) && isEmpty( record ); + const isPlaceholderVisible = ( + placeholder && + ( ! isSelected || keepPlaceholderOnFocus ) && + isEmpty( record ) + ); + const classes = classnames( 'rich-text', className ); + + const Placeholder = isPlaceholderVisible && ( + <Tagname className={ classes } style={ style }> + { MultilineTag ? + <MultilineTag>{ placeholder }</MultilineTag> : + placeholder + } + </Tagname> + ); + + if ( isReadOnly ) { + return ( + <> + <ReadOnly + tagName={ Tagname } + style={ style } + record={ record } + valueToEditableHTML={ this.valueToEditableHTML } + isPlaceholderVisible={ isPlaceholderVisible } + className={ classes } + key={ key } + /> + { Placeholder } + </> + ); + } + const ariaProps = pickAriaProps( this.props ); const autoCompleteContent = ( { listBoxId, activeId } ) => ( <> <Editable @@ -888,7 +921,7 @@ class RichText extends Component { aria-owns={ listBoxId } aria-activedescendant={ activeId } { ...ariaProps } - className={ classnames( 'rich-text', className ) } + className={ classes } key={ key } onPaste={ this.onPaste } onInput={ this.onInput } @@ -900,14 +933,7 @@ class RichText extends Component { onTouchStart={ this.onPointerDown } setRef={ this.setRef } /> - { isPlaceholderVisible && - <Tagname - className={ classnames( 'rich-text', className ) } - style={ style } - > - { MultilineTag ? <MultilineTag>{ placeholder }</MultilineTag> : placeholder } - </Tagname> - } + { Placeholder } { isSelected && <FormatEdit value={ record } onChange={ this.onChange } /> } </> ); diff --git a/packages/rich-text/src/component/read-only.js b/packages/rich-text/src/component/read-only.js new file mode 100644 index 00000000000000..18bacc491d325a --- /dev/null +++ b/packages/rich-text/src/component/read-only.js @@ -0,0 +1,15 @@ +export default function readOnly( { + tagName: Tag = 'div', + record, + valueToEditableHTML, + isPlaceholderVisible, + ...props +} ) { + return ( + <Tag + data-is-placeholder-visible={ isPlaceholderVisible } + dangerouslySetInnerHTML={ { __html: valueToEditableHTML( record ) } } + { ...props } + /> + ); +} From 6bc9cf8e8d81ce565a8638ca5ed361c28b491f4c Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Mon, 22 Jul 2019 11:15:41 +0200 Subject: [PATCH 54/70] Don't select template part on load --- packages/block-library/src/template-part/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/template-part/edit.js b/packages/block-library/src/template-part/edit.js index f190697b7fdd8b..0bc651a25f65fa 100644 --- a/packages/block-library/src/template-part/edit.js +++ b/packages/block-library/src/template-part/edit.js @@ -37,7 +37,7 @@ export default function TemplatePartEdit( { attributes, clientId, setAttributes if ( ! rawTemplatePartContent ) { return; } - replaceInnerBlocks( clientId, parse( rawTemplatePartContent ) ); + replaceInnerBlocks( clientId, parse( rawTemplatePartContent ), false ); }, [ rawTemplatePartContent, replaceInnerBlocks ] ); From edd9eeb8d484c2f63bd5f6fadfae235468906cd2 Mon Sep 17 00:00:00 2001 From: iseulde <wp@iseulde.com> Date: Mon, 22 Jul 2019 11:41:41 +0200 Subject: [PATCH 55/70] Enable readonly post content --- packages/block-library/src/post-content/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/post-content/edit.js b/packages/block-library/src/post-content/edit.js index 62224133b44c2a..58336deb2c5fd9 100644 --- a/packages/block-library/src/post-content/edit.js +++ b/packages/block-library/src/post-content/edit.js @@ -22,7 +22,7 @@ export default function PostContentEdit( { clientId } ) { return ( <InnerBlocks - templateLock={ false } + templateLock="readonly" allowedBlocks={ allowedBlocks } renderAppender={ ! hasInnerBlocks && InnerBlocks.ButtonBlockAppender } /> From 1aff7ae1b4599970ff09bac6b69bcbb4e7386aca Mon Sep 17 00:00:00 2001 From: Riad Benguella <benguella@gmail.com> Date: Mon, 22 Jul 2019 11:40:40 +0200 Subject: [PATCH 56/70] Avoid race conditions when running multiple animations in a row --- .../src/components/block-list/moving-animation.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-list/moving-animation.js b/packages/block-editor/src/components/block-list/moving-animation.js index 393bfa33e2a7ca..adc9ddb01c009e 100644 --- a/packages/block-editor/src/components/block-list/moving-animation.js +++ b/packages/block-editor/src/components/block-list/moving-animation.js @@ -31,8 +31,16 @@ function useMovingAnimation( ref, isSelected, enableAnimation, triggerAnimationO const prefersReducedMotion = useReducedMotion() || ! enableAnimation; const [ resetAnimation, setResetAnimation ] = useState( false ); const [ transform, setTransform ] = useState( { x: 0, y: 0 } ); - const previous = ref.current ? ref.current.getBoundingClientRect() : null; + + // This effect should be before the animation computation effect + // otherwise we might have a race condition causing the animation + // to not run. + useLayoutEffect( () => { + if ( resetAnimation ) { + setResetAnimation( false ); + } + }, [ resetAnimation ] ); useLayoutEffect( () => { if ( prefersReducedMotion ) { return; @@ -49,11 +57,6 @@ function useMovingAnimation( ref, isSelected, enableAnimation, triggerAnimationO setResetAnimation( true ); setTransform( newTransform ); }, [ triggerAnimationOnChange ] ); - useLayoutEffect( () => { - if ( resetAnimation ) { - setResetAnimation( false ); - } - }, [ resetAnimation ] ); const animationProps = useSpring( { from: transform, to: { From 7502c8b70993937e52ad76ea53271e855ce33f20 Mon Sep 17 00:00:00 2001 From: iseulde <wp@iseulde.com> Date: Mon, 22 Jul 2019 11:55:19 +0200 Subject: [PATCH 57/70] Revert "Enable readonly post content" This reverts commit edd9eeb8d484c2f63bd5f6fadfae235468906cd2. --- packages/block-library/src/post-content/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/post-content/edit.js b/packages/block-library/src/post-content/edit.js index 58336deb2c5fd9..62224133b44c2a 100644 --- a/packages/block-library/src/post-content/edit.js +++ b/packages/block-library/src/post-content/edit.js @@ -22,7 +22,7 @@ export default function PostContentEdit( { clientId } ) { return ( <InnerBlocks - templateLock="readonly" + templateLock={ false } allowedBlocks={ allowedBlocks } renderAppender={ ! hasInnerBlocks && InnerBlocks.ButtonBlockAppender } /> From edbd90f9f200008f44839b3f47cbd3011fc18aaf Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski <grzegorz@gziolo.pl> Date: Mon, 22 Jul 2019 11:38:30 +0200 Subject: [PATCH 58/70] Extend template mode lock to title and date blocks --- .../src/components/block-list/block.js | 25 ++++--------- .../src/components/layout/style.scss | 14 +++++-- packages/editor/src/hooks/view-edit-mode.js | 37 ++++++++++++++----- 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 49790435a3816b..0c4f89c50964bf 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -2,7 +2,7 @@ * External dependencies */ import classnames from 'classnames'; -import { first, last } from 'lodash'; +import { first, last, some } from 'lodash'; import { animated } from 'react-spring/web.cjs'; /** @@ -22,7 +22,7 @@ import { isUnmodifiedDefaultBlock, getUnregisteredTypeHandlerName, } from '@wordpress/blocks'; -import { Disabled, KeyboardShortcuts, withFilters } from '@wordpress/components'; +import { KeyboardShortcuts, withFilters } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { withDispatch, @@ -53,6 +53,8 @@ import useHoveredArea from './hover-area'; import { isInsideRootBlock } from '../../utils/dom'; import useMovingAnimation from './moving-animation'; +const postBlockTypes = [ 'core/post-content', 'core/post-date', 'core/post-title' ]; + /** * Prevents default dragging behavior within a block to allow for multi- * selection to take effect unhampered. @@ -102,7 +104,7 @@ function BlockListBlock( { onSelectionStart, animateOnChange, enableAnimation, - isAncestorOfPostContent, + isAncestorOfPostBlock, noInserters, noMovers, noToolbars, @@ -417,7 +419,8 @@ function BlockListBlock( { 'is-typing': isTypingWithinBlock, 'is-focused': isFocusMode && ( isSelected || isParentOfSelectedBlock ), 'is-focus-mode': isFocusMode, - 'is-ancestor-of-post-content': isAncestorOfPostContent, + 'is-ancestor-of-post-block': isAncestorOfPostBlock, + 'is-post-block': postBlockTypes.includes( name ), }, className ); @@ -612,9 +615,6 @@ const applyWithSelect = withSelect( __unstableGetBlockWithoutInnerBlocks, isAncestorOfBlockTypeName, } = select( 'core/block-editor' ); - const { - getViewEditingMode, - } = select( 'core/editor' ); const block = __unstableGetBlockWithoutInnerBlocks( clientId ); const isSelected = isBlockSelected( clientId ); const { hasFixedToolbar, focusMode, isRTL } = getSettings(); @@ -660,8 +660,7 @@ const applyWithSelect = withSelect( isValid, isSelected, isParentOfSelectedBlock, - isAncestorOfPostContent: isAncestorOfBlockTypeName( clientId, 'core/post-content' ), - viewEditingMode: getViewEditingMode(), + isAncestorOfPostBlock: some( postBlockTypes, ( blockTypeName ) => isAncestorOfBlockTypeName( clientId, blockTypeName ) ), }; } ); @@ -758,18 +757,10 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { }; } ); -const applyDisabled = ( WrappedComponent ) => ( props ) => - props.viewEditingMode === 'design' ? - <Disabled><WrappedComponent { ...props } - className={ ( props.className || '' ) + ' is-disabled' } - /></Disabled> : - <WrappedComponent { ...props } />; - export default compose( pure, withViewportMatch( { isLargeViewport: 'medium' } ), applyWithSelect, applyWithDispatch, withFilters( 'editor.BlockListBlock' ), - applyDisabled, )( BlockListBlock ); diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index 9800ca3d06af57..8210f7ae5656d6 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -285,8 +285,16 @@ opacity: 0.2; } -.is-mode-focus .is-ancestor-of-post-content, -.is-mode-focus [data-type="core/post-content"], -.is-mode-focus [data-type="core/post-content"] .block-editor-block-list__block { +.is-mode-focus .is-ancestor-of-post-block, +.is-mode-focus .is-post-block, +.is-mode-focus .is-post-block .block-editor-block-list__block { opacity: 1; } + +.is-mode-template .is-post-block { + background-color: $light-gray-100; + + .block-editor-block-list__block { + opacity: 0.5; + } +} diff --git a/packages/editor/src/hooks/view-edit-mode.js b/packages/editor/src/hooks/view-edit-mode.js index 9c9e22ee7e14fc..48e875a642c71c 100644 --- a/packages/editor/src/hooks/view-edit-mode.js +++ b/packages/editor/src/hooks/view-edit-mode.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { some } from 'lodash'; + /** * WordPress dependencies */ @@ -6,23 +11,29 @@ import { createHigherOrderComponent } from '@wordpress/compose'; import { useSelect } from '@wordpress/data'; import { addFilter } from '@wordpress/hooks'; -export const withViewEditMode = createHigherOrderComponent( ( BlockListBlock ) => { +const postBlockTypes = [ 'core/post-content', 'core/post-date', 'core/post-title' ]; + +export const withViewEditingMode = createHigherOrderComponent( ( BlockListBlock ) => { return ( props ) => { const { clientId, name } = props; - const { isAncestorOfPostContent, isDescendantOfPostContent, viewEditingMode } = useSelect( ( select ) => { + const { + isAncestorOfPostBlock, + isDescendantOfPostBlock, + viewEditingMode, + } = useSelect( ( select ) => { const { isAncestorOfBlockTypeName, isDescendantOfBlockTypeName } = select( 'core/block-editor' ); const { getViewEditingMode } = select( 'core/editor' ); return { - isAncestorOfPostContent: isAncestorOfBlockTypeName( clientId, 'core/post-content' ), - isDescendantOfPostContent: isDescendantOfBlockTypeName( clientId, 'core/post-content' ), + isAncestorOfPostBlock: some( postBlockTypes, ( postBlockType ) => isAncestorOfBlockTypeName( clientId, postBlockType ) ), + isDescendantOfPostBlock: some( postBlockTypes, ( postBlockType ) => isDescendantOfBlockTypeName( clientId, postBlockType ) ), viewEditingMode: getViewEditingMode(), }; }, [ clientId ] ); if ( viewEditingMode === 'focus' ) { - if ( isAncestorOfPostContent ) { + if ( isAncestorOfPostBlock ) { return ( <BlockListBlock { ...props } noInserters={ true } @@ -32,7 +43,7 @@ export const withViewEditMode = createHigherOrderComponent( ( BlockListBlock ) = ); } - if ( name === 'core/post-content' ) { + if ( postBlockTypes.includes( name ) ) { return ( <BlockListBlock { ...props } noInserters={ true } @@ -41,7 +52,7 @@ export const withViewEditMode = createHigherOrderComponent( ( BlockListBlock ) = ); } - if ( isDescendantOfPostContent ) { + if ( isDescendantOfPostBlock ) { return <BlockListBlock { ...props } />; } @@ -60,10 +71,18 @@ export const withViewEditMode = createHigherOrderComponent( ( BlockListBlock ) = ); } + if ( viewEditingMode === 'template' && isDescendantOfPostBlock ) { + return ( + <Disabled> + <BlockListBlock { ...props } /> + </Disabled> + ); + } + return ( <BlockListBlock { ...props } /> ); }; -}, 'withViewEditMode' ); +}, 'withViewEditingMode' ); -addFilter( 'editor.BlockListBlock', 'core/editor/with-view-edit-mode', withViewEditMode ); +addFilter( 'editor.BlockListBlock', 'core/editor/with-view-edit-mode', withViewEditingMode ); From d2c25309d7f1887e436e0a9d7c0ca0f9ffc85c23 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski <grzegorz@gziolo.pl> Date: Mon, 22 Jul 2019 11:54:42 +0200 Subject: [PATCH 59/70] Add locking for post blocks when in template mode --- packages/editor/src/hooks/view-edit-mode.js | 33 +++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/hooks/view-edit-mode.js b/packages/editor/src/hooks/view-edit-mode.js index 48e875a642c71c..eb807dfde31f90 100644 --- a/packages/editor/src/hooks/view-edit-mode.js +++ b/packages/editor/src/hooks/view-edit-mode.js @@ -13,7 +13,35 @@ import { addFilter } from '@wordpress/hooks'; const postBlockTypes = [ 'core/post-content', 'core/post-date', 'core/post-title' ]; -export const withViewEditingMode = createHigherOrderComponent( ( BlockListBlock ) => { +export const withViewEditingModeForBlockEdit = createHigherOrderComponent( ( BlockEdit ) => { + return ( props ) => { + const { clientId, name } = props; + + const { + viewEditingMode, + } = useSelect( ( select ) => { + const { getViewEditingMode } = select( 'core/editor' ); + + return { + viewEditingMode: getViewEditingMode(), + }; + }, [ clientId ] ); + + if ( viewEditingMode === 'template' && postBlockTypes.includes( name ) ) { + return ( + <Disabled> + <BlockEdit { ...props } /> + </Disabled> + ); + } + + return ( + <BlockEdit { ...props } /> + ); + }; +}, 'withViewEditingMode' ); + +export const withViewEditingModeForBlockListBlock = createHigherOrderComponent( ( BlockListBlock ) => { return ( props ) => { const { clientId, name } = props; @@ -85,4 +113,5 @@ export const withViewEditingMode = createHigherOrderComponent( ( BlockListBlock }; }, 'withViewEditingMode' ); -addFilter( 'editor.BlockListBlock', 'core/editor/with-view-edit-mode', withViewEditingMode ); +addFilter( 'editor.BlockEdit', 'core/editor/block-edit/with-view-edit-mode', withViewEditingModeForBlockEdit ); +addFilter( 'editor.BlockListBlock', 'core/editor/block-list-block/with-view-edit-mode', withViewEditingModeForBlockListBlock ); From 44838bb8246b66b639c9c7df1b469cc76f6dc5ec Mon Sep 17 00:00:00 2001 From: Riad Benguella <benguella@gmail.com> Date: Mon, 22 Jul 2019 12:03:01 +0200 Subject: [PATCH 60/70] Fix animation retriggered on each change --- .../components/block-list/moving-animation.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/block-editor/src/components/block-list/moving-animation.js b/packages/block-editor/src/components/block-list/moving-animation.js index adc9ddb01c009e..99a25024ed465f 100644 --- a/packages/block-editor/src/components/block-list/moving-animation.js +++ b/packages/block-editor/src/components/block-list/moving-animation.js @@ -6,7 +6,7 @@ import { useSpring, interpolate } from 'react-spring/web.cjs'; /** * WordPress dependencies */ -import { useState, useLayoutEffect } from '@wordpress/element'; +import { useState, useLayoutEffect, useReducer } from '@wordpress/element'; import { useReducedMotion } from '@wordpress/compose'; /** @@ -29,18 +29,16 @@ import { useReducedMotion } from '@wordpress/compose'; */ function useMovingAnimation( ref, isSelected, enableAnimation, triggerAnimationOnChange ) { const prefersReducedMotion = useReducedMotion() || ! enableAnimation; - const [ resetAnimation, setResetAnimation ] = useState( false ); + const [ triggeredAnimation, triggerAnimation ] = useReducer( ( state = 0 ) => state + 1 ); + const [ finishedAnimation, endAnimation ] = useReducer( ( state = 0 ) => state + 1 ); const [ transform, setTransform ] = useState( { x: 0, y: 0 } ); const previous = ref.current ? ref.current.getBoundingClientRect() : null; - // This effect should be before the animation computation effect - // otherwise we might have a race condition causing the animation - // to not run. useLayoutEffect( () => { - if ( resetAnimation ) { - setResetAnimation( false ); + if ( triggeredAnimation ) { + endAnimation(); } - }, [ resetAnimation ] ); + }, [ triggeredAnimation ] ); useLayoutEffect( () => { if ( prefersReducedMotion ) { return; @@ -54,7 +52,7 @@ function useMovingAnimation( ref, isSelected, enableAnimation, triggerAnimationO ref.current.style.transform = newTransform.x === 0 && newTransform.y === 0 ? undefined : `translate3d(${ newTransform.x }px,${ newTransform.y }px,0)`; - setResetAnimation( true ); + triggerAnimation(); setTransform( newTransform ); }, [ triggerAnimationOnChange ] ); const animationProps = useSpring( { @@ -63,7 +61,7 @@ function useMovingAnimation( ref, isSelected, enableAnimation, triggerAnimationO x: 0, y: 0, }, - reset: resetAnimation, + reset: triggeredAnimation !== finishedAnimation, config: { mass: 5, tension: 2000, friction: 200 }, immediate: prefersReducedMotion, } ); From 8cfb1e4e0268596c92d646bcf4510a659799332f Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <miguelcsf@gmail.com> Date: Mon, 22 Jul 2019 12:10:52 +0200 Subject: [PATCH 61/70] Design mode: draw grid lines (hardcoded for gutenberg-theme) --- .../src/components/block-list/style.scss | 15 ---------- .../src/components/layout/style.scss | 28 +++++++++---------- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index fb6db319118cc9..d25c7c578d8776 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -7,21 +7,6 @@ } } -// TODO: move to edit-post//components/layout along with the other modes -.block-editor-block-list__layout .block-editor-block-list__block.is-disabled { - background-color: rgba(200, 200, 200, 0.2); - > .block-editor-block-list__block-edit { - // opacity: 0.5; - } - // &::before { - // content: 'h'; - // } - - .editor-rich-text { - background: #ccc !important; - } -} - .block-editor-block-list__layout .block-editor-block-list__block.is-selected { // Needs specificity to override inherited styles. // While block is being dragged, dim the slot dragged from, and hide some UI. &.is-dragging { diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index 8210f7ae5656d6..9e282e89e178b6 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -235,25 +235,25 @@ /* Settings */ :root { - --offset: 2rem; - --max_width: 72rem; - --columns: 5; - --gutter: 1rem; - --color: hsla(204, 80%, 72%, 0.15); -} - -/* Helper variables */ -:root { - --repeating-width: calc(100% / var(--columns)); - --column-width: calc((100% / var(--columns)) - var(--gutter)); - --background-width: calc(100% + var(--gutter)); - --background-columns: repeating-linear-gradient(to right, var(--color), var(--color) var(--column-width), transparent var(--column-width), transparent var(--repeating-width)); + --color: #000; + /* The following is hard-coded for `gutenberg-theme` */ + --background-columns: linear-gradient(to right, var(--color), var(--color) 1px, transparent 1px, transparent 245px, var(--color) 245px, var(--color) 246px, transparent 246px, transparent 854px, var(--color) 854px, var(--color) 855px, transparent 855px, transparent 1099px, var(--color) 1099px, var(--color) 1100px, transparent 1100px); } .edit-post-layout.is-mode-design { background-image: var(--background-columns); - background-size: var(--background-width) 100%; + background-size: 1100px 100%; + background-repeat: no-repeat; + background-position-x: center; z-index: 1000; + + .block-editor-block-list__block { + background-color: rgba(200, 200, 200, 0.2); + } + + .editor-rich-text { + background: #ccc !important; + } } .is-mode-template-part { From dedc11ca8c08e979a55cc19b6a56d8658874953f Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Mon, 22 Jul 2019 13:17:21 +0200 Subject: [PATCH 62/70] Cache edits across modes. --- .../block-library/src/template-part/edit.js | 2 +- .../view-editing-mode-picker/index.js | 12 +-- packages/editor/src/store/actions.js | 77 +++++++++++++++---- 3 files changed, 63 insertions(+), 28 deletions(-) diff --git a/packages/block-library/src/template-part/edit.js b/packages/block-library/src/template-part/edit.js index 0bc651a25f65fa..1786dad097dd4b 100644 --- a/packages/block-library/src/template-part/edit.js +++ b/packages/block-library/src/template-part/edit.js @@ -34,7 +34,7 @@ export default function TemplatePartEdit( { attributes, clientId, setAttributes const { replaceInnerBlocks } = useDispatch( 'core/block-editor' ); useEffect( () => { - if ( ! rawTemplatePartContent ) { + if ( ! rawTemplatePartContent || hasInnerBlocks ) { return; } replaceInnerBlocks( clientId, parse( rawTemplatePartContent ), false ); diff --git a/packages/editor/src/components/view-editing-mode-picker/index.js b/packages/editor/src/components/view-editing-mode-picker/index.js index b380c200f4a1a9..903ea74ae2c8fc 100644 --- a/packages/editor/src/components/view-editing-mode-picker/index.js +++ b/packages/editor/src/components/view-editing-mode-picker/index.js @@ -10,7 +10,6 @@ import { SelectControl } from '@wordpress/components'; * Internal dependencies */ import { getModeConfig, modes } from '../../editor-modes'; -import { findDeepBlock } from '../../utils'; export default function ViewEditingModePicker() { const { post, blocks, settings, viewEditingMode } = useSelect( ( select ) => { @@ -30,21 +29,12 @@ export default function ViewEditingModePicker() { const { setupEditor, updateViewEditingMode } = useDispatch( 'core/editor' ); const updateViewEditingModeCallback = useCallback( ( newViewEditingMode ) => { - const currentModeConfig = getModeConfig( viewEditingMode, settings.templateParts ); const newModeConfig = getModeConfig( newViewEditingMode, settings.templateParts ); updateViewEditingMode( newViewEditingMode ); - let newBlocks = blocks; - if ( currentModeConfig.showTemplate ) { // Leaving template mode. - const postContentBlock = findDeepBlock( blocks, 'core/post-content' ); - newBlocks = postContentBlock ? postContentBlock.innerBlocks : []; - } - setupEditor( post, - { - blocks: newBlocks, - }, + null, settings.template, newModeConfig.showTemplate && settings.templatePost, newModeConfig.id diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 872ff6fbf2c51d..a950a57169e334 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -46,6 +46,8 @@ import { findDeepBlock, findDeepBlocks } from '../utils'; */ const lastBlockSourceDependenciesByRegistry = new WeakMap; +const templatePartsBlocksCache = {}; + /** * Given a blocks array, returns a blocks array with sourced attribute values * applied. The reference will remain consistent with the original argument if @@ -169,19 +171,25 @@ export function* setupEditor( post, edits, template, templatePost, templatePartI if ( has( edits, [ 'content' ] ) ) { content = edits.content; } else { - content = post.content.raw; + content = post.content.raw || post.content; } - let blocks = ( edits && edits.blocks ) || parse( content ); + let blocks = templatePartsBlocksCache[ 'post-content' ] || parse( content ); const isNewPost = 'auto-draft' === post.status; if ( templatePost ) { // Apply template post. const postContentInnerBlocks = blocks; - blocks = parse( templatePost.post_content ); + blocks = templatePartsBlocksCache.template || parse( templatePost.post_content ); + + const templatePartBlocks = findDeepBlocks( blocks, 'core/template-part' ); + templatePartBlocks.forEach( ( block ) => { + if ( templatePartsBlocksCache[ block.attributes.id ] ) { + block.innerBlocks = templatePartsBlocksCache[ block.attributes.id ]; + } + } ); if ( templatePartId ) { - const templatePartBlocks = findDeepBlocks( blocks, 'core/template-part' ); - blocks = [ templatePartBlocks.find( ( block ) => block.attributes.id === templatePartId ) ]; + blocks = templatePartBlocks.find( ( block ) => block.attributes.id === templatePartId ).innerBlocks; } // Post content is nested inside a post content block. @@ -496,6 +504,16 @@ export function* savePost( options = {} ) { yield select( 'core/block-editor', 'getClientIdsWithDescendants' ) ); + const post = yield select( + STORE_KEY, + 'getCurrentPost' + ); + + let editedPostContent = yield select( + STORE_KEY, + 'getEditedPostContent' + ); + const viewEditingMode = yield select( STORE_KEY, 'getViewEditingMode' ); const { templateParts, templatePost } = yield select( STORE_KEY, 'getEditorSettings' ); const viewEditingModeObject = getModeConfig( viewEditingMode, templateParts ); @@ -524,17 +542,21 @@ export function* savePost( options = {} ) { } } } - } - - const post = yield select( - STORE_KEY, - 'getCurrentPost' - ); - let editedPostContent = yield select( - STORE_KEY, - 'getEditedPostContent' - ); + if ( viewEditingModeObject.id ) { + const templatePartContent = editedPostContent; + yield dispatch( + 'core', + 'saveEntityRecord', + 'postType', + 'wp_template', { + content: templatePartContent, + id: viewEditingModeObject.id, + title: viewEditingModeObject.label, + } + ); + } + } if ( viewEditingModeObject.showTemplate && ! viewEditingModeObject.id && templatePost ) { yield apiFetch( { @@ -1036,9 +1058,32 @@ export function* resetEditorBlocks( blocks, options = {} ) { yield* resetLastBlockSourceDependencies( Array.from( updatedSources ) ); } + const newBlocks = yield* getBlocksWithSourcedAttributes( blocks ); + + const viewEditingMode = yield select( STORE_KEY, 'getViewEditingMode' ); + const viewEditingModeObject = getModeConfig( + viewEditingMode, + ( yield select( STORE_KEY, 'getEditorSettings' ) ).templateParts + ); + const cacheKey = + viewEditingModeObject.id || + ( viewEditingModeObject.showTemplate && 'template' ) || + viewEditingMode; + templatePartsBlocksCache[ cacheKey ] = newBlocks; + + if ( cacheKey === 'template' ) { + const postContentBlock = findDeepBlock( newBlocks, 'core/post-content' ); + if ( postContentBlock ) { + templatePartsBlocksCache[ 'post-content' ] = postContentBlock.innerBlocks; + } + + const templatePartBlocks = findDeepBlocks( newBlocks, 'core/template-part' ); + templatePartBlocks.forEach( ( block ) => templatePartsBlocksCache[ block.attributes.id ] = block.innerBlocks ); + } + return { type: 'RESET_EDITOR_BLOCKS', - blocks: yield* getBlocksWithSourcedAttributes( blocks ), + blocks: newBlocks, shouldCreateUndoLevel: options.__unstableShouldCreateUndoLevel !== false, }; } From 6c0879b3a6e4b3af555c4016eb36cec995c85cc3 Mon Sep 17 00:00:00 2001 From: iseulde <wp@iseulde.com> Date: Mon, 22 Jul 2019 14:04:44 +0200 Subject: [PATCH 63/70] Implement template locking --- .../src/components/block-drop-zone/index.js | 10 ++-- .../src/components/block-edit/context.js | 1 + .../src/components/block-edit/index.js | 8 ++-- .../src/components/block-inspector/index.js | 17 ++++--- .../src/components/block-list/block.js | 6 ++- .../src/components/block-mover/index.js | 8 ++-- .../src/components/inner-blocks/index.js | 8 +++- .../src/components/placeholder/index.js | 12 +---- .../src/components/plain-text/index.js | 15 +----- .../src/components/rich-text/index.js | 6 +-- .../block-library/src/post-content/edit.js | 1 - packages/editor/src/hooks/view-edit-mode.js | 48 ++++--------------- 12 files changed, 43 insertions(+), 97 deletions(-) diff --git a/packages/block-editor/src/components/block-drop-zone/index.js b/packages/block-editor/src/components/block-drop-zone/index.js index 0cce1fbb86564b..f67f36848d1c94 100644 --- a/packages/block-editor/src/components/block-drop-zone/index.js +++ b/packages/block-editor/src/components/block-drop-zone/index.js @@ -111,8 +111,8 @@ class BlockDropZone extends Component { } render() { - const { isLockedAll, index } = this.props; - if ( isLockedAll ) { + const { isReadOnly, index } = this.props; + if ( isReadOnly ) { return null; } const isAppender = index === undefined; @@ -155,12 +155,10 @@ export default compose( }, }; } ), - withSelect( ( select, { rootClientId } ) => { - const { getClientIdsOfDescendants, getTemplateLock, getBlockIndex } = select( 'core/block-editor' ); - const templateLock = getTemplateLock( rootClientId ); + withSelect( ( select ) => { + const { getClientIdsOfDescendants, getBlockIndex } = select( 'core/block-editor' ); return { - isLockedAll: templateLock === 'all' || templateLock === 'readonly', getClientIdsOfDescendants, getBlockIndex, }; diff --git a/packages/block-editor/src/components/block-edit/context.js b/packages/block-editor/src/components/block-edit/context.js index 863cdc3e5d6fa5..d30bbdfc6e3413 100644 --- a/packages/block-editor/src/components/block-edit/context.js +++ b/packages/block-editor/src/components/block-edit/context.js @@ -15,6 +15,7 @@ const { Consumer, Provider } = createContext( { focusedElement: null, setFocusedElement: noop, clientId: null, + isReadOnly: null, } ); export { Provider as BlockEditContextProvider }; diff --git a/packages/block-editor/src/components/block-edit/index.js b/packages/block-editor/src/components/block-edit/index.js index 63c475a50692ff..b60f2f1cd46890 100644 --- a/packages/block-editor/src/components/block-edit/index.js +++ b/packages/block-editor/src/components/block-edit/index.js @@ -27,13 +27,13 @@ class BlockEdit extends Component { ); } - propsToContext( name, isSelected, clientId, onFocus, onCaretVerticalPositionChange ) { - return { name, isSelected, clientId, onFocus, onCaretVerticalPositionChange }; + propsToContext( name, isSelected, clientId, onFocus, onCaretVerticalPositionChange, isReadOnly ) { + return { name, isSelected, clientId, onFocus, onCaretVerticalPositionChange, isReadOnly }; } render() { - const { name, isSelected, clientId, onFocus, onCaretVerticalPositionChange } = this.props; - const value = this.propsToContext( name, isSelected, clientId, onFocus, onCaretVerticalPositionChange ); + const { name, isSelected, clientId, onFocus, onCaretVerticalPositionChange, isReadOnly } = this.props; + const value = this.propsToContext( name, isSelected, clientId, onFocus, onCaretVerticalPositionChange, isReadOnly ); return ( <BlockEditContextProvider value={ value }> diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 355697a5fcfb42..7def68fab330ed 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -10,6 +10,7 @@ import { __ } from '@wordpress/i18n'; import { getBlockType, getUnregisteredTypeHandlerName } from '@wordpress/blocks'; import { PanelBody } from '@wordpress/components'; import { withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; /** * Internal dependencies @@ -20,6 +21,8 @@ import InspectorControls from '../inspector-controls'; import InspectorAdvancedControls from '../inspector-advanced-controls'; import BlockStyles from '../block-styles'; import MultiSelectionInspector from '../multi-selection-inspector'; +import { withBlockEditContext } from '../block-edit/context'; + const BlockInspector = ( { blockType, count, @@ -94,29 +97,25 @@ const BlockInspector = ( { ); }; -export default withSelect( - ( select ) => { +export default compose( [ + withBlockEditContext( ( { isReadOnly } ) => ( { isReadOnly } ) ), + withSelect( ( select ) => { const { getSelectedBlockClientId, getSelectedBlockCount, getBlockName, - getTemplateLock, - getBlockRootClientId, } = select( 'core/block-editor' ); const { getBlockStyles } = select( 'core/blocks' ); const selectedBlockClientId = getSelectedBlockClientId(); const selectedBlockName = selectedBlockClientId && getBlockName( selectedBlockClientId ); const blockType = selectedBlockClientId && getBlockType( selectedBlockName ); const blockStyles = selectedBlockClientId && getBlockStyles( selectedBlockName ); - const rootClientId = getBlockRootClientId( selectedBlockClientId ); - const isReadOnly = getTemplateLock( rootClientId ) === 'readonly'; return { count: getSelectedBlockCount(), hasBlockStyles: blockStyles && blockStyles.length > 0, selectedBlockName, selectedBlockClientId, blockType, - isReadOnly, }; - } -)( BlockInspector ); + } ), +] )( BlockInspector ); diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 0c4f89c50964bf..89779aecf14ad6 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -497,6 +497,7 @@ function BlockListBlock( { <BlockDropZone clientId={ clientId } rootClientId={ rootClientId } + isReadOnly={ isReadOnly } /> { isFirstMultiSelected && ( <BlockMultiControls rootClientId={ rootClientId } /> @@ -513,6 +514,7 @@ function BlockListBlock( { } onDragStart={ onDragStart } onDragEnd={ onDragEnd } + isReadOnly /> ) } { shouldShowBreadcrumb && ( @@ -596,7 +598,7 @@ function BlockListBlock( { } const applyWithSelect = withSelect( - ( select, { clientId, rootClientId, isLargeViewport } ) => { + ( select, { clientId, rootClientId, isLargeViewport, isReadOnly } ) => { const { isBlockSelected, isAncestorMultiSelected, @@ -644,7 +646,7 @@ const applyWithSelect = withSelect( name && isUnmodifiedDefaultBlock( { name, attributes } ), isMovable: templateLock !== 'all' && templateLock !== 'readonly', isLocked: !! templateLock, - isReadOnly: templateLock === 'readonly', + isReadOnly: isReadOnly === undefined && templateLock === 'readonly', isFocusMode: focusMode && isLargeViewport, hasFixedToolbar: hasFixedToolbar && isLargeViewport, isLast: index === blockOrder.length - 1, diff --git a/packages/block-editor/src/components/block-mover/index.js b/packages/block-editor/src/components/block-mover/index.js index 0842d93beed283..931c731d969928 100644 --- a/packages/block-editor/src/components/block-mover/index.js +++ b/packages/block-editor/src/components/block-mover/index.js @@ -44,10 +44,10 @@ export class BlockMover extends Component { } render() { - const { onMoveUp, onMoveDown, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, isLocked, instanceId, isHidden, rootClientId } = this.props; + const { onMoveUp, onMoveDown, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, isReadOnly, instanceId, isHidden, rootClientId } = this.props; const { isFocused } = this.state; const blocksCount = castArray( clientIds ).length; - if ( isLocked || ( isFirst && isLast && ! rootClientId ) ) { + if ( isReadOnly || ( isFirst && isLast && ! rootClientId ) ) { return null; } @@ -117,7 +117,7 @@ export class BlockMover extends Component { export default compose( withSelect( ( select, { clientIds } ) => { - const { getBlock, getBlockIndex, getTemplateLock, getBlockRootClientId, getBlockOrder } = select( 'core/block-editor' ); + const { getBlock, getBlockIndex, getBlockRootClientId, getBlockOrder } = select( 'core/block-editor' ); const normalizedClientIds = castArray( clientIds ); const firstClientId = first( normalizedClientIds ); const block = getBlock( firstClientId ); @@ -125,11 +125,9 @@ export default compose( const blockOrder = getBlockOrder( rootClientId ); const firstIndex = getBlockIndex( firstClientId, rootClientId ); const lastIndex = getBlockIndex( last( normalizedClientIds ), rootClientId ); - const templateLock = getTemplateLock( rootClientId ); return { blockType: block ? getBlockType( block.name ) : null, - isLocked: templateLock === 'all' || templateLock === 'readonly', rootClientId, firstIndex, isFirst: firstIndex === 0, diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 787ef42e9a337d..6c851d1a58c0a4 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -39,7 +39,13 @@ class InnerBlocks extends Component { const { templateLock, parentLock, + isReadOnly, } = this.props; + + if ( isReadOnly !== null ) { + return isReadOnly ? 'readonly' : false; + } + return templateLock === undefined ? parentLock : templateLock; } @@ -145,7 +151,7 @@ class InnerBlocks extends Component { } InnerBlocks = compose( [ - withBlockEditContext( ( context ) => pick( context, [ 'clientId' ] ) ), + withBlockEditContext( ( context ) => pick( context, [ 'clientId', 'isReadOnly' ] ) ), withSelect( ( select, ownProps ) => { const { isBlockSelected, diff --git a/packages/block-editor/src/components/placeholder/index.js b/packages/block-editor/src/components/placeholder/index.js index 7a4a5c41aa049c..22bdd7677e9fd3 100644 --- a/packages/block-editor/src/components/placeholder/index.js +++ b/packages/block-editor/src/components/placeholder/index.js @@ -2,7 +2,6 @@ * WordPress dependencies */ import { Placeholder } from '@wordpress/components'; -import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; /** @@ -21,14 +20,5 @@ function PlaceholderInBlockContext( { isReadOnly, ...props } ) { } export default compose( [ - withBlockEditContext( ( { clientId } ) => ( { clientId } ) ), - withSelect( ( select, { clientId } ) => { - const { - getBlockRootClientId, - getTemplateLock, - } = select( 'core/block-editor' ); - const rootClientId = getBlockRootClientId( clientId ); - const isReadOnly = getTemplateLock( rootClientId ) === 'readonly'; - return { isReadOnly }; - } ), + withBlockEditContext( ( { isReadOnly } ) => ( { isReadOnly } ) ), ] )( PlaceholderInBlockContext ); diff --git a/packages/block-editor/src/components/plain-text/index.js b/packages/block-editor/src/components/plain-text/index.js index d18acbbc51c48c..3828f2bbea21f6 100644 --- a/packages/block-editor/src/components/plain-text/index.js +++ b/packages/block-editor/src/components/plain-text/index.js @@ -2,7 +2,6 @@ * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; -import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; /** @@ -33,17 +32,5 @@ const PlainText = forwardRef( ( { onChange, className, isReadOnly, ...props }, r * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/plain-text/README.md */ export default compose( [ - withBlockEditContext( ( { clientId } ) => ( { clientId } ) ), - withSelect( ( select, { - clientId, - } ) => { - const { - getBlockRootClientId, - getTemplateLock, - } = select( 'core/block-editor' ); - - const isReadOnly = getTemplateLock( getBlockRootClientId( clientId ) ) === 'readonly'; - - return { isReadOnly }; - } ), + withBlockEditContext( ( { isReadOnly } ) => ( { isReadOnly } ) ), ] )( PlainText ); diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 18e23ffa1f7d23..0e4c6f19e33f60 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -365,7 +365,7 @@ class RichTextWraper extends Component { const RichTextContainer = compose( [ withInstanceId, - withBlockEditContext( ( { clientId } ) => ( { clientId } ) ), + withBlockEditContext( ( { clientId, isReadOnly } ) => ( { clientId, isReadOnly } ) ), withSelect( ( select, { clientId, instanceId, @@ -377,11 +377,8 @@ const RichTextContainer = compose( [ getSelectionStart, getSelectionEnd, getSettings, - getBlockRootClientId, - getTemplateLock, } = select( 'core/block-editor' ); - const isReadOnly = getTemplateLock( getBlockRootClientId( clientId ) ) === 'readonly'; const selectionStart = getSelectionStart(); const selectionEnd = getSelectionEnd(); const { __experimentalCanUserUseUnfilteredHTML } = getSettings(); @@ -398,7 +395,6 @@ const RichTextContainer = compose( [ selectionStart: isSelected ? selectionStart.offset : undefined, selectionEnd: isSelected ? selectionEnd.offset : undefined, isSelected, - isReadOnly, }; } ), withDispatch( ( dispatch, { diff --git a/packages/block-library/src/post-content/edit.js b/packages/block-library/src/post-content/edit.js index 62224133b44c2a..ad0ab117664e59 100644 --- a/packages/block-library/src/post-content/edit.js +++ b/packages/block-library/src/post-content/edit.js @@ -22,7 +22,6 @@ export default function PostContentEdit( { clientId } ) { return ( <InnerBlocks - templateLock={ false } allowedBlocks={ allowedBlocks } renderAppender={ ! hasInnerBlocks && InnerBlocks.ButtonBlockAppender } /> diff --git a/packages/editor/src/hooks/view-edit-mode.js b/packages/editor/src/hooks/view-edit-mode.js index eb807dfde31f90..c03a0b57a6b832 100644 --- a/packages/editor/src/hooks/view-edit-mode.js +++ b/packages/editor/src/hooks/view-edit-mode.js @@ -6,7 +6,6 @@ import { some } from 'lodash'; /** * WordPress dependencies */ -import { Disabled } from '@wordpress/components'; import { createHigherOrderComponent } from '@wordpress/compose'; import { useSelect } from '@wordpress/data'; import { addFilter } from '@wordpress/hooks'; @@ -28,15 +27,11 @@ export const withViewEditingModeForBlockEdit = createHigherOrderComponent( ( Blo }, [ clientId ] ); if ( viewEditingMode === 'template' && postBlockTypes.includes( name ) ) { - return ( - <Disabled> - <BlockEdit { ...props } /> - </Disabled> - ); + return <BlockEdit { ...props } isReadOnly />; } return ( - <BlockEdit { ...props } /> + <BlockEdit { ...props } isReadOnly={ false } /> ); }; }, 'withViewEditingMode' ); @@ -62,54 +57,29 @@ export const withViewEditingModeForBlockListBlock = createHigherOrderComponent( if ( viewEditingMode === 'focus' ) { if ( isAncestorOfPostBlock ) { - return ( - <BlockListBlock { ...props } - noInserters={ true } - noMovers={ true } - noToolbars={ true } - /> - ); + return <BlockListBlock { ...props } isReadOnly />; } if ( postBlockTypes.includes( name ) ) { - return ( - <BlockListBlock { ...props } - noInserters={ true } - noMovers={ true } - /> - ); + return <BlockListBlock { ...props } isReadOnly />; } if ( isDescendantOfPostBlock ) { - return <BlockListBlock { ...props } />; + return <BlockListBlock { ...props } isReadOnly={ false } />; } - return ( - <Disabled> - <BlockListBlock { ...props } /> - </Disabled> - ); + return <BlockListBlock { ...props } isReadOnly />; } if ( viewEditingMode === 'preview' ) { - return ( - <Disabled> - <BlockListBlock { ...props } /> - </Disabled> - ); + return <BlockListBlock { ...props } isReadOnly />; } if ( viewEditingMode === 'template' && isDescendantOfPostBlock ) { - return ( - <Disabled> - <BlockListBlock { ...props } /> - </Disabled> - ); + return <BlockListBlock { ...props } isReadOnly />; } - return ( - <BlockListBlock { ...props } /> - ); + return <BlockListBlock { ...props } isReadOnly={ false } />; }; }, 'withViewEditingMode' ); From fbb9dc87ff75caa83ab4aa6913881820d6936ddc Mon Sep 17 00:00:00 2001 From: epiqueras <epiquerass@gmail.com> Date: Mon, 22 Jul 2019 14:29:30 +0200 Subject: [PATCH 64/70] Fix demo template. --- lib/templates.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/templates.php b/lib/templates.php index fcf134ae83aa23..58926dcf39d9f8 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -94,14 +94,6 @@ function gutenberg_register_templates() { $template = $template[0]; } - if ( isset( $_GET['gutenberg-demo'] ) ) { - ob_start(); - echo '<!-- wp:post-title /--><!-- wp:post-content -->'; - include gutenberg_dir_path() . 'post-content.php'; - echo '<!-- /wp:post-content -->'; - $template->post_content = ob_get_clean(); - } - $post_type_object = get_post_type_object( 'post' ); $post_type_object->template_post = $template; } From 2e609620037414087a4ef1858d31793b35c127ed Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <miguelcsf@gmail.com> Date: Mon, 22 Jul 2019 14:36:38 +0200 Subject: [PATCH 65/70] Design mode: correct and widen grid lines --- packages/edit-post/src/components/layout/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index 9e282e89e178b6..f555e9f422deed 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -237,7 +237,7 @@ :root { --color: #000; /* The following is hard-coded for `gutenberg-theme` */ - --background-columns: linear-gradient(to right, var(--color), var(--color) 1px, transparent 1px, transparent 245px, var(--color) 245px, var(--color) 246px, transparent 246px, transparent 854px, var(--color) 854px, var(--color) 855px, transparent 855px, transparent 1099px, var(--color) 1099px, var(--color) 1100px, transparent 1100px); + --background-columns: linear-gradient(to right, var(--color), var(--color) 15px, transparent 16px, transparent 200px, var(--color) 200px, var(--color) 215px, transparent 215px, transparent 985px, var(--color) 985px, var(--color) 1000px, transparent 1000px, transparent 1184px, var(--color) 1184px, var(--color) 1200px, transparent 1200px); } .edit-post-layout.is-mode-design { From d3e6561e7037020c1c93fa11a681235593b0163b Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <miguelcsf@gmail.com> Date: Mon, 22 Jul 2019 14:43:22 +0200 Subject: [PATCH 66/70] Design mode: invert colours; correct width --- packages/edit-post/src/components/layout/style.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index f555e9f422deed..78d9027e4ec35a 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -235,14 +235,14 @@ /* Settings */ :root { - --color: #000; + --color: hsla(204, 80%, 72%, 0.15); /* The following is hard-coded for `gutenberg-theme` */ - --background-columns: linear-gradient(to right, var(--color), var(--color) 15px, transparent 16px, transparent 200px, var(--color) 200px, var(--color) 215px, transparent 215px, transparent 985px, var(--color) 985px, var(--color) 1000px, transparent 1000px, transparent 1184px, var(--color) 1184px, var(--color) 1200px, transparent 1200px); + --background-columns: linear-gradient(to right, transparent, transparent 15px, var(--color) 16px, var(--color) 200px, transparent 200px, transparent 215px, var(--color) 215px, var(--color) 985px, transparent 985px, transparent 1000px, var(--color) 1000px, var(--color) 1184px, transparent 1184px, transparent 1200px, var(--color) 1200px); } .edit-post-layout.is-mode-design { background-image: var(--background-columns); - background-size: 1100px 100%; + background-size: 1200px 100%; background-repeat: no-repeat; background-position-x: center; z-index: 1000; From 941fe2ba2d6715177b76eb0fd6faf34a41141626 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <miguelcsf@gmail.com> Date: Mon, 22 Jul 2019 14:49:31 +0200 Subject: [PATCH 67/70] Design mode: Background colour for lateral spaces --- packages/edit-post/src/components/layout/style.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index 78d9027e4ec35a..0545feb906d202 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -237,10 +237,11 @@ :root { --color: hsla(204, 80%, 72%, 0.15); /* The following is hard-coded for `gutenberg-theme` */ - --background-columns: linear-gradient(to right, transparent, transparent 15px, var(--color) 16px, var(--color) 200px, transparent 200px, transparent 215px, var(--color) 215px, var(--color) 985px, transparent 985px, transparent 1000px, var(--color) 1000px, var(--color) 1184px, transparent 1184px, transparent 1200px, var(--color) 1200px); + --background-columns: linear-gradient(to right, #fff, #fff 15px, var(--color) 16px, var(--color) 200px, #fff 200px, #fff 215px, var(--color) 215px, var(--color) 985px, #fff 985px, #fff 1000px, var(--color) 1000px, var(--color) 1184px, #fff 1184px, #fff 1200px, var(--color) 1200px); } .edit-post-layout.is-mode-design { + background-color: var(--color); background-image: var(--background-columns); background-size: 1200px 100%; background-repeat: no-repeat; From 36e28f958ccb6867fe005a01cfbf503f1b0a4cac Mon Sep 17 00:00:00 2001 From: iseulde <wp@iseulde.com> Date: Mon, 22 Jul 2019 15:20:15 +0200 Subject: [PATCH 68/70] Fix a bunch of locking stuff --- .../src/components/block-drop-zone/index.js | 10 ++++--- .../src/components/block-inspector/index.js | 7 ----- .../src/components/block-list/block.js | 30 ++++++------------- .../src/components/block-mover/index.js | 11 ++++--- .../components/inspector-controls/index.js | 10 +++++-- .../src/components/placeholder/index.js | 17 +++++++++-- .../src/components/plain-text/index.js | 14 ++++++++- 7 files changed, 58 insertions(+), 41 deletions(-) diff --git a/packages/block-editor/src/components/block-drop-zone/index.js b/packages/block-editor/src/components/block-drop-zone/index.js index f67f36848d1c94..df7cb9784c0197 100644 --- a/packages/block-editor/src/components/block-drop-zone/index.js +++ b/packages/block-editor/src/components/block-drop-zone/index.js @@ -111,8 +111,8 @@ class BlockDropZone extends Component { } render() { - const { isReadOnly, index } = this.props; - if ( isReadOnly ) { + const { isLockedAll, index } = this.props; + if ( isLockedAll ) { return null; } const isAppender = index === undefined; @@ -155,10 +155,12 @@ export default compose( }, }; } ), - withSelect( ( select ) => { - const { getClientIdsOfDescendants, getBlockIndex } = select( 'core/block-editor' ); + withSelect( ( select, { rootClientId, isReadOnly } ) => { + const { getClientIdsOfDescendants, getTemplateLock, getBlockIndex } = select( 'core/block-editor' ); + const templateLock = getTemplateLock( rootClientId ); return { + isLockedAll: isReadOnly ? templateLock === 'all' || templateLock === 'readonly' : isReadOnly, getClientIdsOfDescendants, getBlockIndex, }; diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 7def68fab330ed..df4a2bef80d365 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -21,7 +21,6 @@ import InspectorControls from '../inspector-controls'; import InspectorAdvancedControls from '../inspector-advanced-controls'; import BlockStyles from '../block-styles'; import MultiSelectionInspector from '../multi-selection-inspector'; -import { withBlockEditContext } from '../block-edit/context'; const BlockInspector = ( { blockType, @@ -30,12 +29,7 @@ const BlockInspector = ( { selectedBlockClientId, selectedBlockName, showNoBlockSelectedMessage = true, - isReadOnly, } ) => { - if ( isReadOnly ) { - return __( 'The selected block is read-only.' ); - } - if ( count > 1 ) { return <MultiSelectionInspector />; } @@ -98,7 +92,6 @@ const BlockInspector = ( { }; export default compose( [ - withBlockEditContext( ( { isReadOnly } ) => ( { isReadOnly } ) ), withSelect( ( select ) => { const { getSelectedBlockClientId, diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 89779aecf14ad6..bb6105b2f2882f 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -105,9 +105,6 @@ function BlockListBlock( { animateOnChange, enableAnimation, isAncestorOfPostBlock, - noInserters, - noMovers, - noToolbars, } ) { // Random state used to rerender the component if needed, ideally we don't need this const [ , updateRerenderState ] = useState( {} ); @@ -361,12 +358,8 @@ function BlockListBlock( { // If the block is selected and we're typing the block should not appear. // Empty paragraph blocks should always show up as unselected. - const showInserterShortcuts = - ! noInserters && - ( isSelected || isHovered ) && isEmptyDefaultBlock && isValid; - const showEmptyBlockSideInserter = - ! noInserters && - ( isSelected || isHovered || isLast ) && isEmptyDefaultBlock && isValid; + const showInserterShortcuts = ( isSelected || isHovered ) && isEmptyDefaultBlock && isValid; + const showEmptyBlockSideInserter = ( isSelected || isHovered || isLast ) && isEmptyDefaultBlock && isValid; const shouldAppearSelected = ! isFocusMode && ! showEmptyBlockSideInserter && @@ -379,7 +372,6 @@ function BlockListBlock( { ! isEmptyDefaultBlock; // We render block movers and block settings to keep them tabbale even if hidden const shouldRenderMovers = - ! noMovers && ( isSelected || hoverArea === ( isRTL ? 'right' : 'left' ) ) && ! showEmptyBlockSideInserter && ! isPartOfMultiSelection && @@ -387,23 +379,19 @@ function BlockListBlock( { const shouldShowBreadcrumb = ! isFocusMode && isHovered && ! isEmptyDefaultBlock; const shouldShowContextualToolbar = - ! noToolbars && ! hasFixedToolbar && ! showEmptyBlockSideInserter && ( ( isSelected && ( ! isTypingWithinBlock || isCaretWithinFormattedText ) ) || isFirstMultiSelected ); - const shouldShowMobileToolbar = ! noToolbars && shouldAppearSelected; + const shouldShowMobileToolbar = shouldAppearSelected; // Insertion point can only be made visible if the block is at the // the extent of a multi-selection, or not in a multi-selection. const shouldShowInsertionPoint = - ! noInserters && - ( - ( isPartOfMultiSelection && isFirstMultiSelected ) || - ! isPartOfMultiSelection - ); + ( isPartOfMultiSelection && isFirstMultiSelected ) || + ! isPartOfMultiSelection; // The wp-block className is important for editor styles. // Generate the wrapper class names handling the different states of the block. @@ -514,7 +502,7 @@ function BlockListBlock( { } onDragStart={ onDragStart } onDragEnd={ onDragEnd } - isReadOnly + isReadOnly={ isReadOnly } /> ) } { shouldShowBreadcrumb && ( @@ -644,9 +632,9 @@ const applyWithSelect = withSelect( initialPosition: isSelected ? getSelectedBlocksInitialCaretPosition() : null, isEmptyDefaultBlock: name && isUnmodifiedDefaultBlock( { name, attributes } ), - isMovable: templateLock !== 'all' && templateLock !== 'readonly', - isLocked: !! templateLock, - isReadOnly: isReadOnly === undefined && templateLock === 'readonly', + isMovable: isReadOnly === undefined ? templateLock !== 'all' && templateLock !== 'readonly' : ! isReadOnly, + isLocked: isReadOnly === undefined ? !! templateLock : isReadOnly, + isReadOnly: isReadOnly === undefined ? templateLock === 'readonly' : isReadOnly, isFocusMode: focusMode && isLargeViewport, hasFixedToolbar: hasFixedToolbar && isLargeViewport, isLast: index === blockOrder.length - 1, diff --git a/packages/block-editor/src/components/block-mover/index.js b/packages/block-editor/src/components/block-mover/index.js index 931c731d969928..4c14ae6384acaa 100644 --- a/packages/block-editor/src/components/block-mover/index.js +++ b/packages/block-editor/src/components/block-mover/index.js @@ -44,10 +44,11 @@ export class BlockMover extends Component { } render() { - const { onMoveUp, onMoveDown, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, isReadOnly, instanceId, isHidden, rootClientId } = this.props; + const { onMoveUp, onMoveDown, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, isLocked, instanceId, isHidden, rootClientId } = this.props; const { isFocused } = this.state; const blocksCount = castArray( clientIds ).length; - if ( isReadOnly || ( isFirst && isLast && ! rootClientId ) ) { + + if ( isLocked || ( isFirst && isLast && ! rootClientId ) ) { return null; } @@ -116,8 +117,8 @@ export class BlockMover extends Component { } export default compose( - withSelect( ( select, { clientIds } ) => { - const { getBlock, getBlockIndex, getBlockRootClientId, getBlockOrder } = select( 'core/block-editor' ); + withSelect( ( select, { clientIds, isReadOnly } ) => { + const { getBlock, getBlockIndex, getTemplateLock, getBlockRootClientId, getBlockOrder } = select( 'core/block-editor' ); const normalizedClientIds = castArray( clientIds ); const firstClientId = first( normalizedClientIds ); const block = getBlock( firstClientId ); @@ -125,9 +126,11 @@ export default compose( const blockOrder = getBlockOrder( rootClientId ); const firstIndex = getBlockIndex( firstClientId, rootClientId ); const lastIndex = getBlockIndex( last( normalizedClientIds ), rootClientId ); + const templateLock = getTemplateLock( rootClientId ); return { blockType: block ? getBlockType( block.name ) : null, + isLocked: isReadOnly === undefined ? templateLock === 'all' || templateLock === 'readonly' : isReadOnly, rootClientId, firstIndex, isFirst: firstIndex === 0, diff --git a/packages/block-editor/src/components/inspector-controls/index.js b/packages/block-editor/src/components/inspector-controls/index.js index b97df2f8d245ee..b7822226b6cbdf 100644 --- a/packages/block-editor/src/components/inspector-controls/index.js +++ b/packages/block-editor/src/components/inspector-controls/index.js @@ -6,11 +6,17 @@ import { createSlotFill } from '@wordpress/components'; /** * Internal dependencies */ -import { ifBlockEditSelected } from '../block-edit/context'; +import { ifBlockEditSelected, withBlockEditContext } from '../block-edit/context'; const { Fill, Slot } = createSlotFill( 'InspectorControls' ); -const InspectorControls = ifBlockEditSelected( Fill ); +const InspectorControls = withBlockEditContext( ( { isReadOnly } ) => ( { isReadOnly } ) )( ifBlockEditSelected( ( { isReadOnly, ...props } ) => { + if ( isReadOnly ) { + return null; + } + + return <Fill { ...props } />; +} ) ); InspectorControls.Slot = Slot; diff --git a/packages/block-editor/src/components/placeholder/index.js b/packages/block-editor/src/components/placeholder/index.js index 22bdd7677e9fd3..808500eb7ec08d 100644 --- a/packages/block-editor/src/components/placeholder/index.js +++ b/packages/block-editor/src/components/placeholder/index.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { Placeholder } from '@wordpress/components'; +import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; /** @@ -10,7 +11,7 @@ import { compose } from '@wordpress/compose'; import { withBlockEditContext } from '../block-edit/context'; function PlaceholderInBlockContext( { isReadOnly, ...props } ) { - if ( ! isReadOnly ) { + if ( isReadOnly ) { return null; } @@ -20,5 +21,17 @@ function PlaceholderInBlockContext( { isReadOnly, ...props } ) { } export default compose( [ - withBlockEditContext( ( { isReadOnly } ) => ( { isReadOnly } ) ), + withBlockEditContext( ( { clientId, isReadOnly } ) => ( { clientId, isReadOnly } ) ), + withSelect( ( select, { clientId, isReadOnly } ) => { + const { + getBlockRootClientId, + getTemplateLock, + } = select( 'core/block-editor' ); + const rootClientId = getBlockRootClientId( clientId ); + const templateLock = getTemplateLock( rootClientId ); + + return { + isReadOnly: isReadOnly === undefined ? templateLock === 'readonly' : isReadOnly, + }; + } ), ] )( PlaceholderInBlockContext ); diff --git a/packages/block-editor/src/components/plain-text/index.js b/packages/block-editor/src/components/plain-text/index.js index 3828f2bbea21f6..c8abba7db825da 100644 --- a/packages/block-editor/src/components/plain-text/index.js +++ b/packages/block-editor/src/components/plain-text/index.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; +import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; /** @@ -32,5 +33,16 @@ const PlainText = forwardRef( ( { onChange, className, isReadOnly, ...props }, r * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/plain-text/README.md */ export default compose( [ - withBlockEditContext( ( { isReadOnly } ) => ( { isReadOnly } ) ), + withBlockEditContext( ( { clientId, isReadOnly } ) => ( { clientId, isReadOnly } ) ), + withSelect( ( select, { clientId, isReadOnly } ) => { + const { + getBlockRootClientId, + getTemplateLock, + } = select( 'core/block-editor' ); + const rootClientId = getBlockRootClientId( clientId ); + const templateLock = getTemplateLock( rootClientId ); + return { + isReadOnly: isReadOnly === undefined ? templateLock === 'readonly' : isReadOnly, + }; + } ), ] )( PlainText ); From e0f5ad4acbcce7a2791884d0bacf9ab0ef2f2a53 Mon Sep 17 00:00:00 2001 From: Jorge <jorge.costa@developer.pt> Date: Mon, 22 Jul 2019 15:27:19 +0200 Subject: [PATCH 69/70] Add template part display name logic --- .../block-editor/src/components/block-navigation/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-navigation/index.js b/packages/block-editor/src/components/block-navigation/index.js index b0542caa3d6f39..3b009f26587ccb 100644 --- a/packages/block-editor/src/components/block-navigation/index.js +++ b/packages/block-editor/src/components/block-navigation/index.js @@ -35,6 +35,11 @@ function BlockNavigationList( { const blockType = getBlockType( block.name ); const isSelected = block.clientId === selectedBlockClientId; + let blockDisplayName = blockType.title; + if ( block.name === 'core/template-part' && block.attributes.name ) { + blockDisplayName = `Template: ${ block.attributes.name }`; + } + return ( <li key={ block.clientId }> <div className="editor-block-navigation__item block-editor-block-navigation__item"> @@ -45,7 +50,7 @@ function BlockNavigationList( { onClick={ () => selectBlock( block.clientId ) } > <BlockIcon icon={ blockType.icon } showColors /> - { blockType.title } + { blockDisplayName } { isSelected && <span className="screen-reader-text">{ __( '(selected block)' ) }</span> } </Button> </div> From d4f48403611fb6812af61eff789fcaf71592bf8a Mon Sep 17 00:00:00 2001 From: iseulde <wp@iseulde.com> Date: Mon, 22 Jul 2019 15:58:27 +0200 Subject: [PATCH 70/70] Locking: fix problem with movers --- .../src/components/block-drop-zone/index.js | 11 ++---- .../src/components/block-list/block.js | 36 +++++++++++-------- .../src/components/block-mover/index.js | 10 +++--- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/packages/block-editor/src/components/block-drop-zone/index.js b/packages/block-editor/src/components/block-drop-zone/index.js index df7cb9784c0197..9178bcbf343af2 100644 --- a/packages/block-editor/src/components/block-drop-zone/index.js +++ b/packages/block-editor/src/components/block-drop-zone/index.js @@ -111,10 +111,7 @@ class BlockDropZone extends Component { } render() { - const { isLockedAll, index } = this.props; - if ( isLockedAll ) { - return null; - } + const { index } = this.props; const isAppender = index === undefined; return ( @@ -155,12 +152,10 @@ export default compose( }, }; } ), - withSelect( ( select, { rootClientId, isReadOnly } ) => { - const { getClientIdsOfDescendants, getTemplateLock, getBlockIndex } = select( 'core/block-editor' ); - const templateLock = getTemplateLock( rootClientId ); + withSelect( ( select ) => { + const { getClientIdsOfDescendants, getBlockIndex } = select( 'core/block-editor' ); return { - isLockedAll: isReadOnly ? templateLock === 'all' || templateLock === 'readonly' : isReadOnly, getClientIdsOfDescendants, getBlockIndex, }; diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index bb6105b2f2882f..e234193d76b968 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -482,27 +482,22 @@ function BlockListBlock( { rootClientId={ rootClientId } /> ) } - <BlockDropZone + { isLocked && <BlockDropZone clientId={ clientId } rootClientId={ rootClientId } - isReadOnly={ isReadOnly } - /> + /> } { isFirstMultiSelected && ( <BlockMultiControls rootClientId={ rootClientId } /> ) } <div className="editor-block-list__block-edit block-editor-block-list__block-edit"> - { shouldRenderMovers && ( + { isMovable && shouldRenderMovers && ( <BlockMover clientIds={ clientId } blockElementId={ blockElementId } isHidden={ ! ( isHovered || isSelected ) || hoverArea !== ( isRTL ? 'right' : 'left' ) } - isDraggable={ - isDraggable !== false && - ( ! isPartOfMultiSelection && isMovable ) - } + isDraggable={ isDraggable !== false && ! isPartOfMultiSelection } onDragStart={ onDragStart } onDragEnd={ onDragEnd } - isReadOnly={ isReadOnly } /> ) } { shouldShowBreadcrumb && ( @@ -585,8 +580,23 @@ function BlockListBlock( { /* eslint-enable jsx-a11y/mouse-events-have-key-events, jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */ } +const applyWithSelectAfterFilters = withSelect( + ( select, { rootClientId, isReadOnly } ) => { + const { + getTemplateLock, + } = select( 'core/block-editor' ); + const templateLock = getTemplateLock( rootClientId ); + + return { + isMovable: isReadOnly === undefined ? templateLock !== 'all' && templateLock !== 'readonly' : ! isReadOnly, + isLocked: isReadOnly === undefined ? !! templateLock : isReadOnly, + isReadOnly: isReadOnly === undefined ? templateLock === 'readonly' : isReadOnly, + }; + } +); + const applyWithSelect = withSelect( - ( select, { clientId, rootClientId, isLargeViewport, isReadOnly } ) => { + ( select, { clientId, rootClientId, isLargeViewport } ) => { const { isBlockSelected, isAncestorMultiSelected, @@ -599,7 +609,6 @@ const applyWithSelect = withSelect( getSelectedBlocksInitialCaretPosition, getSettings, hasSelectedInnerBlock, - getTemplateLock, getBlockIndex, getBlockOrder, __unstableGetBlockWithoutInnerBlocks, @@ -608,7 +617,6 @@ const applyWithSelect = withSelect( const block = __unstableGetBlockWithoutInnerBlocks( clientId ); const isSelected = isBlockSelected( clientId ); const { hasFixedToolbar, focusMode, isRTL } = getSettings(); - const templateLock = getTemplateLock( rootClientId ); const isParentOfSelectedBlock = hasSelectedInnerBlock( clientId, true ); const index = getBlockIndex( clientId, rootClientId ); const blockOrder = getBlockOrder( rootClientId ); @@ -632,9 +640,6 @@ const applyWithSelect = withSelect( initialPosition: isSelected ? getSelectedBlocksInitialCaretPosition() : null, isEmptyDefaultBlock: name && isUnmodifiedDefaultBlock( { name, attributes } ), - isMovable: isReadOnly === undefined ? templateLock !== 'all' && templateLock !== 'readonly' : ! isReadOnly, - isLocked: isReadOnly === undefined ? !! templateLock : isReadOnly, - isReadOnly: isReadOnly === undefined ? templateLock === 'readonly' : isReadOnly, isFocusMode: focusMode && isLargeViewport, hasFixedToolbar: hasFixedToolbar && isLargeViewport, isLast: index === blockOrder.length - 1, @@ -753,4 +758,5 @@ export default compose( applyWithSelect, applyWithDispatch, withFilters( 'editor.BlockListBlock' ), + applyWithSelectAfterFilters, )( BlockListBlock ); diff --git a/packages/block-editor/src/components/block-mover/index.js b/packages/block-editor/src/components/block-mover/index.js index 4c14ae6384acaa..2251fe34b8d732 100644 --- a/packages/block-editor/src/components/block-mover/index.js +++ b/packages/block-editor/src/components/block-mover/index.js @@ -44,11 +44,11 @@ export class BlockMover extends Component { } render() { - const { onMoveUp, onMoveDown, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, isLocked, instanceId, isHidden, rootClientId } = this.props; + const { onMoveUp, onMoveDown, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, blockType, firstIndex, instanceId, isHidden, rootClientId } = this.props; const { isFocused } = this.state; const blocksCount = castArray( clientIds ).length; - if ( isLocked || ( isFirst && isLast && ! rootClientId ) ) { + if ( isFirst && isLast && ! rootClientId ) { return null; } @@ -117,8 +117,8 @@ export class BlockMover extends Component { } export default compose( - withSelect( ( select, { clientIds, isReadOnly } ) => { - const { getBlock, getBlockIndex, getTemplateLock, getBlockRootClientId, getBlockOrder } = select( 'core/block-editor' ); + withSelect( ( select, { clientIds } ) => { + const { getBlock, getBlockIndex, getBlockRootClientId, getBlockOrder } = select( 'core/block-editor' ); const normalizedClientIds = castArray( clientIds ); const firstClientId = first( normalizedClientIds ); const block = getBlock( firstClientId ); @@ -126,11 +126,9 @@ export default compose( const blockOrder = getBlockOrder( rootClientId ); const firstIndex = getBlockIndex( firstClientId, rootClientId ); const lastIndex = getBlockIndex( last( normalizedClientIds ), rootClientId ); - const templateLock = getTemplateLock( rootClientId ); return { blockType: block ? getBlockType( block.name ) : null, - isLocked: isReadOnly === undefined ? templateLock === 'all' || templateLock === 'readonly' : isReadOnly, rootClientId, firstIndex, isFirst: firstIndex === 0,