'Make every swatch picture\'s files at the sizes the swatch settings set now.' ) ); } } public static function sizes() { $s = get_option( 'aieo_dmm_swatches_settings', array() ); $s = is_array( $s ) ? $s : array(); $out = array(); foreach ( array( 'om_swatch_product' => array( 'image_width', 'image_height', 72 ), 'om_swatch_archive' => array( 'image_width_shop_archive', 'image_height_shop_archive', 26 ), ) as $name => $k ) { $w = (int) ( $s[ $k[0] ] ?? $k[2] ); $h = (int) ( $s[ $k[1] ] ?? $k[2] ); if ( $w > 0 && $h > 0 ) { $out[ $name ] = array( $w, $h ); $out[ $name . '_2x' ] = array( 2 * $w, 2 * $h ); } } return $out; } public static function dims( $name ) { $sizes = self::sizes(); return $sizes[ $name ] ?? array( 0, 0 ); } public static function register() { foreach ( self::sizes() as $name => $wh ) { add_image_size( $name, $wh[0], $wh[1], true ); } } public static function only_for_swatches( $sizes, $metadata = array(), $attachment_id = 0 ) { $ours = array_intersect_key( (array) $sizes, self::sizes() ); if ( ! $ours || ( $attachment_id > 0 && self::is_source( (int) $attachment_id ) ) ) { return $sizes; } return array_diff_key( (array) $sizes, $ours ); } public static function is_source( $attachment_id ) { $attachment_id = (int) $attachment_id; if ( isset( self::$sources[ $attachment_id ] ) ) { return self::$sources[ $attachment_id ]; } global $wpdb; $keys = "'" . implode( "','", self::TERM_KEYS ) . "'"; $hit = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT 1 FROM {$wpdb->termmeta} tm INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = tm.term_id WHERE tm.meta_key IN ({$keys}) AND tm.meta_value = %s AND tt.taxonomy LIKE %s LIMIT 1", (string) $attachment_id, $wpdb->esc_like( 'pa_' ) . '%' ) ); if ( ! $hit ) { $hit = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT 1 FROM {$wpdb->postmeta} pm INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE pm.meta_key = '_thumbnail_id' AND pm.meta_value = %s AND p.post_type = 'product_variation' LIMIT 1", (string) $attachment_id ) ); } self::$sources[ $attachment_id ] = $hit; return $hit; } public static function sources() { global $wpdb; $keys = "'" . implode( "','", self::TERM_KEYS ) . "'"; $ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT a.ID FROM {$wpdb->posts} a INNER JOIN ( SELECT CAST(tm.meta_value AS UNSIGNED) AS id FROM {$wpdb->termmeta} tm INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = tm.term_id WHERE tm.meta_key IN ({$keys}) AND tt.taxonomy LIKE %s UNION SELECT CAST(pm.meta_value AS UNSIGNED) FROM {$wpdb->postmeta} pm INNER JOIN {$wpdb->posts} v ON v.ID = pm.post_id WHERE pm.meta_key = '_thumbnail_id' AND v.post_type = 'product_variation' ) s ON s.id = a.ID WHERE a.post_type = 'attachment' AND a.post_mime_type LIKE 'image/%%' ORDER BY a.ID", $wpdb->esc_like( 'pa_' ) . '%' ) ); return array_map( 'intval', (array) $ids ); } public static function term_meta_set( $meta_id, $term_id, $meta_key, $meta_value ) { if ( ! in_array( $meta_key, self::TERM_KEYS, true ) || (int) $meta_value <= 0 ) { return; } $term = get_term( (int) $term_id ); if ( ! $term instanceof WP_Term || 0 !== strpos( (string) $term->taxonomy, 'pa_' ) ) { return; } self::soon( (int) $meta_value ); } public static function post_meta_set( $meta_id, $post_id, $meta_key, $meta_value ) { if ( '_thumbnail_id' !== $meta_key || (int) $meta_value <= 0 || 'product_variation' !== get_post_type( (int) $post_id ) ) { return; } self::soon( (int) $meta_value ); } private static function soon( $attachment_id ) { self::$sources[ $attachment_id ] = true; if ( ! wp_attachment_is_image( $attachment_id ) ) { return; } if ( ( defined( 'WP_CLI' ) && WP_CLI ) || ! function_exists( 'as_enqueue_async_action' ) ) { self::make( $attachment_id ); return; } if ( ! function_exists( 'as_has_scheduled_action' ) || ! as_has_scheduled_action( self::JOB, array( $attachment_id ), 'aieo-swatches' ) ) { as_enqueue_async_action( self::JOB, array( $attachment_id ), 'aieo-swatches' ); } } public static function make( $attachment_id ) { $attachment_id = (int) $attachment_id; $meta = wp_get_attachment_metadata( $attachment_id ); $file = get_attached_file( $attachment_id ); if ( ! is_array( $meta ) || empty( $meta['width'] ) || ! $file || ! file_exists( $file ) ) { return -1; } $meta['sizes'] = isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ? $meta['sizes'] : array(); $dir = dirname( $file ); $want = array(); foreach ( self::sizes() as $name => $wh ) { $fit = image_resize_dimensions( (int) $meta['width'], (int) $meta['height'], $wh[0], $wh[1], true ); $has = $meta['sizes'][ $name ] ?? null; if ( ! $fit ) { if ( is_array( $has ) ) { self::forget( $meta, $name, $dir ); } continue; } if ( is_array( $has ) && (int) $has['width'] === (int) $fit[4] && (int) $has['height'] === (int) $fit[5] && file_exists( $dir . '/' . $has['file'] ) ) { continue; } if ( is_array( $has ) ) { self::forget( $meta, $name, $dir ); } $want[ $name ] = array( 'width' => $wh[0], 'height' => $wh[1], 'crop' => true ); } if ( ! $want ) { return 0; } $editor = wp_get_image_editor( $file ); if ( is_wp_error( $editor ) ) { return -1; } $made = $editor->multi_resize( $want ); foreach ( (array) $made as $name => $data ) { $meta['sizes'][ $name ] = $data; } wp_update_attachment_metadata( $attachment_id, $meta ); return count( (array) $made ); } private static function forget( array &$meta, $name, $dir ) { $old = (string) ( $meta['sizes'][ $name ]['file'] ?? '' ); unset( $meta['sizes'][ $name ] ); if ( '' === $old || basename( (string) ( $meta['file'] ?? '' ) ) === $old ) { return; } foreach ( $meta['sizes'] as $other ) { if ( ( $other['file'] ?? '' ) === $old ) { return; } } wp_delete_file( $dir . '/' . $old ); } private static function after_regenerate() { if ( class_exists( 'WC_Cache_Helper' ) ) { WC_Cache_Helper::get_transient_version( 'product', true ); } update_option( 'aieo_dmm_swatches_settings_hash', md5( (string) wp_json_encode( get_option( 'aieo_dmm_swatches_settings', array() ) ) . '|' . time() ), false ); } public static function ajax_regenerate() { if ( class_exists( 'AIEO_DMM' ) ) { AIEO_DMM::verify_ajax_request(); } else { check_ajax_referer( 'aieo_dmm_nonce', 'nonce' ); if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'ai-eshop-optimizer' ) ), 403 ); } } $ids = self::sources(); $total = count( $ids ); $offset = isset( $_POST['offset'] ) ? max( 0, (int) $_POST['offset'] ) : 0; $made = isset( $_POST['made'] ) ? max( 0, (int) $_POST['made'] ) : 0; $batch = array_slice( $ids, $offset, self::BATCH ); update_postmeta_cache( $batch ); foreach ( $batch as $id ) { $made += max( 0, self::make( $id ) ); } $next = $offset + self::BATCH < $total ? $offset + self::BATCH : null; if ( null === $next ) { self::after_regenerate(); } wp_send_json_success( array( 'next' => $next, 'made' => $made, 'total' => $total, 'message' => null === $next ? sprintf( __( 'Done: %1$d swatch pictures, %2$d files made.', 'ai-eshop-optimizer' ), $total, $made ) : sprintf( __( '%1$d of %2$d swatch pictures done, %3$d files made…', 'ai-eshop-optimizer' ), $next, $total, $made ), ) ); } public static function cli_regenerate( $args, $assoc ) { $ids = self::sources(); foreach ( self::sizes() as $name => $wh ) { WP_CLI::log( sprintf( '%s: %dx%d', $name, $wh[0], $wh[1] ) ); } WP_CLI::log( sprintf( '%d swatch pictures', count( $ids ) ) ); if ( ! empty( $assoc['dry-run'] ) ) { return; } $made = 0; $failed = 0; $bar = \WP_CLI\Utils\make_progress_bar( 'Swatch sizes', count( $ids ) ); foreach ( $ids as $i => $id ) { if ( 0 === $i % self::BATCH ) { update_postmeta_cache( array_slice( $ids, $i, self::BATCH ) ); } $n = self::make( $id ); if ( $n < 0 ) { ++$failed; } else { $made += $n; } $bar->tick(); } $bar->finish(); self::after_regenerate(); WP_CLI::success( sprintf( '%d files made for %d swatch pictures (%d could not be read).', $made, count( $ids ), $failed ) ); } } endif;