PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.9
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.9
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 / includes / class-updraft-smush-manager.php

class-updraft-smush-manager.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.2.9, at includes/class-updraft-smush-manager.php

1,570 lines 48.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Extends the generic task manager to manage smush related queues
4 */
5
6 if (!defined('ABSPATH')) die('Access denied.');
7
8 if (!class_exists('Updraft_Task_Manager_1_3')) require_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-manager.php');
9
10 if (!class_exists('Updraft_Smush_Manager')) :
11
12 class Updraft_Smush_Manager extends Updraft_Task_Manager_1_3 {
13
14 static protected $_instance = null;
15
16 /**
17 * Options used for smush jobs
18 *
19 * @var array
20 */
21 public $options;
22
23 /**
24 * The service provider to use
25 *
26 * @var string
27 */
28 public $webservice;
29
30 /**
31 * The logger for this instance
32 *
33 * @var mixed
34 */
35 public $logger;
36
37 /**
38 * Require task-level locking
39 *
40 * @var integer
41 */
42 protected $use_per_task_lock = 60;
43
44 /**
45 * The Task Manager constructor
46 */
47 public function __construct() {
48 parent::__construct();
49
50
51 $this->commands = new Updraft_Smush_Manager_Commands($this);
52 $this->options = WP_Optimize()->get_options();
53
54 if (!isset($this->options)) {
55 $this->set_default_options();
56 }
57
58 $this->webservice = $this->options->get_option('compression_server', 'resmushit');
59
60 // Ensure the saved service is valid
61 if (!in_array($this->webservice, $this->get_allowed_services())) {
62 $this->webservice = $this->get_default_webservice();
63 }
64 $this->logger = new Updraft_File_Logger($this->get_logfile_path());
65 $this->add_logger($this->logger);
66
67 add_action('wp_ajax_updraft_smush_ajax', array($this, 'updraft_smush_ajax'));
68 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'), 9);
69 add_action('elementor/editor/before_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
70 add_action('add_attachment', array($this, 'autosmush_create_task'));
71 add_action('ud_task_initialised', array($this, 'set_task_logger'));
72 add_action('ud_task_started', array($this, 'set_task_logger'));
73 add_action('ud_task_completed', array($this, 'record_stats'));
74 add_action('ud_task_failed', array($this, 'record_stats'));
75 add_action('prune_smush_logs', array($this, 'prune_smush_logs'));
76 add_action('autosmush_process_queue', array($this, 'autosmush_process_queue'));
77 if ('show' == $this->options->get_option('show_smush_metabox', 'show')) {
78 add_action('add_meta_boxes_attachment', array($this, 'add_smush_metabox'), 10, 2);
79 add_filter('attachment_fields_to_edit', array($this, 'add_compress_button_to_media_modal' ), 10, 2);
80 }
81 add_action('delete_attachment', array($this, 'unscheduled_original_file_deletion'));
82
83 add_filter('manage_media_columns', array($this, 'manage_media_columns'));
84 add_action('manage_media_custom_column', array($this, 'manage_media_custom_column'), 10, 2);
85
86 // clean backup images cron action.
87 add_action('wpo_smush_clear_backup_images', array($this, 'clear_backup_images'));
88
89 // add filter for already compressed images by EWWW Image Optimizer.
90 add_filter('wpo_get_uncompressed_images_args', array($this, 'ewww_image_optimizer_compressed_images_args'));
91
92 if (!wp_next_scheduled('wpo_smush_clear_backup_images')) {
93 wp_schedule_event(time(), 'daily', 'wpo_smush_clear_backup_images');
94 }
95 }
96
97 /**
98 * Add custom column to Media Library.
99 *
100 * @param array $columns
101 * @return mixed
102 */
103 public function manage_media_columns($columns) {
104 $columns['wpo_smush'] = 'WP-Optimize';
105
106 return $columns;
107 }
108
109 /**
110 * Display content in the custom column.
111 *
112 * @param string $column
113 * @param int $attachment_id
114 */
115 public function manage_media_custom_column($column, $attachment_id) {
116 if ('wpo_smush' !== $column) return;
117
118 if (!$this->is_compressed($attachment_id)) return;
119
120 $smush_stats = get_post_meta($attachment_id, 'smush-stats', true);
121
122 if (empty($smush_stats)) {
123 _e('The file was either compressed using another tool or marked as compressed', 'wp-optimize');
124 return;
125 }
126
127 $original_size = $smush_stats['original-size'];
128 $smushed_size = $smush_stats['smushed-size'];
129
130 if (0 == $original_size) {
131 $info = sprintf(__('The file was compressed to %s using WP-Optimize', 'wp-optimize'), WP_Optimize()->format_size($smushed_size));
132 } else {
133 $saved = round((($original_size - $smushed_size) / $original_size * 100), 2);
134 $info = sprintf(__('The file was compressed from %s to %s, saving %s percent, using WP-Optimize', 'wp-optimize'), WP_Optimize()->format_size($original_size), WP_Optimize()->format_size($smushed_size), $saved);
135 }
136
137 echo htmlentities($info);
138
139 // Display additional information about resized images.
140 if (!empty($smush_stats['sizes-info'])) {
141 WP_Optimize()->include_template('images/smush-details.php', false, array('sizes_info' => $smush_stats['sizes-info']));
142 }
143 }
144
145 /**
146 * The Task Manager AJAX handler
147 */
148 public function updraft_smush_ajax() {
149
150 $nonce = empty($_REQUEST['nonce']) ? '' : $_REQUEST['nonce'];
151
152 if (!wp_verify_nonce($nonce, 'updraft-task-manager-ajax-nonce') || empty($_REQUEST['subaction']))
153 die('Security check failed');
154
155 $subaction = $_REQUEST['subaction'];
156
157 $allowed_commands = Updraft_Smush_Manager_Commands::get_allowed_ajax_commands();
158
159 if (in_array($subaction, $allowed_commands)) {
160
161 if (isset($_REQUEST['data'])) {
162 $data = $_REQUEST['data'];
163 $results = call_user_func(array($this->commands, $subaction), $data);
164 } else {
165 $results = call_user_func(array($this->commands, $subaction));
166 }
167
168 if (is_wp_error($results)) {
169 $results = array(
170 'status' => true,
171 'result' => false,
172 'error_code' => $results->get_error_code(),
173 'error_message' => $results->get_error_message(),
174 'error_data' => $results->get_error_data(),
175 );
176 }
177
178 echo json_encode($results);
179 } else {
180 echo json_encode(array('error' => 'No such command found'));
181 }
182 die;
183 }
184
185 /**
186 * Creates a task to auto compress an image on upload
187 *
188 * @param int $post_id - id of the post
189 */
190 public function autosmush_create_task($post_id) {
191
192 $post = get_post($post_id);
193
194 if (!$this->options->get_option('autosmush', false))
195 return;
196
197 if (!'image' == substr($post->post_mime_type, 0, 5))
198 return;
199
200 if ($this->task_exists($post_id))
201 return;
202
203 $options = array(
204 'attachment_id' => $post_id,
205 'blog_id' => get_current_blog_id(),
206 'image_quality' => $this->options->get_option('image_quality', 96),
207 'keep_original' => $this->options->get_option('back_up_original', true),
208 'preserve_exif' => $this->options->get_option('preserve_exif', true),
209 'lossy_compression' => $this->options->get_option('lossy_compression', false)
210 );
211
212 if (filesize(get_attached_file($post_id)) > 5242880) {
213 $options['request_timeout'] = 180;
214 }
215
216 $server = $this->options->get_option('compression_server', $this->webservice);
217 $task_name = $this->get_associated_task($server);
218
219 $description = "$task_name with attachment ID : ".$post_id.", autocreated on : ".date("F d, Y h:i:s", time());
220 $task = call_user_func(array($task_name, 'create_task'), 'smush', $description, $options, $task_name);
221
222 if ($task) $task->add_logger($this->logger);
223 $this->log($description);
224
225 if (!wp_next_scheduled('autosmush_process_queue')) {
226 wp_schedule_single_event(time() + 300, 'autosmush_process_queue');
227 }
228 }
229
230 /**
231 * Process the autosmush queue and sets up a cron job if needed
232 * for future processing
233 */
234 public function autosmush_process_queue() {
235
236 if (!wp_next_scheduled('autosmush_process_queue') && !$this->is_queue_processed()) {
237 wp_schedule_single_event(time() + 600, 'autosmush_process_queue');
238 }
239
240 $this->write_log_header();
241 $this->clear_cached_data();
242 $this->process_queue('smush');
243
244 if ($this->is_queue_processed()) {
245 $this->clean_up_old_tasks('smush');
246 }
247
248 }
249
250
251 /**
252 * Process the compression of a single image
253 *
254 * @param int $image - ID of image
255 * @param array $options - options to use
256 * @param string $server - the server to process with
257 *
258 * @return boolean - Status of the task
259 */
260 public function compress_single_image($image, $options, $server) {
261 $task_name = $this->get_associated_task($server);
262 $description = "$task_name - attachment ID : ". $image. ", started on : ". date("F d, Y h:i:s", time());
263
264 $task = call_user_func(array($task_name, 'create_task'), 'smush', $description, $options, $task_name);
265 $task->add_logger($this->logger);
266 $this->clear_cached_data();
267
268 if (!wp_next_scheduled('prune_smush_logs')) {
269 wp_schedule_single_event(time() + 7200, 'prune_smush_logs');
270 }
271
272 return $this->process_task($task);
273 }
274
275 /**
276 * Restores a single image if a backup is available
277 *
278 * @param int $image_id - The id of the image
279 * @param int $blog_id - The id of the blog
280 *
281 * @return bool|WP_Error - success or failure
282 */
283 public function restore_single_image($image_id, $blog_id) {
284
285 $switched_blog = false;
286 if (is_multisite() && current_user_can('manage_network_options')) {
287 switch_to_blog($blog_id);
288 $switched_blog = true;
289 } elseif (is_multisite() && get_current_blog_id() != $blog_id) {
290 return new WP_Error('restore_backup_wrong_blog_id', __('The blog ID provided does not match the current blog.', 'wp-optimize'));
291 }
292
293 $error = false;
294
295 $image_path = get_attached_file($image_id);
296 $backup_path = get_post_meta($image_id, 'original-file', true);
297
298 // If the file doesn't exist, check if it's relative
299 if (!is_file($backup_path)) {
300 $uploads_dir = wp_upload_dir();
301 $uploads_basedir = trailingslashit($uploads_dir['basedir']);
302
303 if (is_file($uploads_basedir . $backup_path)) {
304 $backup_path = $uploads_basedir . $backup_path;
305 }
306 }
307
308 // If the file still doesn't exist, the record could be an absolute path from a migrated site
309 // Use the current Uploads path
310 if (!is_file($backup_path)) {
311 $current_uploads_dir_folder = trailingslashit(substr($uploads_basedir, strlen(ABSPATH)));
312 // A strict operator (!==) needs to be used, as 0 is a positive result.
313 if (false !== strpos($backup_path, $current_uploads_dir_folder)) {
314 $temp_relative_backup_path = substr($backup_path, strpos($backup_path, $current_uploads_dir_folder) + strlen($current_uploads_dir_folder));
315 if (is_file($uploads_basedir . $temp_relative_backup_path)) {
316 $backup_path = $uploads_basedir . $temp_relative_backup_path;
317 }
318 }
319
320
321 }
322
323 // If the file still doesn't exist, the record could be an absolute path from a migrated site
324 // The current Uploads path failed, so try with the default uploads directory value
325 if (!is_file($backup_path)) {
326 // A strict operator (!==) needs to be used, as 0 is a positive result.
327 if (false !== strpos($backup_path, 'wp-content/uploads/')) {
328 $backup_path = substr($backup_path, strpos($backup_path, 'wp-content/uploads/') + strlen('wp-content/uploads/'));
329 $backup_path = $uploads_basedir . $backup_path;
330 }
331 }
332
333 if (!is_file($backup_path)) {
334 // Delete information about backup.
335 delete_post_meta($image_id, 'original-file');
336 $error = new WP_Error('restore_backup_not_found', __('The backup was not found; it may have been deleted or was already restored', 'wp-optimize'));
337 } elseif (!is_writable($image_path)) {
338 $error = new WP_Error('restore_failed', __('The destination could not be written to.', 'wp-optimize').' '.__("Please check your folder's permissions", 'wp-optimize'));
339 } elseif (!copy($backup_path, $image_path)) {
340 $error = new WP_Error('restore_failed', __('The file could not be copied; check your PHP error logs for details', 'wp-optimize'));
341 } elseif (!unlink($backup_path)) {
342 $error = new WP_Error('restore_failed', sprintf(__('The backup file %s could not be deleted.', 'wp-optimize'), $backup_path));
343 }
344
345 if (!$error) {
346 // if backup image deleted successfully
347 // then delete from attachment meta associated smush data
348 delete_post_meta($image_id, 'smush-complete');
349 delete_post_meta($image_id, 'smush-stats');
350 delete_post_meta($image_id, 'original-file');
351 delete_post_meta($image_id, 'smush-info');
352 }
353
354 if ($switched_blog) {
355 restore_current_blog();
356 }
357
358 if (is_wp_error($error)) return $error;
359
360 $this->delete_from_cache('uncompressed_images');
361
362 if (!wp_next_scheduled('prune_smush_logs')) {
363 wp_schedule_single_event(time() + 7200, 'prune_smush_logs');
364 }
365
366 return true;
367 }
368
369 /**
370 * Restore compressed images for selected blog.
371 *
372 * @param bool $restore_backup if true then restore images from backup otherwise just delete meta.
373 * @param int $blog_id blog id.
374 * @param int $images_limit how many images process per time.
375 * @param bool $delete_only_backups_meta meta fields will deleted only for images those will restored from backup.
376 *
377 * @return array ['completed' => (bool), 'message' => (string), 'error' => (string)]
378 */
379 public function bulk_restore_compressed_images($restore_backup, $blog_id = 1, $images_limit = 100, $delete_only_backups_meta = false) {
380 global $wpdb;
381
382 if (is_multisite()) {
383 switch_to_blog($blog_id);
384 }
385
386 $result = array(
387 'completed' => false,
388 'message' => '',
389 );
390
391 $processed = 0;
392
393 if ($restore_backup) {
394 // get post ids those have backup meta field.
395 $image_ids = $wpdb->get_results($wpdb->prepare("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'original-file' LIMIT %d;", $images_limit), ARRAY_A);
396
397 if (!empty($image_ids)) {
398 // run restore function for each found image.
399 foreach ($image_ids as $image) {
400 $restore_result = $this->restore_single_image($image['post_id'], $blog_id);
401
402 // if we get an error then we stop the work, except situation when "backup already restored'.
403 if (is_wp_error($restore_result) && 'restore_backup_not_found' != $restore_result->get_error_code()) {
404 // we need to stop the work as we haven't restored the backup.
405 $result['error'] = $restore_result->get_error_message();
406 $this->options->delete_option('smush_images_restored');
407 break;
408 }
409
410 $processed++;
411 }
412 }
413
414 $images_count = count($image_ids);
415
416 // if all images processed then set flag completed to true.
417 if ($processed == $images_count && $images_count < $images_limit) {
418 $this->options->delete_option('smush_images_restored');
419 $result['completed'] = true;
420 } else {
421 // save into options total processed count.
422 $processed += $this->options->get_option('smush_images_restored', 0);
423 $this->options->update_option('smush_images_restored', $processed);
424
425 if (is_multisite()) {
426 $result['message'] = sprintf(__('%s compressed images were restored from their backup for the site %s', 'wp-optimize'), $processed, get_site_url($blog_id));
427 } else {
428 $result['message'] = sprintf(__('%s compressed images were restored from their backup', 'wp-optimize'), $processed);
429 }
430 }
431
432 } else {
433 // if we just delete compressed images meta then set complete flag to true.
434 $result['completed'] = true;
435 }
436
437 if ($result['completed']) {
438 if ($delete_only_backups_meta) {
439 if (is_multisite()) {
440 $result['message'] = sprintf(__('All the compressed images for the site %s were successfully restored.', 'wp-optimize'), get_site_url($blog_id));
441 } else {
442 $result['message'] = __('All the compressed images were successfully restored.', 'wp-optimize');
443 }
444 } else {
445 if (is_multisite()) {
446 $result['message'] = sprintf(__('All the compressed images for the site %s were successfully marked as uncompressed.', 'wp-optimize'), get_site_url($blog_id));
447 } else {
448 $result['message'] = __('All the compressed images were successfully marked as uncompressed.', 'wp-optimize');
449 }
450 }
451
452 // clear all metas for smushed images after work completed.
453 // if $delete_only_backup_meta set to true then all meta fields was deleted in restore_single_image()
454 // and we don't need delete metas for other images.
455 if (!$delete_only_backups_meta) {
456 $wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key IN ('smush-complete', 'smush-stats', 'original-file', 'smush-info');");
457 }
458 }
459
460 if (is_multisite()) {
461 restore_current_blog();
462 }
463
464 return $result;
465 }
466
467 /**
468 * Process bulk smushing operation
469 *
470 * @param array $images - the array of images to process
471 * @return bool - true if processing complete
472 */
473 public function process_bulk_smush($images = array()) {
474
475 // Get a list of pending tasks so we can exclude those
476 $pending_tasks = $this->get_pending_tasks();
477 $queued_images = array();
478
479 $this->write_log_header();
480
481 if (!empty($pending_tasks)) {
482 foreach ($pending_tasks as $task) {
483 $queued_images[] = array(
484 'attachment_id' => $task->get_option('attachment_id'),
485 'blog_id' => $task->get_option('blog_id')
486 );
487 }
488 }
489
490 foreach ($images as $image) {
491 // Skip if already in the queue
492 if (in_array($image, $queued_images)) continue;
493
494 $options = array(
495 'attachment_id' => intval($image['attachment_id']),
496 'blog_id' => intval($image['blog_id']),
497 'image_quality' => $this->options->get_option('image_quality', 85),
498 'keep_original' => $this->options->get_option('back_up_original', true),
499 'preserve_exif' => $this->options->get_option('preserve_exif', true),
500 'lossy_compression' => $this->options->get_option('lossy_compression', false)
501 );
502
503 $server = $this->options->get_option('compression_server', $this->webservice);
504 $task_name = $this->get_associated_task($server);
505
506 $description = "$task_name - Attachment ID : ". intval($image['attachment_id']) . ", Started on : ". date("F d, Y h:i:s", time());
507 $task = call_user_func(array($task_name, 'create_task'), 'smush', $description, $options, $task_name);
508 $task->add_logger($this->logger);
509 }
510
511 $this->clear_cached_data();
512 $this->process_queue('smush');
513
514 if ($this->is_queue_processed()) {
515 $this->clean_up_old_tasks('smush');
516 }
517
518 if (!wp_next_scheduled('prune_smush_logs')) {
519 wp_schedule_single_event(time() + 7200, 'prune_smush_logs');
520 }
521
522 return true;
523 }
524
525
526 /**
527 * Check if a specified server online
528 *
529 * @param string $server - the server to test
530 * @return bool - true if yes, false otherwise
531 */
532 public function check_server_online($server = 'resmushit') {
533 $task = $this->get_associated_task($server);
534 $online = call_user_func(array($task, 'is_server_online'));
535
536 if ($online) {
537 update_option($task, strtotime('now'));
538 } else {
539 $this->log(get_option($task));
540 }
541
542 $this->log(sprintf('%s server status: %s', $task, $online ? 'Online' : 'Offline'));
543 return $online;
544 }
545
546 /**
547 * Checks if the queue for smushing is compleete
548 *
549 * @return bool - true if processed, false otherwise
550 */
551 public function is_queue_processed() {
552
553 $active = $this->get_pending_tasks();
554 if ($active && 0 != count($active)) return false;
555
556 return true;
557 }
558
559 /**
560 * Logs useful data once a smush task completes or if it fails
561 *
562 * @param mixed $task - A task object
563 */
564 public function record_stats($task) {
565
566 $attachment_id = $task->get_option('attachment_id');
567 $completed_task_count = $this->options->get_option('completed_task_count', false);
568 $failed_task_count = $this->options->get_option('failed_task_count', 0);
569 $total_bytes_saved = $this->options->get_option('total_bytes_saved', false);
570 $total_percent_saved = $this->options->get_option('total_percent_saved', 0);
571
572 if ('ud_task_failed' == current_action()) {
573 $this->options->update_option('failed_task_count', ++$failed_task_count);
574 return;
575 }
576
577 if (false === $completed_task_count) {
578 $completed_task_count = $total_bytes_saved = 0;
579 }
580
581 if (!$total_bytes_saved) {
582 $total_bytes_saved = 0;
583 }
584
585 if (is_multisite()) {
586 switch_to_blog($task->get_option('blog_id', 1));
587 $stats = get_post_meta($attachment_id, 'smush-stats', true);
588 restore_current_blog();
589 } else {
590 $stats = get_post_meta($attachment_id, 'smush-stats', true);
591 }
592
593 if (isset($stats['sizes-info'])) {
594
595 $original_size = 0;
596 $compressed_size = 0;
597
598 foreach ($stats['sizes-info'] as $info) {
599 $original_size += $info['original'];
600 $compressed_size += $info['compressed'];
601 }
602
603 $percent = round((($original_size - $compressed_size) / $original_size * 100), 2);
604 } else {
605 $original_size = isset($stats['original-size']) ? $stats['original-size'] : 0;
606 $compressed_size = isset($stats['smushed-size']) ? $stats['smushed-size'] : 0;
607 $percent = isset($stats['savings-percent']) ? $stats['savings-percent'] : 0;
608 }
609
610 $saved = $original_size - $compressed_size;
611 $completed_task_count++;
612
613 $total_bytes_saved += $saved;
614 $total_percent_saved = (($total_percent_saved * ($completed_task_count - 1)) + $percent) / $completed_task_count;
615
616 $this->options->update_option('completed_task_count', $completed_task_count);
617 $this->options->update_option('total_bytes_saved', $total_bytes_saved);
618 $this->options->update_option('total_percent_saved', $total_percent_saved);
619 }
620
621 /**
622 * Cleans out all complete + failed tasks from the DB.
623 *
624 * @param String $type type of the task
625 * @return bool - true if processing complete
626 */
627 public function clean_up_old_tasks($type) {
628 $completed_tasks = $this->get_tasks('all', $type);
629
630 if (!$completed_tasks) return false;
631
632 $this->log(sprintf('Cleaning up tasks of type (%s). A total of %d tasks will be deleted.', $type, count($completed_tasks)));
633
634 foreach ($completed_tasks as $task) {
635 $task->delete_meta();
636 $task->delete();
637 }
638
639 return true;
640 }
641
642 /**
643 * Get current smush options.
644 *
645 * @return array
646 */
647 public function get_smush_options() {
648 static $smush_options = array();
649 if (empty($smush_options)) {
650 $smush_options = array(
651 'compression_server' => $this->options->get_option('compression_server', $this->get_default_webservice()),
652 'image_quality' => $this->options->get_option('image_quality', 'very_good'),
653 'lossy_compression' => $this->options->get_option('lossy_compression', false),
654 'back_up_original' => $this->options->get_option('back_up_original', true),
655 'back_up_delete_after' => $this->options->get_option('back_up_delete_after', true),
656 'back_up_delete_after_days' => $this->options->get_option('back_up_delete_after_days', 50),
657 'preserve_exif' => $this->options->get_option('preserve_exif', false),
658 'autosmush' => $this->options->get_option('autosmush', false),
659 'show_smush_metabox' => $this->options->get_option('show_smush_metabox', 'show') == 'show' ? true : false,
660 'webp_conversion' => $this->options->get_option('webp_conversion', false)
661 );
662 }
663 return $smush_options;
664 }
665
666 /**
667 * Updates global smush options
668 *
669 * @param array $options - sent in via AJAX
670 * @return bool - status of the update
671 */
672 public function update_smush_options($options) {
673
674 foreach ($options as $option => $value) {
675 $this->options->update_option($option, $value);
676 }
677
678 return true;
679 }
680
681 /**
682 * Clears smush related stats
683 *
684 * @return bool - status of the update
685 */
686 public function clear_smush_stats() {
687 $this->options->update_option('failed_task_count', 0);
688 $this->options->update_option('completed_task_count', false);
689 $this->options->update_option('total_bytes_saved', false);
690 $this->options->update_option('total_percent_saved', 0);
691
692 return true;
693 }
694
695 /**
696 * Returns array of translations used in javascript code.
697 *
698 * @return array - translations used in JS
699 */
700 public function smush_js_translations() {
701 return apply_filters('updraft_smush_js_translations', array(
702 'all_images_compressed' => __('No uncompressed images were found.', 'wp-optimize'),
703 'error_unexpected_response' => __('An unexpected response was received from the server. More information has been logged in the browser console.', 'wp-optimize'),
704 'compress_single_image_dialog' => __('Please wait: compressing the selected image.', 'wp-optimize'),
705 'error_try_again_later' => __('Please try again later.', 'wp-optimize'),
706 'server_check' => __('Connecting to the Smush API server, please wait', 'wp-optimize'),
707 'please_wait' => __('Please wait while the request is being processed', 'wp-optimize'),
708 'server_error' => __('There was an error connecting to the image compression server. This could mean either the server is temporarily unavailable or there are connectivity issues with your internet connection. Please try later.', 'wp-optimize'),
709 'please_select_images' => __('Please select the images you want compressed from the "Uncompressed images" panel first', 'wp-optimize'),
710 'please_updating_images_info' => __('Please wait: updating information about the selected image.', 'wp-optimize'),
711 'please_select_compressed_images' => __('Please select the images you want to mark as already compressed from the "Uncompressed images" panel first', 'wp-optimize'),
712 'view_image' => __('View Image', 'wp-optimize'),
713 'delete_image_backup_confirm' => __('Do you really want to delete all backup images now? This action is irreversible.', 'wp-optimize'),
714 'mark_all_images_uncompressed' => __('Do you really want to mark all the images as uncompressed? This action is irreversible.', 'wp-optimize'),
715 'restore_images_from_backup' => __('Do you want to restore the original images from the backup (where they exist?)', 'wp-optimize'),
716 'restore_all_compressed_images' => __('Do you really want to restore all the compressed images?', 'wp-optimize'),
717 'more' => __('More', 'wp-optimize'),
718 'less' => __('Less', 'wp-optimize'),
719 ));
720 }
721
722 /**
723 * Adds a smush metabox on the post edit screen for images
724 *
725 * @param WP_Post $post - a post object
726 */
727 public function add_smush_metabox($post) {
728
729 if (!wp_attachment_is_image($post->ID)) return;
730
731 if (!file_exists(get_attached_file($post->ID))) {
732 return;
733 }
734
735 add_meta_box('smush-metabox', __('Compress Image', 'wp-optimize'), array($this, 'render_smush_metabox'), 'attachment', 'side');
736 }
737
738 /**
739 * Renders a metabox on the post edit screen for images
740 *
741 * @param WP_Post $post - a post object
742 */
743 public function render_smush_metabox($post) {
744
745 $compressed = get_post_meta($post->ID, 'smush-complete', true) ? true : false;
746 $has_backup = get_post_meta($post->ID, 'original-file', true) ? true : false;
747
748 $smush_info = get_post_meta($post->ID, 'smush-info', true);
749 $smush_stats = get_post_meta($post->ID, 'smush-stats', true);
750 $marked = get_post_meta($post->ID, 'smush-marked', false);
751
752 $options = Updraft_Smush_Manager()->get_smush_options();
753
754 $file = get_attached_file($post->ID);
755 $file_size = ($file && is_file($file)) ? filesize($file) : 0;
756
757 $extract = array(
758 'post_id' => $post->ID,
759 'smush_display' => $compressed ? "style='display:none;'" : "style='display:block;'",
760 'restore_display' => $compressed ? "style='display:block;'" : "style='display:none;'",
761 'restore_action' => $has_backup ? "style='display:block;'" : "style='display:none;'",
762 'smush_mark' => !$compressed && !$marked ? "style='display:block;'" : "style='display:none;'",
763 'smush_unmark' => $marked ? "style='display:block;'" : "style='display:none;'",
764 'smush_info' => $smush_info ? $smush_info : ' ',
765 'file_size' => $file_size,
766 'smush_options' => $options,
767 'custom' => 100 == $options['image_quality'] || 90 == $options['image_quality'] ? false : true,
768 'smush_details' => '',
769 );
770
771 if (!empty($smush_stats['sizes-info'])) {
772 $extract['smush_details'] = WP_Optimize()->include_template('images/smush-details.php', true, array('sizes_info' => $smush_stats['sizes-info']));
773 }
774
775 $extract['compressed_by_another_plugin'] = $this->is_image_compressed_by_another_plugin($post->ID);
776
777 WP_Optimize()->include_template('admin-metabox-smush.php', false, $extract);
778 }
779
780 /**
781 * Check if a single image compressed by another plugin.
782 *
783 * @param int $image_id
784 * @return bool
785 */
786 private function is_image_compressed_by_another_plugin($image_id) {
787 global $wpdb;
788
789 $meta = $wpdb->get_results("SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE `post_id`={$image_id}", ARRAY_A);
790
791 if (is_array($meta)) {
792 foreach ($meta as $row) {
793 // Smush, Imagify, Compress JPEG & PNG images by TinyPNG.
794 if (in_array($row['meta_key'], array('wp-smpro-smush-data', '_imagify_optimization_level', 'tiny_compress_images'))) return true;
795 // ShortPixel Image Optimizer
796 if ('_shortpixel_status' == $row['meta_key'] && 2 <= $row['meta_key'] && 3 > $row['meta_key']) return true;
797 }
798 }
799
800 if (WP_Optimize()->get_db_info()->table_exists('ewwwio_images')) {
801 $old_show_errors = $wpdb->show_errors(false);
802 // EWWW Image Optimizer.
803 $ewww_image = $wpdb->get_col("SELECT attachment_id FROM {$wpdb->prefix}ewwwio_images WHERE attachment_id={$image_id} AND gallery='media' LIMIT 1");
804 if (!empty($ewww_image)) return true;
805 $wpdb->show_errors($old_show_errors);
806 }
807
808 return apply_filters('wpo_image_compressed_by_another_plugin', false);
809 }
810
811 /**
812 * Get attachment ids for images those already compressed with EWWW Image Optimizer.
813 * Used with filter `wpo_get_uncompressed_images_args`.
814 *
815 * @param array $args WP_Query arguments.
816 * @return array
817 */
818 public function ewww_image_optimizer_compressed_images_args($args) {
819 global $wpdb;
820
821 if (!WP_Optimize()->get_db_info()->table_exists('ewwwio_images')) return $args;
822
823 $old_show_errors = $wpdb->show_errors(false);
824 $compressed_images = $wpdb->get_col("SELECT DISTINCT(attachment_id) FROM {$wpdb->prefix}ewwwio_images WHERE gallery='media'");
825 $wpdb->show_errors($old_show_errors);
826
827 if (isset($args['post__not_in'])) {
828 $args['post__not_in'] = array_merge($args['post__not_in'], $compressed_images);
829 } else {
830 $args['post__not_in'] = $compressed_images;
831 }
832
833 return $args;
834 }
835
836 /**
837 * Returns a list of images for smush (from cache if available)
838 *
839 * @return array - uncompressed images
840 */
841 public function get_uncompressed_images() {
842
843 $uncompressed_images = $this->get_from_cache('uncompressed_images');
844
845 if ($uncompressed_images) return $uncompressed_images;
846
847 $uncompressed_images = array();
848
849 $args = array(
850 'post_type' => 'attachment',
851 'post_mime_type' => 'image',
852 'post_status' => 'inherit',
853 'posts_per_page' => apply_filters('updraft_smush_posts_per_page', 1000),
854 'meta_query' => array(
855 'relation' => 'AND',
856 array(
857 'relation' => 'OR',
858 array(
859 'key' => 'smush-complete',
860 'compare' => '!=',
861 'value' => '1',
862 ),
863 array(
864 'key' => 'smush-complete',
865 'compare' => 'NOT EXISTS',
866 'value' => '',
867 ),
868 ),
869 // ShortPixel Image Optimizer plugin
870 array(
871 'relation' => 'OR',
872 array(
873 'key' => '_shortpixel_status',
874 'compare' => '<',
875 'value' => '2',
876 ),
877 array(
878 'key' => '_shortpixel_status',
879 'compare' => '>=',
880 'value' => '3',
881 ),
882 array(
883 'key' => '_shortpixel_status',
884 'compare' => 'NOT EXISTS',
885 'value' => '',
886 ),
887 ),
888 // Smush plugin
889 array(
890 'key' => 'wp-smpro-smush-data',
891 'compare' => 'NOT EXISTS',
892 'value' => '',
893 ),
894 // Imagify
895 array(
896 'key' => '_imagify_optimization_level',
897 'compare' => 'NOT EXISTS',
898 'value' => '',
899 ),
900 // Compress JPEG & PNG images by TinyPNG
901 array(
902 'key' => 'tiny_compress_images',
903 'compare' => 'NOT EXISTS',
904 'value' => '',
905 ),
906 )
907 );
908
909 if (is_multisite()) {
910
911 $sites = WP_Optimize()->get_sites();
912
913 foreach ($sites as $site) {
914
915 switch_to_blog($site->blog_id);
916
917 $args = apply_filters('wpo_get_uncompressed_images_args', $args);
918 $images = new WP_Query($args);
919
920 foreach ($images->posts as $image) {
921 if (file_exists(get_attached_file($image->ID))) {
922 $uncompressed_images[$site->blog_id][] = array(
923 'id' => $image->ID,
924 'thumb_url' => wp_get_attachment_thumb_url($image->ID),
925 'filesize' => filesize(get_attached_file($image->ID))
926 );
927 } else {
928 $this->log("Could not find file for image: blog_id={$site->blog_id}, ID={$image->ID}, file=".get_attached_file($image->ID));
929 }
930 }
931
932 restore_current_blog();
933 }
934
935 } else {
936 $args = apply_filters('wpo_get_uncompressed_images_args', $args);
937 $images = new WP_Query($args);
938 foreach ($images->posts as $image) {
939 if (file_exists(get_attached_file($image->ID))) {
940 $uncompressed_images[1][] = array(
941 'id' => $image->ID,
942 'thumb_url' => wp_get_attachment_thumb_url($image->ID),
943 'filesize' => filesize(get_attached_file($image->ID))
944 );
945 } else {
946 $this->log("Could not find file for image: ID={$image->ID}, file=".get_attached_file($image->ID));
947 }
948 }
949 }
950
951 $this->save_to_cache('uncompressed_images', $uncompressed_images);
952 return $uncompressed_images;
953 }
954
955 /**
956 * Returns a list of admin URLs. This is to prevent unnecessary bloat in the output of get_uncompressed_images() (and thus better performance over the network on sites with huge numbers of images)
957 *
958 * @return array - list of admin URLs
959 */
960 public function get_admin_urls() {
961
962 $admin_urls = $this->get_from_cache('admin_urls');
963
964 if ($admin_urls) return $admin_urls;
965
966 $admin_urls = array();
967
968 if (is_multisite()) {
969
970 $sites = WP_Optimize()->get_sites();
971
972 foreach ($sites as $site) {
973 switch_to_blog($site->blog_id);
974 $admin_urls[$site->blog_id] = admin_url();
975 restore_current_blog();
976 }
977
978 } else {
979 // The pseudo-blog_id here (1) matches (and must match) what is used in get_uncompressed_images
980 $admin_urls[1] = admin_url();
981 }
982
983 $this->save_to_cache('admin_urls', $admin_urls);
984 return $admin_urls;
985 }
986
987 /**
988 * Check if a task exists for a given image
989 *
990 * @param string $image - The attachment ID of the image
991 * @return bool - true if yes, false otherwise
992 */
993 public function task_exists($image) {
994
995 $pending_tasks = $this->get_active_tasks('smush');
996 $queued_images = array();
997
998 if (!empty($pending_tasks)) {
999 foreach ($pending_tasks as $task) {
1000 $queued_images[] = $task->get_option('attachment_id');
1001 }
1002 }
1003 return in_array($image, $queued_images);
1004 }
1005
1006 /**
1007 * Returns the status of images compressed in this iteration of the bulk compress
1008 *
1009 * @param array $images - List of images in the current session
1010 *
1011 * @return array - status of the operation
1012 */
1013 public function get_session_stats($images) {
1014 $stats = array();
1015
1016 foreach ($images as $image) {
1017 if (is_multisite()) {
1018 switch_to_blog($image['blog_id'], 1);
1019 $stats[] = get_post_meta($image['attachment_id'], 'smush-complete', true) ? 'success' : 'fail';
1020 restore_current_blog();
1021 } else {
1022 $stats[] = get_post_meta($image['attachment_id'], 'smush-complete', true) ? 'success' : 'fail';
1023 }
1024 }
1025
1026 return array_count_values($stats);
1027 }
1028
1029 /**
1030 * Returns a list of images for smush (from cache if available)
1031 *
1032 * @return array - List of task objects with uncompressed images
1033 */
1034 public function get_pending_tasks() {
1035 return $this->get_active_tasks('smush');
1036 }
1037
1038 /**
1039 * Deletes and removes any pending tasks from queue
1040 */
1041 public function clear_pending_images() {
1042
1043 $pending_tasks = $this->get_active_tasks('smush');
1044
1045 foreach ($pending_tasks as $task) {
1046 $task->delete_meta();
1047 $task->delete();
1048 }
1049
1050 return true;
1051 }
1052
1053
1054 /**
1055 * Returns a count of failed tasks
1056 *
1057 * @return int - failed tasks
1058 */
1059 public function get_failed_task_count() {
1060 return $this->options->get_option('failed_task_count', 0);
1061 }
1062
1063 /**
1064 * Adds the required scripts and styles
1065 */
1066 public function admin_enqueue_scripts() {
1067 $current_screen = get_current_screen();
1068 // load scripts and styles only on WP-Optimize pages or if show_smush_metabox option enabled.
1069 if (!preg_match('/wp\-optimize|attachment|upload/i', $current_screen->id) && 'show' != $this->options->get_option('show_smush_metabox', 'show')) return;
1070
1071 $enqueue_version = (defined('WP_DEBUG') && WP_DEBUG) ? WPO_VERSION.'.'.time() : WPO_VERSION;
1072 $min_or_not = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
1073 $min_or_not_internal = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '-'. str_replace('.', '-', WPO_VERSION). '.min';
1074
1075 $js_variables = $this->smush_js_translations();
1076 $js_variables['ajaxurl'] = admin_url('admin-ajax.php');
1077 $js_variables['features'] = $this->get_features();
1078
1079 $js_variables['smush_ajax_nonce'] = wp_create_nonce('updraft-task-manager-ajax-nonce');
1080
1081 wp_enqueue_script('block-ui-js', WPO_PLUGIN_URL.'/includes/blockui/jquery.blockUI'.$min_or_not.'.js', array('jquery'), $enqueue_version);
1082 wp_enqueue_script('smush-js', WPO_PLUGIN_URL.'js/wposmush'.$min_or_not_internal.'.js', array('jquery', 'block-ui-js', 'wp-optimize-send-command'), $enqueue_version);
1083 wp_enqueue_style('smush-css', WPO_PLUGIN_URL.'css/smush'.$min_or_not_internal.'.css', array(), $enqueue_version);
1084 wp_localize_script('smush-js', 'wposmush', $js_variables);
1085 }
1086
1087 /**
1088 * Gets default service provider for smush
1089 *
1090 * @return string - service name
1091 */
1092 public function get_default_webservice() {
1093 return 'resmushit';
1094 }
1095
1096 /**
1097 * Sets default options for smush
1098 */
1099 public function set_default_options() {
1100
1101 $options = array(
1102 'compression_server' => $this->get_default_webservice(),
1103 'image_quality' => 'very_good',
1104 'lossy_compression' => false,
1105 'back_up_original' => true,
1106 'preserve_exif' => false,
1107 'autosmush' => false,
1108 'back_up_delete_after' => $this->options->get_option('back_up_delete_after', true),
1109 'back_up_delete_after_days' => $this->options->get_option('back_up_delete_after_days', 50),
1110 'webp_conversion' => false,
1111 );
1112
1113 $this->update_smush_options($options);
1114 }
1115
1116 /**
1117 * Gets default service provider for smush
1118 *
1119 * @param string $server - The name of the server
1120 * @return string - associated task type, default if none found
1121 */
1122 public function get_associated_task($server) {
1123 $allowed = $this->get_allowed_services();
1124
1125 if (key_exists($server, $allowed))
1126 return $allowed[$server];
1127
1128 $default = $this->get_default_webservice();
1129 return $allowed[$default];
1130 }
1131
1132 /**
1133 * Gets allowed service providers for smush
1134 *
1135 * @return array - key value pair of service name => task name
1136 */
1137 public function get_allowed_services() {
1138 return array(
1139 'resmushit' => 'Re_Smush_It_Task',
1140 );
1141 }
1142
1143 /**
1144 * Gets allowed service provider features smush
1145 *
1146 * @return array - key value pair of service name => features exposed
1147 */
1148 public function get_features() {
1149 $features = array();
1150 foreach ($this->get_allowed_services() as $service => $class_name) {
1151 $features[$service] = call_user_func(array($class_name, 'get_features'));
1152 }
1153 return $features;
1154 }
1155
1156 /**
1157 * Returns the path to the logfile
1158 *
1159 * @return string - file path
1160 */
1161 public function get_logfile_path() {
1162 $upload_dir = wp_upload_dir();
1163 $upload_base = $upload_dir['basedir'];
1164 return $upload_base . '/smush-' . substr(md5(wp_salt()), 0, 20) . '.log';
1165 }
1166
1167 /**
1168 * Adds a logger to the task
1169 *
1170 * @param Mixed $task - a task object
1171 */
1172 public function set_task_logger($task) {
1173 if (!$this->logger) {
1174 $this->logger = new Updraft_File_Logger($this->get_logfile_path());
1175 }
1176
1177 if (!$task->get_loggers()) {
1178 $task->add_logger($this->logger);
1179 }
1180 }
1181
1182 /**
1183 * Writes a standardised header to the log file
1184 */
1185 public function write_log_header() {
1186
1187 global $wpdb;
1188
1189 // phpcs:disable
1190 $wp_version = $this->get_wordpress_version();
1191 $mysql_version = $wpdb->db_version();
1192 $safe_mode = $this->detect_safe_mode();
1193 $max_execution_time = (int) @ini_get("max_execution_time");
1194
1195 $memory_limit = ini_get('memory_limit');
1196 $memory_usage = round(@memory_get_usage(false)/1048576, 1);
1197 $total_memory_usage = round(@memory_get_usage(true)/1048576, 1);
1198
1199 // Attempt to raise limit
1200 @set_time_limit(330);
1201
1202 $log_header = array();
1203
1204 // phpcs:enable
1205 $log_header[] = "\n";
1206 $log_header[] = "Header for logs at time: ".date('r')." on ".network_site_url();
1207 $log_header[] = "WP: ".$wp_version;
1208 $log_header[] = "PHP: ".phpversion()." (".PHP_SAPI.", ".@php_uname().")";// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1209 $log_header[] = "MySQL: $mysql_version";
1210 $log_header[] = "WPLANG: ".get_locale();
1211 $log_header[] = "Server: ".$_SERVER["SERVER_SOFTWARE"];
1212 $log_header[] = "Outbound connections: ".(defined('WP_HTTP_BLOCK_EXTERNAL') ? 'Y' : 'N');
1213 $log_header[] = "safe_mode: $safe_mode";
1214 $log_header[] = "max_execution_time: $max_execution_time";
1215 $log_header[] = "memory_limit: $memory_limit (used: ${memory_usage}M | ${total_memory_usage}M)";
1216 $log_header[] = "multisite: ".(is_multisite() ? 'Y' : 'N');
1217 $log_header[] = "openssl: ".(defined('OPENSSL_VERSION_TEXT') ? OPENSSL_VERSION_TEXT : 'N');
1218
1219
1220 foreach ($log_header as $log_entry) {
1221 $this->log($log_entry);
1222 }
1223
1224 $memlim = $this->memory_check_current();
1225
1226 if ($memlim<65 && $memlim>0) {
1227 $this->log(sprintf('The amount of memory (RAM) allowed for PHP is very low (%s Mb) - you should increase it to avoid failures due to insufficient memory (consult your web hosting company for more help)', round($memlim, 1)), 'warning');
1228 }
1229
1230 if ($max_execution_time>0 && $max_execution_time<20) {
1231 $this->log(sprintf('The amount of time allowed for WordPress plugins to run is very low (%s seconds) - you should increase it to avoid failures due to time-outs (consult your web hosting company for more help - it is the max_execution_time PHP setting; the recommended value is %s seconds or more)', $max_execution_time, 90), 'warning');
1232 }
1233 }
1234
1235 /**
1236 * Prunes the log file
1237 */
1238 public function prune_smush_logs() {
1239 $this->log("Pruning the smush log file");
1240 $this->logger->prune_logs();
1241 }
1242
1243 /**
1244 * Get the WordPress version
1245 *
1246 * @return String - the version
1247 */
1248 public function get_wordpress_version() {
1249 static $got_wp_version = false;
1250
1251 if (!$got_wp_version) {
1252 global $wp_version;
1253 @include(ABSPATH.WPINC.'/version.php');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1254 $got_wp_version = $wp_version;
1255 }
1256
1257 return $got_wp_version;
1258 }
1259
1260 /**
1261 * Get the current memory limit
1262 *
1263 * @return String - memory limit in megabytes
1264 */
1265 public function memory_check_current($memory_limit = false) {
1266 // Returns in megabytes
1267 if (false == $memory_limit) $memory_limit = ini_get('memory_limit');
1268 $memory_limit = rtrim($memory_limit);
1269 $memory_unit = $memory_limit[strlen($memory_limit)-1];
1270 if (0 == (int) $memory_unit && '0' !== $memory_unit) {
1271 $memory_limit = substr($memory_limit, 0, strlen($memory_limit)-1);
1272 } else {
1273 $memory_unit = '';
1274 }
1275 switch ($memory_unit) {
1276 case '':
1277 $memory_limit = floor($memory_limit/1048576);
1278 break;
1279 case 'K':
1280 case 'k':
1281 $memory_limit = floor($memory_limit/1024);
1282 break;
1283 case 'G':
1284 $memory_limit = $memory_limit*1024;
1285 break;
1286 case 'M':
1287 // assumed size, no change needed
1288 break;
1289 }
1290 return $memory_limit;
1291 }
1292
1293 /**
1294 * Detect if safe_mode is on
1295 *
1296 * @return Integer - 1 or 0
1297 */
1298 public function detect_safe_mode() {
1299 return (@ini_get('safe_mode') && strtolower(@ini_get('safe_mode')) != "off") ? 1 : 0;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1300 }
1301
1302 /**
1303 * Saves a value to the cache.
1304 *
1305 * @param string $key
1306 * @param mixed $value
1307 * @param int $blog_id
1308 */
1309 public function save_to_cache($key, $value, $blog_id = 1) {
1310 $transient_limit = 3600 * 48;
1311 $key = 'wpo_smush_cache_' . $blog_id . '_'. $key;
1312
1313 return WP_Optimize_Transients_Cache::get_instance()->set($key, $value, $transient_limit);
1314 }
1315
1316 /**
1317 * Gets value from the cache.
1318 *
1319 * @param string $key
1320 * @param int $blog_id
1321 * @return mixed
1322 */
1323 public function get_from_cache($key, $blog_id = 1) {
1324 $key = 'wpo_smush_cache_' . $blog_id . '_'. $key;
1325
1326 $value = WP_Optimize_Transients_Cache::get_instance()->get($key);
1327
1328 return $value;
1329 }
1330
1331 /**
1332 * Deletes a value from the cache.
1333 *
1334 * @param string $key
1335 * @param int $blog_id
1336 */
1337 public function delete_from_cache($key, $blog_id = 1) {
1338 $key = 'wpo_smush_cache_' . $blog_id . '_'. $key;
1339
1340 WP_Optimize_Transients_Cache::get_instance()->delete($key);
1341
1342 $this->delete_transient($key);
1343 }
1344
1345 /**
1346 * Wrapper for deleting a transient
1347 *
1348 * @param string $key
1349 */
1350 public function delete_transient($key) {
1351 if ($this->is_multisite_mode()) {
1352 delete_site_transient($key);
1353 } else {
1354 delete_transient($key);
1355 }
1356 }
1357
1358 /**
1359 * Removes all cached data
1360 */
1361 public function clear_cached_data() {
1362 global $wpdb;
1363
1364 // get list of cached data by optimization.
1365 if ($this->is_multisite_mode()) {
1366 $keys = $wpdb->get_col("SELECT meta_key FROM {$wpdb->sitemeta} WHERE meta_key LIKE '%wpo_smush_cache_%'");
1367 } else {
1368 $keys = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%wpo_smush_cache_%'");
1369 }
1370
1371 if (!empty($keys)) {
1372 $transient_keys = array();
1373 foreach ($keys as $key) {
1374 preg_match('/wpo_smush_cache_.+/', $key, $option_name);
1375 $option_name = $option_name[0];
1376 $transient_keys[] = $option_name;
1377 }
1378
1379 // get unique keys.
1380 $transient_keys = array_unique($transient_keys);
1381
1382 // delete transients.
1383 foreach ($transient_keys as $key) {
1384 $this->delete_transient($key);
1385 }
1386 }
1387 }
1388
1389 /**
1390 * Delete recursively all smush backup files created more that $days_ago days.
1391 *
1392 * @param string $directory upload directory
1393 * @param int $days_ago
1394 */
1395 public function clear_backup_images_directory($directory, $days_ago = 30) {
1396
1397 $directory = trailingslashit($directory);
1398 $current_time = time();
1399
1400 if (preg_match('/(\d{4})\/(\d{2})\/$/', $directory, $match)) {
1401
1402 $check_date = false;
1403
1404 if ($days_ago > 0) {
1405 // check if it is end directory then scan for backup images.
1406 $year = (int) $match[1];
1407 $month = (int) $match[2];
1408
1409 $limit = strtotime('-'.$days_ago.' '.(($days_ago > 1) ? 'days' : 'day'));
1410 $year_limit = (int) date('Y', $limit);
1411 $month_limit = (int) date('m', $limit);
1412 $day_limit = (int) date('j', $limit);
1413
1414 // if current directory is newer than needed then we skip it.
1415 if ($year_limit < $year || ($year_limit == $year && $month_limit < $month)) {
1416 return;
1417 }
1418
1419 // we will check dates only in directory that contain limit date.
1420 $check_date = ($year_limit == $year && $month_limit == $month);
1421 }
1422
1423 // GLOB_BRACE isn't defined on some systems (Solaris, SunOS and more) > https://www.php.net/manual/en/function.glob.php
1424 $files = glob($directory . '*-updraft-pre-smush-original.*', (defined('GLOB_BRACE') ? GLOB_BRACE : 0));
1425
1426 foreach ($files as $file) {
1427 if ($check_date) {
1428 $filedate_day = (int) date('j', filectime($file));
1429 if ($filedate_day >= $day_limit) continue;
1430 }
1431
1432 unlink($file);
1433 }
1434
1435 } else {
1436 // scan directories recursively.
1437 $handle = opendir($directory);
1438
1439 if (false === $handle) return;
1440
1441 $file = readdir($handle);
1442
1443 while (false !== $file) {
1444
1445 if ('.' == $file || '..' == $file) {
1446 $file = readdir($handle);
1447 continue;
1448 }
1449
1450 if (is_dir($directory . $file)) {
1451 $this->clear_backup_images_directory($directory . $file, $days_ago);
1452 } elseif (is_file($directory . $file) && preg_match('/^.+-updraft-pre-smush-original\.\S{3,4}/i', $file)) {
1453 // check the file time and compare with $days_ago.
1454 $filedate_day = (int) filectime($directory . $file);
1455 if ($filedate_day > 0 && ($current_time - $filedate_day) / 86400 >= $days_ago) unlink($directory . $file);
1456 }
1457
1458 $file = readdir($handle);
1459 }
1460 }
1461
1462 }
1463
1464 /**
1465 * Clean backup smush images according to saved options.
1466 */
1467 public function clear_backup_images() {
1468 $back_up_delete_after = $this->options->get_option('back_up_delete_after', false);
1469
1470 if (!$back_up_delete_after) return;
1471
1472 $back_up_delete_after_days = $this->options->get_option('back_up_delete_after_days', 50);
1473
1474 $upload_dir = wp_upload_dir(null, false);
1475 $base_dir = $upload_dir['basedir'];
1476
1477 $this->clear_backup_images_directory($base_dir, $back_up_delete_after_days);
1478 }
1479
1480 /**
1481 * Check if attachment already compressed.
1482 *
1483 * @param int $attachment_id
1484 *
1485 * @return bool
1486 */
1487 public function is_compressed($attachment_id) {
1488 return (true == get_post_meta($attachment_id, 'smush-complete', true));
1489 }
1490
1491 /**
1492 * @param array $form_fields
1493 * @param WP_Post $post
1494 *
1495 * @return array
1496 */
1497 public function add_compress_button_to_media_modal($form_fields, $post) {
1498
1499 if (!is_admin() || !function_exists('get_current_screen')) return $form_fields;
1500
1501 /**
1502 * In media modal get_current_screen() return null or id = 'async-upload' We don't need add smush fields elsewhere.
1503 */
1504 $current_screen = get_current_screen();
1505 if (null !== $current_screen && 'async-upload' != $current_screen->id) return $form_fields;
1506
1507 /**
1508 * Don't show additional fields for non-image attachments.
1509 */
1510 if (!wp_attachment_is_image($post->ID)) return $form_fields;
1511
1512 ob_start();
1513 $this->render_smush_metabox($post);
1514 $smush_metabox = ob_get_contents();
1515 ob_end_clean();
1516
1517 $form_fields['wpo_compress_image'] = array(
1518 'value' => '',
1519 'label' => __('Compress image', 'wp-optimize'),
1520 'input' => 'html',
1521 'html' => $smush_metabox,
1522 );
1523
1524 return $form_fields;
1525 }
1526
1527 /**
1528 * Returns true if multisite
1529 *
1530 * @return bool
1531 */
1532 public function is_multisite_mode() {
1533 return WP_Optimize()->is_multisite_mode();
1534 }
1535
1536 /**
1537 * This callback function is triggered due to delete_attachment action (wp-includes/post.php) and is executed prior to deletion of post-type attachment
1538 *
1539 * @param int $post_id - WordPress Post ID
1540 */
1541 public function unscheduled_original_file_deletion($post_id) {
1542 $the_original_file = get_post_meta($post_id, 'original-file', true);
1543 if ('' != $the_original_file && file_exists($the_original_file)) {
1544 @unlink($the_original_file);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1545 }
1546 }
1547
1548 /**
1549 * Instance of WP_Optimize_Page_Cache_Preloader.
1550 *
1551 * @return self
1552 */
1553 public static function instance() {
1554 if (empty(self::$_instance)) {
1555 self::$_instance = new self();
1556 }
1557
1558 return self::$_instance;
1559 }
1560 }
1561
1562 /**
1563 * Returns a Updraft_Smush_Manager instance
1564 */
1565 function Updraft_Smush_Manager() {
1566 return Updraft_Smush_Manager::instance();
1567 }
1568
1569 endif;
1570