Skip to content
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 featured images to Latest Posts block #17151

Merged
21 changes: 21 additions & 0 deletions lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -677,3 +677,24 @@ function gutenberg_extend_block_editor_preload_paths( $preload_paths, $post ) {
return $preload_paths;
}
add_filter( 'block_editor_preload_paths', 'gutenberg_extend_block_editor_preload_paths', 10, 2 );

/**
* Extends block editor settings to include a list of image dimensions per size.
*
* @param array $settings Default editor settings.
*
* @return array Filtered editor settings.
*/
function gutenberg_extend_settings_image_dimensions( $settings ) {
$image_dimensions = array();
$all_sizes = wp_get_registered_image_subsizes();
foreach ( $settings['imageSizes'] as $size ) {
$key = $size['slug'];
if ( isset( $all_sizes[ $key ] ) ) {
$image_dimensions[ $key ] = $all_sizes[ $key ];
}
}
$settings['imageDimensions'] = $image_dimensions;
return $settings;
}
add_filter( 'block_editor_settings', 'gutenberg_extend_settings_image_dimensions' );
147 changes: 141 additions & 6 deletions packages/block-library/src/latest-posts/edit.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
/**
* External dependencies
*/
import { isUndefined, pickBy } from 'lodash';
import { get, isUndefined, pickBy } from 'lodash';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { Component, RawHTML } from '@wordpress/element';
import {
BaseControl,
PanelBody,
Placeholder,
QueryControls,
Expand All @@ -22,7 +23,12 @@ import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import { __ } from '@wordpress/i18n';
import { dateI18n, format, __experimentalGetSettings } from '@wordpress/date';
import { InspectorControls, BlockControls } from '@wordpress/block-editor';
import {
InspectorControls,
BlockAlignmentToolbar,
BlockControls,
__experimentalImageSizeControl as ImageSizeControl,
} from '@wordpress/block-editor';
import { withSelect } from '@wordpress/data';

/**
Expand Down Expand Up @@ -63,9 +69,17 @@ class LatestPostsEdit extends Component {
}

render() {
const { attributes, setAttributes, latestPosts } = this.props;
const {
attributes,
setAttributes,
imageSizeOptions,
latestPosts,
defaultImageWidth,
defaultImageHeight,
} = this.props;
const { categoriesList } = this.state;
const {
displayFeaturedImage,
displayPostContentRadio,
displayPostContent,
displayPostDate,
Expand All @@ -76,6 +90,10 @@ class LatestPostsEdit extends Component {
categories,
postsToShow,
excerptLength,
featuredImageAlign,
featuredImageSizeSlug,
featuredImageSizeWidth,
featuredImageSizeHeight,
} = attributes;

const inspectorControls = (
Expand Down Expand Up @@ -130,6 +148,62 @@ class LatestPostsEdit extends Component {
/>
</PanelBody>

<PanelBody title={ __( 'Featured Image Settings' ) }>
<ToggleControl
label={ __( 'Display featured image' ) }
checked={ displayFeaturedImage }
onChange={ ( value ) =>
setAttributes( { displayFeaturedImage: value } )
}
/>
{ displayFeaturedImage && (
<>
<ImageSizeControl
onChange={ ( value ) => {
const newAttrs = {};
if ( value.hasOwnProperty( 'width' ) ) {
newAttrs.featuredImageSizeWidth =
value.width;
}
if ( value.hasOwnProperty( 'height' ) ) {
newAttrs.featuredImageSizeHeight =
value.height;
}
setAttributes( newAttrs );
} }
slug={ featuredImageSizeSlug }
width={ featuredImageSizeWidth }
height={ featuredImageSizeHeight }
imageWidth={ defaultImageWidth }
imageHeight={ defaultImageHeight }
imageSizeOptions={ imageSizeOptions }
onChangeImage={ ( value ) =>
setAttributes( {
featuredImageSizeSlug: value,
featuredImageSizeWidth: undefined,
featuredImageSizeHeight: undefined,
} )
}
/>
<BaseControl>
<BaseControl.VisualLabel>
{ __( 'Image Alignment' ) }
</BaseControl.VisualLabel>
<BlockAlignmentToolbar
value={ featuredImageAlign }
onChange={ ( value ) =>
setAttributes( {
featuredImageAlign: value,
} )
}
controls={ [ 'left', 'center', 'right' ] }
isCollapsed={ false }
/>
</BaseControl>
</>
) }
</PanelBody>

<PanelBody title={ __( 'Sorting and filtering' ) }>
<QueryControls
{ ...{ order, orderBy } }
Expand Down Expand Up @@ -236,12 +310,35 @@ class LatestPostsEdit extends Component {

const excerptElement = document.createElement( 'div' );
excerptElement.innerHTML = excerpt;

excerpt =
excerptElement.textContent ||
excerptElement.innerText ||
'';

const imageSourceUrl = post.featuredImageSourceUrl;

const imageClasses = classnames( {
'wp-block-latest-posts__featured-image': true,
[ `align${ featuredImageAlign }` ]: !! featuredImageAlign,
} );

return (
<li key={ i }>
{ displayFeaturedImage && (
<div className={ imageClasses }>
{ imageSourceUrl && (
<img
src={ imageSourceUrl }
alt=""
style={ {
maxWidth: featuredImageSizeWidth,
maxHeight: featuredImageSizeHeight,
} }
/>
) }
</div>
) }
<a
href={ post.link }
target="_blank"
Expand Down Expand Up @@ -314,8 +411,16 @@ class LatestPostsEdit extends Component {
}

export default withSelect( ( select, props ) => {
const { postsToShow, order, orderBy, categories } = props.attributes;
const { getEntityRecords } = select( 'core' );
const {
featuredImageSizeSlug,
postsToShow,
order,
orderBy,
categories,
} = props.attributes;
const { getEntityRecords, getMedia } = select( 'core' );
const { getSettings } = select( 'core/block-editor' );
const { imageSizes, imageDimensions } = getSettings();
const latestPostsQuery = pickBy(
{
categories,
Expand All @@ -325,7 +430,37 @@ export default withSelect( ( select, props ) => {
},
( value ) => ! isUndefined( value )
);

const posts = getEntityRecords( 'postType', 'post', latestPostsQuery );
const imageSizeOptions = imageSizes
.filter( ( { slug } ) => slug !== 'full' )
.map( ( { name, slug } ) => ( { value: slug, label: name } ) );

return {
latestPosts: getEntityRecords( 'postType', 'post', latestPostsQuery ),
defaultImageWidth: imageDimensions[ featuredImageSizeSlug ].width,
defaultImageHeight: imageDimensions[ featuredImageSizeSlug ].height,
imageSizeOptions,
latestPosts: ! Array.isArray( posts )
? posts
: posts.map( ( post ) => {
if ( post.featured_media ) {
const image = getMedia( post.featured_media );
let url = get(
image,
[
'media_details',
'sizes',
featuredImageSizeSlug,
'source_url',
],
null
);
if ( ! url ) {
url = get( image, 'source_url', null );
}
return { ...post, featuredImageSourceUrl: url };
}
return post;
} ),
};
} )( LatestPostsEdit );
51 changes: 50 additions & 1 deletion packages/block-library/src/latest-posts/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,41 @@ function render_block_core_latest_posts( $attributes ) {
$list_items_markup = '';

foreach ( $recent_posts as $post ) {
$list_items_markup .= '<li>';

if ( $attributes['displayFeaturedImage'] && has_post_thumbnail( $post ) ) {
$image_style = '';
if ( isset( $attributes['featuredImageSizeWidth'] ) ) {
$image_style .= sprintf( 'max-width:%spx;', $attributes['featuredImageSizeWidth'] );
}
if ( isset( $attributes['featuredImageSizeHeight'] ) ) {
$image_style .= sprintf( 'max-height:%spx;', $attributes['featuredImageSizeHeight'] );
}

$image_classes = 'wp-block-latest-posts__featured-image';
if ( isset( $attributes['featuredImageAlign'] ) ) {
$image_classes .= ' align' . $attributes['featuredImageAlign'];
}

$list_items_markup .= sprintf(
'<div class="%1$s">%2$s</div>',
$image_classes,
get_the_post_thumbnail(
$post,
$attributes['featuredImageSizeSlug'],
array(
'style' => $image_style,
)
)
);
}

$title = get_the_title( $post );
if ( ! $title ) {
$title = __( '(no title)' );
}
$list_items_markup .= sprintf(
'<li><a href="%1$s">%2$s</a>',
'<a href="%1$s">%2$s</a>',
esc_url( get_permalink( $post ) ),
$title
);
Expand Down Expand Up @@ -164,6 +193,26 @@ function register_block_core_latest_posts() {
'type' => 'string',
'default' => 'date',
),
'displayFeaturedImage' => array(
'type' => 'boolean',
'default' => false,
),
'featuredImageAlign' => array(
'type' => 'string',
'enum' => array( 'left', 'center', 'right' ),
),
'featuredImageSizeSlug' => array(
'type' => 'string',
'default' => 'thumbnail',
),
'featuredImageSizeWidth' => array(
'type' => 'number',
'default' => null,
),
'featuredImageSizeHeight' => array(
'type' => 'number',
'default' => null,
),
),
'render_callback' => 'render_block_core_latest_posts',
)
Expand Down
23 changes: 23 additions & 0 deletions packages/block-library/src/latest-posts/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
}
&.wp-block-latest-posts__list {
list-style: none;

li {
clear: both;
}
}
&.is-grid {
display: flex;
Expand Down Expand Up @@ -40,3 +44,22 @@
margin-top: $grid-size;
margin-bottom: $grid-size-large;
}

.wp-block-latest-posts__featured-image {
img {
height: auto;
width: auto;
}
&.alignleft {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alignleft and alignright classes are intended to be used with CSS float styles (and they probably shouldn't be called alignleft and alignright, but that's a whole other topic). This class style override is inconsistent with the usage of the class name elsewhere.

/*rtl:ignore*/
margin-right: 1em;
}
&.alignright {
/*rtl:ignore*/
margin-left: 1em;
}
&.aligncenter {
margin-bottom: 1em;
text-align: center;
}
}
1 change: 1 addition & 0 deletions packages/editor/src/components/provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class EditorProvider extends Component {
'hasFixedToolbar',
'hasPermissionsToManageWidgets',
'imageSizes',
'imageDimensions',
'isRTL',
'maxWidth',
'styles',
Expand Down