PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.12
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.12
4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 3.8.0 All 110 releases
wp-optimize / wp-optimize.php

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

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