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

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