/**
  * Loads an array of attributes used for variations, as well as their possible values.
  *
  * @param WC_Product
  */
 private function read_variation_attributes(&$product)
 {
     global $wpdb;
     $variation_attributes = array();
     $attributes = $product->get_attributes();
     $child_ids = $product->get_children();
     if (!empty($child_ids) && !empty($attributes)) {
         foreach ($attributes as $attribute) {
             if (empty($attribute['is_variation'])) {
                 continue;
             }
             // Get possible values for this attribute, for only visible variations.
             $values = array_unique($wpdb->get_col($wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s AND post_id IN (" . implode(',', array_map('esc_sql', $child_ids)) . ")", wc_variation_attribute_name($attribute['name']))));
             // Empty value indicates that all options for given attribute are available.
             if (in_array('', $values) || empty($values)) {
                 $values = $attribute['is_taxonomy'] ? wc_get_object_terms($product->get_id(), $attribute['name'], 'slug') : wc_get_text_attributes($attribute['value']);
                 // Get custom attributes (non taxonomy) as defined.
             } elseif (!$attribute['is_taxonomy']) {
                 $text_attributes = wc_get_text_attributes($attribute['value']);
                 $assigned_text_attributes = $values;
                 $values = array();
                 // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute
                 if (version_compare(get_post_meta($product->get_id(), '_product_version', true), '2.4.0', '<')) {
                     $assigned_text_attributes = array_map('sanitize_title', $assigned_text_attributes);
                     foreach ($text_attributes as $text_attribute) {
                         if (in_array(sanitize_title($text_attribute), $assigned_text_attributes)) {
                             $values[] = $text_attribute;
                         }
                     }
                 } else {
                     foreach ($text_attributes as $text_attribute) {
                         if (in_array($text_attribute, $assigned_text_attributes)) {
                             $values[] = $text_attribute;
                         }
                     }
                 }
             }
             $variation_attributes[$attribute['name']] = array_unique($values);
         }
     }
     $product->set_variation_attributes($variation_attributes);
 }
 /**
  * Add to cart action
  *
  * Checks for a valid request, does validation (via hooks) and then redirects if valid.
  *
  * @param bool $url (default: false)
  */
 public static function add_to_cart_action($url = false)
 {
     if (empty($_REQUEST['add-to-cart']) || !is_numeric($_REQUEST['add-to-cart'])) {
         return;
     }
     $product_id = apply_filters('woocommerce_add_to_cart_product_id', absint($_REQUEST['add-to-cart']));
     $was_added_to_cart = false;
     $added_to_cart = array();
     $adding_to_cart = wc_get_product($product_id);
     $add_to_cart_handler = apply_filters('woocommerce_add_to_cart_handler', $adding_to_cart->product_type, $adding_to_cart);
     // Check if the product is published
     if ('publish' !== $adding_to_cart->post->post_status) {
         wc_add_notice(__('Sorry, this product is unavailable.', 'woocommerce'), 'error');
         return;
     }
     // Variable product handling
     if ('variable' === $add_to_cart_handler) {
         $variation_id = empty($_REQUEST['variation_id']) ? '' : absint($_REQUEST['variation_id']);
         $quantity = empty($_REQUEST['quantity']) ? 1 : wc_stock_amount($_REQUEST['quantity']);
         $missing_attributes = array();
         $variations = array();
         $attributes = $adding_to_cart->get_attributes();
         $variation = wc_get_product($variation_id);
         // Verify all attributes
         foreach ($attributes as $attribute) {
             if (!$attribute['is_variation']) {
                 continue;
             }
             $taxonomy = 'attribute_' . sanitize_title($attribute['name']);
             if (isset($_REQUEST[$taxonomy])) {
                 // Get value from post data
                 if ($attribute['is_taxonomy']) {
                     // Don't use wc_clean as it destroys sanitized characters
                     $value = sanitize_title(stripslashes($_REQUEST[$taxonomy]));
                 } else {
                     $value = wc_clean(stripslashes($_REQUEST[$taxonomy]));
                 }
                 // Get valid value from variation
                 $valid_value = $variation->variation_data[$taxonomy];
                 // Allow if valid
                 if ('' === $valid_value || $valid_value === $value) {
                     // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute
                     if (!$attribute['is_taxonomy']) {
                         if ($value === sanitize_title($value) && version_compare(get_post_meta($product_id, '_product_version', true), '2.4.0', '<')) {
                             $text_attributes = wc_get_text_attributes($attribute['value']);
                             foreach ($text_attributes as $text_attribute) {
                                 if (sanitize_title($text_attribute) === $value) {
                                     $value = $text_attribute;
                                     break;
                                 }
                             }
                         }
                     }
                     $variations[$taxonomy] = $value;
                     continue;
                 }
             } else {
                 $missing_attributes[] = wc_attribute_label($attribute['name']);
             }
         }
         if ($missing_attributes) {
             wc_add_notice(sprintf(_n('%s is a required field', '%s are required fields', sizeof($missing_attributes), 'woocommerce'), wc_format_list_of_items($missing_attributes)), 'error');
             return;
         } elseif (empty($variation_id)) {
             wc_add_notice(__('Please choose product options&hellip;', 'woocommerce'), 'error');
             return;
         } else {
             // Add to cart validation
             $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations);
             if ($passed_validation) {
                 if (WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variations) !== false) {
                     wc_add_to_cart_message($product_id);
                     $was_added_to_cart = true;
                     $added_to_cart[] = $product_id;
                 }
             }
         }
         // Grouped Products
     } elseif ('grouped' === $add_to_cart_handler) {
         if (!empty($_REQUEST['quantity']) && is_array($_REQUEST['quantity'])) {
             $quantity_set = false;
             foreach ($_REQUEST['quantity'] as $item => $quantity) {
                 if ($quantity <= 0) {
                     continue;
                 }
                 $quantity_set = true;
                 // Add to cart validation
                 $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $item, $quantity);
                 if ($passed_validation) {
                     if (WC()->cart->add_to_cart($item, $quantity) !== false) {
                         $was_added_to_cart = true;
                         $added_to_cart[] = $item;
                     }
                 }
             }
             if ($was_added_to_cart) {
                 wc_add_to_cart_message($added_to_cart);
             }
             if (!$was_added_to_cart && !$quantity_set) {
                 wc_add_notice(__('Please choose the quantity of items you wish to add to your cart&hellip;', 'woocommerce'), 'error');
                 return;
             }
         } elseif ($product_id) {
             /* Link on product archives */
             wc_add_notice(__('Please choose a product to add to your cart&hellip;', 'woocommerce'), 'error');
             return;
         }
         // Custom Handler
     } elseif (has_action('woocommerce_add_to_cart_handler_' . $add_to_cart_handler)) {
         do_action('woocommerce_add_to_cart_handler_' . $add_to_cart_handler, $url);
         return;
         // Simple Products
     } else {
         $quantity = empty($_REQUEST['quantity']) ? 1 : wc_stock_amount($_REQUEST['quantity']);
         // Add to cart validation
         $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity);
         if ($passed_validation) {
             // Add the product to the cart
             if (WC()->cart->add_to_cart($product_id, $quantity) !== false) {
                 wc_add_to_cart_message($product_id);
                 $was_added_to_cart = true;
                 $added_to_cart[] = $product_id;
             }
         }
     }
     // If we added the product to the cart we can now optionally do a redirect.
     if ($was_added_to_cart && wc_notice_count('error') == 0) {
         $url = apply_filters('woocommerce_add_to_cart_redirect', $url);
         // If has custom URL redirect there
         if ($url) {
             wp_safe_redirect($url);
             exit;
         } elseif (get_option('woocommerce_cart_redirect_after_add') == 'yes') {
             wp_safe_redirect(WC()->cart->get_cart_url());
             exit;
         }
     }
 }
 /**
  * Save meta box data
  */
 public static function save($post_id, $post)
 {
     global $wpdb;
     // Add any default post meta
     add_post_meta($post_id, 'total_sales', '0', true);
     // Get types
     $product_type = empty($_POST['product-type']) ? 'simple' : sanitize_title(stripslashes($_POST['product-type']));
     $is_downloadable = isset($_POST['_downloadable']) ? 'yes' : 'no';
     $is_virtual = isset($_POST['_virtual']) ? 'yes' : 'no';
     // Product type + Downloadable/Virtual
     wp_set_object_terms($post_id, $product_type, 'product_type');
     update_post_meta($post_id, '_downloadable', $is_downloadable);
     update_post_meta($post_id, '_virtual', $is_virtual);
     // Update post meta
     if (isset($_POST['_regular_price'])) {
         update_post_meta($post_id, '_regular_price', $_POST['_regular_price'] === '' ? '' : wc_format_decimal($_POST['_regular_price']));
     }
     if (isset($_POST['_sale_price'])) {
         update_post_meta($post_id, '_sale_price', $_POST['_sale_price'] === '' ? '' : wc_format_decimal($_POST['_sale_price']));
     }
     if (isset($_POST['_tax_status'])) {
         update_post_meta($post_id, '_tax_status', wc_clean($_POST['_tax_status']));
     }
     if (isset($_POST['_tax_class'])) {
         update_post_meta($post_id, '_tax_class', wc_clean($_POST['_tax_class']));
     }
     if (isset($_POST['_purchase_note'])) {
         update_post_meta($post_id, '_purchase_note', wp_kses_post(stripslashes($_POST['_purchase_note'])));
     }
     // Featured
     if (update_post_meta($post_id, '_featured', isset($_POST['_featured']) ? 'yes' : 'no')) {
         delete_transient('wc_featured_products');
     }
     // Dimensions
     if ('no' == $is_virtual) {
         if (isset($_POST['_weight'])) {
             update_post_meta($post_id, '_weight', '' === $_POST['_weight'] ? '' : wc_format_decimal($_POST['_weight']));
         }
         if (isset($_POST['_length'])) {
             update_post_meta($post_id, '_length', '' === $_POST['_length'] ? '' : wc_format_decimal($_POST['_length']));
         }
         if (isset($_POST['_width'])) {
             update_post_meta($post_id, '_width', '' === $_POST['_width'] ? '' : wc_format_decimal($_POST['_width']));
         }
         if (isset($_POST['_height'])) {
             update_post_meta($post_id, '_height', '' === $_POST['_height'] ? '' : wc_format_decimal($_POST['_height']));
         }
     } else {
         update_post_meta($post_id, '_weight', '');
         update_post_meta($post_id, '_length', '');
         update_post_meta($post_id, '_width', '');
         update_post_meta($post_id, '_height', '');
     }
     // Save shipping class
     $product_shipping_class = $_POST['product_shipping_class'] > 0 && $product_type != 'external' ? absint($_POST['product_shipping_class']) : '';
     wp_set_object_terms($post_id, $product_shipping_class, 'product_shipping_class');
     // Unique SKU
     $sku = get_post_meta($post_id, '_sku', true);
     $new_sku = wc_clean(stripslashes($_POST['_sku']));
     if ('' == $new_sku) {
         update_post_meta($post_id, '_sku', '');
     } elseif ($new_sku !== $sku) {
         if (!empty($new_sku)) {
             $unique_sku = wc_product_has_unique_sku($post_id, $new_sku);
             if (!$unique_sku) {
                 WC_Admin_Meta_Boxes::add_error(__('Product SKU must be unique.', 'woocommerce'));
             } else {
                 update_post_meta($post_id, '_sku', $new_sku);
             }
         } else {
             update_post_meta($post_id, '_sku', '');
         }
     }
     // Save Attributes
     $attributes = array();
     if (isset($_POST['attribute_names']) && isset($_POST['attribute_values'])) {
         $attribute_names = $_POST['attribute_names'];
         $attribute_values = $_POST['attribute_values'];
         if (isset($_POST['attribute_visibility'])) {
             $attribute_visibility = $_POST['attribute_visibility'];
         }
         if (isset($_POST['attribute_variation'])) {
             $attribute_variation = $_POST['attribute_variation'];
         }
         $attribute_is_taxonomy = $_POST['attribute_is_taxonomy'];
         $attribute_position = $_POST['attribute_position'];
         $attribute_names_max_key = max(array_keys($attribute_names));
         for ($i = 0; $i <= $attribute_names_max_key; $i++) {
             if (empty($attribute_names[$i])) {
                 continue;
             }
             $is_visible = isset($attribute_visibility[$i]) ? 1 : 0;
             $is_variation = isset($attribute_variation[$i]) ? 1 : 0;
             $is_taxonomy = $attribute_is_taxonomy[$i] ? 1 : 0;
             if ($is_taxonomy) {
                 if (isset($attribute_values[$i])) {
                     // Select based attributes - Format values (posted values are slugs)
                     if (is_array($attribute_values[$i])) {
                         $values = array_map('sanitize_title', $attribute_values[$i]);
                         // Text based attributes - Posted values are term names - don't change to slugs
                     } else {
                         $values = array_map('stripslashes', array_map('strip_tags', explode(WC_DELIMITER, $attribute_values[$i])));
                     }
                     // Remove empty items in the array
                     $values = array_filter($values, 'strlen');
                 } else {
                     $values = array();
                 }
                 // Update post terms
                 if (taxonomy_exists($attribute_names[$i])) {
                     foreach ($values as $key => $value) {
                         $term = get_term_by('name', trim($value), $attribute_names[$i]);
                         if ($term) {
                             $values[$key] = intval($term->term_id);
                         } else {
                             $term = wp_insert_term(trim($value), $attribute_names[$i]);
                             if (isset($term->term_id)) {
                                 $values[$key] = intval($term->term_id);
                             }
                         }
                     }
                     wp_set_object_terms($post_id, $values, $attribute_names[$i]);
                 }
                 if (!empty($values)) {
                     // Add attribute to array, but don't set values
                     $attributes[sanitize_title($attribute_names[$i])] = array('name' => wc_clean($attribute_names[$i]), 'value' => '', 'position' => $attribute_position[$i], 'is_visible' => $is_visible, 'is_variation' => $is_variation, 'is_taxonomy' => $is_taxonomy);
                 }
             } elseif (isset($attribute_values[$i])) {
                 // Text based, separate by pipe
                 $values = implode(' ' . WC_DELIMITER . ' ', array_map('wc_clean', wc_get_text_attributes($attribute_values[$i])));
                 // Custom attribute - Add attribute to array and set the values
                 $attributes[sanitize_title($attribute_names[$i])] = array('name' => wc_clean($attribute_names[$i]), 'value' => $values, 'position' => $attribute_position[$i], 'is_visible' => $is_visible, 'is_variation' => $is_variation, 'is_taxonomy' => $is_taxonomy);
             }
         }
     }
     if (!function_exists('attributes_cmp')) {
         function attributes_cmp($a, $b)
         {
             if ($a['position'] == $b['position']) {
                 return 0;
             }
             return $a['position'] < $b['position'] ? -1 : 1;
         }
     }
     uasort($attributes, 'attributes_cmp');
     update_post_meta($post_id, '_product_attributes', $attributes);
     // Sales and prices
     if (in_array($product_type, array('variable', 'grouped'))) {
         // Variable and grouped products have no prices
         update_post_meta($post_id, '_regular_price', '');
         update_post_meta($post_id, '_sale_price', '');
         update_post_meta($post_id, '_sale_price_dates_from', '');
         update_post_meta($post_id, '_sale_price_dates_to', '');
         update_post_meta($post_id, '_price', '');
     } else {
         $date_from = isset($_POST['_sale_price_dates_from']) ? wc_clean($_POST['_sale_price_dates_from']) : '';
         $date_to = isset($_POST['_sale_price_dates_to']) ? wc_clean($_POST['_sale_price_dates_to']) : '';
         // Dates
         if ($date_from) {
             update_post_meta($post_id, '_sale_price_dates_from', strtotime($date_from));
         } else {
             update_post_meta($post_id, '_sale_price_dates_from', '');
         }
         if ($date_to) {
             update_post_meta($post_id, '_sale_price_dates_to', strtotime($date_to));
         } else {
             update_post_meta($post_id, '_sale_price_dates_to', '');
         }
         if ($date_to && !$date_from) {
             $date_from = date('Y-m-d');
             update_post_meta($post_id, '_sale_price_dates_from', strtotime($date_from));
         }
         // Update price if on sale
         if ('' !== $_POST['_sale_price'] && '' == $date_to && '' == $date_from) {
             update_post_meta($post_id, '_price', wc_format_decimal($_POST['_sale_price']));
         } else {
             update_post_meta($post_id, '_price', $_POST['_regular_price'] === '' ? '' : wc_format_decimal($_POST['_regular_price']));
         }
         if ('' !== $_POST['_sale_price'] && $date_from && strtotime($date_from) <= strtotime('NOW', current_time('timestamp'))) {
             update_post_meta($post_id, '_price', wc_format_decimal($_POST['_sale_price']));
         }
         if ($date_to && strtotime($date_to) < strtotime('NOW', current_time('timestamp'))) {
             update_post_meta($post_id, '_price', $_POST['_regular_price'] === '' ? '' : wc_format_decimal($_POST['_regular_price']));
             update_post_meta($post_id, '_sale_price_dates_from', '');
             update_post_meta($post_id, '_sale_price_dates_to', '');
         }
     }
     // Update parent if grouped so price sorting works and stays in sync with the cheapest child
     if ($post->post_parent > 0 || 'grouped' == $product_type || $_POST['previous_parent_id'] > 0) {
         $clear_parent_ids = array();
         if ($post->post_parent > 0) {
             $clear_parent_ids[] = $post->post_parent;
         }
         if ('grouped' == $product_type) {
             $clear_parent_ids[] = $post_id;
         }
         if ($_POST['previous_parent_id'] > 0) {
             $clear_parent_ids[] = absint($_POST['previous_parent_id']);
         }
         if (!empty($clear_parent_ids)) {
             foreach ($clear_parent_ids as $clear_id) {
                 $children_by_price = get_posts(array('post_parent' => $clear_id, 'orderby' => 'meta_value_num', 'order' => 'asc', 'meta_key' => '_price', 'posts_per_page' => 1, 'post_type' => 'product', 'fields' => 'ids'));
                 if ($children_by_price) {
                     foreach ($children_by_price as $child) {
                         $child_price = get_post_meta($child, '_price', true);
                         update_post_meta($clear_id, '_price', $child_price);
                     }
                 }
                 wc_delete_product_transients($clear_id);
             }
         }
     }
     // Sold Individually
     if (!empty($_POST['_sold_individually'])) {
         update_post_meta($post_id, '_sold_individually', 'yes');
     } else {
         update_post_meta($post_id, '_sold_individually', '');
     }
     // Stock Data
     if ('yes' === get_option('woocommerce_manage_stock')) {
         $manage_stock = 'no';
         $backorders = 'no';
         $stock_status = wc_clean($_POST['_stock_status']);
         if ('external' === $product_type) {
             $stock_status = 'instock';
         } elseif ('variable' === $product_type) {
             // Stock status is always determined by children so sync later
             $stock_status = '';
             if (!empty($_POST['_manage_stock'])) {
                 $manage_stock = 'yes';
                 $backorders = wc_clean($_POST['_backorders']);
             }
         } elseif ('grouped' !== $product_type && !empty($_POST['_manage_stock'])) {
             $manage_stock = 'yes';
             $backorders = wc_clean($_POST['_backorders']);
         }
         update_post_meta($post_id, '_manage_stock', $manage_stock);
         update_post_meta($post_id, '_backorders', $backorders);
         if ($stock_status) {
             wc_update_product_stock_status($post_id, $stock_status);
         }
         if (!empty($_POST['_manage_stock'])) {
             wc_update_product_stock($post_id, wc_stock_amount($_POST['_stock']));
         } else {
             update_post_meta($post_id, '_stock', '');
         }
     } else {
         wc_update_product_stock_status($post_id, wc_clean($_POST['_stock_status']));
     }
     // Cross sells and upsells
     $upsells = isset($_POST['upsell_ids']) ? array_filter(array_map('intval', explode(',', $_POST['upsell_ids']))) : array();
     $crosssells = isset($_POST['crosssell_ids']) ? array_filter(array_map('intval', explode(',', $_POST['crosssell_ids']))) : array();
     update_post_meta($post_id, '_upsell_ids', $upsells);
     update_post_meta($post_id, '_crosssell_ids', $crosssells);
     // Downloadable options
     if ('yes' == $is_downloadable) {
         $_download_limit = absint($_POST['_download_limit']);
         if (!$_download_limit) {
             $_download_limit = '';
             // 0 or blank = unlimited
         }
         $_download_expiry = absint($_POST['_download_expiry']);
         if (!$_download_expiry) {
             $_download_expiry = '';
             // 0 or blank = unlimited
         }
         // file paths will be stored in an array keyed off md5(file path)
         $files = array();
         if (isset($_POST['_wc_file_urls'])) {
             $file_names = isset($_POST['_wc_file_names']) ? $_POST['_wc_file_names'] : array();
             $file_urls = isset($_POST['_wc_file_urls']) ? array_map('trim', $_POST['_wc_file_urls']) : array();
             $file_url_size = sizeof($file_urls);
             $allowed_file_types = apply_filters('woocommerce_downloadable_file_allowed_mime_types', get_allowed_mime_types());
             for ($i = 0; $i < $file_url_size; $i++) {
                 if (!empty($file_urls[$i])) {
                     // Find type and file URL
                     if (0 === strpos($file_urls[$i], 'http')) {
                         $file_is = 'absolute';
                         $file_url = esc_url_raw($file_urls[$i]);
                     } elseif ('[' === substr($file_urls[$i], 0, 1) && ']' === substr($file_urls[$i], -1)) {
                         $file_is = 'shortcode';
                         $file_url = wc_clean($file_urls[$i]);
                     } else {
                         $file_is = 'relative';
                         $file_url = wc_clean($file_urls[$i]);
                     }
                     $file_name = wc_clean($file_names[$i]);
                     $file_hash = md5($file_url);
                     // Validate the file extension
                     if (in_array($file_is, array('absolute', 'relative'))) {
                         $file_type = wp_check_filetype(strtok($file_url, '?'));
                         $parsed_url = parse_url($file_url, PHP_URL_PATH);
                         $extension = pathinfo($parsed_url, PATHINFO_EXTENSION);
                         if (!empty($extension) && !in_array($file_type['type'], $allowed_file_types)) {
                             WC_Admin_Meta_Boxes::add_error(sprintf(__('The downloadable file %s cannot be used as it does not have an allowed file type. Allowed types include: %s', 'woocommerce'), '<code>' . basename($file_url) . '</code>', '<code>' . implode(', ', array_keys($allowed_file_types)) . '</code>'));
                             continue;
                         }
                     }
                     // Validate the file exists
                     if ('relative' === $file_is) {
                         $_file_url = $file_url;
                         if ('..' === substr($file_url, 0, 2) || '/' !== substr($file_url, 0, 1)) {
                             $_file_url = realpath(ABSPATH . $file_url);
                         }
                         if (!apply_filters('woocommerce_downloadable_file_exists', file_exists($_file_url), $file_url)) {
                             WC_Admin_Meta_Boxes::add_error(sprintf(__('The downloadable file %s cannot be used as it does not exist on the server.', 'woocommerce'), '<code>' . $file_url . '</code>'));
                             continue;
                         }
                     }
                     $files[$file_hash] = array('name' => $file_name, 'file' => $file_url);
                 }
             }
         }
         // grant permission to any newly added files on any existing orders for this product prior to saving
         do_action('woocommerce_process_product_file_download_paths', $post_id, 0, $files);
         update_post_meta($post_id, '_downloadable_files', $files);
         update_post_meta($post_id, '_download_limit', $_download_limit);
         update_post_meta($post_id, '_download_expiry', $_download_expiry);
         if (isset($_POST['_download_type'])) {
             update_post_meta($post_id, '_download_type', wc_clean($_POST['_download_type']));
         }
     }
     // Product url
     if ('external' == $product_type) {
         if (isset($_POST['_product_url'])) {
             update_post_meta($post_id, '_product_url', esc_url_raw($_POST['_product_url']));
         }
         if (isset($_POST['_button_text'])) {
             update_post_meta($post_id, '_button_text', wc_clean($_POST['_button_text']));
         }
     }
     // Save variations
     if ('variable' == $product_type) {
         // Deprecated since WooCommerce 2.4.0 in favor to WC_AJAX::save_variations()
         // self::save_variations( $post_id, $post );
         // Update parent if variable so price sorting works and stays in sync with the cheapest child
         WC_Product_Variable::sync($post_id);
     }
     // Update version after saving
     update_post_meta($post_id, '_product_version', WC_VERSION);
     // Do action for product type
     do_action('woocommerce_process_product_meta_' . $product_type, $post_id);
     // Clear cache/transients
     wc_delete_product_transients($post_id);
 }
 /**
  * Load variations via AJAX
  */
 public static function load_variations()
 {
     ob_start();
     check_ajax_referer('load-variations', 'security');
     // Check permissions again and make sure we have what we need
     if (!current_user_can('edit_products') || empty($_POST['product_id']) || empty($_POST['attributes'])) {
         die(-1);
     }
     global $post;
     $product_id = absint($_POST['product_id']);
     $post = get_post($product_id);
     // Set $post global so its available like within the admin screens
     $per_page = !empty($_POST['per_page']) ? absint($_POST['per_page']) : 10;
     $page = !empty($_POST['page']) ? absint($_POST['page']) : 1;
     // Get attributes
     $attributes = array();
     foreach ($_POST['attributes'] as $key => $value) {
         $attributes[wc_clean($key)] = array_map('wc_clean', $value);
     }
     // Get tax classes
     $tax_classes = WC_Tax::get_tax_classes();
     $tax_class_options = array();
     $tax_class_options[''] = __('Standard', 'woocommerce');
     if (!empty($tax_classes)) {
         foreach ($tax_classes as $class) {
             $tax_class_options[sanitize_title($class)] = esc_attr($class);
         }
     }
     // Set backorder options
     $backorder_options = array('no' => __('Do not allow', 'woocommerce'), 'notify' => __('Allow, but notify customer', 'woocommerce'), 'yes' => __('Allow', 'woocommerce'));
     // set stock status options
     $stock_status_options = array('instock' => __('In stock', 'woocommerce'), 'outofstock' => __('Out of stock', 'woocommerce'));
     $parent_data = array('id' => $product_id, 'attributes' => $attributes, 'tax_class_options' => $tax_class_options, 'sku' => get_post_meta($product_id, '_sku', true), 'weight' => wc_format_localized_decimal(get_post_meta($product_id, '_weight', true)), 'length' => wc_format_localized_decimal(get_post_meta($product_id, '_length', true)), 'width' => wc_format_localized_decimal(get_post_meta($product_id, '_width', true)), 'height' => wc_format_localized_decimal(get_post_meta($product_id, '_height', true)), 'tax_class' => get_post_meta($product_id, '_tax_class', true), 'backorder_options' => $backorder_options, 'stock_status_options' => $stock_status_options);
     if (!$parent_data['weight']) {
         $parent_data['weight'] = wc_format_localized_decimal(0);
     }
     if (!$parent_data['length']) {
         $parent_data['length'] = wc_format_localized_decimal(0);
     }
     if (!$parent_data['width']) {
         $parent_data['width'] = wc_format_localized_decimal(0);
     }
     if (!$parent_data['height']) {
         $parent_data['height'] = wc_format_localized_decimal(0);
     }
     // Get variations
     $args = array('post_type' => 'product_variation', 'post_status' => array('private', 'publish'), 'posts_per_page' => $per_page, 'paged' => $page, 'orderby' => 'ID', 'order' => 'DESC', 'post_parent' => $product_id);
     $variations = get_posts($args);
     $loop = 0;
     if ($variations) {
         foreach ($variations as $variation) {
             $variation_id = absint($variation->ID);
             $variation_meta = get_post_meta($variation_id);
             $variation_data = array();
             $shipping_classes = get_the_terms($variation_id, 'product_shipping_class');
             $variation_fields = array('_sku' => '', '_stock' => '', '_regular_price' => '', '_sale_price' => '', '_weight' => '', '_length' => '', '_width' => '', '_height' => '', '_download_limit' => '', '_download_expiry' => '', '_downloadable_files' => '', '_downloadable' => '', '_virtual' => '', '_thumbnail_id' => '', '_sale_price_dates_from' => '', '_sale_price_dates_to' => '', '_manage_stock' => '', '_stock_status' => '', '_backorders' => null, '_tax_class' => null, '_variation_description' => '');
             foreach ($variation_fields as $field => $value) {
                 $variation_data[$field] = isset($variation_meta[$field][0]) ? maybe_unserialize($variation_meta[$field][0]) : $value;
             }
             // Add the variation attributes
             foreach ($variation_meta as $key => $value) {
                 if (0 !== strpos($key, 'attribute_')) {
                     continue;
                 }
                 /**
                  * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute.
                  * Attempt to get full version of the text attribute from the parent.
                  */
                 if (sanitize_title($value[0]) === $value[0] && version_compare(get_post_meta($product_id, '_product_version', true), '2.4.0', '<')) {
                     foreach ($attributes as $attribute) {
                         if ($key !== 'attribute_' . sanitize_title($attribute['name'])) {
                             continue;
                         }
                         $text_attributes = wc_get_text_attributes($attribute['value']);
                         foreach ($text_attributes as $text_attribute) {
                             if (sanitize_title($text_attribute) === $value[0]) {
                                 $value[0] = $text_attribute;
                             }
                         }
                     }
                 }
                 $variation_data[$key] = $value[0];
             }
             // Formatting
             $variation_data['_regular_price'] = wc_format_localized_price($variation_data['_regular_price']);
             $variation_data['_sale_price'] = wc_format_localized_price($variation_data['_sale_price']);
             $variation_data['_weight'] = wc_format_localized_decimal($variation_data['_weight']);
             $variation_data['_length'] = wc_format_localized_decimal($variation_data['_length']);
             $variation_data['_width'] = wc_format_localized_decimal($variation_data['_width']);
             $variation_data['_height'] = wc_format_localized_decimal($variation_data['_height']);
             $variation_data['_thumbnail_id'] = absint($variation_data['_thumbnail_id']);
             $variation_data['image'] = $variation_data['_thumbnail_id'] ? wp_get_attachment_thumb_url($variation_data['_thumbnail_id']) : '';
             $variation_data['shipping_class'] = $shipping_classes && !is_wp_error($shipping_classes) ? current($shipping_classes)->term_id : '';
             // Stock BW compat
             if ('' !== $variation_data['_stock']) {
                 $variation_data['_manage_stock'] = 'yes';
             }
             include 'admin/meta-boxes/views/html-variation-admin.php';
             $loop++;
         }
     }
     die;
 }
