PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.7.0
4.7.0 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 All 111 releases
wp-optimize / wp-optimize.php

wp-optimize.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.7.0, at wp-optimize.php

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