-
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 34 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> | ||
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. Makes me wonder: Is there any case where we want 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 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. |
||
<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,145 @@ | ||||||||||||||||||||||
<?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' ); | ||||||||||||||||||||||
|
||||||||||||||||||||||
$default_comments_to_show = 5; | ||||||||||||||||||||||
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. As I understand it, scoping in PHP won't work as expected with what we have here. This would need to be accessed by Simpler demonstration: Before: <?php
$extra = 'bar';
function do_foo() {
echo 'foo' . $extra;
}
do_foo();
After: <?php
$extra = 'bar';
function do_foo() {
global $extra;
echo 'foo' . $extra;
}
do_foo();
Though, as written, it's more of a constant, which is exemplified in Though, above all, does this really need to be in the global scope? I think we're over-optimizing at the risk of outside breakage (anything else in the running WordPress page could access this variable, intentionally or not). 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. Yeah I didn't think using I'll admit that my PHP is wildly rusty and I'm totally unclear what the best practice is for this. I just want a file-scoped constant. I can remove it if that's a pain to make. 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.
Yes, and so is the previous variable assignment, as can be demonstrated: diff --git a/core-blocks/latest-comments/index.php b/core-blocks/latest-comments/index.php
index 33c21e41f..092fe08eb 100644
--- a/core-blocks/latest-comments/index.php
+++ b/core-blocks/latest-comments/index.php
@@ -9,6 +9,7 @@
// appearing with no title.
require_once( ABSPATH . 'wp-admin/includes/template.php' );
+$default_comments_to_show = 5;
define( 'GUTENBERG_LATEST_COMMENTS_BLOCK_DEFAULT_TO_SHOW', 5 );
/**
diff --git a/lib/load.php b/lib/load.php
index f1a8b09c6..f8a4b7985 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -34,3 +34,5 @@ require dirname( __FILE__ ) . '/register.php';
foreach ( glob( dirname( __FILE__ ) . '/../core-blocks/*/index.php' ) as $block_logic ) {
require $block_logic;
}
+
+var_export( $default_comments_to_show ); exit; 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. 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. Yeah, I didn't realise that 😢 If the 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. Since it's prefixed enough it shouldn't be a huge concern. 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. Alternatively, we could consider enhancing our JSON schema support to handle https://spacetelescope.github.io/understanding-json-schema/reference/numeric.html#range In fact, since we use REST validation under the hood, I wonder if this is already possible. 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. It's already supported: Our usage will trigger the default to take its place when outside bounds: gutenberg/lib/class-wp-block-type.php Lines 137 to 146 in 348e463
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. There's also https://spacetelescope.github.io/understanding-json-schema/reference/generic.html#enumerated-values 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. Huh, nice! Okay, I'll look at that. 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. This 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. Entirely not, sorry. Fixed. |
||||||||||||||||||||||
|
||||||||||||||||||||||
/** | ||||||||||||||||||||||
* 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.
IMO we should JSDoc any constant, even just to establish the convention in encouraging the practice for less-obvious constants.
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'm cool with that 👍