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

Post excerpt: Add excerpt length control #44964

Merged
merged 22 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bef63aa
Post excerpt: Add excerpt length control
carolinan Oct 13, 2022
5098f0d
Update edit.js
carolinan Oct 17, 2022
b7a54a3
Update index.php
carolinan Oct 17, 2022
17576fa
Update core__post-excerpt.json
carolinan Oct 17, 2022
5059371
Update index.php
carolinan Oct 17, 2022
aa5db23
Update index.php
carolinan Oct 17, 2022
34b2691
use wp_trim_words instead of the excerpt length filter
carolinan Nov 21, 2022
bc051de
Display a warning when the excerpt has a word count that is higher th…
carolinan Nov 21, 2022
1d686af
Update packages/block-library/src/post-excerpt/index.php
carolinan Nov 21, 2022
86342e2
Support word count types
carolinan Nov 22, 2022
55edf70
Update the word count warning and add speak().
carolinan Nov 25, 2022
ff3f9ee
Add useDebounce hook to delay speak() while typing an excerpt text.
carolinan Nov 25, 2022
5a9178d
Merge branch 'trunk' into try/excerpt-length-setting
carolinan Dec 12, 2022
ef266d5
Merge branch 'trunk' into try/excerpt-length-setting
carolinan Dec 16, 2022
daa5b05
Update edit.js
carolinan Dec 16, 2022
e8c557f
Update core__query__deprecated-3.json
carolinan Dec 16, 2022
085c025
Remove the word count message.
carolinan Jan 20, 2023
5783b0f
Remove the wordcount package
carolinan Jan 20, 2023
7771d02
Remove the unused CSS
carolinan Jan 20, 2023
0ddaf98
Merge branch 'trunk' into try/excerpt-length-setting
carolinan Jan 23, 2023
c32920f
Merge branch 'trunk' into try/excerpt-length-setting
carolinan Feb 1, 2023
64d255c
Merge branch 'trunk' into try/excerpt-length-setting
carolinan Feb 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ Display a post's excerpt. ([Source](https://github.com/WordPress/gutenberg/tree/
- **Name:** core/post-excerpt
- **Category:** theme
- **Supports:** color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** moreText, showMoreOnNewLine, textAlign
- **Attributes:** excerptLength, moreText, showMoreOnNewLine, textAlign

## Post Featured Image

Expand Down
1 change: 1 addition & 0 deletions packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"@wordpress/server-side-render": "file:../server-side-render",
"@wordpress/url": "file:../url",
"@wordpress/viewport": "file:../viewport",
"@wordpress/wordcount": "file:../wordcount",
"change-case": "^4.1.2",
"classnames": "^2.3.1",
"colord": "^2.7.0",
Expand Down
4 changes: 4 additions & 0 deletions packages/block-library/src/post-excerpt/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
"showMoreOnNewLine": {
"type": "boolean",
"default": true
},
"excerptLength": {
"type": "number",
"default": 55
}
},
"usesContext": [ "postId", "postType", "queryId" ],
Expand Down
79 changes: 72 additions & 7 deletions packages/block-library/src/post-excerpt/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,24 @@ 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
*/
import { useCanEditEntity } from '../utils/hooks';

export default function PostExcerptEditor( {
attributes: { textAlign, moreText, showMoreOnNewLine },
attributes: { textAlign, moreText, showMoreOnNewLine, excerptLength },
setAttributes,
isSelected,
context: { postId, postType, queryId },
} ) {
const isDescendentOfQueryLoop = Number.isFinite( queryId );
const userCanEdit = useCanEditEntity( 'postType', postType, postId );
const isEditable = userCanEdit && ! isDescendentOfQueryLoop;

const [
rawExcerpt,
setExcerpt,
Expand All @@ -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
Expand Down Expand Up @@ -99,21 +108,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 + '...';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried using … but it did not work. In the block editor the characters flashed before they were converted and in the site editor only the plain characters showed.

Copy link
Member

Choose a reason for hiding this comment

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

Any reason we shouldn't use the directly?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll ask the same thing Rich asked.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No reason, I only missed the question the first time :)


const excerptContent = isEditable ? (
<RichText
className={ excerptClassName }
aria-label={ __( 'Post excerpt text' ) }
value={
rawExcerpt ||
strippedRenderedExcerpt ||
( isSelected ? '' : __( 'No post excerpt found' ) )
isSelected
? rawOrRenderedExcerpt
: ( trimmedExcerpt !== '...' ? trimmedExcerpt : '' ) ||
__( 'No post excerpt found' )
}
onChange={ setExcerpt }
tagName="p"
/>
) : (
<p className={ excerptClassName }>
{ strippedRenderedExcerpt || __( 'No post excerpt found' ) }
{ trimmedExcerpt !== '...'
? trimmedExcerpt
: __( 'No post excerpt found' ) }
</p>
);
return (
Expand All @@ -137,6 +192,16 @@ export default function PostExcerptEditor( {
} )
}
/>
<RangeControl
label={ __( 'Max number of words' ) }
value={ excerptLength }
onChange={ ( value ) => {
setAttributes( { excerptLength: value } );
setExcerpt();
} }
min="10"
max="100"
/>
</PanelBody>
</InspectorControls>
<div { ...blockProps }>
Expand Down
5 changes: 5 additions & 0 deletions packages/block-library/src/post-excerpt/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@
display: inline-block;
}
}

