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 Retina Support (via WP Retina 2x, aka Perfect Images, plugin compat) #656

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from

Conversation

iCaspar
Copy link
Contributor

@iCaspar iCaspar commented Feb 7, 2022

This is to restore basic compatibility with the Perfect Images (formerly "WP Retina 2x"), enabling Imagify to process the retina images generated by Perfect Images, and also re-optimizing images when the Perfect Images "Regenerate Thumbnails" feature is used.

NOTE: This PR provides compatibility with the FREE Perfect Images version. Neither the PRO version, nor the new use of Perfect Images Easy IO (EWWW) CDN service are included here.

Context:
Compatibility with WP Retina 2x was removed from Imagify in v 1.9 when the "new" architecture and webp was added. See #490. Since that time, WP Retina 2x became "Perfect Images" and implemented several new features, including a Optimization/CDN partnership with EWWW. On the code side, Perfect Images now uses the WP_Rest API instead of AJAX to initiate functionality.

Coding Considerations:
Previously, we attached hooks to WP Retina 2x AJAX hooks, passing our own nonces, etc. With the new Rest API approach, Perfect Images defines each endpoint with its own validation, per the WP endpoint definitions. We hook into the Perfect Images functionality after requests have already been checked and retina processing is already started.

The 3 PI processes we are interested in are: Retina Generation, Regenerate Thumbnails, and remove retina images.

  • For both generation processes, Imagify needs to replace the current full-size image (if it has been previously optimized) with the originally uploaded file we have moved to Imagify's backup. This is to insure that the new retinas or regenerated thumbnails will be created from the original image. Then, after the process is completed, we need to put the original uploaded version back into the backup location. This is done in the compatibility class's restore_originally_uploaded_image() and reset_optimized_full_size_image() methods. (NOTE: similar methods are also used for other compatibilities -- the Regenerate Thumbnails plugin compat, for example. These similar methods should ideally be extracted from the various compatibilities and moved into a "BackupHelper" class of some kind.)

  • For the retina generation, we need to track which sizes PI generates. Then, after all the retina sizes are generated, we need to find them and send them to Imagify background process for optimization. We also need to tell Imagify how to handle the @2x filenames in order to generate the related webp versions, when Imagify's "Generate Webp" option is enabled. Collecting the list of image sizes PI is generating retinas for is in add_retina_size(). Handling retina filenames and queuing them for optimization is done in optimize_retina_sizes(). Finally, after the retina files have been optimized, the optimized image data needs to be stored in the Imagify's data for the attachment/media. That is done in add_retina_sizes_meta() (hooked to the job process for each image as it is optimized from the background).

  • For the regenerate thumbnails, besides handling the backup and restore of the original, we just need to send the newly regenerated images to Imagify's queue for background processing after PI has done it's thing. That is handled by reoptimize_regenerated_images()

  • For the deletion of retina images, we need to do 2 things: (1) we need to delete the corresponding webp images that we created during the generation. (PI doesn't know anything about these images, so getting rid of them is on us!) We do that in remove_retina_webp_size(). Then (2) we need to remove the data from Imagify's data storage that we saved about the retina sizes -- and their related webp retina sizes. We do that in remove_imagify_retina_data().

Other "how it works" considerations:
PI has (Pro) functionality to upload/create a "full size" retina image. The thumbnail retina sizes, as opposed to the full-size, are generated, where possible, from re-sizing the full-size image. So ordinarily any thumbnail sizes that have a dimension more than half the full size will not be generated by PI, the adequate resolution to create the thumbnail not being present. By (pro) uploading an original retina full-size, PI is able to generate all the thumbnail sizes from that 2x original. Provided the pro version uses the same hook when generating retinas from the pro @2x original upload, Imagify should still optimize the retina sizes, as well as optimizing the @2x original upload as it would for any other uploaded image. None of this is verified, however, at this time. It would be great if we could reach out to the PI dev to confirm/add Pro compatibility for this and other pro features.

Also, it's probably out of the picture to be compatible with a direct competitor's Optmimzation/CDN service.

Long term, having native retina support in Imagify would be ideal.

This is just grabbing the WPR2X code that was deleted. It's no longer wired into our stuff, so it won't do anything yet.
PHPCS - update minimum PHP version to 7.0
Rename wp-retina-2x classes to PerfectImages and add namespace
Add PerfectImages namespace to composer PSR4
Use new class names in 3rd-party PerfectImages loader file
Add PerfectImages fileloader to 3rd-party required files
Stop using deprecated user_can() check - use WP context user check instead
Update asset script localization to use updated enqueue
The 3 assets here were included by the previous compatibility.
(Not sure yet if we will still need them, but adding them back because they are still trying to be loaded on library page -- until we can sort it out.)
The WP min version is set in the plugin.php to 5.3 -- phpcs should match.
Add first a new property `$retina_sizes[]` to store some size info when Perfect Images generates a new retina file.

Comment out (for now) the old ajax hooks, as Perfect Image no longer uses ajax.

Add 3 methods:
1. `add_retina_sizes(int $media_id, string $retina_file, string $size_name): void` hooked to Perfect Images `wr2x_retina_file_added `, will store the media ID, retina filepath, and size name in our new `$retina_sizes` property whenever Perfect Images generates a new retina file.

2. `optimize_retina_sizes(int $media_id): void` hooked to Perfect Images `wr2x_generate_retina`, will find all the retina sizes added at the end of a Perfect Images retina generation process for a media ID (as stored in our class property) and start a new Imagify Optimization process for the newly generated retina files.

3. `add_retina_sizes_meta(array $sizes): array` hooked to Imagify's `imagify_media_files` will filter the imagify Media size meta to include the retina sizes, and flag the new sizes as being allowed to optimize.
We add 4 methods here, 2 public, and 2 private helpers:
The 2 public methods are both hooked on Perfect Images `wr2x_retina_file_removed` action.
1. `remove_retina_webp_size(int $media_id, string $retina_file ): void` will check for any webp versions of the retina file that was deleted and remove it if it exists. Note we run this even if Imagify's webp option is not currently enabled, because it may have been enabled at the time the retina was generated, and disabled since. This uses the private helper method:
2. `get_retina_webp_filepath(string $attachment_file, string $retina_file): string`. This method handles the subtask of getting the correct webp file path based on the attachment's original filepath and the retina image's filename.
3. `remove_imagify_retina_data(int $media_id, string $retina_file): void` will purge optimization data imagify uses to track the status of retina images it has optimized. Two entries have to be checked and removed for each retina image size that's been deleted: the optimization data generated when we optimized the @2x image Perfect Images created, and the @2x@imagify-webp data for the webp version (if any) that Imagify created in connection with that retina size. This uses the private helper method:
4. `get_retina_imagify_data_size_names(array $sizes, string $origina_size_name): array`. This method compares the sizes information (formatted per `wp_get_attachment_metadata()` to the original filename of an attachment that included a retina image size, and gives us an array of all the related size names that might be present in Imagify's optimization data for the attachment.
Adds a method to restore a full-size image from the backed up original.
Adds a method to replace back the previously optimized full-size image after image regeneration.
Adds a method to send regenerated images to the queue for re-optimization.

We also hook the restore/replace before and after the generation of retina sizes to insure that the original is being used to generate the retinas as well.
These were added back in on 4ffeb21  just in case. It turns out we don't need them any more.
All Perfect Images functionality is handled in the PerfectImages class now. The PerfectImagesCore is dead code.
Also, cleans up some phpcs spacing and comment formatting.
@iCaspar iCaspar requested a review from a team February 7, 2022 16:37
We're not going to use the related data in this case. Just get the keys and eliminate the unused variable inside foreach.
The cyclomatic complexity (and readability) of `restore_originally_uploaded_image()` was 10. All of that is due to checking the various conditions before doing the restore.

This extracts all the checking to a helper method.
Refactors the process for getting the needed webp sizes for optimization to it's own method.
For new uploads, we're going optimize everything after all the other things run their stuff.
In this case we don't want to start a proces for this media now -- doing so will "Lock" the media when the full process tries to run in a few moments, and while retinas will be processed nothing else will be included!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants