Retain formatting while using excerpt [ wp_trim_words() ] - php

I am using <?php echo wp_trim_words(get_the_content(), 100); ?> in my template file to control amount of words to be displayed on a page and also have a link below it for taking user to the next page to read entire content but this function removes all the formatting of content on preview page.
I can't use wordpress default excerpt function here as it is being used elsewhere and i need this to be of different length than that. Is there a way to retain formatting while using this ?
Thanks
I found a solution to this may be it can help others as well.
function content_excerpt($excerpt_length = 5, $id = false, $echo = true) {
$text = '';
if($id) {
$the_post = & get_post( $my_id = $id );
$text = ($the_post->post_excerpt) ? $the_post->post_excerpt : $the_post->post_content;
} else {
global $post;
$text = ($post->post_excerpt) ? $post->post_excerpt : get_the_content('');
}
$text = strip_shortcodes( $text );
$text = apply_filters('the_content', $text);
$text = str_replace(']]>', ']]>', $text);
$words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
if ( count($words) > $excerpt_length ) {
array_pop($words);
$text = implode(' ', $words);
$text = $text . $excerpt_more;
} else {
$text = implode(' ', $words);
}
if($echo)
echo apply_filters('the_content', $text);
else
return $text;
}
function get_content_excerpt($excerpt_length = 5, $id = false, $echo = false) {
return content_excerpt($excerpt_length, $id, $echo);
}
// Call function in template file via
<?php content_excerpt(50); // 50 is amount to words ?>

To anyone else facing similar issues, I took the default wp_trim_words() function directly out of core, and altered it to not strip tags:
function wp_trim_words_retain_formatting( $text, $num_words = 55, $more = null ) {
if ( null === $more )
$more = __( '…' );
$original_text = $text;
/* translators: If your word count is based on single characters (East Asian characters),
enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
preg_match_all( '/./u', $text, $words_array );
$words_array = array_slice( $words_array[0], 0, $num_words + 1 );
$sep = '';
} else {
$words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
$sep = ' ';
}
if ( count( $words_array ) > $num_words ) {
array_pop( $words_array );
$text = implode( $sep, $words_array );
$text = $text . $more;
} else {
$text = implode( $sep, $words_array );
}
/**
* Filter the text content after words have been trimmed.
*
* #since 3.3.0
*
* #param string $text The trimmed text.
* #param int $num_words The number of words to trim the text to. Default 5.
* #param string $more An optional string to append to the end of the trimmed text, e.g. ….
* #param string $original_text The text before it was trimmed.
*/
return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
}

Try:
echo apply_filters( 'the_content', wp_trim_words( get_the_content(), 100 ) );

Related

Why this function is shortening also my Archive Description?

I don't understand why function below influence woocommerce_taxonomy_archive_description()
Where is connection between those two? Shouldn't this work only for woocommerce_short_description?
add_filter( 'woocommerce_short_description', 'limit_woocommerce_short_description' );
function limit_woocommerce_short_description( $post_post_excerpt ) {
if(!is_product()) { // add in conditionals
$text = $post_post_excerpt;
$words = 24; // change word length
$more = ' […]'; // add a more cta
$post_post_excerpt = wp_trim_words( $text, $words, $more );
}
return $post_post_excerpt;
}

Get first paragraph in wordpress

