PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.20
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.20
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-commands.php

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

554 lines 18.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * A Smush Task manager class
4 */
5
6 if (!defined('ABSPATH')) die('Access denied.');
7
8 if (!class_exists('Updraft_Task_Manager_Commands_1_0')) require_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-manager-commands.php');
9
10 if (!class_exists('Updraft_Smush_Manager_Commands')) :
11
12 class Updraft_Smush_Manager_Commands extends Updraft_Task_Manager_Commands_1_0 {
13
14 /**
15 * The commands constructor
16 *
17 * @param mixed $task_manager - A task manager instance
18 */
19 public function __construct($task_manager) {
20 parent::__construct($task_manager);
21 }
22
23 /**
24 * Returns a list of commands available for smush related operations
25 */
26 public static function get_allowed_ajax_commands() {
27
28 $commands = parent::get_allowed_ajax_commands();
29
30 $smush_commands = array(
31 'compress_single_image',
32 'restore_single_image',
33 'process_bulk_smush',
34 'update_smush_options',
35 'get_ui_update',
36 'process_pending_images',
37 'clear_pending_images',
38 'clear_smush_stats',
39 'check_server_status',
40 'get_smush_logs',
41 'mark_as_compressed',
42 'mark_all_as_uncompressed',
43 'clean_all_backup_images',
44 'reset_webp_serving_method',
45 'convert_to_webp_format',
46 );
47
48 return array_merge($commands, $smush_commands);
49 }
50
51 /**
52 * Process the compression of a single image
53 *
54 * @param mixed $data - sent in via AJAX
55 * @return WP_Error|array - information about the operation or WP_Error object on failure
56 */
57 public function compress_single_image($data) {
58
59 $options = !empty($data['smush_options']) ? $data['smush_options'] : $this->task_manager->get_smush_options();
60 $image = isset($data['selected_image']) ? filter_var($data['selected_image']['attachment_id'], FILTER_SANITIZE_NUMBER_INT) : false;
61 $blog = isset($data['selected_image']) ? filter_var($data['selected_image']['blog_id'], FILTER_SANITIZE_NUMBER_INT) : false;
62
63 // A subsite administrator can only compress their own image. If the blog ID isn't theirs, return an error.
64 if ($blog && is_multisite() && get_current_blog_id() != $blog && !current_user_can('manage_network_options')) {
65 return new WP_Error('compression_not_permitted', __('The blog ID provided does not match the current blog.', 'wp-optimize'));
66 }
67
68 $server = sanitize_text_field($options['compression_server']);
69
70 $lossy = filter_var($options['lossy_compression'], FILTER_VALIDATE_BOOLEAN) ? true : false;
71 $backup = filter_var($options['back_up_original'], FILTER_VALIDATE_BOOLEAN) ? true : false;
72 $exif = filter_var($options['preserve_exif'], FILTER_VALIDATE_BOOLEAN) ? true : false;
73 $quality = filter_var($options['image_quality'], FILTER_SANITIZE_NUMBER_INT);
74
75 $options = array(
76 'attachment_id' => $image,
77 'blog_id' => $blog,
78 'image_quality' => $quality,
79 'keep_original' => $backup,
80 'lossy_compression' => $lossy,
81 'preserve_exif' => $exif
82 );
83
84 if (filesize(get_attached_file($image)) > 5242880) {
85 $options['request_timeout'] = 180;
86 }
87
88 $success = $this->task_manager->compress_single_image($image, $options, $server);
89
90 if (!$success) {
91 return new WP_Error('compress_failed', get_post_meta($image, 'smush-info', true));
92 }
93
94 $response = array();
95 $response['status'] = true;
96 $response['operation'] = 'compress';
97 $response['options'] = $options;
98 $response['server'] = $server;
99 $response['success'] = $success;
100 $response['restore_possible'] = $backup;
101 $response['summary'] = get_post_meta($image, 'smush-info', true);
102
103 $smush_stats = get_post_meta($image, 'smush-stats', true);
104 if (isset($smush_stats['sizes-info'])) {
105 $response['sizes-info'] = WP_Optimize()->include_template('images/smush-details.php', true, array('sizes_info' => $smush_stats['sizes-info']));
106 }
107
108 return $response;
109 }
110
111 /**
112 * Restores a single image, if backup is available
113 *
114 * @param mixed $data - Sent in via AJAX
115 * @return WP_Error|array - information about the operation or a WP_Error object on failure
116 */
117 public function restore_single_image($data) {
118
119 $blog_id = isset($data['blog_id']) ? $data['blog_id'] : false;
120 $image_id = isset($data['selected_image']) ? $data['selected_image'] : false;
121
122 $success = $this->task_manager->restore_single_image($image_id, $blog_id);
123
124 if (is_wp_error($success)) {
125 return $success;
126 }
127
128 $response = array();
129 $response['status'] = true;
130 $response['operation'] = 'restore';
131 $response['blog_id'] = $blog_id;
132 $response['image'] = $image_id;
133 $response['success'] = $success;
134 $response['summary'] = __('The image was restored successfully', 'wp-optimize');
135
136 return $response;
137 }
138
139 /**
140 * Process the compression of multiple images
141 *
142 * @param mixed $data - Sent in via AJAX
143 */
144 public function process_bulk_smush($data = array()) {
145 $images = isset($data['selected_images']) ? $data['selected_images'] : array();
146
147 $ui_update = $this->get_ui_update($images);
148 $this->close_browser_connection(json_encode($ui_update));
149 $this->task_manager->process_bulk_smush($images);
150 // Since we already sent back data and closed the browser connection, we must not return (that would result in further sending back of JSON).
151 die();
152 }
153
154 /**
155 * Returns useful information for the UI and closes the connection
156 *
157 * @param mixed $data - Sent in via AJAX
158 *
159 * @return mixed - Information for the UI
160 */
161 public function get_ui_update($data) {
162 $ui_update = array();
163 $ui_update['status'] = true;
164 $ui_update['is_multisite'] = is_multisite() ? 1 : 0;
165 $pending_tasks = $this->task_manager->get_pending_tasks();
166
167 $ui_update['pending_tasks'] = is_array($pending_tasks) ? count($this->task_manager->get_pending_tasks()) : 0;
168 $ui_update['unsmushed_images'] = $this->task_manager->get_uncompressed_images();
169 $ui_update['admin_urls'] = $this->task_manager->get_admin_urls();
170 $ui_update['completed_task_count'] = $this->task_manager->options->get_option('completed_task_count', 0);
171 $ui_update['bytes_saved'] = WP_Optimize()->format_size($this->task_manager->options->get_option('total_bytes_saved', 0));
172 $ui_update['percent_saved'] = number_format($this->task_manager->options->get_option('total_percent_saved', 1), 2).'%';
173 $ui_update['failed_task_count'] = $this->task_manager->get_failed_task_count();
174
175 $ui_update['summary'] = sprintf(__('Since your compression statistics were last reset, a total of %d image(s) were compressed on this site.', 'wp-optimize').' '.__('This saved approximately %s of space at an average of %02d percent per image.', 'wp-optimize'), $ui_update['completed_task_count'], $ui_update['bytes_saved'], $ui_update['percent_saved']);
176 $ui_update['failed'] = sprintf(__("%d image(s) could not be compressed.", 'wp-optimize'), $ui_update['failed_task_count']) . ' ' . __('Please see the logs for more information, or try again later.', 'wp-optimize');
177 $ui_update['pending'] = sprintf(__("%d image(s) images were selected for compressing previously, but were not all processed.", 'wp-optimize'), $ui_update['pending_tasks']) . ' ' . __('You can either complete them now or cancel and retry later.', 'wp-optimize');
178 $ui_update['smush_complete'] = $this->task_manager->is_queue_processed();
179
180 if (isset($data['image_list'])) {
181 $images = $data['image_list'];
182 $stats = $this->task_manager->get_session_stats($images);
183 $ui_update['session_stats'] = "";
184
185 if (!empty($stats['success'])) {
186 $ui_update['session_stats'] .= sprintf(__("A total of %d image(s) were successfully compressed in this iteration.", 'wp-optimize'), $stats['success']);
187 }
188
189 if (!empty($stats['fail'])) {
190 $ui_update['session_stats'] .= sprintf(__("%d selected image(s) could not be compressed.", 'wp-optimize'), $stats['fail']) . ' ' . __('Please see the logs for more information, you may try again later.', 'wp-optimize');
191 }
192 }
193
194 return $ui_update;
195
196 }
197
198 /**
199 * Updates smush related options
200 *
201 * @param mixed $data - Sent in via AJAX
202 * @return WP_Error|array - information about the operation or WP_Error object on failure
203 */
204 public function update_smush_options($data) {
205 $options = array();
206 $options['compression_server'] = sanitize_text_field($data['compression_server']);
207 $options['lossy_compression'] = filter_var($data['lossy_compression'], FILTER_VALIDATE_BOOLEAN) ? true : false;
208 $options['back_up_original'] = filter_var($data['back_up_original'], FILTER_VALIDATE_BOOLEAN) ? true : false;
209 $options['back_up_delete_after'] = filter_var($data['back_up_delete_after'], FILTER_VALIDATE_BOOLEAN) ? true : false;
210 $options['back_up_delete_after_days'] = filter_var($data['back_up_delete_after_days'], FILTER_SANITIZE_NUMBER_INT);
211 $options['preserve_exif'] = filter_var($data['preserve_exif'], FILTER_VALIDATE_BOOLEAN) ? true : false;
212 $options['autosmush'] = filter_var($data['autosmush'], FILTER_VALIDATE_BOOLEAN) ? true : false;
213 $options['image_quality'] = filter_var($data['image_quality'], FILTER_SANITIZE_NUMBER_INT);
214 $options['show_smush_metabox'] = filter_var($data['show_smush_metabox'], FILTER_VALIDATE_BOOLEAN) ? 'show' : 'hide';
215 $options['webp_conversion'] = filter_var($data['webp_conversion'], FILTER_VALIDATE_BOOLEAN) ? true : false;
216
217 $success = $this->task_manager->update_smush_options($options);
218
219 if (!$this->is_webp_enabled($options['webp_conversion'])) {
220 $this->remove_webp_redirect_rules();
221 }
222
223 if (!$success) {
224 return new WP_Error('update_failed', __('Smush options could not be updated', 'wp-optimize'));
225 }
226
227 do_action('wpo_save_images_settings');
228
229 $response = array();
230 $response['status'] = true;
231 $response['saved'] = $success;
232 $response['summary'] = __('Options updated successfully', 'wp-optimize');
233
234 return $response;
235 }
236
237 /**
238 * Clears any smush related stats
239 *
240 * @return WP_Error|array - information about the operation or WP_Error object on failure
241 */
242 public function clear_smush_stats() {
243
244 $success = $this->task_manager->clear_smush_stats();
245
246 if (!$success) {
247 return new WP_Error('update_failed', __('Stats could not be cleared', 'wp-optimize'));
248 }
249
250 $response = array();
251 $response['status'] = true;
252 $response['summary'] = __('Stats cleared successfully', 'wp-optimize');
253
254 return $response;
255 }
256
257 /**
258 * Checks if the selected server is online
259 *
260 * @param mixed $data - Sent in via AJAX
261 */
262 public function check_server_status($data) {
263 $server = sanitize_text_field($data['server']);
264 $response = array();
265 $response['status'] = true;
266 $response['online'] = $this->task_manager->check_server_online($server);
267
268 if (!$response['online']) {
269 $response['error'] = get_option($this->task_manager->get_associated_task($server));
270 }
271
272 return $response;
273 }
274
275 /**
276 * Completes any pending tasks
277 */
278 public function process_pending_images() {
279 $this->process_bulk_smush();
280 }
281
282 /**
283 * Deletes and removes any pending tasks from queue
284 *
285 * @return WP_Error|array - information about the operation or WP_Error object on failure
286 */
287 public function clear_pending_images() {
288
289 $success = $this->task_manager->clear_pending_images();
290
291 if (!$success) {
292 return new WP_Error('error_deleting_tasks', __('Pending tasks could not be cleared', 'wp-optimize'));
293 }
294
295 $response = array();
296 $response['status'] = true;
297 $response['summary'] = __('Pending tasks cleared successfully', 'wp-optimize');
298
299 return $response;
300 }
301
302 /**
303 * Mark selected images as already compressed.
304 *
305 * @param array $data
306 * @return array
307 */
308 public function mark_as_compressed($data) {
309 $response = array();
310 $selected_images = array();
311
312 $unmark = isset($data['unmark']) && $data['unmark'];
313
314 foreach ($data['selected_images'] as $image) {
315 if (!array_key_exists($image['blog_id'], $selected_images)) $selected_images[$image['blog_id']] = array();
316
317 $selected_images[$image['blog_id']][] = $image['attachment_id'];
318 }
319
320 $info = __('This image is marked as already compressed by another tool.', 'wp-optimize');
321
322 foreach (array_keys($selected_images) as $blog_id) {
323 if (is_multisite()) switch_to_blog($blog_id);
324
325 foreach ($selected_images[$blog_id] as $attachment_id) {
326 if ($unmark) {
327 delete_post_meta($attachment_id, 'smush-complete');
328 delete_post_meta($attachment_id, 'smush-marked');
329 delete_post_meta($attachment_id, 'smush-info');
330 } else {
331 update_post_meta($attachment_id, 'smush-complete', true);
332 update_post_meta($attachment_id, 'smush-marked', true);
333 update_post_meta($attachment_id, 'smush-info', $info);
334 }
335 }
336
337 if (is_multisite()) restore_current_blog();
338 }
339
340 $response['status'] = true;
341
342 if ($unmark) {
343 $response['summary'] = _n('The selected image was successfully marked as uncompressed', 'The selected images were successfully marked as uncompressed', count($data['selected_images']), 'wp-optimize');
344 } else {
345 $response['summary'] = _n('The selected image was successfully marked as compressed', 'The selected images were successfully marked as compressed', count($data['selected_images']), 'wp-optimize');
346 }
347
348 $response['info'] = $info;
349
350 return $response;
351 }
352
353 /**
354 * Mark all images as uncompressed and if posted restore_backup argument
355 * then try to restore images form backup.
356 *
357 * @param array $data
358 * @return array
359 */
360 public function mark_all_as_uncompressed($data) {
361
362 $restore_backup = isset($data['restore_backup']) && $data['restore_backup'];
363 $images_per_request = apply_filters('mark_all_as_uncompressed_images_per_request', 100);
364 $delete_only_backups_meta = isset($data['delete_only_backups_meta']) && $data['delete_only_backups_meta'];
365
366 if (is_multisite()) {
367 // option where we store last completed blog id
368 $option_name = 'mark_as_uncompressed_last_blog_id';
369 // set default value for response
370 $response = array(
371 'completed' => true,
372 'message' => __('All the compressed images were successfully restored.', 'wp-optimize'),
373 );
374
375 // get all blogs ids
376 $blogs = WP_Optimize()->get_sites();
377 $blogs_ids = wp_list_pluck($blogs, 'blog_id');
378 sort($blogs_ids);
379
380 // select the blog for processing
381 $last_completed_blog_id = $this->task_manager->options->get_option($option_name, false);
382 $index = $last_completed_blog_id ? array_search($last_completed_blog_id, $blogs_ids) + 1 : 0;
383
384 if ($index < count($blogs_ids)) {
385 $blog_id = $blogs_ids[$index];
386 $response = $this->task_manager->bulk_restore_compressed_images($restore_backup, $blog_id, $images_per_request, $delete_only_backups_meta);
387
388 // if we get completed the current blog then update last completed blog option value
389 // and if we have other blogs for processing then set complete to false as we have not
390 // processed all blogs
391 if ($response['completed']) {
392 if ($index + 1 < count($blogs_ids)) {
393 $response['completed'] = false;
394 } else {
395 if ($delete_only_backups_meta) {
396 $response['message'] = __('All the compressed images were successfully restored.', 'wp-optimize');
397 } else {
398 $response['message'] = __('All the compressed images were successfully marked as uncompressed.', 'wp-optimize');
399 }
400 }
401 $this->task_manager->options->update_option($option_name, $blog_id);
402 }
403 }
404
405 // if we get an error or completed the work then delete option with last completed blog id.
406 if ($response['completed'] || isset($response['error'])) {
407 $this->task_manager->options->delete_option($option_name);
408 }
409 } else {
410 $response = $this->task_manager->bulk_restore_compressed_images($restore_backup, 0, $images_per_request, $delete_only_backups_meta);
411 }
412
413 return $response;
414 }
415
416 /**
417 * Returns the log file
418 *
419 * @return WP_Error|file - logfile or WP_Error object on failure
420 */
421 public function get_smush_logs() {
422
423 $logfile = $this->task_manager->get_logfile_path();
424
425 if (!file_exists($logfile)) {
426 $this->task_manager->write_log_header();
427 }
428
429 if (is_file($logfile)) {
430 header('Content-Description: File Transfer');
431 header('Content-Type: application/octet-stream');
432 header('Content-Disposition: attachment; filename="'.basename($logfile).'"');
433 header('Expires: 0');
434 header('Cache-Control: must-revalidate');
435 header('Pragma: public');
436 header('Content-Length: ' . filesize($logfile));
437 readfile($logfile);
438 exit;
439 } else {
440 return new WP_Error('log_file_error', __('Log file does not exist or could not be read', 'wp-optimize'));
441 }
442 }
443
444 /**
445 * Clean all backup images command.
446 *
447 * @return array
448 */
449 public function clean_all_backup_images() {
450 $upload_dir = wp_upload_dir(null, false);
451 $base_dir = $upload_dir['basedir'];
452
453 $this->task_manager->clear_backup_images_directory($base_dir, 0);
454
455 return array(
456 'status' => true,
457 );
458 }
459
460 /**
461 * Close browser connection so that it can resume AJAX polling
462 *
463 * @param array $txt Response to browser; this must be JSON (or if not, alter the Content-Type header handling below)
464 * @return void
465 */
466 public function close_browser_connection($txt = '') {
467 header('Content-Length: '.((!empty($txt)) ? 4+strlen($txt) : '0'));
468 header('Content-Type: application/json');
469 header('Connection: close');
470 header('Content-Encoding: none');
471 if (session_id()) session_write_close();
472 echo "\r\n\r\n";
473 echo $txt;
474
475 $levels = ob_get_level();
476
477 for ($i = 0; $i < $levels; $i++) {
478 ob_end_flush();
479 }
480
481 flush();
482
483 if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
484 }
485
486 /**
487 * Resets webp serving method
488 *
489 * @return array
490 */
491 public function reset_webp_serving_method() {
492 WP_Optimize()->get_webp_instance()->reset_webp_serving_method();
493 return array(
494 'success' => true,
495 );
496 }
497
498 /**
499 * Convert the image to webp format
500 *
501 * @param array $data
502 * @return array
503 */
504 public function convert_to_webp_format($data) {
505 $attachment_id = isset($data['attachment_id']) ? $data['attachment_id'] : 0;
506 if (0 === $attachment_id) return $this->image_not_found_response();
507
508 $images = WPO_Image_Utils::get_attachment_files($attachment_id);
509 if (empty($images)) return $this->image_not_found_response();
510
511 $images['original'] = get_attached_file($attachment_id);
512 foreach ($images as $image) {
513 WPO_WebP_Utils::do_webp_conversion($image);
514 }
515
516 return array(
517 'success' => __('Image is converted to WebP format.', 'wp-optimize'),
518 );
519 }
520
521 /**
522 * Returns image not found response
523 *
524 * @return array
525 */
526 private function image_not_found_response() {
527 return array(
528 'error' => __('Image not found', 'wp-optimize'),
529 );
530 }
531
532 /**
533 * Decides whether to use webp images option is enabled or not
534 *
535 * @param bool $webp_option
536 *
537 * @return bool
538 */
539 private function is_webp_enabled($webp_option) {
540 return true === $webp_option;
541 }
542
543 /**
544 * Removes webp redirect rules in .htaccess file
545 *
546 * @return void
547 */
548 private function remove_webp_redirect_rules() {
549 WP_Optimize()->get_webp_instance()->empty_htaccess_file();
550 }
551 }
552
553 endif;
554