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 +612 -244 3.2.21 → 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.21
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,30 +17,70 @@
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.21');
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 38
39 + protected $_cache_init_status = null;
40 +
35 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 + /**
36 64 * Class constructor
37 65 */
38 66 public function __construct() {
67 +
39 68 spl_autoload_register(array($this, 'loader'));
40 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 +
41 83 // Checks if premium is installed along with plugins needed.
42 84 add_action('plugins_loaded', array($this, 'plugins_loaded'), 1);
43 85
44 86 register_activation_hook(__FILE__, array('WPO_Activation', 'actions'));
@@ -43,15 +85,14 @@
43 85
44 86 register_activation_hook(__FILE__, array('WPO_Activation', 'actions'));
45 87 register_deactivation_hook(__FILE__, array('WPO_Deactivation', 'actions'));
46 88 register_uninstall_hook(__FILE__, array('WPO_Uninstall', 'actions'));
47 -
89 +
90 + WPO_Page_Optimizer::instance()->maybe_initialise();
91 +
48 92 $this->load_admin();
49 93 add_action('admin_init', array($this, 'admin_init'));
50 - add_action('admin_bar_menu', array($this, 'cache_admin_bar'), 100, 1);
51 94
52 - add_action('init', array($this, 'schedule_plugin_cron_tasks'));
53 -
54 95 add_filter("plugin_action_links_".plugin_basename(__FILE__), array($this, 'plugin_settings_link'));
55 96 add_action('wpo_cron_event2', array($this, 'cron_action'));
56 97 add_filter('cron_schedules', array($this, 'cron_schedules'));
57 98
@@ -56,22 +97,24 @@
56 97 add_filter('cron_schedules', array($this, 'cron_schedules'));
57 98
58 99 if (!$this->get_options()->get_option('installed-for', false)) $this->get_options()->update_option('installed-for', time());
59 100
60 - if (!self::is_premium()) {
61 - add_action('auto_option_settings', array($this->get_options(), 'auto_option_settings'));
62 - }
101 + add_action('admin_footer-upload.php', array($this, 'add_smush_popup_template'));
63 102
64 103 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
65 104
66 105 add_action('wp_enqueue_scripts', array($this, 'frontend_enqueue_scripts'));
67 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 +
68 111 $this->load_ajax_handler();
69 112
70 113 // Show update to Premium notice for non-premium multisite.
71 114 add_action('wpo_additional_options', array($this, 'show_multisite_update_to_premium_notice'));
72 115
73 - // Action column (show repair button if need).
116 + // Action column (show repair button, if needed).
74 117 add_filter('wpo_tables_list_additional_column_data', array($this, 'tables_list_additional_column_data'), 15, 2);
75 118
76 119 /**
77 120 * Add action for display Images > Compress images tab.
@@ -77,9 +120,9 @@
77 120 * Add action for display Images > Compress images tab.
78 121 */
79 122 add_action('wp_optimize_admin_page_wpo_images_smush', array($this, 'admin_page_wpo_images_smush'));
80 123
81 - include_once(WPO_PLUGIN_MAIN_PATH.'includes/updraftcentral.php');
124 + include_once(WPO_PLUGIN_MAIN_PATH.'includes/updraftcentral/updraftcentral.php');
82 125
83 126 include_once(WPO_PLUGIN_MAIN_PATH.'includes/backward-compatibility-functions.php');
84 127
85 128 register_shutdown_function(array($this, 'log_fatal_errors'));
@@ -88,9 +131,9 @@
88 131
89 132 add_action('upgrader_process_complete', array($this, 'detect_active_plugins_and_themes_updates'), 10, 2);
90 133
91 134 $import_done_hooks = array(
92 - 'import_end', // wordpress importer
135 + 'import_end', // WordPress importer
93 136 'pmxi_after_xml_import', // wp all import
94 137 );
95 138
96 139 $db_update_hooks = apply_filters('wp_optimize_db_update_hooks', $import_done_hooks);
@@ -98,16 +141,88 @@
98 141 foreach ($db_update_hooks as $hook) {
99 142 add_action($hook, array($this, 'maybe_schedule_update_record_count_event'));
100 143 }
101 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 +
102 150 }
103 151
104 152 /**
105 - * 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
106 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 + *
107 222 * @param string $class_name The name of the class.
108 223 */
109 - private function loader($class_name) {
224 + public function loader($class_name) {
110 225 $dirs = $this->get_class_directories();
111 226
112 227 foreach ($dirs as $dir) {
113 228 $class_file = WPO_PLUGIN_MAIN_PATH . trailingslashit($dir) . 'class-' . str_replace('_', '-', strtolower($class_name)) . '.php';
@@ -114,9 +229,29 @@
114 229 if (file_exists($class_file)) {
115 230 require_once($class_file);
116 231 return;
117 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 + }
118 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 + }
119 254 }
120 255
121 256 /**
122 257 * Returns an array of class directories
@@ -127,11 +262,17 @@
127 262 return array(
128 263 'cache',
129 264 'compatibility',
130 265 'includes',
266 + 'includes/tables',
267 + 'includes/list-tables',
268 + 'includes/dashboard',
269 + 'includes/gravatars',
270 + 'includes/lcp',
131 271 'minify',
132 272 'optimizations',
133 273 'webp',
274 + 'includes/updraftcentral',
134 275 );
135 276 }
136 277
137 278 /**
@@ -141,8 +282,17 @@
141 282 $this->get_admin_instance();
142 283 }
143 284
144 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 + /**
145 295 * Returns Admin class instance
146 296 *
147 297 * @return WP_Optimize_Admin
148 298 */
@@ -163,11 +313,23 @@
163 313 $should_purge_cache = false;
164 314 $skin = $upgrader_object->skin;
165 315 if ('plugin' === $options['type']) {
166 316 // A plugin is updated using the default update system (upgrader_overwrote_package is used for the upload method)
167 - if (property_exists($skin, 'plugin_active') && $skin->plugin_active) {
168 - $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'];
169 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 + }
170 332 } elseif ('theme' === $options['type']) {
171 333 $active_theme = get_stylesheet();
172 334 $parent_theme = get_template();
173 335 // A theme is updated using the upload system
@@ -173,9 +335,9 @@
173 335 // A theme is updated using the upload system
174 336 if (isset($options['action']) && 'install' === $options['action'] && isset($skin->options['overwrite']) && 'update-theme' === $skin->options['overwrite']) {
175 337 $updated_theme = $upgrader_object->result['destination_name'];
176 338 // Check if the theme is in use
177 - if ($active_theme == $updated_theme || $parent_theme == $updated_theme) {
339 + if ($active_theme === $updated_theme || $parent_theme === $updated_theme) {
178 340 $should_purge_cache = true;
179 341 }
180 342 // A theme is updated using the classic update system
181 343 } elseif (isset($options['action']) && 'update' === $options['action'] && isset($options['themes']) && is_array($options['themes'])) {
@@ -204,10 +366,12 @@
204 366
205 367 public function admin_page_wpo_images_smush() {
206 368 $options = Updraft_Smush_Manager()->get_smush_options();
207 369 $custom = 90 >= $options['image_quality'] && 65 <= $options['image_quality'];
208 - $sites = WP_Optimize()->get_sites();
209 - $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();
210 374 }
211 375
212 376 public static function instance() {
213 377 if (empty(self::$_instance)) {
@@ -223,12 +387,29 @@
223 387 /**
224 388 * Adds 3rd party plugin compatibilities.
225 389 */
226 390 public function load_compatibilities() {
227 - 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 +
228 401 WPO_Page_Builder_Compatibility::instance();
229 - WPO_Custom_Permalink_Compatibility::instance();
230 -
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 +
231 412 do_action('wpo_load_compatibilities');
232 413 }
233 414
234 415 /**
@@ -248,58 +429,59 @@
248 429 return WP_Optimize_Notices::instance();
249 430 }
250 431
251 432 /**
252 - * Returns instance if WPO_Page_Cache class.
433 + * Get and instantiate WP_Optimize_Delay_JS
253 434 *
254 - * @return WPO_Page_Cache
435 + * @return WP_Optimize_Delay_JS
255 436 */
256 - public function get_page_cache() {
257 - return WPO_Page_Cache::instance();
437 + private function get_delay_js() {
438 + return WP_Optimize_Delay_JS::instance();
258 439 }
259 440
260 441 /**
261 - * Returns instance if WP_Optimize_WebP class.
442 + * Returns instance of WPO_Page_Optimizer class.
262 443 *
263 - * @return WP_Optimize_WebP
444 + * @return WPO_Page_Optimizer
264 445 */
265 - public function get_webp_instance() {
266 - return WP_Optimize_WebP::get_instance();
446 + public function get_page_optimizer() {
447 + return WPO_Page_Optimizer::instance();
267 448 }
268 449
269 450 /**
270 - * Detects if the platform is Kinsta or not
451 + * Returns instance 404 Detector Cron class.
271 452 *
272 - * @return bool Returns true if it is Kinsta platform, otherwise returns false
453 + * @return WP_Optimize_404_Detector_Cron
273 454 */
274 - private function is_kinsta() {
275 - return isset($_SERVER['KINSTA_CACHE_ZONE']);
455 + private function get_404_detector_cron() {
456 + return WP_Optimize_404_Detector_Cron::get_instance();
276 457 }
277 458
278 459 /**
279 - * Detects whether the server handles cache. eg. Nginx cache
460 + * Returns instance of WPO_Page_Cache class.
461 + *
462 + * @return WPO_Page_Cache
280 463 */
281 - public function does_server_handles_cache() {
282 - return $this->is_kinsta();
464 + public function get_page_cache() {
465 + return WPO_Page_Cache::instance();
283 466 }
284 467
285 468 /**
286 - * Detects whether the server supports table optimization.
469 + * Returns instance of WP_Optimize_WebP class.
287 470 *
288 - * Some servers prevent table optimization
289 - * because InnoDB engine does not optimize table
290 - * instead it drops tables and recreate them
291 - * which results in elevated disk write operations
471 + * @return WP_Optimize_WebP
292 472 */
293 - public function does_server_allows_table_optimization() {
294 - return !$this->is_kinsta();
473 + public function get_webp_instance() {
474 + return WP_Optimize_WebP::get_instance();
295 475 }
296 476
297 477 /**
298 - * 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
299 481 */
300 - private function does_server_allows_local_webp_conversion() {
301 - return !$this->is_kinsta();
482 + public function get_server_compatibility_instance(): WP_Optimize_Server_Compatibility {
483 + return WP_Optimize_Server_Compatibility::get_instance();
302 484 }
303 485
304 486 /**
305 487 * Create instance of WP_Optimize_Browser_Cache.
@@ -361,8 +543,9 @@
361 543 /**
362 544 * Enqueue scripts and styles on WP-Optimize pages.
363 545 */
364 546 public function admin_enqueue_scripts() {
547 + global $wp_version;
365 548 $enqueue_version = $this->get_enqueue_version();
366 549 $min_or_not = $this->get_min_or_not_string();
367 550 $min_or_not_internal = $this->get_min_or_not_internal_string();
368 551
@@ -368,34 +551,55 @@
368 551
369 552 // Register or enqueue common scripts
370 553 wp_register_script('wp-optimize-send-command', WPO_PLUGIN_URL.'js/send-command'.$min_or_not_internal.'.js', array(), $enqueue_version);
371 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);
372 556 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
373 557
374 558 // load scripts and styles only on WP-Optimize pages.
375 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);
376 569
377 570 wp_enqueue_script('jquery-serialize-json', WPO_PLUGIN_URL.'js/serialize-json/jquery.serializejson'.$min_or_not.'.js', array('jquery'), $enqueue_version);
378 571
379 572 wp_register_script('updraft-queue-js', WPO_PLUGIN_URL.'js/queue'.$min_or_not_internal.'.js', array(), $enqueue_version);
573 +
380 574 wp_enqueue_script('wp-optimize-modal', WPO_PLUGIN_URL.'js/modal'.$min_or_not_internal.'.js', array('jquery', 'backbone', 'wp-util'), $enqueue_version);
381 - 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);
382 - 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'), $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);
383 577 wp_enqueue_style('wp-optimize-admin-css', WPO_PLUGIN_URL.'css/wp-optimize-admin'.$min_or_not_internal.'.css', array(), $enqueue_version);
384 - // Using tablesorter to help with organising the DB size on Table Information
385 - // https://github.com/Mottie/tablesorter
386 - 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);
387 582
388 - 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);
389 584
390 - // wp_enqueue_style('tablesorter-css', WPO_PLUGIN_URL.'css/tablesorter/theme.default.min.css', array(), $enqueue_version);
391 -
392 585 $js_variables = $this->wpo_js_translations();
393 586 $js_variables['loggers_classes_info'] = $this->get_loggers_classes_info();
394 -
395 587 wp_localize_script('wp-optimize-admin-js', 'wpoptimize', $js_variables);
396 588
397 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 +
398 602 }
399 603
400 604 /**
401 605 * Enqueue any required front-end scripts
@@ -402,11 +606,11 @@
402 606 *
403 607 * @return void
404 608 */
405 609 public function frontend_enqueue_scripts() {
406 - if (!current_user_can('manage_options') || !is_admin_bar_showing()) return;
610 + if (!$this->current_user_can('manage_options') || !is_admin_bar_showing()) return;
407 611 $enqueue_version = $this->get_enqueue_version();
408 - $min_or_not_internal = WP_Optimize()->get_min_or_not_internal_string();
612 + $min_or_not_internal = $this->get_min_or_not_internal_string();
409 613
410 614 // Register or enqueue common scripts
411 615 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
412 616 }
@@ -412,8 +616,10 @@
412 616 }
413 617
414 618 /**
415 619 * Load Task Manager
620 + *
621 + * @return Updraft_Smush_Manager
416 622 */
417 623 public function get_task_manager() {
418 624 include_once(WPO_PLUGIN_MAIN_PATH.'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php');
419 625
@@ -431,9 +637,9 @@
431 637
432 638 /**
433 639 * Indicate whether we have an associated instance of WP-Optimize Premium or not.
434 640 *
435 - * @returns Boolean
641 + * @returns bool
436 642 */
437 643 public static function is_premium() {
438 644 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && function_exists('WP_Optimize_Premium')) {
439 645 $wp_optimize_premium = WP_Optimize_Premium();
@@ -448,9 +654,9 @@
448 654 * @return bool
449 655 */
450 656 public function is_apache_server() {
451 657 global $is_apache;
452 - return $is_apache;
658 + return (bool) $is_apache;
453 659 }
454 660
455 661 /**
456 662 * Check if script running on IIS web server.
@@ -494,12 +700,23 @@
494 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.
495 701 */
496 702 public function plugins_loaded() {
497 703
498 - 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()) {
499 716 add_filter('wp_optimize_admin_page_wpo_cache_tabs', array($this, 'filter_cache_tabs'), 99, 1);
500 717
501 - // If newly migrated to server that handles cache, disable wpo cache
718 + // If newly migrated to a server that handles cache, disable wpo cache
502 719 $cache = $this->get_page_cache();
503 720 if ($cache->is_enabled()) {
504 721 $cache->disable();
505 722 }
@@ -511,23 +728,28 @@
511 728 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && !class_exists('WP_Optimize_Premium')) {
512 729 include_once(WPO_PLUGIN_MAIN_PATH.'premium.php');
513 730 }
514 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 +
515 737 // load defaults
516 - WP_Optimize()->get_options()->set_default_options();
738 + $this->get_options()->set_default_options();
517 739
518 740 // Initialize loggers.
519 - $this->setup_loggers();
741 + add_action('init', array($this, 'setup_loggers'));
520 742
521 743 if ($this->is_active('premium') && false !== ($free_plugin = $this->is_active('free'))) {
522 744 if (!function_exists('deactivate_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
523 745 deactivate_plugins($free_plugin);
524 746
525 - // 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.
526 748 if (defined('WPO_ADVANCED_CACHE') && WPO_ADVANCED_CACHE) {
527 749 $advanced_cache_filename = trailingslashit(WP_CONTENT_DIR) . 'advanced-cache.php';
528 750
529 - 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))) {
530 752 file_put_contents($advanced_cache_filename, '');
531 753 }
532 754 }
533 755
@@ -535,23 +757,29 @@
535 757 add_action('admin_notices', array($this, 'show_admin_notice_premium'));
536 758 return;
537 759 }
538 760
539 - // Loads the task manager
540 - $this->get_task_manager();
761 + add_action('init', array($this, 'init'));
541 762
542 - // Loads the language file.
543 - 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 + }
544 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 +
545 779 // Load 3rd party plugin compatibilities.
546 780 $this->load_compatibilities();
547 781
548 - // Load page cache.
549 - $this->get_page_cache();
550 - $this->init_page_cache();
551 -
552 - // Include minify
553 - $this->get_minify();
554 782 $this->run_updates();
555 783
556 784 // We need this here because webp can be unavailable because of server moves
557 785 // This deletes already converted webp images and original image file when a media is deleted
@@ -556,18 +784,29 @@
556 784 // We need this here because webp can be unavailable because of server moves
557 785 // This deletes already converted webp images and original image file when a media is deleted
558 786 WP_Optimize_WebP_Images::get_instance();
559 787
788 + // Loads the task manager
789 + $this->get_task_manager();
790 +
560 791 // Include WebP
561 - if (WP_Optimize_WebP::is_shell_functions_available() && WPO_USE_WEBP_CONVERSION) {
792 + if (WPO_USE_WEBP_CONVERSION) {
562 793 $this->get_webp_instance();
563 794 }
564 795
565 - // add_filter('updraftcentral_host_plugins', array($this, 'attach_updraftcentral_host'));
566 - // if (file_exists(WPO_PLUGIN_MAIN_PATH.'central/factory.php')) include_once(WPO_PLUGIN_MAIN_PATH.'central/factory.php');
796 + $this->get_onboarding()->init();
567 797 }
568 798
569 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 + /**
570 809 * Attach this wp-optimize plugin as host of the UpdraftCentral libraries
571 810 * (e.g. "central" folder)
572 811 *
573 812 * @param array $hosts List of plugins having the "central" library integrated into them
@@ -579,9 +818,9 @@
579 818 return $hosts;
580 819 }
581 820
582 821 /**
583 - * Filter cache tabs (when it is Kinsta)
822 + * Filter cache tabs (when the server handles it or do not allow it, like Kinsta)
584 823 *
585 824 * @param array $tabs An array of tabs
586 825 *
587 826 * @return array $tabs An array of tabs
@@ -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,9 +927,9 @@
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
@@ -700,9 +938,9 @@
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 945
708 946 if ($this->is_wp_smush_installed()) {
@@ -707,8 +945,11 @@
707 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,17 +986,50 @@
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');
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;
755 1010 }
756 1011
757 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 + /**
758 1032 * Returns array of translations used in javascript code.
759 1033 *
760 1034 * @return array
761 1035 */
@@ -760,9 +1034,9 @@
760 1034 * @return array
761 1035 */
762 1036 public function wpo_js_translations() {
763 1037 $log_message = __('For more details, please check your logs configured in logging destinations settings.', 'wp-optimize');
764 - return apply_filters('wpo_js_translations', array(
1038 + $translations = array(
765 1039 'automatic_backup_before_optimizations' => __('Automatic backup before optimizations', 'wp-optimize'),
766 1040 'error_unexpected_response' => __('An unexpected response was received.', 'wp-optimize'),
767 1041 'optimization_complete' => __('Optimization complete', 'wp-optimize'),
768 1042 'with_warnings' => __('(with warnings - open the browser console for more details)', 'wp-optimize'),
@@ -776,10 +1050,13 @@
776 1050 'disable' => __('Disable', 'wp-optimize'),
777 1051 'please_select_settings_file' => __('Please, select settings file.', 'wp-optimize'),
778 1052 'are_you_sure_you_want_to_remove_logging_destination' => __('Are you sure you want to remove this logging destination?', 'wp-optimize'),
779 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
780 1055 'table_was_not_repaired' => __('%s was not repaired.', 'wp-optimize') . ' ' . $log_message,
1056 + // translators: %s is the table name
781 1057 'table_was_not_deleted' => __('%s was not deleted.', 'wp-optimize') . ' ' . $log_message,
1058 + // translators: %s is the table name
782 1059 'table_was_not_converted' => __('%s was not converted to InnoDB.', 'wp-optimize') . ' ' . $log_message,
783 1060 'please_use_positive_integers' => __('Please use positive integers.', 'wp-optimize'),
784 1061 'please_use_valid_values' => __('Please use valid values.', 'wp-optimize'),
785 1062 'update' => __('Update', 'wp-optimize'),
@@ -789,19 +1066,26 @@
789 1066 'current_cache_size' => __('Current cache size:', 'wp-optimize'),
790 1067 'number_of_files' => __('Number of files:', 'wp-optimize'),
791 1068 'toggle_info' => __('Show information', 'wp-optimize'),
792 1069 'delete_file' => __('Delete', 'wp-optimize'),
1070 + 'deleting' => __('Deleting...', 'wp-optimize'),
793 1071 'added_to_list' => __('Added to the list', 'wp-optimize'),
794 1072 'added_notice' => __('The file was added to the list', 'wp-optimize'),
795 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'),
796 1077 'page_refresh' => __('Refreshing the page to reflect changes...', 'wp-optimize'),
797 1078 'cache_file_not_found' => __('Cache file was not found.', 'wp-optimize'),
798 1079 'settings_have_been_deleted_successfully' => __('WP-Optimize settings have been deleted successfully.', 'wp-optimize'),
799 1080 'loading_data' => __('Loading data...', 'wp-optimize'),
800 - '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'),
801 1083 'settings_page_url' => is_multisite() ? network_admin_url('admin.php?page=wpo_settings') : admin_url('admin.php?page=wpo_settings'),
802 1084 'sites' => $this->get_sites(),
803 - '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),
804 1088 'post_meta_tweak_completed' => __('The tweak has been performed.', 'wp-optimize'),
805 1089 'no_minified_assets' => __('No minified files are present', 'wp-optimize'),
806 1090 'network_site_url' => network_site_url(),
807 1091 'export_settings_file_name' => 'wpoptimize-settings-'.sanitize_title(get_bloginfo('name')).'.json',
@@ -811,65 +1095,57 @@
811 1095 'importing_data_from' => __('This will import data from:', 'wp-optimize'),
812 1096 'exported_on' => __('Which was exported on:', 'wp-optimize'),
813 1097 'continue_import' => __('Do you want to carry out the import?', 'wp-optimize'),
814 1098 'select_destination' => __('Select destination', 'wp-optimize'),
815 - ));
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;
816 1120 }
817 1121
818 1122 /**
819 - * Manages the admin bar menu for caching (currently page and minify)
820 - */
821 - public function cache_admin_bar($wp_admin_bar) {
822 -
823 - $options = $this->get_options();
824 - if (!$options->get_option('enable_cache_in_admin_bar', true)) return;
825 -
826 - /**
827 - * The "purge cache" menu items
828 - *
829 - * @param array $menu_items - The menu items, in the format required by $wp_admin_bar->add_menu()
830 - * @param object $wp_admin_bar
831 - */
832 - $menu_items = apply_filters('wpo_cache_admin_bar_menu_items', array(), $wp_admin_bar);
833 -
834 - if (empty($menu_items) || !is_array($menu_items)) return;
835 -
836 - $wp_admin_bar->add_menu(array(
837 - 'id' => 'wpo_purge_cache',
838 - 'title' => __('Purge cache', 'wp-optimize'),
839 - 'href' => '#',
840 - 'meta' => array(
841 - 'title' => __('Purge cache', 'wp-optimize'),
842 - ),
843 - 'parent' => false,
844 - ));
845 -
846 - foreach ($menu_items as $item) {
847 - $wp_admin_bar->add_menu($item);
848 - }
849 - }
850 -
851 - /**
852 1123 * Add settings link on plugin page
853 1124 *
854 - * @param string $links Passing through the URL to be used within the HREF.
855 - * @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.
856 1127 */
857 1128 public function plugin_settings_link($links) {
858 1129
859 1130 $admin_page_url = $this->get_options()->admin_page_url();
860 1131 $settings_page_url = $this->get_options()->admin_page_url('wpo_settings');
1132 + $database_page_url = $this->get_options()->admin_page_url('wpo_database');
861 1133
862 - if (false == self::is_premium()) {
863 - $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>';
864 1136 array_unshift($links, $premium_link);
865 1137 }
866 1138
867 - $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>';
868 1140 array_unshift($links, $settings_link);
869 1141
870 - $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>';
871 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 +
872 1148 return $links;
873 1149 }
874 1150
875 1151 /**
@@ -883,9 +1159,9 @@
883 1159 public function tables_list_additional_column_data($content, $table_info) {
884 1160 if ($table_info->is_needing_repair) {
885 1161 $content .= '<div class="wpo_button_wrap">'
886 1162 . '<button class="button button-secondary run-single-table-repair" data-table="' . esc_attr($table_info->Name) . '">' . __('Repair', 'wp-optimize') . '</button>'
887 - . '<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
888 1164 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
889 1165 . '</div>';
890 1166 }
891 1167
@@ -892,18 +1168,18 @@
892 1168 // table belongs to plugin.
893 1169 if ($table_info->can_be_removed) {
894 1170 $content .= '<div>'
895 1171 . '<button class="button button-secondary run-single-table-delete" data-table="' . esc_attr($table_info->Name) . '">' . __('Remove', 'wp-optimize') . '</button>'
896 - . '<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
897 1173 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
898 1174 . '</div>';
899 1175 }
900 1176
901 1177 // Add option for MyISAM to InnoDB conversion.
902 - if ('MyISAM' == $table_info->Engine) {
1178 + if ('MyISAM' === $table_info->Engine) {
903 1179 $content .= '<div class="wpo_button_convert wpo_button_wrap">'
904 1180 . '<button class="button button-secondary toinnodb" data-table="' . esc_attr($table_info->Name) . '">' . __('Convert to InnoDB', 'wp-optimize') . '</button>'
905 - . '<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
906 1182 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
907 1183 . '</div>';
908 1184
909 1185 }
@@ -913,17 +1189,26 @@
913 1189
914 1190 /**
915 1191 * Initialize WP-Optimize page cache.
916 1192 */
917 - public function init_page_cache() {
1193 + private function init_page_cache() {
918 1194 if ($this->get_page_cache()->config->get_option('enable_page_caching', false)) {
919 1195 if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
920 - $this->get_page_cache()->enable();
1196 + $this->_cache_init_status = $this->get_page_cache()->enable();
921 1197 }
922 1198 }
923 1199 }
924 1200
925 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 + /**
926 1211 * Schedules cron event based on selected schedule type
927 1212 *
928 1213 * @return void
929 1214 */
@@ -931,17 +1216,17 @@
931 1216 $gmt_offset = (int) (3600 * get_option('gmt_offset'));
932 1217
933 1218 $options = $this->get_options();
934 1219
935 - if ($options->get_option('schedule') === false) {
1220 + if (false === $options->get_option('schedule')) {
936 1221 $options->set_default_options();
937 1222 } else {
938 - if ('true' == $options->get_option('schedule')) {
1223 + if ('true' === $options->get_option('schedule')) {
939 1224 if (!wp_next_scheduled('wpo_cron_event2')) {
940 1225 $schedule_type = $options->get_option('schedule-type', 'wpo_weekly');
941 1226
942 1227 // Backward compatibility
943 - if ('wpo_otherweekly' == $schedule_type) $schedule_type = 'wpo_fortnightly';
1228 + if ('wpo_otherweekly' === $schedule_type) $schedule_type = 'wpo_fortnightly';
944 1229
945 1230 $this_time = (86400 * 7);
946 1231
947 1232 switch ($schedule_type) {
@@ -963,15 +1248,15 @@
963 1248 }
964 1249
965 1250 add_action('wpo_cron_event2', array($this, 'cron_action'));
966 1251 $result = wp_schedule_event((current_time("timestamp", 0) + $this_time - $gmt_offset), $schedule_type, 'wpo_cron_event2');
967 - WP_Optimize()->log('running wp_schedule_event()');
1252 + $this->log('running wp_schedule_event()');
968 1253 if (is_wp_error($result)) {
969 1254 $error_msg = $result->get_error_message();
970 - WP_Optimize()->log($error_msg);
971 - 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
972 1257 } else {
973 - WP_Optimize()->log($result);
1258 + $this->log($result);
974 1259 }
975 1260 }
976 1261 }
977 1262 }
@@ -977,13 +1262,31 @@
977 1262 }
978 1263 }
979 1264
980 1265 /**
981 - * Clears all cron events
1266 + * Unschedules WP-Optimize cron events on deactivation.
982 1267 *
983 1268 * @return void
984 1269 */
985 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() {
986 1289 $cron_jobs = _get_cron_array();
987 1290 foreach ($cron_jobs as $job) {
988 1291 foreach (array_keys($job) as $hook) {
989 1292 if (preg_match('/^wpo_/', $hook)) wp_unschedule_hook($hook);
@@ -1029,8 +1332,12 @@
1029 1332 /**
1030 1333 * Run updates on plugin activation.
1031 1334 */
1032 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');
1033 1340 include_once(WPO_PLUGIN_MAIN_PATH.'includes/class-wp-optimize-updates.php');
1034 1341 WP_Optimize_Updates::check_updates();
1035 1342 }
1036 1343
@@ -1041,13 +1348,21 @@
1041 1348 * @return string
1042 1349 */
1043 1350 public function show_admin_warning_overdue_crons($howmany) {
1044 1351 $ret = '<div class="updated below-h2"><p>';
1045 - $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>';
1046 1354 $ret .= '</p></div>';
1047 1355 return $ret;
1048 1356 }
1049 1357
1358 + /**
1359 + * Normalizes path string
1360 + *
1361 + * @param string $path
1362 + *
1363 + * @return string
1364 + */
1050 1365 private function wp_normalize_path($path) {
1051 1366 // Wp_normalize_path is not present before WP 3.9.
1052 1367 if (function_exists('wp_normalize_path')) return wp_normalize_path($path);
1053 1368 // Taken from WP 4.6.
@@ -1058,24 +1373,38 @@
1058 1373 }
1059 1374 return $path;
1060 1375 }
1061 1376
1377 + /**
1378 + * Returns templates directory path
1379 + *
1380 + * @return string
1381 + */
1062 1382 public function get_templates_dir() {
1063 - 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;
1064 1386 }
1065 1387
1388 + /**
1389 + * Returns templates URL
1390 + *
1391 + * @return string
1392 + */
1066 1393 public function get_templates_url() {
1067 - 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;
1068 1397 }
1069 1398
1070 1399 /**
1071 1400 * Return or output view content
1072 1401 *
1073 - * @param String $path - path to template, usually relative to templates/ within the WP-O directory
1074 - * @param Boolean $return_instead_of_echo - what to do with the results
1075 - * @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
1076 1405 *
1077 - * @return String|Void
1406 + * @return string|void
1078 1407 */
1079 1408 public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) {
1080 1409 if ($return_instead_of_echo) ob_start();
1081 1410
@@ -1095,10 +1424,10 @@
1095 1424
1096 1425 do_action('wp_optimize_before_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1097 1426
1098 1427 if (!file_exists($template_file)) {
1099 - error_log("WP Optimize: template not found: ".$template_file);
1100 - 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).")";
1101 1430 } else {
1102 1431 extract($extract_these);
1103 1432 // The following are useful variables which can be used in the template.
1104 1433 // They appear as unused, but may be used in the $template_file.
@@ -1125,9 +1454,9 @@
1125 1454 $templates_dir = $this->get_templates_dir();
1126 1455
1127 1456 if ($dh = opendir($templates_dir)) {
1128 1457 while (($file = readdir($dh)) !== false) {
1129 - if ('.' == $file || '..' == $file) continue;
1458 + if ('.' === $file || '..' === $file) continue;
1130 1459 if (is_dir($templates_dir.'/'.$file)) {
1131 1460 $template_directories[$file] = $templates_dir.'/'.$file;
1132 1461 }
1133 1462 }
@@ -1157,9 +1486,9 @@
1157 1486 * Format Bytes Into KB/MB
1158 1487 *
1159 1488 * @param mixed $bytes Number of bytes to be converted.
1160 1489 * @param integer $decimals the number of decimal digits
1161 - * @return integer return the correct format size.
1490 + * @return string return the correct format size.
1162 1491 */
1163 1492 public function format_size($bytes, $decimals = 2) {
1164 1493 if (!is_numeric($bytes)) return __('N/A', 'wp-optimize');
1165 1494
@@ -1170,9 +1499,9 @@
1170 1499 } elseif (1024 <= $bytes) {
1171 1500 $bytes = number_format($bytes / 1024, $decimals) . ' KB';
1172 1501 } elseif (1 < $bytes) {
1173 1502 $bytes = $bytes . ' bytes';
1174 - } elseif (1 == $bytes) {
1503 + } elseif (1 === (int) $bytes) {
1175 1504 $bytes = $bytes . ' byte';
1176 1505 } else {
1177 1506 $bytes = '0 bytes';
1178 1507 }
@@ -1180,15 +1509,16 @@
1180 1509 return $bytes;
1181 1510 }
1182 1511
1183 1512 /**
1184 - * Format a timestamp into a juman readable date time
1513 + * Format a timestamp into a human-readable date time
1185 1514 *
1186 - * @param int $timestamp
1515 + * @param int $timestamp Epoch timestamp to convert
1516 + * @param string $separator String separator
1187 1517 * @return string
1188 1518 */
1189 - public function format_date_time($timestamp) {
1190 - 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));
1191 1521 }
1192 1522
1193 1523 /**
1194 1524 * Executed this function on cron event.
@@ -1201,9 +1531,9 @@
1201 1531 $options = $this->get_options();
1202 1532
1203 1533 $this->log('WPO: Starting cron_action()');
1204 1534 $options->update_option('last-optimized', time());
1205 - if ('true' == $options->get_option('schedule')) {
1535 + if ('true' === $options->get_option('schedule')) {
1206 1536 $this_options = $options->get_option('auto');
1207 1537
1208 1538 // Currently the output of the optimizations is not saved/used/logged.
1209 1539 $optimizer->do_optimizations($this_options, 'auto');
@@ -1215,14 +1545,17 @@
1215 1545 * Schedule cron tasks used by plugin.
1216 1546 *
1217 1547 * @return void
1218 1548 */
1219 - public function schedule_plugin_cron_tasks() {
1549 + private function schedule_plugin_cron_tasks() {
1220 1550 if (!wp_next_scheduled('wpo_weekly_cron_tasks')) {
1221 - 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');
1222 1552 }
1223 1553
1224 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();
1225 1558 }
1226 1559
1227 1560 /**
1228 1561 * Do plugin background tasks.
@@ -1235,16 +1568,16 @@
1235 1568 }
1236 1569
1237 1570 /**
1238 1571 * This will customize a URL with a correct Affiliate link
1239 - * 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
1240 1573 *
1241 - * @param String $url - URL to be check to see if it an updraftplus match.
1242 - * @param String $text - Text to be entered within the href a tags.
1243 - * @param String $html - Any specific HTML to be added. Supplied parameter will not be escaped in this function. Provide escaped, safe HTML
1244 - * @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"').
1245 - * @param Boolean $return_instead_of_echo - if set, then the result will be returned, not echo-ed.
1246 - * @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
1247 1580 */
1248 1581 public function wp_optimize_url($url, $text = '', $html = '', $attrs = '', $return_instead_of_echo = false) {
1249 1582 // Check if the URL is UpdraftPlus.
1250 1583 $url = $this->maybe_add_affiliate_params($url); // Return URL - check if there is HTML such as images.
@@ -1258,9 +1591,9 @@
1258 1591 foreach ($attrs as $attr => $value) {
1259 1592 $str_attrs .= $attr . '="' . esc_attr($value) . '" ';
1260 1593 }
1261 1594 } else {
1262 - // 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.
1263 1596 $attrs = explode('=', $attrs);
1264 1597 // Check if $attrs in positions 1 and 2 are not empty and exist; otherwise, return an empty string.
1265 1598 $str_attrs = !empty($attrs[0]) && !empty($attrs[1]) ? $attrs[0] . '="' . esc_attr(str_replace('"', '', $attrs[1])) . '"' : '';
1266 1599 }
@@ -1275,13 +1608,13 @@
1275 1608 $result = sprintf(
1276 1609 '<a href="%s" %s>%s</a>',
1277 1610 esc_url($url),
1278 1611 $str_attrs,
1279 - $content
1612 + wp_kses_post($content)
1280 1613 );
1281 1614
1282 1615 if ($return_instead_of_echo) return $result;
1283 - echo $result;
1616 + echo wp_kses_post($result);
1284 1617 }
1285 1618
1286 1619 /**
1287 1620 * Check if a URL is external
@@ -1286,9 +1619,9 @@
1286 1619 /**
1287 1620 * Check if a URL is external
1288 1621 *
1289 1622 * @param string $url
1290 - * @return string
1623 + * @return bool
1291 1624 */
1292 1625 public function is_external_url($url) {
1293 1626 if (empty($url)) {
1294 1627 return false;
@@ -1301,9 +1634,9 @@
1301 1634 return $current_domain !== $url;
1302 1635 }
1303 1636
1304 1637 /**
1305 - * Get an URL with an eventual affiliate ID
1638 + * Get a URL with an eventual affiliate ID
1306 1639 *
1307 1640 * @param string $url
1308 1641 * @return string
1309 1642 */
@@ -1315,9 +1648,10 @@
1315 1648
1316 1649 // Apply filters.
1317 1650 $url = apply_filters('wpoptimize_updraftplus_com_link', $url);
1318 1651 }
1319 - 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;
1320 1654 }
1321 1655
1322 1656 /**
1323 1657 * Setup WPO logger(s)
@@ -1378,9 +1712,9 @@
1378 1712 // check for previous version options format.
1379 1713 $keys = array_keys($saved_loggers);
1380 1714
1381 1715 // if options stored in old format then reformat it.
1382 - if (false == is_numeric($keys[0])) {
1716 + if (false === is_numeric($keys[0])) {
1383 1717 $_saved_loggers = array();
1384 1718 foreach ($saved_loggers as $logger_id => $enabled) {
1385 1719 if ($enabled) {
1386 1720 $_saved_loggers[] = $logger_id;
@@ -1411,9 +1745,9 @@
1411 1745 foreach (array_keys($logger_options) as $option_name) {
1412 1746 if (array_key_exists($option_name, $options_keys)) {
1413 1747 $options_keys[$option_name]++;
1414 1748 } else {
1415 - $options_keys[$option_name] = 0;
1749 + $options_keys[$option_name] = 0;
1416 1750 }
1417 1751
1418 1752 $option_value = isset($logger_additional_options[$option_name][$options_keys[$option_name]]) ? $logger_additional_options[$option_name][$options_keys[$option_name]] : '';
1419 1753
@@ -1438,11 +1772,10 @@
1438 1772 $loggers[] = $logger;
1439 1773 }
1440 1774 }
1441 1775
1442 - $loggers = apply_filters('wp_optimize_loggers', $loggers);
1443 -
1444 - return $loggers;
1776 + $filtered_loggers = apply_filters('wp_optimize_loggers', $loggers);
1777 + return is_array($filtered_loggers) ? $filtered_loggers : $loggers;
1445 1778 }
1446 1779
1447 1780 /**
1448 1781 * Returns associative array with logger class name in a key and path to class file in a value.
@@ -1510,17 +1843,17 @@
1510 1843 return (is_multisite() && self::is_premium());
1511 1844 }
1512 1845
1513 1846 /**
1514 - * Returns true if current user can run optimizations.
1847 + * Returns true if the current user can run optimizations.
1515 1848 *
1516 1849 * @return bool
1517 1850 */
1518 - public function can_run_optimizations() {
1851 + public function can_run_optimizations(): bool {
1519 1852 // we don't check permissions for cron jobs.
1520 1853 if (defined('DOING_CRON') && DOING_CRON) return true;
1521 1854
1522 - 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;
1523 1856 return true;
1524 1857 }
1525 1858
1526 1859 /**
@@ -1539,13 +1872,10 @@
1539 1872 * @return array
1540 1873 */
1541 1874 public function get_sites() {
1542 1875 $sites = array();
1543 - // check if function get_sites exists (since 4.6.0) else use wp_get_sites.
1544 1876 if (function_exists('get_sites')) {
1545 1877 $sites = get_sites(array('network_id' => null, 'deleted' => 0, 'number' => 999999));
1546 - } elseif (function_exists('wp_get_sites')) {
1547 - $sites = wp_get_sites(array('network_id' => null, 'deleted' => 0, 'limit' => 999999));
1548 1878 }
1549 1879 return $sites;
1550 1880 }
1551 1881
@@ -1551,9 +1881,9 @@
1551 1881
1552 1882 /**
1553 1883 * Returns script memory limit in megabytes.
1554 1884 *
1555 - * @param bool $memory_limit
1885 + * @param string|bool $memory_limit
1556 1886 * @return int
1557 1887 */
1558 1888 public function get_memory_limit($memory_limit = false) {
1559 1889 // Returns in megabytes
@@ -1623,8 +1953,10 @@
1623 1953 * Converts shorthand memory notation value to bytes.
1624 1954 * From http://php.net/manual/en/function.ini-get.php
1625 1955 *
1626 1956 * @param string $val shorthand memory notation value.
1957 + *
1958 + * @return string
1627 1959 */
1628 1960 public function return_bytes($val) {
1629 1961 $val = trim($val);
1630 1962 $last = strtolower($val[strlen($val)-1]);
@@ -1656,9 +1988,9 @@
1656 1988
1657 1989 /**
1658 1990 * Close browser connection and continue script work. - Taken from UpdraftPlus
1659 1991 *
1660 - * @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)
1661 1993 * @return void
1662 1994 */
1663 1995 public function close_browser_connection($txt = '') {
1664 1996 if (!headers_sent()) {
@@ -1663,8 +1995,9 @@
1663 1995 public function close_browser_connection($txt = '') {
1664 1996 if (!headers_sent()) {
1665 1997 // Close browser connection so that it can resume AJAX polling
1666 1998 header('Content-Length: '.(empty($txt) ? '0' : 4+strlen($txt)));
1999 + header('Content-Type: application/json');
1667 2000 header('Connection: close');
1668 2001 header('Content-Encoding: none');
1669 2002 }
1670 2003
@@ -1669,9 +2002,9 @@
1669 2002 }
1670 2003
1671 2004 if (session_id()) session_write_close();
1672 2005 echo "\r\n\r\n";
1673 - echo $txt;
2006 + echo $txt; // phpcs:ignore WordPress.Security.EscapeOutput -- Output is already escaped
1674 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).
1675 2008 $ob_level = ob_get_level();
1676 2009 while ($ob_level > 0) {
1677 2010 ob_end_flush();
@@ -1678,49 +2011,22 @@
1678 2011 $ob_level--;
1679 2012 }
1680 2013 flush();
1681 2014 if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
2015 + if (function_exists('litespeed_finish_request')) litespeed_finish_request();
1682 2016 }
1683 2017
1684 2018 /**
1685 - * Get the current theme's style.css headers
1686 - *
1687 - * @return array|WP_Error
1688 - */
1689 - public function get_stylesheet_headers() {
1690 - static $headers;
1691 - if (isset($headers)) return $headers;
1692 -
1693 - $style = get_template_directory_uri() . '/style.css';
1694 -
1695 - /**
1696 - * Filters wp_remote_get parameters, when checking if browser cache is enabled.
1697 - *
1698 - * @param array $request_params Default parameters
1699 - */
1700 - $request_params = apply_filters('wpoptimize_get_stylesheet_headers_args', array('timeout' => 10));
1701 -
1702 - // trying to load style.css.
1703 - $response = wp_remote_get($style, $request_params);
1704 -
1705 - if (is_a($response, 'WP_Error')) return $response;
1706 -
1707 - $headers = wp_remote_retrieve_headers($response);
1708 -
1709 - if (method_exists($headers, 'getAll')) {
1710 - $headers = $headers->getAll();
1711 - }
1712 -
1713 - return is_array($headers) ? $headers : array();
1714 - }
1715 -
1716 - /**
1717 2019 * Try to change PHP script time limit.
1718 2020 */
1719 2021 public function change_time_limit() {
1720 2022 $time_limit = (defined('WP_OPTIMIZE_SET_TIME_LIMIT') && WP_OPTIMIZE_SET_TIME_LIMIT > 15) ? WP_OPTIMIZE_SET_TIME_LIMIT : 1800;
1721 2023
1722 - @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
1723 2029 }
1724 2030
1725 2031 /**
1726 2032 * Does the request come from UDC
@@ -1737,11 +2043,11 @@
1737 2043 * @return boolean
1738 2044 */
1739 2045 public function template_should_include_data() {
1740 2046 /**
1741 - * Filters wether data should be included in certain templates or not.
2047 + * Filters whether data should be included in certain templates or not.
1742 2048 */
1743 - 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());
1744 2050 }
1745 2051
1746 2052 /**
1747 2053 * Load the templates for the modal window
@@ -1750,8 +2056,18 @@
1750 2056 $this->include_template('modal.php');
1751 2057 }
1752 2058
1753 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 + /**
1754 2070 * Delete transients and semaphores data from options table.
1755 2071 */
1756 2072 public function delete_transients_and_semaphores() {
1757 2073 global $wpdb;
@@ -1775,19 +2091,23 @@
1775 2091 foreach ($masks as $mask) {
1776 2092 $where_parts[] = "(`option_name` LIKE '{$mask}')";
1777 2093 }
1778 2094
1779 - $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));
1780 2097 }
1781 2098
1782 2099 /**
1783 2100 * Prevents bots from indexing plugins list
2101 + *
2102 + * @param string $output
2103 + * @return string
1784 2104 */
1785 2105 public function robots_txt($output) {
1786 2106 $upload_dir = wp_upload_dir();
1787 - $path = parse_url($upload_dir['baseurl']);
2107 + $path = wp_parse_url($upload_dir['baseurl']);
1788 2108 $output .= "\nUser-agent: *";
1789 - $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";
1790 2110 return $output;
1791 2111 }
1792 2112
1793 2113 /**
@@ -1822,10 +2142,58 @@
1822 2142 */
1823 2143 private function load_ajax_handler() {
1824 2144 WPO_Ajax::get_instance();
1825 2145 }
2146 +
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 + }
2155 +
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();
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 + }
1826 2187 }
1827 2188
2189 +/**
2190 + * Unschedule specific tasks:
2191 + * `wpo_cron_event2` => DB optimizations
2192 + * `wpo_weekly_cron_tasks` => ex: plugin.json auto update
2193 + *
2194 + * @return void
2195 + */
1828 2196 function wpo_cron_deactivate() {
1829 2197 WP_Optimize()->log('running wpo_cron_deactivate()');
1830 2198 wp_clear_scheduled_hook('wpo_cron_event2');
1831 2199 wp_clear_scheduled_hook('wpo_weekly_cron_tasks');