<?php
defined('ABSPATH') || exit;
class WC_Regenerate_Images
{
protected static $background_process;
protected static $regenerate_size;
public static function init()
{
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'));
add_filter('wp_get_attachment_image_src', array(__CLASS__, 'maybe_resize_image'), 10, 4);
if (method_exists('Jetpack', 'is_module_active') && Jetpack::is_module_active('photon')) {
return;
}
if (apply_filters('woocommerce_background_image_regeneration', true)) {
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'));
if (!is_multisite()) {
add_action('customize_save_after', array(__CLASS__, 'maybe_regenerate_images'));
add_action('after_switch_theme', array(__CLASS__, 'maybe_regenerate_images'));
}
}
}
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 (!isset($data['width'], $data['height'])) {
return $data;
}
if (!self::image_size_matches_settings($data, $size)) {
if (method_exists('Jetpack', 'is_module_active') && Jetpack::is_module_active('photon')) {
return false;
} else {
$size_data = wc_get_image_size($size);
return image_get_intermediate_size($attachment_id, array(absint($size_data['width']), absint($size_data['height'])));
}
}
return $data;
}
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_data['height']);
}
return $meta_data;
}
protected static function image_size_matches_settings($image, $size)
{
$target_size = wc_get_image_size($size);
$uncropped = '' === $target_size['width'] || '' === $target_size['height'];
if (!$uncropped) {
$ratio_match = wp_image_matches_ratio($image['width'], $image['height'], $target_size['width'], $target_size['height']);
if ($ratio_match && $target_size['width'] !== $image['width']) {
return false;
}
if ($ratio_match && $target_size['height'] && $target_size['height'] !== $image['height']) {
return false;
}
}
if ($uncropped && empty($image['uncropped'])) {
return false;
}
return true;
}
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');
}
}
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');
}
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)) {
self::queue_image_regeneration();
}
}
public static function maybe_resize_image($image, $attachment_id, $size, $icon)
{
if (!apply_filters('woocommerce_resize_images', true)) {
return $image;
}
if (!$image || !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;
}
$target_size = wc_get_image_size($size);
$image_width = $image[1];
$image_height = $image[2];
$ratio_match = false;
$target_uncropped = '' === $target_size['width'] || '' === $target_size['height'] || !$target_size['crop'];
if ($target_uncropped) {
$full_size = self::get_full_size_image_dimensions($attachment_id);
if (!$full_size || !$full_size['width'] || !$full_size['height']) {
return $image;
}
$ratio_match = wp_image_matches_ratio($image_width, $image_height, $full_size['width'], $full_size['height']);
} else {
$ratio_match = wp_image_matches_ratio($image_width, $image_height, $target_size['width'], $target_size['height']);
}
if (!$ratio_match) {
$full_size = self::get_full_size_image_dimensions($attachment_id);
if (!$full_size) {
return $image;
}
if ($image_width === $target_size['width'] && $full_size['height'] < $target_size['height']) {
return $image;
}
if ($image_height === $target_size['height'] && $full_size['width'] < $target_size['width']) {
return $image;
}
if ($full_size['height'] < $target_size['height'] && $full_size['width'] < $target_size['width']) {
return $image;
}
return self::resize_and_return_image($attachment_id, $image, $size, $icon);
}
return $image;
}
private static function get_full_size_image_dimensions($attachment_id)
{
$imagedata = wp_get_attachment_metadata($attachment_id);
if (!$imagedata) {
return array();
}
if (!isset($imagedata['file']) && isset($imagedata['sizes']['full'])) {
$imagedata['height'] = $imagedata['sizes']['full']['height'];
$imagedata['width'] = $imagedata['sizes']['full']['width'];
}
return array('width' => $imagedata['width'], 'height' => $imagedata['height']);
}
public static function is_regeneratable($attachment)
{
if ('site-icon' === get_post_meta(is_object($attachment) ? $attachment->ID : $attachment, '_wp_attachment_context', true)) {
return false;
}
if (wp_attachment_is_image($attachment)) {
return true;
}
return false;
}
public static function adjust_intermediate_image_sizes($sizes)
{
return array(self::$regenerate_size);
}
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);
}
private static function resize_and_return_image($attachment_id, $image, $size, $icon)
{
if (!self::is_regeneratable($attachment_id)) {
return $image;
}
$fullsizepath = get_attached_file($attachment_id);
if (false === $fullsizepath || is_wp_error($fullsizepath) || !file_exists($fullsizepath)) {
return $image;
}
if (!function_exists('wp_crop_image')) {
include ABSPATH . 'wp-admin/includes/image.php';
}
self::$regenerate_size = is_customize_preview() ? $size . '_preview' : $size;
if (is_customize_preview()) {
$image_size = wc_get_image_size($size);
add_image_size(self::$regenerate_size, absint($image_size['width']), absint($image_size['height']), $image_size['crop']);
$thumbnail = self::get_image($fullsizepath, absint($image_size['width']), absint($image_size['height']), $image_size['crop']);
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, $thumbnail['filename']), 1 => $thumbnail['width'], 2 => $thumbnail['height']);
}
}
$metadata = wp_get_attachment_metadata($attachment_id);
if (!is_array($metadata)) {
$metadata = array();
}
add_filter('intermediate_image_sizes', array(__CLASS__, 'adjust_intermediate_image_sizes'));
$new_metadata = wp_generate_attachment_metadata($attachment_id, $fullsizepath);
remove_filter('intermediate_image_sizes', array(__CLASS__, 'adjust_intermediate_image_sizes'));
if (is_wp_error($new_metadata) || empty($new_metadata)) {
return $image;
}
if (isset($new_metadata['sizes'][self::$regenerate_size])) {
$metadata['sizes'][self::$regenerate_size] = $new_metadata['sizes'][self::$regenerate_size];
wp_update_attachment_metadata($attachment_id, $metadata);
}
$new_image = self::unfiltered_image_downsize($attachment_id, self::$regenerate_size);
return $new_image ? $new_image : $image;
}
private static function unfiltered_image_downsize($attachment_id, $size)
{
remove_action('image_get_intermediate_size', array(__CLASS__, 'filter_image_get_intermediate_size'), 10, 3);
$return = image_downsize($attachment_id, $size);
add_action('image_get_intermediate_size', array(__CLASS__, 'filter_image_get_intermediate_size'), 10, 3);
return $return;
}
public static function queue_image_regeneration()
{
global $wpdb;
self::$background_process->kill_process();
$images = $wpdb->get_results("SELECT ID\n\t\t\tFROM {$wpdb->posts}\n\t\t\tWHERE post_type = 'attachment'\n\t\t\tAND post_mime_type LIKE 'image/%'\n\t\t\tORDER BY ID DESC");
foreach ($images as $image) {
self::$background_process->push_to_queue(array('attachment_id' => $image->ID));
}
self::$background_process->save()->dispatch();
}
}
add_action('init', array('WC_Regenerate_Images', 'init'));