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