-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add latest comments block #7941
Changes from all commits
f35aaa9
8492bdf
8d92df9
3f6a3c3
e1adac3
eef19de
f5f037b
4703249
62b5391
e449439
614c0e5
fac0325
1eed90b
4b11aaf
e39bcc3
bce62f6
4a88a56
3ebb9a6
6c2d384
79650bf
4915fff
b280c8c
fd0395d
71be210
8d56db8
5831208
e6021be
1b31098
93c5a98
8d7f46d
6bc741a
a59a647
dfc09c1
49441d7
0d4e583
28ac484
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<Fragment> | ||
<BlockControls> | ||
<BlockAlignmentToolbar | ||
value={ align } | ||
onChange={ this.setAlignment } | ||
/> | ||
</BlockControls> | ||
<InspectorControls> | ||
<PanelBody title={ __( 'Latest Comments Settings' ) }> | ||
<ToggleControl | ||
label={ __( 'Display avatar' ) } | ||
checked={ displayAvatar } | ||
onChange={ this.toggleDisplayAvatar } | ||
/> | ||
<ToggleControl | ||
label={ __( 'Display date' ) } | ||
checked={ displayDate } | ||
onChange={ this.toggleDisplayDate } | ||
/> | ||
<ToggleControl | ||
label={ __( 'Display excerpt' ) } | ||
checked={ displayExcerpt } | ||
onChange={ this.toggleDisplayExcerpt } | ||
/> | ||
<RangeControl | ||
label={ __( 'Number of comments' ) } | ||
value={ commentsToShow } | ||
onChange={ this.setCommentsToShow } | ||
min={ MIN_COMMENTS } | ||
max={ MAX_COMMENTS } | ||
/> | ||
</PanelBody> | ||
</InspectorControls> | ||
<Disabled> | ||
<ServerSideRender | ||
block="core/latest-comments" | ||
attributes={ this.props.attributes } | ||
/> | ||
</Disabled> | ||
</Fragment> | ||
); | ||
} | ||
} | ||
|
||
export default LatestComments; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ) ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should double check that our new ES2015 prototype member polyfills with Babel 7 This is an ES2015+ method which until recently was not expected to be polyfilled and would therefore error in IE11. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested it in IE11 and it worked; I could change the align values and nothing errored on me (it touches this code path). |
||
return { 'data-align': align }; | ||
} | ||
}, | ||
|
||
edit, | ||
|
||
save() { | ||
return null; | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<?php | ||
/** | ||
* Server-side rendering of the `core/latest-comments` block. | ||
* | ||
* @package gutenberg | ||
*/ | ||
|
||
// Used to get `_draft_or_post_title`, which prevents empty-title posts from | ||
// appearing with no title. | ||
require_once( ABSPATH . 'wp-admin/includes/template.php' ); | ||
|
||
/** | ||
* Renders the `core/latest-comments` block on server. | ||
* | ||
* @param array $attributes The block attributes. | ||
* | ||
* @return string Returns the post content with latest comments added. | ||
*/ | ||
function gutenberg_render_block_core_latest_comments( $attributes = array() ) { | ||
// This filter is documented in wp-includes/widgets/class-wp-widget-recent-comments.php. | ||
$comments = get_comments( apply_filters( 'widget_comments_args', array( | ||
'number' => $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 .= '<li class="wp-block-latest-comments__comment">'; | ||
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 .= '<article>'; | ||
$list_items_markup .= '<footer class="wp-block-latest-comments__comment-meta">'; | ||
$author_url = get_comment_author_url( $comment ); | ||
if ( empty( $author_url ) && ! empty( $comment->user_id ) ) { | ||
$author_url = get_author_posts_url( $comment->user_id ); | ||
} | ||
|
||
$author_markup = ''; | ||
if ( $author_url ) { | ||
$author_markup .= '<a class="wp-block-latest-comments__comment-author" href="' . esc_url( $author_url ) . '">' . get_comment_author( $comment ) . '</a>'; | ||
} else { | ||
$author_markup .= '<span class="wp-block-latest-comments__comment-author">' . get_comment_author( $comment ) . '</span>'; | ||
} | ||
|
||
// `_draft_or_post_title` calls `esc_html()` so we don't need to wrap that call in | ||
// `esc_html`. | ||
$post_title = '<a class="wp-block-latest-comments__comment-link" href="' . esc_url( get_comment_link( $comment ) ) . '">' . _draft_or_post_title( $comment->comment_post_ID ) . '</a>'; | ||
|
||
$list_items_markup .= sprintf( | ||
/* translators: 1: author name (inside <a> or <span> tag, based on if they have a URL), 2: post title related to this comment */ | ||
__( '%1$s on %2$s', 'gutenberg' ), | ||
$author_markup, | ||
$post_title | ||
); | ||
|
||
if ( $attributes['displayDate'] ) { | ||
$list_items_markup .= sprintf( | ||
'<time datetime="%1$s" class="wp-block-latest-comments__comment-date">%2$s</time>', | ||
esc_attr( get_comment_date( 'c', $comment ) ), | ||
date_i18n( get_option( 'date_format' ), get_comment_date( 'U', $comment ) ) | ||
); | ||
} | ||
$list_items_markup .= '</footer>'; | ||
if ( $attributes['displayExcerpt'] ) { | ||
$list_items_markup .= '<div class="wp-block-latest-comments__comment-excerpt">' . wpautop( get_comment_excerpt( $comment ) ) . '</div>'; | ||
} | ||
$list_items_markup .= '</article></li>'; | ||
} | ||
} | ||
|
||
$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( | ||
'<ol class="%1$s">%2$s</ol>', | ||
$classnames, | ||
$list_items_markup | ||
) : sprintf( '<div class="%1$s">%2$s</div>', | ||
$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', | ||
) ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<!-- wp:latest-comments {"displayAvatar":true,"displayExcerpt":true,"displayTimestamp":true} /--> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes me wonder: Is there any case where we want
ServerSideRender
to not be disabled? Should this be built-in to the render logic of that component?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wondered that too, we should probably open an issue for it. I suppose building it in and then having some escape hatch prop that doesn't disable it would be good.