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 +562 -282 3.3.2 → 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.3.2
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,19 +17,21 @@
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.3.2');
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__));
22 24 define('WPO_PLUGIN_SLUG', plugin_basename(__FILE__));
23 25 define('WPO_PREMIUM_NOTIFICATION', false);
24 -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');
25 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');
26 30
27 31 class WP_Optimize {
28 32
29 - 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';
30 34
31 35 private $template_directories;
32 36
33 37 protected static $_instance = null;
@@ -34,13 +38,49 @@
34 38
35 39 protected $_cache_init_status = null;
36 40
37 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 + );
55 +
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 + }
62 +
63 + /**
38 64 * Class constructor
39 65 */
40 66 public function __construct() {
67 +
41 68 spl_autoload_register(array($this, 'loader'));
42 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 +
43 83 // Checks if premium is installed along with plugins needed.
44 84 add_action('plugins_loaded', array($this, 'plugins_loaded'), 1);
45 85
46 86 register_activation_hook(__FILE__, array('WPO_Activation', 'actions'));
@@ -45,15 +85,14 @@
45 85
46 86 register_activation_hook(__FILE__, array('WPO_Activation', 'actions'));
47 87 register_deactivation_hook(__FILE__, array('WPO_Deactivation', 'actions'));
48 88 register_uninstall_hook(__FILE__, array('WPO_Uninstall', 'actions'));
49 -
89 +
90 + WPO_Page_Optimizer::instance()->maybe_initialise();
91 +
50 92 $this->load_admin();
51 93 add_action('admin_init', array($this, 'admin_init'));
52 - add_action('admin_bar_menu', array($this, 'cache_admin_bar'), 100, 1);
53 94
54 - add_action('init', array($this, 'schedule_plugin_cron_tasks'));
55 -
56 95 add_filter("plugin_action_links_".plugin_basename(__FILE__), array($this, 'plugin_settings_link'));
57 96 add_action('wpo_cron_event2', array($this, 'cron_action'));
58 97 add_filter('cron_schedules', array($this, 'cron_schedules'));
59 98
@@ -58,22 +97,24 @@
58 97 add_filter('cron_schedules', array($this, 'cron_schedules'));
59 98
60 99 if (!$this->get_options()->get_option('installed-for', false)) $this->get_options()->update_option('installed-for', time());
61 100
62 - if (!self::is_premium()) {
63 - add_action('auto_option_settings', array($this->get_options(), 'auto_option_settings'));
64 - }
101 + add_action('admin_footer-upload.php', array($this, 'add_smush_popup_template'));
65 102
66 103 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
67 104
68 105 add_action('wp_enqueue_scripts', array($this, 'frontend_enqueue_scripts'));
69 106
107 + if ($this->get_options()->get_option('404_detector', 0)) {
108 + WP_Optimize_Performance::get_instance($this->get_404_detector())->hook_404_handler();
109 + }
110 +
70 111 $this->load_ajax_handler();
71 112
72 113 // Show update to Premium notice for non-premium multisite.
73 114 add_action('wpo_additional_options', array($this, 'show_multisite_update_to_premium_notice'));
74 115
75 - // Action column (show repair button if need).
116 + // Action column (show repair button, if needed).
76 117 add_filter('wpo_tables_list_additional_column_data', array($this, 'tables_list_additional_column_data'), 15, 2);
77 118
78 119 /**
79 120 * Add action for display Images > Compress images tab.
@@ -79,9 +120,9 @@
79 120 * Add action for display Images > Compress images tab.
80 121 */
81 122 add_action('wp_optimize_admin_page_wpo_images_smush', array($this, 'admin_page_wpo_images_smush'));
82 123
83 - include_once(WPO_PLUGIN_MAIN_PATH.'includes/updraftcentral.php');
124 + include_once(WPO_PLUGIN_MAIN_PATH.'includes/updraftcentral/updraftcentral.php');
84 125
85 126 include_once(WPO_PLUGIN_MAIN_PATH.'includes/backward-compatibility-functions.php');
86 127
87 128 register_shutdown_function(array($this, 'log_fatal_errors'));
@@ -90,9 +131,9 @@
90 131
91 132 add_action('upgrader_process_complete', array($this, 'detect_active_plugins_and_themes_updates'), 10, 2);
92 133
93 134 $import_done_hooks = array(
94 - 'import_end', // wordpress importer
135 + 'import_end', // WordPress importer
95 136 'pmxi_after_xml_import', // wp all import
96 137 );
97 138
98 139 $db_update_hooks = apply_filters('wp_optimize_db_update_hooks', $import_done_hooks);
@@ -101,17 +142,87 @@
101 142 add_action($hook, array($this, 'maybe_schedule_update_record_count_event'));
102 143 }
103 144 add_action('wpo_update_record_count_event', array($this->get_db_info(), 'wpo_update_record_count'));
104 145
105 - $this->set_heartbeat_api_filters();
146 + add_action('wpo_reset_stats_counter', array($this, 'reset_stats_counters'));
147 +
148 + $this->schedule_reset_stats_counters_event();
149 +
106 150 }
107 151
108 152 /**
109 - * 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
110 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 + *
111 222 * @param string $class_name The name of the class.
112 223 */
113 - private function loader($class_name) {
224 + public function loader($class_name) {
114 225 $dirs = $this->get_class_directories();
115 226
116 227 foreach ($dirs as $dir) {
117 228 $class_file = WPO_PLUGIN_MAIN_PATH . trailingslashit($dir) . 'class-' . str_replace('_', '-', strtolower($class_name)) . '.php';
@@ -118,9 +229,29 @@
118 229 if (file_exists($class_file)) {
119 230 require_once($class_file);
120 231 return;
121 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 + }
122 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 + }
123 254 }
124 255
125 256 /**
126 257 * Returns an array of class directories
@@ -131,11 +262,17 @@
131 262 return array(
132 263 'cache',
133 264 'compatibility',
134 265 'includes',
266 + 'includes/tables',
267 + 'includes/list-tables',
268 + 'includes/dashboard',
269 + 'includes/gravatars',
270 + 'includes/lcp',
135 271 'minify',
136 272 'optimizations',
137 273 'webp',
274 + 'includes/updraftcentral',
138 275 );
139 276 }
140 277
141 278 /**
@@ -145,8 +282,17 @@
145 282 $this->get_admin_instance();
146 283 }
147 284
148 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 + /**
149 295 * Returns Admin class instance
150 296 *
151 297 * @return WP_Optimize_Admin
152 298 */
@@ -167,11 +313,23 @@
167 313 $should_purge_cache = false;
168 314 $skin = $upgrader_object->skin;
169 315 if ('plugin' === $options['type']) {
170 316 // A plugin is updated using the default update system (upgrader_overwrote_package is used for the upload method)
171 - if (property_exists($skin, 'plugin_active') && $skin->plugin_active) {
172 - $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'];
173 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 + }
174 332 } elseif ('theme' === $options['type']) {
175 333 $active_theme = get_stylesheet();
176 334 $parent_theme = get_template();
177 335 // A theme is updated using the upload system
@@ -177,9 +335,9 @@
177 335 // A theme is updated using the upload system
178 336 if (isset($options['action']) && 'install' === $options['action'] && isset($skin->options['overwrite']) && 'update-theme' === $skin->options['overwrite']) {
179 337 $updated_theme = $upgrader_object->result['destination_name'];
180 338 // Check if the theme is in use
181 - if ($active_theme == $updated_theme || $parent_theme == $updated_theme) {
339 + if ($active_theme === $updated_theme || $parent_theme === $updated_theme) {
182 340 $should_purge_cache = true;
183 341 }
184 342 // A theme is updated using the classic update system
185 343 } elseif (isset($options['action']) && 'update' === $options['action'] && isset($options['themes']) && is_array($options['themes'])) {
@@ -208,10 +366,12 @@
208 366
209 367 public function admin_page_wpo_images_smush() {
210 368 $options = Updraft_Smush_Manager()->get_smush_options();
211 369 $custom = 90 >= $options['image_quality'] && 65 <= $options['image_quality'];
212 - $sites = WP_Optimize()->get_sites();
213 - $this->include_template('images/smush.php', false, array('smush_options' => $options, 'custom' => $custom, 'sites' => $sites, 'does_server_allows_local_webp_conversion' => $this->does_server_allows_local_webp_conversion()));
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();
214 374 }
215 375
216 376 public static function instance() {
217 377 if (empty(self::$_instance)) {
@@ -227,13 +387,29 @@
227 387 /**
228 388 * Adds 3rd party plugin compatibilities.
229 389 */
230 390 public function load_compatibilities() {
231 - WPO_Polylang_Compatibility::instance();
391 + global $wp_version;
392 +
393 + if (class_exists('Polylang')) {
394 + WPO_Polylang_Compatibility::instance();
395 + }
396 +
397 + if (version_compare($wp_version, '5.2', '<')) {
398 + WPO_WP_Kses_Compatibility::instance();
399 + }
400 +
232 401 WPO_Page_Builder_Compatibility::instance();
233 - WPO_Custom_Permalink_Compatibility::instance();
234 - WPO_TranslatePress_Compatibility::instance();
402 + WPO_KD_Submissions_Compatibility::instance();
235 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 +
236 412 do_action('wpo_load_compatibilities');
237 413 }
238 414
239 415 /**
@@ -253,58 +429,59 @@
253 429 return WP_Optimize_Notices::instance();
254 430 }
255 431
256 432 /**
257 - * Returns instance if WPO_Page_Cache class.
433 + * Get and instantiate WP_Optimize_Delay_JS
258 434 *
259 - * @return WPO_Page_Cache
435 + * @return WP_Optimize_Delay_JS
260 436 */
261 - public function get_page_cache() {
262 - return WPO_Page_Cache::instance();
437 + private function get_delay_js() {
438 + return WP_Optimize_Delay_JS::instance();
263 439 }
264 440
265 441 /**
266 - * Returns instance if WP_Optimize_WebP class.
442 + * Returns instance of WPO_Page_Optimizer class.
267 443 *
268 - * @return WP_Optimize_WebP
444 + * @return WPO_Page_Optimizer
269 445 */
270 - public function get_webp_instance() {
271 - return WP_Optimize_WebP::get_instance();
446 + public function get_page_optimizer() {
447 + return WPO_Page_Optimizer::instance();
272 448 }
273 449
274 450 /**
275 - * Detects if the platform is Kinsta or not
451 + * Returns instance 404 Detector Cron class.
276 452 *
277 - * @return bool Returns true if it is Kinsta platform, otherwise returns false
453 + * @return WP_Optimize_404_Detector_Cron
278 454 */
279 - private function is_kinsta() {
280 - return isset($_SERVER['KINSTA_CACHE_ZONE']);
455 + private function get_404_detector_cron() {
456 + return WP_Optimize_404_Detector_Cron::get_instance();
281 457 }
282 458
283 459 /**
284 - * Detects whether the server handles cache. eg. Nginx cache
460 + * Returns instance of WPO_Page_Cache class.
461 + *
462 + * @return WPO_Page_Cache
285 463 */
286 - public function does_server_handles_cache() {
287 - return $this->is_kinsta();
464 + public function get_page_cache() {
465 + return WPO_Page_Cache::instance();
288 466 }
289 467
290 468 /**
291 - * Detects whether the server supports table optimization.
469 + * Returns instance of WP_Optimize_WebP class.
292 470 *
293 - * Some servers prevent table optimization
294 - * because InnoDB engine does not optimize table
295 - * instead it drops tables and recreate them
296 - * which results in elevated disk write operations
471 + * @return WP_Optimize_WebP
297 472 */
298 - public function does_server_allows_table_optimization() {
299 - return !$this->is_kinsta();
473 + public function get_webp_instance() {
474 + return WP_Optimize_WebP::get_instance();
300 475 }
301 476
302 477 /**
303 - * 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
304 481 */
305 - private function does_server_allows_local_webp_conversion() {
306 - return !$this->is_kinsta();
482 + public function get_server_compatibility_instance(): WP_Optimize_Server_Compatibility {
483 + return WP_Optimize_Server_Compatibility::get_instance();
307 484 }
308 485
309 486 /**
310 487 * Create instance of WP_Optimize_Browser_Cache.
@@ -366,8 +543,9 @@
366 543 /**
367 544 * Enqueue scripts and styles on WP-Optimize pages.
368 545 */
369 546 public function admin_enqueue_scripts() {
547 + global $wp_version;
370 548 $enqueue_version = $this->get_enqueue_version();
371 549 $min_or_not = $this->get_min_or_not_string();
372 550 $min_or_not_internal = $this->get_min_or_not_internal_string();
373 551
@@ -373,42 +551,55 @@
373 551
374 552 // Register or enqueue common scripts
375 553 wp_register_script('wp-optimize-send-command', WPO_PLUGIN_URL.'js/send-command'.$min_or_not_internal.'.js', array(), $enqueue_version);
376 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);
377 556 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
378 557
379 558 // load scripts and styles only on WP-Optimize pages.
380 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);
381 569
382 570 wp_enqueue_script('jquery-serialize-json', WPO_PLUGIN_URL.'js/serialize-json/jquery.serializejson'.$min_or_not.'.js', array('jquery'), $enqueue_version);
383 571
384 572 wp_register_script('updraft-queue-js', WPO_PLUGIN_URL.'js/queue'.$min_or_not_internal.'.js', array(), $enqueue_version);
385 573
386 - wp_enqueue_script('wp-optimize-heartbeat-js', WPO_PLUGIN_URL.'js/heartbeat'.$min_or_not_internal.'.js', array('jquery'), $enqueue_version);
387 - wp_localize_script('wp-optimize-heartbeat-js', 'wpo_heartbeat_ajax', array(
388 - 'ajaxurl' => admin_url('admin-ajax.php'),
389 - 'nonce' => wp_create_nonce('heartbeat-nonce'),
390 - 'interval' => WPO_Ajax::HEARTBEAT_INTERVAL
391 - ));
392 -
393 574 wp_enqueue_script('wp-optimize-modal', WPO_PLUGIN_URL.'js/modal'.$min_or_not_internal.'.js', array('jquery', 'backbone', 'wp-util'), $enqueue_version);
394 - 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'), $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);
395 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);
396 577 wp_enqueue_style('wp-optimize-admin-css', WPO_PLUGIN_URL.'css/wp-optimize-admin'.$min_or_not_internal.'.css', array(), $enqueue_version);
397 - // Using tablesorter to help with organising the DB size on Table Information
398 - // https://github.com/Mottie/tablesorter
399 - 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);
400 582
401 - 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);
402 584
403 - // wp_enqueue_style('tablesorter-css', WPO_PLUGIN_URL.'css/tablesorter/theme.default.min.css', array(), $enqueue_version);
404 -
405 585 $js_variables = $this->wpo_js_translations();
406 586 $js_variables['loggers_classes_info'] = $this->get_loggers_classes_info();
407 -
408 587 wp_localize_script('wp-optimize-admin-js', 'wpoptimize', $js_variables);
409 588
410 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 +
411 602 }
412 603
413 604 /**
414 605 * Enqueue any required front-end scripts
@@ -415,11 +606,11 @@
415 606 *
416 607 * @return void
417 608 */
418 609 public function frontend_enqueue_scripts() {
419 - if (!current_user_can('manage_options') || !is_admin_bar_showing()) return;
610 + if (!$this->current_user_can('manage_options') || !is_admin_bar_showing()) return;
420 611 $enqueue_version = $this->get_enqueue_version();
421 - $min_or_not_internal = WP_Optimize()->get_min_or_not_internal_string();
612 + $min_or_not_internal = $this->get_min_or_not_internal_string();
422 613
423 614 // Register or enqueue common scripts
424 615 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
425 616 }
@@ -425,8 +616,10 @@
425 616 }
426 617
427 618 /**
428 619 * Load Task Manager
620 + *
621 + * @return Updraft_Smush_Manager
429 622 */
430 623 public function get_task_manager() {
431 624 include_once(WPO_PLUGIN_MAIN_PATH.'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php');
432 625
@@ -444,9 +637,9 @@
444 637
445 638 /**
446 639 * Indicate whether we have an associated instance of WP-Optimize Premium or not.
447 640 *
448 - * @returns Boolean
641 + * @returns bool
449 642 */
450 643 public static function is_premium() {
451 644 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && function_exists('WP_Optimize_Premium')) {
452 645 $wp_optimize_premium = WP_Optimize_Premium();
@@ -461,9 +654,9 @@
461 654 * @return bool
462 655 */
463 656 public function is_apache_server() {
464 657 global $is_apache;
465 - return $is_apache;
658 + return (bool) $is_apache;
466 659 }
467 660
468 661 /**
469 662 * Check if script running on IIS web server.
@@ -507,12 +700,23 @@
507 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.
508 701 */
509 702 public function plugins_loaded() {
510 703
511 - 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()) {
512 716 add_filter('wp_optimize_admin_page_wpo_cache_tabs', array($this, 'filter_cache_tabs'), 99, 1);
513 717
514 - // If newly migrated to server that handles cache, disable wpo cache
718 + // If newly migrated to a server that handles cache, disable wpo cache
515 719 $cache = $this->get_page_cache();
516 720 if ($cache->is_enabled()) {
517 721 $cache->disable();
518 722 }
@@ -524,23 +728,28 @@
524 728 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && !class_exists('WP_Optimize_Premium')) {
525 729 include_once(WPO_PLUGIN_MAIN_PATH.'premium.php');
526 730 }
527 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 +
528 737 // load defaults
529 - WP_Optimize()->get_options()->set_default_options();
738 + $this->get_options()->set_default_options();
530 739
531 740 // Initialize loggers.
532 - $this->setup_loggers();
741 + add_action('init', array($this, 'setup_loggers'));
533 742
534 743 if ($this->is_active('premium') && false !== ($free_plugin = $this->is_active('free'))) {
535 744 if (!function_exists('deactivate_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
536 745 deactivate_plugins($free_plugin);
537 746
538 - // 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.
539 748 if (defined('WPO_ADVANCED_CACHE') && WPO_ADVANCED_CACHE) {
540 749 $advanced_cache_filename = trailingslashit(WP_CONTENT_DIR) . 'advanced-cache.php';
541 750
542 - 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))) {
543 752 file_put_contents($advanced_cache_filename, '');
544 753 }
545 754 }
546 755
@@ -548,23 +757,29 @@
548 757 add_action('admin_notices', array($this, 'show_admin_notice_premium'));
549 758 return;
550 759 }
551 760
552 - // Loads the task manager
553 - $this->get_task_manager();
761 + add_action('init', array($this, 'init'));
554 762
555 - // Loads the language file.
556 - 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 + }
557 766
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();
775 + $this->init_page_cache();
776 + $this->get_minify();
777 + $this->get_delay_js();
778 +
558 779 // Load 3rd party plugin compatibilities.
559 780 $this->load_compatibilities();
560 781
561 - // Load page cache.
562 - $this->get_page_cache();
563 - $this->init_page_cache();
564 -
565 - // Include minify
566 - $this->get_minify();
567 782 $this->run_updates();
568 783
569 784 // We need this here because webp can be unavailable because of server moves
570 785 // This deletes already converted webp images and original image file when a media is deleted
@@ -569,18 +784,29 @@
569 784 // We need this here because webp can be unavailable because of server moves
570 785 // This deletes already converted webp images and original image file when a media is deleted
571 786 WP_Optimize_WebP_Images::get_instance();
572 787
788 + // Loads the task manager
789 + $this->get_task_manager();
790 +
573 791 // Include WebP
574 792 if (WPO_USE_WEBP_CONVERSION) {
575 793 $this->get_webp_instance();
576 794 }
577 795
578 - // add_filter('updraftcentral_host_plugins', array($this, 'attach_updraftcentral_host'));
579 - // if (file_exists(WPO_PLUGIN_MAIN_PATH.'central/factory.php')) include_once(WPO_PLUGIN_MAIN_PATH.'central/factory.php');
796 + $this->get_onboarding()->init();
580 797 }
581 798
582 799 /**
800 + * Loads the language file.
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 + /**
583 809 * Attach this wp-optimize plugin as host of the UpdraftCentral libraries
584 810 * (e.g. "central" folder)
585 811 *
586 812 * @param array $hosts List of plugins having the "central" library integrated into them
@@ -592,9 +818,9 @@
592 818 return $hosts;
593 819 }
594 820
595 821 /**
596 - * Filter cache tabs (when it is Kinsta)
822 + * Filter cache tabs (when the server handles it or do not allow it, like Kinsta)
597 823 *
598 824 * @param array $tabs An array of tabs
599 825 *
600 826 * @return array $tabs An array of tabs
@@ -609,18 +835,18 @@
609 835
610 836 /**
611 837 * Check whether one of free/Premium is active (whether it is this instance or not)
612 838 *
613 - * @param String $which - 'free' or 'premium'
839 + * @param string $which - 'free' or 'premium'
614 840 *
615 - * @return String|Boolean - plugin path (if installed) or false if not
841 + * @return string|boolean - plugin path (if installed) or false if not
616 842 */
617 - private function is_active($which = 'free') {
843 + public function is_active($which = 'free') {
618 844 $active_plugins = $this->get_active_plugins();
619 845 foreach ($active_plugins as $file) {
620 846 if ('wp-optimize.php' == basename($file)) {
621 847 $plugin_dir = WP_PLUGIN_DIR.'/'.dirname($file);
622 - 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;
623 849 }
624 850 }
625 851 return false;
626 852 }
@@ -627,14 +853,13 @@
627 853
628 854 /**
629 855 * Gets an array of plugins active on either the current site, or site-wide
630 856 *
631 - * @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)
632 858 */
633 859 private function get_active_plugins() {
634 -
635 860 // Gets all active plugins on the current site
636 - $active_plugins = get_option('active_plugins');
861 + $active_plugins = (array) get_option('active_plugins', array());
637 862
638 863 if (is_multisite()) {
639 864 $network_active_plugins = get_site_option('active_sitewide_plugins');
640 865 if (!empty($network_active_plugins)) {
@@ -668,9 +893,9 @@
668 893
669 894 // Loops around each plugin available.
670 895 foreach ($get_plugins as $key => $value) {
671 896 // If the plugin name matches that of the specified name, it will gather details.
672 - if ($value['Name'] != $name && $value['TextDomain'] != $name) continue;
897 + if ($value['Name'] !== $name && $value['TextDomain'] !== $name) continue;
673 898 $plugin_info['installed'] = true;
674 899 $plugin_info['name'] = $key;
675 900 $plugin_info['version'] = $value['Version'];
676 901 if (in_array($key, $active_plugins)) {
@@ -684,10 +909,10 @@
684 909 /**
685 910 * This is a notice to show users that premium is installed
686 911 */
687 912 public function show_admin_notice_premium() {
688 - 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>';
689 - 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.
690 915 }
691 916
692 917 /**
693 918 * Show update to Premium notice for non-premium multisite.
@@ -694,9 +919,9 @@
694 919 */
695 920 public function show_multisite_update_to_premium_notice() {
696 921 if (!is_multisite() || self::is_premium()) return;
697 922
698 - 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>';
699 924 }
700 925
701 926 public function admin_init() {
702 927 $pagenow = $GLOBALS['pagenow'];
@@ -702,9 +927,9 @@
702 927 $pagenow = $GLOBALS['pagenow'];
703 928
704 929 $this->register_template_directories();
705 930
706 - 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)) {
707 932 $options = $this->get_options();
708 933
709 934 $dismissed_until = $options->get_option('dismiss_dash_notice_until', 0);
710 935
@@ -720,8 +945,11 @@
720 945
721 946 if ($this->is_wp_smush_installed()) {
722 947 add_filter('transient_wp-smush-conflict_check', array($this, 'modify_wp_smush_conflict_check'), 9, 1);
723 948 }
949 + if ($this->get_options()->get_option('404_detector', 0)) {
950 + WP_Optimize_Performance::get_instance($this->get_404_detector())->init();
951 + }
724 952 }
725 953
726 954 /**
727 955 * Checks whether the WP Smush plugin is active or not
@@ -747,9 +975,9 @@
747 975 return $active_plugins;
748 976 }
749 977
750 978 /**
751 - * Get the install or update notice instance
979 + * Get the installation or update notice instance
752 980 *
753 981 * @return WP_Optimize_Install_Or_Update_Notice
754 982 */
755 983 public function get_install_or_update_notice() {
@@ -766,14 +994,42 @@
766 994 $this->include_template('notices/thanks-for-using-main-dash.php', false, array(
767 995 'is_premium' => WP_Optimize::is_premium()
768 996 ));
769 997 }
770 -
998 +
999 + /**
1000 + * Returns required capability string
1001 + *
1002 + * @return string
1003 + */
771 1004 public function capability_required() {
772 - return apply_filters('wp_optimize_capability_required', 'manage_options');
1005 + $capability = is_multisite() ? 'manage_network_options' : 'manage_options';
1006 +
1007 + $filtered_capability = apply_filters('wp_optimize_capability_required', $capability);
1008 +
1009 + return is_string($filtered_capability) ? $filtered_capability : $capability;
773 1010 }
774 1011
775 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
1022 + }
1023 +
1024 + if (current_user_can($capability)) {
1025 + return true;
1026 + }
1027 +
1028 + return false;
1029 + }
1030 +
1031 + /**
776 1032 * Returns array of translations used in javascript code.
777 1033 *
778 1034 * @return array
779 1035 */
@@ -778,9 +1034,9 @@
778 1034 * @return array
779 1035 */
780 1036 public function wpo_js_translations() {
781 1037 $log_message = __('For more details, please check your logs configured in logging destinations settings.', 'wp-optimize');
782 - return apply_filters('wpo_js_translations', array(
1038 + $translations = array(
783 1039 'automatic_backup_before_optimizations' => __('Automatic backup before optimizations', 'wp-optimize'),
784 1040 'error_unexpected_response' => __('An unexpected response was received.', 'wp-optimize'),
785 1041 'optimization_complete' => __('Optimization complete', 'wp-optimize'),
786 1042 'with_warnings' => __('(with warnings - open the browser console for more details)', 'wp-optimize'),
@@ -794,10 +1050,13 @@
794 1050 'disable' => __('Disable', 'wp-optimize'),
795 1051 'please_select_settings_file' => __('Please, select settings file.', 'wp-optimize'),
796 1052 'are_you_sure_you_want_to_remove_logging_destination' => __('Are you sure you want to remove this logging destination?', 'wp-optimize'),
797 1053 'fill_all_settings_fields' => __('Before saving, you need to complete the currently incomplete settings (or remove them).', 'wp-optimize'),
1054 + // translators: %s is the table name
798 1055 'table_was_not_repaired' => __('%s was not repaired.', 'wp-optimize') . ' ' . $log_message,
1056 + // translators: %s is the table name
799 1057 'table_was_not_deleted' => __('%s was not deleted.', 'wp-optimize') . ' ' . $log_message,
1058 + // translators: %s is the table name
800 1059 'table_was_not_converted' => __('%s was not converted to InnoDB.', 'wp-optimize') . ' ' . $log_message,
801 1060 'please_use_positive_integers' => __('Please use positive integers.', 'wp-optimize'),
802 1061 'please_use_valid_values' => __('Please use valid values.', 'wp-optimize'),
803 1062 'update' => __('Update', 'wp-optimize'),
@@ -807,19 +1066,26 @@
807 1066 'current_cache_size' => __('Current cache size:', 'wp-optimize'),
808 1067 'number_of_files' => __('Number of files:', 'wp-optimize'),
809 1068 'toggle_info' => __('Show information', 'wp-optimize'),
810 1069 'delete_file' => __('Delete', 'wp-optimize'),
1070 + 'deleting' => __('Deleting...', 'wp-optimize'),
811 1071 'added_to_list' => __('Added to the list', 'wp-optimize'),
812 1072 'added_notice' => __('The file was added to the list', 'wp-optimize'),
813 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'),
814 1077 'page_refresh' => __('Refreshing the page to reflect changes...', 'wp-optimize'),
815 1078 'cache_file_not_found' => __('Cache file was not found.', 'wp-optimize'),
816 1079 'settings_have_been_deleted_successfully' => __('WP-Optimize settings have been deleted successfully.', 'wp-optimize'),
817 1080 'loading_data' => __('Loading data...', 'wp-optimize'),
818 - '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'),
819 1083 'settings_page_url' => is_multisite() ? network_admin_url('admin.php?page=wpo_settings') : admin_url('admin.php?page=wpo_settings'),
820 1084 'sites' => $this->get_sites(),
821 - '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),
822 1088 'post_meta_tweak_completed' => __('The tweak has been performed.', 'wp-optimize'),
823 1089 'no_minified_assets' => __('No minified files are present', 'wp-optimize'),
824 1090 'network_site_url' => network_site_url(),
825 1091 'export_settings_file_name' => 'wpoptimize-settings-'.sanitize_title(get_bloginfo('name')).'.json',
@@ -829,65 +1095,57 @@
829 1095 'importing_data_from' => __('This will import data from:', 'wp-optimize'),
830 1096 'exported_on' => __('Which was exported on:', 'wp-optimize'),
831 1097 'continue_import' => __('Do you want to carry out the import?', 'wp-optimize'),
832 1098 'select_destination' => __('Select destination', 'wp-optimize'),
833 - ));
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;
834 1120 }
835 1121
836 1122 /**
837 - * Manages the admin bar menu for caching (currently page and minify)
838 - */
839 - public function cache_admin_bar($wp_admin_bar) {
840 -
841 - $options = $this->get_options();
842 - if (!$options->get_option('enable_cache_in_admin_bar', true)) return;
843 -
844 - /**
845 - * The "purge cache" menu items
846 - *
847 - * @param array $menu_items - The menu items, in the format required by $wp_admin_bar->add_menu()
848 - * @param object $wp_admin_bar
849 - */
850 - $menu_items = apply_filters('wpo_cache_admin_bar_menu_items', array(), $wp_admin_bar);
851 -
852 - if (empty($menu_items) || !is_array($menu_items)) return;
853 -
854 - $wp_admin_bar->add_menu(array(
855 - 'id' => 'wpo_purge_cache',
856 - 'title' => __('Purge cache', 'wp-optimize'),
857 - 'href' => '#',
858 - 'meta' => array(
859 - 'title' => __('Purge cache', 'wp-optimize'),
860 - ),
861 - 'parent' => false,
862 - ));
863 -
864 - foreach ($menu_items as $item) {
865 - $wp_admin_bar->add_menu($item);
866 - }
867 - }
868 -
869 - /**
870 1123 * Add settings link on plugin page
871 1124 *
872 - * @param string $links Passing through the URL to be used within the HREF.
873 - * @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.
874 1127 */
875 1128 public function plugin_settings_link($links) {
876 1129
877 1130 $admin_page_url = $this->get_options()->admin_page_url();
878 1131 $settings_page_url = $this->get_options()->admin_page_url('wpo_settings');
1132 + $database_page_url = $this->get_options()->admin_page_url('wpo_database');
879 1133
880 - if (false == self::is_premium()) {
881 - $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>';
882 1136 array_unshift($links, $premium_link);
883 1137 }
884 1138
885 - $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>';
886 1140 array_unshift($links, $settings_link);
887 1141
888 - $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>';
889 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 +
890 1148 return $links;
891 1149 }
892 1150
893 1151 /**
@@ -901,9 +1159,9 @@
901 1159 public function tables_list_additional_column_data($content, $table_info) {
902 1160 if ($table_info->is_needing_repair) {
903 1161 $content .= '<div class="wpo_button_wrap">'
904 1162 . '<button class="button button-secondary run-single-table-repair" data-table="' . esc_attr($table_info->Name) . '">' . __('Repair', 'wp-optimize') . '</button>'
905 - . '<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
906 1164 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
907 1165 . '</div>';
908 1166 }
909 1167
@@ -910,18 +1168,18 @@
910 1168 // table belongs to plugin.
911 1169 if ($table_info->can_be_removed) {
912 1170 $content .= '<div>'
913 1171 . '<button class="button button-secondary run-single-table-delete" data-table="' . esc_attr($table_info->Name) . '">' . __('Remove', 'wp-optimize') . '</button>'
914 - . '<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
915 1173 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
916 1174 . '</div>';
917 1175 }
918 1176
919 1177 // Add option for MyISAM to InnoDB conversion.
920 - if ('MyISAM' == $table_info->Engine) {
1178 + if ('MyISAM' === $table_info->Engine) {
921 1179 $content .= '<div class="wpo_button_convert wpo_button_wrap">'
922 1180 . '<button class="button button-secondary toinnodb" data-table="' . esc_attr($table_info->Name) . '">' . __('Convert to InnoDB', 'wp-optimize') . '</button>'
923 - . '<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
924 1182 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
925 1183 . '</div>';
926 1184
927 1185 }
@@ -931,9 +1189,9 @@
931 1189
932 1190 /**
933 1191 * Initialize WP-Optimize page cache.
934 1192 */
935 - public function init_page_cache() {
1193 + private function init_page_cache() {
936 1194 if ($this->get_page_cache()->config->get_option('enable_page_caching', false)) {
937 1195 if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
938 1196 $this->_cache_init_status = $this->get_page_cache()->enable();
939 1197 }
@@ -942,9 +1200,9 @@
942 1200
943 1201 /**
944 1202 * Get init_page_cache() status.
945 1203 *
946 - * @return bool
1204 + * @return bool|WP_Error
947 1205 */
948 1206 public function get_init_page_cache_status() {
949 1207 return $this->_cache_init_status;
950 1208 }
@@ -958,17 +1216,17 @@
958 1216 $gmt_offset = (int) (3600 * get_option('gmt_offset'));
959 1217
960 1218 $options = $this->get_options();
961 1219
962 - if ($options->get_option('schedule') === false) {
1220 + if (false === $options->get_option('schedule')) {
963 1221 $options->set_default_options();
964 1222 } else {
965 - if ('true' == $options->get_option('schedule')) {
1223 + if ('true' === $options->get_option('schedule')) {
966 1224 if (!wp_next_scheduled('wpo_cron_event2')) {
967 1225 $schedule_type = $options->get_option('schedule-type', 'wpo_weekly');
968 1226
969 1227 // Backward compatibility
970 - if ('wpo_otherweekly' == $schedule_type) $schedule_type = 'wpo_fortnightly';
1228 + if ('wpo_otherweekly' === $schedule_type) $schedule_type = 'wpo_fortnightly';
971 1229
972 1230 $this_time = (86400 * 7);
973 1231
974 1232 switch ($schedule_type) {
@@ -990,15 +1248,15 @@
990 1248 }
991 1249
992 1250 add_action('wpo_cron_event2', array($this, 'cron_action'));
993 1251 $result = wp_schedule_event((current_time("timestamp", 0) + $this_time - $gmt_offset), $schedule_type, 'wpo_cron_event2');
994 - WP_Optimize()->log('running wp_schedule_event()');
1252 + $this->log('running wp_schedule_event()');
995 1253 if (is_wp_error($result)) {
996 1254 $error_msg = $result->get_error_message();
997 - WP_Optimize()->log($error_msg);
998 - 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
999 1257 } else {
1000 - WP_Optimize()->log($result);
1258 + $this->log($result);
1001 1259 }
1002 1260 }
1003 1261 }
1004 1262 }
@@ -1004,13 +1262,31 @@
1004 1262 }
1005 1263 }
1006 1264
1007 1265 /**
1008 - * Clears all cron events
1266 + * Unschedules WP-Optimize cron events on deactivation.
1009 1267 *
1010 1268 * @return void
1011 1269 */
1012 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() {
1013 1289 $cron_jobs = _get_cron_array();
1014 1290 foreach ($cron_jobs as $job) {
1015 1291 foreach (array_keys($job) as $hook) {
1016 1292 if (preg_match('/^wpo_/', $hook)) wp_unschedule_hook($hook);
@@ -1056,8 +1332,12 @@
1056 1332 /**
1057 1333 * Run updates on plugin activation.
1058 1334 */
1059 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');
1060 1340 include_once(WPO_PLUGIN_MAIN_PATH.'includes/class-wp-optimize-updates.php');
1061 1341 WP_Optimize_Updates::check_updates();
1062 1342 }
1063 1343
@@ -1068,13 +1348,21 @@
1068 1348 * @return string
1069 1349 */
1070 1350 public function show_admin_warning_overdue_crons($howmany) {
1071 1351 $ret = '<div class="updated below-h2"><p>';
1072 - $ret .= '<strong>'.__('Warning', 'wp-optimize').':</strong> '.sprintf(__('WordPress has a number (%d) of scheduled tasks which are overdue.', 'wp-optimize'), $howmany).' '. __('Unless this is a development site, this probably means that the scheduler in your WordPress install is not working.', 'wp-optimize').' <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>';
1073 1354 $ret .= '</p></div>';
1074 1355 return $ret;
1075 1356 }
1076 1357
1358 + /**
1359 + * Normalizes path string
1360 + *
1361 + * @param string $path
1362 + *
1363 + * @return string
1364 + */
1077 1365 private function wp_normalize_path($path) {
1078 1366 // Wp_normalize_path is not present before WP 3.9.
1079 1367 if (function_exists('wp_normalize_path')) return wp_normalize_path($path);
1080 1368 // Taken from WP 4.6.
@@ -1085,24 +1373,38 @@
1085 1373 }
1086 1374 return $path;
1087 1375 }
1088 1376
1377 + /**
1378 + * Returns templates directory path
1379 + *
1380 + * @return string
1381 + */
1089 1382 public function get_templates_dir() {
1090 - 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;
1091 1386 }
1092 1387
1388 + /**
1389 + * Returns templates URL
1390 + *
1391 + * @return string
1392 + */
1093 1393 public function get_templates_url() {
1094 - 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;
1095 1397 }
1096 1398
1097 1399 /**
1098 1400 * Return or output view content
1099 1401 *
1100 - * @param String $path - path to template, usually relative to templates/ within the WP-O directory
1101 - * @param Boolean $return_instead_of_echo - what to do with the results
1102 - * @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
1103 1405 *
1104 - * @return String|Void
1406 + * @return string|void
1105 1407 */
1106 1408 public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) {
1107 1409 if ($return_instead_of_echo) ob_start();
1108 1410
@@ -1122,10 +1424,10 @@
1122 1424
1123 1425 do_action('wp_optimize_before_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1124 1426
1125 1427 if (!file_exists($template_file)) {
1126 - error_log("WP Optimize: template not found: ".$template_file);
1127 - 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).")";
1128 1430 } else {
1129 1431 extract($extract_these);
1130 1432 // The following are useful variables which can be used in the template.
1131 1433 // They appear as unused, but may be used in the $template_file.
@@ -1152,9 +1454,9 @@
1152 1454 $templates_dir = $this->get_templates_dir();
1153 1455
1154 1456 if ($dh = opendir($templates_dir)) {
1155 1457 while (($file = readdir($dh)) !== false) {
1156 - if ('.' == $file || '..' == $file) continue;
1458 + if ('.' === $file || '..' === $file) continue;
1157 1459 if (is_dir($templates_dir.'/'.$file)) {
1158 1460 $template_directories[$file] = $templates_dir.'/'.$file;
1159 1461 }
1160 1462 }
@@ -1184,9 +1486,9 @@
1184 1486 * Format Bytes Into KB/MB
1185 1487 *
1186 1488 * @param mixed $bytes Number of bytes to be converted.
1187 1489 * @param integer $decimals the number of decimal digits
1188 - * @return integer return the correct format size.
1490 + * @return string return the correct format size.
1189 1491 */
1190 1492 public function format_size($bytes, $decimals = 2) {
1191 1493 if (!is_numeric($bytes)) return __('N/A', 'wp-optimize');
1192 1494
@@ -1197,9 +1499,9 @@
1197 1499 } elseif (1024 <= $bytes) {
1198 1500 $bytes = number_format($bytes / 1024, $decimals) . ' KB';
1199 1501 } elseif (1 < $bytes) {
1200 1502 $bytes = $bytes . ' bytes';
1201 - } elseif (1 == $bytes) {
1503 + } elseif (1 === (int) $bytes) {
1202 1504 $bytes = $bytes . ' byte';
1203 1505 } else {
1204 1506 $bytes = '0 bytes';
1205 1507 }
@@ -1207,15 +1509,16 @@
1207 1509 return $bytes;
1208 1510 }
1209 1511
1210 1512 /**
1211 - * Format a timestamp into a juman readable date time
1513 + * Format a timestamp into a human-readable date time
1212 1514 *
1213 - * @param int $timestamp
1515 + * @param int $timestamp Epoch timestamp to convert
1516 + * @param string $separator String separator
1214 1517 * @return string
1215 1518 */
1216 - public function format_date_time($timestamp) {
1217 - 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));
1218 1521 }
1219 1522
1220 1523 /**
1221 1524 * Executed this function on cron event.
@@ -1228,9 +1531,9 @@
1228 1531 $options = $this->get_options();
1229 1532
1230 1533 $this->log('WPO: Starting cron_action()');
1231 1534 $options->update_option('last-optimized', time());
1232 - if ('true' == $options->get_option('schedule')) {
1535 + if ('true' === $options->get_option('schedule')) {
1233 1536 $this_options = $options->get_option('auto');
1234 1537
1235 1538 // Currently the output of the optimizations is not saved/used/logged.
1236 1539 $optimizer->do_optimizations($this_options, 'auto');
@@ -1242,14 +1545,17 @@
1242 1545 * Schedule cron tasks used by plugin.
1243 1546 *
1244 1547 * @return void
1245 1548 */
1246 - public function schedule_plugin_cron_tasks() {
1549 + private function schedule_plugin_cron_tasks() {
1247 1550 if (!wp_next_scheduled('wpo_weekly_cron_tasks')) {
1248 - 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');
1249 1552 }
1250 1553
1251 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();
1252 1558 }
1253 1559
1254 1560 /**
1255 1561 * Do plugin background tasks.
@@ -1262,16 +1568,16 @@
1262 1568 }
1263 1569
1264 1570 /**
1265 1571 * This will customize a URL with a correct Affiliate link
1266 - * 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
1267 1573 *
1268 - * @param String $url - URL to be check to see if it an updraftplus match.
1269 - * @param String $text - Text to be entered within the href a tags.
1270 - * @param String $html - Any specific HTML to be added. Supplied parameter will not be escaped in this function. Provide escaped, safe HTML
1271 - * @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"').
1272 - * @param Boolean $return_instead_of_echo - if set, then the result will be returned, not echo-ed.
1273 - * @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
1274 1580 */
1275 1581 public function wp_optimize_url($url, $text = '', $html = '', $attrs = '', $return_instead_of_echo = false) {
1276 1582 // Check if the URL is UpdraftPlus.
1277 1583 $url = $this->maybe_add_affiliate_params($url); // Return URL - check if there is HTML such as images.
@@ -1285,9 +1591,9 @@
1285 1591 foreach ($attrs as $attr => $value) {
1286 1592 $str_attrs .= $attr . '="' . esc_attr($value) . '" ';
1287 1593 }
1288 1594 } else {
1289 - // If $attrs is empty, the explode function will only return an empty array.
1595 + // If $attrs is empty, the `explode` function will only return an empty array.
1290 1596 $attrs = explode('=', $attrs);
1291 1597 // Check if $attrs in positions 1 and 2 are not empty and exist; otherwise, return an empty string.
1292 1598 $str_attrs = !empty($attrs[0]) && !empty($attrs[1]) ? $attrs[0] . '="' . esc_attr(str_replace('"', '', $attrs[1])) . '"' : '';
1293 1599 }
@@ -1302,13 +1608,13 @@
1302 1608 $result = sprintf(
1303 1609 '<a href="%s" %s>%s</a>',
1304 1610 esc_url($url),
1305 1611 $str_attrs,
1306 - $content
1612 + wp_kses_post($content)
1307 1613 );
1308 1614
1309 1615 if ($return_instead_of_echo) return $result;
1310 - echo $result;
1616 + echo wp_kses_post($result);
1311 1617 }
1312 1618
1313 1619 /**
1314 1620 * Check if a URL is external
@@ -1313,9 +1619,9 @@
1313 1619 /**
1314 1620 * Check if a URL is external
1315 1621 *
1316 1622 * @param string $url
1317 - * @return string
1623 + * @return bool
1318 1624 */
1319 1625 public function is_external_url($url) {
1320 1626 if (empty($url)) {
1321 1627 return false;
@@ -1328,9 +1634,9 @@
1328 1634 return $current_domain !== $url;
1329 1635 }
1330 1636
1331 1637 /**
1332 - * Get an URL with an eventual affiliate ID
1638 + * Get a URL with an eventual affiliate ID
1333 1639 *
1334 1640 * @param string $url
1335 1641 * @return string
1336 1642 */
@@ -1342,9 +1648,10 @@
1342 1648
1343 1649 // Apply filters.
1344 1650 $url = apply_filters('wpoptimize_updraftplus_com_link', $url);
1345 1651 }
1346 - 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;
1347 1654 }
1348 1655
1349 1656 /**
1350 1657 * Setup WPO logger(s)
@@ -1405,9 +1712,9 @@
1405 1712 // check for previous version options format.
1406 1713 $keys = array_keys($saved_loggers);
1407 1714
1408 1715 // if options stored in old format then reformat it.
1409 - if (false == is_numeric($keys[0])) {
1716 + if (false === is_numeric($keys[0])) {
1410 1717 $_saved_loggers = array();
1411 1718 foreach ($saved_loggers as $logger_id => $enabled) {
1412 1719 if ($enabled) {
1413 1720 $_saved_loggers[] = $logger_id;
@@ -1438,9 +1745,9 @@
1438 1745 foreach (array_keys($logger_options) as $option_name) {
1439 1746 if (array_key_exists($option_name, $options_keys)) {
1440 1747 $options_keys[$option_name]++;
1441 1748 } else {
1442 - $options_keys[$option_name] = 0;
1749 + $options_keys[$option_name] = 0;
1443 1750 }
1444 1751
1445 1752 $option_value = isset($logger_additional_options[$option_name][$options_keys[$option_name]]) ? $logger_additional_options[$option_name][$options_keys[$option_name]] : '';
1446 1753
@@ -1465,11 +1772,10 @@
1465 1772 $loggers[] = $logger;
1466 1773 }
1467 1774 }
1468 1775
1469 - $loggers = apply_filters('wp_optimize_loggers', $loggers);
1470 -
1471 - return $loggers;
1776 + $filtered_loggers = apply_filters('wp_optimize_loggers', $loggers);
1777 + return is_array($filtered_loggers) ? $filtered_loggers : $loggers;
1472 1778 }
1473 1779
1474 1780 /**
1475 1781 * Returns associative array with logger class name in a key and path to class file in a value.
@@ -1537,17 +1843,17 @@
1537 1843 return (is_multisite() && self::is_premium());
1538 1844 }
1539 1845
1540 1846 /**
1541 - * Returns true if current user can run optimizations.
1847 + * Returns true if the current user can run optimizations.
1542 1848 *
1543 1849 * @return bool
1544 1850 */
1545 - public function can_run_optimizations() {
1851 + public function can_run_optimizations(): bool {
1546 1852 // we don't check permissions for cron jobs.
1547 1853 if (defined('DOING_CRON') && DOING_CRON) return true;
1548 1854
1549 - 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;
1550 1856 return true;
1551 1857 }
1552 1858
1553 1859 /**
@@ -1566,13 +1872,10 @@
1566 1872 * @return array
1567 1873 */
1568 1874 public function get_sites() {
1569 1875 $sites = array();
1570 - // check if function get_sites exists (since 4.6.0) else use wp_get_sites.
1571 1876 if (function_exists('get_sites')) {
1572 1877 $sites = get_sites(array('network_id' => null, 'deleted' => 0, 'number' => 999999));
1573 - } elseif (function_exists('wp_get_sites')) {
1574 - $sites = wp_get_sites(array('network_id' => null, 'deleted' => 0, 'limit' => 999999));
1575 1878 }
1576 1879 return $sites;
1577 1880 }
1578 1881
@@ -1578,9 +1881,9 @@
1578 1881
1579 1882 /**
1580 1883 * Returns script memory limit in megabytes.
1581 1884 *
1582 - * @param bool $memory_limit
1885 + * @param string|bool $memory_limit
1583 1886 * @return int
1584 1887 */
1585 1888 public function get_memory_limit($memory_limit = false) {
1586 1889 // Returns in megabytes
@@ -1650,8 +1953,10 @@
1650 1953 * Converts shorthand memory notation value to bytes.
1651 1954 * From http://php.net/manual/en/function.ini-get.php
1652 1955 *
1653 1956 * @param string $val shorthand memory notation value.
1957 + *
1958 + * @return string
1654 1959 */
1655 1960 public function return_bytes($val) {
1656 1961 $val = trim($val);
1657 1962 $last = strtolower($val[strlen($val)-1]);
@@ -1683,9 +1988,9 @@
1683 1988
1684 1989 /**
1685 1990 * Close browser connection and continue script work. - Taken from UpdraftPlus
1686 1991 *
1687 - * @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)
1688 1993 * @return void
1689 1994 */
1690 1995 public function close_browser_connection($txt = '') {
1691 1996 if (!headers_sent()) {
@@ -1697,9 +2002,9 @@
1697 2002 }
1698 2003
1699 2004 if (session_id()) session_write_close();
1700 2005 echo "\r\n\r\n";
1701 - echo $txt;
2006 + echo $txt; // phpcs:ignore WordPress.Security.EscapeOutput -- Output is already escaped
1702 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).
1703 2008 $ob_level = ob_get_level();
1704 2009 while ($ob_level > 0) {
1705 2010 ob_end_flush();
@@ -1710,46 +2015,18 @@
1710 2015 if (function_exists('litespeed_finish_request')) litespeed_finish_request();
1711 2016 }
1712 2017
1713 2018 /**
1714 - * Get the current theme's style.css headers
1715 - *
1716 - * @return array|WP_Error
1717 - */
1718 - public function get_stylesheet_headers() {
1719 - static $headers;
1720 - if (isset($headers)) return $headers;
1721 -
1722 - $style = get_template_directory_uri() . '/style.css';
1723 -
1724 - /**
1725 - * Filters wp_remote_get parameters, when checking if browser cache is enabled.
1726 - *
1727 - * @param array $request_params Default parameters
1728 - */
1729 - $request_params = apply_filters('wpoptimize_get_stylesheet_headers_args', array('timeout' => 10));
1730 -
1731 - // trying to load style.css.
1732 - $response = wp_remote_get($style, $request_params);
1733 -
1734 - if (is_a($response, 'WP_Error')) return $response;
1735 -
1736 - $headers = wp_remote_retrieve_headers($response);
1737 -
1738 - if (method_exists($headers, 'getAll')) {
1739 - $headers = $headers->getAll();
1740 - }
1741 -
1742 - return is_array($headers) ? $headers : array();
1743 - }
1744 -
1745 - /**
1746 2019 * Try to change PHP script time limit.
1747 2020 */
1748 2021 public function change_time_limit() {
1749 2022 $time_limit = (defined('WP_OPTIMIZE_SET_TIME_LIMIT') && WP_OPTIMIZE_SET_TIME_LIMIT > 15) ? WP_OPTIMIZE_SET_TIME_LIMIT : 1800;
1750 2023
1751 - @set_time_limit($time_limit); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Try to reduce the chances of PHP self-terminating via reaching max_execution_time.
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
1752 2029 }
1753 2030
1754 2031 /**
1755 2032 * Does the request come from UDC
@@ -1766,11 +2043,11 @@
1766 2043 * @return boolean
1767 2044 */
1768 2045 public function template_should_include_data() {
1769 2046 /**
1770 - * Filters wether data should be included in certain templates or not.
2047 + * Filters whether data should be included in certain templates or not.
1771 2048 */
1772 - 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());
1773 2050 }
1774 2051
1775 2052 /**
1776 2053 * Load the templates for the modal window
@@ -1779,8 +2056,18 @@
1779 2056 $this->include_template('modal.php');
1780 2057 }
1781 2058
1782 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 + /**
1783 2070 * Delete transients and semaphores data from options table.
1784 2071 */
1785 2072 public function delete_transients_and_semaphores() {
1786 2073 global $wpdb;
@@ -1804,19 +2091,23 @@
1804 2091 foreach ($masks as $mask) {
1805 2092 $where_parts[] = "(`option_name` LIKE '{$mask}')";
1806 2093 }
1807 2094
1808 - $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));
1809 2097 }
1810 2098
1811 2099 /**
1812 2100 * Prevents bots from indexing plugins list
2101 + *
2102 + * @param string $output
2103 + * @return string
1813 2104 */
1814 2105 public function robots_txt($output) {
1815 2106 $upload_dir = wp_upload_dir();
1816 - $path = parse_url($upload_dir['baseurl']);
2107 + $path = wp_parse_url($upload_dir['baseurl']);
1817 2108 $output .= "\nUser-agent: *";
1818 - $output .= "\nDisallow: " . str_replace($path['scheme'].'://'.$path['host'], '', $upload_dir['baseurl']) . "/wpo-plugins-tables-list.json\n";
2109 + $output .= "\nDisallow: " . str_replace($path['scheme'].'://'.$path['host'], '', $upload_dir['baseurl']) . "/wpo/wpo-plugins-tables-list.json\n";
1819 2110 return $output;
1820 2111 }
1821 2112
1822 2113 /**
@@ -1853,57 +2144,46 @@
1853 2144 WPO_Ajax::get_instance();
1854 2145 }
1855 2146
1856 2147 /**
1857 - * Set custom heartbeat API interval
2148 + * Get instance of WP_Optimize_Table_Management
1858 2149 *
1859 - * @param array $settings Current WP settings
1860 - * @return array
2150 + * @return WP_Optimize_Table_Management
1861 2151 */
1862 - public function set_heartbeat_time_interval($settings) {
1863 - $settings['interval'] = WPO_Ajax::HEARTBEAT_INTERVAL;
1864 - return $settings;
2152 + public function get_table_management() {
2153 + return WP_Optimize_Table_Management::get_instance();
1865 2154 }
1866 2155
1867 2156 /**
1868 - * Receive Heartbeat data and respond.
2157 + * Get instance of WP_Optimize_404_Detector
1869 2158 *
1870 - * Processes data received via a Heartbeat request, and returns additional data to pass back to the front end.
2159 + * @return WP_Optimize_404_Detector
2160 + */
2161 + public function get_404_detector() {
2162 + return WP_Optimize_404_Detector::get_instance();
2163 + }
2164 +
2165 + /**
2166 + * Returns the message used to notify the user that the PHP version or WordPress version doesn't meet the requirements.
1871 2167 *
1872 - * @param array $response Heartbeat response data to pass back to front end.
1873 - * @param array $data Data received from the front end (unslashed).
1874 - *
1875 - * @return array
2168 + * @return string
1876 2169 */
1877 - public function receive_heartbeat($response, $data) {
1878 - foreach ($data as $uid => $command) {
1879 - $is_wpo_heartbeat = strpos($uid, 'wpo-heartbeat') === 0;
1880 -
1881 - if ($is_wpo_heartbeat) {
1882 - $response['callbacks'][$uid] = apply_filters('wp_optimize_heartbeat', $command);
1883 - }
1884 - }
1885 -
1886 - return $response;
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));
1887 2173 }
1888 2174
1889 2175 /**
1890 - * Add heartbeat API filters if the page is enabled in the array
2176 + * Output notice when PHP version or WordPress version is not meet for WP-Optimize.
1891 2177 *
1892 2178 * @return void
1893 2179 */
1894 - public function set_heartbeat_api_filters() {
1895 - $pages_enabled = array('WP-Optimize', 'wpo_images', 'wpo_cache', 'wpo_minify', 'wpo_settings', 'wpo_support', 'wpo_mayalso');
1896 -
1897 - // Change heartbeat API frequency to 15 seconds to improve UI experience
1898 - // only for the pages that we enable in `$pages_enabled`
1899 - $query_page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- not processing form data
1900 - if (in_array($query_page, $pages_enabled)) {
1901 - add_filter('heartbeat_settings', array($this, 'set_heartbeat_time_interval'), PHP_INT_MAX);
1902 - }
1903 -
1904 - // Handle heartbeat events
1905 - add_filter('heartbeat_received', array($this, 'receive_heartbeat'), 10, 2);
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
1906 2186 }
1907 2187 }
1908 2188
1909 2189 /**