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

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