PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.0
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
← All changes | wp-optimize.php +758 -496 3.2.7 → 4.7.0 View file →
@@ -1,13 +1,15 @@
1 1 <?php
2 2 /**
3 3 Plugin Name: WP-Optimize - Clean, Compress, Cache
4 -Plugin URI: https://getwpo.com
4 +Plugin URI: https://teamupdraft.com/wp-optimize
5 5 Description: WP-Optimize makes your site fast and efficient. It cleans the database, compresses images and caches pages. Fast sites attract more traffic and users.
6 -Version: 3.2.7
6 +Version: 4.7.0
7 +Requires at least: 4.9
8 +Requires PHP: 7.2
7 9 Update URI: https://wordpress.org/plugins/wp-optimize/
8 -Author: David Anderson, Ruhani Rabin, Team Updraft
9 -Author URI: https://updraftplus.com
10 +Author: TeamUpdraft, DavidAnderson
11 +Author URI: https://teamupdraft.com/wp-optimize?utm_source=wpo-wp-dir&utm_medium=referral&utm_campaign=plugin-dir&utm_content=teamupdraft&utm_creative_format=author
10 12 Text Domain: wp-optimize
11 13 Domain Path: /languages
12 14 License: GPLv2 or later
13 15 */
@@ -15,56 +17,81 @@
15 17 if (!defined('ABSPATH')) die('No direct access allowed');
16 18
17 19 // Check to make sure if WP_Optimize is already call and returns.
18 20 if (!class_exists('WP_Optimize')) :
19 -define('WPO_VERSION', '3.2.7');
21 +define('WPO_VERSION', '4.7.0');
20 22 define('WPO_PLUGIN_URL', plugin_dir_url(__FILE__));
21 23 define('WPO_PLUGIN_MAIN_PATH', plugin_dir_path(__FILE__));
24 +define('WPO_PLUGIN_SLUG', plugin_basename(__FILE__));
22 25 define('WPO_PREMIUM_NOTIFICATION', false);
23 -define('WPO_MINIFY_PHP_VERSION_MET', version_compare(PHP_VERSION, '5.4', '>=') ? true : false);
26 +define('WPO_REQUIRED_PHP_VERSION', '7.2');
27 +define('WPO_REQUIRED_WP_VERSION', '4.9');
28 +if (!defined('WPO_USE_WEBP_CONVERSION')) define('WPO_USE_WEBP_CONVERSION', true);
29 +require_once(WPO_PLUGIN_MAIN_PATH.'includes/fragments/input-processing.php');
24 30
25 31 class WP_Optimize {
26 32
27 - public $premium_version_link = 'https://getwpo.com/buy/';
33 + public $premium_version_link = 'https://teamupdraft.com/wp-optimize/pricing/?utm_source=wpo-plugin&utm_medium=referral&utm_campaign=paac&utm_creative_format=overlay';
28 34
29 35 private $template_directories;
30 36
31 37 protected static $_instance = null;
32 38
33 - protected static $_optimizer_instance = null;
39 + protected $_cache_init_status = null;
34 40
35 - protected static $_options_instance = null;
41 + /**
42 + * An array of schedule types
43 + *
44 + * @param boolean $placeholder Determines whether a placeholder select option should be included or not
45 + *
46 + * @return array An array of schedule types
47 + */
48 + public static function get_schedule_types($placeholder = false) {
49 + $schedule_types = array(
50 + 'wpo_daily' => __('Daily', 'wp-optimize'),
51 + 'wpo_weekly' => __('Weekly', 'wp-optimize'),
52 + 'wpo_fortnightly' => __('Fortnightly', 'wp-optimize'),
53 + 'wpo_monthly' => __('Monthly', 'wp-optimize'),
54 + );
36 55
37 - protected static $_minify_instance = null;
56 + $schedule_types = apply_filters('wp_optimize_schedule_types', $schedule_types);
57 + if (true === $placeholder) {
58 + $schedule_types = array_merge(array('' => __('Select schedule', 'wp-optimize')), $schedule_types);
59 + }
60 + return $schedule_types;
61 + }
38 62
39 - protected static $_notices_instance = null;
40 -
41 - protected static $_logger_instance = null;
42 -
43 - protected static $_browser_cache = null;
44 -
45 - protected static $_db_info = null;
46 -
47 - protected static $_cache = null;
48 -
49 - protected static $_gzip_compression = null;
50 -
51 63 /**
52 64 * Class constructor
53 65 */
54 66 public function __construct() {
67 +
55 68 spl_autoload_register(array($this, 'loader'));
56 69
70 + // Don't process when accessing without normal WordPress core loading.
71 + if (!defined('WPINC')) return;
72 +
73 + $bypass_instance = $this->get_bypass_instance();
74 + if ($bypass_instance->should_bypass()) {
75 + // Show a bypass notice in the admin bar
76 + $bypass_instance->show_admin_notice();
77 + if (!headers_sent()) {
78 + header('WPO-Bypass-Mode: active');
79 + }
80 + return;
81 + }
82 +
57 83 // Checks if premium is installed along with plugins needed.
58 84 add_action('plugins_loaded', array($this, 'plugins_loaded'), 1);
59 85
60 - register_activation_hook(__FILE__, 'wpo_activation_actions');
61 - register_deactivation_hook(__FILE__, 'wpo_deactivation_actions');
62 - register_uninstall_hook(__FILE__, 'wpo_uninstall_actions');
63 -
86 + register_activation_hook(__FILE__, array('WPO_Activation', 'actions'));
87 + register_deactivation_hook(__FILE__, array('WPO_Deactivation', 'actions'));
88 + register_uninstall_hook(__FILE__, array('WPO_Uninstall', 'actions'));
89 +
90 + WPO_Page_Optimizer::instance()->maybe_initialise();
91 +
64 92 $this->load_admin();
65 93 add_action('admin_init', array($this, 'admin_init'));
66 - add_action('admin_bar_menu', array($this, 'cache_admin_bar'), 100, 1);
67 94
68 95 add_filter("plugin_action_links_".plugin_basename(__FILE__), array($this, 'plugin_settings_link'));
69 96 add_action('wpo_cron_event2', array($this, 'cron_action'));
70 97 add_filter('cron_schedules', array($this, 'cron_schedules'));
@@ -70,22 +97,24 @@
70 97 add_filter('cron_schedules', array($this, 'cron_schedules'));
71 98
72 99 if (!$this->get_options()->get_option('installed-for', false)) $this->get_options()->update_option('installed-for', time());
73 100
74 - if (!self::is_premium()) {
75 - add_action('auto_option_settings', array($this->get_options(), 'auto_option_settings'));
76 - }
101 + add_action('admin_footer-upload.php', array($this, 'add_smush_popup_template'));
77 102
78 103 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
79 104
80 105 add_action('wp_enqueue_scripts', array($this, 'frontend_enqueue_scripts'));
81 106
82 - add_action('wp_ajax_wp_optimize_ajax', array($this, 'wp_optimize_ajax_handler'));
107 + if ($this->get_options()->get_option('404_detector', 0)) {
108 + WP_Optimize_Performance::get_instance($this->get_404_detector())->hook_404_handler();
109 + }
83 110
111 + $this->load_ajax_handler();
112 +
84 113 // Show update to Premium notice for non-premium multisite.
85 114 add_action('wpo_additional_options', array($this, 'show_multisite_update_to_premium_notice'));
86 115
87 - // Action column (show repair button if need).
116 + // Action column (show repair button, if needed).
88 117 add_filter('wpo_tables_list_additional_column_data', array($this, 'tables_list_additional_column_data'), 15, 2);
89 118
90 119 /**
91 120 * Add action for display Images > Compress images tab.
@@ -91,22 +120,20 @@
91 120 * Add action for display Images > Compress images tab.
92 121 */
93 122 add_action('wp_optimize_admin_page_wpo_images_smush', array($this, 'admin_page_wpo_images_smush'));
94 123
95 - include_once(WPO_PLUGIN_MAIN_PATH.'includes/updraftcentral.php');
124 + include_once(WPO_PLUGIN_MAIN_PATH.'includes/updraftcentral/updraftcentral.php');
96 125
97 126 include_once(WPO_PLUGIN_MAIN_PATH.'includes/backward-compatibility-functions.php');
98 127
99 128 register_shutdown_function(array($this, 'log_fatal_errors'));
100 129
101 - $this->schedule_plugin_cron_tasks();
102 -
103 130 add_action('wpo_admin_before_closing_wrap', array($this, 'load_modal_template'), 20);
104 131
105 132 add_action('upgrader_process_complete', array($this, 'detect_active_plugins_and_themes_updates'), 10, 2);
106 133
107 134 $import_done_hooks = array(
108 - 'import_end', // wordpress importer
135 + 'import_end', // WordPress importer
109 136 'pmxi_after_xml_import', // wp all import
110 137 );
111 138
112 139 $db_update_hooks = apply_filters('wp_optimize_db_update_hooks', $import_done_hooks);
@@ -114,25 +141,117 @@
114 141 foreach ($db_update_hooks as $hook) {
115 142 add_action($hook, array($this, 'maybe_schedule_update_record_count_event'));
116 143 }
117 144 add_action('wpo_update_record_count_event', array($this->get_db_info(), 'wpo_update_record_count'));
145 +
146 + add_action('wpo_reset_stats_counter', array($this, 'reset_stats_counters'));
147 +
148 + $this->schedule_reset_stats_counters_event();
149 +
118 150 }
119 151
120 152 /**
121 - * Auto-loads classes.
153 + * Reset stats counter at the start of each month
154 + */
155 + public function reset_stats_counters() {
156 + // Save current total cleaned as previous month data
157 + $options = $this->get_options();
158 + $last_month_total_cleaned = $options->get_option('total-cleaned-current-month');
159 + $options->update_option('total-cleaned-previous-month', $last_month_total_cleaned);
160 + $options->update_option('total-cleaned-current-month', '0');
161 + $this->schedule_reset_stats_counters_event();
162 + }
163 +
164 + /**
165 + * Schedule the reset of stat's counter at the start of each month
166 + */
167 + private function schedule_reset_stats_counters_event() {
168 + if (!wp_next_scheduled('wpo_reset_stats_counter')) {
169 + $next_reset = strtotime('first day of next month midnight');
170 + wp_schedule_single_event($next_reset, 'wpo_reset_stats_counter');
171 + }
172 + }
173 +
174 + /**
175 + * Returns Onboarding class instance
122 176 *
177 + * @return WPO_Onboarding
178 + */
179 + public function get_onboarding() {
180 + return WPO_Onboarding::instance();
181 + }
182 +
183 + /**
184 + * Check if minimum requirements for WP-Optimize is met or not.
185 + *
186 + * @return bool
187 + */
188 + public function is_minimum_requirement_met() {
189 + global $wp_version;
190 + return (version_compare(WPO_REQUIRED_PHP_VERSION, PHP_VERSION, '<=') && version_compare(WPO_REQUIRED_WP_VERSION, $wp_version, '<='));
191 + }
192 +
193 + /**
194 + * Add admin notice about minimum server requirements.
195 + */
196 + public function add_notice_minimum_requirements_not_met() {
197 + add_action('admin_notices', array($this, 'output_minimum_requirements_notice'));
198 + }
199 +
200 + /**
201 + * Deactivate the plugin programmatically
202 + */
203 + public function deactivate_plugin() {
204 + if (!function_exists('deactivate_plugins')) {
205 + require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
206 + }
207 + deactivate_plugins(plugin_basename(__FILE__));
208 + }
209 +
210 + /**
211 + * Die after stating minimum requirements
212 + */
213 + public function die_minimum_requirement_not_met() {
214 + $message = $this->get_minimum_requirements_notice_message();
215 + $message .= ' <a href="'.esc_url(admin_url('plugins.php')).'">'.esc_html__('Back to Plugins.', 'wp-optimize').'</a>';
216 + wp_die(wp_kses_post($message));
217 + }
218 +
219 + /**
220 + * Autoloads classes.
221 + *
123 222 * @param string $class_name The name of the class.
124 223 */
125 - private function loader($class_name) {
224 + public function loader($class_name) {
126 225 $dirs = $this->get_class_directories();
127 226
128 227 foreach ($dirs as $dir) {
129 - $class_file = WPO_PLUGIN_MAIN_PATH . $dir . '/class-' . str_replace('_', '-', strtolower($class_name)) . '.php';
228 + $class_file = WPO_PLUGIN_MAIN_PATH . trailingslashit($dir) . 'class-' . str_replace('_', '-', strtolower($class_name)) . '.php';
130 229 if (file_exists($class_file)) {
131 230 require_once($class_file);
132 231 return;
133 232 }
233 +
234 + $interface_file = WPO_PLUGIN_MAIN_PATH . trailingslashit($dir) . 'interface-' . str_replace('_', '-', strtolower($class_name)) . '.php';
235 + if (file_exists($interface_file)) {
236 + require_once($interface_file);
237 + return;
238 + }
134 239 }
240 +
241 + // Include PHP Minify - https://github.com/matthiasmullie/minify
242 + if (strpos($class_name, 'MatthiasMullie') !== false) {
243 + $class_name_parts = explode('\\', $class_name);
244 + $class_file = WPO_PLUGIN_MAIN_PATH.'vendor/'.strtolower($class_name_parts[0]).'/'.strtolower(preg_replace('/([a-z])([A-Z])/', '$1-$2', $class_name_parts[1])).'/src/'.implode('/', array_slice($class_name_parts, 2)).'.php';
245 + if (file_exists($class_file)) {
246 + require_once($class_file);
247 + return;
248 + }
249 + }
250 +
251 + if ('Minify_HTML' === $class_name) {
252 + require_once WPO_PLUGIN_MAIN_PATH.'vendor/mrclay/minify/lib/Minify/HTML.php';
253 + }
135 254 }
136 255
137 256 /**
138 257 * Returns an array of class directories
@@ -141,12 +260,19 @@
141 260 */
142 261 private function get_class_directories() {
143 262 return array(
144 263 'cache',
264 + 'compatibility',
145 265 'includes',
266 + 'includes/tables',
267 + 'includes/list-tables',
268 + 'includes/dashboard',
269 + 'includes/gravatars',
270 + 'includes/lcp',
146 271 'minify',
147 272 'optimizations',
148 273 'webp',
274 + 'includes/updraftcentral',
149 275 );
150 276 }
151 277
152 278 /**
@@ -156,8 +282,17 @@
156 282 $this->get_admin_instance();
157 283 }
158 284
159 285 /**
286 + * Returns Bypass class instance
287 + *
288 + * @return WP_Optimize_Bypass
289 + */
290 + private function get_bypass_instance() {
291 + return WP_Optimize_Bypass::instance();
292 + }
293 +
294 + /**
160 295 * Returns Admin class instance
161 296 *
162 297 * @return WP_Optimize_Admin
163 298 */
@@ -178,19 +313,31 @@
178 313 $should_purge_cache = false;
179 314 $skin = $upgrader_object->skin;
180 315 if ('plugin' === $options['type']) {
181 316 // A plugin is updated using the default update system (upgrader_overwrote_package is used for the upload method)
182 - if (property_exists($skin, 'plugin_active') && $skin->plugin_active) {
183 - $should_purge_cache = true;
317 + $plugins = array();
318 + if (!empty($options['plugin'])) {
319 + $plugins = array($options['plugin']);
320 + } elseif (!empty($options['plugins']) && is_array($options['plugins'])) {
321 + $plugins = $options['plugins'];
184 322 }
323 +
324 + foreach ($plugins as $plugin) {
325 + if (!empty($plugin) && ! is_wp_error($upgrader_object->result)) {
326 + if (is_plugin_active($plugin)) {
327 + $should_purge_cache = true;
328 + break;
329 + }
330 + }
331 + }
185 332 } elseif ('theme' === $options['type']) {
186 333 $active_theme = get_stylesheet();
187 334 $parent_theme = get_template();
188 335 // A theme is updated using the upload system
189 - if (isset($options['action']) && 'install' === $options['action'] && 'update-theme' === $skin->options['overwrite']) {
336 + if (isset($options['action']) && 'install' === $options['action'] && isset($skin->options['overwrite']) && 'update-theme' === $skin->options['overwrite']) {
190 337 $updated_theme = $upgrader_object->result['destination_name'];
191 338 // Check if the theme is in use
192 - if ($active_theme == $updated_theme || $parent_theme == $updated_theme) {
339 + if ($active_theme === $updated_theme || $parent_theme === $updated_theme) {
193 340 $should_purge_cache = true;
194 341 }
195 342 // A theme is updated using the classic update system
196 343 } elseif (isset($options['action']) && 'update' === $options['action'] && isset($options['themes']) && is_array($options['themes'])) {
@@ -218,10 +365,13 @@
218 365 }
219 366
220 367 public function admin_page_wpo_images_smush() {
221 368 $options = Updraft_Smush_Manager()->get_smush_options();
222 - $custom = 100 != $options['image_quality'] && 60 != $options['image_quality'] ? true : false;
223 - $this->include_template('images/smush.php', false, array('smush_options' => $options, 'custom' => $custom, 'does_server_allows_local_webp_conversion' => $this->does_server_allows_local_webp_conversion()));
369 + $custom = 90 >= $options['image_quality'] && 65 <= $options['image_quality'];
370 + $sites = $this->get_sites();
371 + $compression_server_hint = Updraft_Smush_Manager()->get_compression_server_hint();
372 + $this->include_template('images/smush.php', false, array('smush_options' => $options, 'custom' => $custom, 'sites' => $sites, 'does_server_allow_local_webp_conversion' => $this->get_server_compatibility_instance()->does_server_allow_local_webp_conversion(), 'compression_server_hint' => $compression_server_hint));
373 + $this->add_smush_popup_template();
224 374 }
225 375
226 376 public static function instance() {
227 377 if (empty(self::$_instance)) {
@@ -229,92 +379,109 @@
229 379 }
230 380 return self::$_instance;
231 381 }
232 382
233 - public static function get_optimizer() {
234 - if (empty(self::$_optimizer_instance)) {
235 - self::$_optimizer_instance = new WP_Optimizer();
383 + public function get_optimizer() {
384 + return WP_Optimizer::instance();
385 + }
386 +
387 + /**
388 + * Adds 3rd party plugin compatibilities.
389 + */
390 + public function load_compatibilities() {
391 + global $wp_version;
392 +
393 + if (class_exists('Polylang')) {
394 + WPO_Polylang_Compatibility::instance();
236 395 }
237 - return self::$_optimizer_instance;
396 +
397 + if (version_compare($wp_version, '5.2', '<')) {
398 + WPO_WP_Kses_Compatibility::instance();
399 + }
400 +
401 + WPO_Page_Builder_Compatibility::instance();
402 + WPO_KD_Submissions_Compatibility::instance();
403 +
404 + if (class_exists('Custom_Permalinks')) {
405 + WPO_Custom_Permalink_Compatibility::instance();
406 + }
407 +
408 + if (class_exists('TRP_Translate_Press')) {
409 + WPO_TranslatePress_Compatibility::instance();
410 + }
411 +
412 + do_action('wpo_load_compatibilities');
238 413 }
239 414
240 415 /**
241 - * Get and instanciate WP_Optimize_Minify
416 + * Get and instantiate WP_Optimize_Minify
242 417 *
243 418 * @return WP_Optimize_Minify
244 419 */
245 420 public function get_minify() {
246 - if (empty(self::$_minify_instance)) {
247 - self::$_minify_instance = new WP_Optimize_Minify();
248 - }
249 - return self::$_minify_instance;
421 + return WP_Optimize_Minify::instance();
250 422 }
251 423
252 - public static function get_options() {
253 - if (empty(self::$_options_instance)) {
254 - self::$_options_instance = new WP_Optimize_Options();
255 - }
256 - return self::$_options_instance;
424 + public function get_options() {
425 + return WP_Optimize_Options::instance();
257 426 }
258 427
259 - public static function get_notices() {
260 - if (empty(self::$_notices_instance)) {
261 - self::$_notices_instance = new WP_Optimize_Notices();
262 - }
263 - return self::$_notices_instance;
428 + public function get_notices() {
429 + return WP_Optimize_Notices::instance();
264 430 }
265 431
266 432 /**
267 - * Returns instance if WPO_Page_Cache class.
433 + * Get and instantiate WP_Optimize_Delay_JS
268 434 *
269 - * @return WPO_Page_Cache
435 + * @return WP_Optimize_Delay_JS
270 436 */
271 - public function get_page_cache() {
272 - return WPO_Page_Cache::instance();
437 + private function get_delay_js() {
438 + return WP_Optimize_Delay_JS::instance();
273 439 }
274 440
275 441 /**
276 - * Returns instance if WP_Optimize_WebP class.
442 + * Returns instance of WPO_Page_Optimizer class.
277 443 *
278 - * @return WP_Optimize_WebP
444 + * @return WPO_Page_Optimizer
279 445 */
280 - public function get_webp_instance() {
281 - return WP_Optimize_WebP::get_instance();
446 + public function get_page_optimizer() {
447 + return WPO_Page_Optimizer::instance();
282 448 }
283 449
284 450 /**
285 - * Detects if the platform is Kinsta or not
451 + * Returns instance 404 Detector Cron class.
286 452 *
287 - * @return bool Returns true if it is Kinsta platform, otherwise returns false
453 + * @return WP_Optimize_404_Detector_Cron
288 454 */
289 - private function is_kinsta() {
290 - return isset($_SERVER['KINSTA_CACHE_ZONE']);
455 + private function get_404_detector_cron() {
456 + return WP_Optimize_404_Detector_Cron::get_instance();
291 457 }
292 458
293 459 /**
294 - * Detects whether the server handles cache. eg. Nginx cache
460 + * Returns instance of WPO_Page_Cache class.
461 + *
462 + * @return WPO_Page_Cache
295 463 */
296 - public function does_server_handles_cache() {
297 - return $this->is_kinsta();
464 + public function get_page_cache() {
465 + return WPO_Page_Cache::instance();
298 466 }
299 467
300 468 /**
301 - * Detects whether the server supports table optimization.
469 + * Returns instance of WP_Optimize_WebP class.
302 470 *
303 - * Some servers prevent table optimization
304 - * because InnoDB engine does not optimize table
305 - * instead it drops tables and recreate them
306 - * which results in elevated disk write operations
471 + * @return WP_Optimize_WebP
307 472 */
308 - public function does_server_allows_table_optimization() {
309 - return !$this->is_kinsta();
473 + public function get_webp_instance() {
474 + return WP_Optimize_WebP::get_instance();
310 475 }
311 476
312 477 /**
313 - * Detects whether the server supports local webp conversion tools
478 + * Returns instance of WP_Optimize_Server_Compatibility class.
479 + *
480 + * @return WP_Optimize_Server_Compatibility
314 481 */
315 - private function does_server_allows_local_webp_conversion() {
316 - return !$this->is_kinsta();
482 + public function get_server_compatibility_instance(): WP_Optimize_Server_Compatibility {
483 + return WP_Optimize_Server_Compatibility::get_instance();
317 484 }
318 485
319 486 /**
320 487 * Create instance of WP_Optimize_Browser_Cache.
@@ -320,13 +487,10 @@
320 487 * Create instance of WP_Optimize_Browser_Cache.
321 488 *
322 489 * @return WP_Optimize_Browser_Cache
323 490 */
324 - public static function get_browser_cache() {
325 - if (empty(self::$_browser_cache)) {
326 - self::$_browser_cache = new WP_Optimize_Browser_Cache();
327 - }
328 - return self::$_browser_cache;
491 + public function get_browser_cache() {
492 + return WP_Optimize_Browser_Cache::instance();
329 493 }
330 494
331 495 /**
332 496 * Returns WP_Optimize_Database_Information instance.
@@ -333,12 +497,9 @@
333 497 *
334 498 * @return WP_Optimize_Database_Information
335 499 */
336 500 public function get_db_info() {
337 - if (empty(self::$_db_info)) {
338 - self::$_db_info = new WP_Optimize_Database_Information();
339 - }
340 - return self::$_db_info;
501 + return WP_Optimize_Database_Information::instance();
341 502 }
342 503
343 504 /**
344 505 * Returns instance of WP_Optimize_Gzip_Compression.
@@ -344,13 +505,10 @@
344 505 * Returns instance of WP_Optimize_Gzip_Compression.
345 506 *
346 507 * @return WP_Optimize_Gzip_Compression
347 508 */
348 - static public function get_gzip_compression() {
349 - if (empty(self::$_gzip_compression)) {
350 - self::$_gzip_compression = new WP_Optimize_Gzip_Compression();
351 - }
352 - return self::$_gzip_compression;
509 + public function get_gzip_compression() {
510 + return WP_Optimize_Gzip_Compression::instance();
353 511 }
354 512
355 513 /**
356 514 * Create instance of WP_Optimize_Htaccess.
@@ -366,13 +524,10 @@
366 524 * Return instance of Updraft_Logger
367 525 *
368 526 * @return Updraft_Logger
369 527 */
370 - public static function get_logger() {
371 - if (empty(self::$_logger_instance)) {
372 - self::$_logger_instance = new Updraft_Logger();
373 - }
374 - return self::$_logger_instance;
528 + public function get_logger() {
529 + return Updraft_Logger::instance();
375 530 }
376 531
377 532 /**
378 533 * Check if the current page belongs to WP-Optimize.
@@ -388,41 +543,63 @@
388 543 /**
389 544 * Enqueue scripts and styles on WP-Optimize pages.
390 545 */
391 546 public function admin_enqueue_scripts() {
392 - $enqueue_version = (defined('WP_DEBUG') && WP_DEBUG) ? WPO_VERSION.'.'.time() : WPO_VERSION;
393 - $min_or_not = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
394 - $min_or_not_internal = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '-'. str_replace('.', '-', WPO_VERSION). '.min';
547 + global $wp_version;
548 + $enqueue_version = $this->get_enqueue_version();
549 + $min_or_not = $this->get_min_or_not_string();
550 + $min_or_not_internal = $this->get_min_or_not_internal_string();
395 551
396 552 // Register or enqueue common scripts
397 553 wp_register_script('wp-optimize-send-command', WPO_PLUGIN_URL.'js/send-command'.$min_or_not_internal.'.js', array(), $enqueue_version);
398 554 wp_localize_script('wp-optimize-send-command', 'wp_optimize_send_command_data', array('nonce' => wp_create_nonce('wp-optimize-ajax-nonce')));
555 + wp_register_script('wp-optimize-block-ui', WPO_PLUGIN_URL.'js/blockUI'.$min_or_not_internal.'.js', array('jquery'), $enqueue_version);
399 556 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
400 557
401 558 // load scripts and styles only on WP-Optimize pages.
402 559 if (!$this->is_wpo_page()) return;
560 +
561 + wp_enqueue_script( 'jquery-ui-tooltip' );
562 + // Defeat other plugins/themes which dump their jQuery UI CSS onto our settings page
563 + wp_deregister_style('jquery-ui');
564 + wp_enqueue_style('jquery-ui', WPO_PLUGIN_URL.'css/jquery-ui.custom' . $min_or_not_internal . '.css', array(), $enqueue_version);
565 +
566 +
567 + wp_enqueue_script('select2', WPO_PLUGIN_URL . 'js/select2/select2' . $min_or_not . '.js', array('jquery'), $enqueue_version);
568 + wp_enqueue_style('select2', WPO_PLUGIN_URL . 'css/select2/select2' . $min_or_not . '.css', array(), $enqueue_version);
403 569
404 570 wp_enqueue_script('jquery-serialize-json', WPO_PLUGIN_URL.'js/serialize-json/jquery.serializejson'.$min_or_not.'.js', array('jquery'), $enqueue_version);
405 571
406 572 wp_register_script('updraft-queue-js', WPO_PLUGIN_URL.'js/queue'.$min_or_not_internal.'.js', array(), $enqueue_version);
573 +
407 574 wp_enqueue_script('wp-optimize-modal', WPO_PLUGIN_URL.'js/modal'.$min_or_not_internal.'.js', array('jquery', 'backbone', 'wp-util'), $enqueue_version);
408 - wp_enqueue_script('wp-optimize-cache-js', WPO_PLUGIN_URL.'js/cache'.$min_or_not_internal.'.js', array('wp-optimize-send-command', 'smush-js'), $enqueue_version);
409 - wp_enqueue_script('wp-optimize-admin-js', WPO_PLUGIN_URL.'js/wpoadmin'.$min_or_not_internal.'.js', array('jquery', 'updraft-queue-js', 'wp-optimize-send-command', 'smush-js', 'wp-optimize-modal'), $enqueue_version);
575 + wp_enqueue_script('wp-optimize-cache-js', WPO_PLUGIN_URL.'js/cache'.$min_or_not_internal.'.js', array('wp-optimize-send-command', 'smush-js', 'wp-optimize-heartbeat-js', 'wp-optimize-block-ui'), $enqueue_version);
576 + wp_enqueue_script('wp-optimize-admin-js', WPO_PLUGIN_URL.'js/wpoadmin'.$min_or_not_internal.'.js', array('jquery', 'updraft-queue-js', 'wp-optimize-send-command', 'smush-js', 'wp-optimize-modal', 'wp-optimize-cache-js', 'wp-optimize-heartbeat-js'), $enqueue_version);
410 577 wp_enqueue_style('wp-optimize-admin-css', WPO_PLUGIN_URL.'css/wp-optimize-admin'.$min_or_not_internal.'.css', array(), $enqueue_version);
411 - // Using tablesorter to help with organising the DB size on Table Information
412 - // https://github.com/Mottie/tablesorter
413 - wp_enqueue_script('tablesorter-js', WPO_PLUGIN_URL.'js/tablesorter/jquery.tablesorter'.$min_or_not.'.js', array('jquery', 'wp-optimize-send-command'), $enqueue_version);
578 + // Using table sorter to help with organising the DB size on Table Information
579 + // https://github.com/tofsjonas/sortable/
580 + wp_enqueue_script('sortable-js', WPO_PLUGIN_URL.'js/sortable/sortable'.$min_or_not.'.js', array('wp-optimize-send-command'), $enqueue_version);
581 + wp_enqueue_script('sortable-a11y-js', WPO_PLUGIN_URL.'js/sortable/sortable.a11y'.$min_or_not.'.js', array('wp-optimize-send-command'), $enqueue_version);
414 582
415 - wp_enqueue_script('tablesorter-widgets-js', WPO_PLUGIN_URL.'js/tablesorter/jquery.tablesorter.widgets'.$min_or_not.'.js', array('jquery'), $enqueue_version);
583 + wp_enqueue_style('sortable-css', WPO_PLUGIN_URL.'css/sortable/sortable'.$min_or_not_internal.'.css', array(), $enqueue_version);
416 584
417 - // wp_enqueue_style('tablesorter-css', WPO_PLUGIN_URL.'css/tablesorter/theme.default.min.css', array(), $enqueue_version);
418 -
419 585 $js_variables = $this->wpo_js_translations();
420 586 $js_variables['loggers_classes_info'] = $this->get_loggers_classes_info();
421 -
422 587 wp_localize_script('wp-optimize-admin-js', 'wpoptimize', $js_variables);
423 588
424 589 do_action('wpo_premium_scripts_styles', $min_or_not_internal, $min_or_not, $enqueue_version);
590 +
591 + $status_report_dependencies = array('wp-optimize-admin-js');
592 +
593 + // Only include wp-api-fetch if WP >= 5.0
594 + if (version_compare($wp_version, '5.0', '>=')) {
595 + $status_report_dependencies[] = 'wp-api-fetch';
596 + }
597 +
598 + wp_enqueue_script('wp-optimize-status-report', WPO_PLUGIN_URL.'js/status'.$min_or_not_internal.'.js', $status_report_dependencies, $enqueue_version);
599 +
600 + wp_enqueue_script('js-zip', WPO_PLUGIN_URL.'/js/jszip/jszip' . $min_or_not . '.js', array(), $enqueue_version);
601 +
425 602 }
426 603
427 604 /**
428 605 * Enqueue any required front-end scripts
@@ -429,11 +606,11 @@
429 606 *
430 607 * @return void
431 608 */
432 609 public function frontend_enqueue_scripts() {
433 - if (!current_user_can('manage_options') || !is_admin_bar_showing()) return;
434 - $enqueue_version = (defined('WP_DEBUG') && WP_DEBUG) ? WPO_VERSION.'.'.time() : WPO_VERSION;
435 - $min_or_not_internal = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '-'. str_replace('.', '-', WPO_VERSION). '.min';
610 + if (!$this->current_user_can('manage_options') || !is_admin_bar_showing()) return;
611 + $enqueue_version = $this->get_enqueue_version();
612 + $min_or_not_internal = $this->get_min_or_not_internal_string();
436 613
437 614 // Register or enqueue common scripts
438 615 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
439 616 }
@@ -439,8 +616,10 @@
439 616 }
440 617
441 618 /**
442 619 * Load Task Manager
620 + *
621 + * @return Updraft_Smush_Manager
443 622 */
444 623 public function get_task_manager() {
445 624 include_once(WPO_PLUGIN_MAIN_PATH.'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php');
446 625
@@ -445,14 +624,14 @@
445 624 include_once(WPO_PLUGIN_MAIN_PATH.'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php');
446 625
447 626 Updraft_Tasks_Activation::check_updates();
448 627
449 - include_once(WPO_PLUGIN_MAIN_PATH . '/vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-meta.php');
450 - include_once(WPO_PLUGIN_MAIN_PATH . '/vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-options.php');
451 - include_once(WPO_PLUGIN_MAIN_PATH . '/vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task.php');
628 + include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-meta.php');
629 + include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-options.php');
630 + include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task.php');
452 631
453 - include_once(WPO_PLUGIN_MAIN_PATH . '/includes/class-updraft-smush-task.php');
454 - include_once(WPO_PLUGIN_MAIN_PATH . '/includes/class-updraft-smush-manager.php');
632 + include_once(WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-smush-task.php');
633 + include_once(WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-smush-manager.php');
455 634
456 635 return Updraft_Smush_Manager();
457 636 }
458 637
@@ -458,9 +637,9 @@
458 637
459 638 /**
460 639 * Indicate whether we have an associated instance of WP-Optimize Premium or not.
461 640 *
462 - * @returns Boolean
641 + * @returns bool
463 642 */
464 643 public static function is_premium() {
465 644 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && function_exists('WP_Optimize_Premium')) {
466 645 $wp_optimize_premium = WP_Optimize_Premium();
@@ -475,9 +654,9 @@
475 654 * @return bool
476 655 */
477 656 public function is_apache_server() {
478 657 global $is_apache;
479 - return $is_apache;
658 + return (bool) $is_apache;
480 659 }
481 660
482 661 /**
483 662 * Check if script running on IIS web server.
@@ -521,12 +700,23 @@
521 700 * Checks if this is the premium version and loads it. It also ensures that if the free version is installed then it is disabled with an appropriate error message.
522 701 */
523 702 public function plugins_loaded() {
524 703
525 - if ($this->does_server_handles_cache()) {
704 + if (!$this->is_minimum_requirement_met()) {
705 + $this->add_notice_minimum_requirements_not_met();
706 + $this->deactivate_plugin();
707 + WPO_Deactivation::actions();
708 + return;
709 + }
710 +
711 + if (is_admin() && $this->current_user_can()) {
712 + WP_Optimize_Heartbeat::get_instance();
713 + }
714 +
715 + if ($this->get_server_compatibility_instance()->does_server_handle_cache()) {
526 716 add_filter('wp_optimize_admin_page_wpo_cache_tabs', array($this, 'filter_cache_tabs'), 99, 1);
527 717
528 - // If newly migrated to server that handles cache, disable wpo cache
718 + // If newly migrated to a server that handles cache, disable wpo cache
529 719 $cache = $this->get_page_cache();
530 720 if ($cache->is_enabled()) {
531 721 $cache->disable();
532 722 }
@@ -538,23 +728,28 @@
538 728 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && !class_exists('WP_Optimize_Premium')) {
539 729 include_once(WPO_PLUGIN_MAIN_PATH.'premium.php');
540 730 }
541 731
732 + if (!self::is_premium()) {
733 + $this->get_server_compatibility_instance()->maybe_disable_unsupported_table_optimization();
734 + add_action('auto_option_settings', array($this->get_options(), 'auto_option_settings'));
735 + }
736 +
542 737 // load defaults
543 - WP_Optimize()->get_options()->set_default_options();
738 + $this->get_options()->set_default_options();
544 739
545 740 // Initialize loggers.
546 - $this->setup_loggers();
741 + add_action('init', array($this, 'setup_loggers'));
547 742
548 743 if ($this->is_active('premium') && false !== ($free_plugin = $this->is_active('free'))) {
549 744 if (!function_exists('deactivate_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
550 745 deactivate_plugins($free_plugin);
551 746
552 - // If WPO_ADVANCED_CACHE is defined, we empty advanced-cache.php to regenerate later. Otherwise it contains the path to free.
747 + // If WPO_ADVANCED_CACHE is defined, we empty advanced-cache.php to regenerate later. Otherwise, it contains the path to free.
553 748 if (defined('WPO_ADVANCED_CACHE') && WPO_ADVANCED_CACHE) {
554 749 $advanced_cache_filename = trailingslashit(WP_CONTENT_DIR) . 'advanced-cache.php';
555 750
556 - if (!is_file($advanced_cache_filename) && is_writable(dirname($advanced_cache_filename)) || (is_file($advanced_cache_filename) && is_writable($advanced_cache_filename))) {
751 + if (!is_file($advanced_cache_filename) && wp_is_writable(dirname($advanced_cache_filename)) || (is_file($advanced_cache_filename) && wp_is_writable($advanced_cache_filename))) {
557 752 file_put_contents($advanced_cache_filename, '');
558 753 }
559 754 }
560 755
@@ -562,27 +757,71 @@
562 757 add_action('admin_notices', array($this, 'show_admin_notice_premium'));
563 758 return;
564 759 }
565 760
566 - // Loads the task manager
567 - $this->get_task_manager();
761 + add_action('init', array($this, 'init'));
568 762
569 - // Loads the language file.
570 - load_plugin_textdomain('wp-optimize', false, dirname(plugin_basename(__FILE__)) . '/languages');
763 + // add_filter('updraftcentral_host_plugins', array($this, 'attach_updraftcentral_host'));
764 + // if (file_exists(WPO_PLUGIN_MAIN_PATH.'central/factory.php')) include_once(WPO_PLUGIN_MAIN_PATH.'central/factory.php');
765 + }
571 766
572 - // Load page cache.
573 - $this->get_page_cache();
767 + /**
768 + * Methods needed to be run in init hook.
769 + *
770 + * @return void
771 + */
772 + public function init() {
773 + $this->load_language_file();
774 + $this->schedule_plugin_cron_tasks();
574 775 $this->init_page_cache();
776 + $this->get_minify();
777 + $this->get_delay_js();
575 778
576 - // Include minify
577 - $this->get_minify();
779 + // Load 3rd party plugin compatibilities.
780 + $this->load_compatibilities();
781 +
578 782 $this->run_updates();
579 - $this->get_webp_instance();
783 +
784 + // We need this here because webp can be unavailable because of server moves
785 + // This deletes already converted webp images and original image file when a media is deleted
786 + WP_Optimize_WebP_Images::get_instance();
787 +
788 + // Loads the task manager
789 + $this->get_task_manager();
790 +
791 + // Include WebP
792 + if (WPO_USE_WEBP_CONVERSION) {
793 + $this->get_webp_instance();
794 + }
795 +
796 + $this->get_onboarding()->init();
580 797 }
581 798
582 799 /**
583 - * Filter cache tabs (when it is Kinsta)
800 + * Loads the language file.
584 801 *
802 + * @return void
803 + */
804 + private function load_language_file() {
805 + load_plugin_textdomain('wp-optimize', false, dirname(plugin_basename(__FILE__)) . '/languages');
806 + }
807 +
808 + /**
809 + * Attach this wp-optimize plugin as host of the UpdraftCentral libraries
810 + * (e.g. "central" folder)
811 + *
812 + * @param array $hosts List of plugins having the "central" library integrated into them
813 + *
814 + * @return array
815 + */
816 + public function attach_updraftcentral_host($hosts) {
817 + $hosts[] = 'wp-optimize';
818 + return $hosts;
819 + }
820 +
821 + /**
822 + * Filter cache tabs (when the server handles it or do not allow it, like Kinsta)
823 + *
585 824 * @param array $tabs An array of tabs
586 825 *
587 826 * @return array $tabs An array of tabs
588 827 */
@@ -596,18 +835,18 @@
596 835
597 836 /**
598 837 * Check whether one of free/Premium is active (whether it is this instance or not)
599 838 *
600 - * @param String $which - 'free' or 'premium'
839 + * @param string $which - 'free' or 'premium'
601 840 *
602 - * @return String|Boolean - plugin path (if installed) or false if not
841 + * @return string|boolean - plugin path (if installed) or false if not
603 842 */
604 - private function is_active($which = 'free') {
843 + public function is_active($which = 'free') {
605 844 $active_plugins = $this->get_active_plugins();
606 845 foreach ($active_plugins as $file) {
607 846 if ('wp-optimize.php' == basename($file)) {
608 847 $plugin_dir = WP_PLUGIN_DIR.'/'.dirname($file);
609 - if (('free' == $which && !file_exists($plugin_dir.'/premium.php')) || ('free' != $which && file_exists($plugin_dir.'/premium.php'))) return $file;
848 + if (('free' === $which && !file_exists($plugin_dir.'/premium.php')) || ('free' !== $which && file_exists($plugin_dir.'/premium.php'))) return $file;
610 849 }
611 850 }
612 851 return false;
613 852 }
@@ -614,14 +853,13 @@
614 853
615 854 /**
616 855 * Gets an array of plugins active on either the current site, or site-wide
617 856 *
618 - * @return Array - a list of plugin paths (relative to the plugin directory)
857 + * @return array - a list of plugin paths (relative to the plugin directory)
619 858 */
620 859 private function get_active_plugins() {
621 -
622 860 // Gets all active plugins on the current site
623 - $active_plugins = get_option('active_plugins');
861 + $active_plugins = (array) get_option('active_plugins', array());
624 862
625 863 if (is_multisite()) {
626 864 $network_active_plugins = get_site_option('active_sitewide_plugins');
627 865 if (!empty($network_active_plugins)) {
@@ -655,9 +893,9 @@
655 893
656 894 // Loops around each plugin available.
657 895 foreach ($get_plugins as $key => $value) {
658 896 // If the plugin name matches that of the specified name, it will gather details.
659 - if ($value['Name'] != $name && $value['TextDomain'] != $name) continue;
897 + if ($value['Name'] !== $name && $value['TextDomain'] !== $name) continue;
660 898 $plugin_info['installed'] = true;
661 899 $plugin_info['name'] = $key;
662 900 $plugin_info['version'] = $value['Version'];
663 901 if (in_array($key, $active_plugins)) {
@@ -671,10 +909,10 @@
671 909 /**
672 910 * This is a notice to show users that premium is installed
673 911 */
674 912 public function show_admin_notice_premium() {
675 - echo '<div id="wp-optimize-premium-installed-warning" class="error"><p>'.__('WP-Optimize (Free) has been de-activated, because WP-Optimize Premium is active.', 'wp-optimize').'</p></div>';
676 - if (isset($_GET['activate'])) unset($_GET['activate']);
913 + echo '<div id="wp-optimize-premium-installed-warning" class="error"><p>'.esc_html__('WP-Optimize (Free) has been de-activated, because WP-Optimize Premium is active.', 'wp-optimize').'</p></div>';
914 + if (isset($_GET['activate'])) unset($_GET['activate']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- only unsetting value, not using it.
677 915 }
678 916
679 917 /**
680 918 * Show update to Premium notice for non-premium multisite.
@@ -681,9 +919,9 @@
681 919 */
682 920 public function show_multisite_update_to_premium_notice() {
683 921 if (!is_multisite() || self::is_premium()) return;
684 922
685 - echo '<p><a href="'.$this->premium_version_link.'">'.__('New feature: WP-Optimize Premium can now optimize all sites within a multisite install, not just the main one.', 'wp-optimize').'</a></p>';
923 + echo '<p><a href="'.esc_url($this->premium_version_link). '&utm_content=multisite-upsell' .'">'.esc_html__('New feature: WP-Optimize Premium can now optimize all sites within a multisite install, not just the main one.', 'wp-optimize').'</a></p>';
686 924 }
687 925
688 926 public function admin_init() {
689 927 $pagenow = $GLOBALS['pagenow'];
@@ -689,26 +927,29 @@
689 927 $pagenow = $GLOBALS['pagenow'];
690 928
691 929 $this->register_template_directories();
692 930
693 - if (('index.php' == $pagenow && current_user_can('update_plugins')) || ('index.php' == $pagenow && defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
931 + if (('index.php' === $pagenow && $this->current_user_can('update_plugins')) || ('index.php' === $pagenow && defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
694 932 $options = $this->get_options();
695 933
696 934 $dismissed_until = $options->get_option('dismiss_dash_notice_until', 0);
697 935
698 - if (file_exists(WPO_PLUGIN_MAIN_PATH . '/index.html')) {
699 - $installed = filemtime(WPO_PLUGIN_MAIN_PATH . '/index.html');
936 + if (file_exists(WPO_PLUGIN_MAIN_PATH . 'index.html')) {
937 + $installed = filemtime(WPO_PLUGIN_MAIN_PATH . 'index.html');
700 938 $installed_for = (time() - $installed);
701 939 }
702 940
703 941 if (($installed && time() > $dismissed_until && $installed_for > (14 * 86400) && !defined('WP_OPTIMIZE_NOADS_B')) || (defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
704 - add_action('all_admin_notices', array($this, 'show_admin_notice_upgradead'));
942 + add_action('all_admin_notices', array($this, 'show_admin_notice_upgraded'));
705 943 }
706 944 }
707 - $this->install_or_update_notice = $this->get_install_or_update_notice();
945 +
708 946 if ($this->is_wp_smush_installed()) {
709 947 add_filter('transient_wp-smush-conflict_check', array($this, 'modify_wp_smush_conflict_check'), 9, 1);
710 948 }
949 + if ($this->get_options()->get_option('404_detector', 0)) {
950 + WP_Optimize_Performance::get_instance($this->get_404_detector())->init();
951 + }
711 952 }
712 953
713 954 /**
714 955 * Checks whether the WP Smush plugin is active or not
@@ -734,9 +975,9 @@
734 975 return $active_plugins;
735 976 }
736 977
737 978 /**
738 - * Get the install or update notice instance
979 + * Get the installation or update notice instance
739 980 *
740 981 * @return WP_Optimize_Install_Or_Update_Notice
741 982 */
742 983 public function get_install_or_update_notice() {
@@ -745,142 +986,47 @@
745 986 $instance = new WP_Optimize_Install_Or_Update_Notice();
746 987 return $instance;
747 988 }
748 989
749 - public function show_admin_notice_upgradead() {
750 - $this->include_template('notices/thanks-for-using-main-dash.php');
990 + /**
991 + * Display an admin notice for an upgraded version.
992 + */
993 + public function show_admin_notice_upgraded() {
994 + $this->include_template('notices/thanks-for-using-main-dash.php', false, array(
995 + 'is_premium' => WP_Optimize::is_premium()
996 + ));
751 997 }
752 -
998 +
999 + /**
1000 + * Returns required capability string
1001 + *
1002 + * @return string
1003 + */
753 1004 public function capability_required() {
754 - return apply_filters('wp_optimize_capability_required', 'manage_options');
755 - }
1005 + $capability = is_multisite() ? 'manage_network_options' : 'manage_options';
756 1006
757 - public function wp_optimize_ajax_handler() {
758 - $nonce = empty($_POST['nonce']) ? '' : $_POST['nonce'];
1007 + $filtered_capability = apply_filters('wp_optimize_capability_required', $capability);
759 1008
760 - if (!wp_verify_nonce($nonce, 'wp-optimize-ajax-nonce') || empty($_POST['subaction'])) {
761 - wp_send_json(array(
762 - 'result' => false,
763 - 'error_code' => 'security_check',
764 - 'error_message' => __('The security check failed; try refreshing the page.', 'wp-optimize')
765 - ));
766 - }
1009 + return is_string($filtered_capability) ? $filtered_capability : $capability;
1010 + }
767 1011
768 - $subaction = $_POST['subaction'];
769 - $data = isset($_POST['data']) ? $_POST['data'] : null;
770 -
771 - if (!current_user_can($this->capability_required())) {
772 - wp_send_json(array(
773 - 'result' => false,
774 - 'error_code' => 'security_check',
775 - 'error_message' => __('You are not allowed to run this command.', 'wp-optimize')
776 - ));
1012 + /**
1013 + * Check if the current user has a given capability.
1014 + * If no capability is provided, it checks for the default capability required to access WP-Optimize.
1015 + *
1016 + * @param string|null $capability The capability to check. Null = use default.
1017 + * @return bool
1018 + */
1019 + public function current_user_can($capability = null) {
1020 + if (null === $capability) {
1021 + $capability = $this->capability_required(); // falls back to default
777 1022 }
778 1023
779 -
780 - // Currently the settings are only available to network admins.
781 - if (is_multisite() && !current_user_can('manage_network_options')) {
782 - /**
783 - * Filters the commands allowed to the subsite admins. Other commands are only available to network admin. Only used in a multisite context.
784 - */
785 - $allowed_commands = apply_filters('wpo_multisite_allowed_commands', array('check_server_status', 'compress_single_image', 'restore_single_image'));
786 - if (!in_array($subaction, $allowed_commands)) wp_send_json(array(
787 - 'result' => false,
788 - 'error_code' => 'update_failed',
789 - 'error_message' => __('Options can only be saved by network admin', 'wp-optimize')
790 - ));
1024 + if (current_user_can($capability)) {
1025 + return true;
791 1026 }
792 -
793 - $options = $this->get_options();
794 1027
795 - $results = array();
796 -
797 - // Some commands that are available via AJAX only.
798 - if (in_array($subaction, array('dismiss_dash_notice_until', 'dismiss_season'))) {
799 - $options->update_option($subaction, (time() + 366 * 86400));
800 - } elseif (in_array($subaction, array('dismiss_page_notice_until', 'dismiss_notice'))) {
801 - $options->update_option($subaction, (time() + 84 * 86400));
802 - } elseif ('dismiss_review_notice' == $subaction) {
803 - if (empty($data['dismiss_forever'])) {
804 - $options->update_option($subaction, time() + 84*86400);
805 - } else {
806 - $options->update_option($subaction, 100 * (365.25 * 86400));
807 - }
808 - } else {
809 -
810 - $commands = new WP_Optimize_Commands();
811 - $minify_commands = new WP_Optimize_Minify_Commands();
812 -
813 -
814 - if (self::is_premium()) {
815 - $cache_commands = new WP_Optimize_Cache_Commands_Premium();
816 - } else {
817 - $cache_commands = new WP_Optimize_Cache_Commands();
818 - }
819 -
820 - // check if called command not in main commands class and exist in cache commands class then change class.
821 - if (!is_callable(array($commands, $subaction)) && is_callable(array($minify_commands, $subaction))) {
822 - $commands = $minify_commands;
823 - }
824 -
825 - // check if called command not in main commands class and exist in cache commands class then change class.
826 - if (!is_callable(array($commands, $subaction)) && is_callable(array($cache_commands, $subaction))) {
827 - $commands = $cache_commands;
828 - }
829 -
830 - if (!is_callable(array($commands, $subaction))) {
831 - error_log("WP-Optimize: ajax_handler: no such command (".$subaction.")");
832 - $results = array(
833 - 'result' => false,
834 - 'error_code' => 'command_not_found',
835 - 'error_message' => sprintf(__('The command "%s" was not found', 'wp-optimize'), $subaction)
836 - );
837 - } else {
838 - $results = call_user_func(array($commands, $subaction), $data);
839 -
840 - // clean status box content, it broke json sometimes.
841 - if (isset($results['status_box_contents'])) {
842 - $results['status_box_contents'] = str_replace(array("\n", "\t"), '', $results['status_box_contents']);
843 - }
844 -
845 - if (is_wp_error($results)) {
846 - $results = array(
847 - 'result' => false,
848 - 'error_code' => $results->get_error_code(),
849 - 'error_message' => $results->get_error_message(),
850 - 'error_data' => $results->get_error_data(),
851 - );
852 - }
853 -
854 - // if nothing was returned for some reason, set as result null.
855 - if (empty($results)) {
856 - $results = array(
857 - 'result' => null
858 - );
859 - }
860 - }
861 - }
862 -
863 - $result = json_encode($results);
864 -
865 - // Requires PHP 5.3+
866 - $json_last_error = function_exists('json_last_error') ? json_last_error() : false;
867 -
868 - // if json_encode returned error then return error.
869 - if ($json_last_error) {
870 - $result = array(
871 - 'result' => false,
872 - 'error_code' => $json_last_error,
873 - 'error_message' => 'json_encode error : '.$json_last_error,
874 - 'error_data' => '',
875 - );
876 -
877 - $result = json_encode($result);
878 - }
879 -
880 - echo $result;
881 -
882 - die;
1028 + return false;
883 1029 }
884 1030
885 1031 /**
886 1032 * Returns array of translations used in javascript code.
@@ -887,9 +1033,10 @@
887 1033 *
888 1034 * @return array
889 1035 */
890 1036 public function wpo_js_translations() {
891 - return apply_filters('wpo_js_translations', array(
1037 + $log_message = __('For more details, please check your logs configured in logging destinations settings.', 'wp-optimize');
1038 + $translations = array(
892 1039 'automatic_backup_before_optimizations' => __('Automatic backup before optimizations', 'wp-optimize'),
893 1040 'error_unexpected_response' => __('An unexpected response was received.', 'wp-optimize'),
894 1041 'optimization_complete' => __('Optimization complete', 'wp-optimize'),
895 1042 'with_warnings' => __('(with warnings - open the browser console for more details)', 'wp-optimize'),
@@ -895,8 +1042,9 @@
895 1042 'with_warnings' => __('(with warnings - open the browser console for more details)', 'wp-optimize'),
896 1043 'optimizing_table' => __('Optimizing table:', 'wp-optimize'),
897 1044 'run_optimizations' => __('Run optimizations', 'wp-optimize'),
898 1045 'table_optimization_timeout' => 120000,
1046 + 'add' => __('Add', 'wp-optimize'),
899 1047 'cancel' => __('Cancel', 'wp-optimize'),
900 1048 'cancelling' => __('Cancelling...', 'wp-optimize'),
901 1049 'enable' => __('Enable', 'wp-optimize'),
902 1050 'disable' => __('Disable', 'wp-optimize'),
@@ -902,11 +1050,14 @@
902 1050 'disable' => __('Disable', 'wp-optimize'),
903 1051 'please_select_settings_file' => __('Please, select settings file.', 'wp-optimize'),
904 1052 'are_you_sure_you_want_to_remove_logging_destination' => __('Are you sure you want to remove this logging destination?', 'wp-optimize'),
905 1053 'fill_all_settings_fields' => __('Before saving, you need to complete the currently incomplete settings (or remove them).', 'wp-optimize'),
906 - 'table_was_not_repaired' => __('%s was not repaired. For more details, please check the logs (configured in your logging destinations settings).', 'wp-optimize'),
907 - 'table_was_not_deleted' => __('%s was not deleted. For more details, please check your logs configured in logging destinations settings.', 'wp-optimize'),
908 - 'table_was_not_converted' => __('%s was not converted to InnoDB. For more details, please check your logs configured in logging destinations settings.', 'wp-optimize'),
1054 + // translators: %s is the table name
1055 + 'table_was_not_repaired' => __('%s was not repaired.', 'wp-optimize') . ' ' . $log_message,
1056 + // translators: %s is the table name
1057 + 'table_was_not_deleted' => __('%s was not deleted.', 'wp-optimize') . ' ' . $log_message,
1058 + // translators: %s is the table name
1059 + 'table_was_not_converted' => __('%s was not converted to InnoDB.', 'wp-optimize') . ' ' . $log_message,
909 1060 'please_use_positive_integers' => __('Please use positive integers.', 'wp-optimize'),
910 1061 'please_use_valid_values' => __('Please use valid values.', 'wp-optimize'),
911 1062 'update' => __('Update', 'wp-optimize'),
912 1063 'run_now' => __('Run now', 'wp-optimize'),
@@ -914,77 +1065,87 @@
914 1065 'loading_urls' => __('Loading URLs...', 'wp-optimize'),
915 1066 'current_cache_size' => __('Current cache size:', 'wp-optimize'),
916 1067 'number_of_files' => __('Number of files:', 'wp-optimize'),
917 1068 'toggle_info' => __('Show information', 'wp-optimize'),
1069 + 'delete_file' => __('Delete', 'wp-optimize'),
1070 + 'deleting' => __('Deleting...', 'wp-optimize'),
918 1071 'added_to_list' => __('Added to the list', 'wp-optimize'),
919 1072 'added_notice' => __('The file was added to the list', 'wp-optimize'),
920 1073 'save_notice' => __('Save the changes', 'wp-optimize'),
1074 + 'saving' => __('Saving...', 'wp-optimize'),
1075 + 'clearing_cache' => __('Clearing cache...', 'wp-optimize'),
1076 + 'creating_cache' => __('Creating cache...', 'wp-optimize'),
921 1077 'page_refresh' => __('Refreshing the page to reflect changes...', 'wp-optimize'),
1078 + 'cache_file_not_found' => __('Cache file was not found.', 'wp-optimize'),
922 1079 'settings_have_been_deleted_successfully' => __('WP-Optimize settings have been deleted successfully.', 'wp-optimize'),
923 1080 'loading_data' => __('Loading data...', 'wp-optimize'),
924 - 'spinner_src' => esc_attr(admin_url('images/spinner-2x.gif')),
1081 + 'spinner_src' => esc_url(admin_url('images/spinner-2x.gif')),
1082 + 'logo_src' => esc_url(WPO_PLUGIN_URL.'images/notices/wp_optimize_logo.png'),
925 1083 'settings_page_url' => is_multisite() ? network_admin_url('admin.php?page=wpo_settings') : admin_url('admin.php?page=wpo_settings'),
926 1084 'sites' => $this->get_sites(),
927 - 'user_always_ignores_table_delete_warning' => (get_user_meta(get_current_user_id(), 'wpo-ignores-table-delete-warning', true)) ? true : false,
1085 + 'user_always_ignores_table_deletion_warning' => (bool) get_user_meta(get_current_user_id(), 'wpo-ignores-table-deletion-warning', true),
1086 + 'user_always_ignores_post_meta_deletion_warning' => (bool) get_user_meta(get_current_user_id(), 'wpo-ignores-post-meta-deletion-warning', true),
1087 + 'user_always_ignores_orphaned_relationship_data_deletion_warning' => (bool) get_user_meta(get_current_user_id(), 'wpo-ignores-orphaned-relationship-data-deletion-warning', true),
928 1088 'post_meta_tweak_completed' => __('The tweak has been performed.', 'wp-optimize'),
929 1089 'no_minified_assets' => __('No minified files are present', 'wp-optimize'),
930 - ));
1090 + 'network_site_url' => network_site_url(),
1091 + 'export_settings_file_name' => 'wpoptimize-settings-'.sanitize_title(get_bloginfo('name')).'.json',
1092 + 'import_select_file' => __('You have not yet selected a file to import.', 'wp-optimize'),
1093 + 'import_invalid_json_file' => __('Error: The chosen file is corrupt.', 'wp-optimize') . ' ' . __('Please choose a valid WP-Optimize export file.', 'wp-optimize'),
1094 + 'importing' => __('Importing...', 'wp-optimize'),
1095 + 'importing_data_from' => __('This will import data from:', 'wp-optimize'),
1096 + 'exported_on' => __('Which was exported on:', 'wp-optimize'),
1097 + 'continue_import' => __('Do you want to carry out the import?', 'wp-optimize'),
1098 + 'select_destination' => __('Select destination', 'wp-optimize'),
1099 + 'show' => __('Show', 'wp-optimize'),
1100 + 'hide' => __('Hide', 'wp-optimize'),
1101 + 'please_wait' => __('Please wait a moment...', 'wp-optimize'),
1102 + 'clipboard_failed' => __('Copy to clipboard failed, please do it manually', 'wp-optimize'),
1103 + 'clipboard_success' => __('System status has been copied to the clipboard', 'wp-optimize'),
1104 + 'high_table_count_threshold' => WP_Optimizer::HIGH_TABLE_COUNT_THRESHOLD,
1105 + // translators: 1: starting table count, 2: ending table count
1106 + 'loading_tables_in_batches' => esc_html__('Loading tables %1$s to %2$s', 'wp-optimize'),
1107 + // translators: %s time to finish
1108 + 'time_left' => esc_html__('Aproximate time to finish: %s', 'wp-optimize'),
1109 + 'done_reloading' => esc_html__('Done, reloading', 'wp-optimize'),
1110 + 'show_information' => __('Show information', 'wp-optimize'),
1111 + 'hide_information' => __('Hide information', 'wp-optimize'),
1112 + 'data_not_available' => __('Not available', 'wp-optimize'),
1113 + 'something_wrong_try_again' => __('Something went wrong; please try again.', 'wp-optimize'),
1114 + 'db_tables_scan_pending_list' => __('Your database tables need to be scanned before they can be listed.', 'wp-optimize'),
1115 + 'db_tables_scan_pending_optimize' => __('Your database tables need to be scanned before they can be optimized.', 'wp-optimize'),
1116 + 'db_tables_scan_button' => __('Scan tables', 'wp-optimize')
1117 + );
1118 + $filtered_translations = apply_filters('wpo_js_translations', $translations);
1119 + return is_array($filtered_translations) ? $filtered_translations : $translations;
931 1120 }
932 1121
933 1122 /**
934 - * Manages the admin bar menu for caching (currently page and minify)
935 - */
936 - public function cache_admin_bar($wp_admin_bar) {
937 -
938 - $options = $this->get_options();
939 - if (!$options->get_option('enable_cache_in_admin_bar', true)) return;
940 -
941 - /**
942 - * The "purge cache" menu items
943 - *
944 - * @param array $menu_items - The menu items, in the format required by $wp_admin_bar->add_menu()
945 - * @param object $wp_admin_bar
946 - */
947 - $menu_items = apply_filters('wpo_cache_admin_bar_menu_items', array(), $wp_admin_bar);
948 -
949 - if (empty($menu_items) || !is_array($menu_items)) return;
950 -
951 - $wp_admin_bar->add_menu(array(
952 - 'id' => 'wpo_purge_cache',
953 - 'title' => __('Purge cache', 'wp-optimize'),
954 - 'href' => '#',
955 - 'meta' => array(
956 - 'title' => __('Purge cache', 'wp-optimize'),
957 - ),
958 - 'parent' => false,
959 - ));
960 -
961 - foreach ($menu_items as $item) {
962 - $wp_admin_bar->add_menu($item);
963 - }
964 - }
965 -
966 - /**
967 1123 * Add settings link on plugin page
968 1124 *
969 - * @param string $links Passing through the URL to be used within the HREF.
970 - * @return string Returns the Links.
1125 + * @param array $links Passing through the URL to be used within the HREF.
1126 + * @return array Returns the Links.
971 1127 */
972 1128 public function plugin_settings_link($links) {
973 1129
974 1130 $admin_page_url = $this->get_options()->admin_page_url();
975 1131 $settings_page_url = $this->get_options()->admin_page_url('wpo_settings');
1132 + $database_page_url = $this->get_options()->admin_page_url('wpo_database');
976 1133
977 - if (false == self::is_premium()) {
978 - $premium_link = '<a href="' . esc_url($this->premium_version_link) . '" target="_blank">' . __('Premium', 'wp-optimize') . '</a>';
1134 + if (!self::is_premium()) {
1135 + $premium_link = '<a href="' . esc_url($this->premium_version_link) . '&utm_content=plugin-page' . '" target="_blank">' . esc_html__('Premium', 'wp-optimize') . '</a>';
979 1136 array_unshift($links, $premium_link);
980 1137 }
981 1138
982 - $settings_link = '<a href="' . esc_url($settings_page_url) . '">' . __('Settings', 'wp-optimize') . '</a>';
1139 + $settings_link = '<a href="' . esc_url($settings_page_url) . '">' . esc_html__('Settings', 'wp-optimize') . '</a>';
983 1140 array_unshift($links, $settings_link);
984 1141
985 - $optimize_link = '<a href="' . esc_url($admin_page_url) . '">' . __('Optimize', 'wp-optimize') . '</a>';
1142 + $optimize_link = '<a href="' . esc_url($database_page_url) . '">' . esc_html__('Optimize', 'wp-optimize') . '</a>';
986 1143 array_unshift($links, $optimize_link);
1144 +
1145 + $dashboard_link = '<a href="' . esc_url($admin_page_url) . '">' . esc_html__('Overview', 'wp-optimize') . '</a>';
1146 + array_unshift($links, $dashboard_link);
1147 +
987 1148 return $links;
988 1149 }
989 1150
990 1151 /**
@@ -998,9 +1159,9 @@
998 1159 public function tables_list_additional_column_data($content, $table_info) {
999 1160 if ($table_info->is_needing_repair) {
1000 1161 $content .= '<div class="wpo_button_wrap">'
1001 1162 . '<button class="button button-secondary run-single-table-repair" data-table="' . esc_attr($table_info->Name) . '">' . __('Repair', 'wp-optimize') . '</button>'
1002 - . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
1163 + . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">' // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- N/A
1003 1164 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
1004 1165 . '</div>';
1005 1166 }
1006 1167
@@ -1007,18 +1168,18 @@
1007 1168 // table belongs to plugin.
1008 1169 if ($table_info->can_be_removed) {
1009 1170 $content .= '<div>'
1010 1171 . '<button class="button button-secondary run-single-table-delete" data-table="' . esc_attr($table_info->Name) . '">' . __('Remove', 'wp-optimize') . '</button>'
1011 - . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
1172 + . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">' // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- N/A
1012 1173 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
1013 1174 . '</div>';
1014 1175 }
1015 1176
1016 1177 // Add option for MyISAM to InnoDB conversion.
1017 - if ('MyISAM' == $table_info->Engine) {
1178 + if ('MyISAM' === $table_info->Engine) {
1018 1179 $content .= '<div class="wpo_button_convert wpo_button_wrap">'
1019 1180 . '<button class="button button-secondary toinnodb" data-table="' . esc_attr($table_info->Name) . '">' . __('Convert to InnoDB', 'wp-optimize') . '</button>'
1020 - . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
1181 + . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">' // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- N/A
1021 1182 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
1022 1183 . '</div>';
1023 1184
1024 1185 }
@@ -1028,15 +1189,26 @@
1028 1189
1029 1190 /**
1030 1191 * Initialize WP-Optimize page cache.
1031 1192 */
1032 - public function init_page_cache() {
1193 + private function init_page_cache() {
1033 1194 if ($this->get_page_cache()->config->get_option('enable_page_caching', false)) {
1034 - $this->get_page_cache()->enable();
1195 + if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
1196 + $this->_cache_init_status = $this->get_page_cache()->enable();
1197 + }
1035 1198 }
1036 1199 }
1037 1200
1038 1201 /**
1202 + * Get init_page_cache() status.
1203 + *
1204 + * @return bool|WP_Error
1205 + */
1206 + public function get_init_page_cache_status() {
1207 + return $this->_cache_init_status;
1208 + }
1209 +
1210 + /**
1039 1211 * Schedules cron event based on selected schedule type
1040 1212 *
1041 1213 * @return void
1042 1214 */
@@ -1044,17 +1216,17 @@
1044 1216 $gmt_offset = (int) (3600 * get_option('gmt_offset'));
1045 1217
1046 1218 $options = $this->get_options();
1047 1219
1048 - if ($options->get_option('schedule') === false) {
1220 + if (false === $options->get_option('schedule')) {
1049 1221 $options->set_default_options();
1050 1222 } else {
1051 - if ('true' == $options->get_option('schedule')) {
1223 + if ('true' === $options->get_option('schedule')) {
1052 1224 if (!wp_next_scheduled('wpo_cron_event2')) {
1053 1225 $schedule_type = $options->get_option('schedule-type', 'wpo_weekly');
1054 1226
1055 1227 // Backward compatibility
1056 - if ('wpo_otherweekly' == $schedule_type) $schedule_type = 'wpo_fortnightly';
1228 + if ('wpo_otherweekly' === $schedule_type) $schedule_type = 'wpo_fortnightly';
1057 1229
1058 1230 $this_time = (86400 * 7);
1059 1231
1060 1232 switch ($schedule_type) {
@@ -1076,15 +1248,15 @@
1076 1248 }
1077 1249
1078 1250 add_action('wpo_cron_event2', array($this, 'cron_action'));
1079 1251 $result = wp_schedule_event((current_time("timestamp", 0) + $this_time - $gmt_offset), $schedule_type, 'wpo_cron_event2');
1080 - WP_Optimize()->log('running wp_schedule_event()');
1252 + $this->log('running wp_schedule_event()');
1081 1253 if (is_wp_error($result)) {
1082 1254 $error_msg = $result->get_error_message();
1083 - WP_Optimize()->log($error_msg);
1084 - WP_Optimize()->log(print_r($result, true));
1255 + $this->log($error_msg);
1256 + $this->log(print_r($result, true)); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Using for debugging purpose, logged into separate file
1085 1257 } else {
1086 - WP_Optimize()->log($result);
1258 + $this->log($result);
1087 1259 }
1088 1260 }
1089 1261 }
1090 1262 }
@@ -1090,13 +1262,31 @@
1090 1262 }
1091 1263 }
1092 1264
1093 1265 /**
1094 - * Clears all cron events
1266 + * Unschedules WP-Optimize cron events on deactivation.
1095 1267 *
1096 1268 * @return void
1097 1269 */
1098 1270 public function wpo_cron_deactivate() {
1271 + if (is_multisite()) {
1272 + $sites = $this->get_sites();
1273 + foreach ($sites as $site) {
1274 + switch_to_blog($site->blog_id);
1275 + $this->unschedule_wpo_cron_jobs();
1276 + restore_current_blog();
1277 + }
1278 + } else {
1279 + $this->unschedule_wpo_cron_jobs();
1280 + }
1281 + }
1282 +
1283 + /**
1284 + * Unschedules all WP-Optimize cron jobs on the current site.
1285 + *
1286 + * @return void
1287 + */
1288 + private function unschedule_wpo_cron_jobs() {
1099 1289 $cron_jobs = _get_cron_array();
1100 1290 foreach ($cron_jobs as $job) {
1101 1291 foreach (array_keys($job) as $hook) {
1102 1292 if (preg_match('/^wpo_/', $hook)) wp_unschedule_hook($hook);
@@ -1142,8 +1332,12 @@
1142 1332 /**
1143 1333 * Run updates on plugin activation.
1144 1334 */
1145 1335 public function run_updates() {
1336 +
1337 + if (!defined('WPO_CACHE_FILES_DIR')) define('WPO_CACHE_FILES_DIR', untrailingslashit(WP_CONTENT_DIR).'/cache/wpo-cache');
1338 +
1339 + require_once(WPO_PLUGIN_MAIN_PATH . 'cache/file-based-page-cache-functions.php');
1146 1340 include_once(WPO_PLUGIN_MAIN_PATH.'includes/class-wp-optimize-updates.php');
1147 1341 WP_Optimize_Updates::check_updates();
1148 1342 }
1149 1343
@@ -1154,13 +1348,21 @@
1154 1348 * @return string
1155 1349 */
1156 1350 public function show_admin_warning_overdue_crons($howmany) {
1157 1351 $ret = '<div class="updated below-h2"><p>';
1158 - $ret .= '<strong>'.__('Warning', 'wp-optimize').':</strong> '.sprintf(__('WordPress has a number (%d) of scheduled tasks which are overdue. Unless this is a development site, this probably means that the scheduler in your WordPress install is not working.', 'wp-optimize'), $howmany).' <a href="'.apply_filters('wpoptimize_com_link', "https://getwpo.com/faqs/the-scheduler-in-my-wordpress-installation-is-not-working-what-should-i-do/").'">'.__('Read this page for a guide to possible causes and how to fix it.', 'wp-optimize').'</a>';
1352 + // translators: %d is number of overdue cron jobs
1353 + $ret .= '<strong>'.esc_html__('Warning', 'wp-optimize').':</strong> '.sprintf(esc_html__('WordPress has a number (%d) of scheduled tasks which are overdue.', 'wp-optimize'), $howmany).' '. esc_html__('Unless this is a development site, this probably means that the scheduler in your WordPress install is not working.', 'wp-optimize').' <a target="_blank" href="'.esc_url(apply_filters('wpoptimize_com_link', "https://teamupdraft.com/blog/the-scheduler-in-wordpress-is-not-working-what-should-i-do/")).'">'.esc_html__('Read this page for a guide to possible causes and how to fix it.', 'wp-optimize').'</a>';
1159 1354 $ret .= '</p></div>';
1160 1355 return $ret;
1161 1356 }
1162 1357
1358 + /**
1359 + * Normalizes path string
1360 + *
1361 + * @param string $path
1362 + *
1363 + * @return string
1364 + */
1163 1365 private function wp_normalize_path($path) {
1164 1366 // Wp_normalize_path is not present before WP 3.9.
1165 1367 if (function_exists('wp_normalize_path')) return wp_normalize_path($path);
1166 1368 // Taken from WP 4.6.
@@ -1171,24 +1373,38 @@
1171 1373 }
1172 1374 return $path;
1173 1375 }
1174 1376
1377 + /**
1378 + * Returns templates directory path
1379 + *
1380 + * @return string
1381 + */
1175 1382 public function get_templates_dir() {
1176 - return apply_filters('wp_optimize_templates_dir', $this->wp_normalize_path(WPO_PLUGIN_MAIN_PATH.'templates'));
1383 + $templates_dir = $this->wp_normalize_path(WPO_PLUGIN_MAIN_PATH.'templates');
1384 + $filtered_templates_dir = apply_filters('wp_optimize_templates_dir', $templates_dir);
1385 + return is_string($filtered_templates_dir) ? $filtered_templates_dir : $templates_dir;
1177 1386 }
1178 1387
1388 + /**
1389 + * Returns templates URL
1390 + *
1391 + * @return string
1392 + */
1179 1393 public function get_templates_url() {
1180 - return apply_filters('wp_optimize_templates_url', WPO_PLUGIN_URL.'/templates');
1394 + $templates_url = WPO_PLUGIN_URL . 'templates';
1395 + $filtered_templates_url = apply_filters('wp_optimize_templates_url', $templates_url);
1396 + return is_string($filtered_templates_url) ? $filtered_templates_url : $templates_url;
1181 1397 }
1182 1398
1183 1399 /**
1184 1400 * Return or output view content
1185 1401 *
1186 - * @param String $path - path to template, usually relative to templates/ within the WP-O directory
1187 - * @param Boolean $return_instead_of_echo - what to do with the results
1188 - * @param Array $extract_these - key/value pairs for substitution into the scope of the template
1402 + * @param string $path - path to template, usually relative to templates/ within the WP-O directory
1403 + * @param boolean $return_instead_of_echo - what to do with the results
1404 + * @param array $extract_these - key/value pairs for substitution into the scope of the template
1189 1405 *
1190 - * @return String|Void
1406 + * @return string|void
1191 1407 */
1192 1408 public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) {
1193 1409 if ($return_instead_of_echo) ob_start();
1194 1410
@@ -1208,10 +1424,10 @@
1208 1424
1209 1425 do_action('wp_optimize_before_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1210 1426
1211 1427 if (!file_exists($template_file)) {
1212 - error_log("WP Optimize: template not found: ".$template_file);
1213 - echo __('Error:', 'wp-optimize').' '.__('template not found', 'wp-optimize')." (".$path.")";
1428 + error_log("WP Optimize: template not found: ".$template_file); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Using for debugging purpose such as incomplete installation
1429 + echo esc_html__('Error:', 'wp-optimize').' '.esc_html__('template not found', 'wp-optimize')." (".esc_html($path).")";
1214 1430 } else {
1215 1431 extract($extract_these);
1216 1432 // The following are useful variables which can be used in the template.
1217 1433 // They appear as unused, but may be used in the $template_file.
@@ -1238,9 +1454,9 @@
1238 1454 $templates_dir = $this->get_templates_dir();
1239 1455
1240 1456 if ($dh = opendir($templates_dir)) {
1241 1457 while (($file = readdir($dh)) !== false) {
1242 - if ('.' == $file || '..' == $file) continue;
1458 + if ('.' === $file || '..' === $file) continue;
1243 1459 if (is_dir($templates_dir.'/'.$file)) {
1244 1460 $template_directories[$file] = $templates_dir.'/'.$file;
1245 1461 }
1246 1462 }
@@ -1270,9 +1486,9 @@
1270 1486 * Format Bytes Into KB/MB
1271 1487 *
1272 1488 * @param mixed $bytes Number of bytes to be converted.
1273 1489 * @param integer $decimals the number of decimal digits
1274 - * @return integer return the correct format size.
1490 + * @return string return the correct format size.
1275 1491 */
1276 1492 public function format_size($bytes, $decimals = 2) {
1277 1493 if (!is_numeric($bytes)) return __('N/A', 'wp-optimize');
1278 1494
@@ -1283,9 +1499,9 @@
1283 1499 } elseif (1024 <= $bytes) {
1284 1500 $bytes = number_format($bytes / 1024, $decimals) . ' KB';
1285 1501 } elseif (1 < $bytes) {
1286 1502 $bytes = $bytes . ' bytes';
1287 - } elseif (1 == $bytes) {
1503 + } elseif (1 === (int) $bytes) {
1288 1504 $bytes = $bytes . ' byte';
1289 1505 } else {
1290 1506 $bytes = '0 bytes';
1291 1507 }
@@ -1293,15 +1509,16 @@
1293 1509 return $bytes;
1294 1510 }
1295 1511
1296 1512 /**
1297 - * Format a timestamp into a juman readable date time
1513 + * Format a timestamp into a human-readable date time
1298 1514 *
1299 - * @param int $timestamp
1515 + * @param int $timestamp Epoch timestamp to convert
1516 + * @param string $separator String separator
1300 1517 * @return string
1301 1518 */
1302 - public function format_date_time($timestamp) {
1303 - return date_i18n(get_option('date_format').' @ '.get_option('time_format'), ($timestamp + get_option('gmt_offset') * 3600));
1519 + public function format_date_time($timestamp, $separator = ' @ ') {
1520 + return date_i18n(get_option('date_format'). $separator .get_option('time_format'), ($timestamp + get_option('gmt_offset') * 3600));
1304 1521 }
1305 1522
1306 1523 /**
1307 1524 * Executed this function on cron event.
@@ -1314,9 +1531,9 @@
1314 1531 $options = $this->get_options();
1315 1532
1316 1533 $this->log('WPO: Starting cron_action()');
1317 1534 $options->update_option('last-optimized', time());
1318 - if ('true' == $options->get_option('schedule')) {
1535 + if ('true' === $options->get_option('schedule')) {
1319 1536 $this_options = $options->get_option('auto');
1320 1537
1321 1538 // Currently the output of the optimizations is not saved/used/logged.
1322 1539 $optimizer->do_optimizations($this_options, 'auto');
@@ -1328,14 +1545,17 @@
1328 1545 * Schedule cron tasks used by plugin.
1329 1546 *
1330 1547 * @return void
1331 1548 */
1332 - public function schedule_plugin_cron_tasks() {
1549 + private function schedule_plugin_cron_tasks() {
1333 1550 if (!wp_next_scheduled('wpo_weekly_cron_tasks')) {
1334 - wp_schedule_event(current_time("timestamp", 0), 'weekly', 'wpo_weekly_cron_tasks');
1551 + wp_schedule_event(current_time("timestamp", 0), 'wpo_weekly', 'wpo_weekly_cron_tasks');
1335 1552 }
1336 1553
1337 1554 add_action('wpo_weekly_cron_tasks', array($this, 'do_weekly_cron_tasks'));
1555 +
1556 + // Run 404 detector cron.
1557 + $this->get_404_detector_cron();
1338 1558 }
1339 1559
1340 1560 /**
1341 1561 * Do plugin background tasks.
@@ -1348,32 +1568,75 @@
1348 1568 }
1349 1569
1350 1570 /**
1351 1571 * This will customize a URL with a correct Affiliate link
1352 - * This function can be update to suit any URL as longs as the URL is passed
1572 + * This function can be updated to suit any URL as longs as the URL is passed
1353 1573 *
1354 - * @param String $url - URL to be check to see if it an updraftplus match.
1355 - * @param String $text - Text to be entered within the href a tags.
1356 - * @param String $html - Any specific HTML to be added.
1357 - * @param String $class - Specify a class for the href (including the attribute label)
1358 - * @param Boolean $return_instead_of_echo - if set, then the result will be returned, not echo-ed.
1359 - *
1360 - * @return String|void
1574 + * @param string $url - URL to check to see if it is an updraftplus match.
1575 + * @param ?string $text - Text to be entered within the href a tags.
1576 + * @param ?string $html - Any specific HTML to be added. Supplied parameter will not be escaped in this function. Provide escaped, safe HTML
1577 + * @param string|array $attrs - Specify the HTML attributes as an array or string. Use the array format for multiple attributes (e.g., array( "class" => "lorem-ipsum", "title" => "Highlighting text" )), and use the string format for a single attribute (e.g., 'class="lorem-ipsum"').
1578 + * @param bool $return_instead_of_echo - if set, then the result will be returned, not echo-ed.
1579 + * @return string|void
1361 1580 */
1362 - public function wp_optimize_url($url, $text, $html = '', $class = '', $return_instead_of_echo = false) {
1581 + public function wp_optimize_url($url, $text = '', $html = '', $attrs = '', $return_instead_of_echo = false) {
1363 1582 // Check if the URL is UpdraftPlus.
1364 1583 $url = $this->maybe_add_affiliate_params($url); // Return URL - check if there is HTML such as images.
1365 - if ('' != $html) {
1366 - $result = '<a '.$class.' href="'.esc_attr($url).'">'.$html.'</a>';
1584 +
1585 + // Check if the variable $text is empty (null value included), otherwise assign $html.
1586 + $content = empty($text) ? $html : esc_html($text);
1587 +
1588 + // Check if $attrs is an array to convert the attributes into a string line.
1589 + $str_attrs = '';
1590 + if (is_array($attrs)) {
1591 + foreach ($attrs as $attr => $value) {
1592 + $str_attrs .= $attr . '="' . esc_attr($value) . '" ';
1593 + }
1367 1594 } else {
1368 - $result = '<a '.$class.' href="'.esc_attr($url).'">'.htmlspecialchars($text).'</a>';
1595 + // If $attrs is empty, the `explode` function will only return an empty array.
1596 + $attrs = explode('=', $attrs);
1597 + // Check if $attrs in positions 1 and 2 are not empty and exist; otherwise, return an empty string.
1598 + $str_attrs = !empty($attrs[0]) && !empty($attrs[1]) ? $attrs[0] . '="' . esc_attr(str_replace('"', '', $attrs[1])) . '"' : '';
1369 1599 }
1600 +
1601 + // Check if it is necessary to add a target value if the url is external
1602 + $is_external_url = $this->is_external_url($url);
1603 + if ($is_external_url) {
1604 + $str_attrs = preg_replace('/\s+target="_blank"/i', '', $str_attrs);
1605 + $str_attrs .= ' target="_blank"';
1606 + }
1607 +
1608 + $result = sprintf(
1609 + '<a href="%s" %s>%s</a>',
1610 + esc_url($url),
1611 + $str_attrs,
1612 + wp_kses_post($content)
1613 + );
1614 +
1370 1615 if ($return_instead_of_echo) return $result;
1371 - echo $result;
1616 + echo wp_kses_post($result);
1372 1617 }
1618 +
1619 + /**
1620 + * Check if a URL is external
1621 + *
1622 + * @param string $url
1623 + * @return bool
1624 + */
1625 + public function is_external_url($url) {
1626 + if (empty($url)) {
1627 + return false;
1628 + }
1629 +
1630 + $current_domain = wp_parse_url(home_url(), PHP_URL_HOST);
1631 + $url = wp_parse_url($url, PHP_URL_HOST);
1373 1632
1633 + // Compare the domains and return true if they are different
1634 + return $current_domain !== $url;
1635 + }
1636 +
1374 1637 /**
1375 - * Get an URL with an eventual affiliate ID
1638 + * Get a URL with an eventual affiliate ID
1376 1639 *
1377 1640 * @param string $url
1378 1641 * @return string
1379 1642 */
@@ -1385,9 +1648,10 @@
1385 1648
1386 1649 // Apply filters.
1387 1650 $url = apply_filters('wpoptimize_updraftplus_com_link', $url);
1388 1651 }
1389 - return apply_filters('wpoptimize_maybe_add_affiliate_params', $url);
1652 + $filtered_url = apply_filters('wpoptimize_maybe_add_affiliate_params', $url);
1653 + return is_string($filtered_url) ? $filtered_url : $url;
1390 1654 }
1391 1655
1392 1656 /**
1393 1657 * Setup WPO logger(s)
@@ -1448,9 +1712,9 @@
1448 1712 // check for previous version options format.
1449 1713 $keys = array_keys($saved_loggers);
1450 1714
1451 1715 // if options stored in old format then reformat it.
1452 - if (false == is_numeric($keys[0])) {
1716 + if (false === is_numeric($keys[0])) {
1453 1717 $_saved_loggers = array();
1454 1718 foreach ($saved_loggers as $logger_id => $enabled) {
1455 1719 if ($enabled) {
1456 1720 $_saved_loggers[] = $logger_id;
@@ -1481,9 +1745,9 @@
1481 1745 foreach (array_keys($logger_options) as $option_name) {
1482 1746 if (array_key_exists($option_name, $options_keys)) {
1483 1747 $options_keys[$option_name]++;
1484 1748 } else {
1485 - $options_keys[$option_name] = 0;
1749 + $options_keys[$option_name] = 0;
1486 1750 }
1487 1751
1488 1752 $option_value = isset($logger_additional_options[$option_name][$options_keys[$option_name]]) ? $logger_additional_options[$option_name][$options_keys[$option_name]] : '';
1489 1753
@@ -1508,11 +1772,10 @@
1508 1772 $loggers[] = $logger;
1509 1773 }
1510 1774 }
1511 1775
1512 - $loggers = apply_filters('wp_optimize_loggers', $loggers);
1513 -
1514 - return $loggers;
1776 + $filtered_loggers = apply_filters('wp_optimize_loggers', $loggers);
1777 + return is_array($filtered_loggers) ? $filtered_loggers : $loggers;
1515 1778 }
1516 1779
1517 1780 /**
1518 1781 * Returns associative array with logger class name in a key and path to class file in a value.
@@ -1580,17 +1843,17 @@
1580 1843 return (is_multisite() && self::is_premium());
1581 1844 }
1582 1845
1583 1846 /**
1584 - * Returns true if current user can run optimizations.
1847 + * Returns true if the current user can run optimizations.
1585 1848 *
1586 1849 * @return bool
1587 1850 */
1588 - public function can_run_optimizations() {
1851 + public function can_run_optimizations(): bool {
1589 1852 // we don't check permissions for cron jobs.
1590 1853 if (defined('DOING_CRON') && DOING_CRON) return true;
1591 1854
1592 - if (self::is_premium() && false == user_can(get_current_user_id(), 'wpo_run_optimizations')) return false;
1855 + if (self::is_premium() && false === user_can(get_current_user_id(), 'wpo_run_optimizations')) return false;
1593 1856 return true;
1594 1857 }
1595 1858
1596 1859 /**
@@ -1603,21 +1866,8 @@
1603 1866 return true;
1604 1867 }
1605 1868
1606 1869 /**
1607 - * CHeck if current user can purge the cache.
1608 - *
1609 - * @return bool
1610 - */
1611 - public function can_purge_the_cache() {
1612 - if (self::is_premium()) {
1613 - return WP_Optimize_Premium()->can_purge_the_cache();
1614 - }
1615 -
1616 - return true;
1617 - }
1618 -
1619 - /**
1620 1870 * Returns list of all sites in multisite
1621 1871 *
1622 1872 * @return array
1623 1873 */
@@ -1622,13 +1872,10 @@
1622 1872 * @return array
1623 1873 */
1624 1874 public function get_sites() {
1625 1875 $sites = array();
1626 - // check if function get_sites exists (since 4.6.0) else use wp_get_sites.
1627 1876 if (function_exists('get_sites')) {
1628 1877 $sites = get_sites(array('network_id' => null, 'deleted' => 0, 'number' => 999999));
1629 - } elseif (function_exists('wp_get_sites')) {
1630 - $sites = wp_get_sites(array('network_id' => null, 'deleted' => 0, 'limit' => 999999));
1631 1878 }
1632 1879 return $sites;
1633 1880 }
1634 1881
@@ -1634,9 +1881,9 @@
1634 1881
1635 1882 /**
1636 1883 * Returns script memory limit in megabytes.
1637 1884 *
1638 - * @param bool $memory_limit
1885 + * @param string|bool $memory_limit
1639 1886 * @return int
1640 1887 */
1641 1888 public function get_memory_limit($memory_limit = false) {
1642 1889 // Returns in megabytes
@@ -1690,9 +1937,9 @@
1690 1937 $mp = (is_numeric($mp) && $mp > 0) ? $mp : 1048576;
1691 1938 // 32MB
1692 1939 if ($mp < 33554432) {
1693 1940 $save = $wpdb->show_errors(false);
1694 - @$wpdb->query("SET GLOBAL max_allowed_packet=33554432");// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1941 + @$wpdb->query("SET GLOBAL max_allowed_packet=33554432");// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress errors from displaying
1695 1942 $wpdb->show_errors($save);
1696 1943
1697 1944 $mp = (int) $wpdb->get_var("SELECT @@session.max_allowed_packet");
1698 1945 // Default to 1MB
@@ -1706,8 +1953,10 @@
1706 1953 * Converts shorthand memory notation value to bytes.
1707 1954 * From http://php.net/manual/en/function.ini-get.php
1708 1955 *
1709 1956 * @param string $val shorthand memory notation value.
1957 + *
1958 + * @return string
1710 1959 */
1711 1960 public function return_bytes($val) {
1712 1961 $val = trim($val);
1713 1962 $last = strtolower($val[strlen($val)-1]);
@@ -1739,9 +1988,9 @@
1739 1988
1740 1989 /**
1741 1990 * Close browser connection and continue script work. - Taken from UpdraftPlus
1742 1991 *
1743 - * @param array $txt Response to browser; this must be JSON (or if not, alter the Content-Type header handling below)
1992 + * @param string $txt Response to browser; this must be JSON (or if not, alter the Content-Type header handling below)
1744 1993 * @return void
1745 1994 */
1746 1995 public function close_browser_connection($txt = '') {
1747 1996 if (!headers_sent()) {
@@ -1746,8 +1995,9 @@
1746 1995 public function close_browser_connection($txt = '') {
1747 1996 if (!headers_sent()) {
1748 1997 // Close browser connection so that it can resume AJAX polling
1749 1998 header('Content-Length: '.(empty($txt) ? '0' : 4+strlen($txt)));
1999 + header('Content-Type: application/json');
1750 2000 header('Connection: close');
1751 2001 header('Content-Encoding: none');
1752 2002 }
1753 2003
@@ -1752,9 +2002,9 @@
1752 2002 }
1753 2003
1754 2004 if (session_id()) session_write_close();
1755 2005 echo "\r\n\r\n";
1756 - echo $txt;
2006 + echo $txt; // phpcs:ignore WordPress.Security.EscapeOutput -- Output is already escaped
1757 2007 // These two added - 19-Feb-15 - started being required on local dev machine, for unknown reason (probably some plugin that started an output buffer).
1758 2008 $ob_level = ob_get_level();
1759 2009 while ($ob_level > 0) {
1760 2010 ob_end_flush();
@@ -1761,50 +2011,22 @@
1761 2011 $ob_level--;
1762 2012 }
1763 2013 flush();
1764 2014 if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
2015 + if (function_exists('litespeed_finish_request')) litespeed_finish_request();
1765 2016 }
1766 2017
1767 2018 /**
1768 - * Get the current theme's style.css headers
1769 - *
1770 - * @return array|WP_Error
1771 - */
1772 - public function get_stylesheet_headers() {
1773 - static $headers;
1774 - if (isset($headers)) return $headers;
1775 -
1776 - $style = get_template_directory_uri() . '/style.css';
1777 -
1778 - /**
1779 - * Filters wp_remote_get parameters, when checking if browser cache is enabled.
1780 - *
1781 - * @param array $request_params Default parameters
1782 - */
1783 - $request_params = apply_filters('wpoptimize_get_stylesheet_headers_args', array('timeout' => 10));
1784 -
1785 - // trying to load style.css.
1786 - $response = wp_remote_get($style, $request_params);
1787 -
1788 - if (is_a($response, 'WP_Error')) return $response;
1789 -
1790 - $headers = wp_remote_retrieve_headers($response);
1791 -
1792 - if (is_a($headers, 'Requests_Utility_CaseInsensitiveDictionary')) {
1793 - $headers = $headers->getAll();
1794 - }
1795 -
1796 - return $headers;
1797 - }
1798 -
1799 - /**
1800 2019 * Try to change PHP script time limit.
1801 2020 */
1802 2021 public function change_time_limit() {
1803 2022 $time_limit = (defined('WP_OPTIMIZE_SET_TIME_LIMIT') && WP_OPTIMIZE_SET_TIME_LIMIT > 15) ? WP_OPTIMIZE_SET_TIME_LIMIT : 1800;
1804 2023
1805 - // Try to reduce the chances of PHP self-terminating via reaching max_execution_time.
1806 - @set_time_limit($time_limit); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
2024 + // phpcs:disable
2025 + // Generic.PHP.NoSilencedErrors.Discouraged -- Try to reduce the chances of PHP self-terminating via reaching max_execution_time.
2026 + // Squiz.PHP.DiscouragedFunctions.Discouraged -- Try to reduce the chances of PHP self-terminating via reaching max_execution_time.
2027 + @set_time_limit($time_limit);
2028 + // phpcs:enable
1807 2029 }
1808 2030
1809 2031 /**
1810 2032 * Does the request come from UDC
@@ -1821,11 +2043,11 @@
1821 2043 * @return boolean
1822 2044 */
1823 2045 public function template_should_include_data() {
1824 2046 /**
1825 - * Filters wether data should be included in certain templates or not.
2047 + * Filters whether data should be included in certain templates or not.
1826 2048 */
1827 - return apply_filters('wpo_template_should_include_data', $this->is_updraft_central_request());
2049 + return (bool) apply_filters('wpo_template_should_include_data', $this->is_updraft_central_request());
1828 2050 }
1829 2051
1830 2052 /**
1831 2053 * Load the templates for the modal window
@@ -1834,8 +2056,18 @@
1834 2056 $this->include_template('modal.php');
1835 2057 }
1836 2058
1837 2059 /**
2060 + * Adds compress popup template
2061 + */
2062 + public function add_smush_popup_template() {
2063 + if ($this->current_user_can()) {
2064 + $compression_server_hint = Updraft_Smush_Manager()->get_compression_server_hint();
2065 + $this->include_template('images/smush-popup.php', false, array('compression_server_hint' => $compression_server_hint));
2066 + }
2067 + }
2068 +
2069 + /**
1838 2070 * Delete transients and semaphores data from options table.
1839 2071 */
1840 2072 public function delete_transients_and_semaphores() {
1841 2073 global $wpdb;
@@ -1851,8 +2083,9 @@
1851 2083 'wpo_semaphore_%',
1852 2084 '_transient_timeout_wpo_%',
1853 2085 '_transient_wpo_%',
1854 2086 'updraft_lock_wpo_%',
2087 + 'wpo_last_scheduled_%',
1855 2088 );
1856 2089
1857 2090 $where_parts = array();
1858 2091 foreach ($masks as $mask) {
@@ -1858,84 +2091,113 @@
1858 2091 foreach ($masks as $mask) {
1859 2092 $where_parts[] = "(`option_name` LIKE '{$mask}')";
1860 2093 }
1861 2094
1862 - $wpdb->query("DELETE FROM {$wpdb->options} WHERE " . join(' OR ', $where_parts));
2095 + $where_clause = implode(' OR ', $where_parts);
2096 + $wpdb->query($wpdb->prepare("DELETE FROM `{$wpdb->options}` WHERE %s", $where_clause));
1863 2097 }
1864 2098
1865 2099 /**
1866 2100 * Prevents bots from indexing plugins list
2101 + *
2102 + * @param string $output
2103 + * @return string
1867 2104 */
1868 2105 public function robots_txt($output) {
1869 2106 $upload_dir = wp_upload_dir();
1870 - $path = parse_url($upload_dir['baseurl']);
1871 - $output .= "\nDisallow: " . str_replace($path['scheme'].'://'.$path['host'], '', $upload_dir['baseurl']) . "/wpo-plugins-tables-list.json\n";
2107 + $path = wp_parse_url($upload_dir['baseurl']);
2108 + $output .= "\nUser-agent: *";
2109 + $output .= "\nDisallow: " . str_replace($path['scheme'].'://'.$path['host'], '', $upload_dir['baseurl']) . "/wpo/wpo-plugins-tables-list.json\n";
1872 2110 return $output;
1873 2111 }
1874 -}
1875 2112
1876 -/**
1877 - * Plugin activation actions.
1878 - */
1879 -function wpo_activation_actions() {
1880 - // If plugin activated by not a Network Administrator then deactivate plugin and show message.
1881 - if (is_multisite() && !is_network_admin()) {
1882 - deactivate_plugins(plugin_basename(__FILE__));
1883 - wp_die(__('Only Network Administrator can activate WP-Optimize plugin.', 'wp-optimize').
1884 - ' <a href="'.admin_url('plugins.php').'">'.__('go back', 'wp-optimize').'</a>');
2113 + /**
2114 + * Returns desired enqueue version string
2115 + *
2116 + * @return string Enqueue version as string
2117 + */
2118 + public function get_enqueue_version() {
2119 + return (defined('WP_DEBUG') && WP_DEBUG) ? WPO_VERSION.'.'.time() : WPO_VERSION;
1885 2120 }
1886 2121
1887 - // On activation, check if last-optimized option exists. If not, add 'newly-activated' option.
1888 - if (!WP_Optimize()->get_options()->get_option('last-optimized', false)) {
1889 - WP_Optimize()->get_options()->update_option('newly-activated', true);
2122 + /**
2123 + * Returns script suffix string
2124 + *
2125 + * @return string empty or `.min` suffix string
2126 + */
2127 + public function get_min_or_not_string() {
2128 + return (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
1890 2129 }
1891 2130
1892 - WP_Optimize()->get_options()->set_default_options();
1893 - WP_Optimize()->get_minify()->plugin_activate();
2131 + /**
2132 + * Returns script suffix string with WPO_VERSION
2133 + *
2134 + * @return string empty or min suffix with wpo_version string
2135 + */
2136 + public function get_min_or_not_internal_string() {
2137 + return (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '-' . str_replace('.', '-', WPO_VERSION) . '.min';
2138 + }
1894 2139
1895 - WP_Optimize::get_gzip_compression()->restore();
1896 - WP_Optimize::get_browser_cache()->restore();
2140 + /**
2141 + * Instantiate Ajax handling class
2142 + */
2143 + private function load_ajax_handler() {
2144 + WPO_Ajax::get_instance();
2145 + }
1897 2146
1898 - if (!class_exists('Updraft_Tasks_Activation')) require_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php');
1899 - Updraft_Tasks_Activation::init_db();
1900 - Updraft_Tasks_Activation::reinstall_if_needed();
2147 + /**
2148 + * Get instance of WP_Optimize_Table_Management
2149 + *
2150 + * @return WP_Optimize_Table_Management
2151 + */
2152 + public function get_table_management() {
2153 + return WP_Optimize_Table_Management::get_instance();
2154 + }
1901 2155
1902 - // run premium activation actions.
1903 - if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php')) {
1904 - if (!class_exists('WP_Optimize_Premium')) {
1905 - include_once(WPO_PLUGIN_MAIN_PATH.'premium.php');
1906 - }
1907 - WP_Optimize_Premium()->plugin_activation_actions();
2156 + /**
2157 + * Get instance of WP_Optimize_404_Detector
2158 + *
2159 + * @return WP_Optimize_404_Detector
2160 + */
2161 + public function get_404_detector() {
2162 + return WP_Optimize_404_Detector::get_instance();
1908 2163 }
2164 +
2165 + /**
2166 + * Returns the message used to notify the user that the PHP version or WordPress version doesn't meet the requirements.
2167 + *
2168 + * @return string
2169 + */
2170 + public function get_minimum_requirements_notice_message() {
2171 + // translators: %1$s is minimum required PHP version, %2$s is minimum required WordPress version
2172 + return sprintf(esc_html__('WP-Optimize requires a minimum PHP version of %1$s and WordPress version of %2$s or higher', 'wp-optimize'), esc_html(WPO_REQUIRED_PHP_VERSION), esc_html(WPO_REQUIRED_WP_VERSION));
2173 + }
2174 +
2175 + /**
2176 + * Output notice when PHP version or WordPress version is not meet for WP-Optimize.
2177 + *
2178 + * @return void
2179 + */
2180 + public function output_minimum_requirements_notice() {
2181 + ?>
2182 + <div class="notice notice-error is-dismissible">
2183 + <p><?php echo $this->get_minimum_requirements_notice_message(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is already escaped ?></p>
2184 + </div>
2185 + <?php
2186 + }
1909 2187 }
1910 2188
1911 2189 /**
1912 - * Plugin deactivation actions.
2190 + * Unschedule specific tasks:
2191 + * `wpo_cron_event2` => DB optimizations
2192 + * `wpo_weekly_cron_tasks` => ex: plugin.json auto update
2193 + *
2194 + * @return void
1913 2195 */
1914 -function wpo_deactivation_actions() {
1915 - WP_Optimize()->wpo_cron_deactivate();
1916 - WP_Optimize()->get_page_cache()->disable();
1917 - WP_Optimize()->get_minify()->plugin_deactivate();
1918 - WP_Optimize::get_gzip_compression()->disable();
1919 - WP_Optimize::get_browser_cache()->disable();
1920 -}
1921 -
1922 2196 function wpo_cron_deactivate() {
1923 2197 WP_Optimize()->log('running wpo_cron_deactivate()');
1924 2198 wp_clear_scheduled_hook('wpo_cron_event2');
1925 2199 wp_clear_scheduled_hook('wpo_weekly_cron_tasks');
1926 -}
1927 -
1928 -/**
1929 - * Plugin uninstall actions.
1930 - */
1931 -function wpo_uninstall_actions() {
1932 - WP_Optimize::get_gzip_compression()->disable();
1933 - WP_Optimize::get_browser_cache()->disable();
1934 - WP_Optimize()->get_options()->delete_all_options();
1935 - WP_Optimize()->get_minify()->plugin_uninstall();
1936 - WP_Optimize()->get_options()->wipe_settings();
1937 - WP_Optimize()->delete_transients_and_semaphores();
1938 2200 }
1939 2201
1940 2202 function WP_Optimize() {
1941 2203 return WP_Optimize::instance();