PluginProbe
Autoptimize / 2.4.3
Autoptimize v2.4.3
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 2.7.8 All 107 releases
autoptimize / classes / autoptimizeExtra.php

autoptimizeExtra.php in Autoptimize 2.4.3, at classes/autoptimizeExtra.php

1,020 lines 49.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles autoptimizeExtra frontend features + admin options page
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit;
8 }
9
10 class autoptimizeExtra
11 {
12 /**
13 * Options
14 *
15 * @var array
16 */
17 protected $options = array();
18
19 /**
20 * Creates an instance and calls run().
21 *
22 * @param array $options Optional. Allows overriding options without having to specify them via admin options page.
23 */
24 public function __construct( $options = array() )
25 {
26 if ( empty( $options ) ) {
27 $options = $this->fetch_options();
28 }
29
30 $this->options = $options;
31 }
32
33 public function run()
34 {
35 if ( is_admin() ) {
36 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
37 add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_extra_tab' ) );
38 } else {
39 $this->run_on_frontend();
40 }
41 }
42
43 protected function fetch_options()
44 {
45 $value = get_option( 'autoptimize_extra_settings' );
46 if ( empty( $value ) ) {
47 // Fallback to returning defaults when no stored option exists yet.
48 $value = autoptimizeConfig::get_ao_extra_default_options();
49 }
50
51 // get service availability.
52 $value['availabilities'] = get_option( 'autoptimize_service_availablity' );
53
54 if ( empty( $value['availabilities'] ) ) {
55 $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
56 }
57
58 return $value;
59 }
60
61 public function disable_emojis()
62 {
63 // Removing all actions related to emojis!
64 remove_action( 'admin_print_styles', 'print_emoji_styles' );
65 remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
66 remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
67 remove_action( 'wp_print_styles', 'print_emoji_styles' );
68 remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
69 remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
70 remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
71
72 // Removes TinyMCE emojis.
73 add_filter( 'tiny_mce_plugins', array( $this, 'filter_disable_emojis_tinymce' ) );
74
75 // Removes emoji dns-preftech.
76 add_filter( 'wp_resource_hints', array( $this, 'filter_remove_emoji_dns_prefetch' ), 10, 2 );
77 }
78
79 public function filter_disable_emojis_tinymce( $plugins )
80 {
81 if ( is_array( $plugins ) ) {
82 return array_diff( $plugins, array( 'wpemoji' ) );
83 } else {
84 return array();
85 }
86 }
87
88 public function filter_remove_qs( $src ) {
89 if ( strpos( $src, '?ver=' ) ) {
90 $src = remove_query_arg( 'ver', $src );
91 }
92
93 return $src;
94 }
95
96 public function extra_async_js( $in )
97 {
98 $exclusions = array();
99 if ( ! empty( $in ) ) {
100 $exclusions = array_fill_keys( array_filter( array_map( 'trim', explode( ',', $in ) ) ), '' );
101 }
102
103 $settings = $this->options['autoptimize_extra_text_field_3'];
104 $async = array_fill_keys( array_filter( array_map( 'trim', explode( ',', $settings ) ) ), '' );
105 $attr = apply_filters( 'autoptimize_filter_extra_async', 'async' );
106 foreach ( $async as $k => $v ) {
107 $async[ $k ] = $attr;
108 }
109
110 // Merge exclusions & asyncs in one array and return to AO API.
111 $merged = array_merge( $exclusions, $async );
112
113 return $merged;
114 }
115
116 protected function run_on_frontend()
117 {
118 $options = $this->options;
119
120 // Disable emojis if specified.
121 if ( ! empty( $options['autoptimize_extra_checkbox_field_1'] ) ) {
122 $this->disable_emojis();
123 }
124
125 // Remove version query parameters.
126 if ( ! empty( $options['autoptimize_extra_checkbox_field_0'] ) ) {
127 add_filter( 'script_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
128 add_filter( 'style_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
129 }
130
131 // Making sure is_plugin_active() exists when we need it.
132 if ( ! function_exists( 'is_plugin_active' ) ) {
133 require_once ABSPATH . 'wp-admin/includes/plugin.php';
134 }
135 // Avoiding conflicts of interest when async-javascript plugin is active!
136 $async_js_plugin_active = false;
137 if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
138 $async_js_plugin_active = true;
139 }
140 if ( ! empty( $options['autoptimize_extra_text_field_3'] ) && ! $async_js_plugin_active ) {
141 add_filter( 'autoptimize_filter_js_exclude', array( $this, 'extra_async_js' ), 10, 1 );
142 }
143
144 // Optimize google fonts!
145 if ( ! empty( $options['autoptimize_extra_radio_field_4'] ) && ( '1' !== $options['autoptimize_extra_radio_field_4'] ) ) {
146 add_filter( 'wp_resource_hints', array( $this, 'filter_remove_gfonts_dnsprefetch' ), 10, 2 );
147 add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_optimize_google_fonts' ), 10, 1 );
148 add_filter( 'autoptimize_extra_filter_tobepreconn', array( $this, 'filter_preconnect_google_fonts' ), 10, 1 );
149 }
150
151 // Preconnect!
152 if ( ! empty( $options['autoptimize_extra_text_field_2'] ) || has_filter( 'autoptimize_extra_filter_tobepreconn' ) ) {
153 add_filter( 'wp_resource_hints', array( $this, 'filter_preconnect' ), 10, 2 );
154 }
155
156 // Optimize Images kicks in if;
157 // * the option is activated by user.
158 // * imgopt user stats does not have status -2.
159 // * imgopt status is not "down".
160 // * imgopt status is not "launch" or imgopt_launch_ok() returns be true.
161 $_do_cdn = true;
162 $_userstatus = $this->get_imgopt_provider_userstatus();
163 if ( -2 == $_userstatus['Status'] ) {
164 $_do_cdn = false;
165 }
166
167 if ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && $_do_cdn && 'down' !== $options['availabilities']['extra_imgopt']['status'] && ( 'launch' !== $options['availabilities']['extra_imgopt']['status'] || $this->imgopt_launch_ok() ) ) {
168 if ( apply_filters( 'autoptimize_filter_extra_imgopt_do', true ) ) {
169 add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_optimize_images' ), 10, 1 );
170 $_imgopt_active = true;
171 }
172 if ( apply_filters( 'autoptimize_filter_extra_imgopt_do_css', true ) ) {
173 add_filter( 'autoptimize_filter_base_replace_cdn', array( $this, 'filter_optimize_css_images' ), 10, 1 );
174 $_imgopt_active = true;
175 }
176 if ( $_imgopt_active ) {
177 add_filter( 'autoptimize_extra_filter_tobepreconn', array( $this, 'filter_preconnect_imgopt_url' ), 10, 1 );
178 }
179 }
180 }
181
182 public function filter_remove_emoji_dns_prefetch( $urls, $relation_type )
183 {
184 $emoji_svg_url = apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/' );
185
186 return $this->filter_remove_dns_prefetch( $urls, $relation_type, $emoji_svg_url );
187 }
188
189 public function filter_remove_gfonts_dnsprefetch( $urls, $relation_type )
190 {
191 return $this->filter_remove_dns_prefetch( $urls, $relation_type, 'fonts.googleapis.com' );
192 }
193
194 public function filter_remove_dns_prefetch( $urls, $relation_type, $url_to_remove )
195 {
196 if ( 'dns-prefetch' === $relation_type ) {
197 $cnt = 0;
198 foreach ( $urls as $url ) {
199 if ( false !== strpos( $url, $url_to_remove ) ) {
200 unset( $urls[ $cnt ] );
201 }
202 $cnt++;
203 }
204 }
205
206 return $urls;
207 }
208
209 public function filter_optimize_google_fonts( $in )
210 {
211 // Extract fonts, partly based on wp rocket's extraction code.
212 $markup = preg_replace( '/<!--(.*)-->/Uis', '', $in );
213 preg_match_all( '#<link(?:\s+(?:(?!href\s*=\s*)[^>])+)?(?:\s+href\s*=\s*([\'"])((?:https?:)?\/\/fonts\.googleapis\.com\/css(?:(?!\1).)+)\1)(?:\s+[^>]*)?>#iU', $markup, $matches );
214
215 $fonts_collection = array();
216 if ( ! $matches[2] ) {
217 return $in;
218 }
219
220 // Store them in $fonts array.
221 $i = 0;
222 foreach ( $matches[2] as $font ) {
223 if ( ! preg_match( '/rel=["\']dns-prefetch["\']/', $matches[0][ $i ] ) ) {
224 // Get fonts name.
225 $font = str_replace( array( '%7C', '%7c' ), '|', $font );
226 $font = explode( 'family=', $font );
227 $font = ( isset( $font[1] ) ) ? explode( '&', $font[1] ) : array();
228 // Add font to $fonts[$i] but make sure not to pollute with an empty family!
229 $_thisfont = array_values( array_filter( explode( '|', reset( $font ) ) ) );
230 if ( ! empty( $_thisfont ) ) {
231 $fonts_collection[ $i ]['fonts'] = $_thisfont;
232 // And add subset if any!
233 $subset = ( is_array( $font ) ) ? end( $font ) : '';
234 if ( false !== strpos( $subset, 'subset=' ) ) {
235 $subset = str_replace( array( '%2C', '%2c' ), ',', $subset );
236 $subset = explode( 'subset=', $subset );
237 $fonts_collection[ $i ]['subsets'] = explode( ',', $subset[1] );
238 }
239 }
240 // And remove Google Fonts.
241 $in = str_replace( $matches[0][ $i ], '', $in );
242 }
243 $i++;
244 }
245
246 $options = $this->options;
247 $fonts_markup = '';
248 if ( '2' === $options['autoptimize_extra_radio_field_4'] ) {
249 // Remove Google Fonts.
250 unset( $fonts_collection );
251 return $in;
252 } elseif ( '3' === $options['autoptimize_extra_radio_field_4'] || '5' === $options['autoptimize_extra_radio_field_4'] ) {
253 // Aggregate & link!
254 $fonts_string = '';
255 $subset_string = '';
256 foreach ( $fonts_collection as $font ) {
257 $fonts_string .= '|' . trim( implode( '|', $font['fonts'] ), '|' );
258 if ( ! empty( $font['subsets'] ) ) {
259 $subset_string .= ',' . trim( implode( ',', $font['subsets'] ), ',' );
260 }
261 }
262
263 if ( ! empty( $subset_string ) ) {
264 $subset_string = str_replace( ',', '%2C', ltrim( $subset_string, ',' ) );
265 $fonts_string = $fonts_string . '&#038;subset=' . $subset_string;
266 }
267
268 $fonts_string = str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) );
269
270 if ( ! empty( $fonts_string ) ) {
271 if ( '5' === $options['autoptimize_extra_radio_field_4'] ) {
272 $rel_string = 'rel="preload" as="style" onload="' . autoptimizeConfig::get_ao_css_preload_onload() . '"';
273 } else {
274 $rel_string = 'rel="stylesheet"';
275 }
276 $fonts_markup = '<link ' . $rel_string . ' id="ao_optimized_gfonts" href="https://fonts.googleapis.com/css?family=' . $fonts_string . '" />';
277 }
278 } elseif ( '4' === $options['autoptimize_extra_radio_field_4'] ) {
279 // Aggregate & load async (webfont.js impl.)!
280 $fonts_array = array();
281 foreach ( $fonts_collection as $_fonts ) {
282 if ( ! empty( $_fonts['subsets'] ) ) {
283 $_subset = implode( ',', $_fonts['subsets'] );
284 foreach ( $_fonts['fonts'] as $key => $_one_font ) {
285 $_one_font = $_one_font . ':' . $_subset;
286 $_fonts['fonts'][ $key ] = $_one_font;
287 }
288 }
289 $fonts_array = array_merge( $fonts_array, $_fonts['fonts'] );
290 }
291
292 $fonts_array = array_map( 'urldecode', $fonts_array );
293 $fonts_markup = '<script data-cfasync="false" id="ao_optimized_gfonts_config" type="text/javascript">WebFontConfig={google:{families:' . wp_json_encode( $fonts_array ) . ' },classes:false, events:false, timeout:1500};</script>';
294 $fonts_library_markup = '<script data-cfasync="false" id="ao_optimized_gfonts_webfontloader" type="text/javascript">(function() {var wf = document.createElement(\'script\');wf.src=\'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js\';wf.type=\'text/javascript\';wf.async=\'true\';var s=document.getElementsByTagName(\'script\')[0];s.parentNode.insertBefore(wf, s);})();</script>';
295 $in = substr_replace( $in, $fonts_library_markup . '</head>', strpos( $in, '</head>' ), strlen( '</head>' ) );
296 }
297
298 // Replace back in markup.
299 $out = substr_replace( $in, $fonts_markup . '<link', strpos( $in, '<link' ), strlen( '<link' ) );
300 unset( $fonts_collection );
301
302 // and insert preload polyfill if "link preload" and if the polyfill isn't there yet (courtesy of inline&defer).
303 $preload_polyfill = autoptimizeConfig::get_ao_css_preload_polyfill();
304 if ( '5' === $options['autoptimize_extra_radio_field_4'] && strpos( $out, $preload_polyfill ) === false ) {
305 $out = str_replace( '</body>', $preload_polyfill . '</body>', $out );
306 }
307 return $out;
308 }
309
310 public function filter_preconnect( $hints, $relation_type )
311 {
312 $options = $this->options;
313
314 // Get settings and store in array.
315 $preconns = array_filter( array_map( 'trim', explode( ',', $options['autoptimize_extra_text_field_2'] ) ) );
316 $preconns = apply_filters( 'autoptimize_extra_filter_tobepreconn', $preconns );
317
318 // Walk array, extract domain and add to new array with crossorigin attribute.
319 foreach ( $preconns as $preconn ) {
320 $parsed = parse_url( $preconn );
321
322 if ( is_array( $parsed ) && empty( $parsed['scheme'] ) ) {
323 $domain = '//' . $parsed['host'];
324 } elseif ( is_array( $parsed ) ) {
325 $domain = $parsed['scheme'] . '://' . $parsed['host'];
326 }
327
328 if ( ! empty( $domain ) ) {
329 $hint = array( 'href' => $domain );
330 // Fonts don't get preconnected unless crossorigin flag is set, non-fonts don't get preconnected if origin flag is set
331 // so hardcode fonts.gstatic.com to come with crossorigin and have filter to add other domains if needed.
332 $crossorigins = apply_filters( 'autoptimize_extra_filter_preconn_crossorigin', array( 'https://fonts.gstatic.com' ) );
333 if ( in_array( $domain, $crossorigins ) ) {
334 $hint['crossorigin'] = 'anonymous';
335 }
336 $new_hints[] = $hint;
337 }
338 }
339
340 // Merge in WP's preconnect hints.
341 if ( 'preconnect' === $relation_type && ! empty( $new_hints ) ) {
342 $hints = array_merge( $hints, $new_hints );
343 }
344
345 return $hints;
346 }
347
348 public function filter_preconnect_google_fonts( $in )
349 {
350 if ( '2' !== $this->options['autoptimize_extra_radio_field_4'] ) {
351 // Preconnect to fonts.gstatic.com unless we remove gfonts.
352 $in[] = 'https://fonts.gstatic.com';
353 }
354
355 if ( '4' === $this->options['autoptimize_extra_radio_field_4'] ) {
356 // Preconnect even more hosts for webfont.js!
357 $in[] = 'https://ajax.googleapis.com';
358 $in[] = 'https://fonts.googleapis.com';
359 }
360
361 return $in;
362 }
363
364 public function filter_optimize_images( $in )
365 {
366 /*
367 * potential future functional improvements:
368 *
369 * picture element.
370 * filter for critical CSS.
371 */
372
373 $imgopt_base_url = $this->get_imgopt_base_url();
374 $to_replace = array();
375
376 // extract img tags.
377 if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
378 foreach ( $matches[0] as $tag ) {
379 $orig_tag = $tag;
380 $imgopt_w = '';
381 $imgopt_h = '';
382
383 // first do (data-)srcsets.
384 if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
385 foreach ( $allsrcsets as $srcset ) {
386 $srcset = $srcset[2];
387 $srcsets = explode( ',', $srcset );
388 foreach ( $srcsets as $indiv_srcset ) {
389 $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
390 if ( $indiv_srcset_parts[1] && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
391 $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
392 }
393 if ( $this->can_optimize_image( $indiv_srcset_parts[0] ) ) {
394 $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
395 $tag = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
396 $to_replace[ $orig_tag ] = $tag;
397 }
398 }
399 }
400 }
401
402 // proceed with img src.
403 // first reset and then get width and height and add to $imgopt_size.
404 $imgopt_w = '';
405 $imgopt_h = '';
406 if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $width ) ) {
407 $imgopt_w = $width[2];
408 }
409 if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $height ) ) {
410 $imgopt_h = $height[2];
411 }
412
413 // then start replacing images src.
414 if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
415 foreach ( $urls as $url ) {
416 $full_src_orig = $url[0];
417 $url = $url[1];
418 if ( $this->can_optimize_image( $url ) ) {
419 $imgopt_url = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
420 $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
421 $tag = str_replace( $full_src_orig, $full_imgopt_src, $tag );
422 $to_replace[ $orig_tag ] = $tag;
423 }
424 }
425 }
426 }
427 }
428 $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
429
430 // img thumbnails in e.g. woocommerce.
431 if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_extra_imgopt_datathumbs', true ) ) {
432 $out = preg_replace_callback(
433 '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
434 array( $this, 'replace_data_thumbs' ),
435 $out
436 );
437 }
438
439 return $out;
440 }
441
442 public function filter_optimize_css_images( $in )
443 {
444 $imgopt_base_url = $this->get_imgopt_base_url();
445 $in = $this->normalize_img_urls( $in );
446
447 if ( $this->can_optimize_image( $in ) ) {
448 return $this->build_imgopt_url( $in, '', '' );
449 } else {
450 return $in;
451 }
452 }
453
454 private function get_imgopt_base_url()
455 {
456 static $imgopt_base_url = null;
457
458 if ( is_null( $imgopt_base_url ) ) {
459 $imgopt_host = $this->get_imgopt_host();
460 $quality = $this->get_img_quality_string();
461 $ret_val = apply_filters( 'autoptimize_filter_extra_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
462 $imgopt_base_url = $imgopt_host . 'client/' . $quality . ',' . $ret_val;
463 $imgopt_base_url = apply_filters( 'autoptimize_filter_extra_imgopt_base_url', $imgopt_base_url );
464 }
465
466 return $imgopt_base_url;
467 }
468
469 private function can_optimize_image( $url )
470 {
471 static $cdn_url = null;
472 static $nopti_images = null;
473
474 if ( is_null( $cdn_url ) ) {
475 $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
476 }
477
478 if ( is_null( $nopti_images ) ) {
479 $nopti_images = apply_filters( 'autoptimize_filter_extra_imgopt_noptimize', '' );
480 }
481
482 $imgopt_base_url = $this->get_imgopt_base_url();
483 $site_host = AUTOPTIMIZE_SITE_DOMAIN;
484 $url_parsed = parse_url( $url );
485
486 if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
487 return false;
488 } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
489 return false;
490 } elseif ( strpos( $url, '.php' ) !== false ) {
491 return false;
492 } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
493 // fixme: better check against end of string.
494 return false;
495 } elseif ( ! empty( $nopti_images ) ) {
496 $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
497 foreach ( $nopti_images_array as $nopti_image ) {
498 if ( strpos( $url, $nopti_image ) !== false ) {
499 return false;
500 }
501 }
502 }
503 return true;
504 }
505
506 private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
507 {
508 // sanitize width and height.
509 if ( strpos( $width, '%' ) !== false ) {
510 $width = 0;
511 }
512 if ( strpos( $height, '%' ) !== false ) {
513 $height = 0;
514 }
515 $width = (int) $width;
516 $height = (int) $height;
517
518 $filtered_url = apply_filters( 'autoptimize_filter_extra_imgopt_build_url', $orig_url, $width, $height );
519
520 if ( $filtered_url !== $orig_url ) {
521 return $filtered_url;
522 }
523
524 $orig_url = $this->normalize_img_urls( $orig_url );
525 $imgopt_base_url = $this->get_imgopt_base_url();
526 $imgopt_size = '';
527
528 if ( $width && 0 !== $width ) {
529 $imgopt_size = ',w_' . $width;
530 }
531
532 if ( $height && 0 !== $height ) {
533 $imgopt_size .= ',h_' . $height;
534 }
535
536 $url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
537
538 return $url;
539 }
540
541 public function replace_data_thumbs( $matches )
542 {
543 if ( $this->can_optimize_image( $matches[1] ) ) {
544 return str_replace( $matches[1], $this->build_imgopt_url( $matches[1], 150, 150 ), $matches[0] );
545 } else {
546 return $matches[0];
547 }
548 }
549
550 public function filter_preconnect_imgopt_url( $in )
551 {
552 $imgopt_url_array = parse_url( $this->get_imgopt_base_url() );
553 $in[] = $imgopt_url_array['scheme'] . '://' . $imgopt_url_array['host'];
554
555 return $in;
556 }
557
558 private function normalize_img_urls( $in )
559 {
560 static $cdn_domain = null;
561 if ( is_null( $cdn_domain ) ) {
562 $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
563 if ( ! empty( $cdn_url ) ) {
564 $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
565 } else {
566 $cdn_domain = '';
567 }
568 }
569
570 $parsed_site_url = parse_url( site_url() );
571
572 if ( strpos( $in, 'http' ) !== 0 && strpos( $in, '//' ) === 0 ) {
573 $in = $parsed_site_url['scheme'] . ':' . $in;
574 } elseif ( strpos( $in, '/' ) === 0 ) {
575 $in = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $in;
576 } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
577 $in = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
578 }
579
580 return apply_filters( 'autoptimize_filter_extra_imgopt_normalized_url', $in );
581 }
582
583 private function get_img_quality_array()
584 {
585 static $img_quality_array = null;
586
587 if ( is_null( $img_quality_array ) ) {
588 $img_quality_array = array(
589 '1' => 'lossy',
590 '2' => 'glossy',
591 '3' => 'lossless',
592 );
593 $img_quality_array = apply_filters( 'autoptimize_filter_extra_imgopt_quality_array', $img_quality_array );
594 }
595
596 return $img_quality_array;
597 }
598
599 private function get_img_quality_setting()
600 {
601 static $_img_q = null;
602
603 if ( is_null( $_img_q ) ) {
604 if ( is_array( $this->options ) && array_key_exists( 'autoptimize_extra_select_field_6', $this->options ) ) {
605 $_setting = $this->options['autoptimize_extra_select_field_6'];
606 }
607
608 if ( ! isset( $_setting ) || empty( $_setting ) || ( '1' !== $_setting && '3' !== $_setting ) ) {
609 // default image opt. value is 2 ("glossy").
610 $_img_q = '2';
611 } else {
612 $_img_q = $_setting;
613 }
614 }
615
616 return $_img_q;
617 }
618
619 private function get_img_quality_string()
620 {
621 static $_img_q_string = null;
622
623 if ( is_null( $_img_q_string ) ) {
624 $_quality_array = $this->get_img_quality_array();
625 $_setting = $this->get_img_quality_setting();
626 $_img_q_string = apply_filters( 'autoptimize_filter_extra_imgopt_quality', 'q_' . $_quality_array[ $_setting ] );
627 }
628
629 return $_img_q_string;
630 }
631
632 public static function get_img_provider_stats()
633 {
634 // wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
635 $self = new self();
636 return $self->query_img_provider_stats();
637 }
638
639 public function query_img_provider_stats()
640 {
641 if ( ! empty( $this->options['autoptimize_extra_checkbox_field_5'] ) ) {
642 $_img_provider_stat_url = '';
643 $_img_provider_endpoint = $this->get_imgopt_host() . 'read-domain/';
644 $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
645
646 // make sure parse_url result makes sense, keeping $_img_provider_stat_url empty if not.
647 if ( $_site_host && ! empty( $_site_host ) ) {
648 $_img_provider_stat_url = $_img_provider_endpoint . $_site_host;
649 }
650
651 $_img_provider_stat_url = apply_filters( 'autoptimize_filter_extra_imgopt_stat_url', $_img_provider_stat_url );
652
653 // only do the remote call if $_img_provider_stat_url is not empty to make sure no parse_url weirdness results in useless calls.
654 if ( ! empty( $_img_provider_stat_url ) ) {
655 $_img_stat_resp = wp_remote_get( $_img_provider_stat_url );
656 if ( ! is_wp_error( $_img_stat_resp ) ) {
657 if ( '200' == wp_remote_retrieve_response_code( $_img_stat_resp ) ) {
658 $_img_provider_stat = json_decode( wp_remote_retrieve_body( $_img_stat_resp ), true );
659 $_img_provider_stat['timestamp'] = time();
660 update_option( 'autoptimize_imgopt_provider_stat', $_img_provider_stat );
661 }
662 }
663 }
664 }
665 }
666
667 public function imgopt_launch_ok()
668 {
669 static $launch_status = null;
670
671 if ( is_null( $launch_status ) ) {
672 $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
673 $magic_number = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
674 $has_launched = get_option( 'autoptimize_imgopt_launched', '' );
675 if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
676 $launch_status = true;
677 if ( ! $has_launched ) {
678 update_option( 'autoptimize_imgopt_launched', 'on' );
679 }
680 } else {
681 $launch_status = false;
682 }
683 }
684
685 return $launch_status;
686 }
687
688 public static function imgopt_launch_ok_wrapper()
689 {
690 // needed for "plug" notice in autoptimizeMain.php.
691 $self = new self();
692 return $self->imgopt_launch_ok();
693 }
694
695 public function get_imgopt_host()
696 {
697 static $imgopt_host = null;
698
699 if ( is_null( $imgopt_host ) ) {
700 $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
701 if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
702 $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
703 } else {
704 $imgopt_host = 'https://cdn.shortpixel.ai/';
705 }
706 }
707
708 return $imgopt_host;
709 }
710
711 public static function get_imgopt_host_wrapper()
712 {
713 // needed for CI tests.
714 $self = new self();
715 return $self->get_imgopt_host();
716 }
717
718 public function get_imgopt_status_notice() {
719 $_extra_options = $this->options;
720 if ( ! empty( $_extra_options ) && is_array( $_extra_options ) && array_key_exists( 'autoptimize_extra_checkbox_field_5', $_extra_options ) && ! empty( $_extra_options['autoptimize_extra_checkbox_field_5'] ) ) {
721 $_imgopt_notice = '';
722 $_stat = $this->get_imgopt_provider_userstatus();
723 $_site_host = AUTOPTIMIZE_SITE_DOMAIN;
724 $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
725
726 if ( is_array( $_stat ) ) {
727 if ( 1 == $_stat['Status'] ) {
728 // translators: "add more credits" will appear in a "a href".
729 $_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>' );
730 } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
731 // translators: "add more credits" will appear in a "a href".
732 $_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>' );
733 $_imgopt_stats_refresh_url = add_query_arg( array(
734 'page' => 'autoptimize_extra',
735 'refreshImgProvStats' => '1',
736 ), admin_url( 'options-general.php' ) );
737 if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
738 $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( get_option( 'time_format' ), $_stat['timestamp'] );
739 } else {
740 $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
741 }
742 $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
743 // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
744 $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
745 } else {
746 $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
747 // translators: "log in to check your account" will appear in a "a href".
748 $_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>' );
749 }
750 $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
751
752 return array(
753 'status' => $_stat['Status'],
754 'notice' => $_imgopt_notice,
755 );
756 }
757 }
758 return false;
759 }
760
761 public static function get_imgopt_status_notice_wrapper() {
762 // needed for notice being shown in autoptimizeCacheChecker.php.
763 $self = new self();
764 return $self->get_imgopt_status_notice();
765 }
766
767 public function get_imgopt_provider_userstatus() {
768 static $_provider_userstatus = null;
769
770 if ( is_null( $_provider_userstatus ) ) {
771 $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
772 if ( is_array( $_stat ) ) {
773 if ( array_key_exists( 'Status', $_stat ) ) {
774 $_provider_userstatus['Status'] = $_stat['Status'];
775 } else {
776 // if no stats then we assume all is well.
777 $_provider_userstatus['Status'] = 2;
778 }
779 if ( array_key_exists( 'timestamp', $_stat ) ) {
780 $_provider_userstatus['timestamp'] = $_stat['timestamp'];
781 } else {
782 // if no timestamp then we return "".
783 $_provider_userstatus['timestamp'] = '';
784 }
785 }
786 }
787
788 return $_provider_userstatus;
789 }
790
791 public function admin_menu()
792 {
793 add_submenu_page( null, 'autoptimize_extra', 'autoptimize_extra', 'manage_options', 'autoptimize_extra', array( $this, 'options_page' ) );
794 register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
795 }
796
797 public function add_extra_tab( $in )
798 {
799 $in = array_merge( $in, array( 'autoptimize_extra' => __( 'Extra', 'autoptimize' ) ) );
800
801 return $in;
802 }
803
804 public function options_page()
805 {
806 // Check querystring for "refreshCacheChecker" and call cachechecker if so.
807 if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
808 $this->query_img_provider_stats();
809 }
810
811 // Working with actual option values from the database here.
812 // That way any saves are still processed as expected, but we can still
813 // override behavior by using `new autoptimizeExtra($custom_options)` and not have that custom
814 // behavior being persisted in the DB even if save is done here.
815 $options = $this->fetch_options();
816 $gfonts = $options['autoptimize_extra_radio_field_4'];
817 $sp_url_suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
818 ?>
819 <style>
820 #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
821 #ao_settings_form .form-table th {font-weight: normal;}
822 #autoptimize_extra_descr{font-size: 120%;}
823 </style>
824 <div class="wrap">
825 <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
826 <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
827 <?php
828 if ( 'on' !== get_option( 'autoptimize_js' ) && 'on' !== get_option( 'autoptimize_css' ) && 'on' !== get_option( 'autoptimize_html' ) ) {
829 ?>
830 <div class="notice-warning notice"><p>
831 <?php
832 _e( 'Most of below Extra optimizations require at least one of HTML, JS or CSS autoptimizations being active.', 'autoptimize' );
833 ?>
834 </p></div>
835 <?php
836 }
837
838 if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) {
839 ?>
840 <div class="notice-warning notice"><p>
841 <?php
842 // translators: "Autoptimize support forum" will appear in a "a href".
843 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>' );
844 ?>
845 </p></div>
846 <?php
847 }
848
849 if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! $this->imgopt_launch_ok() ) {
850 ?>
851 <div class="notice-warning notice"><p>
852 <?php
853 _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' );
854 ?>
855 </p></div>
856 <?php
857 }
858
859 ?>
860 <form id='ao_settings_form' action='options.php' method='post'>
861 <?php settings_fields( 'autoptimize_extra_settings' ); ?>
862 <h2><?php _e( 'Extra Auto-Optimizations', 'autoptimize' ); ?></h2>
863 <span id='autoptimize_extra_descr'><?php _e( 'The following settings can improve your site\'s performance even more.', 'autoptimize' ); ?></span>
864 <table class="form-table">
865 <tr>
866 <th scope="row"><?php _e( 'Google Fonts', 'autoptimize' ); ?></th>
867 <td>
868 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="1" <?php if ( ! in_array( $gfonts, array( 2, 3, 4, 5 ) ) ) { echo 'checked'; } ?> ><?php _e( 'Leave as is', 'autoptimize' ); ?><br/>
869 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="2" <?php checked( 2, $gfonts, true ); ?> ><?php _e( 'Remove Google Fonts', 'autoptimize' ); ?><br/>
870 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="3" <?php checked( 3, $gfonts, true ); ?> ><?php _e( 'Combine and link in head (fonts load fast but are render-blocking)', 'autoptimize' ); ?><br/>
871 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="5" <?php checked( 5, $gfonts, true ); ?> ><?php _e( 'Combine and preload in head (fonts load late, but are not render-blocking)', 'autoptimize' ); ?><br/>
872 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="4" <?php checked( 4, $gfonts, true ); ?> ><?php _e( 'Combine and load fonts asynchronously with <a href="https://github.com/typekit/webfontloader#readme" target="_blank">webfont.js</a>', 'autoptimize' ); ?><br/>
873 </td>
874 </tr>
875 <tr>
876 <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
877 <td>
878 <label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_5]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && '1' === $options['autoptimize_extra_checkbox_field_5'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Optimize images on the fly and serve them from a CDN.', 'autoptimize' ); ?></label>
879 <?php
880 // show shortpixel status.
881 $_notice = $this->get_imgopt_status_notice();
882 if ( $_notice ) {
883 switch ( $_notice['status'] ) {
884 case 2:
885 $_notice_color = 'green';
886 break;
887 case 1:
888 $_notice_color = 'orange';
889 break;
890 case -1:
891 $_notice_color = 'red';
892 break;
893 case -2:
894 $_notice_color = 'red';
895 break;
896 default:
897 $_notice_color = 'green';
898 }
899 echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
900 } else {
901 // translators: link points to shortpixel.
902 $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having the images optimized on the fly by %1$sShortPixel%2$s and then cached and served fast from a CDN.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
903 if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
904 $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' );
905 } else {
906 // translators: link points to shortpixel.
907 $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>' );
908 }
909 echo apply_filters( 'autoptimize_extra_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
910 }
911 // translators: link points to shortpixel FAQ.
912 $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>' );
913 // translators: links points to shortpixel TOS & Privacy Policy.
914 $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>' );
915 echo apply_filters( 'autoptimize_extra_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
916 ?>
917 </td>
918 </tr>
919 <tr id='autoptimize_imgopt_quality' <?php if ( ! array_key_exists( 'autoptimize_extra_checkbox_field_5', $options ) || ( ! empty( $options['autoptimize_extra_checkbox_field_5'] ) && '1' !== $options['autoptimize_extra_checkbox_field_5'] ) ) { echo 'class="hidden"'; } ?>>
920 <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
921 <td>
922 <label>
923 <select name='autoptimize_extra_settings[autoptimize_extra_select_field_6]'>
924 <?php
925 $_imgopt_array = $this->get_img_quality_array();
926 $_imgopt_val = $this->get_img_quality_setting();
927
928 foreach ( $_imgopt_array as $key => $value ) {
929 echo '<option value="' . $key . '"';
930 if ( $_imgopt_val == $key ) {
931 echo ' selected';
932 }
933 echo '>' . ucfirst( $value ) . '</option>';
934 }
935 echo "\n";
936 ?>
937 </select>
938 </label>
939 <p>
940 <?php
941 // translators: link points to shortpixel image test page.
942 echo apply_filters( 'autoptimize_extra_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>' ) );
943 ?>
944 </p>
945 </td>
946 </tr>
947 <tr>
948 <th scope="row"><?php _e( 'Remove emojis', 'autoptimize' ); ?></th>
949 <td>
950 <label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_1'] ) && '1' === $options['autoptimize_extra_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Removes WordPress\' core emojis\' inline CSS, inline JavaScript, and an otherwise un-autoptimized JavaScript file.', 'autoptimize' ); ?></label>
951 </td>
952 </tr>
953 <tr>
954 <th scope="row"><?php _e( 'Remove query strings from static resources', 'autoptimize' ); ?></th>
955 <td>
956 <label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_0]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_0'] ) && '1' === $options['autoptimize_extra_checkbox_field_0'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Removing query strings (or more specifically the <code>ver</code> parameter) will not improve load time, but might improve performance scores.', 'autoptimize' ); ?></label>
957 </td>
958 </tr>
959 <tr>
960 <th scope="row"><?php _e( 'Preconnect to 3rd party domains <em>(advanced users)</em>', 'autoptimize' ); ?></th>
961 <td>
962 <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_2'] ); } ?>'><br /><?php _e( 'Add 3rd party domains you want the browser to <a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">preconnect</a> to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).', 'autoptimize' ); ?></label>
963 </td>
964 </tr>
965 <tr>
966 <th scope="row"><?php _e( 'Async Javascript-files <em>(advanced users)</em>', 'autoptimize' ); ?></th>
967 <td>
968 <?php
969 if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
970 // translators: link points Async Javascript settings page.
971 printf( __( 'You have "Async JavaScript" installed, %1$sconfiguration of async javascript is best done there%2$s.', 'autoptimize' ), '<a href="' . 'options-general.php?page=async-javascript' . '">', '</a>' );
972 } else {
973 ?>
974 <input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_3]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_3', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_3'] ); } ?>'>
975 <br />
976 <?php
977 _e( 'Comma-separated list of local or 3rd party JS-files that should loaded with the <code>async</code> flag. JS-files from your own site will be automatically excluded if added here. ', 'autoptimize' );
978 // translators: %s will be replaced by a link to the "async javascript" plugin.
979 echo sprintf( __( 'Configuration of async javascript is easier and more flexible using the %s plugin.', 'autoptimize' ), '"<a href="https://wordpress.org/plugins/async-javascript" target="_blank">Async Javascript</a>"' );
980 $asj_install_url = network_admin_url() . 'plugin-install.php?s=async+javascript&tab=search&type=term';
981 echo sprintf( ' <a href="' . $asj_install_url . '">%s</a>', __( 'Click here to install and activate it.', 'autoptimize' ) );
982 }
983 ?>
984 </td>
985 </tr>
986 <tr>
987 <th scope="row"><?php _e( 'Optimize YouTube videos', 'autoptimize' ); ?></th>
988 <td>
989 <?php
990 if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'wp-youtube-lyte/wp-youtube-lyte.php' ) ) {
991 _e( 'Great, you have WP YouTube Lyte installed.', 'autoptimize' );
992 $lyte_config_url = 'options-general.php?page=lyte_settings_page';
993 echo sprintf( ' <a href="' . $lyte_config_url . '">%s</a>', __( 'Click here to configure it.', 'autoptimize' ) );
994 } else {
995 // translators: %s will be replaced by a link to "wp youtube lyte" plugin.
996 echo sprintf( __( '%s allows you to “lazy load” your videos, by inserting responsive “Lite YouTube Embeds". ', 'autoptimize' ), '<a href="https://wordpress.org/plugins/wp-youtube-lyte" target="_blank">WP YouTube Lyte</a>' );
997 $lyte_install_url = network_admin_url() . 'plugin-install.php?s=lyte&tab=search&type=term';
998 echo sprintf( ' <a href="' . $lyte_install_url . '">%s</a>', __( 'Click here to install and activate it.', 'autoptimize' ) );
999 }
1000 ?>
1001 </td>
1002 </tr>
1003 </table>
1004 <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
1005 </form>
1006 <script>
1007 jQuery(document).ready(function() {
1008 jQuery( "#autoptimize_imgopt_checkbox" ).change(function() {
1009 if (this.checked) {
1010 jQuery("#autoptimize_imgopt_quality").show("slow");
1011 } else {
1012 jQuery("#autoptimize_imgopt_quality").hide("slow");
1013 }
1014 });
1015 });
1016 </script>
1017 <?php
1018 }
1019 }
1020