PluginProbe
Autoptimize / 3.1.6
Autoptimize v3.1.6
2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 All 107 releases
autoptimize / classes / autoptimizeCriticalCSSSettings.php

autoptimizeCriticalCSSSettings.php in Autoptimize 3.1.6, at classes/autoptimizeCriticalCSSSettings.php

443 lines 23.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Critical CSS Options page.
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit;
8 }
9
10 class autoptimizeCriticalCSSSettings {
11 /**
12 * Options.
13 *
14 * @var bool
15 */
16 private $settings_screen_do_remote_http = true;
17
18 /**
19 * Critical CSS object.
20 *
21 * @var object
22 */
23 protected $criticalcss;
24
25 public function __construct() {
26 $this->criticalcss = autoptimize()->criticalcss();
27 $this->settings_screen_do_remote_http = apply_filters( 'autoptimize_settingsscreen_remotehttp', $this->settings_screen_do_remote_http );
28 $this->run();
29 }
30
31 protected function enabled()
32 {
33 return apply_filters( 'autoptimize_filter_show_criticalcss_tabs', true );
34 }
35
36 public function run()
37 {
38 if ( $this->enabled() ) {
39 add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_critcss_tabs' ), 10, 1 );
40 add_action( 'admin_enqueue_scripts', array( $this, 'admin_assets' ) );
41
42 if ( $this->is_multisite_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
43 add_action( 'network_admin_menu', array( $this, 'add_critcss_admin_menu' ) );
44 } else {
45 add_action( 'admin_menu', array( $this, 'add_critcss_admin_menu' ) );
46 }
47
48 $criticalcss_ajax = new autoptimizeCriticalCSSSettingsAjax();
49
50 // if debug logging is off but the file is present, then remove the debug log file.
51 if ( empty( $this->criticalcss->get_option( 'debug' ) ) && file_exists( AO_CCSS_LOG ) ) {
52 unlink( AO_CCSS_LOG );
53 }
54 }
55 }
56
57 public function add_critcss_tabs( $in )
58 {
59 $in = array_merge( $in, array( 'ao_critcss' => apply_filters( 'autoptimize_filter_ccss_tab_text', 'âš¡ ' . __( 'Critical CSS', 'autoptimize' ) ) ) );
60
61 return $in;
62 }
63
64 public function add_critcss_admin_menu()
65 {
66 // Register settings.
67 register_setting( 'ao_ccss_options_group', 'autoptimize_css_defer_inline' );
68 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_rules' );
69 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_additional' );
70 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_queue' );
71 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_viewport' );
72 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_finclude' );
73 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_rtimelimit' );
74 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_noptimize' );
75 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_debug' );
76 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_key' );
77 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_keyst' );
78 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_loggedin' );
79 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_forcepath' );
80 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_deferjquery' );
81 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_domain' );
82 register_setting( 'ao_ccss_options_group', 'autoptimize_ccss_unloadccss' );
83
84 // And add submenu-page.
85 add_submenu_page( '', 'Critical CSS', 'Critical CSS', 'manage_options', 'ao_critcss', array( $this, 'ao_criticalcsssettings_page' ) );
86 }
87
88 public function admin_assets( $hook ) {
89 // Return if plugin is not hooked.
90 if ( 'settings_page_ao_critcss' != $hook && 'admin_page_ao_critcss' != $hook ) {
91 return;
92 }
93
94 // Stylesheets to add.
95 wp_enqueue_style( 'wp-jquery-ui-dialog' );
96 wp_enqueue_style( 'ao-tablesorter', plugins_url( 'critcss-inc/css/ao-tablesorter/style.css', __FILE__ ) );
97 wp_enqueue_style( 'ao-ccss-admin-css', plugins_url( 'critcss-inc/css/admin_styles.css', __FILE__ ) );
98
99 // Scripts to add.
100 wp_enqueue_script( 'jquery-ui-dialog', array( 'jquery' ) );
101 wp_enqueue_script( 'md5', plugins_url( 'critcss-inc/js/md5.min.js', __FILE__ ), null, null, true );
102 wp_enqueue_script( 'tablesorter', plugins_url( 'critcss-inc/js/jquery.tablesorter.min.js', __FILE__ ), array( 'jquery' ), null, true );
103 wp_enqueue_script( 'ao-ccss-admin-license', plugins_url( 'critcss-inc/js/admin_settings.js', __FILE__ ), array( 'jquery' ), null, true );
104 }
105
106 public function ao_criticalcsssettings_page()
107 {
108 // these are not OO yet, simply require for now.
109 require_once( 'critcss-inc/admin_settings_rules.php' );
110 require_once( 'critcss-inc/admin_settings_queue.php' );
111 require_once( 'critcss-inc/admin_settings_key.php' );
112 require_once( 'critcss-inc/admin_settings_adv.php' );
113 require_once( 'critcss-inc/admin_settings_explain.php' );
114
115 $ao_ccss_key = $this->criticalcss->get_option( 'key' );
116 $ao_ccss_keyst = $this->criticalcss->get_option( 'keyst' );
117 $ao_css_defer = $this->criticalcss->get_option( 'css_defer' );
118 $ao_ccss_deferjquery = $this->criticalcss->get_option( 'deferjquery' );
119 $ao_ccss_queue = $this->criticalcss->get_option( 'queue' );
120 $ao_ccss_rules = $this->criticalcss->get_option( 'rules' );
121 $ao_ccss_servicestatus = $this->criticalcss->get_option( 'servicestatus' );
122 $ao_ccss_finclude = $this->criticalcss->get_option( 'finclude' );
123 $ao_ccss_rtimelimit = $this->criticalcss->get_option( 'rtimelimit' );
124 $ao_ccss_debug = $this->criticalcss->get_option( 'debug' );
125 $ao_ccss_noptimize = $this->criticalcss->get_option( 'noptimize' );
126 $ao_css_defer_inline = $this->criticalcss->get_option( 'css_defer_inline' );
127 $ao_ccss_loggedin = $this->criticalcss->get_option( 'loggedin' );
128 $ao_ccss_forcepath = $this->criticalcss->get_option( 'forcepath' );
129 $ao_ccss_domain = $this->criticalcss->get_option( 'domain' );
130 ?>
131 <script>document.title = "Autoptimize: <?php _e( 'Critical CSS', 'autoptimize' ); ?> " + document.title;</script>
132 <div class="wrap">
133 <div id="autoptimize_main">
134 <div id="ao_title_and_button">
135 <h1><?php apply_filters( 'autoptimize_filter_settings_is_pro', false ) ? _e( 'Autoptimize Pro Settings', 'autoptimize' ) : _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
136 </div>
137
138 <?php
139 // Print AO settings tabs.
140 echo autoptimizeConfig::ao_admin_tabs();
141
142 if ( autoptimizeUtils::is_local_server() && isset( $ao_ccss_key ) ) { ?>
143 <div class="notice-warning notice"><p>
144 <?php
145 echo __( 'The Critical CSS service does not work on locally hosted sites or when the server is on a private network.', 'autoptimize' );
146 ?>
147 </p></div>
148 <?php }
149
150 $mkdirresult = $this->criticalcss->create_ao_ccss_dir();
151
152 // Warn if we could not create those files.
153 if ( ( true !== $mkdirresult ) ) {
154 ?>
155 <div class="notice-error notice"><p>
156 <?php
157 _e( 'Could not create the required directory. Make sure the webserver can write to the wp-content/uploads directory.', 'autoptimize' );
158 ?>
159 </p></div>
160 <?php
161 }
162
163 // Check if CSS optimization is on.
164 if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) || 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css_defer' ) ) {
165 ?>
166 <div class="notice-info notice"><p>
167 <?php
168 _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' );
169 ?>
170 </p></div>
171 <?php
172 }
173
174 // Check for "inline & defer CSS" being active in Autoptimize.
175 if ( ! empty( $ao_ccss_key ) && ! $ao_css_defer && empty( $ao_ccss_keyst ) ) {
176 // no keystate so likely in activation-process of CCSS, let's enable "inline & defer CSS" immediately to make things easier!
177 autoptimizeOptionWrapper::update_option( 'autoptimize_css_defer', 'on' );
178 ?>
179 <div class="notice-info notice"><p>
180 <?php
181 _e( 'The "Eliminate render-blocking CSS" option was activated to allow critical CSS to be used.', 'autoptimize' );
182 ?>
183 </p></div>
184 <?php
185 }
186
187 // check if WordPress cron is disabled and warn if so.
188 if ( ! empty( $ao_ccss_key ) && defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON && PAnD::is_admin_notice_active( 'i-know-about-disable-cron-forever' ) ) {
189 ?>
190 <div data-dismissible="i-know-about-disable-cron-forever" class="notice-warning notice is-dismissible"><p>
191 <?php
192 _e( 'WordPress cron (for task scheduling) seems to be disabled. Have a look at <a href="https://wordpress.org/plugins/autoptimize-criticalcss/faq/" target="_blank">the FAQ</a> or the info in the Job Queue instructions if all jobs remain in "N" status and no rules are created.', 'autoptimize' );
193 ?>
194 </p></div>
195 <?php
196 }
197
198 // check if defer jQuery is active and warn if so.
199 if ( 1 == $ao_ccss_deferjquery && PAnD::is_admin_notice_active( 'i-know-about-defer-inline-forever' ) ) {
200 ?>
201 <div data-dismissible="i-know-about-defer-inline-forever" class="notice-warning notice is-dismissible"><p>
202 <?php
203 _e( 'You have "defer jQuery and other non-aggregated JS-files" active (under Advanced Settings), but that functionality is deprecated and will be removed in the next major version of Autoptimize. Consider using the new "Do not aggregate but defer" and "Also defer inline JS" options on the main settings page instead.', 'autoptimize' );
204 ?>
205 </p></div>
206 <?php
207 }
208
209 // warn if it looks as though the queue processing job looks isn't running
210 // but store result in transient as to not to have to go through 2 arrays each and every time.
211 $_warn_cron = get_transient( 'ao_ccss_cronwarning' );
212 if ( ! empty( $ao_ccss_key ) && false === $_warn_cron ) {
213 $_jobs_all_new = true;
214 $_oldest_job_timestamp = microtime( true ); // now.
215 $_jobs_too_old = true;
216
217 // go over queue array.
218 if ( empty( $ao_ccss_queue ) ) {
219 // no jobs, then no warning.
220 $_jobs_all_new = false;
221 } else {
222 foreach ( $ao_ccss_queue as $job ) {
223 if ( $job['jctime'] < $_oldest_job_timestamp ) {
224 // we need to catch the oldest job's timestamp.
225 $_oldest_job_timestamp = $job['jctime'];
226 }
227
228 if ( 'NEW' !== $job['jqstat'] && 'firstrun' !== $job['ljid'] ) {
229 // we have a non-"NEW" job which is not our pending firstrun job either, break the loop.
230 $_jobs_all_new = false;
231 break;
232 }
233 }
234 }
235
236 // is the oldest job too old (4h)?
237 if ( $_oldest_job_timestamp > microtime( true ) - 60 * 60 * 4 ) {
238 $_jobs_too_old = false;
239 }
240
241 if ( $_jobs_all_new && ! $this->ao_ccss_has_autorules() && $_jobs_too_old ) {
242 $_warn_cron = 'on';
243 $_transient_multiplier = 1; // store for 1 hour.
244 } else {
245 $_warn_cron = 'off';
246 $_transient_multiplier = 4; // store for 4 hours.
247 }
248 // and set transient.
249 set_transient( 'ao_ccss_cronwarning', $_warn_cron, $_transient_multiplier * HOUR_IN_SECONDS );
250 }
251
252 if ( ! empty( $ao_ccss_key ) && 'on' == $_warn_cron && PAnD::is_admin_notice_active( 'i-know-about-cron-1' ) ) {
253 ?>
254 <div data-dismissible="i-know-about-cron-1" class="notice-warning notice is-dismissible"><p>
255 <?php
256 _e( 'It looks like there might be a problem with WordPress cron (task scheduling). Have a look at <a href="https://blog.futtta.be/2023/03/17/how-to-fix-autoptimize-critical-css-cron-issue/" target="_blank">the FAQ</a> or the info in the Job Queue instructions if all jobs remain in "N" status and no rules are created.', 'autoptimize' );
257 ?>
258 </p></div>
259 <?php
260 } elseif ( ! empty( $ao_ccss_key ) && '2' == $ao_ccss_keyst && 'on' != $_warn_cron && ! $this->ao_ccss_has_autorules() ) {
261 ?>
262 <div class="notice-success notice"><p>
263 <?php
264 _e( 'Great, Autoptimize will now automatically start creating new critical CSS rules, you should see those appearing below in the next couple of hours.', 'autoptimize' );
265 echo ' ';
266 _e( 'In the meantime you might want to <strong>edit default rule CSS now</strong>, to avoid all CSS being inlined when no (applicable) rules are found.', 'autoptimize' );
267 ?>
268 </p></div>
269 <?php
270 }
271
272 // warn if service is down.
273 if ( ! empty( $ao_ccss_key ) && ! empty( $ao_ccss_servicestatus ) && is_array( $ao_ccss_servicestatus ) && 'down' === $ao_ccss_servicestatus['critcss']['status'] ) {
274 ?>
275 <div class="notice-warning notice"><p>
276 <?php
277 _e( 'The critical CSS service has been reported to be down. Although no new rules will be created for now, this does not prevent existing rules from being applied.', 'autoptimize' );
278 ?>
279 </p></div>
280 <?php
281 }
282
283 // warn if too many rules (based on length of ao_ccss_rules option) as that might cause issues at e.g. wpengine
284 // see https://wpengine.com/support/database-optimization-best-practices/#Autoloaded_Data .
285 $_raw_rules_length = strlen( get_option( 'autoptimize_ccss_rules', '' ) );
286 if ( $_raw_rules_length > apply_filters( 'autoptimize_ccss_rules_length_warning', 500000 ) ) {
287 ?>
288 <div class="notice-warning notice"><p>
289 <?php
290 _e( 'It looks like the amount of Critical CSS rules is very high, it is recommended to reconfigure Autoptimize (e.g. by manually creating broader rules) to ensure less rules are created.', 'autoptimize' );
291 ?>
292 </p></div>
293 <?php
294 }
295
296 // Settings Form.
297 ?>
298 <form id="settings" method="post" action="options.php">
299 <?php
300 settings_fields( 'ao_ccss_options_group' );
301
302 // Get API key status.
303 $key = $this->criticalcss->key_status( true );
304
305 if ( $this->is_multisite_network_admin() ) {
306 ?>
307 <ul id="key-panel">
308 <li class="itemDetail">
309 <?php
310 // translators: the placesholder is for a line of code in wp-config.php.
311 echo sprintf( __( '<p>Critical CSS settings cannot be set at network level as critical CSS is specific to each sub-site.</p><p>You can however provide the critical CSS API key for use by all sites by adding this your wp-config.php as %s</p>', 'autoptimize' ), '<br/><code>define(\'AUTOPTIMIZE_CRITICALCSS_API_KEY\', \'eyJhbGmorestringsherexHa7MkOQFtDFkZgLmBLe-LpcHx4\');</code>' );
312 ?>
313 </li>
314 </ul>
315 <?php
316 } else {
317 if ( 'valid' == $key['status'] || ( defined( 'AO_PRO_VERSION' ) && has_filter( 'autoptimize_filter_ccss_key' ) ) ) {
318 // If key status is valid, render other panels.
319 // Render rules section.
320 ao_ccss_render_rules();
321 // Render queue section.
322 ao_ccss_render_queue();
323 // Render advanced panel.
324 ao_ccss_render_adv();
325 } else {
326 if ( apply_filters( 'autoptimize_filter_ccss_rules_without_api', true ) ) {
327 // Render rules section for manual rules.
328 ao_ccss_render_rules();
329 } else {
330 echo "<input class='hidden' name='autoptimize_ccss_rules' value='" . json_encode( $ao_ccss_rules, JSON_FORCE_OBJECT ) . "'>";
331 }
332
333 // But if key is other than valid, add hidden fields to persist settings when submitting form
334 // Show explanation of why and how to get a API key.
335 ao_ccss_render_explain();
336
337 // Get viewport size.
338 $viewport = $this->criticalcss->viewport();
339
340 // Add hidden fields.
341 echo "<input class='hidden' name='autoptimize_ccss_queue' value='" . json_encode( $ao_ccss_queue, JSON_FORCE_OBJECT ) . "'>";
342 echo '<input class="hidden" name="autoptimize_ccss_viewport[w]" value="' . esc_attr( $viewport['w'] ) . '">';
343 echo '<input class="hidden" name="autoptimize_ccss_viewport[h]" value="' . esc_attr( $viewport['h'] ) . '">';
344 echo '<input class="hidden" name="autoptimize_ccss_finclude" value="' . esc_attr( $ao_ccss_finclude ) . '">';
345 echo '<input class="hidden" name="autoptimize_ccss_rtimelimit" value="' . esc_attr( $ao_ccss_rtimelimit ) . '">';
346 echo '<input class="hidden" name="autoptimize_ccss_debug" value="' . esc_attr( $ao_ccss_debug ) . '">';
347 echo '<input class="hidden" name="autoptimize_ccss_noptimize" value="' . esc_attr( $ao_ccss_noptimize ) . '">';
348 echo '<input class="hidden" name="autoptimize_css_defer_inline" value="' . esc_attr( $ao_css_defer_inline ) . '">';
349 echo '<input class="hidden" name="autoptimize_ccss_loggedin" value="' . esc_attr( $ao_ccss_loggedin ) . '">';
350 echo '<input class="hidden" name="autoptimize_ccss_forcepath" value="' . esc_attr( $ao_ccss_forcepath ) . '">';
351 echo '<input class="hidden" name="autoptimize_ccss_domain" id="autoptimize_ccss_domain" value="' . esc_attr( $ao_ccss_domain ) . '">';
352 }
353 // Render key panel unconditionally.
354 ao_ccss_render_key( $ao_ccss_key, $key['status'], $key['stmsg'], $key['msg'], $key['color'] );
355 ?>
356 <p class="submit left">
357 <input type="submit" class="button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" />
358 </p>
359 <?php
360 }
361 ?>
362 </form>
363 <script>
364 jQuery("form#settings").submit(function(){
365 var input = jQuery("#autoptimize_ccss_domain");
366 input.val(rot(input.val(), 13));
367 });
368 // rot JS from http://stackoverflow.com/a/617685/987044 .
369 function rot(domainstring, itype) {
370 return domainstring.toString().replace(/[a-zA-Z]/g, function (letter) {
371 return String.fromCharCode((letter <= 'Z' ? 90 : 122) >= (letter = letter.charCodeAt(0) + itype) ? letter : letter - 26);
372 });
373 }
374 </script>
375 <form id="importSettingsForm"<?php if ( $this->is_multisite_network_admin() ) { echo ' class="hidden"'; } // @codingStandardsIgnoreLine ?>>
376 <span id="exportSettings" class="button-secondary"><?php _e( 'Export Settings', 'autoptimize' ); ?></span>
377 <input class="button-secondary" id="importSettings" type="button" value="<?php _e( 'Import Settings', 'autoptimize' ); ?>" onclick="upload();return false;" />
378 <input class="button-secondary" id="settingsfile" name="settingsfile" type="file" />
379 </form>
380 <div id="importdialog"></div>
381 </div><!-- /#autoptimize_main -->
382 </div><!-- /#wrap -->
383 <?php
384 if ( ! $this->is_multisite_network_admin() ) {
385 // Include debug panel if debug mode is enable.
386 if ( $ao_ccss_debug ) {
387 ?>
388 <div id="debug">
389 <?php
390 // Include debug panel.
391 include( 'critcss-inc/admin_settings_debug.php' );
392 ?>
393 </div><!-- /#debug -->
394 <?php
395 }
396 echo '<script>';
397 include( 'critcss-inc/admin_settings_rules.js.php' );
398 include( 'critcss-inc/admin_settings_queue.js.php' );
399 include( 'critcss-inc/admin_settings_impexp.js.php' );
400 echo '</script>';
401 }
402 }
403
404 public function ao_ccss_has_autorules() {
405 static $_has_auto_rules = null;
406
407 if ( null === $_has_auto_rules ) {
408 $ao_ccss_rules = $this->criticalcss->get_option( 'rules' );
409 $_has_auto_rules = false;
410 if ( ! empty( $ao_ccss_rules ) ) {
411 foreach ( array( 'types', 'paths' ) as $_typat ) {
412 foreach ( $ao_ccss_rules[ $_typat ] as $rule ) {
413 if ( ! empty( $rule['hash'] ) ) {
414 // we have at least one AUTO job, so all is fine.
415 $_has_auto_rules = true;
416 break;
417 }
418 }
419 if ( $_has_auto_rules ) {
420 break;
421 }
422 }
423 }
424 }
425
426 return $_has_auto_rules;
427 }
428
429 public static function is_multisite_network_admin() {
430 static $_multisite_network_admin = null;
431
432 if ( null === $_multisite_network_admin ) {
433 if ( is_multisite() && is_network_admin() ) {
434 $_multisite_network_admin = true;
435 } else {
436 $_multisite_network_admin = false;
437 }
438 }
439
440 return $_multisite_network_admin;
441 }
442 }
443