/**
 * Get attibutes/data for an individual variation from the database and maintain it's integrity.
 * @since  2.4.0
 * @param  int $variation_id
 * @return array
 */
function wc_get_product_variation_attributes($variation_id)
{
    // Build variation data from meta
    $all_meta = get_post_meta($variation_id);
    $parent_id = wp_get_post_parent_id($variation_id);
    $parent_attributes = array_filter((array) get_post_meta($parent_id, '_product_attributes', true));
    $found_parent_attributes = array();
    $variation_attributes = array();
    // Compare to parent variable product attributes and ensure they match
    foreach ($parent_attributes as $attribute_name => $options) {
        if (!empty($options['is_variation'])) {
            $attribute = 'attribute_' . sanitize_title($attribute_name);
            $found_parent_attributes[] = $attribute;
            if (!array_key_exists($attribute, $variation_attributes)) {
                $variation_attributes[$attribute] = '';
                // Add it - 'any' will be asumed
            }
        }
    }
    // Get the variation attributes from meta
    foreach ($all_meta as $name => $value) {
        // Only look at valid attribute meta, and also compare variation level attributes and remove any which do not exist at parent level
        if (0 !== strpos($name, 'attribute_') || !in_array($name, $found_parent_attributes)) {
            unset($variation_attributes[$name]);
            continue;
        }
        /**
         * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute.
         * Attempt to get full version of the text attribute from the parent.
         */
        if (sanitize_title($value[0]) === $value[0] && version_compare(get_post_meta($parent_id, '_product_version', true), '2.4.0', '<')) {
            foreach ($parent_attributes as $attribute) {
                if ($name !== 'attribute_' . sanitize_title($attribute['name'])) {
                    continue;
                }
                $text_attributes = wc_get_text_attributes($attribute['value']);
                foreach ($text_attributes as $text_attribute) {
                    if (sanitize_title($text_attribute) === $value[0]) {
                        $value[0] = $text_attribute;
                        break;
                    }
                }
            }
        }
        $variation_attributes[$name] = $value[0];
    }
    return $variation_attributes;
}
 /**
  * Sync the variable product's attributes with the variations.
  */
 public static function sync_attributes($product_id, $children = false)
 {
     if (!$children) {
         $children = get_posts(array('post_parent' => $product_id, 'posts_per_page' => -1, 'post_type' => 'product_variation', 'fields' => 'ids', 'post_status' => 'any'));
     }
     /**
      * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute.
      * Attempt to get full version of the text attribute from the parent and UPDATE meta.
      */
     if (version_compare(get_post_meta($product_id, '_product_version', true), '2.4.0', '<')) {
         $parent_attributes = array_filter((array) get_post_meta($product_id, '_product_attributes', true));
         foreach ($children as $child_id) {
             $all_meta = get_post_meta($child_id);
             foreach ($all_meta as $name => $value) {
                 if (0 !== strpos($name, 'attribute_')) {
                     continue;
                 }
                 if (sanitize_title($value[0]) === $value[0]) {
                     foreach ($parent_attributes as $attribute) {
                         if ($name !== 'attribute_' . sanitize_title($attribute['name'])) {
                             continue;
                         }
                         $text_attributes = wc_get_text_attributes($attribute['value']);
                         foreach ($text_attributes as $text_attribute) {
                             if (sanitize_title($text_attribute) === $value[0]) {
                                 update_post_meta($child_id, $name, $text_attribute);
                                 break;
                             }
                         }
                     }
                 }
             }
         }
     }
 }
    // Only deal with attributes that are variations
    if (!$attribute['is_variation'] || 'false' === $attribute['is_variation']) {
        continue;
    }
    // Get current value for variation (if set)
    $variation_selected_value = isset($variation_data['attribute_' . sanitize_title($attribute['name'])]) ? $variation_data['attribute_' . sanitize_title($attribute['name'])] : '';
    // Name will be something like attribute_pa_color
    echo '<select name="attribute_' . sanitize_title($attribute['name']) . '[' . $loop . ']"><option value="">' . __('Any', 'woocommerce') . ' ' . esc_html(wc_attribute_label($attribute['name'])) . '&hellip;</option>';
    // Get terms for attribute taxonomy or value if its a custom attribute
    if ($attribute['is_taxonomy']) {
        $post_terms = wp_get_post_terms($parent_data['id'], $attribute['name']);
        foreach ($post_terms as $term) {
            echo '<option ' . selected($variation_selected_value, $term->slug, false) . ' value="' . esc_attr($term->slug) . '">' . esc_html(apply_filters('woocommerce_variation_option_name', $term->name)) . '</option>';
        }
    } else {
        $options = wc_get_text_attributes($attribute['value']);
        foreach ($options as $option) {
            $selected = sanitize_title($variation_selected_value) === $variation_selected_value ? selected($variation_selected_value, sanitize_title($option), false) : selected($variation_selected_value, $option, false);
            echo '<option ' . $selected . ' value="' . esc_attr($option) . '">' . esc_html(apply_filters('woocommerce_variation_option_name', $option)) . '</option>';
        }
    }
    echo '</select>';
}
?>
		<input type="hidden" name="variable_post_id[<?php 