.wp-block-post-excerpt .block-editor-warning {
width: fit-content;
margin-left: auto;
}
34 changes: 29 additions & 5 deletions packages/block-library/src/post-excerpt/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Comment on lines +27 to +31
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not inject a filter with the dynamic excerpt length and just call get_the_excerpt, which is already filtered by wp_trim_excerpt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't believe I tried that during the development, so there is no obvious reason for not trying that as a possible improvement in a separate PR.

Choose a reason for hiding this comment

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

The addition of the call to "wp_trim_words" will remove any HTML that users have put into a custom excerpt by over-riding get_the_excerpt. Was this deliberate, or an unfortunate by-product?

Copy link
Contributor

Choose a reason for hiding this comment

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

The addition of the call to "wp_trim_words" will remove any HTML that users have put into a custom excerpt by over-riding get_the_excerpt. Was this deliberate, or an unfortunate by-product?

I believe that was accidental, so thank you for the flag.

Copy link

@frzsombor frzsombor Sep 14, 2023

Choose a reason for hiding this comment

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

Workaround to bypass the wp_trim_words function:

function fzs_filter_metadata_registration( $metadata ) {
    if ($metadata['name'] === 'core/post-excerpt') {
	unset($metadata['attributes']['excerptLength']);
    }
    return $metadata;
};
add_filter( 'block_type_metadata', 'fzs_filter_metadata_registration' );

Choose a reason for hiding this comment

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

Thank you - that's a really helpful workaround for now

Copy link
Member

Choose a reason for hiding this comment

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

This was also reported in the forums and I've confirmed it was introduced in WordPress 6.3. Created an issue at #66195

}

$more_text = ! empty( $attributes['moreText'] ) ? '<a class="wp-block-post-excerpt__more-link" href="' . esc_url( get_the_permalink( $block->context['postId'] ) ) . '">' . wp_kses_post( $attributes['moreText'] ) . '</a>' : '';
$filter_excerpt_more = function( $more ) use ( $more_text ) {
return empty( $more_text ) ? $more : '';
Expand Down Expand Up @@ -67,3 +73,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' ) {
Comment on lines +86 to +88
Copy link
Contributor

Choose a reason for hiding this comment

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

This is very wide net to cast which will affect all instances of excerpt filtering, regardless of source (Post Excerpt block, Latest Post block, traditional theme templates, etc.) — see #48403.

Why not surround the rendering logic in render_block_core_post_excerpt with a pair of add_filter / remove_filter calls?

Copy link
Contributor

Choose a reason for hiding this comment

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

What I meant by my suggestion was something like this (untested):

function render_block_core_post_excerpt( $attributes, $content, $block ) {
    global $_block_core_post_excerpt_length;

    // ...

    $_block_core_post_excerpt_length = int( $attributes['excerptLength'] );
    add_filter( 'excerpt_length', '_block_core_post_excerpt_filter', PHP_INT_MAX );
    get_the_excerpt();
    remove_filter( 'excerpt_length', '_block_core_post_excerpt_filter' );

    // ...

with

function _block_core_post_excerpt_filter() {
  global $_block_core_post_excerpt_length;
  return $_block_core_post_excerpt_length;
}

The whole global dance is a possible way to work with the fact that $attributes['excerptLength'] is dynamic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is already a pull request open that tries to address this.

Copy link
Contributor

Choose a reason for hiding this comment

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

There is already a pull request open that tries to address this.

Which?

Choose a reason for hiding this comment

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

Not sure if it's the one @carolinan was referring to, but I opened #48598 when I discovered that my excerpts outside of the block editor were not obeying my own excerpt_length filter and traced it back to the line you referenced here:

This is very wide net to cast which will affect all instances of excerpt filtering, regardless of source (Post Excerpt block, Latest Post block, traditional theme templates, etc.) — see #48403.

My last revision in my PR did something like what you suggest here:

Why not surround the rendering logic in render_block_core_post_excerpt with a pair of add_filter / remove_filter calls?

But it only applies to the rendered block on the page; it is not reflected in the editor. The condition on REST_REQUEST is the part that makes it work in the editor. I was also concerned that condition casts too wide a net and raised the question in my PR, but received no feedback on that point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that is the PR I was thinking of

add_filter(
'excerpt_length',
function() {
return 100;
},
PHP_INT_MAX
);
}
3 changes: 2 additions & 1 deletion test/integration/fixtures/blocks/core__post-excerpt.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"name": "core/post-excerpt",
"isValid": true,
"attributes": {
"showMoreOnNewLine": true
"showMoreOnNewLine": true,
"excerptLength": 55
},
"innerBlocks": []
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@
"name": "core/post-excerpt",
"isValid": true,
"attributes": {
"showMoreOnNewLine": true
"showMoreOnNewLine": true,
"excerptLength": 55
},
"innerBlocks": []
}
Expand Down