I have a problem with getting the first form the_excerpt();
Function acutally works but only for first post. I added in functions.php
function get_first_paragraph(){
global $post;
$str = wpautop( get_the_content() );
$str = substr( $str, 0, strpos( $str, '</p>' ) + 4 );
$str = strip_tags($str, '<a><strong><em>');
return '<p>' . $str . '</p>';
}
I'm calling this funcion in index.php inside The Loop <?php echo get_first_paragraph(); ?>
I have no idea why it pulls only for first post...
you can put this code in function.php file in your theme,
Get first paragraph
function awesome_excerpt($text, $raw_excerpt) {
if( ! $raw_excerpt ) {
$content = apply_filters( 'the_content', get_the_content() );
$text = substr( $content, 0, strpos( $content, '</p>' ) + 4 );
}
return $text;
}
add_filter( 'wp_trim_excerpt', 'awesome_excerpt', 10, 2 );
For more information, you can follow the reference link WORDPRESS THE_EXCERPT SHOW ONLY FIRST PARAGRAPH
That code didn't work for me in Gutenberg times. So I've used part of that code and did some searching and come up with this solution. Hope it helps.
function get_paragraph_content($paragraph_number){
global $post;
$i = 0;
$paragraph = '';
if ( has_blocks( $post->post_content ) ) {
$blocks = parse_blocks( $post->post_content );
foreach( $blocks as $block ) {
if( 'core/paragraph' === $block['blockName']){
$paragraph = render_block($block);
if (++$i == $paragraph_number) break;
}
}
$paragraph = substr( $paragraph, 0, strpos( $paragraph, '</p>' ) + 4 );
$paragraph = strip_tags($paragraph, '<a><strong><em>');
}
return $paragraph;
}

Display 1st paragraph within Advanced Custom Fields

How do you display only the first paragraph of an advanced custom field.
<?php the_field('lyrics'); ?>
Above is what i use to display the full text.
add_filter( 'wp_trim_excerpt', 'my_custom_excerpt', 10, 2 );
function my_custom_excerpt($text, $raw_excerpt) {
if( ! $raw_excerpt ) {
$content = apply_filters( 'the_content', get_the_content() );
$text = substr( $content, 0, strpos( $content, '</p>' ) + 4 );
}
return $text;
}
try this code will help you to show first 55 character of your first paragraph.
Grab the first paragraph of each post
second option:
function custom_field_excerpt() {
global $post;
$text = get_field('news');
if ( '' != $text ) {
$start = strpos($text, '<p>'); // Locate the first paragraph tag
$end = strpos($text, '</p>', $start); // Locate the first paragraph closing tag
$text = substr($text, $start, $end-$start+4); // Trim off everything after the closing paragraph tag
$text = strip_shortcodes( $text );
$text = apply_filters('the_content', $text);
$text = str_replace(']]>', ']]>', $text);
}
return $text;}
third option :
You can use this function:
function get_first_paragraph(){
global $post;
$str = wpautop( get_the_content() );
$str = substr( $str, 0, strpos( $str, '</p>' ) + 4 );
$str = strip_tags($str, '<a><strong><em>');
return '<p>' . $str . '</p>';}
and then use in it in your loop with:
<?php echo get_first_paragraph(); ?>

Ignore wordpress shortcode when outputting a limited word count

