HEX
Server: Apache
System: Linux webd006.cluster128.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User: zwafgyp (177615)
PHP: 7.4.33
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/zwafgyp/lacalita/wp-content/plugins/woo-poly-integration/src/Hyyan/WPI/Product/Variation.php
<?php
/**
 * This file is part of the hyyan/woo-poly-integration plugin.
 * (c) Hyyan Abo Fakher <hyyanaf@gmail.com>.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Hyyan\WPI\Product;

use Hyyan\WPI\HooksInterface;
use Hyyan\WPI\Product\Meta;
use Hyyan\WPI\Utilities;

/**
 * Variation.
 *
 * Handle Variation Duplicate
 *
 * @author Hyyan Abo Fakher <hyyanaf@gmail.com>
 */
class Variation
{
    const DUPLICATE_KEY = '_point_to_variation';
    /**
     * @var \WC_Product_Variable
     */
    protected $from;
    /**
     * @var \WC_Product
     */
    protected $to;
    /**
     * Construct object.
     *
     * @param \WC_Product_Variable $from the product which contains variations to
     *                                   copy from
     * @param \WC_Product          $to   the product which we will copy the variation to
     */
    public function __construct(\WC_Product_Variable $from, \WC_Product $to)
    {
        $this->from = $from;
        $this->to = $to;
    }
    /**
     * Handle variation duplicate.
     *
     * @return bool false if the from product conatins no variatoins
     */
    public function duplicate()
    {
        //the variations of the product in the from product language
        //JM2021: needs to sync all children not only available variations
        //$fromVariation = $this->from->get_available_variations();
        $fromVariation = $this->from->get_children();
        if (empty($fromVariation)) {
            return false;
        }
        if ($this->to->get_id() === $this->from->get_id()) {
            /*
             * In such a case just add the duplicate meta
             */
            foreach ($fromVariation as $variation_id) {
                $variation = $this->from->get_available_variation( $variation_id );
                if (! metadata_exists('post', $variation['variation_id'], self::DUPLICATE_KEY)) {
                    update_post_meta(
                            $variation['variation_id'], self::DUPLICATE_KEY, $variation['variation_id']
                    );
                }
            }
        } else {
            /* This could be a very long operation */
            set_time_limit(0);
            foreach ($fromVariation as $variation_id) {
                $variation = $this->from->get_available_variation( $variation_id );
                /*
                 * First we check if the "to" product contains the duplicate meta
                 * key to find out if we have to update or insert
                 */
                $posts = get_posts(array(
                    'meta_key' => self::DUPLICATE_KEY,
                    'meta_value' => $variation_id,
                    'post_type' => 'product_variation',
                    'post_status'	 => 'any',
                    'post_parent' => $this->to->get_id(),
                    //JM2021:empty lang parameter required otherwise polylang adds language filter
                    'lang' => '',  
                ));
                switch (count($posts)) {
                    case 1:
                        // update
                        $this->update(
                            wc_get_product($variation_id), $posts[0], $variation
                        );
                        break;
                    case 0:
                        // insert
                        $this->insert(wc_get_product($variation_id), $variation);
                        break;
                    default:
                        //JM2021: duplicate translated variations detected:
                        //-  update first variation eg original
                        $this->update(
                            wc_get_product($variation_id), $posts[0], $variation
                        );
                        //- delete duplicate variations
                        $count = count($posts);
                        error_log("woo-poly is deleting duplicate variations for variation " . $variation['variation_id'] . 
                                " in product " . $this->from->get_id() . " translation " . $this->to->get_id());
                        for($i = 1; $i < $count; $i++)
                        {                            
                            $duplicate = wc_get_product($posts[$i]);
                            if ($duplicate){
                                $duplicate->delete($force_delete = true);
                            }
                        }
                        break;
                }
            }
            $target_status=$this->from->get_stock_status();
            wc_update_product_stock_status ($this->to->get_id(), $target_status);
            utilities::flushCacheUpdateLookupTable($this->to->get_id());
            /* Restor original timeout */
            set_time_limit(ini_get('max_execution_time'));
        }
    }
    /**
     * Get array of variations IDS which point to the given variation ID.
     *
     * @param int $variatonID variation ID
     *
     * @return array array of posts
     */
    public static function getRelatedVariation($variatonID, $returnIDS = false)
    {
        $result = array();
        if ( ! $variatonID ) {
          return false;
        }

        //previous version of code using get_post_meta() was filtered at runtime by Polylang
        //even when adding 'suppress_filters' => true, so there was no way to adjust stock
        //on translations when processing new order
        //it also did not return all versions of post for deletion
        global $wpdb;
        $postids=$wpdb->get_col("select post_id from " . $wpdb->postmeta . " where meta_key='" .
            self::DUPLICATE_KEY .  "' and meta_value=" . $variatonID);

        if (true === $returnIDS) {
            return $postids;
        } else {
            $result = array();
            foreach ($postids as $postid) {
                $product = wc_get_product($postid);
                if ($product) {
                    $result[]=$product;
                }
            }
            return $result;
        }
    }

