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

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