I have created my own custom word limit function in wordpress. I need a way to ignore the shortcode as part of the word count. I don't want to strip it out but ignore shortcodes as part of the word count. Otherwise if you choose a number say 15 and the shortcode is in any part of that 15 word limit then the page will fatal error.
function my_word_limit($limit) {
$content = explode(' ', get_the_content(), $limit);
if (count($content)>=$limit) {
array_pop($content);
$content = implode(" ",$content).'...';
} else {
$content = implode(" ",$content);
}
$content = apply_filters('the_content', $content);
return $content;
}
This is the shortcode I would be using for example.
[di-video-logged-out]<iframe src="https://www.youtube.com/embed/LEIu8gba634" width="854" height="480"></iframe>[/di-video-logged-out]
Trim content and limit with certain count word
( Keep shortcode on output even content trimmed, you can tweak also if you don't )
Here my approach with use wp_trim_word of the content and filter wp_trim_word. Also, you can use this function wpso36236774_trim_words into such as $post->post_content or get_the_content directly ( without filter ). Usage was commented inside code.
add_filter( 'wp_trim_words', 'wpso36236774_trim_words', 10, 4 );
/* Trims text to a certain number of words.
* #author Jevuska
* #version 1.0.1
*
* Kepp shortcode if exist in text.
* Combination function of strip_shortcodes and wp_trim_words
* Default $num_words = 55
*
** USAGE
** Using directly
** wpso36236774_trim_words( $text, 56 )
** wpso36236774_trim_words( $text, 56, null, false, false, true ) - return array
** Shortcode hidden if $num_words is not set or if set with value = 55 with 4 arguments
**
** Use wp_trim_words
** wp_trim_words( $text, $num_words = 56 )
** Fire wp_trim_words
** Shortcode hidden if $num_words is not set or $num_words = 55
** Position always in bottom
** add_filter( 'wp_trim_words', 'wpso36236774_trim_words', 10, 4 );
*
* #param string $text Text to trim.
* #param int $num_words The number of words to trim the text to. Default 5.
* #param string $more An optional string to append to the end of the trimmed text, e.g. ….
* #param string $original_content The text before it was trimmed.
* #param mix $pos Shortcode Position. You can set 'top' value if using directly
* #param boolean $count Get word count
* #return string The text after the filter witch $num_words
* #return array If using directly and parameter $count set to true
*/
function wpso36236774_trim_words( $text, $num_words = 55, $more = null, $original_content = false, $pos = false, $count = false )
{
if ( null === $more)
$more = ' ' . '[…]';
$shortcode = $strip_shortcode = true;
if ( ! $original_content )
$original_content = $text;
$text = $original_content;
/* Check existing shortcode
*
*/
if ( false === strpos( $text, '[' ) )
$strip_shortcode = false;
global $shortcode_tags;
if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) )
$strip_shortcode = false;
/* Strip content from shortcode
*
*/
if ( $strip_shortcode )
{
preg_match_all( '#\[([^<>&/\[\]\x00-\x20=]++)#', $text, $matches );
$tagnames = array_intersect( array_keys( $shortcode_tags ), $matches[1] );
if ( ! empty( $tagnames ) )
{
$text = do_shortcodes_in_html_tags( $text, true, $tagnames );
$pattern = get_shortcode_regex( $tagnames );
preg_match_all( "/$pattern/", $text, $match );
if ( ! empty( $match[0] ) && is_array( $match[0] ) )
{
$shortcode = '';
$length = count( $match[0] );
for ( $i = 0 ; $i < $length; $i++ )
$shortcode .= do_shortcode( $match[0][ $i ] ); //match shortcode
}
$text = preg_replace_callback( "/$pattern/", 'strip_shortcode_tag', $text );
$text = unescape_invalid_shortcodes( $text );
}
}
/* Hide shortcode
* Base on count function arguments
*
*/
if ( func_num_args() == 1 || ( func_num_args() == 4 && 55 == $num_words ) )
$shortcode = '';
/* Split content into array words
*
*/
$text = wp_strip_all_tags( $text );
/*
* 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.
*/
if ( strpos( _x( 'words', 'Word count type. Do not translate!' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) )
{
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
preg_match_all( '/./u', $text, $words_array );
$limit_words_array = array_slice( $words_array[0], 0, $num_words + 1 );
$full_words_array = $words_array[0];
$sep = '';
}
else
{
$limit_words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
$full_words_array = explode( ' ', preg_replace( "/[\n\r\t ]+/", ' ', $text ) );
$sep = ' ';
}
/* Check word count base on $num_words
*
*/
$word_count = count( $full_words_array );
if ( $word_count >= $num_words )
{
array_pop( $limit_words_array );
$text = implode( $sep, $limit_words_array );
$text .= $more;
/* keep shortcode if exists and set position ( top or bottom text )
*
*/
switch( $pos )
{
case 'top' :
$text = $shortcode . $text;
break;
default :
$text .= $shortcode;
break;
}
}
else
{
$text = apply_filters( 'the_content', $original_content );
}
if ( $count )
return array(
'text' => $text,
'count' => $word_count
);
return $text; //output
}
Let me know if any issue about this code. Tweak as you need and I hope this helps.
UPDATE
Fix multiple shortcode
Hide shortcode option base on total argument
Patch $pos for additional shortcode position top and bottom text ( func directly only ). For another positon, you must set class and css for your shortcode.

Modify wp_trim_words function to return content split in 2

