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

Restore and backup image sizes alongside the sources properties #242

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a6f5e07
Add class to mimic image operations on the editor.
mitogh Mar 19, 2022
4820501
Restore and backup image sizes including `sources`.
mitogh Mar 19, 2022
ab7bf05
Merge branch 'trunk' into feature/228-restore-and-backup-image-sources
mitogh Mar 22, 2022
7e82041
Remove handling of edited images
mitogh Mar 23, 2022
a2b82e3
Update the `success` logic to handle additional cases.
mitogh Mar 23, 2022
ba0cbdf
Update logic to store and restore backup.
mitogh Mar 23, 2022
2dd29d8
Remove empty space
mitogh Mar 23, 2022
b16cee1
Update prefix on functions
mitogh Mar 23, 2022
8a8e967
Update prefix on functions
mitogh Mar 23, 2022
322dd3d
Update prefix on functions
mitogh Mar 23, 2022
226a5aa
Update hooks reference
mitogh Mar 23, 2022
8a809ed
Backup the data from previous metadata
mitogh Mar 23, 2022
6d134e5
Merge branch 'trunk' into feature/228-restore-and-backup-image-sources
mitogh Mar 24, 2022
76307e6
Merge branch 'trunk' into feature/228-restore-and-backup-image-sources
mitogh Mar 24, 2022
951c4ca
Remove the need of auxilary variable to hold the sources
mitogh Mar 26, 2022
61e25e8
Add `since` doc block
mitogh Mar 28, 2022
4c26f4f
Align parameters on the doc block
mitogh Mar 28, 2022
973a7b8
Removal of non required parameter
mitogh Mar 28, 2022
cb35f42
Align parameters to follow WordPress guidelines
mitogh Mar 28, 2022
7880f6b
Include the `@since` tags on missing functions
mitogh Mar 28, 2022
ca8d28e
Move logic of the hook into a variable
mitogh Mar 28, 2022
c381398
Add white spaces on tests
mitogh Mar 30, 2022
93e0d01
Add white spaces on tests
mitogh Mar 30, 2022
c165a11
Removal of `sanitize_text_field` due this value is not required.
mitogh Mar 30, 2022
7d96583
Run logic only if was executed when doing ajax.
mitogh Mar 30, 2022
7a113d3
Update code flow for more performant checks
mitogh Mar 30, 2022
e202548
Split conditionals into separate statements
mitogh Mar 30, 2022
188c7df
Removal of non requried conditional
mitogh Mar 30, 2022
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
186 changes: 186 additions & 0 deletions modules/images/webp-uploads/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -704,3 +704,189 @@ function webp_uploads_update_rest_attachment( WP_REST_Response $response, WP_Pos
return rest_ensure_response( $data );
}
add_filter( 'rest_prepare_attachment', 'webp_uploads_update_rest_attachment', 10, 3 );

/**
* Inspect if the current call to `wp_update_attachment_metadata()` was done from within the context
* of an edit to an attachment either restore or other type of edit, in that case we perform operations
* to save the sources properties, specifically for the `full` size image due this is a virtual image size.
*
* @since n.e.x.t
*
* @see wp_update_attachment_metadata()
*
* @param array $data The current metadata of the attachment.
* @param int $attachment_id The ID of the current attachment.
* @return array The updated metadata for the attachment to be stored in the meta table.
*/
function webp_uploads_update_attachment_metadata( $data, $attachment_id ) {
$trace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 10 );
eugene-manuilov marked this conversation as resolved.
Show resolved Hide resolved

foreach ( $trace as $element ) {
if ( ! isset( $element['function'] ) ) {
continue;
}

switch ( $element['function'] ) {
case 'wp_save_image':
// Right after an image has been edited.
return webp_uploads_backup_sources( $attachment_id, $data );
case 'wp_restore_image':
// When an image has been restored.
return webp_uploads_restore_image( $attachment_id, $data );
}
}

return $data;
}
add_filter( 'wp_update_attachment_metadata', 'webp_uploads_update_attachment_metadata', 10, 2 );

