PluginProbe
Autoptimize / trunk
Autoptimize vtrunk
3.1.16 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 All 108 releases
← All changes | classes/autoptimizeImages.php +737 -254 2.5.0 → trunk View file →
@@ -21,8 +21,15 @@
21 21 *
22 22 * @var self|null
23 23 */
24 24 protected static $instance = null;
25 +
26 + /**
27 + * lazyload counter.
28 + *
29 + * @var int
30 + */
31 + protected $lazyload_counter = 0;
25 32
26 33 public function __construct( array $options = array() )
27 34 {
28 35 // If options are not provided, fetch them.
@@ -41,9 +48,9 @@
41 48 }
42 49
43 50 public static function fetch_options()
44 51 {
45 - $value = get_option( 'autoptimize_imgopt_settings' );
52 + $value = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings' );
46 53 if ( empty( $value ) ) {
47 54 // Fallback to returning defaults when no stored option exists yet.
48 55 $value = autoptimizeConfig::get_ao_imgopt_default_options();
49 56 }
@@ -48,12 +55,32 @@
48 55 $value = autoptimizeConfig::get_ao_imgopt_default_options();
49 56 }
50 57
51 58 // get service availability and add it to the options-array.
52 - $value['availabilities'] = get_option( 'autoptimize_service_availablity' );
59 + $value['availabilities'] = autoptimizeOptionWrapper::get_option( 'autoptimize_service_availablity' );
53 60
54 - if ( empty( $value['availabilities'] ) ) {
55 - $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
61 + if ( empty( $value['availabilities'] ) || ! is_array( $value['availabilities'] ) ) {
62 + $value['availabilities'] = null;
63 +
64 + if ( true === autoptimizeImages::imgopt_active() ) {
65 + $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
66 + }
67 +
68 + if ( null === $value['availabilities'] ) {
69 + // We can't seem to check service availability, use mock result with imgopt status UP.
70 + $_mock_settings = array(
71 + 'extra_imgopt' => array(
72 + 'status' => 'up',
73 + 'hosts' => array(
74 + '1' => 'https://sp-ao.shortpixel.ai/',
75 + ),
76 + ),
77 + 'critcss' => array(
78 + 'status' => 'up',
79 + ),
80 + );
81 + $value['availabilities'] = $_mock_settings;
82 + }
56 83 }
57 84
58 85 return $value;
59 86 }
@@ -65,9 +92,9 @@
65 92 // and does not use/ request the availablity data (which could slow things down).
66 93 static $imgopt_active = null;
67 94
68 95 if ( null === $imgopt_active ) {
69 - $opts = get_option( 'autoptimize_imgopt_settings', '' );
96 + $opts = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings', '' );
70 97 if ( ! empty( $opts ) && is_array( $opts ) && array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $opts ) && ! empty( $opts['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $opts['autoptimize_imgopt_checkbox_field_1'] ) {
71 98 $imgopt_active = true;
72 99 } else {
73 100 $imgopt_active = false;
@@ -96,12 +123,16 @@
96 123
97 124 public function run()
98 125 {
99 126 if ( is_admin() ) {
100 - add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
127 + if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
128 + add_action( 'network_admin_menu', array( $this, 'imgopt_admin_menu' ) );
129 + } else {
130 + add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
131 + }
101 132 add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_imgopt_tab' ), 9 );
102 133 } else {
103 - $this->run_on_frontend();
134 + add_action( 'wp', array( $this, 'run_on_frontend' ) );
104 135 }
105 136 }
106 137
107 138 public function run_on_frontend() {
@@ -107,8 +138,14 @@
107 138 public function run_on_frontend() {
108 139 if ( ! $this->should_run() ) {
109 140 if ( $this->should_lazyload() ) {
110 141 add_filter(
142 + 'wp_lazy_loading_enabled',
143 + array( $this, 'should_disable_core_lazyload' ),
144 + 10,
145 + 3
146 + );
147 + add_filter(
111 148 'autoptimize_html_after_minify',
112 149 array( $this, 'filter_lazyload_images' ),
113 150 10,
114 151 1
@@ -135,8 +172,9 @@
135 172 $active = true;
136 173 }
137 174
138 175 if ( apply_filters( 'autoptimize_filter_imgopt_do_css', true ) ) {
176 + // fixme: also act on already minified CSS!
139 177 add_filter(
140 178 'autoptimize_filter_base_replace_cdn',
141 179 array( $this, 'filter_optimize_css_images' ),
142 180 10,
@@ -141,8 +179,16 @@
141 179 array( $this, 'filter_optimize_css_images' ),
142 180 10,
143 181 1
144 182 );
183 +
184 + add_filter(
185 + 'autoptimize_html_after_minify',
186 + array( $this, 'filter_optimize_inline_css_images' ),
187 + 10,
188 + 1
189 + );
190 +
145 191 $active = true;
146 192 }
147 193
148 194 if ( $active ) {
@@ -154,16 +200,40 @@
154 200 );
155 201 }
156 202
157 203 if ( $this->should_lazyload() ) {
204 + add_filter(
205 + 'wp_lazy_loading_enabled',
206 + array( $this, 'should_disable_core_lazyload' ),
207 + 10,
208 + 3
209 + );
158 210 add_action(
159 211 'wp_footer',
160 - array( $this, 'add_lazyload_js_footer' )
212 + array( $this, 'add_lazyload_js_footer' ),
213 + 10,
214 + 0
161 215 );
162 216 }
163 217 }
164 218
165 219 /**
220 + * Disables core's native lazyload for images, not for iframes.
221 + *
222 + * @param bool $flag Incoming flag (mostly true).
223 + * @param string $tag Tag (img or iframe).
224 + * @param string $context Full context.
225 + *
226 + * @return bool
227 + */
228 + public function should_disable_core_lazyload( $flag = true, $tag = '', $context = '' ) {
229 + if ( 'img' === $tag ) {
230 + return false;
231 + }
232 + return $flag;
233 + }
234 +
235 + /**
166 236 * Basic checks before we can run.
167 237 *
168 238 * @return bool
169 239 */
@@ -174,9 +244,10 @@
174 244 $not_launch_status = ( 'launch' !== $opts['availabilities']['extra_imgopt']['status'] );
175 245
176 246 $do_cdn = true;
177 247 $_userstatus = $this->get_imgopt_provider_userstatus();
178 - if ( -2 == $_userstatus['Status'] ) {
248 + if ( isset( $_userstatus['Status'] ) && ( -2 == $_userstatus['Status'] || -3 == $_userstatus['Status'] ) ) {
249 + // don't even attempt to put images on CDN if heavily exceeded threshold or if site not reachable.
179 250 $do_cdn = false;
180 251 }
181 252
182 253 if (
@@ -194,13 +265,14 @@
194 265 {
195 266 static $imgopt_host = null;
196 267
197 268 if ( null === $imgopt_host ) {
198 - $imgopt_host = 'https://cdn.shortpixel.ai/';
269 + $imgopt_host = 'https://sp-ao.shortpixel.ai/';
199 270 $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
200 271 if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
201 272 $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
202 273 }
274 + $imgopt_host = apply_filters( 'autoptimize_filter_imgopt_host', $imgopt_host );
203 275 }
204 276
205 277 return $imgopt_host;
206 278 }
@@ -213,9 +285,9 @@
213 285 }
214 286
215 287 public static function get_service_url_suffix()
216 288 {
217 - $suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
289 + $suffix = '/af/ZQXTBY0109483/' . AUTOPTIMIZE_SITE_DOMAIN;
218 290
219 291 return $suffix;
220 292 }
221 293
@@ -300,9 +372,9 @@
300 372
301 373 // get CDN domain once.
302 374 static $cdn_domain = null;
303 375 if ( is_null( $cdn_domain ) ) {
304 - $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
376 + $cdn_url = $this->get_cdn_url();
305 377 if ( ! empty( $cdn_url ) ) {
306 378 $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
307 379 } else {
308 380 $cdn_domain = '';
@@ -324,24 +396,29 @@
324 396 }
325 397
326 398 // Do the work on cache miss only.
327 399 if ( ! isset( $cache[ $in ] ) ) {
328 - // Default to what was given to us.
329 - $result = $in;
330 - if ( autoptimizeUtils::is_protocol_relative( $in ) ) {
331 - $result = $parsed_site_url['scheme'] . ':' . $in;
332 - } elseif ( 0 === strpos( $in, '/' ) ) {
400 + // Default to (the trimmed version of) what was given to us.
401 + $result = trim( $in );
402 +
403 + // Some silly plugins wrap background images in html-encoded quotes, so remove those from the img url.
404 + $result = $this->fix_silly_bgimg_quotes( $result );
405 +
406 + if ( autoptimizeUtils::is_protocol_relative( $result ) ) {
407 + $result = $parsed_site_url['scheme'] . ':' . $result;
408 + } elseif ( 0 === strpos( $result, '/' ) ) {
333 409 // Root-relative...
334 - $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'];
335 - // Add the path for subfolder installs.
336 - if ( isset( $parsed_site_url['path'] ) ) {
337 - $result .= $parsed_site_url['path'];
338 - }
339 - $result .= $in;
340 - } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
341 - $result = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
410 + $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $result;
411 + } elseif ( ! empty( $cdn_domain ) && false === strpos( $this->get_imgopt_host(), $cdn_domain ) && strpos( $result, $cdn_domain ) !== 0 ) {
412 + // remove CDN except if it is the image optimization one.
413 + $result = str_replace( $cdn_domain, $parsed_site_url['host'], $result );
342 414 }
343 415
416 + // filter (default off) to remove QS from image URL's to avoid eating away optimization credits.
417 + if ( apply_filters( 'autoptimize_filter_imgopt_no_querystring', false ) && strpos( $result, '?' ) !== false ) {
418 + $result = strtok( $result, '?' );
419 + }
420 +
344 421 $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
345 422
346 423 // Store in cache.
347 424 $cache[ $in ] = $result;
@@ -353,15 +430,41 @@
353 430 public function filter_optimize_css_images( $in )
354 431 {
355 432 $in = $this->normalize_img_url( $in );
356 433
357 - if ( $this->can_optimize_image( $in ) ) {
434 + if ( $this->can_optimize_image( $in ) && false === strpos( $in, $this->get_imgopt_host() ) ) {
358 435 return $this->build_imgopt_url( $in, '', '' );
359 436 } else {
360 437 return $in;
361 438 }
362 439 }
440 +
441 + public function filter_optimize_inline_css_images( $html ) {
442 + preg_match_all( '#<style[^>]*>([^<]*)</style>#Um', $html, $inline_css_blocks, PREG_SET_ORDER );
443 + foreach ( $inline_css_blocks as $inline_css_block ) {
444 + if ( false !== strpos( $inline_css_block[0], 'background' ) ) {
445 + $inline_css_block_new = $this->replace_background_img_css( $inline_css_block[0] );
446 + if ( $inline_css_block_new !== $inline_css_block[0] ) {
447 + $html = str_replace( $inline_css_block[0], $inline_css_block_new, $html );
448 + }
449 + }
450 + }
451 + return $html;
452 + }
363 453
454 + public static function replace_background_img_css( $css ) {
455 + // fixme; can/ should we cache these?
456 + preg_match_all( '#background[^;}]*url\((.*)\)#Ui', $css, $backgrounds, PREG_SET_ORDER );
457 + if ( is_array( $backgrounds ) && ! empty( $backgrounds ) ) {
458 + foreach ( $backgrounds as $background ) {
459 + if ( autoptimizeImages::can_optimize_image_wrapper( $background[1] ) ) {
460 + $css = str_replace( $background[1], autoptimizeImages::build_imgopt_url_wrapper( $background[1] ), $css );
461 + }
462 + }
463 + }
464 + return $css;
465 + }
466 +
364 467 private function get_imgopt_base_url()
365 468 {
366 469 static $imgopt_base_url = null;
367 470
@@ -368,9 +471,15 @@
368 471 if ( null === $imgopt_base_url ) {
369 472 $imgopt_host = $this->get_imgopt_host();
370 473 $quality = $this->get_img_quality_string();
371 474 $ret_val = apply_filters( 'autoptimize_filter_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
372 - $imgopt_base_url = $imgopt_host . 'client/' . $quality . ',' . $ret_val;
475 + if ( $this->should_ngimg() ) {
476 + $sp_to_string = 'to_auto';
477 + } else {
478 + $sp_to_string = 'to_webp';
479 + }
480 + $sp_to_string = apply_filters( 'autoptimize_filter_imgopt_format', $sp_to_string ); // values: empty (= jpeg), to_webp (smart; webp or fallback), to_avif (avif or fallback) or to_auto (smart avif, webp or fallback).
481 + $imgopt_base_url = $imgopt_host . 'client/' . $sp_to_string . ',' . $quality . ',' . $ret_val;
373 482 $imgopt_base_url = apply_filters( 'autoptimize_filter_imgopt_base_url', $imgopt_base_url );
374 483 }
375 484
376 485 return $imgopt_base_url;
@@ -375,9 +484,14 @@
375 484
376 485 return $imgopt_base_url;
377 486 }
378 487
379 - private function can_optimize_image( $url )
488 + public static function can_optimize_image_wrapper( $url, $tag = '', $testing = false ) {
489 + $self = new self();
490 + return $self->can_optimize_image( $url, $tag = '', $testing = false );
491 + }
492 +
493 + private function can_optimize_image( $url, $tag = '', $testing = false )
380 494 {
381 495 static $cdn_url = null;
382 496 static $nopti_images = null;
383 497
@@ -383,14 +497,17 @@
383 497
384 498 if ( null === $cdn_url ) {
385 499 $cdn_url = apply_filters(
386 500 'autoptimize_filter_base_cdnurl',
387 - get_option( 'autoptimize_cdn_url', '' )
501 + autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' )
388 502 );
389 503 }
390 504
391 - if ( null === $nopti_images ) {
392 - $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', '' );
505 + if ( null === $nopti_images || $testing ) {
506 + if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_text_field_6', $this->options ) ) {
507 + $nopti_images = $this->options['autoptimize_imgopt_text_field_6'];
508 + }
509 + $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', $nopti_images );
393 510 }
394 511
395 512 $site_host = AUTOPTIMIZE_SITE_DOMAIN;
396 513 $url = $this->normalize_img_url( $url );
@@ -395,21 +512,25 @@
395 512 $site_host = AUTOPTIMIZE_SITE_DOMAIN;
396 513 $url = $this->normalize_img_url( $url );
397 514 $url_parsed = parse_url( $url );
398 515
399 - if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
516 + if ( false === is_array( $url_parsed ) ) {
400 517 return false;
518 + } elseif ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
519 + return false;
520 + } elseif ( autoptimizeUtils::is_local_server() ) {
521 + return false;
401 522 } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
402 523 return false;
403 524 } elseif ( strpos( $url, '.php' ) !== false ) {
404 525 return false;
405 - } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
526 + } elseif ( false === array_key_exists( 'path', $url_parsed ) || str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp', '.avif' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
406 527 // fixme: better check against end of string.
407 528 return false;
408 529 } elseif ( ! empty( $nopti_images ) ) {
409 530 $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
410 531 foreach ( $nopti_images_array as $nopti_image ) {
411 - if ( strpos( $url, $nopti_image ) !== false ) {
532 + if ( strpos( $url, $nopti_image ) !== false || ( ( '' !== $tag && strpos( $tag, $nopti_image ) !== false ) ) ) {
412 533 return false;
413 534 }
414 535 }
415 536 }
@@ -415,8 +536,14 @@
415 536 }
416 537 return true;
417 538 }
418 539
540 + // wrapper for reuse in AOPro.
541 + public static function build_imgopt_url_wrapper( $orig_url, $width = 0, $height = 0 ) {
542 + $self = new self();
543 + return $self->build_imgopt_url( $orig_url, $width = 0, $height = 0 );
544 + }
545 +
419 546 private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
420 547 {
421 548 // sanitize width and height.
422 549 if ( strpos( $width, '%' ) !== false ) {
@@ -439,9 +566,15 @@
439 566 if ( $filtered_url !== $orig_url ) {
440 567 return $filtered_url;
441 568 }
442 569
443 - $orig_url = $this->normalize_img_url( $orig_url );
570 + $normalized_url = $this->normalize_img_url( $orig_url );
571 +
572 + // if the URL is ascii we check if we have a real URL with filter_var (which only works on ascii url's) and if not a real URL we return the original one.
573 + if ( apply_filters( 'autoptimize_filter_imgopt_check_normalized_url', true ) && ! preg_match( '/[^\x20-\x7e]/', $normalized_url ) && false === filter_var( $normalized_url, FILTER_VALIDATE_URL ) ) {
574 + return $orig_url;
575 + }
576 +
444 577 $imgopt_base_url = $this->get_imgopt_base_url();
445 578 $imgopt_size = '';
446 579
447 580 if ( $width && 0 !== $width ) {
@@ -451,9 +584,10 @@
451 584 if ( $height && 0 !== $height ) {
452 585 $imgopt_size .= ',h_' . $height;
453 586 }
454 587
455 - $url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
588 + $url = $imgopt_base_url . $imgopt_size . '/' . $normalized_url;
589 + $url = apply_filters( 'autoptimize_filter_imgopt_after_build_imgopt_url', $url );
456 590
457 591 return $url;
458 592 }
459 593
@@ -463,38 +597,65 @@
463 597 }
464 598
465 599 public function replace_img_callback( $matches, $width = 0, $height = 0 )
466 600 {
467 - if ( $this->can_optimize_image( $matches[1] ) ) {
468 - return str_replace( $matches[1], $this->build_imgopt_url( $matches[1], $width, $height ), $matches[0] );
601 + $_normalized_img_url = $this->normalize_img_url( $matches[1] );
602 + if ( $this->can_optimize_image( $matches[1], $matches[0] ) ) {
603 + return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
469 604 } else {
470 605 return $matches[0];
471 606 }
472 607 }
473 608
474 - public function filter_optimize_images( $in )
609 + public function replace_icon_callback( $matches )
475 610 {
611 + if ( array_key_exists( '2', $matches ) ) {
612 + $sizes = explode( 'x', $matches[2] );
613 + $width = $sizes[0];
614 + $height = $sizes[1];
615 + } else {
616 + $width = 180;
617 + $height = 180;
618 + }
619 +
620 + // make sure we're not trying to optimize a *.ico file.
621 + if ( strpos( $matches[1], '.ico' ) === false ) {
622 + return $this->replace_img_callback( $matches, $width, $height );
623 + } else {
624 + return $matches[0];
625 + }
626 + }
627 +
628 + public function filter_optimize_images( $in, $testing = false )
629 + {
476 630 /*
477 631 * potential future functional improvements:
478 632 *
479 - * picture element.
480 633 * filter for critical CSS.
481 634 */
482 635 $to_replace = array();
636 + $to_preload = '';
483 637
484 - // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
485 - if ( $this->should_lazyload() ) {
638 + // hide (no)script tags to avoid replacing (and potentially breaking) images in script tags.
639 + if ( apply_filters( 'autoptimize_filter_imgopt_hide_script', true ) || $this->should_lazyload() ) {
486 640 $in = autoptimizeBase::replace_contents_with_marker_if_exists(
487 - 'NOSCRIPT',
488 - '<noscript',
489 - '#<noscript.*?<\/noscript>#is',
641 + 'SCRIPT',
642 + '<script',
643 + '#<(?:no)?script.*?<\/(?:no)?script>#is',
490 644 $in
491 645 );
492 646 }
493 647
648 + // get img preloads as set in post metabox, exploding ", " instead of "," because LCP preload
649 + // could be a shortpixel URL, which has comma's and results in way too many preloads.
650 + $metabox_preloads = array_filter( array_map( 'trim', explode( ', ', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
651 + $metabox_preloads = apply_filters( 'autoptimize_filter_images_metabox_preloads', $metabox_preloads );
652 +
494 653 // extract img tags.
495 654 if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
496 655 foreach ( $matches[0] as $tag ) {
656 + $tag = apply_filters( 'autoptimize_filter_imgopt_tag_preopt', $tag );
657 +
497 658 $orig_tag = $tag;
498 659 $imgopt_w = '';
499 660 $imgopt_h = '';
500 661
@@ -500,20 +661,22 @@
500 661
501 662 // first do (data-)srcsets.
502 663 if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
503 664 foreach ( $allsrcsets as $srcset ) {
504 - $srcset = $srcset[2];
505 - $srcsets = explode( ',', $srcset );
665 + $srcset = $srcset[2];
666 + $orig_srcset = $srcset;
667 + $srcsets = explode( ',', $srcset );
506 668 foreach ( $srcsets as $indiv_srcset ) {
507 669 $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
508 670 if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
509 671 $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
510 672 }
511 - if ( $this->can_optimize_image( $indiv_srcset_parts[0] ) ) {
673 + if ( $this->can_optimize_image( $indiv_srcset_parts[0], $tag, $testing ) && false === apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
512 674 $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
513 - $tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
675 + $srcset = str_replace( $indiv_srcset_parts[0], $imgopt_url, $srcset );
514 676 }
515 677 }
678 + $tag = str_replace( $orig_srcset, $srcset, $tag );
516 679 }
517 680 }
518 681
519 682 // proceed with img src.
@@ -522,29 +685,34 @@
522 685 $imgopt_w = $_get_size['width'];
523 686 $imgopt_h = $_get_size['height'];
524 687
525 688 // then start replacing images src.
526 - if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
527 - foreach ( $urls as $url ) {
528 - $full_src_orig = $url[0];
529 - $url = $url[1];
530 - if ( $this->can_optimize_image( $url ) ) {
531 - $imgopt_url = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
532 - $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
533 - $tag = str_replace( $full_src_orig, $full_imgopt_src, $tag );
534 - }
689 + $tag = autoptimizeAttributeParser::modify_attributes( $tag, 'img', '/src$/i', function( $name, $url ) use ( $tag, $testing, $imgopt_w, $imgopt_h ) {
690 + // 1. Skip if empty or a data-URI
691 + if ( ! $url || strpos( $url, 'data:' ) === 0 ) {
692 + return $url;
535 693 }
694 +
695 + // 2. Run Autoptimize logic
696 + if ( $this->can_optimize_image( $url, $tag, $testing ) && ! apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
697 + return $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
698 + }
699 +
700 + // 3. No changes needed
701 + return $url;
702 + }, false, true );
703 +
704 + $_url = autoptimizeAttributeParser::get( $tag, 'img', '/src$/i' );
705 + $url = $_url['src'];
706 +
707 + // check if the image needs to be prelaoded.
708 + if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && str_replace( $metabox_preloads, '', $tag ) !== $tag ) {
709 + $to_preload .= $this->create_img_preload_tag( $tag );
536 710 }
537 711
538 712 // do lazyload stuff.
539 - if ( $this->should_lazyload() && str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
540 - $noscript_tag = '<noscript>' . $tag . '</noscript>';
541 - $tag = str_replace( 'srcset=', 'data-srcset=', $tag );
542 -
543 - // add lazyload class.
544 - $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
545 -
546 - // set placeholder.
713 + if ( $this->should_lazyload( $in ) && ! empty( $url ) ) {
714 + // first do lpiq placeholder logic.
547 715 if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
548 716 // if all img src have been replaced during srcset, we have to extract the
549 717 // origin url from the imgopt one to be able to set a lqip placeholder.
550 718 $_url = substr( $url, strpos( $url, '/http' ) + 1 );
@@ -550,27 +718,35 @@
550 718 $_url = substr( $url, strpos( $url, '/http' ) + 1 );
551 719 } else {
552 720 $_url = $url;
553 721 }
554 - if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
555 - $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait,w_' . $imgopt_w . ',h_' . $imgopt_h . '/' . $_url;
556 - } else {
557 - $placeholder = $this->get_default_lazyload_placeholder( $imgopt_w, $imgopt_h );
558 - }
559 - $placeholder = ' src=\'' . apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $placeholder );
560 722
561 - // add min-heigth off by default as it can deform images, can be enabled with filter.
562 - $min_height = '';
563 - if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_addminheight', false ) ) {
564 - $min_height = ' style="min-height:' . $imgopt_h . 'px;"';
723 + $_url = $this->normalize_img_url( $_url );
724 +
725 + $placeholder = '';
726 + if ( $this->can_optimize_image( $_url, $tag ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', false, $_url ) && false === apply_filters( 'autoptimize_filter_imgopt_do_spai', false ) ) {
727 + $lqip_w = '';
728 + $lqip_h = '';
729 + if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
730 + $lqip_w = ',w_' . $imgopt_w;
731 + }
732 + if ( isset( $imgopt_h ) && ! empty( $imgopt_h ) ) {
733 + $lqip_h = ',h_' . $imgopt_h;
734 + }
735 + $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait' . $lqip_w . $lqip_h . '/' . $_url;
565 736 }
737 + // then call add_lazyload-function with lpiq placeholder if set.
738 + $tag = $this->add_lazyload( $tag, $placeholder );
739 + }
566 740
567 - // add noscript & placeholder.
568 - $tag = $noscript_tag . str_replace( ' src=', $min_height . $placeholder . '\' data-src=', $tag );
569 - $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
741 + // add decoding="async" behind filter, not sure if I'll make it default true yet.
742 + if ( true === apply_filters( 'autoptimize_filter_imgopt_add_decoding', true ) && false === strpos( $tag, ' decoding=' ) ) {
743 + $tag = str_replace( '<img ', '<img decoding="async" ', $tag );
570 744 }
571 745
572 - // add tag to array for later replacement.
746 + $tag = apply_filters( 'autoptimize_filter_imgopt_tag_postopt', $tag );
747 +
748 + // and add tag to array for later replacement.
573 749 if ( $tag !== $orig_tag ) {
574 750 $to_replace[ $orig_tag ] = $tag;
575 751 }
576 752 }
@@ -578,38 +754,72 @@
578 754
579 755 // and replace all.
580 756 $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
581 757
582 - // img thumbnails in e.g. woocommerce.
583 - if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_imgopt_datathumbs', true ) ) {
758 + // misc. node attributes that might hold image url's (incl. the previously separate data-thumb).
759 + $extra_attr_with_img = apply_filters( 'autoptimize_filter_imgopt_attr_with_img', array( array( 'div', 'data-thumb'), array( 'div', 'data-background' ), array( 'img', 'data-retina' ) ) );
760 + if ( ! empty( $extra_attr_with_img ) && is_array( $extra_attr_with_img ) ) {
761 + foreach ( $extra_attr_with_img as $candidate ) {
762 + if ( is_array( $candidate ) && strpos( $out, $candidate[1] ) !== false ) {
763 + $_regex = '/\<' . $candidate[0] . '(?:[^>]*)?\s' . $candidate[1] . '=(?:"|\')(.+?)(?:"|\')(?:[^>]*)?>/s';
764 + $out = preg_replace_callback(
765 + $_regex,
766 + array( $this, 'replace_img_callback' ),
767 + $out
768 + );
769 + }
770 + }
771 + }
772 +
773 + // background-image in inline style.
774 + if ( ( strpos( $out, 'background-image:' ) !== false || strpos( $out, 'background:' ) !== false ) && strpos( $out, 'url(' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
584 775 $out = preg_replace_callback(
585 - '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
586 - array( $this, 'replace_data_thumbs' ),
776 + '/style=(?:"|\')[^<>]*?background(?:-image)?:[^;"\'()>]*url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
777 + array( $this, 'replace_img_callback' ),
587 778 $out
588 779 );
589 780 }
590 781
591 - // background-image in inline style.
592 - if ( strpos( $out, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
782 + // act on icon links.
783 + if ( ( strpos( $out, '<link rel="icon"' ) !== false || ( strpos( $out, "<link rel='icon'" ) !== false ) ) && apply_filters( 'autoptimize_filter_imgopt_linkicon', true ) ) {
593 784 $out = preg_replace_callback(
594 - '/style=(?:"|\').*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
595 - array( $this, 'replace_img_callback' ),
785 + '/<link\srel=(?:"|\')(?:apple-touch-)?icon(?:-precomposed)?(?:"|\').*\shref=(?:"|\')(.*)(?:"|\')(?:\ssizes=(?:"|\')(\d*x\d*)(?:"|\'))?\s?\/?>/Um',
786 + array( $this, 'replace_icon_callback' ),
596 787 $out
597 788 );
598 789 }
599 790
600 - // and restore noscript tags if these were hidden for lazyload purposes.
791 + // lazyload picture source tags and bgimage.
601 792 if ( $this->should_lazyload() ) {
793 + $out = $this->process_picture_tag( $out, true, true );
794 + $out = $this->process_bgimage( $out );
795 + } else {
796 + $out = $this->process_picture_tag( $out, true, false );
797 + }
798 +
799 + // restore (no)script tags.
800 + if ( apply_filters( 'autoptimize_filter_imgopt_hide_script', true ) || $this->should_lazyload() ) {
602 801 $out = autoptimizeBase::restore_marked_content(
603 - 'NOSCRIPT',
802 + 'SCRIPT',
604 803 $out
605 804 );
606 805 }
607 806
807 + if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && empty( $to_preload ) && false !== apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
808 + // the preload was not in an img tag, so adding a non-responsive preload instead.
809 + foreach ( $metabox_preloads as $img_preload ) {
810 + $to_preload .= apply_filters( 'autoptimize_filter_imgopt_preload_tag_result', $this->kses_preload_link( '<link fetchpriority="high" rel="preload" href="' . $img_preload . '" as="image">' ) );
811 + }
812 + }
813 +
814 + if ( ! empty( $to_preload ) ) {
815 + $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
816 + }
817 +
608 818 return $out;
609 819 }
610 820
611 - public function get_size_from_tag( $tag ) {
821 + public static function get_size_from_tag( $tag ) {
612 822 // reusable function to extract widht and height from an image tag
613 823 // enforcing a filterable maximum width and height (default 4999X4999).
614 824 $width = '';
615 825 $height = '';
@@ -628,15 +838,19 @@
628 838 // check for and enforce (filterable) max sizes.
629 839 $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
630 840 if ( $width > $_max_width ) {
631 841 $_width = $_max_width;
632 - $height = $_width / $width * $height;
842 + if ( ! empty( $height ) && is_int( $height ) ) {
843 + $height = $_width / $width * $height;
844 + }
633 845 $width = $_width;
634 846 }
635 847 $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
636 848 if ( $height > $_max_height ) {
637 849 $_height = $_max_height;
638 - $width = $_height / $height * $width;
850 + if ( ! empty( $width ) && is_int( $width ) ) {
851 + $width = $_height / $height * $width;
852 + }
639 853 $height = $_height;
640 854 }
641 855
642 856 return array(
@@ -647,22 +861,37 @@
647 861
648 862 /**
649 863 * Lazyload functions
650 864 */
651 - public function should_lazyload() {
652 - if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) ) {
865 + public static function should_lazyload_wrapper( $no_meta = false ) {
866 + // needed in autoptimizeMain.php.
867 + $self = new self();
868 + return $self->should_lazyload( '', $no_meta );
869 + }
870 +
871 + public function should_lazyload( $context = '', $no_meta = false ) {
872 + if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && false === $this->check_nolazy() ) {
653 873 $lazyload_return = true;
654 874 } else {
655 875 $lazyload_return = false;
656 876 }
657 877
878 + // If page/ post check post_meta to see if lazyload is off for page.
879 + if ( false === $no_meta && false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_lazyload' ) ) {
880 + $lazyload_return = false;
881 + }
882 +
883 + $lazyload_return = apply_filters( 'autoptimize_filter_imgopt_should_lazyload', $lazyload_return, $context );
884 +
658 885 return $lazyload_return;
659 886 }
660 887
661 - public static function should_lazyload_wrapper() {
662 - // needed in autoptimizeMain.php.
663 - $self = new self();
664 - return $self->should_lazyload();
888 + public static function check_nolazy() {
889 + if ( array_key_exists( 'ao_nolazy', $_GET ) && '1' === $_GET['ao_nolazy'] ) {
890 + return true;
891 + } else {
892 + return false;
893 + }
665 894 }
666 895
667 896 public function filter_lazyload_images( $in )
668 897 {
@@ -667,63 +896,115 @@
667 896 public function filter_lazyload_images( $in )
668 897 {
669 898 // only used is image optimization is NOT active but lazyload is.
670 899 $to_replace = array();
900 + $to_preload = '';
671 901
672 - // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
902 + // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
673 903 $out = autoptimizeBase::replace_contents_with_marker_if_exists(
674 - 'NOSCRIPT',
675 - '<noscript',
676 - '#<noscript.*?<\/noscript>#is',
904 + 'SCRIPT',
905 + '<script',
906 + '#<(?:no)?script.*?<\/(?:no)?script>#is',
677 907 $in
678 908 );
679 909
680 - // extract img tags and add lazyload attribs.
910 + // get img preloads as set in post metabox.
911 + $metabox_preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
912 +
913 + // extract img tags and add lazyload attribs/ add preloads.
681 914 if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
682 915 foreach ( $matches[0] as $tag ) {
683 - $to_replace[ $tag ] = $this->add_lazyload( $tag );
916 + // check if image needs to be preloaded.
917 + if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && str_replace( $metabox_preloads, '', $tag ) !== $tag ) {
918 + $to_preload .= $this->create_img_preload_tag( $tag );
919 + }
920 +
921 + // and lazyloaded.
922 + if ( $this->should_lazyload( $out ) ) {
923 + $to_replace[ $tag ] = $this->add_lazyload( $tag );
924 + }
684 925 }
685 926 $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
686 927 }
687 928
929 + // and also lazyload picture tag.
930 + $out = $this->process_picture_tag( $out, false, true );
931 +
932 + // and inline style blocks with background-image.
933 + $out = $this->process_bgimage( $out );
934 +
688 935 // restore noscript tags.
689 936 $out = autoptimizeBase::restore_marked_content(
690 - 'NOSCRIPT',
937 + 'SCRIPT',
691 938 $out
692 939 );
693 940
941 + if ( ! empty( $metabox_preloads ) && is_array( $metabox_preloads ) && empty( $to_preload ) && false !== apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
942 + // the preload was not in an img tag, so adding a non-responsive preload instead.
943 + foreach ( $metabox_preloads as $img_preload ) {
944 + $to_preload .= apply_filters( 'autoptimize_filter_imgopt_preload_tag_result', $this->kses_preload_link( '<link fetchpriority="high" rel="preload" href="' . $img_preload . '" as="image">' ) );
945 + }
946 + }
947 +
948 + if ( ! empty( $to_preload ) ) {
949 + $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
950 + }
951 +
694 952 return $out;
695 953 }
696 954
697 - public function add_lazyload( $tag ) {
955 + public function add_lazyload( $tag, $placeholder = '' ) {
698 956 // adds actual lazyload-attributes to an image node.
699 - if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
957 + $this->lazyload_counter++;
958 +
959 + $_lazyload_from_nth = '';
960 + if ( array_key_exists( 'autoptimize_imgopt_number_field_7', $this->options ) ) {
961 + $_lazyload_from_nth = $this->options['autoptimize_imgopt_number_field_7'];
962 + }
963 + $_lazyload_from_nth = apply_filters( 'autoptimize_filter_imgopt_lazyload_from_nth', $_lazyload_from_nth );
964 +
965 + if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag && $this->lazyload_counter >= $_lazyload_from_nth ) {
966 + $tag = $this->maybe_fix_missing_quotes( $tag );
967 +
700 968 // store original tag for use in noscript version.
701 - $noscript_tag = '<noscript>' . $tag . '</noscript>';
969 + $noscript_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>';
702 970
971 + $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
972 +
703 973 // insert lazyload class.
704 - $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
974 + $tag = $this->inject_classes_in_tag( $tag, "$lazyload_class " );
705 975
706 - // get image width & heigth for placeholder fun (and to prevent content reflow).
707 - $_get_size = $this->get_size_from_tag( $tag );
708 - $width = $_get_size['width'];
709 - $height = $_get_size['height'];
710 - if ( false === $width ) {
711 - $widht = 210; // default width for SVG placeholder.
976 + if ( ! $placeholder || empty( $placeholder ) ) {
977 + // get image width & heigth for placeholder fun (and to prevent content reflow).
978 + $_get_size = $this->get_size_from_tag( $tag );
979 + $width = $_get_size['width'];
980 + $height = $_get_size['height'];
981 + if ( false === $width || empty( $width ) ) {
982 + $width = 210; // default width for SVG placeholder.
983 + }
984 + if ( false === $height || empty( $height ) ) {
985 + $height = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
986 + }
987 +
988 + // insert the actual lazyload stuff.
989 + // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
990 + $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
712 991 }
713 - if ( false === $height ) {
714 - $heigth = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
992 +
993 + $tag = str_replace( ' src=', ' data-src=', $tag );
994 + $tag = str_replace( ' srcset=', ' data-srcset=', $tag );
995 + $tag = str_replace( '<img ', '<img src=\'' . $placeholder . '\' ', $tag );
996 +
997 + // move sizes to data-sizes unless filter says no.
998 + if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
999 + $tag = str_replace( ' sizes=', ' data-sizes=', $tag );
715 1000 }
716 1001
717 - // insert the actual lazyload stuff.
718 - // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
719 - $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
720 - $tag = str_replace( ' src=', ' src=\'' . $placeholder . '\' data-src=', $tag );
721 - $tag = str_replace( ' srcset=', ' data-srcset=', $tag );
722 -
723 1002 // add the noscript-tag from earlier.
724 1003 $tag = $noscript_tag . $tag;
725 1004 $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
1005 + } else {
1006 + $tag = apply_filters( 'autoptimize_filter_imgopt_not_lazyloaded_img', $tag );
726 1007 }
727 1008
728 1009 return $tag;
729 1010 }
@@ -728,8 +1009,12 @@
728 1009 return $tag;
729 1010 }
730 1011
731 1012 public function add_lazyload_js_footer() {
1013 + if ( false === autoptimizeMain::should_buffer() || autoptimizeMain::is_amp_markup( '' ) ) {
1014 + return;
1015 + }
1016 +
732 1017 // The JS will by default be excluded form autoptimization but this can be changed with a filter.
733 1018 $noptimize_flag = '';
734 1019 if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
735 1020 $noptimize_flag = ' data-noptimize="1"';
@@ -734,21 +1019,93 @@
734 1019 if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
735 1020 $noptimize_flag = ' data-noptimize="1"';
736 1021 }
737 1022
1023 + $_extra = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings', '' );
1024 + if ( is_array( $_extra ) && array_key_exists( 'autoptimize_extra_checkbox_field_0', $_extra ) && ! empty( $_extra['autoptimize_extra_checkbox_field_0'] ) ) {
1025 + // if "remove query strings" is active in "extra", then let's be consistant and not add one ourselves :-) ?
1026 + $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js', __FILE__ );
1027 + } else {
1028 + $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
1029 + }
1030 +
1031 + $cdn_url = $this->get_cdn_url();
1032 + if ( ! empty( $cdn_url ) ) {
1033 + $cdn_url = rtrim( $cdn_url, '/' );
1034 + $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
1035 + }
1036 +
1037 + $type_js = '';
1038 + if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
1039 + $type_js = ' type="text/javascript"';
1040 + }
1041 +
738 1042 // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
739 - echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<style>.lazyload,.lazyloading{opacity:0;}.lazyloaded{opacity:1;transition:opacity 300ms;}</style><noscript><style>.lazyload{display:none;}</style></noscript>' );
740 - echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
741 - echo '<script async' . $noptimize_flag . ' src=\'' . plugins_url( 'external/js/lazysizes.min.js', __FILE__ ) . '\'></script>';
1043 + echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<noscript><style>.lazyload{display:none;}</style></noscript>' );
1044 + echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
1045 + echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
1046 + }
742 1047
743 - // And add webp detection and loading JS.
744 - if ( $this->should_webp() ) {
745 - $_webp_detect = "function c_webp(A){var n=new Image;n.onload=function(){var e=0<n.width&&0<n.height;A(e)},n.onerror=function(){A(!1)},n.src='data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='}function s_webp(e){window.supportsWebP=e}c_webp(s_webp);";
746 - $_webp_load = "document.addEventListener('lazybeforeunveil',function({target:c}){supportsWebP&&['data-src','data-srcset'].forEach(function(a){attr=c.getAttribute(a),null!==attr&&c.setAttribute(a,attr.replace(/\/client\//,'/client/to_webp,'))})});";
747 - echo apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $noptimize_flag . '>' . $_webp_detect . $_webp_load . '</script>' );
1048 + public function create_img_preload_tag( $tag ) {
1049 + if ( false === apply_filters( 'autoptimize_filter_imgopt_dopreloads', true ) ) {
1050 + return '';
748 1051 }
1052 +
1053 + // clean up; remove tabs/ linebreaks/ spaces.
1054 + $tag = preg_replace( '/\s+/', ' ', $tag );
1055 +
1056 + // remove noscript.
1057 + if ( false !== strpos( $tag, '<noscript' ) ) {
1058 + $tag = preg_replace( '/<noscript.*<\/noscript>/mU', '', $tag );
1059 + }
1060 +
1061 + // rewrite img tag to link preload img.
1062 + $_from = array( '<img ', ' src=', ' sizes=', ' srcset=' );
1063 + $_to = array( '<link fetchpriority="high" rel="preload" as="image" ', ' href=', ' imagesizes=', ' imagesrcset=' );
1064 + $tag = str_replace( $_from, $_to, $tag );
1065 +
1066 + // sanitize output
1067 + $tag = $this->kses_preload_link( $tag );
1068 +
1069 + // and provide filter for late changes.
1070 + $tag = apply_filters( 'autoptimize_filter_imgopt_preload_tag_result', $tag );
1071 +
1072 + return $tag;
749 1073 }
750 1074
1075 + public function kses_preload_link( $_preload ) {
1076 + // using kses, remove all unneeded attributes
1077 + // keeping only those we *know* are OK and/ or needed.
1078 + $allowed_html = array(
1079 + 'link' => array(
1080 + 'rel' => true,
1081 + 'as' => true,
1082 + 'href' => true,
1083 + 'imagesizes' => true,
1084 + 'imagesrcset' => true,
1085 + 'type' => true,
1086 + 'media' => true,
1087 + 'fetchpriority' => true,
1088 + ),
1089 + );
1090 + $_preload = wp_kses( $_preload, $allowed_html );
1091 +
1092 + return $_preload;
1093 + }
1094 +
1095 + public static function get_cdn_url() {
1096 + // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
1097 + static $cdn_url = null;
1098 +
1099 + if ( null === $cdn_url ) {
1100 + $cdn_url = autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' );
1101 + $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $cdn_url );
1102 + $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
1103 + }
1104 +
1105 + return $cdn_url;
1106 + }
1107 +
751 1108 public function get_lazyload_exclusions() {
752 1109 // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
753 1110 static $exclude_lazyload_array = null;
754 1111
@@ -755,9 +1112,9 @@
755 1112 if ( null === $exclude_lazyload_array ) {
756 1113 $options = $this->options;
757 1114
758 1115 // set default exclusions.
759 - $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset' );
1116 + $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset', 'data:image/', 'data-lazyload', 'rev-slidebg', 'loading="eager"', 'fetchpriority="high"' );
760 1117
761 1118 // add from setting.
762 1119 if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
763 1120 $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
@@ -776,9 +1133,9 @@
776 1133 public function inject_classes_in_tag( $tag, $target_class ) {
777 1134 if ( strpos( $tag, 'class=' ) !== false ) {
778 1135 $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
779 1136 } else {
780 - $tag = str_replace( '<img ', '<img class="' . trim( $target_class ) . '" ', $tag );
1137 + $tag = preg_replace( '/(<[a-zA-Z]*)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
781 1138 }
782 1139
783 1140 return $tag;
784 1141 }
@@ -786,42 +1143,130 @@
786 1143 public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
787 1144 return 'data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20' . $imgopt_w . '%20' . $imgopt_h . '%22%3E%3C/svg%3E';
788 1145 }
789 1146
790 - public function should_webp() {
791 - static $webp_return = null;
1147 + public function should_ngimg() {
1148 + static $ngimg_return = null;
792 1149
793 - if ( is_null( $webp_return ) ) {
794 - // webp only works if imgopt and lazyload are also active.
795 - if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && $this->imgopt_active() ) {
796 - $webp_return = true;
1150 + if ( is_null( $ngimg_return ) ) {
1151 + // nextgen img only works if imgopt is active.
1152 + if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && $this->imgopt_active() ) {
1153 + $ngimg_return = true;
797 1154 } else {
798 - $webp_return = false;
1155 + $ngimg_return = false;
799 1156 }
800 1157 }
801 1158
802 - return $webp_return;
1159 + return $ngimg_return;
803 1160 }
804 1161
1162 + public function process_picture_tag( $in, $imgopt = false, $lazy = false ) {
1163 + // check if "<picture" is present and if filter allows us to process <picture>.
1164 + if ( strpos( $in, '<picture' ) === false || apply_filters( 'autoptimize_filter_imgopt_dopicture', true ) === false ) {
1165 + return $in;
1166 + }
1167 +
1168 + $_exclusions = $this->get_lazyload_exclusions();
1169 + $to_replace_pict = array();
1170 +
1171 + // extract and process each picture-node.
1172 + preg_match_all( '#<picture.*</picture>#Usmi', $in, $_pictures, PREG_SET_ORDER );
1173 + foreach ( $_pictures as $_picture ) {
1174 + $_picture = $this->maybe_fix_missing_quotes( $_picture );
1175 + if ( strpos( $_picture[0], '<source ' ) !== false && preg_match_all( '#<source .*srcset=(?:"|\')(?!data)(.*)(?:"|\').*>#Usmi', $_picture[0], $_sources, PREG_SET_ORDER ) !== false ) {
1176 + foreach ( $_sources as $_source ) {
1177 + $_picture_replacement = $_source[0];
1178 +
1179 + // should we optimize the image?
1180 + if ( $imgopt && $this->can_optimize_image( $_source[1], $_picture[0] ) ) {
1181 + $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
1182 + }
1183 + // should we lazy-load?
1184 + if ( $lazy && $this->should_lazyload() && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
1185 + $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
1186 + }
1187 + $to_replace_pict[ $_source[0] ] = $_picture_replacement;
1188 + }
1189 + }
1190 + }
1191 +
1192 + // and return the fully procesed $in.
1193 + $out = str_replace( array_keys( $to_replace_pict ), array_values( $to_replace_pict ), $in );
1194 +
1195 + return $out;
1196 + }
1197 +
1198 + public function process_bgimage( $in ) {
1199 + if ( strpos( $in, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_lazyload_backgroundimages', true ) ) {
1200 + $out = preg_replace_callback(
1201 + '/(<(?:article|aside|body|div|footer|header|p|section|span|table)[^>]*)\sstyle=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
1202 + array( $this, 'lazyload_bgimg_callback' ),
1203 + $in
1204 + );
1205 + return $out;
1206 + }
1207 + return $in;
1208 + }
1209 +
1210 + public function lazyload_bgimg_callback( $matches ) {
1211 + if ( str_ireplace( $this->get_lazyload_exclusions(), '', $matches[0] ) === $matches[0] ) {
1212 + // get placeholder & lazyload class strings.
1213 + $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( 500, 300 ) );
1214 + $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
1215 + // remove quotes from url() to be able to replace in next step.
1216 + $out = str_replace( array( "url('" . $matches[2] . "')", 'url("' . $matches[2] . '")' ), 'url(' . $matches[2] . ')', $matches[0] );
1217 + // replace background-image URL with SVG placeholder.
1218 + $out = str_replace( 'url(' . $matches[2], 'url(' . $placeholder, $out );
1219 + // sanitize bgimg src for quote sillyness.
1220 + $bgimg_src = $this->fix_silly_bgimg_quotes( $matches[2] );
1221 + // add data-bg attribute with real background-image URL for lazyload to pick up.
1222 + $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . $bgimg_src . '"', $out );
1223 + // and finally add lazyload class to tag.
1224 + $out = $this->inject_classes_in_tag( $out, "$lazyload_class " );
1225 + return $out;
1226 + }
1227 + return $matches[0];
1228 + }
1229 +
1230 + public function fix_silly_bgimg_quotes( $tag_in ) {
1231 + // some themes/ pagebuilders wrap backgroundimages in HTML-encoded quotes (or linebreaks) which breaks imgopt/ lazyloading, this removes them.
1232 + return trim( str_replace( array( "\r\n", '"', '&quot;', '&#034;', '&apos;', '&#039;' ), '', $tag_in ) );
1233 + }
1234 +
1235 + public function maybe_fix_missing_quotes( $tag_in ) {
1236 + // W3TC's Minify_HTML class removes quotes around attribute value, this re-adds them for the class and width/height attributes so we can lazyload properly.
1237 + if ( ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) ) {
1238 + $tag_out = autoptimizeAttributeParser::rebuild( $tag_in );
1239 + return $tag_out;
1240 + } else {
1241 + return $tag_in;
1242 + }
1243 + }
1244 +
805 1245 /**
806 1246 * Admin page logic and related functions below.
807 1247 */
808 1248 public function imgopt_admin_menu()
809 1249 {
810 - add_submenu_page(
811 - null,
812 - 'autoptimize_imgopt',
813 - 'autoptimize_imgopt',
814 - 'manage_options',
815 - 'autoptimize_imgopt',
816 - array( $this, 'imgopt_options_page' )
817 - );
1250 + // no acces if multisite and not network admin and no site config allowed.
1251 + if ( autoptimizeConfig::should_show_menu_tabs() ) {
1252 + add_submenu_page(
1253 + '',
1254 + 'autoptimize_imgopt',
1255 + 'autoptimize_imgopt',
1256 + 'manage_options',
1257 + 'autoptimize_imgopt',
1258 + array( $this, 'imgopt_options_page' )
1259 + );
1260 + }
818 1261 register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
819 1262 }
820 1263
821 1264 public function add_imgopt_tab( $in )
822 1265 {
823 - $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
1266 + if ( autoptimizeConfig::should_show_menu_tabs() ) {
1267 + $in = array_merge( $in, array( 'autoptimize_imgopt' => apply_filters( 'autoptimize_filter_imgopt_tab_text', esc_html__( 'Images', 'autoptimize' ) ) ) );
1268 + }
824 1269
825 1270 return $in;
826 1271 }
827 1272
@@ -826,11 +1271,14 @@
826 1271 }
827 1272
828 1273 public function imgopt_options_page()
829 1274 {
1275 + // phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace
1276 + // phpcs:disable Generic.Formatting.DisallowMultipleStatements.SameLine
1277 +
830 1278 // Check querystring for "refreshCacheChecker" and call cachechecker if so.
831 1279 if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
832 - $this->query_img_provider_stats();
1280 + $this->query_img_provider_stats( true );
833 1281 }
834 1282
835 1283 $options = $this->fetch_options();
836 1284 $sp_url_suffix = $this->get_service_url_suffix();
@@ -838,17 +1286,26 @@
838 1286 <style>
839 1287 #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
840 1288 #ao_settings_form .form-table th {font-weight: normal;}
841 1289 #autoptimize_imgopt_descr{font-size: 120%;}
1290 + #ao_settings_form td p.condition-warning {color: orange;}
842 1291 </style>
1292 + <script>document.title = "Autoptimize: <?php esc_html_e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
843 1293 <div class="wrap">
844 - <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
1294 + <h1><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? esc_html_e( 'Autoptimize Pro Settings', 'autoptimize' ) : esc_html_e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
845 1295 <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
1296 + <?php if ( autoptimizeUtils::is_local_server() ) { ?>
1297 + <div class="notice-warning notice"><p>
1298 + <?php
1299 + echo esc_html__( 'The image optimization service does not work on locally hosted sites or when the server is on a private network.', 'autoptimize' );
1300 + ?>
1301 + </p></div>
1302 + <?php } ?>
846 1303 <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
847 1304 <div class="notice-warning notice"><p>
848 1305 <?php
849 1306 // translators: "Autoptimize support forum" will appear in a "a href".
850 - echo sprintf( __( 'The image optimization service is currently down, image optimization will be skipped until further notice. Check the %1$sAutoptimize support forum%2$s for more info.', 'autoptimize' ), '<a href="https://wordpress.org/support/plugin/autoptimize/" target="_blank">', '</a>' );
1307 + echo sprintf( esc_html__( 'The image optimization service is currently down, image optimization will be skipped until further notice. Check the %1$sAutoptimize support forum%2$s for more info.', 'autoptimize' ), '<a href="https://wordpress.org/support/plugin/autoptimize/" target="_blank">', '</a>' );
851 1308 ?>
852 1309 </p></div>
853 1310 <?php } ?>
854 1311
@@ -853,9 +1310,9 @@
853 1310 <?php } ?>
854 1311
855 1312 <?php if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! autoptimizeImages::instance()->launch_ok() ) { ?>
856 1313 <div class="notice-warning notice"><p>
857 - <?php _e( 'The image optimization service is launching, but not yet available for this domain, it should become available in the next couple of days.', 'autoptimize' ); ?>
1314 + <?php esc_html_e( 'The image optimization service is launching, but not yet available for this domain, it should become available in the next couple of days.', 'autoptimize' ); ?>
858 1315 </p></div>
859 1316 <?php } ?>
860 1317
861 1318 <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
@@ -861,24 +1318,24 @@
861 1318 <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
862 1319 <div class="notice-warning notice"><p>
863 1320 <?php
864 1321 // translators: "disable Jetpack's site accelerator for images" will appear in a "a href" linking to the jetpack settings page.
865 - echo sprintf( __( 'Please %1$sdisable Jetpack\'s site accelerator for images%2$s to be able to use Autoptomize\'s advanced image optimization features below.', 'autoptimize' ), '<a href="admin.php?page=jetpack#/settings">', '</a>' );
1322 + echo sprintf( esc_html__( 'Please %1$sdisable Jetpack\'s site accelerator for images%2$s to be able to use Autoptomize\'s advanced image optimization features below.', 'autoptimize' ), '<a href="admin.php?page=jetpack#/settings">', '</a>' );
866 1323 ?>
867 1324 </p></div>
868 1325 <?php } ?>
869 - <form id='ao_settings_form' action='options.php' method='post'>
1326 + <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
870 1327 <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
871 - <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
872 - <span id='autoptimize_imgopt_descr'><?php _e( 'Make your site significantly faster by just ticking a couple of checkboxes to optimize and lazy load your images, WebP support included!', 'autoptimize' ); ?></span>
1328 + <h2><?php esc_html_e( 'Image optimization', 'autoptimize' ); ?></h2>
1329 + <span id='autoptimize_imgopt_descr'><?php echo apply_filters( 'autoptimize_filter_imgopt_intro_copy', esc_html__( 'Make your site significantly faster by simply ticking a few boxes and start serving CDN powered, optimized images in the next-gen WebP image format! No additional plugins or services needed.', 'autoptimize' ) ); ?></span>
873 1330 <table class="form-table">
874 1331 <tr>
875 - <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
1332 + <th scope="row"><?php esc_html_e( 'Image optimization & CDN', 'autoptimize' ); ?></th>
876 1333 <td>
877 - <label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Optimize images on the fly and serve them from Shortpixel\'s global CDN.', 'autoptimize' ); ?></label>
1334 + <label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php echo apply_filters( 'autoptimize_filter_imgopt_main_setting_copy', esc_html__( 'On-the-fly image optimization and fast delivery via the Shortpixel global CDN.', 'autoptimize' ) ); ?></label>
878 1335 <?php
879 1336 // show shortpixel status.
880 - $_notice = autoptimizeImages::instance()->get_status_notice();
1337 + $_notice = autoptimizeImages::instance()->get_imgopt_status_notice();
881 1338 if ( $_notice ) {
882 1339 switch ( $_notice['status'] ) {
883 1340 case 2:
884 1341 $_notice_color = 'green';
@@ -886,38 +1343,44 @@
886 1343 case 1:
887 1344 $_notice_color = 'orange';
888 1345 break;
889 1346 case -1:
890 - $_notice_color = 'red';
891 - break;
892 1347 case -2:
1348 + case -3:
893 1349 $_notice_color = 'red';
894 1350 break;
895 1351 default:
896 1352 $_notice_color = 'green';
897 1353 }
898 - echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
1354 + echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . esc_html__( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
899 1355 } else {
900 1356 // translators: link points to shortpixel.
901 - $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having the images optimized on the fly (also in the "next-gen" WebP image format) by %1$sShortPixel%2$s and then cached and served fast from Shortpixel\'s global CDN.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
1357 + $upsell_msg_1 = '<p>' . sprintf( esc_html__( 'Get more Google love by speeding up your website. Start serving on-the-fly optimized images (also in the "next-gen" %4$sWebP%5$s image format) by %1$sShortPixel%2$s. No additional image optimization plugins are needed: your images are optimized, cached and served from %3$sShortPixel\'s global CDN%2$s.', 'autoptimize' ), '<a href="https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell" target="_blank">', '</a>', '<a href="https://help.shortpixel.com/article/62-where-does-the-cdn-has-pops" target="_blank">', '<strong>', '</strong>' );
902 1358 if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
903 - $upsell_msg_2 = __( 'For a limited time only, this service is offered free for all Autoptimize users, <b>don\'t miss the chance to test it</b> and see how much it could improve your site\'s speed.', 'autoptimize' );
1359 + $upsell_msg_2 = sprintf( esc_html__( 'For a limited time only, this service is offered free for all Autoptimize users, %1$sdon\'t miss the chance to test it%2$s and see how much it could improve your site\'s speed.', 'autoptimize' ), '<strong>', '</strong>' );
904 1360 } else {
905 - // translators: link points to shortpixel.
906 - $upsell_msg_2 = sprintf( __( '%1$sSign-up now%2$s to receive a 1 000 bonus + 50&#37; more image optimization credits regardless of the traffic used. More image optimizations can be purchased starting with $4.99.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
1361 + // translators: 1st link points to autoptimize.com.pro, 2nd to shortpixel.
1362 + $upsell_msg_2 = sprintf( esc_html__( 'For (nearly) %5$sunlimited image optimizations %1$sbuy Autoptimize Pro%2$s%6$s which also includes Critical CSS and extra "booster" options or %3$ssign up at Shortpixel%4$s.', 'autoptimize' ), '<a href="https://autoptimize.com/pro/" target="_blank">', '</a>', '<a href="https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell" target="_blank">', '</a>', '<strong>', '</strong>' );
907 1363 }
908 - echo apply_filters( 'autoptimize_imgopt_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
1364 + echo apply_filters( 'autoptimize_filter_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
909 1365 }
910 1366 // translators: link points to shortpixel FAQ.
911 - $faqcopy = sprintf( __( '<strong>Questions</strong>? Have a look at the %1$sShortPixel FAQ%2$s!', 'autoptimize' ), '<strong><a href="https://shortpixel.helpscoutdocs.com/category/60-shortpixel-ai-cdn" target="_blank">', '</strong></a>' );
1367 + $faqcopy = sprintf( esc_html__( '%3$sQuestions%4$s? Take a look at the %1$sAutoptimize + ShortPixel FAQ%2$s!', 'autoptimize' ), '<strong><a href="https://help.shortpixel.com/category/405-autoptimize" target="_blank">', '</strong></a>', '<strong>', '</strong>' );
1368 + $faqcopy = $faqcopy . ' ' . esc_html__( 'Only works for websites and images that are publicly available.', 'autoptimize' );
912 1369 // translators: links points to shortpixel TOS & Privacy Policy.
913 - $toscopy = sprintf( __( 'Usage of this feature is subject to Shortpixel\'s %1$sTerms of Use%2$s and %3$sPrivacy policy%4$s.', 'autoptimize' ), '<a href="https://shortpixel.com/tos' . $sp_url_suffix . '" target="_blank">', '</a>', '<a href="https://shortpixel.com/pp' . $sp_url_suffix . '" target="_blank">', '</a>' );
914 - echo apply_filters( 'autoptimize_imgopt_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
1370 + $toscopy = sprintf( esc_html__( 'Usage of this feature is subject to Shortpixel\'s %1$sTerms of Use%2$s and %3$sPrivacy policy%4$s.', 'autoptimize' ), '<a href="https://shortpixel.com/tos' . $sp_url_suffix . '" target="_blank">', '</a>', '<a href="https://shortpixel.com/privacy' . $sp_url_suffix . '" target="_blank">', '</a>' );
1371 + echo apply_filters( 'autoptimize_filter_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
915 1372 ?>
916 1373 </td>
917 1374 </tr>
1375 + <tr id='autoptimize_imgopt_optimization_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
1376 + <th scope="row"><?php esc_html_e( 'Optimization exclusions', 'autoptimize' ); ?></th>
1377 + <td>
1378 + <label><input type='text' style='width:80%' id='autoptimize_imgopt_optimization_exclusions' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_6]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_6'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_6'] ); } ?>'><br /><?php esc_html_e( 'Comma-separated list of image classes or filenames that should not be optimized.', 'autoptimize' ); ?></label>
1379 + </td>
1380 + </tr>
918 1381 <tr id='autoptimize_imgopt_quality' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
919 - <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
1382 + <th scope="row"><?php esc_html_e( 'Image Optimization quality', 'autoptimize' ); ?></th>
920 1383 <td>
921 1384 <label>
922 1385 <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
923 1386 <?php
@@ -937,33 +1400,60 @@
937 1400 </label>
938 1401 <p>
939 1402 <?php
940 1403 // translators: link points to shortpixel image test page.
941 - echo apply_filters( 'autoptimize_imgopt_imgopt_quality_copy', sprintf( __( 'You can %1$stest compression levels here%2$s.', 'autoptimize' ), '<a href="https://shortpixel.com/oic' . $sp_url_suffix . '" target="_blank">', '</a>' ) );
1404 + echo apply_filters( 'autoptimize_filter_imgopt_quality_copy', sprintf( esc_html__( 'You can %1$stest compression levels here%2$s.', 'autoptimize' ), '<a href="https://shortpixel.com/online-image-compression' . $sp_url_suffix . '" target="_blank">', '</a>' ) );
942 1405 ?>
943 1406 </p>
944 1407 </td>
945 1408 </tr>
946 - <tr id='autoptimize_imgopt_webp' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
947 - <th scope="row"><?php _e( 'Load WebP in supported browsers?', 'autoptimize' ); ?></th>
1409 + <?php
1410 + if ( apply_filters( 'autoptimize_filter_imgopt_settings_show_avif', false ) || $this->should_ngimg() ) {
1411 + ?>
1412 + <tr id='autoptimize_imgopt_ngimg' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
1413 + <th scope="row">
1414 + <?php esc_html_e( 'Load AVIF in supported browsers?', 'autoptimize' ); ?>
1415 + </th>
1416 + <td>
1417 + <p class="condition-warning">
1418 + <?php
1419 + if ( '3' === autoptimizeImages::instance()->get_img_quality_setting() && $this->should_ngimg() ) {
1420 + _e( 'Your Image optimization quality is set to "lossless", but AVIF files can end up being bigger than jpeg or webp for lossless images, so consider choosing a different quality or disable the AVIF option.', 'autoptimize' );
1421 + }
1422 + ?>
1423 + </p>
1424 + <label><input type='checkbox' id='autoptimize_imgopt_ngimg_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_4'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_4'] ) { echo 'checked="checked"'; } ?> value='1'>
1425 + <?php esc_html_e( 'Automatically serve AVIF image format to any browser that supports it.', 'autoptimize' ); ?> <?php printf( esc_html__( '%1$sImportant caveat%2$s; although it %5$scan result in even smaller image sizes for lossy images%4$s it should be taken into account that %3$s%1$sAVIF can require more resources%2$s to decode/ display%4$s and as such %1$smight be slower%2$s on low-end (mobile) devices.', 'autoptimize' ), '<strong>', '</strong>', '<a href="https://storage.googleapis.com/avif-comparison/decode-timing.html" target="_blank">', '</a>', '<a href="https://tsev.dev/posts/2023-11-10-should-avif-be-the-dominant-image-format/" target="_blank">' ); ?>
1426 + </label>
1427 + </td>
1428 + </tr>
1429 + <?php
1430 + } else {
1431 + ?>
1432 + <input type='hidden' id='autoptimize_imgopt_ngimg_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' value='0'>
1433 + <?php
1434 + }
1435 + ?>
1436 + <tr>
1437 + <th scope="row"><?php esc_html_e( 'Lazy-load images?', 'autoptimize' ); ?></th>
948 1438 <td>
949 - <label><input type='checkbox' id='autoptimize_imgopt_webp_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_4'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Automatically serve "next-gen" WebP image format to any browser that supports it (requires lazy load to be active).', 'autoptimize' ); ?></label>
1439 + <label><input type='checkbox' id='autoptimize_imgopt_lazyload_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_3]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php esc_html_e( 'Image lazy-loading will delay the loading of non-visible images to allow the browser to optimally load all resources for the "above the fold"-page first.', 'autoptimize' ); ?></label>
950 1440 </td>
951 1441 </tr>
952 - <tr>
953 - <th scope="row"><?php _e( 'Lazy-load images?', 'autoptimize' ); ?></th>
1442 + <tr id='autoptimize_imgopt_lazyload_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="autoptimize_lazyload_child hidden"'; } else { echo 'class="autoptimize_lazyload_child"'; } ?>>
1443 + <th scope="row"><?php esc_html_e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
954 1444 <td>
955 - <label><input type='checkbox' id='autoptimize_imgopt_lazyload_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_3]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Image lazy-loading will delay the loading of non-visible images to allow the browser to optimally load all resources for the "above the fold"-page first.', 'autoptimize' ); ?></label>
1445 + <label><input type='text' style='width:80%' id='autoptimize_imgopt_lazyload_exclusions_text' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_5]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_5'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_5'] ); } ?>'><br /><?php esc_html_e( 'Comma-separated list of to be excluded image classes or filenames.', 'autoptimize' ); ?></label>
956 1446 </td>
957 1447 </tr>
958 - <tr id='autoptimize_imgopt_lazyload_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="hidden"'; } ?>>
959 - <th scope="row"><?php _e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
1448 + <tr id='autoptimize_imgopt_lazyload_from_nth_image' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="autoptimize_lazyload_child hidden"'; } else { echo 'class="autoptimize_lazyload_child"'; } ?>>
1449 + <th scope="row"><?php esc_html_e( 'Lazy-load from nth image', 'autoptimize' ); ?></th>
960 1450 <td>
961 - <label><input type='text' style='width:80%' id='autoptimize_imgopt_lazyload_exclusions' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_5]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_5'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_5'] ); } ?>'><br /><?php _e( 'Comma-separated list of to be excluded image classes or filenames.', 'autoptimize' ); ?></label>
1451 + <label><input type='number' min='0' max='50' style='width:80%' id='autoptimize_imgopt_lazyload_from_nth_image_number' name='autoptimize_imgopt_settings[autoptimize_imgopt_number_field_7]' value='<?php if ( ! empty( $options['autoptimize_imgopt_number_field_7'] ) ) { echo esc_attr( $options['autoptimize_imgopt_number_field_7'] ); } else { echo '1'; } ?>'><br /><?php esc_html_e( 'Don\'t lazyload the first X images, \'1\' lazyloads all.', 'autoptimize' ); ?></label>
962 1452 </td>
963 1453 </tr>
964 1454 </table>
965 - <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
1455 + <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php esc_html_e( 'Save Changes', 'autoptimize' ); ?>" /></p>
966 1456 </form>
967 1457 <script>
968 1458 jQuery(document).ready(function() {
969 1459 jQuery("#autoptimize_imgopt_checkbox").change(function() {
@@ -968,26 +1458,21 @@
968 1458 jQuery(document).ready(function() {
969 1459 jQuery("#autoptimize_imgopt_checkbox").change(function() {
970 1460 if (this.checked) {
971 1461 jQuery("#autoptimize_imgopt_quality").show("slow");
972 - jQuery("#autoptimize_imgopt_webp").show("slow");
1462 + jQuery("#autoptimize_imgopt_ngimg").show("slow");
1463 + jQuery("#autoptimize_imgopt_optimization_exclusions").show("slow");
973 1464 } else {
974 1465 jQuery("#autoptimize_imgopt_quality").hide("slow");
975 - jQuery("#autoptimize_imgopt_webp").hide("slow");
1466 + jQuery("#autoptimize_imgopt_ngimg").hide("slow");
1467 + jQuery("#autoptimize_imgopt_optimization_exclusions").hide("slow");
976 1468 }
977 1469 });
978 - jQuery("#autoptimize_imgopt_webp_checkbox").change(function() {
979 - if (this.checked) {
980 - jQuery("#autoptimize_imgopt_lazyload_checkbox")[0].checked = true;
981 - jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
982 - }
983 - });
984 1470 jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
985 1471 if (this.checked) {
986 - jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1472 + jQuery(".autoptimize_lazyload_child").show("slow");
987 1473 } else {
988 - jQuery("#autoptimize_imgopt_lazyload_exclusions").hide("slow");
989 - jQuery("#autoptimize_imgopt_webp_checkbox")[0].checked = false;
1474 + jQuery(".autoptimize_lazyload_child").hide("slow");
990 1475 }
991 1476 });
992 1477 });
993 1478 </script>
@@ -997,38 +1482,54 @@
997 1482 /**
998 1483 * Ïmg opt status as used on dashboard.
999 1484 */
1000 1485 public function get_imgopt_status_notice() {
1001 - if ( $this->imgopt_active() ) {
1002 - $_imgopt_notice = '';
1003 - $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
1004 - $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
1005 - $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
1486 + if ( $this->imgopt_active() && apply_filters( 'autoptimize_filter_imgopt_status_shortpixel', true ) ) {
1487 + $_imgopt_notice = '';
1488 + $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1489 + $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
1490 + $_imgopt_upsell = 'https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell';
1491 + $_imgopt_assoc = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1492 + $_imgopt_unreach = 'https://shortpixel.helpscoutdocs.com/article/148-why-are-my-images-redirected-from-cdn-shortpixel-ai';
1006 1493
1007 1494 if ( is_array( $_stat ) ) {
1008 1495 if ( 1 == $_stat['Status'] ) {
1009 1496 // translators: "add more credits" will appear in a "a href".
1010 - $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota is almost used, make sure you %1$sadd more credits%2$s to avoid slowing down your website.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1497 + $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota is almost used, make sure you %1$sadd more credits%2$s to avoid slowing down your website %4$sor consider using %3$sAutoptimize Pro%2$s which comes with (nearly) unlimited image optimization%5$s but also automated critical CSS and extra booster options.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>', '<a href="https://autoptimize.com/pro/" target="_blank">', '<strong>', '</strong>' );
1011 1498 } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1012 1499 // translators: "add more credits" will appear in a "a href".
1013 - $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota was used, %1$sadd more credits%2$s to keep fast serving optimized images on your site', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1014 - $_imgopt_stats_refresh_url = add_query_arg( array(
1015 - 'page' => 'autoptimize_imgopt',
1016 - 'refreshImgProvStats' => '1',
1017 - ), admin_url( 'options-general.php' ) );
1500 + $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota has been exhausted, %1$sadd more credits%2$s to continue to quickly deliver optimized images on your website %4$sor consider using %3$sAutoptimize Pro%2$s which comes with (nearly) unlimited image optimization%5$s but also automated critical CSS and extra booster options.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>', '<a href="https://autoptimize.com/pro/" target="_blank">', '<strong>', '</strong>' );
1501 + // translators: "associate your domain" will appear in a "a href".
1502 + $_imgopt_notice = $_imgopt_notice . ' ' . sprintf( esc_html__( 'If you have enough CDN quota remaining, then you may need to %1$sassociate your domain%2$s to your Shortpixel account.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $_imgopt_assoc . '" target="_blank">', '</a>' );
1503 + } elseif ( -3 == $_stat['Status'] ) {
1504 + // translators: "check the documentation here" will appear in a "a href".
1505 + $_imgopt_notice = sprintf( esc_html__( 'It seems ShortPixel image optimization is not able to fetch images from your site, %1$scheck the documentation here%2$s for more information', 'autoptimize' ), '<a href="' . $_imgopt_unreach . '" target="_blank">', '</a>' );
1506 + } else {
1507 + $_imgopt_upsell = 'https://misc.optimizingmatters.com/partners/?from=aofree&partner=shortpixelupsell';
1508 + // translators: "log in to check your account" will appear in a "a href".
1509 + $_imgopt_notice = sprintf( esc_html__( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1510 + }
1511 +
1512 + // add info on freshness + refresh link if status is not 2 (good shape).
1513 + if ( 2 != $_stat['Status'] ) {
1514 + $_imgopt_stats_refresh_url = add_query_arg(
1515 + array(
1516 + 'page' => 'autoptimize_imgopt',
1517 + 'refreshImgProvStats' => '1',
1518 + ),
1519 + admin_url( 'options-general.php' )
1520 + );
1018 1521 if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
1019 - $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( get_option( 'time_format' ), $_stat['timestamp'] );
1522 + $_imgopt_stats_last_run = esc_html__( 'based on status at ', 'autoptimize' ) . date_i18n( autoptimizeOptionWrapper::get_option( 'time_format' ), $_stat['timestamp'] );
1020 1523 } else {
1021 - $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1524 + $_imgopt_stats_last_run = esc_html__( 'based on previously fetched data', 'autoptimize' );
1022 1525 }
1023 1526 $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1024 1527 // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1025 - $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1026 - } else {
1027 - $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1028 - // translators: "log in to check your account" will appear in a "a href".
1029 - $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1528 + $_imgopt_notice .= sprintf( esc_html__( 'you can click %1$shere to refresh your quota status%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1030 1529 }
1530 +
1531 + // and make the full notice filterable.
1031 1532 $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1032 1533
1033 1534 return array(
1034 1535 'status' => $_stat['Status'],
@@ -1046,18 +1547,24 @@
1046 1547 }
1047 1548
1048 1549 /**
1049 1550 * Get img provider stats (used to display notice).
1551 + *
1552 + * @param bool $_refresh Should the stats be forcefully refreshed or not.
1050 1553 */
1051 - public function query_img_provider_stats() {
1052 - if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) ) {
1554 + public function query_img_provider_stats( $_refresh = false ) {
1555 + if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) && apply_filters( 'autoptimize_filter_imgopt_status_shortpixel', true ) ) {
1053 1556 $url = '';
1054 - $endpoint = $this->get_imgopt_host() . 'read-domain/';
1557 + $stat_dom = 'https://no-cdn.shortpixel.ai/';
1558 + $endpoint = $stat_dom . 'read-domain/';
1055 1559 $domain = AUTOPTIMIZE_SITE_DOMAIN;
1056 1560
1057 1561 // make sure parse_url result makes sense, keeping $url empty if not.
1058 1562 if ( $domain && ! empty( $domain ) ) {
1059 1563 $url = $endpoint . $domain;
1564 + if ( true === $_refresh ) {
1565 + $url = $url . '/refresh';
1566 + }
1060 1567 }
1061 1568
1062 1569 $url = apply_filters(
1063 1570 'autoptimize_filter_imgopt_stat_url',
@@ -1070,9 +1577,9 @@
1070 1577 $response = wp_remote_get( $url );
1071 1578 if ( ! is_wp_error( $response ) ) {
1072 1579 if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1073 1580 $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1074 - update_option( 'autoptimize_imgopt_provider_stat', $stats );
1581 + autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_provider_stat', $stats );
1075 1582 }
1076 1583 }
1077 1584 }
1078 1585 }
@@ -1094,16 +1601,20 @@
1094 1601 {
1095 1602 static $launch_status = null;
1096 1603
1097 1604 if ( null === $launch_status ) {
1098 - $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
1605 + $avail_imgopt = '';
1606 + if ( is_array( $this->options ) && array_key_exists( 'availabilities', $this->options ) && is_array( $this->options['availabilities'] ) && array_key_exists( 'extra_imgopt', $this->options['availabilities'] ) ) {
1607 + $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
1608 + }
1609 +
1099 1610 $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1100 - $has_launched = get_option( 'autoptimize_imgopt_launched', '' );
1611 + $has_launched = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_launched', '' );
1101 1612 $launch_status = false;
1102 1613 if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1103 1614 $launch_status = true;
1104 1615 if ( ! $has_launched ) {
1105 - update_option( 'autoptimize_imgopt_launched', 'on' );
1616 + autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_launched', 'on' );
1106 1617 }
1107 1618 }
1108 1619 }
1109 1620
@@ -1119,9 +1630,9 @@
1119 1630 public function get_imgopt_provider_userstatus() {
1120 1631 static $_provider_userstatus = null;
1121 1632
1122 1633 if ( is_null( $_provider_userstatus ) ) {
1123 - $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
1634 + $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1124 1635 if ( is_array( $_stat ) ) {
1125 1636 if ( array_key_exists( 'Status', $_stat ) ) {
1126 1637 $_provider_userstatus['Status'] = $_stat['Status'];
1127 1638 } else {
@@ -1133,42 +1644,14 @@
1133 1644 } else {
1134 1645 // if no timestamp then we return "".
1135 1646 $_provider_userstatus['timestamp'] = '';
1136 1647 }
1648 + } else {
1649 + // no provider_stat yet, assume/ return all OK.
1650 + $_provider_userstatus['Status'] = 2;
1651 + $_provider_userstatus['timestamp'] = '';
1137 1652 }
1138 1653 }
1139 1654
1140 1655 return $_provider_userstatus;
1141 - }
1142 -
1143 - public function get_status_notice() {
1144 - if ( $this->imgopt_active() ) {
1145 - $notice = '';
1146 - $stat = $this->get_imgopt_provider_userstatus();
1147 - $upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
1148 - $assoc = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1149 -
1150 - if ( is_array( $stat ) ) {
1151 - if ( 1 == $stat['Status'] ) {
1152 - // translators: "add more credits" will appear in a "a href".
1153 - $notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota is almost used, make sure you %1$sadd more credits%2$s to avoid slowing down your website.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1154 - } elseif ( -1 == $stat['Status'] || -2 == $stat['Status'] ) {
1155 - // translators: "add more credits" will appear in a "a href".
1156 - $notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota was used, %1$sadd more credits%2$s to keep fast serving optimized images on your site.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1157 - // translators: "associate your domain" will appear in a "a href".
1158 - $notice = $notice . ' ' . sprintf( __( 'If you already have enough credits then you may need to %1$sassociate your domain%2$s to your Shortpixel account.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $assoc . '" target="_blank">', '</a>' );
1159 - } else {
1160 - $upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1161 - // translators: "log in to check your account" will appear in a "a href".
1162 - $notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1163 - }
1164 - $notice = apply_filters( 'autoptimize_filter_imgopt_notice', $notice );
1165 -
1166 - return array(
1167 - 'status' => $stat['Status'],
1168 - 'notice' => $notice,
1169 - );
1170 - }
1171 - }
1172 - return false;
1173 1656 }
1174 1657 }