diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index eb132d03c1d186..761fad23254ecb 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 18a9eb34ff1668..aa0d82c7795d44 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 582de73d8d0e93..042184b9b8d08d 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 00000000000000..4d629a857e6527 --- /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 00000000000000..8d8db650349d87 --- /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 00000000000000..837561aad15c00 --- /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 7b4533e228eed8..18897325027c44 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 6e21a9899421e0..2d7b20ad5ca3cd 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 00000000000000..5df2ed550a21ec --- /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 47c774fc4b9bb1..14a8309eb92c45 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 ebcf192d898b11..732e42d5bcaa4d 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 ba031d6cbdeaa9..138e4802630212 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 ff72f0b90d1010..f0d5924d7248d8 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 f776a160909871..56eab195b4a29e 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 48a38d8e8a5628..00de2cb418d5df 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 dc719fff5d7081..00000000000000 --- 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 251a290eb0a96b..00000000000000 --- 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 9d7c54f020fe37..00000000000000 --- 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 db109a576b7f93..00000000000000 --- 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 494bcb86746fa3..00000000000000 --- 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 c7013a739933a6..2c0817f698fa6b 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 fd40b55e136ba2..c993d450099912 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 03c9002c65bcb1..322aa48e850f86 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 4b3d7bbc44400c..00000000000000 --- 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 00000000000000..8c94e368545dab --- /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 212dd2884fa075..581027f53a09ec 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 6f1d1c5c9e8431..1ea7dcae007f65 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 0b5af1837cca2f..6cea899e1534b6 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 624be4a845676f..a487c09b960ebb 100644 --- a/test/integration/fixtures/blocks/core__post-comments.serialized.html +++ b/test/integration/fixtures/blocks/core__post-comments.serialized.html @@ -1 +1 @@ - +