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

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

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