diff --git a/core-blocks/index.js b/core-blocks/index.js index 89366bdc8a071..117f485aa34c4 100644 --- a/core-blocks/index.js +++ b/core-blocks/index.js @@ -28,6 +28,7 @@ import * as embed from './embed'; import * as file from './file'; import * as freeform from './freeform'; import * as html from './html'; +import * as latestComments from './latest-comments'; import * as latestPosts from './latest-posts'; import * as list from './list'; import * as more from './more'; @@ -71,6 +72,7 @@ export const registerCoreBlocks = () => { file, freeform, html, + latestComments, latestPosts, more, nextpage, diff --git a/core-blocks/latest-comments/edit.js b/core-blocks/latest-comments/edit.js new file mode 100644 index 0000000000000..1f922c03d11bf --- /dev/null +++ b/core-blocks/latest-comments/edit.js @@ -0,0 +1,123 @@ +/** + * WordPress dependencies + */ +import { Component, Fragment } from '@wordpress/element'; +import { + Disabled, + PanelBody, + RangeControl, + ToggleControl, + ServerSideRender, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { + InspectorControls, + BlockAlignmentToolbar, + BlockControls, +} from '@wordpress/editor'; + +/** + * Internal dependencies. + */ +import './editor.scss'; + +/** + * Minimum number of comments a user can show using this block. + * + * @type {number} + */ +const MIN_COMMENTS = 1; +/** + * Maximum number of comments a user can show using this block. + * + * @type {number} + */ +const MAX_COMMENTS = 100; + +class LatestComments extends Component { + constructor() { + super( ...arguments ); + + this.setAlignment = this.setAlignment.bind( this ); + this.setCommentsToShow = this.setCommentsToShow.bind( this ); + + // Create toggles for each attribute; we create them here rather than + // passing `this.createToggleAttribute( 'displayAvatar' )` directly to + // `onChange` to avoid re-renders. + this.toggleDisplayAvatar = this.createToggleAttribute( 'displayAvatar' ); + this.toggleDisplayDate = this.createToggleAttribute( 'displayDate' ); + this.toggleDisplayExcerpt = this.createToggleAttribute( 'displayExcerpt' ); + } + + createToggleAttribute( propName ) { + return () => { + const value = this.props.attributes[ propName ]; + const { setAttributes } = this.props; + + setAttributes( { [ propName ]: ! value } ); + }; + } + + setAlignment( align ) { + this.props.setAttributes( { align } ); + } + + setCommentsToShow( commentsToShow ) { + this.props.setAttributes( { commentsToShow } ); + } + + render() { + const { + align, + commentsToShow, + displayAvatar, + displayDate, + displayExcerpt, + } = this.props.attributes; + + return ( + + + + + + + + + + + + + + + + + ); + } +} + +export default LatestComments; diff --git a/core-blocks/latest-comments/editor.scss b/core-blocks/latest-comments/editor.scss new file mode 100644 index 0000000000000..e5116c2c5bf4a --- /dev/null +++ b/core-blocks/latest-comments/editor.scss @@ -0,0 +1,14 @@ +.wp-block-latest-comments.has-avatars .avatar { + margin-right: 10px; +} + +.wp-block-latest-comments__comment-excerpt p { + font-size: 14px; + line-height: $editor-line-height; + margin: 5px 0 20px; + padding-top: 0; +} + +.wp-block-latest-comments.has-avatars .wp-block-latest-comments__comment { + min-height: 36px; +} diff --git a/core-blocks/latest-comments/index.js b/core-blocks/latest-comments/index.js new file mode 100644 index 0000000000000..9ba41140d2059 --- /dev/null +++ b/core-blocks/latest-comments/index.js @@ -0,0 +1,44 @@ +/** + * WordPress dependencies. + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies. + */ +import './style.scss'; +import edit from './edit'; + +export const name = 'core/latest-comments'; + +export const settings = { + title: __( 'Latest Comments' ), + + description: __( 'Show a list of your site’s most recent comments.' ), + + icon: 'list-view', + + category: 'widgets', + + keywords: [ __( 'recent comments' ) ], + + supports: { + html: false, + }, + + getEditWrapperProps( attributes ) { + const { align } = attributes; + + // TODO: Use consistent values across the app; + // see: https://github.com/WordPress/gutenberg/issues/7908. + if ( [ 'left', 'center', 'right', 'wide', 'full' ].includes( align ) ) { + return { 'data-align': align }; + } + }, + + edit, + + save() { + return null; + }, +}; diff --git a/core-blocks/latest-comments/index.php b/core-blocks/latest-comments/index.php new file mode 100644 index 0000000000000..5533fd4bd7785 --- /dev/null +++ b/core-blocks/latest-comments/index.php @@ -0,0 +1,143 @@ + $attributes['commentsToShow'], + 'status' => 'approve', + 'post_status' => 'publish', + ) ) ); + + $list_items_markup = ''; + if ( ! empty( $comments ) ) { + // Prime the cache for associated posts. This is copied from \WP_Widget_Recent_Comments::widget(). + $post_ids = array_unique( wp_list_pluck( $comments, 'comment_post_ID' ) ); + _prime_post_caches( $post_ids, strpos( get_option( 'permalink_structure' ), '%category%' ), false ); + + foreach ( $comments as $comment ) { + $list_items_markup .= '
  • '; + if ( $attributes['displayAvatar'] ) { + $avatar = get_avatar( $comment, 48, '', '', array( + 'class' => 'wp-block-latest-comments__comment-avatar', + ) ); + if ( $avatar ) { + $list_items_markup .= $avatar; + } + } + + $list_items_markup .= '
    '; + $list_items_markup .= ''; + if ( $attributes['displayExcerpt'] ) { + $list_items_markup .= '
    ' . wpautop( get_comment_excerpt( $comment ) ) . '
    '; + } + $list_items_markup .= '
  • '; + } + } + + $class = 'wp-block-latest-comments'; + if ( $attributes['align'] ) { + $class .= " align{$attributes['align']}"; + } + if ( $attributes['displayAvatar'] ) { + $class .= ' has-avatars'; + } + if ( $attributes['displayDate'] ) { + $class .= ' has-dates'; + } + if ( $attributes['displayExcerpt'] ) { + $class .= ' has-excerpts'; + } + if ( empty( $comments ) ) { + $class .= ' no-comments'; + } + $classnames = esc_attr( $class ); + + $block_content = ! empty( $comments ) ? sprintf( + '
      %2$s
    ', + $classnames, + $list_items_markup + ) : sprintf( '
    %2$s
    ', + $classnames, + __( 'No comments to show.', 'gutenberg' ) + ); + + return $block_content; +} + +register_block_type( 'core/latest-comments', array( + 'attributes' => array( + 'className' => array( + 'type' => 'string', + ), + 'commentsToShow' => array( + 'type' => 'number', + 'default' => 5, + 'minimum' => 1, + 'maximum' => 100, + ), + 'displayAvatar' => array( + 'type' => 'boolean', + 'default' => true, + ), + 'displayDate' => array( + 'type' => 'boolean', + 'default' => true, + ), + 'displayExcerpt' => array( + 'type' => 'boolean', + 'default' => true, + ), + 'align' => array( + 'type' => 'string', + 'enum' => array( 'center', 'left', 'right', 'wide', 'full', '' ), + ), + ), + 'render_callback' => 'gutenberg_render_block_core_latest_comments', +) ); diff --git a/core-blocks/latest-comments/style.scss b/core-blocks/latest-comments/style.scss new file mode 100644 index 0000000000000..81ebe5dbf5937 --- /dev/null +++ b/core-blocks/latest-comments/style.scss @@ -0,0 +1,43 @@ +.wp-block-latest-comments__comment { + font-size: 15px; + line-height: 1.1; + list-style: none; + margin-bottom: 1em; + + .has-avatars & { + min-height: 36px; + list-style: none; + + .wp-block-latest-comments__comment-meta, + .wp-block-latest-comments__comment-excerpt { + margin-left: 52px; + } + } + + .has-dates &, + .has-excerpts & { + line-height: 1.5; + } +} + +.wp-block-latest-comments__comment-excerpt p { + font-size: 14px; + line-height: 1.8; + margin: 5px 0 20px; +} + +.wp-block-latest-comments__comment-date { + color: $dark-gray-100; + display: block; + font-size: 12px; +} + +.wp-block-latest-comments .avatar, +.wp-block-latest-comments__comment-avatar { + border-radius: 24px; + display: block; + float: left; + height: 40px; + margin-right: 12px; + width: 40px; +} diff --git a/core-blocks/test/fixtures/core__latest-comments.html b/core-blocks/test/fixtures/core__latest-comments.html new file mode 100644 index 0000000000000..cc4a2523b9d41 --- /dev/null +++ b/core-blocks/test/fixtures/core__latest-comments.html @@ -0,0 +1 @@ + diff --git a/core-blocks/test/fixtures/core__latest-comments.json b/core-blocks/test/fixtures/core__latest-comments.json new file mode 100644 index 0000000000000..155ef9f06de07 --- /dev/null +++ b/core-blocks/test/fixtures/core__latest-comments.json @@ -0,0 +1,10 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/latest-comments", + "isValid": true, + "attributes": {}, + "innerBlocks": [], + "originalContent": "" + } +] diff --git a/core-blocks/test/fixtures/core__latest-comments.parsed.json b/core-blocks/test/fixtures/core__latest-comments.parsed.json new file mode 100644 index 0000000000000..a8806d27be5a4 --- /dev/null +++ b/core-blocks/test/fixtures/core__latest-comments.parsed.json @@ -0,0 +1,16 @@ +[ + { + "blockName": "core/latest-comments", + "attrs": { + "displayAvatar": true, + "displayExcerpt": true, + "displayTimestamp": true + }, + "innerBlocks": [], + "innerHTML": "" + }, + { + "attrs": {}, + "innerHTML": "\n" + } +] diff --git a/core-blocks/test/fixtures/core__latest-comments.serialized.html b/core-blocks/test/fixtures/core__latest-comments.serialized.html new file mode 100644 index 0000000000000..a50d5664a60a6 --- /dev/null +++ b/core-blocks/test/fixtures/core__latest-comments.serialized.html @@ -0,0 +1 @@ +