'+imagifyOptions.labels.cleaningInfo+'
"+imagifyOptions.labels.themesAdded+"
")}))}(window,document,jQuery),function(t,a,s){imagifyOptions.bulk&&(t.imagify.optionsBulk={error:!1,working:!1,processIsStopped:!1,$button:null,$progressWrap:null,$progressBar:null,$progressText:null,init:function(){var i,e;this.$missingWebpElement=s(".generate-missing-webp"),this.$missingWebpMessage=s(".generate-missing-webp p"),this.$button=s("#imagify-generate-webp-versions"),this.$progressWrap=this.$button.siblings(".imagify-progress"),this.$progressBar=this.$progressWrap.find(".bar"),this.$progressText=this.$progressBar.find(".percent"),s("#imagify_convert_to_webp").on("change.imagify init.imagify",{imagifyOptionsBulk:this},this.toggleButton).trigger("init.imagify"),this.$button.on("click.imagify",{imagifyOptionsBulk:this},this.maybeLaunchMissingWebpProcess),s(a).on("imagifybeat-send",{imagifyOptionsBulk:this},this.addQueueImagifybeat).on("imagifybeat-tick",{imagifyOptionsBulk:this},this.processQueueImagifybeat).on("imagifybeat-send",this.addRequirementsImagifybeat).on("imagifybeat-tick",{imagifyOptionsBulk:this},this.processRequirementsImagifybeat),!1!==imagifyOptions.bulk.progress_webp.total&&!1!==imagifyOptions.bulk.progress_webp.remaining&&(t.imagify.optionsBulk.error=!1,t.imagify.optionsBulk.working=!0,t.imagify.optionsBulk.processIsStopped=!1,this.$button.prop("disabled",!0).find(".dashicons").addClass("rotate"),t.imagify.beat.interval(15),t.imagify.beat.disableSuspend(),this.$missingWebpMessage.hide().attr("aria-hidden","true"),i=imagifyOptions.bulk.progress_webp.total-imagifyOptions.bulk.progress_webp.remaining,e=Math.floor(i/imagifyOptions.bulk.progress_webp.total*100),this.$progressBar.css("width",e+"%"),this.$progressText.text(i+"/"+imagifyOptions.bulk.progress_webp.total),this.$progressWrap.slideDown().attr("aria-hidden","false").removeClass("hidden"))},toggleButton:function(i){this.checked?i.data.imagifyOptionsBulk.$button.prop("disabled",!1):i.data.imagifyOptionsBulk.$button.prop("disabled",!0)},maybeLaunchMissingWebpProcess:function(i){!i.data.imagifyOptionsBulk||i.data.imagifyOptionsBulk.working||i.data.imagifyOptionsBulk.hasBlockingError(!0)||(i.data.imagifyOptionsBulk.error=!1,i.data.imagifyOptionsBulk.working=!0,i.data.imagifyOptionsBulk.processIsStopped=!1,i.data.imagifyOptionsBulk.$button.prop("disabled",!0).find(".dashicons").addClass("rotate"),t.imagify.beat.interval(15),t.imagify.beat.disableSuspend(),i.data.imagifyOptionsBulk.launchProcess())},addQueueImagifybeat:function(i,e){e[imagifyOptions.bulk.imagifybeatIDs.progress]=imagifyOptions.bulk.contexts},processQueueImagifybeat:function(i,e){var a,t;i.data.imagifyOptionsBulk&&void 0===e[imagifyOptions.bulk.imagifybeatIDs.progress]||(i.data.imagifyOptionsBulk.processIsStopped||0===(e=e[imagifyOptions.bulk.imagifybeatIDs.progress]).remaining?i.data.imagifyOptionsBulk.processFinished():(a=e.total-e.remaining,t=Math.floor(a/e.total*100),i.data.imagifyOptionsBulk.$progressBar.css("width",t+"%"),i.data.imagifyOptionsBulk.$progressText.text(a+"/"+e.total)))},addRequirementsImagifybeat:function(i,e){e[imagifyOptions.bulk.imagifybeatIDs.requirements]=1},processRequirementsImagifybeat:function(i,e){i.data.imagifyOptionsBulk&&void 0===e[imagifyOptions.bulk.imagifybeatIDs.requirements]||(e=e[imagifyOptions.bulk.imagifybeatIDs.requirements],imagifyOptions.bulk.curlMissing=e.curl_missing,imagifyOptions.bulk.editorMissing=e.editor_missing,imagifyOptions.bulk.extHttpBlocked=e.external_http_blocked,imagifyOptions.bulk.apiDown=e.api_down,imagifyOptions.bulk.keyIsValid=e.key_is_valid,imagifyOptions.bulk.isOverQuota=e.is_over_quota)},launchProcess:function(){var a;this.processIsStopped||s.get((a=this).getAjaxUrl("MissingWebp",imagifyOptions.bulk.contexts)).done(function(i){var e;a.processIsStopped||(e=i.data&&i.data.message?i.data.message:imagifyOptions.bulk.ajaxErrorText,i.success?0===i.data.total?a.stopProcess("no-images"):(a.$missingWebpMessage.hide().attr("aria-hidden","true"),a.$progressText.text("0"+(i.data.total?"/"+i.data.total:"")),a.$progressWrap.slideDown().attr("aria-hidden","false").removeClass("hidden")):a.error||a.stopProcess(e))}).fail(function(){a.error||a.stopProcess("get-unoptimized-images")})},processFinished:function(){var i={};!1!==this.error&&(i="invalid-api-key"===this.error?{title:imagifyOptions.bulk.labels.invalidAPIKeyTitle,type:"info"}:"over-quota"===this.error?{title:imagifyOptions.bulk.labels.overQuotaTitle,html:s("#tmpl-imagify-overquota-alert").html(),type:"info",customClass:"imagify-swal-has-subtitle imagify-swal-error-header",showConfirmButton:!1}:"get-unoptimized-images"===this.error?{title:imagifyOptions.bulk.labels.getUnoptimizedImagesErrorTitle,html:imagifyOptions.bulk.labels.getUnoptimizedImagesErrorText,type:"info"}:"no-images"===this.error?{title:imagifyOptions.bulk.labels.nothingToDoTitle,html:imagifyOptions.bulk.labels.nothingToDoText,type:"info"}:"no-backup"===this.error?{title:imagifyOptions.bulk.labels.nothingToDoTitle,html:imagifyOptions.bulk.labels.nothingToDoNoBackupText,type:"info"}:{title:imagifyOptions.bulk.labels.error,html:this.error,type:"info"},this.displayError(i),this.error=!1),this.working=!1,this.processIsStopped=!1,t.imagify.beat.resetInterval(),t.imagify.beat.enableSuspend(),this.$progressWrap.slideUp().attr("aria-hidden","true").addClass("hidden"),this.$progressText.text("0"),this.$missingWebpElement.hide().attr("aria-hidden","true"),this.$button.find(".dashicons").removeClass("rotate")},hasBlockingError:function(i){return i=void 0!==i&&i,imagifyOptions.bulk.curlMissing?(i&&this.displayError({html:imagifyOptions.bulk.labels.curlMissing}),!0):imagifyOptions.bulk.editorMissing?(i&&this.displayError({html:imagifyOptions.bulk.labels.editorMissing}),!0):imagifyOptions.bulk.extHttpBlocked?(i&&this.displayError({html:imagifyOptions.bulk.labels.extHttpBlocked}),!0):imagifyOptions.bulk.apiDown?(i&&this.displayError({html:imagifyOptions.bulk.labels.apiDown}),!0):imagifyOptions.bulk.keyIsValid?!!imagifyOptions.bulk.isOverQuota&&(i&&this.displayError({title:imagifyOptions.bulk.labels.overQuotaTitle,html:s("#tmpl-imagify-overquota-alert").html(),type:"info",customClass:"imagify-swal-has-subtitle imagify-swal-error-header",showConfirmButton:!1}),!0):(i&&this.displayError({title:imagifyOptions.bulk.labels.invalidAPIKeyTitle,type:"info"}),!0)},displayError:function(i,e,a){var t={title:"",html:"",type:"error",customClass:"",width:620,padding:0,showCloseButton:!0,showConfirmButton:!0};(a=s.isPlainObject(i)?s.extend({},t,i):s.extend({},t,{title:i||"",html:e||""},a=a||{})).title=a.title||imagifyOptions.bulk.labels.error,a.customClass+=" imagify-sweet-alert",swal(a).catch(swal.noop)},getAjaxUrl:function(i,e){var a=ajaxurl+t.imagify.concat+"_wpnonce="+imagifyOptions.bulk.ajaxNonce;return(a+="&action="+imagifyOptions.bulk.ajaxActions[i])+("&context="+e.join("_"))},stopProcess:function(i){this.processIsStopped=!0,this.error=i,this.processFinished()}},t.imagify.optionsBulk.init())}(window,document,jQuery),function(o){var t=o.propHooks.checked;o.propHooks.checked={set:function(i,e,a){e=void 0===t?i[a]=e:t(i,e,a);return o(i).trigger("change.imagify"),e}},o(".imagify-select-all").on("click.imagify",function(){var i=o(this),e=i.data("action"),a=i.closest(".imagify-select-all-buttons"),t=a.prev(".imagify-check-group"),s="imagify-is-inactive";if(i.hasClass(s))return!1;a.find(".imagify-select-all").removeClass(s).attr("aria-disabled","false"),i.addClass(s).attr("aria-disabled","true"),t.find(".imagify-row-check").prop("checked",function(){return!o(this).is(":hidden,:disabled")&&"select"===e})}),o(".imagify-check-group .imagify-row-check").on("change.imagify",function(){var i=o(this).closest(".imagify-check-group"),e=i.find(".imagify-row-check"),a=e.filter(":visible:enabled").length,e=e.filter(":visible:enabled:checked").length,i=i.next(".imagify-select-all-buttons"),t="imagify-is-inactive";0===e&&i.find('[data-action="unselect"]').addClass(t).attr("aria-disabled","true"),e===a&&i.find('[data-action="select"]').addClass(t).attr("aria-disabled","true"),e!==a&&0'+imagifyOptions.labels.cleaningInfo+'
"+imagifyOptions.labels.themesAdded+"
")}))}(window,document,jQuery),function(t,a,s){imagifyOptions.bulk&&(t.imagify.optionsBulk={error:!1,working:!1,processIsStopped:!1,$button:null,$progressWrap:null,$progressBar:null,$progressText:null,init:function(){var i,e;this.$missingWebpElement=s(".generate-missing-webp"),this.$missingWebpMessage=s(".generate-missing-webp p"),this.$button=s("#imagify-generate-webp-versions"),this.$progressWrap=this.$button.siblings(".imagify-progress"),this.$progressBar=this.$progressWrap.find(".bar"),this.$progressText=this.$progressBar.find(".percent"),s("#imagify_convert_to_webp").on("change.imagify init.imagify",{imagifyOptionsBulk:this},this.toggleButton).trigger("init.imagify"),this.$button.on("click.imagify",{imagifyOptionsBulk:this},this.maybeLaunchMissingWebpProcess),s(a).on("imagifybeat-send",{imagifyOptionsBulk:this},this.addQueueImagifybeat).on("imagifybeat-tick",{imagifyOptionsBulk:this},this.processQueueImagifybeat).on("imagifybeat-send",this.addRequirementsImagifybeat).on("imagifybeat-tick",{imagifyOptionsBulk:this},this.processRequirementsImagifybeat),!1!==imagifyOptions.bulk.progress_next_gen.total&&!1!==imagifyOptions.bulk.progress_next_gen.remaining&&(t.imagify.optionsBulk.error=!1,t.imagify.optionsBulk.working=!0,t.imagify.optionsBulk.processIsStopped=!1,this.$button.prop("disabled",!0).find(".dashicons").addClass("rotate"),t.imagify.beat.interval(15),t.imagify.beat.disableSuspend(),this.$missingWebpMessage.hide().attr("aria-hidden","true"),i=imagifyOptions.bulk.progress_next_gen.total-imagifyOptions.bulk.progress_next_gen.remaining,e=Math.floor(i/imagifyOptions.bulk.progress_next_gen.total*100),this.$progressBar.css("width",e+"%"),this.$progressText.text(i+"/"+imagifyOptions.bulk.progress_next_gen.total),this.$progressWrap.slideDown().attr("aria-hidden","false").removeClass("hidden"))},toggleButton:function(i){this.checked?i.data.imagifyOptionsBulk.$button.prop("disabled",!1):i.data.imagifyOptionsBulk.$button.prop("disabled",!0)},maybeLaunchMissingWebpProcess:function(i){!i.data.imagifyOptionsBulk||i.data.imagifyOptionsBulk.working||i.data.imagifyOptionsBulk.hasBlockingError(!0)||(i.data.imagifyOptionsBulk.error=!1,i.data.imagifyOptionsBulk.working=!0,i.data.imagifyOptionsBulk.processIsStopped=!1,i.data.imagifyOptionsBulk.$button.prop("disabled",!0).find(".dashicons").addClass("rotate"),t.imagify.beat.interval(15),t.imagify.beat.disableSuspend(),i.data.imagifyOptionsBulk.launchProcess())},addQueueImagifybeat:function(i,e){e[imagifyOptions.bulk.imagifybeatIDs.progress]=imagifyOptions.bulk.contexts},processQueueImagifybeat:function(i,e){var a,t;i.data.imagifyOptionsBulk&&void 0===e[imagifyOptions.bulk.imagifybeatIDs.progress]||(i.data.imagifyOptionsBulk.processIsStopped||0===(e=e[imagifyOptions.bulk.imagifybeatIDs.progress]).remaining?i.data.imagifyOptionsBulk.processFinished():(a=e.total-e.remaining,t=Math.floor(a/e.total*100),i.data.imagifyOptionsBulk.$progressBar.css("width",t+"%"),i.data.imagifyOptionsBulk.$progressText.text(a+"/"+e.total)))},addRequirementsImagifybeat:function(i,e){e[imagifyOptions.bulk.imagifybeatIDs.requirements]=1},processRequirementsImagifybeat:function(i,e){i.data.imagifyOptionsBulk&&void 0===e[imagifyOptions.bulk.imagifybeatIDs.requirements]||(e=e[imagifyOptions.bulk.imagifybeatIDs.requirements],imagifyOptions.bulk.curlMissing=e.curl_missing,imagifyOptions.bulk.editorMissing=e.editor_missing,imagifyOptions.bulk.extHttpBlocked=e.external_http_blocked,imagifyOptions.bulk.apiDown=e.api_down,imagifyOptions.bulk.keyIsValid=e.key_is_valid,imagifyOptions.bulk.isOverQuota=e.is_over_quota)},launchProcess:function(){var a;this.processIsStopped||s.get((a=this).getAjaxUrl("MissingNextGen",imagifyOptions.bulk.contexts)).done(function(i){var e;a.processIsStopped||(e=i.data&&i.data.message?i.data.message:imagifyOptions.bulk.ajaxErrorText,i.success?0===i.data.total?a.stopProcess("no-images"):(a.$missingWebpMessage.hide().attr("aria-hidden","true"),a.$progressText.text("0"+(i.data.total?"/"+i.data.total:"")),a.$progressWrap.slideDown().attr("aria-hidden","false").removeClass("hidden")):a.error||a.stopProcess(e))}).fail(function(){a.error||a.stopProcess("get-unoptimized-images")})},processFinished:function(){var i={};!1!==this.error&&(i="invalid-api-key"===this.error?{title:imagifyOptions.bulk.labels.invalidAPIKeyTitle,type:"info"}:"over-quota"===this.error?{title:imagifyOptions.bulk.labels.overQuotaTitle,html:s("#tmpl-imagify-overquota-alert").html(),type:"info",customClass:"imagify-swal-has-subtitle imagify-swal-error-header",showConfirmButton:!1}:"get-unoptimized-images"===this.error?{title:imagifyOptions.bulk.labels.getUnoptimizedImagesErrorTitle,html:imagifyOptions.bulk.labels.getUnoptimizedImagesErrorText,type:"info"}:"no-images"===this.error?{title:imagifyOptions.bulk.labels.nothingToDoTitle,html:imagifyOptions.bulk.labels.nothingToDoText,type:"info"}:"no-backup"===this.error?{title:imagifyOptions.bulk.labels.nothingToDoTitle,html:imagifyOptions.bulk.labels.nothingToDoNoBackupText,type:"info"}:{title:imagifyOptions.bulk.labels.error,html:this.error,type:"info"},this.displayError(i),this.error=!1),this.working=!1,this.processIsStopped=!1,t.imagify.beat.resetInterval(),t.imagify.beat.enableSuspend(),this.$progressWrap.slideUp().attr("aria-hidden","true").addClass("hidden"),this.$progressText.text("0"),this.$missingWebpElement.hide().attr("aria-hidden","true"),this.$button.find(".dashicons").removeClass("rotate")},hasBlockingError:function(i){return i=void 0!==i&&i,imagifyOptions.bulk.curlMissing?(i&&this.displayError({html:imagifyOptions.bulk.labels.curlMissing}),!0):imagifyOptions.bulk.editorMissing?(i&&this.displayError({html:imagifyOptions.bulk.labels.editorMissing}),!0):imagifyOptions.bulk.extHttpBlocked?(i&&this.displayError({html:imagifyOptions.bulk.labels.extHttpBlocked}),!0):imagifyOptions.bulk.apiDown?(i&&this.displayError({html:imagifyOptions.bulk.labels.apiDown}),!0):imagifyOptions.bulk.keyIsValid?!!imagifyOptions.bulk.isOverQuota&&(i&&this.displayError({title:imagifyOptions.bulk.labels.overQuotaTitle,html:s("#tmpl-imagify-overquota-alert").html(),type:"info",customClass:"imagify-swal-has-subtitle imagify-swal-error-header",showConfirmButton:!1}),!0):(i&&this.displayError({title:imagifyOptions.bulk.labels.invalidAPIKeyTitle,type:"info"}),!0)},displayError:function(i,e,a){var t={title:"",html:"",type:"error",customClass:"",width:620,padding:0,showCloseButton:!0,showConfirmButton:!0};(a=s.isPlainObject(i)?s.extend({},t,i):s.extend({},t,{title:i||"",html:e||""},a=a||{})).title=a.title||imagifyOptions.bulk.labels.error,a.customClass+=" imagify-sweet-alert",swal(a).catch(swal.noop)},getAjaxUrl:function(i,e){var a=ajaxurl+t.imagify.concat+"_wpnonce="+imagifyOptions.bulk.ajaxNonce;return(a+="&action="+imagifyOptions.bulk.ajaxActions[i])+("&context="+e.join("_"))},stopProcess:function(i){this.processIsStopped=!0,this.error=i,this.processFinished()}},t.imagify.optionsBulk.init())}(window,document,jQuery),function(n){var t=n.propHooks.checked;n.propHooks.checked={set:function(i,e,a){e=void 0===t?i[a]=e:t(i,e,a);return n(i).trigger("change.imagify"),e}},n(".imagify-select-all").on("click.imagify",function(){var i=n(this),e=i.data("action"),a=i.closest(".imagify-select-all-buttons"),t=a.prev(".imagify-check-group"),s="imagify-is-inactive";if(i.hasClass(s))return!1;a.find(".imagify-select-all").removeClass(s).attr("aria-disabled","false"),i.addClass(s).attr("aria-disabled","true"),t.find(".imagify-row-check").prop("checked",function(){return!n(this).is(":hidden,:disabled")&&"select"===e})}),n(".imagify-check-group .imagify-row-check").on("change.imagify",function(){var i=n(this).closest(".imagify-check-group"),e=i.find(".imagify-row-check"),a=e.filter(":visible:enabled").length,e=e.filter(":visible:enabled:checked").length,i=i.next(".imagify-select-all-buttons"),t="imagify-is-inactive";0===e&&i.find('[data-action="unselect"]').addClass(t).attr("aria-disabled","true"),e===a&&i.find('[data-action="select"]').addClass(t).attr("aria-disabled","true"),e!==a&&0' . $this->get_file_path( true ) . '
'
+ );
+
+ echo '' . esc_html( $rules ) . ''; + } + } + + /** + * Add rules on plugin activation. + */ + public function activate() { + $conf = $this->get_server_conf(); + + if ( ! $conf ) { + return; + } + + if ( ! get_imagify_option( 'display_nextgen' ) ) { + return; + } + + if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) { + return; + } + + if ( is_wp_error( $conf->is_file_writable() ) ) { + return; + } + + $conf->add(); + } + + /** + * Remove rules on plugin deactivation. + * + * @since 1.9 + */ + public function deactivate() { + $conf = $this->get_server_conf(); + + if ( ! $conf ) { + return; + } + + if ( ! get_imagify_option( 'display_nextgen' ) ) { + return; + } + + if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) { + return; + } + + $file_path = $conf->get_file_path(); + $filesystem = \Imagify_Filesystem::get_instance(); + + if ( ! $filesystem->exists( $file_path ) ) { + return; + } + if ( ! $filesystem->is_writable( $file_path ) ) { + return; + } + + $conf->remove(); + } + + /** + * Get the path to the directory conf file. + * + * @param bool $relative True to get a path relative to the site’s root. + * + * @return string|bool The file path. False on failure. + */ + public function get_file_path( $relative = false ) { + if ( ! $this->get_server_conf() ) { + return false; + } + + $file_path = $this->get_server_conf()->get_file_path(); + + if ( $relative ) { + return \Imagify_Filesystem::get_instance()->make_path_relative( $file_path ); + } + + return $file_path; + } + + /** + * Get the server conf instance. + * + * @return WriteFileInterface + */ + protected function get_server_conf() { + global $is_apache, $is_iis7, $is_nginx; + + if ( isset( $this->server_conf ) ) { + return $this->server_conf; + } + + if ( $is_apache ) { + $this->server_conf = new Apache(); + } elseif ( $is_iis7 ) { + $this->server_conf = new IIS(); + } elseif ( $is_nginx ) { + $this->server_conf = new Nginx(); + } + + return $this->server_conf; + } +} diff --git a/classes/Avif/RewriteRules/IIS.php b/classes/Avif/RewriteRules/IIS.php new file mode 100644 index 000000000..8c4295979 --- /dev/null +++ b/classes/Avif/RewriteRules/IIS.php @@ -0,0 +1,58 @@ +get_extensions_pattern(); + $extensions = str_replace( '|avif', '', $extensions ); + $home_root = wp_parse_url( home_url( '/' ) ); + $home_root = $home_root['path']; + + return trim( ' + +
' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -124,7 +124,7 @@ public function can_be_processed() {
return new \WP_Error(
'not_a_file',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'This does not seem to be a file: %s.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -135,7 +135,7 @@ public function can_be_processed() {
return new \WP_Error(
'not_writable',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be writable.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -148,7 +148,7 @@ public function can_be_processed() {
return new \WP_Error(
'folder_not_writable',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The folder %s does not seem to be writable.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $parent_folder ) ) . '
'
)
@@ -197,7 +197,7 @@ public function resize( $dimensions = [], $max_width = 0 ) {
return new \WP_Error(
'not_an_image',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -321,7 +321,7 @@ public function create_thumbnail( $destination ) {
return new \WP_Error(
'not_an_image',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '
'
)
@@ -423,6 +423,22 @@ public function backup( $backup_path = null, $backup_source = null ) {
) );
}
+ // Check if a '-scaled' version of the image exists.
+ $scaled_path = preg_replace( '/(\.)([^\.]+)$/', '-scaled.$2', $backup_source );
+ if ( $this->filesystem->exists( $scaled_path ) ) {
+ // Create a backup path for the scaled image.
+ $scaled_backup_path = preg_replace( '/(\.)([^\.]+)$/', '-scaled.$2', $backup_path );
+ // Copy the '-scaled' version to the backup.
+ $this->filesystem->copy( $scaled_path, $scaled_backup_path, $overwrite, FS_CHMOD_FILE );
+
+ if ( ! $this->filesystem->exists( $scaled_backup_path ) ) {
+ return new \WP_Error( 'backup_doesnt_exist', __( 'The file could not be saved.', 'imagify' ), array(
+ 'file_path' => $this->filesystem->make_path_relative( $scaled_path ),
+ 'backup_path' => $this->filesystem->make_path_relative( $scaled_backup_path ),
+ ) );
+ }
+ }
+
return true;
}
@@ -438,7 +454,7 @@ public function backup( $backup_path = null, $backup_source = null ) {
* @type bool $backup False to prevent backup. True to follow the user's setting. A backup can't be forced.
* @type string $backup_path If a backup must be done, this is the path to use. Default is the backup path used for the WP Media Library.
* @type int $optimization_level The optimization level (2=ultra, 1=aggressive, 0=normal).
- * @type string $convert Set to 'webp' to convert the image to WebP.
+ * @type string $convert Set to 'webp' to convert the image to WebP, 'avif' to convert image to AVIF.
* @type string $context The context.
* @type int $original_size The file size, sent to the API.
* }
@@ -474,7 +490,7 @@ public function optimize( $args = [] ) {
*
* @param string $path Absolute path to the media file.
* @param array $args Arguments passed to the method.
- */
+ */
do_action( 'imagify_before_optimize_file', $this->path, $args );
/**
@@ -485,7 +501,7 @@ public function optimize( $args = [] ) {
*
* @param string $path Absolute path to the image file.
* @param bool $backup True if a backup will be make.
- */
+ */
do_action_deprecated( 'before_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
if ( $args['backup'] ) {
@@ -509,6 +525,7 @@ public function optimize( $args = [] ) {
if ( $args['convert'] ) {
$data['convert'] = $args['convert'];
+ $format = $args['convert'];
}
$response = upload_imagify_image( [
@@ -534,8 +551,12 @@ public function optimize( $args = [] ) {
$args['convert'] = '';
}
- if ( 'webp' === $args['convert'] ) {
- $destination_path = $this->get_path_to_webp();
+ $formats = [
+ 'webp',
+ 'avif',
+ ];
+ if ( in_array( $args['convert'], $formats, true ) ) {
+ $destination_path = $this->get_path_to_nextgen( $args['convert'] );
$this->path = $destination_path;
$this->file_type = null;
$this->editor = null;
@@ -557,7 +578,7 @@ public function optimize( $args = [] ) {
*
* @param string $path Absolute path to the image file.
* @param bool $backup True if a backup has been made.
- */
+ */
do_action_deprecated( 'after_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
/**
@@ -568,7 +589,7 @@ public function optimize( $args = [] ) {
*
* @param string $path Absolute path to the media file.
* @param array $args Arguments passed to the method.
- */
+ */
do_action( 'imagify_after_optimize_file', $this->path, $args );
return $response;
@@ -603,7 +624,7 @@ protected function get_editor() {
$this->editor = new \WP_Error(
'image_editor',
sprintf(
- /* translators: %1$s is an error message, %2$s is a "More info?" link. */
+ /* translators: %1$s is an error message, %2$s is a "More info?" link. */
__( 'No php extensions are available to edit images on the server. ImageMagick or GD is required. The internal error is: %1$s. %2$s', 'imagify' ),
$this->editor->get_error_message(),
'' . __( 'More info?', 'imagify' ) . ''
@@ -765,6 +786,26 @@ public function get_path_to_webp() {
return imagify_path_to_webp( $this->path );
}
+ /**
+ * Replace the file extension by its next-gen format extension.
+ *
+ * @since 2.2
+ *
+ * @param string $format the format we are targeting.
+ * @return string|bool The file path on success. False if not an image or on failure.
+ */
+ public function get_path_to_nextgen( string $format ) {
+ if ( ! $this->is_image() ) {
+ return false;
+ }
+
+ if ( $this->is_webp() || $this->is_avif() ) {
+ return false;
+ }
+
+ return imagify_path_to_nextgen( $this->path, $format );
+ }
+
/**
* Tell if the file is a WebP image.
* Rejects "path/to/.webp" files.
@@ -778,6 +819,18 @@ public function is_webp() {
return preg_match( '@(?!^|/|\\\)\.webp$@i', $this->path );
}
+ /**
+ * Tell if the file is an AVIF image.
+ * Rejects "path/to/.avif" files.
+ *
+ * @since 2.2
+ *
+ * @return bool
+ */
+ public function is_avif() {
+ return preg_match( '@(?!^|/|\\\)\.avif$@i', $this->path );
+ }
+
/**
* Get the file mime type + file extension.
*
diff --git a/classes/Optimization/Process/AbstractProcess.php b/classes/Optimization/Process/AbstractProcess.php
index bec0948df..6ec243805 100644
--- a/classes/Optimization/Process/AbstractProcess.php
+++ b/classes/Optimization/Process/AbstractProcess.php
@@ -1,4 +1,6 @@
filesystem = \Imagify_Filesystem::get_instance();
+ $this->format = $this->get_current_format();
}
/**
@@ -114,7 +143,8 @@ public function __construct( $id ) {
*
* @since 1.9
*
- * @param mixed $id Whatever.
+ * @param mixed $id Whatever.
+ *
* @return bool
*/
public static function constructor_accepts( $id ) {
@@ -212,7 +242,7 @@ public function is_valid() {
*
* @since 1.9
*
- * @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity.
+ * @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity.
* @return bool
*/
public function current_user_can( $describer ) {
@@ -225,19 +255,15 @@ public function current_user_can( $describer ) {
return $media->get_context_instance()->current_user_can( $describer, $media->get_id() );
}
-
- /** ----------------------------------------------------------------------------------------- */
- /** OPTIMIZATION ============================================================================ */
- /** ----------------------------------------------------------------------------------------- */
-
/**
* Optimize a media files.
*
* @since 1.9
*
- * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
- * @param array $args An array of optionnal arguments.
- * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
+ * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
+ * @param array $args An array of optionnal arguments.
+ *
+ * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
*/
public function optimize( $optimization_level = null, $args = [] ) {
if ( ! $this->is_valid() ) {
@@ -256,13 +282,13 @@ public function optimize( $optimization_level = null, $args = [] ) {
return new WP_Error( 'optimized', __( 'This media has already been optimized by Imagify.', 'imagify' ) );
}
- if ( $data->is_already_optimized() && $this->has_webp() ) {
- // If already optimized but has WebP, delete WebP versions and optimization data.
+ if ( $data->is_already_optimized() && $this->has_next_gen() ) {
+ // If already optimized but has next-gen, delete next-gen versions and optimization data.
$data->delete_optimization_data();
- $deleted = $this->delete_webp_files();
+ $deleted = $this->delete_nextgen_files();
if ( is_wp_error( $deleted ) ) {
- return new WP_Error( 'webp_not_deleted', __( 'Previous WebP files could not be deleted.', 'imagify' ) );
+ return new WP_Error( 'next_gen_not_deleted', __( 'Previous Next-Gen files could not be deleted.', 'imagify' ) );
}
}
@@ -280,9 +306,10 @@ public function optimize( $optimization_level = null, $args = [] ) {
*
* @since 1.9
*
- * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
- * @param array $args An array of optionnal arguments.
- * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
+ * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
+ * @param array $args An array of optionnal arguments.
+ *
+ * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
*/
public function reoptimize( $optimization_level = null, $args = [] ) {
if ( ! $this->is_valid() ) {
@@ -322,17 +349,21 @@ public function reoptimize( $optimization_level = null, $args = [] ) {
* Optimize several file sizes by pushing tasks into the queue.
*
* @since 1.9
- * @see MediaOptimization->task_before()
- * @see MediaOptimization->task_after()
+ * @see MediaOptimization->task_before()
+ * @see MediaOptimization->task_after()
*
- * @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
- * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
- * @param array $args {
- * An array of optionnal arguments.
+ * @since 2.2
+ * Addition of the image format
+ *
+ * @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
+ * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
+ * @param array $args {
+ * An array of optionnal arguments.
*
* @type string $hook_suffix Suffix used to trigger hooks before and after optimization.
* }
- * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
+ *
+ * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
*/
public function optimize_sizes( $sizes, $optimization_level = null, $args = [] ) {
if ( ! $this->is_valid() ) {
@@ -358,41 +389,51 @@ public function optimize_sizes( $sizes, $optimization_level = null, $args = [] )
}
if ( $media->is_image() ) {
- if ( $this->get_option( 'convert_to_webp' ) ) {
- // Add WebP convertion.
+ // Add Next-Gen conversion.
+ $formats = imagify_nextgen_images_formats();
+
+ foreach ( $formats as $format ) {
+ if ( 'avif' === $format ) {
+ $format_suffix = static::AVIF_SUFFIX;
+ } elseif ( 'webp' === $format ) {
+ $format_suffix = static::WEBP_SUFFIX;
+ }
+
$files = $media->get_media_files();
foreach ( $sizes as $size_name ) {
if ( empty( $files[ $size_name ] ) ) {
continue;
}
- if ( 'image/webp' === $files[ $size_name ]['mime-type'] ) {
+
+ if ( $this->get_mime_type( $format ) === $files[ $size_name ]['mime-type'] ) {
continue;
}
- if ( in_array( $size_name . static::WEBP_SUFFIX, $sizes, true ) ) {
+
+ if ( in_array( $size_name . $format_suffix, $sizes, true ) ) {
continue;
}
- array_unshift( $sizes, $size_name . static::WEBP_SUFFIX );
+ array_unshift( $sizes, $size_name . $format_suffix );
}
}
if ( ! $media->get_context_instance()->can_backup() && ! $media->get_backup_path() && ! $this->get_data()->get_size_data( 'full', 'success' ) ) {
/**
* Backup is NOT activated, and a backup file does NOT exist yet, and the full size is NOT optimized yet.
- * WebP conversion needs a backup file, even a temporary one: we’ll create one.
+ * Next-Gen conversion needs a backup file, even a temporary one: we’ll create one.
*/
- $webp = false;
+ $next_gen = false;
foreach ( $sizes as $size_name ) {
- if ( $this->is_size_webp( $size_name ) ) {
- $webp = true;
+ if ( $this->is_size_next_gen( $size_name ) ) {
+ $next_gen = true;
break;
}
}
- if ( $webp ) {
- // We have at least one WebP conversion to do: create a temporary backup.
+ if ( $next_gen ) {
+ // We have at least one next-gen conversion to do: create a temporary backup.
$backuped = $this->get_original_file()->backup( $media->get_raw_backup_path() );
if ( $backuped ) {
@@ -443,9 +484,10 @@ public function optimize_sizes( $sizes, $optimization_level = null, $args = [] )
*
* @since 1.9
*
- * @param string $size The media size.
- * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
- * @return array|\WP_Error Optimized image data. A \WP_Error object on error.
+ * @param string $size The media size.
+ * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
+ *
+ * @return array|WP_Error Optimized image data. A WP_Error object on error.
*/
public function optimize_size( $size, $optimization_level = null ) {
if ( ! $this->is_valid() ) { // Bail out.
@@ -455,13 +497,13 @@ public function optimize_size( $size, $optimization_level = null ) {
$media = $this->get_media();
$sizes = $media->get_media_files();
$thumb_size = $size;
- $webp = $this->is_size_webp( $size );
+ $next_gen = $this->is_size_next_gen( $size );
$path_is_temp = false;
- if ( $webp ) {
+ if ( $next_gen ) {
// We'll make sure the file is an image later.
- $thumb_size = $webp; // Contains the name of the non-WebP size.
- $webp = true;
+ $thumb_size = $next_gen; // Contains the name of the non-next-gen size.
+ $next_gen = true;
}
if ( empty( $sizes[ $thumb_size ]['path'] ) ) { // Bail out.
@@ -469,7 +511,7 @@ public function optimize_size( $size, $optimization_level = null ) {
return new WP_Error(
'unknown_size',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'The size %s is unknown.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
@@ -478,12 +520,12 @@ public function optimize_size( $size, $optimization_level = null ) {
if ( $this->get_data()->get_size_data( $size, 'success' ) ) { // Bail out.
// This size is already optimized with Imagify, and must not be optimized again.
- if ( $webp ) {
+ if ( $next_gen ) {
return new WP_Error(
'size_is_successfully_optimized',
sprintf(
- /* translators: %s is a size name. */
- __( 'The WebP format for the size %s already exists.', 'imagify' ),
+ /* translators: %s is a size name. */
+ __( 'The Next-Gen format for the size %s already exists.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
);
@@ -491,7 +533,7 @@ public function optimize_size( $size, $optimization_level = null ) {
return new WP_Error(
'size_is_successfully_optimized',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'The size %s is already optimized by Imagify.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
@@ -506,16 +548,16 @@ public function optimize_size( $size, $optimization_level = null ) {
$optimization_level = $this->sanitize_optimization_level( $optimization_level );
- if ( $webp && $this->get_data()->get_size_data( $thumb_size, 'success' ) ) {
- // We want a WebP version but the source file is already optimized by Imagify.
+ if ( $next_gen && $this->get_data()->get_size_data( $thumb_size, 'success' ) ) {
+ // We want a next-gen version but the source file is already optimized by Imagify.
$result = $this->create_temporary_copy( $thumb_size, $sizes );
if ( ! $result ) { // Bail out.
- // Could not create a copy of the non-WebP version.
+ // Could not create a copy of the non-next-gen version.
$response = new WP_Error(
- 'non_webp_copy_failed',
+ 'non_next_gen_copy_failed',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'Could not create an unoptimized copy of the size %s.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
@@ -548,7 +590,7 @@ public function optimize_size( $size, $optimization_level = null ) {
$response = new WP_Error(
'extension_not_supported',
sprintf(
- /* translators: %s is a file extension. */
+ /* translators: %s is a file extension. */
__( '%s cannot be optimized.', 'imagify' ),
'' . esc_html( strtolower( $extension ) ) . '
'
)
@@ -564,14 +606,14 @@ public function optimize_size( $size, $optimization_level = null ) {
return $response;
}
- if ( $webp && ! $file->is_image() ) { // Bail out.
+ if ( $next_gen && ! $file->is_image() ) { // Bail out.
if ( $path_is_temp ) {
$this->filesystem->delete( $path );
}
$response = new WP_Error(
- 'no_webp',
- __( 'This file is not an image and cannot be converted to WebP format.', 'imagify' )
+ 'no_next_gen',
+ __( 'This file is not an image and cannot be converted to Next-Gen format.', 'imagify' )
);
$this->update_size_optimization_data( $response, $size, $optimization_level );
@@ -583,11 +625,11 @@ public function optimize_size( $size, $optimization_level = null ) {
/**
* Fires before optimizing a file.
- * Return a \WP_Error object to prevent the optimization.
+ * Return a WP_Error object to prevent the optimization.
*
* @since 1.9
*
- * @param null|WP_Error $response Null by default. Return a \WP_Error object to prevent optimization.
+ * @param null|WP_Error $response Null by default. Return a WP_Error object to prevent optimization.
* @param ProcessInterface $process The optimization process instance.
* @param File $file The file instance. If $webp is true, $file references the non-WebP file.
* @param string $thumb_size The media size.
@@ -595,7 +637,7 @@ public function optimize_size( $size, $optimization_level = null ) {
* @param bool $webp The image will be converted to WebP.
* @param bool $is_disabled Tell if this size is disabled from optimization.
*/
- $response = apply_filters( 'imagify_before_optimize_size', null, $this, $file, $thumb_size, $optimization_level, $webp, $is_disabled );
+ $response = apply_filters( 'imagify_before_optimize_size', null, $this, $file, $thumb_size, $optimization_level, $next_gen, $is_disabled );
if ( ! is_wp_error( $response ) ) {
if ( $is_disabled ) {
@@ -603,7 +645,7 @@ public function optimize_size( $size, $optimization_level = null ) {
$response = new WP_Error(
'unauthorized_size',
sprintf(
- /* translators: %s is a size name. */
+ /* translators: %s is a size name. */
__( 'The size %s is not authorized to be optimized. Update your Imagify settings if you want to optimize it.', 'imagify' ),
'' . esc_html( $thumb_size ) . '
'
)
@@ -612,21 +654,21 @@ public function optimize_size( $size, $optimization_level = null ) {
$response = new WP_Error(
'file_not_exists',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to exist.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $file->get_path() ) ) . '
'
)
);
- } elseif ( $webp && ! $this->can_create_webp_version( $file->get_path() ) ) {
+ } elseif ( $next_gen && ! $this->can_create_next_gen_version( $file->get_path() ) ) {
$response = new WP_Error(
'is_animated_gif',
- __( 'This file is an animated gif: since Imagify does not support animated WebP, WebP creation for animated gif is disabled.', 'imagify' )
+ __( 'This file is an animated gif: since Imagify does not support animated WebP/AVIF, WebP/AVIF creation for animated gif is disabled.', 'imagify' )
);
} elseif ( ! $this->filesystem->is_writable( $file->get_path() ) ) {
$response = new WP_Error(
'file_not_writable',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be writable.', 'imagify' ),
'' . esc_html( $this->filesystem->make_path_relative( $file->get_path() ) ) . '
'
)
@@ -635,6 +677,16 @@ public function optimize_size( $size, $optimization_level = null ) {
// Maybe resize the file.
$response = $this->maybe_resize( $thumb_size, $file );
+ $convert = '';
+
+ if ( $next_gen ) {
+ if ( strpos( $size, static::AVIF_SUFFIX ) ) {
+ $convert = 'avif';
+ } elseif ( strpos( $size, static::WEBP_SUFFIX ) ) {
+ $convert = 'webp';
+ }
+ }
+
if ( ! is_wp_error( $response ) ) {
// Resizing succeeded: optimize the file.
$response = $file->optimize( [
@@ -642,19 +694,20 @@ public function optimize_size( $size, $optimization_level = null ) {
'backup_path' => $media->get_raw_backup_path(),
'backup_source' => 'full' === $thumb_size ? $media->get_original_path() : null,
'optimization_level' => $optimization_level,
- 'convert' => $webp ? 'webp' : '',
+ 'convert' => $convert,
'keep_exif' => true,
'context' => $media->get_context(),
'original_size' => $response['file_size'],
] );
- $response = $this->compare_webp_file_size( [
- 'response' => $response,
- 'file' => $file,
- 'is_webp' => $webp,
- 'non_webp_thumb_size' => $thumb_size,
- 'non_webp_file_path' => $sizes[ $thumb_size ]['path'], // Don't use $path nor $file->get_path(), it may return the path to a temporary file.
- 'optimization_level' => $optimization_level,
+ $response = $this->compare_next_gen_file_size( [
+ 'response' => $response,
+ 'file' => $file,
+ 'is_next_gen' => $next_gen,
+ 'next_gen_format' => $convert,
+ 'non_next_gen_thumb_size' => $thumb_size,
+ 'non_next_gen_file_path' => $sizes[ $thumb_size ]['path'], // Don't use $path nor $file->get_path(), it may return the path to a temporary file.
+ 'optimization_level' => $optimization_level,
] );
if ( property_exists( $response, 'message' ) ) {
@@ -682,7 +735,7 @@ public function optimize_size( $size, $optimization_level = null ) {
* @param bool $webp The image was supposed to be converted to WebP.
* @param bool $is_disabled Tell if this size is disabled from optimization.
*/
- do_action( 'imagify_after_optimize_size', $this, $file, $thumb_size, $optimization_level, $webp, $is_disabled );
+ do_action( 'imagify_after_optimize_size', $this, $file, $thumb_size, $optimization_level, $next_gen, $is_disabled );
if ( ! $path_is_temp ) {
return $data;
@@ -704,108 +757,109 @@ public function optimize_size( $size, $optimization_level = null ) {
}
/**
- * Compare the file size of a file and its WebP version: if the WebP version is heavier than the non-WebP file, delete it.
+ * Compare the file size of a file and its Next-Gen version: if the Next-Gen version is heavier than the non-next-gen file, delete it.
*
- * @since 1.9.4
+ * @since 2.2
*
- * @param array $args {
+ * @param array $args {
* A list of mandatory arguments.
*
- * @type \sdtClass|\WP_Error $response Optimized image data. A \WP_Error object on error.
+ * @type \sdtClass|WP_Error $response Optimized image data. A WP_Error object on error.
* @type File $file The File instance of the file currently being optimized.
- * @type bool $is_webp Tell if we're requesting a WebP file.
- * @type string $non_webp_thumb_size Name of the corresponding non-WebP thumbnail size. If we're not creating a WebP file, this corresponds to the current thumbnail size.
- * @type string $non_webp_file_path Path to the corresponding non-WebP file. If we're not creating a WebP file, this corresponds to the current file path.
+ * @type bool $is_next_gen Tell if we're requesting a next-gen file.
+ * @type string $non_next_gen_thumb_size Name of the corresponding non-next-gen thumbnail size. If we're not creating a Next-Gen file, this corresponds to the current thumbnail size.
+ * @type string $non_next_gen_file_path Path to the corresponding non-next-gen file. If we're not creating a Next-Gen file, this corresponds to the current file path.
* @type string $optimization_level The optimization level.
* }
- * @return \sdtClass|WP_Error Optimized image data. A WP_Error object on error.
+ *
+ * @return \sdtClass|WP_Error Optimized image data. A WP_Error object on error.
*/
- protected function compare_webp_file_size( $args ) {
- static $keep_large_webp;
+ protected function compare_next_gen_file_size( $args ) {
+ static $keep_large_next_gen;
- if ( ! isset( $keep_large_webp ) ) {
+ if ( ! isset( $keep_large_next_gen ) ) {
/**
- * Allow to not store WebP images that are larger than their non-WebP version.
+ * Allow to not store next-gen images that are larger than their non-next-gen version.
*
* @since 1.9.4
*
- * @param bool $keep_large_webp Set to false if you prefer your visitors over your Pagespeed score. Default value is true.
+ * @param bool $keep_large_next-gen Set to false if you prefer your visitors over your Pagespeed score. Default value is true.
*/
- $keep_large_webp = apply_filters( 'imagify_keep_large_webp', true );
+ $keep_large_next_gen = apply_filters( 'imagify_keep_large_next_gen', true );
}
- if ( $keep_large_webp || is_wp_error( $args['response'] ) || ! $args['file']->is_image() ) {
+ if ( $keep_large_next_gen || is_wp_error( $args['response'] ) || ! $args['file']->is_image() ) {
return $args['response'];
}
// Optimization succeeded.
- if ( ! property_exists( $args['response'], 'message' ) && $args['is_webp'] ) {
+ if ( ! property_exists( $args['response'], 'message' ) && $args['is_next_gen'] ) {
/**
- * We just created a WebP version:
- * Check if it is lighter than the (maybe optimized) non-WebP file.
+ * We just created a next-gen version:
+ * Check if it is lighter than the (maybe optimized) non-next-gen file.
*/
- $data = $this->get_data()->get_size_data( $args['non_webp_thumb_size'] );
+ $data = $this->get_data()->get_size_data( $args['non_next_gen_thumb_size'] );
if ( ! $data ) {
- // We haven’t tried to optimize the non-WebP size yet.
+ // We haven’t tried to optimize the non-next-gen size yet.
return $args['response'];
}
if ( ! empty( $data['optimized_size'] ) ) {
- // The non-WebP size is optimized, we know the file size.
- $non_webp_file_size = $data['optimized_size'];
+ // The non-next-gen size is optimized, we know the file size.
+ $non_next_gen_file_size = $data['optimized_size'];
} else {
- // The non-WebP size is "already optimized" or "error": grab the file size directly from the file.
- $non_webp_file_size = $this->filesystem->size( $args['non_webp_file_path'] );
+ // The non-next-gen size is "already optimized" or "error": grab the file size directly from the file.
+ $non_next_gen_file_size = $this->filesystem->size( $args['non_next_gen_file_path'] );
}
- if ( ! $non_webp_file_size || $non_webp_file_size > $args['response']->new_size ) {
- // The new WebP file is lighter.
+ if ( ! $non_next_gen_file_size || $non_next_gen_file_size > $args['response']->new_size ) {
+ // The new next-gen file is lighter.
return $args['response'];
}
- // The new WebP file is heavier than the non-WebP file: delete it and return an error.
+ // The new next-gen file is heavier than the non-next-gen file: delete it and return an error.
$this->filesystem->delete( $args['file']->get_path() );
return new WP_Error(
- 'webp_heavy',
+ 'next_gen_heavy',
sprintf(
- /* translators: %s is a size name. */
- __( 'The WebP version of the size %s is heavier than its non-WebP version.', 'imagify' ),
- '' . esc_html( $args['non_webp_thumb_size'] ) . '
'
+ /* translators: %s is a size name. */
+ __( 'The Next-Gen version of the size %s is heavier than its non-next-gen version.', 'imagify' ),
+ '' . esc_html( $args['non_next_gen_thumb_size'] ) . '
'
)
);
}
/**
- * We just created a non-WebP version:
- * Check if its WebP version file is lighter than this one.
+ * We just created a non-next-gen version:
+ * Check if its next-gen version file is lighter than this one.
*/
- $webp_size = $args['non_webp_thumb_size'] . static::WEBP_SUFFIX;
- $webp_file_size = $this->get_data()->get_size_data( $webp_size, 'optimized_size' );
+ $next_gen_size = $args['non_next_gen_thumb_size'] . $args['next_gen_format'];
+ $next_gen_file_size = $this->get_data()->get_size_data( $next_gen_size, 'optimized_size' );
- if ( property_exists( $args['response'], 'message' ) || ! $webp_file_size || $webp_file_size < $args['response']->new_size ) {
- // The WebP file is lighter than this one.
+ if ( property_exists( $args['response'], 'message' ) || ! $next_gen_file_size || $next_gen_file_size < $args['response']->new_size ) {
+ // The next-gen file is lighter than this one.
return $args['response'];
}
- // The new optimized file is lighter than the WebP file: delete the WebP file and store an error.
- $webp_path = $args['file']->get_path_to_webp();
+ // The new optimized file is lighter than the next-gen file: delete the next-gen file and store an error.
+ $next_gen_path = $args['file']->get_path_to_nextgen( $args['next_gen_format'] );
- if ( $webp_path && $this->filesystem->is_writable( $webp_path ) ) {
- $this->filesystem->delete( $webp_path );
+ if ( $next_gen_path && $this->filesystem->is_writable( $next_gen_path ) ) {
+ $this->filesystem->delete( $next_gen_path );
}
- $webp_response = new WP_Error(
- 'webp_heavy',
+ $next_gen_response = new WP_Error(
+ 'next_gen_heavy',
sprintf(
- /* translators: %s is a size name. */
- __( 'The WebP version of the size %s is heavier than its non-WebP version.', 'imagify' ),
- '' . esc_html( $args['non_webp_thumb_size'] ) . '
'
+ /* translators: %s is a size name. */
+ __( 'The Next-Gen version of the size %s is heavier than its non-next-gen version.', 'imagify' ),
+ '' . esc_html( $args['non_next_gen_thumb_size'] ) . '
'
)
);
- $this->update_size_optimization_data( $webp_response, $webp_size, $args['optimization_level'] );
+ $this->update_size_optimization_data( $next_gen_response, $next_gen_size, $args['optimization_level'] );
return $args['response'];
}
@@ -815,7 +869,7 @@ protected function compare_webp_file_size( $args ) {
*
* @since 1.9
*
- * @return bool|WP_Error True on success. A \WP_Error instance on failure.
+ * @return bool|WP_Error True on success. A WP_Error instance on failure.
*/
public function restore() {
if ( ! $this->is_valid() ) {
@@ -866,7 +920,7 @@ public function restore() {
/**
* Fires before restoring a media.
- * Return a \WP_Error object to prevent the restoration.
+ * Return a WP_Error object to prevent the restoration.
*
* @since 1.9
*
@@ -894,7 +948,7 @@ public function restore() {
$media->update_dimensions();
// Delete the WebP version.
- $this->delete_webp_file( $original_path );
+ $this->delete_nextgen_file( $original_path );
// Restore the thumbnails.
$response = $this->restore_thumbnails();
@@ -924,28 +978,23 @@ public function restore() {
*
* @since 1.9
*
- * @return bool|WP_Error True on success. A \WP_Error instance on failure.
+ * @return bool|WP_Error True on success. A WP_Error instance on failure.
*/
protected function restore_thumbnails() {
$media = $this->get_media();
/**
- * Delete the WebP versions.
+ * Delete the next-gen versions.
* If the full size file and the original file are not the same, the full size is considered like a thumbnail.
- * In that case we must also delete the WebP file associated to the full size.
+ * In that case we must also delete the next-gen file associated to the full size.
*/
- $keep_full_webp = $media->get_raw_original_path() === $media->get_raw_fullsize_path();
- $this->delete_webp_files( $keep_full_webp );
+ $keep_full_next_gen = $media->get_raw_original_path() === $media->get_raw_fullsize_path();
+ $this->delete_nextgen_files( $keep_full_next_gen );
// Generate new thumbnails.
return $media->generate_thumbnails();
}
-
- /** ----------------------------------------------------------------------------------------- */
- /** BACKUP FILE ============================================================================= */
- /** ----------------------------------------------------------------------------------------- */
-
/**
* Delete the backup file.
*
@@ -960,28 +1009,29 @@ public function delete_backup() {
if ( $backup_path ) {
$this->filesystem->delete( $backup_path );
+
+ // Check for the -scaled version in the backup.
+ $scaled_backup_path = preg_replace( '/(\.)([^\.]+)$/', '-scaled.$2', $backup_path );
+ if ( $this->filesystem->exists( $scaled_backup_path ) ) {
+ // Delete the -scaled version from the backup.
+ $this->filesystem->delete( $scaled_backup_path );
+ }
}
}
-
- /** ----------------------------------------------------------------------------------------- */
- /** TEMPORARY COPY OF A SIZE FILE =========================================================== */
- /** ----------------------------------------------------------------------------------------- */
-
- /**
- * If we need to create a WebP version, we must create it from an unoptimized image.
- * The full size is always optimized before the WebP version creation, and in some cases it’s the same for the thumbnails.
- * Then we use the backup file to create temporary files.
- */
-
/**
* Create a temporary copy of a size file.
*
+ * If we need to create a next-gen version, we must create it from an unoptimized image.
+ * The full size is always optimized before the next-gen version creation, and in some cases it’s the same for the thumbnails.
+ * Then we use the backup file to create temporary files.
+ *
* @since 1.9
*
- * @param string $size The image size name.
- * @param array $sizes A list of thumbnail sizes being optimized.
- * @return bool True if the file exists/is created. False on failure.
+ * @param string $size The image size name.
+ * @param array $sizes A list of thumbnail sizes being optimized.
+ *
+ * @return bool True if the file exists/is created. False on failure.
*/
protected function create_temporary_copy( $size, $sizes = null ) {
$media = $this->get_media();
@@ -1036,7 +1086,7 @@ protected function create_temporary_copy( $size, $sizes = null ) {
if ( 'full' === $size ) {
/**
- * We create a copy of the backup to be able to create a WebP version from it.
+ * We create a copy of the backup to be able to create a next-gen version from it.
* That means the optimization process will resize the file if needed, so there is nothing more to do here.
*/
return true;
@@ -1126,8 +1176,9 @@ protected function create_temporary_copy( $size, $sizes = null ) {
*
* @since 1.9
*
- * @param string $size The image size name.
- * @param array $sizes A list of thumbnail sizes being optimized.
+ * @param string $size The image size name.
+ * @param array $sizes A list of thumbnail sizes being optimized.
+ *
* @return string|bool An image path. False on failure.
*/
protected function get_temporary_copy_path( $size, $sizes = null ) {
@@ -1154,18 +1205,14 @@ protected function get_temporary_copy_path( $size, $sizes = null ) {
return $info['dir_path'] . $info['file_base'] . static::TMP_SUFFIX . '.' . $info['extension'];
}
-
- /** ----------------------------------------------------------------------------------------- */
- /** RESIZE FILE ============================================================================= */
- /** ----------------------------------------------------------------------------------------- */
-
/**
* Maybe resize an image.
*
* @since 1.9
*
- * @param string $size The size name.
- * @param File $file A File instance.
+ * @param string $size The size name.
+ * @param File $file A File instance.
+ *
* @return array|WP_Error A WP_Error instance on failure, an array on success as follow: {
* @type bool $resized True when the image has been resized.
* @type bool $backuped True when the image has been backuped.
@@ -1189,7 +1236,7 @@ public function maybe_resize( $size, $file ) {
return new WP_Error(
'no_dimensions',
sprintf(
- /* translators: %s is an error message. */
+ /* translators: %s is an error message. */
__( 'Resizing failed: %s', 'imagify' ),
__( 'Imagify could not get the image dimensions.', 'imagify' )
)
@@ -1215,7 +1262,7 @@ public function maybe_resize( $size, $file ) {
return new WP_Error(
'resize_failure',
sprintf(
- /* translators: %s is an error message. */
+ /* translators: %s is an error message. */
__( 'Resizing failed: %s', 'imagify' ),
$resized_path->get_error_message()
)
@@ -1231,7 +1278,7 @@ public function maybe_resize( $size, $file ) {
return new WP_Error(
'backup_failure',
sprintf(
- /* translators: %s is an error message. */
+ /* translators: %s is an error message. */
__( 'Backup failed: %s', 'imagify' ),
$backuped->get_error_message()
)
@@ -1267,8 +1314,9 @@ public function maybe_resize( $size, $file ) {
*
* @since 1.9
*
- * @param string $size The size name.
- * @param File $file A File instance.
+ * @param string $size The size name.
+ * @param File $file A File instance.
+ *
* @return bool
*/
protected function can_resize( $size, $file ) {
@@ -1276,8 +1324,16 @@ protected function can_resize( $size, $file ) {
return false;
}
- if ( 'full' !== $size && 'full' . static::WEBP_SUFFIX !== $size ) {
- // We resize only the main file and its WebP version.
+ if (
+ 'full' !== $size
+ &&
+ (
+ 'full' . static::WEBP_SUFFIX !== $size
+ ||
+ 'full' . static::AVIF_SUFFIX !== $size
+ )
+ ) {
+ // We resize only the main file and its next-gen version.
return false;
}
@@ -1293,7 +1349,8 @@ protected function can_resize( $size, $file ) {
*
* @since 1.9
*
- * @param string $size The size name.
+ * @param string $size The size name.
+ *
* @return bool
*/
protected function can_backup( $size ) {
@@ -1309,76 +1366,32 @@ protected function can_backup( $size ) {
return $this->get_media()->get_context_instance()->can_backup();
}
-
- /** ----------------------------------------------------------------------------------------- */
- /** WEBP ==================================================================================== */
- /** ----------------------------------------------------------------------------------------- */
-
/**
- * Generate WebP images if they are missing.
- *
- * @since 1.9
+ * Get mime type
*
- * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
+ * @param string $format nextgen image format.
*/
- public function generate_webp_versions() {
- if ( ! $this->is_valid() ) {
- return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
- }
-
- $media = $this->get_media();
-
- if ( ! $media->is_image() ) {
- return new WP_Error( 'no_webp', __( 'This media is not an image and cannot be converted to WebP format.', 'imagify' ) );
- }
-
- if ( ! $media->has_backup() ) {
- return new WP_Error( 'no_backup', __( 'This media has no backup file.', 'imagify' ) );
- }
-
- $data = $this->get_data();
-
- if ( ! $data->is_optimized() && ! $data->is_already_optimized() ) {
- return new WP_Error( 'not_optimized', __( 'This media has not been optimized by Imagify yet.', 'imagify' ) );
- }
-
- if ( $this->has_webp() ) {
- return new WP_Error( 'has_webp', __( 'This media already has WebP versions.', 'imagify' ) );
- }
-
- $files = $media->get_media_files();
- $sizes = [];
- $args = [
- 'hook_suffix' => 'generate_webp_versions',
+ private function get_mime_type( $format ) {
+ $mime_types = [
+ 'avif' => 'image/avif',
+ 'webp' => 'image/webp',
];
- foreach ( $files as $size_name => $file ) {
- if ( 'image/webp' !== $files[ $size_name ]['mime-type'] ) {
- array_unshift( $sizes, $size_name . static::WEBP_SUFFIX );
- }
- }
-
- if ( ! $sizes ) {
- return new \WP_Error( 'no_sizes', __( 'This media does not have files that can be converted to WebP format.', 'imagify' ) );
- }
-
- $optimization_level = $data->get_optimization_level();
-
- // Optimize.
- return $this->optimize_sizes( $sizes, $optimization_level, $args );
+ return isset( $mime_types[ $format ] ) ? $mime_types[ $format ] : false;
}
/**
- * Delete the WebP images.
+ * Delete the next gen format images.
* This doesn't delete the related optimization data.
*
- * @since 1.9
- * @since 1.9.6 Return WP_Error or true.
+ * @since 2.2
+ *
+ * @param bool $keep_full Set to true to keep the full size.
+ * @param bool $all_next_gen True: will delete every next-gen format. False: will delete only the current enabled format.
*
- * @param bool $keep_full Set to true to keep the full size.
- * @return bool|WP_Error True on success. A \WP_Error object on failure.
+ * @return bool|WP_Error True on success. A WP_Error object on failure.
*/
- public function delete_webp_files( $keep_full = false ) {
+ public function delete_nextgen_files( $keep_full = false, $all_next_gen = false ) {
if ( ! $this->is_valid() ) {
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
}
@@ -1403,7 +1416,7 @@ public function delete_webp_files( $keep_full = false ) {
foreach ( $files as $file ) {
if ( 0 === strpos( $file['mime-type'], 'image/' ) ) {
- $deleted = $this->delete_webp_file( $file['path'] );
+ $deleted = $this->delete_nextgen_file( $file['path'], $all_next_gen );
if ( is_wp_error( $deleted ) ) {
++$error_count;
@@ -1415,7 +1428,7 @@ public function delete_webp_files( $keep_full = false ) {
return new WP_Error(
'files_not_deleted',
sprintf(
- /* translators: %s is a formatted number, don’t use %d. */
+ /* translators: %s is a formatted number, don’t use %d. */
_n( '%s file could not be deleted.', '%s files could not be deleted.', $error_count, 'imagify' ),
number_format_i18n( $error_count )
)
@@ -1426,62 +1439,86 @@ public function delete_webp_files( $keep_full = false ) {
}
/**
- * Delete a WebP image, given its non-WebP version's path.
+ * Delete a next gen format image, given its non-next-gen version's path.
* This doesn't delete the related optimization data.
*
- * @since 1.9
- * @since 1.9.6 Return WP_Error or true.
+ * @since 2.2
+ *
+ * @param string $file_path Path to the non-next-gen file.
+ * @param bool $all_next_gen True: will delete every next-gen format. False: will delete only the current enabled format.
*
- * @param string $file_path Path to the non-WebP file.
- * @return bool|WP_Error True on success. A \WP_Error object on failure.
+ * @return void|WP_Error A \WP_Error object on failure.
*/
- protected function delete_webp_file( $file_path ) {
+ protected function delete_nextgen_file( $file_path, $all_next_gen = false ) {
if ( ! $file_path ) {
- return new WP_Error( 'no_path', __( 'Path to non-WebP file not provided.', 'imagify' ) );
+ return new WP_Error( 'no_path', __( 'Path to non-next-gen file not provided.', 'imagify' ) );
}
- $webp_file = new File( $file_path );
- $webp_path = $webp_file->get_path_to_webp();
+ $next_gen_file = new File( $file_path );
+ $formats = $this->extensions;
+
+ if ( ! $all_next_gen ) {
+ $formats = imagify_nextgen_images_formats();
+ }
+ // Delete next-gen images.
+ foreach ( $formats as $extension ) {
+ $path = $next_gen_file->get_path_to_nextgen( $extension );
+
+ if ( ! $path ) {
+ continue;
+ }
+
+ $this->delete_file( $path );
+ }
+ }
- if ( ! $webp_path ) {
- return new WP_Error( 'no_webp_path', __( 'Could not get the path to the WebP file.', 'imagify' ) );
+ /**
+ * Delete a next gen format image, given its non-next-gen version's path.
+ *
+ * @param string $next_gen_path Path to the non-next-gen file.
+ *
+ * @return bool|WP_Error True on success. A WP_Error object on failure.
+ */
+ protected function delete_file( string $next_gen_path ) {
+ if ( empty( $next_gen_path ) ) {
+ return new WP_Error( 'no_$next_gen_path', __( 'Could not get the path to the Next-Gen format file.', 'imagify' ) );
}
- if ( ! $this->filesystem->exists( $webp_path ) ) {
+ if ( ! $this->filesystem->exists( $next_gen_path ) ) {
return true;
}
- if ( ! $this->filesystem->is_writable( $webp_path ) ) {
+ if ( ! $this->filesystem->is_writable( $next_gen_path ) ) {
return new WP_Error(
'file_not_writable',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s does not seem to be writable.', 'imagify' ),
- '' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '
'
+ '' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '
'
)
);
}
- if ( ! $this->filesystem->is_file( $webp_path ) ) {
+ if ( ! $this->filesystem->is_file( $next_gen_path ) ) {
return new WP_Error(
'not_a_file',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'This does not seem to be a file: %s.', 'imagify' ),
- '' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '
'
+ '' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '
'
)
);
}
- $deleted = $this->filesystem->delete( $webp_path, false, 'f' );
+ $deleted = $this->filesystem->delete( $next_gen_path, false, 'f' );
if ( ! $deleted ) {
return new WP_Error(
'file_not_deleted',
sprintf(
- /* translators: %s is a file path. */
+ /* translators: %s is a file path. */
__( 'The file %s could not be deleted.', 'imagify' ),
- '' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '
'
+ '' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '
'
)
);
}
@@ -1490,35 +1527,61 @@ protected function delete_webp_file( $file_path ) {
}
/**
- * Tell if a thumbnail size is an "Imagify WebP" size.
+ * Gives the next-gen image format we are processing.
+ *
+ * @return string Current format we are targeting.
+ */
+ public function get_current_format() {
+ return $this->get_option( 'convert_to_avif' ) ? static::AVIF_SUFFIX : static::WEBP_SUFFIX;
+ }
+
+ /**
+ * Tell if a thumbnail size is an "Imagify Next-Gen" size.
*
* @since 1.9
+ * @since 2.2 addition of the format parameter.
*
- * @param string $size_name The size name.
- * @return string|bool The unsuffixed name of the size if WebP. False if not WebP.
+ * @param string $size_name The size name.
+ *
+ * @return string|bool The unsuffixed name of the size if next-gen. False if not next-gen.
*/
- public function is_size_webp( $size_name ) {
- static $suffix;
+ public function is_size_next_gen( $size_name ) {
+ $formats = imagify_nextgen_images_formats();
- if ( ! isset( $suffix ) ) {
- $suffix = preg_quote( static::WEBP_SUFFIX, '/' );
- }
+ foreach ( $formats as $format ) {
+ $suffix = preg_quote( $this->get_suffix_from_format( $format ), '/' );
- if ( preg_match( '/^(?