/**
  * Returns the following arrays:
  *
  * 1. $scenarios             - contains all scenario ids.
  * 2. $scenario_settings     - includes scenario actions and masked components in scenarios.
  * 3. $scenario_data         - maps every product/variation in a group to the scenarios where it is active.
  * 4. $defaults_in_scenarios - the scenarios where all default component selections coexist.
  *
  * @param  array $bto_scenario_meta     scenarios meta
  * @param  array $bto_data              component data - values may contain a 'current_component_options' key to generate scenarios for a subset of all component options
  * @return array
  */
 public static function build_scenarios($bto_scenario_meta, $bto_data)
 {
     $scenarios = empty($bto_scenario_meta) ? array() : array_map('strval', array_keys($bto_scenario_meta));
     $common_scenarios = $scenarios;
     $scenario_data = array();
     $scenario_settings = array();
     $compat_group_count = 0;
     // Store the 'actions' associated with every scenario.
     foreach ($scenarios as $scenario_id) {
         $scenario_settings['scenario_actions'][$scenario_id] = array();
         if (isset($bto_scenario_meta[$scenario_id]['scenario_actions'])) {
             $actions = array();
             foreach ($bto_scenario_meta[$scenario_id]['scenario_actions'] as $action_name => $action_data) {
                 if (isset($action_data['is_active']) && $action_data['is_active'] === 'yes') {
                     $actions[] = $action_name;
                     if ($action_name === 'compat_group') {
                         $compat_group_count++;
                     }
                 }
             }
             $scenario_settings['scenario_actions'][$scenario_id] = $actions;
         } else {
             $scenario_settings['scenario_actions'][$scenario_id] = array('compat_group');
             $compat_group_count++;
         }
     }
     $scenario_settings['scenario_actions']['0'] = array('compat_group');
     // Find which components in every scenario are 'non shaping components' (marked as unrelated).
     if (!empty($bto_scenario_meta)) {
         foreach ($bto_scenario_meta as $scenario_id => $scenario_single_meta) {
             $scenario_settings['masked_components'][$scenario_id] = array();
             foreach ($bto_data as $group_id => $group_data) {
                 if (isset($scenario_single_meta['modifier'][$group_id]) && $scenario_single_meta['modifier'][$group_id] === 'masked') {
                     $scenario_settings['masked_components'][$scenario_id][] = (string) $group_id;
                 }
             }
         }
     }
     $scenario_settings['masked_components']['0'] = array();
     // Include the '0' scenario for use when no 'compat_group' scenarios exist.
     if ($compat_group_count === 0) {
         $scenarios[] = '0';
     }
     // Map each product and variation to the scenarios that contain it.
     foreach ($bto_data as $group_id => $group_data) {
         $scenario_data[$group_id] = array();
         // 'None' option.
         if ($group_data['optional'] === 'yes') {
             $scenarios_for_product = self::get_scenarios_for_product($bto_scenario_meta, $group_id, -1, '', 'none');
             $scenario_data[$group_id][0] = $scenarios_for_product;
         }
         // Component options.
         // When indicated, build scenarios only based on a limited set of component options.
         if (isset($bto_data[$group_id]['current_component_options'])) {
             $component_options = $bto_data[$group_id]['current_component_options'];
             // Otherwise run a query to get all component options.
         } else {
             $component_options = WC_CP()->api->get_component_options($group_data);
         }
         foreach ($component_options as $product_id) {
             if (!is_numeric($product_id)) {
                 continue;
             }
             // 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 === 'variable') {
                 $variations = WC_CP_Helpers::get_product_variations($product_id);
                 if (!empty($variations)) {
                     $scenarios_for_product = array();
                     foreach ($variations as $variation_id) {
                         $scenarios_for_variation = self::get_scenarios_for_product($bto_scenario_meta, $group_id, $product_id, $variation_id, 'variation');
                         $scenarios_for_product = array_merge($scenarios_for_product, $scenarios_for_variation);
                         $scenario_data[$group_id][$variation_id] = $scenarios_for_variation;
                     }
                     $scenario_data[$group_id][$product_id] = array_values(array_unique($scenarios_for_product));
                 }
             } else {
                 $scenarios_for_product = self::get_scenarios_for_product($bto_scenario_meta, $group_id, $product_id, '', $product_type);
                 $scenario_data[$group_id][$product_id] = $scenarios_for_product;
             }
         }
         if (isset($group_data['default_id']) && $group_data['default_id'] !== '') {
             if (!empty($scenario_data[$group_id][$group_data['default_id']])) {
                 $common_scenarios = array_intersect($common_scenarios, $scenario_data[$group_id][$group_data['default_id']]);
             } else {
                 $common_scenarios = array();
             }
         }
     }
     return array('scenarios' => $scenarios, 'scenario_settings' => $scenario_settings, 'scenario_data' => $scenario_data, 'defaults_in_scenarios' => $common_scenarios);
 }
 /**
  * @deprecated 3.5.0
  */
 public function format_product_title($title, $identifier = '', $meta = '', $paren = false)
 {
     _deprecated_function('WC_CP_API::format_product_title()', '3.5.0', 'WC_CP_Helpers::format_product_title()');
     return WC_CP_Helpers::format_product_title($title, $identifier, $meta, $paren);
 }
 /**
  * Search for component options and echo json.
  *
  * @param   string $x (default: '')
  * @param   string $post_types (default: array('product'))
  * @return  void
  */
 public function json_search_component_options($x = 'default', $post_types = array('product'))
 {
     global $wpdb;
     ob_start();
     check_ajax_referer('search-products', 'security');
     $term = (string) wc_clean(stripslashes($_GET['term']));
     $like_term = '%' . $wpdb->esc_like($term) . '%';
     $composite_id = $_GET['composite_id'];
     $component_id = $_GET['component_id'];
     if (empty($term) || empty($composite_id) || empty($component_id)) {
         die;
     }
     $composite_data = get_post_meta($composite_id, '_bto_data', true);
     $component_data = isset($composite_data[$component_id]) ? $composite_data[$component_id] : false;
     if (false == $composite_data || false == $component_data) {
         die;
     }
     // Run query to get component option ids.
     $component_options = WC_CP()->api->get_component_options($component_data);
     // Add variation ids to component option ids.
     if ($x === 'search_component_options_in_scenario') {
         $variations_args = array('post_type' => array('product_variation'), 'post_status' => 'publish', 'posts_per_page' => -1, 'post_parent' => array_merge(array('0'), $component_options), 'fields' => 'ids');
         $component_options_variations = get_posts($variations_args);
         $component_options = array_merge($component_options, $component_options_variations);
     }
     if (is_numeric($term)) {
         $query = $wpdb->prepare("\n\t\t\t\tSELECT ID FROM {$wpdb->posts} posts LEFT JOIN {$wpdb->postmeta} postmeta ON posts.ID = postmeta.post_id\n\t\t\t\tWHERE posts.post_status = 'publish'\n\t\t\t\tAND (\n\t\t\t\t\tposts.post_parent = %s\n\t\t\t\t\tOR posts.ID = %s\n\t\t\t\t\tOR posts.post_title LIKE %s\n\t\t\t\t\tOR (\n\t\t\t\t\t\tpostmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t", $term, $term, $term, $like_term);
     } else {
         $query = $wpdb->prepare("\n\t\t\t\tSELECT ID FROM {$wpdb->posts} posts LEFT JOIN {$wpdb->postmeta} postmeta ON posts.ID = postmeta.post_id\n\t\t\t\tWHERE posts.post_status = 'publish'\n\t\t\t\tAND (\n\t\t\t\t\tposts.post_title LIKE %s\n\t\t\t\t\tor posts.post_content LIKE %s\n\t\t\t\t\tOR (\n\t\t\t\t\t\tpostmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t", $like_term, $like_term, $like_term);
     }
     $query .= " AND posts.post_type IN ('" . implode("','", array_map('esc_sql', $post_types)) . "')";
     // Include results among component options only.
     $query .= " AND posts.ID IN (" . implode(',', array_map('intval', $component_options)) . ")";
     // Include first 1000 results only.
     $query .= " LIMIT 1000";
     $posts = array_unique($wpdb->get_col($query));
     $found_products = array();
     if ($posts) {
         foreach ($posts as $post) {
             $product = wc_get_product($post);
             if ($product->product_type === 'variation') {
                 $found_products[$post] = WC_CP_Helpers::get_product_variation_title($product);
             } else {
                 if ($x === 'search_component_options_in_scenario' && $product->product_type === 'variable') {
                     $found_products[$post] = WC_CP_Helpers::get_product_title($product) . ' ' . __('— All Variations', 'woocommerce-composite-products');
                 } else {
                     $found_products[$post] = WC_CP_Helpers::get_product_title($product);
                 }
             }
         }
     }
     wp_send_json($found_products);
 }
 /**
  * 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;
 }