PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 5.3.2
WP Popular Posts v5.3.2
4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 5.0.0 5.0.1 5.0.2 5.1.0 5.2.0 5.2.1 5.2.2 5.2.3 5.2.4 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.1.0 6.1.1 6.1.2 6.1.3 6.1.4 6.2.0 6.2.1 6.3.0 6.3.1 6.3.2 6.3.3 6.3.4 6.4.0 6.4.1 6.4.2 7.0.0 7.0.1 7.1.0 7.2.0 7.3.0 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.3.7 7.3.8 7.4.0 trunk 2.3.7 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.2 4.0.3 4.0.5 4.0.6
wordpress-popular-posts / src / Image.php
wordpress-popular-posts / src Last commit date
Activation 5 years ago Admin 5 years ago Container 5 years ago Front 5 years ago Rest 5 years ago Widget 5 years ago Bootstrap.php 5 years ago Cache.php 5 years ago Helper.php 5 years ago I18N.php 5 years ago Image.php 5 years ago Output.php 5 years ago Query.php 5 years ago Settings.php 5 years ago Themer.php 5 years ago Translate.php 5 years ago WordPressPopularPosts.php 5 years ago deprecated.php 5 years ago template-tags.php 5 years ago
Image.php
937 lines
1 <?php
2 /**
3 * This class builds/retrieves the thumbnail image of each popular posts.
4 *
5 *
6 * @package WordPressPopularPosts
7 * @author Hector Cabrera <me@cabrerahector.com>
8 */
9
10 namespace WordPressPopularPosts;
11
12 class Image {
13
14 /**
15 * Default thumbnail.
16 *
17 * @since 2.2.0
18 * @var string
19 */
20 private $default_thumbnail = '';
21
22 /**
23 * Plugin uploads directory.
24 *
25 * @since 3.0.4
26 * @var array
27 */
28 private $uploads_dir = [];
29
30 /**
31 * Admin settings.
32 *
33 * @since 5.0.0
34 * @var array
35 */
36 private $admin_options = [];
37
38 /**
39 * Available image sizes.
40 *
41 * @since 5.0.0
42 * @var array
43 */
44 private $available_sizes = [];
45
46 /**
47 * Available image descriptors.
48 *
49 * @since 5.3.0
50 * @var array
51 */
52 private $descriptors = [];
53
54 /**
55 * Construct.
56 *
57 * @since 4.0.0
58 * @param array $admin_options
59 */
60 public function __construct(array $admin_options)
61 {
62 $this->admin_options = $admin_options;
63
64 // Set default thumbnail
65 $this->default_thumbnail = plugins_url() . "/wordpress-popular-posts/assets/images/no_thumb.jpg";
66
67 if ( $this->is_image_url($this->admin_options['tools']['thumbnail']['default']) )
68 $this->default_thumbnail = $this->admin_options['tools']['thumbnail']['default'];
69
70 // Set uploads folder
71 $wp_upload_dir = wp_get_upload_dir();
72 $this->uploads_dir['basedir'] = $wp_upload_dir['basedir'] . "/" . 'wordpress-popular-posts';
73 $this->uploads_dir['baseurl'] = $wp_upload_dir['baseurl'] . "/" . 'wordpress-popular-posts';
74
75 if ( ! is_dir($this->uploads_dir['basedir']) ) {
76 // Couldn't create the folder, store thumbnails in Uploads
77 if ( ! wp_mkdir_p($this->uploads_dir['basedir']) ) {
78 $this->uploads_dir['basedir'] = $wp_upload_dir['basedir'];
79 $this->uploads_dir['baseurl'] = $wp_upload_dir['baseurl'];
80 }
81 }
82
83 // Set descriptors
84 $this->descriptors = [1.5, 2, 2.5, 3];
85 }
86
87 /**
88 * Get WPP's uploads folder.
89 *
90 * @since 4.0.0
91 * @access public
92 * @return array|bool
93 */
94 public function get_plugin_uploads_dir()
95 {
96 if ( is_array($this->uploads_dir) && ! empty($this->uploads_dir) )
97 return $this->uploads_dir;
98 return false;
99 }
100
101 /**
102 * Returns an image.
103 *
104 * @since 5.0.0
105 * @param int $post_id Post ID
106 * @param array $size Image size (width & height)
107 * @param string $source Image source
108 * @param bool $crop Whether to crop the image or not
109 * @param string $build Whether to build the image or get an existing one
110 * @return string
111 */
112 public function get($post_id, $size, $source, $crop = true, $build = 'manual')
113 {
114 // Bail, $post_id is not an integer
115 if ( ! is_numeric($post_id) ) {
116 return '';
117 }
118
119 $alt = '';
120 $classes = ['wpp-thumbnail', 'wpp_' . $source];
121 $filename = $post_id . '-' . $source . '-' . $size[0] . 'x' . $size[1];
122 $cached = $this->exists($filename);
123
124 // We have a thumbnail already, return it
125 if ( $cached ) {
126 $classes[] = 'wpp_cached_thumb';
127
128 /**
129 * Filters CSS classes assigned to the thumbnail
130 *
131 * @since 5.0.0
132 * @param array CSS classes
133 * @param int The post ID
134 * @return array The new CSS classes
135 */
136 $classes = apply_filters(
137 'wpp_thumbnail_class_attribute',
138 $classes,
139 $post_id
140 );
141
142 /**
143 * Filters ALT attribute assigned to the thumbnail
144 *
145 * @since 5.0.0
146 * @param string Original ALT attribute
147 * @param int The post ID
148 * @return string The new ALT attribute
149 */
150 $alt = apply_filters(
151 'wpp_thumbnail_alt_attribute',
152 $this->get_alt_attribute($post_id, $source),
153 $post_id
154 );
155
156 return $this->render(
157 $cached,
158 $size,
159 is_array($classes) ? implode(' ', $classes) : 'wpp-thumbnail wpp_' . $source,
160 is_string($alt) ? $alt : ''
161 );
162 }
163
164 $thumb_url = null;
165
166 // Return image as-is, no need to create a new thumbnail
167 if (
168 ( 'custom_field' == $source && ! $this->admin_options['tools']['thumbnail']['resize'] )
169 || ( 'featured' == $source && 'predefined' == $build )
170 ){
171 // Get custom field image URL
172 if ( 'custom_field' == $source && ! $this->admin_options['tools']['thumbnail']['resize'] ) {
173 $thumb_url = get_post_meta(
174 $post_id,
175 $this->admin_options['tools']['thumbnail']['field'],
176 true
177 );
178
179 if ( ! $thumb_url || ! $this->is_image_url($thumb_url) ) {
180 // Is this an attachment ID instead of an image URL?
181 if ( Helper::is_number($thumb_url) ) {
182 $thumb_url = wp_get_attachment_image_src($thumb_url, 'full');
183 $thumb_url = is_array($thumb_url) ? $thumb_url[0] : null;
184 } else {
185 $thumb_url = null;
186 }
187 }
188 }
189 // Get Post Thumbnail
190 else {
191 if (
192 current_theme_supports('post-thumbnails')
193 && has_post_thumbnail($post_id)
194 ) {
195 // Find corresponding image size
196 $stock_size = null;
197 $images_sizes = $this->get_sizes();
198
199 foreach ( $images_sizes as $name => $attr ) :
200 if (
201 $attr['width'] == $size[0]
202 && $attr['height'] == $size[1]
203 && $attr['crop'] == $crop
204 ) {
205 $stock_size = $name;
206 break;
207 }
208 endforeach;
209
210 // Couldn't find a matching size so let's go with width/height combo instead
211 // (this should never happen but better safe than sorry!)
212 if ( null == $stock_size ) {
213 $stock_size = $size;
214 }
215
216 /**
217 * Filters CSS classes assigned to the thumbnail
218 *
219 * @since 5.0.0
220 * @param array CSS classes
221 * @param int The post ID
222 * @return array The new CSS classes
223 */
224 $classes = apply_filters(
225 'wpp_thumbnail_class_attribute',
226 $classes,
227 $post_id
228 );
229
230 $featured_image = get_the_post_thumbnail(
231 $post_id,
232 $stock_size
233 );
234
235 if ( strpos($featured_image, 'class="') && is_array($classes) && ! empty($classes) )
236 $featured_image = str_replace('class="', 'class="'. esc_attr(implode(' ', $classes)) . ' ', $featured_image);
237
238 if ( $this->admin_options['tools']['thumbnail']['lazyload'] && false == strpos($featured_image, 'loading="lazy"') ) {
239 $featured_image = str_replace('src="', 'loading="lazy" src="', $featured_image);
240 }
241
242 return $featured_image;
243 }
244 }
245 }
246 // Build a new thumbnail and return it
247 else {
248 $file_path = null;
249
250 if ( 'custom_field' == $source && $this->admin_options['tools']['thumbnail']['resize'] ) {
251 $thumb_url = get_post_meta(
252 $post_id,
253 $this->admin_options['tools']['thumbnail']['field'],
254 true
255 );
256
257 if ( ! $thumb_url || ! $this->is_image_url($thumb_url) ) {
258 // Is this an attachment ID instead of an image URL?
259 // If so, try to fetch the image
260 if ( Helper::is_number($thumb_url) ) {
261 $thumb_url = wp_get_attachment_image_src($thumb_url, 'full');
262 $thumb_url = is_array($thumb_url) ? $thumb_url[0] : null;
263 } else {
264 $thumb_url = null;
265 }
266 }
267
268 if ( $thumb_url && $this->is_image_url($thumb_url) ) {
269 $file_path = $this->url_to_path($thumb_url, $post_id);
270 }
271 } else {
272 $file_meta = $this->get_file_meta($post_id, $source);
273
274 if ( is_array($file_meta) && isset($file_meta['path']) ) {
275 $alt = isset($file_meta['alt']) ? $file_meta['alt'] : '';
276 $file_path = $file_meta['path'];
277 }
278 }
279
280 if ( $file_path ) {
281 $extension = pathinfo($file_path, PATHINFO_EXTENSION);
282 $thumb_url = $this->resize(
283 $file_path,
284 $filename . '.' . $extension,
285 $size,
286 $crop
287 );
288 }
289 }
290
291 if ( ! $thumb_url ) {
292 $classes[] = 'wpp_def_no_src';
293 $thumb_url = $this->get_default_url($post_id);
294 }
295
296 /**
297 * Filters CSS classes assigned to the thumbnail
298 *
299 * @since 5.0.0
300 * @param array CSS classes
301 * @param int The post ID
302 * @return array The new CSS classes
303 */
304 $classes = apply_filters(
305 'wpp_thumbnail_class_attribute',
306 $classes,
307 $post_id
308 );
309
310 /**
311 * Filters ALT attribute assigned to the thumbnail
312 *
313 * @since 5.0.0
314 * @param string Original ALT attribute
315 * @param int The post ID
316 * @return string The new ALT attribute
317 */
318 $alt = apply_filters(
319 'wpp_thumbnail_alt_attribute',
320 $this->get_alt_attribute($post_id, $source),
321 $post_id
322 );
323
324 return $this->render(
325 $thumb_url,
326 $size,
327 is_array($classes) ? implode(' ', $classes) : 'wpp-thumbnail wpp_' . $source,
328 is_string($alt) ? $alt : ''
329 );
330 }
331
332 /**
333 * Checks whether a given thumbnail exists.
334 *
335 * @since 5.0.0
336 * @access private
337 * @param string $filename
338 * @return string|bool Full URL to image
339 */
340 private function exists($filename)
341 {
342 // Do we have thumbnail already?
343 $file = $this->resolve(trailingslashit($this->get_plugin_uploads_dir()['basedir']) . $filename);
344
345 if ( $file && is_file($file) ) {
346 $extension = pathinfo($file, PATHINFO_EXTENSION);
347 return trailingslashit($this->get_plugin_uploads_dir()['baseurl']) . $filename . '.' . $extension;
348 }
349
350 return false;
351 }
352
353 /**
354 * Resolves filename.
355 *
356 * @since 5.0.0
357 * @access private
358 * @author Ioan Chiriac
359 * @link https://stackoverflow.com/a/29468093/9131961
360 * @param string $name
361 * @return string|bool Resolved path, or false if not found
362 */
363 private function resolve($name)
364 {
365 $info = pathinfo($name);
366
367 // File already contains an extension, return it
368 if ( isset($info['extension']) && ! empty($info['extension']) ) {
369 return $name;
370 }
371
372 $filename = $info['filename'];
373 $len = strlen($filename);
374
375 // open the folder
376 $dh = opendir($info['dirname']);
377
378 if ( ! $dh ) {
379 return false;
380 }
381
382 // scan each file in the folder
383 while ( ($file = readdir($dh)) !== false ) {
384 if ( strncmp($file, $filename, $len) === 0 ) {
385 if ( strlen($name) > $len ) {
386 // if name contains a directory part
387 $name = substr($name, 0, strlen($name) - $len) . $file;
388 } else {
389 // if the name is at the path root
390 $name = $file;
391 }
392
393 closedir($dh);
394 return $name;
395 }
396 }
397
398 // file not found
399 closedir($dh);
400 return false;
401 }
402
403 /**
404 * Retrieves local path to image.
405 *
406 * @since 5.0.0
407 * @access private
408 * @param string $url
409 * @param integer $post_ID
410 * @return string|boolean Path to image, or false if not found
411 */
412 private function url_to_path($url, $post_ID = null)
413 {
414 if ( $this->is_image_url($url) ) {
415 $attachment_id = $this->get_attachment_id($url);
416
417 // Image is hosted locally
418 if ( $attachment_id ) {
419 return get_attached_file($attachment_id);
420 }
421
422 // Image hosted elsewhere?
423 if ( $post_ID && Helper::is_number($post_ID) )
424 return $this->fetch_external_image($post_ID, $url);
425 }
426
427 return false;
428 }
429
430 /**
431 * Gets image meta.
432 *
433 * @since 5.0.0
434 * @access private
435 * @param int $id Post ID
436 * @param string $source Image source
437 * @return array|bool
438 */
439 private function get_file_meta($id, $source)
440 {
441 // get thumbnail path from the Featured Image
442 if ( "featured" == $source ) {
443 if ( $thumbnail_id = get_post_thumbnail_id($id) ) {
444 // image path
445 return [
446 'path' => get_attached_file($thumbnail_id),
447 'alt' => get_post_meta($thumbnail_id, '_wp_attachment_image_alt', true)
448 ];
449 }
450 }
451 // get thumbnail path from first image attachment
452 elseif ( "first_attachment" == $source ) {
453 $args = [
454 'numberposts' => 1,
455 'order' => 'ASC',
456 'post_parent' => $id,
457 'post_type' => 'attachment',
458 'post_mime_type' => 'image'
459 ];
460 $post_attachments = get_children($args);
461
462 if ( ! empty($post_attachments) ) {
463 $first_img = array_shift($post_attachments);
464
465 return [
466 'path' => get_attached_file($first_img->ID),
467 'alt' => get_post_meta($first_img->ID, '_wp_attachment_image_alt', true)
468 ];
469 }
470 }
471 // get thumbnail path from post content
472 elseif ( "first_image" == $source ) {
473 /** @var wpdb $wpdb */
474 global $wpdb;
475
476 if ( $content = $wpdb->get_var("SELECT post_content FROM {$wpdb->posts} WHERE ID = {$id};") ) {
477 // at least one image has been found
478 if ( preg_match('/<img[^>]+>/i', $content, $img) ) {
479 // get img src attribute from the first image found
480 preg_match('/(src)="([^"]*)"/i', $img[0], $src_attr);
481
482 if ( isset($src_attr[2]) && ! empty($src_attr[2]) ) {
483 // get img alt attribute from the first image found
484 $alt = '';
485 preg_match('/(alt)="([^"]*)"/i', $img[0], $alt_attr);
486
487 if ( isset($alt_attr[2]) && !empty($alt_attr[2]) ) {
488 $alt = $alt_attr[2];
489 }
490
491 // image from Media Library
492 if ( $attachment_id = $this->get_attachment_id($src_attr[2]) ) {
493 return [
494 'path' => get_attached_file($attachment_id),
495 'alt' => $alt
496 ];
497 } // external image?
498 else {
499 return [
500 'path' => $this->fetch_external_image($id, $src_attr[2]),
501 'alt' => $alt
502 ];
503 }
504 }
505 }
506 }
507 }
508
509 return false;
510 }
511
512 /**
513 * Gets image ALT attribute.
514 *
515 * @since 5.0.0
516 * @access private
517 * @param int $id Post ID
518 * @param string $source Image source
519 * @return string
520 */
521 private function get_alt_attribute($id, $source)
522 {
523 $alt = '';
524
525 // get thumbnail path from the Featured Image
526 if ( "featured" == $source ) {
527 if ( $thumbnail_id = get_post_thumbnail_id($id) ) {
528 // image path
529 $alt = get_post_meta($thumbnail_id, '_wp_attachment_image_alt', true);
530 }
531 }
532 // get thumbnail path from first image attachment
533 elseif ( "first_attachment" == $source ) {
534 $args = [
535 'numberposts' => 1,
536 'order' => 'ASC',
537 'post_parent' => $id,
538 'post_type' => 'attachment',
539 'post_mime_type' => 'image'
540 ];
541 $post_attachments = get_children($args);
542
543 if ( ! empty($post_attachments) ) {
544 $first_img = array_shift($post_attachments);
545 $alt = get_post_meta($first_img->ID, '_wp_attachment_image_alt', true);
546 }
547 }
548 // get thumbnail path from post content
549 elseif ( "first_image" == $source ) {
550 /** @var wpdb $wpdb */
551 global $wpdb;
552
553 if ( $content = $wpdb->get_var("SELECT post_content FROM {$wpdb->posts} WHERE ID = {$id};") ) {
554 // at least one image has been found
555 if ( preg_match('/<img[^>]+>/i', $content, $img) ) {
556 // get img alt attribute from the first image found
557 preg_match('/(alt)="([^"]*)"/i', $img[0], $alt_attr);
558
559 if ( isset($alt_attr[2]) && !empty($alt_attr[2]) ) {
560 $alt = $alt_attr[2];
561 }
562 }
563 }
564 }
565
566 return $alt;
567 }
568
569 /**
570 * Get the Attachment ID for a given image URL.
571 *
572 * @since 3.0.0
573 * @access private
574 * @author Frankie Jarrett
575 * @link http://frankiejarrett.com/get-an-attachment-id-by-url-in-wordpress/
576 * @param string $url
577 * @return int|null
578 */
579 private function get_attachment_id($url)
580 {
581 $url = Helper::add_scheme(
582 $url,
583 is_ssl() ? 'https://' : 'http://'
584 );
585
586 // Split the $url into two parts with the wp-content directory as the separator.
587 $parse_url = explode(parse_url(WP_CONTENT_URL, PHP_URL_PATH), $url);
588
589 // Get the host of the current site and the host of the $url, ignoring www.
590 $this_host = str_ireplace('www.', '', parse_url(home_url(), PHP_URL_HOST));
591 $file_host = str_ireplace('www.', '', parse_url($url, PHP_URL_HOST));
592
593 // Return nothing if there aren't any $url parts or if the current host and $url host do not match.
594 if (
595 ! isset($parse_url[1])
596 || empty($parse_url[1])
597 || ($this_host != $file_host)
598 ) {
599 return false;
600 }
601
602 // Now we're going to quickly search the DB for any attachment GUID with a partial path match.
603 // Example: /uploads/2013/05/test-image.jpg
604 global $wpdb;
605
606 if ( ! $attachment = $wpdb->get_col($wpdb->prepare("SELECT ID FROM {$wpdb->prefix}posts WHERE guid RLIKE %s;", $parse_url[1])) ) {
607 // Maybe it's a resized image, so try to get the full one
608 $parse_url[1] = preg_replace('/-[0-9]{1,4}x[0-9]{1,4}\.(jpg|jpeg|png|gif|bmp)$/i', '.$1', $parse_url[1]);
609 $attachment = $wpdb->get_col($wpdb->prepare("SELECT ID FROM {$wpdb->prefix}posts WHERE guid RLIKE %s;", $parse_url[1]));
610 }
611
612 // Returns null if no attachment is found.
613 return isset($attachment[0]) ? $attachment[0] : NULL;
614 }
615
616 /**
617 * Fetchs external images.
618 *
619 * @since 2.3.3
620 * @access private
621 * @param int $id Post ID.
622 * @param string $url Image url.
623 * @return string|bool Image path, or false on failure.
624 */
625 private function fetch_external_image($id, $url)
626 {
627 $full_image_path = trailingslashit($this->get_plugin_uploads_dir()['basedir']) . "{$id}_" . sanitize_file_name(rawurldecode(wp_basename($url)));
628
629 // if the file exists already, return URL and path
630 if ( file_exists($full_image_path) )
631 return $full_image_path;
632
633 $url = Helper::add_scheme(
634 $url,
635 is_ssl() ? 'https://' : 'http://'
636 );
637
638 $accepted_status_codes = [200, 301, 302];
639 $response = wp_remote_head($url, ['timeout' => 5, 'sslverify' => false]);
640
641 if (
642 ! is_wp_error($response)
643 && in_array(wp_remote_retrieve_response_code($response), $accepted_status_codes)
644 ) {
645 require_once(ABSPATH . 'wp-admin/includes/file.php');
646
647 $url = str_replace('https://', 'http://', $url);
648 $tmp = download_url($url);
649
650 // File was downloaded successfully
651 if ( ! is_wp_error($tmp) ) {
652 // Determine image type
653 if ( function_exists('exif_imagetype') ) {
654 $image_type = exif_imagetype($tmp);
655 } else {
656 $image_type = getimagesize($tmp);
657 $image_type = ( isset($image_type[2]) ) ? $image_type[2] : NULL;
658 }
659
660 // Valid image, save it
661 if ( in_array($image_type, [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG]) ) {
662 // move file to Uploads
663 if ( @rename($tmp, $full_image_path) ) {
664 // borrowed from WP - set correct file permissions
665 $stat = stat(dirname($full_image_path));
666 $perms = $stat['mode'] & 0000644;
667 @chmod($full_image_path, $perms);
668
669 return $full_image_path;
670 }
671 }
672
673 // Invalid file, remove it
674 @unlink($tmp);
675 }
676 }
677
678 return false;
679 }
680
681 /**
682 * Creates thumbnails.
683 *
684 * @since 3.0.0
685 * @access private
686 * @param string $path Image path
687 * @param string $filename Image filename
688 * @param array $size Image size
689 * @param bool $crop Whether to crop the image or not
690 * @return string|bool Image URL on success, false on error
691 */
692 private function resize($path, $filename, $size, $crop = true)
693 {
694 $image = wp_get_image_editor($path);
695
696 // valid image, create thumbnails
697 if ( ! is_wp_error($image) ) {
698 $original_size = $image->get_size();
699 $sizes = [
700 '1x' => $size
701 ];
702 $thumbnail = '';
703
704 /**
705 * Hook to enable/disable retina support.
706 * @since 5.3.0
707 */
708 $retina_support = apply_filters('wpp_retina_support', true);
709
710 if ( $retina_support ) {
711 // Calculate thumbnail sizes
712 foreach( $this->descriptors as $descriptor ) {
713 $new_size_width = $descriptor * $size[0];
714 $new_size_height = $descriptor * $size[1];
715
716 if (
717 $new_size_width <= $original_size['width']
718 && $new_size_height <= $original_size['height']
719 ) {
720 $sizes[$descriptor . 'x'] = [$new_size_width, $new_size_height];
721 }
722 }
723 }
724
725 $path_parts = null;
726
727 // Generate thumbnails
728 foreach( $sizes as $d => $s ) {
729 if ( '1x' == $d ) {
730 $thumbnail = $this->generate_thumbnail($path, $filename, $s, $crop);
731
732 // Image could not be generated, let's bail early.
733 if ( ! $thumbnail )
734 break;
735 } else {
736 if ( ! $path_parts )
737 $path_parts = pathinfo($filename);
738
739 $filename_with_descriptor = $path_parts['filename'] . "@{$d}." . $path_parts['extension'];
740 $this->generate_thumbnail($path, $filename_with_descriptor, $s, $crop);
741 }
742 }
743
744 return $thumbnail;
745 }
746
747 return false;
748 }
749
750 /**
751 * Creates image.
752 *
753 * @since 5.3.0
754 * @access private
755 * @param string $path Image path
756 * @param string $filename Image filename
757 * @param array $size Image size
758 * @param bool $crop Whether to crop the image or not
759 * @return string|bool Image URL on success, false on error
760 */
761 private function generate_thumbnail($path, $filename, $size, $crop = true)
762 {
763 $image = wp_get_image_editor($path);
764
765 // valid image, create thumbnail
766 if ( ! is_wp_error($image) ) {
767 /**
768 * Hook to change the image compression quality of WPP's thumbnails.
769 * @since 4.2.1
770 */
771 $quality = apply_filters('wpp_thumbnail_compression_quality', null);
772
773 if ( ! ctype_digit($quality) )
774 $quality = null; // Fallback to core's default
775
776 $image->set_quality($quality);
777
778 $image->resize($size[0], $size[1], $crop);
779 $new_img = $image->save(trailingslashit($this->get_plugin_uploads_dir()['basedir']) . $filename);
780
781 if ( ! is_wp_error($new_img) )
782 return trailingslashit($this->get_plugin_uploads_dir()['baseurl']) . $filename;
783 }
784
785 return false;
786 }
787
788 /**
789 * Generates srcset attribute for this image.
790 *
791 * @since 5.3.0
792 * @param string $src
793 * @return string
794 */
795 private function get_srcset($src)
796 {
797 /**
798 * Hook to enable/disable retina support.
799 * @since 5.3.0
800 */
801 $retina_support = apply_filters('wpp_retina_support', true);
802
803 if ( ! $retina_support )
804 return '';
805
806 $path_parts = pathinfo($src);
807 $srcset = [$src];
808
809 foreach( $this->descriptors as $descriptor ) {
810 $d = "{$descriptor}x";
811 $filename = $path_parts['filename'] . "@{$d}." . $path_parts['extension'];
812
813 if ( @file_exists(trailingslashit($this->get_plugin_uploads_dir()['basedir']) . $filename) ) {
814 $srcset[] = $path_parts['dirname'] . '/' . $filename . ' ' . $d;
815 }
816 }
817
818 return ( count($srcset) > 1 ) ? ' srcset="' . implode(', ', $srcset) . '" ' : '';
819 }
820
821 /**
822 * Render image tag.
823 *
824 * @since 3.0.0
825 * @access public
826 * @param string $src Image URL
827 * @param array $dimension Image's width and height
828 * @param string $class CSS class
829 * @param object $alt Alternative text
830 * @param string $error Error, if the image could not be created
831 * @return string
832 */
833 public function render($src, $size, $class, $alt = '', $error = null)
834 {
835 $img_tag = '';
836
837 if ( $error ) {
838 $img_tag = '<!-- ' . $error . ' --> ';
839 }
840
841 $srcset = $this->get_srcset($src);
842 $src = 'src="' . esc_url(is_ssl() ? str_ireplace("http://", "https://", $src) : $src) . '"' . $srcset;
843 $lazyload = ( $this->admin_options['tools']['thumbnail']['lazyload'] ) ? ' loading="lazy"' : '';
844
845 $img_tag .= '<img ' . $src . ' width="' . $size[0] . '" height="' . $size[1] . '" alt="' . esc_attr($alt) . '" class="' . esc_attr($class) . '"' . $lazyload . ' />';
846
847 return apply_filters('wpp_render_image', $img_tag);
848 }
849
850 /**
851 * Gets list of available thumbnails sizes
852 *
853 * @since 3.2.0
854 * @link http://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
855 * @param string $size
856 * @return array|bool
857 */
858 public function get_sizes($size = '')
859 {
860 if ( ! is_array($this->available_sizes) || empty($this->available_sizes) ) {
861 global $_wp_additional_image_sizes;
862
863 $this->available_sizes = [];
864 $get_intermediate_image_sizes = get_intermediate_image_sizes();
865
866 // Create the full array with sizes and crop info
867 foreach( $get_intermediate_image_sizes as $_size ) {
868 if ( in_array($_size, ['thumbnail', 'medium', 'large']) ) {
869 $this->available_sizes[$_size]['width'] = get_option($_size . '_size_w');
870 $this->available_sizes[$_size]['height'] = get_option($_size . '_size_h');
871 $this->available_sizes[$_size]['crop'] = (bool) get_option($_size . '_crop');
872 } elseif ( isset($_wp_additional_image_sizes[$_size]) ) {
873 $this->available_sizes[$_size] = [
874 'width' => $_wp_additional_image_sizes[$_size]['width'],
875 'height' => $_wp_additional_image_sizes[$_size]['height'],
876 'crop' => $_wp_additional_image_sizes[$_size]['crop']
877 ];
878 }
879 }
880 }
881
882 // Get only 1 size if found
883 if ( $size ) {
884 if ( isset($this->available_sizes[$size]) ) {
885 return $this->available_sizes[$size];
886 }
887 return false;
888 }
889
890 return $this->available_sizes;
891 }
892
893 /**
894 * Returns the URL of the default thumbnail image.
895 *
896 * @since 5.0.0
897 * @param int|null
898 * @return string
899 */
900 public function get_default_url($post_ID = null)
901 {
902 if ( has_filter('wpp_default_thumbnail_url') ) {
903 $default_thumbnail_url = apply_filters('wpp_default_thumbnail_url', $this->default_thumbnail, $post_ID);
904
905 if ( $default_thumbnail_url != $this->default_thumbnail && $this->is_image_url($default_thumbnail_url) )
906 return $default_thumbnail_url;
907 }
908
909 return $this->default_thumbnail;
910 }
911
912 /**
913 * Checks whether an URL points to an actual image.
914 *
915 * @since 5.0.0
916 * @access private
917 * @param string
918 * @return array|bool
919 */
920 private function is_image_url($url)
921 {
922 $path = parse_url($url, PHP_URL_PATH);
923 $encoded_path = array_map('urlencode', explode('/', $path));
924 $parse_url = str_replace($path, implode('/', $encoded_path), $url);
925
926 if ( ! filter_var($parse_url, FILTER_VALIDATE_URL) )
927 return false;
928
929 // sanitize URL, just in case
930 $image_url = esc_url($url);
931 // remove querystring
932 preg_match('/[^\?]+\.(jpg|JPG|jpe|JPE|jpeg|JPEG|gif|GIF|png|PNG)/', $image_url, $matches);
933
934 return ( is_array($matches) && ! empty($matches) ) ? $matches : false;
935 }
936 }
937