Completed
Push — fix/normalize-www-in-site-url-... ( e67e76 )
by
unknown
13:13 queued 02:59
created

class.jetpack-post-images.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Useful for finding an image to display alongside/in representation of a specific post.
5
 *
6
 * Includes a few different methods, all of which return a similar-format array containing
7
 * details of any images found. Everything can (should) be called statically, it's just a
8
 * function-bucket. You can also call Jetpack_PostImages::get_image() to cycle through all of the methods until
9
 * one of them finds something useful.
10
 *
11
 * This file is included verbatim in Jetpack
12
 */
13
class Jetpack_PostImages {
14
	/**
15
	 * If a slideshow is embedded within a post, then parse out the images involved and return them
16
	 */
17
	static function from_slideshow( $post_id, $width = 200, $height = 200 ) {
18
		$images = array();
19
20
		$post = get_post( $post_id );
21
22
		if ( ! $post ) {
23
			return $images;
24
		}
25
26
		if ( ! empty( $post->post_password ) ) {
27
			return $images;
28
		}
29
30
		if ( false === has_shortcode( $post->post_content, 'slideshow' ) ) {
31
			return $images; // no slideshow - bail
32
		}
33
34
		$permalink = get_permalink( $post->ID );
35
36
		// Mechanic: Somebody set us up the bomb
37
		$old_post = $GLOBALS['post'];
38
		$GLOBALS['post'] = $post;
39
		$old_shortcodes = $GLOBALS['shortcode_tags'];
40
		$GLOBALS['shortcode_tags'] = array( 'slideshow' => $old_shortcodes['slideshow'] );
41
42
		// Find all the slideshows
43
		preg_match_all( '/' . get_shortcode_regex() . '/sx', $post->post_content, $slideshow_matches, PREG_SET_ORDER );
44
45
		ob_start(); // The slideshow shortcode handler calls wp_print_scripts and wp_print_styles... not too happy about that
46
47
		foreach ( $slideshow_matches as $slideshow_match ) {
48
			$slideshow = do_shortcode_tag( $slideshow_match );
49
			if ( false === $pos = stripos( $slideshow, 'jetpack-slideshow' ) ) // must be something wrong - or we changed the output format in which case none of the following will work
50
				continue;
51
			$start = strpos( $slideshow, '[', $pos );
52
			$end = strpos( $slideshow, ']', $start );
53
			$post_images = json_decode( wp_specialchars_decode( str_replace( "'", '"', substr( $slideshow, $start, $end - $start + 1 ) ), ENT_QUOTES ) ); // parse via JSON
54
			foreach ( $post_images as $post_image ) {
55
				if ( !$post_image_id = absint( $post_image->id ) )
56
					continue;
57
58
				$meta = wp_get_attachment_metadata( $post_image_id );
59
60
				// Must be larger than 200x200 (or user-specified)
61
				if ( !isset( $meta['width'] ) || $meta['width'] < $width )
62
					continue;
63
				if ( !isset( $meta['height'] ) || $meta['height'] < $height )
64
					continue;
65
66
				$url = wp_get_attachment_url( $post_image_id );
67
68
				$images[] = array(
69
					'type'       => 'image',
70
					'from'       => 'slideshow',
71
					'src'        => $url,
72
					'src_width'  => $meta['width'],
73
					'src_height' => $meta['height'],
74
					'href'       => $permalink,
75
				);
76
			}
77
		}
78
		ob_end_clean();
79
80
		// Operator: Main screen turn on
81
		$GLOBALS['shortcode_tags'] = $old_shortcodes;
82
		$GLOBALS['post'] = $old_post;
83
84
		return $images;
85
	}
86
87
	/**
88
	 * If a gallery is detected, then get all the images from it.
89
	 */
90
	static function from_gallery( $post_id ) {
91
		$images = array();
92
93
		$post = get_post( $post_id );
94
95
		if ( ! $post ) {
96
			return $images;
97
		}
98
99
		if ( ! empty( $post->post_password ) ) {
100
			return $images;
101
		}
102
103
		$permalink = get_permalink( $post->ID );
104
105
		$galleries = get_post_galleries( $post->ID, false );
106
107
		foreach ( $galleries as $gallery ) {
108
			if ( isset( $gallery['type'] ) && 'slideshow' === $gallery['type'] && ! empty( $gallery['ids'] ) ) {
109
				$image_ids = explode( ',', $gallery['ids'] );
110
				$image_size = isset( $gallery['size'] ) ? $gallery['size'] : 'thumbnail';
111
				foreach ( $image_ids as $image_id ) {
112
					$image = wp_get_attachment_image_src( $image_id, $image_size );
113 View Code Duplication
					if ( ! empty( $image[0] ) ) {
114
						list( $raw_src ) = explode( '?', $image[0] ); // pull off any Query string (?w=250)
115
						$raw_src = wp_specialchars_decode( $raw_src ); // rawify it
116
						$raw_src = esc_url_raw( $raw_src ); // clean it
117
						$images[] = array(
118
							'type'  => 'image',
119
							'from'  => 'gallery',
120
							'src'   => $raw_src,
121
							'href'  => $permalink,
122
						);
123
					}
124
				}
125 View Code Duplication
			} elseif ( ! empty( $gallery['src'] ) ) {
126
				foreach ( $gallery['src'] as $src ) {
127
					list( $raw_src ) = explode( '?', $src ); // pull off any Query string (?w=250)
128
					$raw_src = wp_specialchars_decode( $raw_src ); // rawify it
129
					$raw_src = esc_url_raw( $raw_src ); // clean it
130
					$images[] = array(
131
						'type'  => 'image',
132
						'from'  => 'gallery',
133
						'src'   => $raw_src,
134
						'href'  => $permalink,
135
					);
136
				}
137
			}
138
		}
139
140
		return $images;
141
	}
142
143
	/**
144
	 * Get attachment images for a specified post and return them. Also make sure
145
	 * their dimensions are at or above a required minimum.
146
	 */
147
	static function from_attachment( $post_id, $width = 200, $height = 200 ) {
148
		$images = array();
149
150
		$post = get_post( $post_id );
151
152
		if ( ! empty( $post->post_password ) ) {
153
			return $images;
154
		}
155
156
		$post_images = get_posts( array(
157
			'post_parent' => $post_id,   // Must be children of post
158
			'numberposts' => 5,          // No more than 5
159
			'post_type' => 'attachment', // Must be attachments
160
			'post_mime_type' => 'image', // Must be images
161
		) );
162
163
		if ( ! $post_images ) {
164
			return $images;
165
		}
166
167
		$permalink = get_permalink( $post_id );
168
169
		foreach ( $post_images as $post_image ) {
170
			$meta = wp_get_attachment_metadata( $post_image->ID );
171
			// Must be larger than 200x200
172
			if ( !isset( $meta['width'] ) || $meta['width'] < $width )
173
				continue;
174
			if ( !isset( $meta['height'] ) || $meta['height'] < $height )
175
				continue;
176
177
			$url = wp_get_attachment_url( $post_image->ID );
178
179
			$images[] = array(
180
				'type'       => 'image',
181
				'from'       => 'attachment',
182
				'src'        => $url,
183
				'src_width'  => $meta['width'],
184
				'src_height' => $meta['height'],
185
				'href'       => $permalink,
186
			);
187
		}
188
189
		/*
190
		* We only want to pass back attached images that were actually inserted.
191
		* We can load up all the images found in the HTML source and then
192
		* compare URLs to see if an image is attached AND inserted.
193
		*/
194
		$html_images = self::from_html( $post_id );
195
		$inserted_images = array();
196
197
		foreach( $html_images as $html_image ) {
198
			$src = parse_url( $html_image['src'] );
199
			// strip off any query strings from src
200
			if( ! empty( $src['scheme'] ) && ! empty( $src['host'] ) ) {
201
				$inserted_images[] = $src['scheme'] . '://' . $src['host'] . $src['path'];
202
			} elseif( ! empty( $src['host'] ) ) {
203
				$inserted_images[] = set_url_scheme( 'http://' . $src['host'] . $src['path'] );
204
			} else {
205
				$inserted_images[] = site_url( '/' ) . $src['path'];
206
			}
207
		}
208
		foreach( $images as $i => $image ) {
209
			if ( !in_array( $image['src'], $inserted_images ) )
210
				unset( $images[$i] );
211
		}
212
213
		return $images;
214
	}
215
216
	/**
217
	 * Check if a Featured Image is set for this post, and return it in a similar
218
	 * format to the other images?_from_*() methods.
219
	 * @param  int $post_id The post ID to check
220
	 * @return Array containing details of the Featured Image, or empty array if none.
221
	 */
222
	static function from_thumbnail( $post_id, $width = 200, $height = 200 ) {
223
		$images = array();
224
225
		$post = get_post( $post_id );
226
227
		if ( ! empty( $post->post_password ) ) {
228
			return $images;
229
		}
230
231
		if ( ! function_exists( 'get_post_thumbnail_id' ) ) {
232
			return $images;
233
		}
234
235
		$thumb = get_post_thumbnail_id( $post_id );
236
237
		if ( $thumb ) {
238
			$meta = wp_get_attachment_metadata( $thumb );
239
240
			// Must be larger than requested minimums
241
			if ( !isset( $meta['width'] ) || $meta['width'] < $width )
242
				return $images;
243
			if ( !isset( $meta['height'] ) || $meta['height'] < $height )
244
				return $images;
245
246
			$too_big = ( ( ! empty( $meta['width'] ) && $meta['width'] > 1200 ) || ( ! empty( $meta['height'] ) && $meta['height'] > 1200 ) );
247
248
			if (
249
				$too_big &&
250
				(
251
					( method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'photon' ) ) ||
252
					( defined( 'WPCOM' ) && IS_WPCOM )
253
				)
254
			) {
255
				$img_src = wp_get_attachment_image_src( $thumb, array( 1200, 1200 ) );
256
			} else {
257
				$img_src = wp_get_attachment_image_src( $thumb, 'full' );
258
			}
259
260
			$url = $img_src[0];
261
262
			$images = array( array( // Other methods below all return an array of arrays
263
				'type'       => 'image',
264
				'from'       => 'thumbnail',
265
				'src'        => $url,
266
				'src_width'  => $img_src[1],
267
				'src_height' => $img_src[2],
268
				'href'       => get_permalink( $thumb ),
269
			) );
270
		}
271
272
		if ( empty( $images ) && ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
273
			$meta_thumbnail = get_post_meta( $post_id, '_jetpack_post_thumbnail', true );
274
			if ( ! empty( $meta_thumbnail ) ) {
275
				if ( ! isset( $meta_thumbnail['width'] ) || $meta_thumbnail['width'] < $width ) {
276
					return $images;
277
				}
278
279
				if ( ! isset( $meta_thumbnail['height'] ) || $meta_thumbnail['height'] < $height ) {
280
					return $images;
281
				}
282
283
				$images = array( array( // Other methods below all return an array of arrays
284
					'type'       => 'image',
285
					'from'       => 'thumbnail',
286
					'src'        => $meta_thumbnail['URL'],
287
					'src_width'  => $meta_thumbnail['width'],
288
					'src_height' => $meta_thumbnail['height'],
289
					'href'       => $meta_thumbnail['URL'],
290
				) );
291
			}
292
		}
293
294
		return $images;
295
	}