echo $loop;
?>
]" value="<?php 
echo esc_attr($variation_id);
?>
" />
 /**
  * Read attributes from post meta.
  *
  * @param WC_Product
  * @since 2.7.0
  */
 protected function read_attributes(&$product)
 {
     $meta_values = maybe_unserialize(get_post_meta($product->get_id(), '_product_attributes', true));
     if ($meta_values) {
         $attributes = array();
         foreach ($meta_values as $meta_value) {
             if (!empty($meta_value['is_taxonomy'])) {
                 if (!taxonomy_exists($meta_value['name'])) {
                     continue;
                 }
                 $options = wc_get_object_terms($product->get_id(), $meta_value['name'], 'term_id');
             } else {
                 $options = wc_get_text_attributes($meta_value['value']);
             }
             $attribute = new WC_Product_Attribute();
             $attribute->set_id(wc_attribute_taxonomy_id_by_name($meta_value['name']));
             $attribute->set_name($meta_value['name']);
             $attribute->set_options($options);
             $attribute->set_position($meta_value['position']);
             $attribute->set_visible($meta_value['is_visible']);
             $attribute->set_variation($meta_value['is_variation']);
             $attributes[] = $attribute;
         }
         $product->set_attributes($attributes);
     }
 }
Beispiel #9
0
 /**
  * Save attributes via ajax.
  */
 public static function save_attributes()
 {
     check_ajax_referer('save-attributes', 'security');
     if (!current_user_can('edit_products')) {
         die(-1);
     }
     // Get post data
     parse_str($_POST['data'], $data);
     $post_id = absint($_POST['post_id']);
     // Save Attributes
     $attributes = array();
     if (isset($data['attribute_names'])) {
         $attribute_names = array_map('stripslashes', $data['attribute_names']);
         $attribute_values = isset($data['attribute_values']) ? $data['attribute_values'] : array();
         if (isset($data['attribute_visibility'])) {
             $attribute_visibility = $data['attribute_visibility'];
         }
         if (isset($data['attribute_variation'])) {
             $attribute_variation = $data['attribute_variation'];
         }
         $attribute_is_taxonomy = $data['attribute_is_taxonomy'];
         $attribute_position = $data['attribute_position'];
         $attribute_names_max_key = max(array_keys($attribute_names));
         for ($i = 0; $i <= $attribute_names_max_key; $i++) {
             if (empty($attribute_names[$i])) {
                 continue;
             }
             $is_visible = isset($attribute_visibility[$i]) ? 1 : 0;
             $is_variation = isset($attribute_variation[$i]) ? 1 : 0;
             $is_taxonomy = $attribute_is_taxonomy[$i] ? 1 : 0;
             if ($is_taxonomy) {
                 if (isset($attribute_values[$i])) {
                     // Select based attributes - Format values (posted values are slugs)
                     if (is_array($attribute_values[$i])) {
                         $values = array_map('sanitize_title', $attribute_values[$i]);
                         // Text based attributes - Posted values are term names, wp_set_object_terms wants ids or slugs.
                     } else {
                         $values = array();
                         $raw_values = array_map('wc_sanitize_term_text_based', explode(WC_DELIMITER, $attribute_values[$i]));
                         foreach ($raw_values as $value) {
                             $term = get_term_by('name', $value, $attribute_names[$i]);
                             if (!$term) {
                                 $term = wp_insert_term($value, $attribute_names[$i]);
                                 if ($term && !is_wp_error($term)) {
                                     $values[] = $term['term_id'];
                                 }
                             } else {
                                 $values[] = $term->term_id;
                             }
                         }
                     }
                     // Remove empty items in the array
                     $values = array_filter($values, 'strlen');
                 } else {
                     $values = array();
                 }
                 // Update post terms
                 if (taxonomy_exists($attribute_names[$i])) {
                     wp_set_object_terms($post_id, $values, $attribute_names[$i]);
                 }
                 if ($values) {
                     // Add attribute to array, but don't set values
                     $attributes[sanitize_title($attribute_names[$i])] = array('name' => wc_clean($attribute_names[$i]), 'value' => '', 'position' => $attribute_position[$i], 'is_visible' => $is_visible, 'is_variation' => $is_variation, 'is_taxonomy' => $is_taxonomy);
                 }
             } elseif (isset($attribute_values[$i])) {
                 // Text based, possibly separated by pipes (WC_DELIMITER). Preserve line breaks in non-variation attributes.
                 $values = $is_variation ? wc_clean($attribute_values[$i]) : implode("\n", array_map('wc_clean', explode("\n", $attribute_values[$i])));
                 $values = implode(' ' . WC_DELIMITER . ' ', wc_get_text_attributes($values));
                 // Custom attribute - Add attribute to array and set the values
                 $attributes[sanitize_title($attribute_names[$i])] = array('name' => wc_clean($attribute_names[$i]), 'value' => $values, 'position' => $attribute_position[$i], 'is_visible' => $is_visible, 'is_variation' => $is_variation, 'is_taxonomy' => $is_taxonomy);
             }
         }
     }
     if (!function_exists('attributes_cmp')) {
         function attributes_cmp($a, $b)
         {
             if ($a['position'] == $b['position']) {
                 return 0;
             }
             return $a['position'] < $b['position'] ? -1 : 1;
         }
     }
     uasort($attributes, 'attributes_cmp');
     update_post_meta($post_id, '_product_attributes', $attributes);
     die;
 }
 /**
  * Sync the variable product's attributes with the variations.
  */
 public static function sync_attributes($product, $children = false)
 {
     if (!is_a($product, 'WC_Product')) {
         $product = wc_get_product($product);
     }
     /**
      * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute.
      * Attempt to get full version of the text attribute from the parent and UPDATE meta.
      */
     if (version_compare(get_post_meta($product->get_id(), '_product_version', true), '2.4.0', '<')) {
         $parent_attributes = array_filter((array) get_post_meta($product->get_id(), '_product_attributes', true));
         if (!$children) {
             $children = $product->get_children('edit');
         }
         foreach ($children as $child_id) {
             $all_meta = get_post_meta($child_id);
             foreach ($all_meta as $name => $value) {
                 if (0 !== strpos($name, 'attribute_')) {
                     continue;
                 }
                 if (sanitize_title($value[0]) === $value[0]) {
                     foreach ($parent_attributes as $attribute) {
                         if ('attribute_' . sanitize_title($attribute['name']) !== $name) {
                             continue;
                         }
                         $text_attributes = wc_get_text_attributes($attribute['value']);
                         foreach ($text_attributes as $text_attribute) {
                             if (sanitize_title($text_attribute) === $value[0]) {
                                 update_post_meta($child_id, $name, $text_attribute);
                                 break;
                             }
                         }
                     }
                 }
             }
         }
     }
 }
 /**
  * Get formatted variation data with WC < 2.4 back compat and proper formatting of text-based attribute names.
  *
  * @return string
  */
 public function get_formatted_variation_attributes($flat = false)
 {
     $variation_data = $this->get_variation_attributes();
     $attributes = $this->parent->get_attributes();
     $description = array();
     $return = '';
     if (is_array($variation_data)) {
         if (!$flat) {
             $return = '<dl class="variation">';
         }
         foreach ($attributes as $attribute) {
             // Only deal with attributes that are variations
             if (!$attribute['is_variation']) {
                 continue;
             }
             $variation_selected_value = isset($variation_data['attribute_' . sanitize_title($attribute['name'])]) ? $variation_data['attribute_' . sanitize_title($attribute['name'])] : '';
             $description_name = esc_html(wc_attribute_label($attribute['name']));
             $description_value = __('Any', 'woocommerce');
             // Get terms for attribute taxonomy or value if its a custom attribute
             if ($attribute['is_taxonomy']) {
                 $post_terms = wp_get_post_terms($this->id, $attribute['name']);
                 foreach ($post_terms as $term) {
                     if ($variation_selected_value === $term->slug) {
                         $description_value = esc_html(apply_filters('woocommerce_variation_option_name', $term->name));
                     }
                 }
             } else {
                 $options = wc_get_text_attributes($attribute['value']);
                 foreach ($options as $option) {
                     if (sanitize_title($variation_selected_value) === $variation_selected_value) {
                         if ($variation_selected_value !== sanitize_title($option)) {
                             continue;
                         }
                     } else {
                         if ($variation_selected_value !== $option) {
                             continue;
                         }
                     }
                     $description_value = esc_html(apply_filters('woocommerce_variation_option_name', $option));
                 }
             }
             if ($flat) {
                 $description[] = $description_name . ': ' . rawurldecode($description_value);
             } else {
                 $description[] = '<dt>' . $description_name . ':</dt><dd>' . rawurldecode($description_value) . '</dd>';
             }
         }
         if ($flat) {
             $return .= implode(', ', $description);
         } else {
             $return .= implode('', $description);
         }
         if (!$flat) {
             $return .= '</dl>';
         }
     }
     return $return;
 }
 /**
  * Return an array of attributes used for variations, as well as their possible values.
  *
  * @return array of attributes and their available values
  */
 public function get_variation_attributes()
 {
     $variation_attributes = array();
     if (!$this->has_child()) {
         return $variation_attributes;
     }
     $attributes = $this->get_attributes();
     foreach ($attributes as $attribute) {
         if (!$attribute['is_variation']) {
             continue;
         }
         $values = array();
         $attribute_field_name = 'attribute_' . sanitize_title($attribute['name']);
         // Get used values from children variations
         foreach ($this->get_children() as $child_id) {
             $variation = $this->get_child($child_id);
             if (!empty($variation->variation_id)) {
                 if (!$variation->variation_is_visible()) {
                     continue;
                     // Disabled or hidden
                 }
                 $child_variation_attributes = $variation->get_variation_attributes();
                 if (isset($child_variation_attributes[$attribute_field_name])) {
                     $values[] = $child_variation_attributes[$attribute_field_name];
                 }
             }
         }
         // empty value indicates that all options for given attribute are available
         if (in_array('', $values)) {
             $values = $attribute['is_taxonomy'] ? wp_get_post_terms($this->id, $attribute['name'], array('fields' => 'slugs')) : wc_get_text_attributes($attribute['value']);
             // Order custom attributes (non taxonomy) as defined
         } elseif (!$attribute['is_taxonomy']) {
             $text_attributes = wc_get_text_attributes($attribute['value']);
             $assigned_text_attributes = $values;
             $values = array();
             // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute
             if ($assigned_text_attributes === array_map('sanitize_title', $assigned_text_attributes) && version_compare(get_post_meta($this->id, '_product_version', true), '2.4.0', '<')) {
                 $assigned_text_attributes = array_map('sanitize_title', $assigned_text_attributes);
                 foreach ($text_attributes as $text_attribute) {
                     if (in_array(sanitize_title($text_attribute), $assigned_text_attributes)) {
                         $values[] = $text_attribute;
                     }
                 }
             } else {
                 foreach ($text_attributes as $text_attribute) {
                     if (in_array($text_attribute, $assigned_text_attributes)) {
                         $values[] = $text_attribute;
                     }
                 }
             }
         }
         $variation_attributes[$attribute['name']] = array_unique($values);
     }
     return $variation_attributes;
 }
 public function process_matrix_submission()
 {
     global $woocommerce;
     $items = $_POST['order_info'];
     $product_id = $_POST['product_id'];
     $adding_to_cart = wc_get_product($product_id);
     $added_count = 0;
     $failed_count = 0;
     $success_message = '';
     $error_message = '';
     foreach ($items as $item) {
         $q = floatval($item['quantity']) ? floatval($item['quantity']) : 0;
         if ($q) {
             $variation_id = empty($item['variation_id']) ? '' : absint($item['variation_id']);
             $missing_attributes = array();
             //For validation, since 2.4
             $variations = array();
             // Only allow integer variation ID - if its not set, redirect to the product page
             if (empty($variation_id)) {
                 //wc_add_notice( __( 'Please choose product options&hellip;', 'woocommerce' ), 'error' );
                 $failed_count++;
                 continue;
             }
             $attributes = $adding_to_cart->get_attributes();
             $variation = wc_get_product($variation_id);
             // Verify all attributes
             foreach ($attributes as $attribute) {
                 if (!$attribute['is_variation']) {
                     continue;
                 }
                 $taxonomy = 'attribute_' . sanitize_title($attribute['name']);
                 if (isset($item['variation_data'][$taxonomy])) {
                     // Get value from post data
                     if ($attribute['is_taxonomy']) {
                         // Don't use wc_clean as it destroys sanitized characters
                         $value = sanitize_title(stripslashes($item['variation_data'][$taxonomy]));
                     } else {
                         $value = wc_clean(stripslashes($item['variation_data'][$taxonomy]));
                     }
                     // Get valid value from variation
                     $valid_value = $variation->variation_data[$taxonomy];
                     // Allow if valid
                     if ('' === $valid_value || $valid_value === $value) {
                         // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute
                         if (!$attribute['is_taxonomy']) {
                             if ($value === sanitize_title($value) && version_compare(get_post_meta($product_id, '_product_version', true), '2.4.0', '<')) {
                                 $text_attributes = wc_get_text_attributes($attribute['value']);
                                 foreach ($text_attributes as $text_attribute) {
                                     if (sanitize_title($text_attribute) === $value) {
                                         $value = $text_attribute;
                                         break;
                                     }
                                 }
                             }
                         }
                         $variations[$taxonomy] = $value;
                         continue;
                     }
                 } else {
                     $missing_attributes[] = wc_attribute_label($attribute['name']);
                 }
             }
             if (empty($missing_attributes)) {
                 // Add to cart validation
                 $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $q, $variation_id, $variations);
                 if ($passed_validation) {
                     $added = WC()->cart->add_to_cart($product_id, $q, $variation_id, $variations);
                 }
             } else {
                 $failed_count++;
                 continue;
             }
             if ($added) {
                 $added_count++;
             } else {
                 $failed_count++;
             }
         }
     }
     if ($added_count) {
         woocommerce_bulk_variations_add_to_cart_message($added_count);
     }
     if ($failed_count) {
         WC_Bulk_Variations_Compatibility::wc_add_error(sprintf(__('Unable to add %s to the cart.  Please check your quantities and make sure the item is available and in stock', 'wc_bulk_variations'), $failed_count));
     }
     if (!$added_count && !$failed_count) {
         WC_Bulk_Variations_Compatibility::wc_add_error(__('No product quantities entered.', 'wc_bulk_variations'));
     }
     // If we added the products to the cart we can now do a redirect, otherwise just continue loading the page to show errors
     if ($failed_count === 0 && wc_notice_count('error') === 0) {
         // If has custom URL redirect there
         if ($url = apply_filters('woocommerce_add_to_cart_redirect', false)) {
             wp_safe_redirect($url);
             exit;
         } elseif (get_option('woocommerce_cart_redirect_after_add') === 'yes') {
             wp_safe_redirect(WC()->cart->get_cart_url());
             exit;
         }
     }
 }
 /**
  * Prepare attributes for save.
  * @return array
  */
 public static function prepare_attributes($data = false)
 {
     $attributes = array();
     if (!$data) {
         $data = $_POST;
     }
     if (isset($data['attribute_names'], $data['attribute_values'])) {
         $attribute_names = $data['attribute_names'];
         $attribute_values = $data['attribute_values'];
         $attribute_visibility = isset($data['attribute_visibility']) ? $data['attribute_visibility'] : array();
         $attribute_variation = isset($data['attribute_variation']) ? $data['attribute_variation'] : array();
         $attribute_position = $data['attribute_position'];
         $attribute_names_max_key = max(array_keys($attribute_names));
         for ($i = 0; $i <= $attribute_names_max_key; $i++) {
             if (empty($attribute_names[$i]) || !isset($attribute_values[$i])) {
                 continue;
             }
             $attribute_name = wc_clean($attribute_names[$i]);
             $attribute_id = wc_attribute_taxonomy_id_by_name($attribute_name);
             $options = isset($attribute_values[$i]) ? $attribute_values[$i] : '';
             if (is_array($options)) {
                 // Term ids sent as array.
                 $options = wp_parse_id_list($options);
             } else {
                 // Terms or text sent in textarea.
                 $options = 0 < $attribute_id ? wc_sanitize_textarea(wc_sanitize_term_text_based($options)) : wc_sanitize_textarea($options);
                 $options = wc_get_text_attributes($options);
             }
             $attribute = new WC_Product_Attribute();
             $attribute->set_id($attribute_id);
             $attribute->set_name($attribute_name);
             $attribute->set_options($options);
             $attribute->set_position($attribute_position[$i]);
             $attribute->set_visible(isset($attribute_visibility[$i]));
             $attribute->set_variation(isset($attribute_variation[$i]));
             $attributes[] = $attribute;
         }
     }
     return $attributes;
 }
 /**
  * Get method returns variation meta data if set, otherwise in most cases the data from the parent.
  *
  * @param string $key
  * @return mixed
  */
 public function __get($key)
 {
     if (in_array($key, array_keys($this->variation_level_meta_data))) {
         $value = get_post_meta($this->variation_id, '_' . $key, true);
         if ('' === $value) {
             $value = $this->variation_level_meta_data[$key];
         }
     } elseif (in_array($key, array_keys($this->variation_inherited_meta_data))) {
         $value = metadata_exists('post', $this->variation_id, '_' . $key) ? get_post_meta($this->variation_id, '_' . $key, true) : get_post_meta($this->id, '_' . $key, true);
         // Handle meta data keys which can be empty at variation level to cause inheritance
         if ('' === $value && in_array($key, array('sku', 'weight', 'length', 'width', 'height'))) {
             $value = get_post_meta($this->id, '_' . $key, true);
         }
         if ('' === $value) {
             $value = $this->variation_inherited_meta_data[$key];
         }
     } elseif ('variation_data' === $key) {
         $all_meta = get_post_meta($this->variation_id);
         // The variation data array
         $this->variation_data = array();
         // Get the variation attributes from meta
         foreach ($all_meta as $name => $value) {
             if (0 !== strpos($name, 'attribute_')) {
                 continue;
             }
             /**
              * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute.
              * Attempt to get full version of the text attribute from the parent.
              */
             if (sanitize_title($value[0]) === $value[0] && version_compare(get_post_meta($this->id, '_product_version', true), '2.4.0', '<')) {
                 $attributes = $this->parent->get_attributes();
                 foreach ($attributes as $attribute) {
                     if ($name !== 'attribute_' . sanitize_title($attribute['name'])) {
                         continue;
                     }
                     $text_attributes = wc_get_text_attributes($attribute['value']);
                     foreach ($text_attributes as $text_attribute) {
                         if (sanitize_title($text_attribute) === $value[0]) {
                             $value[0] = $text_attribute;
                         }
                     }
                 }
             }
             $this->variation_data[$name] = $value[0];
         }
         return $this->variation_data;
     } elseif ('variation_has_stock' === $key) {
         return $this->managing_stock();
     } else {
         $value = metadata_exists('post', $this->variation_id, '_' . $key) ? get_post_meta($this->variation_id, '_' . $key, true) : parent::__get($key);
     }
     return $value;
 }
