diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index c5cb180117acc..9fe559fc4187b 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -396,6 +396,23 @@ function get_block_editor_settings( array $custom_settings, $block_editor_contex $editor_settings['localAutosaveInterval'] = 15; + $editor_settings['__experimentalDiscussionSettings'] = array( + 'commentOrder' => get_option( 'comment_order' ), + 'commentsPerPage' => get_option( 'comments_per_page' ), + 'defaultCommentsPage' => get_option( 'default_comments_page' ), + 'pageComments' => get_option( 'page_comments' ), + 'threadComments' => get_option( 'thread_comments' ), + 'threadCommentsDepth' => get_option( 'thread_comments_depth' ), + 'avatarURL' => get_avatar_url( + '', + array( + 'size' => 96, + 'force_default' => true, + 'default' => get_option( 'avatar_default' ), + ) + ), + ); + /** * Filters the settings to pass to the block editor for all editor type. * diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 45e36fc1dcc7d..72f0198626e71 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -1358,3 +1358,96 @@ function _wp_multiple_block_styles( $metadata ) { return $metadata; } add_filter( 'block_type_metadata', '_wp_multiple_block_styles' ); + +/** + * Helper function that constructs a comment query vars array from the passed + * block properties. + * + * It's used with the Comment Query Loop inner blocks. + * + * @since 6.0.0 + * + * @param WP_Block $block Block instance. + * + * @return array Returns the comment query parameters to use with the + * WP_Comment_Query constructor. + */ +function build_comment_query_vars_from_block( $block ) { + + $comment_args = array( + 'orderby' => 'comment_date_gmt', + 'order' => 'ASC', + 'status' => 'approve', + 'no_found_rows' => false, + 'update_comment_meta_cache' => false, // We lazy-load comment meta for performance. + ); + + if ( ! empty( $block->context['postId'] ) ) { + $comment_args['post_id'] = (int) $block->context['postId']; + } + + if ( get_option( 'thread_comments' ) ) { + $comment_args['hierarchical'] = 'threaded'; + } else { + $comment_args['hierarchical'] = false; + } + + $per_page = get_option( 'comments_per_page' ); + $default_page = get_option( 'default_comments_page' ); + + if ( $per_page > 0 ) { + $comment_args['number'] = $per_page; + + $page = (int) get_query_var( 'cpage' ); + if ( $page ) { + $comment_args['paged'] = $page; + } elseif ( 'oldest' === $default_page ) { + $comment_args['paged'] = 1; + } elseif ( 'newest' === $default_page ) { + $comment_args['paged'] = (int) ( new WP_Comment_Query( $comment_args ) )->max_num_pages; + } + // Set the `cpage` query var to ensure the previous and next pagination links are correct + // when inheriting the Discussion Settings. + if ( 0 === $page && isset( $comment_args['paged'] ) && $comment_args['paged'] > 0 ) { + set_query_var( 'cpage', $comment_args['paged'] ); + } + } + + return $comment_args; +} + +/** + * Helper function that returns the proper pagination arrow html for + * `CommentsPaginationNext` and `CommentsPaginationPrevious` blocks based on the + * provided `paginationArrow` from `CommentsPagination` context. + * + * It's used in CommentsPaginationNext and CommentsPaginationPrevious blocks. + * + * @since 6.0.0 + * + * @param WP_Block $block Block instance. + * @param string $pagination_type Type of the arrow we will be rendering. + * Default 'next'. Accepts 'next' or 'previous'. + * + * @return string|null Returns the constructed WP_Query arguments. + */ +function get_comments_pagination_arrow( $block, $pagination_type = 'next' ) { + $arrow_map = array( + 'none' => '', + 'arrow' => array( + 'next' => '→', + 'previous' => '←', + ), + 'chevron' => array( + 'next' => '»', + 'previous' => '«', + ), + ); + if ( ! empty( $block->context['comments/paginationArrow'] ) && ! empty( $arrow_map[ $block->context['comments/paginationArrow'] ][ $pagination_type ] ) ) { + $arrow_attribute = $block->context['comments/paginationArrow']; + $arrow = $arrow_map[ $block->context['comments/paginationArrow'] ][ $pagination_type ]; + $arrow_classes = "wp-block-comments-pagination-$pagination_type-arrow is-arrow-$arrow_attribute"; + return "$arrow"; + } + return null; +} diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php index 6e2daa829a508..c2d9e87635a4e 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php @@ -1196,7 +1196,8 @@ protected function prepare_links( $comment ) { $rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) ); $links['children'] = array( - 'href' => $rest_url, + 'href' => $rest_url, + 'embedded' => true, ); } diff --git a/tests/phpunit/tests/block-library/commentTemplate.php b/tests/phpunit/tests/block-library/commentTemplate.php new file mode 100644 index 0000000000000..9555de2fcd14b --- /dev/null +++ b/tests/phpunit/tests/block-library/commentTemplate.php @@ -0,0 +1,206 @@ +post->create_and_get( + array( + 'post_type' => 'dogs', + 'post_status' => 'publish', + 'post_name' => 'metaldog', + 'post_title' => 'Metal Dog', + 'post_content' => 'Metal Dog content', + 'post_excerpt' => 'Metal Dog', + ) + ); + + self::$comment_ids = self::factory()->comment->create_post_comments( + self::$custom_post->ID, + 1, + array( + 'comment_author' => 'Test', + 'comment_author_email' => 'test@example.org', + 'comment_content' => 'Hello world', + ) + ); + } + + function test_build_comment_query_vars_from_block_with_context() { + $parsed_blocks = parse_blocks( + '' + ); + + $block = new WP_Block( + $parsed_blocks[0], + array( + 'postId' => self::$custom_post->ID, + ) + ); + + $this->assertEquals( + array( + 'orderby' => 'comment_date_gmt', + 'order' => 'ASC', + 'status' => 'approve', + 'no_found_rows' => false, + 'update_comment_meta_cache' => false, + 'post_id' => self::$custom_post->ID, + 'hierarchical' => 'threaded', + 'number' => 5, + 'paged' => 1, + ), + build_comment_query_vars_from_block( $block ) + ); + } + + function test_build_comment_query_vars_from_block_no_context() { + $parsed_blocks = parse_blocks( + '' + ); + + $block = new WP_Block( $parsed_blocks[0] ); + + $this->assertEquals( + array( + 'orderby' => 'comment_date_gmt', + 'order' => 'ASC', + 'status' => 'approve', + 'no_found_rows' => false, + 'update_comment_meta_cache' => false, + 'hierarchical' => 'threaded', + 'number' => 5, + 'paged' => 1, + ), + build_comment_query_vars_from_block( $block ) + ); + } + + /** + * Test rendering a single comment + */ + function test_rendering_comment_template() { + $parsed_blocks = parse_blocks( + '' + ); + + $block = new WP_Block( + $parsed_blocks[0], + array( + 'postId' => self::$custom_post->ID, + ) + ); + + // Here we use the function prefixed with 'gutenberg_*' because it's added + // in the build step. + $this->assertEquals( + '
  1. Test
    Hello world
', + render_block_core_comment_template( null, null, $block ) + ); + } + + /** + * Test rendering 3 nested comments: + * + * └─ comment 1 + *   └─ comment 2 + *    └─ comment 3 + */ + function test_rendering_comment_template_nested() { + $nested_comment_ids = self::factory()->comment->create_post_comments( + self::$custom_post->ID, + 1, + array( + 'comment_parent' => self::$comment_ids[0], + 'comment_author' => 'Test', + 'comment_author_email' => 'test@example.org', + 'comment_content' => 'Hello world', + ) + ); + + self::factory()->comment->create_post_comments( + self::$custom_post->ID, + 1, + array( + 'comment_parent' => $nested_comment_ids[0], + 'comment_author' => 'Test', + 'comment_author_email' => 'test@example.org', + 'comment_content' => 'Hello world', + ) + ); + + $parsed_blocks = parse_blocks( + '' + ); + + $block = new WP_Block( + $parsed_blocks[0], + array( + 'postId' => self::$custom_post->ID, + ) + ); + + $this->assertEquals( + '
  1. Test
    Hello world
    1. Test
      Hello world
      1. Test
        Hello world
', + render_block_core_comment_template( null, null, $block ) + ); + } + /** + * Test that both "Older Comments" and "Newer Comments" are displayed in the correct order + * inside the Comment Query Loop when we enable pagination on Discussion Settings. + * In order to do that, it should exist a query var 'cpage' set with the $comment_args['paged'] value. + */ + function test_build_comment_query_vars_from_block_sets_cpage_var() { + + // This could be any number, we set a fixed one instead of a random for better performance. + $comment_query_max_num_pages = 5; + // We substract 1 because we created 1 comment at the beggining. + $post_comments_numbers = ( self::$per_page * $comment_query_max_num_pages ) - 1; + self::factory()->comment->create_post_comments( + self::$custom_post->ID, + $post_comments_numbers, + array( + 'comment_author' => 'Test', + 'comment_author_email' => 'test@example.org', + 'comment_content' => 'Hello world', + ) + ); + $parsed_blocks = parse_blocks( + '' + ); + + $block = new WP_Block( + $parsed_blocks[0], + array( + 'postId' => self::$custom_post->ID, + 'comments/inherit' => true, + ) + ); + $actual = build_comment_query_vars_from_block( $block ); + $this->assertEquals( $comment_query_max_num_pages, $actual['paged'] ); + $this->assertEquals( $comment_query_max_num_pages, get_query_var( 'cpage' ) ); + } +}