diff --git a/src/filters.php b/src/filters.php index e06e954..9250554 100644 --- a/src/filters.php +++ b/src/filters.php @@ -55,7 +55,7 @@ function s3_multisite_upload_dir( $upload ) { // Conditionally adds a filter only during the upload process, this filter adds a second filter that removes all the image sizes. // It also adds a filter to preemptively add the sizes to the attachment metadata, otherwise the first filter would prevent the sizes from being added. add_filter( - 'wp_handle_upload_prefilter', + 'wp_handle_upload', function( $file ) { // This filters the image sizes that are generated during the upload process, removing all of them by returning an empty array. add_filter( @@ -140,3 +140,19 @@ function( $old_site ) { 10, 2 ); + +/** + * Add a filter to the wp_handle_replace event, declared by the enable-media-replace plugin. + * The filter passes the post ID of the file being replaced, and we use this to delete the old rendered media library files. + * Otherwise, the old scaled versions of the file won't be replaced with updated versions. + */ +add_filter( + 'wp_handle_replace', + function( $post_details ) { + // Get the file path from the attachment ID. + $file = get_attached_file( $post_details['post_id'] ); + + // Delete the old rendered media library files for the file being replaced. + delete_scaled_for_s3_key( $file ); + } +); diff --git a/src/s3-assets.php b/src/s3-assets.php index 2704685..96babf7 100644 --- a/src/s3-assets.php +++ b/src/s3-assets.php @@ -123,3 +123,66 @@ function s3_object_exists( $key ) { return $result; } + +/** + * Delete the rendered media library files from S3 for a given key. + * + * Takes a full s3:// path, extracts the path fragment from the key, and deletes all of the rendered media library files for that path fragment. + * The path fragment is everything after bucket name and 'original_media', like 'www.bu.edu/path/image.png'. + * Everything in the /rendered_media/ directory that matches 'www.bu.edu/path/image' will be deleted. + * + * @since 1.0.1 + * + * @param string $s3_key The key of the object to delete. + * + * @return bool True if successful, false if not. + */ +function delete_scaled_for_s3_key( $s3_key ) { + // Extract everything after the occurence of the S3_UPLOADS_BUCKET constant (which includes the 'original_media'). + $partial_path = substr( $s3_key, strpos( $s3_key, S3_UPLOADS_BUCKET ) + strlen( S3_UPLOADS_BUCKET ) ); + // Strip the leading slash if it exists. + $partial_path = ltrim( $partial_path, '/' ); + + // Calculate the path fragment to use as the matching key fragment for the s3 deleteMatchingObjects method. + $filename_root = pathinfo( $partial_path, PATHINFO_FILENAME ); + $path_fragment = dirname( $partial_path ) . '/' . $filename_root; + + // Delete all of the rendered media library files. + return delete_scaled_for_original( $path_fragment ); +} + +/** + * Delete the rendered media library files from S3 for a given original path fragment. + * + * Deletes all of the rendered media library files from S3 for a given original path fragment. + * The path fragment is everything after bucket name and 'original_media', which will be + * the web url without the https:// protocol, like 'www.bu.edu/path/image.png'. This will delete any + * sized files like 'www.bu.edu/path/image-300x300.png'. + * + * @since 1.0.1 + * + * @param string $path_fragment The path fragment to delete, like www.bu.edu/path/image.png. + * + * @return bool True if successful, false if not. + */ +function delete_scaled_for_original( $path_fragment ) { + // Create the S3 client, and get the bucket name and site key. + $s3_client = new_s3_client(); + $bucket = str_replace( '/original_media', '', S3_UPLOADS_BUCKET ); + + // Delete all of the rendered media library files. + try { + // Delete all of the rendered media library files. + $s3_client->deleteMatchingObjects( $bucket, "rendered_media/{$path_fragment}" ); + + } catch ( AwsException $e ) { + // Handle the exception. + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + error_log( $e->getMessage() ); + // Return false if unsuccessful. + return false; + } + + // Return true if successful. + return true; +}