first
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1 @@
|
||||
Deny from all
|
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 947 B |
After Width: | Height: | Size: 10 KiB |
618
wp-content/plugins/webp-converter-for-media/changelog.txt
Normal file
@ -0,0 +1,618 @@
|
||||
== Changelog ==
|
||||
|
||||
= 5.11.5 (2023-12-07) =
|
||||
* `[Added]` Compatibility with PHP 8.3
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
|
||||
= 5.11.4 (2023-11-13) =
|
||||
* `[Fixed]` Removing WebP files converted for AVIF format from /uploads-webpc directory after uninstalling plugin
|
||||
* `[Fixed]` Statistics of conversion progress in WP-CLI
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
* `[Changed]` Error message for rewrites_cached error in server configuration
|
||||
|
||||
= 5.11.3 (2023-11-09) =
|
||||
* `[Fixed]` Automatically conversion of images from /uploads directory, but not from Media Library
|
||||
* `[Added]` Support for WordPress 6.4
|
||||
|
||||
= 5.11.2 (2023-10-16) =
|
||||
* `[Added]` Button to expand/collapse list of directories to optimize in Bulk Optimization of Images section
|
||||
* `[Added]` Notification about plugin requirements in WordPress Playground environment
|
||||
|
||||
= 5.11.1 (2023-10-02) =
|
||||
* `[Fixed]` Duplicated rewrite rules for .jpeg files
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
|
||||
= 5.11.0 (2023-09-27) =
|
||||
* `[Added]` Ability to manually optimize selected images in Media Library
|
||||
* `[Added]` Ability to manually undo optimization of selected images in Media Library
|
||||
|
||||
= 5.10.1 (2023-09-10) =
|
||||
* `[Fixed]` Detection of bypassing_apache error in server configuration
|
||||
|
||||
= 5.10.0 (2023-09-09) =
|
||||
* `[Fixed]` Removing files from /uploads-webpc directory after uninstalling plugin
|
||||
* `[Changed]` Error message for rewrites_not_working error in server configuration
|
||||
* `[Changed]` Verification of correct operation of rewrites from .htaccess file
|
||||
* `[Added]` Changes to improve performance of plugin
|
||||
* `[Added]` Changes to improve loading time of plugin settings
|
||||
|
||||
= 5.9.6 (2023-08-25) =
|
||||
* `[Changed]` Maximum weight of supported files from 25 MB to 32 MB
|
||||
* `[Added]` Displaying image in its original format by adding "?original" suffix to image URL
|
||||
* `[Added]` Removing converted WebP files after deleting WebP format from "Supported output formats" list
|
||||
|
||||
= 5.9.5 (2023-07-31) =
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
* `[Added]` Clearing Cloudflare cache when activating and deactivating plugin
|
||||
|
||||
= 5.9.4 (2023-07-12) =
|
||||
* `[Added]` Support for Bunny CDN and BunnyCDN plugin
|
||||
* `[Added]` Support for WordPress 6.3
|
||||
|
||||
= 5.9.3 (2023-07-04) =
|
||||
* `[Fixed]` Corrupted filenames in image URLs using Bypassing Nginx loading mode
|
||||
|
||||
= 5.9.2 (2023-07-03) =
|
||||
* `[Fixed]` Suffix "-optimized" in image URLs using Bypassing Nginx loading mode
|
||||
* `[Fixed]` Right-to-left styling of plugin settings page
|
||||
* `[Added]` Error message when using WP-CLI when Conversion method is not available
|
||||
|
||||
= 5.9.1 (2023-05-24) =
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
|
||||
= 5.9.0 (2023-05-20) =
|
||||
* `[Fixed]` Conversion of images in WP-CLI using force flag
|
||||
* `[Changed]` Error messages on plugin settings page
|
||||
* `[Added]` Skipping backup files generated by other image optimization plugins
|
||||
|
||||
= 5.8.6 (2023-04-29) =
|
||||
* `[Fixed]` Generating XML files using Pass Thru loading mode
|
||||
|
||||
= 5.8.5 (2023-04-07) =
|
||||
* `[Added]` Skipping .jpg.webp, .jpeg.webp, .png.webp, and .gif.webp files generated by other image optimization plugins
|
||||
* `[Added]` New parameter (to force reconversion) for webpc_convert_paths hook
|
||||
* `[Added]` New parameter (to force reconversion) for webpc_convert_attachment hook
|
||||
|
||||
= 5.8.4 (2023-03-20) =
|
||||
* `[Fixed]` Generating rules in .htaccess file when real DOCUMENT_ROOT path is different from WordPress root directory
|
||||
|
||||
= 5.8.3 (2023-03-09) =
|
||||
* `[Fixed]` Converting images without EXIF data support using GD method
|
||||
* `[Added]` Exception for blocked REST API endpoints by JWT Auth plugin
|
||||
|
||||
= 5.8.2 (2023-03-02) =
|
||||
* `[Changed]` Written content in plugin settings
|
||||
* `[Added]` Support for WordPress 6.2
|
||||
|
||||
= 5.8.1 (2023-02-27) =
|
||||
* `[Fixed]` Converting using GD method without `exif_read_data` function available
|
||||
* `[Fixed]` Redirects of images using Bypassing Nginx loading mode
|
||||
|
||||
= 5.8.0 (2023-02-20) =
|
||||
* `[Fixed]` Generating output paths in WordPress Multisite Network
|
||||
* `[Fixed]` Replacing URLs in Pass Thru loading mode for themes without get_header hook
|
||||
* `[Changed]` Automatic clearing of cache generated by LiteSpeed Cache plugin
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
* `[Added]` Image loading mode: `Bypassing Nginx` (use when you have a problem with the bypassing_apache error)
|
||||
* `[Added]` Support for defined /uploads directory using UPLOADS constant
|
||||
* `[Added]` Support for Orientation parameter in EXIF data
|
||||
* `[Added]` Automatic clearing of cache generated by Breeze plugin
|
||||
* `[Added]` Automatic clearing of cache generated by Cache Enabler plugin
|
||||
* `[Added]` Automatic clearing of cache generated by Hummingbird plugin
|
||||
* `[Added]` Automatic clearing of cache generated by SiteGround Optimizer plugin
|
||||
* `[Added]` Automatic clearing of cache generated by W3 Total Cache plugin
|
||||
* `[Added]` Automatic clearing of cache generated by WP Fastest Cache plugin
|
||||
* `[Added]` Automatic clearing of cache generated by WP-Optimize plugin
|
||||
* `[Added]` Automatic clearing of cache generated by WP Super Cache plugin
|
||||
* `[Added]` Support for defined value of WP_HOME for testing rewrites
|
||||
|
||||
= 5.7.1 (2023-02-04) =
|
||||
* `[Changed]` Verification of rewrites_not_executed server configuration error
|
||||
|
||||
= 5.7.0 (2023-02-01) =
|
||||
* `[Fixed]` Compatibility with PHP 8.2
|
||||
* `[Changed]` Layout of settings in Advanced Settings tab
|
||||
* `[Added]` Error detection of blocked rewrites for images from /uploads directory
|
||||
* `[Added]` Exception for blocked REST API endpoints by external plugins
|
||||
|
||||
= 5.6.4 (2023-01-21) =
|
||||
* `[Fixed]` Error detection of cached redirects of images to WebP files
|
||||
|
||||
= 5.6.3 (2023-01-10) =
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
* `[Changed]` Error message for rewrites_cached error in server configuration
|
||||
* `[Changed]` Error message for rewrites_not_working error in server configuration
|
||||
* `[Changed]` Error message for token_invalid error in server configuration
|
||||
* `[Changed]` Error message for token_limit error in server configuration
|
||||
|
||||
= 5.6.2 (2023-01-05) =
|
||||
* `[Changed]` List of extra features in Advanced settings
|
||||
* `[Changed]` Notification about Cloudflare cache
|
||||
* `[Added]` Ability to disable exclusion of converted images in backups generated by other plugins
|
||||
* `[Added]` Error message for incorrect configuration of Cloudflare CDN cache
|
||||
* `[Added]` Warning about unconverted images when deactivating plugin
|
||||
* `[Added]` Warning when forcing all images to be reconverted
|
||||
|
||||
= 5.6.1 (2022-12-28) =
|
||||
* `[Fixed]` No support for .jpeg files in default plugin settings
|
||||
* `[Added]` Ability to auto clear Cloudflare CDN cache (beta version)
|
||||
|
||||
= 5.6.0 (2022-12-21) =
|
||||
* `[Added]` Ability to convert images from /cache directory
|
||||
* `[Added]` Automatic cleaning of LiteSpeed Cache
|
||||
* `[Added]` Exception for blocked REST API endpoints by Disable REST API plugin
|
||||
* `[Added]` Exception for blocked REST API endpoints by Disable WP REST API plugin
|
||||
* `[Added]` Exception for blocked REST API endpoints by WordPress REST API Authentication plugin
|
||||
|
||||
= 5.5.1 (2022-11-29) =
|
||||
* `[Fixed]` Cache for REST API responses using LiteSpeed Cache plugin
|
||||
* `[Changed]` Authorization method for REST API endpoints
|
||||
|
||||
= 5.5.0 (2022-11-22) =
|
||||
* `[Removed]` Error logging to debug.log file
|
||||
* `[Changed]` Content of welcome notice after plugin installation
|
||||
* `[Added]` Exclusion of directories from converting images in plugin settings
|
||||
* `[Added]` Instruction in "Conversion strategy" field in plugin settings
|
||||
* `[Added]` Possibility to disable rewrite inheritance in .htaccess files in plugin settings
|
||||
|
||||
= 5.4.2 (2022-11-14) =
|
||||
* `[Added]` Possibility to re-convert files that have not been successfully converted (service mode must be active)
|
||||
|
||||
= 5.4.1 (2022-11-13) =
|
||||
* `[Fixed]` Forcing images larger than original to be re-converted
|
||||
|
||||
= 5.4.0 (2022-11-12) =
|
||||
* `[Fixed]` Converting using a Remote server when response is empty
|
||||
* `[Changed]` URLs for REST API endpoints
|
||||
* `[Added]` Conversion of missing images after selecting "Automatic removal of files in output formats larger than original" option
|
||||
* `[Added]` Exclusion of files conversion from /wpmc-trash directory generated by Media Cleaner plugin
|
||||
* `[Added]` Exclusion of files conversion from /__MACOSX directory
|
||||
|
||||
= 5.3.1 (2022-10-12) =
|
||||
* `[Added]` Error handling while connecting to REST API
|
||||
|
||||
= 5.3.0 (2022-10-11) =
|
||||
* `[Removed]` Action `webpc_convert_dir`
|
||||
* `[Removed]` Filter `webpc_dir_files`
|
||||
* `[Fixed]` Support for WordPress older than version 5.3
|
||||
* `[Added]` List of files for optimization in tree form
|
||||
* `[Added]` Handling unknown errors while converting with Imagick method
|
||||
|
||||
= 5.2.4 (2022-09-27) =
|
||||
* `[Changed]` Messages displayed during bulk optimization of images
|
||||
* `[Added]` Debug information about registered image sub-sizes
|
||||
|
||||
= 5.2.3 (2022-09-23) =
|
||||
* `[Added]` Debug information about disabled functions
|
||||
|
||||
= 5.2.2 (2022-09-15) =
|
||||
* `[Fixed]` Closing of admin notice
|
||||
|
||||
= 5.2.1 (2022-09-14) =
|
||||
* `[Fixed]` Deleting converted files during plugin uninstallation
|
||||
|
||||
= 5.2.0 (2022-09-10) =
|
||||
* `[Fixed]` Re-conversion of images modified after previous conversion
|
||||
* `[Added]` Optimization statistics in Media Upload Popup
|
||||
* `[Added]` Filter `webpc_source_directories` to add support for custom directories
|
||||
|
||||
= 5.1.0 (2022-08-28) =
|
||||
* `[Fixed]` Exclusion of converted files from backup generated by UpdraftPlus plugin
|
||||
* `[Added]` Optimization statistics in Media Library
|
||||
|
||||
= 5.0.1 (2022-08-26) =
|
||||
* `[Removed]` Filter `webpc_uploads_prefix`
|
||||
* `[Fixed]` Calculating number of images to convert in WP-CLI
|
||||
* `[Fixed]` Converting files that contain plus sign in filename
|
||||
* `[Changed]` Notification asking to clear cache for LiteSpeed
|
||||
* `[Added]` Exclusion of converted files from backup generated by All-in-One WP Migration plugin
|
||||
* `[Added]` Exclusion of converted files from backup generated by UpdraftPlus plugin
|
||||
* `[Added]` Exclusion of converted files from backup generated by BackWPup plugin
|
||||
|
||||
= 5.0.0 (2022-08-20) =
|
||||
* `[Changed]` Information about plugin operation and technical support on plugin settings page
|
||||
* `[Changed]` Labels of options in plugin settings
|
||||
* `[Changed]` Option field for configuration of quality level
|
||||
* `[Changed]` Widget for conversion process
|
||||
* `[Added]` Filter `webpc_htaccess_cache_control_private` to disable Cache-Control as private
|
||||
* `[Added]` Statistics on number of converted images
|
||||
* `[Added]` Tabs for plugin settings
|
||||
|
||||
= 4.5.1 (2022-07-22) =
|
||||
* `[Removed]` Notification asking to clear cache for Cloudways
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
|
||||
= 4.5.0 (2022-07-15) =
|
||||
* `[Changed]` Plugin name from "WebP Converter to Media" to "Converter for Media"
|
||||
* `[Removed]` "Browser Caching for files in output formats" option in plugin settings (will be active always)
|
||||
* `[Removed]` "Force redirections to output formats for all domains" option in plugin settings (will be active always)
|
||||
* `[Fixed]` Error handling about unreadable source or output path
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
* `[Added]` Limit of 3 attempts to image regeneration to avoid infinity loops
|
||||
* `[Added]` Debug information about saved plugin data
|
||||
* `[Added]` Notification asking to clear cache for Cloudways
|
||||
* `[Added]` Action `webpc_after_conversion` to operation on output path after image conversion
|
||||
* `[Added]` Filter `webpc_htaccess_mod_rewrite_inherit` to disable mod_rewrite inheritance
|
||||
* `[Added]` Filter `webpc_htaccess_mod_rewrite_referer` to disable redirections to output formats for other domains
|
||||
|
||||
= 4.4.1 (2022-06-30) =
|
||||
* `[Added]` Inheritance of mod_rewrite rules from parent directories
|
||||
* `[Added]` Support for custom /wp-content directory name
|
||||
|
||||
= 4.4.0 (2022-06-19) =
|
||||
* `[Changed]` Calculation of number of images to be converted
|
||||
* `[Added]` Resizing of images before conversion
|
||||
* `[Added]` Notification asking to clear cache for LiteSpeed
|
||||
|
||||
= 4.3.6 (2022-06-03) =
|
||||
* `[Fixed]` Notification about Cloudflare cache
|
||||
|
||||
= 4.3.5 (2022-06-02) =
|
||||
* `[Changed]` Generating rewrite rules for via .htaccess loading mode (applies to rewrites_not_working server configuration error)
|
||||
|
||||
= 4.3.4 (2022-05-10) =
|
||||
* `[Changed]` Generating rewrite rules for via .htaccess loading mode (applies to rewrites_not_working server configuration error)
|
||||
* `[Changed]` Message about rewrites_not_executed server configuration error
|
||||
|
||||
= 4.3.3 (2022-05-08) =
|
||||
* `[Fixed]` Calculation of number of images to be converted
|
||||
* `[Added]` Filter `webpc_option_quality_levels` to change conversion quality levels
|
||||
|
||||
= 4.3.2 (2022-04-20) =
|
||||
* `[Added]` Conversion skip for animated GIF images
|
||||
* `[Added]` New information about status of image conversion
|
||||
* `[Added]` Ability to convert only to AVIF format
|
||||
|
||||
= 4.3.1 (2022-04-05) =
|
||||
* `[Fixed]` Generating rewrite rules for via .htaccess loading mode
|
||||
|
||||
= 4.3.0 (2022-04-01) =
|
||||
* `[Fixed]` Authorization of access to REST API
|
||||
* `[Changed]` Description of plugin operation in plugin settings
|
||||
* `[Added]` Command "wp webp-converter calculate" for WP-CLI
|
||||
* `[Added]` Command "wp webp-converter regenerate" for WP-CLI
|
||||
* `[Added]` Converting .webp files to AVIF format
|
||||
* `[Added]` Support for environments where DOCUMENT_ROOT is different from WordPress installation directory
|
||||
|
||||
= 4.2.4 (2022-03-01) =
|
||||
* `[Fixed]` Generating paths for via .htaccess loading mode
|
||||
|
||||
= 4.2.3 (2022-02-27) =
|
||||
* `[Fixed]` Closing of admin notice
|
||||
|
||||
= 4.2.2 (2022-02-21) =
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
* `[Added]` Automatic continuation of conversion process after connection lost
|
||||
* `[Added]` ICC/ICM profile support for conversion using Imagick
|
||||
* `[Added]` Notification asking to clear cache for Cloudflare
|
||||
|
||||
= 4.2.1 (2022-02-17) =
|
||||
* `[Fixed]` Cache settings for .htaccess rewrites
|
||||
* `[Added]` Limit for error messages displayed while converting
|
||||
* `[Added]` Support for plain permalink structure
|
||||
|
||||
= 4.2.0 (2022-02-13) =
|
||||
* `[Removed]` Action `webpc_regenerate_all`
|
||||
* `[Fixed]` Issue with plugin settings after changing Gd or Imagick library configuration
|
||||
* `[Fixed]` Server configuration error detection when cURL is disabled
|
||||
* `[Fixed]` URL for the server configuration tab
|
||||
* `[Changed]` Automatically conversion of images after upload
|
||||
* `[Changed]` Automatically conversion of images from outside the Media Library
|
||||
* `[Changed]` Message for conversion error when converted file is larger than original
|
||||
* `[Added]` List of allowed file extensions for cache rules in .htaccess file
|
||||
* `[Added]` Status of automatic image conversion on the top bar in the WordPress Dashboard
|
||||
* `[Added]` Debug information about plugin settings
|
||||
|
||||
= 4.1.2 (2022-01-29) =
|
||||
* `[Fixed]` Validation of authorization token when images regeneration
|
||||
* `[Fixed]` Validation of loaded images using Pass Thru method
|
||||
|
||||
= 4.1.1 (2022-01-19) =
|
||||
* `[Fixed]` Loading images with special character in filename using Pass Thru method
|
||||
* `[Changed]` Error messages on plugin settings page
|
||||
* `[Added]` Value hiding for access token in plugin settings
|
||||
|
||||
= 4.1.0 (2022-01-12) =
|
||||
* `[Fixed]` Auto-generation of rewrite rules for Multisite Network
|
||||
* `[Fixed]` Detection of server configuration error related to non-working rewrites
|
||||
* `[Fixed]` URL validation for Pass Thru loading mode
|
||||
* `[Fixed]` Verification of supported formats by Imagick
|
||||
* `[Changed]` Error messages on plugin settings page
|
||||
* `[Changed]` Styles for plugin settings page
|
||||
* `[Added]` Debug information about PHP configuration
|
||||
|
||||
= 4.0.5 (2022-01-04) =
|
||||
* `[Changed]` Modal when deactivating plugin
|
||||
|
||||
= 4.0.4 (2021-12-30) =
|
||||
* `[Changed]` Message after successfully completing conversion of images
|
||||
|
||||
= 4.0.3 (2021-12-20) =
|
||||
* `[Fixed]` Auto-conversion images with unsupported extensions when uploading files
|
||||
* `[Fixed]` Generating directory paths when ABSPATH constant is invalid
|
||||
* `[Added]` URL validation for Pass Thru loading mode
|
||||
|
||||
= 4.0.2 (2021-12-17) =
|
||||
* `[Fixed]` Fetching large list of files to conversion
|
||||
* `[Fixed]` Rewrites caching for some servers
|
||||
* `[Changed]` Connection when converting using remote server
|
||||
|
||||
= 4.0.1 (2021-12-10) =
|
||||
* `[Added]` Informational banners on plugin settings page
|
||||
|
||||
= 4.0.0 (2021-12-04) =
|
||||
* `[Added]` Converting images using remote server
|
||||
* `[Added]` Converting images to AVIF format
|
||||
* `[Added]` Error detection for invalid permalinks structure
|
||||
|
||||
= 3.2.4 (2021-11-21) =
|
||||
* `[Fixed]` Automatic image conversion when /uploads directory is not organized into monthly and yearly folders
|
||||
* `[Fixed]` Displaying errors when converting images
|
||||
|
||||
= 3.2.3 (2021-10-15) =
|
||||
* `[Added]` Exclusion of re-converting images that caused regeneration error
|
||||
|
||||
= 3.2.2 (2021-09-26) =
|
||||
* `[Changed]` Option "Force redirections to WebP for all domains" as default
|
||||
* `[Added]` Debug information for regeneration error
|
||||
|
||||
= 3.2.1 (2021-09-12) =
|
||||
* `[Fixed]` Skipping converted images when converting all images
|
||||
|
||||
= 3.2.0 (2021-09-12) =
|
||||
* `[Removed]` Filter `webpc_files_paths`
|
||||
* `[Removed]` Filter `webpc_dir_excluded`
|
||||
* `[Changed]` Error handling when converting images
|
||||
* `[Added]` Filter `webpc_supported_source_file` to exclude paths to files from converting
|
||||
* `[Added]` Filter `webpc_supported_source_directory` to exclude paths to directories from converting
|
||||
* `[Added]` Modifications to appearance of plugin settings page
|
||||
|
||||
= 3.1.1 (2021-09-08) =
|
||||
* `[Fixed]` Generating .htaccess files for multisite websites
|
||||
* `[Changed]` Fields on plugin settings page
|
||||
* `[Changed]` Server configuration error detection
|
||||
|
||||
= 3.1.0 (2021-09-02) =
|
||||
* `[Fixed]` Default state of notice displaying
|
||||
* `[Added]` Error detection for server that does not supports using .htaccess files from custom locations
|
||||
|
||||
= 3.0.8 (2021-07-25) =
|
||||
* `[Fixed]` Compatibility of plugin settings for multisite websites
|
||||
|
||||
= 3.0.7 (2021-07-19) =
|
||||
* `[Added]` Changes to improve performance of plugin
|
||||
|
||||
= 3.0.6 (2021-06-20) =
|
||||
* `[Fixed]` Generating URLs for images using Pass Thru method
|
||||
* `[Added]` Filter `webpc_passthru_url_nocache` to remove nocache param from URL using Pass Thru method
|
||||
|
||||
= 3.0.5 (2021-06-09) =
|
||||
* `[Fixed]` Handling of filters for changing directory paths
|
||||
* `[Fixed]` Converting using Imagick method for 100% image quality
|
||||
* `[Changed]` Default conversion method from Gd to Imagick
|
||||
|
||||
= 3.0.4 (2021-05-28) =
|
||||
* `[Fixed]` Caching rewrites on CDN and Proxy servers
|
||||
|
||||
= 3.0.3 (2021-05-22) =
|
||||
* `[Fixed]` Rewrite rules for via .htaccess loading mode
|
||||
|
||||
= 3.0.2 (2021-05-22) =
|
||||
* `[Fixed]` Rewrite rules for servers where DOCUMENT_ROOT is different from ABSPATH
|
||||
|
||||
= 3.0.1 (2021-05-09) =
|
||||
* `[Fixed]` Filters using for change server paths
|
||||
* `[Added]` Excluding directories when uploading images to Media Library
|
||||
|
||||
= 3.0.0 (2021-05-02) =
|
||||
* `[Removed]` Filter `webpc_get_values`
|
||||
* `[Removed]` Filter `webpc_get_options`
|
||||
* `[Removed]` Filter `webpc_get_methods`
|
||||
* `[Changed]` Error messages on plugin settings page
|
||||
* `[Added]` Conversion of images to multiple output formats
|
||||
* `[Added]` Compatibility with NextGEN Gallery plugin
|
||||
* `[Added]` Data displayed on "Server configuration" tab on plugin settings page
|
||||
* `[Added]` Changes to improve performance of plugin
|
||||
* `[Added]` Changes to improve security of plugin
|
||||
|
||||
= 2.4.0 (2021-02-28) =
|
||||
* `[Fixed]` Error detection of redirects without .png as supported file extension
|
||||
* `[Fixed]` Pass Thru loading mode for servers not supporting `getallheaders()` function
|
||||
* `[Changed]` Level of error for cached redirects of images to WebP files
|
||||
* `[Added]` Skip re-converting images that were larger than original after converting to WebP
|
||||
|
||||
= 2.3.0 (2021-01-31) =
|
||||
* `[Fixed]` Encoding paths to files
|
||||
* `[Fixed]` Retaining PNG transparency using Gd method
|
||||
* `[Added]` Cron to convert images uploaded to Media Library
|
||||
|
||||
= 2.2.0 (2021-01-13) =
|
||||
* `[Added]` Support for WordPress Multisite
|
||||
|
||||
= 2.1.3 (2020-12-28) =
|
||||
* `[Fixed]` Regex for Pass Thru loading mode
|
||||
|
||||
= 2.1.2 (2020-12-27) =
|
||||
* `[Fixed]` Converting images using Imagick method
|
||||
|
||||
= 2.1.1 (2020-12-21) =
|
||||
* `[Fixed]` Modal when deactivating plugin
|
||||
|
||||
= 2.1.0 (2020-12-21) =
|
||||
* `[Changed]` Structure of conversion methods
|
||||
* `[Changed]` Structure of error detection methods
|
||||
|
||||
= 2.0.1 (2020-12-16) =
|
||||
* `[Fixed]` Actions initiated after plugin update
|
||||
|
||||
= 2.0.0 (2020-12-16) =
|
||||
* `[Removed]` Filter `webpc_uploads_path`
|
||||
* `[Removed]` Filter `webpc_uploads_webp`
|
||||
* `[Removed]` Filter `webpc_uploads_dir`
|
||||
* `[Removed]` Filter `webpc_uploads_root`
|
||||
* `[Changed]` Error messages in administration panel
|
||||
* `[Added]` Image loading mode: `Pass Thru` (without rewrites in .htacces files or Nginx configuration)
|
||||
* `[Added]` Filter `webpc_dir_name` to change default directory paths
|
||||
* `[Added]` Filter `webpc_site_root` to change path for root installation directory of WordPress
|
||||
* `[Added]` Filter `webpc_site_url` to change Site URL of WordPress
|
||||
|
||||
= 1.6.0 (2020-12-12) =
|
||||
* `[Added]` Escaping functions for translated phrases
|
||||
* `[Added]` Error codes in error messages on plugin settings page
|
||||
* `[Added]` Modal when deactivating plugin
|
||||
|
||||
= 1.5.1 (2020-11-02) =
|
||||
* `[Changed]` Error messages related to non-working redirects from .htaccess file
|
||||
|
||||
= 1.5.0 (2020-10-28) =
|
||||
* `[Added]` Filter `webpc_files_paths` to modify paths of images to be converted
|
||||
* `[Added]` Filter `webpc_convert_error` to management of errors content displayed during conversion
|
||||
* `[Added]` Filter `webpc_convert_errors` to management of errors displayed during conversion
|
||||
|
||||
= 1.4.6 (2020-10-23) =
|
||||
* `[Fixed]` Error detection of non-working redirects without .png as supported file extension
|
||||
|
||||
= 1.4.5 (2020-10-19) =
|
||||
* `[Fixed]` Content for translations
|
||||
|
||||
= 1.4.4 (2020-10-18) =
|
||||
* `[Changed]` Information after conversion process is completed
|
||||
|
||||
= 1.4.3 (2020-09-30) =
|
||||
* `[Changed]` Directory for error detection of non-working redirects of images to WebP files
|
||||
* `[Changed]` Button for `Server configuration` tab
|
||||
* `[Added]` Information about error detection in `Server configuration` tab
|
||||
|
||||
= 1.4.2 (2020-08-24) =
|
||||
* `[Fixed]` Cache-Control for redirects of images to WebP files
|
||||
* `[Added]` Error detection of cached redirects of images to WebP files
|
||||
|
||||
= 1.4.1 (2020-08-19) =
|
||||
* `[Changed]` Error detection method on plugin settings page
|
||||
* `[Added]` Error detection of non-working redirects of images to WebP files
|
||||
|
||||
= 1.4.0 (2020-08-13) =
|
||||
* `[Removed]` Filter `webpc_notice_url`
|
||||
* `[Changed]` Error messages for server requirements
|
||||
* `[Changed]` Loading CSS and JS files only on plugin settings page
|
||||
* `[Changed]` Minor changes for plugin settings page
|
||||
* `[Changed]` Validation of saved settings values for security
|
||||
* `[Added]` Blocking redirects to WebP when displaying images on other domains
|
||||
* `[Added]` Cron to automatically regenerate new images outside of Media Library
|
||||
* `[Added]` Filter `webpc_cron_interval` to change cron interval
|
||||
* `[Added]` Error message for incorrect plugin settings
|
||||
* `[Added]` Error when converting when WebP file is larger than original and has been deleted
|
||||
* `[Added]` Notice after plugin installation with description of first steps
|
||||
* `[Added]` Option to log errors while converting to debug.log file
|
||||
* `[Added]` Option to preserve metadata for WebP files *(available for Imagick library)*
|
||||
* `[Added]` Value of `ABSPATH` in `Server configuration` tab
|
||||
|
||||
= 1.3.1 (2020-07-03) =
|
||||
* `[Fixed]` Text Domain for Internationalization
|
||||
|
||||
= 1.3.0 (2020-06-12) =
|
||||
* `[Removed]` Ability to skip converting existing images when `Regenerate All`
|
||||
* `[Fixed]` Creating `/uploads-webpc` directory webpc after re-activation plugin
|
||||
* `[Fixed]` Error message about not supporting old PHP version
|
||||
* `[Fixed]` Ignoring case sensitivity when verifying image extensions
|
||||
* `[Changed]` Error messages when converting images
|
||||
* `[Changed]` New argument for filter `webpc_htaccess_mod_rewrite` and support for multiple .htaccess files
|
||||
* `[Added]` Converting all images from `/uploads` directory *(also other than from Media Library)*.
|
||||
* `[Added]` Converting images from `/plugins` directory
|
||||
* `[Added]` Converting images from `/themes` directory
|
||||
* `[Added]` Information about used filters in `Server configuration` tab
|
||||
* `[Added]` Option to force all images to be converted again when `Regenerate All`
|
||||
|
||||
= 1.2.7 (2020-06-11) =
|
||||
* `[Changed]` Moving converted WebP files to `/uploads-webpc/uploads` directory from `/uploads-webpc` directory *(**required manual configuration change for Nginx and WordPress Multisite**)*
|
||||
* `[Changed]` Validation when converting images
|
||||
|
||||
= 1.2.6 (2020-05-28) =
|
||||
* `[Fixed]` Removal of WebP files larger than original during upload
|
||||
|
||||
= 1.2.5 (2020-05-10) =
|
||||
* `[Removed]` Link to plugin settings on Network Admin Screen for WordPress Multisite
|
||||
* `[Fixed]` Path in RewriteRule for WordPress Multisite
|
||||
* `[Changed]` Error messages in administration panel
|
||||
* `[Added]` Support for `disable_functions` setting for using `set_time_limit` function
|
||||
* `[Added]` Support for blocked function `file_get_contents`
|
||||
|
||||
= 1.2.4 (2020-04-24) =
|
||||
* `[Changed]` Error messages in administration panel
|
||||
* `[Added]` Action `webpc_delete_paths` to delete images by paths
|
||||
|
||||
= 1.2.3 (2020-04-15) =
|
||||
* `[Added]` Blocking server cache for rewrite rules
|
||||
* `[Added]` Detecting whether requests to images are processed by server bypassing Apache
|
||||
|
||||
= 1.2.2 (2020-04-08) =
|
||||
* `[Changed]` Moving rules for modules `mod_mime` and `mod_expires` to `/uploads-webpc/.htaccess` file
|
||||
* `[Changed]` New argument for filter `webpc_htaccess_rules` with server path of file
|
||||
|
||||
= 1.2.1 (2020-04-07) =
|
||||
* `[Removed]` Filter `webpc_option_disabled`
|
||||
* `[Fixed]` Converting images multiple times when uploading to Media Library
|
||||
* `[Added]` Action `webpc_convert_paths` to convert images by paths
|
||||
* `[Added]` Action `webpc_convert_attachment` to convert images by Post ID
|
||||
|
||||
= 1.2.0 (2020-04-05) =
|
||||
* `[Changed]` Moving rules from .htaccess file in root directory of WordPress to `/wp-content/uploads` directory
|
||||
* `[Added]` Ability to disable automatic removal of WebP files larger than original
|
||||
* `[Added]` Error validation for a non-writable .htaccess file
|
||||
* `[Added]` Filter `webpc_uploads_root` to change path for root installation directory of WordPress
|
||||
|
||||
= 1.1.2 (2020-03-03) =
|
||||
* `[Added]` Zero padding at end for odd-sized WebP files using `GD` library
|
||||
|
||||
= 1.1.1 (2020-02-13) =
|
||||
* `[Changed]` Unknown error handling when converting images
|
||||
* `[Added]` Ability to skip converting existing images when `Regenerate All`
|
||||
* `[Added]` Button for simple checking of server configuration
|
||||
|
||||
= 1.1.0 (2020-02-10) =
|
||||
* `[Fixed]` Support for WordPress installation in subdirectory
|
||||
* `[Fixed]` Error detecting WebP support by Imagick
|
||||
|
||||
= 1.0.9 (2020-01-03) =
|
||||
* `[Added]` Limit of maximum image resolution limit using `GD` library
|
||||
|
||||
= 1.0.8 (2019-12-19) =
|
||||
* `[Fixed]` File deletion for custom paths with converted WebP files
|
||||
* `[Changed]` Rules management in .htaccess file when activating or deactivating plugin
|
||||
* `[Added]` Error detection system in server configuration
|
||||
* `[Added]` Blocking image conversion when `GD` or `Imagick` libraries are unavailable
|
||||
|
||||
= 1.0.7 (2019-12-17) =
|
||||
* `[Changed]` Rewrite rules in .htaccess file
|
||||
* `[Added]` Custom path support for original uploads files
|
||||
* `[Added]` Custom path support for saving converted WebP files
|
||||
* `[Added]` Filter `webpc_uploads_path` to change path for original uploads files
|
||||
* `[Added]` Filter `webpc_uploads_webp` to change path for saving converted WebP files
|
||||
|
||||
= 1.0.6 (2019-11-06) =
|
||||
* `[Changed]` Way of generating file path _(without `ABSPATH`)_
|
||||
* `[Added]` Automatic deletion of converted files larger than original
|
||||
|
||||
= 1.0.5 (2019-09-16) =
|
||||
* `[Added]` Information on available FAQ
|
||||
|
||||
= 1.0.4 (2019-07-11) =
|
||||
* `[Changed]` Limits of maximum execution time
|
||||
|
||||
= 1.0.3 (2019-06-26) =
|
||||
* `[Added]` Additional security rules
|
||||
|
||||
= 1.0.2 (2019-06-25) =
|
||||
* `[Changed]` Error messages
|
||||
* `[Added]` Tab in Settings page about server configuration
|
||||
|
||||
= 1.0.1 (2019-06-23) =
|
||||
* `[Changed]` Securing access to REST API
|
||||
* `[Added]` Error handler for undefined `GD` extension
|
||||
|
||||
= 1.0.0 (2019-06-16) =
|
||||
* The first stable release
|
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* Loads images in WebP format or original images if browser does not support WebP.
|
||||
*
|
||||
* @category WordPress Plugin
|
||||
* @package Converter for Media
|
||||
* @author Mateusz Gbiorczyk
|
||||
* @link https://wordpress.org/plugins/webp-converter-for-media/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Loads lighter weight image files in output format instead of original formats.
|
||||
*/
|
||||
class PassthruLoader {
|
||||
|
||||
const PATH_UPLOADS = '';
|
||||
const PATH_UPLOADS_WEBP = '';
|
||||
const MIME_TYPES = '';
|
||||
|
||||
public function __construct() {
|
||||
if ( ( self::PATH_UPLOADS === '' ) || ( self::PATH_UPLOADS_WEBP === '' ) || ( self::MIME_TYPES === '' ) ) {
|
||||
http_response_code( 404 );
|
||||
exit;
|
||||
}
|
||||
|
||||
$image_url = $_GET['src'] ?? null; // phpcs:ignore WordPress.Security
|
||||
if ( ! $image_url || ! $this->validate_src_param( $image_url ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->load_converted_image( $image_url );
|
||||
}
|
||||
|
||||
private function validate_src_param( string $image_url ): bool {
|
||||
$url_path = parse_url( $image_url, PHP_URL_PATH ) ?: '';
|
||||
$encoded_path = array_map( 'urlencode', explode( '/', $url_path ) );
|
||||
$encoded_url = str_replace( $url_path, implode( '/', $encoded_path ), $image_url );
|
||||
|
||||
if ( filter_var( $encoded_url, FILTER_VALIDATE_URL ) === false ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$image_host = parse_url( $image_url, PHP_URL_HOST );
|
||||
if ( $image_host !== ( $_SERVER['HTTP_HOST'] ?? '' ) ) { // phpcs:ignore WordPress.Security
|
||||
return false;
|
||||
}
|
||||
|
||||
$image_extension = strtolower( pathinfo( $image_url, PATHINFO_EXTENSION ) );
|
||||
if ( ! in_array( $image_extension, [ 'jpg', 'jpeg', 'png', 'gif', 'png2' ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes loading of image in supported output format.
|
||||
*
|
||||
* @param string $image_url URL of source image.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function load_converted_image( string $image_url ) {
|
||||
$mime_types = json_decode( self::MIME_TYPES, true ) ?: [];
|
||||
$headers = array_change_key_case(
|
||||
array_merge( ( function_exists( 'getallheaders' ) ) ? getallheaders() : [], $_SERVER ),
|
||||
CASE_UPPER
|
||||
);
|
||||
$accept_header = $headers['ACCEPT'] ?? ( $headers['HTTP_ACCEPT'] ?? '' );
|
||||
|
||||
foreach ( $mime_types as $extension => $mime_type ) {
|
||||
if ( ( strpos( $accept_header, $mime_type ) !== false )
|
||||
&& ( $source = $this->load_image_source( $image_url, $extension ) ) ) {
|
||||
header( 'Content-Type: ' . $mime_type );
|
||||
echo $source; // phpcs:ignore
|
||||
exit;
|
||||
}
|
||||
}
|
||||
$this->load_image_default( $image_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads image output format.
|
||||
*
|
||||
* @param string $image_url URL of source image.
|
||||
* @param string $extension Extension of output format.
|
||||
*
|
||||
* @return string|null Content of image in output format.
|
||||
*/
|
||||
private function load_image_source( string $image_url, string $extension ) {
|
||||
$url = $this->generate_source_url( $image_url, $extension );
|
||||
$ch = curl_init( $url );
|
||||
if ( $ch === false ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
curl_close( $ch );
|
||||
|
||||
if ( ( $code !== 200 ) || ! $response ) {
|
||||
return null;
|
||||
} else {
|
||||
return ( is_string( $response ) ) ? $response : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns URL of output image by replacing URL of source image.
|
||||
*
|
||||
* @param string $image_url URL of source image.
|
||||
* @param string $extension Extension of output format.
|
||||
*
|
||||
* @return string URL of image in output format.
|
||||
*/
|
||||
private function generate_source_url( string $image_url, string $extension ): string {
|
||||
return sprintf(
|
||||
'%1$s.%2$s',
|
||||
str_replace( self::PATH_UPLOADS, self::PATH_UPLOADS_WEBP, $image_url ),
|
||||
$extension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to source image.
|
||||
*
|
||||
* @param string $image_url URL of source image.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function load_image_default( string $image_url ) {
|
||||
header( 'Location: ' . $image_url );
|
||||
}
|
||||
}
|
||||
|
||||
new PassthruLoader();
|
348
wp-content/plugins/webp-converter-for-media/readme.txt
Normal file
@ -0,0 +1,348 @@
|
||||
=== Converter for Media - Optimize images | Convert WebP & AVIF ===
|
||||
Contributors: mateuszgbiorczyk
|
||||
Donate link: https://url.mattplugins.com/converter-readme-donate-link
|
||||
Tags: convert webp, webp, optimize images, image optimization, compress images
|
||||
Requires at least: 4.9
|
||||
Tested up to: 6.4
|
||||
Requires PHP: 7.0
|
||||
Stable tag: 5.11.5
|
||||
License: GPLv2 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
Speed up your website by using our WebP & AVIF Converter. Optimize images and serve WebP and AVIF images instead of standard formats!
|
||||
|
||||
== Description ==
|
||||
|
||||
**Speed up your website using our ease image optimizer by serving WebP and AVIF images.** By replacing files in standard JPEG, PNG and GIF formats with WebP and AVIF formats, you can save over a half of the page weight without losing quality.
|
||||
|
||||
[youtube https://www.youtube.com/watch?v=Ls2h38avpRw]
|
||||
|
||||
After installing the plugin you do not have to do anything more. Your current images will be converted into a new format. When image optimization by our image optimizer is finished, users will automatically receive new, much lighter images than the original ones.
|
||||
|
||||
As of today, over 90% of users use browsers that support the WebP format. The loading time of your website depends to a large extent on its weight and the level of image optimization. **Using our WebP Converter, now you can and speed up it in a few seconds without much effort!**
|
||||
|
||||
This will be a profit both for your users who will not have to download so much data, but also for a server that will be less loaded. Remember that a better optimized website also affects your Google ranking. Image optimization is very important.
|
||||
|
||||
#### AVIF support
|
||||
|
||||
Now in [the PRO version](https://url.mattplugins.com/converter-readme-avif-support-upgrade) you can use AVIF as the output format for your images. The AVIF format is a new extension - is the successor to WebP. **AVIF allows you to achieve even higher levels of image compression**, and the quality of the converted images after image optimization is better than in WebP.
|
||||
|
||||
#### How does this work?
|
||||
|
||||
- If you have just installed the plugin, you can optimize images with one click. Image size will be smaller after generate webp!
|
||||
- New images that will be added to the Media Library will be converted automatically.
|
||||
- Our image optimizer does not modify your original images in any way. This means security for you and your files.
|
||||
- When the browser loads an image, our plugin checks if it supports the WebP format. If so, the image in WebP format is loaded.
|
||||
- The plugin does not make redirects in default mode, so the URL is always the same. Only the MIME type of the image changes to `image/webp`.
|
||||
- No redirects means no cache issues, faster and trouble-free operation of your website. If you want to know more about how it works, check out [the plugin FAQ](#faq) below.
|
||||
- It does not matter if the image display as an `img` HTML tag or you use `background-image`. It works always!
|
||||
- In case rewriting by rules from .htaccess file is blocked, a mode is available which loads images via PHP file. Then image URLs are changed, but the logic of operation is the same as in the case of the default mode.
|
||||
- The final result after image optimization is that your users download less than half of the data, and the website itself loads faster!
|
||||
- You lose nothing - if you had to remove the plugin, it will remove everything after itself. It does not leave any trace, so you can check it with ease.
|
||||
|
||||
#### Convert WebP - it is the future of image optimization!
|
||||
|
||||
Optimize images and raise your website to a new level now! Install the plugin and enjoy the website that loads faster by image optimization. Surely you and your users will appreciate it.
|
||||
|
||||
#### Support for additional directories
|
||||
|
||||
You can convert WebP and optimize images not only from `/uploads` directory but also from `/plugins` and `/themes` directories. This allows full integration with the WebP format!
|
||||
|
||||
#### Support to the development of plugin
|
||||
|
||||
We spend hours working on the development of this plugin. Technical support also requires a lot of time, but we do it because we want to offer you the best plugin. We enjoy every new plugin installation.
|
||||
|
||||
If you would like to appreciate it, you can try [the PRO version](https://url.mattplugins.com/converter-readme-development-support-upgrade). In addition, you will gain access to extra functionalities that will allow you to achieve **even better image optimization results**.
|
||||
|
||||
#### Please also read the FAQ below. Thank you for being with us!
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. Upload the plugin files to `/wp-content/plugins/webp-converter-for-media` directory, or install the plugin through the WordPress plugins screen directly.
|
||||
2. Activate the plugin through `Plugins` screen in WordPress Admin Panel.
|
||||
3. Use `Settings -> Settings -> Converter for Media` screen to configure the plugin.
|
||||
4. Click on the button `Start Bulk Optimization` and wait.
|
||||
5. Check if everything works fine using [this tutorial](https://url.mattplugins.com/converter-readme-installation-instruction).
|
||||
|
||||
That's all! Your website is already loading faster!
|
||||
|
||||
== Frequently Asked Questions ==
|
||||
|
||||
= How to get technical support? (before you ask for help) =
|
||||
|
||||
Before you add a new thread, **read all other questions in this FAQ and other threads in [the support forum](https://url.mattplugins.com/converter-plugin-faq-technical-support-forum) first**. Perhaps someone had a similar problem and it has been resolved.
|
||||
|
||||
When adding a thread, follow these steps and reply to each of them:
|
||||
|
||||
**1.** Do you have any errors on the plugin settings page? Please read [this thread](https://url.mattplugins.com/converter-plugin-faq-technical-support-error-instruction) if you have any errors.
|
||||
|
||||
**2.** URL of your website.
|
||||
|
||||
**3.** Screenshot of the Help Center tab on the plugin settings screen - please take a screenshot of the ENTIRE page.
|
||||
|
||||
**4.** Please do the test, which is described in the FAQ in the question "How to check if the plugin works?". Please send a screenshot of Devtools with the test results.
|
||||
|
||||
Please remember to include the answers to all questions by adding a thread. It is much easier and accelerates the solution of your problem.
|
||||
|
||||
= Configuration for Nginx =
|
||||
|
||||
If you are using a Nginx server that does not support .htaccess rules, additional Nginx server configuration is required for the plugin to work properly.
|
||||
|
||||
Please read [this tutorial](https://url.mattplugins.com/converter-plugin-faq-nginx-configuration-instruction) for more information.
|
||||
|
||||
= Configuration for Nginx Proxy =
|
||||
|
||||
If you are using a Nginx server that supports .htaccess rules, but you still have a server configuration error on the plugin settings page, additional Nginx server configuration is required for the plugin to work properly.
|
||||
|
||||
Please read [this tutorial](https://url.mattplugins.com/converter-plugin-faq-nginx-proxy-configuration-instruction) for more information.
|
||||
|
||||
= Error on plugin settings screen? =
|
||||
|
||||
If you have an error on the plugin settings screen, first of all, please read it carefully. They are displayed when there is a problem with the configuration of your server or website.
|
||||
|
||||
The messages are designed to reduce the number of support requests that are repeated. It saves your and our time. Please read [this thread](https://url.mattplugins.com/converter-plugin-faq-configuration-error-instruction) for more information.
|
||||
|
||||
= Error while converting? =
|
||||
|
||||
You can get several types of errors when converting. First of all, carefully read their content. For the most part, you can solve this problem yourself. Try to do this or contact the server administrator.
|
||||
|
||||
If you get an error: `File "%s" does not exist. Please check file path.` means that the [file_exists()](https://www.php.net/manual/en/function.file-exists.php) function in PHP returned `false` using the file path given in the error message. Check this path and make sure it is correct.
|
||||
|
||||
If you get an error: `File "%s" is unreadable. Please check file permissions.` means that the [is_readable()](https://www.php.net/manual/en/function.is-readable.php) function in PHP returned `false` using the file path given in the error message. Check the permissions for the file and the directory in which the file is located.
|
||||
|
||||
If you get an error: `"%s" is not a valid image file.` means that the file is damaged in some way. Download the file to disk, save it again using any graphics program and add it again to the page. If the error applies to individual images then you can ignore it - just the original images will load, not WebP.
|
||||
|
||||
If you get an error: `Image "%s" converted to .webp is larger than original and converted .webp file has been deleted.` means the original image weighed less than WebP. This happens when images have been compressed before. Disable the *"Automatic removal of files in output formats larger than original"* option in plugin settings to force always using WebP.
|
||||
|
||||
= What are requirements of plugin? =
|
||||
|
||||
Practically every hosting meets these requirements. You must use PHP at least 7.0 and have the `GD` or `Imagick` extension installed. **The extension must support `WebP format`.** If you have an error saying that the GD or Imagick library is not installed, but you have it installed then they are probably incorrectly configured and do not have WebP support.
|
||||
|
||||
They are required native PHP extensions, used among others by WordPress to generate thumbnails. Your server must also have the modules `mod_mime`, `mod_rewrite` and `mod_expires` enabled.
|
||||
|
||||
An example of the correct server configuration can be found [here](https://url.mattplugins.com/converter-plugin-faq-plugin-requirements-configuration-image). The link to your current configuration can be found in the Help Center tab on plugin settings screen.
|
||||
|
||||
**Note the items marked in red.** If the values marked in red do not appear in your case, it means that your server does not meet the technical requirements. Pay attention to the **WebP Support** value for the GD library and **WEBP in the list of supported extensions** for the Imagick library.
|
||||
|
||||
In a situation where your server does not meet the technical requirements, please contact your server Administrator. We are not able to help you. Please do not contact us about this matter, because this is a server configuration problem, not a plugin.
|
||||
|
||||
Also, REST API must be enabled and work without additional restrictions. If you have a problem with it, please contact the Developer who created your website. He should easily find the issue with the REST API not working.
|
||||
|
||||
= How to check if the plugin works? =
|
||||
|
||||
You can find more information on how the plugin works in [our manual](https://url.mattplugins.com/converter-plugin-faq-plugin-check-instruction).
|
||||
|
||||
= How to change the path to uploads? =
|
||||
|
||||
This is possible using the following types of filters to change default paths. It is a solution for advanced users. If you are not, please skip this question.
|
||||
|
||||
Path to the root installation directory of WordPress *(`ABSPATH` by default)*:
|
||||
|
||||
`add_filter( 'webpc_site_root', function( $path ) {
|
||||
return ABSPATH;
|
||||
} );`
|
||||
|
||||
Paths to directories *(relative to the root directory)*:
|
||||
|
||||
`add_filter( 'webpc_dir_name', function( $path, $directory ) {
|
||||
switch ( $directory ) {
|
||||
case 'uploads':
|
||||
return 'wp-content/uploads';
|
||||
case 'webp':
|
||||
return 'wp-content/uploads-webpc';
|
||||
case 'plugins':
|
||||
return 'wp-content/plugins';
|
||||
case 'themes':
|
||||
return 'wp-content/themes';
|
||||
}
|
||||
return $path;
|
||||
}, 10, 2 );`
|
||||
|
||||
**Note that the `/uploads-webpc` directory must be at the same nesting level as the `/uploads`, `/plugins` and `/themes` directories.**
|
||||
|
||||
Prefix in URL of `/wp-content/` directory or equivalent *(used in .htaccess)*:
|
||||
|
||||
`add_filter( 'webpc_htaccess_rewrite_path', function( $prefix ) {
|
||||
return '/';
|
||||
} );`
|
||||
|
||||
For the following sample custom WordPress structure:
|
||||
|
||||
`...
|
||||
├── web
|
||||
...
|
||||
├── app
|
||||
│ ├── mu-plugins
|
||||
│ ├── plugins
|
||||
│ ├── themes
|
||||
│ └── uploads
|
||||
├── wp-config.php
|
||||
...`
|
||||
|
||||
Use the following filters:
|
||||
|
||||
`add_filter( 'webpc_site_root', function( $path ) {
|
||||
return 'C:/WAMP/www/project/web'; // your valid path to root
|
||||
} );
|
||||
add_filter( 'webpc_htaccess_rewrite_path', function( $prefix ) {
|
||||
return '/';
|
||||
} );
|
||||
add_filter( 'webpc_dir_name', function( $path, $directory ) {
|
||||
switch ( $directory ) {
|
||||
case 'uploads':
|
||||
return 'app/uploads';
|
||||
case 'webp':
|
||||
return 'app/uploads-webpc';
|
||||
case 'plugins':
|
||||
return 'app/plugins';
|
||||
case 'themes':
|
||||
return 'app/themes';
|
||||
}
|
||||
return $path;
|
||||
}, 10, 2 );`
|
||||
|
||||
After setting the filters go to `Settings -> Converter for Media` in the admin panel and click the `Save Changes` button. `.htaccess` files with appropriate rules should be created in the directories `/uploads` and `/uploads-webpc`.
|
||||
|
||||
= How to exclude paths from converting? =
|
||||
|
||||
To exclude selected directories, use the following filter:
|
||||
|
||||
`add_filter( 'webpc_supported_source_directory', function( bool $status, string $directory_name, string $server_path ): bool {
|
||||
$excluded_directories = [ 'my-directory' ];
|
||||
if ( in_array( $directory_name, $excluded_directories ) ) {
|
||||
return false;
|
||||
}
|
||||
return $status;
|
||||
}, 10, 3 );`
|
||||
|
||||
To exclude selected files use the following filter *(in this case with the suffix "-skipwebp" in a filename, e.g. image-skipwebp.png)*:
|
||||
|
||||
`add_filter( 'webpc_supported_source_file', function( bool $status, string $file_name, string $server_path ): bool {
|
||||
$excluded_suffix = '-skipwebp';
|
||||
if ( strpos( $file_name, $excluded_suffix . '.' ) !== false ) {
|
||||
return false;
|
||||
}
|
||||
return $status;
|
||||
}, 10, 3 );`
|
||||
|
||||
Argument `$server_path` is the absolute server path to a directory or file. Inside the filters, you can apply more complicated rules as needed.
|
||||
|
||||
Filters run before images are converted - they no longer support converted images. You have to delete them manually if they should not be converted.
|
||||
|
||||
= Support for custom directories =
|
||||
|
||||
The plugin supports the following directories by default:
|
||||
- `/gallery`
|
||||
- `/plugins`
|
||||
- `/themes`
|
||||
- `/uploads`
|
||||
|
||||
If you want to add support for a custom directory, add the following code to the functions.php file in your theme directory *(use a correct directory name instead of `custom-directory`)*:
|
||||
|
||||
`add_filter( 'webpc_source_directories', function ( $directories ) {
|
||||
$directories[] = 'custom-directory';
|
||||
return $directories;
|
||||
} );`
|
||||
|
||||
Remember that this directory must be located in the `/wp-content` directory.
|
||||
|
||||
= How to run manually conversion? =
|
||||
|
||||
By default, all images are converted when you click on the `Start Bulk Optimization ` button. In addition, conversion is automatic when you add new files to your Media Library.
|
||||
|
||||
Remember that our plugin takes into account images generated by WordPress. There are many plugins that generate, for example, images of a different size or in a different version.
|
||||
|
||||
If you would like to integrate with your plugin, which generates images by yourself, you can do it. Our plugin provides the possibility of this type of integration. This works for all images in the `/wp-content` directory.
|
||||
|
||||
It is a solution for advanced users. If you would like to integrate with another plugin, it's best to contact the author of that plugin and give him information about the actions available in our plugin. This will help you find a solution faster.
|
||||
|
||||
You can manually run converting selected files, you can use the action to which you will pass an array with a list of paths *(they must be absolute server paths)*:
|
||||
|
||||
`do_action( 'webpc_convert_paths', $paths, true );`
|
||||
|
||||
An alternative method is to manually start converting the selected attachment by passing the post ID from the Media Library. Remember to run this action after registering all image sizes *(i.e. after running the `add_image_size` function)*:
|
||||
|
||||
`do_action( 'webpc_convert_attachment', $post_id, true );`
|
||||
|
||||
To delete manually converted files, use the following action, providing as an argument an array of absolute server paths to the files *(this will delete manually converted files)*:
|
||||
|
||||
`do_action( 'webpc_delete_paths', $paths );`
|
||||
|
||||
= Support for WP-CLI =
|
||||
|
||||
The plugin supports WP-CLI, which enables faster image conversion from the server level. More information on how to get started with WP-CLI can be found in [the Handbook](https://make.wordpress.org/cli/handbook/guides/quick-start/). The supported commands are described below.
|
||||
|
||||
Checking how many maximum images for conversion are on website:
|
||||
|
||||
`wp converter-for-media calculate`
|
||||
|
||||
Converting all images:
|
||||
|
||||
`wp converter-for-media regenerate`
|
||||
|
||||
Converting all images (with "Force convert all images again" option):
|
||||
|
||||
`wp converter-for-media regenerate --force`
|
||||
|
||||
= Does plugin support CDN? =
|
||||
|
||||
The website files (WordPress files) and the images from the Media Library must be on the same server. If they are, everything should work fine.
|
||||
|
||||
If only your images are on another CDN server, unfortunately correct operation is impossible, because such images are managed by another server.
|
||||
|
||||
Current list of supported CDN servers:
|
||||
- BunnyCDN (refer to [the instructions](https://url.mattplugins.com/converter-plugin-faq-cdn-bunny-instruction) before use)
|
||||
|
||||
== Screenshots ==
|
||||
|
||||
1. General tab of the plugin settings
|
||||
2. Advanced tab of the plugin settings
|
||||
3. Bulk optimization of images
|
||||
4. Optimization statistics of Media Library
|
||||
5. Ability to manually undo optimization of selected image
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 5.11.5 (2023-12-07) =
|
||||
* `[Added]` Compatibility with PHP 8.3
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
|
||||
= 5.11.4 (2023-11-13) =
|
||||
* `[Fixed]` Removing WebP files converted for AVIF format from /uploads-webpc directory after uninstalling plugin
|
||||
* `[Fixed]` Statistics of conversion progress in WP-CLI
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
* `[Changed]` Error message for bypassing_apache error in server configuration
|
||||
* `[Changed]` Error message for rewrites_cached error in server configuration
|
||||
|
||||
= 5.11.3 (2023-11-09) =
|
||||
* `[Fixed]` Automatically conversion of images from /uploads directory, but not from Media Library
|
||||
* `[Added]` Support for WordPress 6.4
|
||||
|
||||
= 5.11.2 (2023-10-16) =
|
||||
* `[Added]` Button to expand/collapse list of directories to optimize in Bulk Optimization of Images section
|
||||
* `[Added]` Notification about plugin requirements in WordPress Playground environment
|
||||
|
||||
= 5.11.1 (2023-10-02) =
|
||||
* `[Fixed]` Duplicated rewrite rules for .jpeg files
|
||||
* `[Changed]` Error message for rewrites_not_executed error in server configuration
|
||||
|
||||
= 5.11.0 (2023-09-27) =
|
||||
* `[Added]` Ability to manually optimize selected images in Media Library
|
||||
* `[Added]` Ability to manually undo optimization of selected images in Media Library
|
||||
|
||||
= 5.10.1 (2023-09-10) =
|
||||
* `[Fixed]` Detection of bypassing_apache error in server configuration
|
||||
|
||||
= 5.10.0 (2023-09-09) =
|
||||
* `[Fixed]` Removing files from /uploads-webpc directory after uninstalling plugin
|
||||
* `[Changed]` Error message for rewrites_not_working error in server configuration
|
||||
* `[Changed]` Verification of correct operation of rewrites from .htaccess file
|
||||
* `[Added]` Changes to improve performance of plugin
|
||||
* `[Added]` Changes to improve loading time of plugin settings
|
||||
|
||||
See [changelog.txt](https://url.mattplugins.com/converter-readme-changelog) for previous versions.
|
||||
|
||||
== Upgrade Notice ==
|
||||
|
||||
None.
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\Conversion\AttachmentPathsGenerator;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
|
||||
/**
|
||||
* Initializes conversion of all image sizes for attachment.
|
||||
*/
|
||||
class ConvertAttachmentAction implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'webpc_convert_attachment', [ $this, 'convert_files_by_attachment' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all sizes of attachment to output formats.
|
||||
*
|
||||
* @param int $post_id ID of attachment.
|
||||
* @param bool $regenerate_force .
|
||||
* @param int $quality_level .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function convert_files_by_attachment( int $post_id, bool $regenerate_force = false, int $quality_level = null ) {
|
||||
$attachment = new AttachmentPathsGenerator( $this->plugin_data );
|
||||
|
||||
if ( $quality_level === 0 ) {
|
||||
do_action( 'webpc_delete_paths', $attachment->get_attachment_paths( $post_id ), true );
|
||||
} else {
|
||||
do_action( 'webpc_convert_paths', $attachment->get_attachment_paths( $post_id ), $regenerate_force, $quality_level );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\Conversion\Method\MethodFactory;
|
||||
use WebpConverter\Conversion\Method\MethodIntegrator;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
|
||||
/**
|
||||
* Initializes conversion of all images in list of paths.
|
||||
*/
|
||||
class ConvertPathsAction implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var MethodFactory
|
||||
*/
|
||||
private $method_factory;
|
||||
|
||||
public function __construct( PluginData $plugin_data, MethodFactory $method_factory ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->method_factory = $method_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'webpc_convert_paths', [ $this, 'convert_files_by_paths' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all given images to output formats.
|
||||
*
|
||||
* @param string[] $paths Server paths of images.
|
||||
* @param bool $regenerate_force .
|
||||
* @param int $quality_level .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function convert_files_by_paths( array $paths, bool $regenerate_force = false, int $quality_level = null ) {
|
||||
( new MethodIntegrator( $this->plugin_data, $this->method_factory ) )
|
||||
->init_conversion( $this->remove_paths_from_excluded_paths( $paths ), $regenerate_force, false, $quality_level );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes paths of source images from excluded paths.
|
||||
*
|
||||
* @param string[] $source_paths Server paths of images.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function remove_paths_from_excluded_paths( array $source_paths ): array {
|
||||
foreach ( $source_paths as $path_index => $path ) {
|
||||
if ( ! apply_filters( 'webpc_supported_source_file', true, basename( $path ), $path ) ) {
|
||||
unset( $source_paths[ $path_index ] );
|
||||
}
|
||||
}
|
||||
return $source_paths;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
|
||||
/**
|
||||
* Removes image from its output format when removing image from media library.
|
||||
*/
|
||||
class DeleteFileHandler implements HookableInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_filter( 'wp_delete_file', [ $this, 'delete_attachment_file' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes output image based on server path of source image.
|
||||
*
|
||||
* @param string $path Server path of source image.
|
||||
*
|
||||
* @return string Server path of source image.
|
||||
* @internal
|
||||
*/
|
||||
public function delete_attachment_file( string $path ): string {
|
||||
do_action( 'webpc_delete_paths', [ $path ] );
|
||||
return $path;
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\Conversion\CrashedFilesOperator;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\LargerFilesOperator;
|
||||
use WebpConverter\Conversion\OutputPathGenerator;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Deletes all images in list of paths.
|
||||
*/
|
||||
class DeletePathsAction implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
FormatFactory $format_factory,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'webpc_delete_paths', [ $this, 'delete_files_by_paths' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes images from output directory.
|
||||
*
|
||||
* @param string[] $paths Server paths of output images.
|
||||
* @param bool $set_skipped_flag .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function delete_files_by_paths( array $paths, bool $set_skipped_flag = false ) {
|
||||
foreach ( $paths as $path ) {
|
||||
$this->delete_file_by_path( $path, $set_skipped_flag );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes image from output directory.
|
||||
*
|
||||
* @param string $path Server path of output image.
|
||||
* @param bool $set_skipped_flag .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function delete_file_by_path( string $path, bool $set_skipped_flag ) {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$output_formats = ( $set_skipped_flag ) ? $plugin_settings[ OutputFormatsOption::OPTION_NAME ] : null;
|
||||
|
||||
if ( ! ( $output_paths = $this->output_path->get_paths( $path, $set_skipped_flag, $output_formats ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $output_paths as $output_path ) {
|
||||
if ( is_writable( $output_path ) ) {
|
||||
unlink( $output_path );
|
||||
}
|
||||
|
||||
if ( is_writable( $output_path . '.' . CrashedFilesOperator::CRASHED_FILE_EXTENSION ) ) {
|
||||
unlink( $output_path . '.' . CrashedFilesOperator::CRASHED_FILE_EXTENSION );
|
||||
}
|
||||
|
||||
if ( $set_skipped_flag ) {
|
||||
$file = fopen( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION, 'w' );
|
||||
if ( $file !== false ) {
|
||||
fclose( $file );
|
||||
}
|
||||
} elseif ( is_writable( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION ) ) {
|
||||
unlink( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\Conversion\Cron\CronInitiator;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Settings\Option\AutoConversionOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Initializes image conversion when uploading images to media library.
|
||||
*/
|
||||
class UploadFileHandler implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var CronInitiator
|
||||
*/
|
||||
private $cron_initiator;
|
||||
|
||||
/**
|
||||
* Paths of converted images.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $uploaded_paths = [];
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronInitiator $cron_initiator = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->cron_initiator = $cron_initiator ?: new CronInitiator( $plugin_data, $token_repository, $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'init', [ $this, 'init_hooks_after_setup' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function init_hooks_after_setup() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( ! $plugin_settings[ AutoConversionOption::OPTION_NAME ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'wp_update_attachment_metadata', [ $this, 'init_attachment_conversion' ], 10, 2 );
|
||||
add_filter( 'image_make_intermediate_size', [ $this, 'init_image_conversion' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes converting attachment images while attachment is uploaded.
|
||||
*
|
||||
* @param mixed[]|null $data Updated attachment meta data.
|
||||
* @param int|null $attachment_id ID of attachment.
|
||||
*
|
||||
* @return mixed[]|null Attachment meta data.
|
||||
* @internal
|
||||
*/
|
||||
public function init_attachment_conversion( array $data = null, int $attachment_id = null ) {
|
||||
if ( ( $data === null ) || ( $attachment_id === null )
|
||||
|| ! isset( $data['file'] ) || ! isset( $data['sizes'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$file_extension = strtolower( pathinfo( $data['file'], PATHINFO_EXTENSION ) );
|
||||
if ( ! in_array( $file_extension, $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$paths = $this->get_sizes_paths( $data );
|
||||
$paths = apply_filters( 'webpc_attachment_paths', $paths, $attachment_id );
|
||||
|
||||
$this->uploaded_paths = array_merge( $this->uploaded_paths, $paths );
|
||||
add_action( 'shutdown', [ $this, 'save_paths_to_conversion' ] );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes converting attachment images after file is saved by Image Editor.
|
||||
*
|
||||
* @param string $filename Path of image.
|
||||
*
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function init_image_conversion( string $filename ): string {
|
||||
$upload = wp_upload_dir();
|
||||
if ( strpos( $filename, $upload['basedir'] ) !== 0 ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$file_extension = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
|
||||
if ( ! in_array( $file_extension, $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] ) ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$this->uploaded_paths[] = str_replace( '\\', '/', $filename );
|
||||
|
||||
add_action( 'shutdown', [ $this, 'save_paths_to_conversion' ] );
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server paths of attachment image sizes.
|
||||
*
|
||||
* @param mixed[] $data Updated attachment meta data.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
private function get_sizes_paths( array $data ): array {
|
||||
$directory = $this->get_attachment_directory( $data['file'] );
|
||||
$list = [];
|
||||
|
||||
if ( isset( $data['original_image'] ) ) {
|
||||
$list[] = $directory . $data['original_image'];
|
||||
}
|
||||
|
||||
$list[] = $directory . basename( $data['file'] );
|
||||
foreach ( $data['sizes'] as $size ) {
|
||||
$path = $directory . $size['file'];
|
||||
if ( ! in_array( $path, $list ) ) {
|
||||
$list[] = $path;
|
||||
}
|
||||
}
|
||||
return array_values( array_unique( $list ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server path of source image.
|
||||
*
|
||||
* @param string $path Relative path of source image.
|
||||
*
|
||||
* @return string Server path of source image.
|
||||
*/
|
||||
private function get_attachment_directory( string $path ): string {
|
||||
$upload = wp_upload_dir();
|
||||
$path_directory = rtrim( dirname( $path ), '/\\.' );
|
||||
$source = rtrim( $upload['basedir'], '/\\' ) . '/' . $path_directory . '/';
|
||||
|
||||
return str_replace( '\\', '/', $source );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function save_paths_to_conversion() {
|
||||
$paths = array_unique( $this->uploaded_paths );
|
||||
if ( ! $paths ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cron_initiator->add_paths_to_conversion( $paths, true );
|
||||
$this->cron_initiator->init_async_conversion( true );
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Returns all image paths for attachment.
|
||||
*/
|
||||
class AttachmentPathsGenerator {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* Current upload directory path and URL.
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $upload_dir;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->upload_dir = wp_upload_dir();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server paths to source images of attachment.
|
||||
*
|
||||
* @param int $attachment_id ID of attachment.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
public function get_attachment_paths( int $attachment_id ): array {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
return $this->get_paths_by_attachment( $attachment_id, $settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server paths to source images of attachment by file extensions.
|
||||
*
|
||||
* @param int $post_id ID of attachment.
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
private function get_paths_by_attachment( int $post_id, array $settings ): array {
|
||||
$list = [];
|
||||
$metadata = wp_get_attachment_metadata( $post_id );
|
||||
if ( ! $metadata || ! isset( $metadata['file'] ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$extension = strtolower( pathinfo( $metadata['file'], PATHINFO_EXTENSION ) );
|
||||
if ( ! in_array( $extension, $settings[ SupportedExtensionsOption::OPTION_NAME ] ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$paths = $this->get_paths_by_sizes( $post_id, $metadata );
|
||||
return apply_filters( 'webpc_attachment_paths', $paths, $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique server paths to source images of attachment.
|
||||
*
|
||||
* @param int $post_id ID of attachment.
|
||||
* @param mixed[] $metadata Data of attachment.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
private function get_paths_by_sizes( int $post_id, array $metadata ): array {
|
||||
$main_file = str_replace( '\\', '/', ( $this->upload_dir['basedir'] . '/' . $metadata['file'] ) );
|
||||
$file_path = dirname( $main_file ) . '/';
|
||||
$list = [ $main_file ];
|
||||
|
||||
foreach ( $metadata['sizes'] ?? [] as $size => $size_data ) {
|
||||
$list[] = $file_path . $size_data['file'];
|
||||
}
|
||||
return array_values( array_unique( $list ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
/**
|
||||
* Excludes re-conversion of files that caused converting error.
|
||||
*/
|
||||
class CrashedFilesOperator {
|
||||
|
||||
const CRASHED_FILE_EXTENSION = 'crashed';
|
||||
|
||||
/**
|
||||
* @param string $output_path .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create_crashed_file( string $output_path ) {
|
||||
$file = fopen( $output_path . '.' . self::CRASHED_FILE_EXTENSION, 'w' );
|
||||
if ( $file === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
fclose( $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $output_path .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete_crashed_file( string $output_path ) {
|
||||
if ( ! file_exists( $output_path ) || ! file_exists( $output_path . '.' . self::CRASHED_FILE_EXTENSION ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
unlink( $output_path . '.' . self::CRASHED_FILE_EXTENSION );
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
|
||||
/**
|
||||
* Adds cron event that converts images.
|
||||
*/
|
||||
class CronEventGenerator implements HookableInterface {
|
||||
|
||||
const CRON_PATHS_ACTION = 'webpc_cron_paths';
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var CronInitiator
|
||||
*/
|
||||
private $cron_initiator;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronInitiator $cron_initiator = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->cron_initiator = $cron_initiator ?: new CronInitiator( $plugin_data, $token_repository, $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'init', [ $this, 'add_cron_event' ] );
|
||||
add_action( self::CRON_PATHS_ACTION, [ $this, 'get_paths_to_conversion' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes cron event to convert all images.
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function add_cron_event() {
|
||||
if ( wp_next_scheduled( self::CRON_PATHS_ACTION )
|
||||
|| ! ( $settings = $this->plugin_data->get_plugin_settings() )
|
||||
|| ! in_array( ExtraFeaturesOption::OPTION_VALUE_CRON_ENABLED, $settings[ ExtraFeaturesOption::OPTION_NAME ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_schedule_event( time(), CronSchedulesGenerator::CRON_PATHS_SCHEDULE, self::CRON_PATHS_ACTION );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function get_paths_to_conversion() {
|
||||
$this->cron_initiator->refresh_paths_to_conversion();
|
||||
$this->cron_initiator->init_conversion();
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
use WebpConverter\Conversion\Endpoint\CronConversionEndpoint;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\PathsFinder;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
|
||||
/**
|
||||
* Manages automatic conversion of images.
|
||||
*/
|
||||
class CronInitiator {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
/**
|
||||
* @var PathsFinder
|
||||
*/
|
||||
private $paths_finder;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronStatusManager $cron_status_manager = null,
|
||||
PathsFinder $paths_finder = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
$this->paths_finder = $paths_finder ?: new PathsFinder( $plugin_data, $token_repository, $format_factory );
|
||||
}
|
||||
|
||||
public function refresh_paths_to_conversion( bool $force_init = false ): bool {
|
||||
$saved_request_id = $this->cron_status_manager->get_conversion_request_id();
|
||||
if ( $this->cron_status_manager->is_conversion_locked()
|
||||
|| ( ! $force_init && ( $saved_request_id !== null ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$cron_enabled = in_array( ExtraFeaturesOption::OPTION_VALUE_CRON_ENABLED, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] );
|
||||
|
||||
$this->cron_status_manager->set_conversion_status_locked( true, true );
|
||||
|
||||
$paths = $this->paths_finder->get_paths( true );
|
||||
$this->cron_status_manager->set_paths_to_conversion( $paths, $cron_enabled );
|
||||
$this->cron_status_manager->set_paths_skipped( ( $cron_enabled ) ? $paths : [] );
|
||||
|
||||
$this->cron_status_manager->set_conversion_status_locked( false );
|
||||
|
||||
return (bool) $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $new_paths .
|
||||
* @param bool $force_convert_modified .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_paths_to_conversion( array $new_paths, bool $force_convert_modified = false ) {
|
||||
$paths = $this->cron_status_manager->get_paths_to_conversion();
|
||||
$valid_new_paths = $this->paths_finder->skip_converted_paths( $new_paths, null, $force_convert_modified );
|
||||
|
||||
$this->cron_status_manager->set_paths_to_conversion( array_merge( $valid_new_paths, $paths ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $request_id .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_conversion( string $request_id = null ) {
|
||||
$saved_request_id = $this->cron_status_manager->get_conversion_request_id();
|
||||
if ( $this->cron_status_manager->is_conversion_locked()
|
||||
|| ( ( $saved_request_id !== null ) && ( $request_id !== $saved_request_id ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paths = $this->cron_status_manager->get_paths_to_conversion();
|
||||
if ( ! $paths ) {
|
||||
$this->try_restart_conversion();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cron_status_manager->set_paths_to_conversion( array_slice( $paths, 1 ) );
|
||||
do_action( 'webpc_convert_paths', array_slice( $paths, 0, 1 ) );
|
||||
|
||||
$this->init_async_conversion();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $upload_request .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_async_conversion( bool $upload_request = false ) {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$service_mode = in_array( ExtraFeaturesOption::OPTION_VALUE_SERVICE_MODE, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] );
|
||||
|
||||
$headers = [
|
||||
CronConversionEndpoint::ROUTE_NONCE_HEADER => CronConversionEndpoint::get_route_nonce(),
|
||||
];
|
||||
if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) {
|
||||
$headers['Authorization'] = 'Basic ' . base64_encode( $_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
}
|
||||
|
||||
$args = [
|
||||
'timeout' => 0.01,
|
||||
'blocking' => false,
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
|
||||
'headers' => $headers,
|
||||
];
|
||||
if ( $service_mode && $upload_request ) {
|
||||
unset( $args['timeout'] );
|
||||
unset( $args['blocking'] );
|
||||
}
|
||||
|
||||
$response = wp_remote_post( CronConversionEndpoint::get_route_url(), $args );
|
||||
if ( $service_mode && $upload_request ) {
|
||||
$this->cron_status_manager->set_conversion_request_response( $response );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function try_restart_conversion() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$cron_enabled = in_array( ExtraFeaturesOption::OPTION_VALUE_CRON_ENABLED, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] );
|
||||
|
||||
$this->cron_status_manager->reset_conversion_request_id();
|
||||
if ( ! $cron_enabled || ! $this->cron_status_manager->get_paths_counter() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->refresh_paths_to_conversion( true );
|
||||
$this->init_async_conversion();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
|
||||
/**
|
||||
* Adds time intervals to cron event.
|
||||
*/
|
||||
class CronSchedulesGenerator implements HookableInterface {
|
||||
|
||||
const CRON_PATHS_SCHEDULE = 'webpc_cron_paths';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_filter( 'cron_schedules', [ $this, 'add_cron_interval' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new cron schedules.
|
||||
*
|
||||
* @param mixed[] $schedules Cron schedules.
|
||||
*
|
||||
* @return mixed[] Cron schedules.
|
||||
* @internal
|
||||
*/
|
||||
public function add_cron_interval( array $schedules ): array {
|
||||
$schedules[ self::CRON_PATHS_SCHEDULE ] = [
|
||||
'interval' => apply_filters( 'webpc_cron_paths_interval', 3600 ),
|
||||
'display' => 'Converter for Media',
|
||||
];
|
||||
return $schedules;
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
/**
|
||||
* Stores data related to automatic image conversion.
|
||||
*/
|
||||
class CronStatusManager {
|
||||
|
||||
const CRON_PATHS_TRANSIENT = 'webpc_cron_paths';
|
||||
const CRON_PATHS_SKIPPED_TRANSIENT = 'webpc_cron_paths_skipped';
|
||||
const CRON_STATUS_LOCKED_TRANSIENT = 'webpc_cron_locked';
|
||||
const CRON_REQUEST_ID_TRANSIENT = 'webpc_cron_request_id';
|
||||
const CRON_REQUEST_RESPONSE_TRANSIENT = 'webpc_cron_request_response';
|
||||
const CRON_PATHS_LIMIT = 1000;
|
||||
|
||||
/**
|
||||
* @param string[] $paths .
|
||||
* @param bool $use_paths_limit .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_paths_to_conversion( array $paths, bool $use_paths_limit = true ) {
|
||||
set_site_transient(
|
||||
self::CRON_PATHS_TRANSIENT,
|
||||
( $use_paths_limit ) ? array_slice( $paths, 0, self::CRON_PATHS_LIMIT ) : $paths,
|
||||
3600
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $paths .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_paths_skipped( array $paths ) {
|
||||
$counter = max( ( count( $paths ) - self::CRON_PATHS_LIMIT ), 0 );
|
||||
set_site_transient( self::CRON_PATHS_SKIPPED_TRANSIENT, $counter, 3600 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_paths_to_conversion(): array {
|
||||
$paths = get_site_transient( self::CRON_PATHS_TRANSIENT );
|
||||
return $paths ?: [];
|
||||
}
|
||||
|
||||
public function get_paths_counter(): int {
|
||||
$paths_count = count( $this->get_paths_to_conversion() );
|
||||
$paths_skipped = get_site_transient( self::CRON_PATHS_SKIPPED_TRANSIENT ) ?: 0;
|
||||
return ( $paths_count + $paths_skipped );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $new_status .
|
||||
* @param bool $is_long_expiration .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_conversion_status_locked( bool $new_status = true, bool $is_long_expiration = false ) {
|
||||
set_site_transient(
|
||||
self::CRON_STATUS_LOCKED_TRANSIENT,
|
||||
( $new_status ) ? 'yes' : null,
|
||||
( $is_long_expiration ) ? 900 : 60
|
||||
);
|
||||
if ( $new_status === true ) {
|
||||
$this->reset_conversion_request_id();
|
||||
}
|
||||
}
|
||||
|
||||
public function is_conversion_locked(): bool {
|
||||
return ( get_site_transient( self::CRON_STATUS_LOCKED_TRANSIENT ) === 'yes' );
|
||||
}
|
||||
|
||||
public function refresh_conversion_request_id(): string {
|
||||
$request_id = uniqid( '', true );
|
||||
set_site_transient( self::CRON_REQUEST_ID_TRANSIENT, $request_id, 60 );
|
||||
return $request_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reset_conversion_request_id() {
|
||||
set_site_transient( self::CRON_REQUEST_ID_TRANSIENT, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_conversion_request_id() {
|
||||
$request_id = get_site_transient( self::CRON_REQUEST_ID_TRANSIENT );
|
||||
return $request_id ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[]|\WP_Error $response .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_conversion_request_response( $response ) {
|
||||
set_site_transient( self::CRON_REQUEST_RESPONSE_TRANSIENT, $response );
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\Settings\Page\PageIntegrator;
|
||||
|
||||
/**
|
||||
* Displays converting status on top menu bar in the WordPress Dashboard.
|
||||
*/
|
||||
class CronStatusViewer implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $paths_preview_count = 0;
|
||||
|
||||
public function __construct( CronStatusManager $cron_status_manager = null ) {
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'admin_init', [ $this, 'init_status_preview' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function init_status_preview() {
|
||||
if ( $this->cron_status_manager->get_conversion_request_id() === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->paths_preview_count = $this->cron_status_manager->get_paths_counter();
|
||||
if ( ! $this->paths_preview_count ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'admin_bar_menu', [ $this, 'add_menu_to_top_bar' ], 1000 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_Admin_Bar $wp_admin_bar .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function add_menu_to_top_bar( \WP_Admin_Bar $wp_admin_bar ) {
|
||||
$count = number_format( $this->paths_preview_count, 0, '', ' ' );
|
||||
$menu_parent = [
|
||||
'id' => 'webpc-menu',
|
||||
'href' => PageIntegrator::get_settings_page_url(),
|
||||
'title' => sprintf(
|
||||
'<span class="ab-icon"></span><span class="ab-label">%1$s</span>',
|
||||
$count
|
||||
),
|
||||
];
|
||||
$menu_child = [
|
||||
'id' => 'webpc-menu-message',
|
||||
'title' => sprintf(
|
||||
/* translators: %1$s: progress percent */
|
||||
__( 'Converting images (%s) is in progress.', 'webp-converter-for-media' ),
|
||||
$count
|
||||
),
|
||||
'parent' => $menu_parent['id'],
|
||||
];
|
||||
|
||||
$wp_admin_bar->add_menu( $menu_parent );
|
||||
$wp_admin_bar->add_menu( $menu_child );
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
use WebpConverter\Service\PathsGenerator;
|
||||
|
||||
/**
|
||||
* Abstract class for class that supports data about directory.
|
||||
*/
|
||||
abstract class DirectoryAbstract implements DirectoryInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return '/' . $this->get_type();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available(): bool {
|
||||
return ( file_exists( $this->get_server_path() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_output_directory(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_server_path(): string {
|
||||
$directory_name = apply_filters( 'webpc_dir_name', $this->get_relative_path(), $this->get_type() );
|
||||
return sprintf(
|
||||
'%1$s/%2$s',
|
||||
rtrim( PathsGenerator::get_wordpress_root_path(), DIRECTORY_SEPARATOR ),
|
||||
$directory_name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_path_url(): string {
|
||||
$source_url = apply_filters( 'webpc_site_url', ( defined( 'WP_HOME' ) ) ? WP_HOME : get_site_url() );
|
||||
$directory_name = apply_filters( 'webpc_dir_name', $this->get_relative_path(), $this->get_type() );
|
||||
return sprintf( '%1$s/%2$s', $source_url, $directory_name );
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\Plugin\Uninstall\OutputFilesRemover;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
|
||||
/**
|
||||
* Initializes integration for all directories.
|
||||
*/
|
||||
class DirectoryFactory implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* Object of directories integration.
|
||||
*
|
||||
* @var DirectoryIntegrator
|
||||
*/
|
||||
private $directories_integration;
|
||||
|
||||
public function __construct( FormatFactory $format_factory ) {
|
||||
$this->format_factory = $format_factory;
|
||||
|
||||
$this->set_integration( new SourceDirectory( 'cache' ) );
|
||||
$this->set_integration( new SourceDirectory( 'gallery' ) );
|
||||
$this->set_integration( new SourceDirectory( 'plugins' ) );
|
||||
$this->set_integration( new SourceDirectory( 'themes' ) );
|
||||
$this->set_integration( new UploadsDirectory() );
|
||||
foreach ( apply_filters( 'webpc_source_directories', [] ) as $directory_name ) {
|
||||
$this->set_integration( new SourceDirectory( $directory_name ) );
|
||||
}
|
||||
$this->set_integration( new UploadsWebpcDirectory() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets integration for directory.
|
||||
*
|
||||
* @param DirectoryInterface $directory .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_integration( DirectoryInterface $directory ) {
|
||||
if ( $this->directories_integration === null ) {
|
||||
$this->directories_integration = new DirectoryIntegrator( $this->format_factory );
|
||||
}
|
||||
$this->directories_integration->add_directory( $directory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
$this->directories_integration->init_hooks();
|
||||
add_action( 'init', [ $this, 'init_hooks_after_setup' ] );
|
||||
add_action( 'webpc_settings_updated', [ $this, 'remove_unused_output_directories' ], 10, 2 );
|
||||
add_action( 'webpc_settings_updated', [ $this, 'remove_unused_output_format' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function init_hooks_after_setup() {
|
||||
foreach ( apply_filters( 'webpc_source_directories', [] ) as $directory_name ) {
|
||||
$this->set_integration( new SourceDirectory( $directory_name ) );
|
||||
}
|
||||
$this->directories_integration->init_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source directories.
|
||||
*
|
||||
* @return string[] Types of directories with labels.
|
||||
*/
|
||||
public function get_directories(): array {
|
||||
return $this->directories_integration->get_source_directories();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param mixed[] $previous_plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function remove_unused_output_directories( array $plugin_settings, array $previous_plugin_settings ) {
|
||||
if ( $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] === $previous_plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$all_dirs = $this->directories_integration->get_output_directories();
|
||||
foreach ( $all_dirs as $output_dir => $output_path ) {
|
||||
if ( in_array( $output_dir, $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paths = OutputFilesRemover::get_paths_from_location( $output_path );
|
||||
$paths[] = $output_path;
|
||||
OutputFilesRemover::remove_files( $paths );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param mixed[] $previous_plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function remove_unused_output_format( array $plugin_settings, array $previous_plugin_settings ) {
|
||||
if ( ( $plugin_settings[ OutputFormatsOption::OPTION_NAME ] === $previous_plugin_settings[ OutputFormatsOption::OPTION_NAME ] )
|
||||
|| in_array( WebpFormat::FORMAT_EXTENSION, $plugin_settings[ OutputFormatsOption::OPTION_NAME ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$path = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
$paths = OutputFilesRemover::get_paths_from_location( $path );
|
||||
OutputFilesRemover::remove_files( $paths, [ WebpFormat::FORMAT_EXTENSION ] );
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\OutputPathGenerator;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\Service\PathsGenerator;
|
||||
|
||||
/**
|
||||
* Returns various types of paths for directories.
|
||||
*/
|
||||
class DirectoryIntegrator implements HookableInterface {
|
||||
|
||||
/**
|
||||
* Objects of supported directories.
|
||||
*
|
||||
* @var DirectoryInterface[]
|
||||
*/
|
||||
private $directories = [];
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
public function __construct( FormatFactory $format_factory, OutputPathGenerator $output_path = null ) {
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_filter( 'webpc_dir_name', [ $this, 'get_dir_as_name' ], 0, 2 );
|
||||
add_filter( 'webpc_dir_path', [ $this, 'get_dir_as_path' ], 0, 2 );
|
||||
add_filter( 'webpc_dir_url', [ $this, 'get_dir_as_url' ], 0, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds support of directory, if available.
|
||||
*
|
||||
* @param DirectoryInterface $directory .
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function add_directory( DirectoryInterface $directory ): self {
|
||||
$this->directories[ $directory->get_type() ] = $directory;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source directories.
|
||||
*
|
||||
* @return string[] Types of directories with labels.
|
||||
*/
|
||||
public function get_source_directories(): array {
|
||||
$values = [];
|
||||
foreach ( $this->directories as $directory ) {
|
||||
if ( ! $directory->is_output_directory() && $directory->is_available() ) {
|
||||
$values[ $directory->get_type() ] = $directory->get_label();
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of output directories.
|
||||
*
|
||||
* @return string[] Types of directories with labels.
|
||||
*/
|
||||
public function get_output_directories(): array {
|
||||
$values = [];
|
||||
foreach ( $this->directories as $directory ) {
|
||||
if ( ! $directory->is_output_directory()
|
||||
&& ( $output_path = $this->output_path->get_directory_path( $directory->get_server_path() ) )
|
||||
&& ( $output_path !== $directory->get_server_path() ) ) {
|
||||
$values[ $directory->get_type() ] = $output_path;
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server path of directory relative to WordPress root directory.
|
||||
*
|
||||
* @param string $value Default value.
|
||||
* @param string $directory_type Type of directory.
|
||||
*
|
||||
* @return string Relative server path of directory.
|
||||
* @internal
|
||||
*/
|
||||
public function get_dir_as_name( $value, string $directory_type ): string {
|
||||
if ( isset( $this->directories[ $directory_type ] ) ) {
|
||||
$value = $this->directories[ $directory_type ]->get_relative_path();
|
||||
}
|
||||
return str_replace( '\\', '/', $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server path of directory.
|
||||
*
|
||||
* @param string $value Default value.
|
||||
* @param string $directory_type Type of directory.
|
||||
*
|
||||
* @return string Server path of directory.
|
||||
* @internal
|
||||
*/
|
||||
public function get_dir_as_path( $value, string $directory_type ): string {
|
||||
$directory_name = apply_filters( 'webpc_dir_name', '', $directory_type );
|
||||
if ( $directory_name === '' ) {
|
||||
if ( isset( $this->directories[ $directory_type ] ) ) {
|
||||
return $this->directories[ $directory_type ]->get_server_path();
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%1$s/%2$s',
|
||||
rtrim( PathsGenerator::get_wordpress_root_path(), DIRECTORY_SEPARATOR ),
|
||||
$directory_name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns URL of directory.
|
||||
*
|
||||
* @param string $value Default value.
|
||||
* @param string $directory_type Type of directory.
|
||||
*
|
||||
* @return string URL of directory.
|
||||
* @internal
|
||||
*/
|
||||
public function get_dir_as_url( $value, string $directory_type ): string {
|
||||
$directory_name = apply_filters( 'webpc_dir_name', '', $directory_type );
|
||||
if ( $directory_name === '' ) {
|
||||
if ( isset( $this->directories[ $directory_type ] ) ) {
|
||||
return $this->directories[ $directory_type ]->get_path_url();
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
$source_url = apply_filters( 'webpc_site_url', ( defined( 'WP_HOME' ) ) ? WP_HOME : get_site_url() );
|
||||
return sprintf( '%1$s/%2$s', $source_url, $directory_name );
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
/**
|
||||
* Interface for class that supports data about directory.
|
||||
*/
|
||||
interface DirectoryInterface {
|
||||
|
||||
/**
|
||||
* Returns type of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_type(): string;
|
||||
|
||||
/**
|
||||
* Returns label of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_label(): string;
|
||||
|
||||
/**
|
||||
* Returns status if directory is available.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_available(): bool;
|
||||
|
||||
/**
|
||||
* Returns status if directory is destined for output.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_output_directory(): bool;
|
||||
|
||||
/**
|
||||
* Returns relative path of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_relative_path(): string;
|
||||
|
||||
/**
|
||||
* Returns server path of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_server_path(): string;
|
||||
|
||||
/**
|
||||
* Returns URL of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path_url(): string;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
/**
|
||||
* Supports data about source directory.
|
||||
*/
|
||||
class SourceDirectory extends DirectoryAbstract {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $directory_type;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $directory_path;
|
||||
|
||||
public function __construct( string $directory_name ) {
|
||||
$this->directory_type = trim( $directory_name, '/\\' );
|
||||
$this->directory_path = '%s/' . $this->directory_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_type(): string {
|
||||
return $this->directory_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_relative_path(): string {
|
||||
return sprintf( $this->directory_path, basename( WP_CONTENT_DIR ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
/**
|
||||
* Supports data about /uploads directory.
|
||||
*/
|
||||
class UploadsDirectory extends DirectoryAbstract {
|
||||
|
||||
const DIRECTORY_TYPE = 'uploads';
|
||||
const DIRECTORY_PATH = '%s/uploads';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_type(): string {
|
||||
return self::DIRECTORY_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_relative_path(): string {
|
||||
if ( defined( 'UPLOADS' ) ) {
|
||||
return trim( UPLOADS, '/\\' );
|
||||
}
|
||||
|
||||
return sprintf( self::DIRECTORY_PATH, basename( WP_CONTENT_DIR ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
/**
|
||||
* Supports data about /uploads-webpc directory.
|
||||
*/
|
||||
class UploadsWebpcDirectory extends DirectoryAbstract {
|
||||
|
||||
const DIRECTORY_TYPE = 'webp';
|
||||
const DIRECTORY_PATH = '%s/uploads-webpc';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_type(): string {
|
||||
return self::DIRECTORY_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_output_directory(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_relative_path(): string {
|
||||
if ( defined( 'UPLOADS' ) ) {
|
||||
$uploads_dir = trim( UPLOADS, '/\\' );
|
||||
return trim( sprintf( self::DIRECTORY_PATH, dirname( $uploads_dir ) ), '/\\.' );
|
||||
}
|
||||
|
||||
return sprintf( self::DIRECTORY_PATH, basename( WP_CONTENT_DIR ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Error\Detector\RewritesErrorsDetector;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Returns paths to files in given directory.
|
||||
*/
|
||||
class DirectoryFilesFinder {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var ServerConfigurator
|
||||
*/
|
||||
private $server_configurator;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
ServerConfigurator $server_configurator = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->server_configurator = $server_configurator ?: new ServerConfigurator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source images for directory.
|
||||
*
|
||||
* @param string $dir_path Server path of source directory.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
* @internal
|
||||
*/
|
||||
public function get_files_by_directory( string $dir_path ): array {
|
||||
if ( ! file_exists( $dir_path ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->server_configurator->set_memory_limit();
|
||||
$this->server_configurator->set_execution_time( 900 );
|
||||
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
return $this->find_files_in_directory(
|
||||
$dir_path,
|
||||
$settings[ SupportedExtensionsOption::OPTION_NAME ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source images for directory.
|
||||
*
|
||||
* @param string $dir_path Server path of source directory.
|
||||
* @param string[] $allowed_source_exts File extensions to find.
|
||||
* @param string $path_prefix File path related to directory path.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
private function find_files_in_directory( string $dir_path, array $allowed_source_exts, string $path_prefix = '' ): array {
|
||||
$paths = scandir( $dir_path );
|
||||
$list = [];
|
||||
if ( ! is_array( $paths ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
if ( $path_prefix === '' ) {
|
||||
$paths = array_diff( $paths, [ basename( RewritesErrorsDetector::PATH_OUTPUT_FILE_PNG ) ] );
|
||||
}
|
||||
|
||||
rsort( $paths );
|
||||
foreach ( $paths as $path ) {
|
||||
$current_path = $dir_path . '/' . $path;
|
||||
|
||||
if ( is_dir( $current_path ) ) {
|
||||
if ( apply_filters( 'webpc_supported_source_directory', true, basename( $current_path ), $current_path ) ) {
|
||||
$list = array_merge(
|
||||
$list,
|
||||
$this->find_files_in_directory( $current_path, $allowed_source_exts, trim( $path_prefix . '/' . $path, '/' ) )
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$filename = basename( $current_path );
|
||||
$parts = array_reverse( explode( '.', $filename ) );
|
||||
if ( in_array( strtolower( $parts[0] ?? '' ), $allowed_source_exts ) && ! in_array( strtolower( $parts[1] ?? '' ), ExcludedPathsOperator::EXCLUDED_SUB_EXTENSIONS ) ) {
|
||||
if ( apply_filters( 'webpc_supported_source_file', true, $filename, $current_path ) ) {
|
||||
$list[] = trim( $path_prefix . '/' . $path, '/' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\Conversion\Cron\CronInitiator;
|
||||
use WebpConverter\Conversion\Cron\CronStatusManager;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
|
||||
/**
|
||||
* .
|
||||
*/
|
||||
class CronConversionEndpoint extends EndpointAbstract {
|
||||
|
||||
const ROUTE_NONCE_HEADER = 'Webpc-Nonce';
|
||||
|
||||
/**
|
||||
* @var CronInitiator
|
||||
*/
|
||||
private $cron_initiator;
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronInitiator $cron_initiator = null,
|
||||
CronStatusManager $cron_status_manager = null
|
||||
) {
|
||||
$this->cron_initiator = $cron_initiator ?: new CronInitiator( $plugin_data, $token_repository, $format_factory );
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'cron-conversion';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::CREATABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_valid_request( string $request_nonce ): bool {
|
||||
$nonce_value = $this->cron_status_manager->get_conversion_request_id();
|
||||
if ( $nonce_value === null ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ( $request_nonce === $nonce_value );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_url(): string {
|
||||
return get_rest_url(
|
||||
null,
|
||||
sprintf(
|
||||
'%1$s/%2$s',
|
||||
EndpointIntegrator::ROUTE_NAMESPACE,
|
||||
self::get_route_name()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_nonce(): string {
|
||||
return ( new CronStatusManager() )->refresh_conversion_request_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_nonce_header(): string {
|
||||
return self::ROUTE_NONCE_HEADER;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$request_id = $request->get_header( $this->get_route_nonce_header() ) ?: '';
|
||||
$this->cron_initiator->init_conversion( $request_id );
|
||||
|
||||
return new \WP_REST_Response( null, 200 );
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
/**
|
||||
* Abstract class for class that supports image conversion method.
|
||||
*/
|
||||
abstract class EndpointAbstract implements EndpointInterface {
|
||||
|
||||
const ROUTE_NONCE_HEADER = 'X-WP-Nonce';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_valid_request( string $request_nonce ): bool {
|
||||
return (bool) wp_verify_nonce( $request_nonce, 'wp_rest' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_args(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_url(): string {
|
||||
return get_rest_url(
|
||||
null,
|
||||
sprintf(
|
||||
'%1$s/%2$s',
|
||||
EndpointIntegrator::ROUTE_NAMESPACE,
|
||||
static::get_route_name()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_nonce(): string {
|
||||
return wp_create_nonce( 'wp_rest' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_nonce_header(): string {
|
||||
return self::ROUTE_NONCE_HEADER;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
|
||||
/**
|
||||
* Integrates endpoint class by registering REST API route.
|
||||
*/
|
||||
class EndpointIntegrator implements HookableInterface {
|
||||
|
||||
const ROUTE_NAMESPACE = 'webp-converter/v1';
|
||||
|
||||
/**
|
||||
* Objects of supported REST API endpoints.
|
||||
*
|
||||
* @var EndpointInterface
|
||||
*/
|
||||
private $endpoint_object;
|
||||
|
||||
public function __construct( EndpointInterface $endpoint_object ) {
|
||||
$this->endpoint_object = $endpoint_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'rest_api_init', [ $this, 'register_rest_route' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new endpoint in REST API.
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function register_rest_route() {
|
||||
register_rest_route(
|
||||
self::ROUTE_NAMESPACE,
|
||||
$this->endpoint_object->get_route_name(),
|
||||
[
|
||||
'methods' => $this->endpoint_object->get_http_methods(),
|
||||
'permission_callback' => function ( \WP_REST_Request $request ) {
|
||||
$header_value = $request->get_header( $this->endpoint_object->get_route_nonce_header() );
|
||||
if ( $header_value === null ) {
|
||||
return new \WP_Error(
|
||||
'webpc_rest_token_not_found',
|
||||
__( 'Sorry, you do not have permission to do that.' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
|
||||
[ 'status' => rest_authorization_required_code() ]
|
||||
);
|
||||
} elseif ( ! $this->endpoint_object->is_valid_request( $header_value ) ) {
|
||||
return new \WP_Error(
|
||||
'webpc_rest_token_invalid',
|
||||
__( 'Sorry, you do not have permission to do that.' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
|
||||
[ 'status' => rest_authorization_required_code() ]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
'callback' => [ $this, 'get_route_response' ],
|
||||
'args' => $this->endpoint_object->get_route_args(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_REST_Request $request .
|
||||
*
|
||||
* @return \WP_REST_Response|\WP_Error
|
||||
* @internal
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
nocache_headers();
|
||||
do_action( 'litespeed_control_set_nocache', 'Converter for Media' );
|
||||
|
||||
return $this->endpoint_object->get_route_response( $request );
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
/**
|
||||
* Interface for class that supports endpoint.
|
||||
*/
|
||||
interface EndpointInterface {
|
||||
|
||||
/**
|
||||
* Returns route of endpoint.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_route_name(): string;
|
||||
|
||||
/**
|
||||
* Returns methods separated by space.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_http_methods(): string;
|
||||
|
||||
/**
|
||||
* Returns whether request can be executed.
|
||||
*
|
||||
* @param string $request_nonce .
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid_request( string $request_nonce ): bool;
|
||||
|
||||
/**
|
||||
* Returns list of params for endpoint.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function get_route_args(): array;
|
||||
|
||||
/**
|
||||
* Returns URL of endpoint.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_route_url(): string;
|
||||
|
||||
/**
|
||||
* Returns authorization code of endpoint.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_route_nonce(): string;
|
||||
|
||||
/**
|
||||
* Returns header name with nonce value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_route_nonce_header(): string;
|
||||
|
||||
/**
|
||||
* Returns response to endpoint.
|
||||
*
|
||||
* @param \WP_REST_Request $request REST request object.
|
||||
*
|
||||
* @return \WP_REST_Response|\WP_Error
|
||||
* @internal
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request );
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\Conversion\FilesTreeFinder;
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Generated tree of files that can be optimized.
|
||||
*/
|
||||
class FilesStatsEndpoint extends EndpointAbstract {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
public function __construct( PluginData $plugin_data, FormatFactory $format_factory ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'images-stats';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::READABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$allowed_formats = $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
if ( ! in_array( AvifFormat::FORMAT_EXTENSION, $allowed_formats ) ) {
|
||||
$allowed_formats[] = AvifFormat::FORMAT_EXTENSION;
|
||||
}
|
||||
|
||||
$stats_data = ( new FilesTreeFinder( $this->plugin_data, $this->format_factory ) )
|
||||
->get_tree( $allowed_formats );
|
||||
|
||||
return new \WP_REST_Response(
|
||||
[
|
||||
'value_webp_all' => ( $stats_data['files_converted'][ WebpFormat::FORMAT_EXTENSION ] + $stats_data['files_unconverted'][ WebpFormat::FORMAT_EXTENSION ] ),
|
||||
'value_webp_converted' => $stats_data['files_converted'][ WebpFormat::FORMAT_EXTENSION ],
|
||||
'value_avif_all' => ( $stats_data['files_converted'][ AvifFormat::FORMAT_EXTENSION ] + $stats_data['files_unconverted'][ AvifFormat::FORMAT_EXTENSION ] ),
|
||||
'value_avif_converted' => $stats_data['files_converted'][ AvifFormat::FORMAT_EXTENSION ],
|
||||
'tree' => $stats_data['files_tree'],
|
||||
],
|
||||
200
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\Conversion\Cron\CronStatusManager;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\PathsFinder;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
|
||||
/**
|
||||
* Supports endpoint to get list of image paths to be converted.
|
||||
*/
|
||||
class PathsEndpoint extends EndpointAbstract {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronStatusManager $cron_status_manager = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->token_repository = $token_repository;
|
||||
$this->format_factory = $format_factory;
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'paths';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::CREATABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_args(): array {
|
||||
return array_merge(
|
||||
parent::get_route_args(),
|
||||
[
|
||||
'regenerate_force' => [
|
||||
'description' => 'Option to force all images to be converted again (set `1` to enable)',
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'sanitize_callback' => function ( $value ) {
|
||||
return ( (string) $value === '1' );
|
||||
},
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$this->cron_status_manager->set_conversion_status_locked( true, true );
|
||||
$this->cron_status_manager->set_paths_to_conversion( [] );
|
||||
$this->cron_status_manager->set_paths_skipped( [] );
|
||||
|
||||
$params = $request->get_params();
|
||||
$skip_converted = ( $params['regenerate_force'] !== true );
|
||||
$paths = ( new PathsFinder( $this->plugin_data, $this->token_repository, $this->format_factory ) )
|
||||
->get_paths_by_chunks( $skip_converted );
|
||||
|
||||
if ( ! $paths ) {
|
||||
$this->cron_status_manager->set_conversion_status_locked( false );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response(
|
||||
$paths,
|
||||
200
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
/**
|
||||
* Supports endpoint for converting all sub-sizes of attachment.
|
||||
*/
|
||||
class RegenerateAttachmentEndpoint extends EndpointAbstract {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'regenerate-attachment';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::CREATABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_args(): array {
|
||||
return array_merge(
|
||||
parent::get_route_args(),
|
||||
[
|
||||
'post_id' => [
|
||||
'description' => 'Attachment ID',
|
||||
'required' => true,
|
||||
'validate_callback' => function ( $value ) {
|
||||
return is_int( $value );
|
||||
},
|
||||
],
|
||||
'quality_level' => [
|
||||
'description' => 'Conversion strategy',
|
||||
'required' => true,
|
||||
'validate_callback' => function ( $value ) {
|
||||
return in_array(
|
||||
$value,
|
||||
array_merge(
|
||||
apply_filters( 'webpc_option_quality_levels', [ '75', '80', '85', '90', '95' ] ),
|
||||
[ '0' ]
|
||||
)
|
||||
);
|
||||
},
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$params = $request->get_params();
|
||||
|
||||
return new \WP_REST_Response(
|
||||
[
|
||||
'output_html' => $this->convert_images( $params['post_id'], $params['quality_level'] ),
|
||||
],
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes image conversion to output formats.
|
||||
*
|
||||
* @param int $post_id .
|
||||
* @param int $quality_level .
|
||||
*
|
||||
* @return string|false Status of conversion.
|
||||
*/
|
||||
public function convert_images( int $post_id, int $quality_level ) {
|
||||
do_action( 'webpc_convert_attachment', $post_id, true, $quality_level );
|
||||
|
||||
return apply_filters( 'webpc_attachment_stats', '', $post_id, $quality_level );
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\Conversion\Cron\CronStatusManager;
|
||||
use WebpConverter\Conversion\Method\MethodFactory;
|
||||
use WebpConverter\Conversion\Method\MethodIntegrator;
|
||||
use WebpConverter\PluginData;
|
||||
|
||||
/**
|
||||
* Supports endpoint for converting list of paths to images.
|
||||
*/
|
||||
class RegenerateEndpoint extends EndpointAbstract {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var MethodFactory
|
||||
*/
|
||||
private $method_factory;
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
MethodFactory $method_factory,
|
||||
CronStatusManager $cron_status_manager = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->method_factory = $method_factory;
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'regenerate';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::CREATABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_args(): array {
|
||||
return array_merge(
|
||||
parent::get_route_args(),
|
||||
[
|
||||
'regenerate_force' => [
|
||||
'description' => 'Option to force all images to be converted again (set `1` to enable)',
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'sanitize_callback' => function ( $value ) {
|
||||
return ( (string) $value === '1' );
|
||||
},
|
||||
],
|
||||
'paths' => [
|
||||
'description' => 'Array of file paths (server paths)',
|
||||
'required' => true,
|
||||
'default' => [],
|
||||
'validate_callback' => function ( $value ) {
|
||||
return ( is_array( $value ) && $value );
|
||||
},
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$this->cron_status_manager->set_conversion_status_locked();
|
||||
|
||||
$params = $request->get_params();
|
||||
$data = $this->convert_images( $params['paths'], $params['regenerate_force'] );
|
||||
|
||||
if ( $data !== false ) {
|
||||
return new \WP_REST_Response(
|
||||
$data,
|
||||
200
|
||||
);
|
||||
} else {
|
||||
return new \WP_Error(
|
||||
'webpc_rest_api_error',
|
||||
'',
|
||||
[
|
||||
'status' => 405,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes image conversion to output formats.
|
||||
*
|
||||
* @param string[] $paths Server paths of source images.
|
||||
* @param bool $regenerate_force .
|
||||
*
|
||||
* @return mixed[]|false Status of conversion.
|
||||
*/
|
||||
public function convert_images( array $paths, bool $regenerate_force ) {
|
||||
$response = ( new MethodIntegrator( $this->plugin_data, $this->method_factory ) )->init_conversion( $paths, $regenerate_force );
|
||||
if ( $response === null ) {
|
||||
return false;
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\ExcludedDirectoriesOption;
|
||||
|
||||
/**
|
||||
* Removes from list of source directory paths those that are excluded.
|
||||
*/
|
||||
class ExcludedPathsOperator implements HookableInterface {
|
||||
|
||||
const EXCLUDED_SUB_EXTENSIONS = [
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif',
|
||||
'bk',
|
||||
'bak',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $excluded_dirs = [
|
||||
'.',
|
||||
'..',
|
||||
'.git',
|
||||
'.svn',
|
||||
'node_modules',
|
||||
'wpmc-trash',
|
||||
'__MACOSX',
|
||||
'ShortpixelBackups',
|
||||
'backup',
|
||||
'wio_backup',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$saved_dirs = $plugin_settings[ ExcludedDirectoriesOption::OPTION_NAME ];
|
||||
$this->excluded_dirs = array_merge(
|
||||
$this->excluded_dirs,
|
||||
( $saved_dirs !== '' ) ? explode( ',', $saved_dirs ) : []
|
||||
);
|
||||
|
||||
add_filter( 'webpc_supported_source_directory', [ $this, 'skip_excluded_directory' ], 0, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status if the given directory path should be converted.
|
||||
*
|
||||
* @param bool $path_status .
|
||||
* @param string $dirname .
|
||||
* @param string $server_path .
|
||||
*
|
||||
* @return bool Status if the given path is not excluded.
|
||||
* @internal
|
||||
*/
|
||||
public function skip_excluded_directory( bool $path_status, string $dirname, string $server_path ): bool {
|
||||
if ( in_array( $dirname, $this->excluded_dirs ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $path_status;
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Error\Detector\RewritesErrorsDetector;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
use WebpConverter\Service\StatsManager;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Returns tree of paths to files to conversion.
|
||||
*/
|
||||
class FilesTreeFinder {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var ServerConfigurator
|
||||
*/
|
||||
private $server_configurator;
|
||||
|
||||
/**
|
||||
* @var StatsManager
|
||||
*/
|
||||
private $stats_manager;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $files_converted;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $files_unconverted;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
FormatFactory $format_factory,
|
||||
ServerConfigurator $server_configurator = null,
|
||||
StatsManager $stats_manager = null,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->server_configurator = $server_configurator ?: new ServerConfigurator();
|
||||
$this->stats_manager = $stats_manager ?: new StatsManager();
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source images for directory.
|
||||
*
|
||||
* @param string[] $output_formats Allowed extensions.
|
||||
*
|
||||
* @return mixed[] {
|
||||
* @type int[] $files_converted .
|
||||
* @type int[] $files_unconverted .
|
||||
* @type mixed[] $files_tree .
|
||||
* }
|
||||
* @internal
|
||||
*/
|
||||
public function get_tree( array $output_formats ): array {
|
||||
$this->server_configurator->set_memory_limit();
|
||||
$this->server_configurator->set_execution_time( 900 );
|
||||
|
||||
foreach ( $output_formats as $output_format ) {
|
||||
$this->files_converted[ $output_format ] = 0;
|
||||
$this->files_unconverted[ $output_format ] = 0;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$force_convert_deleted = ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
$force_convert_crashed = ( in_array( ExtraFeaturesOption::OPTION_VALUE_SERVICE_MODE, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
|
||||
$values = [];
|
||||
foreach ( $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] as $dir_name ) {
|
||||
$source_dir = apply_filters( 'webpc_dir_path', '', $dir_name );
|
||||
$values[] = $this->find_tree_in_directory(
|
||||
$source_dir,
|
||||
$plugin_settings[ SupportedExtensionsOption::OPTION_NAME ],
|
||||
$output_formats,
|
||||
$force_convert_deleted,
|
||||
$force_convert_crashed
|
||||
);
|
||||
}
|
||||
|
||||
$this->stats_manager->set_images_webp_all( $this->files_converted[ WebpFormat::FORMAT_EXTENSION ] + $this->files_unconverted[ WebpFormat::FORMAT_EXTENSION ] );
|
||||
$this->stats_manager->set_images_webp_unconverted( $this->files_unconverted[ WebpFormat::FORMAT_EXTENSION ] );
|
||||
$this->stats_manager->set_images_avif_all( $this->files_converted[ AvifFormat::FORMAT_EXTENSION ] + $this->files_unconverted[ AvifFormat::FORMAT_EXTENSION ] );
|
||||
$this->stats_manager->set_images_avif_unconverted( $this->files_unconverted[ AvifFormat::FORMAT_EXTENSION ] );
|
||||
|
||||
return [
|
||||
'files_converted' => $this->files_converted,
|
||||
'files_unconverted' => $this->files_unconverted,
|
||||
'files_tree' => $values,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source images as tree.
|
||||
*
|
||||
* @param string $dir_path Server path of source directory.
|
||||
* @param string[] $source_formats Allowed extensions.
|
||||
* @param string[] $output_formats Allowed extensions.
|
||||
* @param bool $force_convert_deleted Skip .deleted files.
|
||||
* @param bool $force_convert_crashed Skip .crashed files.
|
||||
* @param int $nesting_level .
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function find_tree_in_directory(
|
||||
string $dir_path,
|
||||
array $source_formats,
|
||||
array $output_formats,
|
||||
bool $force_convert_deleted,
|
||||
bool $force_convert_crashed,
|
||||
int $nesting_level = 0
|
||||
): array {
|
||||
$paths = scandir( $dir_path );
|
||||
$list = [
|
||||
'name' => basename( $dir_path ),
|
||||
'items' => [],
|
||||
'files' => [],
|
||||
'count' => 0,
|
||||
];
|
||||
if ( ! is_array( $paths ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
if ( $nesting_level === 0 ) {
|
||||
$paths = array_diff( $paths, [ basename( RewritesErrorsDetector::PATH_OUTPUT_FILE_PNG ) ] );
|
||||
}
|
||||
|
||||
sort( $paths, SORT_NATURAL | SORT_FLAG_CASE );
|
||||
foreach ( $paths as $path ) {
|
||||
$current_path = $dir_path . '/' . $path;
|
||||
|
||||
if ( is_dir( $current_path ) ) {
|
||||
if ( apply_filters( 'webpc_supported_source_directory', true, basename( $current_path ), $current_path ) ) {
|
||||
$children = $this->find_tree_in_directory( $current_path, $source_formats, $output_formats, $force_convert_deleted, $force_convert_crashed, ( $nesting_level + 1 ) );
|
||||
if ( $children['items'] || $children['files'] ) {
|
||||
$list['items'][] = $children;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$filename = basename( $current_path );
|
||||
$parts = array_reverse( explode( '.', $filename ) );
|
||||
if ( in_array( strtolower( $parts[0] ?? '' ), $source_formats ) && ! in_array( strtolower( $parts[1] ?? '' ), ExcludedPathsOperator::EXCLUDED_SUB_EXTENSIONS ) ) {
|
||||
if ( apply_filters( 'webpc_supported_source_file', true, $filename, $current_path )
|
||||
&& ! $this->is_converted_file( $current_path, $output_formats, $force_convert_deleted, $force_convert_crashed ) ) {
|
||||
$list['files'][] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$list['count'] = $this->calculate_tree_count( $list );
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
* @param string[] $output_formats .
|
||||
* @param bool $force_convert_deleted Skip .deleted files.
|
||||
* @param bool $force_convert_crashed Skip .crashed files.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_converted_file( string $source_path, array $output_formats, bool $force_convert_deleted, bool $force_convert_crashed ): bool {
|
||||
$is_not_converted = false;
|
||||
|
||||
foreach ( $output_formats as $output_format ) {
|
||||
$output_path = $this->output_path->get_path( $source_path, false, $output_format );
|
||||
if ( $output_path === null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( file_exists( $output_path ) ) {
|
||||
$this->files_converted[ $output_format ]++;
|
||||
} elseif ( ! $force_convert_deleted && file_exists( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION ) ) {
|
||||
$this->files_converted[ $output_format ]++;
|
||||
} elseif ( ! $force_convert_crashed && file_exists( $output_path . '.' . CrashedFilesOperator::CRASHED_FILE_EXTENSION ) ) {
|
||||
$this->files_converted[ $output_format ]++;
|
||||
} else {
|
||||
$this->files_unconverted[ $output_format ]++;
|
||||
$is_not_converted = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ! $is_not_converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $tree .
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function calculate_tree_count( array $tree ): int {
|
||||
$count = count( $tree['files'] );
|
||||
|
||||
foreach ( $tree['items'] as $tree_item ) {
|
||||
$count += $this->calculate_tree_count( $tree_item );
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
|
||||
/**
|
||||
* Supports AVIF as output format for images.
|
||||
*/
|
||||
class AvifFormat extends FormatAbstract {
|
||||
|
||||
const FORMAT_EXTENSION = 'avif';
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
public function __construct( TokenRepository $token_repository ) {
|
||||
$this->token_repository = $token_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_extension(): string {
|
||||
return self::FORMAT_EXTENSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_mime_type(): string {
|
||||
return 'image/avif';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
if ( $this->token_repository->get_token()->get_valid_status() ) {
|
||||
return 'AVIF';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%1$s (%2$s)',
|
||||
'AVIF',
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'available in %1$sthe PRO version%2$s', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-field-output-format-avif-upgrade" target="_blank">',
|
||||
'</a>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available( string $conversion_method ): bool {
|
||||
return ( $this->token_repository->get_token()->get_valid_status() && ( $conversion_method === RemoteMethod::METHOD_NAME ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
use WebpConverter\Conversion\Method\GdMethod;
|
||||
use WebpConverter\Conversion\Method\ImagickMethod;
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
|
||||
/**
|
||||
* Abstract class for class that supports output format for images.
|
||||
*/
|
||||
abstract class FormatAbstract implements FormatInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return sprintf( '.%s', $this->get_extension() );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available( string $conversion_method ): bool {
|
||||
switch ( $conversion_method ) {
|
||||
case ImagickMethod::METHOD_NAME:
|
||||
return ImagickMethod::is_method_active( $this->get_extension() );
|
||||
case GdMethod::METHOD_NAME:
|
||||
return GdMethod::is_method_active( $this->get_extension() );
|
||||
case RemoteMethod::METHOD_NAME:
|
||||
return RemoteMethod::is_method_active( $this->get_extension() );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
|
||||
/**
|
||||
* Adds support for all output formats and returns information about them.
|
||||
*/
|
||||
class FormatFactory {
|
||||
|
||||
/**
|
||||
* Objects of supported output formats.
|
||||
*
|
||||
* @var FormatInterface[]
|
||||
*/
|
||||
private $formats = [];
|
||||
|
||||
/**
|
||||
* @var string[][]
|
||||
*/
|
||||
private $available_formats = [];
|
||||
|
||||
public function __construct( TokenRepository $token_repository ) {
|
||||
$this->set_integration( new AvifFormat( $token_repository ) );
|
||||
$this->set_integration( new WebpFormat() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets integration for format.
|
||||
*
|
||||
* @param FormatInterface $format .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_integration( FormatInterface $format ) {
|
||||
$this->formats[] = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of output formats.
|
||||
*
|
||||
* @return string[] Extensions of output formats with labels.
|
||||
*/
|
||||
public function get_formats(): array {
|
||||
$values = [];
|
||||
foreach ( $this->formats as $format ) {
|
||||
$values[ $format->get_extension() ] = $format->get_label();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of available output formats.
|
||||
*
|
||||
* @param string|null $conversion_method Name of conversion method.
|
||||
*
|
||||
* @return string[] Extensions of output formats with labels.
|
||||
*/
|
||||
public function get_available_formats( string $conversion_method = null ): array {
|
||||
if ( $conversion_method === null ) {
|
||||
return [];
|
||||
} elseif ( isset( $this->available_formats[ $conversion_method ] ) ) {
|
||||
return $this->available_formats[ $conversion_method ];
|
||||
}
|
||||
|
||||
$this->available_formats[ $conversion_method ] = [];
|
||||
foreach ( $this->formats as $format ) {
|
||||
if ( ! $format->is_available( $conversion_method ) ) {
|
||||
continue;
|
||||
}
|
||||
$this->available_formats[ $conversion_method ][ $format->get_extension() ] = $format->get_label();
|
||||
}
|
||||
return $this->available_formats[ $conversion_method ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reset_available_formats() {
|
||||
$this->available_formats = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns extensions of output formats.
|
||||
*
|
||||
* @return string[] Extensions of output formats.
|
||||
*/
|
||||
public function get_format_extensions(): array {
|
||||
$values = [];
|
||||
foreach ( $this->formats as $format ) {
|
||||
$values[] = $format->get_extension();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns mime types of output formats.
|
||||
*
|
||||
* @param string[]|null $output_formats Extensions of output formats.
|
||||
*
|
||||
* @return string[] Mime types of output formats.
|
||||
*/
|
||||
public function get_mime_types( array $output_formats = null ): array {
|
||||
$values = [];
|
||||
foreach ( $this->formats as $format ) {
|
||||
if ( ( $output_formats !== null ) && ! in_array( $format->get_extension(), $output_formats ) ) {
|
||||
continue;
|
||||
}
|
||||
$values[ $format->get_extension() ] = $format->get_mime_type();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
/**
|
||||
* Interface for class that supports output format for images.
|
||||
*/
|
||||
interface FormatInterface {
|
||||
|
||||
/**
|
||||
* Returns extension of output format.
|
||||
*
|
||||
* @return string Format extension
|
||||
*/
|
||||
public function get_extension(): string;
|
||||
|
||||
/**
|
||||
* Returns mime type of output format.
|
||||
*
|
||||
* @return string Format mime type
|
||||
*/
|
||||
public function get_mime_type(): string;
|
||||
|
||||
/**
|
||||
* Returns label of output format.
|
||||
*
|
||||
* @return string Format label.
|
||||
*/
|
||||
public function get_label(): string;
|
||||
|
||||
/**
|
||||
* Returns status is output format available?
|
||||
*
|
||||
* @param string $conversion_method Type of conversion method.
|
||||
*
|
||||
* @return bool Is format available?
|
||||
*/
|
||||
public function is_available( string $conversion_method ): bool;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
/**
|
||||
* Supports WebP as output format for images.
|
||||
*/
|
||||
class WebpFormat extends FormatAbstract {
|
||||
|
||||
const FORMAT_EXTENSION = 'webp';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_extension(): string {
|
||||
return self::FORMAT_EXTENSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_mime_type(): string {
|
||||
return 'image/webp';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return 'WebP';
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Exception;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
|
||||
/**
|
||||
* Deletes output after conversion if it is larger than original.
|
||||
*/
|
||||
class LargerFilesOperator {
|
||||
|
||||
const DELETED_FILE_EXTENSION = 'deleted';
|
||||
|
||||
/**
|
||||
* Removes converted output image if it is larger than original image.
|
||||
*
|
||||
* @param string $output_path .
|
||||
* @param string $source_path .
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception\LargerThanOriginalException
|
||||
*/
|
||||
public function remove_image_if_is_larger( string $output_path, string $source_path, array $plugin_settings ) {
|
||||
if ( file_exists( $output_path . '.' . self::DELETED_FILE_EXTENSION ) ) {
|
||||
unlink( $output_path . '.' . self::DELETED_FILE_EXTENSION );
|
||||
}
|
||||
|
||||
if ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] )
|
||||
|| ( ! file_exists( $output_path ) || ! file_exists( $source_path ) )
|
||||
|| ( filesize( $output_path ) < filesize( $source_path ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file = fopen( $output_path . '.' . self::DELETED_FILE_EXTENSION, 'w' );
|
||||
if ( $file !== false ) {
|
||||
fclose( $file );
|
||||
unlink( $output_path );
|
||||
}
|
||||
|
||||
throw new Exception\LargerThanOriginalException( [ $source_path, $output_path ] );
|
||||
}
|
||||
}
|
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Exception\ConversionErrorException;
|
||||
use WebpConverter\Exception\ExtensionUnsupportedException;
|
||||
use WebpConverter\Exception\FunctionUnavailableException;
|
||||
use WebpConverter\Exception\ImageAnimatedException;
|
||||
use WebpConverter\Exception\ImageInvalidException;
|
||||
use WebpConverter\Exception\ResolutionOversizeException;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Supports image conversion method using GD library.
|
||||
*/
|
||||
class GdMethod extends LibraryMethodAbstract {
|
||||
|
||||
const METHOD_NAME = 'gd';
|
||||
const MAX_METHOD_QUALITY = 99.9;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return self::METHOD_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return 'GD';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_installed(): bool {
|
||||
return ( extension_loaded( 'gd' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_active( string $format ): bool {
|
||||
if ( ! self::is_method_installed() || ! ( $function = self::get_format_function( $format ) ) ) {
|
||||
return false;
|
||||
}
|
||||
return function_exists( $function );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of function to convert source image to output image.
|
||||
*
|
||||
* @param string $format Extension of output format.
|
||||
*
|
||||
* @return string|null Function name using for conversion.
|
||||
*/
|
||||
private static function get_format_function( string $format ) {
|
||||
switch ( $format ) {
|
||||
case WebpFormat::FORMAT_EXTENSION:
|
||||
return 'imagewebp';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return resource Image object.
|
||||
* @throws ExtensionUnsupportedException
|
||||
* @throws FunctionUnavailableException
|
||||
* @throws ImageInvalidException
|
||||
* @throws ImageAnimatedException
|
||||
*/
|
||||
public function create_image_by_path( string $source_path, array $plugin_settings ) {
|
||||
$extension = strtolower( pathinfo( $source_path, PATHINFO_EXTENSION ) );
|
||||
$methods = apply_filters(
|
||||
'webpc_gd_create_methods',
|
||||
[
|
||||
'imagecreatefromjpeg' => [ 'jpg', 'jpeg' ],
|
||||
'imagecreatefrompng' => [ 'png' ],
|
||||
'imagecreatefromgif' => [ 'gif' ],
|
||||
]
|
||||
);
|
||||
|
||||
if ( ( $extension === 'gif' ) && $this->is_animated( $source_path ) ) {
|
||||
throw new ImageAnimatedException( $source_path );
|
||||
}
|
||||
|
||||
foreach ( $methods as $method => $extensions ) {
|
||||
if ( ! in_array( $extension, $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] )
|
||||
|| ! in_array( $extension, $extensions ) ) {
|
||||
continue;
|
||||
} elseif ( ! function_exists( $method ) ) {
|
||||
throw new FunctionUnavailableException( $method );
|
||||
} elseif ( ! $image = @$method( $source_path ) ) { // phpcs:ignore
|
||||
throw new ImageInvalidException( $source_path );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $image ) ) {
|
||||
throw new ExtensionUnsupportedException( [ $extension, $source_path ] );
|
||||
}
|
||||
|
||||
$exif = ( function_exists( 'exif_read_data' ) )
|
||||
? ( @exif_read_data( $source_path ) ?: [] ) // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
: [];
|
||||
|
||||
switch ( $exif['Orientation'] ?? '' ) {
|
||||
case 2:
|
||||
imageflip( $image, IMG_FLIP_HORIZONTAL );
|
||||
break;
|
||||
case 3:
|
||||
$image = imagerotate( $image, 180, 0 );
|
||||
break;
|
||||
case 4:
|
||||
imageflip( $image, IMG_FLIP_HORIZONTAL );
|
||||
$image = imagerotate( $image, 180, 0 );
|
||||
break;
|
||||
case 5:
|
||||
imageflip( $image, IMG_FLIP_VERTICAL );
|
||||
$image = imagerotate( $image, -90, 0 );
|
||||
break;
|
||||
case 6:
|
||||
$image = imagerotate( $image, -90, 0 );
|
||||
break;
|
||||
case 7:
|
||||
imageflip( $image, IMG_FLIP_VERTICAL );
|
||||
$image = imagerotate( $image, 90, 0 );
|
||||
break;
|
||||
case 8:
|
||||
$image = imagerotate( $image, 90, 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->update_image_resource( $image, $extension );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates image object before converting to output format.
|
||||
*
|
||||
* @param resource $image Image object.
|
||||
* @param string $extension Extension of output format.
|
||||
*
|
||||
* @return resource Image object.
|
||||
* @throws FunctionUnavailableException
|
||||
*/
|
||||
private function update_image_resource( $image, string $extension ) {
|
||||
if ( ! function_exists( 'imageistruecolor' ) ) {
|
||||
throw new FunctionUnavailableException( 'imageistruecolor' );
|
||||
}
|
||||
|
||||
if ( ! imageistruecolor( $image ) ) {
|
||||
if ( ! function_exists( 'imagepalettetotruecolor' ) ) {
|
||||
throw new FunctionUnavailableException( 'imagepalettetotruecolor' );
|
||||
}
|
||||
imagepalettetotruecolor( $image );
|
||||
}
|
||||
|
||||
switch ( $extension ) {
|
||||
case 'png':
|
||||
if ( ! function_exists( 'imagealphablending' ) ) {
|
||||
throw new FunctionUnavailableException( 'imagealphablending' );
|
||||
}
|
||||
imagealphablending( $image, false );
|
||||
|
||||
if ( ! function_exists( 'imagesavealpha' ) ) {
|
||||
throw new FunctionUnavailableException( 'imagesavealpha' );
|
||||
}
|
||||
imagesavealpha( $image, true );
|
||||
break;
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws ConversionErrorException
|
||||
* @throws FunctionUnavailableException
|
||||
* @throws ResolutionOversizeException
|
||||
*/
|
||||
public function convert_image_to_output( $image, string $source_path, string $output_path, string $format, array $plugin_settings ) {
|
||||
$function = self::get_format_function( $format );
|
||||
if ( $function === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$image = apply_filters( 'webpc_gd_before_saving', $image, $source_path );
|
||||
$output_quality = min( $plugin_settings[ ImagesQualityOption::OPTION_NAME ], self::MAX_METHOD_QUALITY );
|
||||
|
||||
if ( ! function_exists( $function ) ) {
|
||||
throw new FunctionUnavailableException( $function );
|
||||
} elseif ( ( imagesx( $image ) > 8192 ) || ( imagesy( $image ) > 8192 ) ) {
|
||||
throw new ResolutionOversizeException( $source_path );
|
||||
} elseif ( is_callable( $function ) && ! $function( $image, $output_path, $output_quality ) ) {
|
||||
throw new ConversionErrorException( $source_path );
|
||||
}
|
||||
|
||||
if ( filesize( $output_path ) % 2 === 1 ) {
|
||||
file_put_contents( $output_path, "\0", FILE_APPEND );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.imagecreatefromgif.php#104473
|
||||
*/
|
||||
private function is_animated( string $source_path ): bool {
|
||||
if ( ! ( $fh = @fopen( $source_path, 'rb' ) ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
return false;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
while ( ! feof( $fh ) && ( $count < 2 ) ) {
|
||||
$chunk = fread( $fh, 1024 * 100 );
|
||||
$count = $count + preg_match_all( '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk ?: '', $matches );
|
||||
}
|
||||
|
||||
fclose( $fh );
|
||||
return ( $count > 1 );
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Exception\ConversionErrorException;
|
||||
use WebpConverter\Exception\ExtensionUnsupportedException;
|
||||
use WebpConverter\Exception\ImageAnimatedException;
|
||||
use WebpConverter\Exception\ImageInvalidException;
|
||||
use WebpConverter\Exception\ImagickNotSupportWebpException;
|
||||
use WebpConverter\Exception\ImagickUnavailableException;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Supports image conversion method using Imagick library.
|
||||
*/
|
||||
class ImagickMethod extends LibraryMethodAbstract {
|
||||
|
||||
const METHOD_NAME = 'imagick';
|
||||
const MAX_METHOD_QUALITY = 99.9;
|
||||
const PROTECTED_IMAGE_PROFILES = [
|
||||
'icc',
|
||||
'icm',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return self::METHOD_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return 'Imagick';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_installed(): bool {
|
||||
return ( extension_loaded( 'imagick' ) && class_exists( '\Imagick' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_active( string $format ): bool {
|
||||
if ( ! self::is_method_installed()
|
||||
|| ! ( $formats = \Imagick::queryformats( 'WEBP' ) )
|
||||
|| ! ( $extension = self::get_format_extension( $format ) ) ) {
|
||||
return false;
|
||||
}
|
||||
return in_array( $extension, $formats );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of supported format to convert source image to output image.
|
||||
*
|
||||
* @param string $format Extension of output format.
|
||||
*
|
||||
* @return string|null Supported format using for conversion.
|
||||
*/
|
||||
private static function get_format_extension( string $format ) {
|
||||
switch ( $format ) {
|
||||
case WebpFormat::FORMAT_EXTENSION:
|
||||
return 'WEBP';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Imagick .
|
||||
* @throws ExtensionUnsupportedException
|
||||
* @throws ImagickUnavailableException
|
||||
* @throws ImageInvalidException
|
||||
* @throws ImageAnimatedException
|
||||
*/
|
||||
public function create_image_by_path( string $source_path, array $plugin_settings ) {
|
||||
$extension = strtolower( pathinfo( $source_path, PATHINFO_EXTENSION ) );
|
||||
|
||||
if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick' ) ) {
|
||||
throw new ImagickUnavailableException();
|
||||
} elseif ( ! in_array( $extension, $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] ) ) {
|
||||
throw new ExtensionUnsupportedException( [ $extension, $source_path ] );
|
||||
}
|
||||
|
||||
try {
|
||||
$imagick = new \Imagick( $source_path );
|
||||
if ( ( $extension === 'gif' ) && ( $imagick->identifyFormat( '%n' ) > 1 ) ) {
|
||||
throw new ImageAnimatedException( $source_path );
|
||||
}
|
||||
|
||||
switch ( $imagick->getImageProperty( 'exif:Orientation' ) ) {
|
||||
case 2:
|
||||
$imagick->flopImage();
|
||||
break;
|
||||
case 3:
|
||||
$imagick->rotateImage( '#000000', 180 );
|
||||
break;
|
||||
case 4:
|
||||
$imagick->flopImage();
|
||||
$imagick->rotateImage( '#000000', 180 );
|
||||
break;
|
||||
case 5:
|
||||
$imagick->flopImage();
|
||||
$imagick->rotateImage( '#000000', -90 );
|
||||
break;
|
||||
case 6:
|
||||
$imagick->rotateImage( '#000000', 90 );
|
||||
break;
|
||||
case 7:
|
||||
$imagick->flopImage();
|
||||
$imagick->rotateImage( '#000000', 90 );
|
||||
break;
|
||||
case 8:
|
||||
$imagick->rotateImage( '#000000', -90 );
|
||||
break;
|
||||
}
|
||||
|
||||
return $imagick;
|
||||
} catch ( \ImagickException $e ) {
|
||||
throw new ImageInvalidException( $source_path );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws ConversionErrorException
|
||||
* @throws ImagickNotSupportWebpException
|
||||
*/
|
||||
public function convert_image_to_output( $image, string $source_path, string $output_path, string $format, array $plugin_settings ) {
|
||||
$extension = self::get_format_extension( $format );
|
||||
$image = apply_filters( 'webpc_imagick_before_saving', $image, $source_path );
|
||||
$output_quality = min( $plugin_settings[ ImagesQualityOption::OPTION_NAME ], self::MAX_METHOD_QUALITY );
|
||||
|
||||
if ( ! in_array( $extension, $image->queryFormats() ) ) {
|
||||
throw new ImagickNotSupportWebpException();
|
||||
}
|
||||
|
||||
$image->setImageFormat( $extension );
|
||||
if ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_KEEP_METADATA, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) ) {
|
||||
$image_profiles = $image->getImageProfiles( '*', true );
|
||||
foreach ( $image_profiles as $profile_name => $image_profile ) {
|
||||
if ( ! in_array( $profile_name, self::PROTECTED_IMAGE_PROFILES ) ) {
|
||||
try {
|
||||
$image->removeImageProfile( $profile_name );
|
||||
} catch ( \ImagickException $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$image->setImageCompressionQuality( $output_quality );
|
||||
$blob = $image->getImageBlob();
|
||||
|
||||
if ( ! file_put_contents( $output_path, $blob ) ) {
|
||||
throw new ConversionErrorException( $source_path );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Exception\ExceptionInterface;
|
||||
use WebpConverter\Exception\LargerThanOriginalException;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Abstract class for class that converts images using the PHP library.
|
||||
*/
|
||||
abstract class LibraryMethodAbstract extends MethodAbstract implements LibraryMethodInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert_paths( array $paths, array $plugin_settings, bool $regenerate_force ) {
|
||||
$output_formats = $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
foreach ( $output_formats as $output_format ) {
|
||||
foreach ( $paths as $path ) {
|
||||
$this->files_available[ $output_format ]++;
|
||||
$this->convert_path( $path, $output_format, $plugin_settings );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts source path to output formats.
|
||||
*
|
||||
* @param string $path Server path of source image.
|
||||
* @param string $format Extension of output format.
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function convert_path( string $path, string $format, array $plugin_settings ) {
|
||||
$this->server_configurator->set_memory_limit();
|
||||
$this->server_configurator->set_execution_time();
|
||||
|
||||
try {
|
||||
$source_path = $this->get_image_source_path( $path );
|
||||
$output_path = $this->get_image_output_path( $source_path, $format );
|
||||
|
||||
$this->skip_crashed->create_crashed_file( $output_path );
|
||||
|
||||
$image = $this->create_image_by_path( $source_path, $plugin_settings );
|
||||
$this->convert_image_to_output( $image, $source_path, $output_path, $format, $plugin_settings );
|
||||
do_action( 'webpc_after_conversion', $output_path, $source_path );
|
||||
|
||||
$this->files_converted[ $format ]++;
|
||||
|
||||
$this->skip_crashed->delete_crashed_file( $output_path );
|
||||
$this->skip_larger->remove_image_if_is_larger( $output_path, $source_path, $plugin_settings );
|
||||
$this->update_conversion_stats( $source_path, $output_path, $format );
|
||||
} catch ( LargerThanOriginalException $e ) {
|
||||
$this->files_converted[ $format ]--;
|
||||
} catch ( ExceptionInterface $e ) {
|
||||
$this->save_conversion_error( $e->getMessage(), $plugin_settings );
|
||||
} catch ( \Exception $e ) {
|
||||
$this->save_conversion_error( $e->getMessage(), $plugin_settings );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
/**
|
||||
* Interface for class that converts images using the PHP library.
|
||||
*/
|
||||
interface LibraryMethodInterface {
|
||||
|
||||
/**
|
||||
* Creates image object based on source path.
|
||||
*
|
||||
* @param string $source_path Server path of source image.
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return mixed Image object.
|
||||
*/
|
||||
public function create_image_by_path( string $source_path, array $plugin_settings );
|
||||
|
||||
/**
|
||||
* Converts image and saves to output location.
|
||||
*
|
||||
* @param mixed $image Image object.
|
||||
* @param string $source_path Server path of source image.
|
||||
* @param string $output_path Server path for output image.
|
||||
* @param string $format Extension of output format.
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function convert_image_to_output( $image, string $source_path, string $output_path, string $format, array $plugin_settings );
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\CrashedFilesOperator;
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Conversion\LargerFilesOperator;
|
||||
use WebpConverter\Conversion\OutputPathGenerator;
|
||||
use WebpConverter\Exception;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
|
||||
/**
|
||||
* Abstract class for class that converts images.
|
||||
*/
|
||||
abstract class MethodAbstract implements MethodInterface {
|
||||
|
||||
/**
|
||||
* @var CrashedFilesOperator
|
||||
*/
|
||||
protected $skip_crashed;
|
||||
|
||||
/**
|
||||
* @var LargerFilesOperator
|
||||
*/
|
||||
protected $skip_larger;
|
||||
|
||||
/**
|
||||
* @var ServerConfigurator
|
||||
*/
|
||||
protected $server_configurator;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
public function __construct(
|
||||
FormatFactory $format_factory,
|
||||
CrashedFilesOperator $skip_crashed,
|
||||
LargerFilesOperator $skip_larger,
|
||||
ServerConfigurator $server_configurator,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->skip_crashed = $skip_crashed;
|
||||
$this->skip_larger = $skip_larger;
|
||||
$this->server_configurator = $server_configurator;
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $is_fatal_error = false;
|
||||
|
||||
/**
|
||||
* Messages of errors that occurred during conversion.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $errors = [];
|
||||
|
||||
/**
|
||||
* Sum of size of source images before conversion.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $size_before = 0;
|
||||
|
||||
/**
|
||||
* Sum of size of output images after conversion.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $size_after = 0;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
protected $files_available = [
|
||||
WebpFormat::FORMAT_EXTENSION => 0,
|
||||
AvifFormat::FORMAT_EXTENSION => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
protected $files_converted = [
|
||||
WebpFormat::FORMAT_EXTENSION => 0,
|
||||
AvifFormat::FORMAT_EXTENSION => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_pro_feature(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_fatal_error(): bool {
|
||||
return $this->is_fatal_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_errors(): array {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_size_before(): int {
|
||||
return $this->size_before;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_files_available( string $output_format ): int {
|
||||
return $this->files_available[ $output_format ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_files_converted( string $output_format ): int {
|
||||
return $this->files_converted[ $output_format ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_size_after(): int {
|
||||
return $this->size_after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks server path of source image.
|
||||
*
|
||||
* @param string $source_path Server path of source image.
|
||||
*
|
||||
* @return string Server path of source image.
|
||||
*
|
||||
* @throws Exception\SourcePathException
|
||||
*/
|
||||
protected function get_image_source_path( string $source_path ): string {
|
||||
if ( ! is_readable( $source_path ) ) {
|
||||
throw new Exception\SourcePathException( $source_path );
|
||||
}
|
||||
|
||||
return $source_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server path for output image.
|
||||
*
|
||||
* @param string $source_path Server path of source image.
|
||||
* @param string $format Extension of output format.
|
||||
*
|
||||
* @return string Server path of output image.
|
||||
*
|
||||
* @throws Exception\OutputPathException
|
||||
*/
|
||||
protected function get_image_output_path( string $source_path, string $format ): string {
|
||||
if ( ! $output_path = $this->output_path->get_path( $source_path, true, $format ) ) {
|
||||
throw new Exception\OutputPathException( $source_path );
|
||||
}
|
||||
|
||||
return $output_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path Server path of source image.
|
||||
* @param string $output_path Server path of output image.
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function update_conversion_stats( string $source_path, string $output_path, string $output_format ) {
|
||||
$output_exist = file_exists( $output_path );
|
||||
$size_before = filesize( $source_path );
|
||||
$size_after = ( $output_exist ) ? filesize( $output_path ) : $size_before;
|
||||
|
||||
$this->size_before += $size_before ?: 0;
|
||||
$this->size_after += $size_after ?: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $error_message .
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param bool $is_fatal_error .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function save_conversion_error( string $error_message, array $plugin_settings, bool $is_fatal_error = false ) {
|
||||
if ( $is_fatal_error ) {
|
||||
$this->is_fatal_error = true;
|
||||
}
|
||||
|
||||
$this->errors[] = $error_message;
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\CrashedFilesOperator;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\LargerFilesOperator;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
|
||||
/**
|
||||
* Adds support for all conversion methods and returns information about them.
|
||||
*/
|
||||
class MethodFactory {
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* @var CrashedFilesOperator
|
||||
*/
|
||||
private $skip_crashed;
|
||||
|
||||
/**
|
||||
* @var LargerFilesOperator
|
||||
*/
|
||||
private $skip_larger;
|
||||
|
||||
/**
|
||||
* @var ServerConfigurator
|
||||
*/
|
||||
private $server_configurator;
|
||||
|
||||
/**
|
||||
* Objects of supported conversion methods.
|
||||
*
|
||||
* @var MethodInterface[]
|
||||
*/
|
||||
private $methods = [];
|
||||
|
||||
public function __construct(
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CrashedFilesOperator $skip_crashed = null,
|
||||
LargerFilesOperator $skip_larger = null,
|
||||
ServerConfigurator $server_configurator = null
|
||||
) {
|
||||
$this->token_repository = $token_repository;
|
||||
$this->format_factory = $format_factory;
|
||||
$this->skip_crashed = $skip_crashed ?: new CrashedFilesOperator();
|
||||
$this->skip_larger = $skip_larger ?: new LargerFilesOperator();
|
||||
$this->server_configurator = $server_configurator ?: new ServerConfigurator();
|
||||
|
||||
$this->set_integration( new ImagickMethod( $format_factory, $this->skip_crashed, $this->skip_larger, $this->server_configurator ) );
|
||||
$this->set_integration( new GdMethod( $format_factory, $this->skip_crashed, $this->skip_larger, $this->server_configurator ) );
|
||||
$this->set_integration( new RemoteMethod( $this->token_repository, $format_factory, $this->skip_crashed, $this->skip_larger, $this->server_configurator ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets integration for method.
|
||||
*
|
||||
* @param MethodInterface $method .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_integration( MethodInterface $method ) {
|
||||
$this->methods[ $method->get_name() ] = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects of conversion methods.
|
||||
*
|
||||
* @return MethodInterface[] .
|
||||
*/
|
||||
public function get_methods_objects(): array {
|
||||
$values = [];
|
||||
foreach ( $this->methods as $method ) {
|
||||
$values[ $method->get_name() ] = $method;
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of conversion methods.
|
||||
*
|
||||
* @return string[] Names of conversion methods with labels.
|
||||
*/
|
||||
public function get_methods(): array {
|
||||
$values = [];
|
||||
foreach ( $this->get_methods_objects() as $method_name => $method ) {
|
||||
$values[ $method_name ] = $method->get_label();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of installed conversion methods.
|
||||
*
|
||||
* @return string[] Names of conversion methods with labels.
|
||||
*/
|
||||
public function get_available_methods(): array {
|
||||
$token_status = $this->token_repository->get_token()->get_valid_status();
|
||||
$values = [];
|
||||
foreach ( $this->get_methods_objects() as $method_name => $method ) {
|
||||
if ( ! $method::is_method_installed()
|
||||
|| ( ! $this->format_factory->get_available_formats( $method_name ) ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ( $token_status && $method::is_pro_feature() ) || ( ! $token_status && ! $method::is_pro_feature() ) ) {
|
||||
$values[ $method_name ] = $method->get_label();
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Service\StatsManager;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Initializes image conversion using active image conversion method.
|
||||
*/
|
||||
class MethodIntegrator {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var MethodFactory
|
||||
*/
|
||||
private $method_factory;
|
||||
|
||||
/**
|
||||
* @var StatsManager
|
||||
*/
|
||||
private $stats_manager;
|
||||
|
||||
public function __construct( PluginData $plugin_data, MethodFactory $method_factory, StatsManager $stats_manager = null ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->method_factory = $method_factory;
|
||||
$this->stats_manager = $stats_manager ?: new StatsManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes converting source images using active and set conversion method.
|
||||
*
|
||||
* @param string[] $paths Server paths for source images.
|
||||
* @param bool $regenerate_force .
|
||||
* @param bool $skip_server_errors .
|
||||
* @param int $quality_level .
|
||||
*
|
||||
* @return mixed[]|null Results data of conversion.
|
||||
*/
|
||||
public function init_conversion( array $paths, bool $regenerate_force, bool $skip_server_errors = false, int $quality_level = null ) {
|
||||
if ( ! $skip_server_errors && apply_filters( 'webpc_server_errors', [], true ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$method = $this->get_method_used();
|
||||
if ( $method === null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $quality_level !== null ) {
|
||||
$plugin_settings[ ImagesQualityOption::OPTION_NAME ] = $quality_level;
|
||||
}
|
||||
|
||||
$this->stats_manager->set_images_webp_unconverted();
|
||||
$this->stats_manager->set_images_avif_unconverted();
|
||||
|
||||
$method->convert_paths( $paths, $plugin_settings, $regenerate_force );
|
||||
return [
|
||||
'is_fatal_error' => $method->is_fatal_error(),
|
||||
'errors' => apply_filters( 'webpc_convert_errors', $method->get_errors() ),
|
||||
'files' => [
|
||||
'webp_available' => $method->get_files_available( WebpFormat::FORMAT_EXTENSION ),
|
||||
'webp_converted' => $method->get_files_converted( WebpFormat::FORMAT_EXTENSION ),
|
||||
'avif_available' => $method->get_files_available( AvifFormat::FORMAT_EXTENSION ),
|
||||
'avif_converted' => $method->get_files_converted( AvifFormat::FORMAT_EXTENSION ),
|
||||
],
|
||||
'size' => [
|
||||
'before' => $method->get_size_before(),
|
||||
'after' => $method->get_size_after(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns active and set conversion method.
|
||||
*
|
||||
* @return MethodInterface|null Object of conversion method.
|
||||
*/
|
||||
public function get_method_used() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$output_formats = $plugin_settings[ OutputFormatsOption::OPTION_NAME ] ?? null;
|
||||
if ( ! $output_formats ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$method_key = $plugin_settings[ ConversionMethodOption::OPTION_NAME ] ?? null;
|
||||
$methods = $this->method_factory->get_methods_objects();
|
||||
foreach ( $methods as $method_name => $method ) {
|
||||
if ( $method_key === $method_name ) {
|
||||
return $method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
/**
|
||||
* Interface for class that converts images.
|
||||
*/
|
||||
interface MethodInterface {
|
||||
|
||||
/**
|
||||
* Returns name of conversion method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name(): string;
|
||||
|
||||
/**
|
||||
* Returns label of conversion method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_label(): string;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_pro_feature(): bool;
|
||||
|
||||
/**
|
||||
* Returns status of whether method is installed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_method_installed(): bool;
|
||||
|
||||
/**
|
||||
* Returns status of whether method is active.
|
||||
*
|
||||
* @param string $format Extension of output format.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_method_active( string $format ): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_fatal_error(): bool;
|
||||
|
||||
/**
|
||||
* Returns errors generated during image conversion.
|
||||
*
|
||||
* @return string[] Errors messages.
|
||||
*/
|
||||
public function get_errors(): array;
|
||||
|
||||
/**
|
||||
* Returns weight of source files before converting.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_size_before(): int;
|
||||
|
||||
/**
|
||||
* Returns weight of output files after converting.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_size_after(): int;
|
||||
|
||||
/**
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_files_available( string $output_format ): int;
|
||||
|
||||
/**
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_files_converted( string $output_format ): int;
|
||||
|
||||
/**
|
||||
* Converts source paths to output formats.
|
||||
*
|
||||
* @param string[] $paths Server paths of source images.
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param bool $regenerate_force .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function convert_paths( array $paths, array $plugin_settings, bool $regenerate_force );
|
||||
}
|
@ -0,0 +1,431 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\CrashedFilesOperator;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\LargerFilesOperator;
|
||||
use WebpConverter\Exception\ExceptionInterface;
|
||||
use WebpConverter\Exception\FilesizeOversizeException;
|
||||
use WebpConverter\Exception\LargerThanOriginalException;
|
||||
use WebpConverter\Exception\OutputPathException;
|
||||
use WebpConverter\Exception\RemoteErrorResponseException;
|
||||
use WebpConverter\Exception\RemoteRequestException;
|
||||
use WebpConverter\Exception\SourcePathException;
|
||||
use WebpConverter\Model\Token;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
use WebpConverter\Settings\Option\AccessTokenOption;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\ImageResizeOption;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\WebpConverterConstants;
|
||||
|
||||
/**
|
||||
* Supports image conversion method using remote API.
|
||||
*/
|
||||
class RemoteMethod extends MethodAbstract {
|
||||
|
||||
const METHOD_NAME = 'remote';
|
||||
const MAX_FILESIZE_BYTES = ( 32 * 1024 * 1024 );
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var Token
|
||||
*/
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $failed_converted_source_files = [];
|
||||
|
||||
public function __construct(
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CrashedFilesOperator $skip_crashed,
|
||||
LargerFilesOperator $skip_larger,
|
||||
ServerConfigurator $server_configurator
|
||||
) {
|
||||
parent::__construct( $format_factory, $skip_crashed, $skip_larger, $server_configurator );
|
||||
$this->token_repository = $token_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return self::METHOD_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
if ( $this->token_repository->get_token()->get_valid_status() ) {
|
||||
return __( 'Remote server', 'webp-converter-for-media' );
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%1$s (%2$s)',
|
||||
__( 'Remote server', 'webp-converter-for-media' ),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'available in %1$sthe PRO version%2$s', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-field-conversion-method-remote-upgrade" target="_blank">',
|
||||
'</a>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_pro_feature(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_installed(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_active( string $format ): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert_paths( array $paths, array $plugin_settings, bool $regenerate_force ) {
|
||||
$this->server_configurator->set_memory_limit();
|
||||
$this->server_configurator->set_execution_time();
|
||||
|
||||
$output_formats = $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
$force_convert_deleted = ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
|
||||
$source_paths = [];
|
||||
$output_paths = [];
|
||||
$this->token = $this->token_repository->get_token();
|
||||
|
||||
foreach ( $output_formats as $output_format ) {
|
||||
try {
|
||||
$file_paths = $this->get_source_paths( $paths, $plugin_settings, $output_format );
|
||||
if ( ! $file_paths ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->files_available[ $output_format ] += count( $file_paths );
|
||||
|
||||
$output_paths[ $output_format ] = $this->get_output_paths( $file_paths, $output_format );
|
||||
$source_paths[ $output_format ] = $file_paths;
|
||||
} catch ( ExceptionInterface $e ) {
|
||||
$this->save_conversion_error( $e->getMessage(), $plugin_settings );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $regenerate_force ) {
|
||||
foreach ( $source_paths as $output_format => $extensions_paths ) {
|
||||
foreach ( $extensions_paths as $path_index => $extensions_path ) {
|
||||
if ( file_exists( $output_paths[ $output_format ][ $path_index ] )
|
||||
|| ( ! $force_convert_deleted && file_exists( $output_paths[ $output_format ][ $path_index ] . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION ) ) ) {
|
||||
unset( $source_paths[ $output_format ][ $path_index ] );
|
||||
unset( $output_paths[ $output_format ][ $path_index ] );
|
||||
|
||||
$this->files_available[ $output_format ]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$converted_files = $this->init_connections( $source_paths, $plugin_settings, $output_paths );
|
||||
$this->save_converted_files( $converted_files, $source_paths, $output_paths, $plugin_settings );
|
||||
|
||||
if ( $this->failed_converted_source_files ) {
|
||||
$converted_files = $this->init_connections( $this->failed_converted_source_files, $plugin_settings, $output_paths );
|
||||
$this->save_converted_files( $converted_files, $source_paths, $output_paths, $plugin_settings );
|
||||
}
|
||||
} catch ( RemoteErrorResponseException $e ) {
|
||||
$this->save_conversion_error( $e->getMessage(), $plugin_settings, true );
|
||||
}
|
||||
|
||||
$this->token_repository->update_token( $this->token );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $converted_files .
|
||||
* @param mixed[] $source_paths .
|
||||
* @param mixed[] $output_paths .
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function save_converted_files( array $converted_files, array $source_paths, array $output_paths, array $plugin_settings ) {
|
||||
foreach ( $converted_files as $output_format => $format_converted_files ) {
|
||||
foreach ( $format_converted_files as $path_index => $converted_file ) {
|
||||
$source_path = $source_paths[ $output_format ][ $path_index ];
|
||||
$output_path = $output_paths[ $output_format ][ $path_index ];
|
||||
|
||||
file_put_contents( $output_path, $converted_file );
|
||||
do_action( 'webpc_after_conversion', $output_path, $source_path );
|
||||
|
||||
try {
|
||||
$this->skip_crashed->delete_crashed_file( $output_path );
|
||||
$this->skip_larger->remove_image_if_is_larger( $output_path, $source_path, $plugin_settings );
|
||||
$this->update_conversion_stats( $source_path, $output_path, $output_format );
|
||||
} catch ( LargerThanOriginalException $e ) {
|
||||
$this->files_converted[ $output_format ]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $paths .
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @throws SourcePathException
|
||||
* @throws OutputPathException
|
||||
*/
|
||||
private function get_source_paths( array $paths, array $plugin_settings, string $output_format ): array {
|
||||
$max_filesize = apply_filters( 'webpc_remote_max_filesize', self::MAX_FILESIZE_BYTES );
|
||||
$source_paths = [];
|
||||
|
||||
foreach ( $paths as $path ) {
|
||||
$source_path = $this->get_image_source_path( $path );
|
||||
if ( filesize( $source_path ) > $max_filesize ) {
|
||||
$this->save_conversion_error(
|
||||
( new FilesizeOversizeException( [ $max_filesize, $source_path ] ) )->getMessage(),
|
||||
$plugin_settings
|
||||
);
|
||||
$this->skip_crashed->create_crashed_file( $this->get_image_output_path( $source_path, $output_format ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
$path_extension = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
|
||||
if ( $path_extension === $output_format ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$source_paths[] = $this->get_image_source_path( $path );
|
||||
}
|
||||
|
||||
return $source_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $source_paths .
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @throws OutputPathException
|
||||
*/
|
||||
private function get_output_paths( array $source_paths, string $output_format ): array {
|
||||
$output_path = [];
|
||||
foreach ( $source_paths as $path ) {
|
||||
$output_path[] = $this->get_image_output_path( $path, $output_format );
|
||||
}
|
||||
|
||||
return $output_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $source_paths .
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param mixed[] $output_paths .
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @throws RemoteErrorResponseException
|
||||
*/
|
||||
private function init_connections( array $source_paths, array $plugin_settings, array $output_paths ): array {
|
||||
$mh_items = [];
|
||||
$values = [];
|
||||
|
||||
$mh = curl_multi_init();
|
||||
if ( ! $mh ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
foreach ( $source_paths as $output_format => $format_source_paths ) {
|
||||
foreach ( $format_source_paths as $resource_id => $source_path ) {
|
||||
$connect = $this->get_curl_connection( $source_path, $output_format, $plugin_settings );
|
||||
if ( ! $connect ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
curl_multi_add_handle( $mh, $connect );
|
||||
$mh_items[ $output_format ] = $mh_items[ $output_format ] ?? [];
|
||||
$mh_items[ $output_format ][ $resource_id ] = $connect;
|
||||
}
|
||||
}
|
||||
|
||||
$running = null;
|
||||
do {
|
||||
curl_multi_exec( $mh, $running );
|
||||
curl_multi_select( $mh );
|
||||
} while ( $running > 0 );
|
||||
|
||||
foreach ( $mh_items as $output_format => $format_mh_items ) {
|
||||
foreach ( $format_mh_items as $resource_id => $mh_item ) {
|
||||
$http_code = curl_getinfo( $mh_item, CURLINFO_HTTP_CODE );
|
||||
$response = curl_multi_getcontent( $mh_item );
|
||||
|
||||
if ( ( $http_code === 200 ) && ( strlen( $response ) > 10 ) ) {
|
||||
$values[ $output_format ] = $values[ $output_format ] ?? [];
|
||||
$values[ $output_format ][ $resource_id ] = $response;
|
||||
|
||||
$this->files_converted[ $output_format ]++;
|
||||
} else {
|
||||
$this->handle_request_error(
|
||||
$source_paths[ $output_format ][ $resource_id ],
|
||||
$output_paths[ $output_format ][ $resource_id ],
|
||||
$output_format,
|
||||
(int) $resource_id,
|
||||
$plugin_settings,
|
||||
$http_code,
|
||||
$response
|
||||
);
|
||||
}
|
||||
curl_multi_remove_handle( $mh, $mh_item );
|
||||
}
|
||||
}
|
||||
|
||||
curl_multi_close( $mh );
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
* @param string $output_format .
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return resource|null
|
||||
*/
|
||||
private function get_curl_connection( string $source_path, string $output_format, array $plugin_settings ) {
|
||||
$connect = curl_init( WebpConverterConstants::API_CONVERSION_URL );
|
||||
if ( ! $connect ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
curl_setopt( $connect, CURLOPT_SSL_VERIFYPEER, false );
|
||||
curl_setopt( $connect, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $connect, CURLOPT_TIMEOUT, apply_filters( 'webpc_remote_timeout', 30 ) );
|
||||
curl_setopt( $connect, CURLOPT_POST, true );
|
||||
curl_setopt(
|
||||
$connect,
|
||||
CURLOPT_POSTFIELDS,
|
||||
[
|
||||
'access_token' => $plugin_settings[ AccessTokenOption::OPTION_NAME ],
|
||||
'domain_host' => parse_url( get_site_url(), PHP_URL_HOST ),
|
||||
'source_file' => curl_file_create( $source_path ),
|
||||
'output_format' => $output_format,
|
||||
'quality_level' => $plugin_settings[ ImagesQualityOption::OPTION_NAME ],
|
||||
'strip_metadata' => ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_KEEP_METADATA, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) ),
|
||||
'max_width' => ( $plugin_settings[ ImageResizeOption::OPTION_NAME ][0] === 'yes' )
|
||||
? $plugin_settings[ ImageResizeOption::OPTION_NAME ][1]
|
||||
: 0,
|
||||
'max_height' => ( $plugin_settings[ ImageResizeOption::OPTION_NAME ][0] === 'yes' )
|
||||
? $plugin_settings[ ImageResizeOption::OPTION_NAME ][2]
|
||||
: 0,
|
||||
]
|
||||
);
|
||||
curl_setopt(
|
||||
$connect,
|
||||
CURLOPT_HTTPHEADER,
|
||||
[
|
||||
'Content-Type: multipart/form-data',
|
||||
'Expect:',
|
||||
]
|
||||
);
|
||||
curl_setopt( $connect, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $connect, CURLOPT_HEADERFUNCTION, [ $this, 'handle_request_header' ] );
|
||||
|
||||
return $connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $curl .
|
||||
* @param string $header .
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle_request_header( $curl, string $header ): int {
|
||||
$header_length = strlen( $header );
|
||||
$header_data = explode( ':', $header );
|
||||
if ( count( $header_data ) > 2 ) {
|
||||
return $header_length;
|
||||
}
|
||||
|
||||
$header_key = strtolower( trim( $header_data[0] ) );
|
||||
if ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_LIMIT_USAGE ) {
|
||||
$this->token->set_images_usage( intval( $header_data[1] ) );
|
||||
} elseif ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_LIMIT_MAX ) {
|
||||
$this->token->set_images_limit( intval( $header_data[1] ) );
|
||||
} elseif ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_SUBSCRIPTION_ACTIVE ) {
|
||||
$this->token->set_valid_status( ( trim( $header_data[1] ) === '1' ) );
|
||||
}
|
||||
|
||||
return $header_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
* @param string $output_path .
|
||||
* @param string $output_format .
|
||||
* @param int $resource_id .
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param int $http_code .
|
||||
* @param string|null $response .
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws RemoteErrorResponseException
|
||||
*/
|
||||
private function handle_request_error(
|
||||
string $source_path,
|
||||
string $output_path,
|
||||
string $output_format,
|
||||
int $resource_id,
|
||||
array $plugin_settings,
|
||||
int $http_code,
|
||||
string $response = null
|
||||
) {
|
||||
$response_value = ( $response ) ? json_decode( $response, true ) : [];
|
||||
$error_message = $response_value[ WebpConverterConstants::API_RESPONSE_VALUE_ERROR_MESSAGE ] ?? '';
|
||||
$error_fatal_status = $response_value[ WebpConverterConstants::API_RESPONSE_VALUE_ERROR_FATAL_STATUS ] ?? false;
|
||||
|
||||
if ( $error_message && $error_fatal_status ) {
|
||||
throw new RemoteErrorResponseException( $error_message );
|
||||
} elseif ( $error_message ) {
|
||||
$this->save_conversion_error( $error_message, $plugin_settings );
|
||||
} elseif ( $http_code === 200 ) {
|
||||
$this->skip_crashed->create_crashed_file( $output_path );
|
||||
|
||||
if ( ! isset( $this->failed_converted_source_files[ $output_format ] ) ) {
|
||||
$this->failed_converted_source_files[ $output_format ] = [];
|
||||
}
|
||||
$this->failed_converted_source_files[ $output_format ][ $resource_id ] = $source_path;
|
||||
} else {
|
||||
$this->save_conversion_error(
|
||||
( new RemoteRequestException( [ $http_code, $source_path ] ) )->getMessage(),
|
||||
$plugin_settings
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
|
||||
/**
|
||||
* Generates output paths from source paths.
|
||||
*/
|
||||
class OutputPathGenerator {
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $path_wp_content_dir = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $path_output_dir = null;
|
||||
|
||||
public function __construct( FormatFactory $format_factory ) {
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates output path from path of source image.
|
||||
*
|
||||
* @param string $path Server path of source image.
|
||||
* @param bool $create_dir Create output directory structure?
|
||||
* @param string $file_extension Output format extension.
|
||||
*
|
||||
* @return string|null Server path for output image.
|
||||
*/
|
||||
public function get_path( string $path, bool $create_dir = false, string $file_extension = '' ) {
|
||||
$paths = $this->get_paths( $path, $create_dir, [ $file_extension ] );
|
||||
return $paths[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates output paths from paths of source image for all output formats.
|
||||
* Creates directory structure of output path, if it does not exist.
|
||||
*
|
||||
* @param string $path Server path of source image.
|
||||
* @param bool $create_dir Create output directory structure?
|
||||
* @param string[] $file_extensions Output format extensions.
|
||||
*
|
||||
* @return string[] Server paths for output images.
|
||||
*/
|
||||
public function get_paths( string $path, bool $create_dir = false, array $file_extensions = null ): array {
|
||||
$new_path = $this->get_directory_path( $path );
|
||||
if ( ! $new_path || ( $create_dir && ! $this->make_directories( $this->check_directories( $new_path ) ) ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$extensions = $this->format_factory->get_format_extensions();
|
||||
$path_extension = strtolower( pathinfo( $new_path, PATHINFO_EXTENSION ) );
|
||||
$paths = [];
|
||||
foreach ( $extensions as $extension ) {
|
||||
if ( $extension === $path_extension ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ( $file_extensions === null ) || in_array( $extension, $file_extensions, true ) ) {
|
||||
$paths[] = sprintf( '%1$s.%2$s', $new_path, $extension );
|
||||
}
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates output path from path of source directory.
|
||||
*
|
||||
* @param string $path Server path of source directory.
|
||||
*
|
||||
* @return string|null Server paths for output directory.
|
||||
*/
|
||||
public function get_directory_path( string $path ) {
|
||||
$webp_root = $this->get_output_dir();
|
||||
$wp_content = $this->get_wp_content_dir();
|
||||
$output_path = str_replace(
|
||||
preg_replace( '/(\/|\\\\)/', DIRECTORY_SEPARATOR, $wp_content ) ?: '',
|
||||
'',
|
||||
preg_replace( '/(\/|\\\\)/', DIRECTORY_SEPARATOR, $path ) ?: ''
|
||||
);
|
||||
$output_path = trim( $output_path, '\/' );
|
||||
|
||||
if ( ! $output_path ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprintf( '%1$s/%2$s', $webp_root, $output_path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if directories for output path exist.
|
||||
*
|
||||
* @param string $path Server path of output.
|
||||
*
|
||||
* @return string[] Directory paths to be created.
|
||||
*/
|
||||
private function check_directories( string $path ): array {
|
||||
$current = dirname( $path );
|
||||
$paths = [];
|
||||
while ( ! file_exists( $current ) ) {
|
||||
$paths[] = $current;
|
||||
$current = dirname( $current );
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new directories.
|
||||
*
|
||||
* @param string[] $paths Output directory paths to be created.
|
||||
*
|
||||
* @return bool Paths created successfully?
|
||||
*/
|
||||
private function make_directories( array $paths ): bool {
|
||||
$paths = array_reverse( $paths );
|
||||
foreach ( $paths as $path ) {
|
||||
if ( ! is_writable( dirname( $path ) ) ) {
|
||||
return false;
|
||||
}
|
||||
mkdir( $path );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function get_wp_content_dir(): string {
|
||||
if ( $this->path_wp_content_dir === null ) {
|
||||
$this->path_wp_content_dir = dirname( apply_filters( 'webpc_dir_path', '', 'uploads' ) );
|
||||
}
|
||||
return $this->path_wp_content_dir;
|
||||
}
|
||||
|
||||
private function get_output_dir(): string {
|
||||
if ( $this->path_output_dir === null ) {
|
||||
$this->path_output_dir = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
}
|
||||
return $this->path_output_dir;
|
||||
}
|
||||
}
|
@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Service\StatsManager;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
|
||||
/**
|
||||
* Finds paths of images to be converted.
|
||||
*/
|
||||
class PathsFinder {
|
||||
|
||||
const PATHS_PER_REQUEST_LOCAL = 10;
|
||||
const PATHS_PER_REQUEST_REMOTE_SMALL = 3;
|
||||
const PATHS_PER_REQUEST_REMOTE_MEDIUM = 5;
|
||||
const PATHS_PER_REQUEST_REMOTE_LARGE = 10;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var StatsManager
|
||||
*/
|
||||
private $stats_manager;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
/**
|
||||
* @var DirectoryFilesFinder
|
||||
*/
|
||||
private $files_finder;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
StatsManager $stats_manager = null,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->token_repository = $token_repository;
|
||||
$this->stats_manager = $stats_manager ?: new StatsManager();
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
$this->files_finder = new DirectoryFilesFinder( $plugin_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of chunked server paths of source images to be converted.
|
||||
*
|
||||
* @param bool $skip_converted Skip converted images?
|
||||
* @param string[]|null $allowed_output_formats List of extensions or use selected in plugin settings.
|
||||
*
|
||||
* @return mixed[] {
|
||||
* @type string $path Directory path.
|
||||
* @type string[] $files Files paths.
|
||||
* }
|
||||
*/
|
||||
public function get_paths_by_chunks( bool $skip_converted = false, array $allowed_output_formats = null ): array {
|
||||
$allowed_output_formats = $allowed_output_formats
|
||||
?: $this->plugin_data->get_plugin_settings()[ OutputFormatsOption::OPTION_NAME ];
|
||||
|
||||
$paths_chunks = $this->find_source_paths();
|
||||
$paths_chunks = $this->skip_converted_paths_chunks( $paths_chunks, $skip_converted, $allowed_output_formats );
|
||||
|
||||
$count = 0;
|
||||
foreach ( $paths_chunks as $dir_data ) {
|
||||
$count += count( $dir_data['files'] );
|
||||
}
|
||||
|
||||
$chunk_size = $this->get_paths_chunk_size( $count );
|
||||
foreach ( $paths_chunks as $dir_name => $dir_data ) {
|
||||
$paths_chunks[ $dir_name ]['files'] = array_chunk( $dir_data['files'], $chunk_size );
|
||||
}
|
||||
|
||||
$this->stats_manager->set_regeneration_images( $count );
|
||||
|
||||
return $paths_chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $skip_converted Skip converted images?
|
||||
* @param string[]|null $allowed_output_formats List of extensions or use selected in plugin settings.
|
||||
*
|
||||
* @return string[] Server paths of source images to be converted.
|
||||
*/
|
||||
public function get_paths( bool $skip_converted = false, array $allowed_output_formats = null ): array {
|
||||
$allowed_output_formats = $allowed_output_formats
|
||||
?: $this->plugin_data->get_plugin_settings()[ OutputFormatsOption::OPTION_NAME ];
|
||||
|
||||
$paths_chunks = $this->find_source_paths();
|
||||
$paths_chunks = $this->skip_converted_paths_chunks( $paths_chunks, $skip_converted, $allowed_output_formats );
|
||||
|
||||
$paths = [];
|
||||
foreach ( $paths_chunks as $dir_data ) {
|
||||
foreach ( $dir_data['files'] as $source_path ) {
|
||||
$paths[] = $dir_data['path'] . '/' . $source_path;
|
||||
}
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $source_paths Server paths of source images.
|
||||
* @param string[]|null $allowed_output_formats List of extensions or use selected in plugin settings.
|
||||
* @param bool $force_convert_modified Force re-conversion of images modified after previous conversion.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
public function skip_converted_paths(
|
||||
array $source_paths,
|
||||
array $allowed_output_formats = null,
|
||||
bool $force_convert_modified = false
|
||||
): array {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$allowed_output_formats = $allowed_output_formats ?: $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
$force_convert_deleted = ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
|
||||
foreach ( $source_paths as $path_index => $source_path ) {
|
||||
$is_converted = true;
|
||||
foreach ( $allowed_output_formats as $output_format ) {
|
||||
$output_path = $this->output_path->get_path( $source_path, false, $output_format );
|
||||
|
||||
if ( $output_path && ! $this->is_converted_file( $source_path, $output_path, $force_convert_deleted, false, $force_convert_modified ) ) {
|
||||
$is_converted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( $is_converted ) {
|
||||
unset( $source_paths[ $path_index ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $source_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $source_dirs Server paths of source images.
|
||||
* @param bool $skip_converted Skip converted images?
|
||||
* @param string[]|null $allowed_output_formats List of extensions or use selected in plugin settings.
|
||||
*
|
||||
* @return mixed[] Server paths of source images.
|
||||
*/
|
||||
private function skip_converted_paths_chunks(
|
||||
array $source_dirs,
|
||||
bool $skip_converted,
|
||||
array $allowed_output_formats = null
|
||||
): array {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$allowed_output_formats = $allowed_output_formats ?: $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
$force_convert_deleted = ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
$force_convert_crashed = ( in_array( ExtraFeaturesOption::OPTION_VALUE_SERVICE_MODE, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
|
||||
foreach ( $source_dirs as $dir_name => $dir_data ) {
|
||||
foreach ( $dir_data['files'] as $path_index => $source_file ) {
|
||||
$source_path = $dir_data['path'] . '/' . $source_file;
|
||||
$is_converted = true;
|
||||
foreach ( $allowed_output_formats as $output_format ) {
|
||||
$output_path = $this->output_path->get_path( $source_path, false, $output_format );
|
||||
|
||||
if ( $output_path && ( ! $skip_converted || ! $this->is_converted_file( $source_path, $output_path, $force_convert_deleted, $force_convert_crashed ) ) ) {
|
||||
$is_converted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( $is_converted ) {
|
||||
unset( $source_dirs[ $dir_name ]['files'][ $path_index ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $source_dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of server paths of source images to be converted.
|
||||
*
|
||||
* @return mixed[] {
|
||||
* @type string $path Directory path.
|
||||
* @type string[] $files Files paths.
|
||||
* }
|
||||
*/
|
||||
private function find_source_paths(): array {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
|
||||
$source_dirs = [];
|
||||
foreach ( $settings[ SupportedDirectoriesOption::OPTION_NAME ] as $dir_name ) {
|
||||
$source_dirs[ $dir_name ] = apply_filters( 'webpc_dir_path', '', $dir_name );
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ( $source_dirs as $dir_name => $dir_path ) {
|
||||
$list[ $dir_name ] = [
|
||||
'path' => $dir_path,
|
||||
'files' => $this->files_finder->get_files_by_directory( $dir_path ),
|
||||
];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
* @param string $output_path .
|
||||
* @param bool $force_convert_deleted Skip .deleted files.
|
||||
* @param bool $force_convert_crashed Skip .crashed files.
|
||||
* @param bool $force_convert_modified .
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_converted_file(
|
||||
string $source_path,
|
||||
string $output_path,
|
||||
bool $force_convert_deleted,
|
||||
bool $force_convert_crashed,
|
||||
bool $force_convert_modified = false
|
||||
): bool {
|
||||
if ( file_exists( $output_path ) ) {
|
||||
return ( $force_convert_modified ) ? ( filemtime( $source_path ) <= filemtime( $output_path ) ) : true;
|
||||
} elseif ( ! $force_convert_deleted && file_exists( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION ) ) {
|
||||
return true;
|
||||
} elseif ( ! $force_convert_crashed && file_exists( $output_path . '.' . CrashedFilesOperator::CRASHED_FILE_EXTENSION ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $paths_count .
|
||||
*
|
||||
* @return int<1, max>
|
||||
*/
|
||||
private function get_paths_chunk_size( int $paths_count ): int {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $settings[ ConversionMethodOption::OPTION_NAME ] !== RemoteMethod::METHOD_NAME ) {
|
||||
return self::PATHS_PER_REQUEST_LOCAL;
|
||||
}
|
||||
|
||||
$output_formats = count( $settings[ OutputFormatsOption::OPTION_NAME ] ) ?: 1;
|
||||
$images_count = $paths_count * $output_formats;
|
||||
$images_limit = $this->token_repository->get_token()->get_images_limit();
|
||||
$images_to_conversion = min( $images_count, $images_limit );
|
||||
|
||||
if ( $images_to_conversion <= 10000 ) {
|
||||
return self::PATHS_PER_REQUEST_REMOTE_SMALL;
|
||||
} elseif ( $images_to_conversion <= 120000 ) {
|
||||
return self::PATHS_PER_REQUEST_REMOTE_MEDIUM;
|
||||
} else {
|
||||
return self::PATHS_PER_REQUEST_REMOTE_LARGE;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\CloudflareSettingsIncorrectNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Service\CloudflareConfigurator;
|
||||
use WebpConverter\Service\OptionsAccessManager;
|
||||
use WebpConverter\Settings\Option\CloudflareZoneIdOption;
|
||||
|
||||
/**
|
||||
* Validates Cloudflare configuration.
|
||||
*/
|
||||
class CloudflareStatusDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( ! $plugin_settings[ CloudflareZoneIdOption::OPTION_NAME ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( OptionsAccessManager::get_option( CloudflareConfigurator::REQUEST_CACHE_PURGE_OPTION, 'yes' ) !== 'yes' ) {
|
||||
return new CloudflareSettingsIncorrectNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\NoticeInterface;
|
||||
|
||||
/**
|
||||
* Interface for class that checks for configuration errors.
|
||||
*/
|
||||
interface DetectorInterface {
|
||||
|
||||
/**
|
||||
* @return NoticeInterface|null
|
||||
*/
|
||||
public function get_error();
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Conversion\Method\GdMethod;
|
||||
use WebpConverter\Conversion\Method\ImagickMethod;
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
use WebpConverter\Error\Notice\LibsNotInstalledNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about non-installed methods for converting images.
|
||||
*/
|
||||
class LibsNotInstalledDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $plugin_settings[ ConversionMethodOption::OPTION_NAME ] === RemoteMethod::METHOD_NAME ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( GdMethod::is_method_installed() || ImagickMethod::is_method_installed() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LibsNotInstalledNotice();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Conversion\Method\GdMethod;
|
||||
use WebpConverter\Conversion\Method\ImagickMethod;
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
use WebpConverter\Error\Notice\LibsWithoutWebpSupportNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about image conversion methods that do not support WebP output format.
|
||||
*/
|
||||
class LibsWithoutWebpSupportDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $plugin_settings[ ConversionMethodOption::OPTION_NAME ] === RemoteMethod::METHOD_NAME ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( GdMethod::is_method_active( WebpFormat::FORMAT_EXTENSION )
|
||||
|| ImagickMethod::is_method_active( WebpFormat::FORMAT_EXTENSION ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LibsWithoutWebpSupportNotice();
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Error\Notice\PassthruExecutionNotice;
|
||||
use WebpConverter\Loader\LoaderAbstract;
|
||||
use WebpConverter\Loader\PassthruLoader;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\PluginInfo;
|
||||
use WebpConverter\Settings\Option\LoaderTypeOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about disabled file supports Pass Thru loader.
|
||||
*/
|
||||
class PassthruExecutionDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginInfo
|
||||
*/
|
||||
private $plugin_info;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
public function __construct( PluginInfo $plugin_info, PluginData $plugin_data, FormatFactory $format_factory ) {
|
||||
$this->plugin_info = $plugin_info;
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $plugin_settings[ LoaderTypeOption::OPTION_NAME ] !== PassthruLoader::LOADER_TYPE ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
do_action( LoaderAbstract::ACTION_NAME, true, true );
|
||||
|
||||
$has_error = false;
|
||||
if ( $this->if_passthru_execution_allowed() !== true ) {
|
||||
$has_error = true;
|
||||
}
|
||||
|
||||
do_action( LoaderAbstract::ACTION_NAME, true );
|
||||
|
||||
return ( $has_error ) ? new PassthruExecutionNotice() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if PHP file required for Passthru loader is available.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_passthru_execution_allowed(): bool {
|
||||
$loader = new PassthruLoader( $this->plugin_info, $this->plugin_data, $this->format_factory );
|
||||
if ( $loader->is_active_loader() !== true ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$url = $loader::get_loader_url() . '?nocache=1';
|
||||
$ch = curl_init( $url );
|
||||
if ( $ch === false ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_NOBODY, true );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
|
||||
curl_setopt( $ch, CURLOPT_TIMEOUT, 3 );
|
||||
curl_exec( $ch );
|
||||
$code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
curl_close( $ch );
|
||||
|
||||
return ( $code === 200 );
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\PathHtaccessNotWritableNotice;
|
||||
use WebpConverter\Error\Notice\PathUploadsUnavailableNotice;
|
||||
use WebpConverter\Error\Notice\PathWebpDuplicatedNotice;
|
||||
use WebpConverter\Error\Notice\PathWebpNotWritableNotice;
|
||||
use WebpConverter\Service\PathsGenerator;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about incorrect paths of directories.
|
||||
*/
|
||||
class PathsErrorsDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
if ( $this->if_uploads_path_exists() !== true ) {
|
||||
return new PathUploadsUnavailableNotice();
|
||||
} elseif ( $this->if_htaccess_is_writeable() !== true ) {
|
||||
return new PathHtaccessNotWritableNotice();
|
||||
} elseif ( $this->if_paths_are_different() !== true ) {
|
||||
return new PathWebpDuplicatedNotice();
|
||||
} elseif ( $this->if_webp_path_is_writeable() !== true ) {
|
||||
return new PathWebpNotWritableNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if path of uploads directory is exists.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_uploads_path_exists(): bool {
|
||||
$path = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
return ( is_dir( $path ) && ( $path !== PathsGenerator::get_wordpress_root_path() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if paths of wp-content and uploads directories are writable.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_htaccess_is_writeable(): bool {
|
||||
$path_dir = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
$path_file = $path_dir . '/.htaccess';
|
||||
if ( file_exists( $path_file ) ) {
|
||||
return ( is_readable( $path_file ) && is_writable( $path_file ) );
|
||||
} else {
|
||||
return is_writable( $path_dir );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if uploads directory path and output directory are different.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_paths_are_different(): bool {
|
||||
$path_uploads = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
$path_webp = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
return ( $path_uploads !== $path_webp );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if path of output directory is writable.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_webp_path_is_writeable(): bool {
|
||||
$path = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
return ( is_dir( $path ) || is_writable( dirname( $path ) ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,325 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Conversion\OutputPathGenerator;
|
||||
use WebpConverter\Error\Notice\BypassingApacheNotice;
|
||||
use WebpConverter\Error\Notice\NoticeInterface;
|
||||
use WebpConverter\Error\Notice\PassthruNotWorkingNotice;
|
||||
use WebpConverter\Error\Notice\RewritesCachedNotice;
|
||||
use WebpConverter\Error\Notice\RewritesNotExecutedNotice;
|
||||
use WebpConverter\Error\Notice\RewritesNotWorkingNotice;
|
||||
use WebpConverter\Error\Notice\RewritesUploadsBlockedNotice;
|
||||
use WebpConverter\Loader\HtaccessBypassingLoader;
|
||||
use WebpConverter\Loader\HtaccessLoader;
|
||||
use WebpConverter\Loader\LoaderAbstract;
|
||||
use WebpConverter\Loader\PassthruLoader;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\PluginInfo;
|
||||
use WebpConverter\Service\FileLoader;
|
||||
use WebpConverter\Settings\Option\LoaderTypeOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about non-working HTTP rewrites.
|
||||
*/
|
||||
class RewritesErrorsDetector implements DetectorInterface {
|
||||
|
||||
const PATH_SOURCE_FILE_PNG = '/assets/img/icon-test.png';
|
||||
const PATH_SOURCE_FILE_WEBP = '/assets/img/icon-test.webp';
|
||||
const PATH_SOURCE_FILE_AVIF = '/assets/img/icon-test.avif';
|
||||
const PATH_OUTPUT_FILE_PNG = '/webp-converter-for-media-test.png';
|
||||
const PATH_OUTPUT_FILE_PNG2 = '/webp-converter-for-media-test.png2';
|
||||
const PATH_OUTPUT_FILE_PLUGINS = '/webp-converter-for-media/assets/img/icon-test.png';
|
||||
const URL_DEBUG_HTACCESS_FILE = 'assets/img/debug-htaccess/icon-test.png2';
|
||||
|
||||
/**
|
||||
* @var PluginInfo
|
||||
*/
|
||||
private $plugin_info;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FileLoader
|
||||
*/
|
||||
private $file_loader;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $test_version;
|
||||
|
||||
public function __construct(
|
||||
PluginInfo $plugin_info,
|
||||
PluginData $plugin_data,
|
||||
FormatFactory $format_factory,
|
||||
FileLoader $file_loader = null,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->plugin_info = $plugin_info;
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->file_loader = $file_loader ?: new FileLoader();
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
$this->test_version = uniqid();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( ! $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ]
|
||||
|| ! $plugin_settings[ OutputFormatsOption::OPTION_NAME ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->convert_images_for_debug();
|
||||
|
||||
do_action( LoaderAbstract::ACTION_NAME, true, true );
|
||||
$error = $this->detect_rewrites_error();
|
||||
do_action( LoaderAbstract::ACTION_NAME, true );
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NoticeInterface|null
|
||||
*/
|
||||
private function detect_rewrites_error() {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
$loader_type = $settings[ LoaderTypeOption::OPTION_NAME ] ?? '';
|
||||
|
||||
switch ( $loader_type ) {
|
||||
case HtaccessLoader::LOADER_TYPE:
|
||||
case HtaccessBypassingLoader::LOADER_TYPE:
|
||||
if ( $this->if_redirects_are_works() === true ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( $this->if_htaccess_can_be_overwritten() !== true ) {
|
||||
return new RewritesNotExecutedNotice();
|
||||
} elseif ( $this->if_bypassing_apache_is_active() === true ) {
|
||||
return new BypassingApacheNotice();
|
||||
} elseif ( $this->if_redirects_for_plugins_are_works() === true ) {
|
||||
return new RewritesUploadsBlockedNotice();
|
||||
}
|
||||
|
||||
return new RewritesNotWorkingNotice();
|
||||
case PassthruLoader::LOADER_TYPE:
|
||||
if ( $this->if_redirects_are_works() === true ) {
|
||||
break;
|
||||
}
|
||||
|
||||
return new PassthruNotWorkingNotice();
|
||||
}
|
||||
|
||||
$this->test_version = uniqid();
|
||||
if ( $this->if_redirects_are_cached() === true ) {
|
||||
return new RewritesCachedNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts and saves files needed for testing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function convert_images_for_debug() {
|
||||
$uploads_dir = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
if ( ! is_writable( $uploads_dir ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$path_file_png = $uploads_dir . self::PATH_OUTPUT_FILE_PNG;
|
||||
$path_file_png2 = $uploads_dir . self::PATH_OUTPUT_FILE_PNG2;
|
||||
$path_file_plugins = apply_filters( 'webpc_dir_path', '', 'plugins' ) . self::PATH_OUTPUT_FILE_PLUGINS;
|
||||
$file_statuses = [];
|
||||
|
||||
if ( ! file_exists( $path_file_png ) || ! file_exists( $path_file_png2 ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_PNG, $path_file_png );
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_PNG, $path_file_png2 );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
|
||||
if ( ( $output_path = $this->output_path->get_path( $path_file_png, true, WebpFormat::FORMAT_EXTENSION ) )
|
||||
&& ! file_exists( $output_path ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_WEBP, $output_path );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
if ( ( $output_path = $this->output_path->get_path( $path_file_png, true, AvifFormat::FORMAT_EXTENSION ) )
|
||||
&& ! file_exists( $output_path ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_AVIF, $output_path );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
if ( ( $output_path = $this->output_path->get_path( $path_file_png2, true, WebpFormat::FORMAT_EXTENSION ) )
|
||||
&& ! file_exists( $output_path ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_WEBP, $output_path );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
|
||||
if ( ( $output_path = $this->output_path->get_path( $path_file_plugins, true, WebpFormat::FORMAT_EXTENSION ) )
|
||||
&& ! file_exists( $output_path ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_WEBP, $output_path );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
|
||||
if ( in_array( false, $file_statuses, true ) ) {
|
||||
$GLOBALS[ FileLoader::GLOBAL_LOGS_VARIABLE ][] = [
|
||||
'context' => __FUNCTION__,
|
||||
'status' => $file_statuses,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if redirects to output images are works.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_redirects_are_works(): bool {
|
||||
$uploads_dir = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
$uploads_url = apply_filters( 'webpc_dir_url', '', 'uploads' );
|
||||
|
||||
$file_size = $this->file_loader->get_file_size_by_path(
|
||||
$uploads_dir . self::PATH_OUTPUT_FILE_PNG
|
||||
);
|
||||
$file_webp = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
if ( $file_webp > 0 ) {
|
||||
return ( $file_webp < $file_size );
|
||||
}
|
||||
|
||||
$file_png_status = $this->file_loader->get_file_status_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
false,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
if ( $file_png_status === 500 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file_webp_status = $this->file_loader->get_file_status_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
if ( ( $file_png_status === 200 ) && ( $file_webp_status === 404 ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if server supports using .htaccess files from custom locations.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_htaccess_can_be_overwritten(): bool {
|
||||
$file_size = $this->file_loader->get_file_size_by_url(
|
||||
$this->plugin_info->get_plugin_directory_url() . self::URL_DEBUG_HTACCESS_FILE,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
return ( $file_size === 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bypassing of redirects to output images is exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_bypassing_apache_is_active(): bool {
|
||||
$uploads_url = apply_filters( 'webpc_dir_url', '', 'uploads' );
|
||||
|
||||
$file_png = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
$file_png2 = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG2,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
return ( $file_png > $file_png2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if redirects to output images from /plugins directory are works.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_redirects_for_plugins_are_works(): bool {
|
||||
$uploads_dir = apply_filters( 'webpc_dir_path', '', 'plugins' );
|
||||
$uploads_url = apply_filters( 'webpc_dir_url', '', 'plugins' );
|
||||
|
||||
$file_size = $this->file_loader->get_file_size_by_path(
|
||||
$uploads_dir . self::PATH_OUTPUT_FILE_PLUGINS
|
||||
);
|
||||
$file_webp = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PLUGINS,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
return ( ( $file_webp < $file_size ) && ( $file_webp !== 0 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if redirects to output images are cached.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_redirects_are_cached(): bool {
|
||||
$uploads_url = apply_filters( 'webpc_dir_url', '', 'uploads' );
|
||||
|
||||
$file_webp = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
$file_original = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
false,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
return ( ( $file_webp > 0 ) && ( $file_webp === $file_original ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\SettingsIncorrectNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\LoaderTypeOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about incorrectly saved plugin settings.
|
||||
*/
|
||||
class SettingsIncorrectDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
|
||||
if ( ( ! isset( $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ OutputFormatsOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ OutputFormatsOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ ConversionMethodOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ ConversionMethodOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ ImagesQualityOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ ImagesQualityOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ LoaderTypeOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ LoaderTypeOption::OPTION_NAME ] ) ) {
|
||||
return new SettingsIncorrectNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\AccessTokenInvalidNotice;
|
||||
use WebpConverter\Error\Notice\ApiLimitExceededNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Service\TokenValidator;
|
||||
use WebpConverter\Settings\Option\AccessTokenOption;
|
||||
|
||||
/**
|
||||
* Checks for the token status for the PRO version.
|
||||
*/
|
||||
class TokenStatusDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var TokenValidator
|
||||
*/
|
||||
private $token_validator;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository = null,
|
||||
TokenValidator $token_validator = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->token_repository = $token_repository ?: new TokenRepository();
|
||||
$this->token_validator = $token_validator ?: new TokenValidator( $token_repository );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( ! isset( $settings[ AccessTokenOption::OPTION_NAME ] ) || ! $settings[ AccessTokenOption::OPTION_NAME ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$token = $this->token_repository->get_token();
|
||||
if ( ! $token->get_valid_status() ) {
|
||||
return new AccessTokenInvalidNotice();
|
||||
}
|
||||
|
||||
if ( ! $token->is_active() ) {
|
||||
$token = $this->token_validator->validate_token( $token->get_token_value() );
|
||||
}
|
||||
|
||||
if ( ! $token->is_active() ) {
|
||||
return new ApiLimitExceededNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\UnsupportedPlaygroundServerNotice;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about unsupported servers.
|
||||
*/
|
||||
class UnsupportedServerDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
if ( strpos( $_SERVER['SERVER_NAME'] ?? '', 'playground.wordpress.net' ) !== false ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
return new UnsupportedPlaygroundServerNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\WebpRequiredNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Checks if the WebP as output format is active.
|
||||
*/
|
||||
class WebpFormatActivatedDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $plugin_settings[ OutputFormatsOption::OPTION_NAME ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new WebpRequiredNotice();
|
||||
}
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Error\Detector\CloudflareStatusDetector;
|
||||
use WebpConverter\Error\Detector\LibsNotInstalledDetector;
|
||||
use WebpConverter\Error\Detector\LibsWithoutWebpSupportDetector;
|
||||
use WebpConverter\Error\Detector\PassthruExecutionDetector;
|
||||
use WebpConverter\Error\Detector\PathsErrorsDetector;
|
||||
use WebpConverter\Error\Detector\RewritesErrorsDetector;
|
||||
use WebpConverter\Error\Detector\SettingsIncorrectDetector;
|
||||
use WebpConverter\Error\Detector\TokenStatusDetector;
|
||||
use WebpConverter\Error\Detector\UnsupportedServerDetector;
|
||||
use WebpConverter\Error\Detector\WebpFormatActivatedDetector;
|
||||
use WebpConverter\Error\Notice\NoticeInterface;
|
||||
use WebpConverter\Error\Notice\RewritesCachedNotice;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\PluginInfo;
|
||||
use WebpConverter\Service\OptionsAccessManager;
|
||||
|
||||
/**
|
||||
* Supports generating list of server configuration errors.
|
||||
*/
|
||||
class ErrorDetectorAggregator implements HookableInterface {
|
||||
|
||||
const ERRORS_CACHE_OPTION = 'webpc_errors_cache';
|
||||
const ERROR_DETECTOR_DATE_TRANSIENT = 'webpc_error_detector';
|
||||
|
||||
/**
|
||||
* @var PluginInfo
|
||||
*/
|
||||
private $plugin_info;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $not_fatal_errors = [
|
||||
RewritesCachedNotice::ERROR_KEY,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var NoticeInterface[]|null
|
||||
*/
|
||||
private $cached_errors = null;
|
||||
|
||||
public function __construct(
|
||||
PluginInfo $plugin_info,
|
||||
PluginData $plugin_data,
|
||||
FormatFactory $format_factory
|
||||
) {
|
||||
$this->plugin_info = $plugin_info;
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_filter( 'webpc_server_errors', [ $this, 'get_server_errors' ], 10, 2 );
|
||||
add_filter( 'webpc_server_errors_messages', [ $this, 'get_server_errors_messages' ], 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of errors codes for server configuration.
|
||||
*
|
||||
* @param string[] $values Default value of filter.
|
||||
* @param bool $only_errors Only errors, no warnings?
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_server_errors( array $values, bool $only_errors = false ): array {
|
||||
$error_codes = $this->get_cached_error_codes();
|
||||
|
||||
return array_filter(
|
||||
$error_codes,
|
||||
function ( $error ) use ( $only_errors ) {
|
||||
return ( ! $only_errors || ! in_array( $error, $this->not_fatal_errors ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of errors messages for server configuration.
|
||||
*
|
||||
* @param string[] $values Default value of filter.
|
||||
*
|
||||
* @return string[][]
|
||||
*/
|
||||
public function get_server_errors_messages( array $values ): array {
|
||||
$detected_errors = $this->get_errors_list();
|
||||
$this->cache_errors( $detected_errors );
|
||||
|
||||
$error_messages = [];
|
||||
foreach ( $detected_errors as $error ) {
|
||||
$error_messages[] = $error->get_message();
|
||||
}
|
||||
return $error_messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function get_cached_error_codes(): array {
|
||||
$error_codes = [];
|
||||
|
||||
if ( $this->cached_errors !== null ) {
|
||||
foreach ( $this->cached_errors as $error ) {
|
||||
$error_codes[] = $error->get_key();
|
||||
}
|
||||
} else {
|
||||
$error_codes = OptionsAccessManager::get_option( self::ERRORS_CACHE_OPTION, [] );
|
||||
}
|
||||
|
||||
return $error_codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NoticeInterface[] $detected_errors .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function cache_errors( array $detected_errors ) {
|
||||
$error_codes = [];
|
||||
foreach ( $detected_errors as $error ) {
|
||||
$error_codes[] = $error->get_key();
|
||||
}
|
||||
|
||||
OptionsAccessManager::update_option( self::ERRORS_CACHE_OPTION, $error_codes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for configuration errors according to specified logic.
|
||||
* Saves errors to cache.
|
||||
*
|
||||
* @return NoticeInterface[]
|
||||
*/
|
||||
private function get_errors_list(): array {
|
||||
if ( $this->cached_errors !== null ) {
|
||||
return $this->cached_errors;
|
||||
}
|
||||
|
||||
$this->pause_duplicated_detection();
|
||||
$this->cached_errors = [];
|
||||
|
||||
if ( $new_error = ( new UnsupportedServerDetector() )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
return $this->cached_errors;
|
||||
}
|
||||
|
||||
if ( $new_error = ( new TokenStatusDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
} elseif ( $new_error = ( new LibsNotInstalledDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
} elseif ( $new_error = ( new LibsWithoutWebpSupportDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
} elseif ( $new_error = ( new WebpFormatActivatedDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
|
||||
if ( $new_error = ( new PathsErrorsDetector() )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
|
||||
if ( $new_error = ( new PassthruExecutionDetector( $this->plugin_info, $this->plugin_data, $this->format_factory ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
} elseif ( $new_error = ( new RewritesErrorsDetector( $this->plugin_info, $this->plugin_data, $this->format_factory ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
|
||||
if ( $this->cached_errors ) {
|
||||
return $this->cached_errors;
|
||||
}
|
||||
|
||||
if ( $new_error = ( new SettingsIncorrectDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
if ( $new_error = ( new CloudflareStatusDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
|
||||
return $this->cached_errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function pause_duplicated_detection() {
|
||||
$current_date = ( new \DateTime() )->format( 'Uv' );
|
||||
$cached_date = get_site_transient( self::ERROR_DETECTOR_DATE_TRANSIENT );
|
||||
if ( $cached_date && ( $cached_date >= ( $current_date - 1000 ) ) ) {
|
||||
sleep( 1 );
|
||||
$current_date = ( new \DateTime() )->format( 'Uv' );
|
||||
}
|
||||
|
||||
set_site_transient( self::ERROR_DETECTOR_DATE_TRANSIENT, $current_date );
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class AccessTokenInvalidNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'token_invalid';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: field label, %2$s: button label */
|
||||
__( 'It appears that the value of the %1$s field is invalid or your subscription has expired. To use the service, please, check your subscription and click the %2$s button again.', 'webp-converter-for-media' ),
|
||||
__( 'Access Token', 'webp-converter-for-media' ),
|
||||
__( 'Activate Token', 'webp-converter-for-media' )
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'To manage your subscriptions, please visit %1$sour website%2$s.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-token-invalid-panel" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ApiLimitExceededNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'token_limit';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'It appears that you have reached the maximum number of image conversions for your current billing period. To continue using the service, we recommend upgrading your plan.', 'webp-converter-for-media' ),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'To manage your subscriptions, please visit %1$sour website%2$s.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-token-limit-panel" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
use WebpConverter\Settings\Page\AdvancedSettingsPage;
|
||||
use WebpConverter\Settings\Page\PageIntegrator;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class BypassingApacheNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'bypassing_apache';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'It appears that the requests for images on your website are being processed by the Nginx server, bypassing Apache.', 'webp-converter-for-media' ),
|
||||
implode(
|
||||
' ',
|
||||
[
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'Please check %1$sour instruction%2$s which should help you solve your problem. This will allow the plugin to function properly.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-bypassing-apache-instruction" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
__( 'If you have trouble solving this problem, please, contact your hosting\'s technical support and provide them with the following message:', 'webp-converter-for-media' ),
|
||||
]
|
||||
),
|
||||
implode(
|
||||
'',
|
||||
[
|
||||
'<em>' . implode(
|
||||
' ',
|
||||
[
|
||||
sprintf(
|
||||
/* translators: %1$s: setting name, %2$s: setting name, %3$s: home URL */
|
||||
__( 'I would like to disable %1$s (or %2$s) for static content files like .jpg, .jpeg, .png, .gif and .webp on my website - %3$s. These files should have been handled by the Apache server instead of Nginx.', 'webp-converter-for-media' ),
|
||||
'Nginx Caching / Nginx Reverse Proxy',
|
||||
'Nginx Direct Delivery',
|
||||
get_home_url()
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %s: anchor tag */
|
||||
__( 'You can find more information in the instruction: %s', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-bypassing-apache-message" target="_blank">https://url.mattplugins.com/converter-error-bypassing-apache-message</a>'
|
||||
),
|
||||
]
|
||||
) . '</em>',
|
||||
]
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag */
|
||||
__( '%1$sPlease, copy the above message and send it to the technical support of your hosting.%2$s They should help you in this matter.', 'webp-converter-for-media' ),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag, %3$s: field value, %4$s: field label, %5$s: open anchor tag, %6$s: close anchor tag */
|
||||
__( '%1$sThe alternative solution to avoid this problem%2$s may be to set the %3$s option for the %4$s field in %5$sthe Advanced Settings tab%6$s.', 'webp-converter-for-media' ),
|
||||
'<span id="bypassing-notice">',
|
||||
'</span>',
|
||||
'"' . __( 'Bypassing Nginx', 'webp-converter-for-media' ) . '"',
|
||||
'"' . __( 'Image loading mode', 'webp-converter-for-media' ) . '"',
|
||||
'<a href="' . esc_attr( PageIntegrator::get_settings_page_url( AdvancedSettingsPage::PAGE_SLUG ) ) . '#bypassing-notice">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class CloudflareSettingsIncorrectNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'settings_cloudflare';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %s: field labels */
|
||||
__( 'Incorrect values were given in the plugin settings in the fields: %s.', 'webp-converter-for-media' ),
|
||||
implode( ', ', [ 'Cloudflare Zone ID', 'Cloudflare API Token' ] )
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'Please, read %1$sour manual%2$s and follow the steps there.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-cloudflare-settings-docs" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class LibsNotInstalledNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'libs_not_installed';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$message = [
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'GD or Imagick library is not installed on your server.', 'webp-converter-for-media' ) . ' ' . __( 'This means that you cannot convert images to the WebP format on your server, because it does not meet the plugin requirements described in %1$sthe plugin FAQ%2$s. This issue is not dependent on the plugin.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-libs-not-installed-faq" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
|
||||
if ( function_exists( 'curl_init' ) ) {
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag, %3$s: open anchor tag, %4$s: close anchor tag */
|
||||
__( '%1$sHowever, we have a solution for you!%2$s You can activate %3$sthe PRO version%4$s of the plugin that allows you to convert images using a remote server. This will allow you to convert images without any problems and speed up your website now.', 'webp-converter-for-media' ),
|
||||
'<strong class="webpcContent__tip">',
|
||||
'</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-libs-not-installed-upgrade" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class LibsWithoutWebpSupportNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'libs_without_webp_support';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$message = [
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'The required GD or Imagick library is installed on your server, but it does not support the WebP format.', 'webp-converter-for-media' ) . ' ' . __( 'This means that you cannot convert images to the WebP format on your server, because it does not meet the plugin requirements described in %1$sthe plugin FAQ%2$s. This issue is not dependent on the plugin.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-libs-without-webp-support-faq" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
|
||||
if ( function_exists( 'curl_init' ) ) {
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag, %3$s: open anchor tag, %4$s: close anchor tag */
|
||||
__( '%1$sHowever, we have a solution for you!%2$s You can activate %3$sthe PRO version%4$s of the plugin that allows you to convert images using a remote server. This will allow you to convert images without any problems and speed up your website now.', 'webp-converter-for-media' ),
|
||||
'<strong class="webpcContent__tip">',
|
||||
'</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-libs-without-webp-support-upgrade" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* Stores information about server configuration error.
|
||||
*/
|
||||
interface NoticeInterface {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_key(): string;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_message(): array;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
use WebpConverter\Loader\PassthruLoader;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PassthruExecutionNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'passthru_execution';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$passthru_url = PassthruLoader::get_loader_url();
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %s: anchor tag */
|
||||
__( 'Execution of the PHP file from the "%s" path is blocked on your server, or access to this file is blocked. Add an exception and enable this file to be executed via an HTTP request. To do this, check the security plugin settings (if you are using them) or the security settings of your server.', 'webp-converter-for-media' ),
|
||||
'<a href="' . $passthru_url . '" target="_blank">' . $passthru_url . '</a>',
|
||||
'<br><br>'
|
||||
),
|
||||
__( 'In this case, please, contact your server administrator.', 'webp-converter-for-media' ),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PassthruNotWorkingNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'passthru_not_working';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: loader name */
|
||||
__( 'The %1$s loading mode is not compatible with your server. Sorry for the inconvenience.', 'webp-converter-for-media' ),
|
||||
'Pass Thru'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PathHtaccessNotWritableNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'path_htaccess_not_writable';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: server path */
|
||||
__( 'Unable to create or edit .htaccess file (is_readable() or is_writable() function returns false). Change directory permissions. The current path of the file is: %1$s. Please, contact your server administrator.', 'webp-converter-for-media' ),
|
||||
'<strong>' . apply_filters( 'webpc_dir_path', '', 'uploads' ) . '/.htaccess</strong>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PathUploadsUnavailableNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'path_uploads_unavailable';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: filter name, %2$s: server path, %3$s: open anchor tag, %4$s: close anchor tag */
|
||||
__( 'The path for /uploads files does not exist (the is_dir() function returns false). Use the %1$s filter to set the correct path. The current path is: %2$s. Please, read %3$sthe plugin FAQ%4$s to learn more.', 'webp-converter-for-media' ),
|
||||
'<strong>webpc_dir_path</strong>',
|
||||
'<strong>' . apply_filters( 'webpc_dir_path', '', 'uploads' ) . '</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-path-uploads-unavailable-faq" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PathWebpDuplicatedNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'path_webp_duplicated';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: filter name, %2$s: server path */
|
||||
__( 'The paths for /uploads files and for saving converted WebP files are the same. Change them using the %1$s filter. The current path for them is: %2$s.', 'webp-converter-for-media' ),
|
||||
'<strong>webpc_dir_path</strong>',
|
||||
'<strong>' . apply_filters( 'webpc_dir_path', '', 'uploads' ) . '</strong>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PathWebpNotWritableNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'path_webp_not_writable';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: filter name, %2$s: server path, %3$s: open anchor tag, %4$s: close anchor tag */
|
||||
__( 'The path for saving converted WebP files does not exist and cannot be created (the is_writable() function returns false). Use the %1$s filter to set the correct path. The current path is: %2$s. Please, read %3$sthe plugin FAQ%4$s to learn more.', 'webp-converter-for-media' ),
|
||||
'<strong>webpc_dir_path</strong>',
|
||||
'<strong>' . apply_filters( 'webpc_dir_path', '', 'webp' ) . '</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-path-webp-not-writable-faq" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
use WebpConverter\Service\EnvDetector;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RewritesCachedNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'rewrites_cached';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$message = [
|
||||
__( 'It appears that your server uses the cache for HTTP requests. The rules from the .htaccess file or from the Nginx configuration are not executed every time when an image is loaded, but the last redirect from cache is performed.', 'webp-converter-for-media' ),
|
||||
];
|
||||
|
||||
if ( EnvDetector::is_cdn_bunny() ) {
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: service name, %3$s: close strong tag, %4$s: open anchor tag, %5$s: close anchor tag */
|
||||
__( '%1$sIf you are using the %2$s service%3$s, please follow %4$sour manual%5$s first to allow the plugin to work properly.', 'webp-converter-for-media' ),
|
||||
'<strong>',
|
||||
'BunnyCDN',
|
||||
'</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-cached-bunny-instruction" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
$message[] = implode(
|
||||
'',
|
||||
[
|
||||
'<em><strong>' . __( 'Please, contact your hosting\'s technical support or CDN\'s support and send them the following message:', 'webp-converter-for-media' ) . '</strong></em>',
|
||||
'<em>' . implode(
|
||||
'<br>',
|
||||
[
|
||||
sprintf(
|
||||
/* translators: %1$s: home URL */
|
||||
__( 'I have a problem with the cache for HTTP requests on my website - %1$s. This prevents JPEG or PNG files from being dynamically redirected to WebP or AVIF, depending on whether the browser supports the format. Here are potential sources of this issue:', 'webp-converter-for-media' ),
|
||||
get_home_url()
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: header name, %2$s: additional information */
|
||||
__( '- the server or CDN server does not support the %1$s HTTP header or handles it incorrectly (%2$s)', 'webp-converter-for-media' ),
|
||||
'<strong>"Vary: Accept"</strong>',
|
||||
__( 'the cache for redirects should be based not only on the URL to the file, but also on the value of the Accept header sent by the browser', 'webp-converter-for-media' )
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: header name, %2$s: additional information */
|
||||
__( '- the server or CDN server does not support the %1$s HTTP header or handles it incorrectly (%2$s)', 'webp-converter-for-media' ),
|
||||
'<strong>"Cache-Control: private"</strong>',
|
||||
__( 'this header should be able to disable caching for static files on the CDN server or proxy server', 'webp-converter-for-media' )
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %s: anchor tag */
|
||||
__( '- the website is running on the Nginx server without support for .htaccess files and not all the steps described in our instruction (%s) have been followed correctly', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-cached-nginx-instruction" target="_blank">https://url.mattplugins.com/converter-error-rewrites-cached-nginx-instruction</a>'
|
||||
),
|
||||
]
|
||||
) . '</em>',
|
||||
]
|
||||
);
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RewritesNotExecutedNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'rewrites_not_executed';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$server_name = $this->get_nginx_server_name();
|
||||
$message = [];
|
||||
|
||||
if ( $server_name === null ) {
|
||||
$message[] = __( 'It appears that your server does not support using .htaccess files from custom locations, or it requires additional configuration for the plugin to function properly.', 'webp-converter-for-media' );
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'Please check %1$sour instruction%2$s which should help you solve your problem. This will allow the plugin to function properly.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-not-executed-instruction" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
} else {
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: server name */
|
||||
__( 'For the %1$s server, please contact your hosting\'s technical support to allow the plugin to function properly. Your server needs additional configuration, but your hosting\'s technical support will take care of it for you. Please, send them the following message:', 'webp-converter-for-media' ),
|
||||
$server_name
|
||||
);
|
||||
$message[] = implode(
|
||||
'',
|
||||
[
|
||||
'<em>',
|
||||
sprintf(
|
||||
/* translators: %1$s: plugin name, %2$s: home URL */
|
||||
__( 'I am trying to configure the %1$s plugin that supports the WebP and AVIF format. I need your help in adding the required rules to the Nginx configuration of my website - %2$s.', 'webp-converter-for-media' ),
|
||||
'Converter for Media',
|
||||
get_home_url()
|
||||
),
|
||||
' ',
|
||||
sprintf(
|
||||
/* translators: %s: anchor tag */
|
||||
__( 'You can find more information in the instruction: %s', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-not-executed-message" target="_blank">https://url.mattplugins.com/converter-error-rewrites-not-executed-message</a>',
|
||||
'Configuration for Nginx'
|
||||
),
|
||||
'</em>',
|
||||
]
|
||||
);
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag */
|
||||
__( '%1$sPlease, copy the above message and send it to the technical support of your hosting.%2$s They should help you in this matter.', 'webp-converter-for-media' ),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
private function get_nginx_server_name() {
|
||||
if ( getenv( 'IS_WPE' ) ) {
|
||||
return 'WP Engine';
|
||||
} elseif ( strpos( strtolower( $_SERVER['SERVER_SOFTWARE'] ?? '' ), 'nginx' ) !== false ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
return 'Nginx';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RewritesNotWorkingNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'rewrites_not_working';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'It appears that redirects on your server are not working. This means that your server configuration is not compatible with this plugin. Adapting the redirects from the .htaccess file to your server configuration is necessary for the plugin to work properly.', 'webp-converter-for-media' ),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'In this case, please, %1$scontact us%2$s. We will try to help you.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-not-working-contact" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RewritesUploadsBlockedNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'rewrites_uploads_blocked';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: directory path */
|
||||
__( 'It appears that mod_rewrite is blocked on your server for the %1$s directory. The blocking issue applies to rewrites from the .htaccess file for files from the %1$s directory. Please, ask your hosting\'s technical support to unblock rewrites from the .htaccess file for files in the %1$s directory.', 'webp-converter-for-media' ),
|
||||
'/wp-content/uploads/'
|
||||
),
|
||||
__( 'In this case, please, contact your server administrator.', 'webp-converter-for-media' ),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class SettingsIncorrectNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'settings_incorrect';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'The plugin settings are incorrect! Check them out and save them again. Please, remember that you need to have at least one option selected for each field.', 'webp-converter-for-media' ),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class UnsupportedPlaygroundServerNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'unsupported_server';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %s: server name */
|
||||
__( 'The %s environment on which you launched your website does not meet the technical requirements of our plugin. Unfortunately, we have no control over the configuration of this server. Please, test the plugin on a different server and you will surely be satisfied.', 'webp-converter-for-media' ),
|
||||
'WordPress Playground'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class WebpRequiredNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'webp_required';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'WebP as an output format is required. In the "Output formats" option, select the WebP format.', 'webp-converter-for-media' ),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ConversionErrorException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Error occurred while converting image: "%s".';
|
||||
const ERROR_CODE = 'convert_error';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
abstract class ExceptionAbstract extends \Exception implements ExceptionInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
final public function __construct( $value = [] ) {
|
||||
$this->code = $this->get_error_status();
|
||||
parent::__construct( $this->get_error_message( (array) $value ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
interface ExceptionInterface {
|
||||
|
||||
/**
|
||||
* @param mixed[]|string $value Params of exception.
|
||||
*/
|
||||
public function __construct( $value = [] );
|
||||
|
||||
/**
|
||||
* Returns message of error.
|
||||
*
|
||||
* @param string[] $values Params from class constructor.
|
||||
*
|
||||
* @return string Error message.
|
||||
*/
|
||||
public function get_error_message( array $values ): string;
|
||||
|
||||
/**
|
||||
* Returns status of error.
|
||||
*
|
||||
* @return string Error status.
|
||||
*/
|
||||
public function get_error_status(): string;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ExtensionUnsupportedException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Unsupported extension "%s" for file "%s".';
|
||||
const ERROR_CODE = 'unsupported_extension';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0], $values[1] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|