Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support retry mechanism for generating sub-sizes in additional MIME types on constrained environments. #188

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
111 changes: 80 additions & 31 deletions modules/images/webp-uploads/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
* @return array An array with the updated structure for the metadata before is stored in the database.
*/
function webp_uploads_create_sources_property( array $metadata, $attachment_id ) {
// Make sure we have some sizes to work with, otherwise avoid any work.
if ( empty( $metadata['sizes'] ) || ! is_array( $metadata['sizes'] ) ) {
return $metadata;
}
// This should take place only on the JPEG image.
$valid_mime_transforms = webp_uploads_get_supported_image_mime_transforms();

Expand All @@ -42,60 +46,55 @@ function webp_uploads_create_sources_property( array $metadata, $attachment_id )
return $metadata;
}

$dirname = pathinfo( $file, PATHINFO_DIRNAME );
$image_sizes = array();
if ( isset( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ) {
$image_sizes = $metadata['sizes'];
}

foreach ( wp_get_registered_image_subsizes() as $size_name => $properties ) {
// This image size does not exist on the defined sizes.
if ( ! isset( $image_sizes[ $size_name ] ) || ! is_array( $image_sizes[ $size_name ] ) ) {
$dirname = pathinfo( $file, PATHINFO_DIRNAME );
foreach ( $metadata['sizes'] as $size_name => $properties ) {
// This image size is not defined or not an array.
if ( ! isset( $metadata['sizes'][ $size_name ] ) || ! is_array( $metadata['sizes'][ $size_name ] ) ) {
mitogh marked this conversation as resolved.
Show resolved Hide resolved
continue;
}

$current_size = $image_sizes[ $size_name ];
$sources = array();
if ( isset( $current_size['sources'] ) && is_array( $current_size['sources'] ) ) {
$sources = $current_size['sources'];
// Ensure a `sources` property exists on the existing size.
if ( empty( $metadata['sizes'][ $size_name ]['sources'] ) || ! is_array( $metadata['sizes'][ $size_name ]['sources'] ) ) {
$metadata['sizes'][ $size_name ]['sources'] = array();
}
mitogh marked this conversation as resolved.
Show resolved Hide resolved

// Try to find the mime type of the image size.
$current_mime = '';
if ( isset( $current_size['mime-type'] ) ) {
$current_mime = $current_size['mime-type'];
} elseif ( isset( $current_size['file'] ) ) {
$current_mime = wp_check_filetype( $current_size['file'] )['type'];
if ( isset( $properties['mime-type'] ) ) {
$current_mime = $properties['mime-type'];
} elseif ( isset( $properties['file'] ) ) {
$current_mime = wp_check_filetype( $properties['file'] )['type'];
}

// The mime type can't be determined.
if ( empty( $current_mime ) ) {
continue;
}
mitogh marked this conversation as resolved.
Show resolved Hide resolved

$sources[ $current_mime ] = array(
'file' => isset( $current_size['file'] ) ? $current_size['file'] : '',
'filesize' => 0,
);

// Set the filesize from the current mime image.
$file_location = path_join( $dirname, $sources[ $current_mime ]['file'] );
if ( file_exists( $file_location ) ) {
$sources[ $current_mime ]['filesize'] = filesize( $file_location );
if ( empty( $metadata['sizes'][ $size_name ]['sources'][ $current_mime ] ) ) {
$metadata['sizes'][ $size_name ]['sources'][ $current_mime ] = array(
'file' => isset( $properties['file'] ) ? $properties['file'] : '',
'filesize' => 0,
);
// Set the filesize from the current mime image.
$file_location = path_join( $dirname, $properties['file'] );
if ( file_exists( $file_location ) ) {
$metadata['sizes'][ $size_name ]['sources'][ $current_mime ]['filesize'] = filesize( $file_location );
}
wp_update_attachment_metadata( $attachment_id, $metadata );
}

$formats = isset( $valid_mime_transforms[ $current_mime ] ) ? $valid_mime_transforms[ $current_mime ] : array();

foreach ( $formats as $mime ) {
if ( empty( $sources[ $mime ] ) ) {
if ( empty( $metadata['sizes'][ $size_name ]['sources'][ $mime ] ) ) {
mitogh marked this conversation as resolved.
Show resolved Hide resolved
$source = webp_uploads_generate_image_size( $attachment_id, $size_name, $mime );
if ( is_array( $source ) ) {
$sources[ $mime ] = $source;
$metadata['sizes'][ $size_name ]['sources'][ $mime ] = $source;
wp_update_attachment_metadata( $attachment_id, $metadata );
mitogh marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

$current_size['sources'] = $sources;
$metadata['sizes'][ $size_name ] = $current_size;
mitogh marked this conversation as resolved.
Show resolved Hide resolved
}

return $metadata;
Expand Down Expand Up @@ -285,3 +284,53 @@ function webp_uploads_remove_sources_files( $attachment_id ) {
}

add_action( 'delete_attachment', 'webp_uploads_remove_sources_files', 10, 1 );

/**
* Filter on `wp_get_missing_image_subsizes` acting as an action for the logic of the plugin
* to determine if additional mime types still need to be created.
*
* @since n.e.x.t
*
* @see wp_get_missing_image_subsizes()
mitogh marked this conversation as resolved.
Show resolved Hide resolved
*
* @param array[] $missing_sizes Associative array of arrays of image sub-size.
* @param array $image_meta The metadata from the image.
* @param int $attachment_id The ID of the attachment.
*
* @return array[] $missing_sizes Associative array of arrays of image sub-size.
mitogh marked this conversation as resolved.
Show resolved Hide resolved
*/
function webp_uploads_wp_get_missing_image_subsizes( $missing_sizes, $image_meta, $attachment_id ) {
if ( _webp_uploads_is_valid_ajax_for_image_sizes() || _webp_uploads_is_valid_rest_for_post_process( file_get_contents( 'php://input' ) ) ) {
webp_uploads_create_sources_property( $image_meta, $attachment_id );
}
mitogh marked this conversation as resolved.
Show resolved Hide resolved

return $missing_sizes;
}

/**
* Determine if the current request is a valid request to process missing image sizes, executed
* via ajax.
*
* @return bool `true` if the current request is a valid ajax request to recreate missing image sizes.
*/
function _webp_uploads_is_valid_ajax_for_image_sizes() {
return wp_doing_ajax() && isset( $_REQUEST['action'], $_REQUEST['attachment_id'] ) && 'media-create-image-subsizes' === $_REQUEST['action'] && $_REQUEST['attachment_id'] > 0;
}

/**
* Determine if the provided REST request goes to the appropriate endpoint with the right action.
*
* @param string $body The body from the request.
*
* @return bool `true` if the current request is a valid REST request to process
*/
function _webp_uploads_is_valid_rest_for_post_process( $body = '' ) {
$matches = array();
$body = json_decode( $body, true );
$valid_rest = defined( 'REST_REQUEST' ) && REST_REQUEST;
$valid_route = $valid_rest && isset( $GLOBALS['wp'], $GLOBALS['wp']->query_vars['rest_route'] ) && preg_match( '/media\/\d+\/post-process/', $GLOBALS['wp']->query_vars['rest_route'], $matches );

return $valid_route && ! empty( $matches ) && ! empty( $body['action'] ) && 'create-image-subsizes' === $body['action'];
}
felixarntz marked this conversation as resolved.
Show resolved Hide resolved

add_filter( 'wp_get_missing_image_subsizes', 'webp_uploads_wp_get_missing_image_subsizes', 10, 3 );
mitogh marked this conversation as resolved.
Show resolved Hide resolved
180 changes: 180 additions & 0 deletions tests/modules/images/webp-uploads/webp-uploads-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -385,4 +385,184 @@ public function it_should_remove_the_webp_version_of_the_image_if_the_image_is_f
path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] )
);
}

/**
* Process a request where ajax is not defined
*
* @test
*/
public function it_should_process_a_request_where_ajax_is_not_defined() {
add_filter( 'wp_doing_ajax', '__return_false' );
$this->assertFalse( _webp_uploads_is_valid_ajax_for_image_sizes() );
}

/**
* Process an ajax request with no additional parameters
*
* @test
*/
public function it_should_process_an_ajax_request_with_no_additional_parameters() {
// Simulate ajax request.
add_filter( 'wp_doing_ajax', '__return_true' );
$this->assertFalse( _webp_uploads_is_valid_ajax_for_image_sizes() );
}

/**
* Process an ajax request with invalid action
*
* @test
*/
public function it_should_process_an_ajax_request_with_invalid_action() {
// Simulate ajax request.
add_filter( 'wp_doing_ajax', '__return_true' );
$_REQUEST['action'] = 'invalid-action';
$this->assertFalse( _webp_uploads_is_valid_ajax_for_image_sizes() );
}

/**
* Process an ajax request with only the attachment id is included in the request
*
* @test
*/
public function it_should_process_an_ajax_request_with_only_the_attachment_id_is_included_in_the_request() {
// Simulate ajax request.
add_filter( 'wp_doing_ajax', '__return_true' );
$_REQUEST['attachment_id'] = 1;
$this->assertFalse( _webp_uploads_is_valid_ajax_for_image_sizes() );
}

/**
* Process an ajax request with invalid attachment id
*
* @test
*/
public function it_should_process_an_ajax_request_with_invalid_attachment_id() {
// Simulate ajax request.
add_filter( 'wp_doing_ajax', '__return_true' );
$_REQUEST['action'] = 'media-create-image-subsizes';
$_REQUEST['attachment_id'] = 0;
$this->assertFalse( _webp_uploads_is_valid_ajax_for_image_sizes() );
}

/**
* Process an ajax request with valid request parameters
*
* @test
*/
public function it_should_process_an_ajax_request_with_valid_request_parameters() {
// Simulate ajax request.
add_filter( 'wp_doing_ajax', '__return_true' );
$_REQUEST['action'] = 'media-create-image-subsizes';
$_REQUEST['attachment_id'] = 1;
$this->assertTrue( _webp_uploads_is_valid_ajax_for_image_sizes() );
}

/**
* Process a rest request when rest is not defined
*
* @test
*/
public function it_should_process_a_rest_request_when_rest_is_not_defined() {
$this->assertFalse( _webp_uploads_is_valid_rest_for_post_process() );
}

/**
* Process a REST request when the global wp is not defined
*
* @test
*/
public function it_should_process_a_rest_request_when_the_global_wp_is_not_defined() {
define( 'REST_REQUEST', true );
rest_get_server()->dispatch( new WP_REST_Request() );
unset( $GLOBALS['wp'] );
$this->assertFalse( _webp_uploads_is_valid_rest_for_post_process() );
}

/**
* Process a REST request when the rest route query variable is not set
*
* @test
*/
public function it_should_process_a_rest_request_when_the_rest_route_query_variable_is_not_set() {
define( 'REST_REQUEST', true );
$GLOBALS['wp'] = new WP();

$this->assertFalse( _webp_uploads_is_valid_rest_for_post_process() );
}

/**
* Process a REST request when the query vars is set to a different route
*
* @dataProvider provider_rest_route
*
* @test
*/
public function it_should_process_a_rest_request_when_the_query_vars_is_set_to_a_different_route( $route ) {
define( 'REST_REQUEST', true );
$GLOBALS['wp'] = new WP();
$GLOBALS['wp']->query_vars['rest_route'] = $route;

$this->assertFalse( _webp_uploads_is_valid_rest_for_post_process() );
}

public function provider_rest_route() {
yield 'media route' => array( '/wp/v2/media' );
yield 'single attachment route' => array( '/wp/v2/media/1' );
yield 'edit attachment route' => array( '/wp/v2/media/1/edit' );
}

/**
* Process a REST request when dispatched to the right endpoint
*
* @test
*/
public function it_should_process_a_rest_request_when_dispatched_to_the_right_endpoint() {
define( 'REST_REQUEST', true );
$GLOBALS['wp'] = new WP();
$GLOBALS['wp']->query_vars['rest_route'] = '/wp/v2/media/1/post-process';

$this->assertFalse( _webp_uploads_is_valid_rest_for_post_process() );
}

/**
* Process a REST request when an action is provided
*
* @test
*/
public function it_should_process_a_rest_request_when_an_action_is_provided() {
define( 'REST_REQUEST', true );
$GLOBALS['wp'] = new WP();
$GLOBALS['wp']->query_vars['rest_route'] = '/wp/v2/media/1/post-process';
$body = wp_json_encode( array( 'action' => 'subsizes' ) );

$this->assertFalse( _webp_uploads_is_valid_rest_for_post_process( $body ) );
}

/**
* Process a REST request with invalid JSON payload
*
* @test
*/
public function it_should_process_a_rest_request_with_invalid_json_payload() {
define( 'REST_REQUEST', true );
$GLOBALS['wp'] = new WP();
$GLOBALS['wp']->query_vars['rest_route'] = '/wp/v2/media/1/post-process';
$body = ']]]]]][[][][]';

$this->assertFalse( _webp_uploads_is_valid_rest_for_post_process( $body ) );
}

/**
* Process a REST request when the right action is provided
*
* @test
*/
public function it_should_process_a_rest_request_when_the_right_action_is_provided() {
define( 'REST_REQUEST', true );
$GLOBALS['wp'] = new WP();
$GLOBALS['wp']->query_vars['rest_route'] = '/wp/v2/media/1/post-process';
$body = wp_json_encode( array( 'action' => 'create-image-subsizes' ) );

$this->assertTrue( _webp_uploads_is_valid_rest_for_post_process( $body ) );
}
}