PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / trunk
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF vtrunk
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / deprecated / classes / class-imagify-as3cf-attachment.php

class-imagify-as3cf-attachment.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF trunk, at inc/deprecated/classes/class-imagify-as3cf-attachment.php

991 lines 29.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
3
4 /**
5 * Imagify WP Offload S3 attachment class.
6 *
7 * @since 1.6.6
8 * @since 1.9 Deprecated
9 * @author Grégory Viguier
10 * @deprecated
11 */
12 class Imagify_AS3CF_Attachment extends Imagify_Attachment {
13
14 /**
15 * Class version.
16 *
17 * @var string
18 */
19 const VERSION = '1.1.2';
20
21 /**
22 * Tell if AS3CF settings will be used for this attachment.
23 *
24 * @var bool
25 */
26 protected $use_s3_settings;
27
28 /**
29 * Tell if the files should be deleted once sent to S3.
30 *
31 * @var bool
32 */
33 protected $delete_files;
34
35 /**
36 * The constructor.
37 *
38 * @since 1.5
39 * @author Jonathan Buttigieg
40 *
41 * @param int|object $id An image attachment ID or a NGG object.
42 */
43 public function __construct( $id ) {
44 imagify_deprecated_class( get_class( $this ), '1.9' );
45
46 parent::__construct( $id );
47 }
48
49
50 /** ----------------------------------------------------------------------------------------- */
51 /** ATTACHMENT PATHS AND URLS =============================================================== */
52 /** ----------------------------------------------------------------------------------------- */
53
54 /**
55 * Get the original attachment path.
56 *
57 * @since 1.6.6
58 * @author Grégory Viguier
59 *
60 * @return string|bool Path to the file if it exists or has been successfully retrieved from S3. False on failure.
61 */
62 public function get_original_path() {
63 return $this->get_thumbnail_path();
64 }
65
66 /**
67 * Get a thumbnail path.
68 *
69 * @since 1.6.6
70 * @author Grégory Viguier
71 *
72 * @param string $size_file The basename of the file. If not provided, the path to the main file is returned.
73 * @return string|bool Path to the file if it exists or has been successfully retrieved from S3. False on failure.
74 */
75 public function get_thumbnail_path( $size_file = false ) {
76 if ( ! $this->is_valid() ) {
77 return '';
78 }
79
80 $file_path = get_attached_file( $this->id, true );
81
82 if ( $size_file ) {
83 // It's not the full size.
84 $file_path = $this->filesystem->dir_path( $file_path ) . $size_file;
85 }
86
87 return $file_path;
88 }
89
90 /**
91 * Get the original attachment URL.
92 *
93 * @since 1.6.6
94 * @author Grégory Viguier
95 *
96 * @return string|bool The main file URL. False on failure.
97 */
98 public function get_original_url() {
99 return $this->get_thumbnail_url();
100 }
101
102 /**
103 * Get a thumbnail URL.
104 *
105 * @since 1.6.6
106 * @author Grégory Viguier
107 *
108 * @param string $size_file The basename of the file. If not provided, the main file's URL is returned.
109 * @return string|bool The file URL. False on failure.
110 */
111 public function get_thumbnail_url( $size_file = false ) {
112 if ( ! $this->is_extension_supported() ) {
113 return false;
114 }
115
116 $file_url = wp_get_attachment_url( $this->id );
117
118 if ( $size_file ) {
119 // It's not the full size.
120 $file_url = $this->filesystem->dir_path( $file_url ) . $size_file;
121 }
122
123 return $file_url;
124 }
125
126
127 /** ----------------------------------------------------------------------------------------- */
128 /** THE PUBLIC STUFF ======================================================================== */
129 /** ----------------------------------------------------------------------------------------- */
130
131 /**
132 * Optimize all sizes with Imagify.
133 *
134 * @since 1.6.6
135 * @author Grégory Viguier
136 *
137 * @param int $optimization_level The optimization level (2 = ultra, 1 = aggressive, 0 = normal).
138 * @param array $metadata The attachment meta data, containing the sizes. Provide only for a new attachment.
139 * @return array|bool The optimization data. False on failure.
140 */
141 public function optimize( $optimization_level = null, $metadata = array() ) {
142 $metadata_changed = false;
143
144 /**
145 * Make some sanity tests first.
146 */
147
148 // Check if the attachment extension is allowed.
149 if ( ! $this->is_extension_supported() ) {
150 return false;
151 }
152
153 // To avoid issue with "original_size" at 0 in "_imagify_data".
154 if ( 0 === (int) $this->get_stats_data( 'original_size' ) ) {
155 $this->delete_imagify_data();
156 }
157
158 $optimization_level = isset( $optimization_level ) ? (int) $optimization_level : get_imagify_option( 'optimization_level' );
159
160 // Check if the full size is already optimized with this level.
161 if ( $this->is_optimized() && $this->get_optimization_level() === $optimization_level ) {
162 return false;
163 }
164
165 // Get file path of the full size.
166 $attachment_path = $this->get_original_path();
167 $attachment_url = $this->get_original_url();
168
169 if ( ! $attachment_path ) {
170 // We're in deep sh**.
171 return false;
172 }
173
174 if ( ! $this->filesystem->exists( $attachment_path ) && ! $this->get_file_from_s3( $attachment_path ) ) {
175 // The file doesn't exist and couldn't be retrieved from S3.
176 return false;
177 }
178
179 /**
180 * Start the process.
181 */
182 $this->set_running_status();
183
184 /** This hook is documented in /inc/classes/class-imagify-attachment.php. */
185 do_action( 'before_imagify_optimize_attachment', $this->id );
186
187 $metadata = $this->set_deletion_status( $metadata );
188
189 // Store the paths of the files that may be deleted once optimized and sent to S3.
190 $to_delete = array();
191 $filesize_total = 0;
192
193 // Maybe resize (and backup) the image.
194 $resized = $this->is_image() && $this->maybe_resize( $attachment_path );
195
196 if ( $resized ) {
197 $size = $this->filesystem->get_image_size( $attachment_path );
198
199 if ( $size ) {
200 $metadata['width'] = $size['width'];
201 $metadata['height'] = $size['height'];
202 $metadata_changed = true;
203 }
204 }
205
206 // Optimize the full size.
207 $response = do_imagify( $attachment_path, array(
208 'optimization_level' => $optimization_level,
209 'context' => 'wp',
210 'resized' => $resized,
211 'original_size' => $this->get_original_size( false ),
212 ) );
213
214 $data = $this->fill_data( null, $response );
215
216 if ( $this->delete_files ) {
217 $to_delete[] = $attachment_path;
218 // This is used by AS3CF.
219 $bytes = $this->filesystem->size( $attachment_path );
220
221 if ( false !== $bytes ) {
222 $metadata_changed = true;
223 $filesize_total += $bytes;
224 $metadata['filesize'] = $bytes;
225 } else {
226 $metadata['filesize'] = 0;
227 }
228 }
229
230 /** This filter is documented in /inc/classes/class-imagify-attachment.php. */
231 $data = apply_filters( 'imagify_fill_full_size_data', $data, $response, $this->id, $attachment_path, $attachment_url, 'full', $optimization_level, $metadata );
232
233 // Save the optimization level.
234 update_post_meta( $this->id, '_imagify_optimization_level', $optimization_level );
235
236 if ( ! $data ) {
237 // The optimization failed.
238 $metadata = $metadata_changed ? $metadata : false;
239 $this->cleanup( $metadata, $to_delete );
240 return false;
241 }
242
243 // Optimize all thumbnails.
244 if ( ! empty( $metadata['sizes'] ) ) {
245 $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
246 $is_active_for_network = imagify_is_active_for_network();
247
248 foreach ( $metadata['sizes'] as $size_key => $size_data ) {
249 $thumbnail_path = $this->get_thumbnail_path( $size_data['file'] );
250 $thumbnail_url = $this->get_thumbnail_url( $size_data['file'] );
251
252 if ( $this->delete_files ) {
253 $to_delete[] = $thumbnail_path;
254
255 // Even if this size must not be optimized ($disallowed_sizes), we must fetch the file from S3 to get its size, and not to trigger a `WP_Error` in `upload_attachment_to_s3()`.
256 if ( ! $this->filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
257 // Doesn't exist and couldn't be retrieved from S3.
258 $data['sizes'][ $size_key ] = array(
259 'success' => false,
260 'error' => __( 'This size could not be retrieved from Amazon S3.', 'imagify' ),
261 );
262 continue;
263 }
264
265 // This is used by AS3CF.
266 $bytes = $this->filesystem->size( $thumbnail_path );
267
268 if ( false !== $bytes ) {
269 $filesize_total += $bytes;
270 }
271 }
272
273 // Check if this size has to be optimized.
274 if ( ! $is_active_for_network && isset( $disallowed_sizes[ $size_key ] ) && $this->is_image() ) {
275 $data['sizes'][ $size_key ] = array(
276 'success' => false,
277 'error' => __( 'This size is not authorized to be optimized. Update your Imagify settings if you want to optimize it.', 'imagify' ),
278 );
279
280 /** This filter is documented in /inc/classes/class-imagify-attachment.php. */
281 $data = apply_filters( 'imagify_fill_unauthorized_thumbnail_data', $data, $this->id, $thumbnail_path, $thumbnail_url, $size_key, $optimization_level, $metadata );
282 continue;
283 }
284
285 if ( ! $this->delete_files && ! $this->filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
286 // Doesn't exist and couldn't be retrieved from S3.
287 $data['sizes'][ $size_key ] = array(
288 'success' => false,
289 'error' => __( 'This size could not be retrieved from Amazon S3.', 'imagify' ),
290 );
291 continue;
292 }
293
294 if ( ! $this->is_image() ) {
295 continue;
296 }
297
298 // Optimize the thumbnail size.
299 $response = do_imagify( $thumbnail_path, array(
300 'backup' => false,
301 'optimization_level' => $optimization_level,
302 'context' => 'wp',
303 ) );
304
305 $data = $this->fill_data( $data, $response, $size_key );
306
307 /** This filter is documented in /inc/classes/class-imagify-attachment.php. */
308 $data = apply_filters( 'imagify_fill_thumbnail_data', $data, $response, $this->id, $thumbnail_path, $thumbnail_url, $size_key, $optimization_level, $metadata );
309 } // End foreach().
310 } // End if().
311
312 $data['stats']['percent'] = round( ( ( $data['stats']['original_size'] - $data['stats']['optimized_size'] ) / $data['stats']['original_size'] ) * 100, 2 );
313
314 update_post_meta( $this->id, '_imagify_data', $data );
315 update_post_meta( $this->id, '_imagify_status', 'success' );
316
317 if ( $this->delete_files && $filesize_total ) {
318 // Add the total file size for all image sizes. This is a meta used by AS3CF.
319 update_post_meta( $this->id, 'wpos3_filesize_total', $filesize_total );
320 }
321
322 $optimized_data = $this->get_data();
323
324 /** This hook is documented in /inc/classes/class-imagify-attachment.php. */
325 do_action( 'after_imagify_optimize_attachment', $this->id, $optimized_data );
326
327 $sent = $this->maybe_send_attachment_to_s3( $metadata, $attachment_path );
328 // Update metadata only if they changed.
329 $metadata = $metadata_changed ? $metadata : false;
330 // Delete files only if they have been uploaded to S3.
331 $to_delete = $sent ? $to_delete : array();
332
333 $this->cleanup( $metadata, $to_delete );
334
335 return $optimized_data;
336 }
337
338 /**
339 * Optimize missing thumbnail sizes with Imagify.
340 *
341 * @since 1.6.10
342 * @access public
343 * @author Grégory Viguier
344 *
345 * @param int $optimization_level The optimization level (2=ultra, 1=aggressive, 0=normal).
346 * @return array|object An array of thumbnail data, size by size. A WP_Error object on failure.
347 */
348 public function optimize_missing_thumbnails( $optimization_level = null ) {
349 // Check if the attachment extension is allowed.
350 if ( ! $this->is_extension_supported() || ! $this->is_image() ) {
351 return new WP_Error( 'mime_type_not_supported', __( 'This type of file is not supported.', 'imagify' ) );
352 }
353
354 /**
355 * Create missing thumbnails and optimize them.
356 */
357 $result = parent::optimize_missing_thumbnails( $optimization_level );
358 $result_sizes = array();
359
360 $this->set_running_status();
361
362 if ( is_array( $result ) ) {
363 // All good.
364 $result_sizes = $result;
365 } elseif ( is_wp_error( $result ) ) {
366 // Some thumbnails could not be created. Lets see if some were.
367 $result_sizes = $result->get_error_data( 'image_resize_error' );
368 $result_sizes = ! empty( $result_sizes['sizes_succeeded'] ) ? $result_sizes['sizes_succeeded'] : array();
369 }
370
371 if ( ! $result_sizes ) {
372 // No thumbnails created.
373 $this->delete_running_status();
374 return $result;
375 }
376
377 /**
378 * Fetch all images from S3 if they're not on the server.
379 * S3 Offload needs ALL images (so it can update its metas), we can't just send some of them without entering Hell -_-'.
380 */
381 $metadata = $this->set_deletion_status();
382
383 if ( ! $this->can_send_to_s3() ) {
384 // The other thumbnails are not on S3, so we don't need to send the new ones.
385 $this->delete_running_status();
386 return $result;
387 }
388
389 /**
390 * The main file.
391 */
392 $attachment_path = $this->get_original_path();
393 $to_delete = array();
394 $to_skip = array();
395 $filesize_total = 0;
396 $metadata_changed = false;
397
398 if ( ! $attachment_path ) {
399 // WAT?!
400 if ( ! is_wp_error( $result ) ) {
401 $result = new WP_Error( 'no_attachment_path', __( 'Files could not be sent to Amazon S3.', 'imagify' ), array(
402 'sizes_succeeded' => $result_sizes,
403 ) );
404 } else {
405 $result->add( 'no_attachment_path', __( 'Files could not be sent to Amazon S3.', 'imagify' ) );
406 }
407
408 $this->delete_running_status();
409 return $result;
410 }
411
412 if ( ! $this->filesystem->exists( $attachment_path ) && ! $this->get_file_from_s3( $attachment_path ) ) {
413 // The file doesn't exist and couldn't be retrieved from S3.
414 if ( ! is_wp_error( $result ) ) {
415 $result = new WP_Error( 'main_file_not_on_s3', __( 'The main image could not be retrieved from Amazon S3.', 'imagify' ), array(
416 'sizes_succeeded' => $result_sizes,
417 ) );
418 } else {
419 $result->add( 'main_file_not_on_s3', __( 'The main image could not be retrieved from Amazon S3.', 'imagify' ) );
420 }
421
422 $this->delete_running_status();
423 return $result;
424 }
425
426 // Files that must not be retrieved from S3.
427 foreach ( $result_sizes as $size_key => $size_data ) {
428 $to_skip[] = $this->get_thumbnail_path( $size_data['file'] );
429 }
430
431 // Store the paths of the files that may be deleted once sent to S3.
432 if ( $this->delete_files ) {
433 $to_delete[] = $attachment_path;
434 $to_delete = array_merge( $to_delete, $to_skip );
435
436 // This is used by AS3CF.
437 $bytes = $this->filesystem->size( $attachment_path );
438
439 if ( false !== $bytes ) {
440 $metadata_changed = true;
441 $filesize_total += $bytes;
442 $metadata['filesize'] = $bytes;
443 } elseif ( ! isset( $metadata['filesize'] ) ) {
444 $metadata_changed = true;
445 $metadata['filesize'] = 0;
446 }
447 }
448
449 /**
450 * The thumbnails.
451 */
452 if ( ! empty( $metadata['sizes'] ) ) {
453 $to_skip = array_flip( $to_skip );
454
455 foreach ( $metadata['sizes'] as $size_key => $size_data ) {
456 $thumbnail_path = $this->get_thumbnail_path( $size_data['file'] );
457
458 if ( isset( $to_skip[ $thumbnail_path ] ) ) {
459 continue;
460 }
461
462 if ( ! $this->filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
463 // The file doesn't exist and couldn't be retrieved from S3.
464 if ( ! is_wp_error( $result ) ) {
465 $result = new WP_Error( 'thumbnail_not_on_s3', __( 'This size could not be retrieved from Amazon S3.', 'imagify' ), array(
466 'sizes_succeeded' => $result_sizes,
467 'size' => $size_key,
468 ) );
469 } else {
470 $result->add( 'thumbnail_not_on_s3', __( 'This size could not be retrieved from Amazon S3.', 'imagify' ), array(
471 'size' => $size_key,
472 ) );
473 }
474
475 $this->delete_running_status();
476 return $result;
477 }
478
479 if ( $this->delete_files ) {
480 $to_delete[] = $thumbnail_path;
481
482 // This is used by AS3CF.
483 $bytes = $this->filesystem->size( $thumbnail_path );
484
485 if ( false !== $bytes ) {
486 $filesize_total += $bytes;
487 }
488 }
489 } // End foreach().
490 } // End if().
491
492 if ( $this->delete_files && $filesize_total ) {
493 // Add the total file size for all image sizes. This is a meta used by AS3CF.
494 update_post_meta( $this->id, 'wpos3_filesize_total', $filesize_total );
495 }
496
497 $sent = $this->maybe_send_attachment_to_s3( $metadata, $attachment_path );
498 // Update metadata only if they changed.
499 $metadata = $metadata_changed ? $metadata : false;
500 // Delete files only if they have been uploaded to S3.
501 $to_delete = $sent ? $to_delete : array();
502
503 $this->cleanup( $metadata, $to_delete );
504
505 return $result;
506 }
507
508 /**
509 * Re-optimize the given thumbnail sizes to the same level.
510 * Not supported yet in this context.
511 *
512 * @since 1.7.1
513 * @access public
514 * @author Grégory Viguier
515 *
516 * @param array $sizes The sizes to optimize.
517 * @return array|void A WP_Error object on failure.
518 */
519 public function reoptimize_thumbnails( $sizes ) {}
520
521 /**
522 * Process an attachment restoration from the backup file.
523 *
524 * @since 1.6.6
525 * @author Grégory Viguier
526 *
527 * @return array A list of files sent to S3.
528 */
529 public function restore() {
530 // Check if the attachment extension is allowed.
531 if ( ! $this->is_extension_supported() ) {
532 return false;
533 }
534
535 // Stop the process if there is no backup file to restore.
536 if ( ! $this->has_backup() ) {
537 return false;
538 }
539
540 /** This hook is documented in /inc/classes/class-imagify-attachment.php. */
541 do_action( 'before_imagify_restore_attachment', $this->id );
542
543 $backup_path = $this->get_backup_path();
544 $attachment_path = $this->get_original_path();
545
546 if ( ! $attachment_path ) {
547 return false;
548 }
549
550 // Create the original image from the backup.
551 $this->filesystem->copy( $backup_path, $attachment_path, true );
552 $this->filesystem->chmod_file( $attachment_path );
553
554 if ( ! $this->filesystem->exists( $attachment_path ) ) {
555 return false;
556 }
557
558 $this->set_deletion_status();
559
560 // Remove old optimization data.
561 $this->delete_imagify_data();
562
563 if ( ! $this->is_image() ) {
564 // We need to delete the thumbnails for pdf, or new ones will be generated with new unique file names.
565 $metadata = wp_get_attachment_metadata( $this->id );
566
567 if ( ! empty( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ) {
568 foreach ( $metadata['sizes'] as $size_key => $size_data ) {
569 $thumbnail_path = $this->get_thumbnail_path( $size_data['file'] );
570
571 if ( $this->filesystem->exists( $thumbnail_path ) ) {
572 $this->filesystem->delete( $thumbnail_path );
573 }
574 }
575 }
576 }
577
578 if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
579 require_once ABSPATH . 'wp-admin/includes/image.php';
580 }
581
582 // Generate new thumbnails and new metadata.
583 $metadata = wp_generate_attachment_metadata( $this->id, $attachment_path );
584
585 // Send to S3.
586 $sent = $this->maybe_send_attachment_to_s3( $metadata, $attachment_path );
587 // Files restored (and maybe to delete).
588 $files = array();
589 // If the files must be deleted, we need to store the file sizes.
590 $filesize_total = 0;
591
592 if ( $sent ) {
593 $files[] = $attachment_path;
594 }
595
596 if ( $this->delete_files ) {
597 // This is used by AS3CF.
598 $bytes = $this->filesystem->size( $attachment_path );
599
600 if ( false !== $bytes ) {
601 $filesize_total += $bytes;
602 $metadata['filesize'] = $bytes;
603 } else {
604 $metadata['filesize'] = 0;
605 }
606 }
607
608 if ( ! empty( $metadata['sizes'] ) && ( $sent || $this->delete_files ) ) {
609 foreach ( $metadata['sizes'] as $size_key => $size_data ) {
610 $thumbnail_path = $this->get_thumbnail_path( $size_data['file'] );
611
612 if ( $sent ) {
613 $files[] = $thumbnail_path;
614 }
615
616 if ( $this->delete_files ) {
617 // This is used by AS3CF.
618 $bytes = $this->filesystem->size( $thumbnail_path );
619
620 if ( false !== $bytes ) {
621 $filesize_total += $bytes;
622 }
623 }
624 }
625 }
626
627 if ( $this->delete_files && $filesize_total ) {
628 // Add the total file size for all image sizes. This is a meta used by AS3CF.
629 update_post_meta( $this->id, 'wpos3_filesize_total', $filesize_total );
630 }
631
632 /** This hook is documented in /inc/classes/class-imagify-attachment.php. */
633 do_action( 'after_imagify_restore_attachment', $this->id );
634
635 $to_delete = $this->delete_files ? $files : array();
636
637 $this->cleanup( $metadata, $to_delete );
638
639 return $files;
640 }
641
642
643 /** ----------------------------------------------------------------------------------------- */
644 /** INTERNAL UTILITIES ====================================================================== */
645 /** ----------------------------------------------------------------------------------------- */
646
647 /**
648 * Cleanup after optimization or a restore:
649 * - Maybe update metadata.
650 * - Maybe delete local files.
651 * - Delete the "optimization status" transient.
652 *
653 * @since 1.6.6
654 * @author Grégory Viguier
655 *
656 * @param array $new_metadata New attachment metadata to be stored.
657 * @param array $files_to_remove Files to delete.
658 */
659 protected function cleanup( $new_metadata, $files_to_remove ) {
660 if ( $new_metadata ) {
661 /**
662 * Filter the metadata stored after optimization or a restore.
663 *
664 * @since 1.6.6
665 * @author Grégory Viguier
666 *
667 * @param array $new_metadata New attachment metadata to be stored.
668 * @param int $id The attachment ID.
669 */
670 $new_metadata = apply_filters( 'imagify_as3cf_attachment_cleanup_metadata', $new_metadata, $this->id );
671 /**
672 * Update the attachment meta that contains the file sizes.
673 * Here we don't use wp_update_attachment_metadata() to prevent triggering unwanted hooks.
674 */
675 update_post_meta( $this->id, '_wp_attachment_metadata', $new_metadata );
676 }
677
678 if ( $files_to_remove ) {
679 $attachment_path = $this->get_original_path();
680 /** This filter is documented in /amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php. */
681 $files_to_remove = (array) apply_filters( 'as3cf_upload_attachment_local_files_to_remove', $files_to_remove, $this->id, $attachment_path );
682 $files_to_remove = array_filter( $files_to_remove );
683
684 if ( $files_to_remove ) {
685 $files_to_remove = array_unique( $files_to_remove );
686 /**
687 * Delete the local files.
688 */
689 array_map( array( $this, 'maybe_delete_file' ), $files_to_remove );
690 }
691 }
692
693 /**
694 * Delete the "optimization status" transient.
695 */
696 $this->delete_running_status();
697 }
698
699 /**
700 * Maybe resize (and backup) an image.
701 *
702 * @since 1.6.6
703 * @author Grégory Viguier
704 *
705 * @param string $attachment_path The file path.
706 * @return bool True on success. False on failure.
707 */
708 protected function maybe_resize( $attachment_path ) {
709 if ( ! $this->is_image() ) {
710 return false;
711 }
712
713 $do_resize = get_imagify_option( 'resize_larger' );
714 $resize_width = get_imagify_option( 'resize_larger_w' );
715 $attachment_size = $this->filesystem->get_image_size( $attachment_path );
716
717 if ( ! $do_resize || ! $attachment_size || $resize_width >= $attachment_size['width'] ) {
718 return false;
719 }
720
721 $resized_attachment_path = $this->resize( $attachment_path, $attachment_size, $resize_width );
722
723 if ( is_wp_error( $resized_attachment_path ) ) {
724 return false;
725 }
726
727 $backuped = imagify_backup_file( $attachment_path );
728
729 if ( is_wp_error( $backuped ) ) {
730 return false;
731 }
732
733 return $this->filesystem->move( $resized_attachment_path, $attachment_path, true );
734 }
735
736 /**
737 * Tell if the files must be deleted after being optimized or restored.
738 * It sets the 2 properties $this->use_s3_settings and $this->delete_files.
739 *
740 * @since 1.6.6
741 * @author Grégory Viguier
742 *
743 * @param array $metadata Attachment metadata. Provide, only if it comes from a 'wp_generate_attachment_metadata' or 'wp_update_attachment_metadata' hook.
744 * @return array Attachment metadata. If not provided as argument, new values are fetched.
745 */
746 protected function set_deletion_status( $metadata = false ) {
747 global $as3cf;
748
749 if ( $metadata ) {
750 /**
751 * Metadata is provided: we were in a 'wp_generate_attachment_metadata' or 'wp_update_attachment_metadata' hook.
752 * This means we'll follow AS3CF settings to know if the local files must be sent to S3 and/or deleted.
753 */
754 $this->use_s3_settings = true;
755 $this->delete_files = $as3cf && $as3cf->get_setting( 'remove-local-file' ) && $this->can_send_to_s3();
756
757 return $metadata;
758 }
759
760 /**
761 * Metadata is not provided: we were not in a 'wp_generate_attachment_metadata' or 'wp_update_attachment_metadata' hook.
762 * So, we fetch the current meta value.
763 * This also means we won't follow AS3CF settings to know if the local files must be sent to S3 and/or deleted.
764 * In that case we'll send the files to S3 if they already are there, and delete them if they is a 'filesize' entry in the metadata.
765 */
766 $metadata = wp_get_attachment_metadata( $this->id, true );
767 $this->use_s3_settings = false;
768 $this->delete_files = isset( $metadata['filesize'] ) && $this->can_send_to_s3();
769
770 return $metadata;
771 }
772
773
774 /** ----------------------------------------------------------------------------------------- */
775 /** S3 UTILITIES ============================================================================ */
776 /** ----------------------------------------------------------------------------------------- */
777
778 /**
779 * Tell if AS3CF is set up.
780 *
781 * @since 1.6.6
782 * @author Grégory Viguier
783 *
784 * @return bool
785 */
786 public function is_s3_setup() {
787 global $as3cf;
788 static $is;
789
790 if ( ! isset( $is ) ) {
791 $is = $as3cf && $as3cf->is_plugin_setup();
792 }
793
794 return $is;
795 }
796
797 /**
798 * Tell if an attachment is stored on S3.
799 *
800 * @since 1.6.6
801 * @author Grégory Viguier
802 *
803 * @return array|bool The S3 info on success. False if the attachment is not on S3.
804 */
805 public function get_s3_info() {
806 global $as3cf;
807
808 if ( ! $as3cf ) {
809 return false;
810 }
811
812 if ( method_exists( $as3cf, 'get_attachment_s3_info' ) ) {
813 return $as3cf->get_attachment_s3_info( $this->id );
814 }
815
816 return $as3cf->get_attachment_provider_info( $this->id );
817 }
818
819 /**
820 * Get a file from S3.
821 *
822 * @since 1.6.6
823 * @author Grégory Viguier
824 *
825 * @param string $file_path The file path.
826 * @return string|bool The file path on success, false on failure.
827 */
828 protected function get_file_from_s3( $file_path ) {
829 global $as3cf;
830
831 if ( ! $this->is_extension_supported() ) {
832 return false;
833 }
834
835 if ( ! $this->is_s3_setup() ) {
836 return false;
837 }
838
839 $s3_object = $this->get_s3_info();
840
841 if ( ! $s3_object ) {
842 // The attachment is not on S3.
843 return false;
844 }
845
846 $directory = $this->filesystem->dir_path( $s3_object['key'] );
847 $directory = $this->filesystem->is_root( $directory ) ? '' : $directory;
848 $s3_object['key'] = $directory . $this->filesystem->file_name( $file_path );
849
850 // Retrieve file from S3.
851 if ( method_exists( $as3cf->plugin_compat, 'copy_s3_file_to_server' ) ) {
852 $as3cf->plugin_compat->copy_s3_file_to_server( $s3_object, $file_path );
853 } else {
854 $as3cf->plugin_compat->copy_provider_file_to_server( $s3_object, $file_path );
855 }
856
857 return $this->filesystem->exists( $file_path ) ? $file_path : false;
858 }
859
860 /**
861 * Maybe send the attachment to S3.
862 *
863 * @since 1.6.6
864 * @author Grégory Viguier
865 *
866 * @param array $metadata The attachment metadata.
867 * @param string $attachment_path The attachment path.
868 * @param bool $remove_local_files True to let AS3CF delete the local files (if set in the settings). We usually don't want that, we do it by ourselves.
869 * @return bool True on success. False otherwize.
870 */
871 protected function maybe_send_attachment_to_s3( $metadata = null, $attachment_path = null, $remove_local_files = false ) {
872 global $as3cf;
873
874 if ( ! $this->can_send_to_s3() ) {
875 return false;
876 }
877
878 $s3_object = $this->get_s3_info();
879
880 if ( ! $s3_object ) {
881 return false;
882 }
883
884 $full_file_path = $this->get_original_path();
885
886 if ( ! $full_file_path ) {
887 // This is bad.
888 return false;
889 }
890
891 if ( method_exists( $as3cf, 'upload_attachment_to_s3' ) ) {
892 $s3_data = $as3cf->upload_attachment_to_s3( $this->id, $metadata, $attachment_path, false, $remove_local_files );
893 } else {
894 $s3_data = $as3cf->upload_attachment( $this->id, $metadata, $attachment_path, false, $remove_local_files );
895 }
896
897 return ! is_wp_error( $s3_data );
898 }
899
900 /**
901 * Tell if an attachment can be sent to S3.
902 *
903 * @since 1.6.6
904 * @author Grégory Viguier
905 *
906 * @return bool
907 */
908 protected function can_send_to_s3() {
909 global $as3cf;
910 static $can = array();
911 static $copy_to_s3;
912
913 if ( isset( $can[ $this->id ] ) ) {
914 return $can[ $this->id ];
915 }
916
917 if ( ! isset( $copy_to_s3 ) ) {
918 $copy_to_s3 = $as3cf && $as3cf->get_setting( 'copy-to-s3' );
919 }
920
921 $is_s3_setup = $this->is_s3_setup();
922 $s3_object = $this->get_s3_info();
923 // S3 is set up and the attachment is on S3.
924 $can[ $this->id ] = $is_s3_setup && $s3_object;
925
926 if ( $can[ $this->id ] && ! empty( $this->use_s3_settings ) ) {
927 // Use AS3CF setting to tell if we're allowed to send the files.
928 $can[ $this->id ] = $copy_to_s3;
929 }
930
931 /**
932 * Filter the result of Imagify_AS3CF_Attachment::can_send_to_s3().
933 *
934 * @since 1.6.6
935 * @author Grégory Viguier
936 *
937 * @param bool $can True if the attachment can be sent. False otherwize.
938 * @param int $id The attachment ID.
939 * @param array $s3_object The S3 infos.
940 * @param bool $is_s3_setup AS3CF is set up or not.
941 * @param bool $copy_to_s3 AS3CF setting that tells if a "new" attachment can be sent.
942 * @param bool $use_s3_settings Tell if we must use AS3CF setting in this case.
943 */
944 $can[ $this->id ] = (bool) apply_filters( 'imagify_can_send_to_s3', $can[ $this->id ], $this->id, $s3_object, $is_s3_setup, $copy_to_s3, $this->use_s3_settings );
945
946 return $can[ $this->id ];
947 }
948
949 /**
950 * Maybe delete the local file.
951 *
952 * @since 1.6.6
953 * @author Grégory Viguier
954 *
955 * @param string $file_path The file path.
956 * @return bool True if deleted or doesn't exist. False on failure or if the file is not supposed to be deleted.
957 */
958 protected function maybe_delete_file( $file_path ) {
959 if ( ! $this->file_should_be_deleted( $file_path ) ) {
960 return false;
961 }
962
963 if ( ! $this->filesystem->exists( $file_path ) ) {
964 return true;
965 }
966
967 return $this->filesystem->delete( $file_path, false, 'f' );
968 }
969
970 /**
971 * Tell if a file should be deleted.
972 *
973 * @since 1.6.6
974 * @author Grégory Viguier
975 *
976 * @param string $file_path The file path.
977 * @return bool True to delete, false to keep.
978 */
979 protected function file_should_be_deleted( $file_path ) {
980 if ( ! $file_path || ! $this->delete_files ) {
981 // We keep the file.
982 return false;
983 }
984
985 /** This hook is documented in /amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php. */
986 $preserve = apply_filters( 'as3cf_preserve_file_from_local_removal', false, $file_path );
987
988 return false === $preserve;
989 }
990 }
991