function bf_wc_variations_custom($thepostid, $customfield)
{
    global $variation_data, $post;
    $post = get_post($thepostid);
    $variation_data = new WC_Product_Variation($post);
    $variation_data->get_variation_attributes();
    $variation_data = (array) $variation_data;
    echo '<pre>';
    print_r($variation_data);
    echo '</pre>';
    extract($variation_data);
    ?>
    <div class="woocommerce_variation wc-metabox closed">
        <h3>
            <a href="#" class="remove_variation delete" rel="<?php 
    echo esc_attr($variation_id);
    ?>
"><?php 
    _e('Remove', 'woocommerce');
    ?>
</a>
            <div class="handlediv" title="<?php 
    esc_attr_e('Click to toggle', 'woocommerce');
    ?>
"></div>
            <div class="tips sort" data-tip="<?php 
    esc_attr_e('Drag and drop, or click to set menu order manually', 'woocommerce');
    ?>
"></div>
            <strong>#<?php 
    echo esc_html($variation_id);
    ?>
: </strong>
            <?php 
    if (isset($parent_data)) {
        foreach ($parent_data['attributes'] as $attribute) {
            // Only deal with attributes that are variations
            if (!$attribute['is_variation']) {
                continue;
            }
            // Get current value for variation (if set)
            $variation_selected_value = isset($variation_data['attribute_' . sanitize_title($attribute['name'])]) ? $variation_data['attribute_' . sanitize_title($attribute['name'])] : '';
            // Name will be something like attribute_pa_color
            echo '<select name="attribute_' . sanitize_title($attribute['name']) . '[' . $loop . ']"><option value="">' . __('Any', 'woocommerce') . ' ' . esc_html(wc_attribute_label($attribute['name'])) . '&hellip;</option>';
            // Get terms for attribute taxonomy or value if its a custom attribute
            if ($attribute['is_taxonomy']) {
                $post_terms = wp_get_post_terms($parent_data['id'], $attribute['name']);
                foreach ($post_terms as $term) {
                    echo '<option ' . selected($variation_selected_value, $term->slug, false) . ' value="' . esc_attr($term->slug) . '">' . apply_filters('woocommerce_variation_option_name', esc_html($term->name)) . '</option>';
                }
            } else {
                $options = wc_get_text_attributes($attribute['value']);
                foreach ($options as $option) {
                    $selected = sanitize_title($variation_selected_value) === $variation_selected_value ? selected($variation_selected_value, sanitize_title($option), false) : selected($variation_selected_value, $option, false);
                    echo '<option ' . $selected . ' value="' . esc_attr($option) . '">' . esc_html(apply_filters('woocommerce_variation_option_name', $option)) . '</option>';
                }
            }
            echo '</select>';
        }
    }
    ?>
            <input type="hidden" name="variable_post_id[<?php 
    echo $loop;
    ?>
]" value="<?php 
    echo esc_attr($variation_id);
    ?>
