Autoptimize - Version 3.0.0

Version Description

  • fundamental change for new installations: by default Autoptimize will not aggregate JS/ CSS any more (HTTP/2 is ubiquitous and there are other advantages to not aggregating esp. re. inline JS/ CSS and dependancies)
  • new: no API needed any more to create manual critical CSS rules.
  • new: "Remove WordPress blocks CSS" option on the "Extra" tab to remove block- and global styles (and SVG).
  • new: compatibility logic for "edit with elementor", "revolution slider", for non-aggregated inline JS requiring jQuery even if not excluded (
Download this release

Release Info

Developer futtta
Plugin Icon 128x128 Autoptimize
Version 3.0.0
Comparing to
See all releases

Code changes from version 2.9.5.1 to 3.0.0

autoptimize.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Autoptimize
4
  * Plugin URI: https://autoptimize.com/
5
  * Description: Makes your site faster by optimizing CSS, JS, Images, Google fonts and more.
6
- * Version: 2.9.5.1
7
  * Author: Frank Goossens (futtta)
8
  * Author URI: https://autoptimize.com/
9
  * Text Domain: autoptimize
@@ -21,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
21
  exit;
22
  }
23
 
24
- define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.9.5.1' );
25
 
26
  // plugin_dir_path() returns the trailing slash!
27
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
3
  * Plugin Name: Autoptimize
4
  * Plugin URI: https://autoptimize.com/
5
  * Description: Makes your site faster by optimizing CSS, JS, Images, Google fonts and more.
6
+ * Version: 3.0.0
7
  * Author: Frank Goossens (futtta)
8
  * Author URI: https://autoptimize.com/
9
  * Text Domain: autoptimize
21
  exit;
22
  }
23
 
24
+ define( 'AUTOPTIMIZE_PLUGIN_VERSION', '3.0.0' );
25
 
26
  // plugin_dir_path() returns the trailing slash!
27
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
classes/autoptimizeBase.php CHANGED
@@ -77,6 +77,10 @@ abstract class autoptimizeBase
77
  {
78
  $url = apply_filters( 'autoptimize_filter_cssjs_alter_url', $url );
79
 
 
 
 
 
80
  if ( false !== strpos( $url, '%' ) ) {
81
  $url = urldecode( $url );
82
  }
@@ -669,7 +673,7 @@ abstract class autoptimizeBase
669
  'js/jquery/jquery.js',
670
  );
671
  foreach ( $minified_variants as $ending ) {
672
- if ( autoptimizeUtils::str_ends_in( $filepath, $ending ) ) {
673
  return false;
674
  }
675
  }
77
  {
78
  $url = apply_filters( 'autoptimize_filter_cssjs_alter_url', $url );
79
 
80
+ if ( is_null( $url ) ) {
81
+ return false;
82
+ }
83
+
84
  if ( false !== strpos( $url, '%' ) ) {
85
  $url = urldecode( $url );
86
  }
673
  'js/jquery/jquery.js',
674
  );
675
  foreach ( $minified_variants as $ending ) {
676
+ if ( autoptimizeUtils::str_ends_in( $filepath, $ending ) && true === apply_filters( 'autoptimize_filter_base_prepare_exclude_minified', true ) ) {
677
  return false;
678
  }
679
  }
classes/autoptimizeCache.php CHANGED
@@ -382,6 +382,16 @@ class autoptimizeCache
382
  */
383
  public static function clearall( $propagate = true )
384
  {
 
 
 
 
 
 
 
 
 
 
385
  if ( ! self::cacheavail() ) {
386
  return false;
387
  }
@@ -636,6 +646,7 @@ class autoptimizeCache
636
  $_fallback_php_contents = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $_fallback_filename );
637
  $_fallback_php_contents = str_replace( '<?php exit;', '<?php', $_fallback_php_contents );
638
  $_fallback_php_contents = str_replace( '<!--ao-cache-dir-->', AUTOPTIMIZE_CACHE_DIR, $_fallback_php_contents );
 
639
  if ( is_multisite() ) {
640
  $_fallback_php_contents = str_replace( '$multisite = false;', '$multisite = true;', $_fallback_php_contents );
641
  }
@@ -817,6 +828,9 @@ class autoptimizeCache
817
  'timeout' => 5,
818
  )
819
  );
 
 
 
820
  } elseif ( defined('NGINX_HELPER_BASENAME') ) {
821
  do_action( 'rt_nginx_helper_purge_all' );
822
  } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
382
  */
383
  public static function clearall( $propagate = true )
384
  {
385
+ if ( defined( 'ET_CORE_VERSION' ) && 'Divi' === get_template() ) {
386
+ // see https://blog.futtta.be/2018/11/17/warning-divi-purging-autoptimizes-cache/ .
387
+ $dbt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
388
+ $caller = isset( $dbt[1]['function'] ) ? $dbt[1]['function'] : null;
389
+ if ( $caller === 'et_core_clear_wp_cache' ) {
390
+ _doing_it_wrong( 'autoptimizeCache::clearall', 'Divi devs: please don\'t clear Autoptimize\'s cache, it is unneeded and can break sites. You can contact me at futtta@gmail.com to discuss.', 'Autoptimize 2.9.6' );
391
+ return false;
392
+ }
393
+ }
394
+
395
  if ( ! self::cacheavail() ) {
396
  return false;
397
  }
646
  $_fallback_php_contents = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $_fallback_filename );
647
  $_fallback_php_contents = str_replace( '<?php exit;', '<?php', $_fallback_php_contents );
648
  $_fallback_php_contents = str_replace( '<!--ao-cache-dir-->', AUTOPTIMIZE_CACHE_DIR, $_fallback_php_contents );
649
+ $_fallback_php_contents = str_replace( '<!--ao-cachefile-prefix-->', AUTOPTIMIZE_CACHEFILE_PREFIX, $_fallback_php_contents );
650
  if ( is_multisite() ) {
651
  $_fallback_php_contents = str_replace( '$multisite = false;', '$multisite = true;', $_fallback_php_contents );
652
  }
828
  'timeout' => 5,
829
  )
830
  );
831
+ } elseif ( class_exists( 'RaidboxesNginxCacheFunctions' ) ) {
832
+ $rb_cache_helper = new RaidboxesNginxCacheFunctions();
833
+ $rb_cache_helper->purge_cache();
834
  } elseif ( defined('NGINX_HELPER_BASENAME') ) {
835
  do_action( 'rt_nginx_helper_purge_all' );
836
  } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
classes/autoptimizeCompatibility.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Multiple compatibility snippets to ensure important/ stubborn plugins work out of the box.
4
+ *
5
+ */
6
+
7
+ if ( ! defined( 'ABSPATH' ) ) {
8
+ exit;
9
+ }
10
+
11
+ class autoptimizeCompatibility
12
+ {
13
+ /**
14
+ * Constructor.
15
+ *
16
+ */
17
+ public function __construct()
18
+ {
19
+ if ( ! is_admin() && ! defined( 'DOING_CRON' ) ) {
20
+ $this->conf = autoptimizeConfig::instance();
21
+ $this->run();
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Runs multiple compatibility snippets to ensure important plugins work out of the box.
27
+ *
28
+ */
29
+ public function run()
30
+ {
31
+ // Edit with Elementor in frontend admin menu (so for editors/ administrators) needs JS opt. disabled to appear & function.
32
+ if ( defined( 'ELEMENTOR_VERSION' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) && apply_filters( 'autoptimize_filter_compatibility_editelementor_active', true ) ) {
33
+ add_filter( 'autoptimize_filter_js_noptimize', '__return_true' );
34
+ }
35
+
36
+ // Revslider; jQuery should not be deferred + exclude all revslider JS.
37
+ if ( defined( 'RS_REVISION' ) && $this->conf->get( 'autoptimize_js' ) && true == $this->inline_js_config_checker() && apply_filters( 'autoptimize_filter_compatibility_revslider_active', true ) ) {
38
+ add_filter( 'autoptimize_filter_js_exclude', function( $js_excl = '', $html = '' ) {
39
+ $revslider_excl = 'revslider, setREVStartSize, window.RSIW, window.RS_MODULES, jquery.min.js';
40
+ if ( ! empty( $html ) && false !== strpos( $html, '<rs-slides>' ) ) {
41
+ if ( is_array( $js_excl ) ) {
42
+ $js_excl = implode( ',', $js_excl );
43
+ }
44
+
45
+ $js_excl .= ',' . $revslider_excl;
46
+ }
47
+ return $js_excl;
48
+ }, 11, 2 );
49
+ }
50
+
51
+ // Revslider; remove revslider JS if no slides in HTML for non-logged in users.
52
+ if ( defined( 'RS_REVISION' ) && $this->conf->get( 'autoptimize_js' ) && false === is_user_logged_in() && apply_filters( 'autoptimize_filter_compatibility_revslider_remover_active', true ) ) {
53
+ add_filter( 'autoptimize_filter_js_removables', function( $to_remove = '', $html = '' ) {
54
+ if ( ! empty( $html ) && false === strpos( $html, '<rs-slides>') ) {
55
+ $to_remove .= 'plugins/revslider, setREVStartSize, window.RSIW, window.RS_MODULES';
56
+ }
57
+
58
+ return $to_remove;
59
+ }, 11, 2 );
60
+ }
61
+
62
+ // Exclude jQuery if inline JS is found that requires jQuery.
63
+ if ( $this->inline_js_config_checker() && false === strpos( $this->conf->get( 'autoptimize_js_exclude' ), 'jquery.min.js' ) && apply_filters( 'autoptimize_filter_compatibility_inline_jquery', true ) ) {
64
+ add_filter( 'autoptimize_filter_js_exclude', function( $js_excl = '', $html = '' ) {
65
+ if ( ! empty( $html ) && preg_match( '/<script[^>]*>[^<]*(jQuery|\$)\([^<]*<\/script>/Usm', $html ) ) {
66
+ if ( is_array( $js_excl ) ) {
67
+ $js_excl = implode( ',', $js_excl );
68
+ }
69
+
70
+ if ( false === strpos( $js_excl, 'jquery.min.js' ) ) {
71
+ $js_excl .= ', jquery.min.js';
72
+ }
73
+ }
74
+ return $js_excl;
75
+ }, 12, 2 );
76
+ }
77
+
78
+ // Make JS-based Gutenberg blocks work OOTB.
79
+ if ( $this->inline_js_config_checker() && apply_filters( 'autoptimize_filter_compatibility_gutenberg_js', true ) ) {
80
+ add_filter( 'autoptimize_filter_js_exclude', function( $js_excl = '', $html = '' ) {
81
+ if ( ! empty( $html ) && false !== strpos( $html, 'wp.i18n' ) || false !== strpos( $html, 'wp.apiFetch' ) || false !== strpos( $html, 'window.lodash' ) ) {
82
+ if ( is_array( $js_excl ) ) {
83
+ $js_excl = implode( ',', $js_excl );
84
+ }
85
+
86
+ if ( false === strpos( $js_excl, 'jquery.min.js' ) ) {
87
+ $js_excl .= ', jquery.min.js';
88
+ }
89
+
90
+ if ( false === strpos( $js_excl, 'wp-includes/js/dist' ) ) {
91
+ $js_excl .= ', wp-includes/js/dist';
92
+ }
93
+ }
94
+ return $js_excl;
95
+ }, 13, 2 );
96
+ }
97
+ }
98
+
99
+ public function inline_js_config_checker() {
100
+ static $inline_js_flagged = null;
101
+
102
+ if ( null === $inline_js_flagged ) {
103
+ if ( ( $this->conf->get( 'autoptimize_js_aggregate' ) || apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) && apply_filters( 'autoptimize_js_include_inline', $this->conf->get( 'autoptimize_js_include_inline' ) ) ) {
104
+ // if all files and also inline JS are aggregated we don't have to worry about inline JS.
105
+ $inline_js_flagged = false;
106
+ } else if ( apply_filters( 'autoptimize_filter_js_defer_not_aggregate', $this->conf->get( 'autoptimize_js_defer_not_aggregate' ) ) && apply_filters( 'autoptimize_js_filter_defer_inline', $this->conf->get( 'autoptimize_js_defer_inline' ) ) ) {
107
+ // and when not aggregating but deferring all including inline JS, then all is OK too.
108
+ $inline_js_flagged = false;
109
+ }
110
+
111
+ // in all other cases we need to pay attention to inline JS requiring src'ed JS to be available.
112
+ $inline_js_flagged = true;
113
+ }
114
+
115
+ return $inline_js_flagged;
116
+ }
117
+ }
classes/autoptimizeConfig.php CHANGED
@@ -226,7 +226,7 @@ if ( is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network()
226
  <table class="form-table">
227
  <tr valign="top">
228
  <th scope="row"><?php _e( 'Optimize JavaScript Code?', 'autoptimize' ); ?></th>
229
- <td><input type="checkbox" id="autoptimize_js" name="autoptimize_js" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js' ) ? 'checked="checked" ' : ''; ?>/></td>
230
  </tr>
231
  <tr valign="top" class="js_sub js_aggregate_master">
232
  <th scope="row"><?php _e( 'Aggregate JS-files?', 'autoptimize' ); ?></th>
@@ -235,17 +235,17 @@ if ( is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network()
235
  </tr>
236
  <tr valign="top" class="js_sub js_aggregate hidden">
237
  <th scope="row">&emsp;<?php _e( 'Also aggregate inline JS?', 'autoptimize' ); ?></th>
238
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_include_inline' ) ? 'checked="checked" ' : ''; ?>/>
239
  <?php _e( 'Let Autoptimize also extract JS from the HTML (discouraged as it can make Autoptimize\'s cache size grow quickly)', 'autoptimize' ); ?></label></td>
240
  </tr>
241
  <tr valign="top" class="js_sub js_aggregate hidden">
242
  <th scope="row">&emsp;<?php _e( 'Force JavaScript in &lt;head&gt;?', 'autoptimize' ); ?></th>
243
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_forcehead' ) ? 'checked="checked" ' : ''; ?>/>
244
  <?php _e( 'Load JavaScript early (discouraged as it makes the JS render blocking)', 'autoptimize' ); ?></label></td>
245
  </tr>
246
  <tr valign="top" class="js_sub js_aggregate hidden">
247
  <th scope="row">&emsp;<?php _e( 'Add try-catch wrapping?', 'autoptimize' ); ?></th>
248
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_trycatch" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_trycatch' ) ? 'checked="checked" ' : ''; ?>/>
249
  <?php _e( 'If your aggregated scripts break because of a JS-error, you might want to try this, but generally discouraged.', 'autoptimize' ); ?></label></td>
250
  </tr>
251
  <tr valign="top" class="js_sub js_not_aggregate_master">
@@ -255,7 +255,7 @@ if ( is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network()
255
  </tr>
256
  <tr valign="top" id="js_defer_inline" class="js_sub js_not_aggregate hidden">
257
  <th scope="row">&emsp;<?php _e( 'Also defer inline JS?', 'autoptimize' ); ?></th>
258
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_defer_inline" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_inline' ) ? 'checked="checked" ' : ''; ?>/>
259
  <?php _e( 'Also defer inline JS. Generally this will allow all JS to be deferred, so you should remove default exclusions, test and only exclude specific items if still needed.', 'autoptimize' ); ?></label></td>
260
  </tr>
261
  <?php if ( autoptimizeOptionWrapper::get_option( 'autoptimize_js_justhead' ) ) { ?>
@@ -266,13 +266,13 @@ if ( is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network()
266
  echo ' <i>' . __( '(deprecated)', 'autoptimize' ) . '</i>';
267
  ?>
268
  </th>
269
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_js_justhead' ) ? 'checked="checked" ' : ''; ?>/>
270
  <?php _e( 'Mostly useful in combination with previous option when using jQuery-based templates, but might help keeping cache size under control.', 'autoptimize' ); ?></label></td>
271
  </tr>
272
  <?php } ?>
273
  <tr valign="top" class="js_sub">
274
  <th scope="row"><?php _e( 'Exclude scripts from Autoptimize:', 'autoptimize' ); ?></th>
275
- <td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo esc_attr( autoptimizeOptionWrapper::get_option( 'autoptimize_js_exclude', 'wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.js, js/jquery/jquery.min.js' ) ); ?>"/><br />
276
  <?php
277
  echo __( 'A comma-separated list of scripts you do not want optimized, for example \'whatever.js, my_var\' (without the quotes).', 'autoptimize' ) . ' ' . __( 'Important: when "aggregate JS-files" is on, excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
278
  ?>
@@ -290,7 +290,7 @@ echo __( 'A comma-separated list of scripts you do not want optimized, for examp
290
  <table class="form-table">
291
  <tr valign="top">
292
  <th scope="row"><?php _e( 'Optimize CSS Code?', 'autoptimize' ); ?></th>
293
- <td><input type="checkbox" id="autoptimize_css" name="autoptimize_css" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) ? 'checked="checked" ' : ''; ?>/></td>
294
  </tr>
295
  <tr class="css_sub" valign="top">
296
  <th scope="row"><?php _e( 'Aggregate CSS-files?', 'autoptimize' ); ?></th>
@@ -299,12 +299,12 @@ echo __( 'A comma-separated list of scripts you do not want optimized, for examp
299
  </tr>
300
  <tr valign="top" class="css_sub css_aggregate">
301
  <th scope="row"><?php _e( 'Also aggregate inline CSS?', 'autoptimize' ); ?></th>
302
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_include_inline" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_include_inline', '1' ) ? 'checked="checked" ' : ''; ?>/>
303
  <?php _e( 'Check this option for Autoptimize to also aggregate CSS in the HTML.', 'autoptimize' ); ?></label></td>
304
  </tr>
305
  <tr class="css_sub css_aggregate" valign="top">
306
  <th scope="row"><?php _e( 'Generate data: URIs for images?', 'autoptimize' ); ?></th>
307
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_datauris" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_datauris' ) ? 'checked="checked" ' : ''; ?>/>
308
  <?php _e( 'Enable this to include small background-images in the CSS itself instead of as separate downloads.', 'autoptimize' ); ?></label></td>
309
  </tr>
310
  <?php if ( autoptimizeOptionWrapper::get_option( 'autoptimize_css_justhead' ) ) { ?>
@@ -315,19 +315,19 @@ _e( 'Look for styles only in &lt;head&gt;?', 'autoptimize' );
315
  echo ' <i>' . __( '(deprecated)', 'autoptimize' ) . '</i>';
316
  ?>
317
  </th>
318
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_justhead' ) ? 'checked="checked" ' : ''; ?>/>
319
  <?php _e( 'Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.', 'autoptimize' ); ?></label></td>
320
  </tr>
321
  <?php } ?>
322
  <tr valign="top" class="css_sub">
323
  <th scope="row"><?php _e( 'Eliminate render-blocking CSS?', 'autoptimize' ); ?></th>
324
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_defer" id="autoptimize_css_defer" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' ) ? 'checked="checked" ' : ''; ?>/>
325
  <?php
326
  _e( 'Inline "above the fold CSS" while loading the main autoptimized CSS only after page load. <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank">Check the FAQ</a> for more info.', 'autoptimize' );
327
  echo ' ';
328
  $critcss_settings_url = get_admin_url( null, 'options-general.php?page=ao_critcss' );
329
  // translators: links "autoptimize critical CSS" tab.
330
- echo sprintf( __( 'This can be fully automated for different types of pages on the %s tab.', 'autoptimize' ), '<a href="' . $critcss_settings_url . '">CriticalCSS</a>' );
331
  ?>
332
  </label></td>
333
  </tr>
@@ -337,12 +337,12 @@ echo sprintf( __( 'This can be fully automated for different types of pages on t
337
  </tr>
338
  <tr valign="top" class="css_sub css_aggregate">
339
  <th scope="row"><?php _e( 'Inline all CSS?', 'autoptimize' ); ?></th>
340
- <td><label class="cb_label"><input type="checkbox" id="autoptimize_css_inline" name="autoptimize_css_inline" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_css_inline' ) ? 'checked="checked" ' : ''; ?>/>
341
  <?php _e( 'Inlining all CSS is an easy way to stop the CSS from being render-blocking, but is generally not recommended because the size of the HTML increases significantly. Additionally it might push meta-tags down to a position where e.g. Facebook and Whatsapp will not find them any more, breaking thumbnails when sharing.', 'autoptimize' ); ?></label></td>
342
  </tr>
343
  <tr valign="top" class="css_sub">
344
  <th scope="row"><?php _e( 'Exclude CSS from Autoptimize:', 'autoptimize' ); ?></th>
345
- <td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo esc_attr( autoptimizeOptionWrapper::get_option( 'autoptimize_css_exclude', 'wp-content/cache/, wp-content/uploads/, admin-bar.min.css, dashicons.min.css' ) ); ?>"/><br />
346
  <?php
347
  echo __( 'A comma-separated list of CSS you want to exclude from being optimized.', 'autoptimize' ) . ' ' . __( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
348
  ?>
@@ -365,12 +365,12 @@ $_rapidload_link = 'https://misc.optimizingmatters.com/partners/?from=csssetting
365
  <table class="form-table">
366
  <tr valign="top">
367
  <th scope="row"><?php _e( 'Optimize HTML Code?', 'autoptimize' ); ?></th>
368
- <td><input type="checkbox" id="autoptimize_html" name="autoptimize_html" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_html' ) ? 'checked="checked" ' : ''; ?>/></td>
369
  </tr>
370
  <tr class="html_sub" valign="top">
371
  <th scope="row"><?php _e( 'Keep HTML comments?', 'autoptimize' ); ?></th>
372
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_html_keepcomments" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_html_keepcomments' ) ? 'checked="checked" ' : ''; ?>/>
373
- <?php _e( 'Enable this if you want HTML comments to remain in the page.', 'autoptimize' ); ?></label></td>
374
  </tr>
375
  </table>
376
  </li>
@@ -435,7 +435,7 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
435
  <table class="form-table">
436
  <tr valign="top" >
437
  <th scope="row"><?php _e( 'Save aggregated script/css as static files?', 'autoptimize' ); ?></th>
438
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_cache_nogzip', '1' ) ? 'checked="checked" ' : ''; ?>/>
439
  <?php _e( 'By default files saved are static css/js, uncheck this option if your webserver doesn\'t properly handle the compression and expiry.', 'autoptimize' ); ?></label></td>
440
  </tr>
441
  <?php
@@ -446,17 +446,17 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
446
  ?>
447
  <tr valign="top" id="min_excl_row" class="<?php echo $_min_excl_class; ?>">
448
  <th scope="row"><?php _e( 'Minify excluded CSS and JS files?', 'autoptimize' ); ?></th>
449
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_minify_excluded" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_minify_excluded', '1' ) ? 'checked="checked" ' : ''; ?>/>
450
  <?php _e( 'When aggregating JS or CSS, excluded files that are not minified (based on filename) are by default minified by Autoptimize despite being excluded. Uncheck this option if anything breaks despite excluding.', 'autoptimize' ); ?></label></td>
451
  </tr>
452
  <tr valign="top">
453
  <th scope="row"><?php _e( 'Enable 404 fallbacks?', 'autoptimize' ); ?></th>
454
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_fallback" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_cache_fallback', '1' ) ? 'checked="checked" ' : ''; ?>/>
455
  <?php _e( 'Sometimes Autoptimized JS/ CSS is referenced in cached HTML but is already removed, resulting in broken sites. With this option on, Autoptimize will try to redirect those not-found files to "fallback"-versions, keeping the page/ site somewhat intact. In some cases this will require extra web-server level configuration to ensure <code>wp-content/autoptimize_404_handler.php</code> is set to handle 404\'s in <code>wp-content/cache/autoptimize</code>.', 'autoptimize' ); ?></label></td>
456
  </tr>
457
  <tr valign="top">
458
  <th scope="row"><?php _e( 'Also optimize for logged in editors/ administrators?', 'autoptimize' ); ?></th>
459
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_logged" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_logged', '1' ) ? 'checked="checked" ' : ''; ?>/>
460
  <?php _e( 'By default Autoptimize is also active for logged on editors/ administrators, uncheck this option if you don\'t want Autoptimize to optimize when logged in e.g. to use a pagebuilder.', 'autoptimize' ); ?></label></td>
461
  </tr>
462
  <?php
@@ -464,7 +464,7 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
464
  ?>
465
  <tr valign="top" >
466
  <th scope="row"><?php _e( 'Also optimize shop cart/ checkout?', 'autoptimize' ); ?></th>
467
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_checkout" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_checkout', '0' ) ? 'checked="checked" ' : ''; ?>/>
468
  <?php _e( 'By default Autoptimize is also active on your shop\'s cart/ checkout, uncheck not to optimize those.', 'autoptimize' ); ?></label>
469
  </td>
470
  </tr>
@@ -474,7 +474,7 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
474
  ?>
475
  <tr valign="top">
476
  <th scope="row"><?php _e( 'Enable configuration per post/ page?', 'autoptimize' ); ?></th>
477
- <td><label class="cb_label"><input type="checkbox" name="autoptimize_enable_meta_ao_settings" <?php echo autoptimizeOptionWrapper::get_option( 'autoptimize_enable_meta_ao_settings', '1' ) ? 'checked="checked" ' : ''; ?>/>
478
  <?php _e( 'Add a "metabox" to the post/ page edit screen allowing different optimizations to be turned off on a per post/ page level?', 'autoptimize' ); ?></label></td>
479
  </tr>
480
  <?php } ?>
@@ -558,12 +558,14 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
558
  jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'slow', .33 ); // grey out "not aggregate"
559
  jQuery( ".js_not_aggregate" ).hide( 'slow' ); // hide not aggregate sub-items
560
  jQuery( "#min_excl_row" ).show(); // make sure "minify excluded" is visible
 
561
  } else {
562
  jQuery( ".js_aggregate" ).hide( 'slow' ); // hide sub-itmes
563
  jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'slow', 1 ); // un-grey-out "not aggregate"
564
  if ( jQuery( "#autoptimize_css_aggregate" ).prop( 'checked' ) == false ) { // hide "minify excluded"
565
  jQuery( "#min_excl_row" ).hide();
566
  }
 
567
  }
568
  });
569
 
@@ -574,6 +576,7 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
574
  jQuery( ".js_not_aggregate" ).show( 'slow'); // show sub-items
575
  jQuery( ".js_aggregate_master:visible" ).fadeTo( 'slow', .33 ); // grey out "aggregate"
576
  jQuery( ".js_aggregate" ).hide( 'slow' ); // hide aggregate sub-items
 
577
  } else {
578
  jQuery( ".js_not_aggregate" ).hide( 'slow' ); // hide sub-items
579
  jQuery( ".js_aggregate_master:visible" ).fadeTo( 'slow', 1 ); // un-grey-out "aggregate"
@@ -592,11 +595,13 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
592
  if (this.checked && jQuery("#autoptimize_css").prop('checked')) {
593
  jQuery(".css_aggregate:visible").fadeTo("fast",1);
594
  jQuery( "#min_excl_row" ).show();
 
595
  } else {
596
  jQuery(".css_aggregate:visible").fadeTo("fast",.33);
597
  if ( jQuery( "#autoptimize_js_aggregate" ).prop('checked') == false ) {
598
  jQuery( "#min_excl_row" ).hide();
599
  }
 
600
  }
601
  });
602
 
@@ -650,9 +655,6 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
650
  if (!jQuery("#autoptimize_css_aggregate").prop('checked')) {
651
  jQuery(".css_aggregate:visible").fadeTo('fast',.33);
652
  }
653
- if (!jQuery("#autoptimize_js").prop('checked')) {
654
- jQuery(".js_sub:visible").fadeTo('fast',.33);
655
- }
656
  if (jQuery("#autoptimize_js_aggregate").prop('checked')) {
657
  jQuery( ".js_aggregate" ).show( 'fast' );
658
  jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'fast', .33 );
@@ -664,6 +666,31 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
664
  if (jQuery("#autoptimize_enable_site_config").prop('checked')) {
665
  jQuery("li.itemDetail:not(.multiSite)").fadeTo('fast',.33);
666
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667
  }
668
  </script>
669
  </div>
@@ -769,19 +796,19 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
769
  'autoptimize_html_keepcomments' => 0,
770
  'autoptimize_enable_site_config' => 1,
771
  'autoptimize_js' => 0,
772
- 'autoptimize_js_aggregate' => 1,
773
- 'autoptimize_js_defer_not_aggregate' => 0,
774
- 'autoptimize_js_defer_inline' => 0,
775
- 'autoptimize_js_exclude' => 'wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.js, js/jquery/jquery.min.js',
776
  'autoptimize_js_trycatch' => 0,
777
  'autoptimize_js_justhead' => 0,
778
  'autoptimize_js_include_inline' => 0,
779
  'autoptimize_js_forcehead' => 0,
780
  'autoptimize_css' => 0,
781
- 'autoptimize_css_aggregate' => 1,
782
- 'autoptimize_css_exclude' => 'admin-bar.min.css, dashicons.min.css, wp-content/cache/, wp-content/uploads/',
783
  'autoptimize_css_justhead' => 0,
784
- 'autoptimize_css_include_inline' => 1,
785
  'autoptimize_css_defer' => 0,
786
  'autoptimize_css_defer_inline' => '',
787
  'autoptimize_css_inline' => 0,
@@ -812,6 +839,7 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
812
  'autoptimize_extra_text_field_2' => '',
813
  'autoptimize_extra_text_field_3' => '',
814
  'autoptimize_extra_text_field_7' => '',
 
815
  );
816
 
817
  return $defaults;
@@ -985,15 +1013,40 @@ if ( true === autoptimizeImages::imgopt_active() && true === apply_filters( 'aut
985
  global $wp_query;
986
  if ( isset( $wp_query ) && ( is_page() || is_single() ) ) {
987
  $_meta_value = get_post_meta( get_the_ID(), 'ao_post_optimize', true );
988
- } else {
989
  $_meta_value = false;
990
  }
991
  }
992
 
993
- // If autoptimize_post_optimize !== 'on' then always return false as all is off.
994
- if ( ! empty( $_meta_value ) && is_array( $_meta_value ) && ( ( array_key_exists( 'autoptimize_post_optimize', $_meta_value ) && 'on' !== $_meta_value['autoptimize_post_optimize'] ) || ( array_key_exists( $optim, $_meta_value ) && 'on' !== $_meta_value[$optim] ) ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
995
  return false;
996
  } else {
 
 
 
 
997
  return true;
998
  }
999
  }
226
  <table class="form-table">
227
  <tr valign="top">
228
  <th scope="row"><?php _e( 'Optimize JavaScript Code?', 'autoptimize' ); ?></th>
229
+ <td><input type="checkbox" id="autoptimize_js" name="autoptimize_js" <?php echo $conf->get( 'autoptimize_js' ) ? 'checked="checked" ' : ''; ?>/></td>
230
  </tr>
231
  <tr valign="top" class="js_sub js_aggregate_master">
232
  <th scope="row"><?php _e( 'Aggregate JS-files?', 'autoptimize' ); ?></th>
235
  </tr>
236
  <tr valign="top" class="js_sub js_aggregate hidden">
237
  <th scope="row">&emsp;<?php _e( 'Also aggregate inline JS?', 'autoptimize' ); ?></th>
238
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_include_inline" <?php echo $conf->get( 'autoptimize_js_include_inline' ) ? 'checked="checked" ' : ''; ?>/>
239
  <?php _e( 'Let Autoptimize also extract JS from the HTML (discouraged as it can make Autoptimize\'s cache size grow quickly)', 'autoptimize' ); ?></label></td>
240
  </tr>
241
  <tr valign="top" class="js_sub js_aggregate hidden">
242
  <th scope="row">&emsp;<?php _e( 'Force JavaScript in &lt;head&gt;?', 'autoptimize' ); ?></th>
243
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_forcehead" <?php echo $conf->get( 'autoptimize_js_forcehead' ) ? 'checked="checked" ' : ''; ?>/>
244
  <?php _e( 'Load JavaScript early (discouraged as it makes the JS render blocking)', 'autoptimize' ); ?></label></td>
245
  </tr>
246
  <tr valign="top" class="js_sub js_aggregate hidden">
247
  <th scope="row">&emsp;<?php _e( 'Add try-catch wrapping?', 'autoptimize' ); ?></th>
248
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_trycatch" <?php echo $conf->get( 'autoptimize_js_trycatch' ) ? 'checked="checked" ' : ''; ?>/>
249
  <?php _e( 'If your aggregated scripts break because of a JS-error, you might want to try this, but generally discouraged.', 'autoptimize' ); ?></label></td>
250
  </tr>
251
  <tr valign="top" class="js_sub js_not_aggregate_master">
255
  </tr>
256
  <tr valign="top" id="js_defer_inline" class="js_sub js_not_aggregate hidden">
257
  <th scope="row">&emsp;<?php _e( 'Also defer inline JS?', 'autoptimize' ); ?></th>
258
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_defer_inline" <?php echo $conf->get( 'autoptimize_js_defer_inline' ) ? 'checked="checked" ' : ''; ?>/>
259
  <?php _e( 'Also defer inline JS. Generally this will allow all JS to be deferred, so you should remove default exclusions, test and only exclude specific items if still needed.', 'autoptimize' ); ?></label></td>
260
  </tr>
261
  <?php if ( autoptimizeOptionWrapper::get_option( 'autoptimize_js_justhead' ) ) { ?>
266
  echo ' <i>' . __( '(deprecated)', 'autoptimize' ) . '</i>';
267
  ?>
268
  </th>
269
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_js_justhead" <?php echo $conf->get( 'autoptimize_js_justhead' ) ? 'checked="checked" ' : ''; ?>/>
270
  <?php _e( 'Mostly useful in combination with previous option when using jQuery-based templates, but might help keeping cache size under control.', 'autoptimize' ); ?></label></td>
271
  </tr>
272
  <?php } ?>
273
  <tr valign="top" class="js_sub">
274
  <th scope="row"><?php _e( 'Exclude scripts from Autoptimize:', 'autoptimize' ); ?></th>
275
+ <td><label><input type="text" style="width:100%;" name="autoptimize_js_exclude" value="<?php echo esc_attr( autoptimizeOptionWrapper::get_option( 'autoptimize_js_exclude', '' ) ); ?>"/><br />
276
  <?php
277
  echo __( 'A comma-separated list of scripts you do not want optimized, for example \'whatever.js, my_var\' (without the quotes).', 'autoptimize' ) . ' ' . __( 'Important: when "aggregate JS-files" is on, excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
278
  ?>
290
  <table class="form-table">
291
  <tr valign="top">
292
  <th scope="row"><?php _e( 'Optimize CSS Code?', 'autoptimize' ); ?></th>
293
+ <td><input type="checkbox" id="autoptimize_css" name="autoptimize_css" <?php echo $conf->get( 'autoptimize_css' ) ? 'checked="checked" ' : ''; ?>/></td>
294
  </tr>
295
  <tr class="css_sub" valign="top">
296
  <th scope="row"><?php _e( 'Aggregate CSS-files?', 'autoptimize' ); ?></th>
299
  </tr>
300
  <tr valign="top" class="css_sub css_aggregate">
301
  <th scope="row"><?php _e( 'Also aggregate inline CSS?', 'autoptimize' ); ?></th>
302
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_include_inline" <?php echo $conf->get( 'autoptimize_css_include_inline', '1' ) ? 'checked="checked" ' : ''; ?>/>
303
  <?php _e( 'Check this option for Autoptimize to also aggregate CSS in the HTML.', 'autoptimize' ); ?></label></td>
304
  </tr>
305
  <tr class="css_sub css_aggregate" valign="top">
306
  <th scope="row"><?php _e( 'Generate data: URIs for images?', 'autoptimize' ); ?></th>
307
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_datauris" <?php echo $conf->get( 'autoptimize_css_datauris' ) ? 'checked="checked" ' : ''; ?>/>
308
  <?php _e( 'Enable this to include small background-images in the CSS itself instead of as separate downloads.', 'autoptimize' ); ?></label></td>
309
  </tr>
310
  <?php if ( autoptimizeOptionWrapper::get_option( 'autoptimize_css_justhead' ) ) { ?>
315
  echo ' <i>' . __( '(deprecated)', 'autoptimize' ) . '</i>';
316
  ?>
317
  </th>
318
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_justhead" <?php echo $conf->get( 'autoptimize_css_justhead' ) ? 'checked="checked" ' : ''; ?>/>
319
  <?php _e( 'Don\'t autoptimize CSS outside the head-section. If the cache gets big, you might want to enable this.', 'autoptimize' ); ?></label></td>
320
  </tr>
321
  <?php } ?>
322
  <tr valign="top" class="css_sub">
323
  <th scope="row"><?php _e( 'Eliminate render-blocking CSS?', 'autoptimize' ); ?></th>
324
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_css_defer" id="autoptimize_css_defer" <?php echo $conf->get( 'autoptimize_css_defer' ) ? 'checked="checked" ' : ''; ?>/>
325
  <?php
326
  _e( 'Inline "above the fold CSS" while loading the main autoptimized CSS only after page load. <a href="https://wordpress.org/plugins/autoptimize/faq/" target="_blank">Check the FAQ</a> for more info.', 'autoptimize' );
327
  echo ' ';
328
  $critcss_settings_url = get_admin_url( null, 'options-general.php?page=ao_critcss' );
329
  // translators: links "autoptimize critical CSS" tab.
330
+ echo sprintf( __( 'You can manually create rules for different types of pages or have this done fully automated on the %s tab.', 'autoptimize' ), '<a href="' . $critcss_settings_url . '">CriticalCSS</a>' );
331
  ?>
332
  </label></td>
333
  </tr>
337
  </tr>
338
  <tr valign="top" class="css_sub css_aggregate">
339
  <th scope="row"><?php _e( 'Inline all CSS?', 'autoptimize' ); ?></th>
340
+ <td><label class="cb_label"><input type="checkbox" id="autoptimize_css_inline" name="autoptimize_css_inline" <?php echo $conf->get( 'autoptimize_css_inline' ) ? 'checked="checked" ' : ''; ?>/>
341
  <?php _e( 'Inlining all CSS is an easy way to stop the CSS from being render-blocking, but is generally not recommended because the size of the HTML increases significantly. Additionally it might push meta-tags down to a position where e.g. Facebook and Whatsapp will not find them any more, breaking thumbnails when sharing.', 'autoptimize' ); ?></label></td>
342
  </tr>
343
  <tr valign="top" class="css_sub">
344
  <th scope="row"><?php _e( 'Exclude CSS from Autoptimize:', 'autoptimize' ); ?></th>
345
+ <td><label><input type="text" style="width:100%;" name="autoptimize_css_exclude" value="<?php echo esc_attr( $conf->get( 'autoptimize_css_exclude', '' ) ); ?>"/><br />
346
  <?php
347
  echo __( 'A comma-separated list of CSS you want to exclude from being optimized.', 'autoptimize' ) . ' ' . __( 'Important: excluded non-minified files are still minified by Autoptimize unless that option under "misc" is disabled.', 'autoptimize' );
348
  ?>
365
  <table class="form-table">
366
  <tr valign="top">
367
  <th scope="row"><?php _e( 'Optimize HTML Code?', 'autoptimize' ); ?></th>
368
+ <td><input type="checkbox" id="autoptimize_html" name="autoptimize_html" <?php echo $conf->get( 'autoptimize_html' ) ? 'checked="checked" ' : ''; ?>/></td>
369
  </tr>
370
  <tr class="html_sub" valign="top">
371
  <th scope="row"><?php _e( 'Keep HTML comments?', 'autoptimize' ); ?></th>
372
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_html_keepcomments" <?php echo $conf->get( 'autoptimize_html_keepcomments' ) ? 'checked="checked" ' : ''; ?>/>
373
+ <?php _e( 'Enable this if you want HTML comments to remain in the page or if you want the inline CSS/ JS not to be minified.', 'autoptimize' ); ?></label></td>
374
  </tr>
375
  </table>
376
  </li>
435
  <table class="form-table">
436
  <tr valign="top" >
437
  <th scope="row"><?php _e( 'Save aggregated script/css as static files?', 'autoptimize' ); ?></th>
438
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_nogzip" <?php echo $conf->get( 'autoptimize_cache_nogzip') ? 'checked="checked" ' : ''; ?>/>
439
  <?php _e( 'By default files saved are static css/js, uncheck this option if your webserver doesn\'t properly handle the compression and expiry.', 'autoptimize' ); ?></label></td>
440
  </tr>
441
  <?php
446
  ?>
447
  <tr valign="top" id="min_excl_row" class="<?php echo $_min_excl_class; ?>">
448
  <th scope="row"><?php _e( 'Minify excluded CSS and JS files?', 'autoptimize' ); ?></th>
449
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_minify_excluded" <?php echo $conf->get( 'autoptimize_minify_excluded' ) ? 'checked="checked" ' : ''; ?>/>
450
  <?php _e( 'When aggregating JS or CSS, excluded files that are not minified (based on filename) are by default minified by Autoptimize despite being excluded. Uncheck this option if anything breaks despite excluding.', 'autoptimize' ); ?></label></td>
451
  </tr>
452
  <tr valign="top">
453
  <th scope="row"><?php _e( 'Enable 404 fallbacks?', 'autoptimize' ); ?></th>
454
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_cache_fallback" <?php echo $conf->get( 'autoptimize_cache_fallback' ) ? 'checked="checked" ' : ''; ?>/>
455
  <?php _e( 'Sometimes Autoptimized JS/ CSS is referenced in cached HTML but is already removed, resulting in broken sites. With this option on, Autoptimize will try to redirect those not-found files to "fallback"-versions, keeping the page/ site somewhat intact. In some cases this will require extra web-server level configuration to ensure <code>wp-content/autoptimize_404_handler.php</code> is set to handle 404\'s in <code>wp-content/cache/autoptimize</code>.', 'autoptimize' ); ?></label></td>
456
  </tr>
457
  <tr valign="top">
458
  <th scope="row"><?php _e( 'Also optimize for logged in editors/ administrators?', 'autoptimize' ); ?></th>
459
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_logged" <?php echo $conf->get( 'autoptimize_optimize_logged' ) ? 'checked="checked" ' : ''; ?>/>
460
  <?php _e( 'By default Autoptimize is also active for logged on editors/ administrators, uncheck this option if you don\'t want Autoptimize to optimize when logged in e.g. to use a pagebuilder.', 'autoptimize' ); ?></label></td>
461
  </tr>
462
  <?php
464
  ?>
465
  <tr valign="top" >
466
  <th scope="row"><?php _e( 'Also optimize shop cart/ checkout?', 'autoptimize' ); ?></th>
467
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_optimize_checkout" <?php echo $conf->get( 'autoptimize_optimize_checkout' ) ? 'checked="checked" ' : ''; ?>/>
468
  <?php _e( 'By default Autoptimize is also active on your shop\'s cart/ checkout, uncheck not to optimize those.', 'autoptimize' ); ?></label>
469
  </td>
470
  </tr>
474
  ?>
475
  <tr valign="top">
476
  <th scope="row"><?php _e( 'Enable configuration per post/ page?', 'autoptimize' ); ?></th>
477
+ <td><label class="cb_label"><input type="checkbox" name="autoptimize_enable_meta_ao_settings" <?php echo $conf->get( 'autoptimize_enable_meta_ao_settings' ) ? 'checked="checked" ' : ''; ?>/>
478
  <?php _e( 'Add a "metabox" to the post/ page edit screen allowing different optimizations to be turned off on a per post/ page level?', 'autoptimize' ); ?></label></td>
479
  </tr>
480
  <?php } ?>
558
  jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'slow', .33 ); // grey out "not aggregate"
559
  jQuery( ".js_not_aggregate" ).hide( 'slow' ); // hide not aggregate sub-items
560
  jQuery( "#min_excl_row" ).show(); // make sure "minify excluded" is visible
561
+ check_exclusions( "js", "on" );
562
  } else {
563
  jQuery( ".js_aggregate" ).hide( 'slow' ); // hide sub-itmes
564
  jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'slow', 1 ); // un-grey-out "not aggregate"
565
  if ( jQuery( "#autoptimize_css_aggregate" ).prop( 'checked' ) == false ) { // hide "minify excluded"
566
  jQuery( "#min_excl_row" ).hide();
567
  }
568
+ check_exclusions( "js", "off" );
569
  }