/**
* Before saving the metadata of the image store a backup values for the sources and file property
* those files would be used and deleted by the backup mechanism, right after the metadata has
* been updated. It removes the current sources property due once this function is executed
* right after an edit has taken place and the current sources are no longer accurate.
*
* @since n.e.x.t
*
* @param int $attachment_id The ID representing the attachment.
* @param array $data The current metadata of the attachment.
* @return array The updated metadata for the attachment.
*/
function webp_uploads_backup_sources( $attachment_id, $data ) {
$target = isset( $_REQUEST['target'] ) ? $_REQUEST['target'] : 'all';

// When an edit to an image is only applied to a thumbnail there's nothing we need to back up.
if ( 'thumbnail' === $target ) {
return $data;
}

$metadata = wp_get_attachment_metadata( $attachment_id );
// Nothing to back up.
if ( ! isset( $metadata['sources'] ) ) {
return $data;
}

$sources = $metadata['sources'];
// Prevent execution of the callbacks more than once if the callback was already executed.
$has_been_processed = false;

$hook = function ( $meta_id, $post_id, $meta_name ) use ( $attachment_id, $sources, &$has_been_processed ) {
// Make sure this hook is only executed in the same context for the provided $attachment_id.
if ( $post_id !== $attachment_id ) {
return;
}

// This logic should work only if we are looking at the meta key: `_wp_attachment_backup_sizes`.
if ( '_wp_attachment_backup_sizes' !== $meta_name ) {
return;
}

if ( $has_been_processed ) {
return;
}

$has_been_processed = true;
webp_uploads_backup_full_image_sources( $post_id, $sources );
};

add_action( 'added_post_meta', $hook, 10, 3 );
add_action( 'updated_post_meta', $hook, 10, 3 );

// Remove the current sources as at this point the current values are no longer accurate.
// TODO: Requires to be updated from https://github.com/WordPress/performance/issues/158.
unset( $data['sources'] );

return $data;
}

/**
* Stores the provided sources for the attachment ID in the `_wp_attachment_backup_sources` with
* the next available target if target is `null` no source would be stored.
*
mitogh marked this conversation as resolved.
Show resolved Hide resolved
* @since n.e.x.t
*
* @param int $attachment_id The ID of the attachment.
* @param array $sources An array with the full sources to be stored on the next available key.
*/
function webp_uploads_backup_full_image_sources( $attachment_id, $sources ) {
if ( empty( $sources ) ) {
return;
}

$target = webp_uploads_get_next_full_size_key_from_backup( $attachment_id );
if ( null === $target ) {
return;
}

$backup_sources = get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true );
$backup_sources = is_array( $backup_sources ) ? $backup_sources : array();
mitogh marked this conversation as resolved.
Show resolved Hide resolved
$backup_sources[ $target ] = $sources;
// Store the `sources` property into the full size if present.
update_post_meta( $attachment_id, '_wp_attachment_backup_sources', $backup_sources );
}

/**
* It finds the next available `full-{orig or hash}` key on the images if the name
* has not been used as part of the backup sources it would be used if no size is
* found or backup exists `null` would be returned instead.
*
mitogh marked this conversation as resolved.
Show resolved Hide resolved
* @since n.e.x.t
*
* @param int $attachment_id The ID of the attachment.
* @return null|string The next available full size name.
*/
function webp_uploads_get_next_full_size_key_from_backup( $attachment_id ) {
$backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true );
$backup_sizes = is_array( $backup_sizes ) ? $backup_sizes : array();

if ( empty( $backup_sizes ) ) {
return null;
}

$backup_sources = get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true );
$backup_sources = is_array( $backup_sources ) ? $backup_sources : array();
foreach ( array_keys( $backup_sizes ) as $size_name ) {
// If the target already has the sources attributes find the next one.
if ( isset( $backup_sources[ $size_name ] ) ) {
continue;
}

// We are only interested in the `full-` sizes.
if ( strpos( $size_name, 'full-' ) === false ) {
continue;
}

return $size_name;
}

return null;
}

/**
* Restore an image from the backup sizes, the current hook moves the `sources` from the `full-orig` key into
* the top level `sources` into the metadata, in order to ensure the restore process has a reference to the right
* images.
*
* @since n.e.x.t
*
* @param int $attachment_id The ID of the attachment.
* @param array $data The current metadata to be stored in the attachment.
* @return array The updated metadata of the attachment.
*/
function webp_uploads_restore_image( $attachment_id, $data ) {
$backup_sources = get_post_meta( $attachment_id, '_wp_attachment_backup_sources', true );

if ( ! is_array( $backup_sources ) ) {
return $data;
}

if ( ! isset( $backup_sources['full-orig'] ) || ! is_array( $backup_sources['full-orig'] ) ) {
return $data;
}

// TODO: Handle the case If `IMAGE_EDIT_OVERWRITE` is defined and is truthy remove any edited images if present before replacing the metadata.
// See: https://github.com/WordPress/performance/issues/158.
$data['sources'] = $backup_sources['full-orig'];

return $data;
}