diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index d18ac79b8270c..c0c60f792ac43 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -546,7 +546,7 @@ Display a post's excerpt. ([Source](https://github.com/WordPress/gutenberg/tree/ - **Name:** core/post-excerpt - **Category:** theme - **Supports:** anchor, color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ -- **Attributes:** moreText, showMoreOnNewLine, textAlign +- **Attributes:** excerptLength, moreText, showMoreOnNewLine, textAlign ## Post Featured Image diff --git a/packages/block-library/src/post-excerpt/block.json b/packages/block-library/src/post-excerpt/block.json index 223b4c68bde0e..53a92cb0bda63 100644 --- a/packages/block-library/src/post-excerpt/block.json +++ b/packages/block-library/src/post-excerpt/block.json @@ -16,6 +16,10 @@ "showMoreOnNewLine": { "type": "boolean", "default": true + }, + "excerptLength": { + "type": "number", + "default": 55 } }, "usesContext": [ "postId", "postType", "queryId" ], diff --git a/packages/block-library/src/post-excerpt/edit.js b/packages/block-library/src/post-excerpt/edit.js index fb2df88e87214..abe03705db680 100644 --- a/packages/block-library/src/post-excerpt/edit.js +++ b/packages/block-library/src/post-excerpt/edit.js @@ -16,8 +16,8 @@ import { Warning, useBlockProps, } from '@wordpress/block-editor'; -import { PanelBody, ToggleControl } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { PanelBody, ToggleControl, RangeControl } from '@wordpress/components'; +import { __, _x } from '@wordpress/i18n'; /** * Internal dependencies @@ -25,7 +25,7 @@ import { __ } from '@wordpress/i18n'; import { useCanEditEntity } from '../utils/hooks'; export default function PostExcerptEditor( { - attributes: { textAlign, moreText, showMoreOnNewLine }, + attributes: { textAlign, moreText, showMoreOnNewLine, excerptLength }, setAttributes, isSelected, context: { postId, postType, queryId }, @@ -33,6 +33,7 @@ export default function PostExcerptEditor( { const isDescendentOfQueryLoop = Number.isFinite( queryId ); const userCanEdit = useCanEditEntity( 'postType', postType, postId ); const isEditable = userCanEdit && ! isDescendentOfQueryLoop; + const [ rawExcerpt, setExcerpt, @@ -43,6 +44,14 @@ export default function PostExcerptEditor( { [ `has-text-align-${ textAlign }` ]: textAlign, } ), } ); + + /** + * translators: If your word count is based on single characters (e.g. East Asian characters), + * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'. + * Do not translate into your own language. + */ + const wordCountType = _x( 'words', 'Word count type. Do not translate!' ); + /** * When excerpt is editable, strip the html tags from * rendered excerpt. This will be used if the entity's @@ -109,21 +118,67 @@ export default function PostExcerptEditor( { const excerptClassName = classnames( 'wp-block-post-excerpt__excerpt', { 'is-inline': ! showMoreOnNewLine, } ); + + /** + * The excerpt length setting needs to be applied to both + * the raw and the rendered excerpt depending on which is being used. + */ + const rawOrRenderedExcerpt = !! renderedExcerpt + ? strippedRenderedExcerpt + : rawExcerpt; + + let trimmedExcerpt = ''; + if ( wordCountType === 'words' ) { + trimmedExcerpt = rawOrRenderedExcerpt + .trim() + .split( ' ', excerptLength ) + .join( ' ' ); + } else if ( wordCountType === 'characters_excluding_spaces' ) { + /* + * 1. Split the excerpt at the character limit, + * then join the substrings back into one string. + * 2. Count the number of spaces in the excerpt + * by comparing the lengths of the string with and without spaces. + * 3. Add the number to the length of the visible excerpt, + * so that the spaces are excluded from the word count. + */ + const excerptWithSpaces = rawOrRenderedExcerpt + .trim() + .split( '', excerptLength ) + .join( '' ); + + const numberOfSpaces = + excerptWithSpaces.length - + excerptWithSpaces.replaceAll( ' ', '' ).length; + + trimmedExcerpt = rawOrRenderedExcerpt + .trim() + .split( '', excerptLength + numberOfSpaces ) + .join( '' ); + } else if ( wordCountType === 'characters_including_spaces' ) { + trimmedExcerpt = rawOrRenderedExcerpt.trim().split( '', excerptLength ); + } + + trimmedExcerpt = trimmedExcerpt + '...'; + const excerptContent = isEditable ? ( ) : (

- { strippedRenderedExcerpt || __( 'No post excerpt found' ) } + { trimmedExcerpt !== '...' + ? trimmedExcerpt + : __( 'No post excerpt found' ) }

); return ( @@ -147,6 +202,16 @@ export default function PostExcerptEditor( { } ) } /> + { + setAttributes( { excerptLength: value } ); + setExcerpt(); + } } + min="10" + max="100" + />
diff --git a/packages/block-library/src/post-excerpt/index.php b/packages/block-library/src/post-excerpt/index.php index 1380918fe1058..a9dde84d80148 100644 --- a/packages/block-library/src/post-excerpt/index.php +++ b/packages/block-library/src/post-excerpt/index.php @@ -18,12 +18,18 @@ function render_block_core_post_excerpt( $attributes, $content, $block ) { return ''; } - $excerpt = get_the_excerpt(); - - if ( empty( $excerpt ) ) { - return ''; + /* + * The purpose of the excerpt length setting is to limit the length of both + * automatically generated and user-created excerpts. + * Because the excerpt_length filter only applies to auto generated excerpts, + * wp_trim_words is used instead. + */ + $excerpt_length = $attributes['excerptLength']; + if ( isset( $excerpt_length ) ) { + $excerpt = wp_trim_words( get_the_excerpt(), $excerpt_length ); + } else { + $excerpt = get_the_excerpt(); } - $more_text = ! empty( $attributes['moreText'] ) ? '' . wp_kses_post( $attributes['moreText'] ) . '' : ''; $filter_excerpt_more = function( $more ) use ( $more_text ) { return empty( $more_text ) ? $more : ''; @@ -70,3 +76,21 @@ function register_block_core_post_excerpt() { ); } add_action( 'init', 'register_block_core_post_excerpt' ); + +/** + * If themes or plugins filter the excerpt_length, we need to + * override the filter in the editor, otherwise + * the excerpt length block setting has no effect. + * Returns 100 because 100 is the max length in the setting. + */ +if ( is_admin() || + defined( 'REST_REQUEST' ) || + 'REST_REQUEST' ) { + add_filter( + 'excerpt_length', + function() { + return 100; + }, + PHP_INT_MAX + ); +} diff --git a/test/integration/fixtures/blocks/core__post-excerpt.json b/test/integration/fixtures/blocks/core__post-excerpt.json index 57775868bdc86..827deda1b7215 100644 --- a/test/integration/fixtures/blocks/core__post-excerpt.json +++ b/test/integration/fixtures/blocks/core__post-excerpt.json @@ -3,7 +3,8 @@ "name": "core/post-excerpt", "isValid": true, "attributes": { - "showMoreOnNewLine": true + "showMoreOnNewLine": true, + "excerptLength": 55 }, "innerBlocks": [] } diff --git a/test/integration/fixtures/blocks/core__query__deprecated-3.json b/test/integration/fixtures/blocks/core__query__deprecated-3.json index bb1478cd676f2..2c682de49bdda 100644 --- a/test/integration/fixtures/blocks/core__query__deprecated-3.json +++ b/test/integration/fixtures/blocks/core__query__deprecated-3.json @@ -76,7 +76,8 @@ "name": "core/post-excerpt", "isValid": true, "attributes": { - "showMoreOnNewLine": true + "showMoreOnNewLine": true, + "excerptLength": 55 }, "innerBlocks": [] }