PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.3
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
wp-optimize / wp-optimize.php

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

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