diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index eb132d03c1d18..761fad23254ec 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -177,7 +177,7 @@ An advanced block that allows displaying post comments using different visual co - **Name:** core/comments - **Category:** theme - **Supports:** align (full, wide), color (background, gradients, link, text), ~~html~~ -- **Attributes:** tagName +- **Attributes:** legacy, tagName ## Comments Pagination @@ -485,15 +485,6 @@ This block is deprecated. Please use the Comments block instead. ([Source](https - **Supports:** ~~html~~, ~~inserter~~ - **Attributes:** commentId -## Post Comments (deprecated) - -This block is deprecated. Please use the Comments block instead. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/post-comments)) - -- **Name:** core/post-comments -- **Category:** theme -- **Supports:** align (full, wide), color (background, gradients, link, text), typography (fontSize, lineHeight), ~~html~~, ~~inserter~~ -- **Attributes:** textAlign - ## Post Comments Count Display a post's comments count. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/post-comments-count)) diff --git a/lib/blocks.php b/lib/blocks.php index 18a9eb34ff166..aa0d82c7795d4 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -63,6 +63,7 @@ function gutenberg_reregister_core_block_types() { 'comments-pagination-numbers.php' => 'core/comments-pagination-numbers', 'comments-pagination-previous.php' => 'core/comments-pagination-previous', 'comments-title.php' => 'core/comments-title', + 'comments.php' => 'core/comments', 'file.php' => 'core/file', 'home-link.php' => 'core/home-link', 'image.php' => 'core/image', @@ -79,7 +80,6 @@ function gutenberg_reregister_core_block_types() { 'post-author-name.php' => 'core/post-author-name', 'post-author-biography.php' => 'core/post-author-biography', 'post-comment.php' => 'core/post-comment', - 'post-comments.php' => 'core/post-comments', 'post-comments-count.php' => 'core/post-comments-count', 'post-comments-form.php' => 'core/post-comments-form', 'post-comments-link.php' => 'core/post-comments-link', diff --git a/packages/block-library/src/comments/block.json b/packages/block-library/src/comments/block.json index 582de73d8d0e9..042184b9b8d08 100644 --- a/packages/block-library/src/comments/block.json +++ b/packages/block-library/src/comments/block.json @@ -10,6 +10,10 @@ "tagName": { "type": "string", "default": "div" + }, + "legacy": { + "type": "boolean", + "default": false } }, "supports": { @@ -25,5 +29,6 @@ } } }, - "editorStyle": "wp-block-comments-editor" + "editorStyle": "wp-block-comments-editor", + "usesContext": [ "postId", "postType" ] } diff --git a/packages/block-library/src/comments/edit/comments-legacy.js b/packages/block-library/src/comments/edit/comments-legacy.js new file mode 100644 index 0000000000000..4d629a857e652 --- /dev/null +++ b/packages/block-library/src/comments/edit/comments-legacy.js @@ -0,0 +1,71 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { + AlignmentControl, + BlockControls, + Warning, + useBlockProps, +} from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; +import { Button } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import Placeholder from './placeholder'; + +export default function CommentsLegacy( { + attributes, + setAttributes, + context: { postType, postId }, +} ) { + const { textAlign } = attributes; + + const actions = [ + , + ]; + + const blockProps = useBlockProps( { + className: classnames( { + [ `has-text-align-${ textAlign }` ]: textAlign, + } ), + } ); + + return ( + <> + + { + setAttributes( { textAlign: nextAlign } ); + } } + /> + + +
+ + { __( + "Comments block: You're currently using this block in legacy mode. " + + 'The following is just a placeholder, not a real comment. ' + + 'The final styling may differ because it also depends on the current theme. ' + + 'For better compatibility with the Block Editor, ' + + 'please consider switching the block to its editable mode.' + ) } + + +
+ + ); +} diff --git a/packages/block-library/src/comments/edit/index.js b/packages/block-library/src/comments/edit/index.js new file mode 100644 index 0000000000000..8d8db650349d8 --- /dev/null +++ b/packages/block-library/src/comments/edit/index.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies + */ +import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import CommentsInspectorControls from './comments-inspector-controls'; +import CommentsLegacy from './comments-legacy'; +import TEMPLATE from './template'; + +export default function CommentsEdit( props ) { + const { attributes, setAttributes } = props; + const { tagName: TagName, legacy } = attributes; + + const blockProps = useBlockProps(); + const innerBlocksProps = useInnerBlocksProps( blockProps, { + template: TEMPLATE, + } ); + + if ( legacy ) { + return ; + } + + return ( + <> + + + + ); +} diff --git a/packages/block-library/src/comments/edit/placeholder.js b/packages/block-library/src/comments/edit/placeholder.js new file mode 100644 index 0000000000000..837561aad15c0 --- /dev/null +++ b/packages/block-library/src/comments/edit/placeholder.js @@ -0,0 +1,124 @@ +/** + * WordPress dependencies + */ +import { store as blockEditorStore } from '@wordpress/block-editor'; +import { __, sprintf } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { useEntityProp } from '@wordpress/core-data'; +import { useDisabled } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import CommentsForm from '../../post-comments-form/form'; + +export default function PostCommentsPlaceholder( { postType, postId } ) { + let [ postTitle ] = useEntityProp( 'postType', postType, 'title', postId ); + postTitle = postTitle || __( 'Post Title' ); + + const { avatarURL } = useSelect( + ( select ) => + select( blockEditorStore ).getSettings() + .__experimentalDiscussionSettings + ); + + const disabledRef = useDisabled(); + + return ( +
+

+ { + /* translators: %s: Post title. */ + sprintf( __( 'One response to %s' ), postTitle ) + } +

+ +
+ + +
+ +
    +
  1. + +
  2. +
+ +
+ + +
+ + +
+ ); +} diff --git a/packages/block-library/src/comments/edit.js b/packages/block-library/src/comments/edit/template.js similarity index 66% rename from packages/block-library/src/comments/edit.js rename to packages/block-library/src/comments/edit/template.js index 7b4533e228eed..18897325027c4 100644 --- a/packages/block-library/src/comments/edit.js +++ b/packages/block-library/src/comments/edit/template.js @@ -1,13 +1,3 @@ -/** - * WordPress dependencies - */ -import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; - -/** - * Internal dependencies - */ -import CommentsInspectorControls from './edit/comments-inspector-controls'; - const TEMPLATE = [ [ 'core/comments-title' ], [ @@ -88,21 +78,4 @@ const TEMPLATE = [ [ 'core/post-comments-form' ], ]; -export default function CommentsEdit( { attributes, setAttributes } ) { - const { tagName: TagName } = attributes; - - const blockProps = useBlockProps(); - const innerBlocksProps = useInnerBlocksProps( blockProps, { - template: TEMPLATE, - } ); - - return ( - <> - - - - ); -} +export default TEMPLATE; diff --git a/packages/block-library/src/comments/editor.scss b/packages/block-library/src/comments/editor.scss index 6e21a9899421e..2d7b20ad5ca3c 100644 --- a/packages/block-library/src/comments/editor.scss +++ b/packages/block-library/src/comments/editor.scss @@ -1,3 +1,12 @@ +@import "./style.scss"; + .block-library-comments-toolbar__popover .components-popover__content { min-width: 230px; } + +.wp-block-comments__legacy-placeholder { + @extend .wp-block-post-comments; + * { + pointer-events: none; + } +} diff --git a/packages/block-library/src/comments/index.php b/packages/block-library/src/comments/index.php new file mode 100644 index 0000000000000..5df2ed550a21e --- /dev/null +++ b/packages/block-library/src/comments/index.php @@ -0,0 +1,219 @@ +name || ! empty( $attributes['legacy'] ); + if ( ! $is_legacy ) { + return $block->render( array( 'dynamic' => false ) ); + } + + $post_id = $block->context['postId']; + if ( ! isset( $post_id ) ) { + return ''; + } + + $comment_args = array( + 'post_id' => $post_id, + 'count' => true, + 'status' => 'approve', + ); + // Return early if there are no comments and comments are closed. + if ( ! comments_open( $post_id ) && get_comments( $comment_args ) === 0 ) { + return ''; + } + + $post_before = $post; + $post = get_post( $post_id ); + setup_postdata( $post ); + + ob_start(); + + /* + * There's a deprecation warning generated by WP Core. + * Ideally this deprecation is removed from Core. + * In the meantime, this removes it from the output. + */ + add_filter( 'deprecated_file_trigger_error', '__return_false' ); + comments_template(); + remove_filter( 'deprecated_file_trigger_error', '__return_false' ); + + $output = ob_get_clean(); + $post = $post_before; + + $classnames = array(); + // Adds the old class name for styles' backwards compatibility. + if ( isset( $attributes['legacy'] ) ) { + $classnames[] = 'wp-block-post-comments'; + } + if ( isset( $attributes['textAlign'] ) ) { + $classnames[] = 'has-text-align-' . $attributes['textAlign']; + } + + $wrapper_attributes = get_block_wrapper_attributes( + array( 'class' => implode( ' ', $classnames ) ) + ); + + /* + * Enqueues scripts and styles required only for the legacy version. That is + * why they are not defined in `block.json`. + */ + wp_enqueue_script( 'comment-reply' ); + enqueue_legacy_post_comments_block_styles( $block->name ); + + return sprintf( '
%2$s
', $wrapper_attributes, $output ); +} + +/** + * Registers the `core/comments` block on the server. + */ +function register_block_core_comments() { + register_block_type_from_metadata( + __DIR__ . '/comments', + array( + 'render_callback' => 'render_block_core_comments', + 'skip_inner_blocks' => true, + ) + ); +} +add_action( 'init', 'register_block_core_comments' ); + +/** + * Use the button block classes for the form-submit button. + * + * @param array $fields The default comment form arguments. + * + * @return array Returns the modified fields. + */ +function comments_block_form_defaults( $fields ) { + if ( wp_is_block_theme() ) { + $fields['submit_button'] = ''; + $fields['submit_field'] = '

%1$s %2$s

'; + } + + return $fields; +} +add_filter( 'comment_form_defaults', 'comments_block_form_defaults' ); + +/** + * Enqueues styles from the legacy `core/post-comments` block. These styles are + * required only by the block's fallback. + * + * @param string $block_name Name of the new block type. + */ +function enqueue_legacy_post_comments_block_styles( $block_name ) { + static $are_styles_enqueued = false; + + if ( ! $are_styles_enqueued ) { + $handles = array( + 'wp-block-post-comments', + 'wp-block-buttons', + 'wp-block-button', + ); + foreach ( $handles as $handle ) { + wp_enqueue_block_style( $block_name, array( 'handle' => $handle ) ); + } + $are_styles_enqueued = true; + } +} + +/** + * Ensures backwards compatibility for any users running the Gutenberg plugin + * who have used Post Comments before it was merged into Comments Query Loop. + * + * The same approach was followed when core/query-loop was renamed to + * core/post-template. + * + * @see https://github.com/WordPress/gutenberg/pull/41807 + * @see https://github.com/WordPress/gutenberg/pull/32514 + */ +function register_legacy_post_comments_block() { + $registry = WP_Block_Type_Registry::get_instance(); + + /* + * Remove the old `post-comments` block if it was already registered, as it + * is about to be replaced by the type defined below. + */ + if ( $registry->is_registered( 'core/post-comments' ) ) { + unregister_block_type( 'core/post-comments' ); + } + + // Recreate the legacy block metadata. + $metadata = array( + 'name' => 'core/post-comments', + 'category' => 'theme', + 'attributes' => array( + 'textAlign' => array( + 'type' => 'string', + ), + ), + 'uses_context' => array( + 'postId', + 'postType', + ), + 'supports' => array( + 'html' => false, + 'align' => array( 'wide', 'full' ), + 'typography' => array( + 'fontSize' => true, + 'lineHeight' => true, + '__experimentalFontStyle' => true, + '__experimentalFontWeight' => true, + '__experimentalLetterSpacing' => true, + '__experimentalTextTransform' => true, + '__experimentalDefaultControls' => array( + 'fontSize' => true, + ), + ), + 'color' => array( + 'gradients' => true, + 'link' => true, + '__experimentalDefaultControls' => array( + 'background' => true, + 'text' => true, + ), + ), + 'inserter' => false, + ), + 'style' => array( + 'wp-block-post-comments', + 'wp-block-buttons', + 'wp-block-button', + ), + 'editorStyle' => 'wp-block-post-comments-editor', + 'render_callback' => 'render_block_core_comments', + 'skip_inner_blocks' => true, + ); + + /* + * Filters the metadata object, the same way it's done inside + * `register_block_type_from_metadata()`. This applies some default filters, + * like `_wp_multiple_block_styles`, which is required in this case because + * the block has multiple styles. + */ + $metadata = apply_filters( 'block_type_metadata', $metadata ); + + register_block_type( 'core/post-comments', $metadata ); +} +add_action( 'init', 'register_legacy_post_comments_block', 21 ); diff --git a/packages/block-library/src/comments/save.js b/packages/block-library/src/comments/save.js index 47c774fc4b9bb..14a8309eb92c4 100644 --- a/packages/block-library/src/comments/save.js +++ b/packages/block-library/src/comments/save.js @@ -1,12 +1,13 @@ /** * WordPress dependencies */ -import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; +import { useInnerBlocksProps, useBlockProps } from '@wordpress/block-editor'; -export default function CommentsSave( { attributes: { tagName: Tag } } ) { - return ( - - - - ); +export default function save( { attributes: { tagName: Tag, legacy } } ) { + const blockProps = useBlockProps.save(); + const innerBlocksProps = useInnerBlocksProps.save( blockProps ); + + // The legacy version is dynamic (i.e. PHP rendered) and doesn't allow inner + // blocks, so nothing is saved in that case. + return legacy ? null : ; } diff --git a/packages/block-library/src/post-comments/style.scss b/packages/block-library/src/comments/style.scss similarity index 96% rename from packages/block-library/src/post-comments/style.scss rename to packages/block-library/src/comments/style.scss index ebcf192d898b1..732e42d5bcaa4 100644 --- a/packages/block-library/src/post-comments/style.scss +++ b/packages/block-library/src/comments/style.scss @@ -1,4 +1,6 @@ +/* Styles for backwards compatibility with the legacy `post-comments` block */ .wp-block-post-comments { + /* utility classes */ .alignleft { float: left; @@ -7,6 +9,7 @@ .alignright { float: right; } + /* end utility classes */ .navigation { @@ -62,9 +65,11 @@ .comment-meta { font-size: 0.875em; line-height: 1.5; + b { font-weight: normal; } + .comment-awaiting-moderation { margin-top: 1em; margin-bottom: 1em; @@ -87,6 +92,7 @@ } .comment-form { + textarea, input:not([type="submit"]):not([type="checkbox"]) { display: block; @@ -106,6 +112,7 @@ .comment-reply-title { margin-bottom: 0; + :where(small) { font-size: var(--wp--preset--font-size--medium, smaller); margin-left: 0.5em; diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss index ba031d6cbdeaa..138e480263021 100644 --- a/packages/block-library/src/editor.scss +++ b/packages/block-library/src/editor.scss @@ -49,7 +49,6 @@ @import "./query-pagination/editor.scss"; @import "./query-pagination-numbers/editor.scss"; @import "./post-featured-image/editor.scss"; -@import "./post-comments/editor.scss"; @import "./post-comments-form/editor.scss"; :root .editor-styles-wrapper { diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index ff72f0b90d101..f0d5924d7248d 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -73,7 +73,6 @@ import * as postAuthor from './post-author'; import * as postAuthorName from './post-author-name'; import * as postAuthorBiography from './post-author-biography'; import * as postComment from './post-comment'; -import * as postComments from './post-comments'; import * as postCommentsCount from './post-comments-count'; import * as postCommentsForm from './post-comments-form'; import * as postCommentsLink from './post-comments-link'; @@ -228,7 +227,6 @@ const getAllBlocks = () => [ commentsPaginationNext, commentsPaginationNumbers, commentsPaginationPrevious, - postComments, postCommentsForm, tableOfContents, homeLink, diff --git a/packages/block-library/src/post-comments-form/edit.js b/packages/block-library/src/post-comments-form/edit.js index f776a16090987..56eab195b4a29 100644 --- a/packages/block-library/src/post-comments-form/edit.js +++ b/packages/block-library/src/post-comments-form/edit.js @@ -9,14 +9,8 @@ import classnames from 'classnames'; import { AlignmentControl, BlockControls, - Warning, useBlockProps, - store as blockEditorStore, } from '@wordpress/block-editor'; -import { Button } from '@wordpress/components'; -import { useEntityProp, store as coreStore } from '@wordpress/core-data'; -import { __, _x, sprintf } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; /** * Internal dependencies @@ -30,72 +24,13 @@ export default function PostCommentsFormEdit( { } ) { const { textAlign } = attributes; const { postId, postType } = context; - const [ commentStatus, setCommentStatus ] = useEntityProp( - 'postType', - postType, - 'comment_status', - postId - ); + const blockProps = useBlockProps( { className: classnames( { [ `has-text-align-${ textAlign }` ]: textAlign, } ), } ); - const isSiteEditor = postType === undefined || postId === undefined; - - const { defaultCommentStatus } = useSelect( - ( select ) => - select( blockEditorStore ).getSettings() - .__experimentalDiscussionSettings - ); - - const postTypeSupportsComments = useSelect( ( select ) => - postType - ? !! select( coreStore ).getPostType( postType )?.supports.comments - : false - ); - - let warning = false; - let actions; - let showPlaceholder = true; - - if ( ! isSiteEditor && 'open' !== commentStatus ) { - if ( 'closed' === commentStatus ) { - warning = __( - 'Post Comments Form block: Comments are not enabled for this item.' - ); - - actions = [ - , - ]; - showPlaceholder = false; - } else if ( ! postTypeSupportsComments ) { - warning = sprintf( - /* translators: 1: Post type (i.e. "post", "page") */ - __( - 'Post Comments Form block: Comments are not enabled for this post type (%s).' - ), - postType - ); - showPlaceholder = false; - } else if ( 'open' !== defaultCommentStatus ) { - warning = __( - 'Post Comments Form block: Comments are not enabled.' - ); - showPlaceholder = false; - } - } - return ( <> @@ -107,11 +42,7 @@ export default function PostCommentsFormEdit( { />
- { warning && ( - { warning } - ) } - - { showPlaceholder ? : null } +
); diff --git a/packages/block-library/src/post-comments-form/form.js b/packages/block-library/src/post-comments-form/form.js index 48a38d8e8a562..00de2cb418d5d 100644 --- a/packages/block-library/src/post-comments-form/form.js +++ b/packages/block-library/src/post-comments-form/form.js @@ -6,13 +6,20 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; -import { __experimentalGetElementClassName } from '@wordpress/block-editor'; +import { __, _x, sprintf } from '@wordpress/i18n'; +import { + Warning, + store as blockEditorStore, + __experimentalGetElementClassName, +} from '@wordpress/block-editor'; +import { Button } from '@wordpress/components'; import { useDisabled, useInstanceId } from '@wordpress/compose'; +import { useEntityProp, store as coreStore } from '@wordpress/core-data'; +import { useSelect } from '@wordpress/data'; -const CommentsForm = () => { +const CommentsFormPlaceholder = () => { const disabledFormRef = useDisabled(); - const instanceId = useInstanceId( CommentsForm ); + const instanceId = useInstanceId( CommentsFormPlaceholder ); return (
@@ -47,4 +54,73 @@ const CommentsForm = () => { ); }; +const CommentsForm = ( { postId, postType } ) => { + const [ commentStatus, setCommentStatus ] = useEntityProp( + 'postType', + postType, + 'comment_status', + postId + ); + + const isSiteEditor = postType === undefined || postId === undefined; + + const { defaultCommentStatus } = useSelect( + ( select ) => + select( blockEditorStore ).getSettings() + .__experimentalDiscussionSettings + ); + + const postTypeSupportsComments = useSelect( ( select ) => + postType + ? !! select( coreStore ).getPostType( postType )?.supports.comments + : false + ); + + if ( ! isSiteEditor && 'open' !== commentStatus ) { + if ( 'closed' === commentStatus ) { + const actions = [ + , + ]; + return ( + + { __( + 'Post Comments Form block: Comments are not enabled for this item.' + ) } + + ); + } else if ( ! postTypeSupportsComments ) { + return ( + + { sprintf( + /* translators: 1: Post type (i.e. "post", "page") */ + __( + 'Post Comments Form block: Comments are not enabled for this post type (%s).' + ), + postType + ) } + + ); + } else if ( 'open' !== defaultCommentStatus ) { + return ( + + { __( + 'Post Comments Form block: Comments are not enabled.' + ) } + + ); + } + } + + return ; +}; + export default CommentsForm; diff --git a/packages/block-library/src/post-comments/block.json b/packages/block-library/src/post-comments/block.json deleted file mode 100644 index dc719fff5d708..0000000000000 --- a/packages/block-library/src/post-comments/block.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, - "name": "core/post-comments", - "title": "Post Comments (deprecated)", - "category": "theme", - "description": "This block is deprecated. Please use the Comments block instead.", - "textdomain": "default", - "attributes": { - "textAlign": { - "type": "string" - } - }, - "usesContext": [ "postId", "postType" ], - "supports": { - "html": false, - "align": [ "wide", "full" ], - "typography": { - "fontSize": true, - "lineHeight": true, - "__experimentalFontStyle": true, - "__experimentalFontWeight": true, - "__experimentalLetterSpacing": true, - "__experimentalTextTransform": true, - "__experimentalDefaultControls": { - "fontSize": true - } - }, - "color": { - "gradients": true, - "link": true, - "__experimentalDefaultControls": { - "background": true, - "text": true - } - }, - "inserter": false - }, - "style": [ - "wp-block-post-comments", - "wp-block-buttons", - "wp-block-button" - ], - "editorStyle": "wp-block-post-comments-editor" -} diff --git a/packages/block-library/src/post-comments/edit.js b/packages/block-library/src/post-comments/edit.js deleted file mode 100644 index 251a290eb0a96..0000000000000 --- a/packages/block-library/src/post-comments/edit.js +++ /dev/null @@ -1,247 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ -import { - AlignmentControl, - BlockControls, - Warning, - useBlockProps, - store as blockEditorStore, -} from '@wordpress/block-editor'; -import { __, sprintf } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; -import { useEntityProp, store as coreStore } from '@wordpress/core-data'; -import { useDisabled } from '@wordpress/compose'; -import { createInterpolateElement } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import CommentsForm from '../post-comments-form/form'; - -export default function PostCommentsEdit( { - attributes: { textAlign }, - setAttributes, - context: { postType, postId }, -} ) { - let [ postTitle ] = useEntityProp( 'postType', postType, 'title', postId ); - postTitle = postTitle || __( 'Post Title' ); - - const [ commentStatus ] = useEntityProp( - 'postType', - postType, - 'comment_status', - postId - ); - - const { avatarURL, defaultCommentStatus } = useSelect( - ( select ) => - select( blockEditorStore ).getSettings() - .__experimentalDiscussionSettings - ); - - const isSiteEditor = postType === undefined || postId === undefined; - - const postTypeSupportsComments = useSelect( ( select ) => - postType - ? !! select( coreStore ).getPostType( postType )?.supports.comments - : false - ); - - let warning = __( - 'Post Comments block: This is just a placeholder, not a real comment. The final styling may differ because it also depends on the current theme. For better compatibility with the Block Editor, please consider replacing this block with the "Comments" block.' - ); - let showPlaceholder = true; - - if ( ! isSiteEditor && 'open' !== commentStatus ) { - if ( 'closed' === commentStatus ) { - warning = sprintf( - /* translators: 1: Post type (i.e. "post", "page") */ - __( - 'Post Comments block: Comments to this %s are not allowed.' - ), - postType - ); - showPlaceholder = false; - } else if ( ! postTypeSupportsComments ) { - warning = sprintf( - /* translators: 1: Post type (i.e. "post", "page") */ - __( - 'Post Comments block: Comments for this post type (%s) are not enabled.' - ), - postType - ); - showPlaceholder = false; - } else if ( 'open' !== defaultCommentStatus ) { - warning = __( 'Post Comments block: Comments are not enabled.' ); - showPlaceholder = false; - } - } - - const blockProps = useBlockProps( { - className: classnames( { - [ `has-text-align-${ textAlign }` ]: textAlign, - } ), - } ); - - const disabledRef = useDisabled(); - - return ( - <> - - { - setAttributes( { textAlign: nextAlign } ); - } } - /> - - -
- { warning } - - { showPlaceholder && ( -
-

- { - /* translators: %s: Post title. */ - sprintf( __( 'One response to %s' ), postTitle ) - } -

- - - -
    -
  1. -
    - - -
    -

    - { __( 'Hi, this is a comment.' ) } -
    - { __( - 'To get started with moderating, editing, and deleting comments, please visit the Comments screen in the dashboard.' - ) } -
    - { createInterpolateElement( - __( - 'Commenter avatars come from Gravatar' - ), - { - a: ( - /* eslint-disable-next-line jsx-a11y/anchor-has-content */ - - ), - } - ) } -

    -
    - - -
    -
  2. -
- - - - -
- ) } -
- - ); -} diff --git a/packages/block-library/src/post-comments/editor.scss b/packages/block-library/src/post-comments/editor.scss deleted file mode 100644 index 9d7c54f020fe3..0000000000000 --- a/packages/block-library/src/post-comments/editor.scss +++ /dev/null @@ -1,3 +0,0 @@ -.wp-block-post-comments__placeholder * { - pointer-events: none; -} diff --git a/packages/block-library/src/post-comments/index.js b/packages/block-library/src/post-comments/index.js deleted file mode 100644 index db109a576b7f9..0000000000000 --- a/packages/block-library/src/post-comments/index.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * WordPress dependencies - */ -import { postComments as icon } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import metadata from './block.json'; -import edit from './edit'; - -const { name } = metadata; -export { metadata, name }; - -export const settings = { - icon, - edit, -}; diff --git a/packages/block-library/src/post-comments/index.php b/packages/block-library/src/post-comments/index.php deleted file mode 100644 index 494bcb86746fa..0000000000000 --- a/packages/block-library/src/post-comments/index.php +++ /dev/null @@ -1,87 +0,0 @@ -context['postId']; - if ( ! isset( $post_id ) ) { - return ''; - } - - $comment_args = array( - 'post_id' => $post_id, - 'count' => true, - ); - // Return early if there are no comments and comments are closed. - if ( ! comments_open( $post_id ) && get_comments( $comment_args ) === 0 ) { - return ''; - } - - $post_before = $post; - $post = get_post( $post_id ); - setup_postdata( $post ); - - ob_start(); - // There's a deprecation warning generated by WP Core. - // Ideally this deprecation is removed from Core. - // In the meantime, this removes it from the output. - add_filter( 'deprecated_file_trigger_error', '__return_false' ); - comments_template(); - remove_filter( 'deprecated_file_trigger_error', '__return_false' ); - $post = $post_before; - - $classes = ''; - if ( isset( $attributes['textAlign'] ) ) { - $classes .= 'has-text-align-' . $attributes['textAlign']; - } - - $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classes ) ); - $output = ob_get_clean(); - - wp_enqueue_script( 'comment-reply' ); - - return sprintf( '
%2$s
', $wrapper_attributes, $output ); -} - -/** - * Registers the `core/post-comments` block on the server. - */ -function register_block_core_post_comments() { - register_block_type_from_metadata( - __DIR__ . '/post-comments', - array( - 'render_callback' => 'render_block_core_post_comments', - ) - ); -} -add_action( 'init', 'register_block_core_post_comments' ); - -/** - * Use the button block classes for the form-submit button. - * - * @param array $fields The default comment form arguments. - * - * @return array Returns the modified fields. - */ -function post_comments_block_form_defaults( $fields ) { - if ( wp_is_block_theme() ) { - $fields['submit_button'] = ''; - $fields['submit_field'] = '

%1$s %2$s

'; - } - - return $fields; -} -add_filter( 'comment_form_defaults', 'post_comments_block_form_defaults' ); diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index c7013a739933a..2c0817f698fa6 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -7,6 +7,7 @@ @import "./categories/style.scss"; @import "./code/style.scss"; @import "./columns/style.scss"; +@import "./comments/style.scss"; @import "./comments-pagination/style.scss"; @import "./comment-template/style.scss"; @import "./cover/style.scss"; @@ -25,7 +26,6 @@ @import "./page-list/style.scss"; @import "./paragraph/style.scss"; @import "./post-author/style.scss"; -@import "./post-comments/style.scss"; @import "./post-comments-form/style.scss"; @import "./post-excerpt/style.scss"; @import "./post-featured-image/style.scss"; diff --git a/packages/blocks/src/api/parser/convert-legacy-block.js b/packages/blocks/src/api/parser/convert-legacy-block.js index fd40b55e136ba..c993d45009991 100644 --- a/packages/blocks/src/api/parser/convert-legacy-block.js +++ b/packages/blocks/src/api/parser/convert-legacy-block.js @@ -72,6 +72,10 @@ export function convertLegacyBlockNameAndAttributes( name, attributes ) { // Note that we also had to add a deprecation to the block in order // for the ID change to work. } + if ( name === 'core/post-comments' ) { + name = 'core/comments'; + newAttributes.legacy = true; + } return [ name, newAttributes ]; } diff --git a/packages/e2e-test-utils-playwright/src/request-utils/index.ts b/packages/e2e-test-utils-playwright/src/request-utils/index.ts index 03c9002c65bcb..322aa48e850f8 100644 --- a/packages/e2e-test-utils-playwright/src/request-utils/index.ts +++ b/packages/e2e-test-utils-playwright/src/request-utils/index.ts @@ -19,7 +19,7 @@ import { deleteAllTemplates } from './templates'; import { activateTheme } from './themes'; import { deleteAllBlocks } from './blocks'; import { createComment, deleteAllComments } from './comments'; -import { deleteAllPosts } from './posts'; +import { createPost, deleteAllPosts } from './posts'; import { resetPreferences } from './preferences'; import { deleteAllWidgets, addWidgetBlock } from './widgets'; @@ -121,6 +121,7 @@ class RequestUtils { deactivatePlugin = deactivatePlugin.bind( this ); activateTheme = activateTheme.bind( this ); deleteAllBlocks = deleteAllBlocks; + createPost = createPost.bind( this ); deleteAllPosts = deleteAllPosts.bind( this ); createComment = createComment.bind( this ); deleteAllComments = deleteAllComments.bind( this ); diff --git a/packages/e2e-test-utils-playwright/src/request-utils/posts.js b/packages/e2e-test-utils-playwright/src/request-utils/posts.js deleted file mode 100644 index 4b3d7bbc44400..0000000000000 --- a/packages/e2e-test-utils-playwright/src/request-utils/posts.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Delete all posts using REST API. - * - * @this {import('./index').RequestUtils} - */ -export async function deleteAllPosts() { - // List all posts. - // https://developer.wordpress.org/rest-api/reference/posts/#list-posts - const posts = await this.rest( { - path: '/wp/v2/posts', - params: { - per_page: 100, - // All possible statuses. - status: 'publish,future,draft,pending,private,trash', - }, - } ); - - // Delete all posts one by one. - // https://developer.wordpress.org/rest-api/reference/posts/#delete-a-post - // "/wp/v2/posts" not yet supports batch requests. - await Promise.all( - posts.map( ( post ) => - this.rest( { - method: 'DELETE', - path: `/wp/v2/posts/${ post.id }`, - params: { - force: true, - }, - } ) - ) - ); -} diff --git a/packages/e2e-test-utils-playwright/src/request-utils/posts.ts b/packages/e2e-test-utils-playwright/src/request-utils/posts.ts new file mode 100644 index 0000000000000..8c94e368545da --- /dev/null +++ b/packages/e2e-test-utils-playwright/src/request-utils/posts.ts @@ -0,0 +1,67 @@ +/** + * Internal dependencies + */ +import type { RequestUtils } from './index'; + +export interface Post { + id: number; + content: string; + status: 'publish' | 'future' | 'draft' | 'pending' | 'private'; +} + +export interface CreatePostPayload { + content: string; + status: 'publish' | 'future' | 'draft' | 'pending' | 'private'; +} + +/** + * Delete all posts using REST API. + * + * @param {} this RequestUtils. + */ +export async function deleteAllPosts( this: RequestUtils ) { + // List all posts. + // https://developer.wordpress.org/rest-api/reference/posts/#list-posts + const posts = await this.rest< Post[] >( { + path: '/wp/v2/posts', + params: { + per_page: 100, + // All possible statuses. + status: 'publish,future,draft,pending,private,trash', + }, + } ); + + // Delete all posts one by one. + // https://developer.wordpress.org/rest-api/reference/posts/#delete-a-post + // "/wp/v2/posts" not yet supports batch requests. + await Promise.all( + posts.map( ( post ) => + this.rest( { + method: 'DELETE', + path: `/wp/v2/posts/${ post.id }`, + params: { + force: true, + }, + } ) + ) + ); +} + +/** + * Creates a new post using the REST API. + * + * @param {} this RequestUtils. + * @param {} payload Post attributes. + */ +export async function createPost( + this: RequestUtils, + payload: CreatePostPayload +) { + const post = await this.rest< Post >( { + method: 'POST', + path: `/wp/v2/posts`, + params: { ...payload }, + } ); + + return post; +} diff --git a/test/e2e/specs/editor/blocks/comments.spec.js b/test/e2e/specs/editor/blocks/comments.spec.js index 212dd2884fa07..581027f53a09e 100644 --- a/test/e2e/specs/editor/blocks/comments.spec.js +++ b/test/e2e/specs/editor/blocks/comments.spec.js @@ -9,37 +9,37 @@ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); */ test.use( { - commentsBlockUtils: async ( { page, admin }, use ) => { - await use( new CommentsBlockUtils( { page, admin } ) ); + commentsBlockUtils: async ( { page, admin, requestUtils }, use ) => { + await use( new CommentsBlockUtils( { page, admin, requestUtils } ) ); }, } ); test.describe( 'Comments', () => { - let previousPageComments, - previousCommentsPerPage, - previousDefaultCommentsPage; + let previousOptions; test.beforeAll( async ( { requestUtils } ) => { await requestUtils.activateTheme( 'emptytheme' ); } ); test.beforeEach( async ( { commentsBlockUtils } ) => { - // Ideally, we'd set options in beforeAll. Unfortunately, these + // Ideally, we'd set options in beforeAll or afterAll. Unfortunately, these // aren't exposed via the REST API, so we have to set them through the // relevant wp-admin screen, which involves page utils; but those are - // prohibited from beforeAll. - previousPageComments = await commentsBlockUtils.setOption( - 'page_comments', - '1' - ); - previousCommentsPerPage = await commentsBlockUtils.setOption( - 'comments_per_page', - '1' - ); - previousDefaultCommentsPage = await commentsBlockUtils.setOption( - 'default_comments_page', - 'newest' - ); + // prohibited from beforeAll/afterAll. + previousOptions = await commentsBlockUtils.setOptions( { + page_comments: '1', + comments_per_page: '1', + default_comments_page: 'newest', + } ); + } ); + + test.afterEach( async ( { requestUtils, commentsBlockUtils } ) => { + await commentsBlockUtils.setOptions( previousOptions ); + await requestUtils.deleteAllComments(); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); } ); test( 'We show no results message if there are no comments', async ( { @@ -107,6 +107,7 @@ test.describe( 'Comments', () => { page.locator( 'role=link[name="Newer Comments"i]' ) ).toBeVisible(); } ); + test( 'Pagination links are not appearing if break comments is not enabled', async ( { admin, editor, @@ -141,35 +142,205 @@ test.describe( 'Comments', () => { ).toBeHidden(); } ); - test.afterEach( async ( { requestUtils, commentsBlockUtils } ) => { - // Ideally, we'd set options in afterAll. Unfortunately, these - // aren't exposed via the REST API, so we have to set them through the - // relevant wp-admin screen, which involves page utils; but those are - // prohibited from beforeAll. - await commentsBlockUtils.setOption( - 'page_comments', - previousPageComments + test( 'A button allows the block to switch from legacy mode to editable mode', async ( { + admin, + editor, + page, + } ) => { + await admin.createNewPost(); + await editor.insertBlock( { + name: 'core/comments', + attributes: { legacy: true, textColor: 'vivid-purple' }, + } ); + + const block = page.locator( 'role=document[name="Block: Comments"i]' ); + const warning = block.locator( '.block-editor-warning' ); + const placeholder = block.locator( + '.wp-block-comments__legacy-placeholder' ); - await commentsBlockUtils.setOption( - 'comments_per_page', - previousCommentsPerPage + + await expect( block ).toHaveClass( /has-vivid-purple-color/ ); + await expect( warning ).toBeVisible(); + await expect( placeholder ).toBeVisible(); + + await page.click( 'role=button[name="Switch to editable mode"i]' ); + + const commentTemplate = block.locator( + 'role=document[name="Block: Comment Template"i]' ); - await commentsBlockUtils.setOption( - 'default_comments_page', - previousDefaultCommentsPage + await expect( block ).toHaveClass( /has-vivid-purple-color/ ); + await expect( commentTemplate ).toBeVisible(); + await expect( warning ).toBeHidden(); + await expect( placeholder ).toBeHidden(); + } ); + + test( 'The editable block version is rendered if the legacy attribute is false', async ( { + page, + admin, + editor, + requestUtils, + } ) => { + await admin.createNewPost(); + await editor.insertBlock( { name: 'core/comments' } ); + const postId = await editor.publishPost(); + + // Create a comments for that post. + await requestUtils.createComment( { + content: 'This is an automated comment', + post: postId, + } ); + + // Visit the post that was just published. + await page.click( + 'role=region[name="Editor publish"] >> role=link[name="View Post"i]' ); + + // Check that the Comment Template block (an inner block) is rendered. + await expect( + page.locator( '.wp-block-comment-template' ) + ).toBeVisible(); + await expect( page.locator( '.commentlist' ) ).toBeHidden(); + } ); + + test( 'The PHP version is rendered if the legacy attribute is true', async ( { + page, + admin, + editor, + requestUtils, + } ) => { + await admin.createNewPost(); + await editor.insertBlock( { + name: 'core/comments', + attributes: { legacy: true }, + } ); + const postId = await editor.publishPost(); + + // Create a comments for that post. + await requestUtils.createComment( { + content: 'This is an automated comment', + post: postId, + } ); + + // Visit the post that was just published. + await page.click( + 'role=region[name="Editor publish"] >> role=link[name="View Post"i]' + ); + + // Check that the Comment Template block (an inner block) is NOT rendered. + await expect( + page.locator( '.wp-block-comment-template' ) + ).toBeHidden(); + await expect( page.locator( '.commentlist' ) ).toBeVisible(); + } ); +} ); + +/* + * The following test suite ensures that the legacy Post Comments block is still + * supported and it is converted into the Comments block on the editor. + */ +test.describe( 'Post Comments', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'emptytheme' ); + } ); + + test.afterEach( async ( { requestUtils } ) => { await requestUtils.deleteAllComments(); } ); test.afterAll( async ( { requestUtils } ) => { await requestUtils.activateTheme( 'twentytwentyone' ); } ); + + test( 'is still supported', async ( { page, requestUtils } ) => { + // Create a post with the old "Post Comments" block. + const { id: postId } = await requestUtils.createPost( { + content: '', + status: 'publish', + } ); + + // Publish a comment on that post. + await requestUtils.createComment( { + content: 'This is an automated comment', + post: postId, + } ); + + // Visit created post. + await page.goto( `/?p=${ postId }` ); + + // Ensure that the rendered post is the legacy version of Post Comments. + await expect( page.locator( '.wp-block-post-comments' ) ).toBeVisible(); + await expect( page.locator( '.comment-content' ) ).toContainText( + 'This is an automated comment' + ); + } ); + + test( 'is converted to Comments with legacy attribute', async ( { + page, + admin, + editor, + requestUtils, + commentsBlockUtils, + } ) => { + // Create a post with the old "Post Comments" block. + const { id: postId } = await requestUtils.createPost( { + content: '', + status: 'publish', + } ); + await requestUtils.createComment( { + content: 'This is an automated comment', + post: postId, + } ); + + // Go to the post editor. + await admin.visitAdminPage( + '/post.php', + `post=${ postId }&action=edit` + ); + + // Hide welcome guide. + await commentsBlockUtils.hideWelcomeGuide(); + + // Check that the Post Comments block has been replaced with Comments. + await expect( page.locator( '.wp-block-post-comments' ) ).toBeHidden(); + await expect( page.locator( '.wp-block-comments' ) ).toBeVisible(); + + // Check the block definition has changed. + const content = await editor.getEditedPostContent(); + expect( content ).toBe( '' ); + + // Visit post + await page.goto( `/?p=${ postId }` ); + + // Rendered block should be the same as Post Comments. + await expect( page.locator( '.wp-block-post-comments' ) ).toBeVisible(); + await expect( page.locator( '.comment-content' ) ).toContainText( + 'This is an automated comment' + ); + } ); } ); class CommentsBlockUtils { - constructor( { page, admin } ) { + constructor( { page, admin, requestUtils } ) { this.page = page; this.admin = admin; + this.requestUtils = requestUtils; + } + + /** + * Sets a group of site options, from the options-general admin page. + * + * This is a temporary solution until we can handle options through the REST + * API. + * + * @param {Record} options Options in key-value format. + * @return {Record} Previous options. + */ + async setOptions( options ) { + const previousValues = {}; + for ( const [ key, value ] of Object.entries( options ) ) { + previousValues[ key ] = await this.setOption( key, value ); + } + return previousValues; } /** @@ -193,4 +364,21 @@ class CommentsBlockUtils { return previousValue; } + + async hideWelcomeGuide() { + await this.page.evaluate( async () => { + const isWelcomeGuideActive = window.wp.data + .select( 'core/edit-post' ) + .isFeatureActive( 'welcomeGuide' ); + + if ( isWelcomeGuideActive ) { + window.wp.data + .dispatch( 'core/edit-post' ) + .toggleFeature( 'welcomeGuide' ); + } + } ); + + await this.page.reload(); + await this.page.waitForSelector( '.edit-post-layout' ); + } } diff --git a/test/integration/fixtures/blocks/core__comments.json b/test/integration/fixtures/blocks/core__comments.json index 6f1d1c5c9e843..1ea7dcae007f6 100644 --- a/test/integration/fixtures/blocks/core__comments.json +++ b/test/integration/fixtures/blocks/core__comments.json @@ -4,6 +4,7 @@ "isValid": true, "attributes": { "tagName": "div", + "legacy": false, "className": "comments-post-extra" }, "innerBlocks": [ diff --git a/test/integration/fixtures/blocks/core__post-comments.json b/test/integration/fixtures/blocks/core__post-comments.json index 0b5af1837cca2..6cea899e1534b 100644 --- a/test/integration/fixtures/blocks/core__post-comments.json +++ b/test/integration/fixtures/blocks/core__post-comments.json @@ -1,8 +1,11 @@ [ { - "name": "core/post-comments", + "name": "core/comments", "isValid": true, - "attributes": {}, + "attributes": { + "tagName": "div", + "legacy": true + }, "innerBlocks": [] } ] diff --git a/test/integration/fixtures/blocks/core__post-comments.serialized.html b/test/integration/fixtures/blocks/core__post-comments.serialized.html index 624be4a845676..a487c09b960eb 100644 --- a/test/integration/fixtures/blocks/core__post-comments.serialized.html +++ b/test/integration/fixtures/blocks/core__post-comments.serialized.html @@ -1 +1 @@ - +