296
297
	/**
298
	 * Very raw -- just parse the HTML and pull out any/all img tags and return their src
299
	 * @param  mixed $html_or_id The HTML string to parse for images, or a post id
300
	 * @return Array containing images
301
	 */
302
	static function from_html( $html_or_id ) {
303
		$images = array();
304
305
		if ( is_numeric( $html_or_id ) ) {
306
			$post = get_post( $html_or_id );
307
308
			if ( empty( $post ) || ! empty( $post->post_password ) ) {
309
				return $images;
310
			}
311
312
			$html = $post->post_content; // DO NOT apply the_content filters here, it will cause loops
313
		} else {
314
			$html = $html_or_id;
315
		}
316
317
		if ( ! $html ) {
318
			return $images;
319
		}
320
321
		preg_match_all( '!<img.*src=[\'"]([^"]+)[\'"].*/?>!iUs', $html, $matches );
322
		if ( !empty( $matches[1] ) ) {
323
			foreach ( $matches[1] as $match ) {
324
				if ( stristr( $match, '/smilies/' ) )
325
					continue;
326
327
				$images[] = array(
328
					'type'  => 'image',
329
					'from'  => 'html',
330
					'src'   => html_entity_decode( $match ),
331
					'href'  => '', // No link to apply to these. Might potentially parse for that as well, but not for now
332
				);
333
			}
334
		}
335
336
		return $images;
337
	}
