PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 11.5
Jetpack – WP Security, Backup, Speed, & Growth v11.5
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / class.jetpack-post-images.php

class.jetpack-post-images.php in Jetpack – WP Security, Backup, Speed, & Growth 11.5, at class.jetpack-post-images.php

960 lines 29.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Useful for finding an image to display alongside/in representation of a specific post.
4 *
5 * @package automattic/jetpack
6 */
7
8 /**
9 * Useful for finding an image to display alongside/in representation of a specific post.
10 *
11 * Includes a few different methods, all of which return a similar-format array containing
12 * details of any images found. Everything can (should) be called statically, it's just a
13 * function-bucket. You can also call Jetpack_PostImages::get_image() to cycle through all of the methods until
14 * one of them finds something useful.
15 *
16 * This file is included verbatim in Jetpack
17 */
18 class Jetpack_PostImages {
19 /**
20 * If a slideshow is embedded within a post, then parse out the images involved and return them
21 *
22 * @param int $post_id Post ID.
23 * @param int $width Image width.
24 * @param int $height Image height.
25 * @return array Images.
26 */
27 public static function from_slideshow( $post_id, $width = 200, $height = 200 ) {
28 $images = array();
29
30 $post = get_post( $post_id );
31
32 if ( ! $post ) {
33 return $images;
34 }
35
36 if ( ! empty( $post->post_password ) ) {
37 return $images;
38 }
39
40 if ( false === has_shortcode( $post->post_content, 'slideshow' ) ) {
41 return $images; // no slideshow - bail.
42 }
43
44 $permalink = get_permalink( $post->ID );
45
46 // Mechanic: Somebody set us up the bomb.
47 $old_post = $GLOBALS['post'];
48 $GLOBALS['post'] = $post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
49 $old_shortcodes = $GLOBALS['shortcode_tags'];
50 $GLOBALS['shortcode_tags'] = array( 'slideshow' => $old_shortcodes['slideshow'] ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
51
52 // Find all the slideshows.
53 preg_match_all( '/' . get_shortcode_regex() . '/sx', $post->post_content, $slideshow_matches, PREG_SET_ORDER );
54
55 ob_start(); // The slideshow shortcode handler calls wp_print_scripts and wp_print_styles... not too happy about that.
56
57 foreach ( $slideshow_matches as $slideshow_match ) {
58 $slideshow = do_shortcode_tag( $slideshow_match );
59 $pos = stripos( $slideshow, 'jetpack-slideshow' );
60 if ( false === $pos ) { // must be something wrong - or we changed the output format in which case none of the following will work.
61 continue;
62 }
63 $start = strpos( $slideshow, '[', $pos );
64 $end = strpos( $slideshow, ']', $start );
65 $post_images = json_decode( wp_specialchars_decode( str_replace( "'", '"', substr( $slideshow, $start, $end - $start + 1 ) ), ENT_QUOTES ) ); // parse via JSON
66 // If the JSON didn't decode don't try and act on it.
67 if ( is_array( $post_images ) ) {
68 foreach ( $post_images as $post_image ) {
69 $post_image_id = absint( $post_image->id );
70 if ( ! $post_image_id ) {
71 continue;
72 }
73
74 $meta = wp_get_attachment_metadata( $post_image_id );
75
76 // Must be larger than 200x200 (or user-specified).
77 if ( ! isset( $meta['width'] ) || $meta['width'] < $width ) {
78 continue;
79 }
80 if ( ! isset( $meta['height'] ) || $meta['height'] < $height ) {
81 continue;
82 }
83
84 $url = wp_get_attachment_url( $post_image_id );
85
86 $images[] = array(
87 'type' => 'image',
88 'from' => 'slideshow',
89 'src' => $url,
90 'src_width' => $meta['width'],
91 'src_height' => $meta['height'],
92 'href' => $permalink,
93 );
94 }
95 }
96 }
97 ob_end_clean();
98
99 // Operator: Main screen turn on.
100 $GLOBALS['shortcode_tags'] = $old_shortcodes; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
101 $GLOBALS['post'] = $old_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
102
103 return $images;
104 }
105
106 /**
107 * If a gallery is detected, then get all the images from it.
108 *
109 * @param int $post_id Post ID.
110 * @return array Images.
111 */
112 public static function from_gallery( $post_id ) {
113 $images = array();
114
115 $post = get_post( $post_id );
116
117 if ( ! $post ) {
118 return $images;
119 }
120
121 if ( ! empty( $post->post_password ) ) {
122 return $images;
123 }
124
125 $permalink = get_permalink( $post->ID );
126
127 /**
128 * Juggle global post object because the gallery shortcode uses the
129 * global object.
130 *
131 * See core ticket:
132 * https://core.trac.wordpress.org/ticket/39304
133 */
134 // phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
135 if ( isset( $GLOBALS['post'] ) ) {
136 $juggle_post = $GLOBALS['post'];
137 $GLOBALS['post'] = $post;
138 $galleries = get_post_galleries( $post->ID, false );
139 $GLOBALS['post'] = $juggle_post;
140 } else {
141 $GLOBALS['post'] = $post;
142 $galleries = get_post_galleries( $post->ID, false );
143 unset( $GLOBALS['post'] );
144 }
145 // phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited
146
147 foreach ( $galleries as $gallery ) {
148 if ( isset( $gallery['type'] ) && 'slideshow' === $gallery['type'] && ! empty( $gallery['ids'] ) ) {
149 $image_ids = explode( ',', $gallery['ids'] );
150 $image_size = isset( $gallery['size'] ) ? $gallery['size'] : 'thumbnail';
151 foreach ( $image_ids as $image_id ) {
152 $image = wp_get_attachment_image_src( $image_id, $image_size );
153 if ( ! empty( $image[0] ) ) {
154 list( $raw_src ) = explode( '?', $image[0] ); // pull off any Query string (?w=250).
155 $raw_src = wp_specialchars_decode( $raw_src ); // rawify it.
156 $raw_src = esc_url_raw( $raw_src ); // clean it.
157 $images[] = array(
158 'type' => 'image',
159 'from' => 'gallery',
160 'src' => $raw_src,
161 'href' => $permalink,
162 );
163 }
164 }
165 } elseif ( ! empty( $gallery['src'] ) ) {
166 foreach ( $gallery['src'] as $src ) {
167 list( $raw_src ) = explode( '?', $src ); // pull off any Query string (?w=250).
168 $raw_src = wp_specialchars_decode( $raw_src ); // rawify it.
169 $raw_src = esc_url_raw( $raw_src ); // clean it.
170 $images[] = array(
171 'type' => 'image',
172 'from' => 'gallery',
173 'src' => $raw_src,
174 'href' => $permalink,
175 );
176 }
177 }
178 }
179
180 return $images;
181 }
182
183 /**
184 * Get attachment images for a specified post and return them. Also make sure
185 * their dimensions are at or above a required minimum.
186 *
187 * @param int $post_id The post ID to check.
188 * @param int $width Image width.
189 * @param int $height Image height.
190 * @return array Containing details of the image, or empty array if none.
191 */
192 public static function from_attachment( $post_id, $width = 200, $height = 200 ) {
193 $images = array();
194
195 $post = get_post( $post_id );
196
197 if ( ! empty( $post->post_password ) ) {
198 return $images;
199 }
200
201 $post_images = get_posts(
202 array(
203 'post_parent' => $post_id, // Must be children of post.
204 'numberposts' => 5, // No more than 5.
205 'post_type' => 'attachment', // Must be attachments.
206 'post_mime_type' => 'image', // Must be images.
207 'suppress_filters' => false,
208 )
209 );
210
211 if ( ! $post_images ) {
212 return $images;
213 }
214
215 $permalink = get_permalink( $post_id );
216
217 foreach ( $post_images as $post_image ) {
218 $current_image = self::get_attachment_data( $post_image->ID, $permalink, $width, $height );
219 if ( false !== $current_image ) {
220 $images[] = $current_image;
221 }
222 }
223
224 /*
225 * We only want to pass back attached images that were actually inserted.
226 * We can load up all the images found in the HTML source and then
227 * compare URLs to see if an image is attached AND inserted.
228 */
229 $html_images = self::from_html( $post_id );
230 $inserted_images = array();
231
232 foreach ( $html_images as $html_image ) {
233 $src = wp_parse_url( $html_image['src'] );
234 // strip off any query strings from src.
235 if ( ! empty( $src['scheme'] ) && ! empty( $src['host'] ) ) {
236 $inserted_images[] = $src['scheme'] . '://' . $src['host'] . $src['path'];
237 } elseif ( ! empty( $src['host'] ) ) {
238 $inserted_images[] = set_url_scheme( 'http://' . $src['host'] . $src['path'] );
239 } else {
240 $inserted_images[] = site_url( '/' ) . $src['path'];
241 }
242 }
243 foreach ( $images as $i => $image ) {
244 if ( ! in_array( $image['src'], $inserted_images, true ) ) {
245 unset( $images[ $i ] );
246 }
247 }
248
249 return $images;
250 }
251
252 /**
253 * Check if a Featured Image is set for this post, and return it in a similar
254 * format to the other images?_from_*() methods.
255 *
256 * @param int $post_id The post ID to check.
257 * @param int $width Image width.
258 * @param int $height Image height.
259 * @return array containing details of the Featured Image, or empty array if none.
260 */
261 public static function from_thumbnail( $post_id, $width = 200, $height = 200 ) {
262 $images = array();
263
264 $post = get_post( $post_id );
265
266 if ( ! empty( $post->post_password ) ) {
267 return $images;
268 }
269
270 if ( 'attachment' === get_post_type( $post ) && wp_attachment_is_image( $post ) ) {
271 $thumb = $post_id;
272 } else {
273 $thumb = get_post_thumbnail_id( $post );
274 }
275
276 if ( $thumb ) {
277 $meta = wp_get_attachment_metadata( $thumb );
278 // Must be larger than requested minimums.
279 if ( ! isset( $meta['width'] ) || $meta['width'] < $width ) {
280 return $images;
281 }
282 if ( ! isset( $meta['height'] ) || $meta['height'] < $height ) {
283 return $images;
284 }
285
286 $too_big = ( ( ! empty( $meta['width'] ) && $meta['width'] > 1200 ) || ( ! empty( $meta['height'] ) && $meta['height'] > 1200 ) );
287
288 if (
289 $too_big &&
290 (
291 ( method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'photon' ) ) ||
292 ( defined( 'IS_WPCOM' ) && IS_WPCOM )
293 )
294 ) {
295 $img_src = wp_get_attachment_image_src( $thumb, array( 1200, 1200 ) );
296 } else {
297 $img_src = wp_get_attachment_image_src( $thumb, 'full' );
298 }
299 if ( ! is_array( $img_src ) ) {
300 // If wp_get_attachment_image_src returns false but we know that there should be an image that could be used.
301 // we try a bit harder and user the data that we have.
302 $thumb_post_data = get_post( $thumb );
303 $img_src = array( $thumb_post_data->guid, $meta['width'], $meta['height'] );
304 }
305
306 // Let's try to use the postmeta if we can, since it seems to be
307 // more reliable
308 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
309 $featured_image = get_post_meta( $post->ID, '_jetpack_featured_image' );
310 if ( $featured_image ) {
311 $url = $featured_image[0];
312 } else {
313 $url = $img_src[0];
314 }
315 } else {
316 $url = $img_src[0];
317 }
318 $images = array(
319 array( // Other methods below all return an array of arrays.
320 'type' => 'image',
321 'from' => 'thumbnail',
322 'src' => $url,
323 'src_width' => $img_src[1],
324 'src_height' => $img_src[2],
325 'href' => get_permalink( $thumb ),
326 'alt_text' => self::get_alt_text( $thumb ),
327 ),
328 );
329
330 }
331
332 if ( empty( $images ) && ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
333 $meta_thumbnail = get_post_meta( $post_id, '_jetpack_post_thumbnail', true );
334 if ( ! empty( $meta_thumbnail ) ) {
335 if ( ! isset( $meta_thumbnail['width'] ) || $meta_thumbnail['width'] < $width ) {
336 return $images;
337 }
338
339 if ( ! isset( $meta_thumbnail['height'] ) || $meta_thumbnail['height'] < $height ) {
340 return $images;
341 }
342
343 $images = array(
344 array( // Other methods below all return an array of arrays.
345 'type' => 'image',
346 'from' => 'thumbnail',
347 'src' => $meta_thumbnail['URL'],
348 'src_width' => $meta_thumbnail['width'],
349 'src_height' => $meta_thumbnail['height'],
350 'href' => $meta_thumbnail['URL'],
351 'alt_text' => self::get_alt_text( $thumb ),
352 ),
353 );
354 }
355 }
356
357 return $images;
358 }
359
360 /**
361 * Get images from Gutenberg Image blocks.
362 *
363 * @since 6.9.0
364 *
365 * @param mixed $html_or_id The HTML string to parse for images, or a post id.
366 * @param int $width Minimum Image width.
367 * @param int $height Minimum Image height.
368 */
369 public static function from_blocks( $html_or_id, $width = 200, $height = 200 ) {
370 $images = array();
371
372 $html_info = self::get_post_html( $html_or_id );
373
374 if ( empty( $html_info['html'] ) ) {
375 return $images;
376 }
377
378 // Look for block information in the HTML.
379 $blocks = parse_blocks( $html_info['html'] );
380 if ( empty( $blocks ) ) {
381 return $images;
382 }
383
384 /*
385 * Let's loop through our blocks.
386 * Some blocks may include some other blocks. Let's go 2 levels deep to look for blocks
387 * that we support and that may include images (see get_images_from_block)
388 *
389 * @to-do: instead of looping manually (that's a lot of if and loops), search recursively instead.
390 */
391 foreach ( $blocks as $block ) {
392 if ( ! self::is_nested_block( $block ) || 'core/media-text' === $block['blockName'] ) {
393 $images = self::get_images_from_block( $images, $block, $html_info, $width, $height );
394 } else {
395 foreach ( $block['innerBlocks'] as $inner_block ) {
396 if ( ! self::is_nested_block( $inner_block ) ) {
397 $images = self::get_images_from_block( $images, $inner_block, $html_info, $width, $height );
398 } else {
399 foreach ( $inner_block['innerBlocks'] as $inner_inner_block ) {
400 $images = self::get_images_from_block( $images, $inner_inner_block, $html_info, $width, $height );
401 }
402 }
403 }
404 }
405 }
406
407 /**
408 * Returning a filtered array because get_attachment_data returns false
409 * for unsuccessful attempts.
410 */
411 return array_filter( $images );
412 }
413
414 /**
415 * Very raw -- just parse the HTML and pull out any/all img tags and return their src
416 *
417 * @param mixed $html_or_id The HTML string to parse for images, or a post id.
418 * @param int $width Minimum Image width.
419 * @param int $height Minimum Image height.
420 *
421 * @uses DOMDocument
422 *
423 * @return array containing images
424 */
425 public static function from_html( $html_or_id, $width = 200, $height = 200 ) {
426 $images = array();
427
428 $html_info = self::get_post_html( $html_or_id );
429
430 if ( empty( $html_info['html'] ) ) {
431 return $images;
432 }
433
434 // Do not go any further if DOMDocument is disabled on the server.
435 if ( ! class_exists( 'DOMDocument' ) ) {
436 return $images;
437 }
438
439 // Let's grab all image tags from the HTML.
440 $dom_doc = new DOMDocument();
441
442 // The @ is not enough to suppress errors when dealing with libxml,
443 // we have to tell it directly how we want to handle errors.
444 libxml_use_internal_errors( true );
445 @$dom_doc->loadHTML( $html_info['html'] ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
446 libxml_use_internal_errors( false );
447
448 $image_tags = $dom_doc->getElementsByTagName( 'img' );
449
450 // For each image Tag, make sure it can be added to the $images array, and add it.
451 foreach ( $image_tags as $image_tag ) {
452 $img_src = $image_tag->getAttribute( 'src' );
453
454 if ( empty( $img_src ) ) {
455 continue;
456 }
457
458 // Do not grab smiley images that were automatically created by WP when entering text smilies.
459 if ( stripos( $img_src, '/smilies/' ) ) {
460 continue;
461 }
462
463 $meta = array(
464 'width' => (int) $image_tag->getAttribute( 'width' ),
465 'height' => (int) $image_tag->getAttribute( 'height' ),
466 'alt_text' => $image_tag->getAttribute( 'alt' ),
467 );
468
469 /**
470 * Filters the switch to ignore minimum image size requirements. Can be used
471 * to add custom logic to image dimensions, like only enforcing one of the dimensions,
472 * or disabling it entirely.
473 *
474 * @since 6.4.0
475 *
476 * @param bool $ignore Should the image dimensions be ignored?
477 * @param array $meta Array containing image dimensions parsed from the markup.
478 */
479 $ignore_dimensions = apply_filters( 'jetpack_postimages_ignore_minimum_dimensions', false, $meta );
480
481 // Must be larger than 200x200 (or user-specified).
482 if (
483 ! $ignore_dimensions
484 && (
485 empty( $meta['width'] )
486 || empty( $meta['height'] )
487 || $meta['width'] < $width
488 || $meta['height'] < $height
489 )
490 ) {
491 continue;
492 }
493
494 $images[] = array(
495 'type' => 'image',
496 'from' => 'html',
497 'src' => $img_src,
498 'src_width' => $meta['width'],
499 'src_height' => $meta['height'],
500 'href' => $html_info['post_url'],
501 'alt_text' => $meta['alt_text'],
502 );
503 }
504 return $images;
505 }
506
507 /**
508 * Data from blavatar.
509 *
510 * @param int $post_id The post ID to check.
511 * @param int $size Size.
512 * @return array containing details of the image, or empty array if none.
513 */
514 public static function from_blavatar( $post_id, $size = 96 ) {
515
516 $permalink = get_permalink( $post_id );
517
518 if ( function_exists( 'blavatar_domain' ) && function_exists( 'blavatar_exists' ) && function_exists( 'blavatar_url' ) ) {
519 $domain = blavatar_domain( $permalink );
520
521 if ( ! blavatar_exists( $domain ) ) {
522 return array();
523 }
524
525 $url = blavatar_url( $domain, 'img', $size );
526 } else {
527 $url = get_site_icon_url( $size );
528 if ( ! $url ) {
529 return array();
530 }
531 }
532
533 return array(
534 array(
535 'type' => 'image',
536 'from' => 'blavatar',
537 'src' => $url,
538 'src_width' => $size,
539 'src_height' => $size,
540 'href' => $permalink,
541 'alt_text' => '',
542 ),
543 );
544 }
545
546 /**
547 * Gets a post image from the author avatar.
548 *
549 * @param int $post_id The post ID to check.
550 * @param int $size The size of the avatar to get.
551 * @param string $default The default image to use.
552 * @return array containing details of the image, or empty array if none.
553 */
554 public static function from_gravatar( $post_id, $size = 96, $default = false ) {
555 $post = get_post( $post_id );
556 $permalink = get_permalink( $post_id );
557
558 if ( ! $post instanceof WP_Post ) {
559 return array();
560 }
561
562 if ( function_exists( 'wpcom_get_avatar_url' ) ) {
563 $url = wpcom_get_avatar_url( $post->post_author, $size, $default, true );
564 if ( $url && is_array( $url ) ) {
565 $url = $url[0];
566 }
567 } else {
568 $url = get_avatar_url(
569 $post->post_author,
570 array(
571 'size' => $size,
572 'default' => $default,
573 )
574 );
575 }
576
577 return array(
578 array(
579 'type' => 'image',
580 'from' => 'gravatar',
581 'src' => $url,
582 'src_width' => $size,
583 'src_height' => $size,
584 'href' => $permalink,
585 'alt_text' => '',
586 ),
587 );
588 }
589
590 /**
591 * Run through the different methods that we have available to try to find a single good
592 * display image for this post.
593 *
594 * @param int $post_id Post ID.
595 * @param array $args Other arguments (currently width and height required for images where possible to determine).
596 * @return array|null containing details of the best image to be used, or null if no image is found.
597 */
598 public static function get_image( $post_id, $args = array() ) {
599 $image = null;
600
601 /**
602 * Fires before we find a single good image for a specific post.
603 *
604 * @since 2.2.0
605 *
606 * @param int $post_id Post ID.
607 */
608 do_action( 'jetpack_postimages_pre_get_image', $post_id );
609 $media = self::get_images( $post_id, $args );
610
611 if ( is_array( $media ) ) {
612 foreach ( $media as $item ) {
613 if ( 'image' === $item['type'] ) {
614 $image = $item;
615 break;
616 }
617 }
618 }
619
620 /**
621 * Fires after we find a single good image for a specific post.
622 *
623 * @since 2.2.0
624 *
625 * @param int $post_id Post ID.
626 */
627 do_action( 'jetpack_postimages_post_get_image', $post_id );
628
629 return $image;
630 }
631
632 /**
633 * Get an array containing a collection of possible images for this post, stopping once we hit a method
634 * that returns something useful.
635 *
636 * @param int $post_id Post ID.
637 * @param array $args Optional args, see defaults list for details.
638 * @return array containing images that would be good for representing this post
639 */
640 public static function get_images( $post_id, $args = array() ) {
641 // Figure out which image to attach to this post.
642 $media = array();
643
644 /**
645 * Filters the array of images that would be good for a specific post.
646 * This filter is applied before options ($args) filter the original array.
647 *
648 * @since 2.0.0
649 *
650 * @param array $media Array of images that would be good for a specific post.
651 * @param int $post_id Post ID.
652 * @param array $args Array of options to get images.
653 */
654 $media = apply_filters( 'jetpack_images_pre_get_images', $media, $post_id, $args );
655 if ( $media ) {
656 return $media;
657 }
658
659 $defaults = array(
660 'width' => 200, // Required minimum width (if possible to determine).
661 'height' => 200, // Required minimum height (if possible to determine).
662
663 'fallback_to_avatars' => false, // Optionally include Blavatar and Gravatar (in that order) in the image stack.
664 'avatar_size' => 96, // Used for both Grav and Blav.
665 'gravatar_default' => false, // Default image to use if we end up with no Gravatar.
666
667 'from_thumbnail' => true, // Use these flags to specify which methods to use to find an image.
668 'from_slideshow' => true,
669 'from_gallery' => true,
670 'from_attachment' => true,
671 'from_blocks' => true,
672 'from_html' => true,
673
674 'html_content' => '', // HTML string to pass to from_html().
675 );
676 $args = wp_parse_args( $args, $defaults );
677
678 $media = array();
679 if ( $args['from_thumbnail'] ) {
680 $media = self::from_thumbnail( $post_id, $args['width'], $args['height'] );
681 }
682 if ( ! $media && $args['from_slideshow'] ) {
683 $media = self::from_slideshow( $post_id, $args['width'], $args['height'] );
684 }
685 if ( ! $media && $args['from_gallery'] ) {
686 $media = self::from_gallery( $post_id );
687 }
688 if ( ! $media && $args['from_attachment'] ) {
689 $media = self::from_attachment( $post_id, $args['width'], $args['height'] );
690 }
691 if ( ! $media && $args['from_blocks'] ) {
692 if ( empty( $args['html_content'] ) ) {
693 $media = self::from_blocks( $post_id, $args['width'], $args['height'] ); // Use the post_id, which will load the content.
694 } else {
695 $media = self::from_blocks( $args['html_content'], $args['width'], $args['height'] ); // If html_content is provided, use that.
696 }
697 }
698 if ( ! $media && $args['from_html'] ) {
699 if ( empty( $args['html_content'] ) ) {
700 $media = self::from_html( $post_id, $args['width'], $args['height'] ); // Use the post_id, which will load the content.
701 } else {
702 $media = self::from_html( $args['html_content'], $args['width'], $args['height'] ); // If html_content is provided, use that.
703 }
704 }
705
706 if ( ! $media && $args['fallback_to_avatars'] ) {
707 $media = self::from_blavatar( $post_id, $args['avatar_size'] );
708 if ( ! $media ) {
709 $media = self::from_gravatar( $post_id, $args['avatar_size'], $args['gravatar_default'] );
710 }
711 }
712
713 /**
714 * Filters the array of images that would be good for a specific post.
715 * This filter is applied after options ($args) filter the original array.
716 *
717 * @since 2.0.0
718 *
719 * @param array $media Array of images that would be good for a specific post.
720 * @param int $post_id Post ID.
721 * @param array $args Array of options to get images.
722 */
723 return apply_filters( 'jetpack_images_get_images', $media, $post_id, $args );
724 }
725
726 /**
727 * Takes an image URL and pixel dimensions then returns a URL for the
728 * resized and cropped image.
729 *
730 * @param string $src Image URL.
731 * @param int $width Image width.
732 * @param int $height Image height.
733 * @return string Transformed image URL
734 */
735 public static function fit_image_url( $src, $width, $height ) {
736 $width = (int) $width;
737 $height = (int) $height;
738
739 if ( $width < 1 || $height < 1 ) {
740 return $src;
741 }
742
743 // See if we should bypass WordPress.com SaaS resizing.
744 if ( has_filter( 'jetpack_images_fit_image_url_override' ) ) {
745 /**
746 * Filters the image URL used after dimensions are set by Photon.
747 *
748 * @since 3.3.0
749 *
750 * @param string $src Image URL.
751 * @param int $width Image width.
752 * @param int $width Image height.
753 */
754 return apply_filters( 'jetpack_images_fit_image_url_override', $src, $width, $height );
755 }
756
757 // If WPCOM hosted image use native transformations.
758 $img_host = wp_parse_url( $src, PHP_URL_HOST );
759 if ( '.files.wordpress.com' === substr( $img_host, -20 ) ) {
760 return add_query_arg(
761 array(
762 'w' => $width,
763 'h' => $height,
764 'crop' => 1,
765 ),
766 set_url_scheme( $src )
767 );
768 }
769
770 // Use Photon magic.
771 if ( function_exists( 'jetpack_photon_url' ) ) {
772 return jetpack_photon_url( $src, array( 'resize' => "$width,$height" ) );
773 }
774
775 // Arg... no way to resize image using WordPress.com infrastructure!
776 return $src;
777 }
778
779 /**
780 * Get HTML from given post content.
781 *
782 * @since 6.9.0
783 *
784 * @param mixed $html_or_id The HTML string to parse for images, or a post id.
785 *
786 * @return array $html_info {
787 * @type string $html Post content.
788 * @type string $post_url Post URL.
789 * }
790 */
791 public static function get_post_html( $html_or_id ) {
792 if ( is_numeric( $html_or_id ) ) {
793 $post = get_post( $html_or_id );
794
795 if ( empty( $post ) || ! empty( $post->post_password ) ) {
796 return '';
797 }
798
799 $html_info = array(
800 'html' => $post->post_content, // DO NOT apply the_content filters here, it will cause loops.
801 'post_url' => get_permalink( $post->ID ),
802 );
803 } else {
804 $html_info = array(
805 'html' => $html_or_id,
806 'post_url' => '',
807 );
808 }
809 return $html_info;
810 }
811
812 /**
813 * Get info about a WordPress attachment.
814 *
815 * @since 6.9.0
816 *
817 * @param int $attachment_id Attachment ID.
818 * @param string $post_url URL of the post, if we have one.
819 * @param int $width Minimum Image width.
820 * @param int $height Minimum Image height.
821 * @return array|bool Image data or false if unavailable.
822 */
823 public static function get_attachment_data( $attachment_id, $post_url, $width, $height ) {
824 if ( empty( $attachment_id ) ) {
825 return false;
826 }
827
828 $meta = wp_get_attachment_metadata( $attachment_id );
829
830 if ( empty( $meta ) ) {
831 return false;
832 }
833
834 if ( ! empty( $meta['videopress'] ) ) {
835 // Use poster image for VideoPress videos.
836 $url = $meta['videopress']['poster'];
837 $meta_width = $meta['videopress']['width'];
838 $meta_height = $meta['videopress']['height'];
839 } elseif ( ! empty( $meta['thumb'] ) ) {
840 // On WordPress.com, VideoPress videos have a 'thumb' property with the
841 // poster image filename instead.
842 $media_url = wp_get_attachment_url( $attachment_id );
843 $url = str_replace( wp_basename( $media_url ), $meta['thumb'], $media_url );
844 $meta_width = $meta['width'];
845 $meta_height = $meta['height'];
846 } elseif ( wp_attachment_is( 'video', $attachment_id ) ) {
847 // We don't have thumbnail images for non-VideoPress videos - skip them.
848 return false;
849 } else {
850 if ( ! isset( $meta['width'] ) || ! isset( $meta['height'] ) ) {
851 return false;
852 }
853 $url = wp_get_attachment_url( $attachment_id );
854 $meta_width = $meta['width'];
855 $meta_height = $meta['height'];
856 }
857
858 if ( $meta_width < $width || $meta_height < $height ) {
859 return false;
860 }
861
862 return array(
863 'type' => 'image',
864 'from' => 'attachment',
865 'src' => $url,
866 'src_width' => $meta_width,
867 'src_height' => $meta_height,
868 'href' => $post_url,
869 'alt_text' => self::get_alt_text( $attachment_id ),
870 );
871 }
872
873 /**
874 * Get the alt text for an image or other media from the Media Library.
875 *
876 * @since 7.1
877 *
878 * @param int $attachment_id The Post ID of the media.
879 * @return string The alt text value or an empty string.
880 */
881 public static function get_alt_text( $attachment_id ) {
882 return (string) get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
883 }
884
885 /**
886 * Get an image from a block.
887 *
888 * @since 7.8.0
889 *
890 * @param array $images Images found.
891 * @param array $block Block and its attributes.
892 * @param array $html_info Info about the post where the block is found.
893 * @param int $width Desired image width.
894 * @param int $height Desired image height.
895 *
896 * @return array Array of images found.
897 */
898 private static function get_images_from_block( $images, $block, $html_info, $width, $height ) {
899 /**
900 * Parse content from Core Image blocks.
901 * If it is an image block for an image hosted on our site, it will have an ID.
902 * If it does not have an ID, let `from_html` parse that content later,
903 * and extract an image if it has size parameters.
904 */
905 if (
906 'core/image' === $block['blockName']
907 && ! empty( $block['attrs']['id'] )
908 ) {
909 $images[] = self::get_attachment_data( $block['attrs']['id'], $html_info['post_url'], $width, $height );
910 } elseif (
911 'core/media-text' === $block['blockName']
912 && ! empty( $block['attrs']['mediaId'] )
913 ) {
914 $images[] = self::get_attachment_data( $block['attrs']['mediaId'], $html_info['post_url'], $width, $height );
915 } elseif (
916 /**
917 * Parse content from Core Gallery blocks as well from Jetpack's Tiled Gallery and Slideshow blocks.
918 * Gallery blocks include the ID of each one of the images in the gallery.
919 */
920 in_array( $block['blockName'], array( 'core/gallery', 'jetpack/tiled-gallery', 'jetpack/slideshow' ), true )
921 && ! empty( $block['attrs']['ids'] )
922 ) {
923 foreach ( $block['attrs']['ids'] as $img_id ) {
924 $images[] = self::get_attachment_data( $img_id, $html_info['post_url'], $width, $height );
925 }
926 } elseif (
927 /**
928 * Parse content from Jetpack's Story block.
929 */
930 'jetpack/story' === $block['blockName']
931 && ! empty( $block['attrs']['mediaFiles'] )
932 ) {
933 foreach ( $block['attrs']['mediaFiles'] as $media_file ) {
934 if ( ! empty( $media_file['id'] ) ) {
935 $images[] = self::get_attachment_data( $media_file['id'], $html_info['post_url'], $width, $height );
936 }
937 }
938 }
939
940 return $images;
941 }
942
943 /**
944 * Check if a block has inner blocks.
945 *
946 * @since 7.8.0
947 *
948 * @param array $block Block and its attributes.
949 *
950 * @return bool
951 */
952 private static function is_nested_block( $block ) {
953 if ( ! empty( $block['innerBlocks'] ) ) {
954 return true;
955 }
956
957 return false;
958 }
959 }
960