" />
            <input type="hidden" class="variation_menu_order" name="variation_menu_order[<?php 
    echo $loop;
    ?>
]" value="<?php 
    echo absint($menu_order);
    ?>
" />
        </h3>
        <div class="woocommerce_variable_attributes wc-metabox-content" style="display: none;">
            <div class="data">
                <p class="form-row form-row-first upload_image">
                    <a href="#" class="upload_image_button tips <?php 
    if ($_thumbnail_id > 0) {
        echo 'remove';
    }
    ?>
" data-tip="<?php 
    if ($_thumbnail_id > 0) {
        echo __('Remove this image', 'woocommerce');
    } else {
        echo __('Upload an image', 'woocommerce');
    }
    ?>
" rel="<?php 
    echo esc_attr($variation_id);
    ?>
"><img src="<?php 
    if (!empty($image)) {
        echo esc_attr($image);
    } else {
        echo esc_attr(wc_placeholder_img_src());
    }
    ?>
" /><input type="hidden" name="upload_image_id[<?php 
    echo $loop;
    ?>
]" class="upload_image_id" value="<?php 
    echo esc_attr($_thumbnail_id);
    ?>
" /></a>
                </p>
                <?php 
    if (wc_product_sku_enabled()) {
        ?>
                    <p class="sku form-row form-row-last">
                        <label><?php 
        _e('SKU', 'woocommerce');
        ?>
: <a class="tips" data-tip="<?php 
        esc_attr_e('Enter a SKU for this variation or leave blank to use the parent product SKU.', 'woocommerce');
        ?>
" href="#">[?]</a></label>
                        <input type="text" size="5" name="variable_sku[<?php 
        echo $loop;
        ?>
]" value="<?php 
        if (isset($_sku)) {
            echo esc_attr($_sku);
        }
        ?>
