$id, 'url' => $url); } } return $out; } public static function product_videos($product_id, $shown_only = true) { $product_id = (int) $product_id; $position = (string) get_post_meta($product_id, '_merchant_video_position', true); $ratio = (string) get_post_meta($product_id, '_merchant_product_video_aspect_ratio', true); $out = array(); $featured = trim((string) get_post_meta($product_id, '_merchant_product_video_url', true)); if ($featured !== '') { $enabled = (string) get_post_meta($product_id, '_merchant_enable_featured_video', true) !== ''; if ($enabled || !$shown_only) { $out[] = array( 'url' => self::encode_commas($featured), 'kind' => self::video_kind($featured), 'role' => 'featured', 'enabled' => $enabled, 'position' => 'featured', 'aspect_ratio' => $ratio, ); } } $gallery = get_post_meta($product_id, '_merchant_videos_in_gallery', true); if (is_array($gallery)) { $enabled = (string) get_post_meta($product_id, '_merchant_enable_gallery', true) !== ''; if ($enabled || !$shown_only) { foreach ($gallery as $url) { $url = trim((string) $url); if ($url === '' || !preg_match('#^https?://#i', $url)) { continue; } $out[] = array( 'url' => self::encode_commas($url), 'kind' => self::video_kind($url), 'role' => 'gallery', 'enabled' => $enabled, 'position' => $position === 'after' ? 'after' : 'before', 'aspect_ratio' => $ratio, ); } } } return $out; } public static function social_video($product_id) { if (!class_exists('AIEO_Social_Video')) { return null; } $v = AIEO_Social_Video::for_object((int) $product_id); if (empty($v['url']) || $v['source'] === 'product_meta') { return null; } return array('url' => self::encode_commas((string) $v['url']), 'source' => (string) $v['source']); } public static function video_kind($url) { $url = strtolower((string) $url); if (strpos($url, 'youtube.com') !== false || strpos($url, 'youtu.be') !== false) { return 'youtube'; } if (strpos($url, 'vimeo.com') !== false) { return 'vimeo'; } $path = (string) wp_parse_url($url, PHP_URL_PATH); $ft = wp_check_filetype($path); if (!empty($ft['ext']) && wp_ext2type($ft['ext']) === 'video') { return 'file'; } return 'embed'; } public static function variation_gallery_ids($variation) { $vid = (int) $variation->get_id(); $ids = array(); if (class_exists('AIEO_DMM_Variations_Gallery')) { $ids = AIEO_DMM_Variations_Gallery::resolve_image_ids($vid, 'export'); } else { $csv = (string) get_post_meta($vid, '_product_image_gallery', true); $ids = $csv !== '' ? array_map('intval', explode(',', $csv)) : array(); if (empty(array_filter($ids))) { $ids = self::legacy_image_ids($vid); } } if (empty($ids) && method_exists($variation, 'get_gallery_image_ids')) { $ids = array_map('intval', (array) $variation->get_gallery_image_ids()); } $own = (int) $variation->get_image_id('edit'); $out = array(); foreach ($ids as $id) { $id = (int) $id; if ($id > 0 && $id !== $own && !in_array($id, $out, true)) { $out[] = $id; } } return $out; } public static function variation_videos($variation_id) { $variation_id = (int) $variation_id; $entries = array(); $canonical = get_post_meta($variation_id, '_aieo_variation_gallery_videos', true); if (is_array($canonical) && !empty($canonical)) { $entries = $canonical; } elseif ((string) get_post_meta($variation_id, '_product_image_gallery', true) === '') { $legacy = get_post_meta($variation_id, 'botiga_variation_gallery', true); if (is_array($legacy)) { foreach ($legacy as $position => $entry) { $c = self::classify_legacy_entry($entry); if ($c !== null && $c['kind'] === 'video') { $entries[] = array_merge($c, array('position' => (int) $position)); } } } } $out = array(); foreach ($entries as $v) { if (!is_array($v)) { continue; } $type = (string) ($v['type'] ?? ''); $url = ''; if ($type === 'attachment' && !empty($v['id'])) { $url = self::attachment_url((int) $v['id']); } elseif (!empty($v['url'])) { $url = self::encode_commas((string) $v['url']); } if ($url === '') { continue; } $out[] = array( 'url' => $url, 'kind' => $type === 'attachment' ? 'file' : self::video_kind($url), 'position' => (int) ($v['position'] ?? 0), ); } usort($out, static function ($a, $b) { return $a['position'] <=> $b['position']; }); return $out; } private static function legacy_image_ids($variation_id) { $legacy = get_post_meta((int) $variation_id, 'botiga_variation_gallery', true); $ids = array(); if (is_array($legacy)) { foreach ($legacy as $entry) { $c = self::classify_legacy_entry($entry); if ($c !== null && $c['kind'] === 'image') { $ids[] = (int) $c['id']; } } } return $ids; } private static function classify_legacy_entry($entry) { if (class_exists('AIEO_WC_Compat_Migration')) { return AIEO_WC_Compat_Migration::classify_gallery_entry($entry); } if (is_numeric($entry) && (int) $entry > 0) { $id = (int) $entry; if (wp_attachment_is('video', $id)) { return array('kind' => 'video', 'type' => 'attachment', 'id' => $id); } return wp_attachment_is_image($id) ? array('kind' => 'image', 'id' => $id) : null; } if (is_string($entry)) { $obj = json_decode($entry); if ($obj && isset($obj->url)) { $kind = self::video_kind((string) $obj->url); return array('kind' => 'video', 'type' => in_array($kind, array('youtube', 'vimeo'), true) ? $kind : 'embed', 'url' => (string) $obj->url); } } return null; } public static function display_type($taxonomy) { $taxonomy = (string) $taxonomy; if ($taxonomy === '' || strpos($taxonomy, 'pa_') !== 0) { return 'select'; } if (self::$attribute_types === null) { self::$attribute_types = array(); foreach ((array) wc_get_attribute_taxonomies() as $t) { self::$attribute_types['pa_' . $t->attribute_name] = (string) $t->attribute_type; } } $raw = self::$attribute_types[$taxonomy] ?? ''; $type = class_exists('AIEO_Swatch_Type_Detector') ? (string) AIEO_Swatch_Type_Detector::resolve($raw, $taxonomy) : $raw; if (in_array($type, array('color', 'image', 'button', 'select'), true)) { return $type; } if ($raw === 'wc-visual' || $type === 'wc-visual') { return 'visual'; } return 'select'; } public static function term_swatch($term_id, $taxonomy, $display_type = '') { $term_id = (int) $term_id; $all = get_term_meta($term_id); if (!is_array($all)) { $all = array(); } $meta = static function ($key) use ($all) { return isset($all[$key][0]) ? maybe_unserialize($all[$key][0]) : ''; }; $image = array('type' => 'none', 'image_id' => 0, 'image_url' => '', 'colors' => array(), 'direction' => ''); $image_id = (int) $meta('_aieo_swatch_image_id'); if ($image_id <= 0) { $image_id = (int) $meta('product_attribute_image'); } $image_id = (int) apply_filters('aieo_swatch_image_id_for_term', $image_id, $term_id, (string) $taxonomy); if ($image_id <= 0) { $image_id = (int) $meta('image'); } if ($image_id > 0) { $url = self::attachment_url($image_id); if ($url !== '') { $image = array('type' => 'image', 'image_id' => $image_id, 'image_url' => $url, 'colors' => array(), 'direction' => ''); } } $color = array('type' => 'none', 'image_id' => 0, 'image_url' => '', 'colors' => array(), 'direction' => ''); $kind = (string) $meta('_aieo_swatch_color_type'); if ($kind === '') { $kind = (string) $meta('product_attribute_color_type'); } if ($kind === 'gradient') { $p = $meta('_aieo_swatch_gradient'); $c1 = is_array($p) && isset($p['color_1']) ? (string) $p['color_1'] : (string) $meta('product_attribute_color_gradient_color_1'); $c2 = is_array($p) && isset($p['color_2']) ? (string) $p['color_2'] : (string) $meta('product_attribute_color_gradient_color_2'); $dir = is_array($p) && isset($p['direction']) ? (string) $p['direction'] : (string) $meta('product_attribute_color_gradient_direction'); $cs = array_values(array_filter(array($c1, $c2))); if (!empty($cs)) { $color = array('type' => 'gradient', 'image_id' => 0, 'image_url' => '', 'colors' => $cs, 'direction' => $dir); } } elseif ($kind === 'multiple') { $p = $meta('_aieo_swatch_multiple'); $cs = is_array($p) && isset($p['colors']) && is_array($p['colors']) ? array_values(array_filter(array_map('strval', $p['colors']))) : array_values(array_filter(array( (string) $meta('product_attribute_color_multiple_color_1'), (string) $meta('product_attribute_color_multiple_color_2'), (string) $meta('product_attribute_color_multiple_color_3'), (string) $meta('product_attribute_color_multiple_color_4'), ))); $dir = is_array($p) && isset($p['direction']) ? (string) $p['direction'] : (string) $meta('product_attribute_color_multiple_direction'); if (!empty($cs)) { $color = array('type' => 'multiple', 'image_id' => 0, 'image_url' => '', 'colors' => $cs, 'direction' => $dir); } } if ($color['type'] === 'none') { $hex = (string) $meta('color'); if ($hex === '') { $hex = (string) $meta('product_attribute_color'); } if ($hex !== '') { $color = array('type' => 'color', 'image_id' => 0, 'image_url' => '', 'colors' => array($hex), 'direction' => ''); } } if ($display_type === 'color') { return $color['type'] !== 'none' ? $color : $image; } return $image['type'] !== 'none' ? $image : $color; } public static function variation_swatch($taxonomy, $term, $display_type, $variation_image_id, $parent_image_id) { $none = array('type' => 'none', 'source' => 'none', 'image_id' => 0, 'image_url' => '', 'colors' => array(), 'direction' => ''); if ($display_type === 'button') { return array_merge($none, array('type' => 'button', 'source' => 'label')); } if (!in_array($display_type, array('image', 'color', 'visual'), true)) { return $none; } $swatch = ($term instanceof WP_Term) ? self::term_swatch((int) $term->term_id, (string) $taxonomy, $display_type) : array('type' => 'none', 'image_id' => 0, 'image_url' => '', 'colors' => array(), 'direction' => ''); if ($display_type === 'image') { if ($swatch['type'] === 'image') { $swatch['source'] = 'term_image'; return $swatch; } foreach (array('variation_image' => $variation_image_id, 'parent_image' => $parent_image_id) as $source => $id) { $url = self::attachment_url((int) $id); if ($url !== '') { return array('type' => 'image', 'source' => $source, 'image_id' => (int) $id, 'image_url' => $url, 'colors' => array(), 'direction' => ''); } } return $none; } if ($swatch['type'] !== 'none') { $swatch['source'] = $swatch['type'] === 'image' ? 'term_image' : 'term_color'; return $swatch; } return $none; } public static function swatch_settings($product_id) { $product_id = (int) $product_id; if (class_exists('AIEO_DMM_Swatches_Runtime') && method_exists('AIEO_DMM_Swatches_Runtime', 'product_settings')) { $s = AIEO_DMM_Swatches_Runtime::instance()->product_settings($product_id); if (is_array($s)) { return array( 'display_style' => (string) ($s['display_style'] ?? ''), 'multi_select' => !empty($s['multi_select_enabled']), 'source' => (string) ($s['source'] ?? ''), ); } } $multi = (string) get_post_meta($product_id, '_om_swatch_multi_select', true); return array( 'display_style' => (string) get_post_meta($product_id, '_om_swatch_display_style', true), 'multi_select' => $multi === 'yes' ? true : ($multi === 'no' ? false : null), 'source' => $multi !== '' ? 'product' : '', ); } public static function describe_product($product) { $product_id = (int) $product->get_id(); $parent_image = (int) $product->get_image_id('edit'); $children = $product->is_type('variable') ? array_map('intval', (array) $product->get_children()) : array(); if (!empty($children)) { _prime_post_caches($children, false, true); } self::prime_media($product, $children); $attributes = array(); $term_maps = array(); foreach ($product->get_attributes() as $key => $attr) { if (!($attr instanceof WC_Product_Attribute)) { continue; } $taxonomy = $attr->is_taxonomy() ? (string) $attr->get_taxonomy() : ''; $display = self::display_type($taxonomy); $values = array(); if ($taxonomy !== '') { $terms = $attr->get_terms(); $terms = is_array($terms) ? $terms : array(); if (!empty($terms)) { update_termmeta_cache(wp_list_pluck($terms, 'term_id')); $pictures = array(); foreach ($terms as $t) { foreach (array('_aieo_swatch_image_id', 'product_attribute_image', 'image') as $k) { $pictures[] = (int) get_term_meta((int) $t->term_id, $k, true); } } $pictures = array_values(array_unique(array_filter($pictures))); if (!empty($pictures)) { _prime_post_caches($pictures, false, true); } } foreach ($terms as $term) { $term_maps[$key][$term->slug] = $term; $sw = self::term_swatch((int) $term->term_id, $taxonomy, $display); $values[] = array( 'slug' => (string) $term->slug, 'name' => (string) $term->name, 'swatch' => $sw, ); } } else { foreach ((array) $attr->get_options() as $opt) { $opt = (string) $opt; if ($opt === '') { continue; } $values[] = array('slug' => sanitize_title($opt), 'name' => $opt, 'swatch' => null); } } $attributes[] = array( 'key' => (string) $key, 'taxonomy' => $taxonomy, 'name' => $taxonomy !== '' ? (string) wc_attribute_label($taxonomy, $product) : (string) $attr->get_name(), 'display_type' => $display, 'variation' => (bool) $attr->get_variation(), 'visible' => (bool) $attr->get_visible(), 'values' => $values, ); } $variations = array(); foreach ($children as $vid) { $v = wc_get_product($vid); if (!$v || !$v->is_type('variation')) { continue; } $variations[] = self::describe_variation($v, $attributes, $term_maps, $parent_image); } $image = self::attachments(array($parent_image)); return array( 'id' => $product_id, 'type' => (string) $product->get_type(), 'status' => (string) $product->get_status(), 'sku' => (string) $product->get_sku('edit'), 'barcode' => self::barcode($product), 'title' => (string) $product->get_name('edit'), 'handle' => (string) $product->get_slug('edit'), 'permalink' => (string) get_permalink($product_id), 'created' => self::iso_date($product->get_date_created('edit')), 'modified' => self::iso_date($product->get_date_modified('edit')), 'image' => $image ? $image[0] : null, 'gallery' => self::attachments(array_map('intval', (array) $product->get_gallery_image_ids('edit'))), 'videos' => self::product_videos($product_id), 'social_video' => self::social_video($product_id), 'swatch_settings' => $product->is_type('variable') ? self::swatch_settings($product_id) : null, 'attributes' => $attributes, 'variations' => $variations, ); } private static function describe_variation($v, array $attributes, array $term_maps, $parent_image) { $own_image = (int) $v->get_image_id('edit'); $chosen = (array) $v->get_attributes('edit'); $options = array(); $swatch = null; foreach ($attributes as $a) { if (empty($a['variation'])) { continue; } $key = $a['key']; $slug = isset($chosen[$key]) ? (string) $chosen[$key] : ''; $term = ($slug !== '' && isset($term_maps[$key][$slug])) ? $term_maps[$key][$slug] : null; if ($term === null && $slug !== '' && $a['taxonomy'] !== '') { $found = get_term_by('slug', $slug, $a['taxonomy']); $term = ($found instanceof WP_Term) ? $found : null; } $value = $term ? (string) $term->name : $slug; $sw = self::variation_swatch($a['taxonomy'], $term, $a['display_type'], $own_image, $parent_image); $options[] = array( 'key' => $key, 'name' => $a['name'], 'slug' => $slug, 'value' => $value, 'swatch' => $sw, ); if ($swatch === null && in_array($sw['type'], array('image', 'color', 'gradient', 'multiple'), true)) { $swatch = $sw; } } $image = self::attachments(array($own_image)); return array( 'id' => (int) $v->get_id(), 'sku' => (string) $v->get_sku('edit'), 'barcode' => self::barcode($v), 'title' => (string) $v->get_name('edit'), 'enabled' => (string) $v->get_status('edit') === 'publish', 'created' => self::iso_date($v->get_date_created('edit')), 'modified' => self::iso_date($v->get_date_modified('edit')), 'options' => $options, 'swatch' => $swatch, 'image' => $image ? $image[0] : null, 'gallery' => self::attachments(self::variation_gallery_ids($v)), 'videos' => self::variation_videos((int) $v->get_id()), ); } private static function prime_media($product, array $children) { $ids = array_merge( array((int) $product->get_image_id('edit')), array_map('intval', (array) $product->get_gallery_image_ids('edit')) ); foreach ($children as $vid) { $ids[] = (int) get_post_meta($vid, '_thumbnail_id', true); $csv = (string) get_post_meta($vid, '_product_image_gallery', true); if ($csv !== '') { $ids = array_merge($ids, array_map('intval', explode(',', $csv))); } } $ids = array_values(array_unique(array_filter($ids))); if (!empty($ids)) { _prime_post_caches($ids, false, true); } } private static function barcode($product) { if (method_exists($product, 'get_global_unique_id')) { return (string) $product->get_global_unique_id('edit'); } return (string) get_post_meta($product->get_id(), '_global_unique_id', true); } private static function iso_date($date) { return ($date instanceof WC_DateTime) ? $date->format('c') : ''; } private static function encode_commas($url) { return str_replace(',', '%2C', (string) $url); } }