    /**
     * Delete all variation related to the given variation ID.
     *
     * @param int $variationID variation ID
     */
    public static function deleteRelatedVariation($variationID)
    {
        $products = (array) static::getRelatedVariation($variationID);
        foreach ($products as $product) {
            wp_delete_post($product->get_id(), true);
        }
    }
    /**
     * Create new variation.
     *
     * @param \WC_Product_Variation $variation the variation product
     * @param array                 $metas     variation array
     */
    protected function insert(\WC_Product_Variation $variation, array $metas)
    {
        // Add the duplicate meta to the default language product variation,
        // just in case the product was created before plugin acivation.
        $this->addDuplicateMeta($variation->get_id());
        $data = (array) get_post($variation->get_id());
        $newParentId = $this->to->get_id();
        unset($data['ID']);
        $data['post_parent'] = $newParentId;
        $ID = wp_insert_post($data);
        if ($ID) {
            //JM2021: extra protection to ensure language set as early as possible
            //as had been failing on parent language [though now fixed]
            $targetLang = pll_get_post_language( $newParentId );
            if ( (! $targetLang) && isset($_GET['new_lang'])) {
                $targetLang=$_GET['new_lang'];
            } 
            if ($targetLang){
                pll_set_post_language( $ID, $targetLang );
            }
            update_post_meta(
                    $ID, self::DUPLICATE_KEY, $metas['variation_id']
            );
            $this->copyVariationMetas($variation->get_id(), $ID);
            //JM2021:within current execution woo may cache the fact that the new product does not have children 
            //so force flush transients etc on the *parent*
            //wc_delete_product_transients($this->to->get_id());
            Utilities::flushCacheUpdateLookupTable( $ID );
            Utilities::flushCacheUpdateLookupTable( $newParentId );
        }
    }
    /**
     * Update variation product from given post object.
     *
     * @param \WC_Product_Variation $variation
     * @param \WP_Post              $post
     * @param array                 $metas
     */
    protected function update(\WC_Product_Variation $variation, \WP_Post $post, array $metas)
    {
        $this->copyVariationMetas($variation->get_id(), $post->ID);
		    pll_set_post_language( $post->ID, pll_get_post_language( $post->post_parent ) );
        if ( $post->post_status != $variation->get_status() ) {
          $destVariation = wc_get_product( $post->ID );
          $destVariation->set_status( $variation->get_status() );
          $destVariation->save();
        }
		    Utilities::flushCacheUpdateLookupTable( $post->ID );
    }
    /**
     * Add duplicate meta key to products created before plugin activation.
     *
     * @param int $ID   Id of the product in the default language
     */
    public function addDuplicateMeta($ID)
    {
        if ($ID) {
            $meta = get_post_meta($ID, self::DUPLICATE_KEY);
            if (empty($meta)) {
                update_post_meta($ID, self::DUPLICATE_KEY, $ID);
            }
        }
    }
    /**
     * Sync Product Shipping Class.
     *
     * Shipping Class translation is not supported after WooCommerce 2.6
     * but it is still implemented by WooCommerce as a taxonomy (no longer a meta).
     * Therefore, Polylang will not copy the Shipping Class meta.
     *
     * @param int $from product variation ID
     * @param int $to   product variation ID
     */
    public function syncShippingClass($from, $to)
    {
        if (in_array('product_shipping_class', Meta::getProductMetaToCopy())) {
            $variation_from = wc_get_product($from);
            if ($variation_from) {
                $shipping_class = $variation_from->get_shipping_class();
                if ($shipping_class) {
                    $shipping_terms = get_term_by('slug', $shipping_class, 'product_shipping_class');
                    if ($shipping_terms) {
                        wp_set_post_terms($to, array($shipping_terms->term_id), 'product_shipping_class');
                    }
                } else {
                    //if no shipping class found this would mean "Same as parent"
                    //so we need to clear existing setting if there is one
                    //however get_shipping_class() actually gets the parent value,
                    //so this code shouldn't be executed,
                    //instead the parent value will be copied to variation
                    wp_set_post_terms($to, array(), 'product_shipping_class');
                }
            }
        }
    }
    /**
     * Copy variation meta.
     *
     * The method follow the same method polylang use to sync metas between
     * translations
     *
     * @param int $from product variation ID
     * @param int $to   product variation ID
     *
     * @return boolean false if something went wrong
     */
    protected function copyVariationMetas($from, $to)
    {
        /* copy or synchronize post metas and allow plugins to do the same */
        $metas_from     = get_post_custom($from);
        $metas_to       = get_post_custom($to);
        /* get public and protected meta keys */
        $keys           = array_unique(array_merge(array_keys($metas_from), array_keys($metas_to)));
        /* metas disabled for sync */
        $metas_nosync   = Meta::getDisabledProductMetaToCopy();
        /*
         * _variation_description meta is a text-based string and generally needs to be translated.
         * _variation_description meta is copied from product in default language to the translations
         * when the translation is first created. But the meta can be edited/changed and will not be
         * overwriten when product is saved or updated.
         */
        if (isset($metas_to['_variation_description'])) {
            $metas_nosync[] = '_variation_description';
        }
        /* synchronize */
        foreach ($keys as $key) {
            if (!in_array($key, $metas_nosync)) {
                /*
                 * the synchronization process of multiple values custom fields is
                 * easier if we delete all metas first
                 */
                delete_post_meta($to, $key);
                if (isset($metas_from[$key])) {
                    if (substr($key, 0, 10) == 'attribute_') {
                        $translated = array();
                        $tax = str_replace('attribute_', '', $key);
                        foreach ($metas_from[$key] as $termSlug) {
							if ( pll_is_translated_taxonomy( $tax ) && $termSlug ) {
                                $term = $this->getTermBySlug($tax, $termSlug);
                                if ($term) {
                                    $term_id = $term->term_id;
                                    //ok we got a term to translate, now get translation if available
                                    $lang = isset($_GET['new_lang']) ? esc_attr($_GET['new_lang']) : pll_get_post_language($this->to->get_id());
                                    $translated_term = pll_get_term($term_id, $lang);
                                    if ($translated_term) {
                                        $translated[] = get_term_by('id', $translated_term, $tax)->slug;
                                    } else {
                                        // Attribute term has no translation, so get the previous term
                                        // and create the translation
                                        $result = false;
                                        $fromLang = pll_get_post_language($from);
                                        if (! $fromLang) {
                                            $fromLang = pll_get_post_language(wc_get_product($from)->get_parent_id());
                                        }
                                        if ($fromLang) {
                                            $term = pll_get_term($term_id, $fromLang);
                                            if ($term) {
                                                $term = get_term_by('id', $term, $tax);
                                                $result = Meta::createDefaultTermTranslation($tax, $term, $termSlug, $lang, false);
                                            }
                                        }
                                        if ($result) {
                                            $translated[] = $result;
                                        } else {
                                            $translated[] = $termSlug;
                                        }
                                    }
                                } else {
                                    $translated[] = $termSlug;
                                }
                            } else {
                                $translated[] = $termSlug;
                            }
                        }
                        $metas_from[$key] = $translated;
                    }
                    foreach ($metas_from[$key] as $value) {
                        /*
                         * Important: always maybe_unserialize value coming from
                         *            get_post_custom. See codex.
                         */
                        $value = maybe_unserialize($value);
                        add_post_meta($to, $key, $value);
                    }
                }
            }
        }
        
        //add shipping class not included in metas as now a taxonomy
        $this->syncShippingClass($from, $to);

        do_action(HooksInterface::PRODUCT_VARIATION_COPY_META_ACTION, $from, $to, $this->from, $this->to);
    }
    /**
     * Get Term By Slug.
     *
     * Why not get_term_by method ?! since 4.8 we were unable to force polylang to
     * fetch terms with the default language
     *
     * @param string $taxonomy taxonomy name
     * @param string $value    term slug
     *
     * @return bool false if the term can not fetched , the term object otherwise
     */
    private function getTermBySlug($taxonomy, $value)
    {
        $query = array(
            'get' => 'all',
            'number' => 1,
            'taxonomy' => $taxonomy,
            'update_term_meta_cache' => false,
            'orderby' => 'none',
            'suppress_filter' => true,
            'slug' => $value,
            'lang' => pll_default_language(),
        );
        $terms = get_terms($query);
        if (is_wp_error($terms) || empty($terms)) {
            return false;
        }
        $term = array_shift($terms);
        return get_term($term, $taxonomy);
    }
}