" placeholder="<?php 
        echo esc_attr($parent_data['sku']);
        ?>
" />
                    </p>
                <?php 
    } else {
        ?>
                    <input type="hidden" name="variable_sku[<?php 
        echo $loop;
        ?>
]" value="<?php 
        if (isset($_sku)) {
            echo esc_attr($_sku);
        }
        ?>
" />
                <?php 
    }
    ?>

                <p class="form-row form-row-full options">
                    <label><input type="checkbox" class="checkbox" name="variable_enabled[<?php 
    echo $loop;
    ?>
]" <?php 
    checked($variation->post_status, 'publish');
    ?>
 /> <?php 
    _e('Enabled', 'woocommerce');
    ?>
</label>

                    <label><input type="checkbox" class="checkbox variable_is_downloadable" name="variable_is_downloadable[<?php 
    echo $loop;
    ?>
]" <?php 
    checked(isset($_downloadable) ? $_downloadable : '', 'yes');
    ?>
 /> <?php 
    _e('Downloadable', 'woocommerce');
    ?>
 <a class="tips" data-tip="<?php 
    esc_attr_e('Enable this option if access is given to a downloadable file upon purchase of a product', 'woocommerce');
    ?>
" href="#">[?]</a></label>

                    <label><input type="checkbox" class="checkbox variable_is_virtual" name="variable_is_virtual[<?php 
    echo $loop;
    ?>
]" <?php 
    checked(isset($_virtual) ? $_virtual : '', 'yes');
    ?>
 /> <?php 
    _e('Virtual', 'woocommerce');
    ?>
 <a class="tips" data-tip="<?php 
    esc_attr_e('Enable this option if a product is not shipped or there is no shipping cost', 'woocommerce');
    ?>
