PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.6.1
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.6.1
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 3.8.0 All 110 releases
wp-optimize / wp-optimize.php

wp-optimize.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.6.1, at wp-optimize.php

2,185 lines 72.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 Plugin Name: WP-Optimize - Clean, Compress, Cache
4 Plugin URI: https://teamupdraft.com/wp-optimize
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: 4.6.1
7 Requires at least: 4.9
8 Requires PHP: 7.2
9 Update URI: https://wordpress.org/plugins/wp-optimize/
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
12 Text Domain: wp-optimize
13 Domain Path: /languages
14 License: GPLv2 or later
15 */
16
17 if (!defined('ABSPATH')) die('No direct access allowed');
18
19 // Check to make sure if WP_Optimize is already call and returns.
20 if (!class_exists('WP_Optimize')) :
21 define('WPO_VERSION', '4.6.1');
22 define('WPO_PLUGIN_URL', plugin_dir_url(__FILE__));
23 define('WPO_PLUGIN_MAIN_PATH', plugin_dir_path(__FILE__));
24 define('WPO_PLUGIN_SLUG', plugin_basename(__FILE__));
25 define('WPO_PREMIUM_NOTIFICATION', false);
26 define('WPO_REQUIRED_PHP_VERSION', '7.2');
27 define('WPO_REQUIRED_WP_VERSION', '4.9');
28 if (!defined('WPO_USE_WEBP_CONVERSION')) define('WPO_USE_WEBP_CONVERSION', true);
29 require_once(WPO_PLUGIN_MAIN_PATH.'includes/fragments/input-processing.php');
30
31 class WP_Optimize {
32
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';
34
35 private $template_directories;
36
37 protected static $_instance = null;
38
39 protected $_cache_init_status = null;
40
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 /**
64 * Class constructor
65 */
66 public function __construct() {
67
68 spl_autoload_register(array($this, 'loader'));
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
83 // Checks if premium is installed along with plugins needed.
84 add_action('plugins_loaded', array($this, 'plugins_loaded'), 1);
85
86 register_activation_hook(__FILE__, array('WPO_Activation', 'actions'));
87 register_deactivation_hook(__FILE__, array('WPO_Deactivation', 'actions'));
88 register_uninstall_hook(__FILE__, array('WPO_Uninstall', 'actions'));
89
90 WPO_Page_Optimizer::instance()->maybe_initialise();
91
92 $this->load_admin();
93 add_action('admin_init', array($this, 'admin_init'));
94
95 add_filter("plugin_action_links_".plugin_basename(__FILE__), array($this, 'plugin_settings_link'));
96 add_action('wpo_cron_event2', array($this, 'cron_action'));
97 add_filter('cron_schedules', array($this, 'cron_schedules'));
98
99 if (!$this->get_options()->get_option('installed-for', false)) $this->get_options()->update_option('installed-for', time());
100
101 add_action('admin_footer-upload.php', array($this, 'add_smush_popup_template'));
102
103 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
104
105 add_action('wp_enqueue_scripts', array($this, 'frontend_enqueue_scripts'));
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
111 $this->load_ajax_handler();
112
113 // Show update to Premium notice for non-premium multisite.
114 add_action('wpo_additional_options', array($this, 'show_multisite_update_to_premium_notice'));
115
116 // Action column (show repair button, if needed).
117 add_filter('wpo_tables_list_additional_column_data', array($this, 'tables_list_additional_column_data'), 15, 2);
118
119 /**
120 * Add action for display Images > Compress images tab.
121 */
122 add_action('wp_optimize_admin_page_wpo_images_smush', array($this, 'admin_page_wpo_images_smush'));
123
124 include_once(WPO_PLUGIN_MAIN_PATH.'includes/updraftcentral/updraftcentral.php');
125
126 include_once(WPO_PLUGIN_MAIN_PATH.'includes/backward-compatibility-functions.php');
127
128 register_shutdown_function(array($this, 'log_fatal_errors'));
129
130 add_action('wpo_admin_before_closing_wrap', array($this, 'load_modal_template'), 20);
131
132 add_action('upgrader_process_complete', array($this, 'detect_active_plugins_and_themes_updates'), 10, 2);
133
134 $import_done_hooks = array(
135 'import_end', // WordPress importer
136 'pmxi_after_xml_import', // wp all import
137 );
138
139 $db_update_hooks = apply_filters('wp_optimize_db_update_hooks', $import_done_hooks);
140
141 foreach ($db_update_hooks as $hook) {
142 add_action($hook, array($this, 'maybe_schedule_update_record_count_event'));
143 }
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
150 }
151
152 /**
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
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 *
222 * @param string $class_name The name of the class.
223 */
224 public function loader($class_name) {
225 $dirs = $this->get_class_directories();
226
227 foreach ($dirs as $dir) {
228 $class_file = WPO_PLUGIN_MAIN_PATH . trailingslashit($dir) . 'class-' . str_replace('_', '-', strtolower($class_name)) . '.php';
229 if (file_exists($class_file)) {
230 require_once($class_file);
231 return;
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 }
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 }
254 }
255
256 /**
257 * Returns an array of class directories
258 *
259 * @return array
260 */
261 private function get_class_directories() {
262 return array(
263 'cache',
264 'compatibility',
265 'includes',
266 'includes/tables',
267 'includes/list-tables',
268 'includes/gravatars',
269 'includes/lcp',
270 'minify',
271 'optimizations',
272 'webp',
273 'includes/updraftcentral',
274 );
275 }
276
277 /**
278 * Initialize Admin class to load admin UI
279 */
280 private function load_admin() {
281 $this->get_admin_instance();
282 }
283
284 /**
285 * Returns Bypass class instance
286 *
287 * @return WP_Optimize_Bypass
288 */
289 private function get_bypass_instance() {
290 return WP_Optimize_Bypass::instance();
291 }
292
293 /**
294 * Returns Admin class instance
295 *
296 * @return WP_Optimize_Admin
297 */
298 public function get_admin_instance() {
299 return WP_Optimize_Admin::instance();
300 }
301
302 /**
303 * Detect when an active plugin or theme is updated, and trigger an action
304 *
305 * @param object $upgrader_object
306 * @param array $options
307 * @return void
308 */
309 public function detect_active_plugins_and_themes_updates($upgrader_object, $options) {
310 if (empty($options) || !isset($options['type'])) return;
311
312 $should_purge_cache = false;
313 $skin = $upgrader_object->skin;
314 if ('plugin' === $options['type']) {
315 // A plugin is updated using the default update system (upgrader_overwrote_package is used for the upload method)
316 $plugins = array();
317 if (!empty($options['plugin'])) {
318 $plugins = array($options['plugin']);
319 } elseif (!empty($options['plugins']) && is_array($options['plugins'])) {
320 $plugins = $options['plugins'];
321 }
322
323 foreach ($plugins as $plugin) {
324 if (!empty($plugin) && ! is_wp_error($upgrader_object->result)) {
325 if (is_plugin_active($plugin)) {
326 $should_purge_cache = true;
327 break;
328 }
329 }
330 }
331 } elseif ('theme' === $options['type']) {
332 $active_theme = get_stylesheet();
333 $parent_theme = get_template();
334 // A theme is updated using the upload system
335 if (isset($options['action']) && 'install' === $options['action'] && isset($skin->options['overwrite']) && 'update-theme' === $skin->options['overwrite']) {
336 $updated_theme = $upgrader_object->result['destination_name'];
337 // Check if the theme is in use
338 if ($active_theme === $updated_theme || $parent_theme === $updated_theme) {
339 $should_purge_cache = true;
340 }
341 // A theme is updated using the classic update system
342 } elseif (isset($options['action']) && 'update' === $options['action'] && isset($options['themes']) && is_array($options['themes'])) {
343 // Check if the theme is in use
344 if (in_array($active_theme, $options['themes']) || in_array($parent_theme, $options['themes'])) {
345 $should_purge_cache = true;
346 }
347 }
348 }
349
350 /**
351 * Action executed when an active theme or plugin was updated
352 */
353 if ($should_purge_cache) do_action('wpo_active_plugin_or_theme_updated');
354
355 }
356
357 /**
358 * Sets a flag to indicate an import action is done, if needed
359 */
360 public function maybe_schedule_update_record_count_event() {
361 if (!wp_next_scheduled('wpo_update_record_count_event')) {
362 wp_schedule_single_event(time() + 60, 'wpo_update_record_count_event');
363 }
364 }
365
366 public function admin_page_wpo_images_smush() {
367 $options = Updraft_Smush_Manager()->get_smush_options();
368 $custom = 90 >= $options['image_quality'] && 65 <= $options['image_quality'];
369 $sites = $this->get_sites();
370 $compression_server_hint = Updraft_Smush_Manager()->get_compression_server_hint();
371 $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));
372 $this->add_smush_popup_template();
373 }
374
375 public static function instance() {
376 if (empty(self::$_instance)) {
377 self::$_instance = new self();
378 }
379 return self::$_instance;
380 }
381
382 public function get_optimizer() {
383 return WP_Optimizer::instance();
384 }
385
386 /**
387 * Adds 3rd party plugin compatibilities.
388 */
389 public function load_compatibilities() {
390
391 if (class_exists('Polylang')) {
392 WPO_Polylang_Compatibility::instance();
393 }
394
395 WPO_Page_Builder_Compatibility::instance();
396 WPO_KD_Submissions_Compatibility::instance();
397
398 if (class_exists('Custom_Permalinks')) {
399 WPO_Custom_Permalink_Compatibility::instance();
400 }
401
402 if (class_exists('TRP_Translate_Press')) {
403 WPO_TranslatePress_Compatibility::instance();
404 }
405
406 do_action('wpo_load_compatibilities');
407 }
408
409 /**
410 * Get and instantiate WP_Optimize_Minify
411 *
412 * @return WP_Optimize_Minify
413 */
414 public function get_minify() {
415 return WP_Optimize_Minify::instance();
416 }
417
418 public function get_options() {
419 return WP_Optimize_Options::instance();
420 }
421
422 public function get_notices() {
423 return WP_Optimize_Notices::instance();
424 }
425
426 /**
427 * Get and instantiate WP_Optimize_Delay_JS
428 *
429 * @return WP_Optimize_Delay_JS
430 */
431 private function get_delay_js() {
432 return WP_Optimize_Delay_JS::instance();
433 }
434
435 /**
436 * Returns instance of WPO_Page_Optimizer class.
437 *
438 * @return WPO_Page_Optimizer
439 */
440 public function get_page_optimizer() {
441 return WPO_Page_Optimizer::instance();
442 }
443
444 /**
445 * Returns instance 404 Detector Cron class.
446 *
447 * @return WP_Optimize_404_Detector_Cron
448 */
449 private function get_404_detector_cron() {
450 return WP_Optimize_404_Detector_Cron::get_instance();
451 }
452
453 /**
454 * Returns instance of WPO_Page_Cache class.
455 *
456 * @return WPO_Page_Cache
457 */
458 public function get_page_cache() {
459 return WPO_Page_Cache::instance();
460 }
461
462 /**
463 * Returns instance of WP_Optimize_WebP class.
464 *
465 * @return WP_Optimize_WebP
466 */
467 public function get_webp_instance() {
468 return WP_Optimize_WebP::get_instance();
469 }
470
471 /**
472 * Returns instance of WP_Optimize_Server_Compatibility class.
473 *
474 * @return WP_Optimize_Server_Compatibility
475 */
476 public function get_server_compatibility_instance(): WP_Optimize_Server_Compatibility {
477 return WP_Optimize_Server_Compatibility::get_instance();
478 }
479
480 /**
481 * Create instance of WP_Optimize_Browser_Cache.
482 *
483 * @return WP_Optimize_Browser_Cache
484 */
485 public function get_browser_cache() {
486 return WP_Optimize_Browser_Cache::instance();
487 }
488
489 /**
490 * Returns WP_Optimize_Database_Information instance.
491 *
492 * @return WP_Optimize_Database_Information
493 */
494 public function get_db_info() {
495 return WP_Optimize_Database_Information::instance();
496 }
497
498 /**
499 * Returns instance of WP_Optimize_Gzip_Compression.
500 *
501 * @return WP_Optimize_Gzip_Compression
502 */
503 public function get_gzip_compression() {
504 return WP_Optimize_Gzip_Compression::instance();
505 }
506
507 /**
508 * Create instance of WP_Optimize_Htaccess.
509 *
510 * @param string $htaccess_file absolute path to htaccess file, by default it use .htaccess in WordPress root directory.
511 * @return WP_Optimize_Htaccess
512 */
513 public static function get_htaccess($htaccess_file = '') {
514 return new WP_Optimize_Htaccess($htaccess_file);
515 }
516
517 /**
518 * Return instance of Updraft_Logger
519 *
520 * @return Updraft_Logger
521 */
522 public function get_logger() {
523 return Updraft_Logger::instance();
524 }
525
526 /**
527 * Check if the current page belongs to WP-Optimize.
528 *
529 * @return bool
530 */
531 public function is_wpo_page() {
532 $current_screen = get_current_screen();
533
534 return (bool) preg_match('/wp\-optimize/i', $current_screen->id);
535 }
536
537 /**
538 * Enqueue scripts and styles on WP-Optimize pages.
539 */
540 public function admin_enqueue_scripts() {
541 global $wp_version;
542 $enqueue_version = $this->get_enqueue_version();
543 $min_or_not = $this->get_min_or_not_string();
544 $min_or_not_internal = $this->get_min_or_not_internal_string();
545
546 // Register or enqueue common scripts
547 wp_register_script('wp-optimize-send-command', WPO_PLUGIN_URL.'js/send-command'.$min_or_not_internal.'.js', array(), $enqueue_version);
548 wp_localize_script('wp-optimize-send-command', 'wp_optimize_send_command_data', array('nonce' => wp_create_nonce('wp-optimize-ajax-nonce')));
549 wp_register_script('wp-optimize-block-ui', WPO_PLUGIN_URL.'js/blockUI'.$min_or_not_internal.'.js', array('jquery'), $enqueue_version);
550 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
551
552 // load scripts and styles only on WP-Optimize pages.
553 if (!$this->is_wpo_page()) return;
554
555 wp_enqueue_script( 'jquery-ui-tooltip' );
556 // Defeat other plugins/themes which dump their jQuery UI CSS onto our settings page
557 wp_deregister_style('jquery-ui');
558 wp_enqueue_style('jquery-ui', WPO_PLUGIN_URL.'css/jquery-ui.custom' . $min_or_not_internal . '.css', array(), $enqueue_version);
559
560
561 wp_enqueue_script('select2', WPO_PLUGIN_URL . 'js/select2/select2' . $min_or_not . '.js', array('jquery'), $enqueue_version);
562 wp_enqueue_style('select2', WPO_PLUGIN_URL . 'css/select2/select2' . $min_or_not . '.css', array(), $enqueue_version);
563
564 wp_enqueue_script('jquery-serialize-json', WPO_PLUGIN_URL.'js/serialize-json/jquery.serializejson'.$min_or_not.'.js', array('jquery'), $enqueue_version);
565
566 wp_register_script('updraft-queue-js', WPO_PLUGIN_URL.'js/queue'.$min_or_not_internal.'.js', array(), $enqueue_version);
567
568 wp_enqueue_script('wp-optimize-modal', WPO_PLUGIN_URL.'js/modal'.$min_or_not_internal.'.js', array('jquery', 'backbone', 'wp-util'), $enqueue_version);
569 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);
570 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);
571 wp_enqueue_style('wp-optimize-admin-css', WPO_PLUGIN_URL.'css/wp-optimize-admin'.$min_or_not_internal.'.css', array(), $enqueue_version);
572 // Using table sorter to help with organising the DB size on Table Information
573 // https://github.com/tofsjonas/sortable/
574 wp_enqueue_script('sortable-js', WPO_PLUGIN_URL.'js/sortable/sortable'.$min_or_not.'.js', array('wp-optimize-send-command'), $enqueue_version);
575 wp_enqueue_script('sortable-a11y-js', WPO_PLUGIN_URL.'js/sortable/sortable.a11y'.$min_or_not.'.js', array('wp-optimize-send-command'), $enqueue_version);
576
577 wp_enqueue_style('sortable-css', WPO_PLUGIN_URL.'css/sortable/sortable'.$min_or_not_internal.'.css', array(), $enqueue_version);
578
579 $js_variables = $this->wpo_js_translations();
580 $js_variables['loggers_classes_info'] = $this->get_loggers_classes_info();
581 wp_localize_script('wp-optimize-admin-js', 'wpoptimize', $js_variables);
582
583 do_action('wpo_premium_scripts_styles', $min_or_not_internal, $min_or_not, $enqueue_version);
584
585 $status_report_dependencies = array('wp-optimize-admin-js');
586
587 // Only include wp-api-fetch if WP >= 5.0
588 if (version_compare($wp_version, '5.0', '>=')) {
589 $status_report_dependencies[] = 'wp-api-fetch';
590 }
591
592 wp_enqueue_script('wp-optimize-status-report', WPO_PLUGIN_URL.'js/status'.$min_or_not_internal.'.js', $status_report_dependencies, $enqueue_version);
593
594 wp_enqueue_script('js-zip', WPO_PLUGIN_URL.'/js/jszip/jszip' . $min_or_not . '.js', array(), $enqueue_version);
595
596 }
597
598 /**
599 * Enqueue any required front-end scripts
600 *
601 * @return void
602 */
603 public function frontend_enqueue_scripts() {
604 if (!$this->current_user_can('manage_options') || !is_admin_bar_showing()) return;
605 $enqueue_version = $this->get_enqueue_version();
606 $min_or_not_internal = $this->get_min_or_not_internal_string();
607
608 // Register or enqueue common scripts
609 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
610 }
611
612 /**
613 * Load Task Manager
614 *
615 * @return Updraft_Smush_Manager
616 */
617 public function get_task_manager() {
618 include_once(WPO_PLUGIN_MAIN_PATH.'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php');
619
620 Updraft_Tasks_Activation::check_updates();
621
622 include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-meta.php');
623 include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-options.php');
624 include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task.php');
625
626 include_once(WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-smush-task.php');
627 include_once(WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-smush-manager.php');
628
629 return Updraft_Smush_Manager();
630 }
631
632 /**
633 * Indicate whether we have an associated instance of WP-Optimize Premium or not.
634 *
635 * @returns bool
636 */
637 public static function is_premium() {
638 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && function_exists('WP_Optimize_Premium')) {
639 $wp_optimize_premium = WP_Optimize_Premium();
640 if (is_a($wp_optimize_premium, 'WP_Optimize_Premium')) return true;
641 }
642 return false;
643 }
644
645 /**
646 * Check if script running on Apache web server. $is_apache is set in wp-includes/vars.php. Also returns true if the server uses litespeed.
647 *
648 * @return bool
649 */
650 public function is_apache_server() {
651 global $is_apache;
652 return (bool) $is_apache;
653 }
654
655 /**
656 * Check if script running on IIS web server.
657 *
658 * @return bool
659 */
660 public function is_IIS_server() {
661 global $is_IIS, $is_iis7;
662 return $is_IIS || $is_iis7;
663 }
664
665 /**
666 * Check if Apache module or modules active.
667 *
668 * @param string|array $module - single Apache module name or list of Apache module names.
669 *
670 * @return bool|null - if null, the result was indeterminate
671 */
672 public function is_apache_module_loaded($module) {
673 if (!$this->is_apache_server()) return false;
674
675 if (!function_exists('apache_get_modules')) return null;
676
677 $module_loaded = true;
678
679 if (is_array($module)) {
680 foreach ($module as $single_module) {
681 if (!in_array($single_module, apache_get_modules())) {
682 $module_loaded = false;
683 break;
684 }
685 }
686 } else {
687 $module_loaded = in_array($module, apache_get_modules());
688 }
689
690 return $module_loaded;
691 }
692
693 /**
694 * 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.
695 */
696 public function plugins_loaded() {
697
698 if (!$this->is_minimum_requirement_met()) {
699 $this->add_notice_minimum_requirements_not_met();
700 $this->deactivate_plugin();
701 WPO_Deactivation::actions();
702 return;
703 }
704
705 if (is_admin() && $this->current_user_can()) {
706 WP_Optimize_Heartbeat::get_instance();
707 }
708
709 if ($this->get_server_compatibility_instance()->does_server_handle_cache()) {
710 add_filter('wp_optimize_admin_page_wpo_cache_tabs', array($this, 'filter_cache_tabs'), 99, 1);
711
712 // If newly migrated to a server that handles cache, disable wpo cache
713 $cache = $this->get_page_cache();
714 if ($cache->is_enabled()) {
715 $cache->disable();
716 }
717 }
718
719 add_filter('robots_txt', array($this, 'robots_txt'), 99, 1);
720
721 // Run Premium loader if it exists
722 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && !class_exists('WP_Optimize_Premium')) {
723 include_once(WPO_PLUGIN_MAIN_PATH.'premium.php');
724 }
725
726 if (!self::is_premium()) {
727 $this->get_server_compatibility_instance()->maybe_disable_unsupported_table_optimization();
728 add_action('auto_option_settings', array($this->get_options(), 'auto_option_settings'));
729 }
730
731 // load defaults
732 $this->get_options()->set_default_options();
733
734 // Initialize loggers.
735 add_action('init', array($this, 'setup_loggers'));
736
737 if ($this->is_active('premium') && false !== ($free_plugin = $this->is_active('free'))) {
738 if (!function_exists('deactivate_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
739 deactivate_plugins($free_plugin);
740
741 // If WPO_ADVANCED_CACHE is defined, we empty advanced-cache.php to regenerate later. Otherwise, it contains the path to free.
742 if (defined('WPO_ADVANCED_CACHE') && WPO_ADVANCED_CACHE) {
743 $advanced_cache_filename = trailingslashit(WP_CONTENT_DIR) . 'advanced-cache.php';
744
745 if (!is_file($advanced_cache_filename) && wp_is_writable(dirname($advanced_cache_filename)) || (is_file($advanced_cache_filename) && wp_is_writable($advanced_cache_filename))) {
746 file_put_contents($advanced_cache_filename, '');
747 }
748 }
749
750 // Registers the notice letting the user know it cannot be active if premium is active.
751 add_action('admin_notices', array($this, 'show_admin_notice_premium'));
752 return;
753 }
754
755 add_action('init', array($this, 'init'));
756
757 // add_filter('updraftcentral_host_plugins', array($this, 'attach_updraftcentral_host'));
758 // if (file_exists(WPO_PLUGIN_MAIN_PATH.'central/factory.php')) include_once(WPO_PLUGIN_MAIN_PATH.'central/factory.php');
759 }
760
761 /**
762 * Methods needed to be run in init hook.
763 *
764 * @return void
765 */
766 public function init() {
767 $this->load_language_file();
768 $this->schedule_plugin_cron_tasks();
769 $this->init_page_cache();
770 $this->get_minify();
771 $this->get_delay_js();
772
773 // Load 3rd party plugin compatibilities.
774 $this->load_compatibilities();
775
776 $this->run_updates();
777
778 // We need this here because webp can be unavailable because of server moves
779 // This deletes already converted webp images and original image file when a media is deleted
780 WP_Optimize_WebP_Images::get_instance();
781
782 // Loads the task manager
783 $this->get_task_manager();
784
785 // Include WebP
786 if (WPO_USE_WEBP_CONVERSION) {
787 $this->get_webp_instance();
788 }
789
790 $this->get_onboarding()->init();
791 }
792
793 /**
794 * Loads the language file.
795 *
796 * @return void
797 */
798 private function load_language_file() {
799 load_plugin_textdomain('wp-optimize', false, dirname(plugin_basename(__FILE__)) . '/languages');
800 }
801
802 /**
803 * Attach this wp-optimize plugin as host of the UpdraftCentral libraries
804 * (e.g. "central" folder)
805 *
806 * @param array $hosts List of plugins having the "central" library integrated into them
807 *
808 * @return array
809 */
810 public function attach_updraftcentral_host($hosts) {
811 $hosts[] = 'wp-optimize';
812 return $hosts;
813 }
814
815 /**
816 * Filter cache tabs (when the server handles it or do not allow it, like Kinsta)
817 *
818 * @param array $tabs An array of tabs
819 *
820 * @return array $tabs An array of tabs
821 */
822 public function filter_cache_tabs($tabs) {
823 unset($tabs['preload']);
824 unset($tabs['advanced']);
825 unset($tabs['gzip']);
826 unset($tabs['settings']);
827 return $tabs;
828 }
829
830 /**
831 * Check whether one of free/Premium is active (whether it is this instance or not)
832 *
833 * @param string $which - 'free' or 'premium'
834 *
835 * @return string|boolean - plugin path (if installed) or false if not
836 */
837 public function is_active($which = 'free') {
838 $active_plugins = $this->get_active_plugins();
839 foreach ($active_plugins as $file) {
840 if ('wp-optimize.php' == basename($file)) {
841 $plugin_dir = WP_PLUGIN_DIR.'/'.dirname($file);
842 if (('free' === $which && !file_exists($plugin_dir.'/premium.php')) || ('free' !== $which && file_exists($plugin_dir.'/premium.php'))) return $file;
843 }
844 }
845 return false;
846 }
847
848 /**
849 * Gets an array of plugins active on either the current site, or site-wide
850 *
851 * @return array - a list of plugin paths (relative to the plugin directory)
852 */
853 private function get_active_plugins() {
854 // Gets all active plugins on the current site
855 $active_plugins = (array) get_option('active_plugins', array());
856
857 if (is_multisite()) {
858 $network_active_plugins = get_site_option('active_sitewide_plugins');
859 if (!empty($network_active_plugins)) {
860 $network_active_plugins = array_keys($network_active_plugins);
861 $active_plugins = array_merge($active_plugins, $network_active_plugins);
862 }
863 }
864
865 return $active_plugins;
866 }
867
868 /**
869 * This function checks whether a specific plugin is installed, and returns information about it
870 *
871 * @param string $name Specify "Plugin Name" to return details about it.
872 * @return array Returns an array of details such as if installed, the name of the plugin and if it is active.
873 */
874 public function is_installed($name) {
875
876 // Needed to have the 'get_plugins()' function
877 include_once(ABSPATH.'wp-admin/includes/plugin.php');
878
879 // Gets all plugins available
880 $get_plugins = get_plugins();
881
882 $active_plugins = $this->get_active_plugins();
883
884 $plugin_info = array();
885 $plugin_info['installed'] = false;
886 $plugin_info['active'] = false;
887
888 // Loops around each plugin available.
889 foreach ($get_plugins as $key => $value) {
890 // If the plugin name matches that of the specified name, it will gather details.
891 if ($value['Name'] !== $name && $value['TextDomain'] !== $name) continue;
892 $plugin_info['installed'] = true;
893 $plugin_info['name'] = $key;
894 $plugin_info['version'] = $value['Version'];
895 if (in_array($key, $active_plugins)) {
896 $plugin_info['active'] = true;
897 }
898 break;
899 }
900 return $plugin_info;
901 }
902
903 /**
904 * This is a notice to show users that premium is installed
905 */
906 public function show_admin_notice_premium() {
907 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>';
908 if (isset($_GET['activate'])) unset($_GET['activate']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- only unsetting value, not using it.
909 }
910
911 /**
912 * Show update to Premium notice for non-premium multisite.
913 */
914 public function show_multisite_update_to_premium_notice() {
915 if (!is_multisite() || self::is_premium()) return;
916
917 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>';
918 }
919
920 public function admin_init() {
921 $pagenow = $GLOBALS['pagenow'];
922
923 $this->register_template_directories();
924
925 if (('index.php' === $pagenow && $this->current_user_can('update_plugins')) || ('index.php' === $pagenow && defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
926 $options = $this->get_options();
927
928 $dismissed_until = $options->get_option('dismiss_dash_notice_until', 0);
929
930 if (file_exists(WPO_PLUGIN_MAIN_PATH . 'index.html')) {
931 $installed = filemtime(WPO_PLUGIN_MAIN_PATH . 'index.html');
932 $installed_for = (time() - $installed);
933 }
934
935 if (($installed && time() > $dismissed_until && $installed_for > (14 * 86400) && !defined('WP_OPTIMIZE_NOADS_B')) || (defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
936 add_action('all_admin_notices', array($this, 'show_admin_notice_upgraded'));
937 }
938 }
939
940 if ($this->is_wp_smush_installed()) {
941 add_filter('transient_wp-smush-conflict_check', array($this, 'modify_wp_smush_conflict_check'), 9, 1);
942 }
943 if ($this->get_options()->get_option('404_detector', 0)) {
944 WP_Optimize_Performance::get_instance($this->get_404_detector())->init();
945 }
946 }
947
948 /**
949 * Checks whether the WP Smush plugin is active or not
950 *
951 * @return bool
952 */
953 private function is_wp_smush_installed() {
954 return is_plugin_active('wp-smushit/wp-smush.php');
955 }
956
957 /**
958 * Remove WPO plugin name from WP Smushit transient value
959 *
960 * @return array $active_plugins
961 */
962 public function modify_wp_smush_conflict_check($active_plugins) {
963 // This can be boolean value since it is return value of get_transient
964 if (!is_array($active_plugins)) return $active_plugins;
965
966 if (false !== ($key = array_search('WP-Optimize - Clean, Compress, Cache', $active_plugins))) {
967 unset($active_plugins[$key]);
968 }
969 return $active_plugins;
970 }
971
972 /**
973 * Get the installation or update notice instance
974 *
975 * @return WP_Optimize_Install_Or_Update_Notice
976 */
977 public function get_install_or_update_notice() {
978 static $instance = null;
979 if (is_a($instance, 'WP_Optimize_Install_Or_Update_Notice')) return $instance;
980 $instance = new WP_Optimize_Install_Or_Update_Notice();
981 return $instance;
982 }
983
984 /**
985 * Display an admin notice for an upgraded version.
986 */
987 public function show_admin_notice_upgraded() {
988 $this->include_template('notices/thanks-for-using-main-dash.php', false, array(
989 'is_premium' => WP_Optimize::is_premium()
990 ));
991 }
992
993 /**
994 * Returns required capability string
995 *
996 * @return string
997 */
998 public function capability_required() {
999 $capability = is_multisite() ? 'manage_network_options' : 'manage_options';
1000
1001 $filtered_capability = apply_filters('wp_optimize_capability_required', $capability);
1002
1003 return is_string($filtered_capability) ? $filtered_capability : $capability;
1004 }
1005
1006 /**
1007 * Check if the current user has a given capability.
1008 * If no capability is provided, it checks for the default capability required to access WP-Optimize.
1009 *
1010 * @param string|null $capability The capability to check. Null = use default.
1011 * @return bool
1012 */
1013 public function current_user_can($capability = null) {
1014 if (null === $capability) {
1015 $capability = $this->capability_required(); // falls back to default
1016 }
1017
1018 if (current_user_can($capability)) {
1019 return true;
1020 }
1021
1022 return false;
1023 }
1024
1025 /**
1026 * Returns array of translations used in javascript code.
1027 *
1028 * @return array
1029 */
1030 public function wpo_js_translations() {
1031 $log_message = __('For more details, please check your logs configured in logging destinations settings.', 'wp-optimize');
1032 $translations = array(
1033 'automatic_backup_before_optimizations' => __('Automatic backup before optimizations', 'wp-optimize'),
1034 'error_unexpected_response' => __('An unexpected response was received.', 'wp-optimize'),
1035 'optimization_complete' => __('Optimization complete', 'wp-optimize'),
1036 'with_warnings' => __('(with warnings - open the browser console for more details)', 'wp-optimize'),
1037 'optimizing_table' => __('Optimizing table:', 'wp-optimize'),
1038 'run_optimizations' => __('Run optimizations', 'wp-optimize'),
1039 'table_optimization_timeout' => 120000,
1040 'add' => __('Add', 'wp-optimize'),
1041 'cancel' => __('Cancel', 'wp-optimize'),
1042 'cancelling' => __('Cancelling...', 'wp-optimize'),
1043 'enable' => __('Enable', 'wp-optimize'),
1044 'disable' => __('Disable', 'wp-optimize'),
1045 'please_select_settings_file' => __('Please, select settings file.', 'wp-optimize'),
1046 'are_you_sure_you_want_to_remove_logging_destination' => __('Are you sure you want to remove this logging destination?', 'wp-optimize'),
1047 'fill_all_settings_fields' => __('Before saving, you need to complete the currently incomplete settings (or remove them).', 'wp-optimize'),
1048 // translators: %s is the table name
1049 'table_was_not_repaired' => __('%s was not repaired.', 'wp-optimize') . ' ' . $log_message,
1050 // translators: %s is the table name
1051 'table_was_not_deleted' => __('%s was not deleted.', 'wp-optimize') . ' ' . $log_message,
1052 // translators: %s is the table name
1053 'table_was_not_converted' => __('%s was not converted to InnoDB.', 'wp-optimize') . ' ' . $log_message,
1054 'please_use_positive_integers' => __('Please use positive integers.', 'wp-optimize'),
1055 'please_use_valid_values' => __('Please use valid values.', 'wp-optimize'),
1056 'update' => __('Update', 'wp-optimize'),
1057 'run_now' => __('Run now', 'wp-optimize'),
1058 'starting_preload' => __('Started preload...', 'wp-optimize'),
1059 'loading_urls' => __('Loading URLs...', 'wp-optimize'),
1060 'current_cache_size' => __('Current cache size:', 'wp-optimize'),
1061 'number_of_files' => __('Number of files:', 'wp-optimize'),
1062 'toggle_info' => __('Show information', 'wp-optimize'),
1063 'delete_file' => __('Delete', 'wp-optimize'),
1064 'deleting' => __('Deleting...', 'wp-optimize'),
1065 'added_to_list' => __('Added to the list', 'wp-optimize'),
1066 'added_notice' => __('The file was added to the list', 'wp-optimize'),
1067 'save_notice' => __('Save the changes', 'wp-optimize'),
1068 'saving' => __('Saving...', 'wp-optimize'),
1069 'clearing_cache' => __('Clearing cache...', 'wp-optimize'),
1070 'creating_cache' => __('Creating cache...', 'wp-optimize'),
1071 'page_refresh' => __('Refreshing the page to reflect changes...', 'wp-optimize'),
1072 'cache_file_not_found' => __('Cache file was not found.', 'wp-optimize'),
1073 'settings_have_been_deleted_successfully' => __('WP-Optimize settings have been deleted successfully.', 'wp-optimize'),
1074 'loading_data' => __('Loading data...', 'wp-optimize'),
1075 'spinner_src' => esc_url(admin_url('images/spinner-2x.gif')),
1076 'logo_src' => esc_url(WPO_PLUGIN_URL.'images/notices/wp_optimize_logo.png'),
1077 'settings_page_url' => is_multisite() ? network_admin_url('admin.php?page=wpo_settings') : admin_url('admin.php?page=wpo_settings'),
1078 'sites' => $this->get_sites(),
1079 'user_always_ignores_table_deletion_warning' => (bool) get_user_meta(get_current_user_id(), 'wpo-ignores-table-deletion-warning', true),
1080 'user_always_ignores_post_meta_deletion_warning' => (bool) get_user_meta(get_current_user_id(), 'wpo-ignores-post-meta-deletion-warning', true),
1081 'user_always_ignores_orphaned_relationship_data_deletion_warning' => (bool) get_user_meta(get_current_user_id(), 'wpo-ignores-orphaned-relationship-data-deletion-warning', true),
1082 'post_meta_tweak_completed' => __('The tweak has been performed.', 'wp-optimize'),
1083 'no_minified_assets' => __('No minified files are present', 'wp-optimize'),
1084 'network_site_url' => network_site_url(),
1085 'export_settings_file_name' => 'wpoptimize-settings-'.sanitize_title(get_bloginfo('name')).'.json',
1086 'import_select_file' => __('You have not yet selected a file to import.', 'wp-optimize'),
1087 'import_invalid_json_file' => __('Error: The chosen file is corrupt.', 'wp-optimize') . ' ' . __('Please choose a valid WP-Optimize export file.', 'wp-optimize'),
1088 'importing' => __('Importing...', 'wp-optimize'),
1089 'importing_data_from' => __('This will import data from:', 'wp-optimize'),
1090 'exported_on' => __('Which was exported on:', 'wp-optimize'),
1091 'continue_import' => __('Do you want to carry out the import?', 'wp-optimize'),
1092 'select_destination' => __('Select destination', 'wp-optimize'),
1093 'show' => __('Show', 'wp-optimize'),
1094 'hide' => __('Hide', 'wp-optimize'),
1095 'please_wait' => __('Please wait a moment...', 'wp-optimize'),
1096 'clipboard_failed' => __('Copy to clipboard failed, please do it manually', 'wp-optimize'),
1097 'clipboard_success' => __('System status has been copied to the clipboard', 'wp-optimize'),
1098 'show_information' => __('Show information', 'wp-optimize'),
1099 'hide_information' => __('Hide information', 'wp-optimize'),
1100 'data_not_available' => __('Not available', 'wp-optimize'),
1101 'something_wrong_try_again' => __('Something went wrong; please try again.', 'wp-optimize')
1102 );
1103 $filtered_translations = apply_filters('wpo_js_translations', $translations);
1104 return is_array($filtered_translations) ? $filtered_translations : $translations;
1105 }
1106
1107 /**
1108 * Add settings link on plugin page
1109 *
1110 * @param array $links Passing through the URL to be used within the HREF.
1111 * @return array Returns the Links.
1112 */
1113 public function plugin_settings_link($links) {
1114
1115 $admin_page_url = $this->get_options()->admin_page_url();
1116 $settings_page_url = $this->get_options()->admin_page_url('wpo_settings');
1117
1118 if (!self::is_premium()) {
1119 $premium_link = '<a href="' . esc_url($this->premium_version_link) . '&utm_content=plugin-page' . '" target="_blank">' . __('Premium', 'wp-optimize') . '</a>';
1120 array_unshift($links, $premium_link);
1121 }
1122
1123 $settings_link = '<a href="' . esc_url($settings_page_url) . '">' . __('Settings', 'wp-optimize') . '</a>';
1124 array_unshift($links, $settings_link);
1125
1126 $optimize_link = '<a href="' . esc_url($admin_page_url) . '">' . __('Optimize', 'wp-optimize') . '</a>';
1127 array_unshift($links, $optimize_link);
1128 return $links;
1129 }
1130
1131 /**
1132 * Action wpo_tables_list_additional_column_data. Output button Optimize in the action column.
1133 *
1134 * @param string $content String for output to column
1135 * @param object $table_info Object with table info.
1136 *
1137 * @return string
1138 */
1139 public function tables_list_additional_column_data($content, $table_info) {
1140 if ($table_info->is_needing_repair) {
1141 $content .= '<div class="wpo_button_wrap">'
1142 . '<button class="button button-secondary run-single-table-repair" data-table="' . esc_attr($table_info->Name) . '">' . __('Repair', 'wp-optimize') . '</button>'
1143 . '<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
1144 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
1145 . '</div>';
1146 }
1147
1148 // table belongs to plugin.
1149 if ($table_info->can_be_removed) {
1150 $content .= '<div>'
1151 . '<button class="button button-secondary run-single-table-delete" data-table="' . esc_attr($table_info->Name) . '">' . __('Remove', 'wp-optimize') . '</button>'
1152 . '<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
1153 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
1154 . '</div>';
1155 }
1156
1157 // Add option for MyISAM to InnoDB conversion.
1158 if ('MyISAM' === $table_info->Engine) {
1159 $content .= '<div class="wpo_button_convert wpo_button_wrap">'
1160 . '<button class="button button-secondary toinnodb" data-table="' . esc_attr($table_info->Name) . '">' . __('Convert to InnoDB', 'wp-optimize') . '</button>'
1161 . '<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
1162 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
1163 . '</div>';
1164
1165 }
1166
1167 return $content;
1168 }
1169
1170 /**
1171 * Initialize WP-Optimize page cache.
1172 */
1173 private function init_page_cache() {
1174 if ($this->get_page_cache()->config->get_option('enable_page_caching', false)) {
1175 if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
1176 $this->_cache_init_status = $this->get_page_cache()->enable();
1177 }
1178 }
1179 }
1180
1181 /**
1182 * Get init_page_cache() status.
1183 *
1184 * @return bool|WP_Error
1185 */
1186 public function get_init_page_cache_status() {
1187 return $this->_cache_init_status;
1188 }
1189
1190 /**
1191 * Schedules cron event based on selected schedule type
1192 *
1193 * @return void
1194 */
1195 public function cron_activate() {
1196 $gmt_offset = (int) (3600 * get_option('gmt_offset'));
1197
1198 $options = $this->get_options();
1199
1200 if (false === $options->get_option('schedule')) {
1201 $options->set_default_options();
1202 } else {
1203 if ('true' === $options->get_option('schedule')) {
1204 if (!wp_next_scheduled('wpo_cron_event2')) {
1205 $schedule_type = $options->get_option('schedule-type', 'wpo_weekly');
1206
1207 // Backward compatibility
1208 if ('wpo_otherweekly' === $schedule_type) $schedule_type = 'wpo_fortnightly';
1209
1210 $this_time = (86400 * 7);
1211
1212 switch ($schedule_type) {
1213 case "wpo_daily":
1214 $this_time = 86400;
1215 break;
1216
1217 case "wpo_weekly":
1218 $this_time = (86400 * 7);
1219 break;
1220
1221 case "wpo_fortnightly":
1222 $this_time = (86400 * 14);
1223 break;
1224
1225 case "wpo_monthly":
1226 $this_time = (86400 * 30);
1227 break;
1228 }
1229
1230 add_action('wpo_cron_event2', array($this, 'cron_action'));
1231 $result = wp_schedule_event((current_time("timestamp", 0) + $this_time - $gmt_offset), $schedule_type, 'wpo_cron_event2');
1232 $this->log('running wp_schedule_event()');
1233 if (is_wp_error($result)) {
1234 $error_msg = $result->get_error_message();
1235 $this->log($error_msg);
1236 $this->log(print_r($result, true)); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Using for debugging purpose, logged into separate file
1237 } else {
1238 $this->log($result);
1239 }
1240 }
1241 }
1242 }
1243 }
1244
1245 /**
1246 * Unschedules WP-Optimize cron events on deactivation.
1247 *
1248 * @return void
1249 */
1250 public function wpo_cron_deactivate() {
1251 if (is_multisite()) {
1252 $sites = $this->get_sites();
1253 foreach ($sites as $site) {
1254 switch_to_blog($site->blog_id);
1255 $this->unschedule_wpo_cron_jobs();
1256 restore_current_blog();
1257 }
1258 } else {
1259 $this->unschedule_wpo_cron_jobs();
1260 }
1261 }
1262
1263 /**
1264 * Unschedules all WP-Optimize cron jobs on the current site.
1265 *
1266 * @return void
1267 */
1268 private function unschedule_wpo_cron_jobs() {
1269 $cron_jobs = _get_cron_array();
1270 foreach ($cron_jobs as $job) {
1271 foreach (array_keys($job) as $hook) {
1272 if (preg_match('/^wpo_/', $hook)) wp_unschedule_hook($hook);
1273 }
1274 }
1275 }
1276
1277 /**
1278 * Scheduler public functions to update schedulers
1279 *
1280 * @param array $schedules An array of schedules being passed.
1281 * @return array An array of schedules being returned.
1282 */
1283 public function cron_schedules($schedules) {
1284 $schedules['wpo_daily'] = array('interval' => 86400, 'display' => 'Once Daily');
1285 $schedules['wpo_weekly'] = array('interval' => 86400 * 7, 'display' => 'Once Weekly');
1286 $schedules['wpo_fortnightly'] = array('interval' => 86400 * 14, 'display' => 'Once Every Fortnight');
1287 $schedules['wpo_monthly'] = array('interval' => 86400 * 30, 'display' => 'Once Every Month');
1288 return $schedules;
1289 }
1290
1291 /**
1292 * Returns count of overdue cron jobs.
1293 *
1294 * @return integer
1295 */
1296 public function howmany_overdue_crons() {
1297 $how_many_overdue = 0;
1298 if (function_exists('_get_cron_array') || (is_file(ABSPATH.WPINC.'/cron.php') && include_once(ABSPATH.WPINC.'/cron.php') && function_exists('_get_cron_array'))) {
1299 $crons = _get_cron_array();
1300 if (is_array($crons)) {
1301 $timenow = time();
1302 foreach ($crons as $jt => $job) {
1303 if ($jt < $timenow) {
1304 $how_many_overdue++;
1305 }
1306 }
1307 }
1308 }
1309 return $how_many_overdue;
1310 }
1311
1312 /**
1313 * Run updates on plugin activation.
1314 */
1315 public function run_updates() {
1316 include_once(WPO_PLUGIN_MAIN_PATH.'includes/class-wp-optimize-updates.php');
1317 WP_Optimize_Updates::check_updates();
1318 }
1319
1320 /**
1321 * Returns warning about overdue crons.
1322 *
1323 * @param int $howmany count of overdue crons
1324 * @return string
1325 */
1326 public function show_admin_warning_overdue_crons($howmany) {
1327 $ret = '<div class="updated below-h2"><p>';
1328 // translators: %d is number of overdue cron jobs
1329 $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>';
1330 $ret .= '</p></div>';
1331 return $ret;
1332 }
1333
1334 /**
1335 * Normalizes path string
1336 *
1337 * @param string $path
1338 *
1339 * @return string
1340 */
1341 private function wp_normalize_path($path) {
1342 // Wp_normalize_path is not present before WP 3.9.
1343 if (function_exists('wp_normalize_path')) return wp_normalize_path($path);
1344 // Taken from WP 4.6.
1345 $path = str_replace('\\', '/', $path);
1346 $path = preg_replace('|(?<=.)/+|', '/', $path);
1347 if (':' === substr($path, 1, 1)) {
1348 $path = ucfirst($path);
1349 }
1350 return $path;
1351 }
1352
1353 /**
1354 * Returns templates directory path
1355 *
1356 * @return string
1357 */
1358 public function get_templates_dir() {
1359 $templates_dir = $this->wp_normalize_path(WPO_PLUGIN_MAIN_PATH.'templates');
1360 $filtered_templates_dir = apply_filters('wp_optimize_templates_dir', $templates_dir);
1361 return is_string($filtered_templates_dir) ? $filtered_templates_dir : $templates_dir;
1362 }
1363
1364 /**
1365 * Returns templates URL
1366 *
1367 * @return string
1368 */
1369 public function get_templates_url() {
1370 $templates_url = WPO_PLUGIN_URL . 'templates';
1371 $filtered_templates_url = apply_filters('wp_optimize_templates_url', $templates_url);
1372 return is_string($filtered_templates_url) ? $filtered_templates_url : $templates_url;
1373 }
1374
1375 /**
1376 * Return or output view content
1377 *
1378 * @param string $path - path to template, usually relative to templates/ within the WP-O directory
1379 * @param boolean $return_instead_of_echo - what to do with the results
1380 * @param array $extract_these - key/value pairs for substitution into the scope of the template
1381 *
1382 * @return string|void
1383 */
1384 public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) {
1385 if ($return_instead_of_echo) ob_start();
1386
1387 if (preg_match('#^([^/]+)/(.*)$#', $path, $matches)) {
1388 $prefix = $matches[1];
1389 $suffix = $matches[2];
1390 if (isset($this->template_directories[$prefix])) {
1391 $template_file = $this->template_directories[$prefix].'/'.$suffix;
1392 }
1393 }
1394
1395 if (!isset($template_file)) {
1396 $template_file = WPO_PLUGIN_MAIN_PATH.'templates/'.$path;
1397 }
1398
1399 $template_file = apply_filters('wp_optimize_template', $template_file, $path);
1400
1401 do_action('wp_optimize_before_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1402
1403 if (!file_exists($template_file)) {
1404 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
1405 echo esc_html__('Error:', 'wp-optimize').' '.esc_html__('template not found', 'wp-optimize')." (".esc_html($path).")";
1406 } else {
1407 extract($extract_these);
1408 // The following are useful variables which can be used in the template.
1409 // They appear as unused, but may be used in the $template_file.
1410 $wpdb = $GLOBALS['wpdb'];// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wpdb might be used in the included template
1411 $wp_optimize = $this;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wp_optimize might be used in the included template
1412 $optimizer = $this->get_optimizer();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $optimizer might be used in the included template
1413 $options = $this->get_options();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $options might be used in the included template
1414 $wp_optimize_notices = $this->get_notices();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wp_optimize_notices might be used in the included template
1415 include $template_file;
1416 }
1417
1418 do_action('wp_optimize_after_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1419
1420 if ($return_instead_of_echo) return ob_get_clean();
1421 }
1422
1423 /**
1424 * Build a list of template directories (stored in self::$template_directories)
1425 */
1426 private function register_template_directories() {
1427
1428 $template_directories = array();
1429
1430 $templates_dir = $this->get_templates_dir();
1431
1432 if ($dh = opendir($templates_dir)) {
1433 while (($file = readdir($dh)) !== false) {
1434 if ('.' === $file || '..' === $file) continue;
1435 if (is_dir($templates_dir.'/'.$file)) {
1436 $template_directories[$file] = $templates_dir.'/'.$file;
1437 }
1438 }
1439 closedir($dh);
1440 }
1441
1442 // Optimal hook for most extensions to hook into.
1443 $this->template_directories = apply_filters('wp_optimize_template_directories', $template_directories);
1444
1445 }
1446
1447 /**
1448 * Message to debug
1449 *
1450 * @param string $message Message to insert into the log.
1451 * @param array $context array with variables used in $message like in template,
1452 * for ex.
1453 * $message = 'Hello {message}';
1454 * $context = ['message' => 'world']
1455 * 'Hello world' string will be saved in log.
1456 */
1457 public function log($message, $context = array()) {
1458 $this->get_logger()->debug($message, $context);
1459 }
1460
1461 /**
1462 * Format Bytes Into KB/MB
1463 *
1464 * @param mixed $bytes Number of bytes to be converted.
1465 * @param integer $decimals the number of decimal digits
1466 * @return string return the correct format size.
1467 */
1468 public function format_size($bytes, $decimals = 2) {
1469 if (!is_numeric($bytes)) return __('N/A', 'wp-optimize');
1470
1471 if (1073741824 <= $bytes) {
1472 $bytes = number_format($bytes / 1073741824, $decimals) . ' GB';
1473 } elseif (1048576 <= $bytes) {
1474 $bytes = number_format($bytes / 1048576, $decimals) . ' MB';
1475 } elseif (1024 <= $bytes) {
1476 $bytes = number_format($bytes / 1024, $decimals) . ' KB';
1477 } elseif (1 < $bytes) {
1478 $bytes = $bytes . ' bytes';
1479 } elseif (1 === (int) $bytes) {
1480 $bytes = $bytes . ' byte';
1481 } else {
1482 $bytes = '0 bytes';
1483 }
1484
1485 return $bytes;
1486 }
1487
1488 /**
1489 * Format a timestamp into a human-readable date time
1490 *
1491 * @param int $timestamp Epoch timestamp to convert
1492 * @param string $separator String separator
1493 * @return string
1494 */
1495 public function format_date_time($timestamp, $separator = ' @ ') {
1496 return date_i18n(get_option('date_format'). $separator .get_option('time_format'), ($timestamp + get_option('gmt_offset') * 3600));
1497 }
1498
1499 /**
1500 * Executed this function on cron event.
1501 *
1502 * @return void
1503 */
1504 public function cron_action() {
1505
1506 $optimizer = $this->get_optimizer();
1507 $options = $this->get_options();
1508
1509 $this->log('WPO: Starting cron_action()');
1510 $options->update_option('last-optimized', time());
1511 if ('true' === $options->get_option('schedule')) {
1512 $this_options = $options->get_option('auto');
1513
1514 // Currently the output of the optimizations is not saved/used/logged.
1515 $optimizer->do_optimizations($this_options, 'auto');
1516 }
1517
1518 }
1519
1520 /**
1521 * Schedule cron tasks used by plugin.
1522 *
1523 * @return void
1524 */
1525 private function schedule_plugin_cron_tasks() {
1526 if (!wp_next_scheduled('wpo_weekly_cron_tasks')) {
1527 wp_schedule_event(current_time("timestamp", 0), 'wpo_weekly', 'wpo_weekly_cron_tasks');
1528 }
1529
1530 add_action('wpo_weekly_cron_tasks', array($this, 'do_weekly_cron_tasks'));
1531
1532 // Run 404 detector cron.
1533 $this->get_404_detector_cron();
1534 }
1535
1536 /**
1537 * Do plugin background tasks.
1538 *
1539 * @return void
1540 */
1541 public function do_weekly_cron_tasks() {
1542 // add tasks here.
1543 $this->get_db_info()->update_plugin_json();
1544 }
1545
1546 /**
1547 * This will customize a URL with a correct Affiliate link
1548 * This function can be updated to suit any URL as longs as the URL is passed
1549 *
1550 * @param string $url - URL to check to see if it is an updraftplus match.
1551 * @param ?string $text - Text to be entered within the href a tags.
1552 * @param ?string $html - Any specific HTML to be added. Supplied parameter will not be escaped in this function. Provide escaped, safe HTML
1553 * @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"').
1554 * @param bool $return_instead_of_echo - if set, then the result will be returned, not echo-ed.
1555 * @return string|void
1556 */
1557 public function wp_optimize_url($url, $text = '', $html = '', $attrs = '', $return_instead_of_echo = false) {
1558 // Check if the URL is UpdraftPlus.
1559 $url = $this->maybe_add_affiliate_params($url); // Return URL - check if there is HTML such as images.
1560
1561 // Check if the variable $text is empty (null value included), otherwise assign $html.
1562 $content = empty($text) ? $html : esc_html($text);
1563
1564 // Check if $attrs is an array to convert the attributes into a string line.
1565 $str_attrs = '';
1566 if (is_array($attrs)) {
1567 foreach ($attrs as $attr => $value) {
1568 $str_attrs .= $attr . '="' . esc_attr($value) . '" ';
1569 }
1570 } else {
1571 // If $attrs is empty, the `explode` function will only return an empty array.
1572 $attrs = explode('=', $attrs);
1573 // Check if $attrs in positions 1 and 2 are not empty and exist; otherwise, return an empty string.
1574 $str_attrs = !empty($attrs[0]) && !empty($attrs[1]) ? $attrs[0] . '="' . esc_attr(str_replace('"', '', $attrs[1])) . '"' : '';
1575 }
1576
1577 // Check if it is necessary to add a target value if the url is external
1578 $is_external_url = $this->is_external_url($url);
1579 if ($is_external_url) {
1580 $str_attrs = preg_replace('/\s+target="_blank"/i', '', $str_attrs);
1581 $str_attrs .= ' target="_blank"';
1582 }
1583
1584 $result = sprintf(
1585 '<a href="%s" %s>%s</a>',
1586 esc_url($url),
1587 $str_attrs,
1588 wp_kses_post($content)
1589 );
1590
1591 if ($return_instead_of_echo) return $result;
1592 echo wp_kses_post($result);
1593 }
1594
1595 /**
1596 * Check if a URL is external
1597 *
1598 * @param string $url
1599 * @return bool
1600 */
1601 public function is_external_url($url) {
1602 if (empty($url)) {
1603 return false;
1604 }
1605
1606 $current_domain = wp_parse_url(home_url(), PHP_URL_HOST);
1607 $url = wp_parse_url($url, PHP_URL_HOST);
1608
1609 // Compare the domains and return true if they are different
1610 return $current_domain !== $url;
1611 }
1612
1613 /**
1614 * Get a URL with an eventual affiliate ID
1615 *
1616 * @param string $url
1617 * @return string
1618 */
1619 public function maybe_add_affiliate_params($url) {
1620 // Check if the URL is UpdraftPlus.
1621 if (false !== strpos($url, '//updraftplus.com')) {
1622 // Set URL with Affiliate ID.
1623 $url = add_query_arg(array('afref' => $this->get_notices()->get_affiliate_id()), $url);
1624
1625 // Apply filters.
1626 $url = apply_filters('wpoptimize_updraftplus_com_link', $url);
1627 }
1628 $filtered_url = apply_filters('wpoptimize_maybe_add_affiliate_params', $url);
1629 return is_string($filtered_url) ? $filtered_url : $url;
1630 }
1631
1632 /**
1633 * Setup WPO logger(s)
1634 */
1635 public function setup_loggers() {
1636
1637 $logger = $this->get_logger();
1638 $loggers = $this->wpo_loggers();
1639
1640 if (!empty($loggers)) {
1641 foreach ($loggers as $_logger) {
1642 $logger->add_logger($_logger);
1643 }
1644 }
1645
1646 add_action('wp_optimize_after_optimizations', array($this, 'after_optimizations_logger_action'));
1647 }
1648
1649 /**
1650 * Run logger actions after all optimizations done
1651 */
1652 public function after_optimizations_logger_action() {
1653 $loggers = $this->get_logger()->get_loggers();
1654 if (!empty($loggers)) {
1655 foreach ($loggers as $logger) {
1656 if (is_a($logger, 'Updraft_Email_Logger')) {
1657 $logger->flush_log();
1658 }
1659 }
1660 }
1661 }
1662
1663 /**
1664 * Returns list of WPO loggers instances
1665 * Apply filter wp_optimize_loggers
1666 *
1667 * @return array
1668 */
1669 public function wpo_loggers() {
1670
1671 $loggers = array();
1672 $loggers_classes_by_id = array();
1673 $options_keys = array();
1674
1675 $loggers_classes = $this->get_loggers_classes();
1676
1677 foreach ($loggers_classes as $logger_class => $source) {
1678 $loggers_classes_by_id[strtolower($logger_class)] = $logger_class;
1679 }
1680
1681 $options = $this->get_options();
1682
1683 $saved_loggers = $options->get_option('logging');
1684 $logger_additional_options = $options->get_option('logging-additional');
1685
1686 // create loggers classes instances.
1687 if (!empty($saved_loggers)) {
1688 // check for previous version options format.
1689 $keys = array_keys($saved_loggers);
1690
1691 // if options stored in old format then reformat it.
1692 if (false === is_numeric($keys[0])) {
1693 $_saved_loggers = array();
1694 foreach ($saved_loggers as $logger_id => $enabled) {
1695 if ($enabled) {
1696 $_saved_loggers[] = $logger_id;
1697 }
1698 }
1699
1700 // fill email with admin.
1701 if (array_key_exists('updraft_email_logger', $saved_loggers) && $saved_loggers['updraft_email_logger']) {
1702 $logger_additional_options['updraft_email_logger'] = array(
1703 get_option('admin_email')
1704 );
1705 }
1706
1707 $saved_loggers = $_saved_loggers;
1708 }
1709
1710 foreach ($saved_loggers as $i => $logger_id) {
1711
1712 if (!array_key_exists($logger_id, $loggers_classes_by_id)) continue;
1713
1714 $logger_class = $loggers_classes_by_id[$logger_id];
1715
1716 $logger = new $logger_class();
1717
1718 $logger_options = $logger->get_options_list();
1719
1720 if (!empty($logger_options)) {
1721 foreach (array_keys($logger_options) as $option_name) {
1722 if (array_key_exists($option_name, $options_keys)) {
1723 $options_keys[$option_name]++;
1724 } else {
1725 $options_keys[$option_name] = 0;
1726 }
1727
1728 $option_value = isset($logger_additional_options[$option_name][$options_keys[$option_name]]) ? $logger_additional_options[$option_name][$options_keys[$option_name]] : '';
1729
1730 // if options in old format then get correct value.
1731 if ('' === $option_value && array_key_exists($logger_id, $logger_additional_options)) {
1732 $option_value = array_shift($logger_additional_options[$logger_id]);
1733 }
1734
1735 $logger->set_option($option_name, $option_value);
1736 }
1737 }
1738
1739 // check if logger is active.
1740 $active = (!is_array($logger_additional_options) || (array_key_exists('active', $logger_additional_options) && empty($logger_additional_options['active'][$i]))) ? false : true;
1741
1742 if ($active) {
1743 $logger->enable();
1744 } else {
1745 $logger->disable();
1746 }
1747
1748 $loggers[] = $logger;
1749 }
1750 }
1751
1752 $filtered_loggers = apply_filters('wp_optimize_loggers', $loggers);
1753 return is_array($filtered_loggers) ? $filtered_loggers : $loggers;
1754 }
1755
1756 /**
1757 * Returns associative array with logger class name in a key and path to class file in a value.
1758 *
1759 * @return array
1760 */
1761 public function get_loggers_classes() {
1762 $loggers_classes = array(
1763 'Updraft_PHP_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-php-logger.php',
1764 'Updraft_Email_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-email-logger.php',
1765 'Updraft_Ring_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-ring-logger.php'
1766 );
1767
1768 $loggers_classes = apply_filters('wp_optimize_loggers_classes', $loggers_classes);
1769
1770 if (!empty($loggers_classes)) {
1771 foreach ($loggers_classes as $logger_class => $logger_file) {
1772 if (!class_exists($logger_class)) {
1773 if (is_file($logger_file)) {
1774 include_once($logger_file);
1775 }
1776 }
1777 }
1778 }
1779
1780 return $loggers_classes;
1781 }
1782
1783 /**
1784 * Returns information about all loggers classes.
1785 *
1786 * @return array
1787 */
1788 public function get_loggers_classes_info() {
1789 $loggers_classes = $this->get_loggers_classes();
1790
1791 $loggers_classes_info = array();
1792
1793 if (!empty($loggers_classes)) {
1794 foreach (array_keys($loggers_classes) as $logger_class_name) {
1795
1796 if (!class_exists($logger_class_name)) continue;
1797
1798 $logger_id = strtolower($logger_class_name);
1799 $logger_class = new $logger_class_name();
1800
1801 $loggers_classes_info[$logger_id] = array(
1802 'description' => $logger_class->get_description(),
1803 'available' => $logger_class->is_available(),
1804 'allow_multiple' => $logger_class->is_allow_multiple(),
1805 'options' => $logger_class->get_options_list()
1806 );
1807 }
1808 }
1809
1810 return $loggers_classes_info;
1811 }
1812
1813 /**
1814 * Returns true if optimization works in multisite mode
1815 *
1816 * @return boolean
1817 */
1818 public function is_multisite_mode() {
1819 return (is_multisite() && self::is_premium());
1820 }
1821
1822 /**
1823 * Returns true if the current user can run optimizations.
1824 *
1825 * @return bool
1826 */
1827 public function can_run_optimizations(): bool {
1828 // we don't check permissions for cron jobs.
1829 if (defined('DOING_CRON') && DOING_CRON) return true;
1830
1831 if (self::is_premium() && false === user_can(get_current_user_id(), 'wpo_run_optimizations')) return false;
1832 return true;
1833 }
1834
1835 /**
1836 * Returns true if current user can manage plugin options.
1837 *
1838 * @return bool
1839 */
1840 public function can_manage_options() {
1841 if (self::is_premium() && false == user_can(get_current_user_id(), 'wpo_manage_settings')) return false;
1842 return true;
1843 }
1844
1845 /**
1846 * Returns list of all sites in multisite
1847 *
1848 * @return array
1849 */
1850 public function get_sites() {
1851 $sites = array();
1852 if (function_exists('get_sites')) {
1853 $sites = get_sites(array('network_id' => null, 'deleted' => 0, 'number' => 999999));
1854 }
1855 return $sites;
1856 }
1857
1858 /**
1859 * Returns script memory limit in megabytes.
1860 *
1861 * @param string|bool $memory_limit
1862 * @return int
1863 */
1864 public function get_memory_limit($memory_limit = false) {
1865 // Returns in megabytes
1866 if (false == $memory_limit) $memory_limit = ini_get('memory_limit');
1867 $memory_limit = rtrim($memory_limit);
1868
1869 return $this->return_bytes($memory_limit);
1870 }
1871
1872 /**
1873 * Returns free memory in bytes.
1874 *
1875 * @return int
1876 */
1877 public function get_free_memory() {
1878 return $this->get_memory_limit() - memory_get_usage();
1879 }
1880
1881 /**
1882 * Checks PHP memory_limit and WP_MAX_MEMORY_LIMIT values and return minimal.
1883 *
1884 * @return int memory limit in bytes.
1885 */
1886 public function get_script_memory_limit() {
1887 $memory_limit = $this->get_memory_limit();
1888
1889 if (defined('WP_MAX_MEMORY_LIMIT')) {
1890 $wp_memory_limit = $this->get_memory_limit(WP_MAX_MEMORY_LIMIT);
1891
1892 if ($wp_memory_limit > 0 && $wp_memory_limit < $memory_limit) {
1893 $memory_limit = $wp_memory_limit;
1894 }
1895 }
1896
1897 return $memory_limit;
1898 }
1899
1900 /**
1901 * Returns max packet size for database.
1902 *
1903 * @return int|string
1904 */
1905 public function get_max_packet_size() {
1906 global $wpdb;
1907 static $mp = 0;
1908
1909 if ($mp > 0) return $mp;
1910
1911 $mp = (int) $wpdb->get_var("SELECT @@session.max_allowed_packet");
1912 // Default to 1MB
1913 $mp = (is_numeric($mp) && $mp > 0) ? $mp : 1048576;
1914 // 32MB
1915 if ($mp < 33554432) {
1916 $save = $wpdb->show_errors(false);
1917 @$wpdb->query("SET GLOBAL max_allowed_packet=33554432");// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress errors from displaying
1918 $wpdb->show_errors($save);
1919
1920 $mp = (int) $wpdb->get_var("SELECT @@session.max_allowed_packet");
1921 // Default to 1MB
1922 $mp = (is_numeric($mp) && $mp > 0) ? $mp : 1048576;
1923 }
1924
1925 return $mp;
1926 }
1927
1928 /**
1929 * Converts shorthand memory notation value to bytes.
1930 * From http://php.net/manual/en/function.ini-get.php
1931 *
1932 * @param string $val shorthand memory notation value.
1933 *
1934 * @return string
1935 */
1936 public function return_bytes($val) {
1937 $val = trim($val);
1938 $last = strtolower($val[strlen($val)-1]);
1939 $val = (int) $val;
1940 switch ($last) {
1941 case 'g':
1942 $val *= 1024;
1943 // no break
1944 case 'm':
1945 $val *= 1024;
1946 // no break
1947 case 'k':
1948 $val *= 1024;
1949 }
1950
1951 return $val;
1952 }
1953
1954 /**
1955 * Log fatal errors to defined log destinations.
1956 */
1957 public function log_fatal_errors() {
1958 $last_error = error_get_last();
1959
1960 if (isset($last_error['type']) && E_ERROR === $last_error['type']) {
1961 $this->get_logger()->critical($last_error['message']);
1962 }
1963 }
1964
1965 /**
1966 * Close browser connection and continue script work. - Taken from UpdraftPlus
1967 *
1968 * @param string $txt Response to browser; this must be JSON (or if not, alter the Content-Type header handling below)
1969 * @return void
1970 */
1971 public function close_browser_connection($txt = '') {
1972 if (!headers_sent()) {
1973 // Close browser connection so that it can resume AJAX polling
1974 header('Content-Length: '.(empty($txt) ? '0' : 4+strlen($txt)));
1975 header('Content-Type: application/json');
1976 header('Connection: close');
1977 header('Content-Encoding: none');
1978 }
1979
1980 if (session_id()) session_write_close();
1981 echo "\r\n\r\n";
1982 echo $txt; // phpcs:ignore WordPress.Security.EscapeOutput -- Output is already escaped
1983 // These two added - 19-Feb-15 - started being required on local dev machine, for unknown reason (probably some plugin that started an output buffer).
1984 $ob_level = ob_get_level();
1985 while ($ob_level > 0) {
1986 ob_end_flush();
1987 $ob_level--;
1988 }
1989 flush();
1990 if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
1991 if (function_exists('litespeed_finish_request')) litespeed_finish_request();
1992 }
1993
1994 /**
1995 * Try to change PHP script time limit.
1996 */
1997 public function change_time_limit() {
1998 $time_limit = (defined('WP_OPTIMIZE_SET_TIME_LIMIT') && WP_OPTIMIZE_SET_TIME_LIMIT > 15) ? WP_OPTIMIZE_SET_TIME_LIMIT : 1800;
1999
2000 // phpcs:disable
2001 // Generic.PHP.NoSilencedErrors.Discouraged -- Try to reduce the chances of PHP self-terminating via reaching max_execution_time.
2002 // Squiz.PHP.DiscouragedFunctions.Discouraged -- Try to reduce the chances of PHP self-terminating via reaching max_execution_time.
2003 @set_time_limit($time_limit);
2004 // phpcs:enable
2005 }
2006
2007 /**
2008 * Does the request come from UDC
2009 *
2010 * @return boolean
2011 */
2012 public function is_updraft_central_request() {
2013 return defined('UPDRAFTCENTRAL_COMMAND') && UPDRAFTCENTRAL_COMMAND;
2014 }
2015
2016 /**
2017 * Does the data need to be included in this request. Currently only true if the request is made from UpdraftCentral.
2018 *
2019 * @return boolean
2020 */
2021 public function template_should_include_data() {
2022 /**
2023 * Filters whether data should be included in certain templates or not.
2024 */
2025 return (bool) apply_filters('wpo_template_should_include_data', $this->is_updraft_central_request());
2026 }
2027
2028 /**
2029 * Load the templates for the modal window
2030 */
2031 public function load_modal_template() {
2032 $this->include_template('modal.php');
2033 }
2034
2035 /**
2036 * Adds compress popup template
2037 */
2038 public function add_smush_popup_template() {
2039 if ($this->current_user_can()) {
2040 $compression_server_hint = Updraft_Smush_Manager()->get_compression_server_hint();
2041 $this->include_template('images/smush-popup.php', false, array('compression_server_hint' => $compression_server_hint));
2042 }
2043 }
2044
2045 /**
2046 * Delete transients and semaphores data from options table.
2047 */
2048 public function delete_transients_and_semaphores() {
2049 global $wpdb;
2050
2051 $masks = array(
2052 'updraft_locked_wpo_%',
2053 'updraft_unlocked_wpo_%',
2054 'updraft_last_lock_time_wpo_%',
2055 'updraft_semaphore_wpo_%',
2056 'wpo_locked_%',
2057 'wpo_unlocked_%',
2058 'wpo_last_lock_time_%',
2059 'wpo_semaphore_%',
2060 '_transient_timeout_wpo_%',
2061 '_transient_wpo_%',
2062 'updraft_lock_wpo_%',
2063 'wpo_last_scheduled_%',
2064 );
2065
2066 $where_parts = array();
2067 foreach ($masks as $mask) {
2068 $where_parts[] = "(`option_name` LIKE '{$mask}')";
2069 }
2070
2071 $where_clause = implode(' OR ', $where_parts);
2072 $wpdb->query($wpdb->prepare("DELETE FROM `{$wpdb->options}` WHERE %s", $where_clause));
2073 }
2074
2075 /**
2076 * Prevents bots from indexing plugins list
2077 *
2078 * @param string $output
2079 * @return string
2080 */
2081 public function robots_txt($output) {
2082 $upload_dir = wp_upload_dir();
2083 $path = wp_parse_url($upload_dir['baseurl']);
2084 $output .= "\nUser-agent: *";
2085 $output .= "\nDisallow: " . str_replace($path['scheme'].'://'.$path['host'], '', $upload_dir['baseurl']) . "/wpo/wpo-plugins-tables-list.json\n";
2086 return $output;
2087 }
2088
2089 /**
2090 * Returns desired enqueue version string
2091 *
2092 * @return string Enqueue version as string
2093 */
2094 public function get_enqueue_version() {
2095 return (defined('WP_DEBUG') && WP_DEBUG) ? WPO_VERSION.'.'.time() : WPO_VERSION;
2096 }
2097
2098 /**
2099 * Returns script suffix string
2100 *
2101 * @return string empty or `.min` suffix string
2102 */
2103 public function get_min_or_not_string() {
2104 return (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
2105 }
2106
2107 /**
2108 * Returns script suffix string with WPO_VERSION
2109 *
2110 * @return string empty or min suffix with wpo_version string
2111 */
2112 public function get_min_or_not_internal_string() {
2113 return (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '-' . str_replace('.', '-', WPO_VERSION) . '.min';
2114 }
2115
2116 /**
2117 * Instantiate Ajax handling class
2118 */
2119 private function load_ajax_handler() {
2120 WPO_Ajax::get_instance();
2121 }
2122
2123 /**
2124 * Get instance of WP_Optimize_Table_Management
2125 *
2126 * @return WP_Optimize_Table_Management
2127 */
2128 public function get_table_management() {
2129 return WP_Optimize_Table_Management::get_instance();
2130 }
2131
2132 /**
2133 * Get instance of WP_Optimize_404_Detector
2134 *
2135 * @return WP_Optimize_404_Detector
2136 */
2137 public function get_404_detector() {
2138 return WP_Optimize_404_Detector::get_instance();
2139 }
2140
2141 /**
2142 * Returns the message used to notify the user that the PHP version or WordPress version doesn't meet the requirements.
2143 *
2144 * @return string
2145 */
2146 public function get_minimum_requirements_notice_message() {
2147 // translators: %1$s is minimum required PHP version, %2$s is minimum required WordPress version
2148 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));
2149 }
2150
2151 /**
2152 * Output notice when PHP version or WordPress version is not meet for WP-Optimize.
2153 *
2154 * @return void
2155 */
2156 public function output_minimum_requirements_notice() {
2157 ?>
2158 <div class="notice notice-error is-dismissible">
2159 <p><?php echo $this->get_minimum_requirements_notice_message(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is already escaped ?></p>
2160 </div>
2161 <?php
2162 }
2163 }
2164
2165 /**
2166 * Unschedule specific tasks:
2167 * `wpo_cron_event2` => DB optimizations
2168 * `wpo_weekly_cron_tasks` => ex: plugin.json auto update
2169 *
2170 * @return void
2171 */
2172 function wpo_cron_deactivate() {
2173 WP_Optimize()->log('running wpo_cron_deactivate()');
2174 wp_clear_scheduled_hook('wpo_cron_event2');
2175 wp_clear_scheduled_hook('wpo_weekly_cron_tasks');
2176 }
2177
2178 function WP_Optimize() {
2179 return WP_Optimize::instance();
2180 }
2181
2182 endif;
2183
2184 $GLOBALS['wp_optimize'] = WP_Optimize();
2185