570
  });
571
 
576
  jQuery( ".js_not_aggregate" ).show( 'slow'); // show sub-items
577
  jQuery( ".js_aggregate_master:visible" ).fadeTo( 'slow', .33 ); // grey out "aggregate"
578
  jQuery( ".js_aggregate" ).hide( 'slow' ); // hide aggregate sub-items
579
+ check_exclusions( "js", "off" );
580
  } else {
581
  jQuery( ".js_not_aggregate" ).hide( 'slow' ); // hide sub-items
582
  jQuery( ".js_aggregate_master:visible" ).fadeTo( 'slow', 1 ); // un-grey-out "aggregate"
595
  if (this.checked && jQuery("#autoptimize_css").prop('checked')) {
596
  jQuery(".css_aggregate:visible").fadeTo("fast",1);
597
  jQuery( "#min_excl_row" ).show();
598
+ check_exclusions( "css", "on" );
599
  } else {
600
  jQuery(".css_aggregate:visible").fadeTo("fast",.33);
601
  if ( jQuery( "#autoptimize_js_aggregate" ).prop('checked') == false ) {
602
  jQuery( "#min_excl_row" ).hide();
603
  }
604
+ check_exclusions( "css", "off" );
605
  }
606
  });
607
 
655
  if (!jQuery("#autoptimize_css_aggregate").prop('checked')) {
656
  jQuery(".css_aggregate:visible").fadeTo('fast',.33);
657
  }
 
 
 
658
  if (jQuery("#autoptimize_js_aggregate").prop('checked')) {
659
  jQuery( ".js_aggregate" ).show( 'fast' );
660
  jQuery( ".js_not_aggregate_master:visible" ).fadeTo( 'fast', .33 );
666
  if (jQuery("#autoptimize_enable_site_config").prop('checked')) {
667
  jQuery("li.itemDetail:not(.multiSite)").fadeTo('fast',.33);
668
  }
669
+ if (!jQuery("#autoptimize_js").prop('checked')) {
670
+ jQuery(".js_sub:visible").fadeTo('fast',.33);
671
+ }
672
+ }
673
+
674
+ function check_exclusions( what, state ) {
675
+ exclusion_node = 'input[name="autoptimize_' + what + '_exclude"]';
676
+ current_exclusion = jQuery( exclusion_node ).val();
677
+
678
+ if ( what == "js" ) {
679
+ default_exclusion = ", wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.min.js";
680
+ } else if ( what == "css") {
681
+ default_exclusion = ", admin-bar.min.css, dashicons.min.css, wp-content/cache/, wp-content/uploads/";
682
+ }
683
+
684
+ default_in_current = current_exclusion.indexOf(default_exclusion);
685
+
686
+ if ( state == "on" && default_in_current == -1 ) {
687
+ jQuery( exclusion_node ).val( current_exclusion + default_exclusion );
688
+ } else if ( state = "off" && current_exclusion == default_exclusion ) {
689
+ jQuery( exclusion_node ).val( "" );
690
+ } else if ( state = "off" && default_in_current != -1 ) {
691
+ new_exclusion = current_exclusion.substring( 0, default_in_current) + current_exclusion.substring( default_in_current + default_exclusion.length, current_exclusion.length );
692
+ jQuery( exclusion_node ).val( new_exclusion );
693
+ }
694
  }
695
  </script>
696
  </div>
796
  'autoptimize_html_keepcomments' => 0,
797
  'autoptimize_enable_site_config' => 1,
798
  'autoptimize_js' => 0,
799
+ 'autoptimize_js_aggregate' => 0,
800
+ 'autoptimize_js_defer_not_aggregate' => 1,
801
+ 'autoptimize_js_defer_inline' => 1,
802
+ 'autoptimize_js_exclude' => '', // 'wp-includes/js/dist/, wp-includes/js/tinymce/, js/jquery/jquery.min.js',
803
  'autoptimize_js_trycatch' => 0,
804
  'autoptimize_js_justhead' => 0,
805
  'autoptimize_js_include_inline' => 0,
806
  'autoptimize_js_forcehead' => 0,
807
  'autoptimize_css' => 0,
808
+ 'autoptimize_css_aggregate' => 0,
809
+ 'autoptimize_css_exclude' => '', // admin-bar.min.css, dashicons.min.css, wp-content/cache/, wp-content/uploads/',
810
  'autoptimize_css_justhead' => 0,
811
+ 'autoptimize_css_include_inline' => 0,
812
  'autoptimize_css_defer' => 0,
813
  'autoptimize_css_defer_inline' => '',
814
  'autoptimize_css_inline' => 0,
839
  'autoptimize_extra_text_field_2' => '',
840
  'autoptimize_extra_text_field_3' => '',
841
  'autoptimize_extra_text_field_7' => '',
842
+ 'autoptimize_extra_checkbox_field_8' => '0',
843
  );
844
 
845
  return $defaults;
1013
  global $wp_query;
1014
  if ( isset( $wp_query ) && ( is_page() || is_single() ) ) {
1015
  $_meta_value = get_post_meta( get_the_ID(), 'ao_post_optimize', true );
1016
+ } else if ( isset( $wp_query ) ) {
1017
  $_meta_value = false;
1018
  }
1019
  }
1020
 
1021
+ // If autoptimize_post_optimize !== 'on' (except for ao_post_preload, which can have other values) then always return false as all is off.
1022
+ // fixme: need unit tests to ensure below logic is sane!
1023
+ if ( empty( $_meta_value ) || ! is_array( $_meta_value ) ) {
1024
+ // no metabox values so all optimizations are a go.
1025
+ if ( in_array( $optim, array( 'ao_post_preload' ) ) ) {
1026
+ // but make sure to return false for text input.
1027
+ return false;
1028
+ }
1029
+ return true;
1030
+ } else if ( array_key_exists( 'ao_post_optimize', $_meta_value ) && 'on' !== $_meta_value['ao_post_optimize'] ) {
1031
+ // ao entirely off for this page.
1032
+ return false;
1033
+ } else if ( array_key_exists( $optim, $_meta_value ) && empty( $_meta_value[$optim] ) ) {
1034
+ // sub-optimization off for this page.
1035
+ return false;
1036
+ } else if ( array_key_exists( $optim, $_meta_value ) && 'on' === $_meta_value[$optim] ) {
1037
+ // sub-optimization is explictly on.
1038
+ return true;
1039
+ } else if ( array_key_exists( $optim, $_meta_value ) && in_array( $optim, array( 'ao_post_preload' ) ) && ! empty( $_meta_value[$optim] ) ) {
1040
+ // a non-bool metabox optimization (currently only preload field), return value instead of bool.
1041
+ return $_meta_value[$optim];
1042
+ } else if ( in_array( $optim, array( 'ao_post_preload' ) ) && ( ! array_key_exists( $optim, $_meta_value ) || empty( $_meta_value[$optim] ) ) ) {
1043
+ // a non-bool metabox optimization not found or empty, so returning false.
1044
  return false;
1045
  } else {
1046
+ // when in doubt "go" for optimization, but this should never happen?
1047
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
1048
+ error_log( 'AO metabox logic fallback; well, how did I get here? Maybe this helps: looking for ' . $optim . ' in ' . json_encode( $_meta_value ) );
1049
+ }
1050
  return true;
1051
  }
1052
  }
classes/autoptimizeCriticalCSSBase.php CHANGED
@@ -16,6 +16,41 @@ class autoptimizeCriticalCSSBase {
16
  */
17
  protected $filepath = null;
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  public function __construct()
20
  {
21
  // define constant, but only once.
@@ -35,7 +70,7 @@ class autoptimizeCriticalCSSBase {
35
  // Define constants for criticalcss.com base path and API endpoints.
36
  // fixme: AO_CCSS_URL should be read from the autoptimize availability json stored as option.
37
  define( 'AO_CCSS_URL', 'https://criticalcss.com' );
38
- define( 'AO_CCSS_API', AO_CCSS_URL . '/api/premium/' );
39
  define( 'AO_CCSS_SLEEP', 10 );
40
  }
41
 
@@ -51,118 +86,243 @@ class autoptimizeCriticalCSSBase {
51
  }
52
 
53
  $this->filepath = __FILE__;
54
-
55
- $this->setup();
56
- $this->load_requires();
57
  }
58
 
59
- public function setup()
60
- {
61
- // get all options.
62
- $all_options = $this->fetch_options();
63
- foreach ( $all_options as $option => $value ) {
64
- ${$option} = $value;
65
- }
66
-
67
- // make sure the 10 minutes cron schedule is added.
68
- add_filter( 'cron_schedules', array( $this, 'ao_ccss_interval' ) );
69
-
70
  // check if we need to upgrade.
71
  $this->check_upgrade();
72
 
73
- // make sure ao_ccss_queue is scheduled OK if an API key is set.
74
- if ( isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) && ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
75
- wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
  }
78
 
79
  public function load_requires() {
80
  // Required libs, core is always needed.
81
- $criticalcss_core = new autoptimizeCriticalCSSCore();
82
 
83
- if ( defined( 'WP_CLI' ) || defined( 'DOING_CRON' ) || is_admin() ) {
84
- // TODO: also include if overridden somehow to force queue processing to be executed?
85
- $criticalcss_cron = new autoptimizeCriticalCSSCron();
86
  }
87
 
88
  if ( is_admin() ) {
89
- $criticalcss_settings = new autoptimizeCriticalCSSSettings();
90
- } else {
91
- // enqueuing only done when not wp-admin.
92
- $criticalcss_enqueue = new autoptimizeCriticalCSSEnqueue();
93
- }
94
- }
95
-
96
- public static function fetch_options() {
97
- static $autoptimize_ccss_options = null;
98
-
99
- if ( null === $autoptimize_ccss_options ) {
100
- // not cached yet, fetching from WordPress options.
101
- $autoptimize_ccss_options['ao_css_defer'] = autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' );
102
- $autoptimize_ccss_options['ao_css_defer_inline'] = autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer_inline' );
103
- $autoptimize_ccss_options['ao_ccss_rules_raw'] = get_option( 'autoptimize_ccss_rules', false );
104
- $autoptimize_ccss_options['ao_ccss_additional'] = get_option( 'autoptimize_ccss_additional' );
105
- $autoptimize_ccss_options['ao_ccss_queue_raw'] = get_option( 'autoptimize_ccss_queue', false );
106
- $autoptimize_ccss_options['ao_ccss_viewport'] = get_option( 'autoptimize_ccss_viewport', false );
107
- $autoptimize_ccss_options['ao_ccss_finclude'] = get_option( 'autoptimize_ccss_finclude', false );
108
- $autoptimize_ccss_options['ao_ccss_rtimelimit'] = get_option( 'autoptimize_ccss_rtimelimit', '30' );
109
- $autoptimize_ccss_options['ao_ccss_noptimize'] = get_option( 'autoptimize_ccss_noptimize', false );
110
- $autoptimize_ccss_options['ao_ccss_debug'] = get_option( 'autoptimize_ccss_debug', false );
111
- $autoptimize_ccss_options['ao_ccss_key'] = apply_filters( 'autoptimize_filter_ccss_key', get_option( 'autoptimize_ccss_key' ) );
112
- $autoptimize_ccss_options['ao_ccss_keyst'] = get_option( 'autoptimize_ccss_keyst' );
113
- $autoptimize_ccss_options['ao_ccss_loggedin'] = get_option( 'autoptimize_ccss_loggedin', '1' );
114
- $autoptimize_ccss_options['ao_ccss_forcepath'] = get_option( 'autoptimize_ccss_forcepath', '1' );
115
- $autoptimize_ccss_options['ao_ccss_servicestatus'] = get_option( 'autoptimize_service_availablity' );
116
- $autoptimize_ccss_options['ao_ccss_deferjquery'] = get_option( 'autoptimize_ccss_deferjquery', false );
117
- $autoptimize_ccss_options['ao_ccss_domain'] = get_option( 'autoptimize_ccss_domain' );
118
- $autoptimize_ccss_options['ao_ccss_unloadccss'] = get_option( 'autoptimize_ccss_unloadccss', false );
119
-
120
- if ( strpos( $autoptimize_ccss_options['ao_ccss_domain'], 'http' ) === false && strpos( $autoptimize_ccss_options['ao_ccss_domain'], 'uggc' ) === 0 ) {
121
- $autoptimize_ccss_options['ao_ccss_domain'] = str_rot13( $autoptimize_ccss_options['ao_ccss_domain'] );
122
- } elseif ( strpos( $autoptimize_ccss_options['ao_ccss_domain'], 'http' ) !== false ) {
123
- // not rot13'ed yet, do so now (goal; avoid migration plugins change the bound domain).
124
- update_option( 'autoptimize_ccss_domain', str_rot13( $autoptimize_ccss_options['ao_ccss_domain'] ) );
125
- }
126
 
127
- // Setup the rules array.
128
- if ( empty( $autoptimize_ccss_options['ao_ccss_rules_raw'] ) ) {
129
- $autoptimize_ccss_options['ao_ccss_rules']['paths'] = array();
130
- $autoptimize_ccss_options['ao_ccss_rules']['types'] = array();
131
- } else {
132
- $autoptimize_ccss_options['ao_ccss_rules'] = json_decode( $autoptimize_ccss_options['ao_ccss_rules_raw'], true );
133
- }
 
 
 
 
134
 
135
- // Setup the queue array.
136
- if ( empty( $autoptimize_ccss_options['ao_ccss_queue_raw'] ) ) {
137
- $autoptimize_ccss_options['ao_ccss_queue'] = array();
138
- } else {
139
- $autoptimize_ccss_options['ao_ccss_queue'] = json_decode( $autoptimize_ccss_options['ao_ccss_queue_raw'], true );
140
- }
 
 
141
 
142
- // Override API key if constant is defined.
143
- if ( defined( 'AUTOPTIMIZE_CRITICALCSS_API_KEY' ) ) {
144
- $autoptimize_ccss_options['ao_ccss_key'] = AUTOPTIMIZE_CRITICALCSS_API_KEY;
145
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  }
147
 
148
- return $autoptimize_ccss_options;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  }
150
 
151
  public function on_upgrade() {
152
- global $ao_ccss_key;
153
 
154
  // Create the cache directory if it doesn't exist already.
155
  if ( ! file_exists( AO_CCSS_DIR ) ) {
156
- mkdir( AO_CCSS_DIR, 0755, true );
157
  }
158
 
159
  // Create a scheduled event for the queue.
160
- if ( isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) && ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
161
  wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
162
  }
163
 
164
  // Create a scheduled event for log maintenance.
165
- if ( isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) && ! wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
166
  wp_schedule_event( time(), 'twicedaily', 'ao_ccss_maintenance' );
167
  }
168
  }
@@ -181,6 +341,7 @@ class autoptimizeCriticalCSSBase {
181
  // Let interval be configurable.
182
  if ( ! defined( 'AO_CCSS_DEBUG_INTERVAL' ) ) {
183
  $intsec = 600;
 
184
  } else {
185
  $intsec = AO_CCSS_DEBUG_INTERVAL;
186
  if ( $intsec >= 120 ) {
@@ -188,14 +349,81 @@ class autoptimizeCriticalCSSBase {
188
  } else {
189
  $inttxt = $intsec . ' second(s)';
190
  }
191
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Using custom WP-Cron interval of ' . $inttxt, 3 );
192
  }
193
 
194
  // Attach interval to schedule.
195
  $schedules['ao_ccss'] = array(
196
  'interval' => $intsec,
197
- 'display' => __( 'Autoptimize CriticalCSS' ),
198
  );
199
  return $schedules;
200
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  }
16
  */
17
  protected $filepath = null;
18
 
19
+ /**
20
+ * Critical CSS options
21
+ *
22
+ * @var array
23
+ */
24
+ protected $_options = null;
25
+
26
+ /**
27
+ * Core object
28
+ *
29
+ * @var object
30
+ */
31
+ protected $_core = null;
32
+
33
+ /**
34
+ * Cron object
35
+ *
36
+ * @var object
37
+ */
38
+ protected $_cron = null;
39
+
40
+ /**
41
+ * Settings object
42
+ *
43
+ * @var object
44
+ */
45
+ protected $_settings = null;
46
+
47
+ /**
48
+ * Enqueue object
49
+ *
50
+ * @var object
51
+ */
52
+ protected $_enqueue = null;
53
+
54
  public function __construct()
55
  {
56
  // define constant, but only once.
70
  // Define constants for criticalcss.com base path and API endpoints.
71
  // fixme: AO_CCSS_URL should be read from the autoptimize availability json stored as option.
72
  define( 'AO_CCSS_URL', 'https://criticalcss.com' );
73
+ define( 'AO_CCSS_API', apply_filters( 'autoptimize_filter_ccss_service_url', AO_CCSS_URL . '/api/premium/' ) );
74
  define( 'AO_CCSS_SLEEP', 10 );
75
  }
76
 
86
  }
87
 
88
  $this->filepath = __FILE__;
89
+
90
+ // Add keychecker action for scheduled use.
91
+ add_action( 'ao_ccss_keychecker', array( $this, 'ao_ccss_check_key' ) );
92
  }
93
 
94
+ public function setup() {
 
 
 
 
 
 
 
 
 
 
95
  // check if we need to upgrade.
96
  $this->check_upgrade();
97
 
98
+ // add/ remove scheduled jobs.
99
+ if ( $this->is_api_active() ) {
100
+ if ( ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
101
+ // make sure the 10 minutes cron schedule is added.
102
+ add_filter( 'cron_schedules', array( $this, 'ao_ccss_interval' ) );
103
+
104
+ // make sure ao_ccss_queue is scheduled OK if an API key is active.
105
+ wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
106
+ }
107
+
108
+ if ( ! wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
109
+ // and schedule maintenance job.
110
+ wp_schedule_event( time(), 'twicedaily', 'ao_ccss_maintenance' );
111
+ }
112
+
113
+ if ( wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
114
+ // api is active now, no need to check key as it is checked by using the API.
115
+ wp_clear_scheduled_hook( 'ao_ccss_keychecker' );
116
+ }
117
+ } else {
118
+ if ( wp_next_scheduled( 'ao_ccss_queue' ) ) {
119
+ wp_clear_scheduled_hook( 'ao_ccss_queue' );
120
+ }
121
+
122
+ if ( wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
123
+ wp_clear_scheduled_hook( 'ao_ccss_maintenance' );
124
+ }
125
+
126
+ // add keychecker logic if api is not active but we have a key so maybe this is a temporary issue, check if key is OK daily.
127
+ $ao_ccss_key = $this->get_option( 'key' );
128
+ if ( ! empty( $ao_ccss_key ) && ! wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
129
+ wp_schedule_event( time(), 'twicedaily', 'ao_ccss_keychecker' );
130
+ } else if ( empty( $ao_ccss_key ) && wp_next_scheduled( 'ao_ccss_keychecker' ) ) {
131
+ // edge case: we had a inactive key that was checked daily, but it is now removed, so remove keychecker from schedule.
132
+ wp_clear_scheduled_hook( 'ao_ccss_keychecker' );
133
+ }
134
+ }
135
+
136
+ // check/ create AO_CCSS_DIR.
137
+ if ( ! file_exists( AO_CCSS_DIR ) ) {
138
+ $this->create_ao_ccss_dir();
139
  }
140
  }
141
 
142
  public function load_requires() {
143
  // Required libs, core is always needed.
144
+ $this->_core = new autoptimizeCriticalCSSCore();
145
 
146
+ if ( ( defined( 'WP_CLI' ) || defined( 'DOING_CRON' ) || is_admin() ) && $this->is_api_active() ) {
147
+ // cron only initiated when doing cron (or wp_cli or is_amdin) and when we have an active API key.
148
+ $this->_cron = new autoptimizeCriticalCSSCron();
149
  }
150
 
151
  if ( is_admin() ) {
152
+ $this->_settings = new autoptimizeCriticalCSSSettings();
153
+ } else if ( $this->is_api_active() ) {
154
+ // enqueuing only done when not wp-admin and when API is active.
155
+ $this->_enqueue = new autoptimizeCriticalCSSEnqueue();
156
+ }
157
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
+ /**
160
+ * Log a message via CCSS Core object
161
+ *
162
+ * @param string $msg
163
+ * @param int $lvl
164
+ *
165
+ * @return void
166
+ */
167
+ public function log( $msg, $lvl ) {
168
+ return $this->_core->ao_ccss_log( $msg, $lvl );
169
+ }
170
 
171
+ /**
172
+ * Get viewport from CCSS Core object
173
+ *
174
+ * @return array
175
+ */
176
+ public function viewport() {
177
+ return $this->_core->ao_ccss_viewport();
178
+ }
179
 
180
+ /**
181
+ * Check CCSS contents from Core object
182
+ *
183
+ * @param string $ccss
184
+ *
185
+ * @return bool
186
+ */
187
+ public function check_contents( $ccss ) {
188
+ return $this->_core->ao_ccss_check_contents( $ccss );
189
+ }
190
+
191
+ /**
192
+ * Get key status from Core object
193
+ *
194
+ * @param bool $render
195
+ *
196
+ * @return array
197
+ */
198
+ public function key_status( $render ) {
199
+ return $this->_core->ao_ccss_key_status( $render );
200
+ }
201
+
202
+ /**
203
+ * Return valid types from core object
204
+ *
205
+ * @return array
206
+ */
207
+ public function get_types() {
208
+ return $this->_core->get_types();
209
+ }
210
+
211
+ /**
212
+ * Run enqueue in CCSS Enqueue object
213
+ */
214
+ public function enqueue( $hash = '', $path = '', $type = 'is_page' ) {
215
+ // Enqueue is sometimes required on wp-admin requests, load it just-in-time.
216
+ if ( is_null( $this->_enqueue ) && $this->is_api_active() ) {
217
+ $this->_enqueue = new autoptimizeCriticalCSSEnqueue();
218
  }
219
 
220
+ return $this->_enqueue->ao_ccss_enqueue( $hash, $path, $type );
221
+ }
222
+
223
+ /**
224
+ * Check auto-rules in CCSS Settings object
225
+ */
226
+ public function has_autorules() {
227
+ return $this->_settings->ao_ccss_has_autorules();
228
+ }
229
+
230
+ /**
231
+ * Get a Critical CSS option
232
+ *
233
+ * @param string $name The option name
234
+ *
235
+ * @return mixed
236
+ */
237
+ public function get_option( $name ) {
238
+ if ( is_null( $this->_options ) ) {
239
+ $this->fetch_options();
240
+ }
241
+
242
+ if ( isset( $this->_options[ $name ] ) ) {
243
+ return $this->_options[ $name ];
244
+ }
245
+
246
+ return null;
247
+ }
248
+
249
+ public function flush_options() {
250
+ $this->_options = null;
251
+ }
252
+
253
+ protected function fetch_options() {
254
+ if ( ! is_null( $this->_options ) ) {
255
+ return $this->_options;
256
+ }
257
+
258
+ $this->_options = array(
259
+ 'css_defer' => autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' ),
260
+ 'css_defer_inline' => autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer_inline' ),
261
+ 'rules_raw' => get_option( 'autoptimize_ccss_rules', false ),
262
+ 'additional' => get_option( 'autoptimize_ccss_additional' ),
263
+ 'queue_raw' => get_option( 'autoptimize_ccss_queue', false ),
264
+ 'viewport' => get_option( 'autoptimize_ccss_viewport', false ),
265
+ 'finclude' => get_option( 'autoptimize_ccss_finclude', false ),
266
+ 'rtimelimit' => get_option( 'autoptimize_ccss_rtimelimit', '30' ),
267
+ 'noptimize' => get_option( 'autoptimize_ccss_noptimize', false ),
268
+ 'debug' => get_option( 'autoptimize_ccss_debug', false ),
269
+ 'key' => apply_filters( 'autoptimize_filter_ccss_key', get_option( 'autoptimize_ccss_key' ) ),
270
+ 'keyst' => get_option( 'autoptimize_ccss_keyst' ),
271
+ 'loggedin' => get_option( 'autoptimize_ccss_loggedin', '1' ),
272
+ 'forcepath' => get_option( 'autoptimize_ccss_forcepath', '1' ),
273
+ 'servicestatus' => get_option( 'autoptimize_service_availablity' ),
274
+ 'deferjquery' => get_option( 'autoptimize_ccss_deferjquery', false ),
275
+ 'domain' => get_option( 'autoptimize_ccss_domain' ),
276
+ 'unloadccss' => get_option( 'autoptimize_ccss_unloadccss', false ),
277
+ );
278
+
279
+ if ( strpos( $this->_options['domain'], 'http' ) === false && strpos( $this->_options['domain'], 'uggc' ) === 0 ) {
280
+ $this->_options['domain'] = str_rot13( $this->_options['domain'] );
281
+ } elseif ( strpos( $this->_options['domain'], 'http' ) !== false ) {
282
+ // not rot13'ed yet, do so now (goal; avoid migration plugins change the bound domain).
283
+ update_option( 'autoptimize_ccss_domain', str_rot13( $this->_options['domain'] ) );
284
+ }
285
+
286
+ // Setup the rules array.
287
+ if ( empty( $this->_options['rules_raw'] ) ) {
288
+ $this->_options['rules'] = array(
289
+ 'paths' => array(),
290
+ 'types' => array(),
291
+ );
292
+ } else {
293
+ $this->_options['rules'] = json_decode( $this->_options['rules_raw'], true );
294
+ }
295
+
296
+ // Setup the queue array.
297
+ if ( empty( $this->_options['queue_raw'] ) ) {
298
+ $this->_options['queue'] = array();
299
+ } else {
300
+ $this->_options['queue'] = json_decode( $this->_options['queue_raw'], true );
301
+ }
302
+
303
+ // Override API key if constant is defined.
304
+ if ( defined( 'AUTOPTIMIZE_CRITICALCSS_API_KEY' ) ) {
305
+ $this->_options['key'] = AUTOPTIMIZE_CRITICALCSS_API_KEY;
306
+ }
307
+
308
+ return $this->_options;
309
  }
310
 
311
  public function on_upgrade() {
312
+ $key = $this->get_option( 'key' );
313
 
314
  // Create the cache directory if it doesn't exist already.
315
  if ( ! file_exists( AO_CCSS_DIR ) ) {
316
+ $this->create_ao_ccss_dir();
317
  }
318
 
319
  // Create a scheduled event for the queue.
320
+ if ( $this->is_api_active() && ! wp_next_scheduled( 'ao_ccss_queue' ) ) {
321
  wp_schedule_event( time(), apply_filters( 'ao_ccss_queue_schedule', 'ao_ccss' ), 'ao_ccss_queue' );
322
  }
323
 
324
  // Create a scheduled event for log maintenance.
325
+ if ( $this->is_api_active() && ! wp_next_scheduled( 'ao_ccss_maintenance' ) ) {
326
  wp_schedule_event( time(), 'twicedaily', 'ao_ccss_maintenance' );
327
  }
328
  }
341
  // Let interval be configurable.
342
  if ( ! defined( 'AO_CCSS_DEBUG_INTERVAL' ) ) {
343
  $intsec = 600;
344
+ $inttxt = '10 minutes';
345
  } else {
346
  $intsec = AO_CCSS_DEBUG_INTERVAL;
347
  if ( $intsec >= 120 ) {
349
  } else {
350
  $inttxt = $intsec . ' second(s)';
351
  }
352
+ $this->log( 'Using custom WP-Cron interval of ' . $inttxt, 3 );
353
  }
354
 
355
  // Attach interval to schedule.
356
  $schedules['ao_ccss'] = array(
357
  'interval' => $intsec,
358
+ 'display' => __( 'Every ' . $inttxt . ' (Autoptimize Crit. CSS)' ),
359
  );
360
  return $schedules;
361
  }