" href="#">[?]</a></label>

                    <?php 
    if (get_option('woocommerce_manage_stock') == 'yes') {
        ?>

                        <label><input type="checkbox" class="checkbox variable_manage_stock" name="variable_manage_stock[<?php 
        echo $loop;
        ?>
]" <?php 
        checked(isset($_manage_stock) ? $_manage_stock : '', 'yes');
        ?>
 /> <?php 
        _e('Manage stock?', 'woocommerce');
        ?>
 <a class="tips" data-tip="<?php 
        esc_attr_e('Enable this option to enable stock management at variation level', 'woocommerce');
        ?>
" href="#">[?]</a></label>

                    <?php 
    }
    ?>

                    <?php 
    do_action('woocommerce_variation_options', $loop, $variation_data, $variation);
    ?>
                </p>

                <div class="variable_pricing">
                    <p class="form-row form-row-first">
                        <label><?php 
    echo __('Regular Price:', 'woocommerce') . ' (' . get_woocommerce_currency_symbol() . ')';
    ?>
</label>
                        <input type="text" size="5" name="variable_regular_price[<?php 
    echo $loop;
    ?>
]" value="<?php 
    if (isset($_regular_price)) {
        echo esc_attr($_regular_price);
    }
    ?>
" class="wc_input_price" placeholder="<?php 
    esc_attr_e('Variation price (required)', 'woocommerce');
    ?>
" />
                    </p>
                    <p class="form-row form-row-last">
                        <label><?php 
    echo __('Sale Price:', 'woocommerce') . ' (' . get_woocommerce_currency_symbol() . ')';
    ?>
 <a href="#" class="sale_schedule"><?php 
    _e('Schedule', 'woocommerce');
    ?>
</a><a href="#" class="cancel_sale_schedule" style="display:none"><?php 
    _e('Cancel schedule', 'woocommerce');
    ?>
</a></label>
                        <input type="text" size="5" name="variable_sale_price[<?php 
    echo $loop;
    ?>
]" value="<?php 
    if (isset($_sale_price)) {
        echo esc_attr($_sale_price);
    }
    ?>
" class="wc_input_price" />
                    </p>

                    <div class="sale_price_dates_fields" style="display: none">
                        <p class="form-row form-row-first">
                            <label><?php 
    _e('Sale start date:', 'woocommerce');
    ?>
</label>
                            <input type="text" class="sale_price_dates_from" name="variable_sale_price_dates_from[<?php 
    echo $loop;
    ?>
]" value="<?php 
    echo !empty($_sale_price_dates_from) ? date_i18n('Y-m-d', $_sale_price_dates_from) : '';
    ?>
" placeholder="<?php 
    echo esc_attr_x('From&hellip;', 'placeholder', 'woocommerce');
    ?>
 YYYY-MM-DD" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" />
                        </p>
                        <p class="form-row form-row-last">
                            <label><?php 
    _e('Sale end date:', 'woocommerce');
    ?>
</label>
                            <input type="text" class="sale_price_dates_to" name="variable_sale_price_dates_to[<?php 
    echo $loop;
    ?>
]" value="<?php 
    echo !empty($_sale_price_dates_to) ? date_i18n('Y-m-d', $_sale_price_dates_to) : '';
    ?>
