/** * get_option( 'woocommerce_price_num_decimals' ) cache. * * @return string */ function wc_cp_price_num_decimals() { $wc_price_num_decimals = WC_CP()->api->cache_get('wc_price_num_decimals'); if (null === $wc_price_num_decimals) { $wc_price_num_decimals = absint(get_option('woocommerce_price_num_decimals')); WC_CP()->api->cache_set('wc_price_num_decimals', $wc_price_num_decimals); } return $wc_price_num_decimals; }
/** * Load scripts for use by QV on non-product pages. * * @return void */ public static function load_scripts() { if (!is_product()) { WC_CP()->display->wc_cp_frontend_scripts(); // Enqueue script. wp_enqueue_script('wc-add-to-cart-composite'); // Enqueue styles. wp_enqueue_style('wc-composite-single-css'); } }
/** * Remove filters that modify bundled product prices when parent product is composited and has a discount. * * @param string $cart_item_data * @return void */ public static function bundled_cart_item_after_price_modification($cart_item_data) { if (isset($cart_item_data['bundled_by'])) { $bundle_key = $cart_item_data['bundled_by']; if (isset(WC()->cart->cart_contents[$bundle_key])) { $bundle_cart_data = WC()->cart->cart_contents[$bundle_key]; if (isset($bundle_cart_data['composite_parent'])) { $composite_key = $bundle_cart_data['composite_parent']; if (isset(WC()->cart->cart_contents[$composite_key])) { WC_CP()->api->remove_composited_product_filters(); } } } } return $cart_item_data; }
/** * OPC Single-product bundle-type add-to-cart template * * @param int $opc_post_id * @return void */ public static function opc_single_add_to_cart_composite($opc_post_id) { global $product; // Enqueue scripts wp_enqueue_script('wc-add-to-cart-composite'); // Enqueue styles wp_enqueue_style('wc-composite-single-css'); // Load NYP scripts if (function_exists('WC_Name_Your_Price')) { WC_Name_Your_Price()->display->nyp_scripts(); } // Enqueue Bundle styles if (class_exists('WC_Bundles')) { wp_enqueue_style('wc-bundle-css'); } $navigation_style = $product->get_composite_layout_style(); $components = $product->get_composite_data(); ob_start(); if (!empty($components)) { wc_get_template('single-product/add-to-cart/composite.php', array('navigation_style' => $navigation_style, 'components' => $components, 'product' => $product), '', WC_CP()->plugin_path() . '/templates/'); } echo str_replace(array('<form method="post" enctype="multipart/form-data"', '</form>'), array('<div', '</div>'), ob_get_clean()); }
/** * Get component thumbnail. * * @param string $component_id * @return array */ public function get_component_image($component_id) { $component_data = $this->get_component_data($component_id); if (!$component_data) { return ''; } $image_src = ''; if (!empty($component_data['thumbnail_id'])) { $image_src_data = wp_get_attachment_image_src($component_data['thumbnail_id'], apply_filters('woocommerce_composite_component_image_size', 'shop_catalog')); $image_src = $image_src_data ? current($image_src_data) : false; } if (!$image_src) { $image_src = WC_CP()->plugin_url() . '/assets/images/placeholder.png'; } $image = sprintf('<img class="summary_element_content" alt="%s" src="%s"/>', __('Component image', 'woocommerce-composite-products'), $image_src); // Add class="norefresh" to prevent summary image updates and keep the original image static. // Return '' to hide all images from the summary section. return apply_filters('woocommerce_composite_component_image', $image, $image_src, $component_id, $this); }
/** * Composited single variation template. * * @param WC_Product_Variable $product * @param string $component_id * @param WC_Product_Composite $composite * @return void */ function wc_cp_composited_single_variation($product, $component_id, $composite) { $component_data = $composite->get_component_data($component_id); $quantity_min = $component_data['quantity_min']; $quantity_max = $component_data['quantity_max']; if ($product->sold_individually === 'yes') { $quantity_max = 1; $quantity_min = min($quantity_min, 1); } wc_get_template('composited-product/variation.php', array('quantity_min' => $quantity_min, 'quantity_max' => $quantity_max, 'component_id' => $component_id, 'product' => $product, 'composite_product' => $composite), '', WC_CP()->plugin_path() . '/templates/'); }
?> " data-item_id="<?php echo $component_id; ?> " style="display:none;"> <div class="component_title_wrapper"><?php $title = apply_filters('woocommerce_composite_component_title', esc_html($component_data['title']), $component_id, $product->id); wc_get_template('single-product/component-title.php', array('title' => apply_filters('woocommerce_composite_component_step_title', sprintf(__('<span class="step_index">%d</span> <span class="step_title">%s</span>', 'woocommerce-composite-products'), $step, $title), $title, $step, $steps, $product), 'toggled' => false), '', WC_CP()->plugin_path() . '/templates/'); ?> </div> <div class="component_inner"> <div class="component_description_wrapper"><?php if ($component_data['description'] !== '') { wc_get_template('single-product/component-description.php', array('description' => apply_filters('woocommerce_composite_component_description', wpautop(do_shortcode(wp_kses_post($component_data['description']))), $component_id, $product->id)), '', WC_CP()->plugin_path() . '/templates/'); } ?> </div> <div class="component_selections"><?php /** * woocommerce_composite_component_selections_paged hook (multi-page) * * @hooked wc_cp_add_sorting - 15 * @hooked wc_cp_add_filtering - 20 * @hooked wc_cp_add_component_options - 25 * @hooked wc_cp_add_component_options_pagination - 26 * @hooked wc_cp_add_current_selection_details - 30 */ do_action('woocommerce_composite_component_selections_paged', $component_id, $product); ?>
/** * Get bundled item regular price after discount. * * @param string $min_or_max * @param boolean $display * @return double */ public function get_regular_price($min_or_max = 'min', $display = false) { if (!$this->exists()) { return false; } $prop = $min_or_max . '_regular_price_product'; $product = !empty($this->{$prop}) ? $this->{$prop} : $this->product; $price = $min_or_max === 'min' ? $this->min_regular_price : $this->max_regular_price; $price = apply_filters('woocommerce_get_regular_price', $price, $product); return apply_filters('woocommerce_composited_product_get_regular_price', $display ? WC_CP()->api->get_composited_product_price($product, $price) : $price, $min_or_max, $display, $this); }
/** * Adds component title preambles to order-details template. * * @param string $content * @param array $order_item * @return string */ public function wc_cp_order_table_component_title($content, $order_item) { if (!empty($order_item['composite_item'])) { $item_id = $order_item['composite_item']; $composite_data = maybe_unserialize($order_item['composite_data']); $composite_id = !empty($composite_data[$item_id]['composite_id']) ? $composite_data[$item_id]['composite_id'] : ''; $item_title = apply_filters('woocommerce_composite_component_title', $composite_data[$item_id]['title'], $item_id, $composite_id); $item_quantity = apply_filters('woocommerce_composited_order_item_quantity_html', '<strong class="composited-product-quantity">' . sprintf(_x(' × %s', 'qty string', 'woocommerce-composite-products'), $order_item['qty']) . '</strong>', $order_item); if (did_action('woocommerce_view_order') || did_action('woocommerce_thankyou')) { $item_data = array('key' => $item_title, 'value' => $content . $item_quantity); $this->wc_cp_enqueue_composited_table_item_js(); ob_start(); wc_get_template('component-item.php', array('component_data' => $item_data), '', WC_CP()->plugin_path() . '/templates/'); $content = ob_get_clean(); } elseif (did_action('woocommerce_email_before_order_table') > did_action('woocommerce_email_after_order_table')) { $content = '<small><span style="display:block">' . wp_kses_post($item_title) . ':</span> ' . wp_kses_post($content) . '</small>'; } } return $content; }
/** * Ajax listener that fetches product markup when a new selection is made. * * @param mixed $product_id * @param mixed $item_id * @param mixed $container_id * @return string */ public static function show_composited_product_ajax($product_id = '', $component_id = '', $composite_id = '') { global $product; if (isset($_POST['product_id']) && intval($_POST['product_id']) > 0 && isset($_POST['component_id']) && !empty($_POST['component_id']) && isset($_POST['composite_id']) && !empty($_POST['composite_id'])) { $product_id = intval($_POST['product_id']); $component_id = intval($_POST['component_id']); $composite_id = intval($_POST['composite_id']); } else { wp_send_json(array('result' => 'failure', 'reason' => 'required params missing', 'markup' => sprintf('<div class="component_data woocommerce-error" data-component_set="false" data-price="0" data-regular_price="0" data-product_type="invalid-data">%s</div>', __('Sorry, the selected item cannot be purchased at the moment.', 'woocommerce-composite-products')))); } $composite = wc_get_product($composite_id); $composited_product = $composite->get_composited_product($component_id, $product_id); $product = $composited_product->get_product(); if (!$product || !$composited_product->is_purchasable()) { wp_send_json(array('result' => 'failure', 'reason' => 'product does not exist or is not purchasable', 'markup' => sprintf('<div class="component_data woocommerce-error" data-component_set="false" data-price="0" data-regular_price="0" data-product_type="invalid-product">%s</div>', __('Sorry, the selected item cannot be purchased at the moment.', 'woocommerce-composite-products')))); } $composite->sync_composite(); ob_start(); WC_CP()->api->apply_composited_product_filters($product, $component_id, $composite); do_action('woocommerce_composite_show_composited_product', $product, $component_id, $composite); WC_CP()->api->remove_composited_product_filters(); $output = ob_get_clean(); wp_send_json(array('result' => 'success', 'markup' => $output)); }
/** * 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); }
/** * Support scanning for template overrides in extension. * * @param array $paths * @return array */ public function composite_template_scan_path($paths) { $paths['WooCommerce Composite Products'] = WC_CP()->plugin_path() . '/templates/'; return $paths; }
} ?> <form method="post" enctype="multipart/form-data" class="composite_form"><?php $loop = 0; $steps = count($components); /** * woocommerce_composite_before_components hook * * @hooked wc_cp_before_components - 10 */ do_action('woocommerce_composite_before_components', $components, $product); foreach ($components as $component_id => $component_data) { $loop++; if ($navigation_style == 'single') { wc_get_template('single-product/component-single-page.php', array('product' => $product, 'component_id' => $component_id, 'component_data' => $component_data, 'step' => $loop, 'steps' => $steps), '', WC_CP()->plugin_path() . '/templates/'); } elseif ($navigation_style == 'progressive') { wc_get_template('single-product/component-single-page-progressive.php', array('product' => $product, 'component_id' => $component_id, 'component_data' => $component_data, 'step' => $loop, 'steps' => $steps), '', WC_CP()->plugin_path() . '/templates/'); } else { wc_get_template('single-product/component-multi-page.php', array('product' => $product, 'component_id' => $component_id, 'component_data' => $component_data, 'step' => $loop, 'steps' => $steps), '', WC_CP()->plugin_path() . '/templates/'); } } /** * woocommerce_composite_after_components hook * * @hooked wc_cp_after_components - 10 * @hooked wc_cp_no_js_msg - 15 */ do_action('woocommerce_composite_after_components', $components, $product); ?> </form>
{ delete_option('woocommerce_composite_products_version'); } /** * Show row meta on the plugin screen. * * @param mixed $links Plugin Row Meta * @param mixed $file Plugin Base file * @return array */ public function plugin_meta_links($links, $file) { if ($file == plugin_basename(__FILE__)) { $links[] = '<a href="http://docs.woothemes.com/document/composite-products/">' . __('Docs', 'woocommerce-composite-products') . '</a>'; $links[] = '<a href="http://support.woothemes.com/">' . __('Support', 'woocommerce-composite-products') . '</a>'; } return $links; } } /** * Returns the main instance of WC_Composite_Products to prevent the need to use globals. * * @since 3.2.3 * @return WooCommerce Composite Products */ function WC_CP() { return WC_Composite_Products::instance(); } $GLOBALS['woocommerce_composite_products'] = WC_CP();
public function tm_post_class($classes = "") { if (is_admin() || !$this->tm_related_products_output) { return $classes; } global $post; $post_id = get_the_ID(); if ($post && ('product' == get_post_type($post_id) || $this->wc_vars['is_product'])) { $has_options = $this->get_product_tm_epos($post_id); if (is_array($has_options) && (!empty($has_options['global']) || !empty($has_options['local']))) { $classes[] = 'tm-has-options'; } elseif ($this->tm_epo_enable_final_total_box_all == "yes") { $classes[] = 'tm-no-options-pxq'; } else { $terms = get_the_terms($post_id, 'product_type'); $product_type = !empty($terms) && isset(current($terms)->name) ? sanitize_title(current($terms)->name) : 'simple'; if (($product_type == 'bto' || $product_type == 'composite') && !(is_array($has_options) && (!empty($has_options['global']) || !empty($has_options['local']))) && $this->tm_epo_enable_final_total_box_all != "yes") { // search components for options $product = wc_get_product($post_id); $composite_data = $product->get_composite_data(); foreach ($composite_data as $component_id => $component_data) { $component_options = array(); if (function_exists('WC_CP')) { $component_options = WC_CP()->api->get_component_options($component_data); } else { global $woocommerce_composite_products; if (is_object($woocommerce_composite_products) && function_exists('WC_CP')) { $component_options = WC_CP()->api->get_component_options($component_data); } else { if (isset($component_data['assigned_ids']) && is_array($component_data['assigned_ids'])) { $component_options = $component_data['assigned_ids']; } } } foreach ($component_options as $key => $pid) { $has_options = $this->get_product_tm_epos($pid); if (is_array($has_options) && (!empty($has_options['global']) || !empty($has_options['local']))) { $classes[] = 'tm-no-options-composite'; return $classes; } } } } $classes[] = 'tm-no-options'; } } return $classes; }
" data-product_type="simple" data-custom="<?php echo esc_attr(json_encode($custom_data)); ?> "><?php /** * woocommerce_composited_product_details hook. * @since 3.2.0 * * @hooked wc_cp_composited_product_excerpt - 10 */ do_action('woocommerce_composited_product_details', $product, $component_id, $composite_product); ?> <div class="component_wrap"><?php /** * woocommerce_composited_product_add_to_cart hook. * * @hooked wc_cp_composited_product_price - 8 */ do_action('woocommerce_composited_product_add_to_cart', $product, $component_id, $composite_product); $availability = WC_CP()->api->get_composited_item_availability($product, $quantity_min); $availability_html = empty($availability['availability']) ? '' : '<p class="stock ' . esc_attr($availability['class']) . '">' . esc_html($availability['availability']) . '</p>'; echo apply_filters('woocommerce_stock_html', $availability_html, $availability['availability'], $product); ?> <div class="quantity_button"><?php wc_get_template('composited-product/quantity.php', array('quantity_min' => $quantity_min, 'quantity_max' => $quantity_max, 'component_id' => $component_id, 'product' => $product, 'composite_product' => $composite_product), '', WC_CP()->plugin_path() . '/templates/'); ?> </div> </div> </div>
/** * 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 "%1$s" configuration cannot be added to the cart. The quantity of "%2$s" 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 "%1$s" configuration cannot be added to the cart. The quantity of "%2$s" 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; }
echo $is_static ? 'display:none;' : ''; ?> "> <div class="component_options_inner cp_clearfix"> <p class="component_section_title"> <label class="select_label"> <?php echo $selection_mode === 'thumbnails' ? __('Available options:', 'woocommerce-composite-products') : __('Select an option…', 'woocommerce-composite-products'); ?> </label> </p><?php // Thumbnails template if ($selection_mode === 'thumbnails') { wc_get_template('single-product/component-option-thumbnails.php', array('product' => $product, 'component_id' => $component_id, 'quantity_min' => $quantity_min, 'quantity_max' => $quantity_max, 'component_options' => $component_options, 'selected_option' => $selected_option), '', WC_CP()->plugin_path() . '/templates/'); } ?> <div class="component_options_select_wrapper" style="<?php echo $selection_mode === 'thumbnails' ? 'display:none;' : ''; ?> "> <select id="component_options_<?php echo $component_id; ?> " class="component_options_select" name="wccp_component_selection[<?php echo $component_id; ?> ]"><?php if (!$is_static) { ?>