Skip to content

Commit

Permalink
Merge pull request #8 from bu-ist/custom-image-editor
Browse files Browse the repository at this point in the history
Suppress generation of sized images with custom image editor
  • Loading branch information
jdub233 authored Aug 29, 2024
2 parents 9275180 + 48bbf13 commit 40f32c8
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 62 deletions.
6 changes: 3 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ BU Media S3 is a WordPress plugin designed to work with the [Human Made S3 Uploa

- **S3 Upload Directory**: The plugin filters the `upload_dir` hook and rewrites the values in a way that is compatible with the S3 Uploads plugin and the BU Protected S3 Object Lambda stack, redirecting all media uploads to the S3 bucket. It also changes the default upload location from `wp-content/uploads` to `files`, which is the convention for BU WordPress sites.

- **Prevent Image Scaling**: By default, WordPress generates a scaled derivative for every image size that is defined for the current site. Because the BU Protected S3 Object Lambda stack handles image resizing automatically, the WordPress media library only needs to handle the full sized original upload. This plugin tells WordPress not to generate any derivative sizes on upload. It preserves the attachment metadata for all of the defined sizes by generating it directly during the upload process.
- **Prevent Image Scaling**: By default, WordPress generates scaled derivatives for every image size defined for the current site. This plugin sets a custom filter flag during the upload process and uses it to interpose a custom Image Editor object. This approach leverages the technique used by the S3 Uploads plugin, ensuring that the core WordPress image size metadata generation remains intact while skipping the actual resizing. This allows the BU Protected S3 Object Lambda stack to handle image resizing automatically, and preserves the attachment metadata for all defined sizes.

- **Suppress Big Image Threshold Resizing**: WordPress 5.3 introduced a new feature that automatically resizes images for "web-ready" dimensions. This plugin suppresses this feature, allowing you to upload images at their full size.
- **Suppress Big Image Threshold Resizing**: WordPress 5.3 introduced a feature that automatically resizes images to "web-ready" dimensions. This plugin suppresses this feature, allowing you to upload images at their full size.

- **DynamoDB Crop Data**: This plugin provides a way to write crop data to DynamoDB. The BU Protected S3 Object Lambda stack can read this crop data and apply it when the image is requested. The site-manager plugin automatically writes crop data to DynamoDB when a sites are moved or cloned. I'm not sure about site creation though, that might be a todo.
- **DynamoDB Crop Data**: This plugin provides a way to write crop data to DynamoDB. The BU Protected S3 Object Lambda stack can read this crop data and apply it when the image is requested. The site-manager plugin automatically writes crop data to DynamoDB when sites are moved or cloned. Site creation might be a todo.

- **Handle Site File Deletion**: When a site is deleted, this plugin handles the deletion of files from S3 and the corresponding crop data in DynamoDB. It deletes both the original and derivative images from S3.

Expand Down
78 changes: 19 additions & 59 deletions src/filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,75 +52,35 @@ function s3_multisite_upload_dir( $upload ) {
}
add_filter( 'upload_dir', __NAMESPACE__ . '\s3_multisite_upload_dir' );

// 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 a custom filter to indicate that this is an upload request.
add_filter(
'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(
'image_resize_dimensions',
function( $orig_w, $orig_h ) {
return array();
},
10,
6
);

// Preemptively add the sizes to the attachment metadata.
add_filter(
'wp_generate_attachment_metadata',
__NAMESPACE__ . '\generate_metadata_sizes',
10,
2
);
// Set a custom flag to indicate that this is an upload request.
add_filter('is_upload_request', '__return_true');

// May still want to set a generate_metadata filter here to add the file size, which seems to be getting dropped.

// We need to pass along the original prefilter value unaltered; we're not actually changing it, just using it as a hook for the resize filter.
return $file;
}
);

