/** * Add to cart messages. * * @access public * @param int|array $products * @param bool $show_qty Should qty's be shown? Added in 2.6.0 */ function wc_add_to_cart_message($products, $show_qty = false) { $titles = array(); $count = 0; if (!is_array($products)) { $products = array($products); $show_qty = false; } if (!$show_qty && !is_array($products)) { $products = array_fill_keys(array_values($products), 1); } foreach ($products as $product_id => $qty) { $titles[] = ($qty > 1 ? absint($qty) . ' × ' : '') . sprintf(_x('“%s”', 'Item name in quotes', 'woocommerce'), strip_tags(get_the_title($product_id))); $count += $qty; } $titles = array_filter($titles); $added_text = sprintf(_n('%s has been added to your cart.', '%s have been added to your cart.', $count, 'woocommerce'), wc_format_list_of_items($titles)); // Output success messages if ('yes' === get_option('woocommerce_cart_redirect_after_add')) { $return_to = apply_filters('woocommerce_continue_shopping_redirect', wp_get_raw_referer() ? wp_validate_redirect(wp_get_raw_referer(), false) : wc_get_page_permalink('shop')); $message = sprintf('<a href="%s" class="button wc-forward">%s</a> %s', esc_url($return_to), esc_html__('Continue Shopping', 'woocommerce'), esc_html($added_text)); } else { $message = sprintf('<a href="%s" class="button wc-forward">%s</a> %s', esc_url(wc_get_page_permalink('cart')), esc_html__('View Cart', 'woocommerce'), esc_html($added_text)); } wc_add_notice(apply_filters('wc_add_to_cart_message', $message, $product_id)); }
/** * Add to cart messages. * * @access public * @param int|array $product_id */ function wc_add_to_cart_message($product_id) { if (is_array($product_id)) { $titles = array(); foreach ($product_id as $id) { $titles[] = get_the_title($id); } $added_text = sprintf(__('Added %s to your cart.', 'woocommerce'), wc_format_list_of_items($titles)); } else { $added_text = sprintf(__('"%s" was successfully added to your cart.', 'woocommerce'), get_the_title($product_id)); } // Output success messages if (get_option('woocommerce_cart_redirect_after_add') == 'yes') { $return_to = apply_filters('woocommerce_continue_shopping_redirect', wp_get_referer() ? wp_get_referer() : home_url()); $message = sprintf('<p class="[ text-center ]"> %3$s <br /><br /><a href="%s" class="[ button button--primary ][ no-margin ][ pull-none ][ wc-forward ]">%s</a></p>', $return_to, __('Continue Shopping', 'woocommerce'), $added_text); } else { $message = sprintf('<p class="[ text-center ]"> %3$s <br /><br /><a href="%s" class="[ button button--primary ][ no-margin ][ pull-none ][ wc-forward ]">%s</a></p>', wc_get_page_permalink('cart'), __('View Cart', 'woocommerce'), $added_text); } wc_add_notice(apply_filters('wc_add_to_cart_message', $message, $product_id)); }
/** * Add to cart messages. * * @access public * @param int|array $product_id */ function wc_add_to_cart_message($product_id) { $titles = array(); if (is_array($product_id)) { foreach ($product_id as $id) { $titles[] = get_the_title($id); } } else { $titles[] = get_the_title($product_id); } $titles = array_filter($titles); $added_text = sprintf(_n('%s has been added to your cart.', '%s have been added to your cart.', sizeof($titles), 'woocommerce'), wc_format_list_of_items($titles)); // Output success messages if ('yes' === get_option('woocommerce_cart_redirect_after_add')) { $return_to = apply_filters('woocommerce_continue_shopping_redirect', wp_get_referer() ? wp_get_referer() : home_url()); $message = sprintf('<a href="%s" class="button wc-forward">%s</a> %s', esc_url($return_to), esc_html__('Continue Shopping', 'woocommerce'), esc_html($added_text)); } else { $message = sprintf('<a href="%s" class="button wc-forward">%s</a> %s', esc_url(wc_get_page_permalink('cart')), esc_html__('View Cart', 'woocommerce'), esc_html($added_text)); } wc_add_notice(apply_filters('wc_add_to_cart_message', $message, $product_id)); }
/** * Handle adding variable products to the cart. * @since 2.4.6 Split from add_to_cart_action * @param int $product_id * @return bool success or not */ private static function add_to_cart_handler_variable($product_id) { $adding_to_cart = wc_get_product($product_id); $variation_id = empty($_REQUEST['variation_id']) ? '' : absint($_REQUEST['variation_id']); $quantity = empty($_REQUEST['quantity']) ? 1 : wc_stock_amount($_REQUEST['quantity']); $missing_attributes = array(); $variations = array(); $attributes = $adding_to_cart->get_attributes(); // If no variation ID is set, attempt to get a variation ID from posted attributes. if (empty($variation_id)) { $variation_id = $adding_to_cart->get_matching_variation(wp_unslash($_POST)); } $variation = wc_get_product($variation_id); // Validate the attributes. try { if (empty($variation_id)) { throw new Exception(__('Please choose product options…', 'woocommerce')); } foreach ($attributes as $attribute) { if (!$attribute['is_variation']) { continue; } $taxonomy = 'attribute_' . sanitize_title($attribute['name']); if (isset($_REQUEST[$taxonomy])) { // Get value from post data if ($attribute['is_taxonomy']) { // Don't use wc_clean as it destroys sanitized characters $value = sanitize_title(stripslashes($_REQUEST[$taxonomy])); } else { $value = wc_clean(stripslashes($_REQUEST[$taxonomy])); } // Get valid value from variation $valid_value = isset($variation->variation_data[$taxonomy]) ? $variation->variation_data[$taxonomy] : ''; // Allow if valid or show error. if ('' === $valid_value || $valid_value === $value) { $variations[$taxonomy] = $value; } else { throw new Exception(sprintf(__('Invalid value posted for %s', 'woocommerce'), wc_attribute_label($attribute['name']))); } } else { $missing_attributes[] = wc_attribute_label($attribute['name']); } } if (!empty($missing_attributes)) { throw new Exception(sprintf(_n('%s is a required field', '%s are required fields', sizeof($missing_attributes), 'woocommerce'), wc_format_list_of_items($missing_attributes))); } } catch (Exception $e) { wc_add_notice($e->getMessage(), 'error'); return false; } // Add to cart validation $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations); if ($passed_validation && WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variations) !== false) { wc_add_to_cart_message(array($product_id => $quantity), true); return true; } return false; }
/** * Add to cart action * * Checks for a valid request, does validation (via hooks) and then redirects if valid. * * @param bool $url (default: false) */ public static function add_to_cart_action($url = false) { if (empty($_REQUEST['add-to-cart']) || !is_numeric($_REQUEST['add-to-cart'])) { return; } $product_id = apply_filters('woocommerce_add_to_cart_product_id', absint($_REQUEST['add-to-cart'])); $was_added_to_cart = false; $added_to_cart = array(); $adding_to_cart = wc_get_product($product_id); $add_to_cart_handler = apply_filters('woocommerce_add_to_cart_handler', $adding_to_cart->product_type, $adding_to_cart); // Check if the product is published if ('publish' !== $adding_to_cart->post->post_status) { wc_add_notice(__('Sorry, this product is unavailable.', 'woocommerce'), 'error'); return; } // Variable product handling if ('variable' === $add_to_cart_handler) { $variation_id = empty($_REQUEST['variation_id']) ? '' : absint($_REQUEST['variation_id']); $quantity = empty($_REQUEST['quantity']) ? 1 : wc_stock_amount($_REQUEST['quantity']); $missing_attributes = array(); $variations = array(); $attributes = $adding_to_cart->get_attributes(); $variation = wc_get_product($variation_id); // Verify all attributes foreach ($attributes as $attribute) { if (!$attribute['is_variation']) { continue; } $taxonomy = 'attribute_' . sanitize_title($attribute['name']); if (isset($_REQUEST[$taxonomy])) { // Get value from post data if ($attribute['is_taxonomy']) { // Don't use wc_clean as it destroys sanitized characters $value = sanitize_title(stripslashes($_REQUEST[$taxonomy])); } else { $value = wc_clean(stripslashes($_REQUEST[$taxonomy])); } // Get valid value from variation $valid_value = $variation->variation_data[$taxonomy]; // Allow if valid if ('' === $valid_value || $valid_value === $value) { // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute if (!$attribute['is_taxonomy']) { if ($value === sanitize_title($value) && version_compare(get_post_meta($product_id, '_product_version', true), '2.4.0', '<')) { $text_attributes = wc_get_text_attributes($attribute['value']); foreach ($text_attributes as $text_attribute) { if (sanitize_title($text_attribute) === $value) { $value = $text_attribute; break; } } } } $variations[$taxonomy] = $value; continue; } } else { $missing_attributes[] = wc_attribute_label($attribute['name']); } } if ($missing_attributes) { wc_add_notice(sprintf(_n('%s is a required field', '%s are required fields', sizeof($missing_attributes), 'woocommerce'), wc_format_list_of_items($missing_attributes)), 'error'); return; } elseif (empty($variation_id)) { wc_add_notice(__('Please choose product options…', 'woocommerce'), 'error'); return; } else { // Add to cart validation $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations); if ($passed_validation) { if (WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variations) !== false) { wc_add_to_cart_message($product_id); $was_added_to_cart = true; $added_to_cart[] = $product_id; } } } // Grouped Products } elseif ('grouped' === $add_to_cart_handler) { if (!empty($_REQUEST['quantity']) && is_array($_REQUEST['quantity'])) { $quantity_set = false; foreach ($_REQUEST['quantity'] as $item => $quantity) { if ($quantity <= 0) { continue; } $quantity_set = true; // Add to cart validation $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $item, $quantity); if ($passed_validation) { if (WC()->cart->add_to_cart($item, $quantity) !== false) { $was_added_to_cart = true; $added_to_cart[] = $item; } } } if ($was_added_to_cart) { wc_add_to_cart_message($added_to_cart); } if (!$was_added_to_cart && !$quantity_set) { wc_add_notice(__('Please choose the quantity of items you wish to add to your cart…', 'woocommerce'), 'error'); return; } } elseif ($product_id) { /* Link on product archives */ wc_add_notice(__('Please choose a product to add to your cart…', 'woocommerce'), 'error'); return; } // Custom Handler } elseif (has_action('woocommerce_add_to_cart_handler_' . $add_to_cart_handler)) { do_action('woocommerce_add_to_cart_handler_' . $add_to_cart_handler, $url); return; // Simple Products } else { $quantity = empty($_REQUEST['quantity']) ? 1 : wc_stock_amount($_REQUEST['quantity']); // Add to cart validation $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity); if ($passed_validation) { // Add the product to the cart if (WC()->cart->add_to_cart($product_id, $quantity) !== false) { wc_add_to_cart_message($product_id); $was_added_to_cart = true; $added_to_cart[] = $product_id; } } } // If we added the product to the cart we can now optionally do a redirect. if ($was_added_to_cart && wc_notice_count('error') == 0) { $url = apply_filters('woocommerce_add_to_cart_redirect', $url); // If has custom URL redirect there if ($url) { wp_safe_redirect($url); exit; } elseif (get_option('woocommerce_cart_redirect_after_add') == 'yes') { wp_safe_redirect(WC()->cart->get_cart_url()); exit; } } }
/** * Handle adding variable products to the cart * @since 2.4.6 Split from add_to_cart_action * @param int $product_id * @return bool success or not */ private static function add_to_cart_handler_variable($product_id) { $adding_to_cart = wc_get_product($product_id); $variation_id = empty($_REQUEST['variation_id']) ? '' : absint($_REQUEST['variation_id']); $quantity = empty($_REQUEST['quantity']) ? 1 : wc_stock_amount($_REQUEST['quantity']); $missing_attributes = array(); $variations = array(); $attributes = $adding_to_cart->get_attributes(); $variation = wc_get_product($variation_id); // Verify all attributes foreach ($attributes as $attribute) { if (!$attribute['is_variation']) { continue; } $taxonomy = 'attribute_' . sanitize_title($attribute['name']); if (isset($_REQUEST[$taxonomy])) { // Get value from post data if ($attribute['is_taxonomy']) { // Don't use wc_clean as it destroys sanitized characters $value = sanitize_title(stripslashes($_REQUEST[$taxonomy])); } else { $value = wc_clean(stripslashes($_REQUEST[$taxonomy])); } // Get valid value from variation $valid_value = $variation->variation_data[$taxonomy]; // Allow if valid if ('' === $valid_value || $valid_value === $value) { $variations[$taxonomy] = $value; continue; } } else { $missing_attributes[] = wc_attribute_label($attribute['name']); } } if ($missing_attributes) { wc_add_notice(sprintf(_n('%s is a required field', '%s are required fields', sizeof($missing_attributes), 'woocommerce'), wc_format_list_of_items($missing_attributes)), 'error'); } elseif (empty($variation_id)) { wc_add_notice(__('Please choose product options…', 'woocommerce'), 'error'); } else { // Add to cart validation $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations); if ($passed_validation && WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variations) !== false) { wc_add_to_cart_message($product_id); return true; } } return false; }
/** * 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; }
/** * Test wc_format_list_of_items(). * * @since 2.4 */ public function test_wc_format_list_of_items() { $items = array('Title 1', 'Title 2'); $this->assertEquals('Title 1 and Title 2', wc_format_list_of_items($items)); }
/** * Test wc_format_list_of_items(). * * @since 2.4 */ public function test_wc_format_list_of_items() { $items = array('Title 1', 'Title 2'); $this->assertEquals("“Title 1” and “Title 2”", wc_format_list_of_items($items)); }
/** * Custom add to cart handler for variable products * * Based on function add_to_cart_handler_variable( $product_id ) from * <install_dir>/wp-content/plugins/woocommerce/includes/class-wc-form-handler.php * but using $url as argument.Therefore we use the initial bits from * add_to_cart_action( $url ). * * @param string $url Add to cart url (e.g. https://www.yourdomain.com/?add-to-cart=123&quantity=1&variation_id=117&attribute_size=Small&attribute_color=Black ) */ public function add_to_cart_handler_variable($url) { // From add_to_cart_action( $url ) if (empty($_REQUEST['add-to-cart']) || !is_numeric($_REQUEST['add-to-cart'])) { return; } $product_id = apply_filters('woocommerce_add_to_cart_product_id', absint($_REQUEST['add-to-cart'])); $was_added_to_cart = false; $adding_to_cart = wc_get_product($product_id); if (!$adding_to_cart) { return; } // End: From add_to_cart_action( $url ) // From add_to_cart_handler_variable( $product_id ) $variation_id = empty($_REQUEST['variation_id']) ? '' : absint($_REQUEST['variation_id']); $quantity = empty($_REQUEST['quantity']) ? 1 : wc_stock_amount($_REQUEST['quantity']); $missing_attributes = array(); $variations = array(); $attributes = $adding_to_cart->get_attributes(); // If no variation ID is set, attempt to get a variation ID from posted attributes. if (empty($variation_id)) { $variation_id = $adding_to_cart->get_matching_variation(wp_unslash($_POST)); } /** * Custom code to check if a translation of the product is already in the * cart,* and in that case, replace the variation being added to the cart * by the respective translation in the language of the product already * in the cart. * NOTE: The product_id is filtered by $this->add_to_cart() and holds the * id of the product translation, if one exists in the cart. */ if ($product_id != absint($_REQUEST['add-to-cart'])) { // There is a translation of the product already in the cart: // Get the language of the product in the cart $lang = pll_get_post_language($product_id); // Get the respective variation in the language of the product in the cart $variation = $this->get_variation_translation($variation_id, $lang); $variation_id = $variation->variation_id; } else { $variation = wc_get_product($variation_id); } /** * End of custom code. */ //$variation = wc_get_product( $variation_id ); // Verify all attributes foreach ($attributes as $attribute) { if (!$attribute['is_variation']) { continue; } $taxonomy = 'attribute_' . sanitize_title($attribute['name']); if (isset($_REQUEST[$taxonomy])) { // Get value from post data if ($attribute['is_taxonomy']) { // Don't use wc_clean as it destroys sanitized characters $value = sanitize_title(stripslashes($_REQUEST[$taxonomy])); /** * Custom code to check if a translation of the product is already in the cart, * and in that case, replace the variation attribute being added to the cart by * the respective translation in the language of the product already in the cart * NOTE: The product_id is filtered by $this->add_to_cart() and holds the id of * the product translation, if one exists in the cart. */ if ($product_id != absint($_REQUEST['add-to-cart'])) { // Get the translation of the term $term = get_term_by('slug', $value, $attribute['name']); $_term = get_term_by('id', pll_get_term(absint($term->term_id), $lang), $attribute['name']); if ($_term) { $value = $_term->slug; } } /** * End of custom code. */ } else { $value = wc_clean(stripslashes($_REQUEST[$taxonomy])); } // Get valid value from variation $valid_value = isset($variation->variation_data[$taxonomy]) ? $variation->variation_data[$taxonomy] : ''; // Allow if valid if ('' === $valid_value || $valid_value === $value) { $variations[$taxonomy] = $value; continue; } } else { $missing_attributes[] = wc_attribute_label($attribute['name']); } } if (!empty($missing_attributes)) { wc_add_notice(sprintf(_n('%s is a required field', '%s are required fields', sizeof($missing_attributes), 'woocommerce'), wc_format_list_of_items($missing_attributes)), 'error'); } elseif (empty($variation_id)) { wc_add_notice(__('Please choose product options…', 'woocommerce'), 'error'); } else { // Add to cart validation $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations); if ($passed_validation && WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variations) !== false) { wc_add_to_cart_message(array($product_id => $quantity), true); //return true; Doing an action, no return needed but we need to set $was_added_to_cart to trigger the redirect $was_added_to_cart = true; } else { $was_added_to_cart = false; } } //return false; Doing an action, no return needed but we need to set $was_added_to_cart to trigger the redirect // End: From add_to_cart_handler_variable( $product_id ) /** * Because this is a custom handler we need to take care of the rediret * to the cart. Again we use the code from add_to_cart_action( $url ) */ // From add_to_cart_action( $url ) // If we added the product to the cart we can now optionally do a redirect. if ($was_added_to_cart && wc_notice_count('error') === 0) { // If has custom URL redirect there if ($url = apply_filters('woocommerce_add_to_cart_redirect', $url)) { wp_safe_redirect($url); exit; } elseif (get_option('woocommerce_cart_redirect_after_add') === 'yes') { wp_safe_redirect(wc_get_cart_url()); exit; } } // End: From add_to_cart_action( $url ) }
/** * Validates add-to-cart for bundles. * Basically ensures that stock for all bundled products exists before attempting to add them to cart. * * @param boolean $add core validation add to cart flag * @param int $product_id the product id * @param int $product_quantity quantity * @param mixed $variation_id variation id * @param array $variations variation data * @param array $cart_item_data cart item data * @return boolean modified add to cart validation flag */ public function woo_bundles_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 bundled items from getting validated - they will be added by the container item. if (isset($cart_item_data['is_bundled']) && $order_again) { return false; } if ($product_type === 'bundle') { $product = wc_get_product($product_id); if (!$product) { return false; } if (!apply_filters('woocommerce_bundle_before_validation', true, $product)) { return false; } // 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 stock manager class keeps a record of stock-managed product / variation ids. $bundled_stock = new WC_PB_Stock_Manager($product); // Grab bundled items. $bundled_items = $product->get_bundled_items(); if (!$bundled_items) { return $add; } foreach ($bundled_items as $bundled_item_id => $bundled_item) { $id = $bundled_item->product_id; $bundled_product_type = $bundled_item->product->product_type; // Optional. $is_optional = $bundled_item->is_optional(); if ($is_optional) { if (isset($cart_item_data['stamp'][$bundled_item_id]['optional_selected']) && $order_again) { $is_optional_selected = 'yes' === $cart_item_data['stamp'][$bundled_item_id]['optional_selected'] ? true : false; } elseif (isset($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_selected_optional_' . $bundled_item_id])) { $is_optional_selected = isset($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_selected_optional_' . $bundled_item_id]); } else { $is_optional_selected = false; } } if ($is_optional && !$is_optional_selected) { continue; } // Check quantity. $item_quantity_min = $bundled_item->get_quantity(); $item_quantity_max = $bundled_item->get_quantity('max'); if (isset($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_quantity_' . $bundled_item_id]) && is_numeric($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_quantity_' . $bundled_item_id])) { $item_quantity = absint($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_quantity_' . $bundled_item_id]); } elseif (isset($cart_item_data['stamp'][$bundled_item_id]['quantity']) && $order_again) { $item_quantity = $cart_item_data['stamp'][$bundled_item_id]['quantity']; } else { $item_quantity = $item_quantity_min; } if ($item_quantity < $item_quantity_min) { wc_add_notice(sprintf(__('"%1$s" cannot be added to the cart. The quantity of "%2$s" cannot be lower than %3$d.', 'woocommerce-product-bundles'), get_the_title($product_id), $bundled_item->get_raw_title(), $item_quantity_min), 'error'); return false; } elseif ($item_quantity_max && $item_quantity > $item_quantity_max) { wc_add_notice(sprintf(__('"%1$s" cannot be added to the cart. The quantity of "%2$s" cannot be higher than %3$d.', 'woocommerce-product-bundles'), get_the_title($product_id), $bundled_item->get_raw_title(), $item_quantity_max), 'error'); return false; } $quantity = $item_quantity * $product_quantity; // If quantity is zero, continue. if ($quantity == 0) { continue; } // Purchasable if (!$bundled_item->is_purchasable()) { wc_add_notice(sprintf(__('"%1$s" cannot be added to the cart because "%2$s" cannot be purchased at the moment.', 'woocommerce-product-bundles'), get_the_title($product_id), $bundled_item->get_raw_title()), 'error'); return false; } // Validate variation id. if ($bundled_product_type === 'variable' || $bundled_product_type === 'variable-subscription') { $variation_id = ''; if (isset($cart_item_data['stamp'][$bundled_item_id]['variation_id']) && $order_again) { $variation_id = $cart_item_data['stamp'][$bundled_item_id]['variation_id']; } elseif (isset($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_variation_id_' . $bundled_item_id])) { $variation_id = $_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_variation_id_' . $bundled_item_id]; } if ($variation_id && is_numeric($variation_id) && $variation_id > 1) { if (get_post_meta($variation_id, '_price', true) === '') { wc_add_notice(sprintf(__('"%1$s" cannot be added to the cart. The selected variation of "%2$s" cannot be purchased.', 'woocommerce-product-bundles'), get_the_title($product_id), $bundled_item->product->get_title()), 'error'); return false; } // Add item for validation. $bundled_stock->add_item($id, $variation_id, $quantity); } // Verify all attributes for the variable product were set. $bundled_variation = wc_get_product($variation_id); $attributes = (array) maybe_unserialize(get_post_meta($id, '_product_attributes', true)); $variation_data = array(); $missing_attributes = array(); $all_set = true; if ($bundled_variation) { $variation_data = $bundled_variation->variation_data; // Verify all attributes. foreach ($attributes as $attribute) { if (!$attribute['is_variation']) { continue; } $taxonomy = 'attribute_' . sanitize_title($attribute['name']); if (!empty($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_' . $taxonomy . '_' . $bundled_item_id])) { if (WC_PB_Core_Compatibility::is_wc_version_gte_2_4()) { // Get value from post data. if ($attribute['is_taxonomy']) { $value = sanitize_title(stripslashes($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_' . $taxonomy . '_' . $bundled_item_id])); } else { $value = wc_clean(stripslashes($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_' . $taxonomy . '_' . $bundled_item_id])); } } else { // Get value from post data. $value = sanitize_title(trim(stripslashes($_REQUEST[apply_filters('woocommerce_product_bundle_field_prefix', '', $product_id) . 'bundle_' . $taxonomy . '_' . $bundled_item_id]))); } // 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['stamp'][$bundled_item_id]['attributes'][$taxonomy]) && isset($cart_item_data['stamp'][$bundled_item_id]['variation_id']) && $order_again) { if (WC_PB_Core_Compatibility::is_wc_version_gte_2_4()) { // Get value from post data if ($attribute['is_taxonomy']) { $value = sanitize_title(stripslashes($cart_item_data['stamp'][$bundled_item_id]['attributes'][$taxonomy])); } else { $value = wc_clean(stripslashes($cart_item_data['stamp'][$bundled_item_id]['attributes'][$taxonomy])); } } else { // Get value from post data $value = sanitize_title(trim(stripslashes($cart_item_data['stamp'][$bundled_item_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; } } else { $all_set = false; } if (!$all_set) { if ($missing_attributes && WC_PB_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-product-bundles'), wc_format_list_of_items($missing_attributes), $bundled_item->product->get_title()); wc_add_notice(sprintf(__('"%1$s" cannot be added to the cart. %2$s.', 'woocommerce-product-bundles'), get_the_title($product_id), $required_fields_notice), 'error'); return false; } else { wc_add_notice(sprintf(__('"%1$s" cannot be added to the cart. Please choose "%2$s" options…', 'woocommerce-product-bundles'), get_the_title($product_id), $bundled_item->product->get_title()), 'error'); return false; } } } elseif ($bundled_product_type === 'simple' || $bundled_product_type === 'subscription') { // Add item for validation. $bundled_stock->add_item($id, false, $quantity); } if (!apply_filters('woocommerce_bundled_item_add_to_cart_validation', true, $product, $bundled_item, $quantity, $variation_id)) { return false; } } // Check stock for stock-managed bundled items. // If out of stock, don't proceed. if (false === apply_filters('woocommerce_add_to_cart_bundle_validation', $bundled_stock->validate_stock(), $product_id, $bundled_stock)) { return false; } // Composite Products compatibility. WC_PB_Compatibility::$stock_data = $bundled_stock; } return $add; }
public function validate_rule($rule, $value, $field_config, $product) { $type = is_string($rule) ? $rule : null; $rule_error = null; if (is_array($rule)) { $function = isset($rule['function']) ? $rule['function'] : null; if ($function && is_callable($function)) { return call_user_func_array($function, array($rule, $value, $field_config, $product)); } if (!empty($rule['message'])) { $rule_error = $rule['message']; } if (!empty($rule['type'])) { $type = $rule['type']; } } $errors = array(); switch ($type) { case 'required': if (empty($value)) { $message_template = $rule_error ? $rule_error : esc_html__('Fill in the required field.', 'adventure-tours'); $errors[] = $this->format_message($message_template, $value, $field_config); } break; case 'email': if ($value && !filter_var($value, FILTER_VALIDATE_EMAIL)) { $message_template = $rule_error ? $rule_error : esc_html__('Email invalid.', 'adventure-tours'); $errors[] = $this->format_message($message_template, $value, $field_config); } break; case 'date': if ($value) { $converted_date = $this->convert_date_for_system($value); if (!$converted_date) { $message_template = $rule_error ? $rule_error : esc_html__('Date invalid.', 'adventure-tours'); $errors[] = $this->format_message($message_template, $value, $field_config); } } break; case 'number': if ($value) { $is_config = is_array($rule); $filter_type = $is_config && !empty($rule['float']) ? FILTER_VALIDATE_FLOAT : FILTER_VALIDATE_INT; if (!filter_var($value, $filter_type)) { $message_template = $rule_error ? $rule_error : esc_html__('Check field format.', 'adventure-tours'); $errors[] = $this->format_message($message_template, $value, $field_config); } else { $min = $is_config && !empty($rule['min']) ? $rule['min'] : null; $max = $is_config && !empty($rule['max']) ? $rule['max'] : null; if (null !== $min && $value < $min) { $message_template = $is_config && !empty($rule['min_message']) ? $rule['min_message'] : esc_html__('Min value is', 'adventure-tours') . ' "{min}".'; $errors[] = $this->format_message($message_template, $value, $field_config, array('{min}' => $min, '{max}' => $max)); } elseif (null !== $max && $value > $max) { $message_template = $is_config && !empty($rule['max_message']) ? $rule['max_message'] : esc_html__('Max value is', 'adventure-tours') . ' "{max}".'; $errors[] = $this->format_message($message_template, $value, $field_config, array('{min}' => $min, '{max}' => $max)); } } } break; case 'booking_date': if ($value) { $max_allowed_tickets = $this->get_open_tour_tickets($product, $value); $tickets_in_cart = $this->get_count_tickets_in_cart($product, $value); if ($tickets_in_cart <= $max_allowed_tickets) { $max_allowed_tickets -= $tickets_in_cart; } else { $max_allowed_tickets = 0; } if ($max_allowed_tickets < 1) { $message_template = $rule_error ? $rule_error : esc_html__('There are no tickets for this date.', 'adventure-tours'); $errors[] = $this->format_message($message_template, $value, $field_config); } else { // hack :( $this->set_state($this->make_product_state_key('valid_booking_date', $product), array('max_allowed_tickets' => $max_allowed_tickets)); } } break; case 'booking_tickets': if ($value && $value > 0) { $date_limit_state = $this->get_state($this->make_product_state_key('valid_booking_date', $product), false); $max_allowed_tickets = $date_limit_state && !empty($date_limit_state['max_allowed_tickets']) ? $date_limit_state['max_allowed_tickets'] : 0; // $max_allowed_tickets = $this->get_open_tour_tickets( $product, '{booking_date}' ); if ($max_allowed_tickets > 0 && $value > $max_allowed_tickets) { $message_template = $rule_error ? $rule_error : esc_html(_n('Only 1 ticket is left.', 'Only {left_tickets} tickets are left.', $max_allowed_tickets, 'adventure-tours')); $errors[] = $this->format_message($message_template, $value, $field_config, array('{left_tickets}' => $max_allowed_tickets)); } } break; case 'combined_ticket_number': if ($value && '0' !== $value) { if (!filter_var($value, FILTER_VALIDATE_INT) || $value < 1) { $message_template = $rule_error ? $rule_error : esc_html__('Check field format.', 'adventure-tours'); $errors[] = $this->format_message($message_template, $value, $field_config); } } break; case 'combined_booking_tickets': if ($value && $value > 0) { $combined_quantity_state_key = $this->make_product_state_key('combined_quantity_vals', $product); $combined_quantity_values = $this->get_state($combined_quantity_state_key, array()); $prev_added_quantity = array_sum($combined_quantity_values); $combined_quantity_values[] = $value; $this->set_state($combined_quantity_state_key, $combined_quantity_values); $date_limit_state = $this->get_state($this->make_product_state_key('valid_booking_date', $product), false); $max_allowed_tickets = $date_limit_state && !empty($date_limit_state['max_allowed_tickets']) ? $date_limit_state['max_allowed_tickets'] : 0; $max_allowed_tickets = $max_allowed_tickets - $prev_added_quantity; if ($max_allowed_tickets > 0) { if ($value > $max_allowed_tickets) { $message_template = $rule_error ? $rule_error : esc_html(_n('Only 1 ticket is left.', 'Only {left_tickets} tickets are left.', $max_allowed_tickets, 'adventure-tours')); $errors[] = $this->format_message($message_template, $value, $field_config, array('{left_tickets}' => $max_allowed_tickets)); } } else { $errors[] = esc_html__('There are no more tickets available.', 'adventure-tours'); } } break; case 'variation_id': if ($value > 0) { $missing_attributes = array(); $attributes = $product->get_attributes(); $variation = wc_get_product($value); $variations = array(); $request_data = $this->get_request_data(); // Verify all attributes foreach ($attributes as $attribute) { if (!$attribute['is_variation']) { continue; } $taxonomy = 'attribute_' . sanitize_title($attribute['name']); if (isset($request_data[$taxonomy])) { if ($attribute['is_taxonomy']) { // Don't use wc_clean as it destroys sanitized characters $variation_value = sanitize_title(stripslashes($request_data[$taxonomy])); } else { $variation_value = wc_clean(stripslashes($request_data[$taxonomy])); } // Get valid value from variation $valid_value = $variation->variation_data[$taxonomy]; // Allow if valid if ('' === $valid_value || $valid_value === $variation_value) { $variations[$taxonomy] = $variation_value; continue; } } else { $missing_attributes[] = wc_attribute_label($attribute['name']); } } if ($missing_attributes) { $errors[] = sprintf(_n('%s is a required field', '%s are required fields', sizeof($missing_attributes), 'adventure-tours'), wc_format_list_of_items($missing_attributes)); } elseif (empty($attributes)) { $errors[] = esc_html__('Please choose product options', 'adventure-tours') . '…'; } if (empty($errors)) { $this->set_state($this->make_product_state_key('variations_settings', $product), array('variation_id' => $value, 'variations' => $variations)); } } break; } return $errors; }