338
339
	/**
340
	 * @param    int $post_id The post ID to check
341
	 * @param    int $size
342
	 * @return Array containing details of the image, or empty array if none.
343
	 */
344
	static function from_blavatar( $post_id, $size = 96 ) {
345
346
		$permalink = get_permalink( $post_id );
347
348
		if ( function_exists( 'blavatar_domain' ) && function_exists( 'blavatar_exists' ) && function_exists( 'blavatar_url' ) ) {
349
			$domain = blavatar_domain( $permalink );
350
351
			if ( ! blavatar_exists( $domain ) ) {
352
				return array();
353
			}
354
355
			$url = blavatar_url( $domain, 'img', $size );
356
		} elseif ( function_exists( 'jetpack_has_site_icon' ) && jetpack_has_site_icon() ) {
357
			$url = jetpack_site_icon_url( null, $size, $default = false );
358
		} else {
359
			return array();
360
		}
361
362
		return array( array(
363
			'type'       => 'image',
364
			'from'       => 'blavatar',
365
			'src'        => $url,
366
			'src_width'  => $size,
367
			'src_height' => $size,
368
			'href'       => $permalink,
369
		) );
370
	}
371
372
	/**
373
	 * @param    int $post_id The post ID to check
374
	 * @param    int $size
375
	 * @param string $default The default image to use.
376
	 * @return Array containing details of the image, or empty array if none.
377
	 */
