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

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