PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 8.2.6
Jetpack – WP Security, Backup, Speed, & Growth v8.2.6
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 14.4.2 All 500 releases
jetpack / class.photon.php
class.photon.php
1,311 lines 46.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Automattic\Jetpack\Assets;
4
5 class Jetpack_Photon {
6 /**
7 * Class variables
8 */
9 // Oh look, a singleton
10 private static $__instance = null;
11
12 // Allowed extensions must match https://code.trac.wordpress.org/browser/photon/index.php#L31
13 protected static $extensions = array(
14 'gif',
15 'jpg',
16 'jpeg',
17 'png',
18 );
19
20 // Don't access this directly. Instead, use self::image_sizes() so it's actually populated with something.
21 protected static $image_sizes = null;
22
23 /**
24 * Singleton implementation
25 *
26 * @return object
27 */
28 public static function instance() {
29 if ( ! is_a( self::$__instance, 'Jetpack_Photon' ) ) {
30 self::$__instance = new Jetpack_Photon();
31 self::$__instance->setup();
32 }
33
34 return self::$__instance;
35 }
36
37 /**
38 * Silence is golden.
39 */
40 private function __construct() {}
41
42 /**
43 * Register actions and filters, but only if basic Photon functions are available.
44 * The basic functions are found in ./functions.photon.php.
45 *
46 * @uses add_action, add_filter
47 * @return null
48 */
49 private function setup() {
50 if ( ! function_exists( 'jetpack_photon_url' ) ) {
51 return;
52 }
53
54 // Images in post content and galleries
55 add_filter( 'the_content', array( __CLASS__, 'filter_the_content' ), 999999 );
56 add_filter( 'get_post_galleries', array( __CLASS__, 'filter_the_galleries' ), 999999 );
57 add_filter( 'widget_media_image_instance', array( __CLASS__, 'filter_the_image_widget' ), 999999 );
58
59 // Core image retrieval
60 add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), 10, 3 );
61 add_filter( 'rest_request_before_callbacks', array( $this, 'should_rest_photon_image_downsize' ), 10, 3 );
62 add_filter( 'rest_request_after_callbacks', array( $this, 'cleanup_rest_photon_image_downsize' ) );
63
64 // Responsive image srcset substitution
65 add_filter( 'wp_calculate_image_srcset', array( $this, 'filter_srcset_array' ), 10, 5 );
66 add_filter( 'wp_calculate_image_sizes', array( $this, 'filter_sizes' ), 1, 2 ); // Early so themes can still easily filter.
67
68 // Helpers for maniuplated images
69 add_action( 'wp_enqueue_scripts', array( $this, 'action_wp_enqueue_scripts' ), 9 );
70
71 /**
72 * Allow Photon to disable uploaded images resizing and use its own resize capabilities instead.
73 *
74 * @module photon
75 *
76 * @since 7.1.0
77 *
78 * @param bool false Should Photon enable noresize mode. Default to false.
79 */
80 if ( apply_filters( 'jetpack_photon_noresize_mode', false ) ) {
81 $this->enable_noresize_mode();
82 }
83 }
84
85 /**
86 * Enables the noresize mode for Photon, allowing to avoid intermediate size files generation.
87 */
88 private function enable_noresize_mode() {
89 jetpack_require_lib( 'class.jetpack-photon-image-sizes' );
90
91 // The main objective of noresize mode is to disable additional resized image versions creation.
92 // This filter handles removal of additional sizes.
93 add_filter( 'intermediate_image_sizes_advanced', array( __CLASS__, 'filter_photon_noresize_intermediate_sizes' ) );
94
95 // Load the noresize srcset solution on priority of 20, allowing other plugins to set sizes earlier.
96 add_filter( 'wp_get_attachment_metadata', array( __CLASS__, 'filter_photon_norezise_maybe_inject_sizes' ), 20, 2 );
97
98 // Photonize thumbnail URLs in the API response.
99 add_filter( 'rest_api_thumbnail_size_urls', array( __CLASS__, 'filter_photon_noresize_thumbnail_urls' ) );
100
101 // This allows to assign the Photon domain to images that normally use the home URL as base.
102 add_filter( 'jetpack_photon_domain', array( __CLASS__, 'filter_photon_norezise_domain' ), 10, 2 );
103
104 add_filter( 'the_content', array( __CLASS__, 'filter_content_add' ), 0 );
105
106 // Jetpack hooks in at six nines (999999) so this filter does at seven.
107 add_filter( 'the_content', array( __CLASS__, 'filter_content_remove' ), 9999999 );
108
109 // Regular Photon operation mode filter doesn't run when is_admin(), so we need an additional filter.
110 // This is temporary until Jetpack allows more easily running these filters for is_admin().
111 if ( is_admin() ) {
112 add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), 5, 3 );
113
114 // Allows any image that gets passed to Photon to be resized via Photon.
115 add_filter( 'jetpack_photon_admin_allow_image_downsize', '__return_true' );
116 }
117 }
118
119 /**
120 * This is our catch-all to strip dimensions from intermediate images in content.
121 * Since this primarily only impacts post_content we do a little dance to add the filter early
122 * to `the_content` and then remove it later on in the same hook.
123 *
124 * @param String $content the post content.
125 * @return String the post content unchanged.
126 */
127 public static function filter_content_add( $content ) {
128 add_filter( 'jetpack_photon_pre_image_url', array( __CLASS__, 'strip_image_dimensions_maybe' ) );
129 return $content;
130 }
131
132 /**
133 * Removing the content filter that was set previously.
134 *
135 * @param String $content the post content.
136 * @return String the post content unchanged.
137 */
138 public static function filter_content_remove( $content ) {
139 remove_filter( 'jetpack_photon_pre_image_url', array( __CLASS__, 'strip_image_dimensions_maybe' ) );
140 return $content;
141 }
142
143 /**
144 * Short circuits the Photon filter to enable Photon processing for any URL.
145 *
146 * @param String $photon_url a proposed Photon URL for the media file.
147 * @param String $image_url the original media URL.
148 * @return String an URL to be used for the media file.
149 */
150 public static function filter_photon_norezise_domain( $photon_url, $image_url ) {
151 return $photon_url;
152 }
153
154 /**
155 * Disables intermediate sizes to disallow resizing.
156 *
157 * @param Array $sizes an array containing image sizes.
158 * @return Boolean
159 */
160 public static function filter_photon_noresize_intermediate_sizes( $sizes ) {
161 return array();
162 }
163
164 public static function filter_photon_noresize_thumbnail_urls( $sizes ) {
165 foreach ( $sizes as $size => $url ) {
166 $parts = explode( '?', $url );
167 $arguments = isset( $parts[1] ) ? $parts[1] : array();
168
169 $sizes[ $size ] = jetpack_photon_url( $url, wp_parse_args( $arguments ) );
170 }
171
172 return $sizes;
173 }
174
175 /**
176 * Inject image sizes to attachment metadata.
177 *
178 * @param array $data Attachment metadata.
179 * @param int $attachment_id Attachment's post ID.
180 *
181 * @return array Attachment metadata.
182 */
183 public static function filter_photon_norezise_maybe_inject_sizes( $data, $attachment_id ) {
184 // Can't do much if data is empty.
185 if ( empty( $data ) ) {
186 return $data;
187 }
188 $sizes_already_exist = (
189 true === is_array( $data )
190 && true === array_key_exists( 'sizes', $data )
191 && true === is_array( $data['sizes'] )
192 && false === empty( $data['sizes'] )
193 );
194 if ( $sizes_already_exist ) {
195 return $data;
196 }
197 // Missing some critical data we need to determine sizes, not processing.
198 if ( ! isset( $data['file'] )
199 || ! isset( $data['width'] )
200 || ! isset( $data['height'] )
201 ) {
202 return $data;
203 }
204
205 $mime_type = get_post_mime_type( $attachment_id );
206 $attachment_is_image = preg_match( '!^image/!', $mime_type );
207
208 if ( 1 === $attachment_is_image ) {
209 $image_sizes = new Jetpack_Photon_ImageSizes( $attachment_id, $data );
210 $data['sizes'] = $image_sizes->generate_sizes_meta();
211 }
212 return $data;
213 }
214
215 /**
216 * Inject image sizes to Jetpack REST API responses. This wraps the filter_photon_norezise_maybe_inject_sizes function.
217 *
218 * @param array $data Attachment sizes data.
219 * @param int $attachment_id Attachment's post ID.
220 *
221 * @return array Attachment sizes array.
222 */
223 public static function filter_photon_norezise_maybe_inject_sizes_api( $sizes, $attachment_id ) {
224 return self::filter_photon_norezise_maybe_inject_sizes( wp_get_attachment_metadata( $attachment_id ), $attachment_id );
225 }
226
227 /**
228 * * IN-CONTENT IMAGE MANIPULATION FUNCTIONS
229 **/
230
231 /**
232 * Match all images and any relevant <a> tags in a block of HTML.
233 *
234 * @param string $content Some HTML.
235 * @return array An array of $images matches, where $images[0] is
236 * an array of full matches, and the link_url, img_tag,
237 * and img_url keys are arrays of those matches.
238 */
239 public static function parse_images_from_html( $content ) {
240 $images = array();
241
242 if ( preg_match_all( '#(?:<a[^>]+?href=["|\'](?P<link_url>[^\s]+?)["|\'][^>]*?>\s*)?(?P<img_tag><(?:img|amp-img|amp-anim)[^>]*?\s+?src=["|\'](?P<img_url>[^\s]+?)["|\'].*?>){1}(?:\s*</a>)?#is', $content, $images ) ) {
243 foreach ( $images as $key => $unused ) {
244 // Simplify the output as much as possible, mostly for confirming test results.
245 if ( is_numeric( $key ) && $key > 0 ) {
246 unset( $images[ $key ] );
247 }
248 }
249
250 return $images;
251 }
252
253 return array();
254 }
255
256 /**
257 * Try to determine height and width from strings WP appends to resized image filenames.
258 *
259 * @param string $src The image URL.
260 * @return array An array consisting of width and height.
261 */
262 public static function parse_dimensions_from_filename( $src ) {
263 $width_height_string = array();
264
265 if ( preg_match( '#-(\d+)x(\d+)\.(?:' . implode( '|', self::$extensions ) . '){1}$#i', $src, $width_height_string ) ) {
266 $width = (int) $width_height_string[1];
267 $height = (int) $width_height_string[2];
268
269 if ( $width && $height ) {
270 return array( $width, $height );
271 }
272 }
273
274 return array( false, false );
275 }
276
277 /**
278 * Identify images in post content, and if images are local (uploaded to the current site), pass through Photon.
279 *
280 * @param string $content
281 * @uses self::validate_image_url, apply_filters, jetpack_photon_url, esc_url
282 * @filter the_content
283 * @return string
284 */
285 public static function filter_the_content( $content ) {
286 $images = self::parse_images_from_html( $content );
287
288 if ( ! empty( $images ) ) {
289 $content_width = Jetpack::get_content_width();
290
291 $image_sizes = self::image_sizes();
292
293 $upload_dir = wp_get_upload_dir();
294
295 foreach ( $images[0] as $index => $tag ) {
296 // Default to resize, though fit may be used in certain cases where a dimension cannot be ascertained
297 $transform = 'resize';
298
299 // Start with a clean attachment ID each time
300 $attachment_id = false;
301
302 // Flag if we need to munge a fullsize URL
303 $fullsize_url = false;
304
305 // Identify image source
306 $src = $src_orig = $images['img_url'][ $index ];
307
308 /**
309 * Allow specific images to be skipped by Photon.
310 *
311 * @module photon
312 *
313 * @since 2.0.3
314 *
315 * @param bool false Should Photon ignore this image. Default to false.
316 * @param string $src Image URL.
317 * @param string $tag Image Tag (Image HTML output).
318 */
319 if ( apply_filters( 'jetpack_photon_skip_image', false, $src, $tag ) ) {
320 continue;
321 }
322
323 // Support Automattic's Lazy Load plugin
324 // Can't modify $tag yet as we need unadulterated version later
325 if ( preg_match( '#data-lazy-src=["|\'](.+?)["|\']#i', $images['img_tag'][ $index ], $lazy_load_src ) ) {
326 $placeholder_src = $placeholder_src_orig = $src;
327 $src = $src_orig = $lazy_load_src[1];
328 } elseif ( preg_match( '#data-lazy-original=["|\'](.+?)["|\']#i', $images['img_tag'][ $index ], $lazy_load_src ) ) {
329 $placeholder_src = $placeholder_src_orig = $src;
330 $src = $src_orig = $lazy_load_src[1];
331 }
332
333 // Check if image URL should be used with Photon
334 if ( self::validate_image_url( $src ) ) {
335 // Find the width and height attributes
336 $width = $height = false;
337
338 // First, check the image tag
339 if ( preg_match( '#[\s|"|\']width=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $width_string ) ) {
340 $width = $width_string[1];
341 }
342
343 if ( preg_match( '#[\s|"|\']height=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $height_string ) ) {
344 $height = $height_string[1];
345 }
346
347 // Can't pass both a relative width and height, so unset the height in favor of not breaking the horizontal layout.
348 if ( false !== strpos( $width, '%' ) && false !== strpos( $height, '%' ) ) {
349 $width = $height = false;
350 }
351
352 // Detect WP registered image size from HTML class
353 if ( preg_match( '#class=["|\']?[^"\']*size-([^"\'\s]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $size ) ) {
354 $size = array_pop( $size );
355
356 if ( false === $width && false === $height && 'full' != $size && array_key_exists( $size, $image_sizes ) ) {
357 $width = (int) $image_sizes[ $size ]['width'];
358 $height = (int) $image_sizes[ $size ]['height'];
359 $transform = $image_sizes[ $size ]['crop'] ? 'resize' : 'fit';
360 }
361 } else {
362 unset( $size );
363 }
364
365 // WP Attachment ID, if uploaded to this site
366 if (
367 preg_match( '#class=["|\']?[^"\']*wp-image-([\d]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $attachment_id ) &&
368 0 === strpos( $src, $upload_dir['baseurl'] ) &&
369 /**
370 * Filter whether an image using an attachment ID in its class has to be uploaded to the local site to go through Photon.
371 *
372 * @module photon
373 *
374 * @since 2.0.3
375 *
376 * @param bool false Was the image uploaded to the local site. Default to false.
377 * @param array $args {
378 * Array of image details.
379 *
380 * @type $src Image URL.
381 * @type tag Image tag (Image HTML output).
382 * @type $images Array of information about the image.
383 * @type $index Image index.
384 * }
385 */
386 apply_filters( 'jetpack_photon_image_is_local', false, compact( 'src', 'tag', 'images', 'index' ) )
387 ) {
388 $attachment_id = intval( array_pop( $attachment_id ) );
389
390 if ( $attachment_id ) {
391 $attachment = get_post( $attachment_id );
392
393 // Basic check on returned post object
394 if ( is_object( $attachment ) && ! is_wp_error( $attachment ) && 'attachment' == $attachment->post_type ) {
395 $src_per_wp = wp_get_attachment_image_src( $attachment_id, isset( $size ) ? $size : 'full' );
396
397 if ( self::validate_image_url( $src_per_wp[0] ) ) {
398 $src = $src_per_wp[0];
399 $fullsize_url = true;
400
401 // Prevent image distortion if a detected dimension exceeds the image's natural dimensions
402 if ( ( false !== $width && $width > $src_per_wp[1] ) || ( false !== $height && $height > $src_per_wp[2] ) ) {
403 $width = false === $width ? false : min( $width, $src_per_wp[1] );
404 $height = false === $height ? false : min( $height, $src_per_wp[2] );
405 }
406
407 // If no width and height are found, max out at source image's natural dimensions
408 // Otherwise, respect registered image sizes' cropping setting
409 if ( false === $width && false === $height ) {
410 $width = $src_per_wp[1];
411 $height = $src_per_wp[2];
412 $transform = 'fit';
413 } elseif ( isset( $size ) && array_key_exists( $size, $image_sizes ) && isset( $image_sizes[ $size ]['crop'] ) ) {
414 $transform = (bool) $image_sizes[ $size ]['crop'] ? 'resize' : 'fit';
415 }
416 }
417 } else {
418 unset( $attachment_id );
419 unset( $attachment );
420 }
421 }
422 }
423
424 // If image tag lacks width and height arguments, try to determine from strings WP appends to resized image filenames.
425 if ( false === $width && false === $height ) {
426 list( $width, $height ) = self::parse_dimensions_from_filename( $src );
427 }
428
429 $width_orig = $width;
430 $height_orig = $height;
431 $transform_orig = $transform;
432
433 // If width is available, constrain to $content_width
434 if ( false !== $width && false === strpos( $width, '%' ) && is_numeric( $content_width ) ) {
435 if ( $width > $content_width && false !== $height && false === strpos( $height, '%' ) ) {
436 $height = round( ( $content_width * $height ) / $width );
437 $width = $content_width;
438 } elseif ( $width > $content_width ) {
439 $width = $content_width;
440 }
441 }
442
443 // Set a width if none is found and $content_width is available
444 // If width is set in this manner and height is available, use `fit` instead of `resize` to prevent skewing
445 if ( false === $width && is_numeric( $content_width ) ) {
446 $width = (int) $content_width;
447
448 if ( false !== $height ) {
449 $transform = 'fit';
450 }
451 }
452
453 // Detect if image source is for a custom-cropped thumbnail and prevent further URL manipulation.
454 if ( ! $fullsize_url && preg_match_all( '#-e[a-z0-9]+(-\d+x\d+)?\.(' . implode( '|', self::$extensions ) . '){1}$#i', basename( $src ), $filename ) ) {
455 $fullsize_url = true;
456 }
457
458 // Build URL, first maybe removing WP's resized string so we pass the original image to Photon
459 if ( ! $fullsize_url && 0 === strpos( $src, $upload_dir['baseurl'] ) ) {
460 $src = self::strip_image_dimensions_maybe( $src );
461 }
462
463 // Build array of Photon args and expose to filter before passing to Photon URL function
464 $args = array();
465
466 if ( false !== $width && false !== $height && false === strpos( $width, '%' ) && false === strpos( $height, '%' ) ) {
467 $args[ $transform ] = $width . ',' . $height;
468 } elseif ( false !== $width ) {
469 $args['w'] = $width;
470 } elseif ( false !== $height ) {
471 $args['h'] = $height;
472 }
473
474 /**
475 * Filter the array of Photon arguments added to an image when it goes through Photon.
476 * By default, only includes width and height values.
477 *
478 * @see https://developer.wordpress.com/docs/photon/api/
479 *
480 * @module photon
481 *
482 * @since 2.0.0
483 *
484 * @param array $args Array of Photon Arguments.
485 * @param array $details {
486 * Array of image details.
487 *
488 * @type string $tag Image tag (Image HTML output).
489 * @type string $src Image URL.
490 * @type string $src_orig Original Image URL.
491 * @type int|false $width Image width.
492 * @type int|false $height Image height.
493 * @type int|false $width_orig Original image width before constrained by content_width.
494 * @type int|false $height_orig Original Image height before constrained by content_width.
495 * @type string $transform Transform.
496 * @type string $transform_orig Original transform before constrained by content_width.
497 * }
498 */
499 $args = apply_filters( 'jetpack_photon_post_image_args', $args, compact( 'tag', 'src', 'src_orig', 'width', 'height', 'width_orig', 'height_orig', 'transform', 'transform_orig' ) );
500
501 $photon_url = jetpack_photon_url( $src, $args );
502
503 // Modify image tag if Photon function provides a URL
504 // Ensure changes are only applied to the current image by copying and modifying the matched tag, then replacing the entire tag with our modified version.
505 if ( $src != $photon_url ) {
506 $new_tag = $tag;
507
508 // If present, replace the link href with a Photoned URL for the full-size image.
509 if ( ! empty( $images['link_url'][ $index ] ) && self::validate_image_url( $images['link_url'][ $index ] ) ) {
510 $new_tag = preg_replace( '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i', '\1' . jetpack_photon_url( $images['link_url'][ $index ] ) . '\2', $new_tag, 1 );
511 }
512
513 // Supplant the original source value with our Photon URL
514 $photon_url = esc_url( $photon_url );
515 $new_tag = str_replace( $src_orig, $photon_url, $new_tag );
516
517 // If Lazy Load is in use, pass placeholder image through Photon
518 if ( isset( $placeholder_src ) && self::validate_image_url( $placeholder_src ) ) {
519 $placeholder_src = jetpack_photon_url( $placeholder_src );
520
521 if ( $placeholder_src != $placeholder_src_orig ) {
522 $new_tag = str_replace( $placeholder_src_orig, esc_url( $placeholder_src ), $new_tag );
523 }
524
525 unset( $placeholder_src );
526 }
527
528 // If we are not transforming the image with resize, fit, or letterbox (lb), then we should remove
529 // the width and height arguments from the image to prevent distortion. Even if $args['w'] and $args['h']
530 // are present, Photon does not crop to those dimensions. Instead, it appears to favor height.
531 //
532 // If we are transforming the image via one of those methods, let's update the width and height attributes.
533 if ( empty( $args['resize'] ) && empty( $args['fit'] ) && empty( $args['lb'] ) ) {
534 $new_tag = preg_replace( '#(?<=\s)(width|height)=["|\']?[\d%]+["|\']?\s?#i', '', $new_tag );
535 } else {
536 $resize_args = isset( $args['resize'] ) ? $args['resize'] : false;
537 if ( false == $resize_args ) {
538 $resize_args = ( ! $resize_args && isset( $args['fit'] ) )
539 ? $args['fit']
540 : false;
541 }
542 if ( false == $resize_args ) {
543 $resize_args = ( ! $resize_args && isset( $args['lb'] ) )
544 ? $args['lb']
545 : false;
546 }
547
548 $resize_args = array_map( 'trim', explode( ',', $resize_args ) );
549
550 // (?<=\s) - Ensure width or height attribute is preceded by a space
551 // (width=["|\']?) - Matches, and captures, width=, width=", or width='
552 // [\d%]+ - Matches 1 or more digits
553 // (["|\']?) - Matches, and captures, ", ', or empty string
554 // \s - Ensures there's a space after the attribute
555 $new_tag = preg_replace( '#(?<=\s)(width=["|\']?)[\d%]+(["|\']?)\s?#i', sprintf( '${1}%d${2} ', $resize_args[0] ), $new_tag );
556 $new_tag = preg_replace( '#(?<=\s)(height=["|\']?)[\d%]+(["|\']?)\s?#i', sprintf( '${1}%d${2} ', $resize_args[1] ), $new_tag );
557 }
558
559 // Tag an image for dimension checking
560 if ( ! self::is_amp_endpoint() ) {
561 $new_tag = preg_replace( '#(\s?/)?>(\s*</a>)?$#i', ' data-recalc-dims="1"\1>\2', $new_tag );
562 }
563
564 // Replace original tag with modified version
565 $content = str_replace( $tag, $new_tag, $content );
566 }
567 } elseif ( preg_match( '#^http(s)?://i[\d]{1}.wp.com#', $src ) && ! empty( $images['link_url'][ $index ] ) && self::validate_image_url( $images['link_url'][ $index ] ) ) {
568 $new_tag = preg_replace( '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i', '\1' . jetpack_photon_url( $images['link_url'][ $index ] ) . '\2', $tag, 1 );
569
570 $content = str_replace( $tag, $new_tag, $content );
571 }
572 }
573 }
574
575 return $content;
576 }
577
578 public static function filter_the_galleries( $galleries ) {
579 if ( empty( $galleries ) || ! is_array( $galleries ) ) {
580 return $galleries;
581 }
582
583 // Pass by reference, so we can modify them in place.
584 foreach ( $galleries as &$this_gallery ) {
585 if ( is_string( $this_gallery ) ) {
586 $this_gallery = self::filter_the_content( $this_gallery );
587 // LEAVING COMMENTED OUT as for the moment it doesn't seem
588 // necessary and I'm not sure how it would propagate through.
589 // } elseif ( is_array( $this_gallery )
590 // && ! empty( $this_gallery['src'] )
591 // && ! empty( $this_gallery['type'] )
592 // && in_array( $this_gallery['type'], array( 'rectangle', 'square', 'circle' ) ) ) {
593 // $this_gallery['src'] = array_map( 'jetpack_photon_url', $this_gallery['src'] );
594 }
595 }
596 unset( $this_gallery ); // break the reference.
597
598 return $galleries;
599 }
600
601
602 /**
603 * Runs the image widget through photon.
604 *
605 * @param array $instance Image widget instance data.
606 * @return array
607 */
608 public static function filter_the_image_widget( $instance ) {
609 if ( Jetpack::is_module_active( 'photon' ) && ! $instance['attachment_id'] && $instance['url'] ) {
610 jetpack_photon_url(
611 $instance['url'],
612 array(
613 'w' => $instance['width'],
614 'h' => $instance['height'],
615 )
616 );
617 }
618
619 return $instance;
620 }
621
622 /**
623 * * CORE IMAGE RETRIEVAL
624 **/
625
626 /**
627 * Filter post thumbnail image retrieval, passing images through Photon
628 *
629 * @param string|bool $image
630 * @param int $attachment_id
631 * @param string|array $size
632 * @uses is_admin, apply_filters, wp_get_attachment_url, self::validate_image_url, this::image_sizes, jetpack_photon_url
633 * @filter image_downsize
634 * @return string|bool
635 */
636 public function filter_image_downsize( $image, $attachment_id, $size ) {
637 // Don't foul up the admin side of things, unless a plugin wants to.
638 if ( is_admin() &&
639 /**
640 * Provide plugins a way of running Photon for images in the WordPress Dashboard (wp-admin).
641 *
642 * Note: enabling this will result in Photon URLs added to your post content, which could make migrations across domains (and off Photon) a bit more challenging.
643 *
644 * @module photon
645 *
646 * @since 4.8.0
647 *
648 * @param bool false Stop Photon from being run on the Dashboard. Default to false.
649 * @param array $args {
650 * Array of image details.
651 *
652 * @type $image Image URL.
653 * @type $attachment_id Attachment ID of the image.
654 * @type $size Image size. Can be a string (name of the image size, e.g. full) or an array of width and height.
655 * }
656 */
657 false === apply_filters( 'jetpack_photon_admin_allow_image_downsize', false, compact( 'image', 'attachment_id', 'size' ) )
658 ) {
659 return $image;
660 }
661
662 /**
663 * Provide plugins a way of preventing Photon from being applied to images retrieved from WordPress Core.
664 *
665 * @module photon
666 *
667 * @since 2.0.0
668 *
669 * @param bool false Stop Photon from being applied to the image. Default to false.
670 * @param array $args {
671 * Array of image details.
672 *
673 * @type $image Image URL.
674 * @type $attachment_id Attachment ID of the image.
675 * @type $size Image size. Can be a string (name of the image size, e.g. full) or an array of width and height.
676 * }
677 */
678 if ( apply_filters( 'jetpack_photon_override_image_downsize', false, compact( 'image', 'attachment_id', 'size' ) ) ) {
679 return $image;
680 }
681
682 // Get the image URL and proceed with Photon-ification if successful
683 $image_url = wp_get_attachment_url( $attachment_id );
684
685 // Set this to true later when we know we have size meta.
686 $has_size_meta = false;
687
688 if ( $image_url ) {
689 // Check if image URL should be used with Photon
690 if ( ! self::validate_image_url( $image_url ) ) {
691 return $image;
692 }
693
694 $intermediate = true; // For the fourth array item returned by the image_downsize filter.
695
696 // If an image is requested with a size known to WordPress, use that size's settings with Photon.
697 // WP states that `add_image_size()` should use a string for the name, but doesn't enforce that.
698 // Due to differences in how Core and Photon check for the registered image size, we check both types.
699 if ( ( is_string( $size ) || is_int( $size ) ) && array_key_exists( $size, self::image_sizes() ) ) {
700 $image_args = self::image_sizes();
701 $image_args = $image_args[ $size ];
702
703 $photon_args = array();
704
705 $image_meta = image_get_intermediate_size( $attachment_id, $size );
706
707 // 'full' is a special case: We need consistent data regardless of the requested size.
708 if ( 'full' == $size ) {
709 $image_meta = wp_get_attachment_metadata( $attachment_id );
710 $intermediate = false;
711 } elseif ( ! $image_meta ) {
712 // If we still don't have any image meta at this point, it's probably from a custom thumbnail size
713 // for an image that was uploaded before the custom image was added to the theme. Try to determine the size manually.
714 $image_meta = wp_get_attachment_metadata( $attachment_id );
715
716 if ( isset( $image_meta['width'], $image_meta['height'] ) ) {
717 $image_resized = image_resize_dimensions( $image_meta['width'], $image_meta['height'], $image_args['width'], $image_args['height'], $image_args['crop'] );
718 if ( $image_resized ) { // This could be false when the requested image size is larger than the full-size image.
719 $image_meta['width'] = $image_resized[6];
720 $image_meta['height'] = $image_resized[7];
721 }
722 }
723 }
724
725 if ( isset( $image_meta['width'], $image_meta['height'] ) ) {
726 $image_args['width'] = $image_meta['width'];
727 $image_args['height'] = $image_meta['height'];
728
729 list( $image_args['width'], $image_args['height'] ) = image_constrain_size_for_editor( $image_args['width'], $image_args['height'], $size, 'display' );
730 $has_size_meta = true;
731 }
732
733 // Expose determined arguments to a filter before passing to Photon
734 $transform = $image_args['crop'] ? 'resize' : 'fit';
735
736 // Check specified image dimensions and account for possible zero values; photon fails to resize if a dimension is zero.
737 if ( 0 == $image_args['width'] || 0 == $image_args['height'] ) {
738 if ( 0 == $image_args['width'] && 0 < $image_args['height'] ) {
739 $photon_args['h'] = $image_args['height'];
740 } elseif ( 0 == $image_args['height'] && 0 < $image_args['width'] ) {
741 $photon_args['w'] = $image_args['width'];
742 }
743 } else {
744 if ( ( 'resize' === $transform ) && $image_meta = wp_get_attachment_metadata( $attachment_id ) ) {
745 if ( isset( $image_meta['width'], $image_meta['height'] ) ) {
746 // Lets make sure that we don't upscale images since wp never upscales them as well
747 $smaller_width = ( ( $image_meta['width'] < $image_args['width'] ) ? $image_meta['width'] : $image_args['width'] );
748 $smaller_height = ( ( $image_meta['height'] < $image_args['height'] ) ? $image_meta['height'] : $image_args['height'] );
749
750 $photon_args[ $transform ] = $smaller_width . ',' . $smaller_height;
751 }
752 } else {
753 $photon_args[ $transform ] = $image_args['width'] . ',' . $image_args['height'];
754 }
755 }
756
757 /**
758 * Filter the Photon Arguments added to an image when going through Photon, when that image size is a string.
759 * Image size will be a string (e.g. "full", "medium") when it is known to WordPress.
760 *
761 * @module photon
762 *
763 * @since 2.0.0
764 *
765 * @param array $photon_args Array of Photon arguments.
766 * @param array $args {
767 * Array of image details.
768 *
769 * @type $image_args Array of Image arguments (width, height, crop).
770 * @type $image_url Image URL.
771 * @type $attachment_id Attachment ID of the image.
772 * @type $size Image size. Can be a string (name of the image size, e.g. full) or an integer.
773 * @type $transform Value can be resize or fit.
774 * @see https://developer.wordpress.com/docs/photon/api
775 * }
776 */
777 $photon_args = apply_filters( 'jetpack_photon_image_downsize_string', $photon_args, compact( 'image_args', 'image_url', 'attachment_id', 'size', 'transform' ) );
778
779 // Generate Photon URL
780 $image = array(
781 jetpack_photon_url( $image_url, $photon_args ),
782 $has_size_meta ? $image_args['width'] : false,
783 $has_size_meta ? $image_args['height'] : false,
784 $intermediate,
785 );
786 } elseif ( is_array( $size ) ) {
787 // Pull width and height values from the provided array, if possible
788 $width = isset( $size[0] ) ? (int) $size[0] : false;
789 $height = isset( $size[1] ) ? (int) $size[1] : false;
790
791 // Don't bother if necessary parameters aren't passed.
792 if ( ! $width || ! $height ) {
793 return $image;
794 }
795
796 $image_meta = wp_get_attachment_metadata( $attachment_id );
797 if ( isset( $image_meta['width'], $image_meta['height'] ) ) {
798 $image_resized = image_resize_dimensions( $image_meta['width'], $image_meta['height'], $width, $height );
799
800 if ( $image_resized ) { // This could be false when the requested image size is larger than the full-size image.
801 $width = $image_resized[6];
802 $height = $image_resized[7];
803 } else {
804 $width = $image_meta['width'];
805 $height = $image_meta['height'];
806 }
807
808 $has_size_meta = true;
809 }
810
811 list( $width, $height ) = image_constrain_size_for_editor( $width, $height, $size );
812
813 // Expose arguments to a filter before passing to Photon
814 $photon_args = array(
815 'fit' => $width . ',' . $height,
816 );
817
818 /**
819 * Filter the Photon Arguments added to an image when going through Photon,
820 * when the image size is an array of height and width values.
821 *
822 * @module photon
823 *
824 * @since 2.0.0
825 *
826 * @param array $photon_args Array of Photon arguments.
827 * @param array $args {
828 * Array of image details.
829 *
830 * @type $width Image width.
831 * @type height Image height.
832 * @type $image_url Image URL.
833 * @type $attachment_id Attachment ID of the image.
834 * }
835 */
836 $photon_args = apply_filters( 'jetpack_photon_image_downsize_array', $photon_args, compact( 'width', 'height', 'image_url', 'attachment_id' ) );
837
838 // Generate Photon URL
839 $image = array(
840 jetpack_photon_url( $image_url, $photon_args ),
841 $has_size_meta ? $width : false,
842 $has_size_meta ? $height : false,
843 $intermediate,
844 );
845 }
846 }
847
848 return $image;
849 }
850
851 /**
852 * Filters an array of image `srcset` values, replacing each URL with its Photon equivalent.
853 *
854 * @since 3.8.0
855 * @since 4.0.4 Added automatically additional sizes beyond declared image sizes.
856 * @param array $sources An array of image urls and widths.
857 * @uses self::validate_image_url, jetpack_photon_url, Jetpack_Photon::parse_from_filename
858 * @uses Jetpack_Photon::strip_image_dimensions_maybe, Jetpack::get_content_width
859 * @return array An array of Photon image urls and widths.
860 */
861 public function filter_srcset_array( $sources = array(), $size_array = array(), $image_src = array(), $image_meta = array(), $attachment_id = 0 ) {
862 if ( ! is_array( $sources ) ) {
863 return $sources;
864 }
865 $upload_dir = wp_get_upload_dir();
866
867 foreach ( $sources as $i => $source ) {
868 if ( ! self::validate_image_url( $source['url'] ) ) {
869 continue;
870 }
871
872 /** This filter is already documented in class.photon.php */
873 if ( apply_filters( 'jetpack_photon_skip_image', false, $source['url'], $source ) ) {
874 continue;
875 }
876
877 $url = $source['url'];
878 list( $width, $height ) = self::parse_dimensions_from_filename( $url );
879
880 // It's quicker to get the full size with the data we have already, if available
881 if ( ! empty( $attachment_id ) ) {
882 $url = wp_get_attachment_url( $attachment_id );
883 } else {
884 $url = self::strip_image_dimensions_maybe( $url );
885 }
886
887 $args = array();
888 if ( 'w' === $source['descriptor'] ) {
889 if ( $height && ( $source['value'] == $width ) ) {
890 $args['resize'] = $width . ',' . $height;
891 } else {
892 $args['w'] = $source['value'];
893 }
894 }
895
896 $sources[ $i ]['url'] = jetpack_photon_url( $url, $args );
897 }
898
899 /**
900 * At this point, $sources is the original srcset with Photonized URLs.
901 * Now, we're going to construct additional sizes based on multiples of the content_width.
902 * This will reduce the gap between the largest defined size and the original image.
903 */
904
905 /**
906 * Filter the multiplier Photon uses to create new srcset items.
907 * Return false to short-circuit and bypass auto-generation.
908 *
909 * @module photon
910 *
911 * @since 4.0.4
912 *
913 * @param array|bool $multipliers Array of multipliers to use or false to bypass.
914 */
915 $multipliers = apply_filters( 'jetpack_photon_srcset_multipliers', array( 2, 3 ) );
916 $url = trailingslashit( $upload_dir['baseurl'] ) . $image_meta['file'];
917
918 if (
919 /** Short-circuit via jetpack_photon_srcset_multipliers filter. */
920 is_array( $multipliers )
921 /** This filter is already documented in class.photon.php */
922 && ! apply_filters( 'jetpack_photon_skip_image', false, $url, null )
923 /** Verify basic meta is intact. */
924 && isset( $image_meta['width'] ) && isset( $image_meta['height'] ) && isset( $image_meta['file'] )
925 /** Verify we have the requested width/height. */
926 && isset( $size_array[0] ) && isset( $size_array[1] )
927 ) {
928
929 $fullwidth = $image_meta['width'];
930 $fullheight = $image_meta['height'];
931 $reqwidth = $size_array[0];
932 $reqheight = $size_array[1];
933
934 $constrained_size = wp_constrain_dimensions( $fullwidth, $fullheight, $reqwidth );
935 $expected_size = array( $reqwidth, $reqheight );
936
937 if ( abs( $constrained_size[0] - $expected_size[0] ) <= 1 && abs( $constrained_size[1] - $expected_size[1] ) <= 1 ) {
938 $crop = 'soft';
939 $base = Jetpack::get_content_width() ? Jetpack::get_content_width() : 1000; // Provide a default width if none set by the theme.
940 } else {
941 $crop = 'hard';
942 $base = $reqwidth;
943 }
944
945 $currentwidths = array_keys( $sources );
946 $newsources = null;
947
948 foreach ( $multipliers as $multiplier ) {
949
950 $newwidth = $base * $multiplier;
951 foreach ( $currentwidths as $currentwidth ) {
952 // If a new width would be within 100 pixes of an existing one or larger than the full size image, skip.
953 if ( abs( $currentwidth - $newwidth ) < 50 || ( $newwidth > $fullwidth ) ) {
954 continue 2; // Back to the foreach ( $multipliers as $multiplier )
955 }
956 } // foreach ( $currentwidths as $currentwidth ){
957
958 if ( 'soft' == $crop ) {
959 $args = array(
960 'w' => $newwidth,
961 );
962 } else { // hard crop, e.g. add_image_size( 'example', 200, 200, true );
963 $args = array(
964 'zoom' => $multiplier,
965 'resize' => $reqwidth . ',' . $reqheight,
966 );
967 }
968
969 $newsources[ $newwidth ] = array(
970 'url' => jetpack_photon_url( $url, $args ),
971 'descriptor' => 'w',
972 'value' => $newwidth,
973 );
974 } // foreach ( $multipliers as $multiplier )
975 if ( is_array( $newsources ) ) {
976 $sources = array_replace( $sources, $newsources );
977 }
978 } // if ( isset( $image_meta['width'] ) && isset( $image_meta['file'] ) )
979
980 return $sources;
981 }
982
983 /**
984 * Filters an array of image `sizes` values, using $content_width instead of image's full size.
985 *
986 * @since 4.0.4
987 * @since 4.1.0 Returns early for images not within the_content.
988 * @param array $sizes An array of media query breakpoints.
989 * @param array $size Width and height of the image
990 * @uses Jetpack::get_content_width
991 * @return array An array of media query breakpoints.
992 */
993 public function filter_sizes( $sizes, $size ) {
994 if ( ! doing_filter( 'the_content' ) ) {
995 return $sizes;
996 }
997 $content_width = Jetpack::get_content_width();
998 if ( ! $content_width ) {
999 $content_width = 1000;
1000 }
1001
1002 if ( ( is_array( $size ) && $size[0] < $content_width ) ) {
1003 return $sizes;
1004 }
1005
1006 return sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $content_width );
1007 }
1008
1009 /**
1010 * * GENERAL FUNCTIONS
1011 **/
1012
1013 /**
1014 * Ensure image URL is valid for Photon.
1015 * Though Photon functions address some of the URL issues, we should avoid unnecessary processing if we know early on that the image isn't supported.
1016 *
1017 * @param string $url
1018 * @uses wp_parse_args
1019 * @return bool
1020 */
1021 protected static function validate_image_url( $url ) {
1022 $parsed_url = wp_parse_url( $url );
1023
1024 if ( ! $parsed_url ) {
1025 return false;
1026 }
1027
1028 // Parse URL and ensure needed keys exist, since the array returned by `wp_parse_url` only includes the URL components it finds.
1029 $url_info = wp_parse_args(
1030 $parsed_url,
1031 array(
1032 'scheme' => null,
1033 'host' => null,
1034 'port' => null,
1035 'path' => null,
1036 )
1037 );
1038
1039 // Bail if scheme isn't http or port is set that isn't port 80
1040 if (
1041 ( 'http' != $url_info['scheme'] || ! in_array( $url_info['port'], array( 80, null ) ) ) &&
1042 /**
1043 * Allow Photon to fetch images that are served via HTTPS.
1044 *
1045 * @module photon
1046 *
1047 * @since 2.4.0
1048 * @since 3.9.0 Default to false.
1049 *
1050 * @param bool $reject_https Should Photon ignore images using the HTTPS scheme. Default to false.
1051 */
1052 apply_filters( 'jetpack_photon_reject_https', false )
1053 ) {
1054 return false;
1055 }
1056
1057 // Bail if no host is found
1058 if ( is_null( $url_info['host'] ) ) {
1059 return false;
1060 }
1061
1062 // Bail if the image alredy went through Photon
1063 if ( preg_match( '#^i[\d]{1}.wp.com$#i', $url_info['host'] ) ) {
1064 return false;
1065 }
1066
1067 // Bail if no path is found
1068 if ( is_null( $url_info['path'] ) ) {
1069 return false;
1070 }
1071
1072 // Ensure image extension is acceptable
1073 if ( ! in_array( strtolower( pathinfo( $url_info['path'], PATHINFO_EXTENSION ) ), self::$extensions ) ) {
1074 return false;
1075 }
1076
1077 // If we got this far, we should have an acceptable image URL
1078 // But let folks filter to decline if they prefer.
1079 /**
1080 * Overwrite the results of the validation steps an image goes through before to be considered valid to be used by Photon.
1081 *
1082 * @module photon
1083 *
1084 * @since 3.0.0
1085 *
1086 * @param bool true Is the image URL valid and can it be used by Photon. Default to true.
1087 * @param string $url Image URL.
1088 * @param array $parsed_url Array of information about the image.
1089 */
1090 return apply_filters( 'photon_validate_image_url', true, $url, $parsed_url );
1091 }
1092
1093 /**
1094 * Checks if the file exists before it passes the file to photon
1095 *
1096 * @param string $src The image URL
1097 * @return string
1098 **/
1099 public static function strip_image_dimensions_maybe( $src ) {
1100 $stripped_src = $src;
1101
1102 // Build URL, first removing WP's resized string so we pass the original image to Photon
1103 if ( preg_match( '#(-\d+x\d+)\.(' . implode( '|', self::$extensions ) . '){1}$#i', $src, $src_parts ) ) {
1104 $stripped_src = str_replace( $src_parts[1], '', $src );
1105 $upload_dir = wp_get_upload_dir();
1106
1107 // Extracts the file path to the image minus the base url
1108 $file_path = substr( $stripped_src, strlen( $upload_dir['baseurl'] ) );
1109
1110 if ( file_exists( $upload_dir['basedir'] . $file_path ) ) {
1111 $src = $stripped_src;
1112 }
1113 }
1114
1115 return $src;
1116 }
1117
1118 /**
1119 * Provide an array of available image sizes and corresponding dimensions.
1120 * Similar to get_intermediate_image_sizes() except that it includes image sizes' dimensions, not just their names.
1121 *
1122 * @global $wp_additional_image_sizes
1123 * @uses get_option
1124 * @return array
1125 */
1126 protected static function image_sizes() {
1127 if ( null == self::$image_sizes ) {
1128 global $_wp_additional_image_sizes;
1129
1130 // Populate an array matching the data structure of $_wp_additional_image_sizes so we have a consistent structure for image sizes
1131 $images = array(
1132 'thumb' => array(
1133 'width' => intval( get_option( 'thumbnail_size_w' ) ),
1134 'height' => intval( get_option( 'thumbnail_size_h' ) ),
1135 'crop' => (bool) get_option( 'thumbnail_crop' ),
1136 ),
1137 'medium' => array(
1138 'width' => intval( get_option( 'medium_size_w' ) ),
1139 'height' => intval( get_option( 'medium_size_h' ) ),
1140 'crop' => false,
1141 ),
1142 'medium_large' => array(
1143 'width' => intval( get_option( 'medium_large_size_w' ) ),
1144 'height' => intval( get_option( 'medium_large_size_h' ) ),
1145 'crop' => false,
1146 ),
1147 'large' => array(
1148 'width' => intval( get_option( 'large_size_w' ) ),
1149 'height' => intval( get_option( 'large_size_h' ) ),
1150 'crop' => false,
1151 ),
1152 'full' => array(
1153 'width' => null,
1154 'height' => null,
1155 'crop' => false,
1156 ),
1157 );
1158
1159 // Compatibility mapping as found in wp-includes/media.php
1160 $images['thumbnail'] = $images['thumb'];
1161
1162 // Update class variable, merging in $_wp_additional_image_sizes if any are set
1163 if ( is_array( $_wp_additional_image_sizes ) && ! empty( $_wp_additional_image_sizes ) ) {
1164 self::$image_sizes = array_merge( $images, $_wp_additional_image_sizes );
1165 } else {
1166 self::$image_sizes = $images;
1167 }
1168 }
1169
1170 return is_array( self::$image_sizes ) ? self::$image_sizes : array();
1171 }
1172
1173 /**
1174 * Pass og:image URLs through Photon
1175 *
1176 * @param array $tags
1177 * @param array $parameters
1178 * @uses jetpack_photon_url
1179 * @return array
1180 */
1181 function filter_open_graph_tags( $tags, $parameters ) {
1182 if ( empty( $tags['og:image'] ) ) {
1183 return $tags;
1184 }
1185
1186 $photon_args = array(
1187 'fit' => sprintf( '%d,%d', 2 * $parameters['image_width'], 2 * $parameters['image_height'] ),
1188 );
1189
1190 if ( is_array( $tags['og:image'] ) ) {
1191 $images = array();
1192 foreach ( $tags['og:image'] as $image ) {
1193 $images[] = jetpack_photon_url( $image, $photon_args );
1194 }
1195 $tags['og:image'] = $images;
1196 } else {
1197 $tags['og:image'] = jetpack_photon_url( $tags['og:image'], $photon_args );
1198 }
1199
1200 return $tags;
1201 }
1202
1203 public function noresize_intermediate_sizes( $sizes ) {
1204 return __return_empty_array();
1205 }
1206
1207 /**
1208 * Enqueue Photon helper script
1209 *
1210 * @uses wp_enqueue_script, plugins_url
1211 * @action wp_enqueue_script
1212 * @return null
1213 */
1214 public function action_wp_enqueue_scripts() {
1215 if ( self::is_amp_endpoint() ) {
1216 return;
1217 }
1218 wp_enqueue_script(
1219 'jetpack-photon',
1220 Assets::get_file_url_for_environment(
1221 '_inc/build/photon/photon.min.js',
1222 'modules/photon/photon.js'
1223 ),
1224 array(),
1225 20191001,
1226 true
1227 );
1228 }
1229
1230 /**
1231 * Determine if image_downsize should utilize Photon via REST API.
1232 *
1233 * The WordPress Block Editor (Gutenberg) and other REST API consumers using the wp/v2/media endpoint, especially in the "edit"
1234 * context is more akin to the is_admin usage of Photon (see filter_image_downsize). Since consumers are trying to edit content in posts,
1235 * Photon should not fire as it will fire later on display. By aborting an attempt to Photonize an image here, we
1236 * prevents issues like https://github.com/Automattic/jetpack/issues/10580 .
1237 *
1238 * To determine if we're using the wp/v2/media endpoint, we hook onto the `rest_request_before_callbacks` filter and
1239 * if determined we are using it in the edit context, we'll false out the `jetpack_photon_override_image_downsize` filter.
1240 *
1241 * @see Jetpack_Photon::filter_image_downsize()
1242 *
1243 * @param null|WP_Error $response
1244 * @param array $endpoint_data
1245 * @param WP_REST_Request $request Request used to generate the response.
1246 *
1247 * @return null|WP_Error The original response object without modification.
1248 */
1249 public function should_rest_photon_image_downsize( $response, $endpoint_data, $request ) {
1250 if ( ! is_a( $request, 'WP_REST_Request' ) ) {
1251 return $response; // Something odd is happening. Do nothing and return the response.
1252 }
1253
1254 if ( is_wp_error( $response ) ) {
1255 // If we're going to return an error, we don't need to do anything with Photon.
1256 return $response;
1257 }
1258
1259 $route = $request->get_route();
1260
1261 if ( false !== strpos( $route, 'wp/v2/media' ) && 'edit' === $request['context'] ) {
1262 // Don't use `__return_true()`: Use something unique. See ::_override_image_downsize_in_rest_edit_context()
1263 // Late execution to avoid conflict with other plugins as we really don't want to run in this situation.
1264 add_filter( 'jetpack_photon_override_image_downsize', array( $this, '_override_image_downsize_in_rest_edit_context' ), 999999 );
1265 }
1266
1267 return $response;
1268
1269 }
1270
1271 /**
1272 * Remove the override we may have added in ::should_rest_photon_image_downsize()
1273 * Since ::_override_image_downsize_in_rest_edit_context() is only
1274 * every used here, we can always remove it without ever worrying
1275 * about breaking any other configuration.
1276 *
1277 * @param mixed $response
1278 * @return mixed Unchanged $response
1279 */
1280 public function cleanup_rest_photon_image_downsize( $response ) {
1281 remove_filter( 'jetpack_photon_override_image_downsize', array( $this, '_override_image_downsize_in_rest_edit_context' ), 999999 );
1282 return $response;
1283 }
1284
1285 /**
1286 * Used internally by ::should_rest_photon_image_downsize() to not photonize
1287 * image URLs in ?context=edit REST requests.
1288 * MUST NOT be used anywhere else.
1289 * We use a unique function instead of __return_true so that we can clean up
1290 * after ourselves without breaking anyone else's filters.
1291 *
1292 * @internal
1293 * @return true
1294 */
1295 public function _override_image_downsize_in_rest_edit_context() {
1296 return true;
1297 }
1298
1299 /**
1300 * Return whether the current page is AMP.
1301 *
1302 * This is only present for the sake of WordPress.com where the Jetpack_AMP_Support
1303 * class does not yet exist. This mehod may only be called at the wp action or later.
1304 *
1305 * @return bool Whether AMP page.
1306 */
1307 private static function is_amp_endpoint() {
1308 return class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request();
1309 }
1310 }
1311