Merge pull request #18955 from woocommerce/update/3.3-image-handling

Image regeneration and thumbnail improvements
This commit is contained in:
Mike Jolley 2018-02-14 17:35:53 +00:00 committed by GitHub
commit 1b03a80ecb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 449 additions and 181 deletions

View File

@ -1 +1 @@
div.woocommerce-message{overflow:hidden;position:relative;border-right-color:#cc99c2!important}div.woocommerce-message p{max-width:700px}.woocommerce-message .button-primary,p.woocommerce-actions .button-primary{background:#bb77ae;border-color:#a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;color:#fff;text-shadow:0 -1px 1px #a36597,-1px 0 1px #a36597,0 1px 1px #a36597,1px 0 1px #a36597}.woocommerce-message .button-primary:active,.woocommerce-message .button-primary:focus,.woocommerce-message .button-primary:hover,p.woocommerce-actions .button-primary:active,p.woocommerce-actions .button-primary:focus,p.woocommerce-actions .button-primary:hover{background:#a36597;border-color:#a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597}.woocommerce-message a.woocommerce-message-close,p.woocommerce-actions a.woocommerce-message-close{position:absolute;top:0;left:0;padding:10px 21px 10px 15px;font-size:13px;line-height:1.23076923;text-decoration:none}.woocommerce-message a.woocommerce-message-close::before,p.woocommerce-actions a.woocommerce-message-close::before{position:absolute;top:8px;right:0;-webkit-transition:all .1s ease-in-out;transition:all .1s ease-in-out}.woocommerce-message .button-primary,.woocommerce-message .button-secondary,p.woocommerce-actions .button-primary,p.woocommerce-actions .button-secondary{text-decoration:none!important}.woocommerce-message .twitter-share-button,p.woocommerce-actions .twitter-share-button{margin-top:-3px;margin-right:3px;vertical-align:middle}.woocommerce-about-text,p.woocommerce-actions{margin-bottom:1em!important}div.woocommerce-legacy-shipping-notice,div.woocommerce-no-shipping-methods-notice{overflow:hidden;padding:1px 12px}div.woocommerce-legacy-shipping-notice p,div.woocommerce-no-shipping-methods-notice p{position:relative;z-index:1;max-width:700px;line-height:1.5em;margin:12px 0}div.woocommerce-legacy-shipping-notice p.main,div.woocommerce-no-shipping-methods-notice p.main{font-size:1.1em}div.woocommerce-legacy-shipping-notice::before,div.woocommerce-no-shipping-methods-notice::before{content:'\e01b';font-family:WooCommerce;text-align:center;line-height:1;color:#f7f1f6;display:block;width:1em;font-size:20em;top:36px;left:12px;position:absolute}
div.woocommerce-message{overflow:hidden;position:relative;border-right-color:#cc99c2!important}div.woocommerce-message p{max-width:700px}div.woocommerce-message p:last-child{max-width:inherit}.woocommerce-message .button-primary,p.woocommerce-actions .button-primary{background:#bb77ae;border-color:#a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;color:#fff;text-shadow:0 -1px 1px #a36597,-1px 0 1px #a36597,0 1px 1px #a36597,1px 0 1px #a36597}.woocommerce-message .button-primary:active,.woocommerce-message .button-primary:focus,.woocommerce-message .button-primary:hover,p.woocommerce-actions .button-primary:active,p.woocommerce-actions .button-primary:focus,p.woocommerce-actions .button-primary:hover{background:#a36597;border-color:#a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597}.woocommerce-message a.woocommerce-message-close,p.woocommerce-actions a.woocommerce-message-close{position:absolute;top:0;left:0;padding:10px 21px 10px 15px;font-size:13px;line-height:1.23076923;text-decoration:none}.woocommerce-message a.woocommerce-message-close::before,p.woocommerce-actions a.woocommerce-message-close::before{position:absolute;top:8px;right:0;-webkit-transition:all .1s ease-in-out;transition:all .1s ease-in-out}.woocommerce-message .button-primary,.woocommerce-message .button-secondary,p.woocommerce-actions .button-primary,p.woocommerce-actions .button-secondary{text-decoration:none!important}.woocommerce-message .twitter-share-button,p.woocommerce-actions .twitter-share-button{margin-top:-3px;margin-right:3px;vertical-align:middle}.woocommerce-about-text,p.woocommerce-actions{margin-bottom:1em!important}div.woocommerce-legacy-shipping-notice,div.woocommerce-no-shipping-methods-notice{overflow:hidden;padding:1px 12px}div.woocommerce-legacy-shipping-notice p,div.woocommerce-no-shipping-methods-notice p{position:relative;z-index:1;max-width:700px;line-height:1.5em;margin:12px 0}div.woocommerce-legacy-shipping-notice p.main,div.woocommerce-no-shipping-methods-notice p.main{font-size:1.1em}div.woocommerce-legacy-shipping-notice::before,div.woocommerce-no-shipping-methods-notice::before{content:'\e01b';font-family:WooCommerce;text-align:center;line-height:1;color:#f7f1f6;display:block;width:1em;font-size:20em;top:36px;left:12px;position:absolute}

View File

@ -1 +1 @@
div.woocommerce-message{overflow:hidden;position:relative;border-left-color:#cc99c2!important}div.woocommerce-message p{max-width:700px}.woocommerce-message .button-primary,p.woocommerce-actions .button-primary{background:#bb77ae;border-color:#a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;color:#fff;text-shadow:0 -1px 1px #a36597,1px 0 1px #a36597,0 1px 1px #a36597,-1px 0 1px #a36597}.woocommerce-message .button-primary:active,.woocommerce-message .button-primary:focus,.woocommerce-message .button-primary:hover,p.woocommerce-actions .button-primary:active,p.woocommerce-actions .button-primary:focus,p.woocommerce-actions .button-primary:hover{background:#a36597;border-color:#a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597}.woocommerce-message a.woocommerce-message-close,p.woocommerce-actions a.woocommerce-message-close{position:absolute;top:0;right:0;padding:10px 15px 10px 21px;font-size:13px;line-height:1.23076923;text-decoration:none}.woocommerce-message a.woocommerce-message-close::before,p.woocommerce-actions a.woocommerce-message-close::before{position:absolute;top:8px;left:0;-webkit-transition:all .1s ease-in-out;transition:all .1s ease-in-out}.woocommerce-message .button-primary,.woocommerce-message .button-secondary,p.woocommerce-actions .button-primary,p.woocommerce-actions .button-secondary{text-decoration:none!important}.woocommerce-message .twitter-share-button,p.woocommerce-actions .twitter-share-button{margin-top:-3px;margin-left:3px;vertical-align:middle}.woocommerce-about-text,p.woocommerce-actions{margin-bottom:1em!important}div.woocommerce-legacy-shipping-notice,div.woocommerce-no-shipping-methods-notice{overflow:hidden;padding:1px 12px}div.woocommerce-legacy-shipping-notice p,div.woocommerce-no-shipping-methods-notice p{position:relative;z-index:1;max-width:700px;line-height:1.5em;margin:12px 0}div.woocommerce-legacy-shipping-notice p.main,div.woocommerce-no-shipping-methods-notice p.main{font-size:1.1em}div.woocommerce-legacy-shipping-notice::before,div.woocommerce-no-shipping-methods-notice::before{content:'\e01b';font-family:WooCommerce;text-align:center;line-height:1;color:#f7f1f6;display:block;width:1em;font-size:20em;top:36px;right:12px;position:absolute}
div.woocommerce-message{overflow:hidden;position:relative;border-left-color:#cc99c2!important}div.woocommerce-message p{max-width:700px}div.woocommerce-message p:last-child{max-width:inherit}.woocommerce-message .button-primary,p.woocommerce-actions .button-primary{background:#bb77ae;border-color:#a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;color:#fff;text-shadow:0 -1px 1px #a36597,1px 0 1px #a36597,0 1px 1px #a36597,-1px 0 1px #a36597}.woocommerce-message .button-primary:active,.woocommerce-message .button-primary:focus,.woocommerce-message .button-primary:hover,p.woocommerce-actions .button-primary:active,p.woocommerce-actions .button-primary:focus,p.woocommerce-actions .button-primary:hover{background:#a36597;border-color:#a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597}.woocommerce-message a.woocommerce-message-close,p.woocommerce-actions a.woocommerce-message-close{position:absolute;top:0;right:0;padding:10px 15px 10px 21px;font-size:13px;line-height:1.23076923;text-decoration:none}.woocommerce-message a.woocommerce-message-close::before,p.woocommerce-actions a.woocommerce-message-close::before{position:absolute;top:8px;left:0;-webkit-transition:all .1s ease-in-out;transition:all .1s ease-in-out}.woocommerce-message .button-primary,.woocommerce-message .button-secondary,p.woocommerce-actions .button-primary,p.woocommerce-actions .button-secondary{text-decoration:none!important}.woocommerce-message .twitter-share-button,p.woocommerce-actions .twitter-share-button{margin-top:-3px;margin-left:3px;vertical-align:middle}.woocommerce-about-text,p.woocommerce-actions{margin-bottom:1em!important}div.woocommerce-legacy-shipping-notice,div.woocommerce-no-shipping-methods-notice{overflow:hidden;padding:1px 12px}div.woocommerce-legacy-shipping-notice p,div.woocommerce-no-shipping-methods-notice p{position:relative;z-index:1;max-width:700px;line-height:1.5em;margin:12px 0}div.woocommerce-legacy-shipping-notice p.main,div.woocommerce-no-shipping-methods-notice p.main{font-size:1.1em}div.woocommerce-legacy-shipping-notice::before,div.woocommerce-no-shipping-methods-notice::before{content:'\e01b';font-family:WooCommerce;text-align:center;line-height:1;color:#f7f1f6;display:block;width:1em;font-size:20em;top:36px;right:12px;position:absolute}

View File

@ -13,6 +13,9 @@ div.woocommerce-message {
p {
max-width: 700px;
}
p:last-child {
max-width: inherit;
}
}
p.woocommerce-actions,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -28,12 +28,13 @@ class WC_Admin_Notices {
* @var array
*/
private static $core_notices = array(
'install' => 'install_notice',
'update' => 'update_notice',
'template_files' => 'template_file_check_notice',
'legacy_shipping' => 'legacy_shipping_notice',
'no_shipping_methods' => 'no_shipping_methods_notice',
'simplify_commerce' => 'simplify_commerce_notice',
'install' => 'install_notice',
'update' => 'update_notice',
'template_files' => 'template_file_check_notice',
'legacy_shipping' => 'legacy_shipping_notice',
'no_shipping_methods' => 'no_shipping_methods_notice',
'simplify_commerce' => 'simplify_commerce_notice',
'regenerating_thumbnails' => 'regenerating_thumbnails_notice',
);
/**
@ -315,6 +316,13 @@ class WC_Admin_Notices {
include( 'views/html-notice-simplify-commerce.php' );
}
}
/**
* Notice shown when regenerating thumbnails background process is running.
*/
public static function regenerating_thumbnails_notice() {
include( 'views/html-notice-regenerating-thumbnails.php' );
}
}
WC_Admin_Notices::init();

View File

@ -2,8 +2,6 @@
/**
* Debug/Status page
*
* @author WooThemes
* @category Admin
* @package WooCommerce/Admin/System Status
* @version 2.2.0
*/

View File

@ -0,0 +1,13 @@
<?php
/**
* Admin View: Notice - Regenerating thumbnails.
*/
defined( 'ABSPATH' ) || exit;
?>
<div id="message" class="updated woocommerce-message">
<a class="woocommerce-message-close notice-dismiss" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc-hide-notice', 'regenerating_thumbnails' ), 'woocommerce_hide_notices_nonce', '_wc_notice_nonce' ) ); ?>"><?php _e( 'Cancel thumbnail regeneration', 'woocommerce' ); ?></a>
<p><?php esc_html_e( 'Thumbnail regeneration is running in the background. Depending on the amount of images in your store this may take a while.', 'woocommerce' ); ?></p>
</div>

View File

@ -4,15 +4,11 @@
*
* Handles requests to the /system_status/tools/* endpoints.
*
* @author WooThemes
* @category API
* @package WooCommerce/API
* @since 3.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
defined( 'ABSPATH' ) || exit;
/**
* @package WooCommerce/API
@ -179,8 +175,18 @@ class WC_REST_System_Status_Tools_Controller extends WC_REST_Controller {
'button' => __( 'Reset', 'woocommerce' ),
'desc' => __( 'This will reset your usage tracking settings, causing it to show the opt-in banner again and not sending any data.', 'woocommerce' ),
),
'regenerate_thumbnails' => array(
'name' => __( 'Regenerate shop thumbnails', 'woocommerce' ),
'button' => __( 'Regenerate', 'woocommerce' ),
'desc' => __( 'This will regenerate all shop thumbnails to match your theme and/or image settings.', 'woocommerce' ),
)
);
// Jetpack does the image resizing heavy lifting so you don't have to.
if ( ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) ) || ! apply_filters( 'woocommerce_background_image_regeneration', true ) ) {
unset( $tools['regenerate_thumbnails'] );
}
return apply_filters( 'woocommerce_debug_tools', $tools );
}
@ -468,6 +474,11 @@ class WC_REST_System_Status_Tools_Controller extends WC_REST_Controller {
$message = __( 'Usage tracking settings successfully reset.', 'woocommerce' );
break;
case 'regenerate_thumbnails':
WC_Regenerate_Images::queue_image_regeneration();
$message = __( 'Thumbnail regeneration has been scheduled to run in the background.', 'woocommerce' );
break;
default:
$tools = $this->get_tools();
if ( isset( $tools[ $tool ]['callback'] ) ) {

View File

@ -39,6 +39,15 @@ class WC_Regenerate_Images_Request extends WC_Background_Process {
parent::__construct();
}
/**
* Is job running?
*
* @return boolean
*/
public function is_running() {
return $this->is_queue_empty();
}
/**
* Limit each task ran per batch to 1 for image regen.
*
@ -132,6 +141,16 @@ class WC_Regenerate_Images_Request extends WC_Background_Process {
$new_metadata['sizes'][ $old_size ] = $old_metadata['sizes'][ $old_size ];
}
}
// Handle legacy sizes.
if ( isset( $new_metadata['sizes']['shop_thumbnail'], $new_metadata['sizes']['woocommerce_gallery_thumbnail'] ) ) {
$new_metadata['sizes']['shop_thumbnail'] = $new_metadata['sizes']['woocommerce_gallery_thumbnail'];
}
if ( isset( $new_metadata['sizes']['shop_catalog'], $new_metadata['sizes']['woocommerce_thumbnail'] ) ) {
$new_metadata['sizes']['shop_catalog'] = $new_metadata['sizes']['woocommerce_thumbnail'];
}
if ( isset( $new_metadata['sizes']['shop_single'], $new_metadata['sizes']['woocommerce_single'] ) ) {
$new_metadata['sizes']['shop_single'] = $new_metadata['sizes']['woocommerce_single'];
}
}
// Update the meta data with the new size values.
@ -213,7 +232,7 @@ class WC_Regenerate_Images_Request extends WC_Background_Process {
* @return array
*/
public function adjust_intermediate_image_sizes( $sizes ) {
return apply_filters( 'woocommerce_regenerate_images_intermediate_image_sizes', array( 'woocommerce_thumbnail', 'woocommerce_thumbnail_2x', 'woocommerce_single' ) );
return apply_filters( 'woocommerce_regenerate_images_intermediate_image_sizes', array( 'woocommerce_thumbnail', 'woocommerce_gallery_thumbnail', 'woocommerce_single' ) );
}
/**

View File

@ -23,24 +23,166 @@ class WC_Regenerate_Images {
*/
protected static $background_process;
/**
* Stores size being generated on the fly.
*
* @var string
*/
protected static $regenerate_size;
/**
* Init function
*/
public static function init() {
include_once WC_ABSPATH . 'includes/class-wc-regenerate-images-request.php';
self::$background_process = new WC_Regenerate_Images_Request();
add_action( 'image_get_intermediate_size', array( __CLASS__, 'filter_image_get_intermediate_size' ), 10, 3 );
add_filter( 'wp_generate_attachment_metadata', array( __CLASS__, 'add_uncropped_metadata' ) );
if ( ! is_admin() ) {
// Handle on-the-fly image resizing.
// Resize WooCommerce images on the fly when browsing site through customizer as to showcase image setting changes in real time.
if ( is_customize_preview() ) {
add_filter( 'wp_get_attachment_image_src', array( __CLASS__, 'maybe_resize_image' ), 10, 4 );
}
// Not required when Jetpack Photon is in use.
if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) ) {
return;
}
if ( apply_filters( 'woocommerce_background_image_regeneration', true ) ) {
// Actions to handle image generation when settings change.
add_action( 'update_option_woocommerce_thumbnail_cropping', array( __CLASS__, 'maybe_regenerate_images_option_update' ), 10, 3 );
add_action( 'update_option_woocommerce_thumbnail_image_width', array( __CLASS__, 'maybe_regenerate_images_option_update' ), 10, 3 );
add_action( 'update_option_woocommerce_single_image_width', array( __CLASS__, 'maybe_regenerate_images_option_update' ), 10, 3 );
add_action( 'after_switch_theme', array( __CLASS__, 'maybe_regenerate_image_theme_switch' ) );
include_once WC_ABSPATH . 'includes/class-wc-regenerate-images-request.php';
self::$background_process = new WC_Regenerate_Images_Request();
add_action( 'admin_init', array( __CLASS__, 'regenerating_notice' ) );
add_action( 'woocommerce_hide_regenerating_thumbnails_notice', array( __CLASS__, 'dismiss_regenerating_notice' ) );
// Regenerate thumbnails in the background after settings changes. Not ran on multisite to avoid multiple simultanious jobs.
if ( ! is_multisite() ) {
add_action( 'customize_save_after', array( __CLASS__, 'maybe_regenerate_images' ) );
add_action( 'after_switch_theme', array( __CLASS__, 'maybe_regenerate_images' ) );
}
}
}
/**
* If an intermediate size meta differs from the actual image size (settings were changed?) return false so the wrong size is not used.
*
* @param array $data Size data.
* @param int $attachment_id Attachment ID.
* @param string $size Size name.
* @return array
*/
public static function filter_image_get_intermediate_size( $data, $attachment_id, $size ) {
if ( ! is_string( $size ) || ! in_array( $size, apply_filters( 'woocommerce_image_sizes_to_resize', array( 'woocommerce_thumbnail', 'woocommerce_gallery_thumbnail', 'woocommerce_single', 'shop_thumbnail', 'shop_catalog', 'shop_single' ) ), true ) ) {
return $data;
}
// If we don't have sizes, we cannot proceed.
if ( ! isset( $data['width'], $data['height'] ) ) {
return $data;
}
// See if the image size has changed from our settings.
if ( ! self::image_size_matches_settings( $data, $size ) ) {
// If Photon is running we can just return false and let Jetpack handle regeneration.
if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) ) {
return false;
} else {
// If we get here, Jetpack is not running and we don't have the correct image sized stored. Try to return closest match.
$size_data = wc_get_image_size( $size );
return image_get_intermediate_size( $attachment_id, array( absint( $size_data['width'] ), absint( $size_data['height'] ) ) );
}
return false;
}
return $data;
}
/**
* We need to track if uncropped was on or off when generating the images.
*
* @param array $meta_data Array of meta data.
* @return array
*/
public static function add_uncropped_metadata( $meta_data ) {
$size_data = wc_get_image_size( 'woocommerce_thumbnail' );
if ( isset( $meta_data['sizes'], $meta_data['sizes']['woocommerce_thumbnail'] ) ) {
$meta_data['sizes']['woocommerce_thumbnail']['uncropped'] = empty( $size_settings['height'] );
}
return $meta_data;
}
/**
* See if an image's dimensions match actual settings.
*
* @param array $image Image dimensions array.
* @param string $size Named size.
* @return bool
*/
protected static function image_size_matches_settings( $image, $size ) {
$size_data = wc_get_image_size( $size );
// Size is invalid if the widths or crop setting don't match.
if ( $size_data['width'] !== $image['width'] ) {
return false;
}
// Size is invalid if the heights don't match.
if ( $size_data['height'] && $size_data['height'] !== $image['height'] ) {
return false;
}
// If cropping mode has changed, regenerate the image.
if ( '' === $size_data['height'] && empty( $image['uncropped'] ) ) {
return false;
}
return true;
}
/**
* Show notice when job is running in background.
*/
public static function regenerating_notice() {
if ( ! self::$background_process->is_running() ) {
WC_Admin_Notices::add_notice( 'regenerating_thumbnails' );
} else {
WC_Admin_Notices::remove_notice( 'regenerating_thumbnails' );
}
}
/**
* Dismiss notice and cancel jobs.
*/
public static function dismiss_regenerating_notice() {
if ( self::$background_process ) {
self::$background_process->kill_process();
$log = wc_get_logger();
$log->info( __( 'Cancelled product image regeneration job.', 'woocommerce' ),
array(
'source' => 'wc-image-regeneration',
)
);
}
WC_Admin_Notices::remove_notice( 'regenerating_thumbnails' );
}
/**
* Regenerate images if the settings have changed since last re-generation.
*
* @return void
*/
public static function maybe_regenerate_images() {
$size_hash = md5( wp_json_encode( array(
wc_get_image_size( 'thumbnail' ),
wc_get_image_size( 'single' ),
wc_get_image_size( 'gallery_thumbnail' ),
) ) );
if ( update_option( 'woocommerce_maybe_regenerate_images_hash', $size_hash ) ) {
// Size settings have changed. Trigger regen.
self::queue_image_regeneration();
}
}
@ -54,33 +196,91 @@ class WC_Regenerate_Images {
* @return array
*/
public static function maybe_resize_image( $image, $attachment_id, $size, $icon ) {
if ( ! apply_filters( 'woocommerce_resize_images', true ) ) {
return $image;
}
// Use a whitelist of sizes we want to resize. Ignore others.
if ( ! in_array( $size, apply_filters( 'woocommerce_image_sizes_to_resize', array( 'woocommerce_thumbnail', 'woocommerce_single', 'shop_thumbnail', 'shop_catalog', 'shop_single' ) ), true ) ) {
if ( ! in_array( $size, apply_filters( 'woocommerce_image_sizes_to_resize', array( 'woocommerce_thumbnail', 'woocommerce_gallery_thumbnail', 'woocommerce_single', 'shop_thumbnail', 'shop_catalog', 'shop_single' ) ), true ) ) {
return $image;
}
// Get image metadata - we need it to proceed.
$imagemeta = wp_get_attachment_metadata( $attachment_id );
if ( false === $imagemeta || empty( $imagemeta ) ) {
if ( empty( $imagemeta ) ) {
return $image;
}
$size_settings = wc_get_image_size( $size );
// If size differs from image meta, regen.
if ( ! isset( $imagemeta['sizes'], $imagemeta['sizes'][ $size ] ) || $imagemeta['sizes'][ $size ]['width'] !== $size_settings['width'] || ( $size_settings['crop'] && $imagemeta['sizes'][ $size ]['height'] !== $size_settings['height'] ) ) {
$image = self::resize_and_return_image( $attachment_id, $image, $size, $icon );
if ( ! isset( $imagemeta['sizes'], $imagemeta['sizes'][ $size ] ) || ! self::image_size_matches_settings( $imagemeta['sizes'][ $size ], $size ) ) {
return self::resize_and_return_image( $attachment_id, $image, $size, $icon );
}
return $image;
}
/**
* Ensure we are dealing with the correct image attachment
*
* @param WP_Post $attachment Attachment object.
* @return boolean
*/
public static function is_regeneratable( $attachment ) {
if ( 'site-icon' === get_post_meta( $attachment->ID, '_wp_attachment_context', true ) ) {
return false;
}
if ( wp_attachment_is_image( $attachment ) ) {
return true;
}
return false;
}
/**
* Only regenerate images for the requested size.
*
* @param array $sizes Array of image sizes.
* @return array
*/
public static function adjust_intermediate_image_sizes( $sizes ) {
return array( self::$regenerate_size );
}
/**
* Generate the thumbnail filename and dimensions for a given file.
*
* @param string $fullsizepath Path to full size image.
* @param int $thumbnail_width The width of the thumbnail.
* @param int $thumbnail_height The height of the thumbnail.
* @param bool $crop Whether to crop or not.
* @return array|false An array of the filename, thumbnail width, and thumbnail height, or false on failure to resize such as the thumbnail being larger than the fullsize image.
*/
private static function get_image( $fullsizepath, $thumbnail_width, $thumbnail_height, $crop ) {
list( $fullsize_width, $fullsize_height ) = getimagesize( $fullsizepath );
$dimensions = image_resize_dimensions( $fullsize_width, $fullsize_height, $thumbnail_width, $thumbnail_height, $crop );
$editor = wp_get_image_editor( $fullsizepath );
if ( is_wp_error( $editor ) ) {
return false;
}
if ( ! $dimensions || ! is_array( $dimensions ) ) {
return false;
}
list( , , , , $dst_w, $dst_h ) = $dimensions;
$suffix = "{$dst_w}x{$dst_h}";
$file_ext = strtolower( pathinfo( $fullsizepath, PATHINFO_EXTENSION ) );
return array(
'filename' => $editor->generate_filename( $suffix, null, $file_ext ),
'width' => $dst_w,
'height' => $dst_h,
);
}
/**
* Regenerate the image according to the required size
*
@ -91,8 +291,19 @@ class WC_Regenerate_Images {
* @return string
*/
private static function resize_and_return_image( $attachment_id, $image, $size, $icon ) {
$attachment = get_post( $attachment_id );
if ( ! $attachment || 'attachment' !== $attachment->post_type || 'image/' !== substr( $attachment->post_mime_type, 0, 6 ) ) {
$image_size = wc_get_image_size( $size );
$wp_uploads = wp_upload_dir( null, false );
$wp_uploads_dir = $wp_uploads['basedir'];
$wp_uploads_url = $wp_uploads['baseurl'];
$attachment = get_post( $attachment_id );
if ( ! $attachment || 'attachment' !== $attachment->post_type || ! self::is_regeneratable( $attachment ) ) {
return $image;
}
$fullsizepath = get_attached_file( $attachment_id );
if ( false === $fullsizepath || is_wp_error( $fullsizepath ) || ! file_exists( $fullsizepath ) ) {
return $image;
}
@ -100,83 +311,52 @@ class WC_Regenerate_Images {
include ABSPATH . 'wp-admin/includes/image.php';
}
$wp_uploads = wp_upload_dir( null, false );
$wp_uploads_dir = $wp_uploads['basedir'];
$wp_uploads_url = $wp_uploads['baseurl'];
$original_image_file_path = get_attached_file( $attachment->ID );
if ( ! file_exists( $original_image_file_path ) || ! getimagesize( $original_image_file_path ) ) {
return $image;
}
$info = pathinfo( $original_image_file_path );
$ext = $info['extension'];
list( $orig_w, $orig_h ) = getimagesize( $original_image_file_path );
// Get image size after cropping.
$image_size = wc_get_image_size( $size );
$dimensions = image_resize_dimensions( $orig_w, $orig_h, $image_size['width'], $image_size['height'], $image_size['crop'] );
if ( ! $dimensions || ! is_array( $dimensions ) ) {
return $image;
}
$dst_w = $dimensions[4];
$dst_h = $dimensions[5];
$suffix = "{$dst_w}x{$dst_h}";
$dst_rel_path = str_replace( '.' . $ext, '', $original_image_file_path );
$destfilename = "{$dst_rel_path}-{$suffix}.{$ext}";
$thumbnail = self::get_image( $fullsizepath, $image_size['width'], $image_size['height'], $image_size['crop'] );
// If the file is already there perhaps just load it.
if ( file_exists( $destfilename ) ) {
if ( $thumbnail && file_exists( $thumbnail['filename'] ) ) {
$wp_uploads = wp_upload_dir( null, false );
$wp_uploads_dir = $wp_uploads['basedir'];
$wp_uploads_url = $wp_uploads['baseurl'];
return array(
0 => str_replace( $wp_uploads_dir, $wp_uploads_url, $destfilename ),
1 => $image_size['width'],
2 => $image_size['height'],
0 => str_replace( $wp_uploads_dir, $wp_uploads_url, $thumbnail['filename'] ),
1 => $thumbnail['width'],
2 => $thumbnail['height'],
);
}
// Lets resize the image if it does not exist yet.
$editor = wp_get_image_editor( $original_image_file_path );
if ( is_wp_error( $editor ) || is_wp_error( $editor->resize( $image_size['width'], $image_size['height'], $image_size['crop'] ) ) ) {
$metadata = wp_get_attachment_metadata( $attachment_id );
// Make sure registered image size matches the size we're requesting.
add_image_size( $size . '_preview', $image_size['width'], $image_size['height'], $image_size['crop'] );
self::$regenerate_size = $size . '_preview';
// We only want to regen a specific image size.
add_filter( 'intermediate_image_sizes', array( __CLASS__, 'adjust_intermediate_image_sizes' ) );
// This function will generate the new image sizes.
$new_metadata = wp_generate_attachment_metadata( $attachment_id, $fullsizepath );
// Remove custom filter.
remove_filter( 'intermediate_image_sizes', array( __CLASS__, 'adjust_intermediate_image_sizes' ) );
// If something went wrong lets just return the original image.
if ( is_wp_error( $new_metadata ) || empty( $new_metadata ) ) {
return $image;
}
$resized_file = $editor->save();
if ( ! is_wp_error( $resized_file ) ) {
$img_url = str_replace( $wp_uploads_dir, $wp_uploads_url, $resized_file['path'] );
return array(
0 => $img_url,
1 => $image_size['width'],
2 => $image_size['height'],
);
// Since this is only a preview we should not update the actual size. That will be done later by the background job.
if ( isset( $new_metadata['sizes'][ $size . '_preview' ] ) ) {
$metadata['sizes'][ $size . '_preview' ] = $new_metadata['sizes'][ $size . '_preview' ];
wp_update_attachment_metadata( $attachment_id, $metadata );
}
// Lets just add this here as a fallback.
return $image;
}
// Now we've done our regen, attempt to return the new size.
$new_image = image_downsize( $attachment_id, $size . '_preview' );
/**
* Check if we should regenerate the product images when options change.
*
* @param mixed $old_value Old option value.
* @param mixed $new_value New option value.
* @param string $option Option name.
*/
public static function maybe_regenerate_images_option_update( $old_value, $new_value, $option ) {
if ( $new_value === $old_value ) {
return;
}
self::queue_image_regeneration();
}
/**
* Check if we should generate images when new themes declares custom sizes.
*/
public static function maybe_regenerate_image_theme_switch() {
if ( wc_get_theme_support( 'single_image_width' ) || wc_get_theme_support( 'thumbnail_image_width' ) ) {
self::queue_image_regeneration();
}
return $new_image ? $new_image : $image;
}
/**
@ -184,7 +364,7 @@ class WC_Regenerate_Images {
*
* @return void
*/
private static function queue_image_regeneration() {
public static function queue_image_regeneration() {
global $wpdb;
// First lets cancel existing running queue to avoid running it more than once.
self::$background_process->kill_process();
@ -207,4 +387,5 @@ class WC_Regenerate_Images {
self::$background_process->save()->dispatch();
}
}
WC_Regenerate_Images::init();
add_action( 'init', array( 'WC_Regenerate_Images', 'init' ) );

View File

@ -539,19 +539,18 @@ final class WooCommerce {
* @since 2.3
*/
public function add_image_sizes() {
$thumbnail = wc_get_image_size( 'thumbnail' );
$single = wc_get_image_size( 'single' );
$thumbnail = wc_get_image_size( 'thumbnail' );
$single = wc_get_image_size( 'single' );
$gallery_thumbnail = wc_get_image_size( 'gallery_thumbnail' );
add_image_size( 'woocommerce_thumbnail', $thumbnail['width'], $thumbnail['height'], $thumbnail['crop'] );
add_image_size( 'woocommerce_single', $single['width'], $single['height'], $single['crop'] );
// 2x thumbnail size for retina, and when showing less columns.
add_image_size( 'woocommerce_thumbnail_2x', $thumbnail['width'] * 2, '' !== $thumbnail['height'] ? $thumbnail['height'] * 2 : '', $thumbnail['crop'] );
add_image_size( 'woocommerce_gallery_thumbnail', $gallery_thumbnail['width'], $gallery_thumbnail['height'], $gallery_thumbnail['crop'] );
// Registered for bw compat. @todo remove in 4.0.
add_image_size( 'shop_thumbnail', $thumbnail['width'], $thumbnail['height'], $thumbnail['crop'] );
add_image_size( 'shop_catalog', $thumbnail['width'], $thumbnail['height'], $thumbnail['crop'] );
add_image_size( 'shop_single', $single['width'], $single['height'], $single['crop'] );
add_image_size( 'shop_thumbnail', $gallery_thumbnail['width'], $gallery_thumbnail['height'], $gallery_thumbnail['crop'] );
}
/**

View File

@ -156,7 +156,7 @@ class WC_Shop_Customizer {
setting.bind( function( value ) {
var min = parseInt( '<?php echo esc_js( $min_rows ); ?>', 10 );
var max = parseInt( '<?php echo esc_js( $max_rows ); ?>', 10 );
value = parseInt( value, 10 );
if ( max && value > max ) {
@ -443,12 +443,23 @@ class WC_Shop_Customizer {
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
*/
private function add_product_images_section( $wp_customize ) {
if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) ) {
$regen_description = ''; // Nothing to report; Jetpack will handle magically.
} elseif ( apply_filters( 'woocommerce_background_image_regeneration', true ) && ! is_multisite() ) {
$regen_description = __( 'After publishing your changes, new image sizes will be generated automatically.', 'woocommerce' );
} elseif ( apply_filters( 'woocommerce_background_image_regeneration', true ) && is_multisite() ) {
$regen_description = sprintf( __( 'After publishing your changes, new image sizes may not be shown until you regenerate thumbnails. You can do this from the <a href="%1$s" target="_blank">tools section in WooCommerce</a> or by using a plugin such as <a href="%2$s" target="_blank">Regenerate Thumbnails</a>.', 'woocommerce' ), admin_url( 'admin.php?page=wc-status&tab=tools' ), 'https://en-gb.wordpress.org/plugins/regenerate-thumbnails/' );
} else {
$regen_description = sprintf( __( 'After publishing your changes, new image sizes may not be shown until you <a href="%2$s" target="_blank">Regenerate Thumbnails</a>.', 'woocommerce' ), 'https://en-gb.wordpress.org/plugins/regenerate-thumbnails/' );
}
$wp_customize->add_section(
'woocommerce_product_images',
array(
'title' => __( 'Product Images', 'woocommerce' ),
'priority' => 20,
'panel' => 'woocommerce',
'title' => __( 'Product Images', 'woocommerce' ),
'description' => $regen_description,
'priority' => 20,
'panel' => 'woocommerce',
)
);
@ -496,7 +507,7 @@ class WC_Shop_Customizer {
'woocommerce_thumbnail_image_width',
array(
'label' => __( 'Thumbnail width', 'woocommerce' ),
'description' => __( 'Image size used for products in the catalog and product gallery thumbnails.', 'woocommerce' ),
'description' => __( 'Image size used for products in the catalog.', 'woocommerce' ),
'section' => 'woocommerce_product_images',
'settings' => 'woocommerce_thumbnail_image_width',
'type' => 'number',

View File

@ -723,37 +723,54 @@ function wc_get_image_size( $image_size ) {
);
if ( is_array( $image_size ) ) {
$size = array(
$size = array(
'width' => isset( $image_size[0] ) ? absint( $image_size[0] ) : 600,
'height' => isset( $image_size[1] ) ? absint( $image_size[1] ) : 600,
'crop' => isset( $image_size[2] ) ? absint( $image_size[2] ) : 1,
);
$image_size = $size['width'] . '_' . $size['height'];
} elseif ( in_array( $image_size, array( 'single', 'shop_single', 'woocommerce_single' ), true ) ) {
$size['width'] = absint( wc_get_theme_support( 'single_image_width', get_option( 'woocommerce_single_image_width', 600 ) ) );
$size['height'] = '';
$size['crop'] = 0;
$image_size = 'single';
} elseif ( in_array( $image_size, array( 'thumbnail', 'shop_thumbnail', 'shop_catalog', 'woocommerce_thumbnail' ), true ) ) {
$size['width'] = absint( wc_get_theme_support( 'thumbnail_image_width', get_option( 'woocommerce_thumbnail_image_width', 300 ) ) );
$cropping = get_option( 'woocommerce_thumbnail_cropping', '1:1' );
} else {
$image_size = str_replace( 'woocommerce_', '', $image_size );
if ( 'uncropped' === $cropping ) {
// Legacy size mapping.
if ( 'shop_single' === $image_size ) {
$image_size = 'single';
} elseif ( 'shop_catalog' === $image_size ) {
$image_size = 'thumbnail';
} elseif ( 'shop_thumbnail' === $image_size ) {
$image_size = 'gallery_thumbnail';
}
if ( 'single' === $image_size ) {
$size['width'] = absint( wc_get_theme_support( 'single_image_width', get_option( 'woocommerce_single_image_width', 600 ) ) );
$size['height'] = '';
$size['crop'] = 0;
} elseif ( 'custom' === $cropping ) {
$width = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_width', '4' ) );
$height = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_height', '3' ) );
$size['height'] = round( ( $size['width'] / $width ) * $height );
$size['crop'] = 1;
} else {
$cropping_split = explode( ':', $cropping );
$width = max( 1, current( $cropping_split ) );
$height = max( 1, end( $cropping_split ) );
$size['height'] = round( ( $size['width'] / $width ) * $height );
} elseif ( 'gallery_thumbnail' === $image_size ) {
$size['width'] = absint( wc_get_theme_support( 'gallery_thumbnail_image_width', 100 ) );
$size['height'] = $size['width'];
$size['crop'] = 1;
} elseif ( 'thumbnail' === $image_size ) {
$size['width'] = absint( wc_get_theme_support( 'thumbnail_image_width', get_option( 'woocommerce_thumbnail_image_width', 300 ) ) );
$cropping = get_option( 'woocommerce_thumbnail_cropping', '1:1' );
if ( 'uncropped' === $cropping ) {
$size['height'] = '';
$size['crop'] = 0;
} elseif ( 'custom' === $cropping ) {
$width = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_width', '4' ) );
$height = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_height', '3' ) );
$size['height'] = absint( round( ( $size['width'] / $width ) * $height ) );
$size['crop'] = 1;
} else {
$cropping_split = explode( ':', $cropping );
$width = max( 1, current( $cropping_split ) );
$height = max( 1, end( $cropping_split ) );
$size['height'] = absint( round( ( $size['width'] / $width ) * $height ) );
$size['crop'] = 1;
}
}
$image_size = 'thumbnail';
}
return apply_filters( 'woocommerce_get_image_size_' . $image_size, $size );

View File

@ -1050,6 +1050,36 @@ if ( ! function_exists( 'woocommerce_show_product_thumbnails' ) ) {
}
}
/**
* Get HTML for a gallery image.
*
* woocommerce_gallery_thumbnail_size, woocommerce_gallery_image_size and woocommerce_gallery_full_size accept name based image sizes, or an array of width/height values.
*
* @since 3.3.2
* @param int $attachment_id
* @param bool $main_image Is this the main image or a thumbnail?
* @return string
*/
function wc_get_gallery_image_html( $attachment_id, $main_image = false ) {
$flexslider = (bool) apply_filters( 'woocommerce_single_product_flexslider_enabled', get_theme_support( 'wc-product-gallery-slider' ) );
$gallery_thumbnail = wc_get_image_size( 'gallery_thumbnail' );
$thumbnail_size = apply_filters( 'woocommerce_gallery_thumbnail_size', array( $gallery_thumbnail['width'], $gallery_thumbnail['height'] ) );
$image_size = apply_filters( 'woocommerce_gallery_image_size', $flexslider || $main_image ? 'woocommerce_single': $thumbnail_size );
$full_size = apply_filters( 'woocommerce_gallery_full_size', apply_filters( 'woocommerce_product_thumbnails_large_size', 'full' ) );
$thumbnail_src = wp_get_attachment_image_src( $attachment_id, $thumbnail_size );
$full_src = wp_get_attachment_image_src( $attachment_id, $full_size );
$image = wp_get_attachment_image( $attachment_id, $image_size, false, array(
'title' => get_post_field( 'post_title', $attachment_id ),
'data-caption' => get_post_field( 'post_excerpt', $attachment_id ),
'data-src' => $full_src[0],
'data-large_image' => $full_src[0],
'data-large_image_width' => $full_src[1],
'data-large_image_height' => $full_src[2],
) );
return '<div data-thumb="' . esc_url( $thumbnail_src[0] ) . '" class="woocommerce-product-gallery__image"><a href="' . esc_url( $full_src[0] ) . '">' . $image . '</a></div>';
}
if ( ! function_exists( 'woocommerce_output_product_data_tabs' ) ) {
/**

View File

@ -13,22 +13,23 @@
* @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes
* @package WooCommerce/Templates
* @version 3.1.0
* @version 3.3.2
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
defined( 'ABSPATH' ) || exit;
// Note: `wc_get_gallery_image_html` was added in WC 3.3.2 and did not exist prior. This check protects against theme overrides being used on older versions of WC.
if ( ! function_exists( 'wc_get_gallery_image_html' ) ) {
return;
}
global $post, $product;
global $product;
$columns = apply_filters( 'woocommerce_product_thumbnails_columns', 4 );
$thumbnail_size = apply_filters( 'woocommerce_product_thumbnails_large_size', 'full' );
$post_thumbnail_id = get_post_thumbnail_id( $post->ID );
$full_size_image = wp_get_attachment_image_src( $post_thumbnail_id, $thumbnail_size );
$placeholder = has_post_thumbnail() ? 'with-images' : 'without-images';
$post_thumbnail_id = $product->get_image_id();
$wrapper_classes = apply_filters( 'woocommerce_single_product_image_gallery_classes', array(
'woocommerce-product-gallery',
'woocommerce-product-gallery--' . $placeholder,
'woocommerce-product-gallery--' . ( has_post_thumbnail() ? 'with-images' : 'without-images' ),
'woocommerce-product-gallery--columns-' . absint( $columns ),
'images',
) );
@ -36,26 +37,15 @@ $wrapper_classes = apply_filters( 'woocommerce_single_product_image_gallery_cl
<div class="<?php echo esc_attr( implode( ' ', array_map( 'sanitize_html_class', $wrapper_classes ) ) ); ?>" data-columns="<?php echo esc_attr( $columns ); ?>" style="opacity: 0; transition: opacity .25s ease-in-out;">
<figure class="woocommerce-product-gallery__wrapper">
<?php
$attributes = array(
'title' => get_post_field( 'post_title', $post_thumbnail_id ),
'data-caption' => get_post_field( 'post_excerpt', $post_thumbnail_id ),
'data-src' => $full_size_image[0],
'data-large_image' => $full_size_image[0],
'data-large_image_width' => $full_size_image[1],
'data-large_image_height' => $full_size_image[2],
);
if ( has_post_thumbnail() ) {
$html = '<div data-thumb="' . get_the_post_thumbnail_url( $post->ID, 'woocommerce_thumbnail' ) . '" class="woocommerce-product-gallery__image"><a href="' . esc_url( $full_size_image[0] ) . '">';
$html .= get_the_post_thumbnail( $post->ID, 'woocommerce_single', $attributes );
$html .= '</a></div>';
$html = wc_get_gallery_image_html( $post_thumbnail_id, true );
} else {
$html = '<div class="woocommerce-product-gallery__image--placeholder">';
$html .= sprintf( '<img src="%s" alt="%s" class="wp-post-image" />', esc_url( wc_placeholder_img_src() ), esc_html__( 'Awaiting product image', 'woocommerce' ) );
$html .= '</div>';
}
echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', $html, get_post_thumbnail_id( $post->ID ) );
echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', $html, $post_thumbnail_id );
do_action( 'woocommerce_product_thumbnails' );
?>

View File

@ -13,34 +13,22 @@
* @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes
* @package WooCommerce/Templates
* @version 3.1.0
* @version 3.3.2
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
defined( 'ABSPATH' ) || exit;
// Note: `wc_get_gallery_image_html` was added in WC 3.3.2 and did not exist prior. This check protects against theme overrides being used on older versions of WC.
if ( ! function_exists( 'wc_get_gallery_image_html' ) ) {
return;
}
global $post, $product;
global $product;
$attachment_ids = $product->get_gallery_image_ids();
if ( $attachment_ids && has_post_thumbnail() ) {
foreach ( $attachment_ids as $attachment_id ) {
$full_size_image = wp_get_attachment_image_src( $attachment_id, 'full' );
$thumbnail = wp_get_attachment_image_src( $attachment_id, 'woocommerce_thumbnail' );
$attributes = array(
'title' => get_post_field( 'post_title', $attachment_id ),
'data-caption' => get_post_field( 'post_excerpt', $attachment_id ),
'data-src' => $full_size_image[0],
'data-large_image' => $full_size_image[0],
'data-large_image_width' => $full_size_image[1],
'data-large_image_height' => $full_size_image[2],
);
$html = '<div data-thumb="' . esc_url( $thumbnail[0] ) . '" class="woocommerce-product-gallery__image"><a href="' . esc_url( $full_size_image[0] ) . '">';
$html .= wp_get_attachment_image( $attachment_id, 'woocommerce_single', false, $attributes );
$html .= '</a></div>';
echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', $html, $attachment_id );
echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', wc_get_gallery_image_html( $attachment_id ), $attachment_id );
}
}