/**
  * Validates that all composited items chosen can be added-to-cart before actually starting to add items.
  *
  * @param  bool 	$add
  * @param  int 		$product_id
  * @param  int 		$product_quantity
  * @return bool
  */
 public function wc_cp_validation($add, $product_id, $product_quantity, $variation_id = '', $variations = array(), $cart_item_data = array())
 {
     // 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';
     // Ordering again?
     $order_again = isset($_GET['order_again']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'woocommerce-order_again');
     // prevent composited items from getting validated - they will be added by the container item.
     if (isset($cart_item_data['is_composited']) && $order_again) {
         return false;
     }
     if ($product_type == 'composite') {
         // Get composite data
         $composite = wc_get_product($product_id);
         $composite_data = $composite->get_composite_data();
         $component_ids = array_keys($composite_data);
         // Check request and prepare validation data for stock and scenarios.
         $validate_scenarios = array();
         // If a stock-managed product / variation exists in the bundle multiple times, its stock will be checked only once for the sum of all bundled quantities.
         // The WC_CP_Stock_Manager class does exactly that
         $composited_stock = new WC_CP_Stock_Manager($composite);
         foreach ($component_ids as $component_id) {
             // Check that a product has been selected
             if (isset($_REQUEST['wccp_component_selection'][$component_id]) && $_REQUEST['wccp_component_selection'][$component_id] !== '') {
                 $composited_product_id = $_REQUEST['wccp_component_selection'][$component_id];
             } elseif (isset($cart_item_data['composite_data'][$component_id]['product_id']) && $order_again) {
                 $composited_product_id = $cart_item_data['composite_data'][$component_id]['product_id'];
             } else {
                 $_REQUEST['wccp_component_selection'][$component_id] = '';
                 // Save for later
                 $validate_scenarios[$component_id] = array();
                 $validate_scenarios[$component_id]['product_id'] = '0';
                 $validate_scenarios[$component_id]['product_type'] = 'none';
                 continue;
             }
             // Prevent people from f*****g around - only valid component options can be added to the cart.
             if (!in_array($composited_product_id, WC_CP()->api->get_component_options($composite_data[$component_id]))) {
                 return false;
             }
             $item_quantity_min = absint($composite_data[$component_id]['quantity_min']);
             $item_quantity_max = $composite_data[$component_id]['quantity_max'] !== '' ? absint($composite_data[$component_id]['quantity_max']) : '';
             // Check quantity
             if (isset($_REQUEST['wccp_component_quantity'][$component_id]) && is_numeric($_REQUEST['wccp_component_quantity'][$component_id])) {
                 $item_quantity = absint($_REQUEST['wccp_component_quantity'][$component_id]);
             } elseif (isset($cart_item_data['composite_data'][$component_id]['quantity']) && $order_again) {
                 $item_quantity = absint($cart_item_data['composite_data'][$component_id]['quantity']);
             } else {
                 $item_quantity = $item_quantity_min;
             }
             $quantity = $item_quantity * $product_quantity;
             $item_sold_individually = get_post_meta($composited_product_id, '_sold_individually', true);
             if ($item_sold_individually === 'yes' && $quantity > 1) {
                 $quantity = 1;
             }
             // Save for later.
             $validate_scenarios[$component_id] = array();
             $validate_scenarios[$component_id]['quantity'] = $item_quantity;
             $validate_scenarios[$component_id]['quantity_min'] = $item_quantity_min;
             $validate_scenarios[$component_id]['quantity_max'] = $item_quantity_max;
             $validate_scenarios[$component_id]['sold_individually'] = $item_sold_individually;
             // Get composited product type.
             $composited_product_wrapper = $composite->get_composited_product($component_id, $composited_product_id);
             if (!$composited_product_wrapper) {
                 wc_add_notice(sprintf(__('This "%1$s" configuration cannot be added to the cart. Please choose another "%2$s" option…', 'woocommerce-composite-products'), get_the_title($product_id), apply_filters('woocommerce_composite_component_title', $composite_data[$component_id]['title'], $component_id, $product_id)), 'error');
                 return false;
             }
             $composited_product = $composited_product_wrapper->get_product();
             $composited_product_type = $composited_product->product_type;
             // Save for later.
             $validate_scenarios[$component_id]['product_type'] = $composited_product_type;
             $validate_scenarios[$component_id]['product_id'] = $composited_product_id;
             // Validate attributes.
             if ($composited_product_type === 'variable') {
                 $variation_id = '';
                 if (isset($cart_item_data['composite_data'][$component_id]['variation_id']) && $order_again) {
                     $variation_id = $cart_item_data['composite_data'][$component_id]['variation_id'];
                 } elseif (isset($_REQUEST['wccp_variation_id'][$component_id])) {
                     $variation_id = $_REQUEST['wccp_variation_id'][$component_id];
                 }
                 if ($variation_id && is_numeric($variation_id) && $variation_id > 1) {
                     // Add item for validation.
                     $composited_stock->add_item($composited_product_id, $variation_id, $quantity);
                     // Save for later.
                     $validate_scenarios[$component_id]['variation_id'] = $variation_id;
                 }
                 // Verify all attributes for the variable product were set.
                 $composited_variation = wc_get_product($variation_id);
                 $attributes = $composited_product->get_attributes();
                 $variation_data = array();
                 $missing_attributes = array();
                 $all_set = true;
                 if ($composited_variation) {
                     $variation_data = $composited_variation->variation_data;
                 }
                 foreach ($attributes as $attribute) {
                     if (!$attribute['is_variation']) {
                         continue;
                     }
                     $taxonomy = 'attribute_' . sanitize_title($attribute['name']);
                     if (isset($_REQUEST['wccp_' . $taxonomy][$component_id])) {
                         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]));
                             }
                         } else {
                             // Get value from post data.
                             $value = sanitize_title(trim(stripslashes($_REQUEST['wccp_' . $taxonomy][$component_id])));
                         }
                         // Get valid value from variation.
                         $valid_value = $variation_data[$taxonomy];
                         // Allow if valid.
                         if ($valid_value === '' || $valid_value === $value) {
                             continue;
                         }
                     } elseif (isset($cart_item_data['composite_data'][$component_id]['attributes'][$taxonomy]) && isset($cart_item_data['composite_data'][$component_id]['variation_id']) && $order_again) {
                         if (WC_CP_Core_Compatibility::is_wc_version_gte_2_4()) {
                             // Get value from post data.
                             if ($attribute['is_taxonomy']) {
                                 $value = sanitize_title(stripslashes($cart_item_data['composite_data'][$component_id]['attributes'][$taxonomy]));
                             } else {
                                 $value = wc_clean(stripslashes($cart_item_data['composite_data'][$component_id]['attributes'][$taxonomy]));
                             }
                         } else {
                             // Get value from post data.
                             $value = sanitize_title(trim(stripslashes($cart_item_data['composite_data'][$component_id]['attributes'][$taxonomy])));
                         }
                         $valid_value = $variation_data[$taxonomy];
                         if ($valid_value === '' || $valid_value === $value) {
                             continue;
                         }
                     } else {
                         $missing_attributes[] = wc_attribute_label($attribute['name']);
                     }
                     $all_set = false;
                 }
                 if (!$all_set) {
                     if ($missing_attributes && WC_CP_Core_Compatibility::is_wc_version_gte_2_3()) {
                         $required_fields_notice = sprintf(_n('%1$s is a required "%2$s" field', '%1$s are required "%2$s" fields', sizeof($missing_attributes), 'woocommerce-composite-products'), wc_format_list_of_items($missing_attributes), apply_filters('woocommerce_composite_component_title', $composite_data[$component_id]['title'], $component_id, $product_id));
                         wc_add_notice(sprintf(__('This "%1$s" configuration cannot be added to the cart. %2$s.', 'woocommerce-composite-products'), get_the_title($product_id), $required_fields_notice), 'error');
                         return false;
                     } else {
                         wc_add_notice(sprintf(__('This "%1$s" configuration cannot be added to the cart. Please choose "%2$s" options…', 'woocommerce-composite-products'), get_the_title($product_id), apply_filters('woocommerce_composite_component_title', $composite_data[$component_id]['title'], $component_id, $product_id)), 'error');
                         return false;
                     }
                 }
             } elseif ($composited_product_type === 'simple') {
                 // Add item for validation.
                 $composited_stock->add_item($composited_product_id, false, $quantity);
             } else {
                 // Add item for validation.
                 $composited_stock->add_item($composited_product_id, false, $quantity);
             }
             if (!apply_filters('woocommerce_composite_component_add_to_cart_validation', true, $product_id, $component_id, $composited_product_id, $quantity, $cart_item_data)) {
                 return false;
             }
             // Allow composited products to add extra items to the stock manager.
             $composited_stock->add_stock(apply_filters('woocommerce_composite_component_associated_stock', '', $product_id, $component_id, $composited_product_id, $quantity));
         }
         /*
          * Stock Validation.
          */
         if (false === $composited_stock->validate_stock()) {
             return false;
         }
         /*
          * Scenarios Validation.
          */
         $scenario_data = get_post_meta($product_id, '_bto_scenario_data', true);
         $scenario_data = apply_filters('woocommerce_composite_scenario_meta', $scenario_data, $composite);
         $posted_scenarios = !empty($_POST['wccp_active_scenarios']) ? array_map('wc_clean', explode(',', $_POST['wccp_active_scenarios'])) : array();
         if (!empty($posted_scenarios)) {
             $scenario_data = array_intersect_key($scenario_data, array_flip($posted_scenarios));
         }
         // Build scenarios for the selected combination of options
         foreach ($composite_data as $component_id => &$modified_component_data) {
             if (isset($validate_scenarios[$component_id]) && $validate_scenarios[$component_id]['product_type'] !== 'none') {
                 $modified_component_data['current_component_options'] = array($validate_scenarios[$component_id]['product_id']);
             } else {
                 $modified_component_data['current_component_options'] = array('');
             }
         }
         $scenarios_for_products = apply_filters('woocommerce_composite_validation_scenario_data', WC_CP_Scenarios::build_scenarios($scenario_data, $composite_data), $composite_data, $scenario_data, $product_id);
         $common_scenarios = array_values($scenarios_for_products['scenarios']);
         $common_compat_group_scenarios = WC_CP_Scenarios::filter_scenarios_by_type($common_scenarios, 'compat_group', $scenarios_for_products);
         // Validate Selections.
         foreach ($composite_data as $component_id => $component_data) {
             if (isset($validate_scenarios[$component_id])) {
                 $validate_product_id = isset($validate_scenarios[$component_id]['variation_id']) ? $validate_scenarios[$component_id]['variation_id'] : $validate_scenarios[$component_id]['product_id'];
                 $scenarios_for_product = array();
                 $mandatory_override_check = false;
                 if ($validate_product_id === '0' && $component_data['optional'] === 'no') {
                     $mandatory_override_check = true;
                 }
                 if (!empty($scenarios_for_products['scenario_data'][$component_id][$validate_product_id])) {
                     $scenarios_for_product = $scenarios_for_products['scenario_data'][$component_id][$validate_product_id];
                     $compat_group_scenarios_for_product = WC_CP_Scenarios::filter_scenarios_by_type($scenarios_for_product, 'compat_group', $scenarios_for_products);
                 }
                 if (empty($compat_group_scenarios_for_product) || $mandatory_override_check) {
                     if ($validate_product_id === '0') {
                         // Allow 3rd parties to override notices for empty selections in non-optional components conditionally through scenarios.
                         if (apply_filters('woocommerce_composite_validation_component_is_mandatory', true, $component_id, $validate_scenarios[$component_id], $common_scenarios, $scenarios_for_products, $product_id)) {
                             wc_add_notice(sprintf(__('Please select a "%s" option.', 'woocommerce-composite-products'), apply_filters('woocommerce_composite_component_title', $component_data['title'], $component_id, $product_id)), 'error');
                             return false;
                         }
                     } else {
                         wc_add_notice(sprintf(__('Please select a different "%s" option — the selected product cannot be purchased at the moment.', 'woocommerce-composite-products'), apply_filters('woocommerce_composite_component_title', $component_data['title'], $component_id, $product_id)), 'error');
                         return false;
                     }
                 } else {
                     $common_scenarios = array_intersect($common_scenarios, $scenarios_for_product);
                     $common_compat_group_scenarios = array_intersect($common_compat_group_scenarios, $compat_group_scenarios_for_product);
                 }
             }
         }
         if (empty($common_compat_group_scenarios)) {
             wc_add_notice(__('The selected options cannot be purchased together. Please select a different configuration and try again.', 'woocommerce-composite-products'), 'error');
             return false;
         }
         // Validate Quantities.
         foreach ($composite_data as $component_id => $component_data) {
             if (!isset($validate_scenarios[$component_id]) || $validate_scenarios[$component_id]['product_type'] === 'none') {
                 continue;
             }
             $qty = $validate_scenarios[$component_id]['quantity'];
             // Allow 3rd parties to modify the min/max qty settings of a component conditionally through scenarios.
             $qty_min = absint(apply_filters('woocommerce_composite_validation_component_quantity_min', $component_data['quantity_min'], $component_id, $validate_scenarios[$component_id], $common_scenarios, $scenarios_for_products, $product_id));
             $qty_max = absint(apply_filters('woocommerce_composite_validation_component_quantity_max', $component_data['quantity_max'], $component_id, $validate_scenarios[$component_id], $common_scenarios, $scenarios_for_products, $product_id));
             $sold_individually = $validate_scenarios[$component_id]['sold_individually'];
             if ($qty < $qty_min && $sold_individually !== 'yes') {
                 wc_add_notice(sprintf(__('This &quot;%1$s&quot; configuration cannot be added to the cart. The quantity of &quot;%2$s&quot; cannot be lower than %3$d.', 'woocommerce-composite-products'), get_the_title($product_id), apply_filters('woocommerce_composite_component_title', $composite_data[$component_id]['title'], $component_id, $product_id), $qty_min), 'error');
                 return false;
             } elseif ($qty_max && $qty > $qty_max) {
                 wc_add_notice(sprintf(__('This &quot;%1$s&quot; configuration cannot be added to the cart. The quantity of &quot;%2$s&quot; cannot be higher than %3$d.', 'woocommerce-composite-products'), get_the_title($product_id), apply_filters('woocommerce_composite_component_title', $composite_data[$component_id]['title'], $component_id, $product_id), $qty_max), 'error');
                 return false;
             }
         }
         $add = apply_filters('woocommerce_add_to_cart_composite_validation', $add, $product_id, $composited_stock);
     }
     return $add;
 }
 /**
  * Save components and scenarios.
  *
  * @param  int   $post_id
  * @param  array $posted_composite_data
  * @return boolean
  */
 public function save_composite_config($post_id, $posted_composite_data)
 {
     global $wpdb;
     // Composite style.
     $composite_layout = 'single';
     if (isset($posted_composite_data['bto_style'])) {
         $composite_layout = stripslashes($posted_composite_data['bto_style']);
     }
     update_post_meta($post_id, '_bto_style', $composite_layout);
     // Composite selection mode.
     $composite_options_style = 'dropdowns';
     if (isset($posted_composite_data['bto_selection_mode'])) {
         $composite_options_style = stripslashes($posted_composite_data['bto_selection_mode']);
     }
     update_post_meta($post_id, '_bto_selection_mode', $composite_options_style);
     // Process Composite Product Configuration.
     $zero_product_item_exists = false;
     $component_options_count = 0;
     $bto_data = get_post_meta($post_id, '_bto_data', true);
     if (!$bto_data) {
         $bto_data = array();
     }
     if (isset($posted_composite_data['bto_data'])) {
         /*--------------------------*/
         /*  Components.             */
         /*--------------------------*/
         $counter = 0;
         $ordering = array();
         foreach ($posted_composite_data['bto_data'] as $row_id => $post_data) {
             $bto_ids = isset($post_data['assigned_ids']) ? $post_data['assigned_ids'] : '';
             $bto_cat_ids = isset($post_data['assigned_category_ids']) ? $post_data['assigned_category_ids'] : '';
             $group_id = isset($post_data['group_id']) ? stripslashes($post_data['group_id']) : current_time('timestamp') + $counter;
             $counter++;
             $bto_data[$group_id] = array();
             // Save component id.
             $bto_data[$group_id]['component_id'] = $group_id;
             // Save query type.
             if (isset($post_data['query_type']) && !empty($post_data['query_type'])) {
                 $bto_data[$group_id]['query_type'] = stripslashes($post_data['query_type']);
             } else {
                 $bto_data[$group_id]['query_type'] = 'product_ids';
             }
             if (!empty($bto_ids)) {
                 if (is_array($bto_ids)) {
                     $bto_ids = array_map('intval', $post_data['assigned_ids']);
                 } else {
                     $bto_ids = array_filter(array_map('intval', explode(',', $post_data['assigned_ids'])));
                 }
                 foreach ($bto_ids as $key => $id) {
                     // Get product type.
                     $terms = get_the_terms($id, 'product_type');
                     $product_type = !empty($terms) && isset(current($terms)->name) ? sanitize_title(current($terms)->name) : 'simple';
                     if ($id && $id > 0 && in_array($product_type, apply_filters('woocommerce_composite_products_supported_types', array('simple', 'variable', 'bundle'))) && $post_id != $id) {
                         // Check that product exists
                         if (!get_post($id)) {
                             continue;
                         }
                         $error = apply_filters('woocommerce_composite_products_custom_type_save_error', false, $id);
                         if ($error) {
                             $this->save_errors[] = $this->add_admin_error($error);
                             continue;
                         }
                         // Save assigned ids.
                         $bto_data[$group_id]['assigned_ids'][] = $id;
                     }
                 }
                 if (!empty($bto_data[$group_id]['assigned_ids'])) {
                     $bto_data[$group_id]['assigned_ids'] = array_unique($bto_data[$group_id]['assigned_ids']);
                 }
             }
             if (!empty($bto_cat_ids)) {
                 $bto_cat_ids = array_map('absint', $post_data['assigned_category_ids']);
                 $bto_data[$group_id]['assigned_category_ids'] = array_values($bto_cat_ids);
             }
             // True if no products were added.
             if ($bto_data[$group_id]['query_type'] === 'product_ids' && empty($bto_data[$group_id]['assigned_ids']) || $bto_data[$group_id]['query_type'] === 'category_ids' && empty($bto_data[$group_id]['assigned_category_ids'])) {
                 unset($bto_data[$group_id]);
                 $zero_product_item_exists = true;
                 continue;
             }
             // Run query to get component option ids.
             $component_options = WC_CP()->api->get_component_options($bto_data[$group_id]);
             // Add up options.
             $component_options_count += count($component_options);
             // Save default preferences.
             if (isset($post_data['default_id']) && !empty($post_data['default_id']) && count($component_options) > 0) {
                 if (in_array($post_data['default_id'], $component_options)) {
                     $bto_data[$group_id]['default_id'] = stripslashes($post_data['default_id']);
                 } else {
                     $bto_data[$group_id]['default_id'] = '';
                 }
             } else {
                 // If the component option is only one, set it as default.
                 if (count($component_options) == 1 && !isset($post_data['optional'])) {
                     $bto_data[$group_id]['default_id'] = $component_options[0];
                 } else {
                     $bto_data[$group_id]['default_id'] = '';
                 }
             }
             // Save title preferences.
             if (isset($post_data['title']) && !empty($post_data['title'])) {
                 $bto_data[$group_id]['title'] = strip_tags(stripslashes($post_data['title']));
             } else {
                 $bto_data[$group_id]['title'] = 'Untitled Component';
                 $this->save_errors[] = $this->add_admin_error(__('Please give a valid Name to all Components before publishing.', 'woocommerce-composite-products'));
                 if (isset($posted_composite_data['post_status']) && $posted_composite_data['post_status'] === 'publish') {
                     global $wpdb;
                     $wpdb->update($wpdb->posts, array('post_status' => 'draft'), array('ID' => $post_id));
                 }
             }
             // Save description preferences.
             if (isset($post_data['description']) && !empty($post_data['description'])) {
                 $bto_data[$group_id]['description'] = wp_kses_post(stripslashes($post_data['description']));
             } else {
                 $bto_data[$group_id]['description'] = '';
             }
             // Save min quantity data.
             if (isset($post_data['quantity_min']) && is_numeric($post_data['quantity_min'])) {
                 $quantity_min = absint($post_data['quantity_min']);
                 if ($quantity_min >= 0) {
                     $bto_data[$group_id]['quantity_min'] = $quantity_min;
                 } else {
                     $this->save_errors[] = $this->add_admin_error(sprintf(__('The Min Quantity entered for &quot;%s&quot; was not valid and has been reset. Please enter a non-negative integer value.', 'woocommerce-composite-products'), strip_tags(stripslashes($post_data['title']))));
                     $bto_data[$group_id]['quantity_min'] = 1;
                 }
             } else {
                 // If its not there, it means the product was just added.
                 $bto_data[$group_id]['quantity_min'] = 1;
                 $this->save_errors[] = $this->add_admin_error(sprintf(__('The Min Quantity entered for &quot;%s&quot; was not valid and has been reset. Please enter a non-negative integer value.', 'woocommerce-composite-products'), strip_tags(stripslashes($post_data['title']))));
             }
             $quantity_min = $bto_data[$group_id]['quantity_min'];
             // Save max quantity data.
             if (isset($post_data['quantity_max']) && (is_numeric($post_data['quantity_max']) || $post_data['quantity_max'] === '')) {
                 $quantity_max = $post_data['quantity_max'] !== '' ? absint($post_data['quantity_max']) : '';
                 if ($quantity_max === '' || $quantity_max > 0 && $quantity_max >= $quantity_min) {
                     $bto_data[$group_id]['quantity_max'] = $quantity_max;
                 } else {
                     $this->save_errors[] = $this->add_admin_error(sprintf(__('The Max Quantity you entered for &quot;%s&quot; was not valid and has been reset. Please enter a positive integer value greater than (or equal to) Quantity Min, or leave the field empty.', 'woocommerce-composite-products'), strip_tags(stripslashes($post_data['title']))));
                     $bto_data[$group_id]['quantity_max'] = 1;
                 }
             } else {
                 // If its not there, it means the product was just added.
                 $bto_data[$group_id]['quantity_max'] = 1;
                 $this->save_errors[] = $this->add_admin_error(sprintf(__('The Max Quantity you entered for &quot;%s&quot; was not valid and has been reset. Please enter a positive integer value greater than (or equal to) Quantity Min, or leave the field empty.', 'woocommerce-composite-products'), strip_tags(stripslashes($post_data['title']))));
             }
             // Save discount data.
             if (isset($post_data['discount'])) {
                 if (is_numeric($post_data['discount'])) {
                     $discount = (double) wc_format_decimal($post_data['discount']);
                     if ($discount < 0 || $discount > 100) {
                         $this->save_errors[] = $this->add_admin_error(sprintf(__('The discount value you entered for &quot;%s&quot; was not valid and has been reset. Please enter a positive number between 0-100.', 'woocommerce-composite-products'), strip_tags(stripslashes($post_data['title']))));
                         $bto_data[$group_id]['discount'] = '';
                     } else {
                         $bto_data[$group_id]['discount'] = $discount;
                     }
                 } else {
                     $bto_data[$group_id]['discount'] = '';
                 }
             } else {
                 $bto_data[$group_id]['discount'] = '';
             }
             // Save optional data.
             if (isset($post_data['optional'])) {
                 $bto_data[$group_id]['optional'] = 'yes';
             } else {
                 $bto_data[$group_id]['optional'] = 'no';
             }
             // Save hide product title data.
             if (isset($post_data['hide_product_title'])) {
                 $bto_data[$group_id]['hide_product_title'] = 'yes';
             } else {
                 $bto_data[$group_id]['hide_product_title'] = 'no';
             }
             // Save hide product description data.
             if (isset($post_data['hide_product_description'])) {
                 $bto_data[$group_id]['hide_product_description'] = 'yes';
             } else {
                 $bto_data[$group_id]['hide_product_description'] = 'no';
             }
             // Save hide product thumbnail data.
             if (isset($post_data['hide_product_thumbnail'])) {
                 $bto_data[$group_id]['hide_product_thumbnail'] = 'yes';
             } else {
                 $bto_data[$group_id]['hide_product_thumbnail'] = 'no';
             }
             // Save show orderby data.
             if (isset($post_data['show_orderby'])) {
                 $bto_data[$group_id]['show_orderby'] = 'yes';
             } else {
                 $bto_data[$group_id]['show_orderby'] = 'no';
             }
             // Save show filters data.
             if (isset($post_data['show_filters'])) {
                 $bto_data[$group_id]['show_filters'] = 'yes';
             } else {
                 $bto_data[$group_id]['show_filters'] = 'no';
             }
             // Save filters.
             if (!empty($post_data['attribute_filters'])) {
                 $attribute_filter_ids = array_map('absint', $post_data['attribute_filters']);
                 $bto_data[$group_id]['attribute_filters'] = array_values($attribute_filter_ids);
             }
             // Prepare position data.
             if (isset($post_data['position'])) {
                 $ordering[(int) $post_data['position']] = $group_id;
             } else {
                 $ordering[count($ordering)] = $group_id;
             }
             // Process custom data - add custom errors via WC_CP()->admin->add_error().
             $bto_data[$group_id] = apply_filters('woocommerce_composite_process_component_data', $bto_data[$group_id], $post_data, $group_id, $post_id);
             // Invalidate query cache.
             if (class_exists('WC_Cache_Helper')) {
                 WC_Cache_Helper::get_transient_version('wccp_q', true);
             }
             if (!wp_using_ext_object_cache()) {
                 // Delete query transients
                 $wpdb->query("DELETE FROM `{$wpdb->options}` WHERE `option_name` LIKE ('_transient_wccp_q_" . $group_id . "_%') OR `option_name` LIKE ('_transient_timeout_wccp_q_" . $group_id . "_%')");
             }
         }
         ksort($ordering);
         $ordered_bto_data = array();
         $ordering_loop = 0;
         foreach ($ordering as $group_id) {
             $ordered_bto_data[$group_id] = $bto_data[$group_id];
             $ordered_bto_data[$group_id]['position'] = $ordering_loop;
             $ordering_loop++;
         }
         // Prompt user to activate the right options when a Composite includes a large number of Component Options.
         if ($component_options_count > 100) {
             $show_large_composite_prompt = false;
             $ppp_prompt = '';
             $dropdowns_prompt = '';
             if (isset($_POST['_per_product_pricing_bto']) && empty($_POST['_bto_hide_shop_price'])) {
                 $show_large_composite_prompt = true;
                 $ppp_prompt = __(' To avoid placing a big load on your server, consider checking the Hide Price option, located in the General tab. This setting will bypass all min/max pricing calculations which typically happen during product load when the Per-Item Pricing option is checked.', 'woocommerce-composite-products');
             }
             if (isset($posted_composite_data['bto_selection_mode']) && $posted_composite_data['bto_selection_mode'] === 'dropdowns') {
                 $show_large_composite_prompt = true;
                 $dropdowns_prompt = sprintf(__(' %s consider switching the Options Style of your Composite to Product Thumbnails. This setting can be changed from the Components tab.', 'woocommerce-composite-products'), $ppp_prompt === '' ? __('To reduce the load on your server and make the configuration process easier,', 'woocommerce-composite-products') : __('To further reduce the load on your server and make the configuration process easier,', 'woocommerce-composite-products'));
             }
             if ($show_large_composite_prompt) {
                 $large_composite_prompt = sprintf(__('You have added a significant number of Component Options to this Composite.%1$s%2$s', 'woocommerce-composite-products'), $ppp_prompt, $dropdowns_prompt);
                 $this->save_errors[] = $this->add_admin_error($large_composite_prompt);
             }
         }
         /*--------------------------*/
         /*  Scenarios.              */
         /*--------------------------*/
         // Convert posted data coming from select2 ajax inputs.
         $compat_scenario_data = array();
         if (isset($posted_composite_data['bto_scenario_data'])) {
             foreach ($posted_composite_data['bto_scenario_data'] as $scenario_id => $scenario_post_data) {
                 $compat_scenario_data[$scenario_id] = $scenario_post_data;
                 if (isset($scenario_post_data['component_data'])) {
                     foreach ($scenario_post_data['component_data'] as $component_id => $products_in_scenario) {
                         if (!empty($products_in_scenario)) {
                             if (is_array($products_in_scenario)) {
                                 $compat_scenario_data[$scenario_id]['component_data'][$component_id] = array_unique(array_map('intval', $products_in_scenario));
                             } else {
                                 $compat_scenario_data[$scenario_id]['component_data'][$component_id] = array_unique(array_map('intval', explode(',', $products_in_scenario)));
                             }
                         } else {
                             $compat_scenario_data[$scenario_id]['component_data'][$component_id] = array();
                         }
                     }
                 }
             }
             $posted_composite_data['bto_scenario_data'] = $compat_scenario_data;
         }
         // End conversion.
         // Start processing.
         $bto_scenario_data = array();
         $ordered_bto_scenario_data = array();
         $compat_group_actions_exist = false;
         $masked_rules_exist = false;
         if (isset($posted_composite_data['bto_scenario_data'])) {
             $counter = 0;
             $scenario_ordering = array();
             foreach ($posted_composite_data['bto_scenario_data'] as $scenario_id => $scenario_post_data) {
                 $scenario_id = isset($scenario_post_data['scenario_id']) ? stripslashes($scenario_post_data['scenario_id']) : current_time('timestamp') + $counter;
                 $counter++;
                 $bto_scenario_data[$scenario_id] = array();
                 // Save scenario title.
                 if (isset($scenario_post_data['title']) && !empty($scenario_post_data['title'])) {
                     $bto_scenario_data[$scenario_id]['title'] = strip_tags(stripslashes($scenario_post_data['title']));
                 } else {
                     unset($bto_scenario_data[$scenario_id]);
                     $this->save_errors[] = $this->add_admin_error(__('Please give a valid Name to all Scenarios before saving.', 'woocommerce-composite-products'));
                     continue;
                 }
                 // Save scenario description.
                 if (isset($scenario_post_data['description']) && !empty($scenario_post_data['description'])) {
                     $bto_scenario_data[$scenario_id]['description'] = wp_kses_post(stripslashes($scenario_post_data['description']));
                 } else {
                     $bto_scenario_data[$scenario_id]['description'] = '';
                 }
                 // Prepare position data.
                 if (isset($scenario_post_data['position'])) {
                     $scenario_ordering[(int) $scenario_post_data['position']] = $scenario_id;
                 } else {
                     $scenario_ordering[count($scenario_ordering)] = $scenario_id;
                 }
                 $bto_scenario_data[$scenario_id]['scenario_actions'] = array();
                 // Save scenario action(s).
                 if (isset($scenario_post_data['scenario_actions']['compat_group'])) {
                     if (!empty($scenario_post_data['scenario_actions']['compat_group']['is_active'])) {
                         $bto_scenario_data[$scenario_id]['scenario_actions']['compat_group']['is_active'] = 'yes';
                         $compat_group_actions_exist = true;
                     }
                 } else {
                     $bto_scenario_data[$scenario_id]['scenario_actions']['compat_group']['is_active'] = 'no';
                 }
                 // Save component options in scenario.
                 $bto_scenario_data[$scenario_id]['component_data'] = array();
                 foreach ($ordered_bto_data as $group_id => $group_data) {
                     // Save modifier flag.
                     if (isset($scenario_post_data['modifier'][$group_id]) && $scenario_post_data['modifier'][$group_id] === 'not-in') {
                         if (!empty($scenario_post_data['component_data'][$group_id])) {
                             if (WC_CP_Scenarios::scenario_contains_product($scenario_post_data, $group_id, 0)) {
                                 $bto_scenario_data[$scenario_id]['modifier'][$group_id] = 'in';
                             } else {
                                 $bto_scenario_data[$scenario_id]['modifier'][$group_id] = 'not-in';
                             }
                         } else {
                             $bto_scenario_data[$scenario_id]['modifier'][$group_id] = 'in';
                         }
                     } elseif (isset($scenario_post_data['modifier'][$group_id]) && $scenario_post_data['modifier'][$group_id] === 'masked') {
                         $bto_scenario_data[$scenario_id]['modifier'][$group_id] = 'masked';
                         $masked_rules_exist = true;
                         if (!WC_CP_Scenarios::scenario_contains_product($scenario_post_data, $group_id, 0)) {
                             $scenario_post_data['component_data'][$group_id][] = 0;
                         }
                     } else {
                         $bto_scenario_data[$scenario_id]['modifier'][$group_id] = 'in';
                     }
                     $all_active = false;
                     if (!empty($scenario_post_data['component_data'][$group_id])) {
                         $bto_scenario_data[$scenario_id]['component_data'][$group_id] = array();
                         if (WC_CP_Scenarios::scenario_contains_product($scenario_post_data, $group_id, 0)) {
                             $bto_scenario_data[$scenario_id]['component_data'][$group_id][] = 0;
                             $all_active = true;
                         }
                         if ($all_active) {
                             continue;
                         }
                         if (WC_CP_Scenarios::scenario_contains_product($scenario_post_data, $group_id, -1)) {
                             $bto_scenario_data[$scenario_id]['component_data'][$group_id][] = -1;
                         }
                         // Run query to get component option ids.
                         $component_options = WC_CP()->api->get_component_options($group_data);
                         foreach ($scenario_post_data['component_data'][$group_id] as $item_in_scenario) {
                             if ((int) $item_in_scenario === -1 || (int) $item_in_scenario === 0) {
                                 continue;
                             }
                             // Get product.
                             $product_in_scenario = get_product($item_in_scenario);
                             if ($product_in_scenario->product_type === 'variation') {
                                 $parent_id = $product_in_scenario->id;
                                 if ($parent_id && in_array($parent_id, $component_options) && !in_array($parent_id, $scenario_post_data['component_data'][$group_id])) {
                                     $bto_scenario_data[$scenario_id]['component_data'][$group_id][] = $item_in_scenario;
                                 }
                             } else {
                                 if (in_array($item_in_scenario, $component_options)) {
                                     $bto_scenario_data[$scenario_id]['component_data'][$group_id][] = $item_in_scenario;
                                 }
                             }
                         }
                     } else {
                         $bto_scenario_data[$scenario_id]['component_data'][$group_id] = array();
                         $bto_scenario_data[$scenario_id]['component_data'][$group_id][] = 0;
                     }
                 }
                 // Process custom data - add custom errors via WC_CP()->admin->add_error().
                 $bto_scenario_data[$scenario_id] = apply_filters('woocommerce_composite_process_scenario_data', $bto_scenario_data[$scenario_id], $scenario_post_data, $scenario_id, $ordered_bto_data, $post_id);
             }
             // Re-order and save position data.
             ksort($scenario_ordering);
             $ordering_loop = 0;
             foreach ($scenario_ordering as $scenario_id) {
                 $ordered_bto_scenario_data[$scenario_id] = $bto_scenario_data[$scenario_id];
                 $ordered_bto_scenario_data[$scenario_id]['position'] = $ordering_loop;
                 $ordering_loop++;
             }
         }
         // Verify defaults.
         if (!empty($ordered_bto_scenario_data)) {
             // Stacked layout notices.
             if ($composite_layout === 'single' && $masked_rules_exist) {
                 $this->save_errors[] = $this->add_admin_error(__('The use of Component masks in Scenarios is only available with the Progressive, Stepped, and Componentized layout options. To keep your Scenario settings intact, the Composite has been set to use the Progressive layout.', 'woocommerce-composite-products'));
                 update_post_meta($post_id, '_bto_style', 'progressive');
             } elseif ($composite_layout === 'single' && $compat_group_actions_exist) {
                 $this->save_errors[] = $this->add_admin_error(__('For a more streamlined user experience in applications that involve Scenarios and dependent Component Options, please consider selecting the Progressive, Stepped or Componentized layout.', 'woocommerce-composite-products'));
             }
             // Only build scenarios for the defaults.
             foreach ($ordered_bto_data as $group_id => $group_data) {
                 $bto_data[$group_id]['current_component_options'] = array($group_data['default_id']);
             }
             $scenarios_for_products = WC_CP_Scenarios::build_scenarios($ordered_bto_scenario_data, $bto_data);
             $common_scenarios = array_values($scenarios_for_products['scenarios']);
             foreach ($ordered_bto_data as $group_id => $group_data) {
                 $default_option_id = $group_data['default_id'];
                 if ($default_option_id !== '') {
                     if (empty($scenarios_for_products['scenario_data'][$group_id][$default_option_id])) {
                         $this->save_errors[] = $this->add_admin_error(sprintf(__('The default option that you selected for &quot;%s&quot; is not active in any Scenarios. The default Component Options must be compatible in order to work. Always double-check your preferences before saving, and always save any changes made to the Component Options before choosing new defaults.', 'woocommerce-composite-products'), $group_data['title']));
                     } else {
                         $common_scenarios = array_intersect($common_scenarios, $scenarios_for_products['scenario_data'][$group_id][$default_option_id]);
                     }
                 }
             }
             if (empty($common_scenarios)) {
                 $this->save_errors[] = $this->add_admin_error(__('The set of default Component Options that you selected was not found in any of the defined Scenarios. The default Component Options must be compatible in order to work. Always double-check the default Component Options before creating or modifying Scenarios.', 'woocommerce-composite-products'));
             }
         }
         // Save config.
         update_post_meta($post_id, '_bto_data', $ordered_bto_data);
         update_post_meta($post_id, '_bto_scenario_data', $ordered_bto_scenario_data);
     }
     if (!isset($posted_composite_data['bto_data']) || count($bto_data) == 0) {
         delete_post_meta($post_id, '_bto_data');
         $this->save_errors[] = $this->add_admin_error(__('Please create at least one Component before publishing. To add a Component, go to the Components tab and click on the Add Component button.', 'woocommerce-composite-products'));
         if (isset($posted_composite_data['post_status']) && $posted_composite_data['post_status'] === 'publish') {
             global $wpdb;
             $wpdb->update($wpdb->posts, array('post_status' => 'draft'), array('ID' => $post_id));
         }
         return false;
     }
     if ($zero_product_item_exists) {
         $this->save_errors[] = $this->add_admin_error(__('Please assign at least one valid Component Option to every Component. Once you have added a Component, you can add Component Options to it by selecting products individually, or by choosing product categories.', 'woocommerce-composite-products'));
         return false;
     }
     return true;
 }
 /**
  * Scenario data arrays used by JS scripts.
  *
  * @return array
  */
 public function get_composite_scenario_data()
 {
     $composite_scenario_meta = get_post_meta($this->id, '_bto_scenario_data', true);
     $composite_scenario_meta = apply_filters('woocommerce_composite_scenario_meta', $composite_scenario_meta, $this);
     $composite_data = $this->get_composite_data();
     foreach ($composite_data as $component_id => &$component_data) {
         $current_component_options = $this->get_current_component_options($component_id);
         $default_option = $this->get_component_default_option($component_id);
         if ($default_option && !in_array($default_option, $current_component_options)) {
             $current_component_options[] = $default_option;
         }
         $component_data['current_component_options'] = $current_component_options;
     }
     $composite_scenario_data = WC_CP_Scenarios::build_scenarios($composite_scenario_meta, $composite_data);
     return apply_filters('woocommerce_composite_initial_scenario_data', $composite_scenario_data, $composite_data, $composite_scenario_meta, $this);
 }
 /**
  * @deprecated 3.5.0
  */
 public function product_active_in_scenario($scenario_data, $group_id, $product_id, $variation_id, $product_type)
 {
     _deprecated_function('WC_CP_API::product_active_in_scenario()', '3.5.0', 'WC_CP_Scenarios::product_active_in_scenario()');
     return WC_CP_Scenarios::product_active_in_scenario($scenario_data, $group_id, $product_id, $variation_id, $product_type);
 }