2020-09-15 14:30:05 +02:00
< ? php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
* Provides media summary of a post .
*
2021-04-27 08:32:47 +02:00
* @ package automattic / jetpack
2020-09-15 14:30:05 +02:00
*/
2023-09-26 10:24:36 +02:00
use Automattic\Jetpack\Image_CDN\Image_CDN_Core ;
2019-11-15 23:26:29 +01:00
/**
* Class Jetpack_Media_Summary
*
2020-09-15 14:30:05 +02:00
* Priority : embed [ video ] > gallery > image > text
2019-11-15 23:26:29 +01:00
*/
class Jetpack_Media_Summary {
2020-09-15 14:30:05 +02:00
/**
* Media cache .
*
* @ var array
*/
2019-11-15 23:26:29 +01:00
private static $cache = array ();
2020-09-15 14:30:05 +02:00
/**
* Get media summary for a post .
*
2025-02-28 08:42:11 +01:00
* @ param ? int $post_id Post ID .
2020-09-15 14:30:05 +02:00
* @ param int $blog_id Blog ID , if applicable .
* @ param array $args {
* Optional . An array of arguments .
* @ type int $max_words Maximum number of words .
* @ type int $max_chars Maximum number of characters .
* }
*
* @ return array | mixed | void
*/
2025-02-28 08:42:11 +01:00
public static function get ( ? int $post_id , int $blog_id = 0 , array $args = array () ) {
2020-09-15 14:30:05 +02:00
$post_id = ( int ) $post_id ;
$blog_id = ( int ) $blog_id ;
2019-11-15 23:26:29 +01:00
$defaults = array (
'max_words' => 16 ,
'max_chars' => 256 ,
);
2020-09-15 14:30:05 +02:00
$args = wp_parse_args ( $args , $defaults );
2019-11-15 23:26:29 +01:00
$switched = false ;
2020-09-15 14:30:05 +02:00
if ( ! empty ( $blog_id ) && get_current_blog_id () !== $blog_id && function_exists ( 'switch_to_blog' ) ) {
2019-11-15 23:26:29 +01:00
switch_to_blog ( $blog_id );
$switched = true ;
} else {
$blog_id = get_current_blog_id ();
}
$cache_key = " { $blog_id } _ { $post_id } _ { $args [ 'max_words' ] } _ { $args [ 'max_chars' ] } " ;
if ( isset ( self :: $cache [ $cache_key ] ) ) {
2020-10-20 18:05:12 +02:00
if ( $switched ) {
restore_current_blog ();
}
2019-11-15 23:26:29 +01:00
return self :: $cache [ $cache_key ];
}
if ( ! class_exists ( 'Jetpack_Media_Meta_Extractor' ) ) {
2022-12-15 17:41:53 +01:00
require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.media-extractor.php' ;
2019-11-15 23:26:29 +01:00
}
$post = get_post ( $post_id );
$permalink = get_permalink ( $post_id );
$return = array (
'type' => 'standard' ,
'permalink' => $permalink ,
'image' => '' ,
'excerpt' => '' ,
'word_count' => 0 ,
'secure' => array (
2020-09-15 14:30:05 +02:00
'image' => '' ,
2019-11-15 23:26:29 +01:00
),
2020-09-15 14:30:05 +02:00
'count' => array (
2019-11-15 23:26:29 +01:00
'image' => 0 ,
'video' => 0 ,
'word' => 0 ,
'link' => 0 ,
),
);
2020-09-15 14:30:05 +02:00
if ( $post instanceof WP_Post && empty ( $post -> post_password ) ) {
$return [ 'excerpt' ] = self :: get_excerpt ( $post -> post_content , $post -> post_excerpt , $args [ 'max_words' ], $args [ 'max_chars' ], $post );
$return [ 'count' ][ 'word' ] = self :: get_word_count ( $post -> post_content );
2019-11-15 23:26:29 +01:00
$return [ 'count' ][ 'word_remaining' ] = self :: get_word_remaining_count ( $post -> post_content , $return [ 'excerpt' ] );
2020-09-15 14:30:05 +02:00
$return [ 'count' ][ 'link' ] = self :: get_link_count ( $post -> post_content );
2019-11-15 23:26:29 +01:00
}
$extract = Jetpack_Media_Meta_Extractor :: extract ( $blog_id , $post_id , Jetpack_Media_Meta_Extractor :: ALL );
2020-09-15 14:30:05 +02:00
if ( empty ( $extract [ 'has' ] ) ) {
2020-10-20 18:05:12 +02:00
if ( $switched ) {
restore_current_blog ();
}
2023-04-26 17:39:43 +02:00
self :: $cache [ $cache_key ] = $return ;
2019-11-15 23:26:29 +01:00
return $return ;
2020-09-15 14:30:05 +02:00
}
2019-11-15 23:26:29 +01:00
2020-09-15 14:30:05 +02:00
// Prioritize [some] video embeds.
if ( ! empty ( $extract [ 'has' ][ 'shortcode' ] ) ) {
2019-11-15 23:26:29 +01:00
foreach ( $extract [ 'shortcode' ] as $type => $data ) {
switch ( $type ) {
case 'videopress' :
case 'wpvideo' :
2020-09-15 14:30:05 +02:00
if ( 0 === $return [ 'count' ][ 'video' ] ) {
// If there is no id on the video, then let's just skip this.
if ( ! isset ( $data [ 'id' ][ 0 ] ) ) {
2019-11-15 23:26:29 +01:00
break ;
}
2020-09-15 14:30:05 +02:00
$guid = $data [ 'id' ][ 0 ];
2019-11-15 23:26:29 +01:00
$video_info = videopress_get_video_details ( $guid );
// Only add the video tags if the guid returns a valid videopress object.
if ( $video_info instanceof stdClass ) {
// Continue early if we can't find a Video slug.
if ( empty ( $video_info -> files -> std -> mp4 ) ) {
break ;
}
$url = sprintf (
'https://videos.files.wordpress.com/%1$s/%2$s' ,
$guid ,
$video_info -> files -> std -> mp4
);
$thumbnail = $video_info -> poster ;
if ( ! empty ( $thumbnail ) ) {
2020-09-15 14:30:05 +02:00
$return [ 'image' ] = $thumbnail ;
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'image' ] = $thumbnail ;
}
2020-09-15 14:30:05 +02:00
$return [ 'type' ] = 'video' ;
$return [ 'video' ] = esc_url_raw ( $url );
$return [ 'video_type' ] = 'video/mp4' ;
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'video' ] = $return [ 'video' ];
}
}
2023-04-26 17:39:43 +02:00
++ $return [ 'count' ][ 'video' ];
2019-11-15 23:26:29 +01:00
break ;
case 'youtube' :
2020-09-15 14:30:05 +02:00
if ( 0 === $return [ 'count' ][ 'video' ] ) {
2024-04-17 11:32:24 +02:00
if ( ! isset ( $extract [ 'shortcode' ][ 'youtube' ][ 'id' ][ 0 ] ) ) {
break ;
}
2020-09-15 14:30:05 +02:00
$return [ 'type' ] = 'video' ;
$return [ 'video' ] = esc_url_raw ( 'http://www.youtube.com/watch?feature=player_embedded&v=' . $extract [ 'shortcode' ][ 'youtube' ][ 'id' ][ 0 ] );
$return [ 'image' ] = self :: get_video_poster ( 'youtube' , $extract [ 'shortcode' ][ 'youtube' ][ 'id' ][ 0 ] );
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'video' ] = self :: https ( $return [ 'video' ] );
$return [ 'secure' ][ 'image' ] = self :: https ( $return [ 'image' ] );
}
2023-04-26 17:39:43 +02:00
++ $return [ 'count' ][ 'video' ];
2019-11-15 23:26:29 +01:00
break ;
case 'vimeo' :
2020-09-15 14:30:05 +02:00
if ( 0 === $return [ 'count' ][ 'video' ] ) {
2025-07-27 19:58:08 +02:00
if ( ! isset ( $extract [ 'shortcode' ][ 'vimeo' ][ 'id' ][ 0 ] ) ) {
break ;
}
2020-09-15 14:30:05 +02:00
$return [ 'type' ] = 'video' ;
$return [ 'video' ] = esc_url_raw ( 'http://vimeo.com/' . $extract [ 'shortcode' ][ 'vimeo' ][ 'id' ][ 0 ] );
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'video' ] = self :: https ( $return [ 'video' ] );
$poster_image = get_post_meta ( $post_id , 'vimeo_poster_image' , true );
2020-09-15 14:30:05 +02:00
if ( ! empty ( $poster_image ) ) {
$return [ 'image' ] = $poster_image ;
$poster_url_parts = wp_parse_url ( $poster_image );
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'image' ] = 'https://secure-a.vimeocdn.com' . $poster_url_parts [ 'path' ];
}
}
2023-04-26 17:39:43 +02:00
++ $return [ 'count' ][ 'video' ];
2019-11-15 23:26:29 +01:00
break ;
}
}
}
2020-09-15 14:30:05 +02:00
if ( ! empty ( $extract [ 'has' ][ 'embed' ] ) ) {
foreach ( $extract [ 'embed' ][ 'url' ] as $embed ) {
2019-11-15 23:26:29 +01:00
if ( preg_match ( '/((youtube|vimeo|dailymotion)\.com|youtu.be)/' , $embed ) ) {
2020-09-15 14:30:05 +02:00
if ( 0 === $return [ 'count' ][ 'video' ] ) {
$return [ 'type' ] = 'video' ;
$return [ 'video' ] = 'http://' . $embed ;
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'video' ] = self :: https ( $return [ 'video' ] );
2023-12-07 09:44:11 +01:00
if ( str_contains ( $embed , 'youtube' ) ) {
2020-09-15 14:30:05 +02:00
$return [ 'image' ] = self :: get_video_poster ( 'youtube' , jetpack_get_youtube_id ( $return [ 'video' ] ) );
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'image' ] = self :: https ( $return [ 'image' ] );
2023-12-07 09:44:11 +01:00
} elseif ( str_contains ( $embed , 'youtu.be' ) ) {
2020-09-15 14:30:05 +02:00
$youtube_id = jetpack_get_youtube_id ( $return [ 'video' ] );
$return [ 'video' ] = 'http://youtube.com/watch?v=' . $youtube_id . '&feature=youtu.be' ;
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'video' ] = self :: https ( $return [ 'video' ] );
2020-09-15 14:30:05 +02:00
$return [ 'image' ] = self :: get_video_poster ( 'youtube' , jetpack_get_youtube_id ( $return [ 'video' ] ) );
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'image' ] = self :: https ( $return [ 'image' ] );
2023-12-07 09:44:11 +01:00
} elseif ( str_contains ( $embed , 'vimeo' ) ) {
2019-11-15 23:26:29 +01:00
$poster_image = get_post_meta ( $post_id , 'vimeo_poster_image' , true );
2020-09-15 14:30:05 +02:00
if ( ! empty ( $poster_image ) ) {
$return [ 'image' ] = $poster_image ;
$poster_url_parts = wp_parse_url ( $poster_image );
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'image' ] = 'https://secure-a.vimeocdn.com' . $poster_url_parts [ 'path' ];
}
2023-12-07 09:44:11 +01:00
} elseif ( str_contains ( $embed , 'dailymotion' ) ) {
2020-09-15 14:30:05 +02:00
$return [ 'image' ] = str_replace ( 'dailymotion.com/video/' , 'dailymotion.com/thumbnail/video/' , $embed );
$return [ 'image' ] = wp_parse_url ( $return [ 'image' ], PHP_URL_SCHEME ) === null ? 'http://' . $return [ 'image' ] : $return [ 'image' ];
2019-11-15 23:26:29 +01:00
$return [ 'secure' ][ 'image' ] = self :: https ( $return [ 'image' ] );
}
}
2023-04-26 17:39:43 +02:00
++ $return [ 'count' ][ 'video' ];
2019-11-15 23:26:29 +01:00
}
}
}
// Do we really want to make the video the primary focus of the post?
2020-09-15 14:30:05 +02:00
if ( 'video' === $return [ 'type' ] ) {
$content = wpautop ( wp_strip_all_tags ( $post -> post_content ) );
$paragraphs = explode ( '</p>' , $content );
2019-11-15 23:26:29 +01:00
$number_of_paragraphs = 0 ;
foreach ( $paragraphs as $i => $paragraph ) {
2020-09-15 14:30:05 +02:00
// Don't include blank lines as a paragraph.
if ( '' === trim ( $paragraph ) ) {
unset ( $paragraphs [ $i ] );
2019-11-15 23:26:29 +01:00
continue ;
}
2023-04-26 17:39:43 +02:00
++ $number_of_paragraphs ;
2019-11-15 23:26:29 +01:00
}
2020-09-15 14:30:05 +02:00
$number_of_paragraphs = $number_of_paragraphs - $return [ 'count' ][ 'video' ]; // subtract amount for videos.
2019-11-15 23:26:29 +01:00
2020-09-15 14:30:05 +02:00
// More than 2 paragraph? The video is not the primary focus so we can do some more analysis.
if ( $number_of_paragraphs > 2 ) {
2019-11-15 23:26:29 +01:00
$return [ 'type' ] = 'standard' ;
2020-09-15 14:30:05 +02:00
}
2019-11-15 23:26:29 +01:00
}
// If we don't have any prioritized embed...
2020-09-15 14:30:05 +02:00
if ( 'standard' === $return [ 'type' ] ) {
2019-11-15 23:26:29 +01:00
if ( ( ! empty ( $extract [ 'has' ][ 'gallery' ] ) || ! empty ( $extract [ 'shortcode' ][ 'gallery' ][ 'count' ] ) ) && ! empty ( $extract [ 'image' ] ) ) {
2020-09-15 14:30:05 +02:00
// ... Then we prioritize galleries first (multiple images returned)
2019-11-15 23:26:29 +01:00
$return [ 'type' ] = 'gallery' ;
$return [ 'images' ] = $extract [ 'image' ];
foreach ( $return [ 'images' ] as $image ) {
$return [ 'secure' ][ 'images' ][] = array ( 'url' => self :: ssl_img ( $image [ 'url' ] ) );
2023-04-26 17:39:43 +02:00
++ $return [ 'count' ][ 'image' ];
2019-11-15 23:26:29 +01:00
}
2020-09-15 14:30:05 +02:00
} elseif ( ! empty ( $extract [ 'has' ][ 'image' ] ) ) {
// ... Or we try and select a single image that would make sense.
$content = wpautop ( wp_strip_all_tags ( $post -> post_content ) );
$paragraphs = explode ( '</p>' , $content );
2019-11-15 23:26:29 +01:00
$number_of_paragraphs = 0 ;
foreach ( $paragraphs as $i => $paragraph ) {
2020-09-15 14:30:05 +02:00
// Don't include 'actual' captions as a paragraph.
2023-12-07 09:44:11 +01:00
if ( str_contains ( $paragraph , '[caption' ) ) {
2020-09-15 14:30:05 +02:00
unset ( $paragraphs [ $i ] );
2019-11-15 23:26:29 +01:00
continue ;
}
2020-09-15 14:30:05 +02:00
// Don't include blank lines as a paragraph.
if ( '' === trim ( $paragraph ) ) {
unset ( $paragraphs [ $i ] );
2019-11-15 23:26:29 +01:00
continue ;
}
2023-04-26 17:39:43 +02:00
++ $number_of_paragraphs ;
2019-11-15 23:26:29 +01:00
}
2025-07-27 19:58:08 +02:00
if ( isset ( $extract [ 'image' ][ 0 ][ 'url' ] ) ) {
$return [ 'image' ] = $extract [ 'image' ][ 0 ][ 'url' ];
$return [ 'secure' ][ 'image' ] = self :: ssl_img ( $return [ 'image' ] );
++ $return [ 'count' ][ 'image' ];
}
2019-11-15 23:26:29 +01:00
2025-07-27 19:58:08 +02:00
if ( $number_of_paragraphs <= 2 && is_countable ( $extract [ 'image' ] ) && 1 === count ( $extract [ 'image' ] ) ) { // @phan-suppress-current-line PhanTypePossiblyInvalidDimOffset -- We established the image offset exists with '! empty( $extract['has']['image']' earlier.
2020-09-15 14:30:05 +02:00
// If we have lots of text or images, let's not treat it as an image post, but return its first image.
$return [ 'type' ] = 'image' ;
2019-11-15 23:26:29 +01:00
}
}
}
if ( $switched ) {
restore_current_blog ();
}
/**
* Allow a theme or plugin to inspect and ultimately change the media summary .
*
* @ since 4.4 . 0
*
* @ param array $data The calculated media summary data .
* @ param int $post_id The id of the post this data applies to .
*/
$return = apply_filters ( 'jetpack_media_summary_output' , $return , $post_id );
self :: $cache [ $cache_key ] = $return ;
return $return ;
}
2020-09-15 14:30:05 +02:00
/**
* Converts http to https ://
*
* @ param string $str URL .
*
* @ return string URL .
*/
public static function https ( $str ) {
2019-11-15 23:26:29 +01:00
return str_replace ( 'http://' , 'https://' , $str );
}
2020-09-15 14:30:05 +02:00
/**
* Returns a Photonized version of the URL .
*
* @ param string $url URL .
*
* @ return string URL .
*/
public static function ssl_img ( $url ) {
2023-12-07 09:44:11 +01:00
if ( str_contains ( $url , 'files.wordpress.com' ) ) {
2019-11-15 23:26:29 +01:00
return self :: https ( $url );
} else {
2023-09-26 10:24:36 +02:00
return self :: https ( Image_CDN_Core :: cdn_url ( $url ) );
2019-11-15 23:26:29 +01:00
}
}
2020-09-15 14:30:05 +02:00
/**
* Get the video poster .
*
* @ param string $type Video service .
* @ param string $id Video ID for the service .
*
* @ return string URL of image thumbnail for the video .
*/
public static function get_video_poster ( $type , $id ) {
if ( 'videopress' === $type ) {
2019-11-15 23:26:29 +01:00
if ( function_exists ( 'video_get_highest_resolution_image_url' ) ) {
return video_get_highest_resolution_image_url ( $id );
2020-09-15 14:30:05 +02:00
} elseif ( class_exists ( 'VideoPress_Video' ) ) {
2019-11-15 23:26:29 +01:00
$video = new VideoPress_Video ( $id );
return $video -> poster_frame_uri ;
}
2020-09-15 14:30:05 +02:00
} elseif ( 'youtube' === $type ) {
return 'http://img.youtube.com/vi/' . $id . '/0.jpg' ;
2019-11-15 23:26:29 +01:00
}
}
2020-09-15 14:30:05 +02:00
/**
* Clean text of shortcodes and tags .
*
* @ param string $text Dirty text .
*
* @ return string Clean text .
*/
public static function clean_text ( $text ) {
2019-11-15 23:26:29 +01:00
return trim (
preg_replace (
'/[\s]+/' ,
' ' ,
preg_replace (
'@https?://[\S]+@' ,
'' ,
strip_shortcodes (
2020-09-15 14:30:05 +02:00
wp_strip_all_tags (
2019-11-15 23:26:29 +01:00
$text
)
)
)
)
);
}
/**
* Retrieve an excerpt for the post summary .
*
* This function works around a suspected problem with Core . If resolved , this function should be simplified .
2020-09-15 14:30:05 +02:00
*
2019-11-15 23:26:29 +01:00
* @ link https :// github . com / Automattic / jetpack / pull / 8510
* @ link https :// core . trac . wordpress . org / ticket / 42814
*
* @ param string $post_content The post ' s content .
* @ param string $post_excerpt The post ' s excerpt . Empty if none was explicitly set .
* @ param int $max_words Maximum number of words for the excerpt . Used on wp . com . Default 16.
* @ param int $max_chars Maximum characters in the excerpt . Used on wp . com . Default 256.
* @ param WP_Post $requested_post The post object .
* @ return string Post excerpt .
**/
2020-09-15 14:30:05 +02:00
public static function get_excerpt ( $post_content , $post_excerpt , $max_words = 16 , $max_chars = 256 , $requested_post = null ) {
2019-11-15 23:26:29 +01:00
global $post ;
$original_post = $post ; // Saving the global for later use.
2022-04-02 10:26:41 +02:00
if ( empty ( $post_excerpt ) && function_exists ( 'wpcom_enhanced_excerpt_extract_excerpt' ) ) {
2020-09-15 14:30:05 +02:00
return self :: clean_text (
wpcom_enhanced_excerpt_extract_excerpt (
array (
'text' => $post_content ,
'excerpt_only' => true ,
'show_read_more' => false ,
'max_words' => $max_words ,
'max_chars' => $max_chars ,
'read_more_threshold' => 25 ,
)
)
);
2019-11-15 23:26:29 +01:00
} elseif ( $requested_post instanceof WP_Post ) {
2020-09-15 14:30:05 +02:00
// @todo Refactor to not need to override the global.
// phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited
2019-11-15 23:26:29 +01:00
$post = $requested_post ; // setup_postdata does not set the global.
setup_postdata ( $post );
/** This filter is documented in core/src/wp-includes/post-template.php */
$post_excerpt = apply_filters ( 'get_the_excerpt' , $post_excerpt , $post );
2020-09-15 14:30:05 +02:00
// phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited
$post = $original_post ; // wp_reset_postdata uses the $post global.
2019-11-15 23:26:29 +01:00
wp_reset_postdata ();
return self :: clean_text ( $post_excerpt );
}
return '' ;
}
/**
* Split a string into an array of words .
*
* @ param string $text Post content or excerpt .
2020-09-15 14:30:05 +02:00
*
* @ return array Array of words .
2019-11-15 23:26:29 +01:00
*/
2020-09-15 14:30:05 +02:00
public static function split_content_in_words ( $text ) {
2022-04-02 10:26:41 +02:00
$words = preg_split ( '/[\s!?;,.]+/' , $text , - 1 , PREG_SPLIT_NO_EMPTY );
2019-11-15 23:26:29 +01:00
// Return an empty array if the split above fails.
return $words ? $words : array ();
}
2020-09-15 14:30:05 +02:00
/**
* Get the word count .
*
* @ param string $post_content Post content .
*
* @ return int Word count .
*/
public static function get_word_count ( $post_content ) {
2019-11-15 23:26:29 +01:00
return ( int ) count ( self :: split_content_in_words ( self :: clean_text ( $post_content ) ) );
}
2020-09-15 14:30:05 +02:00
/**
* Get remainder word count ( after the excerpt ) .
*
* @ param string $post_content Post content .
* @ param string $excerpt_content Excerpt content .
*
* @ return int Number of words after the excerpt .
*/
public static function get_word_remaining_count ( $post_content , $excerpt_content ) {
2019-11-15 23:26:29 +01:00
$content_word_count = count ( self :: split_content_in_words ( self :: clean_text ( $post_content ) ) );
$excerpt_word_count = count ( self :: split_content_in_words ( self :: clean_text ( $excerpt_content ) ) );
return ( int ) $content_word_count - $excerpt_word_count ;
}
2020-09-15 14:30:05 +02:00
/**
* Counts the number of links in a post .
*
* @ param string $post_content Post content .
*
* @ return false | int Number of links .
*/
public static function get_link_count ( $post_content ) {
2019-11-15 23:26:29 +01:00
return preg_match_all ( '/\<a[\> ]/' , $post_content , $matches );
}
}