378
	static function from_gravatar( $post_id, $size = 96, $default = false ) {
379
		$post = get_post( $post_id );
380
		$permalink = get_permalink( $post_id );
381
382
		if ( function_exists( 'wpcom_get_avatar_url' ) ) {
383
			$url = wpcom_get_avatar_url( $post->post_author, $size, $default, true );
384
			if ( $url && is_array( $url ) ) {
385
				$url = $url[0];
386
			}
387
		} else {
388
			$has_filter = has_filter( 'pre_option_show_avatars', '__return_true' );
389
			if ( !$has_filter ) {
390
				add_filter( 'pre_option_show_avatars', '__return_true' );
391
			}
392
			$avatar = get_avatar( $post->post_author, $size, $default );
393
			if ( !$has_filter ) {
394
				remove_filter( 'pre_option_show_avatars', '__return_true' );
395
			}
396
397
			if ( !$avatar ) {
398
				return array();
399
			}
400
401
			if ( !preg_match( '/src=["\']([^"\']+)["\']/', $avatar, $matches ) ) {
402
				return array();
403
			}
404
405
			$url = wp_specialchars_decode( $matches[1], ENT_QUOTES );
406
		}
407
408
		return array( array(
409
			'type'       => 'image',
410
			'from'       => 'gravatar',
411
			'src'        => $url,
412
			'src_width'  => $size,
413
			'src_height' => $size,
414
			'href'       => $permalink,
415
		) );
416
	}
417
418
	/**
419
	 * Run through the different methods that we have available to try to find a single good
420
	 * display image for this post.
421
	 * @param  int $post_id
422
	 * @param array $args Other arguments (currently width and height required for images where possible to determine)
423
	 * @return Array containing details of the best image to be used
424
	 */
425
	static function get_image( $post_id, $args = array() ) {
426
		$image = '';
427
428
		/**
429
		 * Fires before we find a single good image for a specific post.
430
		 *
431
		 * @since 2.2.0
432
		 *
433
		 * @param int $post_id Post ID.
434
		 */
435
		do_action( 'jetpack_postimages_pre_get_image', $post_id );
436
		$media = self::get_images( $post_id, $args );
437
438
439
		if ( is_array( $media ) ) {
440
			foreach ( $media as $item ) {
441
				if ( 'image' == $item['type'] ) {
442
					$image = $item;
443
					break;
444
				}
445
			}
446
		}
447
448
		/**
449
		 * Fires after we find a single good image for a specific post.
450
		 *
451
		 * @since 2.2.0
452
		 *
453
		 * @param int $post_id Post ID.
454
		 */
455
		do_action( 'jetpack_postimages_post_get_image', $post_id );
456
457
		return $image;
458
	}
459
460
	/**
461
	 * Get an array containing a collection of possible images for this post, stopping once we hit a method
462
	 * that returns something useful.
463
	 * @param  int $post_id
464
	 * @param  array  $args Optional args, see defaults list for details
465
	 * @return Array containing images that would be good for representing this post
466
	 */
