/**
  * Hide attributes if they correspond to filtered-out variations.
  *
  * @param  string   $output     original output
  * @param  array    $attribute  attribute data
  * @param  array    $values     attribute values
  * @return string               modified output
  */
 public function bundled_item_attribute($output, $attribute, $values)
 {
     if ($attribute['is_variation']) {
         $variation_attribute_values = array();
         $bundled_item = $this->get_bundled_item($this->listing_attributes_of);
         $bundled_item_variations = $bundled_item->get_product_variations();
         if (empty($bundled_item_variations)) {
             return $output;
         }
         $attribute_key = 'attribute_' . sanitize_title($attribute['name']);
         // Find active attribute values from the bundled item variation data.
         foreach ($bundled_item_variations as $variation_data) {
             if (isset($variation_data['attributes'][$attribute_key])) {
                 $variation_attribute_values[] = $variation_data['attributes'][$attribute_key];
                 $variation_attribute_values = array_unique($variation_attribute_values);
             }
         }
         if (!empty($variation_attribute_values) && in_array('', $variation_attribute_values)) {
             return $output;
         }
         $attribute_name = $attribute['name'];
         $filtered_values = array();
         if ($attribute['is_taxonomy']) {
             $product_terms = WC_PB_Core_Compatibility::wc_get_product_terms($bundled_item->product_id, $attribute_name, array('fields' => 'all'));
             foreach ($product_terms as $product_term) {
                 if (in_array($product_term->slug, $variation_attribute_values)) {
                     $filtered_values[] = $product_term->name;
                 }
             }
             return wpautop(wptexturize(implode(', ', $filtered_values)));
         } else {
             foreach ($values as $value) {
                 $check_value = WC_PB_Core_Compatibility::is_wc_version_gte_2_4() ? $value : sanitize_title($value);
                 if (in_array($check_value, $variation_attribute_values)) {
                     $filtered_values[] = $value;
                 }
             }
             return wpautop(wptexturize(implode(', ', $filtered_values)));
         }
     }
     return $output;
 }
 /**
  * Adds bundle specific cart-item data.
  * The 'stamp' var is a unique identifier for that particular bundle configuration.
  *
  * @param  array    $cart_item_data    the cart item data
  * @param  int      $product_id	       the product id
  * @return array                       modified cart item data
  */
 public function woo_bundles_add_cart_item_data($cart_item_data, $product_id)
 {
     // Get product type.
     $terms = get_the_terms($product_id, 'product_type');
     $product_type = !empty($terms) && isset(current($terms)->name) ? sanitize_title(current($terms)->name) : 'simple';
     if ($product_type == 'bundle') {
         if (isset($cart_item_data['stamp']) && isset($cart_item_data['bundled_items'])) {
             return $cart_item_data;
         }
         $product = wc_get_product($product_id);
         // grab bundled items.
         $bundled_items = $product->get_bundled_items();
         if (empty($bundled_items)) {
             return $cart_item_data;
         }
         // Create a unique stamp id with the bundled items' configuration.
         $stamp = array();
         foreach ($bundled_items as $bundled_item_id => $bundled_item) {
             $id = $bundled_item->product_id;
             $bundled_product_type = $bundled_item->product->product_type;
             $bundled_product_qty = isset($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_quantity_' . $bundled_item_id]) ? absint($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_quantity_' . $bundled_item_id]) : $bundled_item->get_quantity();
             $stamp[$bundled_item_id]['product_id'] = $id;
             $stamp[$bundled_item_id]['type'] = $bundled_product_type;
             $stamp[$bundled_item_id]['quantity'] = $bundled_product_qty;
             $stamp[$bundled_item_id]['discount'] = $bundled_item->get_discount();
             if ($bundled_item->has_title_override()) {
                 $stamp[$bundled_item_id]['title'] = $bundled_item->get_raw_title();
             }
             // Optional.
             $is_optional = $bundled_item->is_optional();
             if ($is_optional) {
                 if (isset($cart_item_data['stamp'][$bundled_item_id]['optional_selected']) && isset($_GET['order_again'])) {
                     $stamp[$bundled_item_id]['optional_selected'] = 'yes' === $cart_item_data['stamp'][$bundled_item_id]['optional_selected'] ? 'yes' : 'no';
                 } elseif (isset($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_selected_optional_' . $bundled_item_id])) {
                     $stamp[$bundled_item_id]['optional_selected'] = isset($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_selected_optional_' . $bundled_item_id]) ? 'yes' : 'no';
                 } else {
                     $stamp[$bundled_item_id]['optional_selected'] = 'no';
                 }
                 if ($stamp[$bundled_item_id]['optional_selected'] === 'no') {
                     $bundled_product_qty = 0;
                 }
             }
             // If quantity is zero, continue.
             if ($bundled_product_qty == 0) {
                 continue;
             }
             // Store variable product options in stamp to avoid generating the same bundle cart id.
             if ($bundled_product_type === 'variable' || $bundled_product_type === 'variable-subscription') {
                 if (isset($cart_item_data['stamp'][$bundled_item_id]['attributes']) && isset($_GET['order_again'])) {
                     $stamp[$bundled_item_id]['attributes'] = $cart_item_data['stamp'][$bundled_item_id]['attributes'];
                     $stamp[$bundled_item_id]['variation_id'] = $cart_item_data['stamp'][$bundled_item_id]['variation_id'];
                     continue;
                 }
                 $attr_stamp = array();
                 $attributes = (array) maybe_unserialize(get_post_meta($id, '_product_attributes', true));
                 foreach ($attributes as $attribute) {
                     if (!$attribute['is_variation']) {
                         continue;
                     }
                     $taxonomy = 'attribute_' . sanitize_title($attribute['name']);
                     // Value has already been checked for validity in function 'woo_bundles_validation'.
                     if (WC_PB_Core_Compatibility::is_wc_version_gte_2_4()) {
                         // Get value from post data.
                         if ($attribute['is_taxonomy']) {
                             $value = sanitize_title(stripslashes($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_' . $taxonomy . '_' . $bundled_item_id]));
                         } else {
                             $value = wc_clean(stripslashes($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_' . $taxonomy . '_' . $bundled_item_id]));
                         }
                         $attr_stamp[$taxonomy] = $value;
                     } else {
                         // Get value from post data.
                         $value = sanitize_title(trim(stripslashes($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_' . $taxonomy . '_' . $bundled_item_id])));
                         if ($attribute['is_taxonomy']) {
                             $attr_stamp[$taxonomy] = $value;
                         } else {
                             // For custom attributes, get the name from the slug.
                             $options = array_map('trim', explode(WC_DELIMITER, $attribute['value']));
                             foreach ($options as $option) {
                                 if (sanitize_title($option) == $value) {
                                     $value = $option;
                                     break;
                                 }
                             }
                             $attr_stamp[$taxonomy] = $value;
                         }
                     }
                 }
                 $stamp[$bundled_item_id]['attributes'] = $attr_stamp;
                 $stamp[$bundled_item_id]['variation_id'] = $_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_variation_id_' . $bundled_item_id];
             }
             $stamp[$bundled_item_id] = apply_filters('woocommerce_bundled_item_cart_item_identifier', $stamp[$bundled_item_id], $bundled_item_id);
         }
         $cart_item_data['stamp'] = $stamp;
         // Prepare additional data for later use.
         $cart_item_data['bundled_items'] = array();
         return $cart_item_data;
     } else {
         return $cart_item_data;
     }
 }
 /**
  * Update bundle post_meta on save.
  *
  * @return 	mixed     bundle data array configuration or false if unsuccessful
  */
 public function build_bundle_config($post_id, $posted_bundle_data)
 {
     // Process Bundled Product Configuration.
     $bundle_data = array();
     $ordered_bundle_data = array();
     $bundle_data_old = get_post_meta($post_id, '_bundle_data', true);
     $bundled_subs = 0;
     // Now start saving new data.
     $times = array();
     $save_defaults = array();
     $ordering = array();
     if (!empty($posted_bundle_data)) {
         foreach ($posted_bundle_data as $val => $data) {
             $id = isset($data['product_id']) ? $data['product_id'] : false;
             if (!$id) {
                 continue;
             }
             $terms = get_the_terms($id, 'product_type');
             $product_type = !empty($terms) && isset(current($terms)->name) ? sanitize_title(current($terms)->name) : 'simple';
             $is_sub = class_exists('WC_Subscriptions') && WC_PB()->compatibility->is_subscription($id);
             if ($id && $id > 0 && ($product_type === 'simple' || $product_type === 'variable' || $is_sub) && $post_id != $id) {
                 // Only allow saving 1 sub.
                 if ($is_sub) {
                     if ($bundled_subs > 0 && version_compare(WC_Subscriptions::$version, '2.0.0', '<')) {
                         $this->add_admin_error(sprintf(__('&quot;%1$s&quot; (#%2$s) was not saved. Only one simple Subscription per Bundle is supported.', 'woocommerce-product-bundles'), get_the_title($id), $id));
                         continue;
                     } else {
                         $bundled_subs++;
                     }
                 }
                 // Allow bundling the same item id multiple times by adding a suffix.
                 if (!isset($times[$id])) {
                     $times[$id] = 1;
                     $val = $id;
                 } else {
                     // Only allow multiple instances of non-sold-individually items.
                     if (get_post_meta($id, '_sold_individually', true) == 'yes') {
                         $this->add_admin_error(sprintf(__('&quot;%1$s&quot; (#%2$s) is sold individually and cannot be bundled more than once.', 'woocommerce-product-bundles'), get_the_title($id), $id));
                         continue;
                     }
                     $times[$id] += 1;
                     $val = isset($data['item_id']) ? $data['item_id'] : $id . '_' . $times[$id];
                 }
                 $bundle_data[$val] = array();
                 $bundle_data[$val]['product_id'] = $id;
                 // Save thumbnail preferences first.
                 if (isset($data['hide_thumbnail'])) {
                     $bundle_data[$val]['hide_thumbnail'] = 'yes';
                 } else {
                     $bundle_data[$val]['hide_thumbnail'] = 'no';
                 }
                 // Save title preferences.
                 if (isset($data['override_title'])) {
                     $bundle_data[$val]['override_title'] = 'yes';
                     $bundle_data[$val]['product_title'] = isset($data['product_title']) ? $data['product_title'] : '';
                 } else {
                     $bundle_data[$val]['override_title'] = 'no';
                 }
                 // Save description preferences.
                 if (isset($data['override_description'])) {
                     $bundle_data[$val]['override_description'] = 'yes';
                     $bundle_data[$val]['product_description'] = isset($data['product_description']) ? wp_kses_post(stripslashes($data['product_description'])) : '';
                 } else {
                     $bundle_data[$val]['override_description'] = 'no';
                 }
                 // Save optional.
                 if (isset($data['optional'])) {
                     $bundle_data[$val]['optional'] = 'yes';
                 } else {
                     $bundle_data[$val]['optional'] = 'no';
                 }
                 // Save quantity data.
                 if (isset($data['bundle_quantity'])) {
                     if (is_numeric($data['bundle_quantity'])) {
                         $quantity = absint($data['bundle_quantity']);
                         if ($quantity >= 0 && $data['bundle_quantity'] - $quantity == 0) {
                             if ($quantity !== 1 && get_post_meta($id, '_sold_individually', true) === 'yes') {
                                 $this->add_admin_error(sprintf(__('&quot;%1$s&quot; (#%2$s) is sold individually and cannot be bundled with a minimum quantity higher than 1.', 'woocommerce-product-bundles'), get_the_title($id), $id));
                                 $bundle_data[$val]['bundle_quantity'] = 1;
                             } else {
                                 $bundle_data[$val]['bundle_quantity'] = $quantity;
                             }
                         } else {
                             $this->add_admin_error(sprintf(__('The quantity you entered for &quot;%1$s%2$s&quot; (#%3$s) was not valid and has been reset. Please enter a non-negative integer value.', 'woocommerce-product-bundles'), get_the_title($id), $id != $val ? ' #' . $times[$id] : '', $id));
                         }
                     }
                 } else {
                     $bundle_data[$val]['bundle_quantity'] = 1;
                 }
                 $quantity_min = $bundle_data[$val]['bundle_quantity'];
                 // Save max quantity data.
                 if (isset($data['bundle_quantity_max']) && (is_numeric($data['bundle_quantity_max']) || $data['bundle_quantity_max'] === '')) {
                     $quantity = $data['bundle_quantity_max'] !== '' ? absint($data['bundle_quantity_max']) : '';
                     if ($quantity === '' || $quantity > 0 && $quantity >= $quantity_min && $data['bundle_quantity_max'] - $quantity == 0) {
                         if ($quantity !== 1 && get_post_meta($id, '_sold_individually', true) === 'yes') {
                             $this->add_admin_error(sprintf(__('&quot;%1$s&quot; (#%2$s) is sold individually and cannot be bundled with a maximum quantity higher than 1.', 'woocommerce-product-bundles'), get_the_title($id), $id));
                             $bundle_data[$val]['bundle_quantity_max'] = 1;
                         } else {
                             $bundle_data[$val]['bundle_quantity_max'] = $quantity;
                         }
                     } else {
                         $this->add_admin_error(sprintf(__('The maximum product quantity that you entered for &quot;%1$s%2$s&quot; (#%3$s) was not valid and has been reset. Please enter a positive integer value, at least as high as the minimum quantity. Otherwise, leave the field empty for an unlimited maximum quantity.', 'woocommerce-product-bundles'), get_the_title($id), $id != $val ? ' #' . $times[$id] : '', $id));
                     }
                 } else {
                     $bundle_data[$val]['bundle_quantity_max'] = max($quantity_min, 1);
                 }
                 // Save sale price data.
                 if (isset($data['bundle_discount'])) {
                     if (is_numeric($data['bundle_discount'])) {
                         $discount = (double) wc_format_decimal($data['bundle_discount']);
                         if ($discount < 0 || $discount > 100) {
                             $this->add_admin_error(sprintf(__('The discount value you entered for &quot;%1$s%2$s&quot; (#%3$s) was not valid and has been reset. Please enter a positive number between 0-100.', 'woocommerce-product-bundles'), get_the_title($id), $id != $val ? ' #' . $times[$id] : '', $id));
                             $bundle_data[$val]['bundle_discount'] = '';
                         } else {
                             $bundle_data[$val]['bundle_discount'] = $discount;
                         }
                     } else {
                         $bundle_data[$val]['bundle_discount'] = '';
                     }
                 } else {
                     $bundle_data[$val]['bundle_discount'] = '';
                 }
                 // Save data related to variable items.
                 if ($product_type === 'variable') {
                     // Save variation filtering options.
                     if (isset($data['filter_variations'])) {
                         if (isset($data['allowed_variations']) && count($data['allowed_variations']) > 0) {
                             $bundle_data[$val]['filter_variations'] = 'yes';
                             $bundle_data[$val]['allowed_variations'] = $data['allowed_variations'];
                             if (isset($data['hide_filtered_variations'])) {
                                 $bundle_data[$val]['hide_filtered_variations'] = 'yes';
                             } else {
                                 $bundle_data[$val]['hide_filtered_variations'] = 'no';
                             }
                         } else {
                             $bundle_data[$val]['filter_variations'] = 'no';
                             $this->add_admin_error(__('Please select at least one variation for each bundled product you want to filter.', 'woocommerce-product-bundles'));
                         }
                     } else {
                         $bundle_data[$val]['filter_variations'] = 'no';
                     }
                     // Save defaults options.
                     if (isset($data['override_defaults'])) {
                         if (isset($data['default_attributes'])) {
                             // if filters are set, check that the selections are valid.
                             if (isset($data['filter_variations']) && !empty($data['allowed_variations'])) {
                                 $allowed_variations = $data['allowed_variations'];
                                 // the array to store all valid attribute options of the iterated product.
                                 $filtered_attributes = array();
                                 // populate array with valid attributes.
                                 foreach ($allowed_variations as $variation) {
                                     $variation_data = array();
                                     // sweep the post meta for attributes.
                                     if (WC_PB_Core_Compatibility::is_wc_version_gte_2_4()) {
                                         $variation_data = wc_get_product_variation_attributes($variation);
                                     } else {
                                         $post_meta = get_post_meta($variation);
                                         foreach ($post_meta as $field => $value) {
                                             if (!strstr($field, 'attribute_')) {
                                                 continue;
                                             }
                                             $variation_data[$field] = $value[0];
                                         }
                                     }
                                     foreach ($variation_data as $name => $value) {
                                         $attribute_name = substr($name, strlen('attribute_'));
                                         $attribute_value = sanitize_title($value);
                                         // Populate array.
                                         if (!isset($filtered_attributes[sanitize_title($attribute_name)])) {
                                             $filtered_attributes[sanitize_title($attribute_name)][] = $attribute_value;
                                         } elseif (!in_array($attribute_value, $filtered_attributes[sanitize_title($attribute_name)])) {
                                             $filtered_attributes[sanitize_title($attribute_name)][] = $attribute_value;
                                         }
                                     }
                                 }
                                 // Check validity.
                                 foreach ($data['default_attributes'] as $sanitized_name => $value) {
                                     if ($value === '') {
                                         continue;
                                     }
                                     if (!in_array(sanitize_title($value), $filtered_attributes[$sanitized_name]) && !in_array('', $filtered_attributes[$sanitized_name])) {
                                         // Set option to "Any".
                                         $data['default_attributes'][$sanitized_name] = '';
                                         // Throw an error.
                                         $this->add_admin_error(sprintf(__('The defaults that you selected for &quot;%1$s%2$s&quot; (#%3$s) are inconsistent with the set of active variations. Always double-check your preferences before saving, and always save any changes made to the variation filters before choosing new defaults.', 'woocommerce-product-bundles'), get_the_title($id), $id != $val ? ' #' . $times[$id] : '', $id));
                                         continue;
                                     }
                                 }
                             }
                             // Save.
                             foreach ($data['default_attributes'] as $sanitized_name => $value) {
                                 $bundle_data[$val]['bundle_defaults'][$sanitized_name] = $value;
                             }
                             $bundle_data[$val]['override_defaults'] = 'yes';
                         }
                     } else {
                         $bundle_data[$val]['override_defaults'] = 'no';
                     }
                 }
                 // Save visibility preferences.
                 $visibility = array('product' => 'visible', 'cart' => 'visible', 'order' => 'visible');
                 $visibility['product'] = isset($data['visibility']['product']) ? 'visible' : 'hidden';
                 $visibility['cart'] = isset($data['visibility']['cart']) ? 'visible' : 'hidden';
                 $visibility['order'] = isset($data['visibility']['order']) ? 'visible' : 'hidden';
                 if ($visibility['product'] === 'hidden') {
                     if ($product_type === 'variable') {
                         if ($bundle_data[$val]['override_defaults'] === 'yes') {
                             if (isset($data['default_attributes'])) {
                                 foreach ($data['default_attributes'] as $default_name => $default_value) {
                                     if (!$default_value) {
                                         $visibility['product'] = 'visible';
                                         $this->add_admin_error(sprintf(__('&quot;%1$s%2$s&quot; (#%s) cannot be hidden from the single-product template unless all default options of the bundled product are defined.', 'woocommerce-product-bundles'), get_the_title($id), $id != $val ? ' #' . $times[$id] : '', $id));
                                         break;
                                     }
                                 }
                             } else {
                                 $visibility['product'] = 'visible';
                             }
                         } else {
                             $this->add_admin_error(sprintf(__('&quot;%1$s%2$s&quot; (#%3$s) cannot be hidden from the single-product template unless all default options of the bundled product are defined.', 'woocommerce-product-bundles'), get_the_title($id), $id != $val ? ' #' . $times[$id] : '', $id));
                             $visibility['product'] = 'visible';
                         }
                     }
                 }
                 $bundle_data[$val]['visibility'] = $visibility;
                 // Save position data.
                 if (isset($data['bundle_order'])) {
                     $ordering[(int) $data['bundle_order']] = $val;
                 } else {
                     $ordering[count($ordering)] = $val;
                 }
                 $bundle_data[$val] = apply_filters('woocommerce_bundles_process_bundled_item_admin_data', $bundle_data[$val], $data, $val, $post_id);
             }
         }
         // Check empty.
         if (empty($bundle_data)) {
             return false;
         }
         // Sorting
         ksort($ordering);
         $ordered_bundle_data = array();
         foreach ($ordering as $item_id) {
             $ordered_bundle_data[$item_id] = $bundle_data[$item_id];
         }
         return $ordered_bundle_data;
     } else {
         return false;
     }
 }
 /**
  * Order API Modification #3 (unused):
  *
  * Exclude/modify order items depending on the "per-item pricing" and "per-item shipping" settings.
  *
  * @param  array    $items
  * @param  WC_Order $order
  * @return array
  */
 public function order_items($items, $order)
 {
     $return_items = $items;
     if (false === self::$override_order_items_filters && apply_filters('woocommerce_bundles_filter_order_items', false, $order)) {
         $return_items = array();
         foreach ($items as $item_id => $item) {
             if (isset($item['bundled_items']) && isset($item['bundle_cart_key'])) {
                 /*
                  * Do not export bundled items that are shipped packaged in the container ("bundled" shipping).
                  * Instead, add their totals into the container and create a container "Contents" meta field to provide a description of the included products.
                  */
                 if (isset($item['per_product_shipping']) && $item['per_product_shipping'] === 'no') {
                     $bundle_key = $item['bundle_cart_key'];
                     // Aggregate contents
                     $meta_key = __('Contents', 'woocommerce-product-bundles');
                     $meta_values = array();
                     // Aggregate prices
                     $bundle_totals = array('line_subtotal' => $item['line_subtotal'], 'line_total' => $item['line_total'], 'line_subtotal_tax' => $item['line_subtotal_tax'], 'line_tax' => $item['line_tax'], 'line_tax_data' => maybe_unserialize($item['line_tax_data']));
                     foreach ($items as $child_item_id => $child_item) {
                         if (isset($child_item['bundled_by']) && $child_item['bundled_by'] === $bundle_key && isset($child_item['bundled_shipping']) && $child_item['bundled_shipping'] === 'no') {
                             /*
                              * Aggregate bundled items shipped within the container as "Contents" meta of container.
                              */
                             $child = $order->get_product_from_item($child_item);
                             if (!$child) {
                                 continue;
                             }
                             $sku = $child->get_sku();
                             if (!$sku) {
                                 $sku = '#' . (isset($child->variation_id) ? $child->variation_id : $child->id);
                             }
                             $title = WC_PB_Helpers::format_product_shop_title($child_item['name'], $child_item['qty']);
                             $meta = '';
                             if (!empty($child_item['item_meta'])) {
                                 if (!empty($child_item['item_meta'][__('Part of', 'woocommerce-product-bundles')])) {
                                     unset($child_item['item_meta'][__('Part of', 'woocommerce-product-bundles')]);
                                 }
                                 if (WC_PB_Core_Compatibility::is_wc_version_gte_2_4()) {
                                     $item_meta = new WC_Order_Item_Meta($child_item);
                                 } else {
                                     $item_meta = new WC_Order_Item_Meta($child_item['item_meta']);
                                 }
                                 $formatted_meta = $item_meta->display(true, true, '_', ', ');
                                 if ($formatted_meta) {
                                     $meta = $formatted_meta;
                                 }
                             }
                             $meta_values[] = WC_PB_Helpers::format_product_title($title, $sku, $meta, true);
                             /*
                              * Aggregate the totals of bundled items shipped within the container into the container price.
                              */
                             $bundle_totals['line_subtotal'] += $child_item['line_subtotal'];
                             $bundle_totals['line_total'] += $child_item['line_total'];
                             $bundle_totals['line_subtotal_tax'] += $child_item['line_subtotal_tax'];
                             $bundle_totals['line_tax'] += $child_item['line_tax'];
                             $child_item_line_tax_data = maybe_unserialize($child_item['line_tax_data']);
                             $bundle_totals['line_tax_data']['total'] = array_merge($bundle_totals['line_tax_data']['total'], $child_item_line_tax_data['total']);
                         }
                     }
                     $items[$item_id]['line_tax_data'] = serialize($bundle_totals['line_tax_data']);
                     $items[$item_id] = array_merge($item, $bundle_totals);
                     if (WC_PB_Core_Compatibility::is_wc_version_gte_2_4()) {
                         // Terrible hack: add an element in the 'item_meta_array' array.
                         // A puppy somewhere just died.
                         if (!empty($items[$item_id]['item_meta_array'])) {
                             $keys = array_keys($items[$item_id]['item_meta_array']);
                             $last_key = end($keys);
                             $entry = new stdClass();
                             $entry->key = $meta_key;
                             $entry->value = implode(', ', $meta_values);
                             $items[$item_id]['item_meta_array'][$last_key + 1] = $entry;
                         }
                     }
                     $items[$item_id]['item_meta'][$meta_key] = implode(', ', $meta_values);
                     $return_items[$item_id] = $items[$item_id];
                     /*
                      * If the bundled items are shipped individually ("per-item" shipping), do not export the container unless it has a non-zero price.
                      * In this case, instead of marking it as virtual, modify its weight and dimensions (tiny values) to avoid any extra shipping costs and ensure that its value is included in the shipment - @see get_product_from_item
                      */
                 } elseif ($item['line_total'] > 0) {
                     $return_items[$item_id] = $items[$item_id];
                 }
             } elseif (isset($item['bundled_by']) && isset($item['bundle_cart_key'])) {
                 if (!isset($item['bundled_shipping']) || $item['bundled_shipping'] === 'yes') {
                     $return_items[$item_id] = $items[$item_id];
                 }
             } else {
                 $return_items[$item_id] = $items[$item_id];
             }
         }
     }
     return $return_items;
 }
 /**
  * Initializes a bundled item for access by the container: Calculates min and max prices, checks availability info, etc.
  *
  * @return void
  */
 public function init()
 {
     $product_id = $this->product_id;
     $bundled_product = $this->product;
     $quantity = $this->get_quantity();
     $discount = $this->get_discount();
     /*------------------------------*/
     /*  Simple Subs                 */
     /*------------------------------*/
     if ($bundled_product->product_type === 'subscription') {
         if ($this->is_front_end) {
             if ($bundled_product->is_sold_individually()) {
                 $this->sold_individually = true;
             }
             if (!$bundled_product->is_in_stock() || !$bundled_product->has_enough_stock($quantity)) {
                 $this->stock_status = 'out-of-stock';
             }
             if ($bundled_product->is_on_backorder() && $bundled_product->backorders_require_notification()) {
                 $this->stock_status = 'available-on-backorder';
             }
             $this->total_stock = $bundled_product->get_total_stock();
         }
         if ($this->is_priced_per_product()) {
             // Recurring price.
             $regular_recurring_fee = $this->get_bundled_product_regular_price();
             $recurring_fee = $this->get_bundled_product_price();
             $this->min_regular_recurring_price = $this->max_regular_recurring_price = $regular_recurring_fee;
             $this->min_recurring_price = $this->max_recurring_price = $recurring_fee;
             if ($regular_recurring_fee > $recurring_fee) {
                 $this->on_sale = true;
             }
             // Sign up price.
             $regular_signup_fee = $bundled_product->get_sign_up_fee();
             $signup_fee = $this->get_sign_up_fee($regular_signup_fee, $bundled_product);
             $regular_up_front_fee = $this->get_prorated_price_for_subscription($regular_recurring_fee, $regular_signup_fee, $bundled_product);
             $up_front_fee = $this->get_prorated_price_for_subscription($recurring_fee, $signup_fee, $bundled_product);
             $this->min_regular_price = $this->max_regular_price = $regular_up_front_fee;
             $this->min_price = $this->max_price = $up_front_fee;
             if ($regular_up_front_fee > $up_front_fee) {
                 $this->on_sale = true;
             }
         }
         /*-----------------------------------*/
         /*  Simple Products                  */
         /*-----------------------------------*/
     } elseif ($bundled_product->product_type === 'simple') {
         if ($this->is_front_end) {
             if ($bundled_product->is_sold_individually()) {
                 $this->sold_individually = true;
             }
             if (!$bundled_product->is_in_stock() || !$bundled_product->has_enough_stock($quantity)) {
                 $this->stock_status = 'out-of-stock';
             }
             if ($bundled_product->is_on_backorder() && $bundled_product->backorders_require_notification()) {
                 $this->stock_status = 'available-on-backorder';
             }
             $this->total_stock = $bundled_product->get_total_stock();
         }
         if ($this->is_priced_per_product()) {
             $regular_price = $this->get_bundled_product_regular_price();
             $price = $this->get_bundled_product_price();
             // Name your price support.
             if (WC_PB()->compatibility->is_nyp($bundled_product)) {
                 $regular_price = $price = WC_Name_Your_Price_Helpers::get_minimum_price($product_id) ? WC_Name_Your_Price_Helpers::get_minimum_price($product_id) : 0;
                 $this->nyp = true;
             }
             $this->min_regular_price = $this->max_regular_price = $regular_price;
             $this->min_price = $this->max_price = $price;
             if ($regular_price > $price) {
                 $this->on_sale = true;
             }
         }
         /*-------------------------------------------*/
         /*	Variable Products                        */
         /*-------------------------------------------*/
     } elseif ($bundled_product->product_type === 'variable' || $bundled_product->product_type === 'variable-subscription') {
         $calc_prices = $this->is_priced_per_product();
         $min_variation = $max_variation = false;
         if ($bundled_product->is_sold_individually()) {
             $this->sold_individually = true;
         }
         // Without any variation filters present, we can just rely on parent methods.
         if (empty($this->allowed_variations)) {
             if ($this->is_front_end) {
                 $this->total_stock = $bundled_product->get_total_stock();
                 if (!$bundled_product->is_in_stock() || !$bundled_product->backorders_allowed() && $bundled_product->managing_stock() && $this->total_stock < $quantity) {
                     $this->stock_status = 'out-of-stock';
                 } else {
                     $variation_in_stock_exists = false;
                     foreach ($bundled_product->get_children(true) as $child_id) {
                         if ('yes' === get_post_meta($child_id, '_manage_stock', true)) {
                             $stock = get_post_meta($child_id, '_stock', true);
                             if ($stock >= $quantity) {
                                 $variation_in_stock_exists = true;
                                 break;
                             }
                         } else {
                             $variation_in_stock_exists = true;
                             break;
                         }
                     }
                     if (!$variation_in_stock_exists) {
                         $this->stock_status = 'out-of-stock';
                     }
                 }
                 if ($bundled_product->is_on_backorder() && $bundled_product->backorders_require_notification()) {
                     $this->stock_status = 'available-on-backorder';
                 }
             }
             if ($calc_prices) {
                 if ($bundled_product->product_type === 'variable-subscription') {
                     if (!isset($bundled_product->subscription_period) || !isset($bundled_product->subscription_period_interval) || !isset($bundled_product->max_variation_period) || !isset($bundled_product->max_variation_period_interval)) {
                         $bundled_product->variable_product_sync();
                     }
                     $min_variation_price_id = get_post_meta($bundled_product->id, '_min_price_variation_id', true);
                     $max_variation_price_id = get_post_meta($bundled_product->id, '_max_price_variation_id', true);
                 } else {
                     if (WC_PB_Core_Compatibility::is_wc_version_gte_2_4()) {
                         $variation_prices = $bundled_product->get_variation_prices(false);
                         if (!empty($discount) && apply_filters('woocommerce_bundled_item_discount_from_regular', true, $this)) {
                             $variation_price_ids = array_keys($variation_prices['regular_price']);
                         } else {
                             $variation_price_ids = array_keys($variation_prices['price']);
                         }
                         $min_variation_price_id = current($variation_price_ids);
                         $max_variation_price_id = end($variation_price_ids);
                     } else {
                         if (!empty($discount) && apply_filters('woocommerce_bundled_item_discount_from_regular', true, $this)) {
                             // Product may need to be synced.
                             if ($bundled_product->get_variation_regular_price('min', false) === false) {
                                 $bundled_product->variable_product_sync();
                             }
                             $min_variation_price_id = get_post_meta($this->product_id, '_min_regular_price_variation_id', true);
                             $max_variation_price_id = get_post_meta($this->product_id, '_max_regular_price_variation_id', true);
                         } else {
                             // Product may need to be synced.
                             if ($bundled_product->get_variation_price('min', false) === false) {
                                 $bundled_product->variable_product_sync();
                             }
                             $min_variation_price_id = get_post_meta($this->product_id, '_min_price_variation_id', true);
                             $max_variation_price_id = get_post_meta($this->product_id, '_max_price_variation_id', true);
                         }
                     }
                 }
                 $min_variation = $bundled_product->get_child($min_variation_price_id);
                 $max_variation = $bundled_product->get_child($max_variation_price_id);
             }
             // When variation filters are present, we need to iterate over the variations.
         } else {
             $variation_in_stock_exists = $this->is_front_end ? false : true;
             $all_variations_on_backorder = $this->is_front_end ? true : false;
             $min_variation_price = '';
             $max_variation_price = '';
             $this->total_stock = max(0, wc_stock_amount($bundled_product->stock));
             foreach ($bundled_product->get_children(true) as $child_id) {
                 // Do not continue if variation is filtered.
                 if (is_array($this->allowed_variations) && !in_array($child_id, $this->allowed_variations)) {
                     continue;
                 }
                 $variation = $bundled_product->get_child($child_id);
                 if (!$variation) {
                     continue;
                 }
                 // Stock status.
                 if (!$variation_in_stock_exists) {
                     if ($variation->is_in_stock() && $variation->has_enough_stock($quantity)) {
                         $variation_in_stock_exists = true;
                     }
                 }
                 // Total stock.
                 if ($variation->managing_stock()) {
                     $this->total_stock += max(0, wc_stock_amount($variation->stock));
                 }
                 // Backorder.
                 if ($all_variations_on_backorder) {
                     if ($bundled_product->backorders_allowed() && $bundled_product->backorders_require_notification()) {
                         if (!$variation->is_on_backorder()) {
                             $all_variations_on_backorder = false;
                         }
                     } else {
                         $all_variations_on_backorder = false;
                     }
                 }
                 // Prices.
                 if ($calc_prices) {
                     if (!empty($discount) && apply_filters('woocommerce_bundled_item_discount_from_regular', true, $this)) {
                         // Lowest price.
                         if ('' === $min_variation_price || $variation->regular_price < $min_variation_price) {
                             $min_variation_price = $variation->regular_price;
                             $min_variation = $variation;
                         }
                         // Highest price.
                         if ('' === $max_variation_price || $variation->regular_price > $max_variation_price) {
                             $max_variation_price = $variation->regular_price;
                             $max_variation = $variation;
                         }
                     } else {
                         // Lowest price.
                         if ('' === $min_variation_price || $variation->price < $min_variation_price) {
                             $min_variation_price = $variation->price;
                             $min_variation = $variation;
                         }
                         // Highest price.
                         if ('' === $max_variation_price || $variation->price > $max_variation_price) {
                             $max_variation_price = $variation->price;
                             $max_variation = $variation;
                         }
                     }
                 }
             }
             if (!$variation_in_stock_exists) {
                 $this->stock_status = 'out-of-stock';
             }
             if ($all_variations_on_backorder) {
                 $this->stock_status = 'available-on-backorder';
             }
         }
         if ($min_variation && $max_variation) {
             $this->min_price_product = $this->min_regular_price_product = $min_variation;
             $this->max_price_product = $this->min_regular_price_product = $max_variation;
             if ($bundled_product->product_type === 'variable-subscription') {
                 $this->min_recurring_price = $this->max_recurring_price = $this->get_bundled_product_price($min_variation);
                 $this->min_regular_recurring_price = $this->max_regular_recurring_price = $this->get_bundled_product_regular_price($min_variation);
                 $min_regular_signup_fee = $min_variation->get_sign_up_fee();
                 $min_signup_fee = $this->get_sign_up_fee($min_regular_signup_fee, $min_variation);
                 $min_regular_up_front_fee = $this->get_prorated_price_for_subscription($this->min_regular_recurring_price, $min_regular_signup_fee, $min_variation);
                 $min_up_front_fee = $this->get_prorated_price_for_subscription($this->min_recurring_price, $min_signup_fee, $min_variation);
                 $this->min_regular_price = $this->max_regular_price = $min_regular_up_front_fee;
                 $this->min_price = $this->max_price = $min_up_front_fee;
             } else {
                 $this->min_price = $this->get_bundled_product_price($min_variation);
                 $this->max_price = $this->get_bundled_product_price($max_variation);
                 $min_variation_regular_price = $this->get_bundled_product_regular_price($min_variation);
                 $max_variation_regular_price = $this->get_bundled_product_regular_price($max_variation);
                 // The variation with the lowest price may have a higher regular price then the variation with the highest price.
                 if ($max_variation_regular_price < $min_variation_regular_price) {
                     $this->min_regular_price_product = $max_variation;
                     $this->max_regular_price_product = $min_variation;
                 }
                 $this->min_regular_price = min($min_variation_regular_price, $max_variation_regular_price);
                 $this->max_regular_price = max($min_variation_regular_price, $max_variation_regular_price);
             }
             if ($this->min_regular_price > $this->min_price) {
                 $this->on_sale = true;
             }
         }
     }
 }