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

1,841 lines 59.6 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.20
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.20');
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 // add_filter('updraftcentral_host_plugins', array($this, 'attach_updraftcentral_host'));
566 // if (file_exists(WPO_PLUGIN_MAIN_PATH.'central/factory.php')) include_once(WPO_PLUGIN_MAIN_PATH.'central/factory.php');
567 }
568
569 /**
570 * Attach this wp-optimize plugin as host of the UpdraftCentral libraries
571 * (e.g. "central" folder)
572 *
573 * @param array $hosts List of plugins having the "central" library integrated into them
574 *
575 * @return array
576 */
577 public function attach_updraftcentral_host($hosts) {
578 $hosts[] = 'wp-optimize';
579 return $hosts;
580 }
581
582 /**
583 * Filter cache tabs (when it is Kinsta)
584 *
585 * @param array $tabs An array of tabs
586 *
587 * @return array $tabs An array of tabs
588 */
589 public function filter_cache_tabs($tabs) {
590 unset($tabs['preload']);
591 unset($tabs['advanced']);
592 unset($tabs['gzip']);
593 unset($tabs['settings']);
594 return $tabs;
595 }
596
597 /**
598 * Check whether one of free/Premium is active (whether it is this instance or not)
599 *
600 * @param String $which - 'free' or 'premium'
601 *
602 * @return String|Boolean - plugin path (if installed) or false if not
603 */
604 private function is_active($which = 'free') {
605 $active_plugins = $this->get_active_plugins();
606 foreach ($active_plugins as $file) {
607 if ('wp-optimize.php' == basename($file)) {
608 $plugin_dir = WP_PLUGIN_DIR.'/'.dirname($file);
609 if (('free' == $which && !file_exists($plugin_dir.'/premium.php')) || ('free' != $which && file_exists($plugin_dir.'/premium.php'))) return $file;
610 }
611 }
612 return false;
613 }
614
615 /**
616 * Gets an array of plugins active on either the current site, or site-wide
617 *
618 * @return Array - a list of plugin paths (relative to the plugin directory)
619 */
620 private function get_active_plugins() {
621
622 // Gets all active plugins on the current site
623 $active_plugins = get_option('active_plugins');
624
625 if (is_multisite()) {
626 $network_active_plugins = get_site_option('active_sitewide_plugins');
627 if (!empty($network_active_plugins)) {
628 $network_active_plugins = array_keys($network_active_plugins);
629 $active_plugins = array_merge($active_plugins, $network_active_plugins);
630 }
631 }
632
633 return $active_plugins;
634 }
635
636 /**
637 * This function checks whether a specific plugin is installed, and returns information about it
638 *
639 * @param string $name Specify "Plugin Name" to return details about it.
640 * @return array Returns an array of details such as if installed, the name of the plugin and if it is active.
641 */
642 public function is_installed($name) {
643
644 // Needed to have the 'get_plugins()' function
645 include_once(ABSPATH.'wp-admin/includes/plugin.php');
646
647 // Gets all plugins available
648 $get_plugins = get_plugins();
649
650 $active_plugins = $this->get_active_plugins();
651
652 $plugin_info = array();
653 $plugin_info['installed'] = false;
654 $plugin_info['active'] = false;
655
656 // Loops around each plugin available.
657 foreach ($get_plugins as $key => $value) {
658 // If the plugin name matches that of the specified name, it will gather details.
659 if ($value['Name'] != $name && $value['TextDomain'] != $name) continue;
660 $plugin_info['installed'] = true;
661 $plugin_info['name'] = $key;
662 $plugin_info['version'] = $value['Version'];
663 if (in_array($key, $active_plugins)) {
664 $plugin_info['active'] = true;
665 }
666 break;
667 }
668 return $plugin_info;
669 }
670
671 /**
672 * This is a notice to show users that premium is installed
673 */
674 public function show_admin_notice_premium() {
675 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>';
676 if (isset($_GET['activate'])) unset($_GET['activate']);
677 }
678
679 /**
680 * Show update to Premium notice for non-premium multisite.
681 */
682 public function show_multisite_update_to_premium_notice() {
683 if (!is_multisite() || self::is_premium()) return;
684
685 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>';
686 }
687
688 public function admin_init() {
689 $pagenow = $GLOBALS['pagenow'];
690
691 $this->register_template_directories();
692
693 if (('index.php' == $pagenow && current_user_can('update_plugins')) || ('index.php' == $pagenow && defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
694 $options = $this->get_options();
695
696 $dismissed_until = $options->get_option('dismiss_dash_notice_until', 0);
697
698 if (file_exists(WPO_PLUGIN_MAIN_PATH . 'index.html')) {
699 $installed = filemtime(WPO_PLUGIN_MAIN_PATH . 'index.html');
700 $installed_for = (time() - $installed);
701 }
702
703 if (($installed && time() > $dismissed_until && $installed_for > (14 * 86400) && !defined('WP_OPTIMIZE_NOADS_B')) || (defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
704 add_action('all_admin_notices', array($this, 'show_admin_notice_upgradead'));
705 }
706 }
707
708 if ($this->is_wp_smush_installed()) {
709 add_filter('transient_wp-smush-conflict_check', array($this, 'modify_wp_smush_conflict_check'), 9, 1);
710 }
711 }
712
713 /**
714 * Checks whether the WP Smush plugin is active or not
715 *
716 * @return bool
717 */
718 private function is_wp_smush_installed() {
719 return is_plugin_active('wp-smushit/wp-smush.php');
720 }
721
722 /**
723 * Remove WPO plugin name from WP Smushit transient value
724 *
725 * @return array $active_plugins
726 */
727 public function modify_wp_smush_conflict_check($active_plugins) {
728 // This can be boolean value since it is return value of get_transient
729 if (!is_array($active_plugins)) return $active_plugins;
730
731 if (false !== ($key = array_search('WP-Optimize - Clean, Compress, Cache', $active_plugins))) {
732 unset($active_plugins[$key]);
733 }
734 return $active_plugins;
735 }
736
737 /**
738 * Get the install or update notice instance
739 *
740 * @return WP_Optimize_Install_Or_Update_Notice
741 */
742 public function get_install_or_update_notice() {
743 static $instance = null;
744 if (is_a($instance, 'WP_Optimize_Install_Or_Update_Notice')) return $instance;
745 $instance = new WP_Optimize_Install_Or_Update_Notice();
746 return $instance;
747 }
748
749 public function show_admin_notice_upgradead() {
750 $this->include_template('notices/thanks-for-using-main-dash.php');
751 }
752
753 public function capability_required() {
754 return apply_filters('wp_optimize_capability_required', 'manage_options');
755 }
756
757 /**
758 * Returns array of translations used in javascript code.
759 *
760 * @return array
761 */
762 public function wpo_js_translations() {
763 $log_message = __('For more details, please check your logs configured in logging destinations settings.', 'wp-optimize');
764 return apply_filters('wpo_js_translations', array(
765 'automatic_backup_before_optimizations' => __('Automatic backup before optimizations', 'wp-optimize'),
766 'error_unexpected_response' => __('An unexpected response was received.', 'wp-optimize'),
767 'optimization_complete' => __('Optimization complete', 'wp-optimize'),
768 'with_warnings' => __('(with warnings - open the browser console for more details)', 'wp-optimize'),
769 'optimizing_table' => __('Optimizing table:', 'wp-optimize'),
770 'run_optimizations' => __('Run optimizations', 'wp-optimize'),
771 'table_optimization_timeout' => 120000,
772 'add' => __('Add', 'wp-optimize'),
773 'cancel' => __('Cancel', 'wp-optimize'),
774 'cancelling' => __('Cancelling...', 'wp-optimize'),
775 'enable' => __('Enable', 'wp-optimize'),
776 'disable' => __('Disable', 'wp-optimize'),
777 'please_select_settings_file' => __('Please, select settings file.', 'wp-optimize'),
778 'are_you_sure_you_want_to_remove_logging_destination' => __('Are you sure you want to remove this logging destination?', 'wp-optimize'),
779 'fill_all_settings_fields' => __('Before saving, you need to complete the currently incomplete settings (or remove them).', 'wp-optimize'),
780 'table_was_not_repaired' => __('%s was not repaired.', 'wp-optimize') . ' ' . $log_message,
781 'table_was_not_deleted' => __('%s was not deleted.', 'wp-optimize') . ' ' . $log_message,
782 'table_was_not_converted' => __('%s was not converted to InnoDB.', 'wp-optimize') . ' ' . $log_message,
783 'please_use_positive_integers' => __('Please use positive integers.', 'wp-optimize'),
784 'please_use_valid_values' => __('Please use valid values.', 'wp-optimize'),
785 'update' => __('Update', 'wp-optimize'),
786 'run_now' => __('Run now', 'wp-optimize'),
787 'starting_preload' => __('Started preload...', 'wp-optimize'),
788 'loading_urls' => __('Loading URLs...', 'wp-optimize'),
789 'current_cache_size' => __('Current cache size:', 'wp-optimize'),
790 'number_of_files' => __('Number of files:', 'wp-optimize'),
791 'toggle_info' => __('Show information', 'wp-optimize'),
792 'delete_file' => __('Delete', 'wp-optimize'),
793 'added_to_list' => __('Added to the list', 'wp-optimize'),
794 'added_notice' => __('The file was added to the list', 'wp-optimize'),
795 'save_notice' => __('Save the changes', 'wp-optimize'),
796 'page_refresh' => __('Refreshing the page to reflect changes...', 'wp-optimize'),
797 'cache_file_not_found' => __('Cache file was not found.', 'wp-optimize'),
798 'settings_have_been_deleted_successfully' => __('WP-Optimize settings have been deleted successfully.', 'wp-optimize'),
799 'loading_data' => __('Loading data...', 'wp-optimize'),
800 'spinner_src' => esc_attr(admin_url('images/spinner-2x.gif')),
801 'settings_page_url' => is_multisite() ? network_admin_url('admin.php?page=wpo_settings') : admin_url('admin.php?page=wpo_settings'),
802 'sites' => $this->get_sites(),
803 'user_always_ignores_table_delete_warning' => (get_user_meta(get_current_user_id(), 'wpo-ignores-table-delete-warning', true)) ? true : false,
804 'post_meta_tweak_completed' => __('The tweak has been performed.', 'wp-optimize'),
805 'no_minified_assets' => __('No minified files are present', 'wp-optimize'),
806 'network_site_url' => network_site_url(),
807 'export_settings_file_name' => 'wpoptimize-settings-'.sanitize_title(get_bloginfo('name')).'.json',
808 'import_select_file' => __('You have not yet selected a file to import.', 'wp-optimize'),
809 'import_invalid_json_file' => __('Error: The chosen file is corrupt.', 'wp-optimize') . ' ' . __('Please choose a valid WP-Optimize export file.', 'wp-optimize'),
810 'importing' => __('Importing...', 'wp-optimize'),
811 'importing_data_from' => __('This will import data from:', 'wp-optimize'),
812 'exported_on' => __('Which was exported on:', 'wp-optimize'),
813 'continue_import' => __('Do you want to carry out the import?', 'wp-optimize'),
814 'select_destination' => __('Select destination', 'wp-optimize'),
815 ));
816 }
817
818 /**
819 * Manages the admin bar menu for caching (currently page and minify)
820 */
821 public function cache_admin_bar($wp_admin_bar) {
822
823 $options = $this->get_options();
824 if (!$options->get_option('enable_cache_in_admin_bar', true)) return;
825
826 /**
827 * The "purge cache" menu items
828 *
829 * @param array $menu_items - The menu items, in the format required by $wp_admin_bar->add_menu()
830 * @param object $wp_admin_bar
831 */
832 $menu_items = apply_filters('wpo_cache_admin_bar_menu_items', array(), $wp_admin_bar);
833
834 if (empty($menu_items) || !is_array($menu_items)) return;
835
836 $wp_admin_bar->add_menu(array(
837 'id' => 'wpo_purge_cache',
838 'title' => __('Purge cache', 'wp-optimize'),
839 'href' => '#',
840 'meta' => array(
841 'title' => __('Purge cache', 'wp-optimize'),
842 ),
843 'parent' => false,
844 ));
845
846 foreach ($menu_items as $item) {
847 $wp_admin_bar->add_menu($item);
848 }
849 }
850
851 /**
852 * Add settings link on plugin page
853 *
854 * @param string $links Passing through the URL to be used within the HREF.
855 * @return string Returns the Links.
856 */
857 public function plugin_settings_link($links) {
858
859 $admin_page_url = $this->get_options()->admin_page_url();
860 $settings_page_url = $this->get_options()->admin_page_url('wpo_settings');
861
862 if (false == self::is_premium()) {
863 $premium_link = '<a href="' . esc_url($this->premium_version_link) . '" target="_blank">' . __('Premium', 'wp-optimize') . '</a>';
864 array_unshift($links, $premium_link);
865 }
866
867 $settings_link = '<a href="' . esc_url($settings_page_url) . '">' . __('Settings', 'wp-optimize') . '</a>';
868 array_unshift($links, $settings_link);
869
870 $optimize_link = '<a href="' . esc_url($admin_page_url) . '">' . __('Optimize', 'wp-optimize') . '</a>';
871 array_unshift($links, $optimize_link);
872 return $links;
873 }
874
875 /**
876 * Action wpo_tables_list_additional_column_data. Output button Optimize in the action column.
877 *
878 * @param string $content String for output to column
879 * @param object $table_info Object with table info.
880 *
881 * @return string
882 */
883 public function tables_list_additional_column_data($content, $table_info) {
884 if ($table_info->is_needing_repair) {
885 $content .= '<div class="wpo_button_wrap">'
886 . '<button class="button button-secondary run-single-table-repair" data-table="' . esc_attr($table_info->Name) . '">' . __('Repair', 'wp-optimize') . '</button>'
887 . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
888 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
889 . '</div>';
890 }
891
892 // table belongs to plugin.
893 if ($table_info->can_be_removed) {
894 $content .= '<div>'
895 . '<button class="button button-secondary run-single-table-delete" data-table="' . esc_attr($table_info->Name) . '">' . __('Remove', 'wp-optimize') . '</button>'
896 . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
897 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
898 . '</div>';
899 }
900
901 // Add option for MyISAM to InnoDB conversion.
902 if ('MyISAM' == $table_info->Engine) {
903 $content .= '<div class="wpo_button_convert wpo_button_wrap">'
904 . '<button class="button button-secondary toinnodb" data-table="' . esc_attr($table_info->Name) . '">' . __('Convert to InnoDB', 'wp-optimize') . '</button>'
905 . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
906 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
907 . '</div>';
908
909 }
910
911 return $content;
912 }
913
914 /**
915 * Initialize WP-Optimize page cache.
916 */
917 public function init_page_cache() {
918 if ($this->get_page_cache()->config->get_option('enable_page_caching', false)) {
919 if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
920 $this->get_page_cache()->enable();
921 }
922 }
923 }
924
925 /**
926 * Schedules cron event based on selected schedule type
927 *
928 * @return void
929 */
930 public function cron_activate() {
931 $gmt_offset = (int) (3600 * get_option('gmt_offset'));
932
933 $options = $this->get_options();
934
935 if ($options->get_option('schedule') === false) {
936 $options->set_default_options();
937 } else {
938 if ('true' == $options->get_option('schedule')) {
939 if (!wp_next_scheduled('wpo_cron_event2')) {
940 $schedule_type = $options->get_option('schedule-type', 'wpo_weekly');
941
942 // Backward compatibility
943 if ('wpo_otherweekly' == $schedule_type) $schedule_type = 'wpo_fortnightly';
944
945 $this_time = (86400 * 7);
946
947 switch ($schedule_type) {
948 case "wpo_daily":
949 $this_time = 86400;
950 break;
951
952 case "wpo_weekly":
953 $this_time = (86400 * 7);
954 break;
955
956 case "wpo_fortnightly":
957 $this_time = (86400 * 14);
958 break;
959
960 case "wpo_monthly":
961 $this_time = (86400 * 30);
962 break;
963 }
964
965 add_action('wpo_cron_event2', array($this, 'cron_action'));
966 $result = wp_schedule_event((current_time("timestamp", 0) + $this_time - $gmt_offset), $schedule_type, 'wpo_cron_event2');
967 WP_Optimize()->log('running wp_schedule_event()');
968 if (is_wp_error($result)) {
969 $error_msg = $result->get_error_message();
970 WP_Optimize()->log($error_msg);
971 WP_Optimize()->log(print_r($result, true));
972 } else {
973 WP_Optimize()->log($result);
974 }
975 }
976 }
977 }
978 }
979
980 /**
981 * Clears all cron events
982 *
983 * @return void
984 */
985 public function wpo_cron_deactivate() {
986 $cron_jobs = _get_cron_array();
987 foreach ($cron_jobs as $job) {
988 foreach (array_keys($job) as $hook) {
989 if (preg_match('/^wpo_/', $hook)) wp_unschedule_hook($hook);
990 }
991 }
992 }
993
994 /**
995 * Scheduler public functions to update schedulers
996 *
997 * @param array $schedules An array of schedules being passed.
998 * @return array An array of schedules being returned.
999 */
1000 public function cron_schedules($schedules) {
1001 $schedules['wpo_daily'] = array('interval' => 86400, 'display' => 'Once Daily');
1002 $schedules['wpo_weekly'] = array('interval' => 86400 * 7, 'display' => 'Once Weekly');
1003 $schedules['wpo_fortnightly'] = array('interval' => 86400 * 14, 'display' => 'Once Every Fortnight');
1004 $schedules['wpo_monthly'] = array('interval' => 86400 * 30, 'display' => 'Once Every Month');
1005 return $schedules;
1006 }
1007
1008 /**
1009 * Returns count of overdue cron jobs.
1010 *
1011 * @return integer
1012 */
1013 public function howmany_overdue_crons() {
1014 $how_many_overdue = 0;
1015 if (function_exists('_get_cron_array') || (is_file(ABSPATH.WPINC.'/cron.php') && include_once(ABSPATH.WPINC.'/cron.php') && function_exists('_get_cron_array'))) {
1016 $crons = _get_cron_array();
1017 if (is_array($crons)) {
1018 $timenow = time();
1019 foreach ($crons as $jt => $job) {
1020 if ($jt < $timenow) {
1021 $how_many_overdue++;
1022 }
1023 }
1024 }
1025 }
1026 return $how_many_overdue;
1027 }
1028
1029 /**
1030 * Run updates on plugin activation.
1031 */
1032 public function run_updates() {
1033 include_once(WPO_PLUGIN_MAIN_PATH.'includes/class-wp-optimize-updates.php');
1034 WP_Optimize_Updates::check_updates();
1035 }
1036
1037 /**
1038 * Returns warning about overdue crons.
1039 *
1040 * @param int $howmany count of overdue crons
1041 * @return string
1042 */
1043 public function show_admin_warning_overdue_crons($howmany) {
1044 $ret = '<div class="updated below-h2"><p>';
1045 $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>';
1046 $ret .= '</p></div>';
1047 return $ret;
1048 }
1049
1050 private function wp_normalize_path($path) {
1051 // Wp_normalize_path is not present before WP 3.9.
1052 if (function_exists('wp_normalize_path')) return wp_normalize_path($path);
1053 // Taken from WP 4.6.
1054 $path = str_replace('\\', '/', $path);
1055 $path = preg_replace('|(?<=.)/+|', '/', $path);
1056 if (':' === substr($path, 1, 1)) {
1057 $path = ucfirst($path);
1058 }
1059 return $path;
1060 }
1061
1062 public function get_templates_dir() {
1063 return apply_filters('wp_optimize_templates_dir', $this->wp_normalize_path(WPO_PLUGIN_MAIN_PATH.'templates'));
1064 }
1065
1066 public function get_templates_url() {
1067 return apply_filters('wp_optimize_templates_url', WPO_PLUGIN_URL.'templates');
1068 }
1069
1070 /**
1071 * Return or output view content
1072 *
1073 * @param String $path - path to template, usually relative to templates/ within the WP-O directory
1074 * @param Boolean $return_instead_of_echo - what to do with the results
1075 * @param Array $extract_these - key/value pairs for substitution into the scope of the template
1076 *
1077 * @return String|Void
1078 */
1079 public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) {
1080 if ($return_instead_of_echo) ob_start();
1081
1082 if (preg_match('#^([^/]+)/(.*)$#', $path, $matches)) {
1083 $prefix = $matches[1];
1084 $suffix = $matches[2];
1085 if (isset($this->template_directories[$prefix])) {
1086 $template_file = $this->template_directories[$prefix].'/'.$suffix;
1087 }
1088 }
1089
1090 if (!isset($template_file)) {
1091 $template_file = WPO_PLUGIN_MAIN_PATH.'templates/'.$path;
1092 }
1093
1094 $template_file = apply_filters('wp_optimize_template', $template_file, $path);
1095
1096 do_action('wp_optimize_before_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1097
1098 if (!file_exists($template_file)) {
1099 error_log("WP Optimize: template not found: ".$template_file);
1100 echo __('Error:', 'wp-optimize').' '.__('template not found', 'wp-optimize')." (".$path.")";
1101 } else {
1102 extract($extract_these);
1103 // The following are useful variables which can be used in the template.
1104 // They appear as unused, but may be used in the $template_file.
1105 $wpdb = $GLOBALS['wpdb'];// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wpdb might be used in the included template
1106 $wp_optimize = $this;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wp_optimize might be used in the included template
1107 $optimizer = $this->get_optimizer();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $optimizer might be used in the included template
1108 $options = $this->get_options();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $options might be used in the included template
1109 $wp_optimize_notices = $this->get_notices();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wp_optimize_notices might be used in the included template
1110 include $template_file;
1111 }
1112
1113 do_action('wp_optimize_after_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1114
1115 if ($return_instead_of_echo) return ob_get_clean();
1116 }
1117
1118 /**
1119 * Build a list of template directories (stored in self::$template_directories)
1120 */
1121 private function register_template_directories() {
1122
1123 $template_directories = array();
1124
1125 $templates_dir = $this->get_templates_dir();
1126
1127 if ($dh = opendir($templates_dir)) {
1128 while (($file = readdir($dh)) !== false) {
1129 if ('.' == $file || '..' == $file) continue;
1130 if (is_dir($templates_dir.'/'.$file)) {
1131 $template_directories[$file] = $templates_dir.'/'.$file;
1132 }
1133 }
1134 closedir($dh);
1135 }
1136
1137 // Optimal hook for most extensions to hook into.
1138 $this->template_directories = apply_filters('wp_optimize_template_directories', $template_directories);
1139
1140 }
1141
1142 /**
1143 * Message to debug
1144 *
1145 * @param string $message Message to insert into the log.
1146 * @param array $context array with variables used in $message like in template,
1147 * for ex.
1148 * $message = 'Hello {message}';
1149 * $context = ['message' => 'world']
1150 * 'Hello world' string will be saved in log.
1151 */
1152 public function log($message, $context = array()) {
1153 $this->get_logger()->debug($message, $context);
1154 }
1155
1156 /**
1157 * Format Bytes Into KB/MB
1158 *
1159 * @param mixed $bytes Number of bytes to be converted.
1160 * @param integer $decimals the number of decimal digits
1161 * @return integer return the correct format size.
1162 */
1163 public function format_size($bytes, $decimals = 2) {
1164 if (!is_numeric($bytes)) return __('N/A', 'wp-optimize');
1165
1166 if (1073741824 <= $bytes) {
1167 $bytes = number_format($bytes / 1073741824, $decimals) . ' GB';
1168 } elseif (1048576 <= $bytes) {
1169 $bytes = number_format($bytes / 1048576, $decimals) . ' MB';
1170 } elseif (1024 <= $bytes) {
1171 $bytes = number_format($bytes / 1024, $decimals) . ' KB';
1172 } elseif (1 < $bytes) {
1173 $bytes = $bytes . ' bytes';
1174 } elseif (1 == $bytes) {
1175 $bytes = $bytes . ' byte';
1176 } else {
1177 $bytes = '0 bytes';
1178 }
1179
1180 return $bytes;
1181 }
1182
1183 /**
1184 * Format a timestamp into a juman readable date time
1185 *
1186 * @param int $timestamp
1187 * @return string
1188 */
1189 public function format_date_time($timestamp) {
1190 return date_i18n(get_option('date_format').' @ '.get_option('time_format'), ($timestamp + get_option('gmt_offset') * 3600));
1191 }
1192
1193 /**
1194 * Executed this function on cron event.
1195 *
1196 * @return void
1197 */
1198 public function cron_action() {
1199
1200 $optimizer = $this->get_optimizer();
1201 $options = $this->get_options();
1202
1203 $this->log('WPO: Starting cron_action()');
1204 $options->update_option('last-optimized', time());
1205 if ('true' == $options->get_option('schedule')) {
1206 $this_options = $options->get_option('auto');
1207
1208 // Currently the output of the optimizations is not saved/used/logged.
1209 $optimizer->do_optimizations($this_options, 'auto');
1210 }
1211
1212 }
1213
1214 /**
1215 * Schedule cron tasks used by plugin.
1216 *
1217 * @return void
1218 */
1219 public function schedule_plugin_cron_tasks() {
1220 if (!wp_next_scheduled('wpo_weekly_cron_tasks')) {
1221 wp_schedule_event(current_time("timestamp", 0), 'weekly', 'wpo_weekly_cron_tasks');
1222 }
1223
1224 add_action('wpo_weekly_cron_tasks', array($this, 'do_weekly_cron_tasks'));
1225 }
1226
1227 /**
1228 * Do plugin background tasks.
1229 *
1230 * @return void
1231 */
1232 public function do_weekly_cron_tasks() {
1233 // add tasks here.
1234 $this->get_db_info()->update_plugin_json();
1235 }
1236
1237 /**
1238 * This will customize a URL with a correct Affiliate link
1239 * This function can be update to suit any URL as longs as the URL is passed
1240 *
1241 * @param String $url - URL to be check to see if it an updraftplus match.
1242 * @param String $text - Text to be entered within the href a tags.
1243 * @param String $html - Any specific HTML to be added. Supplied parameter will not be escaped in this function. Provide escaped, safe HTML
1244 * @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"').
1245 * @param Boolean $return_instead_of_echo - if set, then the result will be returned, not echo-ed.
1246 * @return String|void
1247 */
1248 public function wp_optimize_url($url, $text = '', $html = '', $attrs = '', $return_instead_of_echo = false) {
1249 // Check if the URL is UpdraftPlus.
1250 $url = $this->maybe_add_affiliate_params($url); // Return URL - check if there is HTML such as images.
1251
1252 // Check if the variable $text is empty (null value included), otherwise assign $html.
1253 $content = empty($text) ? $html : esc_html($text);
1254
1255 // Check if $attrs is an array to convert the attributes into a string line.
1256 $str_attrs = '';
1257 if (is_array($attrs)) {
1258 foreach ($attrs as $attr => $value) {
1259 $str_attrs .= $attr . '="' . esc_attr($value) . '" ';
1260 }
1261 } else {
1262 // If $attrs is empty, the explode function will only return an empty array.
1263 $attrs = explode('=', $attrs);
1264 // Check if $attrs in positions 1 and 2 are not empty and exist; otherwise, return an empty string.
1265 $str_attrs = !empty($attrs[0]) && !empty($attrs[1]) ? $attrs[0] . '="' . esc_attr(str_replace('"', '', $attrs[1])) . '"' : '';
1266 }
1267
1268 // Check if it is necessary to add a target value if the url is external
1269 $is_external_url = $this->is_external_url($url);
1270 if ($is_external_url) {
1271 $str_attrs = preg_replace('/\s+target="_blank"/i', '', $str_attrs);
1272 $str_attrs .= ' target="_blank"';
1273 }
1274
1275 $result = sprintf(
1276 '<a href="%s" %s>%s</a>',
1277 esc_url($url),
1278 $str_attrs,
1279 $content
1280 );
1281
1282 if ($return_instead_of_echo) return $result;
1283 echo $result;
1284 }
1285
1286 /**
1287 * Check if a URL is external
1288 *
1289 * @param string $url
1290 * @return string
1291 */
1292 public function is_external_url($url) {
1293 if (empty($url)) {
1294 return false;
1295 }
1296
1297 $current_domain = wp_parse_url(home_url(), PHP_URL_HOST);
1298 $url = wp_parse_url($url, PHP_URL_HOST);
1299
1300 // Compare the domains and return true if they are different
1301 return $current_domain !== $url;
1302 }
1303
1304 /**
1305 * Get an URL with an eventual affiliate ID
1306 *
1307 * @param string $url
1308 * @return string
1309 */
1310 public function maybe_add_affiliate_params($url) {
1311 // Check if the URL is UpdraftPlus.
1312 if (false !== strpos($url, '//updraftplus.com')) {
1313 // Set URL with Affiliate ID.
1314 $url = add_query_arg(array('afref' => $this->get_notices()->get_affiliate_id()), $url);
1315
1316 // Apply filters.
1317 $url = apply_filters('wpoptimize_updraftplus_com_link', $url);
1318 }
1319 return apply_filters('wpoptimize_maybe_add_affiliate_params', $url);
1320 }
1321
1322 /**
1323 * Setup WPO logger(s)
1324 */
1325 public function setup_loggers() {
1326
1327 $logger = $this->get_logger();
1328 $loggers = $this->wpo_loggers();
1329
1330 if (!empty($loggers)) {
1331 foreach ($loggers as $_logger) {
1332 $logger->add_logger($_logger);
1333 }
1334 }
1335
1336 add_action('wp_optimize_after_optimizations', array($this, 'after_optimizations_logger_action'));
1337 }
1338
1339 /**
1340 * Run logger actions after all optimizations done
1341 */
1342 public function after_optimizations_logger_action() {
1343 $loggers = $this->get_logger()->get_loggers();
1344 if (!empty($loggers)) {
1345 foreach ($loggers as $logger) {
1346 if (is_a($logger, 'Updraft_Email_Logger')) {
1347 $logger->flush_log();
1348 }
1349 }
1350 }
1351 }
1352
1353 /**
1354 * Returns list of WPO loggers instances
1355 * Apply filter wp_optimize_loggers
1356 *
1357 * @return array
1358 */
1359 public function wpo_loggers() {
1360
1361 $loggers = array();
1362 $loggers_classes_by_id = array();
1363 $options_keys = array();
1364
1365 $loggers_classes = $this->get_loggers_classes();
1366
1367 foreach ($loggers_classes as $logger_class => $source) {
1368 $loggers_classes_by_id[strtolower($logger_class)] = $logger_class;
1369 }
1370
1371 $options = $this->get_options();
1372
1373 $saved_loggers = $options->get_option('logging');
1374 $logger_additional_options = $options->get_option('logging-additional');
1375
1376 // create loggers classes instances.
1377 if (!empty($saved_loggers)) {
1378 // check for previous version options format.
1379 $keys = array_keys($saved_loggers);
1380
1381 // if options stored in old format then reformat it.
1382 if (false == is_numeric($keys[0])) {
1383 $_saved_loggers = array();
1384 foreach ($saved_loggers as $logger_id => $enabled) {
1385 if ($enabled) {
1386 $_saved_loggers[] = $logger_id;
1387 }
1388 }
1389
1390 // fill email with admin.
1391 if (array_key_exists('updraft_email_logger', $saved_loggers) && $saved_loggers['updraft_email_logger']) {
1392 $logger_additional_options['updraft_email_logger'] = array(
1393 get_option('admin_email')
1394 );
1395 }
1396
1397 $saved_loggers = $_saved_loggers;
1398 }
1399
1400 foreach ($saved_loggers as $i => $logger_id) {
1401
1402 if (!array_key_exists($logger_id, $loggers_classes_by_id)) continue;
1403
1404 $logger_class = $loggers_classes_by_id[$logger_id];
1405
1406 $logger = new $logger_class();
1407
1408 $logger_options = $logger->get_options_list();
1409
1410 if (!empty($logger_options)) {
1411 foreach (array_keys($logger_options) as $option_name) {
1412 if (array_key_exists($option_name, $options_keys)) {
1413 $options_keys[$option_name]++;
1414 } else {
1415 $options_keys[$option_name] = 0;
1416 }
1417
1418 $option_value = isset($logger_additional_options[$option_name][$options_keys[$option_name]]) ? $logger_additional_options[$option_name][$options_keys[$option_name]] : '';
1419
1420 // if options in old format then get correct value.
1421 if ('' === $option_value && array_key_exists($logger_id, $logger_additional_options)) {
1422 $option_value = array_shift($logger_additional_options[$logger_id]);
1423 }
1424
1425 $logger->set_option($option_name, $option_value);
1426 }
1427 }
1428
1429 // check if logger is active.
1430 $active = (!is_array($logger_additional_options) || (array_key_exists('active', $logger_additional_options) && empty($logger_additional_options['active'][$i]))) ? false : true;
1431
1432 if ($active) {
1433 $logger->enable();
1434 } else {
1435 $logger->disable();
1436 }
1437
1438 $loggers[] = $logger;
1439 }
1440 }
1441
1442 $loggers = apply_filters('wp_optimize_loggers', $loggers);
1443
1444 return $loggers;
1445 }
1446
1447 /**
1448 * Returns associative array with logger class name in a key and path to class file in a value.
1449 *
1450 * @return array
1451 */
1452 public function get_loggers_classes() {
1453 $loggers_classes = array(
1454 'Updraft_PHP_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-php-logger.php',
1455 'Updraft_Email_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-email-logger.php',
1456 'Updraft_Ring_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-ring-logger.php'
1457 );
1458
1459 $loggers_classes = apply_filters('wp_optimize_loggers_classes', $loggers_classes);
1460
1461 if (!empty($loggers_classes)) {
1462 foreach ($loggers_classes as $logger_class => $logger_file) {
1463 if (!class_exists($logger_class)) {
1464 if (is_file($logger_file)) {
1465 include_once($logger_file);
1466 }
1467 }
1468 }
1469 }
1470
1471 return $loggers_classes;
1472 }
1473
1474 /**
1475 * Returns information about all loggers classes.
1476 *
1477 * @return array
1478 */
1479 public function get_loggers_classes_info() {
1480 $loggers_classes = $this->get_loggers_classes();
1481
1482 $loggers_classes_info = array();
1483
1484 if (!empty($loggers_classes)) {
1485 foreach (array_keys($loggers_classes) as $logger_class_name) {
1486
1487 if (!class_exists($logger_class_name)) continue;
1488
1489 $logger_id = strtolower($logger_class_name);
1490 $logger_class = new $logger_class_name();
1491
1492 $loggers_classes_info[$logger_id] = array(
1493 'description' => $logger_class->get_description(),
1494 'available' => $logger_class->is_available(),
1495 'allow_multiple' => $logger_class->is_allow_multiple(),
1496 'options' => $logger_class->get_options_list()
1497 );
1498 }
1499 }
1500
1501 return $loggers_classes_info;
1502 }
1503
1504 /**
1505 * Returns true if optimization works in multisite mode
1506 *
1507 * @return boolean
1508 */
1509 public function is_multisite_mode() {
1510 return (is_multisite() && self::is_premium());
1511 }
1512
1513 /**
1514 * Returns true if current user can run optimizations.
1515 *
1516 * @return bool
1517 */
1518 public function can_run_optimizations() {
1519 // we don't check permissions for cron jobs.
1520 if (defined('DOING_CRON') && DOING_CRON) return true;
1521
1522 if (self::is_premium() && false == user_can(get_current_user_id(), 'wpo_run_optimizations')) return false;
1523 return true;
1524 }
1525
1526 /**
1527 * Returns true if current user can manage plugin options.
1528 *
1529 * @return bool
1530 */
1531 public function can_manage_options() {
1532 if (self::is_premium() && false == user_can(get_current_user_id(), 'wpo_manage_settings')) return false;
1533 return true;
1534 }
1535
1536 /**
1537 * Returns list of all sites in multisite
1538 *
1539 * @return array
1540 */
1541 public function get_sites() {
1542 $sites = array();
1543 // check if function get_sites exists (since 4.6.0) else use wp_get_sites.
1544 if (function_exists('get_sites')) {
1545 $sites = get_sites(array('network_id' => null, 'deleted' => 0, 'number' => 999999));
1546 } elseif (function_exists('wp_get_sites')) {
1547 $sites = wp_get_sites(array('network_id' => null, 'deleted' => 0, 'limit' => 999999));
1548 }
1549 return $sites;
1550 }
1551
1552 /**
1553 * Returns script memory limit in megabytes.
1554 *
1555 * @param bool $memory_limit
1556 * @return int
1557 */
1558 public function get_memory_limit($memory_limit = false) {
1559 // Returns in megabytes
1560 if (false == $memory_limit) $memory_limit = ini_get('memory_limit');
1561 $memory_limit = rtrim($memory_limit);
1562
1563 return $this->return_bytes($memory_limit);
1564 }
1565
1566 /**
1567 * Returns free memory in bytes.
1568 *
1569 * @return int
1570 */
1571 public function get_free_memory() {
1572 return $this->get_memory_limit() - memory_get_usage();
1573 }
1574
1575 /**
1576 * Checks PHP memory_limit and WP_MAX_MEMORY_LIMIT values and return minimal.
1577 *
1578 * @return int memory limit in bytes.
1579 */
1580 public function get_script_memory_limit() {
1581 $memory_limit = $this->get_memory_limit();
1582
1583 if (defined('WP_MAX_MEMORY_LIMIT')) {
1584 $wp_memory_limit = $this->get_memory_limit(WP_MAX_MEMORY_LIMIT);
1585
1586 if ($wp_memory_limit > 0 && $wp_memory_limit < $memory_limit) {
1587 $memory_limit = $wp_memory_limit;
1588 }
1589 }
1590
1591 return $memory_limit;
1592 }
1593
1594 /**
1595 * Returns max packet size for database.
1596 *
1597 * @return int|string
1598 */
1599 public function get_max_packet_size() {
1600 global $wpdb;
1601 static $mp = 0;
1602
1603 if ($mp > 0) return $mp;
1604
1605 $mp = (int) $wpdb->get_var("SELECT @@session.max_allowed_packet");
1606 // Default to 1MB
1607 $mp = (is_numeric($mp) && $mp > 0) ? $mp : 1048576;
1608 // 32MB
1609 if ($mp < 33554432) {
1610 $save = $wpdb->show_errors(false);
1611 @$wpdb->query("SET GLOBAL max_allowed_packet=33554432");// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress errors from displaying
1612 $wpdb->show_errors($save);
1613
1614 $mp = (int) $wpdb->get_var("SELECT @@session.max_allowed_packet");
1615 // Default to 1MB
1616 $mp = (is_numeric($mp) && $mp > 0) ? $mp : 1048576;
1617 }
1618
1619 return $mp;
1620 }
1621
1622 /**
1623 * Converts shorthand memory notation value to bytes.
1624 * From http://php.net/manual/en/function.ini-get.php
1625 *
1626 * @param string $val shorthand memory notation value.
1627 */
1628 public function return_bytes($val) {
1629 $val = trim($val);
1630 $last = strtolower($val[strlen($val)-1]);
1631 $val = (int) $val;
1632 switch ($last) {
1633 case 'g':
1634 $val *= 1024;
1635 // no break
1636 case 'm':
1637 $val *= 1024;
1638 // no break
1639 case 'k':
1640 $val *= 1024;
1641 }
1642
1643 return $val;
1644 }
1645
1646 /**
1647 * Log fatal errors to defined log destinations.
1648 */
1649 public function log_fatal_errors() {
1650 $last_error = error_get_last();
1651
1652 if (isset($last_error['type']) && E_ERROR === $last_error['type']) {
1653 $this->get_logger()->critical($last_error['message']);
1654 }
1655 }
1656
1657 /**
1658 * Close browser connection and continue script work. - Taken from UpdraftPlus
1659 *
1660 * @param array $txt Response to browser; this must be JSON (or if not, alter the Content-Type header handling below)
1661 * @return void
1662 */
1663 public function close_browser_connection($txt = '') {
1664 if (!headers_sent()) {
1665 // Close browser connection so that it can resume AJAX polling
1666 header('Content-Length: '.(empty($txt) ? '0' : 4+strlen($txt)));
1667 header('Connection: close');
1668 header('Content-Encoding: none');
1669 }
1670
1671 if (session_id()) session_write_close();
1672 echo "\r\n\r\n";
1673 echo $txt;
1674 // These two added - 19-Feb-15 - started being required on local dev machine, for unknown reason (probably some plugin that started an output buffer).
1675 $ob_level = ob_get_level();
1676 while ($ob_level > 0) {
1677 ob_end_flush();
1678 $ob_level--;
1679 }
1680 flush();
1681 if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
1682 }
1683
1684 /**
1685 * Get the current theme's style.css headers
1686 *
1687 * @return array|WP_Error
1688 */
1689 public function get_stylesheet_headers() {
1690 static $headers;
1691 if (isset($headers)) return $headers;
1692
1693 $style = get_template_directory_uri() . '/style.css';
1694
1695 /**
1696 * Filters wp_remote_get parameters, when checking if browser cache is enabled.
1697 *
1698 * @param array $request_params Default parameters
1699 */
1700 $request_params = apply_filters('wpoptimize_get_stylesheet_headers_args', array('timeout' => 10));
1701
1702 // trying to load style.css.
1703 $response = wp_remote_get($style, $request_params);
1704
1705 if (is_a($response, 'WP_Error')) return $response;
1706
1707 $headers = wp_remote_retrieve_headers($response);
1708
1709 if (method_exists($headers, 'getAll')) {
1710 $headers = $headers->getAll();
1711 }
1712
1713 return is_array($headers) ? $headers : array();
1714 }
1715
1716 /**
1717 * Try to change PHP script time limit.
1718 */
1719 public function change_time_limit() {
1720 $time_limit = (defined('WP_OPTIMIZE_SET_TIME_LIMIT') && WP_OPTIMIZE_SET_TIME_LIMIT > 15) ? WP_OPTIMIZE_SET_TIME_LIMIT : 1800;
1721
1722 @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.
1723 }
1724
1725 /**
1726 * Does the request come from UDC
1727 *
1728 * @return boolean
1729 */
1730 public function is_updraft_central_request() {
1731 return defined('UPDRAFTCENTRAL_COMMAND') && UPDRAFTCENTRAL_COMMAND;
1732 }
1733
1734 /**
1735 * Does the data need to be included in this request. Currently only true if the request is made from UpdraftCentral.
1736 *
1737 * @return boolean
1738 */
1739 public function template_should_include_data() {
1740 /**
1741 * Filters wether data should be included in certain templates or not.
1742 */
1743 return apply_filters('wpo_template_should_include_data', $this->is_updraft_central_request());
1744 }
1745
1746 /**
1747 * Load the templates for the modal window
1748 */
1749 public function load_modal_template() {
1750 $this->include_template('modal.php');
1751 }
1752
1753 /**
1754 * Delete transients and semaphores data from options table.
1755 */
1756 public function delete_transients_and_semaphores() {
1757 global $wpdb;
1758
1759 $masks = array(
1760 'updraft_locked_wpo_%',
1761 'updraft_unlocked_wpo_%',
1762 'updraft_last_lock_time_wpo_%',
1763 'updraft_semaphore_wpo_%',
1764 'wpo_locked_%',
1765 'wpo_unlocked_%',
1766 'wpo_last_lock_time_%',
1767 'wpo_semaphore_%',
1768 '_transient_timeout_wpo_%',
1769 '_transient_wpo_%',
1770 'updraft_lock_wpo_%',
1771 'wpo_last_scheduled_%',
1772 );
1773
1774 $where_parts = array();
1775 foreach ($masks as $mask) {
1776 $where_parts[] = "(`option_name` LIKE '{$mask}')";
1777 }
1778
1779 $wpdb->query("DELETE FROM {$wpdb->options} WHERE " . join(' OR ', $where_parts));
1780 }
1781
1782 /**
1783 * Prevents bots from indexing plugins list
1784 */
1785 public function robots_txt($output) {
1786 $upload_dir = wp_upload_dir();
1787 $path = parse_url($upload_dir['baseurl']);
1788 $output .= "\nUser-agent: *";
1789 $output .= "\nDisallow: " . str_replace($path['scheme'].'://'.$path['host'], '', $upload_dir['baseurl']) . "/wpo-plugins-tables-list.json\n";
1790 return $output;
1791 }
1792
1793 /**
1794 * Returns desired enqueue version string
1795 *
1796 * @return string Enqueue version as string
1797 */
1798 public function get_enqueue_version() {
1799 return (defined('WP_DEBUG') && WP_DEBUG) ? WPO_VERSION.'.'.time() : WPO_VERSION;
1800 }
1801
1802 /**
1803 * Returns script suffix string
1804 *
1805 * @return string empty or `.min` suffix string
1806 */
1807 public function get_min_or_not_string() {
1808 return (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
1809 }
1810
1811 /**
1812 * Returns script suffix string with WPO_VERSION
1813 *
1814 * @return string empty or min suffix with wpo_version string
1815 */
1816 public function get_min_or_not_internal_string() {
1817 return (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '-' . str_replace('.', '-', WPO_VERSION) . '.min';
1818 }
1819
1820 /**
1821 * Instantiate Ajax handling class
1822 */
1823 private function load_ajax_handler() {
1824 WPO_Ajax::get_instance();
1825 }
1826 }
1827
1828 function wpo_cron_deactivate() {
1829 WP_Optimize()->log('running wpo_cron_deactivate()');
1830 wp_clear_scheduled_hook('wpo_cron_event2');
1831 wp_clear_scheduled_hook('wpo_weekly_cron_tasks');
1832 }
1833
1834 function WP_Optimize() {
1835 return WP_Optimize::instance();
1836 }
1837
1838 endif;
1839
1840 $GLOBALS['wp_optimize'] = WP_Optimize();
1841