467
	static function get_images( $post_id, $args = array() ) {
468
		// Figure out which image to attach to this post.
469
		$media = false;
470
471
		/**
472
		 * Filters the array of images that would be good for a specific post.
473
		 * This filter is applied before options ($args) filter the original array.
474
		 *
475
		 * @since 2.0.0
476
		 *
477
		 * @param array $media Array of images that would be good for a specific post.
478
		 * @param int $post_id Post ID.
479
		 * @param array $args Array of options to get images.
480
		 */
481
		$media = apply_filters( 'jetpack_images_pre_get_images', $media, $post_id, $args );
482
		if ( $media )
483
			return $media;
484
485
		$defaults = array(
486
			'width'               => 200, // Required minimum width (if possible to determine)
487
			'height'              => 200, // Required minimum height (if possible to determine)
488
489
			'fallback_to_avatars' => false, // Optionally include Blavatar and Gravatar (in that order) in the image stack
490
			'avatar_size'         => 96, // Used for both Grav and Blav
491
			'gravatar_default'    => false, // Default image to use if we end up with no Gravatar
492
493
			'from_thumbnail'      => true, // Use these flags to specify which methods to use to find an image
494
			'from_slideshow'      => true,
495
			'from_gallery'        => true,
496
			'from_attachment'     => true,
497
			'from_html'           => true,
498
499
			'html_content'        => '' // HTML string to pass to from_html()
500
		);
501
		$args = wp_parse_args( $args, $defaults );
0 ignored issues
show
Consider using a different name than the parameter $args. This often makes code more readable.
Loading history...
502
503
		$media = false;
504
		if ( $args['from_thumbnail'] )
505
			$media = self::from_thumbnail( $post_id, $args['width'], $args['height'] );
506 View Code Duplication
		if ( !$media && $args['from_slideshow'] )
507
			$media = self::from_slideshow( $post_id, $args['width'], $args['height'] );
508
		if ( !$media && $args['from_gallery'] )
509
			$media = self::from_gallery( $post_id );
510 View Code Duplication
		if ( !$media && $args['from_attachment'] )
511
			$media = self::from_attachment( $post_id, $args['width'], $args['height'] );
512
		if ( !$media && $args['from_html'] ) {
513
			if ( empty( $args['html_content'] ) )
514
				$media = self::from_html( $post_id ); // Use the post_id, which will load the content
515
			else
516
				$media = self::from_html( $args['html_content'] ); // If html_content is provided, use that
517
		}
518
519
		if ( !$media && $args['fallback_to_avatars'] ) {
520
			$media = self::from_blavatar( $post_id, $args['avatar_size'] );
521
			if ( !$media )
522
				$media = self::from_gravatar( $post_id, $args['avatar_size'], $args['gravatar_default'] );
523
		}
524
525
		/**
526
		 * Filters the array of images that would be good for a specific post.
527
		 * This filter is applied after options ($args) filter the original array.
528
		 *
529
		 * @since 2.0.0
530
		 *
531
		 * @param array $media Array of images that would be good for a specific post.
532
		 * @param int $post_id Post ID.
533
		 * @param array $args Array of options to get images.
534
		 */
535
		return apply_filters( 'jetpack_images_get_images', $media, $post_id, $args );
536
	}
537
538
	/**
539
	 * Takes an image URL and pixel dimensions then returns a URL for the
540
	 * resized and croped image.
541
	 *
542
	 * @param  string $src
543
	 * @param  int    $dimension
544
	 * @return string            Transformed image URL
545
	 */
546
	static function fit_image_url( $src, $width, $height ) {
547
		$width = (int) $width;
0 ignored issues
show
Consider using a different name than the parameter $width. This often makes code more readable.
Loading history...
548
		$height = (int) $height;
0 ignored issues
show
Consider using a different name than the parameter $height. This often makes code more readable.
Loading history...
549
550
		// Umm...
551
		if ( $width < 1 || $height < 1 ) {
552
			return $src;
553
		}
554
555
		// See if we should bypass WordPress.com SaaS resizing
556
		if ( has_filter( 'jetpack_images_fit_image_url_override' ) ) {
557
			/**
558
			 * Filters the image URL used after dimensions are set by Photon.
559
			 *
560
			 * @since 3.3.0
561
			 *
562
			 * @param string $src Image URL.
563
			 * @param int $width Image width.
564
			 * @param int $width Image height.
565
			 */
566
			return apply_filters( 'jetpack_images_fit_image_url_override', $src, $width, $height );
567
		}
568
569
		// If WPCOM hosted image use native transformations
570
		$img_host = parse_url( $src, PHP_URL_HOST );
571
		if ( '.files.wordpress.com' == substr( $img_host, -20 ) ) {
572
			return add_query_arg( array( 'w' => $width, 'h' => $height, 'crop' => 1 ), $src );
573
		}
574
575
		// Use Photon magic
576
		if( function_exists( 'jetpack_photon_url' ) ) {
577
			return jetpack_photon_url( $src, array( 'resize' => "$width,$height" ) );
578
		}
579
580
		// Arg... no way to resize image using WordPress.com infrastructure!
581
		return $src;
582
	}
583
}
584