" placeholder="<?php 
    echo esc_attr_x('To&hellip;', 'placeholder', 'woocommerce');
    ?>
 YYYY-MM-DD" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" />
                        </p>
                    </div>
                </div>

                <?php 
    if ('yes' == get_option('woocommerce_manage_stock')) {
        ?>
                    <div class="show_if_variation_manage_stock" style="display: none;">
                        <p class="form-row form-row-first">
                            <label><?php 
        _e('Stock Qty:', 'woocommerce');
        ?>
 <a class="tips" data-tip="<?php 
        esc_attr_e('Enter a quantity to enable stock management at variation level, or leave blank to use the parent product\'s options.', 'woocommerce');
        ?>
" href="#">[?]</a></label>
                            <input type="number" size="5" name="variable_stock[<?php 
        echo $loop;
        ?>
]" value="<?php 
        if (isset($_stock)) {
            echo esc_attr(wc_stock_amount($_stock));
        }
        ?>
" step="any" />
                        </p>
                        <p class="form-row form-row-last">
                            <label><?php 
        _e('Allow Backorders?', 'woocommerce');
        ?>
</label>
                            <select name="variable_backorders[<?php 
        echo $loop;
        ?>
]">
                                <?php 
        foreach ($parent_data['backorder_options'] as $key => $value) {
            echo '<option value="' . esc_attr($key) . '" ' . selected($key === $_backorders, true, false) . '>' . esc_html($value) . '</option>';
        }
        ?>
                            </select>
                        </p>
                    </div>
                <?php 
    }
    ?>

                <div class="">
                    <p class="form-row form-row-full">
                        <label><?php 
    _e('Stock status', 'woocommerce');
    ?>
 <a class="tips" data-tip="<?php 
    esc_attr_e('Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce');
    ?>
" href="#">[?]</a></label>
                        <select name="variable_stock_status[<?php 
    echo $loop;
    ?>
]">
                            <?php 
    foreach ($parent_data['stock_status_options'] as $key => $value) {
        echo '<option value="' . esc_attr($key === $_stock_status ? '' : $key) . '" ' . selected($key === $_stock_status, true, false) . '>' . esc_html($value) . '</option>';
    }
    ?>
                        </select>
                    </p>
                </div>

                <?php 
    if (wc_product_weight_enabled() || wc_product_dimensions_enabled()) {
        ?>
                    <div>
                        <?php 
        if (wc_product_weight_enabled()) {
            ?>
                            <p class="form-row hide_if_variation_virtual form-row-first">
                                <label><?php 
            echo __('Weight', 'woocommerce') . ' (' . esc_html(get_option('woocommerce_weight_unit')) . '):';
            ?>
 <a class="tips" data-tip="<?php 
            esc_attr_e('Enter a weight for this variation or leave blank to use the parent product weight.', 'woocommerce');
            ?>
" href="#">[?]</a></label>
                                <input type="text" size="5" name="variable_weight[<?php 
            echo $loop;
            ?>
]" value="<?php 
            if (isset($_weight)) {
                echo esc_attr($_weight);
            }
            ?>
" placeholder="<?php 
            echo esc_attr($parent_data['weight']);
            ?>
" class="wc_input_decimal" />
                            </p>
                        <?php 
        } else {
            ?>
                            <p>&nbsp;</p>
                        <?php 
        }
        ?>
                        <?php 
        if (wc_product_dimensions_enabled()) {
            ?>
                            <p class="form-row dimensions_field hide_if_variation_virtual form-row-last">
                                <label for="product_length"><?php 
            echo __('Dimensions (L&times;W&times;H)', 'woocommerce') . ' (' . esc_html(get_option('woocommerce_dimension_unit')) . '):';
            ?>
</label>
                                <input id="product_length" class="input-text wc_input_decimal" size="6" type="text" name="variable_length[<?php 
            echo $loop;
            ?>
]" value="<?php 
            if (isset($_length)) {
                echo esc_attr($_length);
            }
            ?>
" placeholder="<?php 
            echo esc_attr($parent_data['length']);
            ?>
" />
                                <input class="input-text wc_input_decimal" size="6" type="text" name="variable_width[<?php 
            echo $loop;
            ?>
]" value="<?php 
            if (isset($_width)) {
                echo esc_attr($_width);
            }
            ?>
" placeholder="<?php 
            echo esc_attr($parent_data['width']);
            ?>
" />
                                <input class="input-text wc_input_decimal last" size="6" type="text" name="variable_height[<?php 
            echo $loop;
            ?>
]" value="<?php 
            if (isset($_height)) {
                echo esc_attr($_height);
            }
            ?>
" placeholder="<?php 
            echo esc_attr($parent_data['height']);
            ?>
" />
                            </p>
                        <?php 
        } else {
            ?>
                            <p>&nbsp;</p>
                        <?php 
        }
        ?>
                    </div>
                <?php 
    }
    ?>
                <div>
                    <p class="form-row hide_if_variation_virtual form-row-full"><label><?php 
    _e('Shipping class:', 'woocommerce');
    ?>
</label> <?php 
    $args = array('taxonomy' => 'product_shipping_class', 'hide_empty' => 0, 'show_option_none' => __('Same as parent', 'woocommerce'), 'name' => 'variable_shipping_class[' . $loop . ']', 'id' => '', 'selected' => isset($shipping_class) ? esc_attr($shipping_class) : '', 'echo' => 0);
    echo wp_dropdown_categories($args);
    ?>
</p>

                    <?php 
    if (wc_tax_enabled()) {
        ?>
                        <p class="form-row form-row-full">
                            <label><?php 
        _e('Tax class:', 'woocommerce');
        ?>
</label>
                            <select name="variable_tax_class[<?php 
        echo $loop;
        ?>
]">
                                <option value="parent" <?php 
        selected(is_null($_tax_class), true);
        ?>
><?php 
        _e('Same as parent', 'woocommerce');
        ?>
</option>
                                <?php 
        foreach ($parent_data['tax_class_options'] as $key => $value) {
            echo '<option value="' . esc_attr($key) . '" ' . selected($key === $_tax_class, true, false) . '>' . esc_html($value) . '</option>';
        }
        ?>
</select>
                        </p>
                    <?php 
    }
    ?>

                    <p class="form-row form-row-full">
                        <label><?php 
    _e('Variation Description:', 'woocommerce');
    ?>
</label>
                        <textarea name="variable_description[<?php 
    echo $loop;
    ?>
]" rows="3" style="width:100%;"><?php 
    echo isset($variation_data['_variation_description']) ? esc_textarea($variation_data['_variation_description']) : '';
    ?>
</textarea>
                    </p>
                </div>
                <div class="show_if_variation_downloadable" style="display: none;">
                    <div class="form-row form-row-full downloadable_files">
                        <label><?php 
    _e('Downloadable Files', 'woocommerce');
    ?>
:</label>
                        <table class="widefat">
                            <thead>
                            <div>
                                <th><?php 
    _e('Name', 'woocommerce');
    ?>
 <span class="tips" data-tip="<?php 
    esc_attr_e('This is the name of the download shown to the customer.', 'woocommerce');
    ?>
">[?]</span></th>
                                <th colspan="2"><?php 
    _e('File URL', 'woocommerce');
    ?>
 <span class="tips" data-tip="<?php 
    esc_attr_e('This is the URL or absolute path to the file which customers will get access to. URLs entered here should already be encoded.', 'woocommerce');
    ?>
">[?]</span></th>
                                <th>&nbsp;</th>
                            </div>
                            </thead>
                            <tbody>
                            <?php 
    if ($_downloadable_files) {
        foreach ($_downloadable_files as $key => $file) {
            if (!is_array($file)) {
                $file = array('file' => $file, 'name' => '');
            }
            include 'html-product-variation-download.php';
        }
    }
    ?>
                            </tbody>
                            <tfoot>
                            <div>
                                <th colspan="4">
                                    <a href="#" class="button insert" data-row="<?php 
    $file = array('file' => '', 'name' => '');
    ob_start();
    include 'html-product-variation-download.php';
    echo esc_attr(ob_get_clean());
    ?>
"><?php 
    _e('Add File', 'woocommerce');
    ?>
</a>
                                </th>
                            </div>
                            </tfoot>
                        </table>
                    </div>
                </div>
                <div class="show_if_variation_downloadable" style="display: none;">
                    <p class="form-row form-row-first">
                        <label><?php 
    _e('Download Limit:', 'woocommerce');
    ?>
 <a class="tips" data-tip="<?php 
    esc_attr_e('Leave blank for unlimited re-downloads.', 'woocommerce');
    ?>
" href="#">[?]</a></label>
                        <input type="number" size="5" name="variable_download_limit[<?php 
    echo $loop;
    ?>
]" value="<?php 
    if (isset($_download_limit)) {
        echo esc_attr($_download_limit);
    }
    ?>
" placeholder="<?php 
    esc_attr_e('Unlimited', 'woocommerce');
    ?>
" step="1" min="0" />
                    </p>
                    <p class="form-row form-row-last">
                        <label><?php 
    _e('Download Expiry:', 'woocommerce');
    ?>
 <a class="tips" data-tip="<?php 
    esc_attr_e('Enter the number of days before a download link expires, or leave blank.', 'woocommerce');
    ?>
" href="#">[?]</a></label>
                        <input type="number" size="5" name="variable_download_expiry[<?php 
    echo $loop;
    ?>
]" value="<?php 
    if (isset($_download_expiry)) {
        echo esc_attr($_download_expiry);
    }
    ?>
" placeholder="<?php 
    esc_attr_e('Unlimited', 'woocommerce');
    ?>
" step="1" min="0" />
                    </p>
                </div>
                <?php 
    do_action('woocommerce_product_after_variable_attributes', $loop, $variation_data, $variation);
    ?>
            </div>
        </div>
    </div>

    <?php 
}