362
+
363
+ public function create_ao_ccss_dir() {
364
+ // Make sure dir to write ao_ccss exists and is writable.
365
+ if ( ! is_dir( AO_CCSS_DIR ) ) {
366
+ // TODO: use wp_mkdir_p()
367
+ $mkdirresp = @mkdir( AO_CCSS_DIR, 0775, true ); // @codingStandardsIgnoreLine
368
+ } else {
369
+ $mkdirresp = true;
370
+ }
371
+
372
+ // Make sure our index.html is there.
373
+ if ( ! is_file( AO_CCSS_DIR . 'index.html' ) ) {
374
+ $fileresp = file_put_contents( AO_CCSS_DIR . 'index.html', '<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>' );
375
+ } else {
376
+ $fileresp = true;
377
+ }
378
+
379
+ if ( true === $fileresp && true === $mkdirresp ) {
380
+ return true;
381
+ } else {
382
+ return false;
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Helper function to determine if there is an active API key.
388
+ *
389
+ * @return bool
390
+ */
391
+ public function is_api_active() {
392
+ // using options instead of more complex $this->key_status (which gave some dependancy issues ... ;-) ).
393
+ $ao_ccss_key = $this->get_option( 'key' );
394
+ $ao_ccss_keyst = $this->get_option( 'keyst' );
395
+
396
+ if ( ! empty( $ao_ccss_key ) && $ao_ccss_keyst && 2 == $ao_ccss_keyst ) {
397
+ return true;
398
+ }
399
+
400
+ return false;
401
+ }
402
+
403
+ /**
404
+ * Helper function to determine if a rule is MANUAL.
405
+ *
406
+ * @param array $rule
407
+ *
408
+ * @return bool
409
+ */
410
+ public function is_rule_manual( $rule ) {
411
+ if ( is_array( $rule ) && false == $rule['hash'] && false != $rule['file'] ) {
412
+ return true;
413
+ }
414
+
415
+ return false;
416
+ }
417
+
418
+ /**
419
+ * Scheduled action to check an inactive key. Not part of autoptimizeCriticalCSSCron.php
420
+ * to allow us to only load the main cron logic if we have an active key to begin with.
421
+ *
422
+ */
423
+ public function ao_ccss_check_key() {
424
+ $ao_ccss_key = $this->get_option( 'key' );
425
+ $_result = $this->_core->ao_ccss_key_validation( $ao_ccss_key );
426
+ $_resmsg = ( true === $_result) ? 'ok' : 'nok';
427
+ $this->log( 'Inactive key checked, result was ' . $_resmsg, 3 );
428
+ }
429
  }
classes/autoptimizeCriticalCSSCore.php CHANGED
@@ -9,94 +9,91 @@ if ( ! defined( 'ABSPATH' ) ) {
9
  }
10
 
11
  class autoptimizeCriticalCSSCore {
12
- public function __construct()
13
- {
14
- // fetch all options at once and populate them individually explicitely as globals.
15
- $all_options = autoptimizeCriticalCSSBase::fetch_options();
16
- foreach ( $all_options as $_option => $_value ) {
17
- global ${$_option};
18
- ${$_option} = $_value;
19
- }
20
 
 
 
21
  $this->run();
22
  }
23
 
24
  public function run() {
25
- global $ao_css_defer;
26
- global $ao_ccss_deferjquery;
27
- global $ao_ccss_key;
28
- global $ao_ccss_unloadccss;
29
-
30
- // add all filters to do CCSS if key present.
31
- if ( $ao_css_defer && isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) ) {
32
- // Set AO behavior: disable minification to avoid double minifying and caching.
33
- add_filter( 'autoptimize_filter_css_critcss_minify', '__return_false' );
34
- add_filter( 'autoptimize_filter_css_defer_inline', array( $this, 'ao_ccss_frontend' ), 10, 1 );
35
-
36
- // Add the action to enqueue jobs for CriticalCSS cron.
37
- add_action( 'autoptimize_action_css_hash', array( 'autoptimizeCriticalCSSEnqueue', 'ao_ccss_enqueue' ), 10, 1 );
38
-
39
- // conditionally add the filter to defer jquery and others but only if not done so in autoptimizeScripts.
40
- $_native_defer = false;
41
- if ( 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_not_aggregate' ) && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_inline' ) ) {
42
- $_native_defer = true;
43
- }
44
- if ( $ao_ccss_deferjquery && ! $_native_defer ) {
45
- add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_defer_jquery' ), 11, 1 );
46
- }
47
 
48
- // conditionally add filter to unload the CCSS.
49
- if ( $ao_ccss_unloadccss ) {
50
- add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_unloadccss' ), 12, 1 );
51
- }
52
 
53
- // Order paths by length, as longest ones have greater priority in the rules.
54
- if ( ! empty( $ao_ccss_rules['paths'] ) ) {
55
- $keys = array_map( 'strlen', array_keys( $ao_ccss_rules['paths'] ) );
56
- array_multisort( $keys, SORT_DESC, $ao_ccss_rules['paths'] );
57
- }
58
 
59
- // Add an array with default WordPress's conditional tags
60
- // NOTE: these tags are sorted.
61
- global $ao_ccss_types;
62
- $ao_ccss_types = $this->get_ao_ccss_core_types();
63
 
64
- // Extend conditional tags on plugin initalization.
65
- add_action( apply_filters( 'autoptimize_filter_ccss_extend_types_hook', 'init' ), array( $this, 'ao_ccss_extend_types' ) );
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- // When autoptimize cache is cleared, also clear transient cache for page templates.
68
- add_action( 'autoptimize_action_cachepurged', array( 'autoptimizeCriticalCSSCore', 'ao_ccss_clear_page_tpl_cache' ), 10, 0 );
 
 
 
 
 
69
  }
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
  public function ao_ccss_frontend( $inlined ) {
73
  // Apply CriticalCSS to frontend pages
74
  // Attach types and settings arrays.
75
- global $ao_ccss_types;
76
- global $ao_ccss_rules;
77
- global $ao_ccss_additional;
78
- global $ao_ccss_loggedin;
79
- global $ao_ccss_debug;
80
- global $ao_ccss_keyst;
81
-
82
- $no_ccss = '';
83
- $ao_ccss_additional = autoptimizeStyles::sanitize_css( $ao_ccss_additional );
84
 
85
  // Only if keystatus is OK and option to add CCSS for logged on users is on or user is not logged in.
86
- if ( ( $ao_ccss_keyst && 2 == $ao_ccss_keyst ) && ( $ao_ccss_loggedin || ! is_user_logged_in() ) ) {
87
  // Check for a valid CriticalCSS based on path to return its contents.
88
  $req_path = strtok( $_SERVER['REQUEST_URI'], '?' );
89
- if ( ! empty( $ao_ccss_rules['paths'] ) ) {
90
- foreach ( $ao_ccss_rules['paths'] as $path => $rule ) {
91
  // explicit match OR partial match if MANUAL rule.
92
- if ( $req_path == $path || urldecode( $req_path ) == $path || ( apply_filters( 'autoptimize_filter_ccss_core_path_partial_match', true ) && false == $rule['hash'] && false != $rule['file'] && strpos( $req_path, str_replace( site_url(), '', $path ) ) !== false ) ) {
93
  if ( file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
94
  $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
95
  if ( 'none' != $_ccss_contents ) {
96
- if ( $ao_ccss_debug ) {
97
  $_ccss_contents = '/* PATH: ' . $path . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
98
  }
99
- return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
100
  } else {
101
  $no_ccss = 'none';
102
  }
@@ -106,55 +103,55 @@ class autoptimizeCriticalCSSCore {
106
  }
107
 
108
  // Check for a valid CriticalCSS based on conditional tags to return its contents.
109
- if ( ! empty( $ao_ccss_rules['types'] ) && 'none' !== $no_ccss ) {
110
  // order types-rules by the order of the original $ao_ccss_types array so as not to depend on the order in which rules were added.
111
- $ao_ccss_rules['types'] = array_replace( array_intersect_key( array_flip( $ao_ccss_types ), $ao_ccss_rules['types'] ), $ao_ccss_rules['types'] );
112
- $is_front_page = is_front_page();
113
 
114
- foreach ( $ao_ccss_rules['types'] as $type => $rule ) {
115
- if ( in_array( $type, $ao_ccss_types ) && file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
116
  $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
117
  if ( $is_front_page && 'is_front_page' == $type ) {
118
  if ( 'none' != $_ccss_contents ) {
119
- if ( $ao_ccss_debug ) {
120
  $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
121
  }
122
- return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
123
  } else {
124
  $no_ccss = 'none';
125
  }
126
- } elseif ( strpos( $type, 'custom_post_' ) === 0 && ! $is_front_page ) {
127
  if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
128
  if ( 'none' != $_ccss_contents ) {
129
- if ( $ao_ccss_debug ) {
130
  $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
131
  }
132
- return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
133
  } else {
134
  $no_ccss = 'none';
135
  }
136
  }
137
- } elseif ( 0 === strpos( $type, 'template_' ) && ! $is_front_page ) {
138
  if ( is_page_template( substr( $type, 9 ) ) ) {
139
  if ( 'none' != $_ccss_contents ) {
140
- if ( $ao_ccss_debug ) {
141
  $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
142
  }
143
- return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
144
  } else {
145
  $no_ccss = 'none';
146
  }
147
  }
148
- } elseif ( ! $is_front_page ) {
149
  // all "normal" conditional tags, core + woo + buddypress + edd + bbpress
150
  // but we have to remove the prefix for the non-core ones for them to function.
151
  $type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
152
  if ( function_exists( $type ) && call_user_func( $type ) ) {
153
  if ( 'none' != $_ccss_contents ) {
154
- if ( $ao_ccss_debug ) {
155
  $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
156
  }
157
- return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
158
  } else {
159
  $no_ccss = 'none';
160
  }
@@ -168,7 +165,7 @@ class autoptimizeCriticalCSSCore {
168
  // Finally, inline the default CriticalCSS if any or else the entire CSS for the page
169
  // This also applies to logged in users if the option to add CCSS for logged in users has been disabled.
170
  if ( ! empty( $inlined ) && 'none' !== $no_ccss ) {
171
- return apply_filters( 'autoptimize_filter_ccss_core_ccss', $inlined . $ao_ccss_additional );
172
  } else {
173
  add_filter( 'autoptimize_filter_css_inline', '__return_true' );
174
  return;
@@ -176,9 +173,10 @@ class autoptimizeCriticalCSSCore {
176
  }
177
 
178
  public function ao_ccss_defer_jquery( $in ) {
179
- global $ao_ccss_loggedin;
 
180
  // defer all linked and inline JS.
181
- if ( ( ! is_user_logged_in() || $ao_ccss_loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
182
  foreach ( $matches as $match ) {
183
  if ( str_replace( apply_filters( 'autoptimize_filter_ccss_core_defer_exclude', array( 'data-noptimize="1"', 'data-cfasync="false"', 'data-pagespeed-no-defer' ) ), '', $match[0] ) !== $match[0] ) {
184
  // do not touch JS with noptimize/ cfasync/ pagespeed-no-defer flags.
@@ -208,15 +206,23 @@ class autoptimizeCriticalCSSCore {
208
  return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
209
  }
210
 
 
 
 
 
 
 
 
 
 
211
  public function ao_ccss_extend_types() {
212
  // Extend contidional tags
213
  // Attach the conditional tags array.
214
- global $ao_ccss_types;
215
 
216
  // in some cases $ao_ccss_types is empty and/or not an array, this should work around that problem.
217
- if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
218
- $ao_ccss_types = get_ao_ccss_core_types();
219
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Empty types array in extend, refetching array with core conditionals.', 3 );
220
  }
221
 
222
  // Custom Post Types.
@@ -229,7 +235,7 @@ class autoptimizeCriticalCSSCore {
229
  'and'
230
  );
231
  foreach ( $cpts as $cpt ) {
232
- array_unshift( $ao_ccss_types, 'custom_post_' . $cpt );
233
  }
234
 
235
  // Templates.
@@ -240,12 +246,12 @@ class autoptimizeCriticalCSSCore {
240
  set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
241
  }
242
  foreach ( $templates as $tplfile => $tplname ) {
243
- array_unshift( $ao_ccss_types, 'template_' . $tplfile );
244
  }
245
 
246
  // bbPress tags.
247
  if ( function_exists( 'is_bbpress' ) ) {
248
- $ao_ccss_types = array_merge(
249
  array(
250
  'bbp_is_bbpress',
251
  'bbp_is_favorites',
@@ -271,13 +277,13 @@ class autoptimizeCriticalCSSCore {
271
  'bbp_is_topics_created',
272
  'bbp_is_user_home',
273
  'bbp_is_user_home_edit',
274
- ), $ao_ccss_types
275
  );
276
  }
277
 
278
  // BuddyPress tags.
279
  if ( function_exists( 'is_buddypress' ) ) {
280
- $ao_ccss_types = array_merge(
281
  array(
282
  'bp_is_activation_page',
283
  'bp_is_activity',
@@ -313,25 +319,25 @@ class autoptimizeCriticalCSSCore {
313
  'bp_is_user',
314
  'bp_is_user_profile',
315
  'bp_is_wire',
316
- ), $ao_ccss_types
317
  );
318
  }
319
 
320
  // Easy Digital Downloads (EDD) tags.
321
  if ( function_exists( 'edd_is_checkout' ) ) {
322
- $ao_ccss_types = array_merge(
323
  array(
324
  'edd_is_checkout',
325
  'edd_is_failed_transaction_page',
326
  'edd_is_purchase_history_page',
327
  'edd_is_success_page',
328
- ), $ao_ccss_types
329
  );
330
  }
331
 
332
  // WooCommerce tags.
333
  if ( class_exists( 'WooCommerce' ) ) {
334
- $ao_ccss_types = array_merge(
335
  array(
336
  'woo_is_account_page',
337
  'woo_is_cart',
@@ -342,42 +348,33 @@ class autoptimizeCriticalCSSCore {
342
  'woo_is_shop',
343
  'woo_is_wc_endpoint_url',
344
  'woo_is_woocommerce',
345
- ), $ao_ccss_types
346
  );
347
  }
348
  }
349
 
350
  public function get_ao_ccss_core_types() {
351
- global $ao_ccss_types;
352
- if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
353
- return array(
354
- 'is_404',
355
- 'is_archive',
356
- 'is_author',
357
- 'is_category',
358
- 'is_front_page',
359
- 'is_home',
360
- 'is_page',
361
- 'is_post',
362
- 'is_search',
363
- 'is_attachment',
364
- 'is_single',
365
- 'is_sticky',
366
- 'is_paged',
367
- );
368
- } else {
369
- return $ao_ccss_types;
370
- }
371
  }
372
 
373
- public static function ao_ccss_key_status( $render ) {
374
  // Provide key status
375
  // Get key and key status.
376
- global $ao_ccss_key;
377
- global $ao_ccss_keyst;
378
- $self = new self();
379
- $key = $ao_ccss_key;
380
- $key_status = $ao_ccss_keyst;
381
 
382
  // Prepare returned variables.
383
  $key_return = array();
@@ -400,7 +397,7 @@ class autoptimizeCriticalCSSCore {
400
  } elseif ( $key && ! $key_status ) {
401
  // Key exists but it has no valid status yet
402
  // Perform key validation.
403
- $key_check = $self->ao_ccss_key_validation( $key );
404
 
405
  // Key is valid, set valid status.
406
  if ( $key_check ) {
@@ -442,14 +439,14 @@ class autoptimizeCriticalCSSCore {
442
  }
443
 
444
  public function ao_ccss_key_validation( $key ) {
445
- global $ao_ccss_noptimize;
446
 
447
  // POST a dummy job to criticalcss.com to check for key validation
448
  // Prepare home URL for the request.
449
  $src_url = get_home_url();
450
 
451
  // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
452
- if ( ! empty( $ao_ccss_noptimize ) ) {
453
  $src_url .= '?ao_noptirocket=1';
454
  } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
455
  $src_url .= '?ao_nolazy=1';
@@ -460,12 +457,12 @@ class autoptimizeCriticalCSSCore {
460
  // Prepare the request.
461
  $url = esc_url_raw( AO_CCSS_API . 'generate' );
462
  $args = array(
463
- 'headers' => array(
464
  'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
465
  'Content-type' => 'application/json; charset=utf-8',
466
  'Authorization' => 'JWT ' . $key,
467
  'Connection' => 'close',
468
- ),
469
  // Body must be JSON.
470
  'body' => json_encode(
471
  apply_filters( 'autoptimize_ccss_cron_api_generate_body',
@@ -487,14 +484,14 @@ class autoptimizeCriticalCSSCore {
487
  // Response is OK.
488
  // Set key status as valid and log key check.
489
  update_option( 'autoptimize_ccss_keyst', 2 );
490
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );
491
 
492
  // extract job-id from $body and put it in the queue as a P job
493
  // but only if no jobs and no rules!
494
- global $ao_ccss_queue;
495
- global $ao_ccss_rules;
496
 
497
- if ( 0 == count( $ao_ccss_queue ) && 0 == count( $ao_ccss_rules['types'] ) && 0 == count( $ao_ccss_rules['paths'] ) ) {
498
  if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
499
  $jprops['ljid'] = 'firstrun';
500
  $jprops['rtarget'] = 'types|is_front_page';
@@ -508,10 +505,10 @@ class autoptimizeCriticalCSSCore {
508
  $jprops['jvstat'] = null;
509
  $jprops['jctime'] = microtime( true );
510
  $jprops['jftime'] = null;
511
- $ao_ccss_queue['/'] = $jprops;
512
- $ao_ccss_queue_raw = json_encode( $ao_ccss_queue );
513
- update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false );
514
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Created P job for is_front_page based on API key check response.', 3 );
515
  }
516
  }
517
  return true;
@@ -519,55 +516,41 @@ class autoptimizeCriticalCSSCore {
519
  // Response is unauthorized
520
  // Set key status as invalid and log key check.
521
  update_option( 'autoptimize_ccss_keyst', 1 );
522
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
523
  return false;
524
  } else {
525
  // Response unkown
526
  // Log key check attempt.
527
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
528
  if ( ! empty( $body ) ) {
529
- autoptimizeCriticalCSSCore::ao_ccss_log( print_r( $body, true ), 2 );
530
  }
531
  if ( is_wp_error( $req ) ) {
532
- autoptimizeCriticalCSSCore::ao_ccss_log( $req->get_error_message(), 2 );
533
  }
534
  return false;
535
  }
536
  }
537
 
538
- public static function ao_ccss_viewport() {
539
  // Get viewport size
540
  // Attach viewport option.
541
- global $ao_ccss_viewport;
542
-
543
- // Prepare viewport array.
544
- $viewport = array();
545
 
546
- // Viewport Width.
547
- if ( ! empty( $ao_ccss_viewport['w'] ) ) {
548
- $viewport['w'] = $ao_ccss_viewport['w'];
549
- } else {
550
- $viewport['w'] = '';
551
- }
552
-
553
- // Viewport Height.
554
- if ( ! empty( $ao_ccss_viewport['h'] ) ) {
555
- $viewport['h'] = $ao_ccss_viewport['h'];
556
- } else {
557
- $viewport['h'] = '';
558
- }
559
-
560
- return $viewport;
561
  }
562
 
563
- public static function ao_ccss_check_contents( $ccss ) {
564
  // Perform basic exploit avoidance and CSS validation.
565
  if ( ! empty( $ccss ) ) {
566
  // Try to avoid code injection.
567
  $blocklist = array( '#!/', 'function(', '<script', '<?php' );
568
  foreach ( $blocklist as $blocklisted ) {
569
  if ( strpos( $ccss, $blocklisted ) !== false ) {
570
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
571
  return false;
572
  }
573
  }
@@ -576,7 +559,7 @@ class autoptimizeCriticalCSSCore {
576
  $needlist = array( '{', '}', ':' );
577
  foreach ( $needlist as $needed ) {
578
  if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
579
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
580
  return false;
581
  }
582
  }
@@ -586,10 +569,10 @@ class autoptimizeCriticalCSSCore {
586
  return true;
587
  }
588
 
589
- public static function ao_ccss_log( $msg, $lvl ) {
590
  // Commom logging facility
591
  // Attach debug option.
592
- global $ao_ccss_debug;
593
 
594
  // Prepare log levels, where accepted $lvl are:
595
  // 1: II (for info)
@@ -606,7 +589,7 @@ class autoptimizeCriticalCSSCore {
606
  break;
607
  case 3:
608
  // Output debug messages only if debug mode is enabled.
609
- if ( $ao_ccss_debug ) {
610
  $level = 'DD';
611
  }
612
  break;
@@ -625,9 +608,8 @@ class autoptimizeCriticalCSSCore {
625
  }
626
  }
627
 
628
- public static function ao_ccss_clear_page_tpl_cache() {
629
  // Clears transient cache for page templates.
630
  delete_transient( 'autoptimize_ccss_page_templates' );
631
  }
632
-
633
  }
9
  }
10
 
11
  class autoptimizeCriticalCSSCore {
12
+ protected $_types = null;
 
 
 
 
 
 
 
13
 
14
+ public function __construct() {
15
+ $this->criticalcss = autoptimize()->criticalcss();
16
  $this->run();
17
  }
18
 
19
  public function run() {
20
+ $css_defer = $this->criticalcss->get_option( 'css_defer' );
21
+ $deferjquery = $this->criticalcss->get_option( 'deferjquery' );
22
+ $unloadccss = $this->criticalcss->get_option( 'unloadccss' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ if ( ! $css_defer ) {
25
+ return;
26
+ }
 
27
 
28
+ // add all filters to do CCSS
29
+ // Set AO behavior: disable minification to avoid double minifying and caching.
30
+ add_filter( 'autoptimize_filter_css_critcss_minify', '__return_false' );
31
+ add_filter( 'autoptimize_filter_css_defer_inline', array( $this, 'ao_ccss_frontend' ), 10, 1 );
 
32
 
33
+ // Add the action to enqueue jobs for CriticalCSS cron.
34
+ if ( $this->criticalcss->is_api_active() ) {
35
+ add_action( 'autoptimize_action_css_hash', array( $this->criticalcss, 'enqueue' ), 10, 1 );
36
+ }
37
 
38
+ // conditionally add the filter to defer jquery and others but only if not done so in autoptimizeScripts.
39
+ $_native_defer = false;
40
+ if ( 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_not_aggregate' ) && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_inline' ) ) {
41
+ $_native_defer = true;
42
+ }
43
+ if ( $deferjquery && ! $_native_defer ) {
44
+ add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_defer_jquery' ), 11, 1 );
45
+ }
46
+
47
+ // conditionally add filter to unload the CCSS.
48
+ if ( $unloadccss ) {
49
+ add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_unloadccss' ), 12, 1 );
50
+ }
51
 
52
+ // Order paths by length, as longest ones have greater priority in the rules.
53
+ $rules = $this->criticalcss->get_option( 'rules' );
54
+ if ( ! empty( $rules['paths'] ) ) {
55
+ $keys = array_map( 'strlen', array_keys( $rules['paths'] ) );
56
+ array_multisort( $keys, SORT_DESC, $rules['paths'] );
57
+ // TODO: Not sure what we're doing here. Sorted the $keys,
58
+ // but they don't seem to be used anywhere.
59
  }
60
+
61
+ // Add an array with default WordPress's conditional tags
62
+ // NOTE: these tags are sorted.
63
+ $this->_types = $this->get_ao_ccss_core_types();
64
+
65
+ // Extend conditional tags on plugin initalization.
66
+ add_action( apply_filters( 'autoptimize_filter_ccss_extend_types_hook', 'init' ), array( $this, 'ao_ccss_extend_types' ) );
67
+
68
+ // When autoptimize cache is cleared, also clear transient cache for page templates.
69
+ add_action( 'autoptimize_action_cachepurged', array( $this, 'ao_ccss_clear_page_tpl_cache' ), 10, 0 );
70
  }
71
 
72
  public function ao_ccss_frontend( $inlined ) {
73
  // Apply CriticalCSS to frontend pages
74
  // Attach types and settings arrays.
75
+ $rules = $this->criticalcss->get_option( 'rules' );
76
+ $additional = $this->criticalcss->get_option( 'additional' );
77
+ $loggedin = $this->criticalcss->get_option( 'loggedin' );
78
+ $debug = $this->criticalcss->get_option( 'debug' );
79
+ $no_ccss = '';
80
+ $additional = autoptimizeStyles::sanitize_css( $additional );
 
 
 
81
 
82
  // Only if keystatus is OK and option to add CCSS for logged on users is on or user is not logged in.
83
+ if ( $loggedin || ! is_user_logged_in() ) {
84
  // Check for a valid CriticalCSS based on path to return its contents.
85
  $req_path = strtok( $_SERVER['REQUEST_URI'], '?' );
86
+ if ( ! empty( $rules['paths'] ) ) {
87
+ foreach ( $rules['paths'] as $path => $rule ) {
88
  // explicit match OR partial match if MANUAL rule.
89
+ if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ( $req_path == $path || urldecode( $req_path ) == $path || ( apply_filters( 'autoptimize_filter_ccss_core_path_partial_match', true ) && false == $rule['hash'] && false != $rule['file'] && strpos( $req_path, str_replace( site_url(), '', $path ) ) !== false ) ) ) {
90
  if ( file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
91
  $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
92
  if ( 'none' != $_ccss_contents ) {
93
+ if ( $debug ) {
94
  $_ccss_contents = '/* PATH: ' . $path . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
95
  }
96
+ return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
97
  } else {
98
  $no_ccss = 'none';
99
  }
103
  }
104
 
105
  // Check for a valid CriticalCSS based on conditional tags to return its contents.
106
+ if ( ! empty( $rules['types'] ) && 'none' !== $no_ccss ) {
107
  // order types-rules by the order of the original $ao_ccss_types array so as not to depend on the order in which rules were added.
108
+ $rules['types'] = array_replace( array_intersect_key( array_flip( $this->_types ), $rules['types'] ), $rules['types'] );
109
+ $is_front_page = is_front_page();
110
 
111
+ foreach ( $rules['types'] as $type => $rule ) {
112
+ if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && in_array( $type, $this->_types ) && file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
113
  $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
114
  if ( $is_front_page && 'is_front_page' == $type ) {
115
  if ( 'none' != $_ccss_contents ) {
116
+ if ( $debug ) {
117
  $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
118
  }
119
+ return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
120
  } else {
121
  $no_ccss = 'none';
122
  }
123
+ } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && strpos( $type, 'custom_post_' ) === 0 && ! $is_front_page ) {
124
  if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
125
  if ( 'none' != $_ccss_contents ) {
126
+ if ( $debug ) {
127
  $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
128
  }
129
+ return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
130
  } else {
131
  $no_ccss = 'none';
132
  }
133
  }
134
+ } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && 0 === strpos( $type, 'template_' ) && ! $is_front_page ) {
135
  if ( is_page_template( substr( $type, 9 ) ) ) {
136
  if ( 'none' != $_ccss_contents ) {
137
+ if ( $debug ) {
138
  $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
139
  }
140
+ return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
141
  } else {
142
  $no_ccss = 'none';
143
  }
144
  }
145
+ } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ! $is_front_page ) {
146
  // all "normal" conditional tags, core + woo + buddypress + edd + bbpress
147
  // but we have to remove the prefix for the non-core ones for them to function.
148
  $type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
149
  if ( function_exists( $type ) && call_user_func( $type ) ) {
150
  if ( 'none' != $_ccss_contents ) {
151
+ if ( $debug ) {
152
  $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
153
  }
154
+ return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
155
  } else {
156
  $no_ccss = 'none';
157
  }
165
  // Finally, inline the default CriticalCSS if any or else the entire CSS for the page
166
  // This also applies to logged in users if the option to add CCSS for logged in users has been disabled.
167
  if ( ! empty( $inlined ) && 'none' !== $no_ccss ) {
168
+ return apply_filters( 'autoptimize_filter_ccss_core_ccss', $inlined . $additional );
169
  } else {
170
  add_filter( 'autoptimize_filter_css_inline', '__return_true' );
171
  return;
173
  }
174
 
175
  public function ao_ccss_defer_jquery( $in ) {
176
+ $loggedin = $this->criticalcss->get_option( 'loggedin' );
177
+
178
  // defer all linked and inline JS.
179
+ if ( ( ! is_user_logged_in() || $loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
180
  foreach ( $matches as $match ) {
181
  if ( str_replace( apply_filters( 'autoptimize_filter_ccss_core_defer_exclude', array( 'data-noptimize="1"', 'data-cfasync="false"', 'data-pagespeed-no-defer' ) ), '', $match[0] ) !== $match[0] ) {
182
  // do not touch JS with noptimize/ cfasync/ pagespeed-no-defer flags.
206
  return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
207
  }
208
 
209
+ /**
210
+ * Get the types array.
211
+ *
212
+ * @return array|null
213
+ */
214
+ public function get_types() {
215
+ return $this->_types;
216
+ }
217
+
218
  public function ao_ccss_extend_types() {
219
  // Extend contidional tags
220
  // Attach the conditional tags array.
 
221
 
222
  // in some cases $ao_ccss_types is empty and/or not an array, this should work around that problem.
223
+ if ( empty( $this->_types ) || ! is_array( $this->_types ) ) {
224
+ $this->_types = $this->get_ao_ccss_core_types();
225
+ $this->ao_ccss_log( 'Empty types array in extend, refetching array with core conditionals.', 3 );
226
  }
227
 
228
  // Custom Post Types.
235
  'and'
236
  );
237
  foreach ( $cpts as $cpt ) {
238
+ array_unshift( $this->_types, 'custom_post_' . $cpt );
239
  }
240
 
241
  // Templates.
246
  set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
247
  }
248
  foreach ( $templates as $tplfile => $tplname ) {
249
+ array_unshift( $this->_types, 'template_' . $tplfile );
250
  }
251
 
252
  // bbPress tags.
253
  if ( function_exists( 'is_bbpress' ) ) {
254
+ $this->_types = array_merge(
255
  array(
256
  'bbp_is_bbpress',
257
  'bbp_is_favorites',
277
  'bbp_is_topics_created',
278
  'bbp_is_user_home',
279
  'bbp_is_user_home_edit',
280
+ ), $this->_types
281
  );
282
  }
283
 
284
  // BuddyPress tags.
285
  if ( function_exists( 'is_buddypress' ) ) {
286
+ $this->_types = array_merge(
287
  array(
288
  'bp_is_activation_page',
289
  'bp_is_activity',
319
  'bp_is_user',
320
  'bp_is_user_profile',
321
  'bp_is_wire',
322
+ ), $this->_types
323
  );
324
  }
325
 
326
  // Easy Digital Downloads (EDD) tags.
327
  if ( function_exists( 'edd_is_checkout' ) ) {
328
+ $this->_types = array_merge(
329
  array(
330
  'edd_is_checkout',
331
  'edd_is_failed_transaction_page',
332
  'edd_is_purchase_history_page',
333
  'edd_is_success_page',
334
+ ), $this->_types
335
  );
336
  }
337
 
338
  // WooCommerce tags.
339
  if ( class_exists( 'WooCommerce' ) ) {
340
+ $this->_types = array_merge(
341
  array(
342
  'woo_is_account_page',
343
  'woo_is_cart',
348
  'woo_is_shop',
349
  'woo_is_wc_endpoint_url',
350
  'woo_is_woocommerce',
351
+ ), $this->_types
352
  );
353
  }
354
  }
355
 
356
  public function get_ao_ccss_core_types() {
357
+ return array(
358
+ 'is_404',
359
+ 'is_front_page',
360
+ 'is_home',
361
+ 'is_page',
362
+ 'is_single',
363
+ 'is_category',
364
+ 'is_author',
365
+ 'is_archive',
366
+ 'is_search',
367
+ 'is_attachment',
368
+ 'is_sticky',
369
+ 'is_paged',
370
+ );
 
 
 
 
 
 
371
  }
372
 
373
+ public function ao_ccss_key_status( $render ) {
374
  // Provide key status
375
  // Get key and key status.
376
+ $key = $this->criticalcss->get_option( 'key' );
377
+ $key_status = $this->criticalcss->get_option( 'keyst' );
 
 
 
378
 
379
  // Prepare returned variables.
380
  $key_return = array();
397
  } elseif ( $key && ! $key_status ) {
398
  // Key exists but it has no valid status yet
399
  // Perform key validation.
400
+ $key_check = $this->ao_ccss_key_validation( $key );
401
 
402
  // Key is valid, set valid status.
403
  if ( $key_check ) {
439
  }
440
 
441
  public function ao_ccss_key_validation( $key ) {
442
+ $noptimize = $this->criticalcss->get_option( 'noptimize' );
443
 
444
  // POST a dummy job to criticalcss.com to check for key validation
445
  // Prepare home URL for the request.
446
  $src_url = get_home_url();
447
 
448
  // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
449
+ if ( ! empty( $noptimize ) ) {
450
  $src_url .= '?ao_noptirocket=1';
451
  } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
452
  $src_url .= '?ao_nolazy=1';
457
  // Prepare the request.
458
  $url = esc_url_raw( AO_CCSS_API . 'generate' );
459
  $args = array(
460
+ 'headers' => apply_filters( 'autoptimize_ccss_cron_api_generate_headers', array(
461
  'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
462
  'Content-type' => 'application/json; charset=utf-8',
463
  'Authorization' => 'JWT ' . $key,
464
  'Connection' => 'close',
465
+ ) ),
466
  // Body must be JSON.
467
  'body' => json_encode(
468
  apply_filters( 'autoptimize_ccss_cron_api_generate_body',
484
  // Response is OK.
485
  // Set key status as valid and log key check.
486
  update_option( 'autoptimize_ccss_keyst', 2 );
487
+ $this->ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );
488
 
489
  // extract job-id from $body and put it in the queue as a P job
490
  // but only if no jobs and no rules!
491
+ $queue = $this->criticalcss->get_option( 'queue' );
492
+ $rules = $this->criticalcss->get_option( 'rules' );
493
 
494
+ if ( 0 == count( $queue ) && 0 == count( $rules['types'] ) && 0 == count( $rules['paths'] ) ) {
495
  if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
496
  $jprops['ljid'] = 'firstrun';
497
  $jprops['rtarget'] = 'types|is_front_page';
505
  $jprops['jvstat'] = null;
506
  $jprops['jctime'] = microtime( true );
507
  $jprops['jftime'] = null;
508
+ $queue['/'] = $jprops;
509
+ $queue_raw = json_encode( $queue );
510
+ update_option( 'autoptimize_ccss_queue', $queue_raw, false );
511
+ $this->ao_ccss_log( 'Created P job for is_front_page based on API key check response.', 3 );
512
  }
513
  }
514
  return true;
516
  // Response is unauthorized
517
  // Set key status as invalid and log key check.
518
  update_option( 'autoptimize_ccss_keyst', 1 );
519
+ $this->ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
520
  return false;
521
  } else {
522
  // Response unkown
523
  // Log key check attempt.
524
+ $this->ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
525
  if ( ! empty( $body ) ) {
526
+ $this->ao_ccss_log( print_r( $body, true ), 2 );
527
  }
528
  if ( is_wp_error( $req ) ) {
529
+ $this->ao_ccss_log( $req->get_error_message(), 2 );
530
  }
531
  return false;
532
  }
533
  }
534
 
535
+ public function ao_ccss_viewport() {
536
  // Get viewport size
537
  // Attach viewport option.
538
+ $viewport = $this->criticalcss->get_option( 'viewport' );
 
 
 
539
 
540
+ return array(
541
+ 'w' => ! empty( $viewport['w'] ) ? $viewport['w'] : '',
542
+ 'h' => ! empty( $viewport['h'] ) ? $viewport['h'] : '',
543
+ );
 
 
 
 
 
 
 
 
 
 
 
544
  }
545
 
546
+ public function ao_ccss_check_contents( $ccss ) {
547
  // Perform basic exploit avoidance and CSS validation.
548
  if ( ! empty( $ccss ) ) {
549
  // Try to avoid code injection.
550
  $blocklist = array( '#!/', 'function(', '<script', '<?php' );
551
  foreach ( $blocklist as $blocklisted ) {
552
  if ( strpos( $ccss, $blocklisted ) !== false ) {
553
+ $this->ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
554
  return false;
555
  }
556
  }
559
  $needlist = array( '{', '}', ':' );
560
  foreach ( $needlist as $needed ) {
561
  if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
562
+ $this->ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
563
  return false;
564
  }
565
  }
569
  return true;
570
  }
571
 
572
+ public function ao_ccss_log( $msg, $lvl ) {
573
  // Commom logging facility
574
  // Attach debug option.
575
+ $debug = $this->criticalcss->get_option( 'debug' );
576
 
577
  // Prepare log levels, where accepted $lvl are:
578
  // 1: II (for info)
589
  break;
590
  case 3:
591
  // Output debug messages only if debug mode is enabled.
592
+ if ( $debug ) {
593
  $level = 'DD';
594
  }
595
  break;
608
  }
609
  }
610
 
611
+ public function ao_ccss_clear_page_tpl_cache() {
612
  // Clears transient cache for page templates.
613
  delete_transient( 'autoptimize_ccss_page_templates' );
614
  }
 
615
  }
classes/autoptimizeCriticalCSSCron.php CHANGED
@@ -9,14 +9,8 @@ if ( ! defined( 'ABSPATH' ) ) {
9
  }
10
 
11
  class autoptimizeCriticalCSSCron {
12
- public function __construct()
13
- {
14
- // fetch all options at once and populate them individually explicitely as globals.
15
- $all_options = autoptimizeCriticalCSSBase::fetch_options();
16
- foreach ( $all_options as $_option => $_value ) {
17
- global ${$_option};
18
- ${$_option} = $_value;
19
- }
20
 
21
  // Add queue control to a registered event.
22
  add_action( 'ao_ccss_queue', array( $this, 'ao_ccss_queue_control' ) );
@@ -26,10 +20,11 @@ class autoptimizeCriticalCSSCron {
26
 
27
  public function ao_ccss_queue_control() {
28
  // The queue execution backend.
29
- global $ao_ccss_key;
30
- if ( ! isset( $ao_ccss_key ) || empty( $ao_ccss_key ) ) {
 
31
  // no key set, not processing the queue!
32
- autoptimizeCriticalCSSCore::ao_ccss_log( 'No key set, so not processing queue.', 3 );
33
  return;
34
  }
35
 
@@ -56,7 +51,7 @@ class autoptimizeCriticalCSSCron {
56
  if ( $qdobj ) {
57
  if ( 1 === $qdobj['enable'] ) {
58
  $queue_debug = true;
59
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Queue operating in debug mode with the following settings: <' . $qdobj_raw . '>', 3 );
60
  }
61
  }
62
  }
@@ -76,30 +71,30 @@ class autoptimizeCriticalCSSCron {
76
  if ( ! $queue_lock ) {
77
 
78
  // Log queue start and create the lock file.
79
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Queue control started', 3 );
80
  if ( touch( AO_CCSS_LOCK ) ) {
81
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Queue control locked', 3 );
82
  }
83
 
84
  // Attach required variables.
85
- global $ao_ccss_queue;
86
- global $ao_ccss_rtimelimit;
87
 
88
  // Initialize counters.
89
- if ( $ao_ccss_rtimelimit == 0 ) {
90
  // no time limit set, let's go with 1000 seconds.
91
- $ao_ccss_rtimelimit = 1000;
92
  }
93
- $mt = time() + $ao_ccss_rtimelimit; // maxtime queue processing can run.
94
  $jc = 1; // job count number.
95
  $jr = 1; // jobs requests number.
96
- $jt = count( $ao_ccss_queue ); // number of jobs in queue.
97
 
98
  // Sort queue by ascending job status (e.g. ERROR, JOB_ONGOING, JOB_QUEUED, NEW...).
99
- array_multisort( array_column( $ao_ccss_queue, 'jqstat' ), $ao_ccss_queue ); // @codingStandardsIgnoreLine
100
 
101
  // Iterates over the entire queue.
102
- foreach ( $ao_ccss_queue as $path => $jprops ) {
103
  // Prepare flags and target rule.
104
  $update = false;
105
  $deljob = false;
@@ -108,13 +103,13 @@ class autoptimizeCriticalCSSCron {
108
  $trule = explode( '|', $jprops['rtarget'] );
109
 
110
  // Log job count.
111
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Processing job ' . $jc . ' of ' . $jt . ' with id <' . $jprops['ljid'] . '> and status <' . $jprops['jqstat'] . '>', 3 );
112
 
113
  // Process NEW jobs.
114
  if ( 'NEW' == $jprops['jqstat'] ) {
115
 
116
  // Log the new job.
117
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Found NEW job with local ID <' . $jprops['ljid'] . '>, starting its queue processing', 3 );
118
 
119
  // Compare job and rule hashes (if any).
120
  $hash = $this->ao_ccss_diff_hashes( $jprops['ljid'], $jprops['hash'], $jprops['hashes'], $jprops['rtarget'] );
@@ -124,7 +119,7 @@ class autoptimizeCriticalCSSCron {
124
  if ( $jr > 2 ) {
125
  // we already posted 2 jobs to criticalcss.com, don't post more this run
126
  // but we can keep on processing the queue to keep it tidy.
127
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Holding off on generating request for job with local ID <' . $jprops['ljid'] . '>, maximum number of POSTS reached.', 3 );
128
  continue;
129
  }
130
 
@@ -136,12 +131,28 @@ class autoptimizeCriticalCSSCron {
136
  $jr++;
137
 
138
  // NOTE: All the following conditions maps to the ones in admin_settings_queue.js.php.
139
- if ( 'JOB_QUEUED' == $apireq['job']['status'] || 'JOB_ONGOING' == $apireq['job']['status'] ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  // SUCCESS: request has a valid result.
141
  // Update job properties.
142
  $jprops['jid'] = $apireq['job']['id'];
143
  $jprops['jqstat'] = $apireq['job']['status'];
144
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> generate request successful, remote id <' . $jprops['jid'] . '>, status now is <' . $jprops['jqstat'] . '>', 3 );
145
  } elseif ( 'STATUS_JOB_BAD' == $apireq['job']['status'] ) {
146
  // ERROR: concurrent requests
147
  // Update job properties.
@@ -154,23 +165,7 @@ class autoptimizeCriticalCSSCron {
154
  }
155
  $jprops['jvstat'] = 'NONE';
156
  $jprops['jftime'] = microtime( true );
157
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Concurrent requests when processing job id <' . $jprops['ljid'] . '>, job status is now <' . $jprops['jqstat'] . '>', 3 );
158
- } elseif ( 'INVALID_JWT_TOKEN' == $apireq['errorCode'] ) {
159
- // ERROR: key validation
160
- // Update job properties.
161
- $jprops['jqstat'] = $apireq['errorCode'];
162
- $jprops['jrstat'] = $apireq['error'];
163
- $jprops['jvstat'] = 'NONE';
164
- $jprops['jftime'] = microtime( true );
165
- autoptimizeCriticalCSSCore::ao_ccss_log( 'API key validation error when processing job id <' . $jprops['ljid'] . '>, job status is now <' . $jprops['jqstat'] . '>', 3 );
166
- } elseif ( empty( $apireq ) ) {
167
- // ERROR: no response
168
- // Update job properties.
169
- $jprops['jqstat'] = 'NO_RESPONSE';
170
- $jprops['jrstat'] = 'NONE';
171
- $jprops['jvstat'] = 'NONE';
172
- $jprops['jftime'] = microtime( true );
173
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> request has no response, status now is <' . $jprops['jqstat'] . '>', 3 );
174
  } else {
175
  // UNKNOWN: unhandled generate exception
176
  // Update job properties.
@@ -178,15 +173,15 @@ class autoptimizeCriticalCSSCron {
178
  $jprops['jrstat'] = 'NONE';
179
  $jprops['jvstat'] = 'NONE';
180
  $jprops['jftime'] = microtime( true );
181
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> generate request has an UNKNOWN condition, status now is <' . $jprops['jqstat'] . '>, check log messages above for more information', 2 );
182
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job response was: ' . json_encode( $apireq ), 3 );
183
  }
184
  } else {
185
  // SUCCESS: Job hash is equal to a previous one, so it's done
186
  // Update job status and finish time.
187
  $jprops['jqstat'] = 'JOB_DONE';
188
  $jprops['jftime'] = microtime( true );
189
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> requires no further processing, status now is <' . $jprops['jqstat'] . '>', 3 );
190
  }
191
 
192
  // Set queue update flag.
@@ -195,7 +190,7 @@ class autoptimizeCriticalCSSCron {
195
  } elseif ( 'JOB_QUEUED' == $jprops['jqstat'] || 'JOB_ONGOING' == $jprops['jqstat'] ) {
196
  // Process QUEUED and ONGOING jobs
197
  // Log the pending job.
198
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Found PENDING job with local ID <' . $jprops['ljid'] . '>, continuing its queue processing', 3 );
199
 
200
  // Dispatch the job result request and increment request count.
201
  $apireq = $this->ao_ccss_api_results( $jprops['jid'], $queue_debug, $qdobj['htcode'] );
@@ -219,7 +214,7 @@ class autoptimizeCriticalCSSCron {
219
  // Process a PENDING job
220
  // Update job properties.
221
  $jprops['jqstat'] = $apireq['status'];
222
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . '> unchanged', 3 );
223
  } elseif ( 'JOB_DONE' == $apireq['status'] ) {
224
  // Process a DONE job
225
  // New resultStatus from ccss.com "HTML_404", consider as "GOOD" for now.
@@ -236,7 +231,7 @@ class autoptimizeCriticalCSSCron {
236
  $jprops['jvstat'] = $apireq['validationStatus'];
237
  $jprops['jftime'] = microtime( true );
238
  $rule_update = true;
239
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . '>, file saved <' . $jprops['file'] . '>', 3 );
240
  } elseif ( 'GOOD' == $apireq['resultStatus'] && ( 'BAD' == $apireq['validationStatus'] || 'SCREENSHOT_WARN_BLANK' == $apireq['validationStatus'] ) ) {
241
  // SUCCESS: GOOD job with BAD or SCREENSHOT_WARN_BLANK validation
242
  // Update job properties.
@@ -247,9 +242,9 @@ class autoptimizeCriticalCSSCron {
247
  if ( apply_filters( 'autoptimize_filter_ccss_save_review_rules', true ) ) {
248
  $jprops['file'] = $this->ao_ccss_save_file( $apireq['css'], $trule, true );
249
  $rule_update = true;
250
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . ', file saved <' . $jprops['file'] . '> but requires REVIEW', 3 );
251
  } else {
252
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . ', file not saved because it required REVIEW.', 3 );
253
  }
254
  } elseif ( 'GOOD' != $apireq['resultStatus'] && ( 'GOOD' != $apireq['validationStatus'] || 'WARN' != $apireq['validationStatus'] || 'BAD' != $apireq['validationStatus'] || 'SCREENSHOT_WARN_BLANK' != $apireq['validationStatus'] ) ) {
255
  // ERROR: no GOOD, WARN or BAD results
@@ -258,9 +253,9 @@ class autoptimizeCriticalCSSCron {
258
  $jprops['jrstat'] = $apireq['resultStatus'];
259
  $jprops['jvstat'] = $apireq['validationStatus'];
260
  $jprops['jftime'] = microtime( true );
261
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
262
  $apireq['css'] = '/* critical css removed for DEBUG logging purposes */';
263
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job response was: ' . json_encode( $apireq ), 3 );
264
  } else {
265
  // UNKNOWN: unhandled JOB_DONE exception
266
  // Update job properties.
@@ -268,9 +263,9 @@ class autoptimizeCriticalCSSCron {
268
  $jprops['jrstat'] = $apireq['resultStatus'];
269
  $jprops['jvstat'] = $apireq['validationStatus'];
270
  $jprops['jftime'] = microtime( true );
271
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request successful but job is UNKNOWN, status now is <' . $jprops['jqstat'] . '>', 2 );
272
  $apireq['css'] = '/* critical css removed for DEBUG logging purposes */';
273
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job response was: ' . json_encode( $apireq ), 3 );
274
  }
275
  } elseif ( 'JOB_FAILED' == $apireq['job']['status'] || 'STATUS_JOB_BAD' == $apireq['job']['status'] ) {
276
  // ERROR: failed job
@@ -283,7 +278,7 @@ class autoptimizeCriticalCSSCron {
283
  }
284
  $jprops['jvstat'] = 'NONE';
285
  $jprops['jftime'] = microtime( true );
286
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
287
  } elseif ( 'This css no longer exists. Please re-generate it.' == $apireq['error'] ) {
288
  // ERROR: CSS doesn't exist
289
  // Update job properties.
@@ -291,7 +286,7 @@ class autoptimizeCriticalCSSCron {
291
  $jprops['jrstat'] = $apireq['error'];
292
  $jprops['jvstat'] = 'NONE';
293
  $jprops['jftime'] = microtime( true );
294
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
295
  } elseif ( empty( $apireq ) ) {
296
  // ERROR: no response
297
  // Update job properties.
@@ -299,7 +294,7 @@ class autoptimizeCriticalCSSCron {
299
  $jprops['jrstat'] = 'NONE';
300
  $jprops['jvstat'] = 'NONE';
301
  $jprops['jftime'] = microtime( true );
302
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> request has no response, status now is <' . $jprops['jqstat'] . '>', 3 );
303
  } else {
304
  // UNKNOWN: unhandled results exception
305
  // Update job properties.
@@ -307,7 +302,7 @@ class autoptimizeCriticalCSSCron {
307
  $jprops['jrstat'] = 'NONE';
308
  $jprops['jvstat'] = 'NONE';
309
  $jprops['jftime'] = microtime( true );
310
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> result request has an UNKNOWN condition, status now is <' . $jprops['jqstat'] . '>, check log messages above for more information', 2 );
311
  }
312
 
313
  // Set queue update flag.
@@ -324,31 +319,31 @@ class autoptimizeCriticalCSSCron {
324
  if ( $update ) {
325
  if ( ! $deljob ) {
326
  // Update properties of a NEW or PENDING job...
327
- $ao_ccss_queue[ $path ] = $jprops;
328
  } else {
329
  // ...or remove the DONE job.
330
- unset( $ao_ccss_queue[ $path ] );
331
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> is DONE and was removed from the queue', 3 );
332
  }
333
 
334
  // Update queue object.
335
- $ao_ccss_queue_raw = json_encode( $ao_ccss_queue );
336
- update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false );
337
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Queue updated by job id <' . $jprops['ljid'] . '>', 3 );
338
 
339
  // Update target rule.
340
  if ( $rule_update ) {
341
  $this->ao_ccss_rule_update( $jprops['ljid'], $jprops['rtarget'], $jprops['file'], $jprops['hash'] );
342
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $jprops['ljid'] . '> updated the target rule <' . $jprops['rtarget'] . '>', 3 );
343
  }
344
  } else {
345
  // Or log no queue action.
346
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Nothing to do on this job', 3 );
347
  }
348
 
349
  // Break the loop if request time limit is (almost exceeded).
350
  if ( time() > $mt ) {
351
- autoptimizeCriticalCSSCore::ao_ccss_log( 'The time limit of ' . $ao_ccss_rtimelimit . ' seconds was exceeded, queue control must finish now', 3 );
352
  break;
353
  }
354
 
@@ -359,13 +354,13 @@ class autoptimizeCriticalCSSCron {
359
  // Remove the lock file and log the queue end.
360
  if ( file_exists( AO_CCSS_LOCK ) ) {
361
  unlink( AO_CCSS_LOCK );
362
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Queue control unlocked', 3 );
363
  }
364
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Queue control finished', 3 );
365
 
366
  // Log that queue is locked.
367
  } else {
368
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Queue is already running, skipping the attempt to run it again', 3 );
369
  }
370
  }
371
 
@@ -376,7 +371,7 @@ class autoptimizeCriticalCSSCron {
376
  // Job with a single hash
377
  // Set job hash.
378
  $hash = $hashes[0];
379
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $ljid . '> updated with SINGLE hash <' . $hash . '>', 3 );
380
  } else {
381
  // Job with multiple hashes
382
  // Loop through hashes to concatenate them.
@@ -387,52 +382,56 @@ class autoptimizeCriticalCSSCron {
387
 
388
  // Set job hash.
389
  $hash = md5( $nhash );
390
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $ljid . '> updated with a COMPOSITE hash <' . $hash . '>', 3 );
391
  }
392
 
393
  // STEP 2: compare job to existing jobs to prevent double submission for same type+hash.
394
- global $ao_ccss_queue;
395
 
396
- foreach ( $ao_ccss_queue as $queue_item ) {
397
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Comparing <' . $rule . $hash . '> with <' . $queue_item['rtarget'] . $queue_item['hash'] . '>', 3 );
398
  if ( $queue_item['hash'] == $hash && $queue_item['rtarget'] == $rule && in_array( $queue_item['jqstat'], array( 'JOB_QUEUED', 'JOB_ONGOING', 'JOB_DONE' ) ) ) {
399
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $ljid . '> matches the already pending job <' . $queue_item['ljid'] . '>', 3 );
400
  return false;
401
  }
402
  }
403
 
404
  // STEP 3: compare job and existing rule (if any) hashes
405
  // Attach required arrays.
406
- global $ao_ccss_rules;
407
 
408
  // Prepare rule variables.
409
  $trule = explode( '|', $rule );
410
- $srule = $ao_ccss_rules[ $trule[0] ][ $trule[1] ];
411
-
 
 
 
 
412
  // If hash is empty, set it to now for a "forced job".
413
- if ( empty( $hash ) ) {
414
  $hash = 'new';
415
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $ljid . '> had no hash, assuming forced job so setting hash to new', 3 );
416
  }
417
 
418
  // Check if a MANUAL rule exist and return false.
419
  if ( ! empty( $srule ) && ( 0 == $srule['hash'] && 0 != $srule['file'] ) ) {
420
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $ljid . '> matches the MANUAL rule <' . $trule[0] . '|' . $trule[1] . '>', 3 );
421
  return false;
422
  } elseif ( ! empty( $srule ) ) {
423
  // Check if an AUTO rule exist.
424
  if ( $hash === $srule['hash'] && is_file( AO_CCSS_DIR . $srule['file'] ) && 0 != filesize( AO_CCSS_DIR . $srule['file'] ) ) {
425
  // Check if job hash matches rule, if the CCSS file exists said file is not empty and return FALSE is so.
426
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $ljid . '> with hash <' . $hash . '> MATCH the one in rule <' . $trule[0] . '|' . $trule[1] . '>', 3 );
427
  return false;
428
  } else {
429
  // Or return the new hash if they differ.
430
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $ljid . '> with hash <' . $hash . '> DOES NOT MATCH the one in rule <' . $trule[0] . '|' . $trule[1] . '> or rule\'s CCSS file was invalid.', 3 );
431
  return $hash;
432
  }
433
  } else {
434
  // Return the hash for a job that has no rule yet.
435
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job id <' . $ljid . '> with hash <' . $hash . '> has no rule yet', 3 );
436
  return $hash;
437
  }
438
  }
@@ -440,14 +439,11 @@ class autoptimizeCriticalCSSCron {
440
  public function ao_ccss_api_generate( $path, $debug, $dcode ) {
441
  // POST jobs to criticalcss.com and return responses
442
  // Get key and key status.
443
- global $ao_ccss_key;
444
- global $ao_ccss_keyst;
445
- $key = $ao_ccss_key;
446
- $key_status = $ao_ccss_keyst;
447
 
448
  // Prepare full URL to request.
449
- global $ao_ccss_noptimize;
450
-
451
  $site_host = get_site_url();
452
  $site_path = parse_url( $site_host, PHP_URL_PATH );
453
 
@@ -458,20 +454,20 @@ class autoptimizeCriticalCSSCron {
458
  // Logic to bind to one domain to avoid site clones of sites would
459
  // automatically begin spawning requests to criticalcss.com which has
460
  // a per domain cost.
461
- global $ao_ccss_domain;
462
- if ( empty( $ao_ccss_domain ) ) {
463
  // first request being done, update option to allow future requests are only allowed if from same domain.
464
  update_option( 'autoptimize_ccss_domain', str_rot13( $site_host ) );
465
- } elseif ( trim( $ao_ccss_domain, '\'"' ) !== 'none' && parse_url( $site_host, PHP_URL_HOST ) !== parse_url( $ao_ccss_domain, PHP_URL_HOST ) && apply_filters( 'autoptimize_filter_ccss_bind_domain', true ) ) {
466
  // not the same domain, log as error and return without posting to criticalcss.com.
467
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Request for domain ' . $site_host . ' does not match bound domain ' . $ao_ccss_domain . ' so not proceeding.', 2 );
468
  return false;
469
  }
470
 
471
  $src_url = $site_host . $path;
472
 
473
  // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
474
- if ( ! empty( $ao_ccss_noptimize ) ) {
475
  $src_url .= '?ao_noptirocket=1';
476
  } elseif ( ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) || apply_filters( 'autoptimize_filter_ccss_enforce_nolazy', false ) ) {
477
  $src_url .= '?ao_nolazy=1';
@@ -486,15 +482,15 @@ class autoptimizeCriticalCSSCron {
486
  $body['aocssv'] = AO_CCSS_VER;
487
 
488
  // Prepare and add viewport size to the body if available.
489
- $viewport = autoptimizeCriticalCSSCore::ao_ccss_viewport();
490
  if ( ! empty( $viewport['w'] ) && ! empty( $viewport['h'] ) ) {
491
  $body['width'] = $viewport['w'];
492
  $body['height'] = $viewport['h'];
493
  }
494
 
495
  // Prepare and add forceInclude to the body if available.
496
- global $ao_ccss_finclude;
497
- $finclude = $this->ao_ccss_finclude( $ao_ccss_finclude );
498
  if ( ! empty( $finclude ) ) {
499
  $body['forceInclude'] = $finclude;
500
  }
@@ -504,17 +500,17 @@ class autoptimizeCriticalCSSCron {
504
 
505
  // Body must be json and log it.
506
  $body = json_encode( $body );
507
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: POST generate request body is ' . $body, 3 );
508
 
509
  // Prepare the request.
510
  $url = esc_url_raw( AO_CCSS_API . 'generate?aover=' . AO_CCSS_VER );
511
  $args = array(
512
- 'headers' => array(
513
  'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
514
  'Content-type' => 'application/json; charset=utf-8',
515
  'Authorization' => 'JWT ' . $key,
516
  'Connection' => 'close',
517
- ),
518
  'body' => $body,
519
  );
520
 
@@ -533,42 +529,42 @@ class autoptimizeCriticalCSSCron {
533
  // Workaround criticalcss.com non-RESTful reponses.
534
  if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] || 'STATUS_JOB_BAD' == $body['job']['status'] ) {
535
  // Log successful and return encoded request body.
536
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied successfully', 3 );
537
 
538
  // This code also means the key is valid, so cache key status for 24h if not already cached.
539
  if ( ( ! $key_status || 2 != $key_status ) && $key ) {
540
  update_option( 'autoptimize_ccss_keyst', 2 );
541
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );
542
  }
543
 
544
  // Return the request body.
545
  return $body;
546
  } else {
547
  // Log successful requests with invalid reponses.
548
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied with code <' . $code . '> and an UNKNOWN error condition, body follows...', 2 );
549
- autoptimizeCriticalCSSCore::ao_ccss_log( print_r( $body, true ), 2 );
550
  return $body;
551
  }
552
  } else {
553
  // Response code is anything else.
554
  // Log failed request with a valid response code and return body.
555
  if ( $code ) {
556
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied with error code <' . $code . '>, body follows...', 2 );
557
- autoptimizeCriticalCSSCore::ao_ccss_log( print_r( $body, true ), 2 );
558
 
559
  if ( 401 == $code ) {
560
  // If request is unauthorized, also clear key status.
561
  update_option( 'autoptimize_ccss_keyst', 1 );
562
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
563
  }
564
 
565
  // Return the request body.
566
  return $body;
567
  } else {
568
  // Log failed request with no response and return false.
569
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: POST generate request for path <' . $src_url . '> has no response, this could be a service timeout', 2 );
570
  if ( is_wp_error( $req ) ) {
571
- autoptimizeCriticalCSSCore::ao_ccss_log( $req->get_error_message(), 2 );
572
  }
573
 
574
  return false;
@@ -579,17 +575,16 @@ class autoptimizeCriticalCSSCron {
579
  public function ao_ccss_api_results( $jobid, $debug, $dcode ) {
580
  // GET jobs from criticalcss.com and return responses
581
  // Get key.
582
- global $ao_ccss_key;
583
- $key = $ao_ccss_key;
584
 
585
  // Prepare the request.
586
  $url = AO_CCSS_API . 'results?resultId=' . $jobid;
587
  $args = array(
588
- 'headers' => array(
589
  'User-Agent' => 'Autoptimize CriticalCSS Power-Up v' . AO_CCSS_VER,
590
  'Authorization' => 'JWT ' . $key,
591
  'Connection' => 'close',
592
- ),
593
  );
594
 
595
  // Dispatch the request and store its response code.
@@ -607,36 +602,36 @@ class autoptimizeCriticalCSSCron {
607
  if ( is_array( $body ) && ( array_key_exists( 'status', $body ) || array_key_exists( 'job', $body ) ) && ( 'JOB_QUEUED' == $body['status'] || 'JOB_ONGOING' == $body['status'] || 'JOB_DONE' == $body['status'] || 'JOB_FAILED' == $body['status'] || 'JOB_UNKNOWN' == $body['status'] || 'STATUS_JOB_BAD' == $body['job']['status'] ) ) {
608
  // Workaround criticalcss.com non-RESTful reponses
609
  // Log successful and return encoded request body.
610
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied successfully', 3 );
611
  return $body;
612
  } elseif ( is_array( $body ) && ( array_key_exists( 'error', $body ) && 'This css no longer exists. Please re-generate it.' == $body['error'] ) ) {
613
  // Handle no CSS reply
614
  // Log no CSS error and return encoded request body.
615
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied successfully but the CSS for it does not exist anymore', 3 );
616
  return $body;
617
  } else {
618
  // Log failed request and return false.
619
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied with code <' . $code . '> and an UNKNOWN error condition, body follows...', 2 );
620
- autoptimizeCriticalCSSCore::ao_ccss_log( print_r( $body, true ), 2 );
621
  return false;
622
  }
623
  } else {
624
  // Response code is anything else
625
  // Log failed request with a valid response code and return body.
626
  if ( $code ) {
627
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied with error code <' . $code . '>, body follows...', 2 );
628
- autoptimizeCriticalCSSCore::ao_ccss_log( print_r( $body, true ), 2 );
629
  if ( 401 == $code ) {
630
  // If request is unauthorized, also clear key status.
631
  update_option( 'autoptimize_ccss_keyst', 1 );
632
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
633
  }
634
 
635
  // Return the request body.
636
  return $body;
637
  } else {
638
  // Log failed request with no response and return false.
639
- autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> has no response, this could be a service timeout', 2 );
640
  return false;
641
  }
642
  }
@@ -655,39 +650,40 @@ class autoptimizeCriticalCSSCron {
655
  $filename = false;
656
  $content = $ccss;
657
 
658
- if ( autoptimizeCriticalCSSCore::ao_ccss_check_contents( $content ) ) {
659
  // Sanitize content, set filename and try to save file.
660
  $file = AO_CCSS_DIR . 'ccss_' . md5( $ccss . $target[1] ) . $rmark . '.css';
661
  $status = file_put_contents( $file, $content, LOCK_EX );
662
  $filename = pathinfo( $file, PATHINFO_BASENAME );
663
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS file for the rule <' . $target[0] . '|' . $target[1] . '> was saved as <' . $filename . '>, size in bytes is <' . $status . '>', 3 );
664
 
665
  if ( ! $status ) {
666
  // If file has not been saved, reset filename.
667
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS file <' . $filename . '> could not be not saved', 2 );
668
  $filename = false;
669
  return $filename;
670
  }
671
  } else {
672
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received did not pass content check', 2 );
673
  return $filename;
674
  }
675
 
676
  // Remove old critical CSS if a previous one existed in the rule and if that file exists in filesystem
677
- // NOTE: out of scope critical CSS file removal (issue #5)
678
  // Attach required arrays.
679
- global $ao_ccss_rules;
680
-
681
- // Prepare rule variables.
682
- $srule = $ao_ccss_rules[ $target[0] ][ $target[1] ];
683
- $oldfile = $srule['file'];
684
-
685
- if ( $oldfile && $oldfile !== $filename ) {
686
- $delfile = AO_CCSS_DIR . $oldfile;
687
- if ( file_exists( $delfile ) ) {
688
- $unlinkst = unlink( $delfile );
689
- if ( $unlinkst ) {
690
- autoptimizeCriticalCSSCore::ao_ccss_log( 'A previous critical CSS file <' . $oldfile . '> was removed for the rule <' . $target[0] . '|' . $target[1] . '>', 3 );
 
 
691
  }
692
  }
693
  }
@@ -699,26 +695,30 @@ class autoptimizeCriticalCSSCron {
699
  public function ao_ccss_rule_update( $ljid, $srule, $file, $hash ) {
700
  // Update or create a rule
701
  // Attach required arrays.
702
- global $ao_ccss_rules;
703
 
704
  // Prepare rule variables.
705
  $trule = explode( '|', $srule );
706
- $rule = $ao_ccss_rules[ $trule[0] ][ $trule[1] ];
 
 
 
 
707
  $action = false;
708
  $rtype = '';
709
 
710
- if ( 0 === $rule['hash'] && 0 !== $rule['file'] ) {
711
  // manual rule, don't ever overwrite.
712
  $action = 'NOT UPDATED';
713
  $rtype = 'MANUAL';
714
- } elseif ( 0 === $rule['hash'] && 0 === $rule['file'] ) {
715
  // If this is an user created AUTO rule with no hash and file yet, update its hash and filename
716
  // Set rule hash, file and action flag.
717
  $rule['hash'] = $hash;
718
  $rule['file'] = $file;
719
  $action = 'UPDATED';
720
  $rtype = 'AUTO';
721
- } elseif ( 0 !== $rule['hash'] && ctype_alnum( $rule['hash'] ) ) {
722
  // If this is an genuine AUTO rule, update its hash and filename
723
  // Set rule hash, file and action flag.
724
  $rule['hash'] = $hash;
@@ -736,18 +736,19 @@ class autoptimizeCriticalCSSCron {
736
  $rtype = 'AUTO';
737
  } else {
738
  // Log that no rule was created.
739
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Exception, no AUTO rule created', 3 );
740
  }
741
  }
742
 
743
  if ( $action ) {
744
  // If a rule creation/update is required, persist updated rules object.
745
- $ao_ccss_rules[ $trule[0] ][ $trule[1] ] = $rule;
746
- $ao_ccss_rules_raw = json_encode( $ao_ccss_rules );
747
- update_option( 'autoptimize_ccss_rules', $ao_ccss_rules_raw );
748
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Target rule <' . $srule . '> of type <' . $rtype . '> was ' . $action . ' for job id <' . $ljid . '>', 3 );
 
749
  } else {
750
- autoptimizeCriticalCSSCore::ao_ccss_log( 'No rule action required', 3 );
751
  }
752
  }
753
 
@@ -811,10 +812,11 @@ class autoptimizeCriticalCSSCron {
811
  }
812
 
813
  // Queue cleaning.
814
- global $ao_ccss_queue;
 
815
  $queue_purge_threshold = 100;
816
  $queue_purge_age = 24 * 60 * 60;
817
- $queue_length = count( $ao_ccss_queue );
818
  $timestamp_yesterday = microtime( true ) - $queue_purge_age;
819
  $remove_old_new = false;
820
  $queue_altered = false;
@@ -823,23 +825,23 @@ class autoptimizeCriticalCSSCron {
823
  $remove_old_new = true;
824
  }
825
 
826
- foreach ( $ao_ccss_queue as $path => $job ) {
827
  if ( ( $remove_old_new && 'NEW' == $job['jqstat'] && $job['jctime'] < $timestamp_yesterday ) || in_array( $job['jqstat'], array( 'JOB_FAILED', 'STATUS_JOB_BAD', 'NO_CSS', 'NO_RESPONSE' ) ) ) {
828
- unset( $ao_ccss_queue[ $path ] );
829
  $queue_altered = true;
830
  }
831
  }
832
 
833
  // save queue to options!
834
  if ( $queue_altered ) {
835
- $ao_ccss_queue_raw = json_encode( $ao_ccss_queue );
836
- update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false );
837
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Queue cleaning done.', 3 );
838
  }
839
 
840
  // re-check key if invalid.
841
- global $ao_ccss_keyst;
842
- if ( 1 == $ao_ccss_keyst ) {
843
  $this->ao_ccss_api_generate( '', '', '' );
844
  }
845
  }
9
  }
10
 
11
  class autoptimizeCriticalCSSCron {
12
+ public function __construct() {
13
+ $this->criticalcss = autoptimize()->criticalcss();
 
 
 
 
 
 
14
 
15
  // Add queue control to a registered event.
16
  add_action( 'ao_ccss_queue', array( $this, 'ao_ccss_queue_control' ) );
20
 
21
  public function ao_ccss_queue_control() {
22
  // The queue execution backend.
23
+ $key = $this->criticalcss->get_option( 'key' );
24
+
25
+ if ( empty( $key ) ) {
26
  // no key set, not processing the queue!
27
+ $this->criticalcss->log( 'No key set, so not processing queue.', 3 );
28
  return;
29
  }
30
 
51
  if ( $qdobj ) {
52
  if ( 1 === $qdobj['enable'] ) {
53
  $queue_debug = true;
54
+ $this->criticalcss->log( 'Queue operating in debug mode with the following settings: <' . $qdobj_raw . '>', 3 );
55
  }
56
  }
57
  }
71
  if ( ! $queue_lock ) {
72
 
73
  // Log queue start and create the lock file.
74
+ $this->criticalcss->log( 'Queue control started', 3 );
75
  if ( touch( AO_CCSS_LOCK ) ) {
76
+ $this->criticalcss->log( 'Queue control locked', 3 );
77
  }
78
 
79
  // Attach required variables.
80
+ $queue = $this->criticalcss->get_option( 'queue' );
81
+ $rtimelimit = $this->criticalcss->get_option( 'rtimelimit' );
82
 
83
  // Initialize counters.
84
+ if ( $rtimelimit == 0 ) {
85
  // no time limit set, let's go with 1000 seconds.
86
+ $rtimelimit = 1000;
87
  }
88
+ $mt = time() + $rtimelimit; // maxtime queue processing can run.
89
  $jc = 1; // job count number.
90
  $jr = 1; // jobs requests number.
91
+ $jt = count( $queue ); // number of jobs in queue.
92
 
93
  // Sort queue by ascending job status (e.g. ERROR, JOB_ONGOING, JOB_QUEUED, NEW...).
94
+ array_multisort( array_column( $queue, 'jqstat' ), $queue ); // @codingStandardsIgnoreLine
95
 
96
  // Iterates over the entire queue.
97
+ foreach ( $queue as $path => $jprops ) {
98
  // Prepare flags and target rule.
99
  $update = false;
100
  $deljob = false;
103
  $trule = explode( '|', $jprops['rtarget'] );
104
 
105
  // Log job count.
106
+ $this->criticalcss->log( 'Processing job ' . $jc . ' of ' . $jt . ' with id <' . $jprops['ljid'] . '> and status <' . $jprops['jqstat'] . '>', 3 );
107
 
108
  // Process NEW jobs.
109
  if ( 'NEW' == $jprops['jqstat'] ) {
110
 
111
  // Log the new job.
112
+ $this->criticalcss->log( 'Found NEW job with local ID <' . $jprops['ljid'] . '>, starting its queue processing', 3 );
113
 
114
  // Compare job and rule hashes (if any).
115
  $hash = $this->ao_ccss_diff_hashes( $jprops['ljid'], $jprops['hash'], $jprops['hashes'], $jprops['rtarget'] );
119
  if ( $jr > 2 ) {
120
  // we already posted 2 jobs to criticalcss.com, don't post more this run
121
  // but we can keep on processing the queue to keep it tidy.
122
+ $this->criticalcss->log( 'Holding off on generating request for job with local ID <' . $jprops['ljid'] . '>, maximum number of POSTS reached.', 3 );
123
  continue;
124
  }
125
 
131
  $jr++;
132
 
133
  // NOTE: All the following conditions maps to the ones in admin_settings_queue.js.php.
134
+ if ( empty( $apireq ) ) {
135
+ // ERROR: no response
136
+ // Update job properties.
137
+ $jprops['jqstat'] = 'NO_RESPONSE';
138
+ $jprops['jrstat'] = 'NONE';
139
+ $jprops['jvstat'] = 'NONE';
140
+ $jprops['jftime'] = microtime( true );
141
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> request has no response, status now is <' . $jprops['jqstat'] . '>', 3 );
142
+ } elseif ( array_key_exists( 'errorCode', $apireq ) && 'INVALID_JWT_TOKEN' == $apireq['errorCode'] ) {
143
+ // ERROR: key validation
144
+ // Update job properties.
145
+ $jprops['jqstat'] = $apireq['errorCode'];
146
+ $jprops['jrstat'] = $apireq['error'];
147
+ $jprops['jvstat'] = 'NONE';
148
+ $jprops['jftime'] = microtime( true );
149
+ $this->criticalcss->log( 'API key validation error when processing job id <' . $jprops['ljid'] . '>, job status is now <' . $jprops['jqstat'] . '>', 3 );
150
+ } elseif ( array_key_exists( 'job', $apireq ) && 'JOB_QUEUED' == $apireq['job']['status'] || 'JOB_ONGOING' == $apireq['job']['status'] ) {
151
  // SUCCESS: request has a valid result.
152
  // Update job properties.
153
  $jprops['jid'] = $apireq['job']['id'];
154
  $jprops['jqstat'] = $apireq['job']['status'];
155
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> generate request successful, remote id <' . $jprops['jid'] . '>, status now is <' . $jprops['jqstat'] . '>', 3 );
156
  } elseif ( 'STATUS_JOB_BAD' == $apireq['job']['status'] ) {
157
  // ERROR: concurrent requests
158
  // Update job properties.
165
  }
166
  $jprops['jvstat'] = 'NONE';
167
  $jprops['jftime'] = microtime( true );
168
+ $this->criticalcss->log( 'Concurrent requests when processing job id <' . $jprops['ljid'] . '>, job status is now <' . $jprops['jqstat'] . '>', 3 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  } else {
170
  // UNKNOWN: unhandled generate exception
171
  // Update job properties.
173
  $jprops['jrstat'] = 'NONE';
174
  $jprops['jvstat'] = 'NONE';
175
  $jprops['jftime'] = microtime( true );
176
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> generate request has an UNKNOWN condition, status now is <' . $jprops['jqstat'] . '>, check log messages above for more information', 2 );
177
+ $this->criticalcss->log( 'Job response was: ' . json_encode( $apireq ), 3 );
178
  }
179
  } else {
180
  // SUCCESS: Job hash is equal to a previous one, so it's done
181
  // Update job status and finish time.
182
  $jprops['jqstat'] = 'JOB_DONE';
183
  $jprops['jftime'] = microtime( true );
184
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> requires no further processing, status now is <' . $jprops['jqstat'] . '>', 3 );
185
  }
186
 
187
  // Set queue update flag.
190
  } elseif ( 'JOB_QUEUED' == $jprops['jqstat'] || 'JOB_ONGOING' == $jprops['jqstat'] ) {
191
  // Process QUEUED and ONGOING jobs
192
  // Log the pending job.
193
+ $this->criticalcss->log( 'Found PENDING job with local ID <' . $jprops['ljid'] . '>, continuing its queue processing', 3 );
194
 
195
  // Dispatch the job result request and increment request count.
196
  $apireq = $this->ao_ccss_api_results( $jprops['jid'], $queue_debug, $qdobj['htcode'] );
214
  // Process a PENDING job
215
  // Update job properties.
216
  $jprops['jqstat'] = $apireq['status'];
217
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . '> unchanged', 3 );
218
  } elseif ( 'JOB_DONE' == $apireq['status'] ) {
219
  // Process a DONE job
220
  // New resultStatus from ccss.com "HTML_404", consider as "GOOD" for now.
231
  $jprops['jvstat'] = $apireq['validationStatus'];
232
  $jprops['jftime'] = microtime( true );
233
  $rule_update = true;
234
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . '>, file saved <' . $jprops['file'] . '>', 3 );
235
  } elseif ( 'GOOD' == $apireq['resultStatus'] && ( 'BAD' == $apireq['validationStatus'] || 'SCREENSHOT_WARN_BLANK' == $apireq['validationStatus'] ) ) {
236
  // SUCCESS: GOOD job with BAD or SCREENSHOT_WARN_BLANK validation
237
  // Update job properties.
242
  if ( apply_filters( 'autoptimize_filter_ccss_save_review_rules', true ) ) {
243
  $jprops['file'] = $this->ao_ccss_save_file( $apireq['css'], $trule, true );
244
  $rule_update = true;
245
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . ', file saved <' . $jprops['file'] . '> but requires REVIEW', 3 );
246
  } else {
247
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful, remote id <' . $jprops['jid'] . '>, status <' . $jprops['jqstat'] . ', file not saved because it required REVIEW.', 3 );
248
  }
249
  } elseif ( 'GOOD' != $apireq['resultStatus'] && ( 'GOOD' != $apireq['validationStatus'] || 'WARN' != $apireq['validationStatus'] || 'BAD' != $apireq['validationStatus'] || 'SCREENSHOT_WARN_BLANK' != $apireq['validationStatus'] ) ) {
250
  // ERROR: no GOOD, WARN or BAD results
253
  $jprops['jrstat'] = $apireq['resultStatus'];
254
  $jprops['jvstat'] = $apireq['validationStatus'];
255
  $jprops['jftime'] = microtime( true );
256
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
257
  $apireq['css'] = '/* critical css removed for DEBUG logging purposes */';
258
+ $this->criticalcss->log( 'Job response was: ' . json_encode( $apireq ), 3 );
259
  } else {
260
  // UNKNOWN: unhandled JOB_DONE exception
261
  // Update job properties.
263
  $jprops['jrstat'] = $apireq['resultStatus'];
264
  $jprops['jvstat'] = $apireq['validationStatus'];
265
  $jprops['jftime'] = microtime( true );
266
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful but job is UNKNOWN, status now is <' . $jprops['jqstat'] . '>', 2 );
267
  $apireq['css'] = '/* critical css removed for DEBUG logging purposes */';
268
+ $this->criticalcss->log( 'Job response was: ' . json_encode( $apireq ), 3 );
269
  }
270
  } elseif ( 'JOB_FAILED' == $apireq['job']['status'] || 'STATUS_JOB_BAD' == $apireq['job']['status'] ) {
271
  // ERROR: failed job
278
  }
279
  $jprops['jvstat'] = 'NONE';
280
  $jprops['jftime'] = microtime( true );
281
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
282
  } elseif ( 'This css no longer exists. Please re-generate it.' == $apireq['error'] ) {
283
  // ERROR: CSS doesn't exist
284
  // Update job properties.
286
  $jprops['jrstat'] = $apireq['error'];
287
  $jprops['jvstat'] = 'NONE';
288
  $jprops['jftime'] = microtime( true );
289
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request successful but job FAILED, status now is <' . $jprops['jqstat'] . '>', 3 );
290
  } elseif ( empty( $apireq ) ) {
291
  // ERROR: no response
292
  // Update job properties.
294
  $jprops['jrstat'] = 'NONE';
295
  $jprops['jvstat'] = 'NONE';
296
  $jprops['jftime'] = microtime( true );
297
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> request has no response, status now is <' . $jprops['jqstat'] . '>', 3 );
298
  } else {
299
  // UNKNOWN: unhandled results exception
300
  // Update job properties.
302
  $jprops['jrstat'] = 'NONE';
303
  $jprops['jvstat'] = 'NONE';
304
  $jprops['jftime'] = microtime( true );
305
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> result request has an UNKNOWN condition, status now is <' . $jprops['jqstat'] . '>, check log messages above for more information', 2 );
306
  }
307
 
308
  // Set queue update flag.
319
  if ( $update ) {
320
  if ( ! $deljob ) {
321
  // Update properties of a NEW or PENDING job...
322
+ $queue[ $path ] = $jprops;
323
  } else {
324
  // ...or remove the DONE job.
325
+ unset( $queue[ $path ] );
326
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> is DONE and was removed from the queue', 3 );
327
  }
328
 
329
  // Update queue object.
330
+ $queue_raw = json_encode( $queue );
331
+ update_option( 'autoptimize_ccss_queue', $queue_raw, false );
332
+ $this->criticalcss->log( 'Queue updated by job id <' . $jprops['ljid'] . '>', 3 );
333
 
334
  // Update target rule.
335
  if ( $rule_update ) {
336
  $this->ao_ccss_rule_update( $jprops['ljid'], $jprops['rtarget'], $jprops['file'], $jprops['hash'] );
337
+ $this->criticalcss->log( 'Job id <' . $jprops['ljid'] . '> updated the target rule <' . $jprops['rtarget'] . '>', 3 );
338
  }
339
  } else {
340
  // Or log no queue action.
341
+ $this->criticalcss->log( 'Nothing to do on this job', 3 );
342
  }
343
 
344
  // Break the loop if request time limit is (almost exceeded).
345
  if ( time() > $mt ) {
346
+ $this->criticalcss->log( 'The time limit of ' . $rtimelimit . ' seconds was exceeded, queue control must finish now', 3 );
347
  break;
348
  }
349
 
354
  // Remove the lock file and log the queue end.
355
  if ( file_exists( AO_CCSS_LOCK ) ) {
356
  unlink( AO_CCSS_LOCK );
357
+ $this->criticalcss->log( 'Queue control unlocked', 3 );
358
  }
359
+ $this->criticalcss->log( 'Queue control finished', 3 );
360
 
361
  // Log that queue is locked.
362
  } else {
363
+ $this->criticalcss->log( 'Queue is already running, skipping the attempt to run it again', 3 );
364
  }
365
  }
366
 
371
  // Job with a single hash
372
  // Set job hash.
373
  $hash = $hashes[0];
374
+ $this->criticalcss->log( 'Job id <' . $ljid . '> updated with SINGLE hash <' . $hash . '>', 3 );
375
  } else {
376
  // Job with multiple hashes
377
  // Loop through hashes to concatenate them.
382
 
383
  // Set job hash.
384
  $hash = md5( $nhash );
385
+ $this->criticalcss->log( 'Job id <' . $ljid . '> updated with a COMPOSITE hash <' . $hash . '>', 3 );
386
  }
387
 
388
  // STEP 2: compare job to existing jobs to prevent double submission for same type+hash.
389
+ $queue = $this->criticalcss->get_option( 'queue' );
390
 
391
+ foreach ( $queue as $queue_item ) {
392
+ $this->criticalcss->log( 'Comparing <' . $rule . $hash . '> with <' . $queue_item['rtarget'] . $queue_item['hash'] . '>', 3 );
393
  if ( $queue_item['hash'] == $hash && $queue_item['rtarget'] == $rule && in_array( $queue_item['jqstat'], array( 'JOB_QUEUED', 'JOB_ONGOING', 'JOB_DONE' ) ) ) {
394
+ $this->criticalcss->log( 'Job id <' . $ljid . '> matches the already pending job <' . $queue_item['ljid'] . '>', 3 );
395
  return false;
396
  }
397
  }
398
 
399
  // STEP 3: compare job and existing rule (if any) hashes
400
  // Attach required arrays.
401
+ $rules = $this->criticalcss->get_option( 'rules' );
402
 
403
  // Prepare rule variables.
404
  $trule = explode( '|', $rule );
405
+ if ( is_array( $trule ) && ! empty( $trule ) && array_key_exists( $trule[1], $rules[ $trule[0] ] ) ) {
406
+ $srule = $rules[ $trule[0] ][ $trule[1] ];
407
+ } else {
408
+ $srule = '';
409
+ }
410
+
411
  // If hash is empty, set it to now for a "forced job".
412
+ if ( empty( $hash ) ) {
413
  $hash = 'new';
414
+ $this->criticalcss->log( 'Job id <' . $ljid . '> had no hash, assuming forced job so setting hash to new', 3 );
415
  }
416
 
417
  // Check if a MANUAL rule exist and return false.
418
  if ( ! empty( $srule ) && ( 0 == $srule['hash'] && 0 != $srule['file'] ) ) {
419
+ $this->criticalcss->log( 'Job id <' . $ljid . '> matches the MANUAL rule <' . $trule[0] . '|' . $trule[1] . '>', 3 );
420
  return false;
421
  } elseif ( ! empty( $srule ) ) {
422
  // Check if an AUTO rule exist.
423
  if ( $hash === $srule['hash'] && is_file( AO_CCSS_DIR . $srule['file'] ) && 0 != filesize( AO_CCSS_DIR . $srule['file'] ) ) {
424
  // Check if job hash matches rule, if the CCSS file exists said file is not empty and return FALSE is so.
425
+ $this->criticalcss->log( 'Job id <' . $ljid . '> with hash <' . $hash . '> MATCH the one in rule <' . $trule[0] . '|' . $trule[1] . '>', 3 );
426
  return false;
427
  } else {
428
  // Or return the new hash if they differ.
429
+ $this->criticalcss->log( 'Job id <' . $ljid . '> with hash <' . $hash . '> DOES NOT MATCH the one in rule <' . $trule[0] . '|' . $trule[1] . '> or rule\'s CCSS file was invalid.', 3 );
430
  return $hash;
431
  }
432
  } else {
433
  // Return the hash for a job that has no rule yet.
434
+ $this->criticalcss->log( 'Job id <' . $ljid . '> with hash <' . $hash . '> has no rule yet', 3 );
435
  return $hash;
436
  }
437
  }
439
  public function ao_ccss_api_generate( $path, $debug, $dcode ) {
440
  // POST jobs to criticalcss.com and return responses
441
  // Get key and key status.
442
+ $key = $this->criticalcss->get_option( 'key' );
443
+ $key_status = $this->criticalcss->get_option( 'keyst' );
444
+ $noptimize = $this->criticalcss->get_option( 'noptimize' );
 
445
 
446
  // Prepare full URL to request.
 
 
447
  $site_host = get_site_url();
448
  $site_path = parse_url( $site_host, PHP_URL_PATH );
449
 
454
  // Logic to bind to one domain to avoid site clones of sites would
455
  // automatically begin spawning requests to criticalcss.com which has
456
  // a per domain cost.
457
+ $domain = $this->criticalcss->get_option( 'domain' );
458
+ if ( empty( $domain ) ) {
459
  // first request being done, update option to allow future requests are only allowed if from same domain.
460
  update_option( 'autoptimize_ccss_domain', str_rot13( $site_host ) );
461
+ } elseif ( trim( $domain, '\'"' ) !== 'none' && parse_url( $site_host, PHP_URL_HOST ) !== parse_url( $domain, PHP_URL_HOST ) && apply_filters( 'autoptimize_filter_ccss_bind_domain', true ) ) {
462
  // not the same domain, log as error and return without posting to criticalcss.com.
463
+ $this->criticalcss->log( 'Request for domain ' . $site_host . ' does not match bound domain ' . $domain . ' so not proceeding.', 2 );
464
  return false;
465
  }
466
 
467
  $src_url = $site_host . $path;
468
 
469
  // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
470
+ if ( ! empty( $noptimize ) ) {
471
  $src_url .= '?ao_noptirocket=1';
472
  } elseif ( ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) || apply_filters( 'autoptimize_filter_ccss_enforce_nolazy', false ) ) {
473
  $src_url .= '?ao_nolazy=1';
482
  $body['aocssv'] = AO_CCSS_VER;
483
 
484
  // Prepare and add viewport size to the body if available.
485
+ $viewport = $this->criticalcss->viewport();
486
  if ( ! empty( $viewport['w'] ) && ! empty( $viewport['h'] ) ) {
487
  $body['width'] = $viewport['w'];
488
  $body['height'] = $viewport['h'];
489
  }
490
 
491
  // Prepare and add forceInclude to the body if available.
492
+ $finclude = $this->criticalcss->get_option( 'finclude' );
493
+ $finclude = $this->ao_ccss_finclude( $finclude );
494
  if ( ! empty( $finclude ) ) {
495
  $body['forceInclude'] = $finclude;
496
  }
500
 
501
  // Body must be json and log it.
502
  $body = json_encode( $body );
503
+ $this->criticalcss->log( 'criticalcss.com: POST generate request body is ' . $body, 3 );
504
 
505
  // Prepare the request.
506
  $url = esc_url_raw( AO_CCSS_API . 'generate?aover=' . AO_CCSS_VER );
507
  $args = array(
508
+ 'headers' => apply_filters( 'autoptimize_ccss_cron_api_generate_headers', array(
509
  'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
510
  'Content-type' => 'application/json; charset=utf-8',
511
  'Authorization' => 'JWT ' . $key,
512
  'Connection' => 'close',
513
+ ) ),
514
  'body' => $body,
515
  );
516
 
529
  // Workaround criticalcss.com non-RESTful reponses.
530
  if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] || 'STATUS_JOB_BAD' == $body['job']['status'] ) {
531
  // Log successful and return encoded request body.
532
+ $this->criticalcss->log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied successfully', 3 );
533
 
534
  // This code also means the key is valid, so cache key status for 24h if not already cached.
535
  if ( ( ! $key_status || 2 != $key_status ) && $key ) {
536
  update_option( 'autoptimize_ccss_keyst', 2 );
537
+ $this->criticalcss->log( 'criticalcss.com: API key is valid, updating key status', 3 );
538
  }
539
 
540
  // Return the request body.
541
  return $body;
542
  } else {
543
  // Log successful requests with invalid reponses.
544
+ $this->criticalcss->log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied with code <' . $code . '> and an UNKNOWN error condition, body follows...', 2 );
545
+ $this->criticalcss->log( print_r( $body, true ), 2 );
546
  return $body;
547
  }
548
  } else {
549
  // Response code is anything else.
550
  // Log failed request with a valid response code and return body.
551
  if ( $code ) {
552
+ $this->criticalcss->log( 'criticalcss.com: POST generate request for path <' . $src_url . '> replied with error code <' . $code . '>, body follows...', 2 );
553
+ $this->criticalcss->log( print_r( $body, true ), 2 );
554
 
555
  if ( 401 == $code ) {
556
  // If request is unauthorized, also clear key status.
557
  update_option( 'autoptimize_ccss_keyst', 1 );
558
+ $this->criticalcss->log( 'criticalcss.com: API key is invalid, updating key status', 3 );
559
  }
560
 
561
  // Return the request body.
562
  return $body;
563
  } else {
564
  // Log failed request with no response and return false.
565
+ $this->criticalcss->log( 'criticalcss.com: POST generate request for path <' . $src_url . '> has no response, this could be a service timeout', 2 );
566
  if ( is_wp_error( $req ) ) {
567
+ $this->criticalcss->log( $req->get_error_message(), 2 );
568
  }
569
 
570
  return false;
575
  public function ao_ccss_api_results( $jobid, $debug, $dcode ) {
576
  // GET jobs from criticalcss.com and return responses
577
  // Get key.
578
+ $key = $this->criticalcss->get_option( 'key' );
 
579
 
580
  // Prepare the request.
581
  $url = AO_CCSS_API . 'results?resultId=' . $jobid;
582
  $args = array(
583
+ 'headers' => apply_filters( 'autoptimize_ccss_cron_api_generate_headers', array(
584
  'User-Agent' => 'Autoptimize CriticalCSS Power-Up v' . AO_CCSS_VER,
585
  'Authorization' => 'JWT ' . $key,
586
  'Connection' => 'close',
587
+ ) ),
588
  );
589
 
590
  // Dispatch the request and store its response code.
602
  if ( is_array( $body ) && ( array_key_exists( 'status', $body ) || array_key_exists( 'job', $body ) ) && ( 'JOB_QUEUED' == $body['status'] || 'JOB_ONGOING' == $body['status'] || 'JOB_DONE' == $body['status'] || 'JOB_FAILED' == $body['status'] || 'JOB_UNKNOWN' == $body['status'] || 'STATUS_JOB_BAD' == $body['job']['status'] ) ) {
603
  // Workaround criticalcss.com non-RESTful reponses
604
  // Log successful and return encoded request body.
605
+ $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied successfully', 3 );
606
  return $body;
607
  } elseif ( is_array( $body ) && ( array_key_exists( 'error', $body ) && 'This css no longer exists. Please re-generate it.' == $body['error'] ) ) {
608
  // Handle no CSS reply
609
  // Log no CSS error and return encoded request body.
610
+ $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied successfully but the CSS for it does not exist anymore', 3 );
611
  return $body;
612
  } else {
613
  // Log failed request and return false.
614
+ $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied with code <' . $code . '> and an UNKNOWN error condition, body follows...', 2 );
615
+ $this->criticalcss->log( print_r( $body, true ), 2 );
616
  return false;
617
  }
618
  } else {
619
  // Response code is anything else
620
  // Log failed request with a valid response code and return body.
621
  if ( $code ) {
622
+ $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> replied with error code <' . $code . '>, body follows...', 2 );
623
+ $this->criticalcss->log( print_r( $body, true ), 2 );
624
  if ( 401 == $code ) {
625
  // If request is unauthorized, also clear key status.
626
  update_option( 'autoptimize_ccss_keyst', 1 );
627
+ $this->criticalcss->log( 'criticalcss.com: API key is invalid, updating key status', 3 );
628
  }
629
 
630
  // Return the request body.
631
  return $body;
632
  } else {
633
  // Log failed request with no response and return false.
634
+ $this->criticalcss->log( 'criticalcss.com: GET results request for remote job id <' . $jobid . '> has no response, this could be a service timeout', 2 );
635
  return false;
636
  }
637
  }
650
  $filename = false;
651
  $content = $ccss;
652
 
653
+ if ( $this->criticalcss->check_contents( $content ) ) {
654
  // Sanitize content, set filename and try to save file.
655
  $file = AO_CCSS_DIR . 'ccss_' . md5( $ccss . $target[1] ) . $rmark . '.css';
656
  $status = file_put_contents( $file, $content, LOCK_EX );
657
  $filename = pathinfo( $file, PATHINFO_BASENAME );
658
+ $this->criticalcss->log( 'Critical CSS file for the rule <' . $target[0] . '|' . $target[1] . '> was saved as <' . $filename . '>, size in bytes is <' . $status . '>', 3 );
659
 
660
  if ( ! $status ) {
661
  // If file has not been saved, reset filename.
662
+ $this->criticalcss->log( 'Critical CSS file <' . $filename . '> could not be not saved', 2 );
663
  $filename = false;
664
  return $filename;
665
  }
666
  } else {
667
+ $this->criticalcss->log( 'Critical CSS received did not pass content check', 2 );
668
  return $filename;
669
  }
670
 
671
  // Remove old critical CSS if a previous one existed in the rule and if that file exists in filesystem
 
672
  // Attach required arrays.
673
+ $rules = $this->criticalcss->get_option( 'rules' );
674
+
675
+ // Only proceed if the rule already existed.
676
+ if ( array_key_exists( $target[1], $rules[ $target[0] ] ) ) {
677
+ $srule = $rules[ $target[0] ][ $target[1] ];
678
+ $oldfile = $srule['file'];
679
+
680
+ if ( $oldfile && $oldfile !== $filename ) {
681
+ $delfile = AO_CCSS_DIR . $oldfile;
682
+ if ( file_exists( $delfile ) ) {
683
+ $unlinkst = unlink( $delfile );
684
+ if ( $unlinkst ) {
685
+ $this->criticalcss->log( 'A previous critical CSS file <' . $oldfile . '> was removed for the rule <' . $target[0] . '|' . $target[1] . '>', 3 );
686
+ }
687
  }
688
  }
689
  }
695
  public function ao_ccss_rule_update( $ljid, $srule, $file, $hash ) {
696
  // Update or create a rule
697
  // Attach required arrays.
698
+ $rules = $this->criticalcss->get_option( 'rules' );
699
 
700
  // Prepare rule variables.
701
  $trule = explode( '|', $srule );
702
+ if ( array_key_exists( $trule[1], $rules[$trule[0]] ) ) {
703
+ $rule = $rules[ $trule[0] ][ $trule[1] ];
704
+ } else {
705
+ $rule = array();
706
+ }
707
  $action = false;
708
  $rtype = '';
709
 
710
+ if ( is_array( $rule ) && 0 === $rule['hash'] && 0 !== $rule['file'] ) {
711
  // manual rule, don't ever overwrite.
712
  $action = 'NOT UPDATED';
713
  $rtype = 'MANUAL';
714
+ } elseif ( is_array( $rule ) && 0 === $rule['hash'] && 0 === $rule['file'] ) {
715
  // If this is an user created AUTO rule with no hash and file yet, update its hash and filename
716
  // Set rule hash, file and action flag.
717
  $rule['hash'] = $hash;
718
  $rule['file'] = $file;
719
  $action = 'UPDATED';
720
  $rtype = 'AUTO';
721
+ } elseif ( is_array( $rule ) && 0 !== $rule['hash'] && ctype_alnum( $rule['hash'] ) ) {
722
  // If this is an genuine AUTO rule, update its hash and filename
723
  // Set rule hash, file and action flag.
724
  $rule['hash'] = $hash;
736
  $rtype = 'AUTO';
737
  } else {
738
  // Log that no rule was created.
739
+ $this->criticalcss->log( 'Exception, no AUTO rule created', 3 );
740
  }
741
  }
742
 
743
  if ( $action ) {
744
  // If a rule creation/update is required, persist updated rules object.
745
+ $rules[ $trule[0] ][ $trule[1] ] = $rule;
746
+ $rules_raw = json_encode( $rules );
747
+ update_option( 'autoptimize_ccss_rules', $rules_raw );
748
+ $this->criticalcss->flush_options();
749
+ $this->criticalcss->log( 'Target rule <' . $srule . '> of type <' . $rtype . '> was ' . $action . ' for job id <' . $ljid . '>', 3 );
750
  } else {
751
+ $this->criticalcss->log( 'No rule action required', 3 );
752
  }
753
  }
754
 
812
  }
813
 
814
  // Queue cleaning.
815
+ $queue = $this->criticalcss->get_option( 'queue' );
816
+
817
  $queue_purge_threshold = 100;
818
  $queue_purge_age = 24 * 60 * 60;
819
+ $queue_length = count( $queue );
820
  $timestamp_yesterday = microtime( true ) - $queue_purge_age;
821
  $remove_old_new = false;
822
  $queue_altered = false;
825
  $remove_old_new = true;
826
  }
827
 
828
+ foreach ( $queue as $path => $job ) {
829
  if ( ( $remove_old_new && 'NEW' == $job['jqstat'] && $job['jctime'] < $timestamp_yesterday ) || in_array( $job['jqstat'], array( 'JOB_FAILED', 'STATUS_JOB_BAD', 'NO_CSS', 'NO_RESPONSE' ) ) ) {
830
+ unset( $queue[ $path ] );
831
  $queue_altered = true;
832
  }
833
  }
834
 
835
  // save queue to options!
836
  if ( $queue_altered ) {
837
+ $queue_raw = json_encode( $queue );
838
+ update_option( 'autoptimize_ccss_queue', $queue_raw, false );
839
+ $this->criticalcss->log( 'Queue cleaning done.', 3 );
840
  }
841
 
842
  // re-check key if invalid.
843
+ $keyst = $this->criticalcss->get_option( 'keyst' );
844
+ if ( 1 == $keyst ) {
845
  $this->ao_ccss_api_generate( '', '', '' );
846
  }
847
  }
classes/autoptimizeCriticalCSSEnqueue.php CHANGED
@@ -8,20 +8,13 @@ if ( ! defined( 'ABSPATH' ) ) {
8
  }
9
 
10
  class autoptimizeCriticalCSSEnqueue {
11
- public function __construct()
12
- {
13
- // fetch all options at once and populate them individually explicitely as globals.
14
- $all_options = autoptimizeCriticalCSSBase::fetch_options();
15
- foreach ( $all_options as $_option => $_value ) {
16
- global ${$_option};
17
- ${$_option} = $_value;
18
- }
19
  }
20
 
21
- public static function ao_ccss_enqueue( $hash = '', $path = '', $type = 'is_page' ) {
22
- $self = new self();
23
  // Get key status.
24
- $key = autoptimizeCriticalCSSCore::ao_ccss_key_status( false );
25
 
26
  // Queue is available to anyone...
27
  $enqueue = true;
@@ -29,187 +22,190 @@ class autoptimizeCriticalCSSEnqueue {
29
  // ... which are not the ones below.
30
  if ( 'nokey' == $key['status'] || 'invalid' == $key['status'] ) {
31
  $enqueue = false;
32
- autoptimizeCriticalCSSCore::ao_ccss_log( "Job queuing is not available: no valid API key found.", 3 );
33
- } elseif ( ! empty( $hash ) && ( is_user_logged_in() || is_feed() || is_404() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || $self->ao_ccss_ua() || false === apply_filters( 'autoptimize_filter_ccss_enqueue_should_enqueue', true ) ) ) {
34
  $enqueue = false;
35
- autoptimizeCriticalCSSCore::ao_ccss_log( "Job queuing is not available for WordPress's logged in users, feeds, error pages, ajax calls or calls from criticalcss.com itself.", 3 );
36
  } elseif ( empty( $hash ) && empty( $path ) || ( ( 'is_single' !== $type ) && ( 'is_page' !== $type ) ) ) {
37
  $enqueue = false;
38
- autoptimizeCriticalCSSCore::ao_ccss_log( "Forced job queuing failed, no path or not right type", 3 );
39
  }
40
 
41
- if ( $enqueue ) {
42
- // Continue if queue is available
43
- // Attach required arrays/ vars.
44
- global $ao_ccss_rules;
45
- global $ao_ccss_queue_raw;
46
- global $ao_ccss_queue;
47
- global $ao_ccss_forcepath;
48
-
49
- // Get request path and page type, and initialize the queue update flag.
50
- if ( ! empty( $hash ) ) {
51
- $req_orig = $_SERVER['REQUEST_URI'];
52
- $req_type = $self->ao_ccss_get_type();
53
- } elseif ( ! empty( $path ) ) {
54
- $req_orig = $path;
55
- if ( $path === '/' ) {
56
- $req_type = 'is_front_page';
57
- } else {
58
- $req_type = $type;
59
- }
 
 
60
  }
61
- $req_path = strtok( $req_orig, '?' );
62
-
63
- // Check if we have a lang param. we need to keep as WPML can switch languages based on that
64
- // and that includes RTL -> LTR so diff. structure, so rules would be RTL vs LTR
65
- // but this needs changes in the structur of the rule object so off by default for now
66
- // as now this will simply result in conditional rules being overwritten.
67
- if ( apply_filters( 'autoptimize_filter_ccss_coreenqueue_honor_lang', false ) && strpos( $req_orig, 'lang=' ) !== false ) {
68
- $req_params = strtok( '?' );
69
- parse_str( $req_params, $req_params_arr );
70
- if ( array_key_exists( 'lang', $req_params_arr ) && !empty( $req_params_arr['lang'] ) ) {
71
- $req_path .= '?lang=' . $req_params_arr['lang'];
72
- }
73
  }
 
74
 
75
- $job_qualify = false;
76
- $target_rule = false;
77
- $rule_properties = false;
78
- $queue_update = false;
79
 
80
- // Match for paths in rules.
81
- foreach ( $ao_ccss_rules['paths'] as $path => $props ) {
82
 
83
- // Prepare rule target and log.
84
- $target_rule = 'paths|' . $path;
85
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Qualifying path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );
86
 
87
- // Path match
88
- // -> exact match needed for AUTO rules
89
- // -> partial match OK for MANUAL rules (which have empty hash and a file with CCSS).
90
- if ( $path === $req_path || ( false == $props['hash'] && false != $props['file'] && preg_match( '|' . $path . '|', $req_path ) ) ) {
91
 
92
- // There's a path match in the rule, so job QUALIFIES with a path rule match.
93
- $job_qualify = true;
94
- $rule_properties = $props;
95
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );
96
 
97
- // Stop processing other path rules.
98
- break;
99
- }
100
  }
 
101
 
102
- // Match for types in rules if no path rule matches and if we're not enforcing paths.
103
- if ( '' !== $hash && ! $job_qualify && ( ! $ao_ccss_forcepath || ! in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) || ! apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) ) ) {
104
- foreach ( $ao_ccss_rules['types'] as $type => $props ) {
105
 
106
- // Prepare rule target and log.
107
- $target_rule = 'types|' . $type;
108
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Qualifying page type <' . $req_type . '> on path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );
109
 
110
- if ( $req_type == $type ) {
111
- // Type match.
112
- // There's a type match in the rule, so job QUALIFIES with a type rule match.
113
- $job_qualify = true;
114
- $rule_properties = $props;
115
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Page type <' . $req_type . '> on path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );
116
 
117
- // Stop processing other type rules.
118
- break;
119
- }
120
  }
121
  }
 
122
 
123
- if ( $job_qualify && ( ( false == $rule_properties['hash'] && false != $rule_properties['file'] ) || strpos( $req_type, 'template_' ) !== false ) ) {
124
- // If job qualifies but rule hash is false and file isn't false (MANUAL rule) or if template, job does not qualify despite what previous evaluations says.
125
- $job_qualify = false;
126
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission DISQUALIFIED by MANUAL rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
127
- } elseif ( ! $job_qualify && empty( $rule_properties ) ) {
128
- // But if job does not qualify and rule properties are set, job qualifies as there is no matching rule for it yet
129
- // Fill-in the new target rule.
130
- $job_qualify = true;
131
-
132
- // Should we switch to path-base AUTO-rules? Conditions:
133
- // 1. forcepath option has to be enabled (off by default)
134
- // 2. request type should be (by default, but filterable) one of is_page (removed for now: woo_is_product or woo_is_product_category).
135
- if ( ( $ao_ccss_forcepath && in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) ) || apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) || empty( $hash )) {
136
- if ( '/' !== $req_path ) {
137
- $target_rule = 'paths|' . $req_path;
138
- } else {
139
- // Exception; we don't want a path-based rule for "/" as that messes things up, hard-switch this to a type-based is_front_page rule.
140
- $target_rule = 'types|' . 'is_front_page';
141
- }
142
  } else {
143
- $target_rule = 'types|' . $req_type;
 
144
  }
145
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission QUALIFIED by MISSING rule for page type <' . $req_type . '> on path <' . $req_path . '>, new rule target is <' . $target_rule . '>', 3 );
146
  } else {
147
- // Or just log a job qualified by a matching rule.
148
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission QUALIFIED by AUTO rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
149
  }
 
 
 
 
 
150
 
151
- // Submit job.
152
- if ( $job_qualify ) {
153
- if ( ! array_key_exists( $req_path, $ao_ccss_queue ) ) {
154
- // This is a NEW job
155
- // Merge job into the queue.
156
- $ao_ccss_queue[ $req_path ] = $self->ao_ccss_define_job(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  $req_path,
158
  $target_rule,
159
  $req_type,
160
  $hash,
161
- null,
162
- null,
163
- null,
164
- null,
165
- true
166
  );
167
  // Set update flag.
168
  $queue_update = true;
169
- } else {
170
- // This is an existing job
171
- // The job is still NEW, most likely this is extra CSS file for the same page that needs a hash.
172
- if ( 'NEW' == $ao_ccss_queue[ $req_path ]['jqstat'] ) {
173
- // Add hash if it's not already in the job.
174
- if ( ! in_array( $hash, $ao_ccss_queue[ $req_path ]['hashes'] ) ) {
175
- // Push new hash to its array and update flag.
176
- $queue_update = array_push( $ao_ccss_queue[ $req_path ]['hashes'], $hash );
177
-
178
- // Log job update.
179
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Hashes UPDATED on local job id <' . $ao_ccss_queue[ $req_path ]['ljid'] . '>, job status NEW, target rule <' . $ao_ccss_queue[ $req_path ]['rtarget'] . '>, hash added: ' . $hash, 3 );
180
-
181
- // Return from here as the hash array is already updated.
182
- return true;
183
- }
184
- } elseif ( 'NEW' != $ao_ccss_queue[ $req_path ]['jqstat'] && 'JOB_QUEUED' != $ao_ccss_queue[ $req_path ]['jqstat'] && 'JOB_ONGOING' != $ao_ccss_queue[ $req_path ]['jqstat'] ) {
185
- // Allow requeuing jobs that are not NEW, JOB_QUEUED or JOB_ONGOING
186
- // Merge new job keeping some previous job values.
187
- $ao_ccss_queue[ $req_path ] = $self->ao_ccss_define_job(
188
- $req_path,
189
- $target_rule,
190
- $req_type,
191
- $hash,
192
- $ao_ccss_queue[ $req_path ]['file'],
193
- $ao_ccss_queue[ $req_path ]['jid'],
194
- $ao_ccss_queue[ $req_path ]['jrstat'],
195
- $ao_ccss_queue[ $req_path ]['jvstat'],
196
- false
197
- );
198
- // Set update flag.
199
- $queue_update = true;
200
- }
201
  }
 
202
 
203
- if ( $queue_update ) {
204
- // Persist the job to the queue and return.
205
- $ao_ccss_queue_raw = json_encode( $ao_ccss_queue );
206
- update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false );
207
- return true;
208
- } else {
209
- // Or just return false if no job was added.
210
- autoptimizeCriticalCSSCore::ao_ccss_log( 'A job for path <' . $req_path . '> already exist with NEW or PENDING status, skipping job creation', 3 );
211
- return false;
212
- }
213
  }
214
  }
215
  }
@@ -217,14 +213,14 @@ class autoptimizeCriticalCSSEnqueue {
217
  public function ao_ccss_get_type() {
218
  // Get the type of a page
219
  // Attach the conditional tags array.
220
- global $ao_ccss_types;
221
- global $ao_ccss_forcepath;
222
 
223
  // By default, a page type is false.
224
  $page_type = false;
225
 
226
  // Iterates over the array to match a type.
227
- foreach ( $ao_ccss_types as $type ) {
228
  if ( is_404() ) {
229
  $page_type = 'is_404';
230
  break;
@@ -232,13 +228,13 @@ class autoptimizeCriticalCSSEnqueue {
232
  // identify frontpage immediately to avoid it also matching a CPT or template.
233
  $page_type = 'is_front_page';
234
  break;
235
- } elseif ( strpos( $type, 'custom_post_' ) !== false && ( ! $ao_ccss_forcepath || ! is_page() ) ) {
236
  // Match custom post types and not page or page not forced to path-based.
237
  if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
238
  $page_type = $type;
239
  break;
240
  }
241
- } elseif ( strpos( $type, 'template_' ) !== false && ( ! $ao_ccss_forcepath || ! is_page() ) ) {
242
  // Match templates if not page or if page is not forced to path-based.
243
  if ( is_page_template( substr( $type, 9 ) ) ) {
244
  $page_type = $type;
@@ -287,7 +283,7 @@ class autoptimizeCriticalCSSEnqueue {
287
  }
288
 
289
  // Log job creation.
290
- autoptimizeCriticalCSSCore::ao_ccss_log( 'Job ' . $operation . ' with local job id <' . $path['ljid'] . '> for target rule <' . $target . '>', 3 );
291
 
292
  return $path;
293
  }
8
  }
9
 
10
  class autoptimizeCriticalCSSEnqueue {
11
+ public function __construct() {
12
+ $this->criticalcss = autoptimize()->criticalcss();
 
 
 
 
 
 
13
  }
14
 
15
+ public function ao_ccss_enqueue( $hash = '', $path = '', $type = 'is_page' ) {
 
16
  // Get key status.
17
+ $key = $this->criticalcss->key_status( false );
18
 
19
  // Queue is available to anyone...
20
  $enqueue = true;
22
  // ... which are not the ones below.
23
  if ( 'nokey' == $key['status'] || 'invalid' == $key['status'] ) {
24
  $enqueue = false;
25
+ $this->criticalcss->log( "Job queuing is not available: no valid API key found.", 3 );
26
+ } elseif ( ! empty( $hash ) && ( is_user_logged_in() || is_feed() || is_404() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || $this->ao_ccss_ua() || false === apply_filters( 'autoptimize_filter_ccss_enqueue_should_enqueue', true ) ) ) {
27
  $enqueue = false;
28
+ $this->criticalcss->log( "Job queuing is not available for WordPress's logged in users, feeds, error pages, ajax calls or calls from criticalcss.com itself.", 3 );
29
  } elseif ( empty( $hash ) && empty( $path ) || ( ( 'is_single' !== $type ) && ( 'is_page' !== $type ) ) ) {
30
  $enqueue = false;
31
+ $this->criticalcss->log( "Forced job queuing failed, no path or not right type", 3 );
32
  }
33
 
34
+ if ( ! $enqueue ) {
35
+ return;
36
+ }
37
+
38
+ // Continue if queue is available
39
+ // Attach required arrays/ vars.
40
+ $rules = $this->criticalcss->get_option( 'rules' );
41
+ $queue_raw = $this->criticalcss->get_option( 'queue_raw' );
42
+ $queue = $this->criticalcss->get_option( 'queue' );
43
+ $forcepath = $this->criticalcss->get_option( 'forcepath' );
44
+
45
+ // Get request path and page type, and initialize the queue update flag.
46
+ if ( ! empty( $hash ) ) {
47
+ $req_orig = $_SERVER['REQUEST_URI'];
48
+ $req_type = $this->ao_ccss_get_type();
49
+ } elseif ( ! empty( $path ) ) {
50
+ $req_orig = $path;
51
+ if ( $path === '/' ) {
52
+ $req_type = 'is_front_page';
53
+ } else {
54
+ $req_type = $type;
55
  }
56
+ }
57
+ $req_path = strtok( $req_orig, '?' );
58
+
59
+ // Check if we have a lang param. we need to keep as WPML can switch languages based on that
60
+ // and that includes RTL -> LTR so diff. structure, so rules would be RTL vs LTR
61
+ // but this needs changes in the structur of the rule object so off by default for now
62
+ // as now this will simply result in conditional rules being overwritten.
63
+ if ( apply_filters( 'autoptimize_filter_ccss_coreenqueue_honor_lang', false ) && strpos( $req_orig, 'lang=' ) !== false ) {
64
+ $req_params = strtok( '?' );
65
+ parse_str( $req_params, $req_params_arr );
66
+ if ( array_key_exists( 'lang', $req_params_arr ) && !empty( $req_params_arr['lang'] ) ) {
67
+ $req_path .= '?lang=' . $req_params_arr['lang'];
68
  }
69
+ }
70
 
71
+ $job_qualify = false;
72
+ $target_rule = false;
73
+ $rule_properties = false;
74
+ $queue_update = false;
75
 
76
+ // Match for paths in rules.
77
+ foreach ( $rules['paths'] as $path => $props ) {
78
 
79
+ // Prepare rule target and log.
80
+ $target_rule = 'paths|' . $path;
81
+ $this->criticalcss->log( 'Qualifying path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );
82
 
83
+ // Path match
84
+ // -> exact match needed for AUTO rules
85
+ // -> partial match OK for MANUAL rules (which have empty hash and a file with CCSS).
86
+ if ( $path === $req_path || ( false == $props['hash'] && false != $props['file'] && preg_match( '|' . $path . '|', $req_path ) ) ) {
87
 
88
+ // There's a path match in the rule, so job QUALIFIES with a path rule match.
89
+ $job_qualify = true;
90
+ $rule_properties = $props;
91
+ $this->criticalcss->log( 'Path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );
92
 
93
+ // Stop processing other path rules.
94
+ break;
 
95
  }
96
+ }
97
 
98
+ // Match for types in rules if no path rule matches and if we're not enforcing paths.
99
+ if ( '' !== $hash && ! $job_qualify && ( ! $forcepath || ! in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) || ! apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) ) ) {
100
+ foreach ( $rules['types'] as $type => $props ) {
101
 
102
+ // Prepare rule target and log.
103
+ $target_rule = 'types|' . $type;
104
+ $this->criticalcss->log( 'Qualifying page type <' . $req_type . '> on path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );
105
 
106
+ if ( $req_type == $type ) {
107
+ // Type match.
108
+ // There's a type match in the rule, so job QUALIFIES with a type rule match.
109
+ $job_qualify = true;
110
+ $rule_properties = $props;
111
+ $this->criticalcss->log( 'Page type <' . $req_type . '> on path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );
112
 
113
+ // Stop processing other type rules.
114
+ break;
 
115
  }
116
  }
117
+ }
118
 
119
+ if ( $job_qualify && ( ( false == $rule_properties['hash'] && false != $rule_properties['file'] ) || strpos( $req_type, 'template_' ) !== false ) ) {
120
+ // If job qualifies but rule hash is false and file isn't false (MANUAL rule) or if template, job does not qualify despite what previous evaluations says.
121
+ $job_qualify = false;
122
+ $this->criticalcss->log( 'Job submission DISQUALIFIED by MANUAL rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
123
+ } elseif ( ! $job_qualify && empty( $rule_properties ) ) {
124
+ // But if job does not qualify and rule properties are set, job qualifies as there is no matching rule for it yet
125
+ // Fill-in the new target rule.
126
+ $job_qualify = true;
127
+
128
+ // Should we switch to path-base AUTO-rules? Conditions:
129
+ // 1. forcepath option has to be enabled (off by default)
130
+ // 2. request type should be (by default, but filterable) one of is_page (removed for now: woo_is_product or woo_is_product_category).
131
+ if ( ( $forcepath && in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) ) || apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) || empty( $hash )) {
132
+ if ( '/' !== $req_path ) {
133
+ $target_rule = 'paths|' . $req_path;
 
 
 
 
134
  } else {
135
+ // Exception; we don't want a path-based rule for "/" as that messes things up, hard-switch this to a type-based is_front_page rule.
136
+ $target_rule = 'types|' . 'is_front_page';
137
  }
 
138
  } else {
139
+ $target_rule = 'types|' . $req_type;
 
140
  }
141
+ $this->criticalcss->log( 'Job submission QUALIFIED by MISSING rule for page type <' . $req_type . '> on path <' . $req_path . '>, new rule target is <' . $target_rule . '>', 3 );
142
+ } else {
143
+ // Or just log a job qualified by a matching rule.
144
+ $this->criticalcss->log( 'Job submission QUALIFIED by AUTO rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
145
+ }
146
 
147
+ // Submit job.
148
+ if ( $job_qualify ) {
149
+ if ( ! array_key_exists( $req_path, $queue ) ) {
150
+ // This is a NEW job
151
+ // Merge job into the queue.
152
+ $queue[ $req_path ] = $this->ao_ccss_define_job(
153
+ $req_path,
154
+ $target_rule,
155
+ $req_type,
156
+ $hash,
157
+ null,
158
+ null,
159
+ null,
160
+ null,
161
+ true
162
+ );
163
+ // Set update flag.
164
+ $queue_update = true;
165
+ } else {
166
+ // This is an existing job
167
+ // The job is still NEW, most likely this is extra CSS file for the same page that needs a hash.
168
+ if ( 'NEW' == $queue[ $req_path ]['jqstat'] ) {
169
+ // Add hash if it's not already in the job.
170
+ if ( ! in_array( $hash, $queue[ $req_path ]['hashes'] ) ) {
171
+ // Push new hash to its array and update flag.
172
+ $queue_update = array_push( $queue[ $req_path ]['hashes'], $hash );
173
+
174
+ // Log job update.
175
+ $this->criticalcss->log( 'Hashes UPDATED on local job id <' . $queue[ $req_path ]['ljid'] . '>, job status NEW, target rule <' . $queue[ $req_path ]['rtarget'] . '>, hash added: ' . $hash, 3 );
176
+
177
+ // Return from here as the hash array is already updated.
178
+ return true;
179
+ }
180
+ } elseif ( 'NEW' != $queue[ $req_path ]['jqstat'] && 'JOB_QUEUED' != $queue[ $req_path ]['jqstat'] && 'JOB_ONGOING' != $queue[ $req_path ]['jqstat'] ) {
181
+ // Allow requeuing jobs that are not NEW, JOB_QUEUED or JOB_ONGOING
182
+ // Merge new job keeping some previous job values.
183
+ $queue[ $req_path ] = $this->ao_ccss_define_job(
184
  $req_path,
185
  $target_rule,
186
  $req_type,
187
  $hash,
188
+ $queue[ $req_path ]['file'],
189
+ $queue[ $req_path ]['jid'],
190
+ $queue[ $req_path ]['jrstat'],
191
+ $queue[ $req_path ]['jvstat'],
192
+ false
193
  );
194
  // Set update flag.
195
  $queue_update = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  }
197
+ }
198
 
199
+ if ( $queue_update ) {
200
+ // Persist the job to the queue and return.
201
+ $queue_raw = json_encode( $queue );
202
+ update_option( 'autoptimize_ccss_queue', $queue_raw, false );
203
+ $this->criticalcss->flush_options();
204
+ return true;
205
+ } else {
206
+ // Or just return false if no job was added.
207
+ $this->criticalcss->log( 'A job for path <' . $req_path . '> already exist with NEW or PENDING status, skipping job creation', 3 );
208
+ return false;
209
  }
210
  }
211
  }
213
  public function ao_ccss_get_type() {
214
  // Get the type of a page
215
  // Attach the conditional tags array.
216
+ $types = $this->criticalcss->get_types();
217
+ $forcepath = $this->criticalcss->get_option( 'forcepath' );
218
 
219
  // By default, a page type is false.
220
  $page_type = false;
221
 
222
  // Iterates over the array to match a type.
223
+ foreach ( $types as $type ) {
224
  if ( is_404() ) {
225
  $page_type = 'is_404';
226
  break;
228
  // identify frontpage immediately to avoid it also matching a CPT or template.
229
  $page_type = 'is_front_page';
230
  break;
231
+ } elseif ( strpos( $type, 'custom_post_' ) !== false && ( ! $forcepath || ! is_page() ) ) {
232
  // Match custom post types and not page or page not forced to path-based.
233
  if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
234
  $page_type = $type;
235
  break;
236
  }
237
+ } elseif ( strpos( $type, 'template_' ) !== false && ( ! $forcepath || ! is_page() ) ) {
238
  // Match templates if not page or if page is not forced to path-based.
239
  if ( is_page_template( substr( $type, 9 ) ) ) {
240
  $page_type = $type;
283
  }
284
 
285
  // Log job creation.
286
+ $this->criticalcss->log( 'Job ' . $operation . ' with local job id <' . $path['ljid'] . '> for target rule <' . $target . '>', 3 );
287
 
288
  return $path;
289
  }
classes/autoptimizeCriticalCSSSettings.php CHANGED
@@ -15,8 +15,8 @@ class autoptimizeCriticalCSSSettings {
15
  */
16
  private $settings_screen_do_remote_http = true;
17
 
18
- public function __construct()
19
- {
20
  $this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );
21
  $this->run();
22
  }
@@ -100,12 +100,20 @@ class autoptimizeCriticalCSSSettings {
100
  require_once( 'critcss-inc/admin_settings_adv.php' );
101
  require_once( 'critcss-inc/admin_settings_explain.php' );
102
 
103
- // fetch all options at once and populate them individually explicitely as globals.
104
- $all_options = autoptimizeCriticalCSSBase::fetch_options();
105
- foreach ( $all_options as $_option => $_value ) {
106
- global ${$_option};
107
- ${$_option} = $_value;
108
- }
 
 
 
 
 
 
 
 
109
  ?>
110
  <script>document.title = "Autoptimize: <?php _e( 'Critical CSS', 'autoptimize' ); ?> " + document.title;</script>
111
  <div class="wrap">
@@ -118,26 +126,25 @@ class autoptimizeCriticalCSSSettings {
118
  // Print AO settings tabs.
119
  echo autoptimizeConfig::ao_admin_tabs();
120
 
121
- // Make sure dir to write ao_ccss exists and is writable.
122
- if ( ! is_dir( AO_CCSS_DIR ) ) {
123
- $mkdirresp = @mkdir( AO_CCSS_DIR, 0775, true ); // @codingStandardsIgnoreLine
124
- } else {
125
- $mkdirresp = true;
126
- }
127
-
128
- // Make sure our index.html is there.
129
- if ( ! is_file( AO_CCSS_DIR . 'index.html' ) ) {
130
- $fileresp = file_put_contents( AO_CCSS_DIR . 'index.html', '<html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>' );
131
- } else {
132
- $fileresp = true;
133
- }
134
 
135
  // Warn if we could not create those files.
136
- if ( ( ! $mkdirresp ) || ( ! $fileresp ) ) {
137
  ?>
138
  <div class="notice-error notice"><p>
139
  <?php
140
- _e( 'Could not create the required directory. Make sure the webserver can write to the wp-content directory.', 'autoptimize' );
 
 
 
 
 
 
 
 
 
 
 
141
  ?>
142
  </p></div>
143
  <?php
@@ -283,7 +290,7 @@ class autoptimizeCriticalCSSSettings {
283
  settings_fields( 'ao_ccss_options_group' );
284
 
285
  // Get API key status.
286
- $key = autoptimizeCriticalCSSCore::ao_ccss_key_status( true );
287
 
288
  if ( $this->is_multisite_network_admin() ) {
289
  ?>
@@ -306,25 +313,31 @@ class autoptimizeCriticalCSSSettings {
306
  // Render advanced panel.
307
  ao_ccss_render_adv();
308
  } else {
 
 
 
 
 
 
 
309
  // But if key is other than valid, add hidden fields to persist settings when submitting form
310
  // Show explanation of why and how to get a API key.
311
  ao_ccss_render_explain();
312
 
313
  // Get viewport size.
314
- $viewport = autoptimizeCriticalCSSCore::ao_ccss_viewport();
315
 
316
  // Add hidden fields.
317
- echo "<input class='hidden' name='autoptimize_ccss_rules' value='" . $ao_ccss_rules_raw . "'>";
318
- echo "<input class='hidden' name='autoptimize_ccss_queue' value='" . $ao_ccss_queue_raw . "'>";
319
- echo '<input class="hidden" name="autoptimize_ccss_viewport[w]" value="' . $viewport['w'] . '">';
320
- echo '<input class="hidden" name="autoptimize_ccss_viewport[h]" value="' . $viewport['h'] . '">';
321
  echo '<input class="hidden" name="autoptimize_ccss_finclude" value="' . esc_attr( $ao_ccss_finclude ) . '">';
322
- echo '<input class="hidden" name="autoptimize_ccss_rtimelimit" value="' . $ao_ccss_rtimelimit . '">';
323
- echo '<input class="hidden" name="autoptimize_ccss_debug" value="' . $ao_ccss_debug . '">';
324
- echo '<input class="hidden" name="autoptimize_ccss_noptimize" value="' . $ao_ccss_noptimize . '">';
325
  echo '<input class="hidden" name="autoptimize_css_defer_inline" value="' . esc_attr( $ao_css_defer_inline ) . '">';
326
- echo '<input class="hidden" name="autoptimize_ccss_loggedin" value="' . $ao_ccss_loggedin . '">';
327
- echo '<input class="hidden" name="autoptimize_ccss_forcepath" value="' . $ao_ccss_forcepath . '">';
328
  }
329
  // Render key panel unconditionally.
330
  ao_ccss_render_key( $ao_ccss_key, $key['status'], $key['stmsg'], $key['msg'], $key['color'] );
@@ -377,11 +390,11 @@ class autoptimizeCriticalCSSSettings {
377
  }
378
  }
379
 
380
- public static function ao_ccss_has_autorules() {
381
  static $_has_auto_rules = null;
382
 
383
  if ( null === $_has_auto_rules ) {
384
- global $ao_ccss_rules;
385
  $_has_auto_rules = false;
386
  if ( ! empty( $ao_ccss_rules ) ) {
387
  foreach ( array( 'types', 'paths' ) as $_typat ) {
15
  */
16
  private $settings_screen_do_remote_http = true;
17
 
18
+ public function __construct() {
19
+ $this->criticalcss = autoptimize()->criticalcss();
20
  $this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );
21
  $this->run();
22
  }
100
  require_once( 'critcss-inc/admin_settings_adv.php' );
101
  require_once( 'critcss-inc/admin_settings_explain.php' );
102
 
103
+ $ao_ccss_key = $this->criticalcss->get_option( 'key' );
104
+ $ao_ccss_keyst = $this->criticalcss->get_option( 'keyst' );
105
+ $ao_css_defer = $this->criticalcss->get_option( 'css_defer' );
106
+ $ao_ccss_deferjquery = $this->criticalcss->get_option( 'deferjquery' );
107
+ $ao_ccss_queue = $this->criticalcss->get_option( 'queue' );
108
+ $ao_ccss_rules = $this->criticalcss->get_option( 'rules' );
109
+ $ao_ccss_servicestatus = $this->criticalcss->get_option( 'servicestatus' );
110
+ $ao_ccss_finclude = $this->criticalcss->get_option( 'finclude' );
111
+ $ao_ccss_rtimelimit = $this->criticalcss->get_option( 'rtimelimit' );
112
+ $ao_ccss_debug = $this->criticalcss->get_option( 'debug' );
113
+ $ao_ccss_noptimize = $this->criticalcss->get_option( 'noptimize' );
114
+ $ao_css_defer_inline = $this->criticalcss->get_option( 'css_defer_inline' );
115
+ $ao_ccss_loggedin = $this->criticalcss->get_option( 'loggedin' );
116
+ $ao_ccss_forcepath = $this->criticalcss->get_option( 'forcepath' );
117
  ?>
118
  <script>document.title = "Autoptimize: <?php _e( 'Critical CSS', 'autoptimize' ); ?> " + document.title;</script>
119
  <div class="wrap">
126
  // Print AO settings tabs.
127
  echo autoptimizeConfig::ao_admin_tabs();
128
 
129
+ $mkdirresult = $this->criticalcss->create_ao_ccss_dir();
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
  // Warn if we could not create those files.
132
+ if ( ( true !== $mkdirresult ) ) {
133
  ?>
134
  <div class="notice-error notice"><p>
135
  <?php
136
+ _e( 'Could not create the required directory. Make sure the webserver can write to the wp-content/uploads directory.', 'autoptimize' );
137
+ ?>
138
+ </p></div>
139
+ <?php
140
+ }
141
+
142
+ // Check if CSS optimization is on.
143
+ if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) || 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' ) ) {
144
+ ?>
145
+ <div class="notice-info notice"><p>
146
+ <?php
147
+ _e( 'To be able to use Critical CSS you will have to enable CSS optimization and make sure "eliminate render-blocking CSS" is active on the main Autoptimize settings page.', 'autoptimize' );
148
  ?>
149
  </p></div>
150
  <?php
290
  settings_fields( 'ao_ccss_options_group' );
291
 
292
  // Get API key status.
293
+ $key = $this->criticalcss->key_status( true );
294
 
295
  if ( $this->is_multisite_network_admin() ) {
296
  ?>
313
  // Render advanced panel.
314
  ao_ccss_render_adv();
315
  } else {
316
+ if ( apply_filters( 'autoptimize_filter_ccss_rules_without_api', true ) ) {
317
+ // Render rules section for manual rules.
318
+ ao_ccss_render_rules();
319
+ } else {
320
+ echo "<input class='hidden' name='autoptimize_ccss_queue' value='" . json_encode( $ao_ccss_rules, JSON_FORCE_OBJECT ) . "'>";
321
+ }
322
+
323
  // But if key is other than valid, add hidden fields to persist settings when submitting form
324
  // Show explanation of why and how to get a API key.
325
  ao_ccss_render_explain();
326
 
327
  // Get viewport size.
328
+ $viewport = $this->criticalcss->viewport();
329
 
330
  // Add hidden fields.
331
+ echo "<input class='hidden' name='autoptimize_ccss_queue' value='" . json_encode( $ao_ccss_queue, JSON_FORCE_OBJECT ) . "'>";
332
+ echo '<input class="hidden" name="autoptimize_ccss_viewport[w]" value="' . esc_attr( $viewport['w'] ) . '">';
333
+ echo '<input class="hidden" name="autoptimize_ccss_viewport[h]" value="' . esc_attr( $viewport['h'] ) . '">';
 
334
  echo '<input class="hidden" name="autoptimize_ccss_finclude" value="' . esc_attr( $ao_ccss_finclude ) . '">';
335
+ echo '<input class="hidden" name="autoptimize_ccss_rtimelimit" value="' . esc_attr( $ao_ccss_rtimelimit ) . '">';
336
+ echo '<input class="hidden" name="autoptimize_ccss_debug" value="' . esc_attr( $ao_ccss_debug ) . '">';
337
+ echo '<input class="hidden" name="autoptimize_ccss_noptimize" value="' . esc_attr( $ao_ccss_noptimize ) . '">';
338
  echo '<input class="hidden" name="autoptimize_css_defer_inline" value="' . esc_attr( $ao_css_defer_inline ) . '">';
339
+ echo '<input class="hidden" name="autoptimize_ccss_loggedin" value="' . esc_attr( $ao_ccss_loggedin ). '">';
340
+ echo '<input class="hidden" name="autoptimize_ccss_forcepath" value="' . esc_attr( $ao_ccss_forcepath ) . '">';
341
  }
342
  // Render key panel unconditionally.
343
  ao_ccss_render_key( $ao_ccss_key, $key['status'], $key['stmsg'], $key['msg'], $key['color'] );
390
  }
391
  }
392
 
393
+ public function ao_ccss_has_autorules() {
394
  static $_has_auto_rules = null;
395
 
396
  if ( null === $_has_auto_rules ) {
397
+ $ao_ccss_rules = $this->criticalcss->get_option( 'rules' );
398
  $_has_auto_rules = false;
399
  if ( ! empty( $ao_ccss_rules ) ) {
400
  foreach ( array( 'types', 'paths' ) as $_typat ) {
classes/autoptimizeCriticalCSSSettingsAjax.php CHANGED
@@ -8,14 +8,8 @@ if ( ! defined( 'ABSPATH' ) ) {
8
  }
9
 
10
  class autoptimizeCriticalCSSSettingsAjax {
11
- public function __construct()
12
- {
13
- // fetch all options at once and populate them individually explicitely as globals.
14
- $all_options = autoptimizeCriticalCSSBase::fetch_options();
15
- foreach ( $all_options as $_option => $_value ) {
16
- global ${$_option};
17
- ${$_option} = $_value;
18
- }
19
  $this->run();
20
  }
21
 
@@ -88,7 +82,7 @@ class autoptimizeCriticalCSSSettingsAjax {
88
  $critcsscontents = stripslashes( $_POST['critcsscontents'] );
89
 
90
  // If there is content and it's valid, write the file.
91
- if ( $critcsscontents && autoptimizeCriticalCSSCore::ao_ccss_check_contents( $critcsscontents ) ) {
92
  // Set file path and status.
93
  $critcssfile = AO_CCSS_DIR . strip_tags( $_POST['critcssfile'] );
94
  $status = file_put_contents( $critcssfile, $critcsscontents, LOCK_EX );
@@ -215,15 +209,64 @@ class autoptimizeCriticalCSSSettingsAjax {
215
  }
216
 
217
  // Init array, get options and prepare the raw object.
218
- $settings = array();
219
- $settings['rules'] = get_option( 'autoptimize_ccss_rules' );
220
- $settings['additional'] = get_option( 'autoptimize_ccss_additional' );
221
- $settings['viewport'] = get_option( 'autoptimize_ccss_viewport' );
222
- $settings['finclude'] = get_option( 'autoptimize_ccss_finclude' );
223
- $settings['rtimelimit'] = get_option( 'autoptimize_ccss_rtimelimit' );
224
- $settings['noptimize'] = get_option( 'autoptimize_ccss_noptimize' );
225
- $settings['debug'] = get_option( 'autoptimize_ccss_debug' );
226
- $settings['key'] = get_option( 'autoptimize_ccss_key' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  // Initialize error flag.
229
  $error = true;
@@ -238,7 +281,7 @@ class autoptimizeCriticalCSSSettingsAjax {
238
  }
239
 
240
  // Prepare archive.
241
- $zipfile = AO_CCSS_DIR . date( 'Ymd-H\hi' ) . '_ao_ccss_settings.zip';
242
  $file = pathinfo( $zipfile, PATHINFO_BASENAME );
243
  $zip = new ZipArchive();
244
  $ret = $zip->open( $zipfile, ZipArchive::CREATE );
@@ -287,7 +330,7 @@ class autoptimizeCriticalCSSSettingsAjax {
287
  // create tmp dir with hard guess name in AO_CCSS_DIR.
288
  $_secret_dir = wp_hash( uniqid( md5( AUTOPTIMIZE_CACHE_URL ), true ) );
289
  $_import_tmp_dir = trailingslashit( AO_CCSS_DIR . $_secret_dir );
290
- mkdir( $_import_tmp_dir );
291
 
292
  // Save file to that tmp directory but give it our own name to prevent directory traversal risks when using original name.
293
  $zipfile = $_import_tmp_dir . uniqid( 'import_settings-', true ) . '.zip';
@@ -299,7 +342,7 @@ class autoptimizeCriticalCSSSettingsAjax {
299
  // loop through all files in the zipfile.
300
  for ($i = 0; $i < $zip->numFiles; $i++) {
301
  // but only extract known good files.
302
- if ( preg_match('/^settings\.json$|^ccss_[a-z0-9]{32}\.css$/', $zip->getNameIndex( $i ) ) > 0 ) {
303
  $zip->extractTo( AO_CCSS_DIR, $zip->getNameIndex( $i ) );
304
  }
305
  }
@@ -307,7 +350,7 @@ class autoptimizeCriticalCSSSettingsAjax {
307
  } else {
308
  $error = 'could not extract';
309
  }
310
-
311
  // and remove temp. dir with all contents (the import-zipfile).
312
  $this->rrmdir( $_import_tmp_dir );
313
 
@@ -320,15 +363,52 @@ class autoptimizeCriticalCSSSettingsAjax {
320
  // Get settings and turn them into an object.
321
  $settings = json_decode( file_get_contents( $importfile ), true );
322
 
323
- // Update options.
324
- update_option( 'autoptimize_ccss_rules', $settings['rules'] );
325
- update_option( 'autoptimize_ccss_additional', $settings['additional'] );
326
- update_option( 'autoptimize_ccss_viewport', $settings['viewport'] );
327
- update_option( 'autoptimize_ccss_finclude', $settings['finclude'] );
328
- update_option( 'autoptimize_ccss_rtimelimit', $settings['rtimelimit'] );
329
- update_option( 'autoptimize_ccss_noptimize', $settings['noptimize'] );
330
- update_option( 'autoptimize_ccss_debug', $settings['debug'] );
331
- update_option( 'autoptimize_ccss_key', $settings['key'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  } else {
333
  // Settings file doesn't exist, update error flag.
334
  $error = 'settings file does not exist';
@@ -353,7 +433,7 @@ class autoptimizeCriticalCSSSettingsAjax {
353
  // Close ajax request.
354
  wp_die();
355
  }
356
-
357
  public function ao_ccss_queuerunner_callback() {
358
  check_ajax_referer( 'ao_ccss_queuerunner_nonce', 'ao_ccss_queuerunner_nonce' );
359
 
@@ -370,7 +450,7 @@ class autoptimizeCriticalCSSSettingsAjax {
370
  }
371
  } else {
372
  $response['code'] = '500';
373
- $response['msg'] = 'Not allowed';
374
  }
375
 
376
  // Dispatch respose.
@@ -443,7 +523,7 @@ class autoptimizeCriticalCSSSettingsAjax {
443
  return true;
444
  }
445
  }
446
-
447
  public function rrmdir( $path ) {
448
  // recursively remove a directory as found on
449
  // https://andy-carter.com/blog/recursively-remove-a-directory-in-php.
8
  }
9
 
10
  class autoptimizeCriticalCSSSettingsAjax {
11
+ public function __construct() {
12
+ $this->criticalcss = autoptimize()->criticalcss();
 
 
 
 
 
 
13
  $this->run();
14
  }
15
 
82
  $critcsscontents = stripslashes( $_POST['critcsscontents'] );
83
 
84
  // If there is content and it's valid, write the file.
85
+ if ( $critcsscontents && $this->criticalcss->check_contents( $critcsscontents ) ) {
86
  // Set file path and status.
87
  $critcssfile = AO_CCSS_DIR . strip_tags( $_POST['critcssfile'] );
88
  $status = file_put_contents( $critcssfile, $critcsscontents, LOCK_EX );
209
  }
210
 
211
  // Init array, get options and prepare the raw object.
212
+ $settings = array();
213
+
214
+ // CCSS settings.
215
+ $settings['ccss']['rules'] = get_option( 'autoptimize_ccss_rules' );
216
+ $settings['ccss']['additional'] = get_option( 'autoptimize_ccss_additional' );
217
+ $settings['ccss']['viewport'] = get_option( 'autoptimize_ccss_viewport' );
218
+ $settings['ccss']['finclude'] = get_option( 'autoptimize_ccss_finclude' );
219
+ $settings['ccss']['rtimelimit'] = get_option( 'autoptimize_ccss_rtimelimit' );
220
+ $settings['ccss']['noptimize'] = get_option( 'autoptimize_ccss_noptimize' );
221
+ $settings['ccss']['debug'] = get_option( 'autoptimize_ccss_debug' );
222
+ $settings['ccss']['key'] = get_option( 'autoptimize_ccss_key' );
223
+ $settings['ccss']['deferjquery'] = get_option( 'autoptimize_ccss_deferjquery' );
224
+ $settings['ccss']['domain'] = get_option( 'autoptimize_ccss_domain' );
225
+ $settings['ccss']['forcepath'] = get_option( 'autoptimize_ccss_forcepath' );
226
+ $settings['ccss']['loggedin'] = get_option( 'autoptimize_ccss_loggedin' );
227
+ $settings['ccss']['rlimit'] = get_option( 'autoptimize_ccss_rlimit' );
228
+ $settings['ccss']['unloadccss'] = get_option( 'autoptimize_ccss_unloadccss' );
229
+
230
+ // JS settings.
231
+ $settings['js']['root'] = get_option( 'autoptimize_js' );
232
+ $settings['js']['aggregate'] = get_option( 'autoptimize_js_aggregate' );
233
+ $settings['js']['defer_not_aggregate'] = get_option( 'autoptimize_js_defer_not_aggregate' );
234
+ $settings['js']['defer_inline'] = get_option( 'autoptimize_js_defer_inline' );
235
+ $settings['js']['exclude'] = get_option( 'autoptimize_js_exclude' );
236
+ $settings['js']['forcehead'] = get_option( 'autoptimize_js_forcehead' );
237
+ $settings['js']['justhead'] = get_option( 'autoptimize_js_justhead' );
238
+ $settings['js']['trycatch'] = get_option( 'autoptimize_js_trycatch' );
239
+ $settings['js']['include_inline'] = get_option( 'autoptimize_js_include_inline');
240
+
241
+ // CSS settings.
242
+ $settings['css']['root'] = get_option( 'autoptimize_css' );
243
+ $settings['css']['aggregate'] = get_option( 'autoptimize_css_aggregate' );
244
+ $settings['css']['datauris'] = get_option( 'autoptimize_css_datauris' );
245
+ $settings['css']['justhead'] = get_option( 'autoptimize_css_justhead' );
246
+ $settings['css']['defer'] = get_option( 'autoptimize_css_defer' );
247
+ $settings['css']['defer_inline'] = get_option( 'autoptimize_css_defer_inline' );
248
+ $settings['css']['inline'] = get_option( 'autoptimize_css_inline' );
249
+ $settings['css']['exclude'] = get_option( 'autoptimize_css_exclude' );
250
+ $settings['css']['include_inline'] = get_option( 'autoptimize_css_include_inline' );
251
+
252
+ // Others.
253
+ $settings['other']['autoptimize_imgopt_settings'] = get_option( 'autoptimize_imgopt_settings' );
254
+ $settings['other']['autoptimize_extra_settings'] = get_option( 'autoptimize_extra_settings' );
255
+ $settings['other']['autoptimize_cache_fallback'] = get_option( 'autoptimize_cache_fallback' );
256
+ $settings['other']['autoptimize_cache_nogzip'] = get_option( 'autoptimize_cache_nogzip' );
257
+ $settings['other']['autoptimize_cdn_url'] = get_option( 'autoptimize_cdn_url' );
258
+ $settings['other']['autoptimize_enable_meta_ao_settings'] = get_option( 'autoptimize_enable_meta_ao_settings' );
259
+ $settings['other']['autoptimize_enable_site_config'] = get_option( 'autoptimize_enable_site_config' );
260
+ $settings['other']['autoptimize_html'] = get_option( 'autoptimize_html' );
261
+ $settings['other']['autoptimize_html_keepcomments'] = get_option( 'autoptimize_html_keepcomments' );
262
+ $settings['other']['autoptimize_minify_excluded'] = get_option( 'autoptimize_minify_excluded' );
263
+ $settings['other']['autoptimize_optimize_checkout'] = get_option( 'autoptimize_optimize_checkout' );
264
+ $settings['other']['autoptimize_optimize_logged'] = get_option( 'autoptimize_optimize_logged' );
265
+
266
+ if ( defined( 'AO_PRO_VERSION' ) ) {
267
+ $settings['pro']['boosters'] = get_option( 'autoptimize_pro_boosters' );
268
+ $settings['pro']['pagecache'] = get_option( 'autoptimize_pro_pagecache' );
269
+ }
270
 
271
  // Initialize error flag.
272
  $error = true;
281
  }
282
 
283
  // Prepare archive.
284
+ $zipfile = AO_CCSS_DIR . str_replace( array('.', '/'), '_', parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ) . '_' . date( 'Ymd-H\hi' ) . '_ao_ccss_settings.zip';
285
  $file = pathinfo( $zipfile, PATHINFO_BASENAME );
286
  $zip = new ZipArchive();
287
  $ret = $zip->open( $zipfile, ZipArchive::CREATE );
330
  // create tmp dir with hard guess name in AO_CCSS_DIR.
331
  $_secret_dir = wp_hash( uniqid( md5( AUTOPTIMIZE_CACHE_URL ), true ) );
332
  $_import_tmp_dir = trailingslashit( AO_CCSS_DIR . $_secret_dir );
333
+ mkdir( $_import_tmp_dir, 0774, true );
334
 
335
  // Save file to that tmp directory but give it our own name to prevent directory traversal risks when using original name.
336
  $zipfile = $_import_tmp_dir . uniqid( 'import_settings-', true ) . '.zip';
342
  // loop through all files in the zipfile.
343
  for ($i = 0; $i < $zip->numFiles; $i++) {
344
  // but only extract known good files.
345
+ if ( preg_match('/^settings\.json$|^\.\/ccss_[a-z0-9]{32}\.css$/', $zip->getNameIndex( $i ) ) > 0 ) {
346
  $zip->extractTo( AO_CCSS_DIR, $zip->getNameIndex( $i ) );
347
  }
348
  }
350
  } else {
351
  $error = 'could not extract';
352
  }
353
+
354
  // and remove temp. dir with all contents (the import-zipfile).
355
  $this->rrmdir( $_import_tmp_dir );
356
 
363
  // Get settings and turn them into an object.
364
  $settings = json_decode( file_get_contents( $importfile ), true );
365
 
366
+ // Update options from settings, but only for known options.
367
+ // CCSS.
368
+ foreach ( array( 'rules', 'additional', 'viewport', 'finclude', 'rtimelimit', 'noptimize', 'debug', 'key', 'deferjquery', 'domain', 'forcepath', 'loggedin', 'rlimit', 'unloadccss' ) as $ccss_setting ) {
369
+ if ( false === array_key_exists( 'ccss', $settings ) || false === array_key_exists( $ccss_setting, $settings['ccss'] ) ) {
370
+ continue;
371
+ } else {
372
+ update_option( 'autoptimize_ccss_' . $ccss_setting, $settings['ccss'][$ccss_setting] );
373
+ }
374
+ }
375
+
376
+ // JS.
377
+ foreach ( array( 'root', 'aggregate', 'defer_not_aggregate', 'defer_inline', 'exclude', 'forcehead', 'trycatch', 'include_inline' ) as $js_setting ) {
378
+ if ( false === array_key_exists( 'js', $settings ) || false === array_key_exists( $js_setting, $settings['js'] ) ) {
379
+ continue;
380
+ } else if ( 'root' === $js_setting ) {
381
+ update_option( 'autoptimize_js', $settings['js']['root'] );
382
+ } else {
383
+ update_option( 'autoptimize_js_' . $js_setting, $settings['js'][$js_setting] );
384
+ }
385
+ }
386
+
387
+ // CSS.
388
+ foreach ( array( 'root', 'aggregate', 'datauris', 'justhead', 'defer', 'defer_inline', 'inline', 'exclude', 'include_inline' ) as $css_setting ) {
389
+ if ( false === array_key_exists( 'css', $settings ) || false === array_key_exists( $css_setting, $settings['css'] ) ) {
390
+ continue;
391
+ } else if ( 'root' === $css_setting ) {
392
+ update_option( 'autoptimize_css', $settings['css']['root'] );
393
+ } else {
394
+ update_option( 'autoptimize_css_' . $css_setting, $settings['css'][$css_setting] );
395
+ }
396
+ }
397
+
398
+ // Other.
399
+ foreach ( array( 'autoptimize_imgopt_settings', 'autoptimize_extra_settings', 'autoptimize_cache_fallback', 'autoptimize_cache_nogzip', 'autoptimize_cdn_url', 'autoptimize_enable_meta_ao_settings', 'autoptimize_enable_site_config', 'autoptimize_html', 'autoptimize_html_keepcomments', 'autoptimize_minify_excluded', 'autoptimize_optimize_checkout', 'autoptimize_optimize_logged' ) as $other_setting ) {
400
+ if ( false === array_key_exists( 'other', $settings ) || false === array_key_exists( $other_setting, $settings['other'] ) ) {
401
+ continue;
402
+ } else {
403
+ update_option( $other_setting, $settings['other'][$other_setting] );
404
+ }
405
+ }
406
+
407
+ // AO Pro.
408
+ if ( defined( 'AO_PRO_VERSION' ) && array_key_exists( 'pro', $settings ) ) {
409
+ update_option( 'autoptimize_pro_boosters', $settings['pro']['boosters'] );
410
+ update_option( 'autoptimize_pro_pagecache', $settings['pro']['pagecache'] );
411
+ }
412
  } else {
413
  // Settings file doesn't exist, update error flag.
414
  $error = 'settings file does not exist';
433
  // Close ajax request.
434
  wp_die();
435
  }
436
+
437
  public function ao_ccss_queuerunner_callback() {
438
  check_ajax_referer( 'ao_ccss_queuerunner_nonce', 'ao_ccss_queuerunner_nonce' );
439
 
450
  }
451
  } else {
452
  $response['code'] = '500';
453
+ $response['msg'] = 'Not allowed';
454
  }
455
 
456
  // Dispatch respose.
523
  return true;
524
  }
525
  }
526
+
527
  public function rrmdir( $path ) {
528
  // recursively remove a directory as found on
529
  // https://andy-carter.com/blog/recursively-remove-a-directory-in-php.
classes/autoptimizeExtra.php CHANGED
@@ -117,6 +117,8 @@ class autoptimizeExtra
117
  {
118
  if ( strpos( $src, '?ver=' ) ) {
119
  $src = remove_query_arg( 'ver', $src );
 
 
120
  }
121
 
122
  return $src;
@@ -184,9 +186,14 @@ class autoptimizeExtra
184
  }
185
 
186
  // Preload!
187
- if ( ! empty( $options['autoptimize_extra_text_field_7'] ) || has_filter( 'autoptimize_filter_extra_tobepreloaded' ) ) {
188
  add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_preload' ), 10, 2 );
189
  }
 
 
 
 
 
190
  }
191
 
192
  public function filter_remove_emoji_dns_prefetch( $urls, $relation_type )
@@ -402,6 +409,15 @@ class autoptimizeExtra
402
  if ( array_key_exists( 'autoptimize_extra_text_field_7', $options ) ) {
403
  $preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( $options['autoptimize_extra_text_field_7'] ) ) ) );
404
  }
 
 
 
 
 
 
 
 
 
405
  $preloads = apply_filters( 'autoptimize_filter_extra_tobepreloaded', $preloads );
406
 
407
  // immediately return if nothing to be preloaded.
@@ -412,6 +428,9 @@ class autoptimizeExtra
412
  // iterate through array and add preload link to tmp string.
413
  $preload_output = '';
414
  foreach ( $preloads as $preload ) {
 
 
 
415
  $preload = esc_url_raw( $preload );
416
  $crossorigin = '';
417
  $preload_as = '';
@@ -439,11 +458,29 @@ class autoptimizeExtra
439
  }
440
  $preload_output = apply_filters( 'autoptimize_filter_extra_preload_output', $preload_output );
441
 
 
 
 
 
442
  // add string to head (before first link node by default).
443
  $preload_inject = apply_filters( 'autoptimize_filter_extra_preload_inject', '<link' );
444
- $position = autoptimizeUtils::strpos( $in, $preload_inject );
445
 
446
- return autoptimizeUtils::substr_replace( $in, $preload_output . $preload_inject, $position, strlen( $preload_inject ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  }
448
 
449
  public function admin_menu()
@@ -524,6 +561,12 @@ class autoptimizeExtra
524
  <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>
525
  </td>
526
  </tr>
 
 
 
 
 
 
527
  <tr>
528
  <th scope="row"><?php _e( 'Preconnect to 3rd party domains <em>(advanced users)</em>', 'autoptimize' ); ?></th>
529
  <td>
117
  {
118
  if ( strpos( $src, '?ver=' ) ) {
119
  $src = remove_query_arg( 'ver', $src );
120
+ } else if ( strpos( $src, '?v=' ) ) {
121
+ $src = remove_query_arg( 'v', $src );
122
  }
123
 
124
  return $src;
186
  }
187
 
188
  // Preload!
189
+ if ( ! empty( $options['autoptimize_extra_text_field_7'] ) || has_filter( 'autoptimize_filter_extra_tobepreloaded' ) || ! empty( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) {
190
  add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_preload' ), 10, 2 );
191
  }
192
+
193
+ // Remove global styles.
194
+ if ( ! empty( $options['autoptimize_extra_checkbox_field_8'] ) ) {
195
+ $this->disable_global_styles();
196
+ }
197
  }
198
 
199
  public function filter_remove_emoji_dns_prefetch( $urls, $relation_type )
409
  if ( array_key_exists( 'autoptimize_extra_text_field_7', $options ) ) {
410
  $preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( $options['autoptimize_extra_text_field_7'] ) ) ) );
411
  }
412
+
413
+ if ( false === autoptimizeImages::imgopt_active() && false === autoptimizeImages::should_lazyload_wrapper() ) {
414
+ // only do this here if imgopt/ lazyload are not active?
415
+ $metabox_preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
416
+ if ( ! empty( $metabox_preloads ) ) {
417
+ $preloads = array_merge( $preloads, $metabox_preloads );
418
+ }
419
+ }
420
+
421
  $preloads = apply_filters( 'autoptimize_filter_extra_tobepreloaded', $preloads );
422
 
423
  // immediately return if nothing to be preloaded.
428
  // iterate through array and add preload link to tmp string.
429
  $preload_output = '';
430
  foreach ( $preloads as $preload ) {
431
+ if ( $preload !== filter_var( $preload, FILTER_VALIDATE_URL ) ) {
432
+ continue;
433
+ }
434
  $preload = esc_url_raw( $preload );
435
  $crossorigin = '';
436
  $preload_as = '';
458
  }
459
  $preload_output = apply_filters( 'autoptimize_filter_extra_preload_output', $preload_output );
460
 
461
+ return $this->inject_preloads( $preload_output, $in );
462
+ }
463
+
464
+ public static function inject_preloads( $preloads, $html ) {
465
  // add string to head (before first link node by default).
466
  $preload_inject = apply_filters( 'autoptimize_filter_extra_preload_inject', '<link' );
467
+ $position = autoptimizeUtils::strpos( $html, $preload_inject );
468
 
469
+ return autoptimizeUtils::substr_replace( $html, $preloads . $preload_inject, $position, strlen( $preload_inject ) );
470
+ }
471
+
472
+ public function disable_global_styles()
473
+ {
474
+ remove_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles' );
475
+ remove_action( 'wp_footer', 'wp_enqueue_global_styles', 1 );
476
+ remove_action( 'wp_body_open', 'wp_global_styles_render_svg_filters' );
477
+
478
+ if ( true === apply_filters( 'autoptimize_filter_extra_global_styles_and_block_css', true ) ) {
479
+ add_action( 'wp_enqueue_scripts', function(){
480
+ wp_dequeue_style( 'wp-block-library' );
481
+ wp_dequeue_style( 'wp-block-library-theme' );
482
+ });
483
+ }
484
  }
485
 
486
  public function admin_menu()
561
  <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>
562
  </td>
563
  </tr>
564
+ <tr>
565
+ <th scope="row"><?php _e( 'Remove WordPress block CSS', 'autoptimize' ); ?></th>
566
+ <td>
567
+ <label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_8]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_8'] ) && '1' === $options['autoptimize_extra_checkbox_field_8'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'WordPress adds block CSS and global styles to improve easy styling of block-based sites, but which can add a significant amount of CSS and SVG. If you are sure your site can do without the block CSS and "global styles", you can disable them here.', 'autoptimize' ); ?></label>
568
+ </td>
569
+ </tr>
570
  <tr>
571
  <th scope="row"><?php _e( 'Preconnect to 3rd party domains <em>(advanced users)</em>', 'autoptimize' ); ?></th>
572
  <td>
classes/autoptimizeHTML.php CHANGED
@@ -82,10 +82,14 @@ class autoptimizeHTML extends autoptimizeBase
82
  $options['xhtml'] = true;
83
  }
84
 
85
- // Optionally (filter-based, GUI later) minify inline JS & CSS.
86
- if ( apply_filters( 'autoptimize_html_minify_inline_js_css', false ) ) {
87
- $options['jsMinifier'] = 'JSMin::minify';
88
- $options['cssMinifier'] = 'autoptimizeCSSmin::minify';
 
 
 
 
89
  }
90
 
91
  $tmp_content = AO_Minify_HTML::minify( $this->content, $options );
82
  $options['xhtml'] = true;
83
  }
84
 
85
+ // Optionally minify inline JS & CSS.
86
+ if ( apply_filters( 'autoptimize_html_minify_inline_js_css', true ) && false === $this->keepcomments ) {
87
+ if ( false != autoptimizeOptionWrapper::get_option( 'autoptimize_js' ) && false != autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_js_optimize' ) && true !== apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content ) ) {
88
+ $options['jsMinifier'] = 'JSMin::minify';
89
+ }
90
+ if ( false != autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) && false != autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_css_optimize' ) && true !== apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content ) ) {
91
+ $options['cssMinifier'] = 'autoptimizeCSSmin::minify';
92
+ }
93
  }
94
 
95
  $tmp_content = AO_Minify_HTML::minify( $this->content, $options );
classes/autoptimizeImages.php CHANGED
@@ -562,6 +562,7 @@ class autoptimizeImages
562
  * filter for critical CSS.
563
  */
564
  $to_replace = array();
 
565
 
566
  // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
567
  if ( $this->should_lazyload() ) {
@@ -573,6 +574,9 @@ class autoptimizeImages
573
  );
574
  }
575
 
 
 
 
576
  // extract img tags.
577
  if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
578
  foreach ( $matches[0] as $tag ) {
@@ -649,6 +653,11 @@ class autoptimizeImages
649
  // then call add_lazyload-function with lpiq placeholder if set.
650
  $tag = $this->add_lazyload( $tag, $placeholder );
651
  }
 
 
 
 
 
652
 
653
  $tag = apply_filters( 'autoptimize_filter_imgopt_tag_postopt' , $tag );
654
 
@@ -656,6 +665,11 @@ class autoptimizeImages
656
  if ( $tag !== $orig_tag ) {
657
  $to_replace[ $orig_tag ] = $tag;
658
  }
 
 
 
 
 
659
  }
660
  }
661
 
@@ -702,6 +716,17 @@ class autoptimizeImages
702
  $out = $this->process_picture_tag( $out, true, false );
703
  }
704
 
 
 
 
 
 
 
 
 
 
 
 
705
  return $out;
706
  }
707
 
@@ -726,13 +751,17 @@ class autoptimizeImages
726
  $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
727
  if ( $width > $_max_width ) {
728
  $_width = $_max_width;
729
- $height = $_width / $width * $height;
 
 
730
  $width = $_width;
731
  }
732
  $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
733
  if ( $height > $_max_height ) {
734
  $_height = $_max_height;
735
- $width = $_height / $height * $width;
 
 
736
  $height = $_height;
737
  }
738
 
@@ -780,6 +809,7 @@ class autoptimizeImages
780
  {
781
  // only used is image optimization is NOT active but lazyload is.
782
  $to_replace = array();
 
783
 
784
  // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
785
  $out = autoptimizeBase::replace_contents_with_marker_if_exists(
@@ -789,12 +819,20 @@ class autoptimizeImages
789
  $in
790
  );
791
 
792
- // extract img tags and add lazyload attribs.
 
 
 
793
  if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
794
  foreach ( $matches[0] as $tag ) {
795
  if ( $this->should_lazyload( $out ) ) {
796
  $to_replace[ $tag ] = $this->add_lazyload( $tag );
797
  }
 
 
 
 
 
798
  }
799
  $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
800
  }
@@ -811,6 +849,17 @@ class autoptimizeImages
811
  $out
812
  );
813
 
 
 
 
 
 
 
 
 
 
 
 
814
  return $out;
815
  }
816
 
@@ -879,7 +928,14 @@ class autoptimizeImages
879
  $noptimize_flag = ' data-noptimize="1"';
880
  }
881
 
882
- $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
 
 
 
 
 
 
 
883
  $cdn_url = $this->get_cdn_url();
884
  if ( ! empty( $cdn_url ) ) {
885
  $cdn_url = rtrim( $cdn_url, '/' );
@@ -896,6 +952,22 @@ class autoptimizeImages
896
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
897
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
898
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
899
 
900
  public static function get_cdn_url() {
901
  // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
@@ -1204,7 +1276,7 @@ class autoptimizeImages
1204
  <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"'; } ?>>
1205
  <th scope="row"><?php _e( 'Load AVIF in supported browsers?', 'autoptimize' ); ?></th>
1206
  <td>
1207
- <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_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Automatically serve AVIF image format to any browser that supports it.', 'autoptimize' ); ?></label>
1208
  </td>
1209
  </tr>
1210
  <tr>
562
  * filter for critical CSS.
563
  */
564
  $to_replace = array();
565
+ $to_preload = '';
566
 
567
  // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
568
  if ( $this->should_lazyload() ) {
574
  );
575
  }
576
 
577
+ // get img preloads as set in post metabox.
578
+ $metabox_preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
579
+
580
  // extract img tags.
581
  if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
582
  foreach ( $matches[0] as $tag ) {
653
  // then call add_lazyload-function with lpiq placeholder if set.
654
  $tag = $this->add_lazyload( $tag, $placeholder );
655
  }
656
+
657
+ // add decoding="async" behind filter, not sure if I'll make it default true yet.
658
+ if ( true === apply_filters( 'autoptimize_filter_imgopt_add_decoding', true ) && false === strpos( $tag, ' decoding=' ) ) {
659
+ $tag = str_replace( '<img ', '<img decoding="async" ', $tag );
660
+ }
661
 
662
  $tag = apply_filters( 'autoptimize_filter_imgopt_tag_postopt' , $tag );
663
 
665
  if ( $tag !== $orig_tag ) {
666
  $to_replace[ $orig_tag ] = $tag;
667
  }
668
+
669
+ // and check if image needs to be prelaoded.
670
+ if ( ! empty( $metabox_preloads ) && str_replace( $metabox_preloads, '', $tag ) !== $tag ) {
671
+ $to_preload .= $this->create_img_preload_tag( $tag );
672
+ }
673
  }
674
  }
675
 
716
  $out = $this->process_picture_tag( $out, true, false );
717
  }
718
 
719
+ if ( ! empty( $metabox_preloads ) && empty( $to_preload ) ) {
720
+ // the preload was not in an img tag, so adding a non-responsive preload instead.
721
+ foreach( $metabox_preloads as $img_preload ) {
722
+ $to_preload .= '<link rel="preload" href="' . $img_preload . '" as="image">' ;
723
+ }
724
+ }
725
+
726
+ if ( ! empty( $to_preload ) ) {
727
+ $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
728
+ }
729
+
730
  return $out;
731
  }
732
 
751
  $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
752
  if ( $width > $_max_width ) {
753
  $_width = $_max_width;
754
+ if ( ! empty( $height ) && is_int( $height ) ) {
755
+ $height = $_width / $width * $height;
756
+ }
757
  $width = $_width;
758
  }
759
  $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
760
  if ( $height > $_max_height ) {
761
  $_height = $_max_height;
762
+ if ( ! empty( $width ) && is_int( $width ) ) {
763
+ $width = $_height / $height * $width;
764
+ }
765
  $height = $_height;
766
  }
767
 
809
  {
810
  // only used is image optimization is NOT active but lazyload is.
811
  $to_replace = array();
812
+ $to_preload = '';
813
 
814
  // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
815
  $out = autoptimizeBase::replace_contents_with_marker_if_exists(
819
  $in
820
  );
821
 
822
+ // get img preloads as set in post metabox.
823
+ $metabox_preloads = array_filter( array_map( 'trim', explode( ',', wp_strip_all_tags( autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_preload' ) ) ) ) );
824
+
825
+ // extract img tags and add lazyload attribs/ add preloads.
826
  if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
827
  foreach ( $matches[0] as $tag ) {
828
  if ( $this->should_lazyload( $out ) ) {
829
  $to_replace[ $tag ] = $this->add_lazyload( $tag );
830
  }
831
+
832
+ // and check if image needs to be prelaoded.
833
+ if ( ! empty( $metabox_preloads ) && str_replace( $metabox_preloads, '', $tag ) !== $tag ) {
834
+ $to_preload .= $this->create_img_preload_tag( $tag );
835
+ }
836
  }
837
  $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
838
  }
849
  $out
850
  );
851
 
852
+ if ( ! empty( $metabox_preloads ) && empty( $to_preload ) ) {
853
+ // the preload was not in an img tag, so adding a non-responsive preload instead.
854
+ foreach( $metabox_preloads as $img_preload ) {
855
+ $to_preload .= '<link rel="preload" href="' . $img_preload . '" as="image">' ;
856
+ }
857
+ }
858
+
859
+ if ( ! empty( $to_preload ) ) {
860
+ $out = autoptimizeExtra::inject_preloads( $to_preload, $out );
861
+ }
862
+
863
  return $out;
864
  }
865
 
928
  $noptimize_flag = ' data-noptimize="1"';
929
  }
930
 
931
+ $_extra = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings', '' );
932
+ if ( is_array( $_extra ) && array_key_exists( 'autoptimize_extra_checkbox_field_0', $_extra ) && ! empty( $_extra['autoptimize_extra_checkbox_field_0'] ) ) {
933
+ // if "remove query strings" is active in "extra", then let's be consistant and not add one ourselves? :)
934
+ $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js', __FILE__ );
935
+ } else {
936
+ $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
937
+ }
938
+
939
  $cdn_url = $this->get_cdn_url();
940
  if ( ! empty( $cdn_url ) ) {
941
  $cdn_url = rtrim( $cdn_url, '/' );
952
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
953
  echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
954
  }
955
+
956
+ public static function create_img_preload_tag( $tag ) {
957
+ // rewrite img tag to link preload img.
958
+ $_from = array( '<img ', ' src=', ' sizes=', ' srcset=' );
959
+ $_to = array( '<link rel="preload" as="image" ', ' href=', ' imagesizes=', ' imagesrcset=' );
960
+ $tag = str_replace( $_from, $_to, $tag );
961
+
962
+ // and remove title, alt, class and id.
963
+ $tag = preg_replace( '/ ((?:title|alt|class|id|loading)=".*")/Um', '', $tag );
964
+ if ( $tag !== str_replace( array(' title=', ' class=', ' alt=', ' id=' ), '', $tag ) ) {
965
+ // 2nd regex pass if still title/ class/ alt in case single quotes were used iso doubles.
966
+ $tag = preg_replace( '/ ((?:title|alt|class|id|loading)=\'.*\')/Um', '', $tag );
967
+ }
968
+
969
+ return $tag;
970
+ }
971
 
972
  public static function get_cdn_url() {
973
  // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
1276
  <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"'; } ?>>
1277
  <th scope="row"><?php _e( 'Load AVIF in supported browsers?', 'autoptimize' ); ?></th>
1278
  <td>
1279
+ <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'><?php _e( 'Automatically serve AVIF image format to any browser that supports it.', 'autoptimize' ); ?></label>
1280
  </td>
1281
  </tr>
1282
  <tr>
classes/autoptimizeMain.php CHANGED
@@ -27,6 +27,13 @@ class autoptimizeMain
27
  */
28
  protected $filepath = null;
29
 
 
 
 
 
 
 
 
30
  /**
31
  * Constructor.
32
  *
@@ -59,6 +66,7 @@ class autoptimizeMain
59
 
60
  add_action( 'autoptimize_setup_done', array( $this, 'version_upgrades_check' ) );
61
  add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
 
62
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_extra' ), 15 );
63
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_admin_only_trinkets' ), 20 );
64
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_criticalcss' ), 11 );
@@ -218,11 +226,18 @@ class autoptimizeMain
218
  }
219
  }
220
 
 
 
 
 
 
221
  public function maybe_run_criticalcss()
222
  {
223
  // Loads criticalcss if the power-up is not active and if the filter returns true.
224
  if ( apply_filters( 'autoptimize_filter_criticalcss_active', true ) && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
225
- new autoptimizeCriticalCSSBase();
 
 
226
  }
227
  }
228
 
@@ -233,6 +248,23 @@ class autoptimizeMain
233
  }
234
  }
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  public function hook_page_cache_purge()
237
  {
238
  // hook into a collection of page cache purge actions if filter allows.
@@ -356,7 +388,7 @@ class autoptimizeMain
356
 
357
  // And make sure pagebuilder previews don't get optimized HTML/ JS/ CSS/ ...
358
  if ( false === $ao_noptimize ) {
359
- $_qs_pagebuilders = array( 'tve', 'elementor-preview', 'fl_builder', 'vc_action', 'et_fb', 'bt-beaverbuildertheme', 'ct_builder', 'fb-edit', 'siteorigin_panels_live_editor' );
360
  foreach ( $_qs_pagebuilders as $_pagebuilder ) {
361
  if ( array_key_exists( $_pagebuilder, $_GET ) ) {
362
  $ao_noptimize = true;
@@ -559,8 +591,15 @@ class autoptimizeMain
559
 
560
  public static function on_uninstall()
561
  {
 
562
  autoptimizeCache::clearall();
563
 
 
 
 
 
 
 
564
  $delete_options = array(
565
  'autoptimize_cache_clean',
566
  'autoptimize_cache_nogzip',
@@ -616,6 +655,7 @@ class autoptimizeMain
616
  'autoptimize_ccss_deferjquery',
617
  'autoptimize_ccss_domain',
618
  'autoptimize_ccss_unloadccss',
 
619
  );
620
 
621
  if ( ! is_multisite() ) {
@@ -671,7 +711,7 @@ class autoptimizeMain
671
 
672
  public static function remove_cronjobs() {
673
  // Remove scheduled events.
674
- foreach ( array( 'ao_cachechecker', 'ao_ccss_queue', 'ao_ccss_maintenance' ) as $_event ) {
675
  if ( wp_get_schedule( $_event ) ) {
676
  wp_clear_scheduled_hook( $_event );
677
  }
27
  */
28
  protected $filepath = null;
29
 
30
+ /**
31
+ * Critical CSS base object
32
+ *
33
+ * @var object
34
+ */
35
+ protected $_criticalcss = null;
36
+
37
  /**
38
  * Constructor.
39
  *
66
 
67
  add_action( 'autoptimize_setup_done', array( $this, 'version_upgrades_check' ) );
68
  add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
69
+ add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_compat' ), 10 );
70
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_extra' ), 15 );
71
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_admin_only_trinkets' ), 20 );
72
  add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_criticalcss' ), 11 );
226
  }
227
  }
228
 
229
+ public function criticalcss()
230
+ {
231
+ return $this->_criticalcss;
232
+ }
233
+
234
  public function maybe_run_criticalcss()
235
  {
236
  // Loads criticalcss if the power-up is not active and if the filter returns true.
237
  if ( apply_filters( 'autoptimize_filter_criticalcss_active', true ) && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
238
+ $this->_criticalcss = new autoptimizeCriticalCSSBase();
239
+ $this->_criticalcss->setup();
240
+ $this->_criticalcss->load_requires();
241
  }
242
  }
243
 
248
  }
249
  }
250
 
251
+ public function maybe_run_ao_compat()
252
+ {
253
+ // Condtionally loads the compatibility-class to ensure more out-of-the-box compatibility with big players.
254
+ $_run_compat = true;
255
+
256
+ if ( autoptimizeOptionWrapper::get_option( 'autoptimize_installed_before_compatibility', false ) ) {
257
+ // If AO was already running before Compatibility logic was added, don't run compat by default
258
+ // because it can be assumed everything works and we want to avoid (perf) regressions that
259
+ // could occur due to compatibility code.
260
+ $_run_compat = false;
261
+ }
262
+
263
+ if ( apply_filters( 'autoptimize_filter_init_compatibility', $_run_compat ) ) {
264
+ new autoptimizeCompatibility();
265
+ }
266
+ }
267
+
268
  public function hook_page_cache_purge()
269
  {
270
  // hook into a collection of page cache purge actions if filter allows.
388
 
389
  // And make sure pagebuilder previews don't get optimized HTML/ JS/ CSS/ ...
390
  if ( false === $ao_noptimize ) {
391
+ $_qs_pagebuilders = array( 'tve', 'elementor-preview', 'fl_builder', 'vc_action', 'et_fb', 'bt-beaverbuildertheme', 'ct_builder', 'fb-edit', 'siteorigin_panels_live_editor', 'preview' );
392
  foreach ( $_qs_pagebuilders as $_pagebuilder ) {
393
  if ( array_key_exists( $_pagebuilder, $_GET ) ) {
394
  $ao_noptimize = true;
591
 
592
  public static function on_uninstall()
593
  {
594
+ // clear the cache.
595
  autoptimizeCache::clearall();
596
 
597
+ // remove postmeta if active.
598
+ if ( autoptimizeConfig::is_ao_meta_settings_active() ) {
599
+ delete_post_meta_by_key( 'ao_post_optimize' );
600
+ }
601
+
602
+ // remove all options.
603
  $delete_options = array(
604
  'autoptimize_cache_clean',
605
  'autoptimize_cache_nogzip',
655
  'autoptimize_ccss_deferjquery',
656
  'autoptimize_ccss_domain',
657
  'autoptimize_ccss_unloadccss',
658
+ 'autoptimize_installed_before_compatibility',
659
  );
660
 
661
  if ( ! is_multisite() ) {
711
 
712
  public static function remove_cronjobs() {
713
  // Remove scheduled events.
714
+ foreach ( array( 'ao_cachechecker', 'ao_ccss_queue', 'ao_ccss_maintenance', 'ao_ccss_keychecker' ) as $_event ) {
715
  if ( wp_get_schedule( $_event ) ) {
716
  wp_clear_scheduled_hook( $_event );
717
  }
classes/autoptimizeMetabox.php CHANGED
@@ -23,7 +23,7 @@ class autoptimizeMetabox
23
 
24
  public function ao_metabox_add_box()
25
  {
26
- $screens = array(
27
  'post',
28
  'page',
29
  // add extra types e.g. product or ... ?
@@ -42,7 +42,7 @@ class autoptimizeMetabox
42
 
43
  /**
44
  * Prints the box content.
45
- *
46
  * @param WP_Post $post The object for the current post/page.
47
  */
48
  function ao_metabox_content( $post )
@@ -50,7 +50,7 @@ class autoptimizeMetabox
50
  wp_nonce_field( 'ao_metabox', 'ao_metabox_nonce' );
51
 
52
  $ao_opt_value = $this->check_ao_opt_sanity( get_post_meta( $post->ID, 'ao_post_optimize', true ) );
53
-
54
  $_ao_meta_sub_opacity = '';
55
  if ( 'on' !== $ao_opt_value['ao_post_optimize'] ) {
56
  $_ao_meta_sub_opacity = 'opacity:.33;';
@@ -62,9 +62,9 @@ class autoptimizeMetabox
62
  <?php _e( 'Optimize this page?', 'autoptimize' ); ?>
63
  </label>
64
  </p>
65
- <?php
66
  $_ao_meta_js_style = '';
67
- if ( 'on' !== get_option( 'autoptimize_js', false ) ) {
68
  $_ao_meta_js_style = 'display:none;';
69
  }
70
  echo '<p class="ao_meta_sub" style="' . $_ao_meta_sub_opacity . $_ao_meta_js_style . '">';
@@ -74,9 +74,9 @@ class autoptimizeMetabox
74
  <?php _e( 'Optimize JS?', 'autoptimize' ); ?>
75
  </label>
76
  </p>
77
- <?php
78
  $_ao_meta_css_style = '';
79
- if ( 'on' !== get_option( 'autoptimize_css', false ) ) {
80
  $_ao_meta_css_style = 'display:none;';
81
  }
82
  echo '<p class="ao_meta_sub" style="' . $_ao_meta_sub_opacity . $_ao_meta_css_style . '">';
@@ -86,9 +86,9 @@ class autoptimizeMetabox
86
  <?php _e( 'Optimize CSS?', 'autoptimize' ); ?>
87
  </label>
88
  </p>
89
- <?php
90
  $_ao_meta_ccss_style = '';
91
- if ( 'on' !== get_option( 'autoptimize_css_defer', false ) ) {
92
  $_ao_meta_ccss_style = 'display:none;';
93
  }
94
  if ( 'on' !== $ao_opt_value['ao_post_css_optimize'] ) {
@@ -101,7 +101,7 @@ class autoptimizeMetabox
101
  <?php _e( 'Inline critical CSS?', 'autoptimize' ); ?>
102
  </label>
103
  </p>
104
- <?php
105
  $_ao_meta_lazyload_style = '';
106
  if ( false === autoptimizeImages::should_lazyload_wrapper() ) {
107
  $_ao_meta_lazyload_style = 'display:none;';
@@ -113,6 +113,13 @@ class autoptimizeMetabox
113
  <?php _e( 'Lazyload images?', 'autoptimize' ); ?>
114
  </label>
115
  </p>
 
 
 
 
 
 
 
116
  <p>
117
  <?php
118
  // Get path + check if button should be enabled or disabled.
@@ -132,7 +139,7 @@ class autoptimizeMetabox
132
  }
133
 
134
  // if CSS opt and inline & defer are on and if we have a slug, the button can be active.
135
- if ( false !== $_slug && 'on' === get_option( 'autoptimize_css', false ) && 'on' === get_option( 'autoptimize_css_defer', false ) && ! empty( apply_filters( 'autoptimize_filter_ccss_key', get_option( 'autoptimize_ccss_key', false ) ) ) && '2' === get_option( 'autoptimize_ccss_keyst', false ) ) {
136
  $_generate_disabled = false;
137
  }
138
  ?>
@@ -229,11 +236,13 @@ class autoptimizeMetabox
229
 
230
  // OK, we can have a look at the actual data now.
231
  // Sanitize user input.
232
- foreach ( array( 'ao_post_optimize', 'ao_post_js_optimize', 'ao_post_css_optimize', 'ao_post_ccss', 'ao_post_lazyload' ) as $opti_type ) {
233
- if ( isset( $_POST[$opti_type] ) ) {
234
- $ao_meta_result[$opti_type] = 'on';
235
- } else {
236
  $ao_meta_result[$opti_type] = '';
 
 
 
 
237
  }
238
  }
239
 
@@ -252,11 +261,9 @@ class autoptimizeMetabox
252
  $type = 'is_single';
253
  }
254
 
255
- $path = wp_strip_all_tags( $_POST['path'] );
256
- $criticalcss_core = new autoptimizeCriticalCSSCore();
257
- $criticalcss_base = new autoptimizeCriticalCSSBase();
258
- $criticalcss_enqueue = new autoptimizeCriticalCSSEnqueue();
259
- $_result = $criticalcss_enqueue->ao_ccss_enqueue( '', $path, $type );
260
 
261
  if ( $_result ) {
262
  $response['code'] = '200';
@@ -285,6 +292,7 @@ class autoptimizeMetabox
285
  'ao_post_css_optimize' => 'on',
286
  'ao_post_ccss' => 'on',
287
  'ao_post_lazyload' => 'on',
 
288
  );
289
  return $ao_metabox_defaults;
290
  }
23
 
24
  public function ao_metabox_add_box()
25
  {
26
+ $screens = array(
27
  'post',
28
  'page',
29
  // add extra types e.g. product or ... ?
42
 
43
  /**
44
  * Prints the box content.
45
+ *
46
  * @param WP_Post $post The object for the current post/page.
47
  */
48
  function ao_metabox_content( $post )
50
  wp_nonce_field( 'ao_metabox', 'ao_metabox_nonce' );
51
 
52
  $ao_opt_value = $this->check_ao_opt_sanity( get_post_meta( $post->ID, 'ao_post_optimize', true ) );
53
+
54
  $_ao_meta_sub_opacity = '';
55
  if ( 'on' !== $ao_opt_value['ao_post_optimize'] ) {
56
  $_ao_meta_sub_opacity = 'opacity:.33;';
62
  <?php _e( 'Optimize this page?', 'autoptimize' ); ?>
63
  </label>
64
  </p>
65
+ <?php
66
  $_ao_meta_js_style = '';
67
+ if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_js', false ) ) {
68
  $_ao_meta_js_style = 'display:none;';
69
  }
70
  echo '<p class="ao_meta_sub" style="' . $_ao_meta_sub_opacity . $_ao_meta_js_style . '">';
74
  <?php _e( 'Optimize JS?', 'autoptimize' ); ?>
75
  </label>
76
  </p>
77
+ <?php
78
  $_ao_meta_css_style = '';
79
+ if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css', false ) ) {
80
  $_ao_meta_css_style = 'display:none;';
81
  }
82
  echo '<p class="ao_meta_sub" style="' . $_ao_meta_sub_opacity . $_ao_meta_css_style . '">';
86
  <?php _e( 'Optimize CSS?', 'autoptimize' ); ?>
87
  </label>
88
  </p>
89
+ <?php
90
  $_ao_meta_ccss_style = '';
91
+ if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer', false ) || 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css', false ) ) {
92
  $_ao_meta_ccss_style = 'display:none;';
93
  }
94
  if ( 'on' !== $ao_opt_value['ao_post_css_optimize'] ) {
101
  <?php _e( 'Inline critical CSS?', 'autoptimize' ); ?>
102
  </label>
103
  </p>
104
+ <?php
105
  $_ao_meta_lazyload_style = '';
106
  if ( false === autoptimizeImages::should_lazyload_wrapper() ) {
107
  $_ao_meta_lazyload_style = 'display:none;';
113
  <?php _e( 'Lazyload images?', 'autoptimize' ); ?>
114
  </label>
115
  </p>
116
+ <p class="ao_meta_sub" style="<?php echo $_ao_meta_sub_opacity ?>">
117
+ <label for="autoptimize_post_preload">
118
+ <?php _e( 'LCP Image to preload', 'autoptimize' ); ?>
119
+ </label>
120
+ <input type="text" id="autoptimize_post_preload" name="ao_post_preload" value="<?php echo esc_attr( $ao_opt_value['ao_post_preload'] ) ?>">
121
+ </p>
122
+ <p>&nbsp;</p>
123
  <p>
124
  <?php
125
  // Get path + check if button should be enabled or disabled.
139
  }
140
 
141
  // if CSS opt and inline & defer are on and if we have a slug, the button can be active.
142
+ if ( false !== $_slug && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_css', false ) && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer', false ) && ! empty( apply_filters( 'autoptimize_filter_ccss_key', autoptimizeOptionWrapper::get_option( 'autoptimize_ccss_key', false ) ) ) && '2' === autoptimizeOptionWrapper::get_option( 'autoptimize_ccss_keyst', false ) ) {
143
  $_generate_disabled = false;
144
  }
145
  ?>
236
 
237
  // OK, we can have a look at the actual data now.
238
  // Sanitize user input.
239
+ foreach ( array( 'ao_post_optimize', 'ao_post_js_optimize', 'ao_post_css_optimize', 'ao_post_ccss', 'ao_post_lazyload', 'ao_post_preload' ) as $opti_type ) {
240
+ if ( ! isset( $_POST[$opti_type] ) ) {
 
 
241
  $ao_meta_result[$opti_type] = '';
242
+ } else if ( 'on' === $_POST[$opti_type] ) {
243
+ $ao_meta_result[$opti_type] = 'on';
244
+ } else if ( in_array( $opti_type, array( 'ao_post_preload' ) ) ) {
245
+ $ao_meta_result[$opti_type] = $_POST[$opti_type];
246
  }
247
  }
248
 
261
  $type = 'is_single';
262
  }
263
 
264
+ $path = wp_strip_all_tags( $_POST['path'] );
265
+ $criticalcss = autoptimize()->criticalcss();
266
+ $_result = $criticalcss->enqueue( '', $path, $type );
 
 
267
 
268
  if ( $_result ) {
269
  $response['code'] = '200';
292
  'ao_post_css_optimize' => 'on',
293
  'ao_post_ccss' => 'on',
294
  'ao_post_lazyload' => 'on',
295
+ 'ao_post_preload' => '',
296
  );
297
  return $ao_metabox_defaults;
298
  }
classes/autoptimizeScripts.php CHANGED
@@ -261,6 +261,8 @@ class autoptimizeScripts extends autoptimizeBase
261
  if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) {
262
  $this->aggregate = false;
263
  }
 
 
264
 
265
  // Defer when not aggregating.
266
  if ( false === $this->aggregate && apply_filters( 'autoptimize_filter_js_defer_not_aggregate', $options['defer_not_aggregate'] ) ) {
@@ -434,6 +436,8 @@ class autoptimizeScripts extends autoptimizeBase
434
  $code = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code );
435
  $this->scripts[] = 'INLINE;' . $code;
436
  } else {
 
 
437
  if ( false === $this->defer_inline ) {
438
  // Can we move this?
439
  $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
@@ -446,10 +450,17 @@ class autoptimizeScripts extends autoptimizeBase
446
  } else {
447
  $tag = '';
448
  }
449
- } elseif ( str_replace( $this->dontmove, '', $tag ) === $tag ) {
450
  // defer inline JS by base64 encoding it.
451
- preg_match( '#<script.*>(.*)</script>#Usmi', $tag, $match );
452
- $new_tag = '<script defer src="data:text/javascript;base64,' . base64_encode( $match[1] ) . '"></script>';
 
 
 
 
 
 
 
453
  $this->content = str_replace( $tag, $new_tag, $this->content );
454
  $tag = '';
455
  } else {
261
  if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) {
262
  $this->aggregate = false;
263
  }
264
+ // and the filter that should have been there to begin with.
265
+ $this->aggregate = apply_filters( 'autoptimize_filter_js_aggregate', $this->aggregate );
266
 
267
  // Defer when not aggregating.
268
  if ( false === $this->aggregate && apply_filters( 'autoptimize_filter_js_defer_not_aggregate', $options['defer_not_aggregate'] ) ) {
436
  $code = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code );
437
  $this->scripts[] = 'INLINE;' . $code;
438
  } else {
439
+ $_inline_deferable = apply_filters( 'autoptimize_filters_js_inline_deferable', array( 'nonce', 'post_id', 'syntaxhighlighter' ) );
440
+ $_inline_dontmove = array_values( array_diff( $this->dontmove, $_inline_deferable ) );
441
  if ( false === $this->defer_inline ) {
442
  // Can we move this?
443
  $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
450
  } else {
451
  $tag = '';
452
  }
453
+ } else if ( str_replace( $_inline_dontmove, '', $tag ) === $tag ) {
454
  // defer inline JS by base64 encoding it.
455
+ // preg_match( '#<script.*>(.*)</script>#Usmi', $tag, $match );
456
+ preg_match( '#<script(?:[^>](?!id=))*\s*(?:id=(["\'])([^"\']+)\1)*+[^>]*+>(.*?)<\/script>#is', $tag, $match );
457
+ if ( $match[2] ) {
458
+ $_id = 'id="' . $match[2] . '" ';
459
+ } else {
460
+ $_id = '';
461
+ }
462
+
463
+ $new_tag = '<script defer ' . $_id . 'src="data:text/javascript;base64,' . base64_encode( $match[3] ) . '"></script>';
464
  $this->content = str_replace( $tag, $new_tag, $this->content );
465
  $tag = '';
466
  } else {
classes/autoptimizeStyles.php CHANGED
@@ -198,6 +198,8 @@ class autoptimizeStyles extends autoptimizeBase
198
  if ( $this->aggregate && apply_filters( 'autoptimize_filter_css_dontaggregate', false ) ) {
199
  $this->aggregate = false;
200
  }
 
 
201
 
202
  // include inline?
203
  if ( apply_filters( 'autoptimize_css_include_inline', $options['include_inline'] ) ) {
@@ -489,7 +491,7 @@ class autoptimizeStyles extends autoptimizeBase
489
  private function check_datauri_exclude_list( $url )
490
  {
491
  static $exclude_list = null;
492
- $no_datauris = array();
493
 
494
  // Again, skip doing certain stuff repeatedly when loop-called.
495
  if ( null === $exclude_list ) {
@@ -871,7 +873,7 @@ class autoptimizeStyles extends autoptimizeBase
871
  }
872
 
873
  if ( ! empty( $code ) ) {
874
- $tmp_thiscss = preg_replace( '#(/\*FILESTART\*/.*)' . preg_quote( $import, '#' ) . '#Us', '/*FILESTART2*/' . $code . '$1', $thiscss );
875
  if ( ! empty( $tmp_thiscss ) ) {
876
  $thiscss = $tmp_thiscss;
877
  $import_ok = true;
198
  if ( $this->aggregate && apply_filters( 'autoptimize_filter_css_dontaggregate', false ) ) {
199
  $this->aggregate = false;
200
  }
201
+ // and the filter that should have been there to begin with.
202
+ $this->aggregate = apply_filters( 'autoptimize_filter_css_aggregate', $this->aggregate );
203
 
204
  // include inline?
205
  if ( apply_filters( 'autoptimize_css_include_inline', $options['include_inline'] ) ) {
491
  private function check_datauri_exclude_list( $url )
492
  {
493
  static $exclude_list = null;
494
+ static $no_datauris = array();
495
 
496
  // Again, skip doing certain stuff repeatedly when loop-called.
497
  if ( null === $exclude_list ) {
873
  }
874
 
875
  if ( ! empty( $code ) ) {
876
+ $tmp_thiscss = str_replace( $import, $code, $thiscss );
877
  if ( ! empty( $tmp_thiscss ) ) {
878
  $thiscss = $tmp_thiscss;
879
  $import_ok = true;
classes/autoptimizeVersionUpdatesHandler.php CHANGED
@@ -57,6 +57,13 @@ class autoptimizeVersionUpdatesHandler
57
  $this->upgrade_from_2_7();
58
  $major_update = true;
59
  // No break, intentionally, so all upgrades are ran during a single request...
 
 
 
 
 
 
 
60
  }
61
 
62
  if ( true === $major_update ) {
@@ -258,11 +265,20 @@ class autoptimizeVersionUpdatesHandler
258
  * remove CCSS request limit option + update jquery exclusion to include WordPress 5.6 jquery.min.js.
259
  */
260
  private function upgrade_from_2_7() {
261
- delete_option( 'autoptimize_ccss_rlimit' );
262
  $js_exclusions = get_option( 'autoptimize_js_exclude', '' );
263
  if ( strpos( $js_exclusions, 'js/jquery/jquery.js' ) !== false && strpos( $js_exclusions, 'js/jquery/jquery.min.js' ) === false ) {
264
  $js_exclusions .= ', js/jquery/jquery.min.js';
265
- update_option( 'autoptimize_js_exclude', $js_exclusions );
266
  }
267
  }
 
 
 
 
 
 
 
 
 
268
  }
57
  $this->upgrade_from_2_7();
58
  $major_update = true;
59
  // No break, intentionally, so all upgrades are ran during a single request...
60
+ case '2.8':
61
+ // nothing
62
+ case '2.9':
63
+ if ( version_compare( autoptimizeOptionWrapper::get_option( 'autoptimize_version', 'none' ), '2.9.999', 'lt' ) ) {
64
+ $this->upgrade_from_2_9_before_compatibility();
65
+ }
66
+ $major_update = false;
67
  }
68
 
69
  if ( true === $major_update ) {
265
  * remove CCSS request limit option + update jquery exclusion to include WordPress 5.6 jquery.min.js.
266
  */
267
  private function upgrade_from_2_7() {
268
+ autoptimizeOptionWrapper::delete_option( 'autoptimize_ccss_rlimit' );
269
  $js_exclusions = get_option( 'autoptimize_js_exclude', '' );
270
  if ( strpos( $js_exclusions, 'js/jquery/jquery.js' ) !== false && strpos( $js_exclusions, 'js/jquery/jquery.min.js' ) === false ) {
271
  $js_exclusions .= ', js/jquery/jquery.min.js';
272
+ autoptimizeOptionWrapper::update_option( 'autoptimize_js_exclude', $js_exclusions );
273
  }
274
  }
275
+
276
+ /**
277
+ * set an option to indicate the AO installation predates the compatibility logic, this way we
278
+ * can avoid adding compatibility code that is likely not needed and maybe not wanted as it
279
+ * can introduce performance regressions.
280
+ */
281
+ private function upgrade_from_2_9_before_compatibility() {
282
+ autoptimizeOptionWrapper::update_option( 'autoptimize_installed_before_compatibility', true );
283
+ }
284
  }
classes/critcss-inc/admin_settings_adv.php CHANGED
@@ -7,16 +7,17 @@
7
  * Function to render the advanced panel.
8
  */
9
  function ao_ccss_render_adv() {
10
- // Attach required globals.
11
- global $ao_ccss_debug;
12
- global $ao_ccss_finclude;
13
- global $ao_ccss_rtimelimit;
14
- global $ao_ccss_noptimize;
15
- global $ao_ccss_loggedin;
16
- global $ao_ccss_forcepath;
17
- global $ao_ccss_deferjquery;
18
- global $ao_ccss_domain;
19
- global $ao_ccss_unloadccss;
 
20
 
21
  // In case domain is not set yet (done in cron.php).
22
  if ( empty( $ao_ccss_domain ) ) {
@@ -24,7 +25,7 @@ function ao_ccss_render_adv() {
24
  }
25
 
26
  // Get viewport size.
27
- $viewport = autoptimizeCriticalCSSCore::ao_ccss_viewport();
28
  ?>
29
  <ul id="adv-panel">
30
  <li class="itemDetail">
@@ -101,6 +102,7 @@ function ao_ccss_render_adv() {
101
  </p>
102
  </td>
103
  </tr>
 
104
  <tr>
105
  <th scope="row">
106
  <?php _e( 'Defer jQuery and other non-aggregated JS-files? (deprecated)', 'autoptimize' ); ?>
@@ -113,6 +115,7 @@ function ao_ccss_render_adv() {
113
  </p>
114
  </td>
115
  </tr>
 
116
  <tr>
117
  <th scope="row">
118
  <?php _e( 'Unload critical CSS after page load?', 'autoptimize' ); ?>
7
  * Function to render the advanced panel.
8
  */
9
  function ao_ccss_render_adv() {
10
+ $criticalcss = autoptimize()->criticalcss();
11
+
12
+ $ao_ccss_debug = esc_attr( $criticalcss->get_option( 'debug' ) );
13
+ $ao_ccss_finclude = esc_textarea( $criticalcss->get_option( 'finclude' ) );
14
+ $ao_ccss_rtimelimit = esc_attr( $criticalcss->get_option( 'rtimelimit' ) );
15
+ $ao_ccss_noptimize = esc_attr( $criticalcss->get_option( 'noptimize' ) );
16
+ $ao_ccss_loggedin = esc_attr( $criticalcss->get_option( 'loggedin' ) );
17
+ $ao_ccss_forcepath = esc_attr( $criticalcss->get_option( 'forcepath' ) );
18
+ $ao_ccss_deferjquery = esc_attr( $criticalcss->get_option( 'deferjquery' ) );
19
+ $ao_ccss_domain = esc_attr( $criticalcss->get_option( 'domain' ) );
20
+ $ao_ccss_unloadccss = esc_attr( $criticalcss->get_option( 'unloadccss' ) );
21
 
22
  // In case domain is not set yet (done in cron.php).
23
  if ( empty( $ao_ccss_domain ) ) {
25
  }
26
 
27
  // Get viewport size.
28
+ $viewport = $criticalcss->viewport();
29
  ?>
30
  <ul id="adv-panel">
31
  <li class="itemDetail">
102
  </p>
103
  </td>
104
  </tr>
105
+ <?php if ( 1 == $ao_ccss_deferjquery ) { ?>
106
  <tr>
107
  <th scope="row">
108
  <?php _e( 'Defer jQuery and other non-aggregated JS-files? (deprecated)', 'autoptimize' ); ?>
115
  </p>
116
  </td>
117
  </tr>
118
+ <?php } ?>
119
  <tr>
120
  <th scope="row">
121
  <?php _e( 'Unload critical CSS after page load?', 'autoptimize' ); ?>
classes/critcss-inc/admin_settings_explain.php CHANGED
@@ -18,15 +18,23 @@ function ao_ccss_render_explain() {
18
  <?php
19
  $ccss_explanation = '';
20
 
 
 
 
 
 
 
 
 
21
  // get the HTML with the explanation of what critical CSS is.
22
  if ( apply_filters( 'autoptimize_settingsscreen_remotehttp', true ) ) {
23
- $ccss_explanation = get_transient( 'ao_ccss_explain' );
24
  if ( empty( $ccss_explanation ) ) {
25
- $ccss_expl_resp = wp_remote_get( 'https://misc.optimizingmatters.com/autoptimize_ccss_explain_i18n.html?ao_ver=' . AUTOPTIMIZE_PLUGIN_VERSION );
26
  if ( ! is_wp_error( $ccss_expl_resp ) ) {
27
  if ( '200' == wp_remote_retrieve_response_code( $ccss_expl_resp ) ) {
28
  $ccss_explanation = wp_kses_post( wp_remote_retrieve_body( $ccss_expl_resp ) );
29
- set_transient( 'ao_ccss_explain', $ccss_explanation, WEEK_IN_SECONDS );
30
  }
31
  }
32
  }
18
  <?php
19
  $ccss_explanation = '';
20
 
21
+ if ( apply_filters( 'autoptimize_filter_ccss_rules_without_api', true ) ) {
22
+ $_transient = 'ao3_ccss_explain';
23
+ $_explain_html = 'https://misc.optimizingmatters.com/autoptimize_ccss_explain_ao30_i18n.html?ao_ver=';
24
+ } else {
25
+ $_transient = 'ao_ccss_explain';
26
+ $_explain_html = 'https://misc.optimizingmatters.com/autoptimize_ccss_explain_i18n.html?ao_ver=';
27
+ }
28
+
29
  // get the HTML with the explanation of what critical CSS is.
30
  if ( apply_filters( 'autoptimize_settingsscreen_remotehttp', true ) ) {
31
+ $ccss_explanation = get_transient( $_transient );
32
  if ( empty( $ccss_explanation ) ) {
33
+ $ccss_expl_resp = wp_remote_get( $_explain_html . AUTOPTIMIZE_PLUGIN_VERSION );
34
  if ( ! is_wp_error( $ccss_expl_resp ) ) {
35
  if ( '200' == wp_remote_retrieve_response_code( $ccss_expl_resp ) ) {
36
  $ccss_explanation = wp_kses_post( wp_remote_retrieve_body( $ccss_expl_resp ) );
37
+ set_transient( 'ao3_ccss_explain', $ccss_explanation, WEEK_IN_SECONDS );
38
  }
39
  }
40
  }
classes/critcss-inc/admin_settings_queue.js.php CHANGED
@@ -63,6 +63,11 @@ function drawQueueTable(queue) {
63
  dbtn = false;
64
  hbtn = false;
65
 
 
 
 
 
 
66
  // Prepare job statuses
67
  if (keys.jqstat === 'NEW') {
68
  // Status: NEW (N, sort order 1)
63
  dbtn = false;
64
  hbtn = false;
65
 
66
+ // don't list jobs that don't have a type, they are irrelevant and this also avoids "type.replace is not a function".
67
+ if ( type == false ) {
68
+ return;
69
+ }
70
+
71
  // Prepare job statuses
72
  if (keys.jqstat === 'NEW') {
73
  // Status: NEW (N, sort order 1)
classes/critcss-inc/admin_settings_queue.php CHANGED
@@ -8,7 +8,8 @@
8
  */
9
  function ao_ccss_render_queue() {
10
  // Attach required arrays.
11
- global $ao_ccss_queue;
 
12
 
13
  // Prepare the queue object.
14
  if ( empty( $ao_ccss_queue ) ) {
@@ -25,7 +26,7 @@ function ao_ccss_render_queue() {
25
  <span class="toggle-indicator dashicons dashicons-arrow-up dashicons-arrow-down"></span>
26
  </button>
27
  <?php
28
- if ( autoptimizeCriticalCSSSettings::ao_ccss_has_autorules() ) {
29
  $_queue_visibility = 'hidden';
30
  } else {
31
  $_queue_visibility = 'visible';
8
  */
9
  function ao_ccss_render_queue() {
10
  // Attach required arrays.
11
+ $criticalcss = autoptimize()->criticalcss();
12
+ $ao_ccss_queue = $criticalcss->get_option( 'queue' );
13
 
14
  // Prepare the queue object.
15
  if ( empty( $ao_ccss_queue ) ) {
26
  <span class="toggle-indicator dashicons dashicons-arrow-up dashicons-arrow-down"></span>
27
  </button>
28
  <?php
29
+ if ( $criticalcss->has_autorules() ) {
30
  $_queue_visibility = 'hidden';
31
  } else {
32
  $_queue_visibility = 'visible';
classes/critcss-inc/admin_settings_rules.js.php CHANGED
@@ -55,6 +55,15 @@ function drawTable(critCssArray) {
55
  hash=nv.hash;
56
  file=nv.file;
57
  filest=nv.file;
 
 
 
 
 
 
 
 
 
58
  if (file == 0) {
59
  file='<?php _e( 'To be fetched from criticalcss.com in the next queue run...', 'autoptimize' ); ?>';
60
  }
@@ -64,6 +73,9 @@ function drawTable(critCssArray) {
64
  } else {
65
  type='<?php _e( 'AUTO', 'autoptimize' ); ?>';
66
  typeClass = 'auto';
 
 
 
67
  }
68
  if (file && typeof file == 'string') {
69
  rmark_find=file.split('_');
@@ -76,8 +88,11 @@ function drawTable(critCssArray) {
76
  } else {
77
  target = i.replace(/(woo_|template_|custom_post_|edd_|bp_|bbp_)/,'');
78
  }
79
- jQuery("#rules-list").append("<tr class='rule "+k+"Rule'><td class='type'><span class='badge " + typeClass + "'>" + type + "</span></td><td class='target'>" + target + "</td><td class='file'>" + file + "</td><td class='btn edit'><span class=\"button-secondary\" id=\"" + nodeId + "_edit\"><?php _e( 'Edit', 'autoptimize' ); ?></span></td><td class='btn delete'><span class=\"button-secondary\" id=\"" + nodeId + "_remove\"><?php _e( 'Remove', 'autoptimize' ); ?></span></td></tr>");
80
- jQuery("#" + nodeId + "_edit").click(function(){addEditRow(this.id);});
 
 
 
81
  jQuery("#" + nodeId + "_remove").click(function(){confirmRemove(this.id);});
82
  })
83
  });
55
  hash=nv.hash;
56
  file=nv.file;
57
  filest=nv.file;
58
+ auto_style = '';
59
+ <?php
60
+ $criticalcss = new autoptimizeCriticalCSSBase();
61
+ if ( $criticalcss->is_api_active() ){
62
+ ?>api_active = 1;<?php
63
+ } else {
64
+ ?>api_active = 0;<?php
65
+ }
66
+ ?>
67
  if (file == 0) {
68
  file='<?php _e( 'To be fetched from criticalcss.com in the next queue run...', 'autoptimize' ); ?>';
69
  }
73
  } else {
74
  type='<?php _e( 'AUTO', 'autoptimize' ); ?>';
75
  typeClass = 'auto';
76
+ if ( api_active != 1 ) {
77
+ auto_style = ' style="opacity:.5;" '
78
+ }
79
  }
80
  if (file && typeof file == 'string') {
81
  rmark_find=file.split('_');
88
  } else {
89
  target = i.replace(/(woo_|template_|custom_post_|edd_|bp_|bbp_)/,'');
90
  }
91
+
92
+ jQuery("#rules-list").append("<tr " + auto_style + "class='rule "+k+"Rule'><td class='type'><span class='badge " + typeClass + "'>" + type + "</span></td><td class='target'>" + target + "</td><td class='file'>" + file + "</td><td class='btn edit'><span class=\"button-secondary\" id=\"" + nodeId + "_edit\"><?php _e( 'Edit', 'autoptimize' ); ?></span></td><td class='btn delete'><span class=\"button-secondary\" id=\"" + nodeId + "_remove\"><?php _e( 'Remove', 'autoptimize' ); ?></span></td></tr>");
93
+ if ( typeClass == 'manual' || api_active == 1 ) {
94
+ jQuery("#" + nodeId + "_edit").click(function(){addEditRow(this.id);});
95
+ }
96
  jQuery("#" + nodeId + "_remove").click(function(){confirmRemove(this.id);});
97
  })
98
  });
classes/critcss-inc/admin_settings_rules.php CHANGED
@@ -8,8 +8,13 @@
8
  */
9
  function ao_ccss_render_rules() {
10
  // Attach required arrays.
11
- global $ao_ccss_rules;
12
- global $ao_ccss_types;
 
 
 
 
 
13
  ?>
14
  <ul id="rules-panel">
15
  <li class="itemDetail">
8
  */
9
  function ao_ccss_render_rules() {
10
  // Attach required arrays.
11
+ $criticalcss = autoptimize()->criticalcss();
12
+ $ao_ccss_rules = $criticalcss->get_option( 'rules' );
13
+ $ao_ccss_types = $criticalcss->get_types();
14
+
15
+ if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
16
+ $ao_ccss_types = array( 'No conditionals, check CSS optimization settings.' );
17
+ }
18
  ?>
19
  <ul id="rules-panel">
20
  <li class="itemDetail">
classes/external/php/jsmin.php CHANGED
@@ -332,7 +332,7 @@ class JSMin {
332
  $c = $this->input[$this->inputIndex];
333
  $this->inputIndex += 1;
334
  } else {
335
- $c = null;
336
  }
337
  }
338
  if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
332
  $c = $this->input[$this->inputIndex];
333
  $this->inputIndex += 1;
334
  } else {
335
+ return $c;
336
  }
337
  }
338
  if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
config/autoptimize_404_handler.php CHANGED
@@ -33,12 +33,12 @@ $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
33
  // add multisite logic.
34
  $multisite = false;
35
  if ( true === $multisite ) {
36
- preg_match( '#\/([0-9]{1,3})\/(?:js|css)\/[a-z0-9]*_fallback\.(?:js|css)$#', $fallback_target, $child_site_id );
37
  $ao_root_cache_dir = preg_replace( '#[0-9]*\/$#', '', $ao_cache_dir );
38
  $ao_cache_dir = $ao_root_cache_dir . $child_site_id[1] . '/';
39
  }
40
 
41
- $fallback_path = $ao_cache_dir . $js_or_css . '/autoptimize_fallback.' . $js_or_css;
42
 
43
  if ( $original_request !== $fallback_target && file_exists( $fallback_path ) ) {
44
  // error_log( 'Autoptimize file ' . $original_request . ' not found, using fallback instead.' );
33
  // add multisite logic.
34
  $multisite = false;
35
  if ( true === $multisite ) {
36
+ preg_match( '#\/([0-9]{1,5})\/(?:js|css)\/[a-z0-9]*_fallback\.(?:js|css)$#', $fallback_target, $child_site_id );
37
  $ao_root_cache_dir = preg_replace( '#[0-9]*\/$#', '', $ao_cache_dir );
38
  $ao_cache_dir = $ao_root_cache_dir . $child_site_id[1] . '/';
39
  }
40
 
41
+ $fallback_path = $ao_cache_dir . $js_or_css . '/<!--ao-cachefile-prefix-->_fallback.' . $js_or_css;
42
 
43
  if ( $original_request !== $fallback_target && file_exists( $fallback_path ) ) {
44
  // error_log( 'Autoptimize file ' . $original_request . ' not found, using fallback instead.' );
readme.txt CHANGED
@@ -5,7 +5,7 @@ Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
5
  Requires at least: 4.9
6
  Tested up to: 5.9
7
  Requires PHP: 5.6
8
- Stable tag: 2.9.5.1
9
 
10
  Autoptimize speeds up your website by optimizing JS, CSS, images (incl. lazy-load), HTML and Google Fonts, asyncing JS, removing emoji cruft and more.
11
 
@@ -15,7 +15,7 @@ Autoptimize makes optimizing your site really easy. It can aggregate, minify and
15
  If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [Speed Booster pack](https://wordpress.org/plugins/speed-booster-pack/), [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler), [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/) or if you use Cloudflare [WP Cloudflare Super Page Cache](https://wordpress.org/plugins/wp-cloudflare-page-cache/).
16
 
17
  > <strong>Premium Support</strong><br>
18
- > We provide great [Autoptimize Pro Support and Web Performance Optimization services](https://misc.optimizingmatters.com/partners/?from=pluginpage&partner=autoptimizepro), check out our offering on [https://accelera.autoptimize.com/](https://misc.optimizingmatters.com/partners/?from=pluginpage&partner=autoptimizepro)!
19
 
20
  (Speed-surfing image under creative commons [by LL Twistiti](https://www.flickr.com/photos/twistiti/818552808/))
21
 
@@ -324,6 +324,19 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
324
 
325
  == Changelog ==
326
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  = 2.9.5.1 =
328
  * fix for CSS cache growing too fast when inline CSS with variable selectors from WordPress 5.9 comment blocks is aggregated.
329
 
5
  Requires at least: 4.9
6
  Tested up to: 5.9
7
  Requires PHP: 5.6
8
+ Stable tag: 3.0.0
9
 
10
  Autoptimize speeds up your website by optimizing JS, CSS, images (incl. lazy-load), HTML and Google Fonts, asyncing JS, removing emoji cruft and more.
11
 
15
  If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [Speed Booster pack](https://wordpress.org/plugins/speed-booster-pack/), [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler), [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/) or if you use Cloudflare [WP Cloudflare Super Page Cache](https://wordpress.org/plugins/wp-cloudflare-page-cache/).
16
 
17
  > <strong>Premium Support</strong><br>
18
+ > We provide great [Autoptimize Pro Support and Web Performance Optimization services](https://misc.optimizingmatters.com/partners/?from=partnertab&partner=autoptimizepro), check out our offering on [https://accelera.autoptimize.com/](https://misc.optimizingmatters.com/partners/?from=partnertab&partner=autoptimizepro)!
19
 
20
  (Speed-surfing image under creative commons [by LL Twistiti](https://www.flickr.com/photos/twistiti/818552808/))
21
 
324
 
325
  == Changelog ==
326
 
327
+ = 3.0.0 =
328
+ * fundamental change for new installations: by default Autoptimize will not aggregate JS/ CSS any more (HTTP/2 is ubiquitous and there are other advantages to not aggregating esp. re. inline JS/ CSS and dependancies)
329
+ * new: no API needed any more to create manual critical CSS rules.
330
+ * new: "Remove WordPress blocks CSS" option on the "Extra" tab to remove block- and global styles (and SVG).
331
+ * new: compatibility logic for "edit with elementor", "revolution slider", for non-aggregated inline JS requiring jQuery even if not excluded (= auto-exclude of jQuery) and JS-heavy WordPress blocks (Gutenberg)
332
+ * new: configure an image to be preloaded on a per page/ post basis for better LCP.
333
+ * improvement: defer inline now also allowed if inline JS contains nonce or post_id.
334
+ * improvement: settings export/ import on critical CSS tab now takes into account all Autoptimize settings, not just the critical CSS ones.
335
+ * technical improvement: all criticalCSS classes were refactored, removing use of global variables.
336
+ * technical improvement: automated unit tests on Travis-CI for PHP versions 7.2 to 8.1.
337
+ * fix: stop Divi from clearing Autoptimize's cache [which is pretty counter-productive](https://blog.futtta.be/2018/11/17/warning-divi-purging-autoptimizes-cache/).
338
+ * misc smaller fixes/ improvements, see the [GitHub commit log](https://github.com/futtta/autoptimize/commits/beta)
339
+
340
  = 2.9.5.1 =
341
  * fix for CSS cache growing too fast when inline CSS with variable selectors from WordPress 5.9 comment blocks is aggregated.
342