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

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