Im trying to modify the wp_trim_words function to return the left over words as well as the first part, any help much appreciated.
function wp_trim_words_new( $text, $num_words = 55, $more = null ) {
if ( null === $more )
$more = __( '…' );
$original_text = $text;
$text = wp_strip_all_tags( $text );
/* translators: If your word count is based on single characters (East Asian characters),
enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
preg_match_all( '/./u', $text, $words_array );
$words_array = array_slice( $words_array[0], 0, $num_words + 1 );
$sep = '';
} else {
$words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
$sep = ' ';
}
if ( count( $words_array ) > $num_words ) {
array_pop( $words_array );
$text = implode( $sep, $words_array );
$text = $text . $more;
} else {
$text = implode( $sep, $words_array );
}
/**
* Filter the text content after words have been trimmed.
*
* #since 3.3.0
*
* #param string $text The trimmed text.
* #param int $num_words The number of words to trim the text to. Default 5.
* #param string $more An optional string to append to the end of the trimmed text, e.g. ….
* #param string $original_text The text before it was trimmed.
*/
return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
}
It seems to me that you simply copied the trim_words() function of Wordpress. You could have done better. Your desired output is unclear.
Supposing that you would be happy with the simple tag-less string of left-over words returned together with the output of the original function, you could use something like this (untested):
function wp_trim_words_2( $text, $num_words = 55, $more = null ) {
if ( null === $more )
$more = __( '…' );
$original_text = $text;
$text = wp_strip_all_tags( $text );
/* translators: If your word count is based on single characters (East Asian characters),
enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
preg_match_all( '/\X/u', $text, $match_array );
$split_array = $match_array[0];
$sep = '';
} else {
$split_array = preg_split( "/[\n\r\t ]+/", $text, -1, PREG_SPLIT_NO_EMPTY );
$sep = ' ';
}
$words_array = array_slice( $split_array, 0, $num_words + 1 );
if ( count( $words_array ) > $num_words ) {
array_pop( $words_array );
$text = implode( $sep, $words_array );
$text = $text . $more;
$rest = implode( $sep, array_slice( $split_array, $num_words ) );
} else {
$text = implode( $sep, $words_array );
$rest = '';
}
/**
* Filter the text content after words have been trimmed.
*
* #since 3.3.0
*
* #param string $text The trimmed text.
* #param int $num_words The number of words to trim the text to. Default 5.
* #param string $more An optional string to append to the end of the trimmed text, e.g. ….
* #param string $original_text The text before it was trimmed.
*/
return array( apply_filters( 'wp_trim_words_2', $text, $num_words, $more, $original_text ), $rest );
}
to be called like this:
list($trimmed, $rest) = wp_trim_words_2($text);
I'm not sure about apply_filters(), though. Should it be passed the $rest too? It's too time-consuming for me to study this one, sorry.
If, on the other hand, you wanted the $rest before tag removal (i.e., before the call to wp_strip_all_tags()), then things are much more complicated, because the original functions splits the input into words after that, and I can think only of inefficient ways to keep the correspondence between the input and the output of wp_strip_all_tags().
BTW, the original code by WordPress contains a bug: words for East Asian languages are wrongly counted (and potentially split!) as single Unicode characters instead of Unicode graphemes. The fix is to use '/\X/u' instead of '/./u' (read here for an explanation).
Not sure you need to tweak that function at all, this code does what you ask:
$trimmed_text = wp_trim_words($text, $wordlength, '');
// Measure full and trimmed widths for comparison
$fw = mb_strwidth($text);
$tw = mb_strwidth($trimmed_text);
if( $fw != $tw ){
$clipped_text = mb_strimwidth($text, $tw, $fw - $tw, '');
// Text has been cut
} else {
// Text has NOT been cut
}
or wrap it in a function behaving as you wanted initially
function wp_trim_words_new($text, $length, $delimiter=''){
$trimmed_text = wp_trim_words($text, $length, $delimiter);
$fw = mb_strwidth($text);
$tw = mb_strwidth($trimmed_text);
if( $fw != $tw ){
return [$trimmed_text, mb_strimwidth($text, $tw, $fw - $tw, $delimiter)];
} else {
return [$text, null];
}
}
You can accomplish this without the wp_trim_words function.
$sentence = "This is a sample sentence";
$words = explode(" ", $sentence);
$half = count($words) / 2;
$first_half = implode(" ", array_slice($words, 0, $half));
$second_half = implode(" ", array_slice($words, $half));
echo $first_half . "\n";
echo $second_half;

Categories