/**
  * Front end styles and scripts.
  *
  * @return void
  */
 public function wc_cp_frontend_scripts()
 {
     $suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
     $dependencies = array('jquery', 'jquery-blockui', 'wc-add-to-cart-variation');
     if (class_exists('WC_Bundles')) {
         $dependencies[] = 'wc-add-to-cart-bundle';
     }
     if (class_exists('Product_Addon_Display')) {
         $dependencies[] = 'woocommerce-addons';
     }
     // Add any custom script dependencies here.
     $dependencies = apply_filters('woocommerce_composite_script_dependencies', $dependencies);
     wp_register_script('wc-add-to-cart-composite', WC_CP()->plugin_url() . '/assets/js/add-to-cart-composite' . $suffix . '.js', $dependencies, WC_CP()->version);
     wp_register_style('wc-composite-single-css', WC_CP()->plugin_url() . '/assets/css/wc-composite-single.css', false, WC_CP()->version, 'all');
     wp_register_style('wc-composite-css', WC_CP()->plugin_url() . '/assets/css/wc-composite-styles.css', false, WC_CP()->version, 'all');
     wp_enqueue_style('wc-composite-css');
     $params = apply_filters('woocommerce_composite_front_end_params', array('small_width_threshold' => 450, 'full_width_threshold' => 450, 'legacy_width_threshold' => 450, 'i18n_qty_string' => _x(' &times; %s', 'qty string', 'woocommerce-composite-products'), 'i18n_price_string' => _x(' &ndash; %s', 'price suffix', 'woocommerce-composite-products'), 'i18n_title_string' => sprintf(_x('%1$s%2$s%3$s', 'title quantity price', 'woocommerce-composite-products'), '%t', '%q', '%p'), 'i18n_selected_product_string' => sprintf(_x('%1$s%2$s', 'product title followed by details', 'woocommerce-composite-products'), '%t', '%m'), 'i18n_free' => __('Free!', 'woocommerce'), 'i18n_total' => __('Total', 'woocommerce-composite-products') . ': ', 'i18n_none' => __('None', 'woocommerce-composite-products'), 'i18n_select_an_option' => __('Select an option&hellip;', 'woocommerce-composite-products'), 'i18n_previous_step' => __('Previous &ndash; %s', 'woocommerce-composite-products'), 'i18n_next_step' => __('Next &ndash; %s', 'woocommerce-composite-products'), 'i18n_final_step' => __('Review Configuration', 'woocommerce-composite-products'), 'i18n_reset_selection' => __('Reset selection', 'woocommerce-composite-products'), 'i18n_clear_selection' => __('Clear selection', 'woocommerce-composite-products'), 'i18n_validation_issues' => __('To continue, please resolve the following issues:', 'woocommerce-composite-products'), 'i18n_validation_issues_for' => sprintf(__('<span class="msg-source">%1$s</span> &rarr; <span class="msg-content">%2$s</span>', 'woocommerce-composite-products'), '%c', '%e'), 'i18n_unavailable_text' => __('Sorry, this product cannot be purchased at the moment.', 'woocommerce-composite-products'), 'i18n_select_component_option' => __('Select an option to continue&hellip;', 'woocommerce-composite-products'), 'i18n_select_component_option_for' => __('Select an option.', 'woocommerce-composite-products'), 'i18n_selected_component_options_no_stock' => __('Insufficient stock. To continue, please check your configuration&hellip;', 'woocommerce-composite-products'), 'i18n_selected_component_options_no_stock_for' => __('Insufficient stock. Please check your configuration.', 'woocommerce-composite-products'), 'i18n_select_product_options' => __('Select product options to continue&hellip;', 'woocommerce-composite-products'), 'i18n_select_product_options_for' => __('Select product options.', 'woocommerce-composite-products'), 'i18n_summary_empty_component' => __('Configure', 'woocommerce-composite-products'), 'i18n_summary_configured_component' => __('Change', 'woocommerce-composite-products'), 'i18n_summary_static_component' => __('View', 'woocommerce-composite-products'), 'i18n_insufficient_stock' => __('Insufficient stock: %s', 'woocommerce-composite-products'), 'i18n_comma_sep' => sprintf(_x('%1$s, %2$s', 'comma-separated items', 'woocommerce-composite-products'), '%s', '%v'), 'i18n_insufficient_item_stock' => sprintf(__('<span class="out-of-stock-component">%2$s</span> &rarr; <span class="out-of-stock-product">%1$s</span>', 'woocommerce-composite-products'), '%s', '%v'), 'i18n_reload_threshold_exceeded' => __('Loading &quot;%s&quot; options is taking a bit longer than usual. Would you like to keep trying?', 'woocommerce-composite-products'), 'i18n_no_compat_options' => __('No compatible options to display.', 'woocommerce-composite-products'), 'no_compat_options_autoload' => 'yes', 'currency_symbol' => get_woocommerce_currency_symbol(), 'currency_position' => esc_attr(stripslashes(get_option('woocommerce_currency_pos'))), 'currency_format_num_decimals' => absint(get_option('woocommerce_price_num_decimals')), 'currency_format_decimal_sep' => esc_attr(stripslashes(get_option('woocommerce_price_decimal_sep'))), 'currency_format_thousand_sep' => esc_attr(stripslashes(get_option('woocommerce_price_thousand_sep'))), 'currency_format_trim_zeros' => false === apply_filters('woocommerce_price_trim_zeros', false) ? 'no' : 'yes', 'script_debug' => 'no', 'show_quantity_buttons' => 'no', 'transition_type' => 'slide', 'relocated_content_reset_on_return' => 'yes', 'is_wc_version_gte_2_3' => WC_CP_Core_Compatibility::is_wc_version_gte_2_3() ? 'yes' : 'no', 'is_wc_version_gte_2_4' => WC_CP_Core_Compatibility::is_wc_version_gte_2_4() ? 'yes' : 'no', 'use_wc_ajax' => WC_CP_Core_Compatibility::use_wc_ajax() ? 'yes' : 'no'));
     wp_localize_script('wc-add-to-cart-composite', 'wc_composite_params', $params);
 }
 /**
  * Adds configuration-specific cart-item data.
  *
  * @param  array 	$cart_item_data
  * @param  int 		$product_id
  * @return void
  */
 public function wc_cp_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 === 'composite' && isset($_REQUEST['wccp_component_selection']) && is_array($_REQUEST['wccp_component_selection'])) {
         // Create a unique array with the composite configuration.
         $composite_config = array();
         // Get composite data.
         $composite = wc_get_product($product_id);
         $composite_data = $composite->get_composite_data();
         foreach ($_REQUEST['wccp_component_selection'] as $component_id => $composited_product_id) {
             $composited_product_quantity = isset($_REQUEST['wccp_component_quantity'][$component_id]) ? absint($_REQUEST['wccp_component_quantity'][$component_id]) : absint($composite_data[$component_id]['quantity_min']);
             if ($composited_product_id) {
                 $composited_product_wrapper = $composite->get_composited_product($component_id, $composited_product_id);
                 if (!$composited_product_wrapper) {
                     continue;
                 }
                 $composited_product = $composited_product_wrapper->get_product();
                 $composited_product_type = $composited_product->product_type;
                 if ($composited_product->sold_individually === 'yes' && $composited_product_quantity > 1) {
                     $composited_product_quantity = 1;
                 }
             }
             $composite_config[$component_id]['product_id'] = $composited_product_id;
             $composite_config[$component_id]['composite_id'] = $product_id;
             $composite_config[$component_id]['quantity'] = $composited_product_quantity;
             $composite_config[$component_id]['title'] = $composite_data[$component_id]['title'];
             $composite_config[$component_id]['quantity_min'] = $composite_data[$component_id]['quantity_min'];
             $composite_config[$component_id]['quantity_max'] = $composite_data[$component_id]['quantity_max'];
             $composite_config[$component_id]['discount'] = isset($composite_data[$component_id]['discount']) ? $composite_data[$component_id]['discount'] : 0;
             $composite_config[$component_id]['optional'] = $composite_data[$component_id]['optional'];
             // Continue when selected product is 'None'.
             if (!$composited_product_id || $composited_product_id === '' || $composited_product_quantity === 0) {
                 $composite_config[$component_id]['type'] = 'none';
                 $composite_config[$component_id]['price'] = 0;
                 continue;
             } else {
                 $composite_config[$component_id]['type'] = $composited_product_type;
             }
             if ($composited_product_type === 'variable') {
                 $attributes_config = array();
                 $attributes = $composited_product->get_attributes();
                 foreach ($attributes as $attribute) {
                     if (!$attribute['is_variation']) {
                         continue;
                     }
                     $taxonomy = 'attribute_' . sanitize_title($attribute['name']);
                     // Value has already been checked for validity in function 'wc_cp_validation()'.
                     if (WC_CP_Core_Compatibility::is_wc_version_gte_2_4()) {
                         // Get value from post data
                         if ($attribute['is_taxonomy']) {
                             $value = sanitize_title(stripslashes($_REQUEST['wccp_' . $taxonomy][$component_id]));
                         } else {
                             $value = wc_clean(stripslashes($_REQUEST['wccp_' . $taxonomy][$component_id]));
                         }
                         $attributes_config[$taxonomy] = $value;
                     } else {
                         // Get value from post data.
                         $value = sanitize_title(trim(stripslashes($_REQUEST['wccp_' . $taxonomy][$component_id])));
                         if ($attribute['is_taxonomy']) {
                             $attributes_config[$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;
                                 }
                             }
                             $attributes_config[$taxonomy] = $value;
                         }
                     }
                 }
                 $composite_config[$component_id]['variation_id'] = $_REQUEST['wccp_variation_id'][$component_id];
                 $composite_config[$component_id]['attributes'] = $attributes_config;
             }
             $composited_product_variation_id = isset($composite_config[$component_id]['variation_id']) ? $composite_config[$component_id]['variation_id'] : '';
             $composite_config[$component_id]['price'] = $this->get_composited_cart_item_price($composited_product_id, $composited_product_variation_id, $component_id, $composite);
             $composite_config[$component_id] = apply_filters('woocommerce_composite_component_cart_item_identifier', $composite_config[$component_id], $component_id);
         }
         $cart_item_data['composite_data'] = $composite_config;
         // Prepare additional data for later use
         $cart_item_data['composite_children'] = array();
         return $cart_item_data;
     } else {
         return $cart_item_data;
     }
 }
 /**
  * Get min/max composite regular price.
  *
  * @param  string $min_or_max
  * @return double
  */
 public function get_composite_regular_price($min_or_max = 'min', $display = false)
 {
     if ($this->is_priced_per_product()) {
         if (!$this->is_synced()) {
             $this->sync_composite();
         }
         $price = $min_or_max === 'min' ? $this->min_composite_regular_price : $this->max_composite_regular_price;
         if ($price && $display) {
             $display_price = WC_CP()->api->get_composited_product_price($this, $this->get_base_regular_price());
             foreach ($this->price_index['regular_price'][$min_or_max] as $component_id => $product_id) {
                 $component_data = $this->get_component_data($component_id);
                 $item_qty = $component_data['optional'] === 'yes' && $min_or_max === 'min' ? 0 : $component_data['quantity_' . $min_or_max];
                 if ($item_qty) {
                     $composited_product = $this->get_composited_product($component_id, $product_id);
                     $display_price += $item_qty * $composited_product->get_regular_price($min_or_max, true);
                 }
             }
             $price = $display_price;
         }
     } else {
         $price = parent::get_regular_price();
         if ($display) {
             $price = WC_CP_Core_Compatibility::is_wc_version_gte_2_4() ? parent::get_display_price($price) : WC_CP()->api->get_composited_product_price($this, $price);
         }
     }
     return $price;
 }
 /**
  * Initialize composited product price data, if needed.
  *
  * @return void
  */
 public function init()
 {
     // Init prices
     $this->min_price = 0;
     $this->max_price = 0;
     $this->min_regular_price = 0;
     $this->max_regular_price = 0;
     $this->min_price_incl_tax = 0;
     $this->min_price_excl_tax = 0;
     $id = $this->get_product()->id;
     // Sold individually status.
     $this->is_sold_individually = get_post_meta($id, '_sold_individually', true);
     // Purchasable status.
     if (!$this->per_product_pricing && $this->product->price === '') {
         $this->product->price = 0;
     }
     $this->is_purchasable = $this->product->is_purchasable();
     // Calculate product prices.
     if ($this->per_product_pricing && $this->is_purchasable) {
         $composited_product = $this->product;
         $product_type = $composited_product->product_type;
         $this->is_nyp = false;
         /*-----------------------------------------------------------------------------------*/
         /*  Simple Products and Static Bundles.                                              */
         /*-----------------------------------------------------------------------------------*/
         if ($product_type === 'simple') {
             $product_price = $this->get_raw_price();
             $product_regular_price = $this->get_raw_regular_price();
             // Name your price support.
             if (WC_CP()->compatibility->is_nyp($composited_product)) {
                 $product_price = $product_regular_price = WC_Name_Your_Price_Helpers::get_minimum_price($id) ? WC_Name_Your_Price_Helpers::get_minimum_price($id) : 0;
                 $this->is_nyp = true;
             }
             $this->min_price = $this->max_price = $product_price;
             $this->min_regular_price = $this->max_regular_price = $product_regular_price;
             /*-----------------------------------------------------------------------------------*/
             /*  Variable Products.                                                               */
             /*-----------------------------------------------------------------------------------*/
         } elseif ($product_type === 'variable') {
             if (WC_CP_Core_Compatibility::is_wc_version_gte_2_4()) {
                 $variation_prices = $composited_product->get_variation_prices(false);
                 if (!empty($this->component_data['discount']) && apply_filters('woocommerce_composited_product_discount_from_regular', true, $this->component_id, $this->composite->id)) {
                     $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($this->component_data['discount']) && apply_filters('woocommerce_composited_product_discount_from_regular', true, $this->component_id, $this->composite->id)) {
                     // Product may need to be synced.
                     if ($composited_product->get_variation_regular_price('min', false) === false) {
                         $composited_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 ($composited_product->get_variation_price('min', false) === false) {
                         $composited_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 = $composited_product->get_child($min_variation_price_id);
             $max_variation = $composited_product->get_child($max_variation_price_id);
             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;
                 $this->min_price = $this->get_raw_price($min_variation);
                 $this->max_price = $this->get_raw_price($max_variation);
                 $min_variation_regular_price = $this->get_raw_regular_price($min_variation);
                 $max_variation_regular_price = $this->get_raw_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);
             }
             /*-----------------------------------------------------------------------------------*/
             /*  Other Product Types.                                                             */
             /*-----------------------------------------------------------------------------------*/
         } else {
             $price = $this->get_raw_price();
             $this->min_price = apply_filters('woocommerce_composited_product_min_price', $price, $this);
             $this->max_price = apply_filters('woocommerce_composited_product_max_price', $price, $this);
             $regular_price = $this->get_raw_regular_price();
             $this->min_regular_price = apply_filters('woocommerce_composited_product_min_regular_price', $regular_price, $this);
             $this->max_regular_price = apply_filters('woocommerce_composited_product_max_regular_price', $regular_price, $this);
             $this->is_nyp = apply_filters('woocommerce_composited_product_is_nyp', $this->is_nyp, $this);
         }
     }
 }
 /**
  * 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_composite_filter_order_items', false, $order)) {
         $return_items = array();
         foreach ($items as $item_id => $item) {
             if (isset($item['composite_children']) && isset($item['composite_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['composite_cart_key'];
                     // Aggregate contents
                     $meta_key = __('Contents', 'woocommerce-composite-products');
                     $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['composite_parent']) && $child_item['composite_parent'] === $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_CP_Product::get_title_string($child_item['name'], $child_item['qty']);
                             $meta = '';
                             if (!empty($child_item['item_meta'])) {
                                 if (!empty($child_item['item_meta'][__('Part of', 'woocommerce-composite-products')])) {
                                     unset($child_item['item_meta'][__('Part of', 'woocommerce-composite-products')]);
                                 }
                                 if (WC_CP_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_CP_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_CP_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['composite_parent']) && isset($item['composite_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;
 }