/**
* Generate metadata for image sizes without creating the actual resized images.
*
* This function gets the registered image sizes and calculates the filename for each size
* using the WordPress convention for size annotation. It then adds this information to the attachment metadata.
*
* This function is designed to be added to the wp_handle_upload_prefilter in order to restore the metadata that
* would have been generated. Becuase the sizes are being suppressed on upload, the actual resized images
* are not being created, but we still need to add the metadata for the sizes to the attachment. This function
* should have the effect of pre-generating the metadata for the sizes that would have been created.
*
* @param array $metadata The attachment metadata.
* @param int $attachment_id The ID of the attachment.
*
* @return array The modified attachment metadata, with added sizes.
*/
function generate_metadata_sizes( $metadata, $attachment_id ) {
// Get the registered image sizes.
$sizes = wp_get_registered_image_subsizes();

// Get the pathinfo for the original file.
$pathinfo = pathinfo( $metadata['file'] );

// Get the mime type for the original file.
$mime_type = get_post_mime_type( $attachment_id );

// Recalculate the sizes that would have been generated and add them to the metadata.
foreach ( $sizes as $size => $size_data ) {
// Calcualte the new filename by adding the size to the original filename using the WordPress convention.
$new_filename = $pathinfo['filename'] . '-' . $size_data['width'] . 'x' . $size_data['height'] . '.' . $pathinfo['extension'];

// Add the new size to the metadata.
$metadata['sizes'][ $size ] = array(
'file' => $new_filename,
'width' => $size_data['width'],
'height' => $size_data['height'],
'mime-type' => $mime_type,
);
// Add a custom filter to suppress resized image generation during the upload process.
add_filter( 'wp_image_editors', function( $editors ) {
// Check if the custom flag indicating an upload request is set.
if ( apply_filters('is_upload_request', false) ) {
// This is an upload request, so we should use the custom image editor that skips saving the image to S3.
// Include the custom image editor that skips saving the image to S3.
require_once dirname( __FILE__ ) . '/s3uploads-plugin/class-skip-save-image-editor.php';

// Add the custom image editor to the list of available editors as the first editor, so that's what WordPress uses.
array_unshift( $editors, 'BU\Plugins\MediaS3\Skip_Save_Image_Editor' );
}
return $metadata;
}

// Return the list of editors.
return $editors;
} );

// Disable the big image threshold, we don't want WordPress to do any resizing at all.
add_filter( 'big_image_size_threshold', '__return_false' );
Expand Down
52 changes: 52 additions & 0 deletions src/s3uploads-plugin/class-skip-save-image-editor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php
/**
* Image Editor subclass of the S3_Uploads plugin Image_Editor_Imagick class.
*
* This custom image editor is used to skip saving the image to S3. It works by subclassing the
* S3_Uploads plugin's Image_Editor_Imagick class and overriding the _save method to skip saving the image to S3.
*
* @package BU MediaS3
*/

namespace BU\Plugins\MediaS3;

use S3_Uploads\Image_Editor_Imagick;
use WP_Error;

class Skip_Save_Image_Editor extends Image_Editor_Imagick {

/**
* Override the _save method to skip saving the image to S3.
*
* @param Imagick $image
* @param ?string $filename
* @param ?string $mime_type
* @return WP_Error|array{path: string, file: string, width: int, height: int, mime-type: string}
*/
protected function _save( $image, $filename = null, $mime_type = null ) {
/**
* @var ?string $filename
* @var string $extension
* @var string $mime_type
*/
list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );

// Crucially, we need to determine the filename so we can return it correctly in the response.
if ( ! $filename ) {
$filename = parent::generate_filename( null, null, $extension );
}

// We don't want to save the image to S3, so we don't call the parent::_save method.

// Return the metadata for the image.
$response = [
'path' => $filename,
'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
'width' => $this->size['width'] ?? 0,
'height' => $this->size['height'] ?? 0,
'mime-type' => $mime_type,
];

return $response;
}
}

0 comments on commit 40f32c8

Please sign in to comment.