/** * Loads an array of attributes used for variations, as well as their possible values. * * @param WC_Product */ private function read_variation_attributes(&$product) { global $wpdb; $variation_attributes = array(); $attributes = $product->get_attributes(); $child_ids = $product->get_children(); if (!empty($child_ids) && !empty($attributes)) { foreach ($attributes as $attribute) { if (empty($attribute['is_variation'])) { continue; } // Get possible values for this attribute, for only visible variations. $values = array_unique($wpdb->get_col($wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s AND post_id IN (" . implode(',', array_map('esc_sql', $child_ids)) . ")", wc_variation_attribute_name($attribute['name'])))); // Empty value indicates that all options for given attribute are available. if (in_array('', $values) || empty($values)) { $values = $attribute['is_taxonomy'] ? wc_get_object_terms($product->get_id(), $attribute['name'], 'slug') : wc_get_text_attributes($attribute['value']); // Get custom attributes (non taxonomy) as defined. } elseif (!$attribute['is_taxonomy']) { $text_attributes = wc_get_text_attributes($attribute['value']); $assigned_text_attributes = $values; $values = array(); // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute if (version_compare(get_post_meta($product->get_id(), '_product_version', true), '2.4.0', '<')) { $assigned_text_attributes = array_map('sanitize_title', $assigned_text_attributes); foreach ($text_attributes as $text_attribute) { if (in_array(sanitize_title($text_attribute), $assigned_text_attributes)) { $values[] = $text_attribute; } } } else { foreach ($text_attributes as $text_attribute) { if (in_array($text_attribute, $assigned_text_attributes)) { $values[] = $text_attribute; } } } } $variation_attributes[$attribute['name']] = array_unique($values); } } $product->set_variation_attributes($variation_attributes); }
/** * 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; } } }
/** * Save meta box data */ public static function save($post_id, $post) { global $wpdb; // Add any default post meta add_post_meta($post_id, 'total_sales', '0', true); // Get types $product_type = empty($_POST['product-type']) ? 'simple' : sanitize_title(stripslashes($_POST['product-type'])); $is_downloadable = isset($_POST['_downloadable']) ? 'yes' : 'no'; $is_virtual = isset($_POST['_virtual']) ? 'yes' : 'no'; // Product type + Downloadable/Virtual wp_set_object_terms($post_id, $product_type, 'product_type'); update_post_meta($post_id, '_downloadable', $is_downloadable); update_post_meta($post_id, '_virtual', $is_virtual); // Update post meta if (isset($_POST['_regular_price'])) { update_post_meta($post_id, '_regular_price', $_POST['_regular_price'] === '' ? '' : wc_format_decimal($_POST['_regular_price'])); } if (isset($_POST['_sale_price'])) { update_post_meta($post_id, '_sale_price', $_POST['_sale_price'] === '' ? '' : wc_format_decimal($_POST['_sale_price'])); } if (isset($_POST['_tax_status'])) { update_post_meta($post_id, '_tax_status', wc_clean($_POST['_tax_status'])); } if (isset($_POST['_tax_class'])) { update_post_meta($post_id, '_tax_class', wc_clean($_POST['_tax_class'])); } if (isset($_POST['_purchase_note'])) { update_post_meta($post_id, '_purchase_note', wp_kses_post(stripslashes($_POST['_purchase_note']))); } // Featured if (update_post_meta($post_id, '_featured', isset($_POST['_featured']) ? 'yes' : 'no')) { delete_transient('wc_featured_products'); } // Dimensions if ('no' == $is_virtual) { if (isset($_POST['_weight'])) { update_post_meta($post_id, '_weight', '' === $_POST['_weight'] ? '' : wc_format_decimal($_POST['_weight'])); } if (isset($_POST['_length'])) { update_post_meta($post_id, '_length', '' === $_POST['_length'] ? '' : wc_format_decimal($_POST['_length'])); } if (isset($_POST['_width'])) { update_post_meta($post_id, '_width', '' === $_POST['_width'] ? '' : wc_format_decimal($_POST['_width'])); } if (isset($_POST['_height'])) { update_post_meta($post_id, '_height', '' === $_POST['_height'] ? '' : wc_format_decimal($_POST['_height'])); } } else { update_post_meta($post_id, '_weight', ''); update_post_meta($post_id, '_length', ''); update_post_meta($post_id, '_width', ''); update_post_meta($post_id, '_height', ''); } // Save shipping class $product_shipping_class = $_POST['product_shipping_class'] > 0 && $product_type != 'external' ? absint($_POST['product_shipping_class']) : ''; wp_set_object_terms($post_id, $product_shipping_class, 'product_shipping_class'); // Unique SKU $sku = get_post_meta($post_id, '_sku', true); $new_sku = wc_clean(stripslashes($_POST['_sku'])); if ('' == $new_sku) { update_post_meta($post_id, '_sku', ''); } elseif ($new_sku !== $sku) { if (!empty($new_sku)) { $unique_sku = wc_product_has_unique_sku($post_id, $new_sku); if (!$unique_sku) { WC_Admin_Meta_Boxes::add_error(__('Product SKU must be unique.', 'woocommerce')); } else { update_post_meta($post_id, '_sku', $new_sku); } } else { update_post_meta($post_id, '_sku', ''); } } // Save Attributes $attributes = array(); if (isset($_POST['attribute_names']) && isset($_POST['attribute_values'])) { $attribute_names = $_POST['attribute_names']; $attribute_values = $_POST['attribute_values']; if (isset($_POST['attribute_visibility'])) { $attribute_visibility = $_POST['attribute_visibility']; } if (isset($_POST['attribute_variation'])) { $attribute_variation = $_POST['attribute_variation']; } $attribute_is_taxonomy = $_POST['attribute_is_taxonomy']; $attribute_position = $_POST['attribute_position']; $attribute_names_max_key = max(array_keys($attribute_names)); for ($i = 0; $i <= $attribute_names_max_key; $i++) { if (empty($attribute_names[$i])) { continue; } $is_visible = isset($attribute_visibility[$i]) ? 1 : 0; $is_variation = isset($attribute_variation[$i]) ? 1 : 0; $is_taxonomy = $attribute_is_taxonomy[$i] ? 1 : 0; if ($is_taxonomy) { if (isset($attribute_values[$i])) { // Select based attributes - Format values (posted values are slugs) if (is_array($attribute_values[$i])) { $values = array_map('sanitize_title', $attribute_values[$i]); // Text based attributes - Posted values are term names - don't change to slugs } else { $values = array_map('stripslashes', array_map('strip_tags', explode(WC_DELIMITER, $attribute_values[$i]))); } // Remove empty items in the array $values = array_filter($values, 'strlen'); } else { $values = array(); } // Update post terms if (taxonomy_exists($attribute_names[$i])) { foreach ($values as $key => $value) { $term = get_term_by('name', trim($value), $attribute_names[$i]); if ($term) { $values[$key] = intval($term->term_id); } else { $term = wp_insert_term(trim($value), $attribute_names[$i]); if (isset($term->term_id)) { $values[$key] = intval($term->term_id); } } } wp_set_object_terms($post_id, $values, $attribute_names[$i]); } if (!empty($values)) { // Add attribute to array, but don't set values $attributes[sanitize_title($attribute_names[$i])] = array('name' => wc_clean($attribute_names[$i]), 'value' => '', 'position' => $attribute_position[$i], 'is_visible' => $is_visible, 'is_variation' => $is_variation, 'is_taxonomy' => $is_taxonomy); } } elseif (isset($attribute_values[$i])) { // Text based, separate by pipe $values = implode(' ' . WC_DELIMITER . ' ', array_map('wc_clean', wc_get_text_attributes($attribute_values[$i]))); // Custom attribute - Add attribute to array and set the values $attributes[sanitize_title($attribute_names[$i])] = array('name' => wc_clean($attribute_names[$i]), 'value' => $values, 'position' => $attribute_position[$i], 'is_visible' => $is_visible, 'is_variation' => $is_variation, 'is_taxonomy' => $is_taxonomy); } } } if (!function_exists('attributes_cmp')) { function attributes_cmp($a, $b) { if ($a['position'] == $b['position']) { return 0; } return $a['position'] < $b['position'] ? -1 : 1; } } uasort($attributes, 'attributes_cmp'); update_post_meta($post_id, '_product_attributes', $attributes); // Sales and prices if (in_array($product_type, array('variable', 'grouped'))) { // Variable and grouped products have no prices update_post_meta($post_id, '_regular_price', ''); update_post_meta($post_id, '_sale_price', ''); update_post_meta($post_id, '_sale_price_dates_from', ''); update_post_meta($post_id, '_sale_price_dates_to', ''); update_post_meta($post_id, '_price', ''); } else { $date_from = isset($_POST['_sale_price_dates_from']) ? wc_clean($_POST['_sale_price_dates_from']) : ''; $date_to = isset($_POST['_sale_price_dates_to']) ? wc_clean($_POST['_sale_price_dates_to']) : ''; // Dates if ($date_from) { update_post_meta($post_id, '_sale_price_dates_from', strtotime($date_from)); } else { update_post_meta($post_id, '_sale_price_dates_from', ''); } if ($date_to) { update_post_meta($post_id, '_sale_price_dates_to', strtotime($date_to)); } else { update_post_meta($post_id, '_sale_price_dates_to', ''); } if ($date_to && !$date_from) { $date_from = date('Y-m-d'); update_post_meta($post_id, '_sale_price_dates_from', strtotime($date_from)); } // Update price if on sale if ('' !== $_POST['_sale_price'] && '' == $date_to && '' == $date_from) { update_post_meta($post_id, '_price', wc_format_decimal($_POST['_sale_price'])); } else { update_post_meta($post_id, '_price', $_POST['_regular_price'] === '' ? '' : wc_format_decimal($_POST['_regular_price'])); } if ('' !== $_POST['_sale_price'] && $date_from && strtotime($date_from) <= strtotime('NOW', current_time('timestamp'))) { update_post_meta($post_id, '_price', wc_format_decimal($_POST['_sale_price'])); } if ($date_to && strtotime($date_to) < strtotime('NOW', current_time('timestamp'))) { update_post_meta($post_id, '_price', $_POST['_regular_price'] === '' ? '' : wc_format_decimal($_POST['_regular_price'])); update_post_meta($post_id, '_sale_price_dates_from', ''); update_post_meta($post_id, '_sale_price_dates_to', ''); } } // Update parent if grouped so price sorting works and stays in sync with the cheapest child if ($post->post_parent > 0 || 'grouped' == $product_type || $_POST['previous_parent_id'] > 0) { $clear_parent_ids = array(); if ($post->post_parent > 0) { $clear_parent_ids[] = $post->post_parent; } if ('grouped' == $product_type) { $clear_parent_ids[] = $post_id; } if ($_POST['previous_parent_id'] > 0) { $clear_parent_ids[] = absint($_POST['previous_parent_id']); } if (!empty($clear_parent_ids)) { foreach ($clear_parent_ids as $clear_id) { $children_by_price = get_posts(array('post_parent' => $clear_id, 'orderby' => 'meta_value_num', 'order' => 'asc', 'meta_key' => '_price', 'posts_per_page' => 1, 'post_type' => 'product', 'fields' => 'ids')); if ($children_by_price) { foreach ($children_by_price as $child) { $child_price = get_post_meta($child, '_price', true); update_post_meta($clear_id, '_price', $child_price); } } wc_delete_product_transients($clear_id); } } } // Sold Individually if (!empty($_POST['_sold_individually'])) { update_post_meta($post_id, '_sold_individually', 'yes'); } else { update_post_meta($post_id, '_sold_individually', ''); } // Stock Data if ('yes' === get_option('woocommerce_manage_stock')) { $manage_stock = 'no'; $backorders = 'no'; $stock_status = wc_clean($_POST['_stock_status']); if ('external' === $product_type) { $stock_status = 'instock'; } elseif ('variable' === $product_type) { // Stock status is always determined by children so sync later $stock_status = ''; if (!empty($_POST['_manage_stock'])) { $manage_stock = 'yes'; $backorders = wc_clean($_POST['_backorders']); } } elseif ('grouped' !== $product_type && !empty($_POST['_manage_stock'])) { $manage_stock = 'yes'; $backorders = wc_clean($_POST['_backorders']); } update_post_meta($post_id, '_manage_stock', $manage_stock); update_post_meta($post_id, '_backorders', $backorders); if ($stock_status) { wc_update_product_stock_status($post_id, $stock_status); } if (!empty($_POST['_manage_stock'])) { wc_update_product_stock($post_id, wc_stock_amount($_POST['_stock'])); } else { update_post_meta($post_id, '_stock', ''); } } else { wc_update_product_stock_status($post_id, wc_clean($_POST['_stock_status'])); } // Cross sells and upsells $upsells = isset($_POST['upsell_ids']) ? array_filter(array_map('intval', explode(',', $_POST['upsell_ids']))) : array(); $crosssells = isset($_POST['crosssell_ids']) ? array_filter(array_map('intval', explode(',', $_POST['crosssell_ids']))) : array(); update_post_meta($post_id, '_upsell_ids', $upsells); update_post_meta($post_id, '_crosssell_ids', $crosssells); // Downloadable options if ('yes' == $is_downloadable) { $_download_limit = absint($_POST['_download_limit']); if (!$_download_limit) { $_download_limit = ''; // 0 or blank = unlimited } $_download_expiry = absint($_POST['_download_expiry']); if (!$_download_expiry) { $_download_expiry = ''; // 0 or blank = unlimited } // file paths will be stored in an array keyed off md5(file path) $files = array(); if (isset($_POST['_wc_file_urls'])) { $file_names = isset($_POST['_wc_file_names']) ? $_POST['_wc_file_names'] : array(); $file_urls = isset($_POST['_wc_file_urls']) ? array_map('trim', $_POST['_wc_file_urls']) : array(); $file_url_size = sizeof($file_urls); $allowed_file_types = apply_filters('woocommerce_downloadable_file_allowed_mime_types', get_allowed_mime_types()); for ($i = 0; $i < $file_url_size; $i++) { if (!empty($file_urls[$i])) { // Find type and file URL if (0 === strpos($file_urls[$i], 'http')) { $file_is = 'absolute'; $file_url = esc_url_raw($file_urls[$i]); } elseif ('[' === substr($file_urls[$i], 0, 1) && ']' === substr($file_urls[$i], -1)) { $file_is = 'shortcode'; $file_url = wc_clean($file_urls[$i]); } else { $file_is = 'relative'; $file_url = wc_clean($file_urls[$i]); } $file_name = wc_clean($file_names[$i]); $file_hash = md5($file_url); // Validate the file extension if (in_array($file_is, array('absolute', 'relative'))) { $file_type = wp_check_filetype(strtok($file_url, '?')); $parsed_url = parse_url($file_url, PHP_URL_PATH); $extension = pathinfo($parsed_url, PATHINFO_EXTENSION); if (!empty($extension) && !in_array($file_type['type'], $allowed_file_types)) { WC_Admin_Meta_Boxes::add_error(sprintf(__('The downloadable file %s cannot be used as it does not have an allowed file type. Allowed types include: %s', 'woocommerce'), '<code>' . basename($file_url) . '</code>', '<code>' . implode(', ', array_keys($allowed_file_types)) . '</code>')); continue; } } // Validate the file exists if ('relative' === $file_is) { $_file_url = $file_url; if ('..' === substr($file_url, 0, 2) || '/' !== substr($file_url, 0, 1)) { $_file_url = realpath(ABSPATH . $file_url); } if (!apply_filters('woocommerce_downloadable_file_exists', file_exists($_file_url), $file_url)) { WC_Admin_Meta_Boxes::add_error(sprintf(__('The downloadable file %s cannot be used as it does not exist on the server.', 'woocommerce'), '<code>' . $file_url . '</code>')); continue; } } $files[$file_hash] = array('name' => $file_name, 'file' => $file_url); } } } // grant permission to any newly added files on any existing orders for this product prior to saving do_action('woocommerce_process_product_file_download_paths', $post_id, 0, $files); update_post_meta($post_id, '_downloadable_files', $files); update_post_meta($post_id, '_download_limit', $_download_limit); update_post_meta($post_id, '_download_expiry', $_download_expiry); if (isset($_POST['_download_type'])) { update_post_meta($post_id, '_download_type', wc_clean($_POST['_download_type'])); } } // Product url if ('external' == $product_type) { if (isset($_POST['_product_url'])) { update_post_meta($post_id, '_product_url', esc_url_raw($_POST['_product_url'])); } if (isset($_POST['_button_text'])) { update_post_meta($post_id, '_button_text', wc_clean($_POST['_button_text'])); } } // Save variations if ('variable' == $product_type) { // Deprecated since WooCommerce 2.4.0 in favor to WC_AJAX::save_variations() // self::save_variations( $post_id, $post ); // Update parent if variable so price sorting works and stays in sync with the cheapest child WC_Product_Variable::sync($post_id); } // Update version after saving update_post_meta($post_id, '_product_version', WC_VERSION); // Do action for product type do_action('woocommerce_process_product_meta_' . $product_type, $post_id); // Clear cache/transients wc_delete_product_transients($post_id); }
/** * Load variations via AJAX */ public static function load_variations() { ob_start(); check_ajax_referer('load-variations', 'security'); // Check permissions again and make sure we have what we need if (!current_user_can('edit_products') || empty($_POST['product_id']) || empty($_POST['attributes'])) { die(-1); } global $post; $product_id = absint($_POST['product_id']); $post = get_post($product_id); // Set $post global so its available like within the admin screens $per_page = !empty($_POST['per_page']) ? absint($_POST['per_page']) : 10; $page = !empty($_POST['page']) ? absint($_POST['page']) : 1; // Get attributes $attributes = array(); foreach ($_POST['attributes'] as $key => $value) { $attributes[wc_clean($key)] = array_map('wc_clean', $value); } // Get tax classes $tax_classes = WC_Tax::get_tax_classes(); $tax_class_options = array(); $tax_class_options[''] = __('Standard', 'woocommerce'); if (!empty($tax_classes)) { foreach ($tax_classes as $class) { $tax_class_options[sanitize_title($class)] = esc_attr($class); } } // Set backorder options $backorder_options = array('no' => __('Do not allow', 'woocommerce'), 'notify' => __('Allow, but notify customer', 'woocommerce'), 'yes' => __('Allow', 'woocommerce')); // set stock status options $stock_status_options = array('instock' => __('In stock', 'woocommerce'), 'outofstock' => __('Out of stock', 'woocommerce')); $parent_data = array('id' => $product_id, 'attributes' => $attributes, 'tax_class_options' => $tax_class_options, 'sku' => get_post_meta($product_id, '_sku', true), 'weight' => wc_format_localized_decimal(get_post_meta($product_id, '_weight', true)), 'length' => wc_format_localized_decimal(get_post_meta($product_id, '_length', true)), 'width' => wc_format_localized_decimal(get_post_meta($product_id, '_width', true)), 'height' => wc_format_localized_decimal(get_post_meta($product_id, '_height', true)), 'tax_class' => get_post_meta($product_id, '_tax_class', true), 'backorder_options' => $backorder_options, 'stock_status_options' => $stock_status_options); if (!$parent_data['weight']) { $parent_data['weight'] = wc_format_localized_decimal(0); } if (!$parent_data['length']) { $parent_data['length'] = wc_format_localized_decimal(0); } if (!$parent_data['width']) { $parent_data['width'] = wc_format_localized_decimal(0); } if (!$parent_data['height']) { $parent_data['height'] = wc_format_localized_decimal(0); } // Get variations $args = array('post_type' => 'product_variation', 'post_status' => array('private', 'publish'), 'posts_per_page' => $per_page, 'paged' => $page, 'orderby' => 'ID', 'order' => 'DESC', 'post_parent' => $product_id); $variations = get_posts($args); $loop = 0; if ($variations) { foreach ($variations as $variation) { $variation_id = absint($variation->ID); $variation_meta = get_post_meta($variation_id); $variation_data = array(); $shipping_classes = get_the_terms($variation_id, 'product_shipping_class'); $variation_fields = array('_sku' => '', '_stock' => '', '_regular_price' => '', '_sale_price' => '', '_weight' => '', '_length' => '', '_width' => '', '_height' => '', '_download_limit' => '', '_download_expiry' => '', '_downloadable_files' => '', '_downloadable' => '', '_virtual' => '', '_thumbnail_id' => '', '_sale_price_dates_from' => '', '_sale_price_dates_to' => '', '_manage_stock' => '', '_stock_status' => '', '_backorders' => null, '_tax_class' => null, '_variation_description' => ''); foreach ($variation_fields as $field => $value) { $variation_data[$field] = isset($variation_meta[$field][0]) ? maybe_unserialize($variation_meta[$field][0]) : $value; } // Add the variation attributes foreach ($variation_meta as $key => $value) { if (0 !== strpos($key, 'attribute_')) { continue; } /** * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. * Attempt to get full version of the text attribute from the parent. */ if (sanitize_title($value[0]) === $value[0] && version_compare(get_post_meta($product_id, '_product_version', true), '2.4.0', '<')) { foreach ($attributes as $attribute) { if ($key !== 'attribute_' . sanitize_title($attribute['name'])) { continue; } $text_attributes = wc_get_text_attributes($attribute['value']); foreach ($text_attributes as $text_attribute) { if (sanitize_title($text_attribute) === $value[0]) { $value[0] = $text_attribute; } } } } $variation_data[$key] = $value[0]; } // Formatting $variation_data['_regular_price'] = wc_format_localized_price($variation_data['_regular_price']); $variation_data['_sale_price'] = wc_format_localized_price($variation_data['_sale_price']); $variation_data['_weight'] = wc_format_localized_decimal($variation_data['_weight']); $variation_data['_length'] = wc_format_localized_decimal($variation_data['_length']); $variation_data['_width'] = wc_format_localized_decimal($variation_data['_width']); $variation_data['_height'] = wc_format_localized_decimal($variation_data['_height']); $variation_data['_thumbnail_id'] = absint($variation_data['_thumbnail_id']); $variation_data['image'] = $variation_data['_thumbnail_id'] ? wp_get_attachment_thumb_url($variation_data['_thumbnail_id']) : ''; $variation_data['shipping_class'] = $shipping_classes && !is_wp_error($shipping_classes) ? current($shipping_classes)->term_id : ''; // Stock BW compat if ('' !== $variation_data['_stock']) { $variation_data['_manage_stock'] = 'yes'; } include 'admin/meta-boxes/views/html-variation-admin.php'; $loop++; } } die; }
/** * Get attibutes/data for an individual variation from the database and maintain it's integrity. * @since 2.4.0 * @param int $variation_id * @return array */ function wc_get_product_variation_attributes($variation_id) { // Build variation data from meta $all_meta = get_post_meta($variation_id); $parent_id = wp_get_post_parent_id($variation_id); $parent_attributes = array_filter((array) get_post_meta($parent_id, '_product_attributes', true)); $found_parent_attributes = array(); $variation_attributes = array(); // Compare to parent variable product attributes and ensure they match foreach ($parent_attributes as $attribute_name => $options) { if (!empty($options['is_variation'])) { $attribute = 'attribute_' . sanitize_title($attribute_name); $found_parent_attributes[] = $attribute; if (!array_key_exists($attribute, $variation_attributes)) { $variation_attributes[$attribute] = ''; // Add it - 'any' will be asumed } } } // Get the variation attributes from meta foreach ($all_meta as $name => $value) { // Only look at valid attribute meta, and also compare variation level attributes and remove any which do not exist at parent level if (0 !== strpos($name, 'attribute_') || !in_array($name, $found_parent_attributes)) { unset($variation_attributes[$name]); continue; } /** * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. * Attempt to get full version of the text attribute from the parent. */ if (sanitize_title($value[0]) === $value[0] && version_compare(get_post_meta($parent_id, '_product_version', true), '2.4.0', '<')) { foreach ($parent_attributes as $attribute) { if ($name !== 'attribute_' . sanitize_title($attribute['name'])) { continue; } $text_attributes = wc_get_text_attributes($attribute['value']); foreach ($text_attributes as $text_attribute) { if (sanitize_title($text_attribute) === $value[0]) { $value[0] = $text_attribute; break; } } } } $variation_attributes[$name] = $value[0]; } return $variation_attributes; }
/** * Sync the variable product's attributes with the variations. */ public static function sync_attributes($product_id, $children = false) { if (!$children) { $children = get_posts(array('post_parent' => $product_id, 'posts_per_page' => -1, 'post_type' => 'product_variation', 'fields' => 'ids', 'post_status' => 'any')); } /** * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. * Attempt to get full version of the text attribute from the parent and UPDATE meta. */ if (version_compare(get_post_meta($product_id, '_product_version', true), '2.4.0', '<')) { $parent_attributes = array_filter((array) get_post_meta($product_id, '_product_attributes', true)); foreach ($children as $child_id) { $all_meta = get_post_meta($child_id); foreach ($all_meta as $name => $value) { if (0 !== strpos($name, 'attribute_')) { continue; } if (sanitize_title($value[0]) === $value[0]) { foreach ($parent_attributes as $attribute) { if ($name !== 'attribute_' . sanitize_title($attribute['name'])) { continue; } $text_attributes = wc_get_text_attributes($attribute['value']); foreach ($text_attributes as $text_attribute) { if (sanitize_title($text_attribute) === $value[0]) { update_post_meta($child_id, $name, $text_attribute); break; } } } } } } } }
// Only deal with attributes that are variations if (!$attribute['is_variation'] || 'false' === $attribute['is_variation']) { continue; } // Get current value for variation (if set) $variation_selected_value = isset($variation_data['attribute_' . sanitize_title($attribute['name'])]) ? $variation_data['attribute_' . sanitize_title($attribute['name'])] : ''; // Name will be something like attribute_pa_color echo '<select name="attribute_' . sanitize_title($attribute['name']) . '[' . $loop . ']"><option value="">' . __('Any', 'woocommerce') . ' ' . esc_html(wc_attribute_label($attribute['name'])) . '…</option>'; // Get terms for attribute taxonomy or value if its a custom attribute if ($attribute['is_taxonomy']) { $post_terms = wp_get_post_terms($parent_data['id'], $attribute['name']); foreach ($post_terms as $term) { echo '<option ' . selected($variation_selected_value, $term->slug, false) . ' value="' . esc_attr($term->slug) . '">' . esc_html(apply_filters('woocommerce_variation_option_name', $term->name)) . '</option>'; } } else { $options = wc_get_text_attributes($attribute['value']); foreach ($options as $option) { $selected = sanitize_title($variation_selected_value) === $variation_selected_value ? selected($variation_selected_value, sanitize_title($option), false) : selected($variation_selected_value, $option, false); echo '<option ' . $selected . ' value="' . esc_attr($option) . '">' . esc_html(apply_filters('woocommerce_variation_option_name', $option)) . '</option>'; } } echo '</select>'; } ?> <input type="hidden" name="variable_post_id[<?php echo $loop; ?> ]" value="<?php echo esc_attr($variation_id); ?> " />
/** * Read attributes from post meta. * * @param WC_Product * @since 2.7.0 */ protected function read_attributes(&$product) { $meta_values = maybe_unserialize(get_post_meta($product->get_id(), '_product_attributes', true)); if ($meta_values) { $attributes = array(); foreach ($meta_values as $meta_value) { if (!empty($meta_value['is_taxonomy'])) { if (!taxonomy_exists($meta_value['name'])) { continue; } $options = wc_get_object_terms($product->get_id(), $meta_value['name'], 'term_id'); } else { $options = wc_get_text_attributes($meta_value['value']); } $attribute = new WC_Product_Attribute(); $attribute->set_id(wc_attribute_taxonomy_id_by_name($meta_value['name'])); $attribute->set_name($meta_value['name']); $attribute->set_options($options); $attribute->set_position($meta_value['position']); $attribute->set_visible($meta_value['is_visible']); $attribute->set_variation($meta_value['is_variation']); $attributes[] = $attribute; } $product->set_attributes($attributes); } }
/** * Save attributes via ajax. */ public static function save_attributes() { check_ajax_referer('save-attributes', 'security'); if (!current_user_can('edit_products')) { die(-1); } // Get post data parse_str($_POST['data'], $data); $post_id = absint($_POST['post_id']); // Save Attributes $attributes = array(); if (isset($data['attribute_names'])) { $attribute_names = array_map('stripslashes', $data['attribute_names']); $attribute_values = isset($data['attribute_values']) ? $data['attribute_values'] : array(); if (isset($data['attribute_visibility'])) { $attribute_visibility = $data['attribute_visibility']; } if (isset($data['attribute_variation'])) { $attribute_variation = $data['attribute_variation']; } $attribute_is_taxonomy = $data['attribute_is_taxonomy']; $attribute_position = $data['attribute_position']; $attribute_names_max_key = max(array_keys($attribute_names)); for ($i = 0; $i <= $attribute_names_max_key; $i++) { if (empty($attribute_names[$i])) { continue; } $is_visible = isset($attribute_visibility[$i]) ? 1 : 0; $is_variation = isset($attribute_variation[$i]) ? 1 : 0; $is_taxonomy = $attribute_is_taxonomy[$i] ? 1 : 0; if ($is_taxonomy) { if (isset($attribute_values[$i])) { // Select based attributes - Format values (posted values are slugs) if (is_array($attribute_values[$i])) { $values = array_map('sanitize_title', $attribute_values[$i]); // Text based attributes - Posted values are term names, wp_set_object_terms wants ids or slugs. } else { $values = array(); $raw_values = array_map('wc_sanitize_term_text_based', explode(WC_DELIMITER, $attribute_values[$i])); foreach ($raw_values as $value) { $term = get_term_by('name', $value, $attribute_names[$i]); if (!$term) { $term = wp_insert_term($value, $attribute_names[$i]); if ($term && !is_wp_error($term)) { $values[] = $term['term_id']; } } else { $values[] = $term->term_id; } } } // Remove empty items in the array $values = array_filter($values, 'strlen'); } else { $values = array(); } // Update post terms if (taxonomy_exists($attribute_names[$i])) { wp_set_object_terms($post_id, $values, $attribute_names[$i]); } if ($values) { // Add attribute to array, but don't set values $attributes[sanitize_title($attribute_names[$i])] = array('name' => wc_clean($attribute_names[$i]), 'value' => '', 'position' => $attribute_position[$i], 'is_visible' => $is_visible, 'is_variation' => $is_variation, 'is_taxonomy' => $is_taxonomy); } } elseif (isset($attribute_values[$i])) { // Text based, possibly separated by pipes (WC_DELIMITER). Preserve line breaks in non-variation attributes. $values = $is_variation ? wc_clean($attribute_values[$i]) : implode("\n", array_map('wc_clean', explode("\n", $attribute_values[$i]))); $values = implode(' ' . WC_DELIMITER . ' ', wc_get_text_attributes($values)); // Custom attribute - Add attribute to array and set the values $attributes[sanitize_title($attribute_names[$i])] = array('name' => wc_clean($attribute_names[$i]), 'value' => $values, 'position' => $attribute_position[$i], 'is_visible' => $is_visible, 'is_variation' => $is_variation, 'is_taxonomy' => $is_taxonomy); } } } if (!function_exists('attributes_cmp')) { function attributes_cmp($a, $b) { if ($a['position'] == $b['position']) { return 0; } return $a['position'] < $b['position'] ? -1 : 1; } } uasort($attributes, 'attributes_cmp'); update_post_meta($post_id, '_product_attributes', $attributes); die; }
/** * Sync the variable product's attributes with the variations. */ public static function sync_attributes($product, $children = false) { if (!is_a($product, 'WC_Product')) { $product = wc_get_product($product); } /** * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. * Attempt to get full version of the text attribute from the parent and UPDATE meta. */ if (version_compare(get_post_meta($product->get_id(), '_product_version', true), '2.4.0', '<')) { $parent_attributes = array_filter((array) get_post_meta($product->get_id(), '_product_attributes', true)); if (!$children) { $children = $product->get_children('edit'); } foreach ($children as $child_id) { $all_meta = get_post_meta($child_id); foreach ($all_meta as $name => $value) { if (0 !== strpos($name, 'attribute_')) { continue; } if (sanitize_title($value[0]) === $value[0]) { foreach ($parent_attributes as $attribute) { if ('attribute_' . sanitize_title($attribute['name']) !== $name) { continue; } $text_attributes = wc_get_text_attributes($attribute['value']); foreach ($text_attributes as $text_attribute) { if (sanitize_title($text_attribute) === $value[0]) { update_post_meta($child_id, $name, $text_attribute); break; } } } } } } } }
/** * Get formatted variation data with WC < 2.4 back compat and proper formatting of text-based attribute names. * * @return string */ public function get_formatted_variation_attributes($flat = false) { $variation_data = $this->get_variation_attributes(); $attributes = $this->parent->get_attributes(); $description = array(); $return = ''; if (is_array($variation_data)) { if (!$flat) { $return = '<dl class="variation">'; } foreach ($attributes as $attribute) { // Only deal with attributes that are variations if (!$attribute['is_variation']) { continue; } $variation_selected_value = isset($variation_data['attribute_' . sanitize_title($attribute['name'])]) ? $variation_data['attribute_' . sanitize_title($attribute['name'])] : ''; $description_name = esc_html(wc_attribute_label($attribute['name'])); $description_value = __('Any', 'woocommerce'); // Get terms for attribute taxonomy or value if its a custom attribute if ($attribute['is_taxonomy']) { $post_terms = wp_get_post_terms($this->id, $attribute['name']); foreach ($post_terms as $term) { if ($variation_selected_value === $term->slug) { $description_value = esc_html(apply_filters('woocommerce_variation_option_name', $term->name)); } } } else { $options = wc_get_text_attributes($attribute['value']); foreach ($options as $option) { if (sanitize_title($variation_selected_value) === $variation_selected_value) { if ($variation_selected_value !== sanitize_title($option)) { continue; } } else { if ($variation_selected_value !== $option) { continue; } } $description_value = esc_html(apply_filters('woocommerce_variation_option_name', $option)); } } if ($flat) { $description[] = $description_name . ': ' . rawurldecode($description_value); } else { $description[] = '<dt>' . $description_name . ':</dt><dd>' . rawurldecode($description_value) . '</dd>'; } } if ($flat) { $return .= implode(', ', $description); } else { $return .= implode('', $description); } if (!$flat) { $return .= '</dl>'; } } return $return; }
/** * Return an array of attributes used for variations, as well as their possible values. * * @return array of attributes and their available values */ public function get_variation_attributes() { $variation_attributes = array(); if (!$this->has_child()) { return $variation_attributes; } $attributes = $this->get_attributes(); foreach ($attributes as $attribute) { if (!$attribute['is_variation']) { continue; } $values = array(); $attribute_field_name = 'attribute_' . sanitize_title($attribute['name']); // Get used values from children variations foreach ($this->get_children() as $child_id) { $variation = $this->get_child($child_id); if (!empty($variation->variation_id)) { if (!$variation->variation_is_visible()) { continue; // Disabled or hidden } $child_variation_attributes = $variation->get_variation_attributes(); if (isset($child_variation_attributes[$attribute_field_name])) { $values[] = $child_variation_attributes[$attribute_field_name]; } } } // empty value indicates that all options for given attribute are available if (in_array('', $values)) { $values = $attribute['is_taxonomy'] ? wp_get_post_terms($this->id, $attribute['name'], array('fields' => 'slugs')) : wc_get_text_attributes($attribute['value']); // Order custom attributes (non taxonomy) as defined } elseif (!$attribute['is_taxonomy']) { $text_attributes = wc_get_text_attributes($attribute['value']); $assigned_text_attributes = $values; $values = array(); // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute if ($assigned_text_attributes === array_map('sanitize_title', $assigned_text_attributes) && version_compare(get_post_meta($this->id, '_product_version', true), '2.4.0', '<')) { $assigned_text_attributes = array_map('sanitize_title', $assigned_text_attributes); foreach ($text_attributes as $text_attribute) { if (in_array(sanitize_title($text_attribute), $assigned_text_attributes)) { $values[] = $text_attribute; } } } else { foreach ($text_attributes as $text_attribute) { if (in_array($text_attribute, $assigned_text_attributes)) { $values[] = $text_attribute; } } } } $variation_attributes[$attribute['name']] = array_unique($values); } return $variation_attributes; }
public function process_matrix_submission() { global $woocommerce; $items = $_POST['order_info']; $product_id = $_POST['product_id']; $adding_to_cart = wc_get_product($product_id); $added_count = 0; $failed_count = 0; $success_message = ''; $error_message = ''; foreach ($items as $item) { $q = floatval($item['quantity']) ? floatval($item['quantity']) : 0; if ($q) { $variation_id = empty($item['variation_id']) ? '' : absint($item['variation_id']); $missing_attributes = array(); //For validation, since 2.4 $variations = array(); // Only allow integer variation ID - if its not set, redirect to the product page if (empty($variation_id)) { //wc_add_notice( __( 'Please choose product options…', 'woocommerce' ), 'error' ); $failed_count++; continue; } $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($item['variation_data'][$taxonomy])) { // Get value from post data if ($attribute['is_taxonomy']) { // Don't use wc_clean as it destroys sanitized characters $value = sanitize_title(stripslashes($item['variation_data'][$taxonomy])); } else { $value = wc_clean(stripslashes($item['variation_data'][$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 (empty($missing_attributes)) { // Add to cart validation $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $q, $variation_id, $variations); if ($passed_validation) { $added = WC()->cart->add_to_cart($product_id, $q, $variation_id, $variations); } } else { $failed_count++; continue; } if ($added) { $added_count++; } else { $failed_count++; } } } if ($added_count) { woocommerce_bulk_variations_add_to_cart_message($added_count); } if ($failed_count) { WC_Bulk_Variations_Compatibility::wc_add_error(sprintf(__('Unable to add %s to the cart. Please check your quantities and make sure the item is available and in stock', 'wc_bulk_variations'), $failed_count)); } if (!$added_count && !$failed_count) { WC_Bulk_Variations_Compatibility::wc_add_error(__('No product quantities entered.', 'wc_bulk_variations')); } // If we added the products to the cart we can now do a redirect, otherwise just continue loading the page to show errors if ($failed_count === 0 && wc_notice_count('error') === 0) { // If has custom URL redirect there if ($url = apply_filters('woocommerce_add_to_cart_redirect', false)) { wp_safe_redirect($url); exit; } elseif (get_option('woocommerce_cart_redirect_after_add') === 'yes') { wp_safe_redirect(WC()->cart->get_cart_url()); exit; } } }
/** * Prepare attributes for save. * @return array */ public static function prepare_attributes($data = false) { $attributes = array(); if (!$data) { $data = $_POST; } if (isset($data['attribute_names'], $data['attribute_values'])) { $attribute_names = $data['attribute_names']; $attribute_values = $data['attribute_values']; $attribute_visibility = isset($data['attribute_visibility']) ? $data['attribute_visibility'] : array(); $attribute_variation = isset($data['attribute_variation']) ? $data['attribute_variation'] : array(); $attribute_position = $data['attribute_position']; $attribute_names_max_key = max(array_keys($attribute_names)); for ($i = 0; $i <= $attribute_names_max_key; $i++) { if (empty($attribute_names[$i]) || !isset($attribute_values[$i])) { continue; } $attribute_name = wc_clean($attribute_names[$i]); $attribute_id = wc_attribute_taxonomy_id_by_name($attribute_name); $options = isset($attribute_values[$i]) ? $attribute_values[$i] : ''; if (is_array($options)) { // Term ids sent as array. $options = wp_parse_id_list($options); } else { // Terms or text sent in textarea. $options = 0 < $attribute_id ? wc_sanitize_textarea(wc_sanitize_term_text_based($options)) : wc_sanitize_textarea($options); $options = wc_get_text_attributes($options); } $attribute = new WC_Product_Attribute(); $attribute->set_id($attribute_id); $attribute->set_name($attribute_name); $attribute->set_options($options); $attribute->set_position($attribute_position[$i]); $attribute->set_visible(isset($attribute_visibility[$i])); $attribute->set_variation(isset($attribute_variation[$i])); $attributes[] = $attribute; } } return $attributes; }
/** * Get method returns variation meta data if set, otherwise in most cases the data from the parent. * * @param string $key * @return mixed */ public function __get($key) { if (in_array($key, array_keys($this->variation_level_meta_data))) { $value = get_post_meta($this->variation_id, '_' . $key, true); if ('' === $value) { $value = $this->variation_level_meta_data[$key]; } } elseif (in_array($key, array_keys($this->variation_inherited_meta_data))) { $value = metadata_exists('post', $this->variation_id, '_' . $key) ? get_post_meta($this->variation_id, '_' . $key, true) : get_post_meta($this->id, '_' . $key, true); // Handle meta data keys which can be empty at variation level to cause inheritance if ('' === $value && in_array($key, array('sku', 'weight', 'length', 'width', 'height'))) { $value = get_post_meta($this->id, '_' . $key, true); } if ('' === $value) { $value = $this->variation_inherited_meta_data[$key]; } } elseif ('variation_data' === $key) { $all_meta = get_post_meta($this->variation_id); // The variation data array $this->variation_data = array(); // Get the variation attributes from meta foreach ($all_meta as $name => $value) { if (0 !== strpos($name, 'attribute_')) { continue; } /** * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. * Attempt to get full version of the text attribute from the parent. */ if (sanitize_title($value[0]) === $value[0] && version_compare(get_post_meta($this->id, '_product_version', true), '2.4.0', '<')) { $attributes = $this->parent->get_attributes(); foreach ($attributes as $attribute) { if ($name !== 'attribute_' . sanitize_title($attribute['name'])) { continue; } $text_attributes = wc_get_text_attributes($attribute['value']); foreach ($text_attributes as $text_attribute) { if (sanitize_title($text_attribute) === $value[0]) { $value[0] = $text_attribute; } } } } $this->variation_data[$name] = $value[0]; } return $this->variation_data; } elseif ('variation_has_stock' === $key) { return $this->managing_stock(); } else { $value = metadata_exists('post', $this->variation_id, '_' . $key) ? get_post_meta($this->variation_id, '_' . $key, true) : parent::__get($key); } return $value; }
function bf_wc_variations_custom($thepostid, $customfield) { global $variation_data, $post; $post = get_post($thepostid); $variation_data = new WC_Product_Variation($post); $variation_data->get_variation_attributes(); $variation_data = (array) $variation_data; echo '<pre>'; print_r($variation_data); echo '</pre>'; extract($variation_data); ?> <div class="woocommerce_variation wc-metabox closed"> <h3> <a href="#" class="remove_variation delete" rel="<?php echo esc_attr($variation_id); ?> "><?php _e('Remove', 'woocommerce'); ?> </a> <div class="handlediv" title="<?php esc_attr_e('Click to toggle', 'woocommerce'); ?> "></div> <div class="tips sort" data-tip="<?php esc_attr_e('Drag and drop, or click to set menu order manually', 'woocommerce'); ?> "></div> <strong>#<?php echo esc_html($variation_id); ?> : </strong> <?php if (isset($parent_data)) { foreach ($parent_data['attributes'] as $attribute) { // Only deal with attributes that are variations if (!$attribute['is_variation']) { continue; } // Get current value for variation (if set) $variation_selected_value = isset($variation_data['attribute_' . sanitize_title($attribute['name'])]) ? $variation_data['attribute_' . sanitize_title($attribute['name'])] : ''; // Name will be something like attribute_pa_color echo '<select name="attribute_' . sanitize_title($attribute['name']) . '[' . $loop . ']"><option value="">' . __('Any', 'woocommerce') . ' ' . esc_html(wc_attribute_label($attribute['name'])) . '…</option>'; // Get terms for attribute taxonomy or value if its a custom attribute if ($attribute['is_taxonomy']) { $post_terms = wp_get_post_terms($parent_data['id'], $attribute['name']); foreach ($post_terms as $term) { echo '<option ' . selected($variation_selected_value, $term->slug, false) . ' value="' . esc_attr($term->slug) . '">' . apply_filters('woocommerce_variation_option_name', esc_html($term->name)) . '</option>'; } } else { $options = wc_get_text_attributes($attribute['value']); foreach ($options as $option) { $selected = sanitize_title($variation_selected_value) === $variation_selected_value ? selected($variation_selected_value, sanitize_title($option), false) : selected($variation_selected_value, $option, false); echo '<option ' . $selected . ' value="' . esc_attr($option) . '">' . esc_html(apply_filters('woocommerce_variation_option_name', $option)) . '</option>'; } } echo '</select>'; } } ?> <input type="hidden" name="variable_post_id[<?php echo $loop; ?> ]" value="<?php echo esc_attr($variation_id); ?> " /> <input type="hidden" class="variation_menu_order" name="variation_menu_order[<?php echo $loop; ?> ]" value="<?php echo absint($menu_order); ?> " /> </h3> <div class="woocommerce_variable_attributes wc-metabox-content" style="display: none;"> <div class="data"> <p class="form-row form-row-first upload_image"> <a href="#" class="upload_image_button tips <?php if ($_thumbnail_id > 0) { echo 'remove'; } ?> " data-tip="<?php if ($_thumbnail_id > 0) { echo __('Remove this image', 'woocommerce'); } else { echo __('Upload an image', 'woocommerce'); } ?> " rel="<?php echo esc_attr($variation_id); ?> "><img src="<?php if (!empty($image)) { echo esc_attr($image); } else { echo esc_attr(wc_placeholder_img_src()); } ?> " /><input type="hidden" name="upload_image_id[<?php echo $loop; ?> ]" class="upload_image_id" value="<?php echo esc_attr($_thumbnail_id); ?> " /></a> </p> <?php if (wc_product_sku_enabled()) { ?> <p class="sku form-row form-row-last"> <label><?php _e('SKU', 'woocommerce'); ?> : <a class="tips" data-tip="<?php esc_attr_e('Enter a SKU for this variation or leave blank to use the parent product SKU.', 'woocommerce'); ?> " href="#">[?]</a></label> <input type="text" size="5" name="variable_sku[<?php echo $loop; ?> ]" value="<?php if (isset($_sku)) { echo esc_attr($_sku); } ?> " placeholder="<?php echo esc_attr($parent_data['sku']); ?> " /> </p> <?php } else { ?> <input type="hidden" name="variable_sku[<?php echo $loop; ?> ]" value="<?php if (isset($_sku)) { echo esc_attr($_sku); } ?> " /> <?php } ?> <p class="form-row form-row-full options"> <label><input type="checkbox" class="checkbox" name="variable_enabled[<?php echo $loop; ?> ]" <?php checked($variation->post_status, 'publish'); ?> /> <?php _e('Enabled', 'woocommerce'); ?> </label> <label><input type="checkbox" class="checkbox variable_is_downloadable" name="variable_is_downloadable[<?php echo $loop; ?> ]" <?php checked(isset($_downloadable) ? $_downloadable : '', 'yes'); ?> /> <?php _e('Downloadable', 'woocommerce'); ?> <a class="tips" data-tip="<?php esc_attr_e('Enable this option if access is given to a downloadable file upon purchase of a product', 'woocommerce'); ?> " href="#">[?]</a></label> <label><input type="checkbox" class="checkbox variable_is_virtual" name="variable_is_virtual[<?php echo $loop; ?> ]" <?php checked(isset($_virtual) ? $_virtual : '', 'yes'); ?> /> <?php _e('Virtual', 'woocommerce'); ?> <a class="tips" data-tip="<?php esc_attr_e('Enable this option if a product is not shipped or there is no shipping cost', 'woocommerce'); ?> " href="#">[?]</a></label> <?php if (get_option('woocommerce_manage_stock') == 'yes') { ?> <label><input type="checkbox" class="checkbox variable_manage_stock" name="variable_manage_stock[<?php echo $loop; ?> ]" <?php checked(isset($_manage_stock) ? $_manage_stock : '', 'yes'); ?> /> <?php _e('Manage stock?', 'woocommerce'); ?> <a class="tips" data-tip="<?php esc_attr_e('Enable this option to enable stock management at variation level', 'woocommerce'); ?> " href="#">[?]</a></label> <?php } ?> <?php do_action('woocommerce_variation_options', $loop, $variation_data, $variation); ?> </p> <div class="variable_pricing"> <p class="form-row form-row-first"> <label><?php echo __('Regular Price:', 'woocommerce') . ' (' . get_woocommerce_currency_symbol() . ')'; ?> </label> <input type="text" size="5" name="variable_regular_price[<?php echo $loop; ?> ]" value="<?php if (isset($_regular_price)) { echo esc_attr($_regular_price); } ?> " class="wc_input_price" placeholder="<?php esc_attr_e('Variation price (required)', 'woocommerce'); ?> " /> </p> <p class="form-row form-row-last"> <label><?php echo __('Sale Price:', 'woocommerce') . ' (' . get_woocommerce_currency_symbol() . ')'; ?> <a href="#" class="sale_schedule"><?php _e('Schedule', 'woocommerce'); ?> </a><a href="#" class="cancel_sale_schedule" style="display:none"><?php _e('Cancel schedule', 'woocommerce'); ?> </a></label> <input type="text" size="5" name="variable_sale_price[<?php echo $loop; ?> ]" value="<?php if (isset($_sale_price)) { echo esc_attr($_sale_price); } ?> " class="wc_input_price" /> </p> <div class="sale_price_dates_fields" style="display: none"> <p class="form-row form-row-first"> <label><?php _e('Sale start date:', 'woocommerce'); ?> </label> <input type="text" class="sale_price_dates_from" name="variable_sale_price_dates_from[<?php echo $loop; ?> ]" value="<?php echo !empty($_sale_price_dates_from) ? date_i18n('Y-m-d', $_sale_price_dates_from) : ''; ?> " placeholder="<?php echo esc_attr_x('From…', 'placeholder', 'woocommerce'); ?> YYYY-MM-DD" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" /> </p> <p class="form-row form-row-last"> <label><?php _e('Sale end date:', 'woocommerce'); ?> </label> <input type="text" class="sale_price_dates_to" name="variable_sale_price_dates_to[<?php echo $loop; ?> ]" value="<?php echo !empty($_sale_price_dates_to) ? date_i18n('Y-m-d', $_sale_price_dates_to) : ''; ?> " placeholder="<?php echo esc_attr_x('To…', 'placeholder', 'woocommerce'); ?> YYYY-MM-DD" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" /> </p> </div> </div> <?php if ('yes' == get_option('woocommerce_manage_stock')) { ?> <div class="show_if_variation_manage_stock" style="display: none;"> <p class="form-row form-row-first"> <label><?php _e('Stock Qty:', 'woocommerce'); ?> <a class="tips" data-tip="<?php esc_attr_e('Enter a quantity to enable stock management at variation level, or leave blank to use the parent product\'s options.', 'woocommerce'); ?> " href="#">[?]</a></label> <input type="number" size="5" name="variable_stock[<?php echo $loop; ?> ]" value="<?php if (isset($_stock)) { echo esc_attr(wc_stock_amount($_stock)); } ?> " step="any" /> </p> <p class="form-row form-row-last"> <label><?php _e('Allow Backorders?', 'woocommerce'); ?> </label> <select name="variable_backorders[<?php echo $loop; ?> ]"> <?php foreach ($parent_data['backorder_options'] as $key => $value) { echo '<option value="' . esc_attr($key) . '" ' . selected($key === $_backorders, true, false) . '>' . esc_html($value) . '</option>'; } ?> </select> </p> </div> <?php } ?> <div class=""> <p class="form-row form-row-full"> <label><?php _e('Stock status', 'woocommerce'); ?> <a class="tips" data-tip="<?php esc_attr_e('Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce'); ?> " href="#">[?]</a></label> <select name="variable_stock_status[<?php echo $loop; ?> ]"> <?php foreach ($parent_data['stock_status_options'] as $key => $value) { echo '<option value="' . esc_attr($key === $_stock_status ? '' : $key) . '" ' . selected($key === $_stock_status, true, false) . '>' . esc_html($value) . '</option>'; } ?> </select> </p> </div> <?php if (wc_product_weight_enabled() || wc_product_dimensions_enabled()) { ?> <div> <?php if (wc_product_weight_enabled()) { ?> <p class="form-row hide_if_variation_virtual form-row-first"> <label><?php echo __('Weight', 'woocommerce') . ' (' . esc_html(get_option('woocommerce_weight_unit')) . '):'; ?> <a class="tips" data-tip="<?php esc_attr_e('Enter a weight for this variation or leave blank to use the parent product weight.', 'woocommerce'); ?> " href="#">[?]</a></label> <input type="text" size="5" name="variable_weight[<?php echo $loop; ?> ]" value="<?php if (isset($_weight)) { echo esc_attr($_weight); } ?> " placeholder="<?php echo esc_attr($parent_data['weight']); ?> " class="wc_input_decimal" /> </p> <?php } else { ?> <p> </p> <?php } ?> <?php if (wc_product_dimensions_enabled()) { ?> <p class="form-row dimensions_field hide_if_variation_virtual form-row-last"> <label for="product_length"><?php echo __('Dimensions (L×W×H)', 'woocommerce') . ' (' . esc_html(get_option('woocommerce_dimension_unit')) . '):'; ?> </label> <input id="product_length" class="input-text wc_input_decimal" size="6" type="text" name="variable_length[<?php echo $loop; ?> ]" value="<?php if (isset($_length)) { echo esc_attr($_length); } ?> " placeholder="<?php echo esc_attr($parent_data['length']); ?> " /> <input class="input-text wc_input_decimal" size="6" type="text" name="variable_width[<?php echo $loop; ?> ]" value="<?php if (isset($_width)) { echo esc_attr($_width); } ?> " placeholder="<?php echo esc_attr($parent_data['width']); ?> " /> <input class="input-text wc_input_decimal last" size="6" type="text" name="variable_height[<?php echo $loop; ?> ]" value="<?php if (isset($_height)) { echo esc_attr($_height); } ?> " placeholder="<?php echo esc_attr($parent_data['height']); ?> " /> </p> <?php } else { ?> <p> </p> <?php } ?> </div> <?php } ?> <div> <p class="form-row hide_if_variation_virtual form-row-full"><label><?php _e('Shipping class:', 'woocommerce'); ?> </label> <?php $args = array('taxonomy' => 'product_shipping_class', 'hide_empty' => 0, 'show_option_none' => __('Same as parent', 'woocommerce'), 'name' => 'variable_shipping_class[' . $loop . ']', 'id' => '', 'selected' => isset($shipping_class) ? esc_attr($shipping_class) : '', 'echo' => 0); echo wp_dropdown_categories($args); ?> </p> <?php if (wc_tax_enabled()) { ?> <p class="form-row form-row-full"> <label><?php _e('Tax class:', 'woocommerce'); ?> </label> <select name="variable_tax_class[<?php echo $loop; ?> ]"> <option value="parent" <?php selected(is_null($_tax_class), true); ?> ><?php _e('Same as parent', 'woocommerce'); ?> </option> <?php foreach ($parent_data['tax_class_options'] as $key => $value) { echo '<option value="' . esc_attr($key) . '" ' . selected($key === $_tax_class, true, false) . '>' . esc_html($value) . '</option>'; } ?> </select> </p> <?php } ?> <p class="form-row form-row-full"> <label><?php _e('Variation Description:', 'woocommerce'); ?> </label> <textarea name="variable_description[<?php echo $loop; ?> ]" rows="3" style="width:100%;"><?php echo isset($variation_data['_variation_description']) ? esc_textarea($variation_data['_variation_description']) : ''; ?> </textarea> </p> </div> <div class="show_if_variation_downloadable" style="display: none;"> <div class="form-row form-row-full downloadable_files"> <label><?php _e('Downloadable Files', 'woocommerce'); ?> :</label> <table class="widefat"> <thead> <div> <th><?php _e('Name', 'woocommerce'); ?> <span class="tips" data-tip="<?php esc_attr_e('This is the name of the download shown to the customer.', 'woocommerce'); ?> ">[?]</span></th> <th colspan="2"><?php _e('File URL', 'woocommerce'); ?> <span class="tips" data-tip="<?php esc_attr_e('This is the URL or absolute path to the file which customers will get access to. URLs entered here should already be encoded.', 'woocommerce'); ?> ">[?]</span></th> <th> </th> </div> </thead> <tbody> <?php if ($_downloadable_files) { foreach ($_downloadable_files as $key => $file) { if (!is_array($file)) { $file = array('file' => $file, 'name' => ''); } include 'html-product-variation-download.php'; } } ?> </tbody> <tfoot> <div> <th colspan="4"> <a href="#" class="button insert" data-row="<?php $file = array('file' => '', 'name' => ''); ob_start(); include 'html-product-variation-download.php'; echo esc_attr(ob_get_clean()); ?> "><?php _e('Add File', 'woocommerce'); ?> </a> </th> </div> </tfoot> </table> </div> </div> <div class="show_if_variation_downloadable" style="display: none;"> <p class="form-row form-row-first"> <label><?php _e('Download Limit:', 'woocommerce'); ?> <a class="tips" data-tip="<?php esc_attr_e('Leave blank for unlimited re-downloads.', 'woocommerce'); ?> " href="#">[?]</a></label> <input type="number" size="5" name="variable_download_limit[<?php echo $loop; ?> ]" value="<?php if (isset($_download_limit)) { echo esc_attr($_download_limit); } ?> " placeholder="<?php esc_attr_e('Unlimited', 'woocommerce'); ?> " step="1" min="0" /> </p> <p class="form-row form-row-last"> <label><?php _e('Download Expiry:', 'woocommerce'); ?> <a class="tips" data-tip="<?php esc_attr_e('Enter the number of days before a download link expires, or leave blank.', 'woocommerce'); ?> " href="#">[?]</a></label> <input type="number" size="5" name="variable_download_expiry[<?php echo $loop; ?> ]" value="<?php if (isset($_download_expiry)) { echo esc_attr($_download_expiry); } ?> " placeholder="<?php esc_attr_e('Unlimited', 'woocommerce'); ?> " step="1" min="0" /> </p> </div> <?php do_action('woocommerce_product_after_variable_attributes', $loop, $variation_data, $variation); ?> </div> </div> </div> <?php }